From 1b81970a1edc19a228793441a21086b6c3b33aa2 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Fri, 17 Aug 2018 22:01:13 -0400 Subject: [PATCH 001/390] initial commit of e2e backup proposal --- .../1219-storing-megolm-keys-serverside.md | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 proposals/1219-storing-megolm-keys-serverside.md diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md new file mode 100644 index 00000000..b78fc953 --- /dev/null +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -0,0 +1,197 @@ +Storing megolm keys serverside +============================== + +Background +---------- + +We *optionally* let clients store a copy of their megolm inbound session keys +on the HS so that they can recover history if all devices are lost without an +explicit key export; transparently share history between a user's devices; +transparently share missing keys between a user's devices to fix UISIs; support +clients with limited local storage for keys. + +See also: + +* https://github.com/matrix-org/matrix-doc/issues/1219 +* https://github.com/vector-im/riot-web/issues/3661 +* https://github.com/vector-im/riot-web/issues/5675 +* https://docs.google.com/document/d/1MOoIA9qEKIhUQ3UmKZG-loqA8e0BzgWKKlKRUGMynVc/edit# + (old version of proposal) + +Proposal +-------- + +This proposal creates new APIs to allow clients to back up room decryption keys +on the server. Decryption keys are encrypted (using public key crypto) before +being sent to the server along with some unencrypted metadata to allow the +server to manage the backups, overwriting backups with "better" versions of the +keys. The user is given a private recovery key to save for recovering the keys +from the backup. + +Clients can create new versions of backups. A client would start a new version +of a backup when, for example, a user loses a device, and wants to ensure that +that device does not get any new decryption keys. + +### Possible UX for interactive clients + +On receipt of encryption keys (1st time): + +1. client checks if there is an existing backup: `GET /room_keys/version` + 1. if not, ask if the user wants to back up keys + 1. if yes: + 1. generate new key pair + 2. create new backup version: `POST /room_keys/version` + 3. display private key to user to save + 2. if no, exit and remember decision (user can change their mind later) + 3. while prompting, continue to poll `GET /room_keys/versions`, as + another device may have created a backup. If so, go to 1.2. + 2. if yes, get public key, prompt user to verify a device that signed the + key¹, or enter recovery key (which can derive the backup key). + 1. User can also decide to create a new backup, in which case, go to 1.1. +2. send key to backup: `PUT /room_keys/keys/${roomId}/${sessionId}?version=$v` +3. continue backing up keys as we receive them (may receive a 403 error if a + new backup version has been created: see below) + +On 403 error when trying to `PUT` keys: + +1. get the current version +2. notify the user that there is a new backup version, and display relevant + information +3. confirm with user that they want to use the backup (user may want use the + backup, to stop backing up keys, or to create a new backup) +4. verify the device that signed the backup key¹, or enter recovery key + +¹: cross-signing (when that is completed) can be used to verify the device +that signed the key. + +On receipt of undecryptable message: + +1. ask user if they want to restore backup (ask whether to get individual key, + room keys, or all keys). (This can be done in the same place as asking if + the user wants to request keys from other devices.) +2. if yes, prompt for private key, and get keys: `GET /room_keys/keys` + +### API + +#### Backup versions + +##### `POST /room_keys/version` + +Create a new backup version. + +Body parameters: + +- `algorithm` (string): Required. The algorithm used for storing backups. + Currently, only `m.megolm_backup.v1` is defined. (FIXME: change the algorithm + name to include the encryption method) +- `auth_data` (string or object): Required. algorithm-dependent data. For + `m.megolm_backup.v1`, this is a signedjson object with the following keys: + - `public_key` (string): ... + - `signatures` (object): signatures of the public key + +Example: + +```javascript +{ + "algorithm": "m.megolm_backup.v1", + "auth_data": { + "public_key": { + "public_key": "abcdefg", + "signatures": { + "something": { + "ed25519:something": "hijklmnop" + } + } + } + } +} +``` + +On success, returns a JSON object with keys: + +- `version` (integer): the backup version + +##### `GET /room_keys/version` + +Get information about the current version. + +On success, returns a JSON object with keys: + +- `algorithm` (string): Required. Same as in the body parameters for `POST + /room_keys/version`. +- `auth_data` (string or object): Required. Same as in the body parameters for + `POST /room_keys/version`. +- `version` (integer): the backup version + + +#### Storing keys + +##### `PUT /room_keys/keys/${roomId}/${sessionId}?version=$v` + +Store the key for the given session in the given room, using the given backup +version. + +If the server already has a backup in the backup version for the given session +and room, then it will keep the "better" one ... + +Body parameters: + +- `first_message_index` (integer): Required. The index of the first message + in the session that the key can decrypt. +- `forwarded_count` (integer): Required. The number of times this key has been + forwarded. +- `is_verified` (boolean): Whether the device backing up the key has verified + the device that the key is from. +- `session_data` (string): The backup of the key, encrypted according to the + backup algorithm. + +On success, returns ... ? + +##### `PUT /room_keys/keys/${roomId}?version=$v` + +Store several keys for the given room, using the given backup version. + +Behaves the same way as if the keys were added individually using `PUT +/room_keys/keys/${roomId}/${sessionId}?version=$v`. + +Body paremeters: +- `sessions` (object): an object where the keys are the session IDs, and the + values are objects of the same form as the body in `PUT + /room_keys/keys/${roomId}/${sessionId}?version=$v`. + +On success, returns same as `PUT +/room_keys/keys/${roomId}/${sessionId}?version=$v` + +##### `PUT /room_keys/keys/?version=$v` + +... + +#### Retrieving keys + +##### `GET /room_keys/keys/${roomId}/${sessionId}?version=$v` +##### `GET /room_keys/keys/${roomId}?version=$v` +##### `GET /room_keys/keys/?version=$v` + +#### Deleting keys + +##### `DELETE /room_keys/keys/${roomId}/${sessionId}?version=$v` +##### `DELETE /room_keys/keys/${roomId}?version=$v` +##### `DELETE /room_keys/keys/?version=$v` + +Tradeoffs +--------- + +Security Considerations +----------------------- + +An attacker who gains access to a user's account can delete or corrupt their +key backup. This proposal does not attempt to protect against that. + +Other Issues +------------ + +Since many clients will receive encryption keys at around the same time, +clients should randomly offset their requests ... + +Conclusion +---------- From 6e8ba1f7f8cd14e8540ef54d3efb1ac9694e5167 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 23 Aug 2018 23:04:21 -0400 Subject: [PATCH 002/390] add more details --- .../1219-storing-megolm-keys-serverside.md | 75 ++++++++++++++----- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index b78fc953..3af86f17 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -6,9 +6,8 @@ Background We *optionally* let clients store a copy of their megolm inbound session keys on the HS so that they can recover history if all devices are lost without an -explicit key export; transparently share history between a user's devices; -transparently share missing keys between a user's devices to fix UISIs; support -clients with limited local storage for keys. +explicit key export; fix UISIs; support clients with limited local storage for +keys. See also: @@ -28,9 +27,14 @@ server to manage the backups, overwriting backups with "better" versions of the keys. The user is given a private recovery key to save for recovering the keys from the backup. -Clients can create new versions of backups. A client would start a new version -of a backup when, for example, a user loses a device, and wants to ensure that -that device does not get any new decryption keys. +Clients can create new versions of backups. Aside from the initial backup +creation, a client might start a new version of a backup when, for example, a +user loses a device, and wants to ensure that that device does not get any new +decryption keys. + +Once one client has created a backup version, other clients can fetch the +public key for the backup from the server and add keys to the backup, if they +trust that the backup was not created by a malicious device. ### Possible UX for interactive clients @@ -49,10 +53,11 @@ On receipt of encryption keys (1st time): key¹, or enter recovery key (which can derive the backup key). 1. User can also decide to create a new backup, in which case, go to 1.1. 2. send key to backup: `PUT /room_keys/keys/${roomId}/${sessionId}?version=$v` -3. continue backing up keys as we receive them (may receive a 403 error if a - new backup version has been created: see below) +3. continue backing up keys as we receive them (may receive a + `M_WRONG_ROOM_KEYS_VERSION` error if a new backup version has been created: + see below) -On 403 error when trying to `PUT` keys: +On `M_WRONG_ROOM_KEYS_VERSION` error when trying to `PUT` keys: 1. get the current version 2. notify the user that there is a new backup version, and display relevant @@ -71,6 +76,9 @@ On receipt of undecryptable message: the user wants to request keys from other devices.) 2. if yes, prompt for private key, and get keys: `GET /room_keys/keys` +Users can also set up or disable backups, or restore from backup via user +settings. + ### API #### Backup versions @@ -86,7 +94,7 @@ Body parameters: name to include the encryption method) - `auth_data` (string or object): Required. algorithm-dependent data. For `m.megolm_backup.v1`, this is a signedjson object with the following keys: - - `public_key` (string): ... + - `public_key` (string): the public key used to encrypt the backups - `signatures` (object): signatures of the public key Example: @@ -95,12 +103,10 @@ Example: { "algorithm": "m.megolm_backup.v1", "auth_data": { - "public_key": { - "public_key": "abcdefg", - "signatures": { - "something": { - "ed25519:something": "hijklmnop" - } + "public_key": "abcdefg", + "signatures": { + "something": { + "ed25519:something": "hijklmnop" } } } @@ -123,6 +129,10 @@ On success, returns a JSON object with keys: `POST /room_keys/version`. - `version` (integer): the backup version +Error codes: + +- `M_UNKNOWN`: No backup version has been created. FIXME: why not + `M_NOT_FOUND`? #### Storing keys @@ -142,11 +152,22 @@ Body parameters: forwarded. - `is_verified` (boolean): Whether the device backing up the key has verified the device that the key is from. -- `session_data` (string): The backup of the key, encrypted according to the - backup algorithm. +- `session_data` (string or object): Algorithm-dependent data. For + `m.megolm_backup.v1`, this is an object with the following keys: + - `ciphertext` (string): the encrypted version of the session key. See below + for how the session key is encoded. + - `ephemeral` (string): the public ephemeral key that was used to encrypt the + session key. + - `mac` (string): the message authentication code for the ciphertext. FIXME: + more details On success, returns ... ? +Error codes: + +- `M_WRONG_ROOM_KEYS_VERSION`: the version specified does not match the current + backup version + ##### `PUT /room_keys/keys/${roomId}?version=$v` Store several keys for the given room, using the given backup version. @@ -159,7 +180,7 @@ Body paremeters: values are objects of the same form as the body in `PUT /room_keys/keys/${roomId}/${sessionId}?version=$v`. -On success, returns same as `PUT +Returns the same as `PUT /room_keys/keys/${roomId}/${sessionId}?version=$v` ##### `PUT /room_keys/keys/?version=$v` @@ -178,6 +199,18 @@ On success, returns same as `PUT ##### `DELETE /room_keys/keys/${roomId}?version=$v` ##### `DELETE /room_keys/keys/?version=$v` +#### Key format + +Session keys are encoded as a JSON object with the properties: + +- `algorithm` (string): `m.megolm.v1.aes-sha2` +- `sender_key` (string): base64-encoded device curve25519 key +- `sender_claimed_keys` (object): object containing the identity keys for the + sending device +- `forwardingCurve25519KeyChain` (array): zero or more curve25519 keys for + devices who forwarded the session key +- `session_key` (string): base64-encoded session key + Tradeoffs --------- @@ -187,6 +220,10 @@ Security Considerations An attacker who gains access to a user's account can delete or corrupt their key backup. This proposal does not attempt to protect against that. +An attacker who gains access to a user's account can create a new backup +version using a key that they control. For this reason, clients SHOULD confirm +with users before sending keys to a new backup version. + Other Issues ------------ From 87772329875666f57f1ed5f5540a5626f6f3432e Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 5 Sep 2018 23:21:35 -0400 Subject: [PATCH 003/390] various clarifications --- proposals/1219-storing-megolm-keys-serverside.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 3af86f17..e24262e7 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -45,7 +45,8 @@ On receipt of encryption keys (1st time): 1. if yes: 1. generate new key pair 2. create new backup version: `POST /room_keys/version` - 3. display private key to user to save + 3. display private key to user to save TODO: specify how the key is + displayed 2. if no, exit and remember decision (user can change their mind later) 3. while prompting, continue to poll `GET /room_keys/versions`, as another device may have created a backup. If so, go to 1.2. @@ -76,7 +77,7 @@ On receipt of undecryptable message: the user wants to request keys from other devices.) 2. if yes, prompt for private key, and get keys: `GET /room_keys/keys` -Users can also set up or disable backups, or restore from backup via user +Users can also set up, disable, or rotate backups, or restore from backup via user settings. ### API @@ -201,7 +202,7 @@ Returns the same as `PUT #### Key format -Session keys are encoded as a JSON object with the properties: +Each session key is encoded as a JSON object with the properties: - `algorithm` (string): `m.megolm.v1.aes-sha2` - `sender_key` (string): base64-encoded device curve25519 key @@ -211,6 +212,8 @@ Session keys are encoded as a JSON object with the properties: devices who forwarded the session key - `session_key` (string): base64-encoded session key +... + Tradeoffs --------- @@ -227,8 +230,11 @@ with users before sending keys to a new backup version. Other Issues ------------ -Since many clients will receive encryption keys at around the same time, -clients should randomly offset their requests ... +Since many clients will receive encryption keys at around the same time, they +will all want to back up their copies of the keys at around the same time, +which may increase load on the server if this happens in a big room. (TODO: +how much of an issue is this?) For this reason, clients should offset their +backup requests randomly. Conclusion ---------- From 846e9e8fdc7d00a0ee163fae49eaebdc8938cd04 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 6 Sep 2018 17:52:31 -0400 Subject: [PATCH 004/390] add clarifications --- .../1219-storing-megolm-keys-serverside.md | 78 ++++++++++++++----- 1 file changed, 58 insertions(+), 20 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index e24262e7..72038436 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -43,7 +43,7 @@ On receipt of encryption keys (1st time): 1. client checks if there is an existing backup: `GET /room_keys/version` 1. if not, ask if the user wants to back up keys 1. if yes: - 1. generate new key pair + 1. generate new curve25519 key pair 2. create new backup version: `POST /room_keys/version` 3. display private key to user to save TODO: specify how the key is displayed @@ -91,18 +91,18 @@ Create a new backup version. Body parameters: - `algorithm` (string): Required. The algorithm used for storing backups. - Currently, only `m.megolm_backup.v1` is defined. (FIXME: change the algorithm - name to include the encryption method) + Currently, only `m.megolm_backup.v1.curve25519-aes-sha2` is defined. - `auth_data` (string or object): Required. algorithm-dependent data. For - `m.megolm_backup.v1`, this is a signedjson object with the following keys: - - `public_key` (string): the public key used to encrypt the backups + `m.megolm_backup.v1.curve25519-aes-sha2`, this is a signedjson object with + the following keys: + - `public_key` (string): the curve25519 public key used to encrypt the backups - `signatures` (object): signatures of the public key Example: ```javascript { - "algorithm": "m.megolm_backup.v1", + "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2", "auth_data": { "public_key": "abcdefg", "signatures": { @@ -143,7 +143,10 @@ Store the key for the given session in the given room, using the given backup version. If the server already has a backup in the backup version for the given session -and room, then it will keep the "better" one ... +and room, then it will keep the "better" one. To determine which one is +"better", key backups are compared first by the `is_verified` flag (`true` is +better than `false`), then by the `first_message_index` (a lower number is better), +and finally by `forwarded_count` (a lower number is better). Body parameters: @@ -154,15 +157,16 @@ Body parameters: - `is_verified` (boolean): Whether the device backing up the key has verified the device that the key is from. - `session_data` (string or object): Algorithm-dependent data. For - `m.megolm_backup.v1`, this is an object with the following keys: - - `ciphertext` (string): the encrypted version of the session key. See below - for how the session key is encoded. - - `ephemeral` (string): the public ephemeral key that was used to encrypt the - session key. + `m.megolm_backup.v1.curve25519-aes-sha2`, this is an object with the + following keys (TODO: this stuff should be moved): + - `ciphertext` (string): the encrypted version of the session key, as an + unpadded base64 string. See below for how the session key is encoded. + - `ephemeral` (string): the public ephemeral curve25519 key that was used to + encrypt the session key, as an unpadded base64 string. - `mac` (string): the message authentication code for the ciphertext. FIXME: more details -On success, returns ... ? +On success, returns the empty JSON object. Error codes: @@ -176,7 +180,7 @@ Store several keys for the given room, using the given backup version. Behaves the same way as if the keys were added individually using `PUT /room_keys/keys/${roomId}/${sessionId}?version=$v`. -Body paremeters: +Body parameters: - `sessions` (object): an object where the keys are the session IDs, and the values are objects of the same form as the body in `PUT /room_keys/keys/${roomId}/${sessionId}?version=$v`. @@ -184,15 +188,43 @@ Body paremeters: Returns the same as `PUT /room_keys/keys/${roomId}/${sessionId}?version=$v` -##### `PUT /room_keys/keys/?version=$v` +##### `PUT /room_keys/keys?version=$v` + +Store several keys, using the given backup version. + +Behaves the same way as if the keys were added individually using `PUT +/room_keys/keys/${roomId}/${sessionId}?version=$v`. + +Body parameters: +- `rooms` (object): an object where the keys are the room IDs, and the values + are objects of the same form as the body in `PUT + /room_keys/keys/${roomId}/?version=$v`. -... +Returns the same as `PUT +/room_keys/keys/${roomId}/${sessionId}?version=$v` #### Retrieving keys ##### `GET /room_keys/keys/${roomId}/${sessionId}?version=$v` + +Retrieve the key for the given session in the given room from the backup. + +On success, returns a JSON object in the same form as the request body of `PUT +/room_keys/keys/${roomId}/${sessionId}?version=$v`. + ##### `GET /room_keys/keys/${roomId}?version=$v` -##### `GET /room_keys/keys/?version=$v` + +Retrieve the all the keys for the given room from the backup. + +On success, returns a JSON object in the same form as the request body of `PUT +/room_keys/keys/${roomId}?version=$v`. + +##### `GET /room_keys/keys?version=$v` + +Retrieve all the keys from the backup. + +On success, returns a JSON object in the same form as the request body of `PUT +/room_keys/keys?version=$v`. #### Deleting keys @@ -200,7 +232,11 @@ Returns the same as `PUT ##### `DELETE /room_keys/keys/${roomId}?version=$v` ##### `DELETE /room_keys/keys/?version=$v` -#### Key format +Deletes keys from the backup. + +On success, returns the empty JSON object. + +#### `m.megolm_backup.v1.curve25519-aes-sha2` Key format Each session key is encoded as a JSON object with the properties: @@ -210,9 +246,11 @@ Each session key is encoded as a JSON object with the properties: sending device - `forwardingCurve25519KeyChain` (array): zero or more curve25519 keys for devices who forwarded the session key -- `session_key` (string): base64-encoded session key +- `session_key` (string): base64-encoded (unpadded) session key -... +The JSON object is then encrypted by generating an ephemeral curve25519 key, +performing an ECDH with the ephemeral key and the backup's public key to +generate an AES key, and encrypting the stringified object using AES. Tradeoffs --------- From 72df5fe43645acb97e6255cbf2c5368ee2aa4ad3 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 10 Oct 2018 16:28:24 -0400 Subject: [PATCH 005/390] add details on recovery key format, and some cleanups/fixes --- .../1219-storing-megolm-keys-serverside.md | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 72038436..ab75ccf2 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -45,8 +45,7 @@ On receipt of encryption keys (1st time): 1. if yes: 1. generate new curve25519 key pair 2. create new backup version: `POST /room_keys/version` - 3. display private key to user to save TODO: specify how the key is - displayed + 3. display private key to user to save (see below for the format) 2. if no, exit and remember decision (user can change their mind later) 3. while prompting, continue to poll `GET /room_keys/versions`, as another device may have created a backup. If so, go to 1.2. @@ -80,6 +79,30 @@ On receipt of undecryptable message: Users can also set up, disable, or rotate backups, or restore from backup via user settings. +### Recovery key + +The recovery key is can either be saved by the user directly, or stored +encrypted on the server (as proposed in +[MSC1687](https://github.com/matrix-org/matrix-doc/issues/1687)). If the key +is saved directly by the user, then it the code is constructed as follows: + +1. The 256-bit curve25519 private key is prepended by the bytes `0x8B` and + `0x01` +2. All the bytes in the string are above are XORed together to form a parity + byte. This parity byte is appended to the byte string. +3. The byte string is encoded using base58, using the same mapping as is used + for Bitcoin addresses. + +This 58-character string is presented to the user to save. Implementations may +add whitespace to the recovery key. + +When reading in a recovery key, clients must disregard whitespace. Clients +must base58-decode the code, ensure that the first two bytes of the decoded +string are `0x8B` and `0x01`, ensure that XOR-ing all the bytes together +results in 0, and ensure that the total length of the decoded string +is 35 bytes. Clients must then remove the first two bytes and the last byte, +and use the resulting string as the private key to decrypt backups. + ### API #### Backup versions @@ -118,9 +141,10 @@ On success, returns a JSON object with keys: - `version` (integer): the backup version -##### `GET /room_keys/version` +##### `GET /room_keys/version/{version}` -Get information about the current version. +Get information about the given version, or the current version if `{version}` +is omitted. On success, returns a JSON object with keys: @@ -128,12 +152,11 @@ On success, returns a JSON object with keys: /room_keys/version`. - `auth_data` (string or object): Required. Same as in the body parameters for `POST /room_keys/version`. -- `version` (integer): the backup version +- `version` (integer): Required. The backup version. Error codes: -- `M_UNKNOWN`: No backup version has been created. FIXME: why not - `M_NOT_FOUND`? +- `M_NOT_FOUND`: No backup version has been created. #### Storing keys From de5120335f950a53fec8374058f8c13055a0cecb Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 11 Oct 2018 10:22:42 -0400 Subject: [PATCH 006/390] change "string or object" to just "object" --- proposals/1219-storing-megolm-keys-serverside.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index ab75ccf2..312e43bf 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -115,7 +115,7 @@ Body parameters: - `algorithm` (string): Required. The algorithm used for storing backups. Currently, only `m.megolm_backup.v1.curve25519-aes-sha2` is defined. -- `auth_data` (string or object): Required. algorithm-dependent data. For +- `auth_data` (object): Required. algorithm-dependent data. For `m.megolm_backup.v1.curve25519-aes-sha2`, this is a signedjson object with the following keys: - `public_key` (string): the curve25519 public key used to encrypt the backups @@ -150,7 +150,7 @@ On success, returns a JSON object with keys: - `algorithm` (string): Required. Same as in the body parameters for `POST /room_keys/version`. -- `auth_data` (string or object): Required. Same as in the body parameters for +- `auth_data` (object): Required. Same as in the body parameters for `POST /room_keys/version`. - `version` (integer): Required. The backup version. @@ -179,7 +179,7 @@ Body parameters: forwarded. - `is_verified` (boolean): Whether the device backing up the key has verified the device that the key is from. -- `session_data` (string or object): Algorithm-dependent data. For +- `session_data` (object): Algorithm-dependent data. For `m.megolm_backup.v1.curve25519-aes-sha2`, this is an object with the following keys (TODO: this stuff should be moved): - `ciphertext` (string): the encrypted version of the session key, as an From b45416e8b0ab432511eb73ece127f623bf7d8cc9 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Fri, 19 Oct 2018 22:19:55 -0400 Subject: [PATCH 007/390] change version from string to integer, plus other minor improvements --- proposals/1219-storing-megolm-keys-serverside.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 312e43bf..14149950 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -139,7 +139,7 @@ Example: On success, returns a JSON object with keys: -- `version` (integer): the backup version +- `version` (string): the backup version ##### `GET /room_keys/version/{version}` @@ -152,7 +152,7 @@ On success, returns a JSON object with keys: /room_keys/version`. - `auth_data` (object): Required. Same as in the body parameters for `POST /room_keys/version`. -- `version` (integer): Required. The backup version. +- `version` (string): Required. The backup version. Error codes: @@ -275,9 +275,6 @@ The JSON object is then encrypted by generating an ephemeral curve25519 key, performing an ECDH with the ephemeral key and the backup's public key to generate an AES key, and encrypting the stringified object using AES. -Tradeoffs ---------- - Security Considerations ----------------------- @@ -286,7 +283,8 @@ key backup. This proposal does not attempt to protect against that. An attacker who gains access to a user's account can create a new backup version using a key that they control. For this reason, clients SHOULD confirm -with users before sending keys to a new backup version. +with users before sending keys to a new backup version or verify that it was +created by a trusted device by checking the signature. Other Issues ------------ @@ -299,3 +297,7 @@ backup requests randomly. Conclusion ---------- + +This proposal allows users to securely and conveniently back up and restore +their decryption keys so that users logging into a new device can decrypt old +messages. From 9d51d1e8b70a63fd36328018fde731f26f6b9b71 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Sat, 20 Oct 2018 13:45:35 -0400 Subject: [PATCH 008/390] expand the background --- .../1219-storing-megolm-keys-serverside.md | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 14149950..905cd264 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -4,10 +4,28 @@ Storing megolm keys serverside Background ---------- -We *optionally* let clients store a copy of their megolm inbound session keys -on the HS so that they can recover history if all devices are lost without an -explicit key export; fix UISIs; support clients with limited local storage for -keys. +A user who uses end-to-end encyrption will usually have many inbound session +keys. Users who log into new devices and want to read old messages will need a +convenient way to transfer the session keys from one device to another. While +users can currently export their keys from one device and import them to +another, this is involves several steps and may be cumbersome for many users. +Users can also share keys from one device to another, but this has several +limitations, such as the fact that key shares only share one key at a time, and +require another logged-in device to be active. + +To help resolve this, we *optionally* let clients store an encrypted copy of +their megolm inbound session keys on the homeserver. Clients can keep the +backup up to date, so that users will always have the keys needed to decrypt +their conversations. The backup could be used not just for new logins, but +also to try to fix UISIs that occur after a device has logged in (as an +alternative to key sharing), or to support clients with limited local storage +for keys (clients can store old keys to the backup, and remove their local +copy, retrieving the key from the backup when needed). + +To recover keys from the backup, a user will need to enter a recovery key to +decrypt the backup. The backup will be encrypted using public key +cryptography, so that any of a user's devices can back up keys without needing +the user to enter the recovery key until they need to read from the backup. See also: From c8eac3ee2dfb7751640fb80688765484631947c9 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 24 Oct 2018 14:48:02 -0400 Subject: [PATCH 009/390] add details on how the encryption is done --- .../1219-storing-megolm-keys-serverside.md | 72 ++++++++++++------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 905cd264..68e23d68 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -134,10 +134,8 @@ Body parameters: - `algorithm` (string): Required. The algorithm used for storing backups. Currently, only `m.megolm_backup.v1.curve25519-aes-sha2` is defined. - `auth_data` (object): Required. algorithm-dependent data. For - `m.megolm_backup.v1.curve25519-aes-sha2`, this is a signedjson object with - the following keys: - - `public_key` (string): the curve25519 public key used to encrypt the backups - - `signatures` (object): signatures of the public key + `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the definition of + this property. Example: @@ -198,14 +196,8 @@ Body parameters: - `is_verified` (boolean): Whether the device backing up the key has verified the device that the key is from. - `session_data` (object): Algorithm-dependent data. For - `m.megolm_backup.v1.curve25519-aes-sha2`, this is an object with the - following keys (TODO: this stuff should be moved): - - `ciphertext` (string): the encrypted version of the session key, as an - unpadded base64 string. See below for how the session key is encoded. - - `ephemeral` (string): the public ephemeral curve25519 key that was used to - encrypt the session key, as an unpadded base64 string. - - `mac` (string): the message authentication code for the ciphertext. FIXME: - more details + `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the definition of + this property. On success, returns the empty JSON object. @@ -277,21 +269,47 @@ Deletes keys from the backup. On success, returns the empty JSON object. -#### `m.megolm_backup.v1.curve25519-aes-sha2` Key format - -Each session key is encoded as a JSON object with the properties: - -- `algorithm` (string): `m.megolm.v1.aes-sha2` -- `sender_key` (string): base64-encoded device curve25519 key -- `sender_claimed_keys` (object): object containing the identity keys for the - sending device -- `forwardingCurve25519KeyChain` (array): zero or more curve25519 keys for - devices who forwarded the session key -- `session_key` (string): base64-encoded (unpadded) session key - -The JSON object is then encrypted by generating an ephemeral curve25519 key, -performing an ECDH with the ephemeral key and the backup's public key to -generate an AES key, and encrypting the stringified object using AES. +#### `m.megolm_backup.v1.curve25519-aes-sha2` definitions + +##### `auth_data` for backup versions + +The `auth_data` property for the backup versions endpoints for +`m.megolm_backup.v1.curve25519-aes-sha2` is a signedjson object with the +followin keys: + +- `public_key` (string): the curve25519 public key used to encrypt the backups +- `signatures` (object): signatures of the public key + +##### `session_data` for key backups + +The `session_data` field in the backups is constructed as follows: + +1. Encode the session key to be backed up as a JSON object with the properties: + - `algorithm` (string): `m.megolm.v1.aes-sha2` + - `sender_key` (string): base64-encoded device curve25519 key + - `sender_claimed_keys` (object): object containing the identity keys for the + sending device + - `forwardingCurve25519KeyChain` (array): zero or more curve25519 keys for + devices who forwarded the session key + - `session_key` (string): base64-encoded (unpadded) session key +2. Generate an ephemeral curve25519 key, and perform an ECDH with the ephemeral + key and the backup's public key to generate a shared secret. The public + half of the ephemeral key, encoded using base64, becomes the `ephemeral` + property of the `session_data`. +3. Using the shared secret, generate 80 bytes by performing an HKDF using + SHA-256 as the hash, with a salt of 32 bytes of 0, and with the empty string + as the info. The first 32 bytes are used as the AES key, the next 32 bytes + are used as the MAC key, and the last 16 bytes are used as the AES + initialization vector. +4. Stringify the JSON object, and encrypt it using AES-CBC-256 with PKCS#7 + padding. This encrypted data, encoded using base64, becomes the + `ciphertext` property of the `session_data`. +5. Pass the raw encrypted data (prior to base64 encoding) through HMAC-SHA-256 + using the MAC key generated above. The first 8 bytes of the resulting MAC + are base64-encoded, and become the `mac` property of the `session_data`. + +(The key HKDF, AES, and HMAC steps are the same as what are used for encryption +in olm and megolm.) Security Considerations ----------------------- From dc0dd18eebbe911f76e8c306d8b13fbb0210c4ea Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 25 Oct 2018 13:49:47 -0400 Subject: [PATCH 010/390] note that version is optional for GET, and say what to do when no keys are found --- .../1219-storing-megolm-keys-serverside.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 68e23d68..9936a6cd 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -238,6 +238,9 @@ Returns the same as `PUT #### Retrieving keys +When retrieving keys, the `version` parameter is optional, and defaults to +retrieving the latest backup version. + ##### `GET /room_keys/keys/${roomId}/${sessionId}?version=$v` Retrieve the key for the given session in the given room from the backup. @@ -245,6 +248,10 @@ Retrieve the key for the given session in the given room from the backup. On success, returns a JSON object in the same form as the request body of `PUT /room_keys/keys/${roomId}/${sessionId}?version=$v`. +Error codes: + +- M_NOT_FOUND: The session is not present in the backup. + ##### `GET /room_keys/keys/${roomId}?version=$v` Retrieve the all the keys for the given room from the backup. @@ -252,6 +259,15 @@ Retrieve the all the keys for the given room from the backup. On success, returns a JSON object in the same form as the request body of `PUT /room_keys/keys/${roomId}?version=$v`. +If no keys are found, then this endpoint returns a successful response with +body: + +``` +{ + "sessions": {} +} +``` + ##### `GET /room_keys/keys?version=$v` Retrieve all the keys from the backup. @@ -259,6 +275,16 @@ Retrieve all the keys from the backup. On success, returns a JSON object in the same form as the request body of `PUT /room_keys/keys?version=$v`. + +If no keys are found, then this endpoint returns a successful response with +body: + +``` +{ + "rooms": {} +} +``` + #### Deleting keys ##### `DELETE /room_keys/keys/${roomId}/${sessionId}?version=$v` From 7b4b4a26886dec39be17816cf4d7ea3c85544ba9 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 30 Oct 2018 00:10:05 -0400 Subject: [PATCH 011/390] fix some English and some minor additions --- .../1219-storing-megolm-keys-serverside.md | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 9936a6cd..800b3814 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -56,6 +56,9 @@ trust that the backup was not created by a malicious device. ### Possible UX for interactive clients +This section gives an example of how a client might handle key backups. Clients +may behave differently. + On receipt of encryption keys (1st time): 1. client checks if there is an existing backup: `GET /room_keys/version` @@ -63,7 +66,7 @@ On receipt of encryption keys (1st time): 1. if yes: 1. generate new curve25519 key pair 2. create new backup version: `POST /room_keys/version` - 3. display private key to user to save (see below for the format) + 3. display private key for user to save (see below for the format) 2. if no, exit and remember decision (user can change their mind later) 3. while prompting, continue to poll `GET /room_keys/versions`, as another device may have created a backup. If so, go to 1.2. @@ -99,10 +102,10 @@ settings. ### Recovery key -The recovery key is can either be saved by the user directly, or stored -encrypted on the server (as proposed in +The recovery key can either be saved by the user directly, or stored encrypted +on the server (as proposed in [MSC1687](https://github.com/matrix-org/matrix-doc/issues/1687)). If the key -is saved directly by the user, then it the code is constructed as follows: +is saved directly by the user, then the code is constructed as follows: 1. The 256-bit curve25519 private key is prepended by the bytes `0x8B` and `0x01` @@ -112,7 +115,8 @@ is saved directly by the user, then it the code is constructed as follows: for Bitcoin addresses. This 58-character string is presented to the user to save. Implementations may -add whitespace to the recovery key. +add whitespace to the recovery key; adding a space every 4th character is +recommended. When reading in a recovery key, clients must disregard whitespace. Clients must base58-decode the code, ensure that the first two bytes of the decoded @@ -193,9 +197,9 @@ Body parameters: in the session that the key can decrypt. - `forwarded_count` (integer): Required. The number of times this key has been forwarded. -- `is_verified` (boolean): Whether the device backing up the key has verified - the device that the key is from. -- `session_data` (object): Algorithm-dependent data. For +- `is_verified` (boolean): Required. Whether the device backing up the key has + verified the device that the key is from. +- `session_data` (object): Required. Algorithm-dependent data. For `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the definition of this property. From 982abc168a47ef6227e64a45f3af6e6489d11c86 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 30 Oct 2018 00:12:26 -0400 Subject: [PATCH 012/390] add some examples --- .../1219-storing-megolm-keys-serverside.md | 81 ++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 800b3814..6fb5c905 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -210,6 +210,28 @@ Error codes: - `M_WRONG_ROOM_KEYS_VERSION`: the version specified does not match the current backup version +Example: + +`PUT /room_keys/keys/!room_id:example.com/sessionid?version=1` + +```javascript +{ + "first_message_index": 1, + "forwarded_count": 0, + "is_verified": true, + "session_data": { + "ephemeral": "base64+ephemeral+key", + "ciphertext": "base64+ciphertext+of+JSON+data", + "mac": "base64+mac+of+ciphertext" + } +} +``` + +Result: + +```javascript +{} +``` ##### `PUT /room_keys/keys/${roomId}?version=$v` Store several keys for the given room, using the given backup version. @@ -223,8 +245,34 @@ Body parameters: /room_keys/keys/${roomId}/${sessionId}?version=$v`. Returns the same as `PUT -/room_keys/keys/${roomId}/${sessionId}?version=$v` +/room_keys/keys/${roomId}/${sessionId}?version=$v`. + +Example: + +`PUT /room_keys/keys/!room_id:example.com?version=1` +```javascript +{ + "sessions": { + "sessionid": { + "first_message_index": 1, + "forwarded_count": 0, + "is_verified": true, + "session_data": { + "ephemeral": "base64+ephemeral+key", + "ciphertext": "base64+ciphertext+of+JSON+data", + "mac": "base64+mac+of+ciphertext" + } + } + } +} +``` + +Result: + +```javascript +{} +``` ##### `PUT /room_keys/keys?version=$v` Store several keys, using the given backup version. @@ -240,6 +288,37 @@ Body parameters: Returns the same as `PUT /room_keys/keys/${roomId}/${sessionId}?version=$v` +Example: + +`PUT /room_keys/keys/!room_id:example.com?version=1` + +```javascript +{ + "rooms": { + "!room_id:example.com": { + "sessions": { + "sessionid": { + "first_message_index": 1, + "forwarded_count": 0, + "is_verified": true, + "session_data": { + "ephemeral": "base64+ephemeral+key", + "ciphertext": "base64+ciphertext+of+JSON+data", + "mac": "base64+mac+of+ciphertext" + } + } + } + } + } +} +``` + +Result: + +```javascript +{} +``` + #### Retrieving keys When retrieving keys, the `version` parameter is optional, and defaults to From 7713a0f402667b3e73efc1edcfac22537de64c5d Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 30 Oct 2018 10:05:27 -0400 Subject: [PATCH 013/390] snake-case for consistency --- proposals/1219-storing-megolm-keys-serverside.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 6fb5c905..194d50d1 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -398,8 +398,8 @@ The `session_data` field in the backups is constructed as follows: - `sender_key` (string): base64-encoded device curve25519 key - `sender_claimed_keys` (object): object containing the identity keys for the sending device - - `forwardingCurve25519KeyChain` (array): zero or more curve25519 keys for - devices who forwarded the session key + - `forwarding_curve25519_key_chain` (array): zero or more curve25519 keys + for devices who forwarded the session key - `session_key` (string): base64-encoded (unpadded) session key 2. Generate an ephemeral curve25519 key, and perform an ECDH with the ephemeral key and the backup's public key to generate a shared secret. The public From 3918ed3c38389ee3058742412fb17e9ef354773a Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 30 Oct 2018 14:09:40 -0400 Subject: [PATCH 014/390] distinguish between retrieving an empty backup and a nonexistent backup --- .../1219-storing-megolm-keys-serverside.md | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 194d50d1..18f6f877 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -333,7 +333,8 @@ On success, returns a JSON object in the same form as the request body of `PUT Error codes: -- M_NOT_FOUND: The session is not present in the backup. +- M_NOT_FOUND: The session is not present in the backup, or the requested + backup version does not exist. ##### `GET /room_keys/keys/${roomId}?version=$v` @@ -342,8 +343,8 @@ Retrieve the all the keys for the given room from the backup. On success, returns a JSON object in the same form as the request body of `PUT /room_keys/keys/${roomId}?version=$v`. -If no keys are found, then this endpoint returns a successful response with -body: +If the backup version exists but no keys are found, then this endpoint returns +a successful response with body: ``` { @@ -351,6 +352,10 @@ body: } ``` +Error codes: + +- `M_NOT_FOUND`: The requested backup version does not exist. + ##### `GET /room_keys/keys?version=$v` Retrieve all the keys from the backup. @@ -358,9 +363,8 @@ Retrieve all the keys from the backup. On success, returns a JSON object in the same form as the request body of `PUT /room_keys/keys?version=$v`. - -If no keys are found, then this endpoint returns a successful response with -body: +If the backup version exists but no keys are found, then this endpoint returns +a successful response with body: ``` { @@ -368,6 +372,10 @@ body: } ``` +Error codes: + +- `M_NOT_FOUND`: The requested backup version does not exist. + #### Deleting keys ##### `DELETE /room_keys/keys/${roomId}/${sessionId}?version=$v` From 2dce23564ff640f1e9e71235126db0aca7b2adef Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 13 Nov 2018 21:37:50 -0500 Subject: [PATCH 015/390] wording fixes --- proposals/1219-storing-megolm-keys-serverside.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 18f6f877..527dfe8f 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -4,7 +4,7 @@ Storing megolm keys serverside Background ---------- -A user who uses end-to-end encyrption will usually have many inbound session +A user who uses end-to-end encyrption will usually have many inbound group session keys. Users who log into new devices and want to read old messages will need a convenient way to transfer the session keys from one device to another. While users can currently export their keys from one device and import them to @@ -102,10 +102,10 @@ settings. ### Recovery key -The recovery key can either be saved by the user directly, or stored encrypted -on the server (as proposed in -[MSC1687](https://github.com/matrix-org/matrix-doc/issues/1687)). If the key -is saved directly by the user, then the code is constructed as follows: +The recovery key can be saved by the user directly, stored encrypted on the +server (as proposed in +[MSC1687](https://github.com/matrix-org/matrix-doc/issues/1687)), or both. If +the key is saved directly by the user, then the code is constructed as follows: 1. The 256-bit curve25519 private key is prepended by the bytes `0x8B` and `0x01` From b45cf4483fd9fbd06f2f5963b31bfef4e2286fae Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 13 Nov 2018 21:46:07 -0500 Subject: [PATCH 016/390] providing an alternative to key sharing is currently a non-goal --- proposals/1219-storing-megolm-keys-serverside.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 527dfe8f..4d8e8f1c 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -17,10 +17,9 @@ To help resolve this, we *optionally* let clients store an encrypted copy of their megolm inbound session keys on the homeserver. Clients can keep the backup up to date, so that users will always have the keys needed to decrypt their conversations. The backup could be used not just for new logins, but -also to try to fix UISIs that occur after a device has logged in (as an -alternative to key sharing), or to support clients with limited local storage -for keys (clients can store old keys to the backup, and remove their local -copy, retrieving the key from the backup when needed). +also to support clients with limited local storage for keys (clients can store +old keys to the backup, and remove their local copy, retrieving the key from +the backup when needed). To recover keys from the backup, a user will need to enter a recovery key to decrypt the backup. The backup will be encrypted using public key From d90aeda65836590b42e5f455ddfc22dccb8140fb Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Fri, 14 Dec 2018 00:14:03 -0500 Subject: [PATCH 017/390] draft of alternate proposal for cross-signing --- proposals/xxxx-cross-signing.md | 135 ++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 proposals/xxxx-cross-signing.md diff --git a/proposals/xxxx-cross-signing.md b/proposals/xxxx-cross-signing.md new file mode 100644 index 00000000..a1b3cf55 --- /dev/null +++ b/proposals/xxxx-cross-signing.md @@ -0,0 +1,135 @@ +Each user has a "master identity key" that is used to sign their devices, and +is signed by all of their devices. When one user (Alice) verifies another +user's (Bob's) identity, Alice will sign Bob's master identity key with her +master identity key. (This will mean that verification methods will need to be +modified to pass along the master identity key.) Alice's device will trust +Bob's device if: + +- Alice's device has signed her master identity key, +- her master identity key has signed Bob's master identity key, +- Bob's master identity key has signed Bob's device, and +- none of those signatures have been revoked. + +If Alice believes that her master identity key has been compromised, she can +revoke it and create a new one. This means that all trust involving Alice +(i.e. Alice trusting other people and other people trusting Alice) needs to +start from scratch. + +The master identity key's private key can be stored encrypted on the server +(possibly along with the megolm key backup). Clients may or may not want to +store a copy of the private key locally. Doing so would mean that an attacker +who steals a device has access to the private key, and so can forge trusted +devices until the user notices and resets their master key. However, not doing +so means that when the user verifies another user, they will need to re-fetch +the private key, which means that they will need to re-enter their recovery +key to decrypt it. + +When a user logs in with a new device, they will fetch and decrypt the private +master key, sign the new device's key with the master key, and sign the master +key with the device's key. + +Users will only be allowed to see signatures made by their own master identity +key, or signatures made by other users' master identity keys on their own +devices. + +# API description + +## Possible API 1 + +Use the same API as MSC1680, but with additions. + +API to create new virtual device: + +`POST /devices/create` + +returns + +``` javascript +{ + "device_id": "ABCDEFG" +} +``` + +Send public key using `/keys/upload` as a normal device, but with a special +"algorithms" list: + +`POST /keys/upload` + +``` javascript +{ + "device_keys": { + "user_id": "@alice:example.com", + "device_id": "ABCDEFG", + "algorithms": ["m.master"], + "keys": { + "ed25519:ABCDEFG": "base64+public+key" + }, + "signatures": { + "@alice:example.com": { + "ed25519:ABCDEFG": "base64+self+signature" + } + } + } +} +``` + +(This may require changes in what `device_id`s are accepted by `/keys/upload`.) + +Attestations/revocations will be uploaded and retrieved as described in +MSC1680. Creating a new master key would involve revoking the old master key +by sending a signed revocation and deleting the device using `DELETE +/devices/{deviceId}`, and then creating a new master key. + +Private master key could be stored as part of the key backup (MSC1219), maybe +as a special room ID + session ID, or possibly in the `auth_data` for the +backup version (the latter would mean that changing the master key would +require creating a new backup version, which may be what users need to do +anyways). Or the private master key could be stored in account data, +e.g. `/user/{userId}/account_data/m.master.{deviceId}`. + +## Possible API 2 + +Treat master key separately from normal devices and adding special handling for +them. This might result in a nicer API, but make the implementation more +complicated. For example, the server could automatically add master key +signatures into a device's `signatures` field, rather than shipping the +attestations separately. + +TODO: write this option out + +# Comparison with MSC1680 + +MSC1680 suffers from the fact that the attestation graph may be arbitrarily +complex and may become ambiguous how the graph should be interpreted. In +particular, it is not obvious exactly how revocations should be interpreted -- +should they be interpreted as only revoking the signature created previously by +the device making the revocation, or should it be interpreted as a statement +that the device should not be trusted at all? As well, a revocation may split +the attestation graph, causing devices that were previously trusted to possibly +become untrusted. Logging out a device may also split the attestation graph. +Moreover, it may not be clear to a user what device verifications would be +needed to reattach the parts of the graph. + +One way to solve this is by registering a "virtual device", which is used to +sign other devices. This solution would be similar to this proposal. However, +real devices would still form an integral part of the attestation graph. For +example, if Alice's phone verifies Bob's tablet, the attestation graph might +look like Alice's laptop <-> Alice's virtual device <-> Alice's phone <-> Bob's +tablet <-> Bob's virtual device <-> Bob's desktop. So if Bob replaces his +tablet without re-verifying with Alice, this will split the graph and Alice +will not be able to verify Bob's other devices. In contrast, in this proposal, +Alice and Bob's master keys directly sign each other, and the attestation graph +would look like Alice's phone <-> Alice's master device <-> Bob's master device +<-> Bob's tablet. In this case, Bob's tablet can be replaced without breaking +the graph. FIXME: graphviz-ify the attestation graphs + +With normal cross-signing, it is not clear how to recover from a stolen device. +For example, if Mallory steals one of Alice's devices and revokes Alice's other +devices, it is unclear how Alice can rebuild the attestation graph with her +devices, as there may be stale attestations and revocations lingering around. +(This also relates to the question of whether a revocation should only revoke +the signature created previously by the device making the attestation, or +whether it should be a statement that the device should not be trusted at all.) +In contrast, with this proposal, there is a clear way to rebuild the +attestation graph: create a new master identity key, and re-verify all devices +with it. From de1173821022d22ea0366231abfcd80363919119 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Fri, 14 Dec 2018 00:25:18 -0500 Subject: [PATCH 018/390] rename to match PR --- proposals/{xxxx-cross-signing.md => 1756-cross-signing.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{xxxx-cross-signing.md => 1756-cross-signing.md} (100%) diff --git a/proposals/xxxx-cross-signing.md b/proposals/1756-cross-signing.md similarity index 100% rename from proposals/xxxx-cross-signing.md rename to proposals/1756-cross-signing.md From f3997cd09ef81fe6d9cd97883103426d79c51d52 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Fri, 14 Dec 2018 19:08:37 -0500 Subject: [PATCH 019/390] graphviz-ify graphs and add some more structure --- proposals/1756-cross-signing.md | 30 ++++++++++++++++++++------- proposals/images/1756-graph1.dot | 13 ++++++++++++ proposals/images/1756-graph1.dot.png | Bin 0 -> 30297 bytes proposals/images/1756-graph2.dot | 13 ++++++++++++ proposals/images/1756-graph2.dot.png | Bin 0 -> 29214 bytes 5 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 proposals/images/1756-graph1.dot create mode 100644 proposals/images/1756-graph1.dot.png create mode 100644 proposals/images/1756-graph2.dot create mode 100644 proposals/images/1756-graph2.dot.png diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index a1b3cf55..0bb365e9 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -1,3 +1,9 @@ +# Background + +FIXME: something something + +# Proposal + Each user has a "master identity key" that is used to sign their devices, and is signed by all of their devices. When one user (Alice) verifies another user's (Bob's) identity, Alice will sign Bob's master identity key with her @@ -114,14 +120,18 @@ One way to solve this is by registering a "virtual device", which is used to sign other devices. This solution would be similar to this proposal. However, real devices would still form an integral part of the attestation graph. For example, if Alice's phone verifies Bob's tablet, the attestation graph might -look like Alice's laptop <-> Alice's virtual device <-> Alice's phone <-> Bob's -tablet <-> Bob's virtual device <-> Bob's desktop. So if Bob replaces his -tablet without re-verifying with Alice, this will split the graph and Alice -will not be able to verify Bob's other devices. In contrast, in this proposal, -Alice and Bob's master keys directly sign each other, and the attestation graph -would look like Alice's phone <-> Alice's master device <-> Bob's master device -<-> Bob's tablet. In this case, Bob's tablet can be replaced without breaking -the graph. FIXME: graphviz-ify the attestation graphs +look like: + +![](images/1756-graph1.dot.png) + +If Bob replaces his tablet without re-verifying with Alice, this will split the +graph and Alice will not be able to verify Bob's other devices. In contrast, +in this proposal, Alice and Bob's master keys directly sign each other, and the +attestation graph would look like: + +![](images/1756-graph2.dot.png) + +In this case, Bob's tablet can be replaced without breaking the graph. With normal cross-signing, it is not clear how to recover from a stolen device. For example, if Mallory steals one of Alice's devices and revokes Alice's other @@ -133,3 +143,7 @@ whether it should be a statement that the device should not be trusted at all.) In contrast, with this proposal, there is a clear way to rebuild the attestation graph: create a new master identity key, and re-verify all devices with it. + +# Conclusion + +This proposal presents an alternative cross-signing mechanism to MSC1680. diff --git a/proposals/images/1756-graph1.dot b/proposals/images/1756-graph1.dot new file mode 100644 index 00000000..8a8c9641 --- /dev/null +++ b/proposals/images/1756-graph1.dot @@ -0,0 +1,13 @@ +graph { +A1 [label="A's PDP-11"] +AV [label="A's virtual device"] +A2 [label="A's Osborne 2"] +B1 [label="B's Dynabook"] +BV [label="B's virtual device"] +B2 [label="B's VAX"] +A1 -- AV +AV -- A2 +A2 -- B1 +B1 -- BV +BV -- B2 +} \ No newline at end of file diff --git a/proposals/images/1756-graph1.dot.png b/proposals/images/1756-graph1.dot.png new file mode 100644 index 0000000000000000000000000000000000000000..87a78f83727c9174c07f3bc1889fc9f9f66553f7 GIT binary patch literal 30297 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%0Adjg5g3&Gz*>?-#Rp z9Y-O{&a-8?4}@|szy4fz(b2Ia0l|)9cM06t0$i|}ADnI(U7~4#qVNoECeru7&T;gp zjQQ~Rz(&{@R#DJ?h7{L9Tn7D`X!4~f{~zPCa~UkD1H`oZ)V!Dj4hRhokHQZN`r5Xk zbmvRwg^Af$)a04+2J`6;2n49I1Asg)&S7FUUhe)JmYYeb_kP92(cSWMT`rh(_ItXF zqJa!ylivPYMIafM$Ng9IP5uHwR@o6lO@TRy#3fM9Z}?T!3iVVhD@N;p3-nuPdLMlK0?`zU$$4ymWVT zO|2Y_|FJAn5PYH_P}O&wa&&SMhSmOGFD?&=i@F&@+y{~0iAB^Y{@b^crY}yd zb@zkqC>H>2+!sv_d^HUiPgWPvefSvdeS<&MDh%H1VX3gPF7>P|@nI7>m$^YaMdIE> zfUq?8k=W|_zMo$}!gT=#N2l|eQlN(3LQn^s9I~D*n%9}a$ZP{Lm7COvxE<3c1S#?` z$tEMtT^N>`BjpL%g1@&j|NKC(g1yWcV^tRnNm46AjaOHPc@NhSdMwA{Q-llBOO}EX znhg<(>G=f!6ym=s=3`@fG-Gau$p2$(>{B-j1A|bQ`lF*Cao=kxr1u3Vtf{RHCsyh` zpD`*7Qu?ck#hg22uqO$9VG_nf6G+IKyxzJBYfF0zTLZSetCUx_cN%_ibaFy!Y#z9j zWE#YM0i3h}0}7dqLm2G{M=T75H@6oAT@|x+<2_}O*sdbkNjUa{0u7jo0{4OmhT{os z!~`0pydseHIqYFh+&K1xVk**z*2r$=stZ;$8F8i$q6S#PYoo+zy?m?=`h9)oq7D)ETB4#+rhfT9SA zediH}&i2w+2RJy5McsjoYnV4b$-6nEBfA!bC=-egtPYV~i0qVd6p)4EVZ6d@VgF%I zWF^!O7x@;dRn&=#2~mWVgX0}j0>(y#kHlXEqWp$olh?7e+!1S#QB~mF!A(iJ4L6JH zV*ZUlBQlgj=@{j-?eFbvNV&*63ye{PX+#naAkf7+GzOu?#N^%3sjs*uurmytJZb5- z*L}pSqfz2Az)V41nDuF>0r`xOS<1;TFHU1HhlbUy=+)4Z2gi`Ss%A#oCSl;Lc>DIi z$a6ehTGaEGAy7TLJo#NyF)hTr#B&1~t%F-m=5V@^;){oHlKLhl?J0Q^HVtoOQbtG55j$jD z9H)70sUZs=E9q33>OOtiDkCF9T|d529TsySRn*)E)LAYu!mwh9vMt0B+6x=S`O>9U z?uW6lnnYv6Y>4O7Cz(zKLFE(!c+GbQg@ojLj7G?OwS6gc5?GKpE7jGp_-Xv%3P=fp6ct@O>u=o!@%Ys9Je(l=c z4l=WZDT~JY`(12t8o@N2@pl0c<+5O@&12``$=V|du?u~`S_iv}P;rk}_Du zqJ%o?0Fl3#Ht)MhW(M%HWGvyut5>8NM1841QU|E89j2Mcx05u=BkTI4dq4`%Z6R{y z56i;0Ko%nGb!Gp^4u@&~tynk_XAy{rg%;*TNLiK7{$0QLqv*#H}U5_|a!s+D#~dai@XA zn+6{h5^i92{BShfxgmB$%Dq%gph_+6*zj~W?G zt4Eg@U~>}v9q;O^P1?a}cMPBiySuxC$PTCb3VV*b4q%EB1)u<|1|@uFDyDr7rrd(0 zr(ylPAZ_D2g-%Un3^-<)JikKGLlw4xoqhV$)L|t+GG&Yn380pPPrydkJv%8u{2!Mr z#4L#8*qSdLy;2XsFGSJ=F^VF&xPl`2+)gUTv zX=FAEJ+X(U=kV}ge|Do^elA>LxsIKC$tfx8*RMCtvpVV_H;B!WD5_DB`I|R9<=cT7W;DAbIU&Bn zboU(_OBso1!cf>#d7?y-iGrq;98>|AstS4;N{5>#b`0#ChYQzhW>13?V3J1!icBE# zTLnZZa-b2U0GLQx@8z6oD^{C&29&f}?h+#*vZyVR^tMUj{X-;KzxzyWIV_@H-C^fz z%VJ_;wBI5Wfb2(s@>sPK$KV6*}uX9fQS z)r1+R>g!jFo)pDAG7b*@`R0u}>DMvE@2|B*6=>1Dq60$$7t$`x3Ha=m_A`RP2{wZ7 z0R3vJs?<*1MXMt;0%2<)GmqfGWU3M8Jrs_|$Bi2TgrG5hoSN#alK%85nS>?|qlt+~ z>;g&i`w|9F?+o#4CypHpMq27Bc4t8hAd)KKrZ_8JiuLojk@mhw2Pp)NtP(BM2Bzla8Ady@9pHH*GxI9K(PV5M3<}P@1IUpZZ$k+L zW69c!vH+)ZG2y<@<)JttL&C)UT8VU=a%4b=k-`x#qNd(uP+U}GI&$pj(UcjL?3^4; zEJJV$J!50qn1d-B{Es;onq_X}!%G9Uv5#otmWGT8>RH~{$VNuzF`OL~w3>ipd;1Sr zOS~5AJKEcGhtzNO1_hIdC30#Ayb*1>Ysp+diCF}*Xa0nv2FzkC~ zm2Euay+aM$P#=pCtCB2bKtm#k|AXjPA3S)FoI!|Vl=eITS1smy)w=SjwwAm^Sf$DF z2*9>bFvtqPK{-fr0&EHPUlamM7k58DPlk8b;3T#J)Vhc=7eEm~4QOFx&`URhSNejp zGui5p_j12>wOZlexE|f)I@lvsG0=T4NFSF7L#6)FqpK-+LcvBmf?%zgbI05hiBf>z z(|<*IPxP!kg1|wV7$mgX+QVJ1J=T+MgB2cKI>3o)Sl}dKo+BgI7-&R8ayF@c6-*x} zy#ycz5}b_+0@cC=Xn4{%h;kcyC5)(q@j~3f5h!&f?mo&BjGvYk=Sbo}KKzTm4dPaS znK-~YiQNC3p2#D|fqH=ibYSKg^Ic7=(bJ#{!y!6qo}RLZ<55r?L6j*hduRSjO`wyQ zUP+S4j`GyB#WnoG2;q0Mj+!Lp^prADaJ^)#03J^A9XL|)d5P?jo}1eqSX7CCiK`(l zQrPYx0l?{?@}G+oqZJ@%KCzhz_0Veg1q7%-M~Twt?7R!FwHM}Eure!n8bO8e(vRrI zzME!4cku`C;vz;diGeChtQ z)%Ap}fuW%tD)cQF3T}`L@VNs4bpG*6pTG5HuP;ZT#rbzc z4W|YEdi+*0T!#=`cw^PU$4L=MMw0QA6vs#Zg#5%wQS2NX8WdumVCUmoDTvs8IMN`q zB^Mr8XLolI44;r;Vm#!j2S?h}k{fIXv4TkBgij*}ER{w`mAtX>)?3(s?gB<60f;&n zY)iq@pSt_nA!$1%r18c;Cm)q9&*Z>6`uB3nZ#WF?OyP#2;-^Tv4I9BZum7{u2Y)ocg_*($2;Xfm^N^OzMN3aPCi;8ZcaxpJ-?yuR|ubtfR&zv`Zp6Ms19=r+! zQ&9Eq51$&OZ^4a|!>~RN!=uVb&@ymH>^mH_Hq<)X*VkwF&-fP^*0*RvwkMq#2AkX+ zj(A%}e?5aaR%ukx@z5G-PQB2K`07FS-usG*0K8HJpp3NFKVd(vHl|r#^lxf6O+N_w|`>p7>>cF|i07N4a|SYJDX5v&q7)mxIa7 zGMa8;qbEl_9XoccXd!4O3R-P3ViO-T<^88m{lS>mA}m02c=#|15R7_VyMO<_Q-X|K z*ONum_G?i19>C*4C)YVtz!Us4IZ{y-1`t@C;iH>!l6WCUA`%%`0A`RmhqVmjCWV5- zv50Z5y~u4Rln!jEaOMFw%n3uZ#*soN!Ew8Op}Va>nS>!9^&>}^(0Jqf&hTUCm!b!e z93b^1)Tjc#6-6BOxEc-)Z?y6Sa8cnQJv;M>+Z+7<5A1``ylP>@6SaE6;2ygf^sL|V+AOMYVni6K`O%KN%tK?t*Kr^UIXYpqGoJ;%#<9z9W?Pjo zg7MEdo!@eyc|&or?A|&Ugov&Kl^}Gm zV$mL_!RC3qg41$gym$hC2opO+nhJDmgwQph~ zX9F3rUr1P)=Nn%JzN>+UsJJJFq@9~Mg^(PIW?HpT)TBHpwV)slzL(rf&;HK&t;kR% zBBLtJIv9Y11g>Ff?2tmiq>VYn$EbzH^D=dDiij}96$kju^6J>;_q@a_B%_|tY`b3# zk1&IrEBrszoq1Hw`P;|8%2Ei!P-A`-4Z@5xGtm$&GP1RxVMu70Q4wicBsW=OFn42_ zOo^hTA!{n-Q7Tqn1dR|xNJm);;Ip_KRIcNTH&Q#y~dw-VedT&>K z3vD=0(uUqhx>0mOMZIrRSxLGs?#c};?0JASLUjZ-^OK_ScxkHz^{*{~CNwNSSVymy)RHo`1C>JkYoF_X65uaEZ)^+G=)~{be(Hju= zM>RL>a!_zrJ=!vqY9+0-Y`aDBE#H-|ubNBVDX7YWr%xkjaOc!`k8V1BDz-fZ(OQ`4 z;*f21RVrsOAtp}MH}efKcbQRoE2`MGoNZgGI$vho2A2=Z@wSgwnHO1bO1RwD$?j>P zvxx#2Jh_G;fL$l9KBHFXY=(3O$~Dbqr+dijtfCP1xi5m zuzkhH1M6&A<@-V9Y@AxW*r%G7m2-Q*e9&yswxxw00hG>VTNAv^%Ag!A;gRwsI)pAg zJ0q+6*m+?-0dZig=32DiPf zW|tzA>tiOo&#ZE3o55QWi!E)BU8V48qX4ix*lVarvBba6m)wc~xBQEU7c%jDR$FD4)c2V%*d?$u&pSLihMvulJ_QE3aD+J;N&vUr^l zYYMMoKrf7&WFQ6v1y@-&K@mA2d^p%CdBl8=5^12V?Ow(E)!*K(IPAG-B`Y!+PLA-= ziQPA^SMr5p2BX3%FjmK2HYot|8g`NcxR3kHnQz7(yGo6wuSkxj;oOxDt!OL)`T_ep zRQk*)edU8zNWeX$urr+w1g;z_>1T*Bohp_UCUR~Tu*=TQzX1#=-e$^UpN6W3R*(_r z;Fr>i27lO5LLJ!=ThbQa9mCZD`FqsjOa_+?DKCE0ZeWYjYfTW~%XEY~>W3*v$6jKZ z5)%^q%Lzo>fdPUO9}1X`B;{wZH>5baH>8&CXs)qq*}>^I(k3Xb=7Kje z2H}{Dn{SNjDX*Hdy6N2^-Pnk6$?j93xi?>W|JBfs;r1==cI85;?WC79zOgj9bo}S6uEL+D;FCb%n`C5*p*taqn_S_ zQ^BEk%In7!;bF1~ektH3hqeTlPjq%xY3_c$)#cDFU%ANh_Sx;DF8Ah?3Vhp8ZT{Qi z9ra&vjo+{up}$SM@-gA;`?HfD0hnzg^I`S^cSl zxp7?7u3fk#R@o!jEEDR4K-=oT@%}J<quKu`J8}bIqW-~1&j7EE*nNV{A$H1xo#IKTQawIqsH0L((rqkxF zCUnpE4fMGfCGLzCmMRg`>{hBMi-DY@#&uH@=4X^g$7HYTvAPuqb%XX{4*}g>Ua#N7 z5=lzMBMDmmuFR-bm-~^$qG`7XOov^?optryrhDg<+Un}(Ln=cn_47=9ta@{GnBi{_ zz3HS$7f^oCoV2Aqpy3ag5mHC$CddKn&g0YCF|eSNFKA3zRRavS6A;kt16KdpcKc~< zVE)=d5vZcde!2H?bRFxgRphWIn3NIjlIyjPht11YI-A-G2a3{4<44v2!m@d53ggi+ z)2DH&8wyAY;30vc_+x>E><<(m!NNhitJC-wc@TN`AJ-I|8B_ENeJj41WlN{OXyJzt zU@28`IY#e?H~3rgBn)w&T*I0FtEclhT^0=R3(MG^85D@O#2`#4qND}b*_n!3ZO~o% z3my!gRQ{~xT&B;(`YB3cX2zTTEkccIs$w{K`ZkvP}%Pp*wFz88J6c6IR1aZxAowc})51?mV`;$xMm)q{54 zdAuXvXk%#j|9=Gcf8x&*5-N4}j^kU1yt;rd!hx3uj0=Ftn3}viM2(6=d_Vxco zW|Wll=1DZsTyQ!Vu_VhEaz`O(!i%`>**2(GQO0p&{s0v1u8^76annkHOu#%PABIXoYW7g{^i`n=nb3+MPh)G0|rMV z^PD)6O-i|qBgBi@D3vF957#!l0nXJ7VgD=S@~@TrlfTyM?}i+ufFg7M(9_?;M)!)Y zLr(hu;#is*V5e{@Cx?{jk|3ptSj^>VWE1=uRfpEoVksgUF`I)DqNVbGRWb}+{-4*b zbq12v)7N((T^eSqPd7cS=aQut#uWVyb%K(^k$aE3tv#@+*M*HD%oGm|J%BDiv??n3 zOWN>1B;FbSJ++@OkA95c|GEh63 zJVam-TefV;oOCmWR!|aPFhEIO3t#&QLcL_L0gZU0h3mfV4zBS4yagg7qI|_Lgia(M znGRIZL=r$J62x^tk^#wtA?=D%Sew<-loi<0tWrfC?5gkjji7?hv1*(DGJY(-LB?*J z3#&hxnfsqzM_{vBodoGe?00o_JYCF=iHU_FGgZ+f z@Q4D67=SBi8PT3i;Cz;!(N^aF{p3mE_WqNkEJ4S?QT;qUVO3IM;szP-0tEgEaMFsF zclF5xH{bvM`P7!q&Io!{4G$w0EM7c$YVuA=yHZ8ntdz(wUWp6M;s9{IMt~Wu=k}x8 ztbEP6pe>R<1=}Djv#*a&LC6HxT`*lMYH!UDE}rGlRDOMJbI)j$Uwc8~8z8zs08uJ!EV*tM86k!yX)Txd?+tf?0~ zQgTNbLnDAoRA#PMvptn5xblR79~M$6X+2#+o;AhaMXC1@f&SFqwVoY|Hj90Zv^_+J z&(n9B-#X)1LvEWmNNF_FJf4RAU-0BMI3w#-;BGJyJ7I}J&(s31itwaOA3+Vt0wgiV zciX9u$~l<51ahs+Y_jmNiVl7{44VD-Lx;TUPq6y7q4mlcoy3AJ0G7)0`}YqtPMwCH zw^yzJ{xsK}$*Nvuq3(_lgA#S;o;{jZH5CpjYbzE5HOV*>Ww)=s9oF06x0sk73Ye{? zO4b7(i*m>pRo(s$F@3oBQkUM(%<+A}pXjehd=x0!s9k3tiM{cbyyOcmsm#DgFEPdnSJrgYouWB)LlLCr1@n*8xQT!AS%d{a%40^4K{R1Ive~ddioW{XE`h0Ok7bfURrrCqir}K z^oqBAz+T^O2QpnT>PT5!<%TCWKfcr*u%{mO%9o> zhFX!S_=$28xnfP)$VQhQxXA@BPbqs2Ve5@pu5CCmA*bt3j-JS63$7zDDTn?q?t@?X z`;cto2m%Z2b}UH#T$?yo+cyapf`1)4*9BE5qlm_};u^48>9e2i0A=XjCXSPxuQo=T zkYmbYJmftbu}2s)K%7|n64 zV^4aq{LyzVKN4}OV?Q-HHE%xnP&4|qfchF{EpdMU1{6FFhUd^%J~A?LBTXp192#i{ zZO%8-r>8YF>}%GV>*PNfxPe|0y2r1_&&7GEpd7(xubWrP3=ULiVF@Wqit8B>g2L4q z;n`0vrOO*Og-5J*yuXY^_a@Tpq-keTQzL;jD~%2DMJCYahcq;x^%{g{ z`a(_?xt|rqrDh*=o7Rx5&~ghGV0rfPP*dF z7a6W`(qobj_#85}O9B-r3%HS@N@AzueE?b$&0FgCxxIecx^?A|17Y>AN^d`msK-dW zz|YSaur5Kufc;F=YW3}r*99R<4SF%1z4frd?yU!XN-s(0(=-s&r3G03&*EuAz`X#x zvY$D2Z(NYIkg840e!~~v2?tSrk^ZlZNgNuV(9p5MRVO;!xM&+1+a4NcCBH}L4DMxa zybF&1gnDyI*I&v;#}`#1mMocI=jFAAj30y^&(ixn`GC_?BI-obi^jhwWQ6+g(vV4E zu&B~UgOPsa|3FF4o-%YmRPN_Qqo7fRF8n}P@&2QaW}Xc#_5~2b0rn^Q;NyhL0xYVc zE~bj75U_jgFy}w*_88`1)f35y=$l1MZ@+Km2eobJB|$GEpS7=82S}A|$4+&g3#_K0Tt& zsqtq&S8q~QP{0MKB8G5bA{@J8c%%2ro>cPBp+H!OxwW0@{Y~}X&2A|hwMacHqbhd`Wt%fqM$RET=4I-lWe>93 z2yTQ>sj8;N5%f>UcGiOJHxW-k&@uZw;EMYy;BP&|7GhE7(vb0TAkoiHb(j_0qgQkU zSHMF-e70@7gfvgW>WS2w96PcnDeBTSP(v6Hd0II&-I;?K&{#mEw;+EsYmy!enhcXj zDZYl$a!I~_R_lFcW!%>c3{Kb3Vz#Kpt4fz<#nX~eXi8Usd_pbJyTo|XzhKQekvVBq zKiUM2;D7vNLRkRJ#Z5nqj%+Jbk^ugw$mrXOL(XROq&a$z@&uTsF(W|i$MJ61x?~vT zd$1$ieefj;>jw;uK!!#tuSxQh#HoZF{tGTvdND#XM1TTqpkY!1>0^q*zzGX2cV;$c z%fm;H3O^ZEr~8?eD_n;eLOLX*#f|=u?}prCn&ogV=mtd6MjID$Dm4}Lcsr^M5%Vb6 z=A^q*>{F&YT14qC?}j;U7Cj8QV*3F*mYr#;uM|5Wf7Oi|a3u^e?s@+EryWG8ij-XI z(=z9Ezi+(*6Vcl6SLCVRj~b=w$WuM=Xn_^r;TR8U5+3Yk(xqF~JOeC?r*#(olZ$xw zu9)b^Wfb`zyc=Li?Q2cb2+@WmFWtBPvSnj7(^f{E$hYk5YNix4TCDYIjC|SBVa$y; WyZl~Ha9~!fGU02Nu}8})G(jZw8Nr)7(Bcl`{%HCve z+4u7}&+B_X?)!)PKe(^!JRhA8eY`*KaU8GLbG_bvnx|FhHnVM}P$+b2s!C@l6e=71 zdubyze!};*U>bi=8=h2AqO6kt$K)r5QYhRMH6?{}4$p^M9d*ymtjLWwz8!ltyqKD? zw_?xB45975PaM7l)aw?!)Z2F`Ir?$yo9Q;Qe-8vi#X z1q`C1v}5gDZv8GCObY9~i>s$5q!_HFY-Pt-<@)WuKez3(bx=~e!s2bPO)K$X-d^%w z3qf8|C87aqjCj6_(#92?>(O)P9U2&rx3v{#V`u+SU%$>z>F&aUQ@A4=8=Li*>4ts# z_Pt6@wq0$gufON*O`ZAveQa_vjh>#~z@I;Kzs;_&D5s}07OI$-2%b23QcG7iAR|NU z+{KG=uU~KO?d^S)n%cM0`s>%mSWVyA*;$3;9Zf31O$JBhb-4OF=OOEGJ`_Y)UZy9&( z+BGmdypAHYcW+5W#m2I-GSNeaywcMT?08Y@{`1EVkJMBVy*jTD#v5bL%fv?`YQ96En{p;7SHP4^-x_^H&K48fj zrxN6ylCp1jc=(E$8O??b8>EgNE&2A1PFY!5aXeLwmQOH}??~zTHB_~=wZY-xn`EPd zo;3!A+LtF=9z{hx zsI5ICw0n2)j~`5ymX(_-om6RA*SnL}+ z!|MGf($LF6Pmkktl(ZjiA1@CNxx*JOT-ZoYuWW5CnxGy*jfd^{G)eQwkt5cBYIpJP z+QqYb_ok$zq=A6}cYlAHYu}&y{eEXsQeJMN&81@)CirWdVdBe|6H5z2X;MzpLMuyC z8hG-2{QT>1HXAo>@(2wL%{#eMOXt!ho0TQU=Z9^nZES3WgoNC^yw<;X@nZZF&HgKc z^+}p%4E6V42{SNs@Xl7^Rk(Rm?Dp_4kL+xzSFd05N=dQ&`0>N{{{6cJ1qFF0T?~7A zdOCV4L#1rG)`?us+h}WRE3EyNa`LIj)$$*#$}8R%eV;yh#4RDg>^wWHfUCdj>6w20 z2X9rB4EwWZ&n892{Or5S=!V<))aQZa; z%1qjw&i?2--hO_Yo`~q(_oCU65o%U)4x7qtb=iJtyo7EuzRzA^Pjs+0#-mJ0f<-`U@sr1Pg)4X$YkCL5*yI^c$V)Wz1ap9uvRoOP(Z*BWex8&Gv znCh=CzhsraUPD7;X=QP2QY6qIYNH}&*W&C*$***yiLd($J0h$Kig9J4wtc5=pE_S0 z9v*Hm)R2OO$RcUEGs>}#RY_U-MMlP%wwDL0d?k!4f1{i{)puh(aNt0g+#T7Qwzlc! zzs2xO8P0F2VzjolE(+mO34ZiwooqA%{`XwgRj2JLe)SMPPgH2=+Wq_YKbLW4E?k~v zFIX7dE73BzQPQI6pz*b9hGhY)4x>5!xOUtTurRv_~hi8pmNV=&)D$znYL}a=k87s*3D*=v}#*N z*?IWpy1E4QwL#_hNAb!P?z?yIirRE3{asu%T3(o`sIDGs%vgMZBE&9XUX(jlu&6yn z`|ph@58^5>_S|s5s!iH5Ti@G&nnJh5LtY+*?x6Fm`3);8t~b{>2l~E6taF#0?+M+o zq9@E8`ug>rk>;#4{jWhmL4uT?v9W3y>^o&#N&M^ACsyaH@A&$@%FKL32}W6{nyRa< z6_S>I{^;@JKlyWQe11xk6<^)Ly32#=@Ypv|Qgm}N+NWqUZTr86BqRt}{CaDk+GG?a z=qsz$5UB9P~LFcK?#Xjwi?%P zlNgKkg2+dY9t{ltey7+VP!e#ZOX2YI45M=TrI|*3DTncn;OHWAGc&~#CpI^v>iMHC zmVW*$V_ks#Fy}n}CQ5nwU3$6)8W-AGO>OODO~@(^_tv6Wf`y&k2#-ccM8qxr>RiJ9 z_UDhvv-UpScgZ`)w!iD=MSU+-ezwV3)@$*sbaCn%#QYCeYg4b zxHF$8d%woMc@vOUxU%3ewNJm`d9Gyd&*Y1w5SahYSg)m}wULHq=wdTE7Ak9)lnw8# zTeqAd_0b6N>&MN^_R@1oOT~QU{4Tq?WLsZfe`Rj8byCF3U|XBJkAtBhFZ$}lT;Zx~ z%#Kz;xs|_smmPbzYwPIniivHxvplmSBO~MEw;i0E-II^DDrR{ z;kj_}qS5b+tD0xdd^$RoSJ&S^zqq)T^4Dd>eZO5rBc;juF;%CU5R zg^oKqI$GP=aSVK(t^8oo{MKdu=4iWtfr0g{Tcs<@i|JRtG6%4VCp6ohJAeLwQ5o%< z%lT}LY5L_@^asou-pIc1A02&g@7}%g*E#9wOcYUUNh%P7|YJixfC6`lSq6%%j zq5+@S#$_fN8ZKNnGh>)GbpBaWpwJqx}AlDjXaffBPb=MDMIDS>Ab zlyQ|CH}?1r#5#9x@moi6;-HDoDzs|-xc&F<-`a(4GUn#y3wh>1Ob$*?UrybZVE*>< z%3NXhNLG8<>+{LuKUBD6 zCNrG*@}KA~d|WtMePw>WKYF4{y!x%pIhH;fy0VMDsI8`79_{u_AFzC|^YAfmZ}0fz z+TJ$ZWjsPc8&QBge{K`6Vq&_Gb~$i=!QVBc323`x$FtQ(9K$B< zDD~SKcF=Uor%#`(Q8S9oAFz7gxN(DV`*tsmqqjQ-e#H6u`Gwf4(TcyIU67G@pP6aB z^w(yTx{_vxh;jE-X`Zg`ZZuX8v&K~1JSxDkNfAE-<>x#(S{WZd9w}H^Fxx5Twlnc` zB-ZR!>*;|XvDcr4c%oAY>lf@4zgAULry&=Jm2NoMQ#mQ(Z=f8{v&kmO*LS0C?rl0Z zH#dh)uN{m#cP+l;x5W|3;pQxc4fH!Z$}CJ+u659v`mS|P zO4{wVGH;@rZB3Puk}^I!TZYz>8@T?H(1Q;jB+Q#X&;eur%C_OVkfP&-RUl){vJUOF zA=oGToV+~c%$YN9t=eVB=Uq_fnQ&#QCr&)Ps8+@IIyt$doa+uPJ3Bk69Qd1T$0-5X zg#o^t0OLwgjTZ{Jr@h+XfO?F9Ax?R~1xy>0UZmSorCi`vhJYV9%~Ey^ywJE6BAvZ9k3#{uS7+I*?H^xp7c}VB>;ppjfW2(#>B>Y z{HaScLM8I`^}W)q$sopi%*p9US63I~jvYSil9omF_1kEeg^OMs_wVTH;vL(<>dpF{ z$1M%Wn><4xYSmMx?kVvW0~9mu+!@f(apmv)yu$hO=i9FCk@k>xkB{f)7ZBi;mF2|7 zdHe2N$(w)PiJCPCpNf(Wr{nl4TH^7^Zse#9MJa|AKsQKtbSKW%2OhV= zmKNRrk%&7h^MZT!?!~R8y?F8B-@kv08XI@CwY6E_zU}rRC~3!z9oa|wj@5ow+Sw|8nUd9`-|hfc)=V0@+Hr{eRO~R{Hds@a0hq9&O$@%7#!S;gJggF z`04j=Lho3_uLb0N+TA$G!TjwYD;`mO?~|% z+g@z#x(b?!MK%5{po%sI0(4neiLr5UXsd~d8!lbC1SeA3lg4IN*_!b2uO% z093i9JEh(GzAFDQJG(<)zJApLQ^ii&hKu?hc~nSPSjoVE=gXHb*_Tqn!@{iDg(W2= zNBgmddl!xX^lS3Tl{(k{{_O`6U2(4?|LpnmkE`+p@%#+T&1w1g_`vBER8%(nN6`CM zQh-ahZ{I#JIC!(VuBImVcU?+a+S4S3BP`mAifiv3<$m)(%hAJ9Qg4&(S^6r+d%PX2aA-|Ey}-f28|N-u05Mzh^XE@+uVc_3 zBoE;LXyj-es7vzB&bd{AA(wP?vSr*lMlTmmZ)V^ODi7TI0yhXf%JQM*>ptD>rUz z-nw-w3oENl;k)hz+7frKzcqhHbH~QPRN@j6l-pi{K4;LjMY=-$2rDi>_S}F)*(06N zgvDFyif}0_#1R%68*c-a^=1G6U;RJsMgs5Hnr&KmEK0mg|2<|NX8#{3zra!9^YKA? zIXBk>cSzkDV04h?gw(Q2JQ_P>dS-@-;^gF11B``T4X6viaKt#x-+)tDXxj^D26(*J zZ``;G_I>gs4Fmw=KC|oBDNL2p3BU`xUamHqU}bnmMR8KDpjiO3-^JR>;uLi)k6UaX z5mdtZ+_PtorjE{i?BDu^h8L-+4==F&wTup5gN{Hs!vwY%(nQ827Y`1AnV8e-Mj*ZOKYWU&w@K7H96NO%O?%c|l zqAU{%vcfgaKZ*7D5vbb>oG5RI@1owv&g<8&cSwlUM(+D_%j!-euJ`fd?Qh<^xngNa z|DWFX3B~dB>C-53toQ7lv^9qr(@+g=-?=!u zCvZ`r-Fpq3>VT!lgR(xJGcmI4G{F*V(UvjfKhD7w4Y+S0NUv|G&Q z@HRDdbvjaZb#<#|?EiSb&dl6qKiZ;j=@PqpF3;G&K!rN6#rXKR`@47VUT|fc3t+SY zH@$XE0L|b5Xc7b^?xRN|n&Yq(iy)okh<=<)%*n}lp3HR_`)qDgZ(DS2`)9)gU_6~Y zJ?l`uvvYGxK7U?+`SRtr@83VRpV#33-`c1^z89d5&9I zSy@|d%}-v<%*s-Pghx+L4*=y23ZSd2OFEykv$Nw<8D7vpuxM^U!43EYh&&aQmBrs8 zB!B$;x!%>)H9IGVV9!uN4e#h^u7JS6UcFggKE{o~5DtMFiN>hA`m|uRwqR^zB+zfG zU`C1Iat+#2SC?UG+Q~&{1qCWfQCk})IRa$J|1^`*&IP~YD?DxZ-1PJ<((Ml)W`$&k z)$f7rke8SDKY^3cw5xdglH*ZQojDjCNy!+_U zHeCoy(8wWZmlGfhq<$(|mZQ5YFw{Qj%;csCJawW};h2Ai(h6%@T4*7HtlO}m`%hxD zcTf<6uAW}>K+xS1JuEGvAwPR|zo*Ao_v5Wi02EkF{}WBI5JU?p?dq3u;6u>FVyp5?QaPsE8}I)Qd&GB9vG4}$7j2ub$gH~F^sF>-MQDf-ZNLd*eoxd)DPOi_`EjqU#Uxb=tPvlOt+ zi)ojSA3L^Y%a$$Ujrw;y%DZ`lg=s++!epE!0ByW8;)`h}P(A=34ciM`r5q>uQD}fd z@4k8^1YN!uQgAk>ks-A_bXJ1=cOJb>Yx4cM0`wCB^=J2RPS9pM{G^6!vA4nQm;QDK z{RP3SX==I;37ed-!^(XBzasUP@N#H*K1SsaI>$RpR8>_eOgxa1i54m2JgccY)3Ax{ z3btjYK?ybF6<(sGX{;_Z779s9+Q&o~Zd1aJkCd{x`$YJkV{yuXyr}F>&>ROK2l8+6 z13q`(x^dlfrC@Y$WF!YwOk$MN{et;EcHq)bd($fJ8`rPLYJorb^q5~VP$$0^?~jnIROwD z9Ch2you^Eyqc{Q*IQN$}H#eI$rt0+!51*3e3<8(_1D;y{`}bkZ^2%=<6D366dG9aK-iO*H4Nh zUxYrijfqKtQqkD>^z?Hn1#}@WMAn2eXV32D<-H5sG5`0k=8YSJ^1`z}6s33V^1@P3 zLgUQ5*?R(wfYtXHi#Pw4O&o`B-g_PysivstDPjIg9@`a1kC>gkz0eb?p^Bc+($X?8 z{ihDhRf`)Hjoh0PCr(T^nWbsDx-NGMr;hQowX~e_-?6{%!5Bnxxzi=Ib#W-E3rkbg zYWn)o=xGCZnm9n{sZCEzoTPfLYf;c-8Nx*z`Rne2C-Ck0+o(ba;W z5n^IuFFZFe%w`?9=8G1lb?sWhr^{4lw6qkU)(C=6EPw^7@rG2qkHQ8E0fAMIJaV3g zelB1g8oy%ggS>{vVA=Qm{CLQR!4ns3rBde@b>~k)j?h=7pLB0XxXW)zuVqRIGSb%5@d{>7d=;Ljj~sE3Jq zEG;b!U?(P^M0>b6y3T%+Y}eG(WX#FVrggehVxTuan4G4HeE?l=?iKq(AD_RYt+{Id z{1bj5Rd;t|P#~0aehS%5(&J->>0!7MQixeHjILiDdcDKmUYe zWz7X|n1={THrMTurbGNAxE{0sPO9MfX(*#qXD(bwAe*wOx!G@EV$sdibT<_@_C1Ft`YsQP-K2qgBcxT0BjeNA*f@$HCRVk>5 zKEkCwUq9aL{IniT>Hh0zeuKi-u0_QRorby0%a0mS1SB)7tXE)7u`UmylV)#fs#E+r*Z)YQa=W)b4( zcJ=Bm;;@kq!pHjzTOsFYvhf!o^OE-C1QXYV{Z{TwQS{`ZqCxP#{7g zB4|2BgI{82dq+gDqeF>;L;U*nt0HqwRPgk3cbMNm`b6cr+)S)I7q&S!I`}sc)*M{husAo$ULg!2+kk+;OK2aO1(^ch-Msa~_k?*4FMo!RD;L z&G{J(I0j;}oSfWf`pWEb0l^!%<3G^dNc~9C(9L(+j$P=9=BuNl^A@HSag-YyFJ4Q0 znPsOkh%1(dd<1vQ6P5W^^Tc77zoJ>`u6^MqZ_(xe_~a&~=bUOjik-XcHh)z~NvQ+- zbhvrzXuD)uQW6jN$pUCK_2k)Og(%NjfGbQ)=vw}i8n_)?3(wPy%4^{c`}w{CP6cFh zdY<+A^?h=zHS%Ad?28Ksphe;5#T8^^WhHLkdx0^!IuTaD`jmRU9S7x&c zZB}Qer{kgIPA5Hq448q#{nKU=?W&w>KPJHY;`{g0H*IWqfbMUYn#R^8YSaOCkZK{j zG_Kf`ZiJn`Fe@cczRbnc?%25h{#a$E@fHF6>ObO01Fr1+wxR|#fR6Ucl;tXUvbpL}@dt3Ju=gYCJtsG-( zWpTS}d^7FzyI9{s;^Lv;cBXB4jxDxMm+Y}iD~pQU z(LYZS6&gMJAsB3UM1<|~l>h#M7@(BVaUI;(!-o(5c(nU$_FkCc}bLc#|KVJzD(e-uj3gdp*>kwAL zLshw4#;K@c)^4C@WcrbuR&au{a4;q;(+C{PsXx+w0C!*P&e%z3^q+Vz-l?c)X~&1M zD}TE$H*=jD%b(+fM@Zi^2~+DN_RG!1jF5w+Muvvhzdt|RvSYZfI$GoDetowN+l9Kh z#VuX6Ga~%__k0%`1W`}*))bAnGqtwz*xtO^wX*!y&S${VHzFe9YyISJlV~Q&{lLJN z&?aje8WMopDypWiTeeV|a_z06`*;Rj^+GnF#X13jI7g%`9w1Ph7%l$kfms&K-Y}zV zSLtbJJmcc{X2;rtpiFI$RXKltJJF;Ee8i(AOw>X*${ovPovmGeB0Gmx|IwUKQt{s3cF#1#bQv2hr-OvO86+g9d%j5WH zw{W;z2FyM--rv3#gx(hdql#s9ZS5As4}M`u&3$Cxl=j7Dn#)-(CWsqcbyPWfZD4hO z$?xAe&5IS>H<^kSz2EH*YTN}O&ps$Ze+wqxib7Koq}5#KUAMD zc{@EbQwe&`+?Y?w(KB=H<~#xd)D&>C;@0)UD?F}zyu1|ozS9N_5&f5CQua;~#{ztH z`iOoOwD0tnJa-ymKeth0@@wCn!AyffWs4aSxsh5j}1L1?};{2B*+ZVk32JmS8gIQ^JL#8!XaTvN?S*H3@py&;^>s3y1GzjbM6Ht ztD*0nVG$9EXU}f6u&@x?vuBOF<u1c-c`%eAPQ9C z!(as0D(JjF7%Is}>F6|#SV8h!PMr$XkS)gEA$BAD|IU!+sfh_9tWRL8cHz$w!~HK& z@nHu-EMR%m%g)LQdDTVp$+8ror4%U5o+QpR=P|nYe>;cd6gcm+$o_b0mnbOB$$)ea zWcao2oN421a&9iseK{93hVDy4lL`w9`}ff@Y}_ah(b(SWt~4|zlH+jfP`M5HE>gBO-51o>UdUPx{ z*f|xU65oxWEl*8RZ@HR3SPa=m!h`>nYrh`~O8TuowQldTu6<*}slX(l#>!t#y&6+m zUS0x7x%f-A2Bl*7h>YsC*gKHNW=bUe-+(5gl`2M{NKrgrT z)w~UP#-}yldqSu@U_Yw6ZrwV04UM$stK~S=>lPNDvBDM)R_8nET=agXEnEhhe_{Gh ze6yc=y7nochUqePE83m|3o0aDuzBOgfr+tp*EtRtes$>hF=)Ppjot^O}Em!eP5h(NtEZW@_co*|O@~{{+6+4|vNJ-@XOn{vzPw zfB*h{PMxo-OVz846aray2?~a43h^VFwMHI?ksfKyeOj`u4XR)>Jj%y9zX{OJ1ZZ;B*Ei&){}=uz~Y zN5R2GG9N5kk2v){zQ}1B)rJ=K0j4)W-QWyu*IdUXC9=;2Xna!b!@5rt;!TpNX5$|7>WG&3pn~4f&OL0%edA zLRRL(hbNbv{_GNykPv`90-Dt+!qWnb0g}i>ahmUa%C)fi4wq)>rC;E3xV6AF0^F2< z5lRhdmCz6vEXLfvKHc9x_$Uzz?jf>-?>~H?zi|{MA?&4nA5r^aKG>#}mMZCF7;kDU z)YH`^nH-R+%Hm=VUS8e-z&r+*(YV=V1hk$>Sg6m9-Y!zIXo zGc`_J^@31XDjFJ|P->tyorbfMX;RHu`{M@>CDW!`d2o2xW#A*fmCIo?E(|AEwEk6Zb(U%#|L0N5UOnxb-4wzfv1Gv%b82wN4`T_{xrs8BR23-~jX zeS%@7DIcD}%3}NKG2W0X*o`umzn4oNhH3?S5wZwf&d;~bbC!AYeGw#nbHH_BTER*K z5NQJg=ep?XaulQ=P$>p1_B`yY5mbIg$1ZVYb!D0KP^c9Pv++L1T_7g@{`D(3H1up5 zS8`ZLKW=AFSJyEV9LOClBU1C4e};$S=h~cupeK-wQ$_PC?w#t%lm6GL!g#V`t$qAE zYjJRNAg7V{jYOkxTbd|WalA3s?RW9~c`d{rO|M^m-%`T3DVBLP(J85!9pzHwX0^iQZW13CRse4wW=v%hrf%1;j1=OS2=h_FXAsS8M% z0d4NJ-M6=7x)H!<_mLydpb%e2^D9}9rUla9eKsK&>?nWo%idc<4f}{F;*OB54q_N+ z`62J#+3G9r9{v^w7v9nFaQ;LYJ9I8}YHI4~P9Ls}(4D6WN%jU&hwS~L04#j}Rl2URY}vRj(`n`g02wb&+VC(%oT22F!A*r{pC8ojg=%)<^${B2u(Ci6UBC-{QD1a775UCc9l+jwjF@ssF zf>0O<1Rxn_Ja|ax%pMVuClCx7VM@heS7DR&%IaCR z7c6Uy8Ma?EX^aFXG~uSXnnAC7IJc8OVABK?pearC2FU zaQeYI&>Oh{|MurkZvg=7)$59Z^FtyD1bw`KcMf#ti^LZ0dG?~Q7@rD4HBjI@2;zi7 zj@1!%$c)+0(2$Xh?MRrqJ^cw^+mmC%N|1OFC$R1+-GsjWtGStP&z{ZIGIKlEtX-QE z!63?~g58J;Wj*}s02F*=q`Q+6qmG?Bw=L?<%Dm;;=^$gM-e5;%h|}GN@(`DlwCR5t zka37xpFV#+-nL6@`P`W^chTcGu+sayE23mwk3co_L}HlXr^B=-d)FKw5Fk<3?+~W`4^-l6xio3 zg@7CL@$h)iOhaiS!8<6|NU4$3ae8`s-dAHMsQLj2S*%4q$2Tab9M2;b;W&ujKpY#G zn3!5el@6}+7AYN@i`lLZ&j#NQLBTUEZ9g2C?B@6bC5q0@M@b4hGxH#}6CZSWXrJbu zOTJJqYwPN4Aoc?q@f|v}9a=gl8dBGH3k&7etXcCbcE{m8`}dP%mxyk*dwu;mTzTxf zcUz;PqFTHqg@jbwRJQi)=$hLrBqR?Z3FO2V-$F8NkW-171``6^26+}LAPVFTGP(y9 z<%PBpIfTF~$UVVNo_M1*0>4~=@-;p);{{(Q;Z*o1`aqckPi(936{GO4g!WO^Vo;CZ(m7gE|C1e(Z&lB*p^nHW6*GUU40w5WA5gq|eegSt3|E z)`(jQ#eh6T=rP%VN%#HzsVF2cZe!yCa5+~8=dhun!Si;M}v#;UT-@vDqWNso&nYQb6C%cqbU{ipUiZe)Jem za7@SuRN2m1R!DI`da@3MB7?RG!@bD?%hj+69I3bP>hU>0yvH`!!remycZg}b$Db1t8qeU zB)_q-F%DZ7Hv(w$_~px-bMHVPl-27U}Wq@-`}xivll9^BHS0rpY`=I85vtZ_*<$`!bRde za_1QH?AHn|>mlO~ZA=;KjO2M-#)~(2x|(hir(hXx-n=*;+Pc@8uE7&jvk!%)>) zriAzHyDD*G?lfP|^KTU!Cwxp{xPs3BT9SyscknW1#)c& z#>9d|0%ruU9okG1!u9o~ue9P~Qd7U%IjO6Evs~LVG&H2B_Gfi8pGa-648k1;hlZ$H z^4B_mVM}VDE`p$f_7fftdH%mZLTa-Zzytt{fuZ4=lP6DxJ$v@)%a_dp0s`bxkx;`~ z8(3Pd)XWV5JpV;yv!3ixLs9N%wQom@S$Fi-3wNw_&2#70Ay!3v(e{&fAjv2K`;;1{ zS4{y=7_ZSoErI<+0ul({9B`VxZplHR$jHcqb8rHW!5Ot#wY0Jt`{1Uw=z^^YVGvh% z52uL~nYC*^03FQ;npJrJUJR4qhGVO~{qUjhc13*QC2j2@7=Q^6m~Qm=IOwZoP1oi( zqX43kLa(L9Jp-jeL>h|MT3kN&7C%jW{rzmkc)TU!kX`ki?Ic7FAGr z_XTEhr;+}SAmL|(B3lH#7Ud2h<9kiMJqQuNE;y*4QTaC`@xN5KI=3on@_i?BAD<>} zHCkX!QQlk~xn@jxb zVPRph4!u>v8>y|ouIQM{b(gtTdk09*SLw}!?xVE1OjZj1MWVbDiDH1rx1uIa=PzBN z0Xe`>3^zZ&Qd@6;aU?@txj>1kqmxrtN3mzcmoInWp~JM;fWZhbVV|RIUql0z-ZGjj zl(x2VkeQGR7nq5%``LfTJ5iG`19vZ}DOJ>%x4`5i-3U`(JI~>Ow6rw&$!$po?~t|3 zU8f33TA?~hCL5atLh3l|5ku5#bh8aKw%)AAgiFKA4WY~<>Q9EFHqz03hLD89bq`9k zSQTJdphU-7jhK-QdPrGf+i8K{{VYBa)Od;W1}pBosgiFO0^nO}eQ7B>Y{=qk3sfq{W^ZI1nsmT}z&XzMs|F4_p<3vU6KcTv3`oHBe9B8r zJ>AxOpz7`CW6xPHfXb%3Ej!^To#2pV@SqHgj6A`FV_&>D_V33^@UbW8f^Tp9+=I-F z0Z1=K5V*-)4u10J>C>;d(%!5Rxc4PZ`355ShrLjrL9KS#uAcLDJcGNv?JhPS37+1) zyT%1}v=l^m6!3IJ-%+P3@#`V%%v}M;v@o)jarG-~GI!A7%^<2>pYk#@&ymvz#_XOD zKf-dtmp+hU|0~B<5R9p_qr*Kcj0N}T-?I=yw#|!B3;c{Udt9&*h9qEvGHu=JiM*ua z({fSI2YAZ7WcCSQ5FLihz=-I5Tn8Y4DWeW7F7gbh$-`6%@ty~CCgF87h$6{(n{eu`o=>_fb|Rtv{TBOf~39=f+H}KNy43c+gfjY+Y7alB|HaTe}6I_mzSRp zpz4QlvI<}B->2>gM*Jp19V7|x z`0?ZMi3tTvqyR0E4@ryRjJB$=^#SJtlc_-V1IS@wX7+~dL+B=w3(7Niuud2!$7_s8RXB89_FspSh@1#+fB^JUdhWO%rbW;dyWQdx0b1-+&*3p<+>t|bg&`d$_ z$1KSjCH}F=Fu|QT)E_=mYm;l&K0yfvLqH7n6I2N8F!7sMcm|w5NPc>}Ljglh05Y5I zEH<~38A0a00A19aFt^F+=~7G#g*<*tCbg;}BpCo~egP9Bl92b+D2fp)qVGUXZmtn( z3#u2%W50EnFl6ql3O2x{t}Vq}BdS(0kWNQ$uNSIT3E&&4V2iWLZycUr#9-Fn$U4TInY*nhDGt*ch}m zHSHlEC?+4M`t^&2fES<>=6yQ%?ksh4k>dacV=R_DBZCJj!_J?A-T+)9#K^eW3-ct3 zP}F@v5%0;X2TGLC z(n&akr1`;0N>IwjG0brcJ<{?J`u6`F!K9*)uTi8^ZS(}@&RZBvCLIhNiymzcfu#!J zvuLYOC5fVqi6=7t3WW)%jp2{kS?frRTO{QL$(mp%V8T}^DaYYSgUjJ-J&}L21jZxL zOiiJkwO>mRcf^R#rvIAfv=l2ldkNyhPFAB}}WgM%NJ%@NYl6oUOQ6@0nCv5EuYQ8O$-KG{gJ zl-k-P^Qm~DTU0DVF%L#G!LrD(fi>c~0O&UVhlY?4{c`KOvu7|DhmbPBUaC@!whfZ5Vk5z?^cnwS8Bm z+`EzlS>X~k4kv+V2m)g06Va&>7|0JEJiuI{`^%TTSMpk`xRX*+J|oH$2wslP(fPjd zGU+e5UQe`zl`-54XrDV{kJCjpvt-bDooM}N))D82Fg1ZpWF25i8Gx!WjhsBfX78#N z8`s7xaynnUxC67;R4uJm*L^X2ofy9 z++;xe1n&+i&0h*(>LTGpWGM+ZdHR&NNazlPA{4dm%LUFydk1x(0wo~v(3Y0xG)G~p z;(IFQA5ZJ!=@AsIt4oWkfOG>r$9k_7uyxi1P-M@VRuC{msJsAz5Ml$=+Sh;a({y}6 z0VlTEUwD#`=KR1OU;IbT5EW4&L(gZ$fK%~(HU`<gY7QQKDjE7@-WEN_LXQPC<^7 z*li?d37QWCZ`r+Txt?lCpZ;Gug%LI6F-YbSc^~QHue5nYw)k12#URbe1EPdsCeXFM z4+DT2ph&|K2%rnyl4p2Cj>Km_M${+3UwIf6NW+d#Ps8ZgfFmA&--Xkv%$*i|h^30q zW@B|4$Rs=r+y2W{-@YM`SvqpHjr}y#%Mja{5+6;W9*JAE8oO&X2L7X(rl$YPS6bWJ zdV-^(yKV1u-Py(?^8X4yl6eAE{<~OX&!0cB(0vSxplw%>5X1q5OE79nEeW?fE-tRy zW4RSr6vSgvk6#e^{&aND+R6iVZqC+1NdCygDPo#Bj~{eJ!{%VnO+=gwiO zZXE;%;Gh+GL?%FKJOBLI^j{Pb1`PxD!lcNIA7)c*;7z0Pb3*_?7?b2CK?#xcfiQHx zrbZJd_n_1$(DMY+hB$xBdq9X%f>?oe3*`xyKt~E)MtXXPeU8R+Yy+T+%2P8lGo%)h zF?d8YaW>rC-12SFlfCR+*bGEuf!Grg9PAE!ODsJNnVD_S1{qmcq;_VxXk?Q4jrjNu za0$}dA^#&h?M1wO=xYwJA<3}(n3&H?;2?|^v*ENLp2?ALHOdgunZ>5>0ZlSyt%X zfF&j-=z<}TK>&Kls5;ac>;a`K80bZc)OFz&ZXm;qqjxL=nKJ}D=t2~bU>vX&xOEjH zKarWJbpBcAI*Kb;y;GDN`GdF0kclFh`kM5pM$ zDDR9tj3~i{Wi8{m*=3@BA;l#sA%R4PI3ou~AYoDJ>K1RmvOHrXGk?G%>GornaV{_L z!UjOqlBsIBpnu+7OE+@nQ$md)auJ`(gN}+2J|Y`Jr-W>n4nP30;B?zAdOz3}-MD9; za3rk@7pS2~fFKY*9O{#ya}(b=>=B~&LCnM6GeG5rVpfE3LO^hEaPh~5ms=0|LVL@H zdT)IFx+;nZN%P4qPj3Ycr@Qv9N))Z+H~?BEV0Xrc4+7Aa&nBFTMON<2zhOdlYqY89 z&pY&kv6-m2O-xLhz#TBP2k@m#4?$r7my`$?wMHIF&~)(zE=>i6VtL73oa2mRgM%-S zOsqMpA#0WU*DS*rdv z&5QXjn;E<5H*5Q(u^NS^AOCB3YSZrROqG{kSnjfWf4$JIy-;rHh@5@X*;bM0{%#vC z%em)M^Lb-4Ib*h?hoh(FMY-Ios!lCHFIVMeL_lQEw{USEh&)$0F2tcg{fJTU+(;qe z605>xWr%MX^!}zLFd8Butb5b49;0AVDTGW{!r>b`Z~l&d0)fXfC^@PM3Ldy}6|@u6 z2ZrGc+Nk4jzynX6I;9G)izGJTbY;*dUj}R?<_hIHMhU0U9V?IUE7KEOj7)>O$VQ{7 z{=vYZ`o6*ChuNA!@fcHNqLA3)Kxc{1*x8*^OvuQ_kq|B@53T~c$VV0N$0KiUAjNG8 zB0wCGniz#!c(KAiKBX=ch`%Q=c1%R`=H>`aX@{C$yw9RaN!$b^lf+GtU$(k&fL2jS z3FSNibm~58A5cZIx}GADjO_+$JTRZv-tX&Ya08cqJl|=Cm!v82pw)z#C2!T02~ME@ z5wC2*L0ad7IqUWvJ0w5&2A#ltd7~J-SJJYDqz7xE_s_5Y1hs}FA9o80)qukNI&5{A zJ497TRyGO&fU6uQ;;_tW0EJ1GmYSBOriNxU@MIT6#`;7JuG;$gcx;}8;|o#V^c*MB zF6Y0t`3IfK?oVxRzR}mp%2;9|f>qM7iroX;P{EMvEoAqopQgJm-kuyE=f>>H-2LMh zh?fA3keCxlqu#>%9WEpvm6MBBey~#wLEucQ_JRw^b9z|B^>Hc;gU}X|mumBn7*~IG z(2v4M8N_0vV>{@J&R+TXvzLTri;$gsjjqMA;?2L~27|RPX{dsSqBMw~jNT)5@L)W& zhlLAP4qK`#D|rAyGTy$u{>3rk+Qh&QzBTf&>XU79w1ow5Uys}x&4u<)jK{$#y*RqF zz)>V1e>sl@;2dMRhf+<9jMnbmn=J5?ykLR|C&*YH5*e#YWLTIT@h6kN*pTY4<5K(s z1CMKJ2G=`qLO%wcQi8RMlQ;al8GVNuJFg{sVxQ^C(mc_~v7N4pJ-KDk{R&W?;&7De z#N#>Bp$vi{fgLQvE+$xF@sU{y^WVY;MkNm_(w2R451O%aJ>dlVU^}z?(FZ zvmU(Wn?Mh%3+7Q&ML(ft;5>yfj5AlGklZJw-=BFOFCG!G>atmYM^f|)qdwY?oyP1E z`7A&a9*#0&=iVN9frc6a2ZQXDC~3zt<$`+RXw%Fx zcVGLf^7BHA)%keK)W2aXJNrpYq$J2_V#(c*-ohn%g@Ov9iT69q>6~qL!k@0l>Kj{F zs3Sgu9;=A2H#Rdn310?o*D(?Y2NgjOA@#O>C$7pAZh0p1GtXdtfhB2Sv*)J+H2A`Q z`9CS{)hW4EMJ2l|NY|^mGx}} zi5@;aDk(!{pFcBEpzOVVSB0eG77CmAwNv?an9T`>kB$)gW55r2j5P40CyR-Sb~qg| zZ_5kEMJeL7QA*RZO=MJTW~@CLdt4843AP&S-MTN2_nJb!AWa61i5Ea;jXcJ75*Fvi z&I0KHfCj@C+d-(9DUtvNOtjr@A5N#3Yw0TQ*4sAw>y21|bc*@$zRn)0cs zsboNdoC87$H4qS9X++E7Ml zX|6_<;-w?XkXcNvUC%dDVsMC?%}@hF06tTqO;1K(y@S;J%zU-N-G0{GdhM9x`l#u;FQt~wQMWE2k!XIG$5SU4KqO;N0ljq%Rxu%22qzT=R5tpL>xa> z78X^DLg217@`zTwgam%0y+DRT%3A(LB;G>tm{J2N$N)Ucl6jj@7pBXT@)$t z4^BX?hI(}0*B4S-67cYu9hDc}Xd{7x4QW?UZNGlQhB`nEW2dbT5No3ByKVCx;aX6x z>qvp^JL9H!e%FLrsK5_6ORwz5+TvfmQr#+WDh8rATb8N$B|%;i;-!iK0~vh}`nZLX zq>zg|_-RVK(>-YKwG47gMavwFjPjUHqr2Zbrk2NJk@;a(KlC+!R7i*d$r?jWQr%~z z>I!U$!wWsuClQlcWL}0+3Sa8x-1MT<-?G$wquuTpj)!&yWGhfEwvN0a!VgN_%{vTi z2RA->@`R|f2t#Og=bS{}qGc5$Xg#Twv}yjXz98RyQuk%-g!8Juwc{_1M`bz|ew z$lLP2-_;^riz* zmf@?CpMmVkWD4!aIFX*~=b??~P+SpK02-kqTdgkk(dKUmEW;3nAE7*{qV*u?sbXipxE>nT)BEsnp z*|O;9>C2$^`vL8fw_*^ZURp-ROqWc;<2`AR*WeqxwriM!97}P}&v%U*qO_g1Oh%|2 zk_w>8nxbab+;)UwXrP4~8ynZ_oK}=}`O64Nu^jJpiqI{)yPgKq0#76k^yNYiLk_!+ z_v*#R$+59NczX*O5IQ&i^_B_}1{o`Oy-OfwV&tvBp^^CH%g%EzbU=IEkhsR{gbaY5 z$VdU;Zm6N@0|}k2_`>1V+#TqCcv+j(Em!i=1g<+v9ssUa@EQh;Lt?VSa|Jx_?cG?z3iT zTOf#xer4j~(l?XFO(bu2`*GZ8@D=3mD<&r8BU)y;I#xzTXH5>zXhFrpYYH@l-h2=~ zdNj9f#2)X%LEN5rTVvZ*Y=c$#$+xFRs-4dn%k4Jb4H8drNBF__bJvZp5w-LYB!mV0 zgSRSCf!6*gyt6uYzt^?-x&><81^`^*ShS?|m(}+V4_D17En-UIQul**hfu;1 zX8RI(z@inDkhM;#A3~FdCt?!WyaH~9m-(g4o|O_79?mzs@fwB#z<<%VZz;#Wka1*W z+N#?0x4*vnA_XtpT1$Zi9e>)MqHZ>%ZU=@zOeq$0>N(^^`?^y1Goy6^6WnYr&|ptF ziT3|LJ-!R1*Zhnt0L3xzb)p_4LMUL^h#hjmw^y*DCfzW?6-q?@*a*_1LJnX;olDD7*+-N@ZGG!H5 z@28Uk86Tw7pqOVjN?5g>qLAjPr1bA7*(?1*H-a<(`S7~Jqe!(TV$P!$J#8@HDsa#` zxZhm^c*)i|KuRn2l$7Ph_`zdu&{8v4~<2VaXMT?qCtuGp*y$ii0p#f`w%68yus+_6P!V$asW$l;GqL}HCxhZ58fhn z^Y-lo9z}05r}i!EfG6Ie*BtXPTJFwi0tb=3elmIzdPv+@QI3`xyV2%12gOI^uIa2K){e3WS{ zxH`a$fg+E!x?GJgAGKf_;5{zLBgdSMJVFEsFtnuaOlz!KvK3HWTKX48713Y>>f8)>? zLovP!70O$QS1U5TCExiekZuJAY}ox@rAp{rT1=}1_{oWEMJVbIl4#`VLlL0VV6bce z@1+S`I9>Jp@H;Idf%oJ+Sdag@xLM?fvfXZWMIHhypef_($tmS>(HThL1aaY=E-AI2 zZ7>NK9}`nV{lpMDf#z6S{?#LbsLl2S12YS2Yc}sOT){i0F8s8YErWjn`k@U1Pn2&H zlg4}Rj?mUgfuMOH)C2N40T%h;#Qxo=&1fiBh;LHj-+wCk4MZf6nmcF$C+YZgCFjm+Ypdd=aXeTgq; z4lVk6kvmoVn>VMRw^|KcQ;#+hhx$$=3d{=&XhfZYexbhazJU-*93dc}TS%(5CJ3B7 zt)>=>S2=XYP=#n!QWEmzQ~dHdVJuUSqrgVZy< z#$kIf79;@o41sbCLO;?;Y%`-FkxM9j@p!Kb;7~krY|wxz(n;)_s9qWx1m-G{S6szY z_uYt@)96yNxc%eJni81UgC~=Tkpv=YP~r6vN3j$9PmRtR8SlpsG?{ot8K^ICbp|m$ zkz!=@H3BnHM^NN22)6}e#>kvP&a}+m`b3n=2NhZeZXM>3w@@%OlZZzv(NCweFSN8B zqSpV_+nI;uocDYDR}nKKBimCFEgHsJOol8qN=nJtD{EP1Fm*Ch8H6_OtSOFZCXv0B zq*9hqqHI}?%2Ft$=uRa}G~#)EnmN}w=ildgt}~Z^=DP0g`?q|T&*!~;C*XZDf@K7k zkcYz$eFoZ^;ajwIE0o(*astSNj69?B0EIa<@YrXD6y#QHiYw~xLmyS$h;hP3zWi#I zi@giV+B6+LD>Bu3Z_BRXF5Hpr~J5p>qJ!ajv=o%Upn1#?EQmAF;DE=cc;)ejf+`WtKsy zNOz$*upbPgE-!!jv@Ms;9(>tk^!>gs&tJQFGoSrI`k9M)c~4;bd5tQzC$6TP1V)xn zc*tDX(cn966T566dY%!ToYOG_hI zU{X?20vVY6T2?iM2E@~cenSq>B!h8@g5SZ##U9T!x0PAV(xn74A`a5Zaq|i}mnC){s`-NTTKZeZnajbc`=s$@kVQp0Yqx!9yp~mZuB?n3(z4Y{K&@({f7xnTg ztDb5}MZ(rkZxykc%^8KV{4xszKzeJMc8rbDjt5yY#;~_rUmbG#?6u36cT9Ay{~`gB z&wC7g6mm3Mz7fHIj6IT<;O(>g=e?Ojx3RH4&Ko|BAK>55EzOpxCr?J8=LHxM4KmUr z8d8YcZ0O*u7btPM*X-JUzl7e~S!7n!acEXw|9v=G(e|SpNK=g`3be8C4YyPjqIuyI-}8 zS{d#$-b5G6+o8ziU}Ar%IIbGL0pFV8lv3YISEI;a1o6qJP!*r~otTmFhM_?Gcs-ZB zKm6^R1yM&34pcvQe`Z3KNrXd<(Sd}RkLL>R{t~NE82yYs9CO_$BrZ;4?vgkL=S2Z^ z3(NPh+W9-ZBk!o9S-?fTkC%Cu--zC+pJt8Kzi8g!)rp0Uk_xx| zL?1%&P4ngB8zUAD+%&91?q!SJk`Y&_=k%mQuKAB!%_J&rWL~6%==c1$|CwvI+nW_d z_qd!9@c;YE|MIVmcWal8>NOs@k0{OwQu_vByAq2Um${$>(#%N81Y4^jJq53d5w9gm zWK>Eti`O6Js{$w3^-&w?ZCpAElJ8~Zl`a%`|7&v7Qu9GkuuyX>Nj%QBCx1-4L8#B31#= z7KAD~vpe+s9RH%^ zdQ!ZW(>^1hw1c+gHf6KN13LJfU+ujq#pT1@VKzXxB>&y}D4VRQoGd_2nAjp38-r2D zp!`{6=#j@T-%SzYh`s-|eb95N}W zX~H&dzCe#72L^!G3k-?@2KmG;zo>$3OsI&J*r+7xRFY-U)ylC3B5TKFvikKiV|BwK zXv4O2(pYZ3q1xhd%md!~ILBCZ1GLcIck>s&u!11@q6w@}?5O$Z(t-hYB?b&ZK@K{9 z53~yEh6j5jj*G(q$c)WK=E!~=Mk~4y+M)5oDw+O`mB=2&%}@KHqv%<$8%<#Z2V-1u zxpD$@)H;lG66nu1s%PcfPjX5qP25z)U3uhNM@tb37#;+ma)IF}xFGHA*o?h;1qqBD z0Zft9gn@8)Hk9SKp7BpUYxe3>|B^AeX&w|?u2?|n3Pm?ri>Dk}N)*`>PG$9uh!O$G zZJ;V$twT!Nv zU`tyNJ={x5WcVr->R$qVClz}*dN4K@)p8?In0bMXLWt6LDK0m+B++ZQv;|01VZrs$ z&74cJeUftL%MPZt=+vUHbdyo6UNWgn!qjPHJ22mSg{w5Qv={J-M9GDU8SzM{?15@> zuP$I(!J_8vI~UV(dETF_Ie8m(3>q4rI>q~u9eo7^VgV({wPIC!}nFcPPHDG2^Hgi2Xy|#%k z=*xDjtI|HAV~)q{aQ_;i^ib7~N*w*;nie{mi`B2<0kf!5?d|L+CYUI|xwexM)S&cj z0;vZGFgum!2S8~DikFbSl|@NcLHjZ{#!nJXB;R^%`eKpjpqjhIdp2m&?%`2>VT1e- zQ6qz9PB0B~V}I0lJL5lyNic;SeM=FGu!E&xBH{8~6wDjd^9bGF50~&&)-hdE`%bNq z9NVs~c~djKss8bCzdxzIr%{hkPla9dT#>APT2&Q^q(&o_5$$vQ=+UEMnxgX@{%*`y za@&Y|cb-4LeoOHk8&4U6i(tPM#K)Xxv1&`l?CB-Dmhe!ON84Y|y2MEGdDTWUsPK)p{(_^^ z^z6IWJvz4qhU%32S|;lS{I`j7$>A zS*{@%H5FN?wX{3L`OS=BZ^ErCaUV*nAwo&v#agKmH|4V0DN1;+F_AT1cY5HbY@LVG zrVcf}Hn)9lz*cO7oAN7e^GMy#9zzhY5+HL2*Z*bz{>42)05&Pln-Qy$Phy#HgTRrE z$}K8}tW@ln?&{|G0WaIGES&s0W7al$t@wn5Vz-s7#bnw<@}Mj?LP;sGfs!WGAVYRko{+&kwY?)XMU$c{Tfv~6VRKv>05a@f-A`Avv=1nJ0&c?VhY<#`qLRL`OtmNKur|l z&)GS2j;(~LZWD-?MP1v3e*I%K2@dWr-r&^)4Z?FUvtnd5$jQXH(?$rDY($IJzhA%a zefHEdk05F1>kiG9DKU<|vgTa;;gN;Kliy>uZxcvEmN;m@ph1VxjICI2z5c@<7N+mn z3w4e)!PB=1q?E#boSf$eItxX$mELRloUuRLGuO0X*Dty{eAuwW-ARcqi2@MktoS`V z;buxraMIQk_Px{w(FY25U;MJpFq(Hzsz%kcXz}9l45r%$i!%6SGth{-!T-g!CIJ(S zG5ex=vJP=trVoCM1PoKe-O~X;S8W1;A1}phcbqtp0(49>aC~BNZ(ZG-meV7May#hh zwYm7M^$&`LfwIM8krAPwc(??qV2l^nK2@nyt&&+9r@&J!36q?i8=BuNrxlZ0?)>{k zr{pdJ2lVgn$$EhB_#y~Q3KTqekY)dkNQOX#4(BaK`E~(Vc?Y1!*HRggtdp8#0VwEFmm5-$7LEE$jsCu z#iL;CSf^DtvFu*9>UktWMEo!mCRm~Irl)q;P*9s|p2}hy$g30Q zG|=!)zY)%+n71Y3mEwN4pJu!4?HBd!kBEC-Tx}F{G^UY!Yz&jnVnMbNcFB`)*^`5u z=M;P~afzPW>Q_}dym%F~4f@v9cVCs5b6^K?%tj_Anglb{Te`Xq^OTijH$F#6I_1z2 z;jwAV)01pHmwgMn$i7*|Qg#u|QOQKE!QNt#-mk-k54Q(nktinc%lqM%avZLZlLNZ% zvTy7*81>wT59byex0VYJ6dbz5NsFfv*0R>~I|C`aK#wY@2{SoqDP&`%mlt)NvjJyW zh(Xcw@(yD?3(q!Urz3UWP4#9<0%@=)5)^olXKJ-6J}R&zq>%Qle<$J8bem=3LqzLc=P$Z93#Lc28I{{wVRZi(2b_xjy4>MHtOJKC8vhSW`JI8M)V-n(s|ID z!oTz2q#u#J^#xC&k z+J>&Xwlu2?dwD)Lw%BmqL79m^dcJKa=dtymIJp%+t*>~F(F};g5kP5(x}g$h$wej# zU!eoA&$JMP6p)EUcKqy2t&_L!8Af+7J<6(n0-Kpsz|3Ueusr5*d3Ci=&~B=`OQ6mw zu;|M&1b)Hs^0Dp@r zeiM~qEc!+C>O1}73hc==pdUjCEpdBvc7zy(13oWg_)3Z(C9psiZmI{JWu5-~KGS*y zAZi^!`upSJ+A}6!*F7{4e>dGCK1Sq{Mrt4R3?O7E>I%L}5MH2o*<@iVS%6@zWAXie-uWqU!e;Lm~$I^=)pV}iGg{x7 literal 0 HcmV?d00001 From 2d3b5f44819f24bbb3673b530503342db11de487 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Fri, 14 Dec 2018 19:11:17 -0500 Subject: [PATCH 020/390] make text consistent with image --- proposals/1756-cross-signing.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 0bb365e9..c6e86554 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -119,19 +119,19 @@ needed to reattach the parts of the graph. One way to solve this is by registering a "virtual device", which is used to sign other devices. This solution would be similar to this proposal. However, real devices would still form an integral part of the attestation graph. For -example, if Alice's phone verifies Bob's tablet, the attestation graph might +example, if Alice's Osborne 2 verifies Bob's Dynabook, the attestation graph might look like: ![](images/1756-graph1.dot.png) -If Bob replaces his tablet without re-verifying with Alice, this will split the -graph and Alice will not be able to verify Bob's other devices. In contrast, -in this proposal, Alice and Bob's master keys directly sign each other, and the -attestation graph would look like: +If Bob replaces his Dynabook without re-verifying with Alice, this will split +the graph and Alice will not be able to verify Bob's other devices. In +contrast, in this proposal, Alice and Bob's master keys directly sign each +other, and the attestation graph would look like: ![](images/1756-graph2.dot.png) -In this case, Bob's tablet can be replaced without breaking the graph. +In this case, Bob's Dynabook can be replaced without breaking the graph. With normal cross-signing, it is not clear how to recover from a stolen device. For example, if Mallory steals one of Alice's devices and revokes Alice's other From e9aa4081bc8e379625afe9eed285073e9f4c7886 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 17 Dec 2018 15:37:26 -0500 Subject: [PATCH 021/390] fix sectioning and add a background --- proposals/1756-cross-signing.md | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index c6e86554..5fe52e4a 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -1,8 +1,22 @@ -# Background +# Cross-signing devices with master keys -FIXME: something something +## Background -# Proposal +A user with multiple devices will have a different key for end-to-end +encryption for each device. Other users who want to communicate securely with +this user must then verify each key on each of their devices. If Alice has *n* +devices, and Bob has *m* devices, then for Alice to be able to communicate with +Bob on any of their devices, this involves *n×m* key verifications. + +One way to addresss this is for each user to use a "master key" for their +identity which signs all of their devices. Thus another user who wishes to +verify their identity only needs to verify their master, key and can use the +master key to verify their devices. + +[MSC1680](https://github.com/matrix-org/matrix-doc/issues/1680) presents a +different solution to the problem. + +## Proposal Each user has a "master identity key" that is used to sign their devices, and is signed by all of their devices. When one user (Alice) verifies another @@ -38,9 +52,9 @@ Users will only be allowed to see signatures made by their own master identity key, or signatures made by other users' master identity keys on their own devices. -# API description +### API description -## Possible API 1 +#### Possible API 1 Use the same API as MSC1680, but with additions. @@ -93,7 +107,7 @@ require creating a new backup version, which may be what users need to do anyways). Or the private master key could be stored in account data, e.g. `/user/{userId}/account_data/m.master.{deviceId}`. -## Possible API 2 +#### Possible API 2 Treat master key separately from normal devices and adding special handling for them. This might result in a nicer API, but make the implementation more @@ -103,7 +117,7 @@ attestations separately. TODO: write this option out -# Comparison with MSC1680 +## Comparison with MSC1680 MSC1680 suffers from the fact that the attestation graph may be arbitrarily complex and may become ambiguous how the graph should be interpreted. In @@ -144,6 +158,8 @@ In contrast, with this proposal, there is a clear way to rebuild the attestation graph: create a new master identity key, and re-verify all devices with it. -# Conclusion +## Security considerations + +## Conclusion This proposal presents an alternative cross-signing mechanism to MSC1680. From d9eddcf7df0dae23a1ded7281324edf090ece1a1 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 17 Dec 2018 20:34:27 -0500 Subject: [PATCH 022/390] change language to json in code blocks --- proposals/1756-cross-signing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 5fe52e4a..758194dd 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -64,7 +64,7 @@ API to create new virtual device: returns -``` javascript +``` json { "device_id": "ABCDEFG" } @@ -75,7 +75,7 @@ Send public key using `/keys/upload` as a normal device, but with a special `POST /keys/upload` -``` javascript +``` json { "device_keys": { "user_id": "@alice:example.com", From c0b0db9a55de9969c96759f32440866cd3f5930b Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 17 Dec 2018 22:33:10 -0500 Subject: [PATCH 023/390] document alternate API, and some other clarifications --- proposals/1756-cross-signing.md | 189 +++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 3 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 758194dd..11bfe2c1 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -25,8 +25,8 @@ master identity key. (This will mean that verification methods will need to be modified to pass along the master identity key.) Alice's device will trust Bob's device if: -- Alice's device has signed her master identity key, -- her master identity key has signed Bob's master identity key, +- Alice's device is using a master identity key that has signed Bob's master + identity key, - Bob's master identity key has signed Bob's device, and - none of those signatures have been revoked. @@ -70,6 +70,10 @@ returns } ``` +The server should not allow any client to use this device ID when logging in or +registering; if a client tries to log in using this device ID, then the server +must respond with an error. (FIXME: what error?) + Send public key using `/keys/upload` as a normal device, but with a special "algorithms" list: @@ -115,7 +119,186 @@ complicated. For example, the server could automatically add master key signatures into a device's `signatures` field, rather than shipping the attestations separately. -TODO: write this option out +Send public key using `/keys/upload`, under the `master_key` property. +(Alternatively, could use a special endpoint, like `/keys/master/upload`.) + +`POST /keys/upload` + +``` json +{ + "master_key": { + "user_id": "@alice:example.com", + "key_id": "ABCDEFG", + "algorithm": "ed25519", + "key": "base64+public+key", + "signatures": { + "@alice:example.com": { + "ed25519:ABCDEFG": "base64+self+signature" + } + } + } +} +``` + +The key ID must be unique within the scope of a given user, and must not match +any device ID. This is required so that there will be no collisions in the +`signatures` property. + +(FIXME: how do we make sure that the key ID doesn't collide with an existing +device ID? Just send an error and let the client retry?) + +The server should not allow any client to use the key ID as their device ID +when logging in or registering; if a client tries to log in using this device +ID, then the server must respond with an error. (FIXME: what error?) + +Uploading a new master key should invalidate any previous master key. + +After uploading a master key, it will be included under the `/keys/query` +endpoint under the `master_key` property. + +`GET /keys/query` + +``` json +{ + "failures": {}, + "master_key": { + "@alice:example.com": { + "user_id": "@alice:example.com", + "key_id": "ABCDEFG", + "algorithm": "ed25519", + "key": "base64+public+key", + "signatures": { + "@alice:example.com": { + "ed25519:ABCDEFG": "base64+self+signature" + } + } + } + } +} +``` + +Signatures can be uploaded using `/keys/upload`, under the `signatures` +property. (Alternatively, could use a special endpoint, like +`/keys/signatures/upload`.) + +For example, Alice signs one of her devices (HIJKLMN), and Bob's master key. + +`POST /keys/upload` + +``` json +{ + "signatures": { + "@alice:example.com": { + "HIJKLMN": { + "user_id": "@alice:example.com", + "device_id": "HIJKLMN", + "algorithms": [ + "m.olm.curve25519-aes-sha256", + "m.megolm.v1.aes-sha" + ], + "keys": { + "curve25519:HIJKLMN": "base64+curve25519+key", + "ed25519:HIJKLMN": "base64+ed25519+key" + }, + "signatures": { + "@alice:example.com": { + "ed25519:ABCDEFG": "base64+signature+of+HIJKLMN" + } + } + } + }, + "@bob:example.com": { + "OPQRSTU": { + "user_id": "@bob:example.com", + "key_id": "OPQRSTU", + "algorithm": "ed25519", + "key": "base64+ed25519+key", + "signatures": { + "@alice:example.com": { + "ed25519:ABCDEFG": "base64+signature+of+OPQRSTU" + } + } + } + } + } +} +``` + +After Alice uploads a signature for her own devices, her signature will be +included in the results of the `/keys/query` request when *anyone* requests her +keys: + +`GET /keys/query` + +``` json +{ + "failures": {}, + "device_keys": { + "@alice:example.com": { + "HIJKLMN": { + "user_id": "@alice:example.com", + "device_id": "HIJKLMN", + "algorithms": [ + "m.olm.v1.curve25519-aes-sha256", + "m.megolm.v1.aes-sha" + ], + "keys": { + "curve25519:HIJKLMN": "base64+curve25519+key", + "ed25519:HIJKLMN": "base64+ed25519+key" + }, + "signatures": { + "@alice:example.com": { + "ed25519:HIJKLMN": "base64+self+signature", + "ed25519:ABCDEFG": "base64+signature+of+HIJKLMN" + } + }, + "unsigned": { + "device_display_name": "Alice's Osborne 2" + } + } + } + }, + "master_keys": { + "@alice:example.com": { + "user_id": "@alice:example.com", + "key_id": "ABCDEFG", + "algorithm": "ed25519", + "key": "base64+public+key", + "signatures": { + "@alice:example.com": { + "ed25519:ABCDEFG": "base64+self+signature" + } + } + } + } +} +``` + +After Alice uploads a signature for Bob's master key, her signature will be +included in the results of the `/keys/query` request when Alice requests Bob's +key: + +`GET /keys/query` + +``` json +{ + "failures": {}, + "master_key": { + "@bob:example.com": { + "user_id": "@bob:example.com", + "key_id": "OPQRSTU", + "algorithm": "ed25519", + "key": "base64+ed25519+key", + "signatures": { + "@alice:example.com": { + "ed25519:OPQRSTU": "base64+self+signature+OPQRSTU", + "ed25519:ABCDEFG": "base64+signature+of+OPQRSTU" + } + } + } + } +} +``` ## Comparison with MSC1680 From d318ff95f3e835687df9d3411cf52613475516a3 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Mon, 14 Jan 2019 11:55:27 +0000 Subject: [PATCH 024/390] MSC1802: Standardised federation response formats --- ...standardised-federation-response-format.md | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 proposals/1802-standardised-federation-response-format.md diff --git a/proposals/1802-standardised-federation-response-format.md b/proposals/1802-standardised-federation-response-format.md new file mode 100644 index 00000000..736cbbd3 --- /dev/null +++ b/proposals/1802-standardised-federation-response-format.md @@ -0,0 +1,55 @@ +# Standardised federation response formats + +Some responses formats in the federation API specifications use the form `[200, +res]` in which `res` is the JSON object containing the actual response for the +affected endpoints. This was due to a mishap while building synapse's federation +features, and has been left as is because fixing it would induce backward +incompatibility. With r0 approaching, and already including features with +backward compatibility issues (e.g. +[MSC1711](https://github.com/matrix-org/matrix-doc/pull/1711)), this is likely +the right timing to address this issue. + +## Proposal + +Change the responses with a 200 status code for the following federation +endpoints: + +* `PUT /_matrix/federation/v1/send/{txnId}` +* `PUT /_matrix/federation/v1/send_join/{roomId}/{eventId}` +* `PUT /_matrix/federation/v1/invite/{roomId}/{eventId}` +* `PUT /_matrix/federation/v1/send_leave/{roomId}/{eventId}` + +From a response using this format: + +``` +[ + 200, + res +] +``` + +To a response using this format: + +``` +res +``` + +Where `res` is the JSON object containing the response to a request directed at +one of the affected endpoints. + +## Potential issues + +As it's already been mentioned in the proposal's introduction, this would induce +backward compatibility issues. However, proposals that have already been merged +at the time this one is being written already induce similar issues. + +As a mitigation solution, we could have a transition period during which both +response formats would be accepted on the affected endpoints. This would give +people time to update their homeservers to a version supporting the new one +without breaking federation entirely. + +## Conclusion + +Such a change would make the federation API specifications more standardised, +but would induce backward incompatible changes. However, with r0 coming up soon, +this is likely the best timing to address this issue. From 749b1777fa549ba1e6c156d26dfb43b5eb88127f Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Mon, 14 Jan 2019 16:17:40 +0000 Subject: [PATCH 025/390] Propose a backward-compatible option instead of an incompatible one --- ...standardised-federation-response-format.md | 44 ++++++++----------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/proposals/1802-standardised-federation-response-format.md b/proposals/1802-standardised-federation-response-format.md index 736cbbd3..f4cefc8f 100644 --- a/proposals/1802-standardised-federation-response-format.md +++ b/proposals/1802-standardised-federation-response-format.md @@ -4,22 +4,21 @@ Some responses formats in the federation API specifications use the form `[200, res]` in which `res` is the JSON object containing the actual response for the affected endpoints. This was due to a mishap while building synapse's federation features, and has been left as is because fixing it would induce backward -incompatibility. With r0 approaching, and already including features with -backward compatibility issues (e.g. -[MSC1711](https://github.com/matrix-org/matrix-doc/pull/1711)), this is likely -the right timing to address this issue. +incompatibility. + +This proposal proposes a backward compatible alternative ## Proposal -Change the responses with a 200 status code for the following federation -endpoints: +Add a new version of the following endpoints under the prefix +`/_matrix/federation/v2`: -* `PUT /_matrix/federation/v1/send/{txnId}` -* `PUT /_matrix/federation/v1/send_join/{roomId}/{eventId}` -* `PUT /_matrix/federation/v1/invite/{roomId}/{eventId}` -* `PUT /_matrix/federation/v1/send_leave/{roomId}/{eventId}` +* `PUT /_matrix/federation/v2/send/{txnId}` +* `PUT /_matrix/federation/v2/send_join/{roomId}/{eventId}` +* `PUT /_matrix/federation/v2/send_leave/{roomId}/{eventId}` -From a response using this format: +Which are the exact same endpoints as their equivalents under the `v1` prefix, +except for the response format, which changes from: ``` [ @@ -28,7 +27,7 @@ From a response using this format: ] ``` -To a response using this format: +To: ``` res @@ -37,19 +36,12 @@ res Where `res` is the JSON object containing the response to a request directed at one of the affected endpoints. -## Potential issues - -As it's already been mentioned in the proposal's introduction, this would induce -backward compatibility issues. However, proposals that have already been merged -at the time this one is being written already induce similar issues. - -As a mitigation solution, we could have a transition period during which both -response formats would be accepted on the affected endpoints. This would give -people time to update their homeservers to a version supporting the new one -without breaking federation entirely. +This proposal doesn't address the `PUT +/_matrix/federation/v1/invite/{roomId}/{eventId}` endpoint since +[MSC1794](https://github.com/matrix-org/matrix-doc/pull/1794) already takes care +of it. -## Conclusion +## Alternative solutions -Such a change would make the federation API specifications more standardised, -but would induce backward incompatible changes. However, with r0 coming up soon, -this is likely the best timing to address this issue. +An alternative solution would be to make the change in the `v1` fedration API, +but would break backward compatibility, thus would be harder to manage. From f4fe318a85f4cefe00ca15e49914ea2b7b061bea Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 17 Jan 2019 23:52:36 -0500 Subject: [PATCH 026/390] update with user-signing and self-signing keys --- proposals/1756-cross-signing.md | 357 +++++++++++++-------------- proposals/images/1756-graph2.dot | 23 +- proposals/images/1756-graph2.dot.png | Bin 29214 -> 49417 bytes 3 files changed, 187 insertions(+), 193 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 11bfe2c1..09747011 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -1,4 +1,4 @@ -# Cross-signing devices with master keys +# Cross-signing devices with device signing keys ## Background @@ -8,215 +8,188 @@ this user must then verify each key on each of their devices. If Alice has *n* devices, and Bob has *m* devices, then for Alice to be able to communicate with Bob on any of their devices, this involves *n×m* key verifications. -One way to addresss this is for each user to use a "master key" for their -identity which signs all of their devices. Thus another user who wishes to -verify their identity only needs to verify their master, key and can use the -master key to verify their devices. +One way to address this is for each user to use a device signing key to signs +all of their devices. Thus another user who wishes to verify their identity +only needs to verify the device signing key and can use the signatures created +by the device signing key to verify their devices. [MSC1680](https://github.com/matrix-org/matrix-doc/issues/1680) presents a -different solution to the problem. +different solution to the problem. A comparison between this proposal and +MSC1680 is presented below. ## Proposal -Each user has a "master identity key" that is used to sign their devices, and -is signed by all of their devices. When one user (Alice) verifies another -user's (Bob's) identity, Alice will sign Bob's master identity key with her -master identity key. (This will mean that verification methods will need to be -modified to pass along the master identity key.) Alice's device will trust -Bob's device if: - -- Alice's device is using a master identity key that has signed Bob's master - identity key, -- Bob's master identity key has signed Bob's device, and -- none of those signatures have been revoked. - -If Alice believes that her master identity key has been compromised, she can -revoke it and create a new one. This means that all trust involving Alice -(i.e. Alice trusting other people and other people trusting Alice) needs to -start from scratch. - -The master identity key's private key can be stored encrypted on the server -(possibly along with the megolm key backup). Clients may or may not want to -store a copy of the private key locally. Doing so would mean that an attacker -who steals a device has access to the private key, and so can forge trusted -devices until the user notices and resets their master key. However, not doing -so means that when the user verifies another user, they will need to re-fetch -the private key, which means that they will need to re-enter their recovery -key to decrypt it. - -When a user logs in with a new device, they will fetch and decrypt the private -master key, sign the new device's key with the master key, and sign the master -key with the device's key. - -Users will only be allowed to see signatures made by their own master identity -key, or signatures made by other users' master identity keys on their own -devices. +Each user has a self-signing key pair that is used to sign their own devices, +and a user-signing key pair that is used to sign other users' signing keys. A +user's user-signing key is also signed by their own self-signing key. When one +user (e.g. Alice) verifies another user's (Bob's) identity, Alice will sign +Bob's self-signing key with her user-signing key. (This will mean that +verification methods will need to be modified to pass along the self-signing +identity key.) Alice's device will trust Bob's device if: + +- Alice's device is using a self-signing key that has signed her user-signing key, +- Alice's user-signing key has signed Bob's self-signing key, and +- Bob's self-signing key has signed Bob's device key. + +### Key security + +A user's private half of their user-signing key pair may be kept unlocked on a +device, but their self-signing key should not; the private half of the +self-signing key pair should only be stored encrypted, requiring a passphrase +to access. By keeping the user-signing key unlocked, Alice can verify Bob's +identity and distribute signatures to all her devices without needing to enter +a passphrase to decrypt the key. + +If a user's device is compromised, they can issue a new user-signing key, +signed by their self-signing key, rendering the old user-signing key useless. +If they are certain that the old user-signing key has not yet been used by an +attacker, then they may also reissue signatures made by the old user-signing +key by using the new user-signing key. Otherwise, they will need to re-verify +the other users. + +If a user's self-signing key is compromised, then the user will need to issue +both a new self-signing key and a new device-signing key. The user may sign +their new self-signing key with their old self-signing key, allowing other +users who have verified the old self-signing key to automatically trust the new +self-signing key if they wish to. Otherwise, the users will need to re-verify +each other. + +The private halves of the user-signing key pair and self-signing key pair may +be stored encrypted on the server (possibly along with the megolm key backup) +so that they may be retrieved by new devices. FIXME: explain how to do this + +### Signature distribution + +Currently, users will only be allowed to see signatures made by their own +self-signing or user-signing keys, or signatures made by other users' +self-signing keys about their own devices. This is done in order to preserve +the privacy of social connections. Future proposals may define mechanisms for +distributing signatures to other users in order to allow for other web-of-trust +use cases. ### API description -#### Possible API 1 +Public keys for the self-signing and user-signing keys are uploaded to the +servers using `/keys/device_signing/upload`. This endpoint requires [UI +Auth](https://matrix.org/docs/spec/client_server/r0.4.0.html#user-interactive-authentication-api). -Use the same API as MSC1680, but with additions. - -API to create new virtual device: - -`POST /devices/create` - -returns - -``` json -{ - "device_id": "ABCDEFG" -} -``` - -The server should not allow any client to use this device ID when logging in or -registering; if a client tries to log in using this device ID, then the server -must respond with an error. (FIXME: what error?) - -Send public key using `/keys/upload` as a normal device, but with a special -"algorithms" list: - -`POST /keys/upload` +`POST /keys/device_signing/upload` ``` json { - "device_keys": { + "self_signing_key": { + "user_id": "@alice:example.com", + "usage": ["self_signing"], + "keys": { + "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key", + } + }, + "user_signing_key": { "user_id": "@alice:example.com", - "device_id": "ABCDEFG", - "algorithms": ["m.master"], "keys": { - "ed25519:ABCDEFG": "base64+public+key" + "ed25519:base64+device+signing+public+key": "base64+device+signing+public+key", }, + "usage": ["user_signing"], "signatures": { "@alice:example.com": { - "ed25519:ABCDEFG": "base64+self+signature" + "ed25519:base64+self+signing+public+key": "base64+signature" } } } } ``` -(This may require changes in what `device_id`s are accepted by `/keys/upload`.) +In order to ensure that there will be no collisions in the `signatures` +property, the server must respond with an error (FIXME: what error?) if any of +the uploaded public keys match an existing device ID for the user. Similarly, +if a user attempts to log in specifying a device ID matching one of the signing +keys, the server must respond with an error (FIXME: what error?). -Attestations/revocations will be uploaded and retrieved as described in -MSC1680. Creating a new master key would involve revoking the old master key -by sending a signed revocation and deleting the device using `DELETE -/devices/{deviceId}`, and then creating a new master key. +If a user-signing key is uploaded, it must be signed by the current +self-signing key (or the self-signing key that is included in the request) -Private master key could be stored as part of the key backup (MSC1219), maybe -as a special room ID + session ID, or possibly in the `auth_data` for the -backup version (the latter would mean that changing the master key would -require creating a new backup version, which may be what users need to do -anyways). Or the private master key could be stored in account data, -e.g. `/user/{userId}/account_data/m.master.{deviceId}`. +If a previous self-signing key exists, then the new self-signing key must have +a `replaces` property whose value is the previous public self-signing key. +Otherwise the server must respond with an error (FIXME: what error?). The new +self-signing key may also be signed with the old self-signing key. -#### Possible API 2 +FIXME: document `usage` property -Treat master key separately from normal devices and adding special handling for -them. This might result in a nicer API, but make the implementation more -complicated. For example, the server could automatically add master key -signatures into a device's `signatures` field, rather than shipping the -attestations separately. +After uploading self-signing and user-signing keys, they will be included under +the `/keys/query` endpoint under the `self_signing_key` and `user_signing_key` +properties, respectively. The `user_signing_key` will only be included when a +user requests their own keys. -Send public key using `/keys/upload`, under the `master_key` property. -(Alternatively, could use a special endpoint, like `/keys/master/upload`.) - -`POST /keys/upload` +`POST /keys/query` ``` json { - "master_key": { - "user_id": "@alice:example.com", - "key_id": "ABCDEFG", - "algorithm": "ed25519", - "key": "base64+public+key", - "signatures": { - "@alice:example.com": { - "ed25519:ABCDEFG": "base64+self+signature" - } - } - } + "device_keys": { + "@alice:example.com": [] + }, + "token": "string" } ``` -The key ID must be unique within the scope of a given user, and must not match -any device ID. This is required so that there will be no collisions in the -`signatures` property. - -(FIXME: how do we make sure that the key ID doesn't collide with an existing -device ID? Just send an error and let the client retry?) - -The server should not allow any client to use the key ID as their device ID -when logging in or registering; if a client tries to log in using this device -ID, then the server must respond with an error. (FIXME: what error?) - -Uploading a new master key should invalidate any previous master key. - -After uploading a master key, it will be included under the `/keys/query` -endpoint under the `master_key` property. - -`GET /keys/query` +response: ``` json { "failures": {}, - "master_key": { + "device_keys": { + "@alice:example.com": { + // ... + } + }, + "self_signing_key": { "@alice:example.com": { "user_id": "@alice:example.com", - "key_id": "ABCDEFG", - "algorithm": "ed25519", - "key": "base64+public+key", - "signatures": { - "@alice:example.com": { - "ed25519:ABCDEFG": "base64+self+signature" - } + "usage": ["self_signing"], + "keys": { + "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key" } } } } ``` -Signatures can be uploaded using `/keys/upload`, under the `signatures` -property. (Alternatively, could use a special endpoint, like -`/keys/signatures/upload`.) +Signatures of keys can be uploaded using `/keys/signatures/upload`. -For example, Alice signs one of her devices (HIJKLMN), and Bob's master key. +For example, Alice signs one of her devices (HIJKLMN), and Bob's self-signing key. -`POST /keys/upload` +`POST /keys/signatures/upload` ``` json { - "signatures": { - "@alice:example.com": { - "HIJKLMN": { - "user_id": "@alice:example.com", - "device_id": "HIJKLMN", - "algorithms": [ - "m.olm.curve25519-aes-sha256", - "m.megolm.v1.aes-sha" - ], - "keys": { - "curve25519:HIJKLMN": "base64+curve25519+key", - "ed25519:HIJKLMN": "base64+ed25519+key" - }, - "signatures": { - "@alice:example.com": { - "ed25519:ABCDEFG": "base64+signature+of+HIJKLMN" - } + "@alice:example.com": { + "HIJKLMN": { + "user_id": "@alice:example.com", + "device_id": "HIJKLMN", + "algorithms": [ + "m.olm.curve25519-aes-sha256", + "m.megolm.v1.aes-sha" + ], + "keys": { + "curve25519:HIJKLMN": "base64+curve25519+key", + "ed25519:HIJKLMN": "base64+ed25519+key" + }, + "signatures": { + "@alice:example.com": { + "ed25519:base64+user+signing+public+key": "base64+signature+of+HIJKLMN" } } - }, - "@bob:example.com": { - "OPQRSTU": { - "user_id": "@bob:example.com", - "key_id": "OPQRSTU", - "algorithm": "ed25519", - "key": "base64+ed25519+key", - "signatures": { - "@alice:example.com": { - "ed25519:ABCDEFG": "base64+signature+of+OPQRSTU" - } + } + }, + "@bob:example.com": { + "bobs+base64+self+signing+public+key": { + "user_id": "@bob:example.com", + "keys": { + "ed25519:bobs+base64+self+signing+public+key": "bobs+base64+self+signing+public+key" + }, + "usage": ["self_signing"], + "signatures": { + "@alice:example.com": { + "ed25519:base64+user+signing+public+key": "base64+signature+of+bobs+self+signing+key" } } } @@ -228,7 +201,18 @@ After Alice uploads a signature for her own devices, her signature will be included in the results of the `/keys/query` request when *anyone* requests her keys: -`GET /keys/query` +`POST /keys/query` + +``` json +{ + "device_keys": { + "@alice:example.com": [] + }, + "token": "string" +} +``` + +response: ``` json { @@ -249,7 +233,7 @@ keys: "signatures": { "@alice:example.com": { "ed25519:HIJKLMN": "base64+self+signature", - "ed25519:ABCDEFG": "base64+signature+of+HIJKLMN" + "ed25519:base64+user+signing+public+key": "base64+signature+of+HIJKLMN" } }, "unsigned": { @@ -258,41 +242,40 @@ keys: } } }, - "master_keys": { - "@alice:example.com": { - "user_id": "@alice:example.com", - "key_id": "ABCDEFG", - "algorithm": "ed25519", - "key": "base64+public+key", - "signatures": { - "@alice:example.com": { - "ed25519:ABCDEFG": "base64+self+signature" - } - } + "self_signing_key": { + "user_id": "@alice:example.com", + "usage": ["self_signing"], + "keys": { + "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key", } } } ``` -After Alice uploads a signature for Bob's master key, her signature will be -included in the results of the `/keys/query` request when Alice requests Bob's -key: +After Alice uploads a signature for Bob's user-signing key, her signature will +be included in the results of the `/keys/query` request when Alice requests +Bob's key: `GET /keys/query` ``` json { "failures": {}, - "master_key": { + "device_keys": { + "@bob:example.com": { + // ... + } + }, + "self_signing_key": { "@bob:example.com": { "user_id": "@bob:example.com", - "key_id": "OPQRSTU", - "algorithm": "ed25519", - "key": "base64+ed25519+key", + "keys": { + "ed25519:bobs+base64+self+signing+public+key": "bobs+base64+self+signing+public+key" + }, + "usage": ["self_signing"], "signatures": { "@alice:example.com": { - "ed25519:OPQRSTU": "base64+self+signature+OPQRSTU", - "ed25519:ABCDEFG": "base64+signature+of+OPQRSTU" + "ed25519:base64+user+signing+public+key": "base64+signature+of+bobs+self+signing+key" } } } @@ -300,6 +283,8 @@ key: } ``` +FIXME: s2s stuff + ## Comparison with MSC1680 MSC1680 suffers from the fact that the attestation graph may be arbitrarily @@ -323,8 +308,8 @@ look like: If Bob replaces his Dynabook without re-verifying with Alice, this will split the graph and Alice will not be able to verify Bob's other devices. In -contrast, in this proposal, Alice and Bob's master keys directly sign each -other, and the attestation graph would look like: +contrast, in this proposal, Alice and Bob sign each other's self-signing key +with their user-signing keys, and the attestation graph would look like: ![](images/1756-graph2.dot.png) @@ -337,12 +322,16 @@ devices, as there may be stale attestations and revocations lingering around. (This also relates to the question of whether a revocation should only revoke the signature created previously by the device making the attestation, or whether it should be a statement that the device should not be trusted at all.) -In contrast, with this proposal, there is a clear way to rebuild the -attestation graph: create a new master identity key, and re-verify all devices -with it. +In contrast, with this proposal, if a device is stolen, then only the +user-signing key must be re-issued. ## Security considerations +This proposal relies on servers to communicate when self-signing or +user-signing keys are deleted and replaced. An attacker who is able to both +steal a user's device and control their homeserver could prevent that device +from being marked as untrusted. + ## Conclusion This proposal presents an alternative cross-signing mechanism to MSC1680. diff --git a/proposals/images/1756-graph2.dot b/proposals/images/1756-graph2.dot index 6a45abe9..8eaa1df8 100644 --- a/proposals/images/1756-graph2.dot +++ b/proposals/images/1756-graph2.dot @@ -1,13 +1,18 @@ -graph { +digraph { A1 [label="A's PDP-11"] -AM [label="A's master key"] A2 [label="A's Osborne 2"] +AS [label="A's self-signing key"] +AU [label="A's user-signing key"] +BU [label="B's user-signing key"] +BS [label="B's self-signing key"] B1 [label="B's Dynabook"] -BM [label="B's master key"] B2 [label="B's VAX"] -A1 -- AM -AM -- A2 -AM -- BM -B1 -- BM -BM -- B2 -} \ No newline at end of file +AS -> A1 +AS -> A2 +AS -> AU +AU -> BS +BS -> BU +BU -> AS +BS -> B1 +BS -> B2 +} diff --git a/proposals/images/1756-graph2.dot.png b/proposals/images/1756-graph2.dot.png index 93379122bb948f3edecd2133c7f34972589825e1..3af9270f094b2361858930630945b05e40b2c6fd 100644 GIT binary patch literal 49417 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 GQu0Bt7+ePe1W@TYUs^3Vjg81* z8tl;3tJ}#`DDJDvpYw8O;g&rOvLgG)@bL3z&oasfZ6?zX_qgob!$>7a?pD)8!F{KH|D93at$DLsESi>sM(l#H8voT!pI^+ZSj9Nc!WRh z{7z#;j|H)g`jlu`>O{N5?C{xl1qSZ92}p||B@hF9s?d4-@ueSsLZDuedP1bKBA3Y) z1ZU?(gBj1TFs;5Nkqde*%+ApI_;3i`{E}K^E<{s@-cf@s(@H9XwvLWVDl$I1>KYsG z96WgNpOw_C9W-(E+BHM-<}_o}Z`-wTAS4C|WiFMpJt9*e7Qm~7$m6ujH3~4RWa6jt zZ>$Y4TyzJA`zJLc08{tjLx^sPd(7yaca&zcV~uG=NYN4-89pNb!}ErNjPzc>h2m~A z7DdYogUDO)Ccnh#m!CsZSf(01;GRt&Np@)1< zUfyQNWf%&9kF9j=+D?Mik!mploOF*mN}GS#pPE8OKTt=>`24A8aVlb^?!PR4Ruu0z zB0^8R^RG3Cn{<&~h0;Hv`6PX68^RSEq012A1>;lHcv!`+x<-fx2CJ2+W+c}5E0Lu_T&5g&8&DN5G@(W&ga|l$6*qEc0pN_o zlQ9A@CplR>Oq-FmgSZU?GSaA%zHVz%bDBSp1biYXBAO849?EUZ`JMNax6yWyd5~zl zABK&=e<%U(+wxX$EPehw7%!8E0Ckms5jyit$7=cjhrA#edxG|0LP6la8^bdF?2RM1 zDJ}}6PC5Ua{pX4Ew!huk7r$t;l0u*XqWb-XdzE@#fJR!j7sU{Ri&6)V-9Ioe8*?Wm zCpY_^Pf)&52a4NrbHgr-{qn#mF(PjsS#q|U_zk^L6DJS$9NzU4jq$88UG)SQ(HUSX z5)gM#oyv*W4QZ%e&_i~|d;&(@bAh${pc_##NC$>Wyd#J9LD{Ji02LXJg}tP86O#s2 z%K|zjaW)epBZC{U$OeE$S75Ph!E&5ZQwztg&P2?jfmwT^h5sP?JB$`*p{79@sD?Py zs-fpm1qo&0iIk8jHo*{@kcfbQHB^tIVad}7*_+yvi!i6YF9)+8*GTc$- z$+RDr5!o%^G-F@RsWBC>dCKJ2~)YUG1|L$EG8M(k< z5GrUn#$m||v=G4h_ZAix6;<$WlAs_XvH1>z90=$TAo?pg>ax$VEx%q%q2T^q>{zLW z{OpoywB5|gs)oe%@CHJB4G?D~L@de31@r{uSi5SsCmXO)&X7_Q)%l2AON_jJeyw6RRs)yqMF3tYCiIi(0w7l(|z z28qmofeK@ZD)0mrEN!3Oc!76xf;G*-^(Ao$5EVnbj>5Sw##9_AE!k`7eHuO)FF8;| zogvT=?;Arzf(mbo6Y2XDw-!ylAhGB=q#_Z7^noRS>uYkDU{Z zVx(H)XBdgX2raxXg3QU78o|2U5z9&x=M*0bh_X3!>+UFX3GbDn>?|$%Czv ztnX|!P4GwTsl(XO46B6H#l{j4LLj4+4jkotrai6kTEQyR@Q%pq9iX?iRek+>Z>HhS zQK5S$NWv9LAbybs&=PE&L==Ia@QSoh42g(F^Kf&I>+a$9>EU6xfgZsU!TdP0^8LX4m=7`i$u}^V@@T=^8rm9Id+U5I>WrET^Yu4 z^WgyI*>LmnuEj(R(oN(!U_0YgCL}J;M_q&TtS|}xK}iEZ!#kPCWCBj!I(GKA{srX? zY}}~8(2T(U!bjDQ?MK@rA!CwyqY*`(^y9~mWHR#E*(aTSeYB|e$i!59{K$lvC>oP@ zCKY1vG;qeqkOVSXN`S)fj>ubHMkJ@+vZ+}s;@z-pu;0#TX!IA_E3vaNjMQ*dB9>3y z$ANvj8SiL7-y*aLJAy=c5adGgX=m0e#plT5k}YK0%dkAx%|ez0w?hJo$Sad=d@4?Q z1MrA@l=OBoc8LB>CJ?|d$Uru}IAXQraD(cTI2XPHZ58cr{V#aQ0pe98j*br}=}lGD z02Lwf;zYdLgq4-mJ0;$g!3bN>KG3OXtYR(R(W-Lg%9f7g+wxd)1R9T``I0nte0&(F zA$hX}UM{W$-dwvQ+T7#Xq{wY}$q=AgT2%>=jQ2y?FnIa+8WPueQOGg;VC5OQ6vm-R7J1nr|LAd9v7ATzdE{9BYXQgZf#aU~kU; zeCp!tsX07^ckir|q8gcUX>g5i`>igHzx%B-y{O2iNn_w@Bo~HnM_g1)+coS>MZGlo zD=0ifOf4P;|4H4NOBA1cI|BoA_D}?)gtVlY0PQa^EBux4=Ho{-)E7E4x6fLG1gGMq zcO$VoxOMTd+PZZsaFrE$5;#d?=RlIrBp~X8v*Op^>$2kyfPneZJaIM&&B-s3m*VbS zQSD2jks<$oP~7uhQ>IVfj0TI9vE(-|{M(4G1_m}WX1s7P>F~Y$CnSPI+HjxHz2C&P z&bQKD{|vdO#1?0E?O=I9WoYMjW7vy{T`+c@T9BVFu?SgOJB4wZ1-u2Z6MeAI z??vCUo1s27Y>$G=f|reNlD%4D1Sn9&Bm^Vs*1g^Ir-R=8${pE~Lx@Wp6L5W9XssWM zu4l5t)74+Ta%JgF_IbC9TNPBB0wQAUA$HTYB09cq-i-!7Pkfs}EhfQW z)20n?=hZkS@MnZcbpM~fvZnw2xA8)ca@U4GmjAo;qek*~lM44$@kM@*t_`u}b}ow# zGqD5hUhG9`$N338Hhf%Rem8I2dmHlRYYpdEkZg>gpgNEM;37wIuYbPNntksMqR&c0 zUyTf_N9zc9Ql$!1R%BP&_;hYfjEe|xTlxfAnTC0i_vT}+G;W2aYQ2`%I%|^1wjO-c zg3HGvlozXM{>Hq0lUJxFwN;+z`lzY%b9$Cu?7i-7%zOiH@aCy)F?!#youfv$T$1XM zvuC$fV>G&!hK}t%1O*3wwXLsrtnm>3n!F1PN2}jzxwXj6*zyBEuDfFL%9WD2=sf`<}r01={->ElF_k9=L zlx=SfbR8GhT!>|uXXkSUy+hb>dx zF#hlXuAtNZu0UI^=+FP=E#^|kL$gcO%W;}}RKvW=)11Kn&KeDekJuFo;i@&83gitp z&bqBl^=}Suxpm`3{*J=5Q|qR^?$-QA(fwNLzlv2ee>3dQ{b*71q2^&4lvp0YW*f4R zcOmQx;`e*2)qF|B2cQLC?Ns!ZN;W(m8DozPfo6Wymk@}%mBpwvnl#ECF3n0$5bMm3kFTF0?7@96(^XI5{lxig^?O#PJkb-aiK_Oh)9&iM0) z3N!=5OoiYLT{!Blu6gxEZBTEkZ{gKV^WiFaEESxshyl?;5*UaHpa^?8XCP!Yt~kHqW^|otKXkIPBPtthA~;qP~Zb6 zS$IuqY+kj#<|dV~B+DKQEG}IcPq}Af6S)4Ao=<7B|J+;4Y28WLO0w`4z84;-&ScAa zg$-&}3{F#$!CLRzrQsCD00?wGm$DKDQs!|Cb&WPKw_a3p89PQCeKgW^*AoIjpXV1$ zXI}%1DhSj6-w#-~cimw$?r%0Op>vGa+~G_t^xy>;@V^%#2dKoj;mA{b5?J!riH-G+ z^#9+>{ovWNmWY2}uWrCIqd*q}?J_5yzyp8Y1shYp^$3t-6GPgdDbI`HekRuZ5m)Cr zMrqt?@uySIVh%DdXsXf6y5>(g%{-~Ga-B5v-6RZwb&j@)2$n>*zkCAy4-H<(9&m`G zm1QWZ7BdcAijZ`SmHfv({rlId==zTMuobJ}>DAf3jDRvo4!oARhJzyXfE>r*O$twb znqSOi*Wrwiosj=iKetUox%xVM!w)EjTSE^>Oc<~MixGBBYKuS9H@?SSxw89vET$bC z5KwM&ot=kTSR~GV%o((`;DCF?!5Qhlm3=7{r0Q^Vti5K{~wyk-|>pv5o(=l&FkEN!tiU|<%qIk!>KjVb8cYSPeR8qRsf z$=}!QAZKdSoL|2dJbU5(tgoUdt}dW4{WXOj?cs{(b67ShK|>ELTX)X;O#ko;7q)*m z$r1KSr(LWROXj0#&qXTzvwH(`wkGZ8a4DWtT~a$R!h-XJevMb?$>xi6wA>KZPBLC zsw`QznSz;u6VwFv)-{iBJimcCsC6Ia&(4ojdcoyzbl}X4C^!P$l9-DzOD zxnw3vByeRse=$ALV}*o-AOom}KhUs|ufF5?KYLo3r)(Qwp%Rve!oG#VQP1>BT{{ZZ z)?ott5Jv@>h>j|{;RIu~V{WJzq7{!@vmk7BW8{&AO!U4Y_jpvQKR6K#-s4 z1;o-BZwXWzpnOLwFSed}y_N>xT>^XnXnwX+Ewp02kbGuJ0x+HFc8pN`Ce9lRwhF|Y zAJ30_hbxc^GX7{eJo3sF{Z5^ZTWEn*TG3rfZ>*_G!>|F7`c2>^VNrl-%6@3fkaL_NA$qWIX zBfQfbM~%Q;6n1zIB`#NM;EHZ;g@uconqQrOl)0==hnoJBfDwPTAHnbmN+YwSwu(x| zU#gZaH9H><&H<~7gn5BGxL|C`yVFptdK@rM&&{-2wyZaht$F3!8;0Ws^$@5JWpT(Qpr7^e^AopHc6MW*L8>*sm%>J( zaGUp-anG%b#SDr+F&e>vgqXND2gTvG3X%n1@DlSL?g_WOKCU;lELbRfcwl9rU;IAan0qAcpaQgH=!riBN!2zj>O{gii!NY`WII(>h+vxVk@CO_XrN(MdvHZ zbqI)i2TWRvp%e|t$@6(9n5wb4#f+6oXu9ga_#eD`8o*NPGc z$-&Fa+}WVadoSk%QUbW5jfe*A(IM?IUTy$dRbjYQO;NKnKw*$sSSQ-1+`wMlAt#IkM$DXf?x8W-{B44N z3-jmT;NbsVa&LfwUK8Jan`~w3(OMfVGYa}~K9Hl>G5DEVe$2_3Nm6(Z;muDchfnpBeP^UT^HPz5xM^7DWQe7Y%T37WDh) ztzVFON${+K?c2tAv`m;8mO%Oyn7+;2tpIz82-(x~*NA?!g4!zD6 zFSh=?U8hQ%+=lKQo1zVb4!S5@Ae+N$sxOaV%at@x)y<*V?}3;-plc+*5tG_TJ@OF#;j z&~b7e)bm~ct3&h6)jE3HXbtGEH?bl9SK%NiYF-u;B;|+*qNq1h7<@!DDp_A*F(nhJ zH2T^BoiwdPnMDH;)(p^Co+E^0tlbkKix@AVkSQ?y%MSmz<2jKtDU1*RIPm>RLtN=! z0P<*$UPc^P2+A+4`Mv_c&t;Vms}S2#;!K&bst(myNm-eMszjw0-T%5ebM2y`mX?xD zLjZ{|9E#E(YY$@G0%^h^CiIqD#ZfPm))Vq>>&Z2i621g{_ua5>n2 z)~$NY3DeV>eTIsyj*7~+f5(gsAc??4>`k+`7@eG$!l}OpoK{?%TZ~o!NvnANEJo5-2z_|$ezr69(>+j~vwuf!Djr|2(TU@-@{UkM(}{QlhiSsYh@X~}TeW}FL*ih2 zWUNVBj}oP|G7E>9Iusk9zFeT@mKw_1TA`5B=<%sZIfk*vlR%%v!72xbLLMDJGKL7A zqh1mM(obnUr=`Jr@t_YG7f`&XxViG)8ZG!h}2~7WJfPJ051L)vXMcT?S^JNeQJfz z76IVH0v*j>6*Ya?$y2B35UY|ATrGjkMs~Ngc>LAKMY)693|yfm^#cqB?dkoxnyHDV7$En&v7KVwyXGww!N>H_zv*J)Tn4@@Aj zgQlXW7{Wl3t~rXboz26}XMS~Nyp>x1^T+>}6j>5o{z#F(JO59LOnt3Th`NA7_1&bb zO?{l7_u-v)8&K}6*05&p#be0kcgL;e2#52dYZri_L%W(A1g*%bRf5LQo_9fFFNbYw zvvJ6;io;1xE6OW4aq;?y=Qz|5;iT)q_k-Dd`WLVNsK0+o*ts$&#-QqG0~&B-k+YO)?D(1W>z(=1P*ss!d2l$I(4o z0l;x`#@fzs3qX53%)vpALn5Yp_^@ux%L{wUPQ$6YJbH9L&ABr_V13r30nor>8a!il zeZ&C+h*lVEq4{Zn3^x2LB9H*Yf}!J(_$sBB_U z)k-*2Ib-`T3ll-mgv^>tcV+W%EES&cKu9{b7mqh~*s^?;JExB#;n}m83A(I-YDMmk zz79<0K#-eLw5GW5GjyN*7MU}qePHL#o!(ev%TBtG^SnjJz2hoam+yted=Ouwy4}gFR5k`Sa&r z#%hM!*A(PH@9Lb-Rq67GWc!vv2_yS$x4!_Y&+pbwqNPg+txXt=Kg(R982qy|)9_#{ z&f{xU?@uGj7D@%58)B=g&ZlFOzQboh0AX?;MydS^C87-66faGSa0&Ek<>Yv&b?&pk2dygK)HWQ}&+4iS6 z7H~#5)~WTQ!fweg{NJwU&;*%yDK<9OA!BlmR9j zT++XU++a0rlChi=(XTX;^LCCn+O22LKv8B)nmqZ7ow`h&I(O}hYOMqX2tfaS$ofS| zLa#cN;yadUG{I@k|F7b6AumhLm9q0mQzlQoh_evWd2f72G*nv99J99kESnaBuB=u9 zck5q@ps5t8;yD>v`((l|A^R})=+ z>;EsrJ~MHJL!tkki!NPmwU^fp%j1e z$vrg~-S69X@9v5r7JVc7!xGbksr`S3kO*FNpKI4>(quhw2Dr>uyKNb4;=Uv*f(A&@ zX1v{9Awm=m3r;A&Kf(VH&!Z#gdFMO{;YVHCR-EpX{Pd~BK(^Ajv?vvJ&7P-6XJXEg zy(Yk~GjJ+L4_KZBUL<9+A*kOHxTm62xj}n61Vt~^LZ#v6LAvnmICTot$Ms^61K(0% zkBbYPU;Ox}9!-gnK6L1iQnPvywlV8@8O&fsS{T|MGVSS;zVVK{wIDXGgS|o|rIu#c zn;xBwfE=C&+P%lB_#xn=pL>PH+JeoWO#{MC`YfvXdaRL68CTjiZCZmh`_7#2G@x@q8n`4>*Xm$CF_Dj_vq~0ogicycF^Z) zrH{<#0xYXQ4=VFl-8MQZ4lff9<^eB=B%4xQzogJCpgTQx{kpcyEx`d-om&DmC}=AB zUyW9+R3#i|WcwpeDUxL+k(Xdgh7)z37*Hwlh=3n(L_J79z+bBKyVD@k0J;{FPlSDT zd-2k{Yp^Ye5Cqt?&721zX260J{`~4q$a?g2bs;eO9BtHc0K^D%Q?zd2-o5o<1w{%& zs)HF)I*`>0!<}m8)%C#x@~)*JBX3UZ6~#-f`@`)>iW8S6${)0oT%G89BsT=w=V6}m}7e+~z9lDvX+jG#0DXsb3isK?7Mlb%4u1$#udz%DC6MaYwcsgAYn zRS!g1X2k+*G2YXP(JS=a3I)3))5n^$s7-v&s)*gyoE9H~DE2n}&0MWf5)yLL`EGK* z_beX?wSfu*@JK-SL%M^cn-S-GC?ev4+cO_|D|g z-hc_;aw|O2mYR^%yB27A$?beSSddjeI1=s+_dx~F>p2R8c_)lu~WyGB7G z;egd4PDeF#1ng&Dx{D5nmy$6ri|J-+j6MnlBcx+I7ypg~nnl`y-t7L@)4B>Y1_3om ziHlqRE}!}I@na>02)$muOekxzl~+%sKTDQ`z0mJvUtNg*^_BQ;>PZP|7oENJ^y#g^ zy|Ww+fBCfzG1anwzU=gKF-eGBDLi0S?t|mIKYur zODvtB_0=veb25~drWy6>^pBX=IwJ76cW+Ar)f)D|A2bMUKn=gJ1{z4_ zv7)~p(c54$ayN=uSc_CUzLQk<@LYm#!k@!v&>2R@fB5iWV7Dax4)t)h6IwZe%|XsY zI73PyFZF>L9rV7K2}1DZ1jsf2M@jZNRtO!9W9_AyXtAYG-r#P@AkvEzTYqwq3V*>x!l<7J5dV{U#k&eAVp6xA$P2W&qVb7Ih!Bo9k6WQ(rkL18Qe> z9>Gt}8P1H`g60w4CE?B;zv87=C9t?!DocYYDds)mDkLJyBY5bB?2{#S%%AFf39S@0 z#$x}@8cT;@2SFbzBQfQ&K8rY$00*goGU^~Vi0R9&I%rCU2|TroUPg*0(m@UaNfD+l z0~NK5sD9tuy$R}!xGfKFZ-2CLN-87M$eX&hW zq;^)uzIq$j4qZik_zfQhmLU)-&ZxJ&njO`*5}ftYrMy_RUZ!ZoTkF;OuNFWcVdPvg zOIuQ;*i9T{ga!aW?4TG?rFePW@GR=G@*y{sjEex;s>W>N38-E#;5vd-{A$-q@8_oK z<8mAs=0pe3?fQt48kAdl1@gRnK%FE%pKoO0GOO3fk*Q|QR257Pu7ix(g17m_y5DPL zh>7f-*qU++E$5r7bge#0tPuW8dY@=!U9L5odL)4;IWn1Rv$|e^q$2YehSfY!`|&?L z{ghk6b8*tEpav5g$&A&79hlA8u46|(ZpNyG)%*E2q*o`7+i%!z-ChhSk{u>tYd`=b zxV>H7z^|&ZgEvxhiO3_lS9o+bUO5n@%#`%U+@p=Z+^soMg?Xg@c+f>lAFsLO_sEoq zpeZwMz$G9{O(|<07~k2Vb6`tI6^Ty>0VeD3lI@9Q8rU{AwEea~uPxO#FB!9llBc=X zj?*VjBp?T5Fnz$fpC4vcWaa+4)3ED%Xmht!wYH&%&+t*+%g_ycTX~dUw>fz@YkU|C zLt-z&cD?Jvs`sQfs62t)T*r9rNI^da=EAd)A=j_TbSv;{c2xIY6WkGmFo#{g9$GxU zDhO0X4m;lHYhO~jN~wqLZ!xqAYxKyJnr}?5iO`5t<06tw=Tl=Gh6WHE?l5(nVEn1A zOMw0Kwp-We9F~55`DPBUkIef+?@?*EHBhJKph>=e-xudhu2bMz{ej~uHg-B-O8+4< zr-_2p!<1PKy7!-}<~MEY_eAfV2e*>M-*HI0TCJpv-@X%C4mACXoysb;iw;9!k7ZRz zGefej9PfNzFu4lDB@bH~gqh0U^sZC$H!-`a*72SQeNM|I(>(^!S}QzpwTZkIEZb&W z*ugX_CjwZh1mrcd+jR5cn>mg>4@I`3Dm3FvB+#{srdlulym}D<-{8B~;k*)kv)en3 z!nP>`l*cWSzomOv#3DF9%^aUa)i0F#+&5ufLsx{Cu;fMZODnR!_2gY0 zgC|@ITOGbc?txE_nx8~fI@+YppaqK-iCKsy@;`4fyNm52k?zIb(o01#3Mb4>%8%Wp zG_qgiG*n|8$$U3`x0>HW`ECUC8<(oJ!!c2WbhE(3zzdStiRVsDS8_Zt+WwnpuBli} zcOGmUQ~d~j3YUiLL{n9BRKGJPppjYasx`_LUCc*_l(8BWS%e*0#Ak)qlg~PGkjPgh zg|BiQvaS+&Ek!fo!5lA1^rrf|fc~{=h}I#FQg$T&M`!N*42w-p<5aolHqWxYJc_`z zyV0hBCV@tj*lik5LVy{I6J_dWs1jCsNbAXuQc^Y*f3a@Fega%L%%o1hyBo=Ah|9V9 z?Mc<|bJgCD9!yV8aO*Onkf>#ubp?S+7xRoa4@8`X>$+v)j=_WX{n%1kE!N6N18#VC zzS&=ap`AbG=lfFTN}GtnS5^XSWN!ZU!QnO!*YRyL+R93^9&(uf$t|s_tV};2KElu3 z?00x_+TFZrtc5 z&2PQRt_e5ZMRQO%Ot#C^bX<%HU=Xm5wu_X;p>TS$ndP@;sBBzS<2VrL5x_(dN_me! z$l&iV^x&Fr(_E&G1IFfyE=8;AMfr?U;WKyiaB^CWz9(=kZ>tLSGl|i)OF6U{SV%o_ zQ(WcS{o?qRcaDnHZI$IV$EJJI4=kv3U;O0~l;S2nJhYzHnpQmU+-@_jX-UHmRgQ(k zbRe^+iejlIgtIHcuMN|~U7E`JCpbQnIGSC_}AW^2~b}%5w zOkZO1s#{n3JV3ryG7UX==*pUhN*rwq)~O6={bHBmsmUmM0OHq#ol+$v;EF#_!cw)t zrq%XK;DC}kAhVM`PCRaJCVK-H4TliSFHwUPKNHo??d!S{pU_n>b zChX^}3BDF%-F6;i(u|m>syB?nPOJ(5q;Q*(-^u(wRA#awfJOkKGtP%wJi?Ry?O`H3 z%}Q=Yvk4lnT>Dp&?AMy3v`)?1YTvzkL%7rGs_f=zRPi#$9hbeNbRcDrjYZ=AWhCyG zdSj{ns}Tw6<($FCSeB$gOthb5`Sw?G+Kjc8PWyf=toOg)OHF0*t@dyfpAEZK{eB+% zHTuM<-|JqUBYJVSnaVS>?V~jt+w{B9U1R9Tlrx&;)g9Nec^$`@b5{NSRbo_La7*9t-uaM%yd`b1W0fB$S1W%IQcmo` zcd)3Jd*>XGxh^1rnZ->zyp6z|cQ`ZMg<6}V_}HBdd)8eoe>;bj<=B772dk;$6MCwB z-Gh#+ObFJNigRgU(@NC$*<|+OH@fV8#Z*^=nQ2pN zjWhaGS#UoMwZsK@seV2S+uqrs?B}p2>BzbEbp*5nL0~_8Sh$qtRVY82)Fq5Zl<+YK zmL%BV05flulU@Ek7B!7rC&ymX_E!$lX@pecp{eDlIlKA~$%7e!2C@?jjljc*|@Ea(z8tdq&$JlN|N)s8o<%{_%`~o7*k6rP}9A*9F2;2H>gMF>; zmG;Amki;U-+G5;cXY#*gwM{e+rX>-gL9*lTg#JTt(5Ld)`neE2A%)*pgE`*u-@Fx`p3GG zARv?aWRUqEbp~e-snfnIg46Ebm&_j{3{8{^C;s~Tt=XS%MxU)Xm{uIw;t^a~(;nAL zcZ@iFknz%Bwb2ek0e>X92H3<)K5z#;1T%Wbs#8R}o7~_$d-m(mNMNs5pdjM$Jaqc> zW`0I$NT!fXoK{5gq(WhPeRVS|QZ7+hTA9nv*W6J)S4Kw@zjmU~M(1ABH__3uAM+jKO0^@FP_0T@eA}L=8X-qyw_6Z~u?K%;n{qk$2fRvJk&~R9at50Yb zdo90JYOC|^TF^WgF{Li2(04uhBm9{VQZ}<4N3BaJ_g>vh39!gWE#S8f1;v>5qPh_Ek^^e!II<@b$P+ zQ>FyBK6J>$(nlT{MDhIi9(5Ws08tg7PDZW3(@;k%e*Df=IqDvN*wFOh}ZGhuME-$DK=p0^Q?F+*R`% z1#kwbsA(yvjkU0j?}RKu;ydX81Afr6BjZ^%!M(rt`@4eEap=5o>wLqV&MAxmRSo}o zK9)dDQ~m9SsF^qoMbEu0**)OCSV$NH3#>|wHh(kz6*E*m;3|yF9JiO94FXAgDF4+$ zo*Fy({p$5R1EHToXH2|T@HOo4VQ+*=gRdCQ{v$GkLH82%V~Z9N8c4^Cwlr^TQ=Jy< z7k`{WoW0yaA-%S4Z3ylt+C{N!%&L0fDZ9j6pHUqX>27Qwb~%c1P{=3_?iL^M)}Uod z;lZ<|fK8{{v~G#}Vh@C&1Ij`Wy6&Ao8s^Tu8)^FM>o}x&kpq=SSats%+MzYIVs<0? z_G;@&5~|pUpi?n1d%u4_m^@l;{;5+F-$qV(hWuJ<(8``u>NnYZTg*5J;HkO=Bmz(L z`2H!Xg57N5lVwLSqFy_rhSGMY(6v1}oZHR6(4wn|$L4hV3y{8`5uRl0^*_hI-`ChJY^bwy{*2iE zm83@vBl?Q&bkdGy`4fo^2|Z~8J?0IR^=IG)?i70^0O(Tfo(^T@4NI;Qql% zxir_Qj-< z56VQ)9^(VH<|MoX!A~VuQV-R9N4CEW#Y2Nj~+PuC{EFB87v_rcoU&fa^t1oAR5^ zN;+5|_a#63)MZ!UoAP)UpMA_HAXfCjH6#xn6B!^(!}!B-n>Pf)rSqPf8wQJM9PkYv zRra)f)Wi`TCUw?adiGfP7_m_@0e(bktIXbH)C>4ed15_sqqCNFI^#VO^&xi4G>V~v#Pfe

uud2Zki_XHF3;Vjc|~P{e<3QC2?#Q= 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 ZwJnJord#H|})G(jZw8Nr)7(Bcl`{%HCve z+4u7}&+B_X?)!)PKe(^!JRhA8eY`*KaU8GLbG_bvnx|FhHnVM}P$+b2s!C@l6e=71 zdubyze!};*U>bi=8=h2AqO6kt$K)r5QYhRMH6?{}4$p^M9d*ymtjLWwz8!ltyqKD? zw_?xB45975PaM7l)aw?!)Z2F`Ir?$yo9Q;Qe-8vi#X z1q`C1v}5gDZv8GCObY9~i>s$5q!_HFY-Pt-<@)WuKez3(bx=~e!s2bPO)K$X-d^%w z3qf8|C87aqjCj6_(#92?>(O)P9U2&rx3v{#V`u+SU%$>z>F&aUQ@A4=8=Li*>4ts# z_Pt6@wq0$gufON*O`ZAveQa_vjh>#~z@I;Kzs;_&D5s}07OI$-2%b23QcG7iAR|NU z+{KG=uU~KO?d^S)n%cM0`s>%mSWVyA*;$3;9Zf31O$JBhb-4OF=OOEGJ`_Y)UZy9&( z+BGmdypAHYcW+5W#m2I-GSNeaywcMT?08Y@{`1EVkJMBVy*jTD#v5bL%fv?`YQ96En{p;7SHP4^-x_^H&K48fj zrxN6ylCp1jc=(E$8O??b8>EgNE&2A1PFY!5aXeLwmQOH}??~zTHB_~=wZY-xn`EPd zo;3!A+LtF=9z{hx zsI5ICw0n2)j~`5ymX(_-om6RA*SnL}+ z!|MGf($LF6Pmkktl(ZjiA1@CNxx*JOT-ZoYuWW5CnxGy*jfd^{G)eQwkt5cBYIpJP z+QqYb_ok$zq=A6}cYlAHYu}&y{eEXsQeJMN&81@)CirWdVdBe|6H5z2X;MzpLMuyC z8hG-2{QT>1HXAo>@(2wL%{#eMOXt!ho0TQU=Z9^nZES3WgoNC^yw<;X@nZZF&HgKc z^+}p%4E6V42{SNs@Xl7^Rk(Rm?Dp_4kL+xzSFd05N=dQ&`0>N{{{6cJ1qFF0T?~7A zdOCV4L#1rG)`?us+h}WRE3EyNa`LIj)$$*#$}8R%eV;yh#4RDg>^wWHfUCdj>6w20 z2X9rB4EwWZ&n892{Or5S=!V<))aQZa; z%1qjw&i?2--hO_Yo`~q(_oCU65o%U)4x7qtb=iJtyo7EuzRzA^Pjs+0#-mJ0f<-`U@sr1Pg)4X$YkCL5*yI^c$V)Wz1ap9uvRoOP(Z*BWex8&Gv znCh=CzhsraUPD7;X=QP2QY6qIYNH}&*W&C*$***yiLd($J0h$Kig9J4wtc5=pE_S0 z9v*Hm)R2OO$RcUEGs>}#RY_U-MMlP%wwDL0d?k!4f1{i{)puh(aNt0g+#T7Qwzlc! zzs2xO8P0F2VzjolE(+mO34ZiwooqA%{`XwgRj2JLe)SMPPgH2=+Wq_YKbLW4E?k~v zFIX7dE73BzQPQI6pz*b9hGhY)4x>5!xOUtTurRv_~hi8pmNV=&)D$znYL}a=k87s*3D*=v}#*N z*?IWpy1E4QwL#_hNAb!P?z?yIirRE3{asu%T3(o`sIDGs%vgMZBE&9XUX(jlu&6yn z`|ph@58^5>_S|s5s!iH5Ti@G&nnJh5LtY+*?x6Fm`3);8t~b{>2l~E6taF#0?+M+o zq9@E8`ug>rk>;#4{jWhmL4uT?v9W3y>^o&#N&M^ACsyaH@A&$@%FKL32}W6{nyRa< z6_S>I{^;@JKlyWQe11xk6<^)Ly32#=@Ypv|Qgm}N+NWqUZTr86BqRt}{CaDk+GG?a z=qsz$5UB9P~LFcK?#Xjwi?%P zlNgKkg2+dY9t{ltey7+VP!e#ZOX2YI45M=TrI|*3DTncn;OHWAGc&~#CpI^v>iMHC zmVW*$V_ks#Fy}n}CQ5nwU3$6)8W-AGO>OODO~@(^_tv6Wf`y&k2#-ccM8qxr>RiJ9 z_UDhvv-UpScgZ`)w!iD=MSU+-ezwV3)@$*sbaCn%#QYCeYg4b zxHF$8d%woMc@vOUxU%3ewNJm`d9Gyd&*Y1w5SahYSg)m}wULHq=wdTE7Ak9)lnw8# zTeqAd_0b6N>&MN^_R@1oOT~QU{4Tq?WLsZfe`Rj8byCF3U|XBJkAtBhFZ$}lT;Zx~ z%#Kz;xs|_smmPbzYwPIniivHxvplmSBO~MEw;i0E-II^DDrR{ z;kj_}qS5b+tD0xdd^$RoSJ&S^zqq)T^4Dd>eZO5rBc;juF;%CU5R zg^oKqI$GP=aSVK(t^8oo{MKdu=4iWtfr0g{Tcs<@i|JRtG6%4VCp6ohJAeLwQ5o%< z%lT}LY5L_@^asou-pIc1A02&g@7}%g*E#9wOcYUUNh%P7|YJixfC6`lSq6%%j zq5+@S#$_fN8ZKNnGh>)GbpBaWpwJqx}AlDjXaffBPb=MDMIDS>Ab zlyQ|CH}?1r#5#9x@moi6;-HDoDzs|-xc&F<-`a(4GUn#y3wh>1Ob$*?UrybZVE*>< z%3NXhNLG8<>+{LuKUBD6 zCNrG*@}KA~d|WtMePw>WKYF4{y!x%pIhH;fy0VMDsI8`79_{u_AFzC|^YAfmZ}0fz z+TJ$ZWjsPc8&QBge{K`6Vq&_Gb~$i=!QVBc323`x$FtQ(9K$B< zDD~SKcF=Uor%#`(Q8S9oAFz7gxN(DV`*tsmqqjQ-e#H6u`Gwf4(TcyIU67G@pP6aB z^w(yTx{_vxh;jE-X`Zg`ZZuX8v&K~1JSxDkNfAE-<>x#(S{WZd9w}H^Fxx5Twlnc` zB-ZR!>*;|XvDcr4c%oAY>lf@4zgAULry&=Jm2NoMQ#mQ(Z=f8{v&kmO*LS0C?rl0Z zH#dh)uN{m#cP+l;x5W|3;pQxc4fH!Z$}CJ+u659v`mS|P zO4{wVGH;@rZB3Puk}^I!TZYz>8@T?H(1Q;jB+Q#X&;eur%C_OVkfP&-RUl){vJUOF zA=oGToV+~c%$YN9t=eVB=Uq_fnQ&#QCr&)Ps8+@IIyt$doa+uPJ3Bk69Qd1T$0-5X zg#o^t0OLwgjTZ{Jr@h+XfO?F9Ax?R~1xy>0UZmSorCi`vhJYV9%~Ey^ywJE6BAvZ9k3#{uS7+I*?H^xp7c}VB>;ppjfW2(#>B>Y z{HaScLM8I`^}W)q$sopi%*p9US63I~jvYSil9omF_1kEeg^OMs_wVTH;vL(<>dpF{ z$1M%Wn><4xYSmMx?kVvW0~9mu+!@f(apmv)yu$hO=i9FCk@k>xkB{f)7ZBi;mF2|7 zdHe2N$(w)PiJCPCpNf(Wr{nl4TH^7^Zse#9MJa|AKsQKtbSKW%2OhV= zmKNRrk%&7h^MZT!?!~R8y?F8B-@kv08XI@CwY6E_zU}rRC~3!z9oa|wj@5ow+Sw|8nUd9`-|hfc)=V0@+Hr{eRO~R{Hds@a0hq9&O$@%7#!S;gJggF z`04j=Lho3_uLb0N+TA$G!TjwYD;`mO?~|% z+g@z#x(b?!MK%5{po%sI0(4neiLr5UXsd~d8!lbC1SeA3lg4IN*_!b2uO% z093i9JEh(GzAFDQJG(<)zJApLQ^ii&hKu?hc~nSPSjoVE=gXHb*_Tqn!@{iDg(W2= zNBgmddl!xX^lS3Tl{(k{{_O`6U2(4?|LpnmkE`+p@%#+T&1w1g_`vBER8%(nN6`CM zQh-ahZ{I#JIC!(VuBImVcU?+a+S4S3BP`mAifiv3<$m)(%hAJ9Qg4&(S^6r+d%PX2aA-|Ey}-f28|N-u05Mzh^XE@+uVc_3 zBoE;LXyj-es7vzB&bd{AA(wP?vSr*lMlTmmZ)V^ODi7TI0yhXf%JQM*>ptD>rUz z-nw-w3oENl;k)hz+7frKzcqhHbH~QPRN@j6l-pi{K4;LjMY=-$2rDi>_S}F)*(06N zgvDFyif}0_#1R%68*c-a^=1G6U;RJsMgs5Hnr&KmEK0mg|2<|NX8#{3zra!9^YKA? zIXBk>cSzkDV04h?gw(Q2JQ_P>dS-@-;^gF11B``T4X6viaKt#x-+)tDXxj^D26(*J zZ``;G_I>gs4Fmw=KC|oBDNL2p3BU`xUamHqU}bnmMR8KDpjiO3-^JR>;uLi)k6UaX z5mdtZ+_PtorjE{i?BDu^h8L-+4==F&wTup5gN{Hs!vwY%(nQ827Y`1AnV8e-Mj*ZOKYWU&w@K7H96NO%O?%c|l zqAU{%vcfgaKZ*7D5vbb>oG5RI@1owv&g<8&cSwlUM(+D_%j!-euJ`fd?Qh<^xngNa z|DWFX3B~dB>C-53toQ7lv^9qr(@+g=-?=!u zCvZ`r-Fpq3>VT!lgR(xJGcmI4G{F*V(UvjfKhD7w4Y+S0NUv|G&Q z@HRDdbvjaZb#<#|?EiSb&dl6qKiZ;j=@PqpF3;G&K!rN6#rXKR`@47VUT|fc3t+SY zH@$XE0L|b5Xc7b^?xRN|n&Yq(iy)okh<=<)%*n}lp3HR_`)qDgZ(DS2`)9)gU_6~Y zJ?l`uvvYGxK7U?+`SRtr@83VRpV#33-`c1^z89d5&9I zSy@|d%}-v<%*s-Pghx+L4*=y23ZSd2OFEykv$Nw<8D7vpuxM^U!43EYh&&aQmBrs8 zB!B$;x!%>)H9IGVV9!uN4e#h^u7JS6UcFggKE{o~5DtMFiN>hA`m|uRwqR^zB+zfG zU`C1Iat+#2SC?UG+Q~&{1qCWfQCk})IRa$J|1^`*&IP~YD?DxZ-1PJ<((Ml)W`$&k z)$f7rke8SDKY^3cw5xdglH*ZQojDjCNy!+_U zHeCoy(8wWZmlGfhq<$(|mZQ5YFw{Qj%;csCJawW};h2Ai(h6%@T4*7HtlO}m`%hxD zcTf<6uAW}>K+xS1JuEGvAwPR|zo*Ao_v5Wi02EkF{}WBI5JU?p?dq3u;6u>FVyp5?QaPsE8}I)Qd&GB9vG4}$7j2ub$gH~F^sF>-MQDf-ZNLd*eoxd)DPOi_`EjqU#Uxb=tPvlOt+ zi)ojSA3L^Y%a$$Ujrw;y%DZ`lg=s++!epE!0ByW8;)`h}P(A=34ciM`r5q>uQD}fd z@4k8^1YN!uQgAk>ks-A_bXJ1=cOJb>Yx4cM0`wCB^=J2RPS9pM{G^6!vA4nQm;QDK z{RP3SX==I;37ed-!^(XBzasUP@N#H*K1SsaI>$RpR8>_eOgxa1i54m2JgccY)3Ax{ z3btjYK?ybF6<(sGX{;_Z779s9+Q&o~Zd1aJkCd{x`$YJkV{yuXyr}F>&>ROK2l8+6 z13q`(x^dlfrC@Y$WF!YwOk$MN{et;EcHq)bd($fJ8`rPLYJorb^q5~VP$$0^?~jnIROwD z9Ch2you^Eyqc{Q*IQN$}H#eI$rt0+!51*3e3<8(_1D;y{`}bkZ^2%=<6D366dG9aK-iO*H4Nh zUxYrijfqKtQqkD>^z?Hn1#}@WMAn2eXV32D<-H5sG5`0k=8YSJ^1`z}6s33V^1@P3 zLgUQ5*?R(wfYtXHi#Pw4O&o`B-g_PysivstDPjIg9@`a1kC>gkz0eb?p^Bc+($X?8 z{ihDhRf`)Hjoh0PCr(T^nWbsDx-NGMr;hQowX~e_-?6{%!5Bnxxzi=Ib#W-E3rkbg zYWn)o=xGCZnm9n{sZCEzoTPfLYf;c-8Nx*z`Rne2C-Ck0+o(ba;W z5n^IuFFZFe%w`?9=8G1lb?sWhr^{4lw6qkU)(C=6EPw^7@rG2qkHQ8E0fAMIJaV3g zelB1g8oy%ggS>{vVA=Qm{CLQR!4ns3rBde@b>~k)j?h=7pLB0XxXW)zuVqRIGSb%5@d{>7d=;Ljj~sE3Jq zEG;b!U?(P^M0>b6y3T%+Y}eG(WX#FVrggehVxTuan4G4HeE?l=?iKq(AD_RYt+{Id z{1bj5Rd;t|P#~0aehS%5(&J->>0!7MQixeHjILiDdcDKmUYe zWz7X|n1={THrMTurbGNAxE{0sPO9MfX(*#qXD(bwAe*wOx!G@EV$sdibT<_@_C1Ft`YsQP-K2qgBcxT0BjeNA*f@$HCRVk>5 zKEkCwUq9aL{IniT>Hh0zeuKi-u0_QRorby0%a0mS1SB)7tXE)7u`UmylV)#fs#E+r*Z)YQa=W)b4( zcJ=Bm;;@kq!pHjzTOsFYvhf!o^OE-C1QXYV{Z{TwQS{`ZqCxP#{7g zB4|2BgI{82dq+gDqeF>;L;U*nt0HqwRPgk3cbMNm`b6cr+)S)I7q&S!I`}sc)*M{husAo$ULg!2+kk+;OK2aO1(^ch-Msa~_k?*4FMo!RD;L z&G{J(I0j;}oSfWf`pWEb0l^!%<3G^dNc~9C(9L(+j$P=9=BuNl^A@HSag-YyFJ4Q0 znPsOkh%1(dd<1vQ6P5W^^Tc77zoJ>`u6^MqZ_(xe_~a&~=bUOjik-XcHh)z~NvQ+- zbhvrzXuD)uQW6jN$pUCK_2k)Og(%NjfGbQ)=vw}i8n_)?3(wPy%4^{c`}w{CP6cFh zdY<+A^?h=zHS%Ad?28Ksphe;5#T8^^WhHLkdx0^!IuTaD`jmRU9S7x&c zZB}Qer{kgIPA5Hq448q#{nKU=?W&w>KPJHY;`{g0H*IWqfbMUYn#R^8YSaOCkZK{j zG_Kf`ZiJn`Fe@cczRbnc?%25h{#a$E@fHF6>ObO01Fr1+wxR|#fR6Ucl;tXUvbpL}@dt3Ju=gYCJtsG-( zWpTS}d^7FzyI9{s;^Lv;cBXB4jxDxMm+Y}iD~pQU z(LYZS6&gMJAsB3UM1<|~l>h#M7@(BVaUI;(!-o(5c(nU$_FkCc}bLc#|KVJzD(e-uj3gdp*>kwAL zLshw4#;K@c)^4C@WcrbuR&au{a4;q;(+C{PsXx+w0C!*P&e%z3^q+Vz-l?c)X~&1M zD}TE$H*=jD%b(+fM@Zi^2~+DN_RG!1jF5w+Muvvhzdt|RvSYZfI$GoDetowN+l9Kh z#VuX6Ga~%__k0%`1W`}*))bAnGqtwz*xtO^wX*!y&S${VHzFe9YyISJlV~Q&{lLJN z&?aje8WMopDypWiTeeV|a_z06`*;Rj^+GnF#X13jI7g%`9w1Ph7%l$kfms&K-Y}zV zSLtbJJmcc{X2;rtpiFI$RXKltJJF;Ee8i(AOw>X*${ovPovmGeB0Gmx|IwUKQt{s3cF#1#bQv2hr-OvO86+g9d%j5WH zw{W;z2FyM--rv3#gx(hdql#s9ZS5As4}M`u&3$Cxl=j7Dn#)-(CWsqcbyPWfZD4hO z$?xAe&5IS>H<^kSz2EH*YTN}O&ps$Ze+wqxib7Koq}5#KUAMD zc{@EbQwe&`+?Y?w(KB=H<~#xd)D&>C;@0)UD?F}zyu1|ozS9N_5&f5CQua;~#{ztH z`iOoOwD0tnJa-ymKeth0@@wCn!AyffWs4aSxsh5j}1L1?};{2B*+ZVk32JmS8gIQ^JL#8!XaTvN?S*H3@py&;^>s3y1GzjbM6Ht ztD*0nVG$9EXU}f6u&@x?vuBOF<u1c-c`%eAPQ9C z!(as0D(JjF7%Is}>F6|#SV8h!PMr$XkS)gEA$BAD|IU!+sfh_9tWRL8cHz$w!~HK& z@nHu-EMR%m%g)LQdDTVp$+8ror4%U5o+QpR=P|nYe>;cd6gcm+$o_b0mnbOB$$)ea zWcao2oN421a&9iseK{93hVDy4lL`w9`}ff@Y}_ah(b(SWt~4|zlH+jfP`M5HE>gBO-51o>UdUPx{ z*f|xU65oxWEl*8RZ@HR3SPa=m!h`>nYrh`~O8TuowQldTu6<*}slX(l#>!t#y&6+m zUS0x7x%f-A2Bl*7h>YsC*gKHNW=bUe-+(5gl`2M{NKrgrT z)w~UP#-}yldqSu@U_Yw6ZrwV04UM$stK~S=>lPNDvBDM)R_8nET=agXEnEhhe_{Gh ze6yc=y7nochUqePE83m|3o0aDuzBOgfr+tp*EtRtes$>hF=)Ppjot^O}Em!eP5h(NtEZW@_co*|O@~{{+6+4|vNJ-@XOn{vzPw zfB*h{PMxo-OVz846aray2?~a43h^VFwMHI?ksfKyeOj`u4XR)>Jj%y9zX{OJ1ZZ;B*Ei&){}=uz~Y zN5R2GG9N5kk2v){zQ}1B)rJ=K0j4)W-QWyu*IdUXC9=;2Xna!b!@5rt;!TpNX5$|7>WG&3pn~4f&OL0%edA zLRRL(hbNbv{_GNykPv`90-Dt+!qWnb0g}i>ahmUa%C)fi4wq)>rC;E3xV6AF0^F2< z5lRhdmCz6vEXLfvKHc9x_$Uzz?jf>-?>~H?zi|{MA?&4nA5r^aKG>#}mMZCF7;kDU z)YH`^nH-R+%Hm=VUS8e-z&r+*(YV=V1hk$>Sg6m9-Y!zIXo zGc`_J^@31XDjFJ|P->tyorbfMX;RHu`{M@>CDW!`d2o2xW#A*fmCIo?E(|AEwEk6Zb(U%#|L0N5UOnxb-4wzfv1Gv%b82wN4`T_{xrs8BR23-~jX zeS%@7DIcD}%3}NKG2W0X*o`umzn4oNhH3?S5wZwf&d;~bbC!AYeGw#nbHH_BTER*K z5NQJg=ep?XaulQ=P$>p1_B`yY5mbIg$1ZVYb!D0KP^c9Pv++L1T_7g@{`D(3H1up5 zS8`ZLKW=AFSJyEV9LOClBU1C4e};$S=h~cupeK-wQ$_PC?w#t%lm6GL!g#V`t$qAE zYjJRNAg7V{jYOkxTbd|WalA3s?RW9~c`d{rO|M^m-%`T3DVBLP(J85!9pzHwX0^iQZW13CRse4wW=v%hrf%1;j1=OS2=h_FXAsS8M% z0d4NJ-M6=7x)H!<_mLydpb%e2^D9}9rUla9eKsK&>?nWo%idc<4f}{F;*OB54q_N+ z`62J#+3G9r9{v^w7v9nFaQ;LYJ9I8}YHI4~P9Ls}(4D6WN%jU&hwS~L04#j}Rl2URY}vRj(`n`g02wb&+VC(%oT22F!A*r{pC8ojg=%)<^${B2u(Ci6UBC-{QD1a775UCc9l+jwjF@ssF zf>0O<1Rxn_Ja|ax%pMVuClCx7VM@heS7DR&%IaCR z7c6Uy8Ma?EX^aFXG~uSXnnAC7IJc8OVABK?pearC2FU zaQeYI&>Oh{|MurkZvg=7)$59Z^FtyD1bw`KcMf#ti^LZ0dG?~Q7@rD4HBjI@2;zi7 zj@1!%$c)+0(2$Xh?MRrqJ^cw^+mmC%N|1OFC$R1+-GsjWtGStP&z{ZIGIKlEtX-QE z!63?~g58J;Wj*}s02F*=q`Q+6qmG?Bw=L?<%Dm;;=^$gM-e5;%h|}GN@(`DlwCR5t zka37xpFV#+-nL6@`P`W^chTcGu+sayE23mwk3co_L}HlXr^B=-d)FKw5Fk<3?+~W`4^-l6xio3 zg@7CL@$h)iOhaiS!8<6|NU4$3ae8`s-dAHMsQLj2S*%4q$2Tab9M2;b;W&ujKpY#G zn3!5el@6}+7AYN@i`lLZ&j#NQLBTUEZ9g2C?B@6bC5q0@M@b4hGxH#}6CZSWXrJbu zOTJJqYwPN4Aoc?q@f|v}9a=gl8dBGH3k&7etXcCbcE{m8`}dP%mxyk*dwu;mTzTxf zcUz;PqFTHqg@jbwRJQi)=$hLrBqR?Z3FO2V-$F8NkW-171``6^26+}LAPVFTGP(y9 z<%PBpIfTF~$UVVNo_M1*0>4~=@-;p);{{(Q;Z*o1`aqckPi(936{GO4g!WO^Vo;CZ(m7gE|C1e(Z&lB*p^nHW6*GUU40w5WA5gq|eegSt3|E z)`(jQ#eh6T=rP%VN%#HzsVF2cZe!yCa5+~8=dhun!Si;M}v#;UT-@vDqWNso&nYQb6C%cqbU{ipUiZe)Jem za7@SuRN2m1R!DI`da@3MB7?RG!@bD?%hj+69I3bP>hU>0yvH`!!remycZg}b$Db1t8qeU zB)_q-F%DZ7Hv(w$_~px-bMHVPl-27U}Wq@-`}xivll9^BHS0rpY`=I85vtZ_*<$`!bRde za_1QH?AHn|>mlO~ZA=;KjO2M-#)~(2x|(hir(hXx-n=*;+Pc@8uE7&jvk!%)>) zriAzHyDD*G?lfP|^KTU!Cwxp{xPs3BT9SyscknW1#)c& z#>9d|0%ruU9okG1!u9o~ue9P~Qd7U%IjO6Evs~LVG&H2B_Gfi8pGa-648k1;hlZ$H z^4B_mVM}VDE`p$f_7fftdH%mZLTa-Zzytt{fuZ4=lP6DxJ$v@)%a_dp0s`bxkx;`~ z8(3Pd)XWV5JpV;yv!3ixLs9N%wQom@S$Fi-3wNw_&2#70Ay!3v(e{&fAjv2K`;;1{ zS4{y=7_ZSoErI<+0ul({9B`VxZplHR$jHcqb8rHW!5Ot#wY0Jt`{1Uw=z^^YVGvh% z52uL~nYC*^03FQ;npJrJUJR4qhGVO~{qUjhc13*QC2j2@7=Q^6m~Qm=IOwZoP1oi( zqX43kLa(L9Jp-jeL>h|MT3kN&7C%jW{rzmkc)TU!kX`ki?Ic7FAGr z_XTEhr;+}SAmL|(B3lH#7Ud2h<9kiMJqQuNE;y*4QTaC`@xN5KI=3on@_i?BAD<>} zHCkX!QQlk~xn@jxb zVPRph4!u>v8>y|ouIQM{b(gtTdk09*SLw}!?xVE1OjZj1MWVbDiDH1rx1uIa=PzBN z0Xe`>3^zZ&Qd@6;aU?@txj>1kqmxrtN3mzcmoInWp~JM;fWZhbVV|RIUql0z-ZGjj zl(x2VkeQGR7nq5%``LfTJ5iG`19vZ}DOJ>%x4`5i-3U`(JI~>Ow6rw&$!$po?~t|3 zU8f33TA?~hCL5atLh3l|5ku5#bh8aKw%)AAgiFKA4WY~<>Q9EFHqz03hLD89bq`9k zSQTJdphU-7jhK-QdPrGf+i8K{{VYBa)Od;W1}pBosgiFO0^nO}eQ7B>Y{=qk3sfq{W^ZI1nsmT}z&XzMs|F4_p<3vU6KcTv3`oHBe9B8r zJ>AxOpz7`CW6xPHfXb%3Ej!^To#2pV@SqHgj6A`FV_&>D_V33^@UbW8f^Tp9+=I-F z0Z1=K5V*-)4u10J>C>;d(%!5Rxc4PZ`355ShrLjrL9KS#uAcLDJcGNv?JhPS37+1) zyT%1}v=l^m6!3IJ-%+P3@#`V%%v}M;v@o)jarG-~GI!A7%^<2>pYk#@&ymvz#_XOD zKf-dtmp+hU|0~B<5R9p_qr*Kcj0N}T-?I=yw#|!B3;c{Udt9&*h9qEvGHu=JiM*ua z({fSI2YAZ7WcCSQ5FLihz=-I5Tn8Y4DWeW7F7gbh$-`6%@ty~CCgF87h$6{(n{eu`o=>_fb|Rtv{TBOf~39=f+H}KNy43c+gfjY+Y7alB|HaTe}6I_mzSRp zpz4QlvI<}B->2>gM*Jp19V7|x z`0?ZMi3tTvqyR0E4@ryRjJB$=^#SJtlc_-V1IS@wX7+~dL+B=w3(7Niuud2!$7_s8RXB89_FspSh@1#+fB^JUdhWO%rbW;dyWQdx0b1-+&*3p<+>t|bg&`d$_ z$1KSjCH}F=Fu|QT)E_=mYm;l&K0yfvLqH7n6I2N8F!7sMcm|w5NPc>}Ljglh05Y5I zEH<~38A0a00A19aFt^F+=~7G#g*<*tCbg;}BpCo~egP9Bl92b+D2fp)qVGUXZmtn( z3#u2%W50EnFl6ql3O2x{t}Vq}BdS(0kWNQ$uNSIT3E&&4V2iWLZycUr#9-Fn$U4TInY*nhDGt*ch}m zHSHlEC?+4M`t^&2fES<>=6yQ%?ksh4k>dacV=R_DBZCJj!_J?A-T+)9#K^eW3-ct3 zP}F@v5%0;X2TGLC z(n&akr1`;0N>IwjG0brcJ<{?J`u6`F!K9*)uTi8^ZS(}@&RZBvCLIhNiymzcfu#!J zvuLYOC5fVqi6=7t3WW)%jp2{kS?frRTO{QL$(mp%V8T}^DaYYSgUjJ-J&}L21jZxL zOiiJkwO>mRcf^R#rvIAfv=l2ldkNyhPFAB}}WgM%NJ%@NYl6oUOQ6@0nCv5EuYQ8O$-KG{gJ zl-k-P^Qm~DTU0DVF%L#G!LrD(fi>c~0O&UVhlY?4{c`KOvu7|DhmbPBUaC@!whfZ5Vk5z?^cnwS8Bm z+`EzlS>X~k4kv+V2m)g06Va&>7|0JEJiuI{`^%TTSMpk`xRX*+J|oH$2wslP(fPjd zGU+e5UQe`zl`-54XrDV{kJCjpvt-bDooM}N))D82Fg1ZpWF25i8Gx!WjhsBfX78#N z8`s7xaynnUxC67;R4uJm*L^X2ofy9 z++;xe1n&+i&0h*(>LTGpWGM+ZdHR&NNazlPA{4dm%LUFydk1x(0wo~v(3Y0xG)G~p z;(IFQA5ZJ!=@AsIt4oWkfOG>r$9k_7uyxi1P-M@VRuC{msJsAz5Ml$=+Sh;a({y}6 z0VlTEUwD#`=KR1OU;IbT5EW4&L(gZ$fK%~(HU`<gY7QQKDjE7@-WEN_LXQPC<^7 z*li?d37QWCZ`r+Txt?lCpZ;Gug%LI6F-YbSc^~QHue5nYw)k12#URbe1EPdsCeXFM z4+DT2ph&|K2%rnyl4p2Cj>Km_M${+3UwIf6NW+d#Ps8ZgfFmA&--Xkv%$*i|h^30q zW@B|4$Rs=r+y2W{-@YM`SvqpHjr}y#%Mja{5+6;W9*JAE8oO&X2L7X(rl$YPS6bWJ zdV-^(yKV1u-Py(?^8X4yl6eAE{<~OX&!0cB(0vSxplw%>5X1q5OE79nEeW?fE-tRy zW4RSr6vSgvk6#e^{&aND+R6iVZqC+1NdCygDPo#Bj~{eJ!{%VnO+=gwiO zZXE;%;Gh+GL?%FKJOBLI^j{Pb1`PxD!lcNIA7)c*;7z0Pb3*_?7?b2CK?#xcfiQHx zrbZJd_n_1$(DMY+hB$xBdq9X%f>?oe3*`xyKt~E)MtXXPeU8R+Yy+T+%2P8lGo%)h zF?d8YaW>rC-12SFlfCR+*bGEuf!Grg9PAE!ODsJNnVD_S1{qmcq;_VxXk?Q4jrjNu za0$}dA^#&h?M1wO=xYwJA<3}(n3&H?;2?|^v*ENLp2?ALHOdgunZ>5>0ZlSyt%X zfF&j-=z<}TK>&Kls5;ac>;a`K80bZc)OFz&ZXm;qqjxL=nKJ}D=t2~bU>vX&xOEjH zKarWJbpBcAI*Kb;y;GDN`GdF0kclFh`kM5pM$ zDDR9tj3~i{Wi8{m*=3@BA;l#sA%R4PI3ou~AYoDJ>K1RmvOHrXGk?G%>GornaV{_L z!UjOqlBsIBpnu+7OE+@nQ$md)auJ`(gN}+2J|Y`Jr-W>n4nP30;B?zAdOz3}-MD9; za3rk@7pS2~fFKY*9O{#ya}(b=>=B~&LCnM6GeG5rVpfE3LO^hEaPh~5ms=0|LVL@H zdT)IFx+;nZN%P4qPj3Ycr@Qv9N))Z+H~?BEV0Xrc4+7Aa&nBFTMON<2zhOdlYqY89 z&pY&kv6-m2O-xLhz#TBP2k@m#4?$r7my`$?wMHIF&~)(zE=>i6VtL73oa2mRgM%-S zOsqMpA#0WU*DS*rdv z&5QXjn;E<5H*5Q(u^NS^AOCB3YSZrROqG{kSnjfWf4$JIy-;rHh@5@X*;bM0{%#vC z%em)M^Lb-4Ib*h?hoh(FMY-Ios!lCHFIVMeL_lQEw{USEh&)$0F2tcg{fJTU+(;qe z605>xWr%MX^!}zLFd8Butb5b49;0AVDTGW{!r>b`Z~l&d0)fXfC^@PM3Ldy}6|@u6 z2ZrGc+Nk4jzynX6I;9G)izGJTbY;*dUj}R?<_hIHMhU0U9V?IUE7KEOj7)>O$VQ{7 z{=vYZ`o6*ChuNA!@fcHNqLA3)Kxc{1*x8*^OvuQ_kq|B@53T~c$VV0N$0KiUAjNG8 zB0wCGniz#!c(KAiKBX=ch`%Q=c1%R`=H>`aX@{C$yw9RaN!$b^lf+GtU$(k&fL2jS z3FSNibm~58A5cZIx}GADjO_+$JTRZv-tX&Ya08cqJl|=Cm!v82pw)z#C2!T02~ME@ z5wC2*L0ad7IqUWvJ0w5&2A#ltd7~J-SJJYDqz7xE_s_5Y1hs}FA9o80)qukNI&5{A zJ497TRyGO&fU6uQ;;_tW0EJ1GmYSBOriNxU@MIT6#`;7JuG;$gcx;}8;|o#V^c*MB zF6Y0t`3IfK?oVxRzR}mp%2;9|f>qM7iroX;P{EMvEoAqopQgJm-kuyE=f>>H-2LMh zh?fA3keCxlqu#>%9WEpvm6MBBey~#wLEucQ_JRw^b9z|B^>Hc;gU}X|mumBn7*~IG z(2v4M8N_0vV>{@J&R+TXvzLTri;$gsjjqMA;?2L~27|RPX{dsSqBMw~jNT)5@L)W& zhlLAP4qK`#D|rAyGTy$u{>3rk+Qh&QzBTf&>XU79w1ow5Uys}x&4u<)jK{$#y*RqF zz)>V1e>sl@;2dMRhf+<9jMnbmn=J5?ykLR|C&*YH5*e#YWLTIT@h6kN*pTY4<5K(s z1CMKJ2G=`qLO%wcQi8RMlQ;al8GVNuJFg{sVxQ^C(mc_~v7N4pJ-KDk{R&W?;&7De z#N#>Bp$vi{fgLQvE+$xF@sU{y^WVY;MkNm_(w2R451O%aJ>dlVU^}z?(FZ zvmU(Wn?Mh%3+7Q&ML(ft;5>yfj5AlGklZJw-=BFOFCG!G>atmYM^f|)qdwY?oyP1E z`7A&a9*#0&=iVN9frc6a2ZQXDC~3zt<$`+RXw%Fx zcVGLf^7BHA)%keK)W2aXJNrpYq$J2_V#(c*-ohn%g@Ov9iT69q>6~qL!k@0l>Kj{F zs3Sgu9;=A2H#Rdn310?o*D(?Y2NgjOA@#O>C$7pAZh0p1GtXdtfhB2Sv*)J+H2A`Q z`9CS{)hW4EMJ2l|NY|^mGx}} zi5@;aDk(!{pFcBEpzOVVSB0eG77CmAwNv?an9T`>kB$)gW55r2j5P40CyR-Sb~qg| zZ_5kEMJeL7QA*RZO=MJTW~@CLdt4843AP&S-MTN2_nJb!AWa61i5Ea;jXcJ75*Fvi z&I0KHfCj@C+d-(9DUtvNOtjr@A5N#3Yw0TQ*4sAw>y21|bc*@$zRn)0cs zsboNdoC87$H4qS9X++E7Ml zX|6_<;-w?XkXcNvUC%dDVsMC?%}@hF06tTqO;1K(y@S;J%zU-N-G0{GdhM9x`l#u;FQt~wQMWE2k!XIG$5SU4KqO;N0ljq%Rxu%22qzT=R5tpL>xa> z78X^DLg217@`zTwgam%0y+DRT%3A(LB;G>tm{J2N$N)Ucl6jj@7pBXT@)$t z4^BX?hI(}0*B4S-67cYu9hDc}Xd{7x4QW?UZNGlQhB`nEW2dbT5No3ByKVCx;aX6x z>qvp^JL9H!e%FLrsK5_6ORwz5+TvfmQr#+WDh8rATb8N$B|%;i;-!iK0~vh}`nZLX zq>zg|_-RVK(>-YKwG47gMavwFjPjUHqr2Zbrk2NJk@;a(KlC+!R7i*d$r?jWQr%~z z>I!U$!wWsuClQlcWL}0+3Sa8x-1MT<-?G$wquuTpj)!&yWGhfEwvN0a!VgN_%{vTi z2RA->@`R|f2t#Og=bS{}qGc5$Xg#Twv}yjXz98RyQuk%-g!8Juwc{_1M`bz|ew z$lLP2-_;^riz* zmf@?CpMmVkWD4!aIFX*~=b??~P+SpK02-kqTdgkk(dKUmEW;3nAE7*{qV*u?sbXipxE>nT)BEsnp z*|O;9>C2$^`vL8fw_*^ZURp-ROqWc;<2`AR*WeqxwriM!97}P}&v%U*qO_g1Oh%|2 zk_w>8nxbab+;)UwXrP4~8ynZ_oK}=}`O64Nu^jJpiqI{)yPgKq0#76k^yNYiLk_!+ z_v*#R$+59NczX*O5IQ&i^_B_}1{o`Oy-OfwV&tvBp^^CH%g%EzbU=IEkhsR{gbaY5 z$VdU;Zm6N@0|}k2_`>1V+#TqCcv+j(Em!i=1g<+v9ssUa@EQh;Lt?VSa|Jx_?cG?z3iT zTOf#xer4j~(l?XFO(bu2`*GZ8@D=3mD<&r8BU)y;I#xzTXH5>zXhFrpYYH@l-h2=~ zdNj9f#2)X%LEN5rTVvZ*Y=c$#$+xFRs-4dn%k4Jb4H8drNBF__bJvZp5w-LYB!mV0 zgSRSCf!6*gyt6uYzt^?-x&><81^`^*ShS?|m(}+V4_D17En-UIQul**hfu;1 zX8RI(z@inDkhM;#A3~FdCt?!WyaH~9m-(g4o|O_79?mzs@fwB#z<<%VZz;#Wka1*W z+N#?0x4*vnA_XtpT1$Zi9e>)MqHZ>%ZU=@zOeq$0>N(^^`?^y1Goy6^6WnYr&|ptF ziT3|LJ-!R1*Zhnt0L3xzb)p_4LMUL^h#hjmw^y*DCfzW?6-q?@*a*_1LJnX;olDD7*+-N@ZGG!H5 z@28Uk86Tw7pqOVjN?5g>qLAjPr1bA7*(?1*H-a<(`S7~Jqe!(TV$P!$J#8@HDsa#` zxZhm^c*)i|KuRn2l$7Ph_`zdu&{8v4~<2VaXMT?qCtuGp*y$ii0p#f`w%68yus+_6P!V$asW$l;GqL}HCxhZ58fhn z^Y-lo9z}05r}i!EfG6Ie*BtXPTJFwi0tb=3elmIzdPv+@QI3`xyV2%12gOI^uIa2K){e3WS{ zxH`a$fg+E!x?GJgAGKf_;5{zLBgdSMJVFEsFtnuaOlz!KvK3HWTKX48713Y>>f8)>? zLovP!70O$QS1U5TCExiekZuJAY}ox@rAp{rT1=}1_{oWEMJVbIl4#`VLlL0VV6bce z@1+S`I9>Jp@H;Idf%oJ+Sdag@xLM?fvfXZWMIHhypef_($tmS>(HThL1aaY=E-AI2 zZ7>NK9}`nV{lpMDf#z6S{?#LbsLl2S12YS2Yc}sOT){i0F8s8YErWjn`k@U1Pn2&H zlg4}Rj?mUgfuMOH)C2N40T%h;#Qxo=&1fiBh;LHj-+wCk4MZf6nmcF$C+YZgCFjm+Ypdd=aXeTgq; z4lVk6kvmoVn>VMRw^|KcQ;#+hhx$$=3d{=&XhfZYexbhazJU-*93dc}TS%(5CJ3B7 zt)>=>S2=XYP=#n!QWEmzQ~dHdVJuUSqrgVZy< z#$kIf79;@o41sbCLO;?;Y%`-FkxM9j@p!Kb;7~krY|wxz(n;)_s9qWx1m-G{S6szY z_uYt@)96yNxc%eJni81UgC~=Tkpv=YP~r6vN3j$9PmRtR8SlpsG?{ot8K^ICbp|m$ zkz!=@H3BnHM^NN22)6}e#>kvP&a}+m`b3n=2NhZeZXM>3w@@%OlZZzv(NCweFSN8B zqSpV_+nI;uocDYDR}nKKBimCFEgHsJOol8qN=nJtD{EP1Fm*Ch8H6_OtSOFZCXv0B zq*9hqqHI}?%2Ft$=uRa}G~#)EnmN}w=ildgt}~Z^=DP0g`?q|T&*!~;C*XZDf@K7k zkcYz$eFoZ^;ajwIE0o(*astSNj69?B0EIa<@YrXD6y#QHiYw~xLmyS$h;hP3zWi#I zi@giV+B6+LD>Bu3Z_BRXF5Hpr~J5p>qJ!ajv=o%Upn1#?EQmAF;DE=cc;)ejf+`WtKsy zNOz$*upbPgE-!!jv@Ms;9(>tk^!>gs&tJQFGoSrI`k9M)c~4;bd5tQzC$6TP1V)xn zc*tDX(cn966T566dY%!ToYOG_hI zU{X?20vVY6T2?iM2E@~cenSq>B!h8@g5SZ##U9T!x0PAV(xn74A`a5Zaq|i}mnC){s`-NTTKZeZnajbc`=s$@kVQp0Yqx!9yp~mZuB?n3(z4Y{K&@({f7xnTg ztDb5}MZ(rkZxykc%^8KV{4xszKzeJMc8rbDjt5yY#;~_rUmbG#?6u36cT9Ay{~`gB z&wC7g6mm3Mz7fHIj6IT<;O(>g=e?Ojx3RH4&Ko|BAK>55EzOpxCr?J8=LHxM4KmUr z8d8YcZ0O*u7btPM*X-JUzl7e~S!7n!acEXw|9v=G(e|SpNK=g`3be8C4YyPjqIuyI-}8 zS{d#$-b5G6+o8ziU}Ar%IIbGL0pFV8lv3YISEI;a1o6qJP!*r~otTmFhM_?Gcs-ZB zKm6^R1yM&34pcvQe`Z3KNrXd<(Sd}RkLL>R{t~NE82yYs9CO_$BrZ;4?vgkL=S2Z^ z3(NPh+W9-ZBk!o9S-?fTkC%Cu--zC+pJt8Kzi8g!)rp0Uk_xx| zL?1%&P4ngB8zUAD+%&91?q!SJk`Y&_=k%mQuKAB!%_J&rWL~6%==c1$|CwvI+nW_d z_qd!9@c;YE|MIVmcWal8>NOs@k0{OwQu_vByAq2Um${$>(#%N81Y4^jJq53d5w9gm zWK>Eti`O6Js{$w3^-&w?ZCpAElJ8~Zl`a%`|7&v7Qu9GkuuyX>Nj%QBCx1-4L8#B31#= z7KAD~vpe+s9RH%^ zdQ!ZW(>^1hw1c+gHf6KN13LJfU+ujq#pT1@VKzXxB>&y}D4VRQoGd_2nAjp38-r2D zp!`{6=#j@T-%SzYh`s-|eb95N}W zX~H&dzCe#72L^!G3k-?@2KmG;zo>$3OsI&J*r+7xRFY-U)ylC3B5TKFvikKiV|BwK zXv4O2(pYZ3q1xhd%md!~ILBCZ1GLcIck>s&u!11@q6w@}?5O$Z(t-hYB?b&ZK@K{9 z53~yEh6j5jj*G(q$c)WK=E!~=Mk~4y+M)5oDw+O`mB=2&%}@KHqv%<$8%<#Z2V-1u zxpD$@)H;lG66nu1s%PcfPjX5qP25z)U3uhNM@tb37#;+ma)IF}xFGHA*o?h;1qqBD z0Zft9gn@8)Hk9SKp7BpUYxe3>|B^AeX&w|?u2?|n3Pm?ri>Dk}N)*`>PG$9uh!O$G zZJ;V$twT!Nv zU`tyNJ={x5WcVr->R$qVClz}*dN4K@)p8?In0bMXLWt6LDK0m+B++ZQv;|01VZrs$ z&74cJeUftL%MPZt=+vUHbdyo6UNWgn!qjPHJ22mSg{w5Qv={J-M9GDU8SzM{?15@> zuP$I(!J_8vI~UV(dETF_Ie8m(3>q4rI>q~u9eo7^VgV({wPIC!}nFcPPHDG2^Hgi2Xy|#%k z=*xDjtI|HAV~)q{aQ_;i^ib7~N*w*;nie{mi`B2<0kf!5?d|L+CYUI|xwexM)S&cj z0;vZGFgum!2S8~DikFbSl|@NcLHjZ{#!nJXB;R^%`eKpjpqjhIdp2m&?%`2>VT1e- zQ6qz9PB0B~V}I0lJL5lyNic;SeM=FGu!E&xBH{8~6wDjd^9bGF50~&&)-hdE`%bNq z9NVs~c~djKss8bCzdxzIr%{hkPla9dT#>APT2&Q^q(&o_5$$vQ=+UEMnxgX@{%*`y za@&Y|cb-4LeoOHk8&4U6i(tPM#K)Xxv1&`l?CB-Dmhe!ON84Y|y2MEGdDTWUsPK)p{(_^^ z^z6IWJvz4qhU%32S|;lS{I`j7$>A zS*{@%H5FN?wX{3L`OS=BZ^ErCaUV*nAwo&v#agKmH|4V0DN1;+F_AT1cY5HbY@LVG zrVcf}Hn)9lz*cO7oAN7e^GMy#9zzhY5+HL2*Z*bz{>42)05&Pln-Qy$Phy#HgTRrE z$}K8}tW@ln?&{|G0WaIGES&s0W7al$t@wn5Vz-s7#bnw<@}Mj?LP;sGfs!WGAVYRko{+&kwY?)XMU$c{Tfv~6VRKv>05a@f-A`Avv=1nJ0&c?VhY<#`qLRL`OtmNKur|l z&)GS2j;(~LZWD-?MP1v3e*I%K2@dWr-r&^)4Z?FUvtnd5$jQXH(?$rDY($IJzhA%a zefHEdk05F1>kiG9DKU<|vgTa;;gN;Kliy>uZxcvEmN;m@ph1VxjICI2z5c@<7N+mn z3w4e)!PB=1q?E#boSf$eItxX$mELRloUuRLGuO0X*Dty{eAuwW-ARcqi2@MktoS`V z;buxraMIQk_Px{w(FY25U;MJpFq(Hzsz%kcXz}9l45r%$i!%6SGth{-!T-g!CIJ(S zG5ex=vJP=trVoCM1PoKe-O~X;S8W1;A1}phcbqtp0(49>aC~BNZ(ZG-meV7May#hh zwYm7M^$&`LfwIM8krAPwc(??qV2l^nK2@nyt&&+9r@&J!36q?i8=BuNrxlZ0?)>{k zr{pdJ2lVgn$$EhB_#y~Q3KTqekY)dkNQOX#4(BaK`E~(Vc?Y1!*HRggtdp8#0VwEFmm5-$7LEE$jsCu z#iL;CSf^DtvFu*9>UktWMEo!mCRm~Irl)q;P*9s|p2}hy$g30Q zG|=!)zY)%+n71Y3mEwN4pJu!4?HBd!kBEC-Tx}F{G^UY!Yz&jnVnMbNcFB`)*^`5u z=M;P~afzPW>Q_}dym%F~4f@v9cVCs5b6^K?%tj_Anglb{Te`Xq^OTijH$F#6I_1z2 z;jwAV)01pHmwgMn$i7*|Qg#u|QOQKE!QNt#-mk-k54Q(nktinc%lqM%avZLZlLNZ% zvTy7*81>wT59byex0VYJ6dbz5NsFfv*0R>~I|C`aK#wY@2{SoqDP&`%mlt)NvjJyW zh(Xcw@(yD?3(q!Urz3UWP4#9<0%@=)5)^olXKJ-6J}R&zq>%Qle<$J8bem=3LqzLc=P$Z93#Lc28I{{wVRZi(2b_xjy4>MHtOJKC8vhSW`JI8M)V-n(s|ID z!oTz2q#u#J^#xC&k z+J>&Xwlu2?dwD)Lw%BmqL79m^dcJKa=dtymIJp%+t*>~F(F};g5kP5(x}g$h$wej# zU!eoA&$JMP6p)EUcKqy2t&_L!8Af+7J<6(n0-Kpsz|3Ueusr5*d3Ci=&~B=`OQ6mw zu;|M&1b)Hs^0Dp@r zeiM~qEc!+C>O1}73hc==pdUjCEpdBvc7zy(13oWg_)3Z(C9psiZmI{JWu5-~KGS*y zAZi^!`upSJ+A}6!*F7{4e>dGCK1Sq{Mrt4R3?O7E>I%L}5MH2o*<@iVS%6@zWAXie-uWqU!e;Lm~$I^=)pV}iGg{x7 From ca0baa54df97eb63e8f6713129f62b9affb7c4b1 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 30 Jan 2019 11:26:21 -0500 Subject: [PATCH 027/390] various fixes - fix typo - make property name plural since it may contain multiple items - mention that users will appear in sync when they upload keys --- proposals/1756-cross-signing.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 09747011..f3c13f01 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -8,7 +8,7 @@ this user must then verify each key on each of their devices. If Alice has *n* devices, and Bob has *m* devices, then for Alice to be able to communicate with Bob on any of their devices, this involves *n×m* key verifications. -One way to address this is for each user to use a device signing key to signs +One way to address this is for each user to use a device signing key to sign all of their devices. Thus another user who wishes to verify their identity only needs to verify the device signing key and can use the signatures created by the device signing key to verify their devices. @@ -141,7 +141,7 @@ response: // ... } }, - "self_signing_key": { + "self_signing_keys": { "@alice:example.com": { "user_id": "@alice:example.com", "usage": ["self_signing"], @@ -153,6 +153,10 @@ response: } ``` +After uploading self-signing and user-signing keys, the user will show up in +the `changed` property of the `device_lists` field of the sync result of any +others users who share an encrypted room with that user. + Signatures of keys can be uploaded using `/keys/signatures/upload`. For example, Alice signs one of her devices (HIJKLMN), and Bob's self-signing key. @@ -242,11 +246,13 @@ response: } } }, - "self_signing_key": { - "user_id": "@alice:example.com", - "usage": ["self_signing"], - "keys": { - "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key", + "self_signing_keys": { + "@alice:example.com": { + "user_id": "@alice:example.com", + "usage": ["self_signing"], + "keys": { + "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key", + } } } } @@ -266,7 +272,7 @@ Bob's key: // ... } }, - "self_signing_key": { + "self_signing_keys": { "@bob:example.com": { "user_id": "@bob:example.com", "keys": { From c9b38cbe5310c8f0a8a637d4ac6ed57dcf1fb8e1 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 6 Feb 2019 11:37:19 +0100 Subject: [PATCH 028/390] Key backup: add `PUT /room_keys/version/{version}` to allow matrix clients to add signatures to an existing backup --- .../1219-storing-megolm-keys-serverside.md | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 4d8e8f1c..4b121054 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -177,6 +177,42 @@ Error codes: - `M_NOT_FOUND`: No backup version has been created. +##### `PUT /room_keys/version/{version}` + +Update information about the given version, or the current version if `{version}` +is omitted. Only `signatures` in `auth_data` can be updated. + +Body parameters: + +- `algorithm` (string): Optional. Must be the same as in the body parameters for `GET + /room_keys/version`. +- `auth_data` (object): Required. algorithm-dependent data. For + `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the definition of + this property. +- `version` (string): Optional. The backup version. Must be the same as the query parameter or must be the current version. + +Example: + +```javascript +{ + "auth_data": { + "public_key": "abcdefg", + "signatures": { + "something": { + "ed25519:something": "hijklmnop" + "ed25519:anotherthing": "abcdef" + } + } + } +} +``` + +On success, returns the empty JSON object. + +Error codes: + +- `M_NOT_FOUND`: No backup version has been created. + #### Storing keys ##### `PUT /room_keys/keys/${roomId}/${sessionId}?version=$v` From e02b345c623210a718e13ac65afd66a120d90711 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 6 Feb 2019 11:45:11 +0100 Subject: [PATCH 029/390] Revert "Key backup: add `PUT /room_keys/version/{version}` to allow matrix clients to add signatures to an existing backup" This reverts commit c9b38cbe5310c8f0a8a637d4ac6ed57dcf1fb8e1. --- .../1219-storing-megolm-keys-serverside.md | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 4b121054..4d8e8f1c 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -177,42 +177,6 @@ Error codes: - `M_NOT_FOUND`: No backup version has been created. -##### `PUT /room_keys/version/{version}` - -Update information about the given version, or the current version if `{version}` -is omitted. Only `signatures` in `auth_data` can be updated. - -Body parameters: - -- `algorithm` (string): Optional. Must be the same as in the body parameters for `GET - /room_keys/version`. -- `auth_data` (object): Required. algorithm-dependent data. For - `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the definition of - this property. -- `version` (string): Optional. The backup version. Must be the same as the query parameter or must be the current version. - -Example: - -```javascript -{ - "auth_data": { - "public_key": "abcdefg", - "signatures": { - "something": { - "ed25519:something": "hijklmnop" - "ed25519:anotherthing": "abcdef" - } - } - } -} -``` - -On success, returns the empty JSON object. - -Error codes: - -- `M_NOT_FOUND`: No backup version has been created. - #### Storing keys ##### `PUT /room_keys/keys/${roomId}/${sessionId}?version=$v` From 2099308d4cf1b733b88976f535590932f9b0eafb Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 6 Feb 2019 11:50:47 +0100 Subject: [PATCH 030/390] Key backup: add `PUT /room_keys/version/{version}` to allow matrix clients to add signatures to an existing backup --- .../1219-storing-megolm-keys-serverside.md | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 4d8e8f1c..ae361d3a 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -177,6 +177,42 @@ Error codes: - `M_NOT_FOUND`: No backup version has been created. +##### `PUT /room_keys/version/{version}` + +Update information about the given version, or the current version if `{version}` +is omitted. Only `auth_data` can be updated. + +Body parameters: + +- `algorithm` (string): Optional. Must be the same as in the body parameters for `GET + /room_keys/version`. +- `auth_data` (object): Required. algorithm-dependent data. For + `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the definition of + this property. +- `version` (string): Optional. The backup version. Must be the same as the query parameter or must be the current version. + +Example: + +```javascript +{ + "auth_data": { + "public_key": "abcdefg", + "signatures": { + "something": { + "ed25519:something": "hijklmnop" + "ed25519:anotherthing": "abcdef" + } + } + } +} +``` + +On success, returns the empty JSON object. + +Error codes: + +- `M_NOT_FOUND`: No backup version found. + #### Storing keys ##### `PUT /room_keys/keys/${roomId}/${sessionId}?version=$v` From d43b595b5e4ec966508b09af010c3273336f10cf Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 7 Feb 2019 10:36:34 +0100 Subject: [PATCH 031/390] Key backup: Fix PR remarks on `PUT /room_keys/version/{version}` --- proposals/1219-storing-megolm-keys-serverside.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index ae361d3a..f4f45827 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -179,17 +179,16 @@ Error codes: ##### `PUT /room_keys/version/{version}` -Update information about the given version, or the current version if `{version}` -is omitted. Only `auth_data` can be updated. +Update information about the given version. Only `auth_data` can be updated. Body parameters: -- `algorithm` (string): Optional. Must be the same as in the body parameters for `GET +- `algorithm` (string): Required. Must be the same as in the body parameters for `GET /room_keys/version`. - `auth_data` (object): Required. algorithm-dependent data. For `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the definition of this property. -- `version` (string): Optional. The backup version. Must be the same as the query parameter or must be the current version. +- `version` (string): Required. The backup version. Must be the same as the query parameter or must be the current version. Example: From e7f792602348f06f3c726f72902a127df0d7269e Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 7 Feb 2019 23:29:11 -0500 Subject: [PATCH 032/390] add algorithm and version to the example since they're marked as required --- proposals/1219-storing-megolm-keys-serverside.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index f4f45827..74a148a2 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -194,6 +194,7 @@ Example: ```javascript { + "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2", "auth_data": { "public_key": "abcdefg", "signatures": { @@ -202,7 +203,8 @@ Example: "ed25519:anotherthing": "abcdef" } } - } + }, + "version": "42" } ``` From ed945d67444a5c011bd55d6cfbcf39ee3812e8bd Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 6 Feb 2019 12:03:44 +0100 Subject: [PATCH 033/390] Key backup: Expose the number of keys stored in the backup so that matrix clients can compare it with the number of keys they have locally. --- proposals/1219-storing-megolm-keys-serverside.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 74a148a2..1f33fb45 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -172,6 +172,7 @@ On success, returns a JSON object with keys: - `auth_data` (object): Required. Same as in the body parameters for `POST /room_keys/version`. - `version` (string): Required. The backup version. +- `count` (number): Required. The number of keys stored in the backup. Error codes: From 82ff866b5890e1e7132c5bdfc603c79ce92d7964 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 6 Feb 2019 12:30:21 +0100 Subject: [PATCH 034/390] Key backup: Add `hash` to represent stored keys so that a matrix client A can check it is synchronised with the backup. If not, that means that another client B has pushed keys client A does not have locally. Client A should then propose to the end user to retrieve keys from the backup. --- .../1219-storing-megolm-keys-serverside.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 1f33fb45..22e57968 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -172,6 +172,7 @@ On success, returns a JSON object with keys: - `auth_data` (object): Required. Same as in the body parameters for `POST /room_keys/version`. - `version` (string): Required. The backup version. +- `hash` (string): Required. A hash value representing stored keys. - `count` (number): Required. The number of keys stored in the backup. Error codes: @@ -240,7 +241,9 @@ Body parameters: `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the definition of this property. -On success, returns the empty JSON object. +On success, returns a JSON object with keys: + +- `hash` (string): Required. The new hash value representing stored keys. Error codes: @@ -267,8 +270,11 @@ Example: Result: ```javascript -{} +{ + "hash": "abcdefghi" +} ``` + ##### `PUT /room_keys/keys/${roomId}?version=$v` Store several keys for the given room, using the given backup version. @@ -308,8 +314,11 @@ Example: Result: ```javascript -{} +{ + "hash": "abcdefghi" +} ``` + ##### `PUT /room_keys/keys?version=$v` Store several keys, using the given backup version. @@ -353,7 +362,9 @@ Example: Result: ```javascript -{} +{ + "hash": "abcdefghi" +} ``` #### Retrieving keys From 7cde3193e535aa3510fc008fb8ce55617815a194 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 7 Feb 2019 10:29:30 +0100 Subject: [PATCH 035/390] Key backup: Explain `hash` better --- proposals/1219-storing-megolm-keys-serverside.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 22e57968..069021c8 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -172,7 +172,10 @@ On success, returns a JSON object with keys: - `auth_data` (object): Required. Same as in the body parameters for `POST /room_keys/version`. - `version` (string): Required. The backup version. -- `hash` (string): Required. A hash value representing stored keys. +- `hash` (string): Required. The hash value which is an opaque string + representing stored keys in the backup. Client can compare it with the `hash` + value they received in the response of their last key storage request. + If not equal, another matrix client pushed new keys to the backup. - `count` (number): Required. The number of keys stored in the backup. Error codes: @@ -243,7 +246,8 @@ Body parameters: On success, returns a JSON object with keys: -- `hash` (string): Required. The new hash value representing stored keys. +- `hash` (string): Required. The new hash value representing stored keys. See +`GET /room_keys/version/{version}` for more details. Error codes: From 0051c6a377ca9fa724f0848b6d2cb58dd1680660 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 7 Feb 2019 22:54:53 +0100 Subject: [PATCH 036/390] Key backup: Return {hash, count} for key upload requests This is this tuple that allows the client to check if it has locally all keys of the backup --- proposals/1219-storing-megolm-keys-serverside.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 069021c8..05c68520 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -248,6 +248,7 @@ On success, returns a JSON object with keys: - `hash` (string): Required. The new hash value representing stored keys. See `GET /room_keys/version/{version}` for more details. +- `count` (number): Required. The new count of keys stored in the backup. Error codes: @@ -275,7 +276,8 @@ Result: ```javascript { - "hash": "abcdefghi" + "hash": "abcdefghi", + "count": 10 } ``` @@ -319,7 +321,8 @@ Result: ```javascript { - "hash": "abcdefghi" + "hash": "abcdefghi", + "count": 10 } ``` @@ -367,7 +370,8 @@ Result: ```javascript { - "hash": "abcdefghi" + "hash": "abcdefghi", + "count": 10 } ``` From ca7aa8b0baca2d8b598449b9ab2d33e7a79a2ad8 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 12 Feb 2019 20:12:17 -0500 Subject: [PATCH 037/390] fill in more details, including federation bits --- proposals/1756-cross-signing.md | 37 +++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index f3c13f01..6d5ddd40 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -69,6 +69,8 @@ use cases. ### API description +#### Uploading signing keys + Public keys for the self-signing and user-signing keys are uploaded to the servers using `/keys/device_signing/upload`. This endpoint requires [UI Auth](https://matrix.org/docs/spec/client_server/r0.4.0.html#user-interactive-authentication-api). @@ -99,6 +101,18 @@ Auth](https://matrix.org/docs/spec/client_server/r0.4.0.html#user-interactive-au } ``` +Self-signing and user-signing keys are JSON objects with the following +properties: + +* `user_id` (string): The user who owns the key +* `usage` ([string]): Allowed uses for the key. Must be `["self_signing"]` for + self-signing keys, and `["user_signing"]` for user-signing keys. +* `keys` ({string: string}): an object that must have one entry, whose name is + "`ed25519:`" followed by the unpadded base64 encoding of the public key, and + whose value is the unpadded base64 encoding of the public key. +* `signatures` ({string: {stringg: string}}): signatures of the key. A + user-signing key must be signed by the self-signing key. + In order to ensure that there will be no collisions in the `signatures` property, the server must respond with an error (FIXME: what error?) if any of the uploaded public keys match an existing device ID for the user. Similarly, @@ -113,8 +127,6 @@ a `replaces` property whose value is the previous public self-signing key. Otherwise the server must respond with an error (FIXME: what error?). The new self-signing key may also be signed with the old self-signing key. -FIXME: document `usage` property - After uploading self-signing and user-signing keys, they will be included under the `/keys/query` endpoint under the `self_signing_key` and `user_signing_key` properties, respectively. The `user_signing_key` will only be included when a @@ -153,10 +165,22 @@ response: } ``` +Similarly, the federation endpoints `GET /user/keys/query` and +`POST /user/devices/{userId}` will include the self-signing key. + +In addition, Alice's homeserver will send a `m.signing_key_update` EDU to +servers that have users who share encrypted rooms with Alice. The `content` of +that EDU has the following properties: + +* `user_id` (string): Required. The user ID who owns the signing key +* `self_signing_key` (object): Required. The self-signing key, as above. + After uploading self-signing and user-signing keys, the user will show up in the `changed` property of the `device_lists` field of the sync result of any others users who share an encrypted room with that user. +#### Uploading signatures + Signatures of keys can be uploaded using `/keys/signatures/upload`. For example, Alice signs one of her devices (HIJKLMN), and Bob's self-signing key. @@ -258,6 +282,13 @@ response: } ``` +Similarly, the federation endpoints `GET /user/keys/query` and +`POST /user/devices/{userId}` will include the new signature. + +In addition, Alice's server will send an `m.device_list_update` EDU to servers +that have users who share encrypted rooms with Alice, updating her device to +include her new signature. + After Alice uploads a signature for Bob's user-signing key, her signature will be included in the results of the `/keys/query` request when Alice requests Bob's key: @@ -289,8 +320,6 @@ Bob's key: } ``` -FIXME: s2s stuff - ## Comparison with MSC1680 MSC1680 suffers from the fact that the attestation graph may be arbitrarily From 87824c1c963f13dfe6072e76fe372eb18c81d65c Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Fri, 15 Mar 2019 13:38:19 -0400 Subject: [PATCH 038/390] Update proposals/1219-storing-megolm-keys-serverside.md Co-Authored-By: uhoreg --- proposals/1219-storing-megolm-keys-serverside.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 05c68520..0863bc8d 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -4,7 +4,7 @@ Storing megolm keys serverside Background ---------- -A user who uses end-to-end encyrption will usually have many inbound group session +A user who uses end-to-end encryption will usually have many inbound group session keys. Users who log into new devices and want to read old messages will need a convenient way to transfer the session keys from one device to another. While users can currently export their keys from one device and import them to From 1c4262e556108d0b1ada17c722659a6d010e5dff Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Fri, 15 Mar 2019 13:40:44 -0400 Subject: [PATCH 039/390] Apply suggestions from code review Co-Authored-By: uhoreg --- proposals/1219-storing-megolm-keys-serverside.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 0863bc8d..09ded1cd 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -162,7 +162,7 @@ On success, returns a JSON object with keys: ##### `GET /room_keys/version/{version}` -Get information about the given version, or the current version if `{version}` +Get information about the given version, or the current version if `/{version}` is omitted. On success, returns a JSON object with keys: @@ -448,7 +448,7 @@ On success, returns the empty JSON object. The `auth_data` property for the backup versions endpoints for `m.megolm_backup.v1.curve25519-aes-sha2` is a signedjson object with the -followin keys: +following keys: - `public_key` (string): the curve25519 public key used to encrypt the backups - `signatures` (object): signatures of the public key From 4e95f8062a8a493cb07a89fc53b5dfad9399507f Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 26 Mar 2019 16:51:46 -0400 Subject: [PATCH 040/390] add examples for federation endpoints --- proposals/1756-cross-signing.md | 52 +++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 6d5ddd40..68143b87 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -168,6 +168,58 @@ response: Similarly, the federation endpoints `GET /user/keys/query` and `POST /user/devices/{userId}` will include the self-signing key. +`POST /keys/query` + +``` json +{ + "device_keys": { + "@alice:example.com": [] + } +} +``` + +response: + +``` json +{ + "device_keys": { + "@alice:example.com": { + // ... + } + }, + "self_signing_keys": { + "@alice:example.com": { + "user_id": "@alice:example.com", + "usage": ["self_signing"], + "keys": { + "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key" + } + } + } +} +``` + +`GET /user/devices/%40alice%3Aexample.com` + +response: + +``` json +{ + "user_id": "@alice:example.com", + "stream_id": 5, + "devices": [ + // ... + ], + "self_signing_keys": { + "user_id": "@alice:example.com", + "usage": ["self_signing"], + "keys": { + "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key" + } + } +} +``` + In addition, Alice's homeserver will send a `m.signing_key_update` EDU to servers that have users who share encrypted rooms with Alice. The `content` of that EDU has the following properties: From 63c6d030fdbcee336e7df03e89d96a63c10fa205 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 27 Mar 2019 21:48:12 -0400 Subject: [PATCH 041/390] draft of secure server-side storage --- proposals/xxxx-secure_server-side_storage.md | 144 +++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 proposals/xxxx-secure_server-side_storage.md diff --git a/proposals/xxxx-secure_server-side_storage.md b/proposals/xxxx-secure_server-side_storage.md new file mode 100644 index 00000000..603fe64e --- /dev/null +++ b/proposals/xxxx-secure_server-side_storage.md @@ -0,0 +1,144 @@ +# Secure Server-side Storage + +Some features may require clients to store encrypted data on the server so that +it can be shared securely between clients. For example, key backups (MSC-1219) +can store the decryption key for the backups on the server, or cross-signing +(MSC-1756) can store the signing keys. This proposal presents a standardized +way of storing such data. + +## Proposal + +A user can have multiple keys used for encrypting data. This allows the user +to selectively decrypt data. For example, the user could have one key that can +decrypt everything, and another key that can only decrypt their user-signing +key for cross-signing. Each key has an ID, and a discription of the key is +stored in the user's `account_data` using the `type` `m.secure_storage.key.[key +ID]`. The contents of the account data for the key will include an `algorithm` +property, which indicates the encryption algorithm used, as well as a `name` +property, which is a human-readable name. Other properties depend on the +encryption algorithm, and are described below. + +Encrypted data can be stored using the `account_data` API. The `type` for the +`account_data` is defined by the feature that uses the data. For example, +decryption keys for key backups could be stored under the type +`m.megolm_backup.v1.recovery_key`, or the self-signing key for cross-signing +could be stored under the type `m.signing_key.self_signing`. + +Data will be stored using using the following format: + +```json +{ + "encrypted": { + [key ID]: { + "ciphertext": "base64+encoded+encrypted+data", + "mac": "base64+encoded+mac" + } + } +} +``` + +The `encrypted` property is map from key ID to an object. The algorithm for +the given key defines how the other properties are interpreted, though it's +expected that most encryption schemes would have `ciphertext` and `mac` +properties, where the `ciphertext` property is the unpadded base64-encoded +ciphertext, and the `mac` is used to ensure the integrity of the data. + +FIXME: the key format was chosen so that existing backups could be easily +migrated by just copying the configuration from the backup config to the key +description. However, we need a way of signalling that the key for the backup +is the same as the key for decrypting the other bits. Maybe a special flag in +the account data? Or special case backups somehow, say, have clients inspect +the backup's `auth_data` to see of the key config is the same? + +### Encryption algorithms + +#### `m.secure_storage.v1.curve25519-aes-sha2` + +The data is encrypted and MACed as follows: + +1. Generate an ephemeral curve25519 key, and perform an ECDH with the ephemeral + key and the backup's public key to generate a shared secret. The public + half of the ephemeral key, encoded using base64, becomes the `ephemeral` + property. +2. Using the shared secret, generate 80 bytes by performing an HKDF using + SHA-256 as the hash, with a salt of 32 bytes of 0, and with the empty string + as the info. The first 32 bytes are used as the AES key, the next 32 bytes + are used as the MAC key, and the last 16 bytes are used as the AES + initialization vector. +4. Encrypt the data using AES-CBC-256 with PKCS#7 padding. This encrypted + data, encoded using base64, becomes the `ciphertext` property. +5. Pass the raw encrypted data (prior to base64 encoding) through HMAC-SHA-256 + using the MAC key generated above. The first 8 bytes of the resulting MAC + are base64-encoded, and become the `mac` property. + +(The key HKDF, AES, and HMAC steps are the same as what are used for encryption +in olm and megolm.) + +FIXME: add an example of `m.secure_storage.key.*`, and of encrypted data. + +##### Keys + +When a user is given a raw key for `m.secure_storage.v1.curve25519-aes-sha2`, +it will be encoded as follows (this is the same as what is proposed in MSC1703): + +* prepend the two bytes 0x8b and 0x01 to the key +* compute a parity byte by XORing all bytes of the resulting string, and append + the parity byte to the string +* base58-encode the resulting byte string with the alphabet + '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'. +* format the resulting ASCII string into groups of 4 characters separated by + spaces. + +When decoding a raw key, the process should be reversed, with the exception +that whitespace is insignificant in the user's ASCII input. + +##### Passphrase + +A user may wish to use a chosen passphrase rather than a randomly generated +key. In this case, information on how to generate the key from a passphrase +will be stored in the `passphrase` property of the `m.secure_storage.key.[key +ID]` account-data: + +```json +{ + "passphrase": { + "algorithm": "m.pbkdf2", + "salt": "MmMsAlty", + "rounds": 100000 + }, + ... +} +``` + +###### `m.pbkdf2` + +The key is generated using PBKDF2 using the salt given in the `salt` +parameter, and the number of rounds given in the `rounds` parameter. + +## Tradeoffs + +Rather than encrypting data on the server using a static key, clients can +exchange data by sending to_device messages encrypted using Olm. This allows +clients to share data securely without requiring the user to enter keys or +passphrases. However, users who have only one device and lose it will still +need a way to, for example, recover their key backup, so we must provide a way +for the data to be stored on the server. + +Currently, only a public/private key mechanism is defined. It may be useful to +also define a secret key mechanism. + +## Potential issues + +Keeping all the data and keys in account data means that it may clutter up the +`/sync`. However, clients can filter out the data that they are not interested +in. One possibility for addressing this would be to add a flag to the account +data to indicate whether it should come down the `/sync` or not. + +## Security considerations + +Yes. + +## Conclusion + +This proposal presents a common way for bits of encrypted data to be stored on +a user's homeserver for use by various features. From 979827bad3ba422b01edaaa1bc49d54e0d0c5f1b Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 27 Mar 2019 21:57:13 -0400 Subject: [PATCH 042/390] rename to match MSC number --- ..._server-side_storage.md => 1946-secure_server-side_storage.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{xxxx-secure_server-side_storage.md => 1946-secure_server-side_storage.md} (100%) diff --git a/proposals/xxxx-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md similarity index 100% rename from proposals/xxxx-secure_server-side_storage.md rename to proposals/1946-secure_server-side_storage.md From 29a9982447a946e7338749031a68e1fa7937df0f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sun, 7 Apr 2019 21:04:52 -0600 Subject: [PATCH 043/390] Proposal for integration manager discovery --- proposals/0000-integrations-discovery.md | 133 +++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 proposals/0000-integrations-discovery.md diff --git a/proposals/0000-integrations-discovery.md b/proposals/0000-integrations-discovery.md new file mode 100644 index 00000000..c09551a8 --- /dev/null +++ b/proposals/0000-integrations-discovery.md @@ -0,0 +1,133 @@ +# MSC0000: Integration manager discovery + +*Note*: This is a required component of [MSC1956 - Integrations API](https://github.com/matrix-org/matrix-doc/pull/1956) + +Users should have the freedom to choose which integration manager they want to use in their client, while +also accepting suggestions from their homeserver and client. Clients need to know where to find the different +integration managers and how to contact them. + + +## Proposal + +A single logged in user may be influenced by zero or more integration managers at any given time. Managers +are sourced from the client's own configuration, homeserver discovery information, and the user's personal +account data in the form of widgets. Clients should support users using more than one integration manager +at a given time, although the rules for how this can be handled are defined later in this proposal. + +#### Client-configured integration managers + +This is left as an implementation detail. In the case of Riot, this is likely to be part of the existing +`config.json` options, although liekly modified to support multiple managers instead of one. + +#### Homeserver-configured integration managers + +The integration managers suggested by a homeserver are done through the existing +[.well-known](https://matrix.org/docs/spec/client_server/r0.4.0.html#get-well-known-matrix-client) discovery +mechanism. The added optional fields, which should not affect a client's ability to log a user in, are: +```json +{ + "m.integrations": { + "managers": [ + { + "api_url": "https://integrations.example.org", + "ui_url": "https://integrations.example.org/ui" + }, + { + "api_url": "https://bots.example.org" + } + ] + } +} +``` + +As shown, the homeserver is able to suggest multiple integration managers through this method. Each manager +must have an `api_url` which must be an `http` or `https` URL. The `ui_url` is optional and if not provided +is the same as the `api_url`. Like the `api_url`, the `ui_url` must be `http` or `https` if supplied. + +The `ui_url` is ultimately treated the same as a widget, except that the custom `data` is not present and +must not be templated here. Variables like `$matrix_display_name` are able to function, however. Integration +managers should never use the `$matrix_user_id` as authoritative and instead seek other ways to determine the +user ID. This is covered by other proposals. + +The `api_url` is the URL clients will use when *not* embedding the integration manager, and instead showing +its own purpose-built interface. + +#### User-configured integration managers + +Users can specify integration managers in the form of account widgets. The `type` is to be `m.integration_manager` +and the content would look something similar to: +```json +{ + "url": "https://integrations.example.org/ui?displayName=$matrix_display_name", + "data": { + "api_url": "https://integrations.example.org" + } +} +``` + +The `api_url` in the `data` object is required and has the same meaning as the homeserver-defined `api_url`. +The `url` of the widget is analogous to the `ui_url` from the homeserver configuration above, however normal +widget rules apply here. + +The user is able to have multiple integration managers through use of multiple widgets. + +#### Display order of integration managers + +Clients which have support for integration managers should display at least 1 manager, but may decide +to display multiple via something like tabs. Clients must prefer to display the user's configured +integration managers over any defaults, and if only displaying one manager must pick the first +manager after sorting the `state_key`s in lexicographical order. Clients may additionally display +default managers if they so wish, and should preserve the order defined in the various defaults. +If the user has no configured integration managers, the client must prefer to display one or more +of the managers suggested by the homeserver over the managers recommended by the client. + +The client may optionally support a way to entirely disable integration manager support, even if the +user and homeserver have managers defined. + +The rationale for having the client prefer to use the user's integration managers first is so that +the user can tailor their experience within Matrix if desired. Similarly, a homeserver may wish to +subject all of their users to the same common integration manager as would be common in some organizations. +The client's own preference is a last ditch effort to have an integration manager available to the +user so they don't get left out. + +#### Displaying integration managers + +Clients simply open the `ui_url` (or equivalent) in an `iframe` or similar. In the current ecosystem, +integration managers would receive a `scalar_token` to idenitify the user - this is no longer the case +and instead integration managers must seek other avenues for determining the user ID. Other proposals +cover how to do this in the context of the integrations API. + +Integration managers shown in this way must be treated like widgets, regardless of source. In practice +this means exposing the Widget API to the manager and applying necessary scoping to keep the manager +as an account widget rather than a room widget. + + +## Tradeoffs + +We could limit the user (and by extension, the homeserver and client) to exactly 1 integration manager +and not worry about tabs or other concepts, however this restricts a user's access to integrations. +In a scenario where the user wants to use widgets from Service A and bots from Service B, they'd +end up switching accounts or clients to gain access to either service, or potentially just give up +and walk away from the problem. Instead of having the user switch between clients, we might as well +support this use case, even if it is moderately rare. + +We could also define the integration managers in a custom account data event rather than defining them +as a widget. Doing so just adds clutter to the account data and risks duplicating code in clients as +using widgets gets us URL templating for free (see the section later on in this proposal about account +widgets for more information). + + +## Future extensions + +Some things which may be desirable in the future are: +* Avatars for the different managers +* Human-readable names for the different managers +* Supporting `ui_url`s targeting specific clients for a more consistent design + + +## Security considerations + +When displaying integration managers, clients should not trust that the input is sanitary. Integration +managers may only be at HTTP(S) endpoints and may still have malicious intent. Ensure any sandboxing +on the manager is appropriate such that it can communicate with the client, but cannot perform unauthorized +actions. From cc10444d4b3dceac24ae15648e2ab0f078fa99a2 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sun, 7 Apr 2019 21:06:42 -0600 Subject: [PATCH 044/390] Assign MSC number --- proposals/0000-integrations-discovery.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-integrations-discovery.md b/proposals/0000-integrations-discovery.md index c09551a8..7672e024 100644 --- a/proposals/0000-integrations-discovery.md +++ b/proposals/0000-integrations-discovery.md @@ -1,4 +1,4 @@ -# MSC0000: Integration manager discovery +# MSC1957: Integration manager discovery *Note*: This is a required component of [MSC1956 - Integrations API](https://github.com/matrix-org/matrix-doc/pull/1956) From dffe19bb76226f956d35d79e6e67c256e4b9a896 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sun, 7 Apr 2019 21:07:46 -0600 Subject: [PATCH 045/390] Rename file to match MSC number --- ...0-integrations-discovery.md => 1957-integrations-discovery.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{0000-integrations-discovery.md => 1957-integrations-discovery.md} (100%) diff --git a/proposals/0000-integrations-discovery.md b/proposals/1957-integrations-discovery.md similarity index 100% rename from proposals/0000-integrations-discovery.md rename to proposals/1957-integrations-discovery.md From 2dcda7d564d9e45538b95a08f5183c1628aff05a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sun, 7 Apr 2019 21:17:14 -0600 Subject: [PATCH 046/390] Add a mention that clients should re-query .well-known --- proposals/1957-integrations-discovery.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/proposals/1957-integrations-discovery.md b/proposals/1957-integrations-discovery.md index 7672e024..2eca300b 100644 --- a/proposals/1957-integrations-discovery.md +++ b/proposals/1957-integrations-discovery.md @@ -52,6 +52,11 @@ user ID. This is covered by other proposals. The `api_url` is the URL clients will use when *not* embedding the integration manager, and instead showing its own purpose-built interface. +Clients should query the `.well-known` information for the homeserver periodically to update the integration +manager settings for that homeserver. The client is not expected to validate or use any other information +contained in the response. Current recommendations are to query the configuration when the client starts up +and every 8 hours after that. + #### User-configured integration managers Users can specify integration managers in the form of account widgets. The `type` is to be `m.integration_manager` From 13d63685d379b68b4bb356822a64d78526ca65b1 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 8 Apr 2019 07:05:38 -0600 Subject: [PATCH 047/390] Spelling Co-Authored-By: turt2live --- proposals/1957-integrations-discovery.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/1957-integrations-discovery.md b/proposals/1957-integrations-discovery.md index 2eca300b..fd6bc8f9 100644 --- a/proposals/1957-integrations-discovery.md +++ b/proposals/1957-integrations-discovery.md @@ -17,7 +17,7 @@ at a given time, although the rules for how this can be handled are defined late #### Client-configured integration managers This is left as an implementation detail. In the case of Riot, this is likely to be part of the existing -`config.json` options, although liekly modified to support multiple managers instead of one. +`config.json` options, although likely modified to support multiple managers instead of one. #### Homeserver-configured integration managers From d6d0f9780dc3948f25bac7880adea6602650ec4e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Apr 2019 21:36:16 -0600 Subject: [PATCH 048/390] Proposal for basic integration manager authentication APIs --- proposals/0000-integrations-auth.md | 69 +++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 proposals/0000-integrations-auth.md diff --git a/proposals/0000-integrations-auth.md b/proposals/0000-integrations-auth.md new file mode 100644 index 00000000..1a491790 --- /dev/null +++ b/proposals/0000-integrations-auth.md @@ -0,0 +1,69 @@ +# MSC0000: Integration manager authentication + +A set of common APIs needs to be defined for clients to be able to interact with an integration +manager. This proposal covers the authentication portion of that API. + + +## Proposal + +All specified APIs (except `/register`) will take an `Authorization` header with a `Bearer` token returned +from a call to `/register`. This token is used to authorize the request and to identify who is making the +request. + +#### POST /_matrix/integrations/v1/account/register + +Exchanges an OpenID object for a token which can be used to authorize future requests to the manager. + +Request body is an OpenID object as returned by `/_matrix/client/r0/user/:userId/openid/request_token`. + +Response is: +```json +{ + "token": "OpaqueString" +} +``` + +The token should consist of URL-safe base64 characters. Integration managers should be careful to validate +the OpenID object by ensuring the `/_matrix/federation/v1/openid/userinfo` response has a `sub` which belongs +to the `matrix_server_name` provided in the original OpenID object. + +Applications which register for a token are responsible for tracking which integration manager they are for. +This can usually be done by tracking the hostname of the integration manager and matching a token with it. + +#### GET /_matrix/integrations/v1/account + +Gets information about the token's owner, such as the user ID for which it belongs. + +Besides a token, no other information is required for the request. + +Response is: +```json +{ + "user_id": "@alice:example.org" +} +``` + +The `user_id` is the user ID which was represented in the OpenID object provided to `/register`. Integration +managers may be interested in also supplying information about the user's credit balance for paid integrations +here. Preferably, custom information is stored under a namespaced key like so: +```json +{ + "user_id": "@alice:example.org", + "org.example.paid.integrations": { + "credit": 20000 + } +} +``` + +#### POST /_matrix/integrations/v1/account/logout + +Logs the token out, rendering it useless for future requests. + +Request body is an empty object. Response body is also an empty object if successful. + + +## Security considerations + +Clients should be sure to call `/logout` where possible when the user is logging out or no longer needs access +to a given manager. Clients should additionally be cautious about which managers they register for tokens with, +as some integration managers may be untrusted. From 0bab70c14e84ff71146cc9a8dce7071e1021d75f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Apr 2019 21:37:35 -0600 Subject: [PATCH 049/390] Assign MSC number --- .../{0000-integrations-auth.md => 1961-integrations-auth.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename proposals/{0000-integrations-auth.md => 1961-integrations-auth.md} (98%) diff --git a/proposals/0000-integrations-auth.md b/proposals/1961-integrations-auth.md similarity index 98% rename from proposals/0000-integrations-auth.md rename to proposals/1961-integrations-auth.md index 1a491790..863ea325 100644 --- a/proposals/0000-integrations-auth.md +++ b/proposals/1961-integrations-auth.md @@ -1,4 +1,4 @@ -# MSC0000: Integration manager authentication +# MSC1961: Integration manager authentication A set of common APIs needs to be defined for clients to be able to interact with an integration manager. This proposal covers the authentication portion of that API. From 2ae122903f1338ca82362dd89e196168164143e2 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Thu, 25 Apr 2019 20:49:58 -0400 Subject: [PATCH 050/390] Use the right name Co-Authored-By: uhoreg --- proposals/1756-cross-signing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 68143b87..17721055 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -48,7 +48,7 @@ key by using the new user-signing key. Otherwise, they will need to re-verify the other users. If a user's self-signing key is compromised, then the user will need to issue -both a new self-signing key and a new device-signing key. The user may sign +both a new self-signing key and a new user-signing key. The user may sign their new self-signing key with their old self-signing key, allowing other users who have verified the old self-signing key to automatically trust the new self-signing key if they wish to. Otherwise, the users will need to re-verify From 661d69858edde5d543a0eaa1503f0383b2ea2b92 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Mon, 29 Apr 2019 10:24:46 +0100 Subject: [PATCH 051/390] Add fallback --- proposals/1802-standardised-federation-response-format.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/1802-standardised-federation-response-format.md b/proposals/1802-standardised-federation-response-format.md index f4cefc8f..da18c254 100644 --- a/proposals/1802-standardised-federation-response-format.md +++ b/proposals/1802-standardised-federation-response-format.md @@ -41,6 +41,10 @@ This proposal doesn't address the `PUT [MSC1794](https://github.com/matrix-org/matrix-doc/pull/1794) already takes care of it. +If a call to any of the `v2` endpoints described in this proposal results in an +unrecognised request exception, then the sending server should retry the request +with the `v1` API. + ## Alternative solutions An alternative solution would be to make the change in the `v1` fedration API, From 97786da4ad9038a29c545315b10de4d305313aab Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 30 Apr 2019 15:13:28 -0600 Subject: [PATCH 052/390] Create 0000-leave-reasons.md --- proposals/0000-leave-reasons.md | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 proposals/0000-leave-reasons.md diff --git a/proposals/0000-leave-reasons.md b/proposals/0000-leave-reasons.md new file mode 100644 index 00000000..8e0710b8 --- /dev/null +++ b/proposals/0000-leave-reasons.md @@ -0,0 +1,39 @@ +# Proposal to add reasons for leaving a room + +This proposal intends to solve [#1295](https://github.com/matrix-org/matrix-doc/issues/1295). Currently +people can be kicked from a room for a human-readable reason, but when voluntarily leaving the `reason` +is not considered. + +## Proposal + +Like when kicking someone, `m.room.member` events with `membership: leave` can have a string `reason`. +The reason is a human-readable string of text which clients should display to users of the room, providing +users with the ability to leave a room for a reason of their choosing. The field is optional. + +Having such a field gives Matrix better compatibility with other networks such as IRC which use the leave +reason to communicate connection problems ("ping timeout", etc). Although Matrix doesn't have a need for a +persistent connection to remain in the room, having a reason can improve the bridge experience. + +An example of a partial leave event with reason would be: +```json +{ + "type": "m.room.member", + "sender": "@travis:t2l.io", + "state_key": "@travis:t2l.io", + "content": { + "membership": "leave", + "reason": "I'm in too many rooms and never talk here" + } +} +``` + + +## Potential issues + +There is a chance that quality of communication may deteriorate as a result of adding this field. Arguments +in rooms may result in the leave reason having personal attacks against other room members or rude remarks. +Moderators would be expected to handle these situations for these cases, including redacting the state event +if needed. + +This also opens up an avenue for spam by having a very large reason for leaving. Clients are encouraged to +trim/hide the reason at a reasonable length. From 62748a026d6688df514c7e0aad544648b8ce1a15 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 30 Apr 2019 15:14:54 -0600 Subject: [PATCH 053/390] Rename 0000-leave-reasons.md to 1983-leave-reasons.md --- proposals/{0000-leave-reasons.md => 1983-leave-reasons.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{0000-leave-reasons.md => 1983-leave-reasons.md} (100%) diff --git a/proposals/0000-leave-reasons.md b/proposals/1983-leave-reasons.md similarity index 100% rename from proposals/0000-leave-reasons.md rename to proposals/1983-leave-reasons.md From e71b3ac754963910ef76e39e29178ad3e40aab9f Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Wed, 1 May 2019 16:53:29 +0100 Subject: [PATCH 054/390] Add details to what an unrecognised request is --- proposals/1802-standardised-federation-response-format.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/1802-standardised-federation-response-format.md b/proposals/1802-standardised-federation-response-format.md index da18c254..bd8b1189 100644 --- a/proposals/1802-standardised-federation-response-format.md +++ b/proposals/1802-standardised-federation-response-format.md @@ -42,8 +42,8 @@ This proposal doesn't address the `PUT of it. If a call to any of the `v2` endpoints described in this proposal results in an -unrecognised request exception, then the sending server should retry the request -with the `v1` API. +unrecognised request exception (i.e. in a response with a 400 or a 404 status +code), then the sending server should retry the request with the `v1` API. ## Alternative solutions From 7bad359b0dd774aa8f41f117782d1c65c733a27d Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 20 May 2019 21:03:48 -0400 Subject: [PATCH 055/390] switch to the 3-key system, and some wording improvements --- proposals/1756-cross-signing.md | 221 +++++++++++++++++---------- proposals/images/1756-graph2.dot | 12 +- proposals/images/1756-graph2.dot.png | Bin 49417 -> 60244 bytes 3 files changed, 152 insertions(+), 81 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 17721055..5595b2b4 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -19,49 +19,51 @@ MSC1680 is presented below. ## Proposal -Each user has a self-signing key pair that is used to sign their own devices, -and a user-signing key pair that is used to sign other users' signing keys. A -user's user-signing key is also signed by their own self-signing key. When one -user (e.g. Alice) verifies another user's (Bob's) identity, Alice will sign -Bob's self-signing key with her user-signing key. (This will mean that +Each user has three sets of key pairs: + +- a master cross-signing key pair that is used to identify themselves and to + sign their other cross-signing keys, +- a self-signing key pair that is used to sign their own devices, and +- a user-signing key pair that is used to sign other users' master keys. + +When one user (e.g. Alice) verifies another user's (Bob's) identity, Alice will +sign Bob's self-signing key with her user-signing key. (This will mean that verification methods will need to be modified to pass along the self-signing identity key.) Alice's device will trust Bob's device if: -- Alice's device is using a self-signing key that has signed her user-signing key, -- Alice's user-signing key has signed Bob's self-signing key, and +- Alice's device is using a master key that has signed her user-signing key, +- Alice's user-signing key has signed Bob's master key, +- Bob's master key has signed Bob's self-signing key, and - Bob's self-signing key has signed Bob's device key. ### Key security -A user's private half of their user-signing key pair may be kept unlocked on a -device, but their self-signing key should not; the private half of the -self-signing key pair should only be stored encrypted, requiring a passphrase -to access. By keeping the user-signing key unlocked, Alice can verify Bob's -identity and distribute signatures to all her devices without needing to enter -a passphrase to decrypt the key. - -If a user's device is compromised, they can issue a new user-signing key, -signed by their self-signing key, rendering the old user-signing key useless. -If they are certain that the old user-signing key has not yet been used by an -attacker, then they may also reissue signatures made by the old user-signing -key by using the new user-signing key. Otherwise, they will need to re-verify -the other users. - -If a user's self-signing key is compromised, then the user will need to issue -both a new self-signing key and a new user-signing key. The user may sign -their new self-signing key with their old self-signing key, allowing other -users who have verified the old self-signing key to automatically trust the new -self-signing key if they wish to. Otherwise, the users will need to re-verify -each other. - -The private halves of the user-signing key pair and self-signing key pair may -be stored encrypted on the server (possibly along with the megolm key backup) -so that they may be retrieved by new devices. FIXME: explain how to do this +A user's master key could allow an attacker to impersonate that user to other +users, or other users to that user. Thus clients must ensure that the private +part of the master key is treated securely. If clients do not have a secure +means of storing the master key (such as an secret storage system provided by +the operating system), then clients must not store the private part. If a user +changes their master key, clients of users that they communicate with must +notify their users about the change. + +A user's user-signing and self-signing keys are intended to be easily +replaceable if they are compromised by re-issuing a new key signed by the +user's master key and possibly by re-verifying devices or users. However, +doing so relies on the user being able to notice when their keys have been +compromised, and it involves extra work for the user, and so although clients +do not have to treat the private parts as sensitively as the master key, +clients should still make efforts to store the private part securely, or not +store it at all. Clients will need to balance the security of the keys with +the usability of signing users and devices when performing key verification. + +The private halves of a user's cross-signing keys be stored encrypted on the +server so that they may be retrieved by new devices. FIXME: explain how to do +this via MSC 1946 ### Signature distribution -Currently, users will only be allowed to see signatures made by their own -self-signing or user-signing keys, or signatures made by other users' +Currently, users will only be allowed to see signatures made by her own master, +self-signing or user-signing keys, or signatures made by other users' master or self-signing keys about their own devices. This is done in order to preserve the privacy of social connections. Future proposals may define mechanisms for distributing signatures to other users in order to allow for other web-of-trust @@ -71,19 +73,31 @@ use cases. #### Uploading signing keys -Public keys for the self-signing and user-signing keys are uploaded to the -servers using `/keys/device_signing/upload`. This endpoint requires [UI +Public keys for the cross-signing keys are uploaded to the servers using +`/keys/device_signing/upload`. This endpoint requires [UI Auth](https://matrix.org/docs/spec/client_server/r0.4.0.html#user-interactive-authentication-api). `POST /keys/device_signing/upload` ``` json { + "master_key": { + "user_id": "@alice:example.com", + "usage": ["master"], + "keys": { + "ed25519:base64+master+public+key": "base64+self+master+key", + } + }, "self_signing_key": { "user_id": "@alice:example.com", "usage": ["self_signing"], "keys": { "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key", + }, + "signatures": { + "@alice:example.com": { + "ed25519:base64+master+public+key": "base64+signature" + } } }, "user_signing_key": { @@ -94,24 +108,25 @@ Auth](https://matrix.org/docs/spec/client_server/r0.4.0.html#user-interactive-au "usage": ["user_signing"], "signatures": { "@alice:example.com": { - "ed25519:base64+self+signing+public+key": "base64+signature" + "ed25519:base64+master+public+key": "base64+signature" } } } } ``` -Self-signing and user-signing keys are JSON objects with the following +Cross-signing keys are JSON objects with the following properties: * `user_id` (string): The user who owns the key -* `usage` ([string]): Allowed uses for the key. Must be `["self_signing"]` for - self-signing keys, and `["user_signing"]` for user-signing keys. +* `usage` ([string]): Allowed uses for the key. Must contain `"master"` for + master keys, `"self_signing"` for self-signing keys, and `"user_signing"` + for user-signing keys. * `keys` ({string: string}): an object that must have one entry, whose name is "`ed25519:`" followed by the unpadded base64 encoding of the public key, and whose value is the unpadded base64 encoding of the public key. -* `signatures` ({string: {stringg: string}}): signatures of the key. A - user-signing key must be signed by the self-signing key. +* `signatures` ({string: {string: string}}): signatures of the key. A + self-signing or user-signing key must be signed by the master key. In order to ensure that there will be no collisions in the `signatures` property, the server must respond with an error (FIXME: what error?) if any of @@ -119,18 +134,14 @@ the uploaded public keys match an existing device ID for the user. Similarly, if a user attempts to log in specifying a device ID matching one of the signing keys, the server must respond with an error (FIXME: what error?). -If a user-signing key is uploaded, it must be signed by the current -self-signing key (or the self-signing key that is included in the request) +If a self-signing or user-signing key is uploaded, it must be signed by the +master key that is included in the request, or the current master key if no +master key is included. -If a previous self-signing key exists, then the new self-signing key must have -a `replaces` property whose value is the previous public self-signing key. -Otherwise the server must respond with an error (FIXME: what error?). The new -self-signing key may also be signed with the old self-signing key. - -After uploading self-signing and user-signing keys, they will be included under -the `/keys/query` endpoint under the `self_signing_key` and `user_signing_key` -properties, respectively. The `user_signing_key` will only be included when a -user requests their own keys. +After uploading cross-signing keys, they will be included under the +`/keys/query` endpoint under the `master_keys`, `self_signing_keys` and +`user_signing_keys` properties. The `user_signing_keys` property will only be +included when a user requests their own keys. `POST /keys/query` @@ -153,12 +164,26 @@ response: // ... } }, + "master_keys": { + "@alice:example.com": { + "user_id": "@alice:example.com", + "usage": ["master"], + "keys": { + "ed25519:base64+master+public+key": "base64+master+public+key" + } + } + }, "self_signing_keys": { "@alice:example.com": { "user_id": "@alice:example.com", "usage": ["self_signing"], "keys": { "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key" + }, + "signatures": { + "@alice:example.com": { + "ed25519:base64+master+public+key": "base64+signature" + } } } } @@ -166,7 +191,9 @@ response: ``` Similarly, the federation endpoints `GET /user/keys/query` and -`POST /user/devices/{userId}` will include the self-signing key. +`POST /user/devices/{userId}` will include the master and self-signing keys. +(It will not include the user-signing key because it is not intended to be +visible to other users.) `POST /keys/query` @@ -187,12 +214,26 @@ response: // ... } }, + "master_keys": { + "@alice:example.com": { + "user_id": "@alice:example.com", + "usage": ["master"], + "keys": { + "ed25519:base64+master+public+key": "base64+master+public+key" + } + } + }, "self_signing_keys": { "@alice:example.com": { "user_id": "@alice:example.com", "usage": ["self_signing"], "keys": { "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key" + }, + "signatures": { + "@alice:example.com": { + "ed25519:base64+master+public+key": "base64+signature" + } } } } @@ -210,11 +251,23 @@ response: "devices": [ // ... ], - "self_signing_keys": { + "master_key": { + "user_id": "@alice:example.com", + "usage": ["master"], + "keys": { + "ed25519:base64+master+public+key": "base64+master+public+key" + } + }, + "self_signing_key": { "user_id": "@alice:example.com", "usage": ["self_signing"], "keys": { "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key" + }, + "signatures": { + "@alice:example.com": { + "ed25519:base64+master+public+key": "base64+signature" + } } } } @@ -225,7 +278,8 @@ servers that have users who share encrypted rooms with Alice. The `content` of that EDU has the following properties: * `user_id` (string): Required. The user ID who owns the signing key -* `self_signing_key` (object): Required. The self-signing key, as above. +* `master_key` (object): The master key, as above. +* `self_signing_key` (object): The self-signing key, as above. After uploading self-signing and user-signing keys, the user will show up in the `changed` property of the `device_lists` field of the sync result of any @@ -235,7 +289,8 @@ others users who share an encrypted room with that user. Signatures of keys can be uploaded using `/keys/signatures/upload`. -For example, Alice signs one of her devices (HIJKLMN), and Bob's self-signing key. +For example, Alice signs one of her devices (HIJKLMN) (using her self-signing +key), and signs Bob's master key (using her user-signing key). `POST /keys/signatures/upload` @@ -255,7 +310,7 @@ For example, Alice signs one of her devices (HIJKLMN), and Bob's self-signing ke }, "signatures": { "@alice:example.com": { - "ed25519:base64+user+signing+public+key": "base64+signature+of+HIJKLMN" + "ed25519:base64+self+signing+public+key": "base64+signature+of+HIJKLMN" } } } @@ -264,12 +319,12 @@ For example, Alice signs one of her devices (HIJKLMN), and Bob's self-signing ke "bobs+base64+self+signing+public+key": { "user_id": "@bob:example.com", "keys": { - "ed25519:bobs+base64+self+signing+public+key": "bobs+base64+self+signing+public+key" + "ed25519:bobs+base64+master+public+key": "bobs+base64+master+public+key" }, - "usage": ["self_signing"], + "usage": ["master"], "signatures": { "@alice:example.com": { - "ed25519:base64+user+signing+public+key": "base64+signature+of+bobs+self+signing+key" + "ed25519:base64+user+signing+public+key": "base64+signature+of+bobs+master+key" } } } @@ -313,7 +368,7 @@ response: "signatures": { "@alice:example.com": { "ed25519:HIJKLMN": "base64+self+signature", - "ed25519:base64+user+signing+public+key": "base64+signature+of+HIJKLMN" + "ed25519:base64+self+signing+public+key": "base64+signature+of+HIJKLMN" } }, "unsigned": { @@ -322,12 +377,22 @@ response: } } }, - "self_signing_keys": { - "@alice:example.com": { - "user_id": "@alice:example.com", - "usage": ["self_signing"], - "keys": { - "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key", + "master_key": { + "user_id": "@alice:example.com", + "usage": ["master"], + "keys": { + "ed25519:base64+master+public+key": "base64+master+public+key" + } + }, + "self_signing_key": { + "user_id": "@alice:example.com", + "usage": ["self_signing"], + "keys": { + "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key" + }, + "signatures": { + "@alice:example.com": { + "ed25519:base64+master+public+key": "base64+signature" } } } @@ -343,7 +408,7 @@ include her new signature. After Alice uploads a signature for Bob's user-signing key, her signature will be included in the results of the `/keys/query` request when Alice requests -Bob's key: +Bob's key, but will not be included when anyone else requests Bob's key: `GET /keys/query` @@ -355,16 +420,16 @@ Bob's key: // ... } }, - "self_signing_keys": { + "master_keys": { "@bob:example.com": { "user_id": "@bob:example.com", "keys": { - "ed25519:bobs+base64+self+signing+public+key": "bobs+base64+self+signing+public+key" + "ed25519:bobs+base64+master+public+key": "bobs+base64+master+public+key" }, - "usage": ["self_signing"], + "usage": ["master"], "signatures": { "@alice:example.com": { - "ed25519:base64+user+signing+public+key": "base64+signature+of+bobs+self+signing+key" + "ed25519:base64+user+signing+public+key": "base64+signature+of+bobs+master+key" } } } @@ -414,11 +479,13 @@ user-signing key must be re-issued. ## Security considerations -This proposal relies on servers to communicate when self-signing or -user-signing keys are deleted and replaced. An attacker who is able to both -steal a user's device and control their homeserver could prevent that device -from being marked as untrusted. +This proposal relies on servers to communicate when cross-signing keys are +deleted and replaced. An attacker who is able to both steal a user's device +and control their homeserver could prevent that device from being marked as +untrusted. ## Conclusion -This proposal presents an alternative cross-signing mechanism to MSC1680. +This proposal presents an alternative cross-signing mechanism to MSC1680, +allowing users to trust another user's devices without needing to verify each +one individually. diff --git a/proposals/images/1756-graph2.dot b/proposals/images/1756-graph2.dot index 8eaa1df8..a8074125 100644 --- a/proposals/images/1756-graph2.dot +++ b/proposals/images/1756-graph2.dot @@ -1,18 +1,22 @@ digraph { A1 [label="A's PDP-11"] A2 [label="A's Osborne 2"] +AM [label="A's master key"] AS [label="A's self-signing key"] AU [label="A's user-signing key"] +BM [label="B's master key"] BU [label="B's user-signing key"] BS [label="B's self-signing key"] B1 [label="B's Dynabook"] B2 [label="B's VAX"] AS -> A1 AS -> A2 -AS -> AU -AU -> BS -BS -> BU -BU -> AS +AM -> AS +AM -> AU +AU -> BM +BM -> BS +BM -> BU +BU -> AM BS -> B1 BS -> B2 } diff --git a/proposals/images/1756-graph2.dot.png b/proposals/images/1756-graph2.dot.png index 3af9270f094b2361858930630945b05e40b2c6fd..e7591f0471c22dda586767c61b87eeab2c0c8f89 100644 GIT binary patch literal 60244 zcmafbcR1JY`}R8`Wm9O_(vXmlUA6{IEt!>+O}41)Eftv+rD-KQd!$IH$X@j3eRevSLS?&~_Q^E|Itz+oLt#x)z(P$(2eZ7sE<6bjW>3S~tB z9WDNZ<6Z#%fI@3>KvRvfME-mKO-dw%!bj0o+jrbKZn(qg)bZxO(%q|8g&QPmAA2g` zueMihp9SBOCzmSL+2_UDfrjPl24vJyRnyV?}ZDS6BONO9~}12&zC!X;)K-K z)a-1I&!0cnO`kk@(t4uc_U*NwK7FE4-o1M_*ij&J{P^+vPoB`7K7G1Ikwrx-Wv{w4 zzjwHmsu0_Q^mLZIygcWL{?+>r95{0H=#9q4qvMaprvn4mD0uu`OLd*1o2WGMG474) z?;FpaZC|@~Ew1FRt_IdpHqThER??TyaXU^fxv`!v5 zeAp{4Zc~4Me|PE4Ra9*PM=(;Gvo*rKR?D_N9H|w3p%_6jflpf=PQ&Us-Q&LuWcz6u{_)&%j*U-=q zmzMU;$dJIj+UCr+co!DYA3x8oLN|1L|Ni}B&4!-*Qc~-%XuS{RI^VdN zmUyioZ+rdvzR0py<7dvE-8eBZu}L{5=cIu_=#3jx{R0Cx>j#3m4)=fiM%FisM^WY4 zHKh%PA6{o=8JU;}?95-qw24kcMC8<&GY_3cJ2xqBP;q70p1GRe+eUTk+TLFs1-VY6 zJV%cn4UdZp?(aXpmV<-JS*iMKZS6sQ@gnD!X=%*cwrvaL-8aC$cFlNqDfM|Xv!JeU zvd)VOb8kO<(6F==O@8#q*u;eD%9SgpPMn?R3h<7r&eEHo~ca?m><-0iqV`18J zZK=n{$9seJ@_D^@vE$&OLzPJ{9UUFhGc#{QM6j-F9N860SIOV-?>6LVy#4(8$ZZ@)HoSZP z{y|dG%?#7(vdT)DazAFj*RQ3{pFf`)y8p}(#FxC&FFs*m+LNjJ`wkvtSV^^_wzgK> zarjj5Mj0k{;e(g5_4)ae*DqhX@Sd+PEqQ~fp(Z+ddQ~jI?c2Az2M6hJ(sJ|j%f5WM z``F#VSRjH9JE?yDypVvPpzXrnt5}@!h6dJSj}MmOr^Z%RAFdu$k^8sqS=^=!M~)n! zTfMsU>9LftnQ>FRf7$!@^v6uv;@ z9e0?`ePoD}X$F7^NjAZl0A(D2L3u2=PA@<~bA(%96L7pke{_h31L-nw;5+;!^G%*>1t zV*PDxZADMHpM=BU;nbvvo%=O3XqA+dwD#{W`})ePbYN2A{9Be625&apxpPOqT2QU` z$y;IXaGryFEZu!!%D!*jD5?ptT6GnLbX}Wctg5Qg*A(pVM!FT&iSy6R-7`NwSx?8r zL>@VmTfXY*ZWW280lNW`nFtLs+_BzGJkl}E95ysw&s&B1%SXmYGNZajUuZHw%#UAt_4waX5F zeU(x<_vq21h^Qz(FG6V)(F|PyG(vJ{$6{JP1aE}R>qnK$M(qa_%XaCLq6uwqs^%XZvv514#vx^LqgWq#B(oKOhvU5C)Qo8i}A7T zt1_+*<&HbZY<~RMv73W~m&k6qxVY@G?NtkEJALX@Np0%<2=2Kg7u*8TH8R zk9v@j;wNeLfh5pVIab`wwsht)q#9^3XP7q$A3u3gaO+mOH?FpGT}vcFN38>C+X?MYY{HJ{cF@a~zD7X0)=hddZ`_c{9z@AI~K}toe=l0aK)~ z!LDNE7cXAee0#T!ZY9+V{X8~dVd25%G~TlE^4MWREZv3oca8BEPi0%If2^Ardg~V3 z_`=Hzve)MKG&VMl_1CcPIGfF|G+XaUQCQ0CO)FjuL{#x2)m3}1W)Ht}hlbKUIZH=J zSAxoWD&@Xq~=#RZ1QS>Q#EWkpfSc$M4T4 zcZiFJ%kJ4Du9Vo-Yf4W?XGA$=Y|M|x`u(%{RY^&SbWU?FGsSV>1NXR+%7bu5UZuj% z1IHUTY}n9(Rs4#5m3_ZgOI}V_5DBi4T1{R30WvgFXusT=-rim#B>#l7^NVw%)D$GP zh962-uU>uo?wyLey8>k|Ak1^UmzS@)iHeHuk&~mMD9w+lwzRgYoIbsthldAISlamL zfW)Pbd|S6|GoGIr$Df#Iz6-d0`*>uN7c-9n*PmY_4XJ=coEjJ<|u23+1XN5xt zTvajDl+x4FvtrAZEu`*LSFd`1SG0VjEeBEE{5O*B{(}cA52_K za+d`}$UqVpzsAbSIyN=sZXSCJsmRBNp0gLJ=hCH1-8)wCkEqA|q#&USjUvi!T)$o_ zLZ1_wZa<)d_z&`QvbVp96v4!!;4gYMn}EW)>4=Dk>jee!O0)g!LR+>7^6^m&3=G69 z{i^#`?2UXXK}$>9+b8t{(N5{EiQMwBv5|_RU*x{NWb(pFyr+Ll%PH}y&aLlXp3OOY z@?>+r1a%2krqWR14z8P(HFf3XRHpl^-PA}MC#tK<$TR~3L-)u?2=yu^qsi}eA3uFk zyL4%5(cd4>rE|nq<49=Q+DfdUrg} z`mGbuGeY?K2L!kz?ZCATA3tt!^=X;Rqx<*06y2svnwvQVHG-W-ru9 zsvzw(`*>C*@&q$IJ$-Gwd`?Y(d24HDx^a1Tf4{fkwOJao4{_$w0v~+v3xf7EHT`~W z^)8h9m%F)S?SuflSvWbXPx}G|47X%ZW4*69CO&w;ub`kXhARgRFcxcVT`PdsE18Up zuBfQ+lJFHdnVRMH0ozJyw`o-{YnR^1lUovYPCVOzw&JoK6*oYtcvXz3Zd81HNW8pL zk*yAZzqhydef*~;Zn)Xp`t!3BZ|m!;pPhJ)tzM`)-FiSxZ6%I+rN}#$#ykH?mlsk4 zEi+I1kE1T!{J=r^`S}P2i}A%Ou?~;9k(}Ne)9P-2eyMwSD0&TC5qbXX8PHqy=jrVt zA}j9K1XyjbcAMxAn8~hsx*Cl{$!wXR8rWx98XxS@Zzb*2sx^#^ z7l6KpTVG${?wm$(jeh)C_?VvFMdgL5W3T3{JMt?ZAG}va5vSBvSNkEO28?pjt+Z%K zH?HmIh^JY(Qe0VCIU6}xudTpgh;8Gp^K{fD-|G|We{~htqp-g7W#WEE5ocy*b{y?g zbnL5Qym;k`h?S(ge5^!OOY!1wvBxi7)NbPDK8;L=Vz8;{)2H*Onddie-YiyU_vhES zi$+EzwRLqUvRp{3C|c5XC=|^CS9s)cz3rEN_Xk!&+BMtX!NW3H<87u3fWj~e%)iZMPkS4%#~M~Qy)>q zI*9xO1n4va-@etNT*}_TJ+$7cJ$D6qIb4=rMDN9=t2JucrosG3dao|l-6$?DcGi*Q z@1P-=xUn3qXYce#TY64T^^+q>=|Bmof)Px~$j;aIdI8)HcfKk3EcS5!QFQo#@8gx6 zhTU_c#nrmX3!i#7eD`*^uzL%^fLp7$ik24L+!uZ~M~fR%Wm~q<+S}Wwnnqad+_8fJ ziA4i3uA-_cVb`yfD#$9Z`rpsCqib|uoE;#o*o?c&Q6WZOHE>JVp7NGA zZZZVXA2@J;0A<|B#?J0x#5TN9$Rs_u9$K0kXp1*(+O*u-a?5iFYKHbt zWp(t|flugtcSof6{leTI{w-Vl0QpVH_2jN@cW`h(>#1d?zI?surZdLhySqR3Hx_$( zrY7CuL@UyRWSHwbE&#yp1C|I~>5b~X()RXEh>{y5p0mNB#?AS5FAQ%>3Ba!v@&uAFB<9#PjMwM%7 zXr(N#4NFZQ0P&hNW!A+DzUV5;N=sMZrv4!zm0g}oj8$Bqwv5cpR})(1^y$*p)(sag zUL-gjASv$}-MJ4a&=Dw4Q&SEE)Bw}h)YJf@?ZYm*{`tj-5;mSfU+&9z)5nL#_4f}S z9A|XawDk#!SB{=9*3#4CD49GmR&`WGM~4aLaJO6oQnEL0k1bVT zU|_V=6^^@NhN1kbzWzCGf2Xz)%#S`PfW8#1Cio?43ZAL~dxB1>q@{%m-GjJ<#C6a$ z4p*5X(I?By!~?mBYnls~`?_S&m{ z007I-eF5wXx;vS+lcW!Jc>n&rDmDmRG9gY0-HEuv8UmEBa8{z}O|)hLn;H{0n~8%? zb^U&gG(r$anPSR-2D{f1g&UmQ5xg3BF0@AeK|efss42Upq`*i~D=I1u4h`)yG~@*~ z5f>NVi$(=h7TvjX=K|`?!a_rRpFbDzRxy6=lbw~dTliqK*DJI7{X*3I=UA5ap|c$) z*0#H3#c)Rfr@Im_**G*%g@uK3mHrurx31+(7Yk*PS!_QwbN9}jeS9qU@sxN#7P^(a zRKOn_UcY{QDD*Xs~Y$y7)R|47ts}Hv2h=HP_ zpeg2BzuuNRt$dTL-AZIYb%c8J5xEMmA3fi{e|hoo?%lh-=<30E`XT3<{`~SXJu9nU zd>em!8O<4y0HB^&be|>*e}7}OO5$YgDGFk{c@Q^kv15k~Rw_5L?#cQw~Wy8wl130a8JZx z#yulqVgl=xmo^nRj@&@*m>&K*`R=ne-H_Gt6{)&s1Gz(zzF?*HxK3>)U892Y*j|Op z3%!9Gk_RTn#!3+GJ->d19!b)WDlkN!Njg2y<>`c%n`+PmIjid6uzQvH>K@vEFr>1& zx{t6~MMX;JBOj!uhWh){VV!$XVtoPTn_0ED>;{7rr{FU2s14E02RIWD5P;*$92Xzo zGddcEjKs8Go6hMUJ@zCslkNKT>x2ddLk1Ef!*S$H|M%~p_}8YUrpB8L?P<*Wja@#} z#<*cO9dL0KAvHlMMc=z;jEbvx@d8Jq}!G&(kx^oOWh zgcgEiksGSzDt`9t+4lUa^zx3w1h4?K-+c09YpN-0mC38DRxq z4lacs`9|ULAuY3p=O@Jo%?Eh_u$WS`SDnXW&J^m6MUB!>ZSj{nY7cdXwf+C z#gc$w7(?i(o;w%+Guk_AzosT>ORK?vAh{OpFwjtn{9C&w2MZ_2A`Q3 zXN$IMMr4Zur%~URFGT@CTtquNJGFFls_yT*NzlOy!(tvV9zRcc7qTuld-UcIWN?lM zqlQvyj6RPkPmISBg17LgC+vtAZ=+&p8b~L;bGt}(3&a+8<%5^2GzoNzxqXGlKQ0X&Fj@yTzS;>Eq-GAVlanc92%PSz>iha8MBaug*900Oh&4xkdnS|G_0luUKtGP>ME2%Rvw;EFqeXgit*Uz-OKRIAsldqY(6-dXnCidly0aG&NE~DR8&+%aj~Li zYxHq;cIM;fUjYbiTMA14kbDsclLw`5+RZ;qg2OjBjiD+rd4LS)5 z0oswm{~&$1RT2ah0@x6vV_(e@`JurT&ri@F$`goNmPT7^0Lg6UXKc|$^6lEi_Wk?! zQw5Hq9AX9xmX?;OeXNZyU!n=PNy}JP=rS1|8yl3KE-p8>t!M2aPV^KxYtbm6LZKiN ze2$6_kiFN{RgN?!G}8Rs=ilAlmg_Ppy!iL0SnwuUHg~1OpIXVQmhauU7bTL4n%elv z6?!)}H~r#k+=-rxvM8D?8#b(gGz9vDoDUu|v^BZc7UUo@k;4cD4;>%RvL@idh_nuB zU}mN-%=q%u46_C8kPl8vIbdv(W|#(4uI30W=H)%l>a9HT^YinQM4f^Ng$FG~ zV*Qw+%bP05redISFhH!m7iR z7yakw-O;#i{P5v}z9!^fUp%KE5f>B8Y%!O&IrxE zlIzq&oy-l@q;oltnVGrAuAk1`-CZ1%eVn`#)6Shcp-^;eB1hQu1$rurubDJ}=Qj5C z1g!n1B6Sov9lL%=OQx)EJRN}rz+!?O1i=3$$2Ro+l29y5V^ZOs%b=cZ3B~&5#c9HU z`ZBC9yQ}{G}xB=23XRN`T_DfEw8U|@+i5}$;->n%+8jA|3hpbO~vaYaDa~a z)~+$k8$p9$Gd0-2q@~_@8vqqc5^?8_4-}M#o=c0^>yCPwn4M6MvVZ*i$+~`h01?U{ zKhK~JNSJ-tbpFBx{+&CSkUEbWMM@tzaiS7eKuI&m8v(4B=VNst2^U(B5J{^)egq&prP^68;y#HCAbsC(!TV`XJ! z_}#}o1^Hl7`3M)ZikTgsYrd0O`M^^xR@%-N0eM~>scgwM{spPhPz6 z4i08OR*bbgcKUPx8b88)bawJUnjqlG&fcE&Uv}JXLN}lh%(a4=ntFT`FL5gC66=Dc zBfFfO+#ZKP7D%VQNT>IH?n$O6=~loN@=kTZXi*USF8*o)U?Gf`78l?Qp&E1l)Y{d> zi!^#F!-VGG!GkQUtPlxyzc`!2bmq*NP0Ca%i<*EJMMXuuCp1vM&7Wz>NM5YvBHY=j zH?C4B)95P zx0WPpN5WOo{=@vq#tj<|!|?O%eN@xY)86c`4?TJL($aCE*JJ6pp`n<;Rj}h6AZ8CA zIkFFgF4`emjmr{|9mQ>@_v4^&o+LD$x(N#V zAiWS{5poNF&c!HX4m^60C+8X;go_$jO2CRUq8sv1B?PY7lJfzV5&CAd-2z2`XT)HF zBi-AEJ%8}@>1uB7cs=@?GK7s^%`SXDHbVV+^l$IM?3&VtF?*@@8S7`>Qs?P3z}UID`IH zMT|wAYySK>ASmc==ic2#8BfyEtQ$k26wlBQpAiDOw+mM!l$WZiD%j+rsRRB|JI~z! zkX%W%?|IVn5@cL&tlieFTeHmF8sCUlfkpI2$sC(?2kK6G^5ls&@3jnF`js~>R?nC< zfUIGFkdC7iXbo&^tye*;8CcHwfC$hE&YcLI9WZ&Et$l&NV(=!RK07!xw921VkfLDN z^ajgR>9Ik1go^Li%@B_qFbH_Oy3UnZzw1;F%%bX4dgNwMnj0Ff!>a+E?qiO%A=ict zD%#pZzZXkgpR~8@!=GY;!?ydXchL<(q5C$qaK{@{! zy3oagB)L2h;D?|B;a0iuI)k}*rT|VMym14N6qeI&W-xaPTLA2A!oSiWkRTX1iX}k& zneXbxmaKemcx$JjwzhVA7>G==pNPja@lw!)nD>p9pT zIMLX`q@}N2{y4Ku1x}^}C6Dc$2FQ-^cl0)>;3@IoAnYjczv00-!g!uR7-?)|Lx=Zm zjP;hx33%^N|M&z2siD-e{OPGm+!$pJHIh&X*xm~3#;M=GE3F$hXzC5IGuF7_{6TrG zT*w_~_pbVh%~ZHHf2#0JYU)l9*Ofj_eZPV3T|Z64fVyE~V!}Z!y(4n+(m98T?Z?kY zh8{V4HVE-Y_~6Z()!pMGBKUb`!Paqx#br70#~-t_eCeEAkePW@|NI+gZCzbX-R2`F zPx_%B!I`OOMi|4X_ie&B{N$78&s70RA{S%9wqW^yS{U~JL~v?dG0$H+6bETw#{F8* zf~=sR>il&03{h4JDx-EO&va97QgmHAG&H2>+RE>; zd3$C}8!O#PKxB1b1P~0w*f%_EAznqSlIUxo>X@LtCRRYC%9L}Cp*DhIV$9?|IyxG# zGUCN66K#+s1L>{s_xH%k{(cgcB438ijFp>P^qOk>%wsMJyq6U;jqp$q4;N@;|Rn=8{Gwc!IL$F*) z(A;p&pxWGk5J8yM71Xpg+&KrfA^l-7&~^oYg6aMcFORC|0}?BDNxCTG!8V0tMSz4p zbkcxItbYbzFv523#cE4SOWQa)5(XE*DZe7?As7e~fC(&>FIab=&jB@cX%|`0f8@$Q z=((=Hw-a@PEx^diitu-(uqy&d9Uo)~c>d%`NytVS8>nMYF$v5^W+jGIPzykFbWTox zc#2yql zrm0Cy0Rp@(VOmvEQ?nY<6!9QcZ(D8t154{47+8jvhXXMH;Yi>!Y?-7z0+MWasuv;W zHoZeZQIYs8gEvS80w+$Q{ZKdwID;#7qbV(cdju=d-P`xs% zN(I4)kT`JuaqYgu(#*PfyPVtxKPK)nEJS!z)G81N2jljz!mfA&td+;UAO~6WeS4sJ zBOv_1^#P6$WD9z}3JeCg%KEEnxdjDD03^1ywjFj->n8M`y!};!yz(JIF$lDM-~8+( z+8^Qu?fLf22k7Oa7UxhV>Fl_XdUX0#pr$iGR> zfeyw_vM;!y1lU=C>xLkuA!!gpWTD6v;EV;Vs(?kbyRYx&ddZ76(udHRyCDlyBE3jh zcPi-Y3fBn=98MU-G7}>adY2;r>o_;0NySA{jt$?kO2P2}&#wxPW+;zhAhF6#^w-Qx zHR<;B_Lf~OY}P|&R$iQ;R$iKyMB+fc@RPOwZnQW*i+g>Oa(zTxdUkddEc@ba z)5BJ_M=2_u$HAv8E{uBiAtJ%pmF9F^3&Pvma6i8XXzBi^!-+f#05h7ZF;JAH@GpXc zbl48ZajEChoFO4K0G($3{!Bw3AIBuSFbmnlcBpYPT*@EiCXyRyHY^TSp%jdh1%vCC zsNQ+@0|X23^73v^uh|Hb0esSgwcTo6+0byr{fjitTWz8;Z(ntoCemti14SY6d#2N< z0ig)dgIAO11md#j$iK73qA@l9Kq8O4;|`rfrI1gbjw5%&ZMh1KYbltN&7ISnhVIu1 zwbNB>*lQV)dNXj?YCBqc4h{}tTuwb#z{1VV$jltZ3q~dl1b)yTFoIkgH}+wp zxMQ@1*!UrofQ{V?5;1a)%akw|tSl@PNl8iM@rOl4i?7>Y%v=ekaP!d;?TMBp2gEv5 zO(XKoVq#IwZrr7{wG70z0}TV%lyL1}MeWbnz^YaX4hyh9TGEUO5GLL3+dG}{3;iEj z{POmCIwCMsGTEg?WH$HnYO+%(+&Cq#UHs-_u&$2c0a<25R`B~B4;%B z7eQL)K$;+`5R3?<>wwJHY^93E87M3~GV)4V3%A{{XACT|7eB;p8+Xj@?(X)&^I3Ei zaTEO~DCoeeV!zO3Yc7`r6*wcUg9j_nCCb-LxW&I_w!i+@VF26g2ad>OwDS!eBcspF zo76Z&o4<4dD-y5|o+9A&zI-l?e8%g0OCIaQze9mK1z^~08#CJwu=!-pBf*NNNH(CdhZgXQFB zx5K6JpzZKChw&}=TaVEQpU@HWL`7NYX?4JO^BqNh^(MKDwkI*p4(tGyoF@it>t3+?# zOJpHH72u*OBni?4uUfSVvmRNl!`Z{lcc*Oc0^%zxCg88bpC3(V+lamjR{COXG=IeC zIV1z(EosTWdZmCj)?r_JXmaHAVNjLC687Ws^Ssc;?Fi^;SemQ2Okosdpd8r0-*rd{ zn>!3EQm{l7W}T|g<#ajEK{tNoF*l9bDfR%h+Q!CPzen1l(1S}>aUIanv64QAw6g_4 z{HtKJb&Bik6=f-@J1_O~Zmlr#T%1jDo`V#-3vV`!kr~_!f{hU+IzFCz4yWQ2ns@uF zS1(XnKu@1WPjukm!Sf(dF4}*uHUHgW`87>+Bk<*q)fS+H~|0%X6JmE{mwd>vN7bfCJT zEX!0V>UW8qTcFy?eP3u$P~_Y3HdW0jh$U3|_{9*(h&?fp9o+3j9vh)ZEDI<5gB*Xguf~BLA0;%jlvE`Q zhVa0*_9~G%1}CTLEs5?_&@j=rV(8}iXB^sve`e!V_!TkML$FlE$kyGR=hnEm%sRRR zr@d=yeCjysu7p?=Ui9cLxeiu*ua#9Q+@dadV!y17jJ)7iFZypGifa)>5Ld2mJN9^Y z-W$>S9B|jrD~WXjn)*r#RH8u4OuT+N5-JUm1JGHkzXc|jk6=pTU2%R8MrB{FLJF-1*%&q+)Ga7bCUDZTf}UNlE^A-gRF)AWEsHobLUZ zNbIBFL80=P0@ZU#n0PyXa7Isi4cHLs}VSGB&rHjZ#kzrne#sReM56|HTEetwBaEvTigiD(!g~h zG3^PlvGjt1g2U~3?8IVk%WaJYmEca?wu(50dwQ-XCkp}OJ*mt-(`JB2#c={*cat1M zkHDay2ag_o{$4E!w|yNy(iHJ`l?flhU2p z3lNTZNK1o84iip7iW|I5&(h&MuT{|WuYpe~ODS7^4}nESk%5EtK>rMkzQkqYBUUs+1w95}B0@_(**+;^ z*vhE4A^>AdYWn({nvcYg^#x%LYcUze#3;cGRBx!%Ll^(h`>3FxQ8{1B^p1|^60bVL zt?l}C8kPg2U}MOPho);je5eA5Ia^oHb;U6j4HzWv7=3V~2J&KK^A-DGzRh*}vs2ot z-5;s2=y&x{TAyWGRAeM+3^4gqA(BCHemUUwf8E(~9hs3KC@>_z(M#>`pMeYr`_Uf9 z;b^tcC26@#X6pK&R)8>JvlB1Tl&h+ii-gUhu>74`n!2sTNWpAmNv>^QtK~bR14IX^ z;$qsghc81~Yd}NYQAQklMC^~aqqW03Ywgt-wn|O4dTw6{5od*h@^g~|G$ZJED#J7( zC*d||M=W2z`I3!6g>pFEZ2&I|Q6Ki$bbC364&j)tpr9?Jc%gwEAKy?v{ik`8C6E$}a z#eCTR@_1irn(Vmr5{{Lw;??S(L3xf=_EeI#X!&^lnx)yt z1`lvA8=&#FV7LXm^Eh1Pm=Pn06l8~(;>ELzzc$d%WG5)aq3#YhkvYM8l4j}f(+6aO zeD_5^(hXE*p5YnJ<8{;eEZb@*5@smR7RVaE0`hWm;Z^VKOgLBQ z907|t$oYF9C1zwV6~colp_EvU_x%cHhKn+dWlD#9`3jx1B#25{FAoSf!(cw@LObp* zs0lQPv{~2uf1TtWMN2h+p*}u7um85&4Z$yXka!Hu7ItjiYJ@$oK$rU>J6rFeF{0?Gql6wGM!%lE}$rHf;>Vx z!X#fXSUoap5G+CdN=Lp&_w4OWz)`p_9Svyj?tvk0`uzDqX=&*TI5~f6_49(L0Q|7+(;xvw zAR(ldTJ)}UdM{4#D+Qf#vQXZ>J@E9IGrQn_k-qftzRp#)N7#k$KYnai66p@w(lxjh z1h@&1tPx(tqB3wDw*gDIjH*Yz{WM9v(&mjq!kaf&;(Dbu4v;&xW8A^L-mCq8ya2Ue z7Y3NDV{HNhhkrfG%DVmXRK|^QX9a1G9;r?KqK%Nuv`71aDp>@ zut75iff|U4bTC?c1vtvFiHV1g%C&MzxysQ2vEpgqWPzY|mtR#-5z@f71u!m*(nlfojVdnTLowd!K^#aSuZ|Uet^M$U z@YKKqt1tqT+Q(9pBv6n-X5uiUPL=?@1mdW?ql0@X?9E(gP;cwps>Rc;s~&AV^lZQ8 z;lSPN4wtaG2{dq-26R3T(6(|nnW*T$eDI-6OvPGuDH-AHjb{&!UskiwT6b9P5`}f> zeq-CtxrK!u>wyT$ns>iSTsza6i@Zc76Flcc^ad&@jIjK0!xb+r%A;uAhwB`EehjjegWg4ZjmvzO-#)pC$V>o` z;C_g51x7~|{Q+DO(Mle2#AO5YU1U1(4By~DAL9?n7sukTp&>K!7Yt3dmMx{F*GKk9 z7NQY{<}}bcSsBDhR2+tl(hTII2S^>_7v8OfMm@p(`(6jeHbCR-@%X!&5K>N?H1bbH z)G4fI#h48v1ne@Xe(+ibfu&T1u@#Oc%r1N7EyVoI^!Rbq`i=}}Mkzru%dkpO^m_=yhw#uLi@+{xNY1H%~wxD3{zMOG} z@;cD+kdiSROuU|5wp>HT>e7OWx60-~q7a=Of-@=*#IXA~MHmYrgft`;GFAjSiCAMm z`4=TgE+j*u0)pF$@5!J^|G!3o-O|!j6j*?Axy7}ON4mR9*8EsO93nDS?L-#tmGXwFOfV|mhmktRx=RE7;>p7U-@o?) zUQ)a;lv9Gof?5snus75|}5`QYs;afP< z(~cD8G`)+KmUZhoZpGsEP^O^$S5Fog2_Z1Z_jfQfnHqh!9Se;iDQXH@Cm87l*6hMU zqwm4g()9&Trd^QBu>$QMZY94ZPA#W(`_cNts&22IgHN5H@RLy{knCU};;S8(Ht1Yj0s@R{h$%<_Oal?M8VJ%cLa)U-C{z>8>GOg_bts zRJ-K^;XE|__VoaKUQo8N%?~U#1b_%7lTM&hU^Mtz^J3vXf`M3D}tSz2}sDS@IwZX-Zp?i>=Vl`99R3na*{AxLBIJMhz7lYA@y zdA4WIeoS4Ur=U&1UXEs|P7dmlz1b)ooZ?i&Kr0a8FY=w(RWO(Hv;>xdY6vh5n4=rI zN`8VH!=Z`S&6^gPU1*qo{_-VpzmP$qlP8__8ZM-zA$Ibl_2GO#3!~%N45182OkYCO zya^<7Untvtl9jPTeyA?X2ZS4I8VrE}CI^f`4h+RBk?&EAy%R|@!pBwIHI0*RuKOB1 zk12FjjG`jF$$69#2!mPximDVY@w{pvKt8sB{Bw8v-=9Iw#_NNC@WQ*Yd+v8|QVHA3 zH|Tgt&E8%T+DCqSS4+#=-}9`jU?JV+w{ZmJ{h7SP-*Ig6?pl5f&HX%{1zS+r+qeD& z^#Cr|WedmE8W$ms<|PT?9e!p}&s-|FJO2Xp8mTPjeIqQ=tr;kH&3Y&Bbt{nK*z8N@ z=C;arZr_#*;l_mdXm+*Up+hg*?pcy2j>A75wcb>2(}Eg+GJi2L%$`E z${HS>vaquH?s`K=8KZ#mzvrc8WJvvg`SPXyzV=U_ym9z{4J6TIXbJsjWM@!_lRjjZ z*_9+@I8#z?l8-G##NcxWqf_ci_ySGp;i&oJ$QWpYPz=cE*o6z%qWvh!pjV!$Ts+Jc6AaLBoZ8&##C03Le4&N0li1Nuy}rDz%QkxP zkKa7QDN`;Aau#epRUlF_&eyQPhQS;|wb9u*6W^2JMAE5tTI*}fr4a}Ro&;D?AgD=h zp9nU+^OAP^gxJVuU2JS^_u)Lkc7ig+WW`lw1fR?e;B21iW0DNz;uHM%N()THgX4u% z7i|EEBnK~Cyhv=D7%eM@hzswd!jlxWHLF+egMNX5iP_LHafWiOBj_#=nwZ6U=d@O1 z4ZsP*8JL8@h*VINb#lbd2b zlhB}OYwon=sjI4}eEjmo7foeR68Y&-n7BzC!Ul|m9Wo{O6c7 zJ!iGkqca}OTRFby!nod;%QwOvhI<%+h&iU~Nc6QTyzAbe;9zfj`9LRL&Ii5=GK7I1 z$G!Si0J^MsNWJdWMp&ZcS~zrpl)lN+{(D?4Fm8nHGH~vpf$^G8M1)vv`{eH)#MCSw zoGI`=ppce}GYG0f-h~9J=H@0(?plGnmdJv?##ac)+#H-3WV8_WU))s|>^)KdBY28U)Pp?9Tb~M&Nwht9wHrc46$LCD#HmC@TY{ z5Pe4mzo4D(x~~kQ{&Zw|3b+SuRsxt|9Nc%{K+x!=LV8>b&KRV^nSY@au$RxMFY_DO zXoGJUbiyY&PQALwNWN4BncdmVZIb&+34~j9e54KHTh)I-Ulqn12Dev{srZ6F?kI-l zJlm>c4f&+Y=g<6@e>sI@ibzm0*v2IcSKmV)=JI4ZlM?%t(^(U=H# z@ENH=$^wX<4V5xFl%Q}AS1uQ~vqA)e$v4s5`Hc>bc@N}GDNvY%Rr?+iR*>E7T(qYa zIdHZBOu<8i{aeu6m<1%<07manBmS-?zlD2@Om>lv0}vIPG-!w;+;8hj8X5q{6%7py z_{$^?-ZnKw{yx9_lt+n`5FC;TU^^&Ub$s^WOX@j#8k+2(-*@2^g;%5{oR#dv0N8u1 z8-#^1#O$gUjmF97Pb2}pmPffkh>&ly5s)PR<^I~`Y_OGu6`2}8-Znlqw%62D5T>UZ zr?roH(}CWdh~^98k9_0`n<0qc!{^Pu8q5T}BH>(J)f z1jUW8k&;30u8e;2nE$bJ{V#t7fH}mMC`z3emfzCl^8ZS;n4AkAJ!K~%B(xIlCiGALov_XDwgI82 zpw8ojnc;*zu1D;Vp&IyPh#rqmoM6z=3l*oUp}!R_rJmv8THjz~l(!kcQ54F?T>`7{ zxKrBLMOc>zu}O+18PFv7>WjUgkdXJk$U6uQ1=St$MZf%-$zbxUz{6WBU=Sd|2g5h! z)3`~bWBs}s5=8K#fY6m__5SB{!5#%$#7tnK5qMB;XG5pkkHYj0 zVqoJgR0qLo>?F2?yraWUgbtVFMF#Tu13XnP%vkVbb<1;1N6dy6u~x*~ zc{>ncG$aOPG8b(fDm-37fqHWhwK(+woPHuJk@+_~dpSrr@^MC|9p72WG#>`pFl-52 z695vY2l0+?W9-FYLUou8eJgI4l99oR?@SCk7~&IF6ttMHn-&1mU_iWkT3TaK%lAC= zbAqP^cGotUs7sdz1P51iEl$he9FZZvXjo9XBBF~4ZiW*W=#qS{0tguKzIW@|8~)-z zAz(~`^?wWwdclUCqZx#>&^$CsgawEmnSoVr`SEEv;3o2!0oWV>q$h8;*s#+6uk0SQ zu7#R>0DZ zVCH8-D;-b>*Av-$?OL8L{ohkl=WqKPC+K45Zu$iyCT=9!cC*wN`5MbQgKYV+jm{aS2AW`x5yv)41D8- zzKr=P*Y{aAP~Ia)1cAwSoXMiwzu%&9=VQ;7mKKsLcKK%roMj?kv&5)7g@@xTQQI*I zLP4Ho1Vh!5Yb!_;M^qj-Dks-RxzHj(|C+XW4TKt76lQ#$Od^RD&+mdzDc2#kcForp z0tbtCp)JJQ%J9ld&ZG;th5sBJgzIb%us=GM6`*Y3AzS`76B=f5nb~K?a=2!JKg4ZnGHzb|DK|FYvd~_2_*4~A~3Wi!cY~X-+ zZPP9(DRscnE{W)riooH0I{>5%-N$K=?DG{KgIL1YQz7P!bmRVptar!kTrhUuwokdz zL?X5f>!F6PQ{Fjg(Bo_QIw$AV1A*Y-LeT#GOOKm|vtYPe2+ zh4mA;gb265K`R618^HUTz$jCN$9V$Y6@00No7qbd!P!U1m5R(1Cg(KypN#S;%`|M zd}(aFfh8?alb_VT<=&|b&pz5T{CX%`bhVEPc%(9r+~n(*KqAoAzkIvYm}IGgxdWU{ zcuc(U$MUP=ht`4qKn&vhxEVwItN2&ce0}6iK4pPvHR6y)9wgZm&1Fj0PlO>l;6wIX z7|At&%F)`PvBK9s{NWie21D6Cif$aW1TkiwR()s`)!uWit;@BL>h}3TFDMaU{77zs zzYWPzaoVmV*dn0P1PAFMb`dut^Yc#N>q&?7$LZKHb~H2v!<7B8+1b*-kT5y;U_tSr zLkjTnIV(bH;LO~+cRETS!Fc8{zF9)3lRI~MprGJd24Tt=miXA(8i*F*-bLJpOr@i& z5epioRq#>KtR~%>I&{nbQ44dMJU@^TLom%SIE*-Cib`&sm)IIK3}*o$LjO%l5%vk& zZ3FsRN0A9}NnGvt>IBrt+%9BReB*)UKX&Ec%oB0-xK0Z#{&hm30F#wlyjgJr82C#T z7X7YOo8R=IktKBuLq9lGhv8<1bb=nz)+ZAvg3KBI`_^*V|Do#4!*brgy?@h|Q05_$ zlz9rtR0)|v)HYKQWr!kDks^{QbI6p6NM;cwC50qJA(a#v8l(&*kW%wxjbK$fY(SiG`AWLJoA0!|v0^5^Z_d6?-=@bU%dLL{~FJ5uhb@n0$zHDMXl17Z`9h}FO$ z_^M>@hVqMnCwG-wbm`d9hJeZJ@>N4CJ;h@4;>9%CkZd935^n zdK5SKyiXLs({J<+m8RhR4j0j;p$s1a9Kh6Gn# zpR6GuIS!ubE3+~);}I{TCSvt}_?g|uvs3f>0H&K*pz~no{yxeU88iu^0H#wy`n?gB z8WseBPy%Q(Ri)Th&@bXQscAy&!t9qW9ldm^u3+Lc@?bP4u3bBzv3Ut-FfAdHW=WYw zBV>JH*uC#sf1PARNonJ-MrS7F4(b+#g4$8^uhWf;XPJg4L^oQK`R&`cFYsX)@dBff z4mH5E1~e_kAp9tZGqDA10Fq&gZ7a#&zHjQZX>SGAN2NqhkbX!oAsTN~9m^dbAc*YI z&e6d^x$Lq1y0aH9e7@Rm(u^7RBQCRT6}hqF+5Azi5FZC?WozG6fDR_$_t`S=^(t?= zu$)p$JAh;%m1!)AP8f>IPn8V-yCyN4V!*}*@g_I7)XMy8&g<6=*AG+h$AD$%Pn0mo zQh|*S&Zb8XA!1O|a|Xnd06__B)5XA8wl{Uy)sgWN-?Hz0HgB%`V|Co7;c82Pka(_=LGb4yj9Qd$Hv(v# ziXYHJqfHFhRMgk}Bdo%!$G#x}^X=>;r9LY|O)Im>MXkL=Dd0c7=8zFj zH@VEt($0+t0qQhVeiBv0c|eL!!#Q*2h=3VJnWr#+;gj-zsREHGZ=rsYTqbd7gJGk& z>bs`gflK7_h%e^CE7DvZ9lEX^M4Sm_h_D0%f})E~V6{*M@p+y($0T&=Asd@83p$hk zylMPZDaLpd;CiD0wBXNXF8iQ(!6k`?dqto*W-~5rajuaK=k~3%)Mm291NA1s9H|&g z@|Hr*3?pR{8zsD3qG#{u$jD2_kBmWqB5-?oNluD<Ar zlO{FI%*tZr#YF((_vT#us=2>llSS8&@P0fqiF-zTBj+-pzBsWPtE+E;%CCW4%c8=` z`j%9oR=sD_9H$LT?GgU%>(^3}1?D34$3VIXO)z<#yMUXUz(1wV=#;iY?3jFAXf@yn zFIs8(6>&CzF#L)#SWHW`fwwERKY1e5?{f9ce#-ls=p58f$&$980#=>{wZ1TK1Wm1a z?S;P1A4nIM4`nTgx8(3v&N2x4srlG$}m3S&Pcmk36=02Z3XMUGHYT}-hpewXC z3!$ML;|$5=X;XFvHT) zmIGPjB!G@TcXss`E51NoXg~x7`_RZ@D7aV<9m!rU$gwkL9-@2bW?*ob+NRR>%l784 zZm#wdKY*UTen-CEEVMZ-nl~@iJez|hI}N#}IR#V~KJ8(n)q2S5BwM{==e_&GaPc_4 zytJOpZ3V9E;H%3~oH-oW*fI-0r0ittO9g0SZu6q%r#{sOPYzIRs=YB`Gl2$aEoROK<{nM?Nh z`6amNM>)N{Q?K?gO7oh>T(HgI@(eSyZk@ST;eSF{Sz9QkD-GLr7=aIpe$^Cd6lto% zQ7i`CyEi)4wrm?83(dyY^776Ki8R}`eS1e59)7igt00(&>@>XWrlUsr5V$yz62c>~ z%_XX`{LsL)$LVYQxaS`o>bDs&aLP^#y^?j%cpVF zUO#NuvYTI&nNL!x8RiGb>|Pv^ZrZ-F1-TDk7GE^CwzdNkEY7(VNHMhtZ`M^bTpwfo z2M`yIt{oryR`Bgd20--UVp|$L3&cD7*1EtFQ}1!;L~8|My9HqIOpDfuj)@6CRXf0@ zf$nJccG;KPbhSOQ?pN`^Hq5LbZsI9rM8(bo)fFuC?;s0%*@LY8`wfpMn=^(8(AQmQPkMa)IZS7&z*zwd2{c6sR4lvpf;?|6ohvgMIhb8|k$TU_<;lhi(?sKtgruU_j;l|;{k*18}YTdl0<7Dt3LnoC|9%@Sj;wI6N3>$Pt-d6 zfscLKsbI+7tVF0^NjgO8UcNdCkE0LC>x4~FQggYe;BpYe0Waim7KTAPmr)3Nw;C+}Zbtw1hIkggpC{j*jF@0t2>UDurkt%$o>-vHX^(M`yQ8Q~PjZWS(%8Lz9SSdf z&$)v>qphHlm}0qt?%Tysa*HJ$$69=Rd@1)rl3k1k9b-I~Gd!+je?Yj5D~{KJj$Va& zGAiYn7???WDlISH1eb^eGkK!nrmE+s-X#T(*o%eTN%o?2JSAgV4l#+9H3FN3!iv=^X6}8oz1}m_8mUl{YshfxXi4MPZJyy z8ISPi&yQPfhtG(1*>3UTaimNir(Ex%JEm*%z3@|bgon7ajP>~W-k(-#G;+=Uq@W!D z@?<|hK{+=6ur0z#Ic>n*^sF!T95s6MUTR!q&8MelOK#Pd^7cH+(T)N;e=YL1o?iDmhU9^VcT+wuV6}g8enDgDJ^DmeJ zeoa>$9g_p=w`|EjfBm%%r4Rj6hhU4B#m>O-`A9I#BYtBd_EYQ*44f1C%Xiy06@?l- zC+O{vRi$2vm6qpN98Z#a-!fC=Ur8}8`W7A-LeH?Z&dO!S(sHQclXxgpMZNvXKtse@ zNyYueAYuL0>q)e8TofI(v;voE?eg{QptuTbKf70Nef^bBT4b=(nyah74D$rDki(o% z$x$Cx$Nv1F(NyK1l>|o}x@egRsWO}k^Qyd2hUYOJC?gwwPASSSa5{}#h$7U2y$Q*h*lxg7 z3yU~N^SO3*8^O9z)hra$C$8(Krxy>5Bk@;Q7fd}pK|f{ok7Nyk&&`6x z-`Vq85`E=ykOJ<@{PN|0TcNkqBNV)!C!et8a5{Zb2g` z>2x`3e>q-Hu|!Eru7pCtxw?wm>XJ**w1-CrH{&2&d&VUPy&-i20dd|34!mdn4p>WsEl2b)dFNs~R>P_Wvkl;gFbSDN`NwX3k zUz=_r4j39Gk=#}?7zu2I2^^oUdg#PUq~5Kn;$^6Q&!zGBGdy^t5cqM1n)982dVZFuRbKx4g*kZvY)3-lb*fb50jM3ikEG2H!ITl=j$c63rq(!6+UVM@D6)2D}mK!lw?F)$=%0!4IV1!vWo zPkL^jUJh3*g55Ef|S79!Wa46*SR&LJE?@bj%7b-=pOUsgOUY|JpKf-g|dnpu~)k#8C5Itc^jeBFlR1a>_Wd9e(l;R9D|d|JEG3n z4Zt?j($dYnijac>QTqIGb#Xa~T)eD%GOb-wYHE1e*LLYG-4`!pjc3PRztynSOtuj2 ztLXG1znjX#LW`_Hivi1~@2WnPO6zqW$FADe{M%(`&^pnMZtjS}!pv;@ z!!+CrX+199jIZ(}I->S43aFAV{egPrx}tty8Mhgu+ifE#Ubn7Yw@`T|acOe+n~S{U zSo#qi%e{TOc0J8OxOe?*JKwwW^!oG(;-!pRTy&7Me6gG^PA*ZK>EH)vsI4&ny}@_O zmiCHwpFjV(<3g(4-mP25hNfc{{`ij<4ES(R^^mZzu|U;D-;W|SXsXD>BGaS^5whDA zPY6iKC|FIEWLzuP)p$$0&)dlf-MgjTu}_9f-I&^QtA-cvK7K^KJ{}!d4Kb7gE0c- zG*eL4=YIah5~`=jj$G~?y$pctpP;m@ZYX#PS6+C8!k-fVEpPU$Ii%IE}IkS4U7Wto|=-Z7yZPlYb7UyS0Y&+ZU z;P5;Le_NluBgTPLRz08jdWj5yqV$Nm#lffwMk+o_QAttAMc*6^HUN9Qs_6W6!5?`_ z-`(3rQI!w~6WGle^g&^RZ?`J;(|RVV#XWucHi2+pDTei$NG6zu2jU)4P9FFR7V2i zxHo8=0*-|uT~`Eb(!ig*+%}ZJLvq;-tFmgvQON?9{dxJLn~YABxL}b>{fvAw2Bugh zt5HJ9X5xRm`t>G@`7aNujvYEA;hRz?QxmW8FPInj!FhK0j_zH5=bSc0%(--R)4c;k zu}dSOh(ils%c$+GwPO5;c2yhseacMtG}$S;t{~(>lzTJz*3@M$7LpuDMJ9Qn6(kFY ztWM%PeUFGZ4&N%uAmiF#&vK1R3&xLZIicFrUg-?_HgNKl#_qh6(}KY1jFsPLKeK ze7%x0u)oxqB4f>4;V!v&kTox14k!1{<3_(Hv^8AUkjV%BK<@Qw)#?ZI_`aXNzs4Xa z*^aY!9`P|1i9`Q9E$-jCGS4|SI-q}&=e)}Yqc0+WtgcMIML^qzfjJ({xY0nxJaUT zWXZX8JFJz6#E?oPU}<<}w|vYZQ(u+qs9`OX43o^Sfg`77g}?z#pMtXfM4}Px;%2yx z^d;r`SuqIFg^@aUP7##oxBXZ|=wv=2o0BrI;OmTK#+K4DNm>zTLdw1qx8R-{Zkbm- zlQLHb1H%l*_RF1}l@wq}Ob>NkZ8a_M8}tdi*(f&FPOYIk$r>`6d|;ttgCRemsIeAo zh7+FH>Qq5-rIC?QWNTk+biLAiAC&<&{6ia)-A>!%_2pxqZikKEgZzl3);jp2lX{8@ zFZS1G!<)2M*6sQc@XB>4iiy^(Gv0pz@)06kQA)yP2MD2&8$mA0B4dGn6uAwn%ifLM zwG7mZ7Jn7JCa~YrqWLrr3D2KLq!nM>TweDEYnm+W6?3+y(hT0!ytcpp%w>LDfSn-o z*4%uM%U;20y-`*GfQfm;PM#q;@tYNIebq@Pr%$+HHimeo4g8>+rST1h=!Oo2n;1S) zexX739Jpm(sBw9OC?js%aQ7ck>Y;Y)K}re}!oCE>Z1`|OM0g^QymP1YKyYZvJ@#L1 z1toZf{h+#~D;*2%xj@zEIs+EyGfBYebC>zdxD|!jkIaSk4R(p~R!A4(hL8oXI#X7a zr&T?SL-Eg-UF#$q$voiXEt+^B1Lt9n-1}JwPhU964tAIk0SZ~Ob}iqsPsEBsp56^l zE4_&;xhk{Q4y)#buv)%^k*;iDgb_a?jgySd!Ncr&)ahr?KGono;^}xz>Iu-r??xbb zS@ug)UhDoE+cBX}$C4uZzz)4Q6E@H2Ioi~8%d;1f2Tp@ZPS>c#MfqFLT^~#7N7#>+ z7?|L049oQCKj#&so)n!zsC|E}>oO{mfIF|O(OXocCX{@9+#lkL;|dssok*CvUf+%^ zi+|M)@UACH0uc;(5~u(CV^&dJIXGqy}g!1A4Y@A}2rF*g#|)eK>z z5cL7dMkgbR*56!q6?jZ{A_{Cbr^^J)|3ic&h3=UBA}5fLy*GJzX?gnlQb&(`b?L~M z&4=M--_UN>_G%ZZT)gDSUJvD=H+GDZ`EGAM`@P#^X5Fo|@mHdc5r8z41S<+N3Bf`( z1j}tafB{tXIBj<$Fckt%`h>{yr~n1mIP`<9+@E*mt{V?T!QP_%BE~Hi6sXB>E6Bodyhc zK9%^|X9PGl{egm!Y7nL`7$2rTC6f=6vKFU?hQ%!v&+k|cTmc8eC7 zDg$vhI%`pAFg6F1&fB9HjDZMFzBd zmY&XWe%BuEo*yNZ`4h;!B?_KH~{rei~&EBUT^|I`4ubS85-> zknJS5Vf$h1GyuMke{=eIKO3XvxskpJBi;GrbKR?Yp`YY>loRje^EmZGtm6%o@|>U~ zpT#|zrrk))j@VQ&(jC$$yl1p-iwXi_i6W$tSd}bFf5u_dr#XMSv#DNyCR7QSM&8cI zKHl+K*|Wb0r+BNXT!UH8&*xjU`sN|y(u{w7>WBVyL}8r)rA{vnFy2I24hYbePGx0% zRZE=;uhBfpxyWPLf3~)UfMx>pd*}IE`R+-ga?*%8(J}tPz z!*8Jo_vy!QD3)B_{JCy4IM)g%r}h+V$n?ee(}BjJ6CUlu$kD;{zqyXGhGYm_TiH!e zJIUWlWcXffFqGe{<^w+~ zW#nrs8ixd#5&>#EJD~lYKAtY*Q=&RPfp3u!*OLNB0#cjNl1;YM;52(ur6rcH_h3XC+$7sA%?ci;;Y}^+*l)c=El28D^ zbJF@}Kc$D%^ut&L(g7}Bw(Mj7@tifSH8lLtRme$_-V>XTy?yXHY}X-Y&WxjMKnHjB zW;5Tr*j78Ec?*&p!7kN(x9d|!?nb?UxKLSWeCZND=<9ij7?nX?0?S$_yV5@4OA`^y9 z1eZiWh(*q==OLm>QI0Bc$9U1)3vO{@mvNzmwr}9LjmaxM_P-H1@zgLlk=5T)OL?1zIQ9=f`L38xPK!kM zEDZGABs+qk>J=Z3ESqupk&z$4dg8he?KR3J7LSCSw3=?QzY|JN^GMTn{o=kylvws` znFho+cFvqYI{pF%4?$ja^qK9tsS)e;B$oRko?Fi1Gf4sQXOvNsc*rt?4 zwTPXej|`bB;W$%kFE77pwu_Ep5qQC?Q5iC4jzZt|uFFt~9PJTaH!!q!cqgR(V_B7* zLFx4d4h)6hW8aKex6wAljE9M|D=9tQUm@s5fU)P{k{KzWLHpNM7Ie9Aunm9`0bBb$ z`wR(%m|jFeL!%~)&3|mreB2%-)!4ACl(yOZ0DO`&Z zfJ43oKw(2y0;IU9 z#DXjQqqYr*i*?ThRu~3%TnypGl_>s9spOK$C!PH>K)}@!-kgTRR3uOEKeFk1DRq3A}hnr zYk*O^%CV?XW#%3~j9+@32w@hnxG^B)8sNK*?eHK1zKx^e3Ke=mwa%}9F%cyKvq=^! zG?AA0`avQEzDLqNZb`($>gkBP_^={T5hV?tNcbyJ9x!Wr$B%-F3K3(7V+o*`vq*A< z@nd5Ah|(}D^0euoBSJQ`9P9w>8N<=}L6P)f&PTaV3d1%wQXu34#>qSn87{5sn!!6kt z#w^yhp*pf#+#gn#4}kMBCWID1L{^|D6i%|#dNf@1jE4dE*s*hG9i|I$Y`}UlUb+_9 zcToWyTtggkF@C(?c7+N&4<-X^%|^8H;@^iTy^0 zzF7K8$JlcKi@nvb(0S}6JO6$AF1^|5z1d;gr!h^MK!GnWeAdk>(y12ad>|$-o*=&) zK?#8ZjS#jZ>%9nK5W+5W=nzDC9HV^mF=&;LV>CgyLn?+)j!6Fb1wkTg#%O271W`$& zMkNI@A|?uMdy!Fb&7sztS0`W}RX;;FW#p|yGIR5@S$Oo~ZkIftzgi?LCJ&trL(6P* zu*&`{2fkWPmVwBvD8JY-kOdnNR6#35t}^hPoI8U z?HaL|abVckkwsyk+|Fx9($Wl&0V(@Iefy^ASu#Z}Ht#{5zmh_Esk{#!1Sb7|n((UX zijESsA~q#dG|Z-6G~ii?Kbl+l#KbP^cflXB*M&UMK^uw`2CkB6feqpE z6bgQpd_|aI*&75Vq2DPzdbD6?$o*LIj?g%uK=|IUdQ|xwl&`Rs`?I0}K<|MbC=@sa z-W#vfsw$bj>D(7X5V=^ulGxx@~*XYMjdWHLi2&Di`U%8C5_fO zxB91f^GH>X*(v=}RNWp_J&b!3TXM)b#q&Gf#d>&wM5hUf66dMRD?ksM>%NZpXTyf5 zX(IAdMY$*{M~XBz%NC7At;Hy(*)i_7>1(0~85$;Vs3T4?y5+r-`Oy-$E;Gz1#@Muy zQ-LFh=$s&nt_q`!C)7TYrz|-%<>jNLOE){xxtAY^D4Gu4#%KQgAQ(Vt)z|7A;%LBS zoLpSq>mq!VJVw&@e`>BYI%Kwqn@4^JxDf9`eh1nnxg}B_qsrz~U_rk#kQK)$tOwr= z+xHq(*Z9oI$F0JT=ym_PPP^ZIr8M7x>mZaqbt z0oiIRqK>XPM~%)Ez^o{V1HlQ(+K$IeKRjCW1LwRS6roa7s*fUG^BPm|nN#)?9^N0q{ zI6#nc``u?wo2>t_x6!%a8+ib940|D-s!@guq= z4oLkQ))cCarBe;pcW1MUM42xm66M@n1<&xmSP{;n#`S%ghm4K3hzMhKaQMmSE6y}# zgji*D*VSG6-H6^EZDt&?C1RG~2I96vLM1|S#KZ_mmOIKEMx_60vlCvvjJ$olm-#Xc zuP&1_YgaE>0t&S|`Tg!BJGt4cbjN@kBUgwTX1&g^mQle-LddxLmC|DovPQM@;G?mI zYl5gy;()?lT3h8#X>)Ocv2E&LMxQE6t~c^rw)0Qj%~!X(Z~pYL8}la+_F#>ZrN)Rm z-X?h=M2)5Ziy!-wY7a2Da`DIC*U%L;LG41)h5;Tgw7SDwZh$4sRXRQdn z9O6*YqLkS3p9efTWm1Sb`@;r@s@ZR8Gx)i~JNqqPKRsf-TrE?&*Rhq+F*|BsGVAO; zDeT?awUx_SDF!~~G-xG{i-Q?c9?rtKOP4h*EFwbtEV+4(PAUcD~d*f<`X0OA=Tl69;i}f zK4(_5(^#^<0HN424Oi4aUns6qJJzM~$u(*#khV&1=se*4PwsX=Lu84tm*bbKsr>hD2q(UX`cTb^o}+mlj$J!=5)` z8rvk|wq>$9ii=zd)a>pyN@DDXiXW?$VEnVVG%d2U-R&K+8bB4 zCcau6(u@w2$(b%$U(S+nFL`a^Eny8)Db1tvA(|>LTK{Y9DcVO)x2n(hc#|9dpf?tO z*0X2LQ4j=0Mbwh9Wq4;LXJOj;Sw2{UYRuZKh3=67oLux*Az<@Z(S;HVo57> z!_rXi&j1%|#}ISgz9huczbsnWr%S<2F0^iTXSHj7=d;5jb#7xk3YyZLhCqxovEvK( z(R(=|^OJl75OPsmNd@6HGo%j9(xPci`j6CRO#PNj2F~9`jQ=m=z6D5q{CH={UW5MV zZlq+F;v6)UE}Jgqcju3VT>|Q=wfgtJPu+<-17B|tES|6>Y?Pc%VQM&%B|Bg6r=4vF z*QPX}S}nD1X|L?#Q1;(1?S{4ih*l0>`K2Pj+xsk4loZf?1Yy|%RLR6M?vV;zUn5*$ z$FnAk%4lIBql}MFV~dIvmV!id-Ei&v>%*KNt|hN`$f$NCg0gavmGQ5iaecmU3_USr z;ooPygZC#zIfBSYU@xLyC>@&AELlXKBFlZKF(1N5dMsk0M?zy3hb`}=h5c%BP`CB)e{r7Ha~~&?DWP&hFNmAdH6qfeCpKX?_)C!B{5|) zS$CrHM*QK8dzxR}KnANp>Z4JuzjfpW>hQF%jMXOEmP2YjaH@S+?Q-g15$Z}ji1noF z{Z^IQK%dwkZ<4S(PB_6G-DYOj|KA%j4Go#n{R`4E8CN0A3>iQ7+{R`ffIyO|=X6No zR$Ev7M@G8wL@IR8TL$&bDBNnoMETtlH}~CyJ(u8OB?-NT2)OMw_8+O`WV8u-hk2X> zR75fWwnF!8@`+>rtqjx9c=ZHyc)|g_MVltN!<fc7U{o0lbO1e&Vp%>5A7G;k7pA@cq=VS}d11S59 zrkl+2{re+C5R%B#y5#(EqV$v{D@=Qk>Lr6iDj-;$keFccd5g&NWAr)QU~E(hXtvwV zokAjNzQUVq$vX7lj6|EHRSE#~)w<#8;S$%zX(g-v*6;VdWp8wBS}$jQm>6yqULbxc z`***nSz#NeqrU2eTA-QieF^R0TZrmiEWt;OJ=;iLR-mz`sIg*`Cs}gTi(is9H|ON; zX@7sjK;duT`!`7HJUTiL{}Gu_&^zpA93s+(x%_`gItG4mK0*K=f$%5t$$U#D{kKix zeL;2dF0QNU!O^!FWax`^L;Jd1IkHlL3kh9lNG&IGI4rvF_wYX@cEd%dw;cYvNfJO* z8i>JJ08{{o4Ln9e4P;#Z{{SC@or4eHgND!`|KACJ*L$$Az#uzsQ6eUQFu27gYir8j zN>s_x9T03Uoz}V8F*0hM{TW8=8T<2pzgogd#2X=oCh-fDtWaqLKJCPSbphSc;l+8? zQmV-;m>Dn)v0Itl*={ulvB4cSCpfZdPpinrGR-pQedBfv+Ys>><1Vzi4@6!F5-}%^@ zrC%c=9adQA7K~)PXV@hXtjE7-u*U*^Qetx$QRd=3a`XaVMLHKW|=j zy#pgLb)g!h}$+LnJ`*H;sE zQk?pdud!HQq=&qC^J{%9MUB=Edvg1E6PE>C!SFUR#zn-~G#|@RY{VuPI`PXjd>l+v zw0ql;`7q&Nq^|a0Z`Z!ST zl3vk!L(R^emJDAp3qY}QEtNUrn`6E|CPA8;zj*L|hh1P0D% z`q&#f&=N8)T4n>WIxBya>|z%)b??GcTQ02alIQ~GVKgOk05vI9#D70_K1T($u-XB< zvcq-#6S(#6+4#8*T=b+p{IFw6o zIFBDw!9988(_j+8WF0dq_|9nUqr0!2I)8o#3|<~jTf#l7|5ROOPne#Z1kvn&#Lqfx zibT#V9;_BLqo+85bLQ`ERZ}>a`a-YP<-mIM`eOY|m_n-d!ng;fZZtGL+^04i8x;VL za!k@MEFEJJY%xq{Pwu0EIqw=inc9lhj#Z;xvub)BpVIv@3|GrXg-!t7%X{P)Z1$jfXu#QmAW z{TEd2K=v}Ri+4T`NtC9QcEMu`HklIy#c?9(*Zy&xjH`b;Qzl*T?y;sX4b8Zddrhvj zIZ?A2NAZi&--SJ$`o@4D7?ccyh~y+?ch%|LD;J_(9S&;cm)PL6Y;m+=I70Iy<7e-l z(NkH`M)&x=nsZ_G1?s8}-MgO|?zP&$D$u}1rmGrF8++p{kSVL<&!Jixt5T0xPo3&R zZEzfH+WX9!zC0g{WT!GlJ^TBNQX*W7XDwZ9Tt7YJN)*m&_aC1hskH$6v@gHR*(pH^ zKyHR-ZngpfVG@k?2X48B7#VkGZQUC6f zCzq0f1k4gSmKX_XLm1N4h{l1ji2AGBd0Ts^K2`|nsj-C+lrt*5x(CL;i2!WWi;{*9D7 zcQ$I=IC;Zy@#6Z(ram7o{oGR)#p-%`hg8u|OKc^bpiD)bHLHHul=_Fv?p0Y~6RzsH zf1EfkFyX_*G5~c$$@QmR-Gk0f?Ad`o!R0D2>aSmje53=Q59iR|Ow;C;1%v`Rfg-=@of2|tutaZ&7o5hgV{~X5>0Q&Bo z1ZUz1WCp3Al|R0v4S5JDn`IHU;dJ4RKcmeENhun9Vcq#c>uJ_Xxu4e@UaXYa{E*p; zcSqyO*4&+1D>yuSZ&8bj^q%d+FilS@_a?%c&DXBEo(NE$s#xvb^BiyHdhj*UNG0)- zzLie~{6}!F43yisb0<${U2sk_?Fq~{-H7T1xiT#`U{Ff2N~`A0Wr!t*3NLWSsY)dU zt^S4cZ8S6nzT7jVSHA%RK4cc8IntHh%Emdzd38F;{YcU3rMfr!-D|&pTvXu6N!`?K zt&Xg#yupdddX=B!`Wu;Ld%vW}0N>D|Trvvap$omZ31kM$ubI3za5r#7T<|`_+An%u za>iG7Wx;GI`}3c6_-~9OTNdr%{Ra=;4tP*D(1E@UsB7`1J!}iduL~PA@a>#%$-s7d zf5T3*Ji!>yiLD>0^QQI$`U>yBPEG59?%=!64*Kz$s0V(XpjH-LHv(J<7F<|ZA4I-Y zEB}Q%FL%lw0}&#Qb|Nv6k}Eq7ghAq$tJOX&a;{IGA~lChjh1+q z*1qV)@XO)hO+}^#+QnN=Ur^+xclrP$zwp91r7(+hBkl#TUOdc=f-PhaI{h+H86^2J zifd=0TlyP0O>P{cWfHs?^brA_RnRn_#ZwzyM6Ty;5n6vWw9G5LHsw!@fx51HPp-D~ zt~4CN+EN@lx@csJ8&hv*{RJKKv#$5%3#?i@Fr*j90&Kw6CDCQQkxTq~aR;vfk3r3S zGD>1ON$L!-w9IN*+;MNK$=24{Hg&r&e(Cmg6};f&{zHe7s&>k!C@A2683N^BRFtflJJz5}*c8T+UgZTqhJeITxYD7MJg~I! zJ@6qh6<3)(L)<#{gl6aGqg*!hfa+55iB~RuiA)s#)ztK5D&6G6jQ}E|rKN`S;f|1a zBrMqcI1r!VROW4}ocU(@4}*M}?P{+V)Tv7snbpj(k35S>&SOZu!o-cecTA31X-77R zM2KcOfhA)lRVXL~7@@OS*a0IW8RyG9tvK|?XUiP+b_U9+nT$zR6}$!nK{7o1#=LsBA^BivN$*tIAY4y z6Fd6WDt))C3kybqw0(W^Tpv(ukV>)@e$IK_P+`PC#6MJbb`%jRlKR}*E{wjU3y?r9 zW6w$_?IfNWlngiw9k$=Z(;SVBw2Vhm^V6tD23!R*;R@sv@5ox+P*rs#lj7Yjt?Duy zTqAy{kI8y&I-!n)ae98-{m6j-X#vho*Y-;y$g4hO6=$_`E3#o@lRGW$+NDeA@cdsi za0mwFu%Yl{NV5uRX$Gkr@B8{Zo>y<7G>Qj;kwbIKN8=GkUemx^)-P$dfB8zwolHC9 z7(@O-)6fU5jfg#g(mc$EoASw0zo^^Fy$Q>`9NiP9#k#CNpm@QfXcC*WZM&UsUuxaG zAYNV$QzIcjCC8d0hnqp2fqAH32?HIsFx)|YlSny4D+DOO?tn{6q&?~5@UoBmV%lX1 zuZW(KI)Smi>T=(pBD#J;hDY-C#YRFao9P_B_ ztuDDPU;ZZAEOiI&Rp<4FaH#AX5L#&kaiNL^Gb48A=f>ksHYFWWBI%tZ1D+8X^1%sh zmQojYzN&g3ubu1K2W$ejLIx7$*ZA(8JCb=LwLcY-u*&S4a$RrvKvgtLQvM5P4Qbro zKe-7LP^O%v_-=mXrLWluG5Ql77lzqrHAC(v826G$OTW;?w z*EeC(x3MT%Ft33$O^JWvN6B!|oK8(s7xQo=_(9rH)DH1r{SqPv8rheUIyQMpKhU*A zroWbgXAbMxGJyn6z((;@YBm~vVxLoOJt}8ftDs;3x1DIqi-S-DwAQU!y); zkJPB1cIF~*A$Cw_G2|_%P%c#sDNl#!asKEav@e&b7Q?9eCrNi2R=sYn`Bz^Zq^bO@QK`04yTG-??$DR>EDK&9q!v49jI76DYmuu zj{{Cr6%rf*@8n#okSxISM08v`anI#Z7ONYr>ceP*qkXA^uAN>ChyX67x3~ z$qQjN0JfF7rm_(f;w?b|brfQ*Iyhv_05JYl7q+i#VI9^> zX&7msh~vC84P6E8lBhLKl3A7Cuk?OaO(k*+txX@IPepDHM#aeFrp#b^Hn)~+$Yn8Q zEz$P}q1b8CVYCg)K!)ckH*C8_)J?Siukzln7>sOFBD1v1!n!uEFH#W^aUo(#&o#cM zF0(ys@S)^GdoclXD;DNy7xOsk*y$+x9nOXuzKaMfXVJ!SuhUil1Qf@{mM(-yL zi>oS1wZ&D8Bu#!l?MZIL1#wgIWh7Bno`XQ7Xk7~-v2-2%8$CN<2xsa3219?mAT-mb z%ak{9=Yj4m%4pEXM8$qR8=ESq--}!8)|GP!eVw*>QkWeTwyFX}Z{C3tjPDdDmpNai zSzA9+w`E4q0o)>25Zt*g+QAKDBMxq*ID|x@%0JG}jye1&aA+VSjWASEI;k^$dU2Tn zC(J=4y5r5wm-!twEQ~l3&LBu-FB(jj`;`W%V3!9Oc|e||4UUxeCpYny`p|F>;Gl1* zp^-dZZF|_5toKS0^UJszGkD4e!REe^MMznYvgSc+*|Q%r8ByL$yksvF7|9P-}O zQVX=k%bnPhvzX-~W`5wc+~}hpZy?S9^zG}EJ8WqNn7Fh#3KTcD4NGo%3n)oqFCvxi z{pOU++_iA~8(Mam*VKo<&r}O#pz}wu$m^p^NhheSkRuqG^pB`Od*`Cw5FRN*tG7!+ zEUQ}WSTo8n0X6wdsC4dNriE3HOef*AH5&yfmpK2e`rfkZVrVA_ z&4)o0Z#X{dmVQe4#g!vbX#fDRRpplShzbRecRk2DMwwolTC}VYaIz>2OEZ^ToQo=v z$e5++Wpu@JK?)>;7(C_4g`&j`95Y5 zD#2K8-PE93`zm^N?!0N#m@!vS2mv;p>dQ8}24=e;Fg@CB85l`_CUH`+_w3p=|4`BD z7kTL*Aq;R#Qnxh=w&;TtX9430cu?xL+tI}bJ$2dx)2-|!&Wc4(e>p6zLJ;AQw6~M0 z!Vo&(M+L+Ur^S7)-#hBI4v0<}&GqBQLTQ%bqXe&_Elc8J%!s!1;{y zrAgmrh5z|rRMp}ezNweo2pUEoRtNsz|4^@)@%ec$u#*GCL$AtnhTF_K+tPD8;EyQ2 zaf}Y&3Wvj~Dc!SNhMa%?ucI5M)5D~su-pHeM%6Or!<9d;!Tu5;g$^Nhvjp(U;)ZCz z05LkQ^A`d$!z-`NubE`JfOGLtRdMP;2NTmYn!|&5zke|e@UC089j6C8!}C8+s)hz+ zV9;3O95|8DcLiLm*@zL|p1|I(!nv4YGF*&`zVM?TGKk~$ZA&gEJH<}SDoYsvuEsVN?YV+w!I9AG0(q5u+JN4Nh zp<2<)#59yJga@82GG~Is`9^D^38B!neG)VrOScMjLmMMTFK7rF34Y^j4LOnT&ercJYq4AM|+A&7wwXWUq6@3%{46rVB=6mBZmJkS`>=WD+? zSE3TTFa?TmMu7VzD-R7?KJRRyF`-EV-7Xa#KdccMQAVNKj>F;AdY4IIR7x#`Jhz;h z=IfqOrlGDbfIq1C0`K>|s&4xCoLM`{YA5ny;5=p42F;@559O>aEKJ4KEUS2a0@tP0 z!NO0OCyK9)w7kA-adLHKz3KE(^U`S8v8_h*?iguO`GpF;Mi&2SuBI?)?JzF{l`fVGjBYr;LCGGU=RQOA6 zdKP6|`PksO2Eu&M1Swu5uauX%mQzl0D({xv-0p8(&1_vCIyce0lFR?9;q384OKK@8 z%dl~&F4>tHWyf7wo@tj*zIZx)6JL}9WbqZhS1%l|cgnosIoa}0;OEo>wb~CJOi1kL zSC5@NpZQU=?FC;!j#m>`)@?_hdxx$5l)f3EFn^p{_I)&)xeZF%ItnSAk+;>hJ(tn$ zT^ zU&4#h)6x=<2}`2&qdT>OrUw21E|tjvpy8rR?EPa?W|GK-+3_&%7p!Nm1OJC;xC`-= zs(Z0o&)|4*ZF3VxKrru_TBIQcPw5``>oCW^Y(Y2sWW^;yKv{|xk_x&Bv~V~Ad4tIE zNl?#?Zr;##yJ6Les>xXk$k6A4QFu|1>i>MjXLQ!E28x5X!?J%d!=?X^ivNfcp>Jw# zCj!l6c9r-UWJf6?FE%Vn@7mJ0R?zJki3?Xxi{Y$R9Q*#~G)C%LGTngV15#vNF7SZb z2TkQ;W($>rZcnko?nIz!VT9$^n+ML_>UJi@G}t0&UG*QS*u`FU&hmOg#jNkE&zc<_ z)CA2>>6b4AFur$A&weU&ADAu*>5%Lfj1SwuE#3;Z;ro7}`->K7X1K=0#LzX!NIY!m zTsIYU1!(8JLRk#`*5C5$k5lzadue_h;TTmEF<((b!D8?5!1O&~Xi#>e=#Cpt_wq5h zjkbFR-*={DSG>Q(MHJ5QqbEZ)NyC$ME>{2)iUU38C(!5##?y8EudpsJ(IJG>#9m=# z$V9;Y(HA}riN%&m!eBq#dNN1|ZS&m;Z(M$rJCdU@NZAX7`7)i}Wmxn9SfiD_%06ZI z@k(}$J(=2SRyBoG24i~E6)hV#j-wvsRt*@md;@j|=fGWsZ-J%o!etPza}at?DGsmD zeL{g=^3)IED^QA)lqddKn;7bpP>Zg|qW_$tdD}&Lfp0Av&enw}2xZ*68H8;u8)mr8 zrCdt+eKT+XN_D1x-d-@&G8Tle4nUB>_vq@Defvs|ouJ>BEzf4R%# zPMdsu`l7R9m}7faRKDeC2CiWK^wS)=_V>xmAN-m_V82RnbP`E?!XFQ3o$lopbBziX zIq^ecVR~6bUW(d$^r_9cLnTX9wEr0fiKeX9<{xShbR%(~vL$;>`%y@50wKpg0>ml* z^=v7MW1H=7FfkUU7S%oW{Q&IpTV74T+fO0gS(=oG`ZyGvptTy>kC9#*c%`pRoI-$V z4u;~`iKzoBmX7zhsFi=yv9&@#c>FFW=GK}5+@>BrWYN`* zqwYg=+tiJmP#^O1U%vAMT~+&#U%aho`rvhcjATl=ft__P7ew73zym4p5&*OfR%V0H z+2Uz?n2_LoH!;yI|3;GUo9JF&V%F||{PI9+Thcik$=wg!L5{WI`icD*vbk~h0W2Q# zWbsL_KzC}^+3$;W)3AjgTB-_5>Oqot=24fOqac?Rf~P2dwT4o}>}pz?L%@GW2&60m zb@emtSbXim3uKK8p@kMwkC46*4r@NU5D0tTh)t?<5o^k?pg-8|Z(y2iVfop^@zCWi z3*Fix!C=~s4ei=I%9oTih(t_Gle_I7ca5DX%Ny)7gHpRfcPrx}eKyE2cPg5`iia?W zM3{%uBIyr`o;BTS9e|?@g(sPjK%;9#H)a)LB{1vST#t;Ca?}y7_e*kT(!68{0}i>t z@bwN6*%P;CuK2h(ncL5SL>nmviK)|H^J!SKMP}ebWk4e$`=D`@;eZvqf$+59rr(~vNtTIexl zT;S{1ujvQs*2rTnX7%gSC)ee~{U4Qvvq;7iI}5OEE_o*spDXk^H@E8it={(hXzc%h_027bOLijkWbeW0W7Nr+Y9mD7{V2WY=L z$x6$cQ(f6gu2}B)c^Fwly8?;+Q+LO1*Jgi*q|61bkUS%7u3N>p2Ih)LU>>K0G%8}y zC(YJY98`39@aXby8%{MCC09nP>$Nvyp zJoPA4%LBKq8?Vj)bomcG6m=C1oP@cGDTSedx<6dp@xh9E2t=y`#sE+cakGO};xc&I z@{M=n*@zpWDRoLE+;?*915d&2eQ|o zvn8mA5-VqZ)A)b8HkX*pR_C;gJycjvH!BOz9>kK(XJ))=irb-LZli`W$wht;FsqUK zcNLk9$4o$hP)SCaIPk;K-Jv+)-lDva&&4KHJ~?UYaBpR?gU}VOKXSx!&G{PGHxE76 z8`hY>zT)2FZerTkJZ=N|P+X6V+qPB3)gC*(Q}z~<;9(Ry^4D2~#BhdiI+IP>|j64LGQ!=Q&nimrq9h8$3tlLj(um~dIC z<`YhbH)yqeigvf5L$!p*0P5q9S**4ANgPW-fW(sZAMFtDjW|}|I$C(~)^LQwzMxR4 zeNSDu#{m835dwJ6cbh3?Fpk}LK#1n^Y;FHVX9brpewn}Y7Zs7PCAn|krncVDBEq6s9(vH0PSGJ;?P))J}6miHMXUKlj~+@2l*)2gzPhfCAWS$_K3qYTYJX&|_sV9yxb^tq z!;L1viqeoO4r9Lt*=#$+Eb>EPdJ7_VOdK6HMTY@i+|gjO!`$5s1c&h{1r2e5(pdUfUt`bwvr1vYxI5g4O)BZ1OeV?RGeg;jD(&ZVdh^3k4^m#BEN;TIl$6gOU z9$d%zh)Hm6=CUfUQ#arZ?hJXsPN&b~R+Qi1^(?%Mo+j*j~E zk$FU2>q!VDT6-QGnJdnQrjVyK488M|>KhFLPj8nm^a}^UCR7K=7+d`o-2t5$C-vyy zMypbbSiwrv@4BljF^^zbLEy`d_xW$GCW3q!0Ki<$Cr@^Vx@BAg{X$g$6ZF7({K-jd zY-|o(9SSZnfoV3)(Ag72HpcBD>9}}c!uj*GTo>a~VW5F3)&k`6-@fZ^HNe&PJ~OQQ ztM?y13{Y9iHF;XgSs;e1S)o~%&%~V3Ez4Rly>e3sPJLVzYPy_7R%@n3WP`csP8kom z->TKXRhqEo%7*%98rBDUyx?d!c(6-)L2qdh?_Yre<`Uc7Ujv|Qaih6iRU#GEkYjY$opS;UV;G-4K&982c6Tb1S&PvRC)O=^ zMY*>JAMDnVsX>p<#M?qXd$;e#2k}on;?au>N7w1dp+m!Ars)-yoIO(%nzf?pGhpYL9jC@Q*Sa32c8bwK zezsEr8-B)G1Kn>m%%YAG>z>zJGoxwHavi#rJFq1s1(UN+eYq6U11!q^$!ugMZOSUg zolG6Q{#w+M{Rn=Rwq4=*sMkP!eTVQLH~)MMRr-^benwNyu?vq5A3D@O|NKCxTKD3y zM`}c{nwXkuDisE^f+1&wC2md|Ju*OahP!tnoJL<*!ztfzIhz*v zuSM9Dx4qZ>Y#`&4#Uw0w=zID5)<>Vei>{4WT6fw7tF01dMNJoAY@_6b1)LhE0pKv5 z_~qLUJux&K+adSwN-yr(x3pUEFgqe9jZL;#orVbE$@D*J7fGKZtd!~fY_O1|}yvrc;`l{DgG9>lOKzKu=pC%e0M-t)Qn zrCPbnvl2h6c&PD#!V#8QH`U%)y7Qi#4C4zQ^xJy%88m$#D0tzsaRI)MZfm0QMpI2* zufO0mLO>G?Kf>Sm$>mOT*i!yrJaUQF?gyi^?~0&p+PzOlKkS$~sj{+C-VoOqN#o0g z1zY_6b(^HGephE@qI^RGgEo{XvWBE7Lan{Av9SgH%f_qI&DweF}1h=s)qq%>VLE}dqQ{5?Q#Du_|EweS42ePN; zm3Da2TGnDGAdStC3O5-ADq`24x?|3B{0h~{I0c!QUNiJVe&}oIMect?dS?SYfdnb! z(8$>q+`b$i8|Nl$63YUr>k%7Nw5{7b(nZ&ta{TbvJM_>Cny#+LB~Gfks75ZYct1{_ z(QelvnD#47>9_)I`r)zfIw7vJB?x8}VTN=CjU-pi!+)fTcJU_J#QL_h&-#9* z8fzr`4|lGhdYD0Fj7UzWCMKrQI5_yE{~R70Qq||42-(Fr!3evNu(P!HjZHG!XjYi2 zXq%rnG%|AzF9&$29;ztR$PB_cTJvOj)$EPRMVS$A1zQk$N*XI8rfRt z&9`h(Y1f^V_2Tb8hc5FW_ekUqU8X>)52P|)mYbFlC+s3cE% zWhcoyVu2Amrm{)3^#*tl=O1v(w(PSRScK#g-T#IY$;8zEI#YdV?*`5|WziL;ezVY@ zzy`UYeNuZ8t+NVu3%f$?K>CXp^I%+%wHZwvt)ks)gfP7)lkD^)TY+u0<1(C<_M4B9@ zXRJ+B8SZD}AD3}mQx%1N`Mz=Ya z60(Qq8FOD9N5|sPdHF>M5)y#*GuPuqG%b?*dF4zV=`y|3oHncxrJ1J}0qyWhAMY^RzQ&aP^F*OO3OnnmzxxH4`uqUl}W8jCI%{q@@-_w!OOi7Al?s7uG zYHR~EL8Q#xx~)fK6!H0>2hev~B7xS^TkQJkv&OQU>m{6#fgTjU6)*)~NJG9>e59l1 zxSlO-A!#L(A4U)M9d+I|8f)b@(Zc77dUi^8Xs|jcvZG!vhvkAo`g|GH`m4u>#K8Lf zyUo(24S`8W5*?1j-t%=w?U1vBL|pLI4A74v8lY1~Lm@DXWK7L)%-=tpsjM*Z@->uw zOrC0&Vbu{Gb#NADVm`MVObSV2UR70=juo^4Dq(RZql`7#+lINyqBuZcs!$93+p*jd*p*lA-~YTG}IvSuY{c zxCDof9<9v6l!!a-HG!Sb=gZgN%=Z4dO-)tRhM7^K^hUjmz5N&coEX4GfWSlf&o?Y` z`qr&$tB-9OL-~2>98>nda@7tnh+QE)CcXMypVioTvuQEKH6l=l05S}NO6Fy2 zoJ->nq6_=mwn#Gx8~Wtz^xg=Djhq)Whz}$uL}vdGY(nDNaMI7zZC|PCfwTYZ3q{(D zC!hApX2F6Nx$~gV(EVSi`@7)Qi03&Ph(*OMz|lqpUZjRbV>l3nDArL)pN;_-3W7G3 zZWkO~C?8!UIWU6-78{67OR5t~0q9WkjUY@xnvSuCAN- z9RJtTxq#(-zkmEIF^6m-w2V=RGK3t;Pzpsk)QII&4jr(fnp1=#DM@3PqB2REN)AOK zJeXgK4jwrsCLNFzD*RtJuK(}ab^SKmRnPN1+@Je?pKbu_&TUG|LS@2VwCGuL`G)DE z{~#^ltIHjqInyebCGvO%>EF*n4?CAp)rJa6Qd&BuNkUVbzo!Zq1AH8e;uFZ@;M6`~ zck=NFr{Sq-3S#nEXa_vSH1&GsAXhEKg&;130WL^$3?kr5kUqEol~_wJp(%UvHaJ@**1^wMX1`<)f{ zC)wIcz=6V|*jN~ivfIag6tsx-NJs%|oLAmvz!E4;^0Y*g1H7f{N>^g_Ra^Y><#$k= zW6f8Grr3|t>z+zT>9 z+)~@e6A>+X=}iDZo$NuOyR(qH-;AGl3Rz$rT`|QDQ8JYj1tGx(vUn+G%;s!lk?7eA zO7C030ElB(jyV(rF^KxVK#JsTi2s?l;TpJTF8~=a#nI6TIxJoXZl6S;e8nd9GOU7E zF=7R0L|lI=a#$9}gu{z{I#>5%gi(lbL(_Msmdtn1ZW^Mo!D5Eh&9==fE_Bejmlt^8 z^!D+NkB7MFoU+R8(aWU#(GY#x!CN1HYrA2K_Pw#)?VMvQbWAsCTesDWZL)W1Swn-t zD37w<&D-4nd)cUGUI!9q7iJcY7v;e?K`QD zGfZd05R){~e9uJw+C*|1II*130MAHXtuf-gi&*aq#`nX>HM$bNNoC{;9s8wzzgk=L#iEmx#2t1ok+3Nk zg`nI6<|^E%c?j11_G+ygvCzI(WC?TTc(x93fhBwbgEu&r3o+^d8an27Q@G;YIDL3v zDSj$CFB7^SQ5sXPUE<%~;|;LjE@yqL_Y^2Kb5*S+U_5>Py(gRfOzJcq>Hn4TtPq2k z_oMSov`;UUnqUv#pzp5tyi&Ptsx3%v|zLCr)_+wekCXFfj<`jp5 z3`})%fqjKiG4m-4K(wV#@Miq+b*=chqj^Lj2HGG%>*H`}F2ZNKx+9}p`zvx;qQRNP zDyK%S#}xf7rS>di%nEkX2pX8+Xyg}C`5Wm<7dXpBM$hra_Prk|k}@+$T!BxedP=2E z=R$sK@|yR{SXviLz^-g1M|u`yGh3a{!ZvGOKA7QPTs~)OZkS77vTny^bw-6Q?YfRI00j=J>| z=Z5$S8Kp&3iEbm!#yRD7PkJbkgrYdI!W)EwRQh28GMp+4bY0hR4@^=7o)kSF(Q}e? z#>?nJd6P|Zt!K?miI1O}@aq2W54&@(W#VowU*NoVy2_mnTl@&%izj8~Bj?LVAP+{fK-d-Vkvo&-^JFCEP2t z1}qOo4ImS>@JhTBdGV8eL~7X)r-)9mu}&+pk4u;n{L7sf1TSH~4s=4i9fWHat^mSg zJ#xsBKj*A<|KCN=WN0Ia0=sx|Oht;&g#d#@Uw1$@Ju`gzZ9#x`?b?Lf9p0e?C68Iy zcM1fUyuP5toll%ES}^AU?gGNch9DWk6Cd_;xjEe5PH5_!@^Kg z#YCl7$O9(PjdY=sxR`G<@x)9!9!kvsj+wxx2#I?pINCx;!-MBdO>IBUcbEq8R{8?9=XLE{*uLD_4jF z`(N)tgB%ulqehhkOr$4CJ3myU_Q!D=qHKqv+PQ1j$dfq`qkVexIQYOHvH4CVkEx~p zk`t7eW&qJpU1yvks<|954UNEv`%MN;Sjy`|T)dY) z1_e>s;FD?`W}FJIynM>m#rm;~-g+EyzU2j5UOYT^EOIv`{1CL;6kp#HMJ{DW`nT5((=!cG z^2kZ7R@aK6b5_=x^aoW8#uH$PC9BzJ=aC0~prF(oLygfG{j*=Qfnhss{e3?1p75CN z=++Z}LDQDp9g?J)7us^$D=%%n57iB?_-0zViuwDN&Z^DPx|6#2k zcmKuylr}Mzh~^-BatGCJKktYfCxhY8%J6|=yda8pY^9GTjkjCT(eF7Z4EiGLXdj&$ z1qv&NE9euC?z_8#-T>a}(bFOBKM&_uO)-q+O`$)e>}I}l)S~72wu{*#?wOQ4v`lmH zn1P@R@(5J~Eqet%G|0ab zv_M_0a0-65792UUs(gqq{J4~VqTM;b>dk*EaWG+?r=J(PpX~4%i75iC5*vbP-!9R+ zJT5G21ZpP+pKfG;@f_qkonpsK;HFJQXR1*9wPlwnF#}x4l0E;`dh=Py&6U0G5~#yF zBnCbz>!*RaDiM~$j_D5{KEtq@41`NryKbnjKPDDEmfyFf<3pZyMKHoy89rMG)3Jm( zlkHZ;00WtKLDVNBThRVs(unh}Vv9GkYJ6M=jTi+#RXge6c;n^1$D@iQ3Sy6?}5QJF=#~B$A zF%aOlZr?UUfos&izoEVy;!}80Z4w;W@1J%?yDFic=SCEpjQi^~;=pPVjDMj7GQ7aTkC>L{d^!`0B@tyEKmp~J_8zpOg;I4d{kX-F;E&ViKO@H+5%hC*Q6+w|%U_R(Q1jg_Ysia5;xB&=4~V@?i8n9%R@l?1aX{8h95{rdE~+G?msMDV;B>vohR%xW2z8WBIdflau}VuoUPhv%ha1LKrEb?QR>3+kQB9wX3<` zQI&TH{Z^2Y$4@zt`iZ_^2Z>XL8pQ__vz(__**pNM?NM1FlC8|D_=$V;olOnvOA~tY zN)QzoXQ=AOvjj0;68YCAou^^$Jc|WKXAD~VZwuGrD?>*PEYe-es##?;xVZHVt4OM{ zq;O#dBXHwW$`a6C8Hf63z0<&tj{4=v!)>dXiHVs?G& z*pNia&w?Ej@5ffWJl3v@m-^$@mo0A;*2cHzJ~dO|2i?-BB%8^|1F-8{aHt;-{F>^^ zop7N~^6eXz=IT_ts+Xx*k>M)d zg2qMksY(EFKJwQQC&Q*DT;jCj0|t&O@~u>B)6!hc$+32EiB*|#a-#k8bbi`(&_XR5 zB}pwpMmu;|?m6h%(unrfs@=PShfNDg`SV|j4;#X!K8wt2>waU$TXqVzzAly{-e5z8 zJr7yqPNJ)LQeJK}u`gjM*I;<&51f&ig2%jta!0Lp>zkXC^EW{Jc8UjCtyi|aC-9*c zqKQ^5TCA9VpIM`8dNpx97v{UTP*lObH3I<9Z2J9~&)rO*DJ@7{Fo9)R2e{9A_=B+}JGmoD=h{}BJ@d_$SNwWH^Rw2pB@w*6E8x|C);;@r7)nH8=sE?4vOk3XozQluF$d))`$ zl55?OSsoz${}#4`+VKrEfju{J*vC&&z+ms%xO1RUMI{T8@6(<4$T%f3PgG zu+?mB+)D~q6E>4oR!-y|u%OHy4eBA%|89W?bUl+~8PkpQZap9}f0$}nXR&HgmVrKN z4LCWPw@g6~;s#TQj??S)+qd!1ar>$K(Yt?ha#9;ymTbhpJFgH<+t7Q(afVE$hP6~a z;Lkir{aVHPh{Lo^PfAKGn7`wGT>EZ4=|g=64A?FekA$Ydkht>O?e>q10%I~=csp_R z58|w4&qIFdg6q8p4tyOkF5%~d92k_q4gG;d_?xlZrye_PTPm5*hdzLMhFRZ*xf8L( zU5u^0W5_ILi;{Wf8F%|X{nJM>L)3OW!*JCD_}!!MNC(!7R!7v1`tT-lz{VcRUT`~bs1H=XvM7^u=) z%&a51>zo`t@3px6adYsa7caKqQG2yJZ|pK@@qnG!5-@XrZ8a^6C}H`(YB&&g(~h3b zR)NU#1A=9jcuM}fqkY~*T%q~}OG``#C`6O7@dSsH$^}Jfb;8EV!m-jP+FX>Ge5CaA zOM4*9`n>#;1Ss4p6Dq1Re4dM#VIEAy>EB;Q7nP={YJdSsIcr50aoIj7rPsz|{En-L zfn-kv;v-3NM_#RM-gC;|2M@}MT|V#_1V^~`+i<~B@>i8~Vevb&xUdY~tp1FOI!&Jw z$0#VJ(#7Sm!O(80&n8h^!J0RVx)pPBC-W|2B!}1|5OXWovv2*i(I~ow&Iglnfd)0(dOg~XW1cOyd5kT+KAj6T znN@0hC)B;A_B^};_?Y^LYZLS3d6f8-Y3kNuBG`>BOqO2}jdtXL%?zIs%E%R>9c3f| zACH|eC~INOS;qHoMf3@6!>_{%Ke6%;T~FJ0wnJQt$qr5?W?o=!re5$nr&CM2AT_&e zAC@_n83a#FQ>1{xr@-aMc2p}OIDnpN4QygTeFeR@&e`nF15XheP3{LL-|6!UCu&mc z4=rGz+vMC&dhledF{p?iL$rrSX>Uu(034Mj|Mu+FZ9q(}#9D7N}YG-VciR~8ozsxFJtXr;wTFa9t) zyD;_hlxK`rZLmykLFhdX@2adr-45YEDZ2ngUioG)hy0jckrqkQ1aW{+|9E}IuswbP zX)-B^@>kN-xPj*(S}N0YJyTC#T{v+w>ak>|Hq3D7dpdye^2}sICBBbvla*kyvW{Gm z0P3lzj3G71QMKd^zGQ!FHkv(p5n0$L+fv9UM=v?6GHtKciJ4)vdF>ed`S*hK^5B8D z_4o4#HFf9P_S!(1&_4wy5VlWL#M z$t3wpAYK+=q6c}GWO758DH#Uh{{~I8Ub{9+ose0;JTHILoi~Q!AB5gDJUm?OZlXF5 zbQjAJb%j@soQ?nxOM0#Ic+@CL?D!Z182qsJ`*u?k3&oVhu6wp6N*soOxd3>qV86)s zxyhuRlE0=GVt2lzWI`}Hw}hMHjD5ADYV0n!Jc8Oj2WXJn2fU( zjk>FE5GChIzbQH1yq=(2;X@y+XbP?&Pl-fL^hsTW_;?G)iUIID1Ez2?8Na{W#yyv? zJI;EScy$?1a0(-&;8u9gvVZ+8Gl(}P7RJjZzRq%LZykQaMZER>uRdLdRZa(gl8UF8 zH^ijw@Q>+N7gi466%`eRN?z8Y3?^T%+`3V?5p(8tClM375XLo=X= zMz)x6yR_GilMHhVV8@RA7P)`B>_xS_;KbuG@8TxO78FwtJ9UlyR^_5p)#}lshjhH~ zqGiP=jO7hMArl9{=MsNKx4~0Bi2jjG+f^(9Ji( z#Oglh94R=0Z&IQ5{k*y(N1@2h`9mPdM_=;C$@6^`-=>xfcPm&7F_R(=fQfliar--z zTAR4yfLePC8gN)|<7NGU^3I*l2GwbWpM!&EE;|pP+H}mVRqFf_*p5FhlPiGq@(bf5 zS?`A(jlfY4$RXhEZ2^B7n0=R2ahnb%^y3nn3scF%P-4t04Zn@divJD_*$@cy(evl> zAgAQ7E#V+3#gS));!6k;c~J;j5}Jq(L;z!-s^Sh)6C5iYJLk~Bdz16AkE}S^H%X5` zQ!T}eaG%gF<{dc{{yR5CHQQOPYU)&@of1X~RVUF@tlxo@r;Aepu%K~JYA_&&y`)u6 zPD@)n@S?pKv9N~;JTFT+_`1RhmMlWYt>ElzD*h3kpIEsWd|?Xh&9f_)M}$9m8apjf z40_g2`CP9s55`2H%#n$X<JcpKsVCj@^hb;jjNF7)8~ zZEU;|9XFx~K&_HQkVd!r8KztcFdU`vX1HgA8#t_2_6)8U9p(WP0^qDf*SF6iHFPr7 zAIAJc+hRdq+(P$jp4Gr{qfsxt8llt8u(FAEJ2Z~j1S36FSKd&Zbxwl(#Wh^4VOmN zub8?@tdh7|YvN2?wS;LSUV+Z{t-++k8~mM#%E_Sg|Nm6QdB-2=x`s{s?tGJ!$hn{Y ep|aT?kR{vPe-SB^6H$>(D literal 49417 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 GQu0Bt7+ePe1W@TYUs^3Vjg81* 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

uud2Zki_XHF3;Vjc|~P{e<3QC2?#Q= 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 ZwJnJord#H| Date: Fri, 31 May 2019 12:16:59 -0400 Subject: [PATCH 056/390] add sharing secrets, and a bunch of cleanups --- proposals/1946-secure_server-side_storage.md | 104 ++++++++++++++----- 1 file changed, 78 insertions(+), 26 deletions(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index 603fe64e..c44cd57e 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -1,28 +1,39 @@ -# Secure Server-side Storage +# Secure Secret Storage and Sharing Some features may require clients to store encrypted data on the server so that -it can be shared securely between clients. For example, key backups (MSC-1219) +it can be shared securely between clients. Clients may also wish to securely +send such data directly to each other. For example, key backups (MSC-1219) can store the decryption key for the backups on the server, or cross-signing (MSC-1756) can store the signing keys. This proposal presents a standardized way of storing such data. ## Proposal -A user can have multiple keys used for encrypting data. This allows the user -to selectively decrypt data. For example, the user could have one key that can +Secrets are data that clients need to use and thate are sent through or stored +on the server, but should not be visible to server operators. Secrets are +plain strings -- if clients need to use more complicated data, it must be +encoded as a string. + +### Storage + +If secret data is stored on the server, it must be encrypted in order to +prevent homeserver administrators from being able to read it. A user can have +multiple keys used for encrypting data. This allows the user to selectively +decrypt data on clients. For example, the user could have one key that can decrypt everything, and another key that can only decrypt their user-signing key for cross-signing. Each key has an ID, and a discription of the key is -stored in the user's `account_data` using the `type` `m.secure_storage.key.[key +stored in the user's `account_data` using the `type` `m.secret_storage.key.[key ID]`. The contents of the account data for the key will include an `algorithm` property, which indicates the encryption algorithm used, as well as a `name` -property, which is a human-readable name. Other properties depend on the -encryption algorithm, and are described below. +property, which is a human-readable name. The contents will be signed as +signed JSON using the user's master cross-signing key. Other properties depend +on the encryption algorithm, and are described below. Encrypted data can be stored using the `account_data` API. The `type` for the `account_data` is defined by the feature that uses the data. For example, decryption keys for key backups could be stored under the type `m.megolm_backup.v1.recovery_key`, or the self-signing key for cross-signing -could be stored under the type `m.signing_key.self_signing`. +could be stored under the type `m.cross_signing.self_signing`. Data will be stored using using the following format: @@ -50,16 +61,18 @@ is the same as the key for decrypting the other bits. Maybe a special flag in the account data? Or special case backups somehow, say, have clients inspect the backup's `auth_data` to see of the key config is the same? -### Encryption algorithms +#### Encryption algorithms -#### `m.secure_storage.v1.curve25519-aes-sha2` +##### `m.secret_storage.v1.curve25519-aes-sha2` + +The public key is stored in the `pubkey` property of the `m.secret_storage.key.[key +ID]` `account_data`. The data is encrypted and MACed as follows: 1. Generate an ephemeral curve25519 key, and perform an ECDH with the ephemeral - key and the backup's public key to generate a shared secret. The public - half of the ephemeral key, encoded using base64, becomes the `ephemeral` - property. + key and the public key to generate a shared secret. The public half of the + ephemeral key, encoded using base64, becomes the `ephemeral` property. 2. Using the shared secret, generate 80 bytes by performing an HKDF using SHA-256 as the hash, with a salt of 32 bytes of 0, and with the empty string as the info. The first 32 bytes are used as the AES key, the next 32 bytes @@ -74,11 +87,11 @@ The data is encrypted and MACed as follows: (The key HKDF, AES, and HMAC steps are the same as what are used for encryption in olm and megolm.) -FIXME: add an example of `m.secure_storage.key.*`, and of encrypted data. +FIXME: add an example of `m.secret_storage.key.*`, and of encrypted data. -##### Keys +###### Keys -When a user is given a raw key for `m.secure_storage.v1.curve25519-aes-sha2`, +When a user is given a raw key for `m.secret_storage.v1.curve25519-aes-sha2`, it will be encoded as follows (this is the same as what is proposed in MSC1703): * prepend the two bytes 0x8b and 0x01 to the key @@ -92,11 +105,11 @@ it will be encoded as follows (this is the same as what is proposed in MSC1703): When decoding a raw key, the process should be reversed, with the exception that whitespace is insignificant in the user's ASCII input. -##### Passphrase +###### Passphrase A user may wish to use a chosen passphrase rather than a randomly generated key. In this case, information on how to generate the key from a passphrase -will be stored in the `passphrase` property of the `m.secure_storage.key.[key +will be stored in the `passphrase` property of the `m.secret_storage.key.[key ID]` account-data: ```json @@ -110,19 +123,58 @@ ID]` account-data: } ``` -###### `m.pbkdf2` +**`m.pbkdf2`** The key is generated using PBKDF2 using the salt given in the `salt` parameter, and the number of rounds given in the `rounds` parameter. -## Tradeoffs +### Sharing + +Rather than (or in addition to) storing secrets on the server encrypted by a +shared key, devices can send secrets to each other, encrypted using olm. + +To request a secret, a client sends a `m.secret.request` event with `action` +set to `request` to other devices, and `name` set to the name of the secret +that it wishes to retrieve. A device that wishes to share the secret will +reply with a `m.secret.share` event, encrypted using olm. When the original +client obtains the secret, it sends a `m.secret.request` event with `action` +set to `cancel_request` to all devices other than the one that it received the +secret from. + +Clients SHOULD ensure that they only share secrets with other devices that are +allowed to see them. For example, clients SHOULD only share secrets with devices +that are verified and MAY prompt the user to confirm sharing the secret. + +If a feature allows secrets to be stored or shared, then for consistency it +SHOULD use the same name for both the `account_data` `type` and the `name` in +the `m.secret.request`. -Rather than encrypting data on the server using a static key, clients can -exchange data by sending to_device messages encrypted using Olm. This allows -clients to share data securely without requiring the user to enter keys or -passphrases. However, users who have only one device and lose it will still -need a way to, for example, recover their key backup, so we must provide a way -for the data to be stored on the server. +#### Event definitions + +##### `m.secret.request` + +Sent by a client to request a secret from another device. It is sent as an +unencrypted to-device event. + +- `name`: (string) Required if `action` is `request`. The name of the secret + that is being requested. +- `action`: (enum) Required. One of ["request", "cancel_request"]. +- `requesting_device_id`: (string) Required. ID of the device requesting the + secret. +- `request_id`: (string) Required. A random string uniquely identifying the + request for a secret. If the secret is requested multiple times, it should be + reused. It should also reused in order to cancel a request. + +##### `m.secret.share` + +Sent by a client to share a secret with another device, in response to an +`m.secret.request` event. Typically it is encrypted as an `m.room.encrypted` +event, then sent as a to-device event. + +- `request_id`: (string) Required. The ID of the request that this a response to. +- `secret`: (string) Required. The contents of the secret. + +## Tradeoffs Currently, only a public/private key mechanism is defined. It may be useful to also define a secret key mechanism. From fee2ebf682c7c83cb807e3e991bf91fe2dac8b88 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Sun, 2 Jun 2019 16:09:47 -0400 Subject: [PATCH 057/390] fix typo Co-Authored-By: Shamil K --- proposals/1946-secure_server-side_storage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index c44cd57e..8487d6d4 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -9,7 +9,7 @@ way of storing such data. ## Proposal -Secrets are data that clients need to use and thate are sent through or stored +Secrets are data that clients need to use and that are sent through or stored on the server, but should not be visible to server operators. Secrets are plain strings -- if clients need to use more complicated data, it must be encoded as a string. From 23af87e9fc898daffd3823490578f9c46442b85e Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 20 Jun 2019 17:41:19 +0100 Subject: [PATCH 058/390] Proposal for IS & IM TOS API --- proposals/2140-terms-of-service-2.md | 203 +++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 proposals/2140-terms-of-service-2.md diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md new file mode 100644 index 00000000..8c2ee75b --- /dev/null +++ b/proposals/2140-terms-of-service-2.md @@ -0,0 +1,203 @@ +# MSCXXXX: Terms of Service API for Identity Servers and Integration Managers + +MSC1692 introduces a method for homeservers to require that users read and +agree to certain documents before being permitted to use the service. This +proposal introduces a corresponding method that can be used with Identity +Servers and Integration Managers. + +The challenge here is that Identity Servers do not require any kind of user +login to access the service and so are unable to track what users have agreed +to what terms in the way that Homeservers do. We thereforce cannot re-use the +same method for Identity Servers without fundamentally changing the Identity +Service API. + +Requirements for this proposal are: + * ISs and IMs should be able to give multiple documents a user must agree to + abide by + * Each document shoud be versioned + * ISs and IMs must be able to prevent users from using the service if they + have not provided agreement. + * A user should only have to agree to each version of each document once for + their Matrix ID, ie. having agreed to a set of terms in one client, they + should not have to agree to them again when using a different client. + +## Proposal + +Throuhgout this proposal, $prefix will be used to refer to the prefix of the +API in question, ie. `/_matrix/identity/api/v1` for the IS API and +`/_matrix/integrations/v1` for the IM API. + +This proposal introduces: + * The `$prefix/terms` endpoint + * The `m.third_party_terms` section in account data + * The `X-TERMS-TOKEN` HTTP header + +### Terms API + +New API endpoints will be introduced: + +#### `GET $prefix/terms`: +This returns a set of documents that the user must agree to abide by in order +to use the service. Its response is similar to the structure used in the +`m.terms` UI auth flow of the Client/Server API: + +```json +{ + "policies": { + "terms_of_service": { + "version": "2.0", + "en": { + "name": "Terms of Service", + "url": "https://example.org/somewhere/terms-2.0-en.html" + }, + "fr": { + "name": "Conditions d'utilisation", + "url": "https://example.org/somewhere/terms-2.0-fr.html" + } + } + } +} +``` + +Each document (ie. key/value pair in the 'policies' object) MUST be +uniquely identified by its URL. It is therefore strongly recommended +that the URL contains the version number of the document. The name +and version keys, however, are used only to provide a human-readable +description of the document to the user. + +In the IM API, the client should provide authentication for this endpoint. + +#### `POST $prefix/terms`: +Requests to this endpoint have a single key, `user_accepts` whose value is +a list of URLs (given by the `url` field in the GET response) of documents that +the user has agreed to: + +```json +{ + "user_accepts": ["https://example.org/somewhere/terms-2.0-en.html"] +} +``` + +In the IM API, the client should provide authentication for this endpoint. + +The clients MUST include the correct URL for the language of the document that +was presented to the user and they agreed to. How servers store or serialise +acceptance into the `acceptance_token` is not defined, eg. they may internally +transform all URLs to the URL of the English-language version of each document +if the server deems it appropriate to do so. Servers should accept agreement of +any one language of each document as sufficient, regardless of what language a +client is operating in: users should not have to re-consent to documents if +they change their client to a different language. + +The response MAY contain a `acceptance_token` which, if given, is an +opaque string that the client must store for user in subsequent requests +to any endpoint to the same server. + +If the server has stored the fact that the user has agreed to these terms, +(which implies the user is authenticated) it can supply no `acceptance_token`. +The server may instead choose to supply an `acceptance_token`, for example if, +as in the IS API, the user is unauthenticated and therefore the server is +unable to store the fact a user has agreed to a set of terms. + +The `acceptance_token` is opaque and it is up to the server how it computes it, +but the server must be able to given an `acceptance_token`, compute whether it +constitutes agreement to a given set of terms. For example, the simplest (but +most verbose) implemenation would be to make the `acceptance_token` the JSON +array of documents as provided in the request. A smarter implementation may be +a simple hash, or even cryptograhic hash if desired. + +### Third-Party Terms Account Data + +This proposal also defines the `m.third_party_terms` section in User Account +Data in the client/server API that clients SHOULD use to track what sets of +terms the user has consented to. This has an array of URLs under the 'accepted' +key to which the user has agreed to. + +An `m.third_party_terms` section therefore resembles the following: + +```json +{ + "accepted": [ + "https://example.org/somewhere/terms-1.2-en.html", + "https://example.org/somewhere/privacy-1.2-en.html" + ] +} +``` + +Whenever a client submits a `POST $prefix/terms` request to an IS or IM, it +SHOULD update this account data section adding any the URLs of any additional +documents that the user agreed to to this list. + +### Terms Acceptance in the API + +Any request to any endpoint in the IS and IM APIs, with the exception of +`/_matrix/identity/api/v1` may return a `M_TERMS_NOT_SIGNED` errcode. This +indicates that the user must agree to (new) terms in order to use or continue +to use the service. + +The client uses the `GET $prefix/terms` endpoint to get the latest set of terms +that must be agreed to. It then cross-references this set of documents against +the `m.third_party_terms` account data and presents to the user any documents +that they have not already agreed to, along with UI for them to indicate their +agreement. Once the user has indicated their agreement, then, and only then, +must the client use the `POST $prefix/terms` API to signal to the server the +set of documents that the user has agreed to. + +If the server returns an `acceptance_token`, the client should include this +token in the `X-TERMS-TOKEN` HTTP header in all subsequent requests to an +endpoint on the API with the exception of `/_matrix/identity/api/v1`. + +Both making the `POST $prefix/terms` request and providing an `X-TERMS-TOKEN` +header signal that the user consents to the terms contained within the +corresponding documents. That is to say, if a client or user obtains an +acceptance token via means other than a response to the `POST $perfix/terms` +API, inclusion of the acceptance token in an `X-TERMS-TOKEN` header in a +request still constitutes agreement to the terms in the corresponding +documents. + +## Tradeoffs + +This introduces a different way of accepting terms from the client/server API +which uses User-Interactive Authentication. In the client/server API, the use +of UI auth allows terms acceptance to be integrated into the registration flow +in a simple and backwards-compatible way. Indtroducing the UI Auth mechanism +into these other APIs would add significant complexity, so this functionality +has been provided with simpler, dedicated endpoints. + +## Potential issues + +If the server does not authentcate users, some mechanism is required to track +users agreement to terms. The introduction of an extra HTTP header on all +requests adds overhead to every request and complexity to the client to add a +custom header. + + +## Security considerations + +The `acceptance_token` is, in effect, a cookie and could be used to identify +users of the service. Users of the Integration manager must be authenticated +anyway, so this is irrelevant for the IM API. It could allow an Identity Server +to identify users where it may otherwise not be able to do so (if a client was +careful to mask other identifying HTTP headers). Given most requests to the IS +API, by their nature, include 3pids which, even if hashed, will make users +easily identifiable, this probably does not add any significant concern. + +It is assumed that once servers publish a given version of a document at a +given URL, the contents of that URL will not change. This could be mitigated by +identifying documents based on a hash of their contents rather than their URLs. +Agreement to terms in the client/server API makes this assumption, so this +proposal aims to be consistent. + + +## Conclusion + +This proposal adds an error response to all endpoints on the API and a custom +HTTP header on all requests that is used to signal agreement to a set of terms +and conditions. The use of the header is only necessary if the server has no +other means of tracking acceptance of terms per-user. The IS API is not +authenticated so ISes will have no choice but to use the header. The IM API is +authenticated so IMs may either use the header or store acceptance per-user. + +A separate endpoint is specified with a GET request for retrieving the set +of terms required and a POST to indicate that the user consents to those +terms. From 32c7fc638dfb31eb05f484fc0c933cef7b604dc5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 20 Jun 2019 17:44:28 +0100 Subject: [PATCH 059/390] you have a number now --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 8c2ee75b..77d44e60 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -1,4 +1,4 @@ -# MSCXXXX: Terms of Service API for Identity Servers and Integration Managers +# MSC2140: Terms of Service API for Identity Servers and Integration Managers MSC1692 introduces a method for homeservers to require that users read and agree to certain documents before being permitted to use the service. This From cf48030d1fe7c3e180cd5a5617261fa508dbad59 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 20 Jun 2019 17:48:21 +0100 Subject: [PATCH 060/390] One more tradeoff --- proposals/2140-terms-of-service-2.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 77d44e60..bf322676 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -164,6 +164,14 @@ in a simple and backwards-compatible way. Indtroducing the UI Auth mechanism into these other APIs would add significant complexity, so this functionality has been provided with simpler, dedicated endpoints. +The `m.third_party_terms` section contains only URLs of the documents that +have been agreed to. This loses information like the name and version of +the document, but: + * It would be up to the clients to copy this information correctly into + account data. + * Having just the URLs makes it much easier for clients to make a list + of URLs and find documents not already agreed to. + ## Potential issues If the server does not authentcate users, some mechanism is required to track From 276e2b6843b9d27e7782222cf66d2c34f6c528f9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 21 Jun 2019 09:14:24 +0100 Subject: [PATCH 061/390] Typo Co-Authored-By: Travis Ralston --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index bf322676..b678881a 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -90,7 +90,7 @@ client is operating in: users should not have to re-consent to documents if they change their client to a different language. The response MAY contain a `acceptance_token` which, if given, is an -opaque string that the client must store for user in subsequent requests +opaque string that the client must store for use in subsequent requests to any endpoint to the same server. If the server has stored the fact that the user has agreed to these terms, From d4ca0c237a7b744ffc05003b3a84b31f0b064367 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 21 Jun 2019 09:25:16 +0100 Subject: [PATCH 062/390] Specify ID grammar and add comma --- proposals/2140-terms-of-service-2.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index b678881a..dbdad334 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -99,12 +99,13 @@ The server may instead choose to supply an `acceptance_token`, for example if, as in the IS API, the user is unauthenticated and therefore the server is unable to store the fact a user has agreed to a set of terms. -The `acceptance_token` is opaque and it is up to the server how it computes it, -but the server must be able to given an `acceptance_token`, compute whether it -constitutes agreement to a given set of terms. For example, the simplest (but -most verbose) implemenation would be to make the `acceptance_token` the JSON -array of documents as provided in the request. A smarter implementation may be -a simple hash, or even cryptograhic hash if desired. +The `acceptance_token` is an opaque string contining characters +`[a-zA-Z0-9._-]`. It is up to the server how it computes it, but the server +must be able to, given an `acceptance_token`, compute whether it constitutes +agreement to a given set of terms. For example, the simplest (but most verbose) +implemenation would be to make the `acceptance_token` the JSON array of +documents as provided in the request. A smarter implementation may be a simple +hash, or even cryptograhic hash if desired. ### Third-Party Terms Account Data From 9ca3ccc81ce0ddfcf529b6c9445a2d11887707c4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 21 Jun 2019 09:35:26 +0100 Subject: [PATCH 063/390] Add requirments section for de-duping between services. --- proposals/2140-terms-of-service-2.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index dbdad334..066a1184 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -20,6 +20,11 @@ Requirements for this proposal are: * A user should only have to agree to each version of each document once for their Matrix ID, ie. having agreed to a set of terms in one client, they should not have to agree to them again when using a different client. + * Documents should be de-duplicated between services. If two or more services + are hosted by the same organisation, the organistation should have the + option to give their users a single document that encompasses both services + (bearing in mind that the user must be able to opt-out of components of a + service whilst still being able to use the service without that component). ## Proposal From a63e4420eb9030d4afb5ac036c9617e5247e466e Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 24 Jun 2019 13:17:58 +0100 Subject: [PATCH 064/390] Linkify Co-Authored-By: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 066a1184..66efa708 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -1,6 +1,6 @@ # MSC2140: Terms of Service API for Identity Servers and Integration Managers -MSC1692 introduces a method for homeservers to require that users read and +[MSC1692](https://github.com/matrix-org/matrix-doc/issues/1692) introduces a method for homeservers to require that users read and agree to certain documents before being permitted to use the service. This proposal introduces a corresponding method that can be used with Identity Servers and Integration Managers. From 4ba9b2a59910ac14d69f92be34e8b123011ea6d5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 24 Jun 2019 13:18:41 +0100 Subject: [PATCH 065/390] perfix --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 66efa708..b43cadf2 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -156,7 +156,7 @@ endpoint on the API with the exception of `/_matrix/identity/api/v1`. Both making the `POST $prefix/terms` request and providing an `X-TERMS-TOKEN` header signal that the user consents to the terms contained within the corresponding documents. That is to say, if a client or user obtains an -acceptance token via means other than a response to the `POST $perfix/terms` +acceptance token via means other than a response to the `POST $prefix/terms` API, inclusion of the acceptance token in an `X-TERMS-TOKEN` header in a request still constitutes agreement to the terms in the corresponding documents. From 25558014580b411249ae3699d798dc9408997978 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 24 Jun 2019 14:56:48 +0100 Subject: [PATCH 066/390] m.third_party_terms -> m.accepted_terms as it will have the HS's terms too --- proposals/2140-terms-of-service-2.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index b43cadf2..8851eda3 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -34,7 +34,7 @@ API in question, ie. `/_matrix/identity/api/v1` for the IS API and This proposal introduces: * The `$prefix/terms` endpoint - * The `m.third_party_terms` section in account data + * The `m.accepted_terms` section in account data * The `X-TERMS-TOKEN` HTTP header ### Terms API @@ -114,12 +114,12 @@ hash, or even cryptograhic hash if desired. ### Third-Party Terms Account Data -This proposal also defines the `m.third_party_terms` section in User Account +This proposal also defines the `m.accepted_terms` section in User Account Data in the client/server API that clients SHOULD use to track what sets of terms the user has consented to. This has an array of URLs under the 'accepted' key to which the user has agreed to. -An `m.third_party_terms` section therefore resembles the following: +An `m.accepted_terms` section therefore resembles the following: ```json { @@ -143,7 +143,7 @@ to use the service. The client uses the `GET $prefix/terms` endpoint to get the latest set of terms that must be agreed to. It then cross-references this set of documents against -the `m.third_party_terms` account data and presents to the user any documents +the `m.accepted_terms` account data and presents to the user any documents that they have not already agreed to, along with UI for them to indicate their agreement. Once the user has indicated their agreement, then, and only then, must the client use the `POST $prefix/terms` API to signal to the server the @@ -170,7 +170,7 @@ in a simple and backwards-compatible way. Indtroducing the UI Auth mechanism into these other APIs would add significant complexity, so this functionality has been provided with simpler, dedicated endpoints. -The `m.third_party_terms` section contains only URLs of the documents that +The `m.accepted_terms` section contains only URLs of the documents that have been agreed to. This loses information like the name and version of the document, but: * It would be up to the clients to copy this information correctly into From 8ae47557c9a9e82e2333894be04583b7d968ca47 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 24 Jun 2019 15:22:19 +0100 Subject: [PATCH 067/390] s/Third Party/Accepted/ --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 8851eda3..f4d78a9d 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -112,7 +112,7 @@ implemenation would be to make the `acceptance_token` the JSON array of documents as provided in the request. A smarter implementation may be a simple hash, or even cryptograhic hash if desired. -### Third-Party Terms Account Data +### Accepted Terms Account Data This proposal also defines the `m.accepted_terms` section in User Account Data in the client/server API that clients SHOULD use to track what sets of From abb407145abe1a51f1ba4d4b7c07e000039791e3 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 24 Jun 2019 15:30:19 +0100 Subject: [PATCH 068/390] HS docs must be added too also, unbind must not error when called by HSes and proxy terms token --- proposals/2140-terms-of-service-2.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index f4d78a9d..6c5bb6a5 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -130,16 +130,19 @@ An `m.accepted_terms` section therefore resembles the following: } ``` -Whenever a client submits a `POST $prefix/terms` request to an IS or IM, it -SHOULD update this account data section adding any the URLs of any additional -documents that the user agreed to to this list. +Whenever a client submits a `POST $prefix/terms` request to an IS or IM or +completes an `m.terms` flow on the HS, it SHOULD update this account data +section adding any the URLs of any additional documents that the user agreed to +to this list. ### Terms Acceptance in the API Any request to any endpoint in the IS and IM APIs, with the exception of `/_matrix/identity/api/v1` may return a `M_TERMS_NOT_SIGNED` errcode. This indicates that the user must agree to (new) terms in order to use or continue -to use the service. +to use the service. The `_matrix/identity/api/v1/3pid/unbind` must also not +return the `M_TERMS_NOT_SIGNED` if the request has a valid signature from a +Homeserver. The client uses the `GET $prefix/terms` endpoint to get the latest set of terms that must be agreed to. It then cross-references this set of documents against @@ -153,6 +156,11 @@ If the server returns an `acceptance_token`, the client should include this token in the `X-TERMS-TOKEN` HTTP header in all subsequent requests to an endpoint on the API with the exception of `/_matrix/identity/api/v1`. +The client must also include the X-TERMS-TOKEN on any request to the Homeserver +where it specifies an Identity Server to be used by the Homeserver. Homeservers +must read this header from the request headers of any such endpoint and add it +to the request headers of any request it makes to the Identity Server. + Both making the `POST $prefix/terms` request and providing an `X-TERMS-TOKEN` header signal that the user consents to the terms contained within the corresponding documents. That is to say, if a client or user obtains an From 2c09580e2798f0cd51590174040df6bde2b996a4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 25 Jun 2019 11:14:35 +0100 Subject: [PATCH 069/390] line wrap --- proposals/2140-terms-of-service-2.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 6c5bb6a5..42a1ea38 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -1,9 +1,10 @@ # MSC2140: Terms of Service API for Identity Servers and Integration Managers -[MSC1692](https://github.com/matrix-org/matrix-doc/issues/1692) introduces a method for homeservers to require that users read and -agree to certain documents before being permitted to use the service. This -proposal introduces a corresponding method that can be used with Identity -Servers and Integration Managers. +[MSC1692](https://github.com/matrix-org/matrix-doc/issues/1692) introduces a +method for homeservers to require that users read and agree to certain +documents before being permitted to use the service. This proposal introduces a +corresponding method that can be used with Identity Servers and Integration +Managers. The challenge here is that Identity Servers do not require any kind of user login to access the service and so are unable to track what users have agreed From 6f374dc981b6330d59cf5f45022f0176ce19cf16 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 25 Jun 2019 14:58:15 +0100 Subject: [PATCH 070/390] Re-write for OpenID auth --- proposals/2140-terms-of-service-2.md | 143 +++++++++++++++------------ 1 file changed, 80 insertions(+), 63 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 42a1ea38..7b6dd4dc 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -6,16 +6,15 @@ documents before being permitted to use the service. This proposal introduces a corresponding method that can be used with Identity Servers and Integration Managers. -The challenge here is that Identity Servers do not require any kind of user -login to access the service and so are unable to track what users have agreed -to what terms in the way that Homeservers do. We thereforce cannot re-use the -same method for Identity Servers without fundamentally changing the Identity -Service API. - Requirements for this proposal are: * ISs and IMs should be able to give multiple documents a user must agree to abide by * Each document shoud be versioned + * ISes and IMs must, for each request that they handle, know that the user + making the request has agreed to their data being used. This need not be + absolute proof (we will always have to trust that the client actually + showed the document to the user) but it must be reasonably demonstrable that + the user has given informed consent for the client to use that service. * ISs and IMs must be able to prevent users from using the service if they have not provided agreement. * A user should only have to agree to each version of each document once for @@ -27,16 +26,61 @@ Requirements for this proposal are: (bearing in mind that the user must be able to opt-out of components of a service whilst still being able to use the service without that component). +Identity Servers do not currently require any kind of user login to access the +service and so are unable to track what users have agreed to what terms in the +way that Homeservers do. + ## Proposal Throuhgout this proposal, $prefix will be used to refer to the prefix of the -API in question, ie. `/_matrix/identity/api/v1` for the IS API and +API in question, ie. `/_matrix/identity/v2` for the IS API and `/_matrix/integrations/v1` for the IM API. +Note the removal of the `/api` prefix and migration to v2 in the IS API +following convention from +[MSC2134](https://github.com/matrix-org/matrix-doc/issues/2134). + This proposal introduces: * The `$prefix/terms` endpoint * The `m.accepted_terms` section in account data - * The `X-TERMS-TOKEN` HTTP header + +This proposal relies on both Integration Managers and Identity Servers being +able to identity users by their mxid and store the fact that a given mxid has +indicated that they accept the terms given. Integration Managers already +identity users in this way by authenticating them using the OpenID endpoint on +the Homeserver. This proposal introduces the same mechanism to Identity Servers +and adds authentication to accross the Identity Service API. + +### IS API Authentication + +All current endpoints within `/_matrix/identity/api/v1/` will be duplicated +into `/_matrix/identity/v2`. + +Any request to any endpoint within `/_matrix/identity/v2`, with the exception of +`/_matrix/identity/v2` and the new `/_matrix/identity/v2/account/register` may +return an error with `M_UNAUTHORIZED` errcode with HTTP status code 401. This +indicates that the user must authenticate with OpenID and supply a valid +`access_token`. + +The existing endpoints under `/_matrix/identity/api/v1/` continue to be unauthenticated. +ISes may support the old v1 API for as long as they wish. Clients must update to use +the v2 API as soon as possible. + +OpenID authentication in the IS API will work the same as in the Integration Manager +API, as specified in [MSC1961](https://github.com/matrix-org/matrix-doc/issues/1961). + +### IS Register API + +The following new APIs will be introduced to support OpenID auth as per +[MSC1961](https://github.com/matrix-org/matrix-doc/issues/1961): + + * `/_matrix/identity/v2/account/register` + * `/_matrix/identity/v2/account` + * `/_matrix/identity/v2/account/logout` + +Note again the removal of the `/api` prefix and migration to v2 following +convention from +[MSC2134](https://github.com/matrix-org/matrix-doc/issues/2134). ### Terms API @@ -71,7 +115,7 @@ that the URL contains the version number of the document. The name and version keys, however, are used only to provide a human-readable description of the document to the user. -In the IM API, the client should provide authentication for this endpoint. +The client should provide authentication for this endpoint. #### `POST $prefix/terms`: Requests to this endpoint have a single key, `user_accepts` whose value is @@ -84,7 +128,7 @@ the user has agreed to: } ``` -In the IM API, the client should provide authentication for this endpoint. +The client should provide authentication for this endpoint. The clients MUST include the correct URL for the language of the document that was presented to the user and they agreed to. How servers store or serialise @@ -95,24 +139,6 @@ any one language of each document as sufficient, regardless of what language a client is operating in: users should not have to re-consent to documents if they change their client to a different language. -The response MAY contain a `acceptance_token` which, if given, is an -opaque string that the client must store for use in subsequent requests -to any endpoint to the same server. - -If the server has stored the fact that the user has agreed to these terms, -(which implies the user is authenticated) it can supply no `acceptance_token`. -The server may instead choose to supply an `acceptance_token`, for example if, -as in the IS API, the user is unauthenticated and therefore the server is -unable to store the fact a user has agreed to a set of terms. - -The `acceptance_token` is an opaque string contining characters -`[a-zA-Z0-9._-]`. It is up to the server how it computes it, but the server -must be able to, given an `acceptance_token`, compute whether it constitutes -agreement to a given set of terms. For example, the simplest (but most verbose) -implemenation would be to make the `acceptance_token` the JSON array of -documents as provided in the request. A smarter implementation may be a simple -hash, or even cryptograhic hash if desired. - ### Accepted Terms Account Data This proposal also defines the `m.accepted_terms` section in User Account @@ -138,12 +164,17 @@ to this list. ### Terms Acceptance in the API -Any request to any endpoint in the IS and IM APIs, with the exception of -`/_matrix/identity/api/v1` may return a `M_TERMS_NOT_SIGNED` errcode. This -indicates that the user must agree to (new) terms in order to use or continue -to use the service. The `_matrix/identity/api/v1/3pid/unbind` must also not -return the `M_TERMS_NOT_SIGNED` if the request has a valid signature from a -Homeserver. +Any request to any endpoint in the IM API, and the `_matrix/identity/v2/` +namespace of the IS API, with the exception of `/_matrix/identity/v2` itself, +may return: + + * `M_UNAUTHORIZED` errcode with HTTP status code 401. This indicates that + the user must authenticate with OpenID and supply a valid `access_token`. + * `M_TERMS_NOT_SIGNED` errcode. This indicates that the user must agree to + (new) terms in order to use or continue to use the service. + +The `_matrix/identity/v2/3pid/unbind` must not return either of these +errors if the request has a valid signature from a Homeserver. The client uses the `GET $prefix/terms` endpoint to get the latest set of terms that must be agreed to. It then cross-references this set of documents against @@ -153,25 +184,17 @@ agreement. Once the user has indicated their agreement, then, and only then, must the client use the `POST $prefix/terms` API to signal to the server the set of documents that the user has agreed to. -If the server returns an `acceptance_token`, the client should include this -token in the `X-TERMS-TOKEN` HTTP header in all subsequent requests to an -endpoint on the API with the exception of `/_matrix/identity/api/v1`. - -The client must also include the X-TERMS-TOKEN on any request to the Homeserver -where it specifies an Identity Server to be used by the Homeserver. Homeservers -must read this header from the request headers of any such endpoint and add it -to the request headers of any request it makes to the Identity Server. - -Both making the `POST $prefix/terms` request and providing an `X-TERMS-TOKEN` -header signal that the user consents to the terms contained within the -corresponding documents. That is to say, if a client or user obtains an -acceptance token via means other than a response to the `POST $prefix/terms` -API, inclusion of the acceptance token in an `X-TERMS-TOKEN` header in a -request still constitutes agreement to the terms in the corresponding -documents. - ## Tradeoffs +The Identity Service API previously did not require authentication, and OpenID +is reasonably complex, adding a significant burden to both clients and servers. +A custom HTTP Header was also considered that could be added to assert that the +client agrees to a particular set of terms. We decided against this in favour +of re-using existing primitives that already exist in the Matrix ecosystem. +Custom HTTP Headers are not used anywhere else within Matrix. This also gives a +very simple and natural way for ISes to enforce that users may only bind 3pids +to their own mxids. + This introduces a different way of accepting terms from the client/server API which uses User-Interactive Authentication. In the client/server API, the use of UI auth allows terms acceptance to be integrated into the registration flow @@ -189,21 +212,15 @@ the document, but: ## Potential issues -If the server does not authentcate users, some mechanism is required to track -users agreement to terms. The introduction of an extra HTTP header on all -requests adds overhead to every request and complexity to the client to add a -custom header. - +This change is not backwards compatible: clients implementing older versions of +the specification will expect to be able to access all IS API endpoints without +authentication. Care should be taken to manage the rollout of authentication +on IS APIs. ## Security considerations -The `acceptance_token` is, in effect, a cookie and could be used to identify -users of the service. Users of the Integration manager must be authenticated -anyway, so this is irrelevant for the IM API. It could allow an Identity Server -to identify users where it may otherwise not be able to do so (if a client was -careful to mask other identifying HTTP headers). Given most requests to the IS -API, by their nature, include 3pids which, even if hashed, will make users -easily identifiable, this probably does not add any significant concern. +Requiring authentication on the IS API means it will no longer be possible to +use it anonymously. It is assumed that once servers publish a given version of a document at a given URL, the contents of that URL will not change. This could be mitigated by From 0dae2d5812d0d22350c9237b84634e81e0306b24 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 25 Jun 2019 15:52:55 +0100 Subject: [PATCH 071/390] GET terms must be unauthed. Detail process for new auth (don't register until consent given). Specifically mention the authentication header. --- proposals/2140-terms-of-service-2.md | 55 +++++++++++++++++++++------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 7b6dd4dc..67e6a2eb 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -56,11 +56,15 @@ and adds authentication to accross the Identity Service API. All current endpoints within `/_matrix/identity/api/v1/` will be duplicated into `/_matrix/identity/v2`. -Any request to any endpoint within `/_matrix/identity/v2`, with the exception of -`/_matrix/identity/v2` and the new `/_matrix/identity/v2/account/register` may -return an error with `M_UNAUTHORIZED` errcode with HTTP status code 401. This -indicates that the user must authenticate with OpenID and supply a valid -`access_token`. +Any request to any endpoint within `/_matrix/identity/v2`, with the exception +of `/_matrix/identity/v2` and the new `/_matrix/identity/v2/account/register` +and `GET /_matrix/identity/v2/terms` may return an error with `M_UNAUTHORIZED` +errcode with HTTP status code 401. This indicates that the user must +authenticate with OpenID and supply a valid `access_token`. + +These endpoints require authentication by the client supplying an access token +either via an `Authorization` header with a `Bearer` token or an `access_token` +query parameter. The existing endpoints under `/_matrix/identity/api/v1/` continue to be unauthenticated. ISes may support the old v1 API for as long as they wish. Clients must update to use @@ -115,7 +119,7 @@ that the URL contains the version number of the document. The name and version keys, however, are used only to provide a human-readable description of the document to the user. -The client should provide authentication for this endpoint. +This endpoint does *not* require authentication. #### `POST $prefix/terms`: Requests to this endpoint have a single key, `user_accepts` whose value is @@ -128,7 +132,7 @@ the user has agreed to: } ``` -The client should provide authentication for this endpoint. +This endpoint requires authentication. The clients MUST include the correct URL for the language of the document that was presented to the user and they agreed to. How servers store or serialise @@ -164,6 +168,22 @@ to this list. ### Terms Acceptance in the API +Before any requests are made to an Identity Server or Integration Manager, +the client must use the `GET $prefix/terms` endpoint to fetch the set of +documents that the user must agree to in order to use the service. + +It then cross-references this set of documents against the `m.accepted_terms` +account data and presents to the user any documents that they have not already +agreed to, along with UI for them to indicate their agreement. Once the user +has indicated their agreement, it adds these URLs to `m.accepted_terms` account +data. Once this has succeeded, then, and only then, must the client perform +OpenID authentication, getting a token from the Homeserver and submitting this +to the service using the `register` endpoint. + +Having done this, if the user agreed to any new documents, it performs a `POST +$prefix/terms` request to signal to the server the set of documents that the +user has agreed to. + Any request to any endpoint in the IM API, and the `_matrix/identity/v2/` namespace of the IS API, with the exception of `/_matrix/identity/v2` itself, may return: @@ -176,13 +196,20 @@ may return: The `_matrix/identity/v2/3pid/unbind` must not return either of these errors if the request has a valid signature from a Homeserver. -The client uses the `GET $prefix/terms` endpoint to get the latest set of terms -that must be agreed to. It then cross-references this set of documents against -the `m.accepted_terms` account data and presents to the user any documents -that they have not already agreed to, along with UI for them to indicate their -agreement. Once the user has indicated their agreement, then, and only then, -must the client use the `POST $prefix/terms` API to signal to the server the -set of documents that the user has agreed to. +In summary, the process for using a service that has not previously been used +in the current login sessions is: + + * `GET $prefix/terms` + * Compare result with `m.accepted_terms` account data, get set of documents + pending agreement + * If non-empty, show this set of documents to the user and wait for the user + to indicate their agreement. + * Add the newly agreed documents to `m.accepted_terms` + * On success, or if there were no documents pending agreement, get an OpenID + token from the Homeserver and submit this token to the `register` endpoint. + Store the resulting access token. + * If the set of documents pending agreement was non-empty, Perform a + `POST $prefix/terms` request to the servcie with these documents. ## Tradeoffs From 9e0d8b9cb29807d2e16b2332dc59f7b0aaad1fb2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 25 Jun 2019 17:17:39 +0100 Subject: [PATCH 072/390] Use M_CONSENT_NOT_GIVEN No idea where I got the other one from: we already have one in the spec, so use it. --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 67e6a2eb..b0b15b07 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -190,7 +190,7 @@ may return: * `M_UNAUTHORIZED` errcode with HTTP status code 401. This indicates that the user must authenticate with OpenID and supply a valid `access_token`. - * `M_TERMS_NOT_SIGNED` errcode. This indicates that the user must agree to + * `M_CONSENT_NOT_GIVEN` errcode. This indicates that the user must agree to (new) terms in order to use or continue to use the service. The `_matrix/identity/v2/3pid/unbind` must not return either of these From 57094276ce01e324b174a74c40b63ea8afdc0ba8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 26 Jun 2019 14:36:15 +0100 Subject: [PATCH 073/390] Typing hard is Co-Authored-By: Travis Ralston --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index b0b15b07..a483607b 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -21,7 +21,7 @@ Requirements for this proposal are: their Matrix ID, ie. having agreed to a set of terms in one client, they should not have to agree to them again when using a different client. * Documents should be de-duplicated between services. If two or more services - are hosted by the same organisation, the organistation should have the + are hosted by the same organisation, the organisation should have the option to give their users a single document that encompasses both services (bearing in mind that the user must be able to opt-out of components of a service whilst still being able to use the service without that component). From af691b5a8ad09ea82cfc49f596eae90448aa4725 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 26 Jun 2019 14:37:06 +0100 Subject: [PATCH 074/390] Clarify this applies to 2134 Co-Authored-By: Travis Ralston --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index a483607b..b508a9e2 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -54,7 +54,7 @@ and adds authentication to accross the Identity Service API. ### IS API Authentication All current endpoints within `/_matrix/identity/api/v1/` will be duplicated -into `/_matrix/identity/v2`. +into `/_matrix/identity/v2`, noting that MSC2134 changes the behaviour of lookups. Authentication is still expected on MSC2134's proposed endpoints. Any request to any endpoint within `/_matrix/identity/v2`, with the exception of `/_matrix/identity/v2` and the new `/_matrix/identity/v2/account/register` From 1d75828c71bb442ceed735c4ee6593171a08319c Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 26 Jun 2019 14:45:25 +0100 Subject: [PATCH 075/390] Clarify what to do if no (new) docs --- proposals/2140-terms-of-service-2.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index b508a9e2..ad617508 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -174,11 +174,13 @@ documents that the user must agree to in order to use the service. It then cross-references this set of documents against the `m.accepted_terms` account data and presents to the user any documents that they have not already -agreed to, along with UI for them to indicate their agreement. Once the user -has indicated their agreement, it adds these URLs to `m.accepted_terms` account -data. Once this has succeeded, then, and only then, must the client perform -OpenID authentication, getting a token from the Homeserver and submitting this -to the service using the `register` endpoint. +agreed to, along with UI for them to indicate their agreement. If there are no +such documents (ie. if the `policies` dict is empty or the user has already +agreed to all documents) the client proceeds to perform the OpenID +registration. Once the user has indicated their agreement, it adds these URLs +to `m.accepted_terms` account data. Once this has succeeded, then, and only +then, must the client perform OpenID authentication, getting a token from the +Homeserver and submitting this to the service using the `register` endpoint. Having done this, if the user agreed to any new documents, it performs a `POST $prefix/terms` request to signal to the server the set of documents that the From ba7047ce7746733f77dbf4d6163767304690a2c7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 26 Jun 2019 14:51:11 +0100 Subject: [PATCH 076/390] Clarify we must be accepting HS auth Co-Authored-By: Travis Ralston --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index ad617508..631ff5e8 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -196,7 +196,7 @@ may return: (new) terms in order to use or continue to use the service. The `_matrix/identity/v2/3pid/unbind` must not return either of these -errors if the request has a valid signature from a Homeserver. +errors if the request has a valid signature from a Homeserver, and is being authenticated as such. In summary, the process for using a service that has not previously been used in the current login sessions is: From 4edf826c9371aad7c5c22fec7434a1889d66b74c Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 26 Jun 2019 14:52:19 +0100 Subject: [PATCH 077/390] Capitalise on our identifiers --- proposals/2140-terms-of-service-2.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 631ff5e8..fc7d33b2 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -45,7 +45,7 @@ This proposal introduces: * The `m.accepted_terms` section in account data This proposal relies on both Integration Managers and Identity Servers being -able to identity users by their mxid and store the fact that a given mxid has +able to identity users by their MXID and store the fact that a given MXID has indicated that they accept the terms given. Integration Managers already identity users in this way by authenticating them using the OpenID endpoint on the Homeserver. This proposal introduces the same mechanism to Identity Servers @@ -221,8 +221,8 @@ A custom HTTP Header was also considered that could be added to assert that the client agrees to a particular set of terms. We decided against this in favour of re-using existing primitives that already exist in the Matrix ecosystem. Custom HTTP Headers are not used anywhere else within Matrix. This also gives a -very simple and natural way for ISes to enforce that users may only bind 3pids -to their own mxids. +very simple and natural way for ISes to enforce that users may only bind 3PIDs +to their own MXIDs. This introduces a different way of accepting terms from the client/server API which uses User-Interactive Authentication. In the client/server API, the use From 6273868323ab19ed42edaaa7d8d4ac5f738e718f Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 26 Jun 2019 15:05:43 +0100 Subject: [PATCH 078/390] Clarify v1 API deprecation --- proposals/2140-terms-of-service-2.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index fc7d33b2..6963c965 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -66,9 +66,11 @@ These endpoints require authentication by the client supplying an access token either via an `Authorization` header with a `Bearer` token or an `access_token` query parameter. -The existing endpoints under `/_matrix/identity/api/v1/` continue to be unauthenticated. -ISes may support the old v1 API for as long as they wish. Clients must update to use -the v2 API as soon as possible. +The existing endpoints under `/_matrix/identity/api/v1/` continue to be +unauthenticated but will be deprecated. ISes may support the old v1 API for as +long as they wish. Once ISes remove support for the old APIs, those endpoints +must return HTTP Status 404. Clients must update to use the v2 API as soon as +possible. OpenID authentication in the IS API will work the same as in the Integration Manager API, as specified in [MSC1961](https://github.com/matrix-org/matrix-doc/issues/1961). From 58cf083a6a93e273385cad5a1621895ca15272f5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 26 Jun 2019 15:31:11 +0100 Subject: [PATCH 079/390] backwards compat --- proposals/2140-terms-of-service-2.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 6963c965..7018e364 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -243,10 +243,8 @@ the document, but: ## Potential issues -This change is not backwards compatible: clients implementing older versions of -the specification will expect to be able to access all IS API endpoints without -authentication. Care should be taken to manage the rollout of authentication -on IS APIs. +This change deprecates all v1 endpoints and so will require clients to update +to continue working. ## Security considerations From 2694bb1090d565a10ef934a45ce01ae06a009804 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 26 Jun 2019 17:41:21 +0100 Subject: [PATCH 080/390] Add really horrible custom HTTP header for giving the IS token to the HS --- proposals/2140-terms-of-service-2.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 7018e364..ed1269a9 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -75,6 +75,12 @@ possible. OpenID authentication in the IS API will work the same as in the Integration Manager API, as specified in [MSC1961](https://github.com/matrix-org/matrix-doc/issues/1961). +When clients supply an identity server to the Homeserver in order for the Homeserver +to make calls to the IS on its behalf, it must also supply its access token for the +Identity Server either as the `is_token` query parameter or a bearer token in the +`X-Identity-Authorization` HTTP header with the same syntax as an `Authorizationn` +header. + ### IS Register API The following new APIs will be introduced to support OpenID auth as per From 21b9eaf8de06d2562c6326dedda66e334f03d6b0 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 26 Jun 2019 17:56:41 +0100 Subject: [PATCH 081/390] No custom HTTP headers Use the obvious way: in the same place as the ID server address --- proposals/2140-terms-of-service-2.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index ed1269a9..00a6ba52 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -77,9 +77,13 @@ API, as specified in [MSC1961](https://github.com/matrix-org/matrix-doc/issues/1 When clients supply an identity server to the Homeserver in order for the Homeserver to make calls to the IS on its behalf, it must also supply its access token for the -Identity Server either as the `is_token` query parameter or a bearer token in the -`X-Identity-Authorization` HTTP header with the same syntax as an `Authorizationn` -header. +Identity Server alongside in the `is_token` key of the same JSON object. That is, +in the main request object for a `requestToken` request and in the `threepidCreds` +object when supplying 3PID credentials (eg. in the `m.email.identity` UI auth stage). +Exxceptions to this are any requests where the only IS operation the Homeserver may +perform is unbinding, ie. `/_matrix/client/r0/account/deactivate` and +`/_matrix/client/r0/account/3pid/delete`, in which case the unbind is authenticated +by a signed request from the Homeserver. ### IS Register API From b5326de1c4c9d894d668cc4e5ec031226b7ad442 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 27 Jun 2019 16:34:46 +0100 Subject: [PATCH 082/390] Exclude requestToken endpoints from auth requirement --- proposals/2140-terms-of-service-2.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 00a6ba52..22bdb1f4 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -57,12 +57,20 @@ All current endpoints within `/_matrix/identity/api/v1/` will be duplicated into `/_matrix/identity/v2`, noting that MSC2134 changes the behaviour of lookups. Authentication is still expected on MSC2134's proposed endpoints. Any request to any endpoint within `/_matrix/identity/v2`, with the exception -of `/_matrix/identity/v2` and the new `/_matrix/identity/v2/account/register` -and `GET /_matrix/identity/v2/terms` may return an error with `M_UNAUTHORIZED` -errcode with HTTP status code 401. This indicates that the user must -authenticate with OpenID and supply a valid `access_token`. - -These endpoints require authentication by the client supplying an access token +of `/_matrix/identity/v2`, any `requestToken` endpoint and the new +`/_matrix/identity/v2/account/register` and `GET /_matrix/identity/v2/terms` +may return an error with `M_UNAUTHORIZED` errcode with HTTP status code 401. +This indicates that the user must authenticate with OpenID and supply a valid +`access_token`. + +`requestToken` endpoints are excluded from the auth check because they are used +in the registration process before the user has an MXID and therefore cannot +log in with OpenID. It is up to the IS to manage its privacy obligations +appropriately when fulfilling these requests, bearing in mind that the user has +not explictly incicated their agreement to any documents, and may abort the +registration process without doing so. + +All other endpoints require authentication by the client supplying an access token either via an `Authorization` header with a `Bearer` token or an `access_token` query parameter. From 10a6a59a12924243c2a321df111eec64d01f0234 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 27 Jun 2019 16:58:22 +0100 Subject: [PATCH 083/390] Deprecate `bind_email` / `bind_msisdn` --- proposals/2140-terms-of-service-2.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 22bdb1f4..74923e64 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -93,6 +93,13 @@ perform is unbinding, ie. `/_matrix/client/r0/account/deactivate` and `/_matrix/client/r0/account/3pid/delete`, in which case the unbind is authenticated by a signed request from the Homeserver. +### HS Register API + +The `bind_email` and `bind_msisdn` options to `/_matrix/client/r0/register` in +the client/server API will be deprecated. Due to the fact that +`/_matrix/identity/v2/3pid/bind` requires authentication, it will no longer be +possible for the Homeserver to bind 3PIDs as part of the registration process. + ### IS Register API The following new APIs will be introduced to support OpenID auth as per From f95197b4228aec45111c75923bca8f499a38eff7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 27 Jun 2019 17:30:36 +0100 Subject: [PATCH 084/390] make the many-anded sentence a list --- proposals/2140-terms-of-service-2.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 74923e64..b06b38eb 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -57,9 +57,13 @@ All current endpoints within `/_matrix/identity/api/v1/` will be duplicated into `/_matrix/identity/v2`, noting that MSC2134 changes the behaviour of lookups. Authentication is still expected on MSC2134's proposed endpoints. Any request to any endpoint within `/_matrix/identity/v2`, with the exception -of `/_matrix/identity/v2`, any `requestToken` endpoint and the new -`/_matrix/identity/v2/account/register` and `GET /_matrix/identity/v2/terms` -may return an error with `M_UNAUTHORIZED` errcode with HTTP status code 401. +of: + * `/_matrix/identity/v2` + * any `requestToken` endpoint + * The new `/_matrix/identity/v2/account/register` + * The new `GET /_matrix/identity/v2/terms` + +...may return an error with `M_UNAUTHORIZED` errcode with HTTP status code 401. This indicates that the user must authenticate with OpenID and supply a valid `access_token`. From 4be283ccb3c25008e53f987c94c3749c40baea14 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 27 Jun 2019 17:31:15 +0100 Subject: [PATCH 085/390] Typing Co-Authored-By: Travis Ralston --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index b06b38eb..cd6be68b 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -71,7 +71,7 @@ This indicates that the user must authenticate with OpenID and supply a valid in the registration process before the user has an MXID and therefore cannot log in with OpenID. It is up to the IS to manage its privacy obligations appropriately when fulfilling these requests, bearing in mind that the user has -not explictly incicated their agreement to any documents, and may abort the +not explicitly indicated their agreement to any documents, and may abort the registration process without doing so. All other endpoints require authentication by the client supplying an access token From e80753e56c70001bb71ac490f063993479db2440 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 27 Jun 2019 18:24:42 -0600 Subject: [PATCH 086/390] Add .well-known discovery --- proposals/1957-integrations-discovery.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/proposals/1957-integrations-discovery.md b/proposals/1957-integrations-discovery.md index fd6bc8f9..cef17098 100644 --- a/proposals/1957-integrations-discovery.md +++ b/proposals/1957-integrations-discovery.md @@ -106,6 +106,30 @@ Integration managers shown in this way must be treated like widgets, regardless this means exposing the Widget API to the manager and applying necessary scoping to keep the manager as an account widget rather than a room widget. +#### Discovering a manager by only the domain name + +Clients may wish to ask users for a single canonical domain name so they can find the manager to add +to the user's account transparently. Similar to the .well-known discovery done by servers (and clients +during login), clients which have an integrations domain (eg: "example.org") make a regular HTTPS +request to `https://example.org/.well-known/matrix/integrations` which returns an object which looks +like the following: +```json +{ + "m.integrations_widget": { + "url": "https://integrations.example.org/ui?displayName=$matrix_display_name", + "data": { + "api_url": "https://integrations.example.org" + } + } +} +``` + +The response should be parsed as JSON. If the endpoint returns an error or is missing the `m.integrations_widget` +property, the client should assume there is no integrations manager running on that domain. The +`m.integrations_widget` is an object which has the exact same format as the account widget for +an integration manager, described above. The client should wrap the object verbatim into the appropriate +account data location. + ## Tradeoffs From d8283b9cdf15f3b37990f67f2d7c49d5b863f9af Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 27 Jun 2019 20:44:49 -0600 Subject: [PATCH 087/390] Add option to use query string --- proposals/1961-integrations-auth.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/1961-integrations-auth.md b/proposals/1961-integrations-auth.md index 863ea325..cf69af27 100644 --- a/proposals/1961-integrations-auth.md +++ b/proposals/1961-integrations-auth.md @@ -8,7 +8,8 @@ manager. This proposal covers the authentication portion of that API. All specified APIs (except `/register`) will take an `Authorization` header with a `Bearer` token returned from a call to `/register`. This token is used to authorize the request and to identify who is making the -request. +request. The token may also be specified as the `access_token` query string parameter, similar to the +Client-Server API. #### POST /_matrix/integrations/v1/account/register From bfd8e52c234d367c9adc4ec686fc77b3b2f02c1d Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 27 Jun 2019 20:45:23 -0600 Subject: [PATCH 088/390] Formatting --- proposals/1961-integrations-auth.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/1961-integrations-auth.md b/proposals/1961-integrations-auth.md index cf69af27..7f735607 100644 --- a/proposals/1961-integrations-auth.md +++ b/proposals/1961-integrations-auth.md @@ -11,7 +11,7 @@ from a call to `/register`. This token is used to authorize the request and to i request. The token may also be specified as the `access_token` query string parameter, similar to the Client-Server API. -#### POST /_matrix/integrations/v1/account/register +#### POST `/_matrix/integrations/v1/account/register` Exchanges an OpenID object for a token which can be used to authorize future requests to the manager. @@ -31,7 +31,7 @@ to the `matrix_server_name` provided in the original OpenID object. Applications which register for a token are responsible for tracking which integration manager they are for. This can usually be done by tracking the hostname of the integration manager and matching a token with it. -#### GET /_matrix/integrations/v1/account +#### GET `/_matrix/integrations/v1/account` Gets information about the token's owner, such as the user ID for which it belongs. @@ -56,7 +56,7 @@ here. Preferably, custom information is stored under a namespaced key like so: } ``` -#### POST /_matrix/integrations/v1/account/logout +#### POST `/_matrix/integrations/v1/account/logout` Logs the token out, rendering it useless for future requests. From 83bb3861ba2926bd711930d1d0e89ff08087383e Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 28 Jun 2019 09:31:53 +0100 Subject: [PATCH 089/390] line wrap --- proposals/2140-terms-of-service-2.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index cd6be68b..69696946 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -54,7 +54,8 @@ and adds authentication to accross the Identity Service API. ### IS API Authentication All current endpoints within `/_matrix/identity/api/v1/` will be duplicated -into `/_matrix/identity/v2`, noting that MSC2134 changes the behaviour of lookups. Authentication is still expected on MSC2134's proposed endpoints. +into `/_matrix/identity/v2`, noting that MSC2134 changes the behaviour of +lookups. Authentication is still expected on MSC2134's proposed endpoints. Any request to any endpoint within `/_matrix/identity/v2`, with the exception of: From 45d630951c6c81aaa7796c08e348e71ac6290f72 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 28 Jun 2019 09:32:15 +0100 Subject: [PATCH 090/390] back to M_TERMS_NOT_SIGNED --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 69696946..5bcb58a7 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -224,7 +224,7 @@ may return: * `M_UNAUTHORIZED` errcode with HTTP status code 401. This indicates that the user must authenticate with OpenID and supply a valid `access_token`. - * `M_CONSENT_NOT_GIVEN` errcode. This indicates that the user must agree to + * `M_TERMS_NOT_SIGNED` errcode. This indicates that the user must agree to (new) terms in order to use or continue to use the service. The `_matrix/identity/v2/3pid/unbind` must not return either of these From 786d5bc281baf8ecbb79e4bf27ead5ab7adbde58 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 28 Jun 2019 10:25:24 +0100 Subject: [PATCH 091/390] rewrite UI auth tradeoffs --- proposals/2140-terms-of-service-2.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 5bcb58a7..f84c026c 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -259,9 +259,10 @@ to their own MXIDs. This introduces a different way of accepting terms from the client/server API which uses User-Interactive Authentication. In the client/server API, the use of UI auth allows terms acceptance to be integrated into the registration flow -in a simple and backwards-compatible way. Indtroducing the UI Auth mechanism -into these other APIs would add significant complexity, so this functionality -has been provided with simpler, dedicated endpoints. +in a simple and backwards-compatible way. Another option here would be to use +UI Auth on the register endpoint. This would also not allow users to register +before accepting the terms. However, this would then make the OpenID +registration process different and non-standard. The `m.accepted_terms` section contains only URLs of the documents that have been agreed to. This loses information like the name and version of From fe14d3c9f0928c037784f029f68af9fa7cd15757 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 28 Jun 2019 18:07:24 +0100 Subject: [PATCH 092/390] Spec terms response --- proposals/2140-terms-of-service-2.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index f84c026c..befa655d 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -175,6 +175,9 @@ any one language of each document as sufficient, regardless of what language a client is operating in: users should not have to re-consent to documents if they change their client to a different language. +The server responds with an empty JSON object. The server must not assume that +the client will agree to all documents in a single request. + ### Accepted Terms Account Data This proposal also defines the `m.accepted_terms` section in User Account From 8af35be13faff18010407f135d9b894fdded5768 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jul 2019 10:56:12 +0100 Subject: [PATCH 093/390] Typo Co-Authored-By: Travis Ralston --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index befa655d..e47584bb 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -47,7 +47,7 @@ This proposal introduces: This proposal relies on both Integration Managers and Identity Servers being able to identity users by their MXID and store the fact that a given MXID has indicated that they accept the terms given. Integration Managers already -identity users in this way by authenticating them using the OpenID endpoint on +identify users in this way by authenticating them using the OpenID endpoint on the Homeserver. This proposal introduces the same mechanism to Identity Servers and adds authentication to accross the Identity Service API. From 2d11217d4ed1282d4c80e97faffd544de2b34bcb Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jul 2019 10:56:32 +0100 Subject: [PATCH 094/390] Typo Co-Authored-By: Travis Ralston --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index e47584bb..2beeafbc 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -49,7 +49,7 @@ able to identity users by their MXID and store the fact that a given MXID has indicated that they accept the terms given. Integration Managers already identify users in this way by authenticating them using the OpenID endpoint on the Homeserver. This proposal introduces the same mechanism to Identity Servers -and adds authentication to accross the Identity Service API. +and adds authentication across the Identity Service API. ### IS API Authentication From 5374030cc0644dadb40bb53a7cde63cb58353376 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jul 2019 10:59:08 +0100 Subject: [PATCH 095/390] Drop application/x-form-www-urlencoded in v2 --- proposals/2140-terms-of-service-2.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 2beeafbc..0d4a9f7c 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -56,6 +56,8 @@ and adds authentication across the Identity Service API. All current endpoints within `/_matrix/identity/api/v1/` will be duplicated into `/_matrix/identity/v2`, noting that MSC2134 changes the behaviour of lookups. Authentication is still expected on MSC2134's proposed endpoints. +Support for `application/x-form-www-urlencoded` parameters in requests will +be dropped from all endpoints. Any request to any endpoint within `/_matrix/identity/v2`, with the exception of: From f02e4c2e9cae69d6cb2a7ffe0dc08edffde30a13 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jul 2019 11:00:39 +0100 Subject: [PATCH 096/390] both registers are excluded from auth Co-Authored-By: Travis Ralston --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 0d4a9f7c..e4a3083b 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -63,7 +63,7 @@ Any request to any endpoint within `/_matrix/identity/v2`, with the exception of: * `/_matrix/identity/v2` * any `requestToken` endpoint - * The new `/_matrix/identity/v2/account/register` + * The new `$prefix/account/register` endpoint * The new `GET /_matrix/identity/v2/terms` ...may return an error with `M_UNAUTHORIZED` errcode with HTTP status code 401. From d00dfb78226b2015633aac4b612f8ea28b9fefdd Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jul 2019 11:04:06 +0100 Subject: [PATCH 097/390] exclude submittoken too --- proposals/2140-terms-of-service-2.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index e4a3083b..568a50df 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -62,20 +62,21 @@ be dropped from all endpoints. Any request to any endpoint within `/_matrix/identity/v2`, with the exception of: * `/_matrix/identity/v2` - * any `requestToken` endpoint + * any `requestToken` or `submitToken` endpoint * The new `$prefix/account/register` endpoint * The new `GET /_matrix/identity/v2/terms` + * `$prefix/logout` ...may return an error with `M_UNAUTHORIZED` errcode with HTTP status code 401. This indicates that the user must authenticate with OpenID and supply a valid `access_token`. -`requestToken` endpoints are excluded from the auth check because they are used -in the registration process before the user has an MXID and therefore cannot -log in with OpenID. It is up to the IS to manage its privacy obligations -appropriately when fulfilling these requests, bearing in mind that the user has -not explicitly indicated their agreement to any documents, and may abort the -registration process without doing so. +`requestToken` and `submitToken` endpoints are excluded from the auth check +because they are used in the registration process before the user has an MXID +and therefore cannot log in with OpenID. It is up to the IS to manage its +privacy obligations appropriately when fulfilling these requests, bearing in +mind that the user has not explicitly indicated their agreement to any +documents, and may abort the registration process without doing so. All other endpoints require authentication by the client supplying an access token either via an `Authorization` header with a `Bearer` token or an `access_token` From 03e6ab0103fad0cf80d31ef0d4003f577bd130d3 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jul 2019 11:11:50 +0100 Subject: [PATCH 098/390] re-word double openid --- proposals/2140-terms-of-service-2.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 568a50df..2e874ee8 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -215,10 +215,11 @@ account data and presents to the user any documents that they have not already agreed to, along with UI for them to indicate their agreement. If there are no such documents (ie. if the `policies` dict is empty or the user has already agreed to all documents) the client proceeds to perform the OpenID -registration. Once the user has indicated their agreement, it adds these URLs -to `m.accepted_terms` account data. Once this has succeeded, then, and only -then, must the client perform OpenID authentication, getting a token from the -Homeserver and submitting this to the service using the `register` endpoint. +registration. If there are new terms documents, the client prompts the user for +agreement, then once the user has indicated their agreement, it adds these URLs +to `m.accepted_terms` account data and then proceeds with OpenID +authentication, getting a token from the Homeserver and submitting this to the +service using the `register` endpoint. Having done this, if the user agreed to any new documents, it performs a `POST $prefix/terms` request to signal to the server the set of documents that the From 7f653648045fded42b8e2321de52ed996dc81da2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jul 2019 11:13:12 +0100 Subject: [PATCH 099/390] Typo Co-Authored-By: Travis Ralston --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 2e874ee8..f7b43376 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -96,7 +96,7 @@ to make calls to the IS on its behalf, it must also supply its access token for Identity Server alongside in the `is_token` key of the same JSON object. That is, in the main request object for a `requestToken` request and in the `threepidCreds` object when supplying 3PID credentials (eg. in the `m.email.identity` UI auth stage). -Exxceptions to this are any requests where the only IS operation the Homeserver may +Exceptions to this are any requests where the only IS operation the Homeserver may perform is unbinding, ie. `/_matrix/client/r0/account/deactivate` and `/_matrix/client/r0/account/3pid/delete`, in which case the unbind is authenticated by a signed request from the Homeserver. From ac6b9bdb7ccf316ee77f65c3bd07edaed4a927f2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jul 2019 11:16:25 +0100 Subject: [PATCH 100/390] s/deprecate/remove/ --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index f7b43376..152f28d3 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -104,7 +104,7 @@ by a signed request from the Homeserver. ### HS Register API The `bind_email` and `bind_msisdn` options to `/_matrix/client/r0/register` in -the client/server API will be deprecated. Due to the fact that +the client/server API will be removed. Due to the fact that `/_matrix/identity/v2/3pid/bind` requires authentication, it will no longer be possible for the Homeserver to bind 3PIDs as part of the registration process. From 79dbad2914b1feeff57a908556fc95254c4de112 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jul 2019 11:17:27 +0100 Subject: [PATCH 101/390] remove acceptance token mention --- proposals/2140-terms-of-service-2.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 152f28d3..cb373678 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -170,12 +170,9 @@ the user has agreed to: This endpoint requires authentication. The clients MUST include the correct URL for the language of the document that -was presented to the user and they agreed to. How servers store or serialise -acceptance into the `acceptance_token` is not defined, eg. they may internally -transform all URLs to the URL of the English-language version of each document -if the server deems it appropriate to do so. Servers should accept agreement of -any one language of each document as sufficient, regardless of what language a -client is operating in: users should not have to re-consent to documents if +was presented to the user and they agreed to. Servers should accept agreement +of any one language of each document as sufficient, regardless of what language +a client is operating in: users should not have to re-consent to documents if they change their client to a different language. The server responds with an empty JSON object. The server must not assume that From 10858bf83b6c01cf8962d63d20f4db3c57e5fde6 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jul 2019 11:22:41 +0100 Subject: [PATCH 102/390] set account data after registration --- proposals/2140-terms-of-service-2.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index cb373678..0ee4ae20 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -197,9 +197,10 @@ An `m.accepted_terms` section therefore resembles the following: ``` Whenever a client submits a `POST $prefix/terms` request to an IS or IM or -completes an `m.terms` flow on the HS, it SHOULD update this account data -section adding any the URLs of any additional documents that the user agreed to -to this list. +completes an `m.terms` flow on the HS (or as soon as possible afterwards, ie. +after registration is complete), it SHOULD update this account data section +adding any the URLs of any additional documents that the user agreed to to this +list. ### Terms Acceptance in the API From 4c72c37b80aefef94c2ab1bc30719351e2f88ece Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jul 2019 11:24:51 +0100 Subject: [PATCH 103/390] slash Co-Authored-By: Travis Ralston --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 0ee4ae20..40278b37 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -223,7 +223,7 @@ Having done this, if the user agreed to any new documents, it performs a `POST $prefix/terms` request to signal to the server the set of documents that the user has agreed to. -Any request to any endpoint in the IM API, and the `_matrix/identity/v2/` +Any request to any endpoint in the IM API, and the `/_matrix/identity/v2/` namespace of the IS API, with the exception of `/_matrix/identity/v2` itself, may return: From e28f7aad7203fab769ea0c18638fdfd10275e6f3 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jul 2019 11:25:12 +0100 Subject: [PATCH 104/390] slash Co-Authored-By: Travis Ralston --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 40278b37..e5fd21d6 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -232,7 +232,7 @@ may return: * `M_TERMS_NOT_SIGNED` errcode. This indicates that the user must agree to (new) terms in order to use or continue to use the service. -The `_matrix/identity/v2/3pid/unbind` must not return either of these +The `/_matrix/identity/v2/3pid/unbind` endpoint must not return either of these errors if the request has a valid signature from a Homeserver, and is being authenticated as such. In summary, the process for using a service that has not previously been used From d15c9df115a09b02cee265063cc328072a4ddaf2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jul 2019 11:25:26 +0100 Subject: [PATCH 105/390] fullstop Co-Authored-By: Travis Ralston --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index e5fd21d6..4a69ee03 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -240,7 +240,7 @@ in the current login sessions is: * `GET $prefix/terms` * Compare result with `m.accepted_terms` account data, get set of documents - pending agreement + pending agreement. * If non-empty, show this set of documents to the user and wait for the user to indicate their agreement. * Add the newly agreed documents to `m.accepted_terms` From 1a669348d8de4e83675e4847abbc67595ac0863b Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jul 2019 11:27:46 +0100 Subject: [PATCH 106/390] http status code --- proposals/2140-terms-of-service-2.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 4a69ee03..dbdb19f6 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -229,8 +229,9 @@ may return: * `M_UNAUTHORIZED` errcode with HTTP status code 401. This indicates that the user must authenticate with OpenID and supply a valid `access_token`. - * `M_TERMS_NOT_SIGNED` errcode. This indicates that the user must agree to - (new) terms in order to use or continue to use the service. + * `M_TERMS_NOT_SIGNED` errcode with HTTP status code 403. This indicates + that the user must agree to (new) terms in order to use or continue to + use the service. The `/_matrix/identity/v2/3pid/unbind` endpoint must not return either of these errors if the request has a valid signature from a Homeserver, and is being authenticated as such. From 3aaf181db24dd0d722c51fc079d90cbcdff30383 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 2 Jul 2019 14:03:35 -0400 Subject: [PATCH 107/390] rename some things and add clarification --- proposals/1946-secure_server-side_storage.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index 8487d6d4..dff7a5c7 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -66,7 +66,7 @@ the backup's `auth_data` to see of the key config is the same? ##### `m.secret_storage.v1.curve25519-aes-sha2` The public key is stored in the `pubkey` property of the `m.secret_storage.key.[key -ID]` `account_data`. +ID]` `account_data` as a base64-encoded string. The data is encrypted and MACed as follows: @@ -117,7 +117,7 @@ ID]` account-data: "passphrase": { "algorithm": "m.pbkdf2", "salt": "MmMsAlty", - "rounds": 100000 + "iterations": 100000 }, ... } @@ -125,8 +125,8 @@ ID]` account-data: **`m.pbkdf2`** -The key is generated using PBKDF2 using the salt given in the `salt` -parameter, and the number of rounds given in the `rounds` parameter. +The key is generated using PBKDF2 using the salt given in the `salt` parameter, +and the number of iterations given in the `iterations` parameter. ### Sharing @@ -136,7 +136,7 @@ shared key, devices can send secrets to each other, encrypted using olm. To request a secret, a client sends a `m.secret.request` event with `action` set to `request` to other devices, and `name` set to the name of the secret that it wishes to retrieve. A device that wishes to share the secret will -reply with a `m.secret.share` event, encrypted using olm. When the original +reply with a `m.secret.send` event, encrypted using olm. When the original client obtains the secret, it sends a `m.secret.request` event with `action` set to `cancel_request` to all devices other than the one that it received the secret from. @@ -165,7 +165,7 @@ unencrypted to-device event. request for a secret. If the secret is requested multiple times, it should be reused. It should also reused in order to cancel a request. -##### `m.secret.share` +##### `m.secret.send` Sent by a client to share a secret with another device, in response to an `m.secret.request` event. Typically it is encrypted as an `m.room.encrypted` From 4d31ddc8c9bb1058c5a24ccf9884b13ef6fdb4a7 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 3 Jul 2019 15:09:06 -0400 Subject: [PATCH 108/390] additions and clarifications - indicate how to use MSC 1946 to store/share private keys - add signing by devices to enable migrating from device verifications - add information about signature upload failures and M_INVALID_SIGNATURE code - add security consideration --- proposals/1756-cross-signing.md | 85 ++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 13 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 5595b2b4..ec3edc33 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -57,18 +57,37 @@ store it at all. Clients will need to balance the security of the keys with the usability of signing users and devices when performing key verification. The private halves of a user's cross-signing keys be stored encrypted on the -server so that they may be retrieved by new devices. FIXME: explain how to do -this via MSC 1946 +server so that they may be retrieved by new devices, or shared between devices +using [MSC 1946](https://github.com/matrix-org/matrix-doc/pull/1946). When +handled in this way, the keys must be base64-encoded, and use the names +`m.cross_signing.master`, `m.cross_signing.self_signing`, and +`m.cross_signing.user_signing` for the master, self-signing, and user-signing +keys, respectively. ### Signature distribution -Currently, users will only be allowed to see signatures made by her own master, -self-signing or user-signing keys, or signatures made by other users' master or -self-signing keys about their own devices. This is done in order to preserve -the privacy of social connections. Future proposals may define mechanisms for +Currently, users will only be allowed to see signatures made by their own +master, self-signing or user-signing keys, signatures of their own master key +made by their own devices, signatures made by other users' master or +self-signing keys about their own devices, or signatures made of other users' +master keys by their own devices. This is done in order to preserve the +privacy of social connections. Future proposals may define mechanisms for distributing signatures to other users in order to allow for other web-of-trust use cases. +### Migrating from device verifications + +Users who have verified individual devices may wish to migrate these +verifications to use cross-signing instead. In order to aid with this, +signatures of a user's master key, made by their own devices, may be uploaded +to the server. If another client sees that the user's master key has a valid +signature from a device that was previously verified, then the client MAY +choose to trust and sign the master key. The client SHOULD take precautions to +ensure that a stolen device cannot be used to cause it to trust a malicious +master key. For example, a client could prompt the user before signing the +master key, or it could only do this migration on the first master key that it +sees from a user. + ### API description #### Uploading signing keys @@ -126,7 +145,8 @@ properties: "`ed25519:`" followed by the unpadded base64 encoding of the public key, and whose value is the unpadded base64 encoding of the public key. * `signatures` ({string: {string: string}}): signatures of the key. A - self-signing or user-signing key must be signed by the master key. + self-signing or user-signing key MUST be signed by the master key. A master + key MAY be signed by a device. In order to ensure that there will be no collisions in the `signatures` property, the server must respond with an error (FIXME: what error?) if any of @@ -136,7 +156,8 @@ keys, the server must respond with an error (FIXME: what error?). If a self-signing or user-signing key is uploaded, it must be signed by the master key that is included in the request, or the current master key if no -master key is included. +master key is included. If the signature from the master key is incorrect, the +server should respond with an error code of `M_INVALID_SIGNATURE`. After uploading cross-signing keys, they will be included under the `/keys/query` endpoint under the `master_keys`, `self_signing_keys` and @@ -287,10 +308,11 @@ others users who share an encrypted room with that user. #### Uploading signatures -Signatures of keys can be uploaded using `/keys/signatures/upload`. +Signatures of device keys can be uploaded using `/keys/signatures/upload`. For example, Alice signs one of her devices (HIJKLMN) (using her self-signing -key), and signs Bob's master key (using her user-signing key). +key), her own master key (using her HIJKLMN device), Bob's master key (using +her user-signing key). `POST /keys/signatures/upload` @@ -313,6 +335,18 @@ key), and signs Bob's master key (using her user-signing key). "ed25519:base64+self+signing+public+key": "base64+signature+of+HIJKLMN" } } + }, + "base64+master+public+key": { + "user_id": "@alice:example.com", + "usage": ["master"], + "keys": { + "ed25519:base64+master+public+key": "base64+master+public+key" + }, + "signatures": { + "@alice:example.com": { + "ed25519:HIJKLMN": "base64+signature+of+master+key" + } + } } }, "@bob:example.com": { @@ -332,9 +366,23 @@ key), and signs Bob's master key (using her user-signing key). } ``` -After Alice uploads a signature for her own devices, her signature will be -included in the results of the `/keys/query` request when *anyone* requests her -keys: +response: + +``` json +{ + "failures": {} +} +``` + +The response contains a `failures` property, which is a map of user ID to +device ID to failure reason, if any of the uploaded keys failed. The +homeserver should verify that the signature is correct. If it is not, the +homeserver should set the corresponding entry in `failures` to a JSON object +with the `errcode` property set to `M_INVALID_SIGNATURE`. + +After Alice uploads a signature for her own devices or master key, her +signature will be included in the results of the `/keys/query` request when +*anyone* requests her keys: `POST /keys/query` @@ -382,6 +430,11 @@ response: "usage": ["master"], "keys": { "ed25519:base64+master+public+key": "base64+master+public+key" + }, + "signatures": { + "@alice:example.com": { + "ed25519:HIJKLMN": "base64+signature+of+master+key" + } } }, "self_signing_key": { @@ -484,6 +537,12 @@ deleted and replaced. An attacker who is able to both steal a user's device and control their homeserver could prevent that device from being marked as untrusted. +An attacker may be able to upload a large number of signatures in a DoS attack +against clients or servers, similar to the [attack against the SKS keyserver +network](https://gist.github.com/rjhansen/67ab921ffb4084c865b3618d6955275f). +Since clients are only sent a subset of signatures, and the attestation graph +is limited, a DoS attack is less likely to be successful in this case. + ## Conclusion This proposal presents an alternative cross-signing mechanism to MSC1680, From 30dcc28f9b7280f88cc6898182ffa3891c1b18e7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 4 Jul 2019 18:38:31 +0100 Subject: [PATCH 109/390] try & clarify that HS signature isn't the only acceptable auth for unbind --- proposals/2140-terms-of-service-2.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index dbdb19f6..2f6c88d8 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -91,15 +91,16 @@ possible. OpenID authentication in the IS API will work the same as in the Integration Manager API, as specified in [MSC1961](https://github.com/matrix-org/matrix-doc/issues/1961). -When clients supply an identity server to the Homeserver in order for the Homeserver -to make calls to the IS on its behalf, it must also supply its access token for the -Identity Server alongside in the `is_token` key of the same JSON object. That is, -in the main request object for a `requestToken` request and in the `threepidCreds` -object when supplying 3PID credentials (eg. in the `m.email.identity` UI auth stage). -Exceptions to this are any requests where the only IS operation the Homeserver may -perform is unbinding, ie. `/_matrix/client/r0/account/deactivate` and -`/_matrix/client/r0/account/3pid/delete`, in which case the unbind is authenticated -by a signed request from the Homeserver. +When clients supply an identity server to the Homeserver in order for the +Homeserver to make calls to the IS on its behalf, it must also supply its +access token for the Identity Server alongside in the `is_token` key of the +same JSON object. That is, in the main request object for a `requestToken` +request and in the `threepidCreds` object when supplying 3PID credentials (eg. +in the `m.email.identity` UI auth stage). Exceptions to this are any requests +where the only IS operation the Homeserver may perform is unbinding, ie. +`/_matrix/client/r0/account/deactivate` and +`/_matrix/client/r0/account/3pid/delete`, in which case the unbind will be +authenticated by a signed request from the Homeserver. ### HS Register API From bf8a1e5d5f14855470ba607976ed4ae4f1c1898c Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 5 Jul 2019 18:11:42 +0100 Subject: [PATCH 110/390] Add way to get the HS to bind/unbind existing 3pids --- proposals/2140-terms-of-service-2.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 2f6c88d8..e203ffcf 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -41,8 +41,14 @@ following convention from [MSC2134](https://github.com/matrix-org/matrix-doc/issues/2134). This proposal introduces: + * A v2 API prefix, with authentication, for the Identity Service * The `$prefix/terms` endpoint * The `m.accepted_terms` section in account data + * `POST /_matrix/client/r0/account/3pid/unbind` endpoints on the client/server + API + +This proposal removes: + * The `bind_email` and `bind_msisdn` on the Homeserver `/register` endpoint This proposal relies on both Integration Managers and Identity Servers being able to identity users by their MXID and store the fact that a given MXID has @@ -252,6 +258,20 @@ in the current login sessions is: * If the set of documents pending agreement was non-empty, Perform a `POST $prefix/terms` request to the servcie with these documents. +### `POST /_matrix/client/r0/account/3pid/unbind` + +A client uses this client/server API endpoint to request that the Homeserver +removes the given 3PID from the given Identity Server, or all Identity Servers. +Takes parameters the same parameters as +`POST /_matrix/client/r0/account/3pid/delete`, ie. `id_server`, `medium`, +`address` and the newly added `is_token`. + +Returns the same as `POST /_matrix/client/r0/account/3pid/delete`. + +Clients may add IS bindings for 3PIDs that already exist on the user's +Homeserver account by using the `POST /_matrix/client/r0/account/3pid` +to re-add the 3PID. + ## Tradeoffs The Identity Service API previously did not require authentication, and OpenID From 701d340da18b9fbfdc98f4f5aff1f8e6ea2e7f54 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 5 Jul 2019 19:00:15 +0100 Subject: [PATCH 111/390] Remove exception for request/submitToken --- proposals/2140-terms-of-service-2.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index e203ffcf..9608093a 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -68,7 +68,6 @@ be dropped from all endpoints. Any request to any endpoint within `/_matrix/identity/v2`, with the exception of: * `/_matrix/identity/v2` - * any `requestToken` or `submitToken` endpoint * The new `$prefix/account/register` endpoint * The new `GET /_matrix/identity/v2/terms` * `$prefix/logout` @@ -77,13 +76,6 @@ of: This indicates that the user must authenticate with OpenID and supply a valid `access_token`. -`requestToken` and `submitToken` endpoints are excluded from the auth check -because they are used in the registration process before the user has an MXID -and therefore cannot log in with OpenID. It is up to the IS to manage its -privacy obligations appropriately when fulfilling these requests, bearing in -mind that the user has not explicitly indicated their agreement to any -documents, and may abort the registration process without doing so. - All other endpoints require authentication by the client supplying an access token either via an `Authorization` header with a `Bearer` token or an `access_token` query parameter. From 9bb6ad80d1c01e6d1588cf77f05fc84af1260e98 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 10 Jul 2019 16:13:38 +0100 Subject: [PATCH 112/390] typo --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 9608093a..fa89f603 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -248,7 +248,7 @@ in the current login sessions is: token from the Homeserver and submit this token to the `register` endpoint. Store the resulting access token. * If the set of documents pending agreement was non-empty, Perform a - `POST $prefix/terms` request to the servcie with these documents. + `POST $prefix/terms` request to the service with these documents. ### `POST /_matrix/client/r0/account/3pid/unbind` From f474b31f5f8b74604ae652ec62eb3090615a80ee Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 12 Jul 2019 11:54:45 +0100 Subject: [PATCH 113/390] typo Co-Authored-By: J. Ryan Stinnett --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index fa89f603..b840932c 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -51,7 +51,7 @@ This proposal removes: * The `bind_email` and `bind_msisdn` on the Homeserver `/register` endpoint This proposal relies on both Integration Managers and Identity Servers being -able to identity users by their MXID and store the fact that a given MXID has +able to identify users by their MXID and store the fact that a given MXID has indicated that they accept the terms given. Integration Managers already identify users in this way by authenticating them using the OpenID endpoint on the Homeserver. This proposal introduces the same mechanism to Identity Servers From 6e061b1baf8e03de4b01525b9aa39d4eb7e82c8b Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 12 Jul 2019 11:55:11 +0100 Subject: [PATCH 114/390] unnecessary capital Co-Authored-By: J. Ryan Stinnett --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index b840932c..21caa305 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -268,7 +268,7 @@ to re-add the 3PID. The Identity Service API previously did not require authentication, and OpenID is reasonably complex, adding a significant burden to both clients and servers. -A custom HTTP Header was also considered that could be added to assert that the +A custom HTTP header was also considered that could be added to assert that the client agrees to a particular set of terms. We decided against this in favour of re-using existing primitives that already exist in the Matrix ecosystem. Custom HTTP Headers are not used anywhere else within Matrix. This also gives a From 25a47afa329d39538b0856a1b6df3b4bec293197 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 12 Jul 2019 11:55:40 +0100 Subject: [PATCH 115/390] unnecessary capital mk. 2 Co-Authored-By: J. Ryan Stinnett --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 21caa305..eb4fd75c 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -271,7 +271,7 @@ is reasonably complex, adding a significant burden to both clients and servers. A custom HTTP header was also considered that could be added to assert that the client agrees to a particular set of terms. We decided against this in favour of re-using existing primitives that already exist in the Matrix ecosystem. -Custom HTTP Headers are not used anywhere else within Matrix. This also gives a +Custom HTTP headers are not used anywhere else within Matrix. This also gives a very simple and natural way for ISes to enforce that users may only bind 3PIDs to their own MXIDs. From cd5549d483782d9171f3c810974786b7672a9d95 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Sun, 14 Jul 2019 22:50:46 +0100 Subject: [PATCH 116/390] Proposal to update the redaction algorithm --- proposals/2176-update-redaction-rules.md | 48 ++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 proposals/2176-update-redaction-rules.md diff --git a/proposals/2176-update-redaction-rules.md b/proposals/2176-update-redaction-rules.md new file mode 100644 index 00000000..a939178a --- /dev/null +++ b/proposals/2176-update-redaction-rules.md @@ -0,0 +1,48 @@ +# MSC2176: Update the redaction rules + +The current [redaction +algorithm](https://matrix.org/docs/spec/client_server/r0.5.0#redactions) is now +somewhat dated. This MSC proposes a number of changes to the rules which will +improve the security and reliability of the Matrix protocol. + +## Proposal + +The following changes will require a new room version, since changes to the +redaction algorithm also change the way that [event +hashes](https://matrix.org/docs/spec/server_server/r0.1.2#calculating-the-reference-hash-for-an-event) +(and hence event IDs) are calculated. + +The following *event* keys should be *removed* from the list of those to be +preserved by a redaction: + + * `membership` + * `prev_state` + +(Note this refers to the *event-level* `membership` property, rather than the +similarly-named sub-property under the `content` key.) + +Ratinale: neither of the above properties have defined meanings in the Matrix +protocol, so there is no reason for them to be special-cased in this way. + +The following should be added to the list of subkeys of the content property +which should be preserved: + + * `m.room.redaction` should allow the `redacts` key (assuming + [MSC2174](https://github.com/matrix-org/matrix-doc/pull/2174) is merged). + Rationale: currently, redacting a redaction can lead to inconsistent results + among homservers, depending on whether they receive the `m.room.redaction` + result before or after it is redacted (and therefore may or may not redact + the original event). + + * `m.room.create` should allow the `room_version` key. Currently, redacting an + `m.room.create` event will make the room revert to a v1 room. + + * `m.room.power_levels` should allow the `notifications` key. Rationale: + symmetry with the other `power_levels` settings. (Maybe? See + https://github.com/matrix-org/matrix-doc/issues/1601#issuecomment-511237744.) + + +## Potential issues + +What if there is spam in sub-properties of the `notifications` property of +power-levels? Should we not be able to redact it? From a1de6ff63445c0e59233875a9c53eee6efc9a9e5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 15 Jul 2019 10:20:25 +0100 Subject: [PATCH 117/390] Hopefully clarify some bits --- proposals/2140-terms-of-service-2.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index eb4fd75c..1120373f 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -76,9 +76,8 @@ of: This indicates that the user must authenticate with OpenID and supply a valid `access_token`. -All other endpoints require authentication by the client supplying an access token -either via an `Authorization` header with a `Bearer` token or an `access_token` -query parameter. +Clients authenticate either via an `Authorization` header with a `Bearer` token +or an `access_token` query parameter. The existing endpoints under `/_matrix/identity/api/v1/` continue to be unauthenticated but will be deprecated. ISes may support the old v1 API for as @@ -142,6 +141,17 @@ to use the service. Its response is similar to the structure used in the "name": "Conditions d'utilisation", "url": "https://example.org/somewhere/terms-2.0-fr.html" } + }, + "privacy_policy": { + "version": "1.2", + "en": { + "name": "Privacy Policy", + "url": "https://example.org/somewhere/privaacy-1.2-en.html" + }, + "fr": { + "name": "Politique de confidentialité", + "url": "https://example.org/somewhere/privacy-1.2-fr.html" + } } } } From d9269b084f6fbb6e95364c9bb919596919f3a3f3 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 15 Jul 2019 16:58:24 +0100 Subject: [PATCH 118/390] Exclude pubkey endpoints from auth --- proposals/2140-terms-of-service-2.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 1120373f..557376cc 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -68,6 +68,7 @@ be dropped from all endpoints. Any request to any endpoint within `/_matrix/identity/v2`, with the exception of: * `/_matrix/identity/v2` + * `/_matrix/identity/v2/pubkey/*` * The new `$prefix/account/register` endpoint * The new `GET /_matrix/identity/v2/terms` * `$prefix/logout` From b49a95024580e77bf45e04913f4bab4aded8e932 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Tue, 16 Jul 2019 19:26:02 +0100 Subject: [PATCH 119/390] Update proposals/2176-update-redaction-rules.md fix typo Co-Authored-By: Kitsune Ral --- proposals/2176-update-redaction-rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2176-update-redaction-rules.md b/proposals/2176-update-redaction-rules.md index a939178a..d32c9cda 100644 --- a/proposals/2176-update-redaction-rules.md +++ b/proposals/2176-update-redaction-rules.md @@ -21,7 +21,7 @@ preserved by a redaction: (Note this refers to the *event-level* `membership` property, rather than the similarly-named sub-property under the `content` key.) -Ratinale: neither of the above properties have defined meanings in the Matrix +Rationale: neither of the above properties have defined meanings in the Matrix protocol, so there is no reason for them to be special-cased in this way. The following should be added to the list of subkeys of the content property From d324cac847507f9527ce53cd224fa6bc0f873015 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 16 Jul 2019 19:32:34 +0100 Subject: [PATCH 120/390] preserve powerlevel --- proposals/2176-update-redaction-rules.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/proposals/2176-update-redaction-rules.md b/proposals/2176-update-redaction-rules.md index d32c9cda..14c97da0 100644 --- a/proposals/2176-update-redaction-rules.md +++ b/proposals/2176-update-redaction-rules.md @@ -37,9 +37,16 @@ which should be preserved: * `m.room.create` should allow the `room_version` key. Currently, redacting an `m.room.create` event will make the room revert to a v1 room. - * `m.room.power_levels` should allow the `notifications` key. Rationale: - symmetry with the other `power_levels` settings. (Maybe? See - https://github.com/matrix-org/matrix-doc/issues/1601#issuecomment-511237744.) + * `m.room.power_levels` should allow: + + * the `invite` key. Rationale: this is required to authenticate + `m.room.member` events with the `invite` membership. Currently, redacting + a `power_levels` event will mean that such events cannot be authenticated, + potentially leading to a split-brain room. + + * the `notifications` key. Rationale: symmetry with the other `power_levels` + settings. (Maybe? See + https://github.com/matrix-org/matrix-doc/issues/1601#issuecomment-511237744.) ## Potential issues From 9e264fedc964c5ed44f39e7ddc23e68c5fd48486 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 22 Jul 2019 16:47:49 +0100 Subject: [PATCH 121/390] Updates * preserve *all* of `create` * don't preserve `notifications` or `algorithm`, and add some justifcation. --- proposals/2176-update-redaction-rules.md | 59 ++++++++++++++++++++---- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/proposals/2176-update-redaction-rules.md b/proposals/2176-update-redaction-rules.md index 14c97da0..dea3015f 100644 --- a/proposals/2176-update-redaction-rules.md +++ b/proposals/2176-update-redaction-rules.md @@ -27,6 +27,12 @@ protocol, so there is no reason for them to be special-cased in this way. The following should be added to the list of subkeys of the content property which should be preserved: + * `m.room.create` should preserve *all* content. Rationale: the values in a + `create` event are deliberately intented to last the lifetime of the room, + and if values are redacted, there is no way to add correct settings + afterwards. It therefore seems non-sensical to allow redaction of a `create` + event. + * `m.room.redaction` should allow the `redacts` key (assuming [MSC2174](https://github.com/matrix-org/matrix-doc/pull/2174) is merged). Rationale: currently, redacting a redaction can lead to inconsistent results @@ -34,9 +40,6 @@ which should be preserved: result before or after it is redacted (and therefore may or may not redact the original event). - * `m.room.create` should allow the `room_version` key. Currently, redacting an - `m.room.create` event will make the room revert to a v1 room. - * `m.room.power_levels` should allow: * the `invite` key. Rationale: this is required to authenticate @@ -44,12 +47,50 @@ which should be preserved: a `power_levels` event will mean that such events cannot be authenticated, potentially leading to a split-brain room. - * the `notifications` key. Rationale: symmetry with the other `power_levels` - settings. (Maybe? See - https://github.com/matrix-org/matrix-doc/issues/1601#issuecomment-511237744.) +## Other properties considered for preservation + +Currently it is *not* proposed to add these to the list of properties which are +proposed for a redaction: + + * The `notifications` key of `m.room.power_levels`. Unlike the other + properties in `power_levels`, `notifications` does not play a part in + authorising the events in the room graph. Once the `power_levels` are + replaced, historical values of the `notifications` property are + irrelevant. There is therefore no need for it to be protected from + redactions. + + * The `algorithm` key of `m.room.encryption`. Again, historical values of + `m.room.encryption` have no effect, and servers do not use the value of the + property to authenticate events. + + The effect of redacting an `m.room.redaction` event is much the same as that + of sending a new `m.room.redaction` event with no `algorithm` key. It's + unlikely to be what was intended, but adding rules to the redaction + algorithm will not help this. + +### Background to things not included in the proposal + +The approach taken here has been to minimise the list of properties preserved +by redaction; in general, the list is limited to those which are required by +servers to authenticate events in the room. One reason for this is to simplify +the implementation of servers and clients, but a more important philosophical +reason is as follows. + +Changing the redaction algorithm requires changes to both servers and clients, +so changes are difficult and will happen rarely. Adding additional keys now +sets an awkward precedent. + +It is likely that in the future more properties will be defined which might be +convenient to preserve under redaction. One of the two scenarios would then +happen: + * We would be forced to issue yet more updates to the redaction algorithm, + with a new room versions and mandatory updates to all servers and clients, or: -## Potential issues + * We would end up with an awkward asymmetry between properties which were + preserved under this MSC, and those which were introduced later so were not + preserved. -What if there is spam in sub-properties of the `notifications` property of -power-levels? Should we not be able to redact it? +In short, I consider it important for the elegance of the Matrix protocol that +we do not add unnecessary properties to the list of those to be preserved by +redaction. From f1f293678bcf1eaaca05a6748bf85639796e4f2b Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Tue, 30 Jul 2019 08:00:48 +0100 Subject: [PATCH 122/390] Apply suggestions from code review Co-Authored-By: Travis Ralston Co-Authored-By: Kitsune Ral --- proposals/2176-update-redaction-rules.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/proposals/2176-update-redaction-rules.md b/proposals/2176-update-redaction-rules.md index dea3015f..75bfc971 100644 --- a/proposals/2176-update-redaction-rules.md +++ b/proposals/2176-update-redaction-rules.md @@ -12,7 +12,7 @@ redaction algorithm also change the way that [event hashes](https://matrix.org/docs/spec/server_server/r0.1.2#calculating-the-reference-hash-for-an-event) (and hence event IDs) are calculated. -The following *event* keys should be *removed* from the list of those to be +The following *event* keys are to be *removed* from the list of those to be preserved by a redaction: * `membership` @@ -21,14 +21,14 @@ preserved by a redaction: (Note this refers to the *event-level* `membership` property, rather than the similarly-named sub-property under the `content` key.) -Rationale: neither of the above properties have defined meanings in the Matrix +Rationale: neither of the above properties have defined meanings any more in the Matrix protocol, so there is no reason for them to be special-cased in this way. -The following should be added to the list of subkeys of the content property -which should be preserved: +The following are to be added to the list of subkeys of the content property +which are preserved: - * `m.room.create` should preserve *all* content. Rationale: the values in a - `create` event are deliberately intented to last the lifetime of the room, + * `m.room.create` preserves *all* content. Rationale: the values in a + `create` event are deliberately intended to last the lifetime of the room, and if values are redacted, there is no way to add correct settings afterwards. It therefore seems non-sensical to allow redaction of a `create` event. @@ -63,8 +63,8 @@ proposed for a redaction: `m.room.encryption` have no effect, and servers do not use the value of the property to authenticate events. - The effect of redacting an `m.room.redaction` event is much the same as that - of sending a new `m.room.redaction` event with no `algorithm` key. It's + The effect of redacting an `m.room.encryption` event is much the same as that + of sending a new `m.room.encryption` event with no `algorithm` key. It's unlikely to be what was intended, but adding rules to the redaction algorithm will not help this. From 395d40314bcbaf39646c03bdada1e7a59efe3ea4 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 31 Jul 2019 16:20:39 -0400 Subject: [PATCH 123/390] fix typos and make valid JSON --- proposals/1946-secure_server-side_storage.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index dff7a5c7..7fb426ce 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -21,7 +21,7 @@ prevent homeserver administrators from being able to read it. A user can have multiple keys used for encrypting data. This allows the user to selectively decrypt data on clients. For example, the user could have one key that can decrypt everything, and another key that can only decrypt their user-signing -key for cross-signing. Each key has an ID, and a discription of the key is +key for cross-signing. Each key has an ID, and a description of the key is stored in the user's `account_data` using the `type` `m.secret_storage.key.[key ID]`. The contents of the account data for the key will include an `algorithm` property, which indicates the encryption algorithm used, as well as a `name` @@ -35,12 +35,12 @@ decryption keys for key backups could be stored under the type `m.megolm_backup.v1.recovery_key`, or the self-signing key for cross-signing could be stored under the type `m.cross_signing.self_signing`. -Data will be stored using using the following format: +Data will be stored using the following format: ```json { "encrypted": { - [key ID]: { + "key_id": { "ciphertext": "base64+encoded+encrypted+data", "mac": "base64+encoded+mac" } From d47e13c6d94392583e3d17b18f72ef71250754c5 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 31 Jul 2019 16:20:55 -0400 Subject: [PATCH 124/390] this FIXME will be addressed in the key backup MSC --- proposals/1946-secure_server-side_storage.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index 7fb426ce..c0c828f2 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -54,13 +54,6 @@ expected that most encryption schemes would have `ciphertext` and `mac` properties, where the `ciphertext` property is the unpadded base64-encoded ciphertext, and the `mac` is used to ensure the integrity of the data. -FIXME: the key format was chosen so that existing backups could be easily -migrated by just copying the configuration from the backup config to the key -description. However, we need a way of signalling that the key for the backup -is the same as the key for decrypting the other bits. Maybe a special flag in -the account data? Or special case backups somehow, say, have clients inspect -the backup's `auth_data` to see of the key config is the same? - #### Encryption algorithms ##### `m.secret_storage.v1.curve25519-aes-sha2` From bd9efcdf53b9fb8ecf55db6f36db951080d8c715 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 31 Jul 2019 16:22:24 -0400 Subject: [PATCH 125/390] add some information and an example --- proposals/1946-secure_server-side_storage.md | 36 ++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index c0c828f2..2f269f23 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -29,6 +29,11 @@ property, which is a human-readable name. The contents will be signed as signed JSON using the user's master cross-signing key. Other properties depend on the encryption algorithm, and are described below. +If a key has the `name` property set to `m.default`, then this key is marked as +the default key for the account. The default key is the one that all secrets +will be encrypted with, and the clients will try to use to decrypt data with, +unless the user specifies otherwise. Only one key can be marked as the default. + Encrypted data can be stored using the `account_data` API. The `type` for the `account_data` is defined by the feature that uses the data. For example, decryption keys for key backups could be stored under the type @@ -80,7 +85,29 @@ The data is encrypted and MACed as follows: (The key HKDF, AES, and HMAC steps are the same as what are used for encryption in olm and megolm.) -FIXME: add an example of `m.secret_storage.key.*`, and of encrypted data. +For example, a key using this algorithm could look like: + +```json +{ + "name": "m.default", + "algorithm": "m.secret_storage.v1.curve25519-aes-sha2", + "pubkey": "base64+public+key" +} +``` + +and data encrypted using this algorithm could look like this: + +```json +{ + "encrypted": { + "key_id": { + "ciphertext": "base64+encoded+encrypted+data", + "ephemeral": "base64+ephemeral+key", + "mac": "base64+encoded+mac" + } + } +} +``` ###### Keys @@ -181,7 +208,12 @@ data to indicate whether it should come down the `/sync` or not. ## Security considerations -Yes. +By storing information encrypted on the server, this allows the server operator +to read the information if they manage to get hold of the decryption keys. +In particular, if the key is based on a passphrase and the passphrase can be +guessed, then the secrets could be compromised. In order to help protect the +secrets, clients should provide feedback to the user when their chosen +passphrase is considered weak. ## Conclusion From 825757ffd8c9f38f588b4d6dadf0a826675ff601 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 31 Jul 2019 16:37:54 -0400 Subject: [PATCH 126/390] add information about verifying backup by entering key --- proposals/1219-storing-megolm-keys-serverside.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 09ded1cd..e1ff0740 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -493,7 +493,10 @@ key backup. This proposal does not attempt to protect against that. An attacker who gains access to a user's account can create a new backup version using a key that they control. For this reason, clients SHOULD confirm with users before sending keys to a new backup version or verify that it was -created by a trusted device by checking the signature. +created by a trusted device by checking the signature. One way to confirm the +new backup version if the signature cannot be checked is by asking the user to +enter the recovery key, and confirming that the backup's public key matches +what is expected. Other Issues ------------ From 80adbaff4c69ae342f16c94894e0b46006a77597 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 31 Jul 2019 16:38:20 -0400 Subject: [PATCH 127/390] switch to MSC1946 for storing recovery key --- .../1219-storing-megolm-keys-serverside.md | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index e1ff0740..7e074a0d 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -102,8 +102,8 @@ settings. ### Recovery key The recovery key can be saved by the user directly, stored encrypted on the -server (as proposed in -[MSC1687](https://github.com/matrix-org/matrix-doc/issues/1687)), or both. If +server (using the method proposed in +[MSC1946](https://github.com/matrix-org/matrix-doc/issues/1946)), or both. If the key is saved directly by the user, then the code is constructed as follows: 1. The 256-bit curve25519 private key is prepended by the bytes `0x8B` and @@ -124,6 +124,29 @@ results in 0, and ensure that the total length of the decoded string is 35 bytes. Clients must then remove the first two bytes and the last byte, and use the resulting string as the private key to decrypt backups. +If MSC1946 is used to store the key on the server, it must be stored using the +`account_data` `type` `m.megolm_backup.v1`. + +As a special case, if the recovery key is the same as the curve25519 key used +for storing the key, then the contents of the `m.megolm_backup.v1` +`account_data` for that key will be the an object with a `passthrough` property +whose value is `true`. For example, if `m.megolm_backup.v1` is set to: + +```json +{ + "encrypted": { + "key_id": { + "passthrough": true + } + } +} +``` + +means that the recovery key for the backup is the same as the private key for +the key with ID `key_id`. (This is mostly intended to provide a migration path +for for backups that were created using an earlier draft that stored the +recovery information in the `auth_data`.) + ### API #### Backup versions From 7ed536751650b2762e370d1fcdbc7ad6aa0410d9 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Sat, 10 Aug 2019 14:14:30 -0700 Subject: [PATCH 128/390] clarifications, fix formatting --- .../1219-storing-megolm-keys-serverside.md | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 7e074a0d..dbcf3091 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -195,10 +195,10 @@ On success, returns a JSON object with keys: - `auth_data` (object): Required. Same as in the body parameters for `POST /room_keys/version`. - `version` (string): Required. The backup version. -- `hash` (string): Required. The hash value which is an opaque string - representing stored keys in the backup. Client can compare it with the `hash` - value they received in the response of their last key storage request. - If not equal, another matrix client pushed new keys to the backup. +- `hash` (string): Required. The hash value which is an opaque string + representing stored keys in the backup. Client can compare it with the `hash` + value they received in the response of their last key storage request. + If not equal, another matrix client pushed new keys to the backup. - `count` (number): Required. The number of keys stored in the backup. Error codes: @@ -212,7 +212,7 @@ Update information about the given version. Only `auth_data` can be updated. Body parameters: - `algorithm` (string): Required. Must be the same as in the body parameters for `GET - /room_keys/version`. + /room_keys/version`. - `auth_data` (object): Required. algorithm-dependent data. For `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the definition of this property. @@ -270,7 +270,7 @@ Body parameters: On success, returns a JSON object with keys: - `hash` (string): Required. The new hash value representing stored keys. See -`GET /room_keys/version/{version}` for more details. + `GET /room_keys/version/{version}` for more details. - `count` (number): Required. The new count of keys stored in the backup. Error codes: @@ -470,11 +470,18 @@ On success, returns the empty JSON object. ##### `auth_data` for backup versions The `auth_data` property for the backup versions endpoints for -`m.megolm_backup.v1.curve25519-aes-sha2` is a signedjson object with the +`m.megolm_backup.v1.curve25519-aes-sha2` is a [signed +json](https://matrix.org/docs/spec/appendices#signing-json) object with the following keys: - `public_key` (string): the curve25519 public key used to encrypt the backups -- `signatures` (object): signatures of the public key +- `signatures` (object): signatures of the `auth_data`. + +The `auth_data` should be signed by the user's [master cross-signing +key](https://github.com/matrix-org/matrix-doc/pull/1756), and may also be +signed by the user's device key. The allows clients to ensure that the public +key is valid, and prevents an attacker from being able to change the backup to +use a public key that have the private key for. ##### `session_data` for key backups @@ -516,10 +523,10 @@ key backup. This proposal does not attempt to protect against that. An attacker who gains access to a user's account can create a new backup version using a key that they control. For this reason, clients SHOULD confirm with users before sending keys to a new backup version or verify that it was -created by a trusted device by checking the signature. One way to confirm the -new backup version if the signature cannot be checked is by asking the user to -enter the recovery key, and confirming that the backup's public key matches -what is expected. +created by a trusted device by checking the signature. Alternatively, if the +signature cannot be verified, the backup can be validated by prompting the user +to enter the recovery key, and confirming that the backup's public key +corresponds to the recovery key. Other Issues ------------ From 353b6cd198aa4f1cb13fd7d3dcfceba323151f3f Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 12 Aug 2019 13:10:22 +0100 Subject: [PATCH 129/390] clarification --- proposals/2176-update-redaction-rules.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/2176-update-redaction-rules.md b/proposals/2176-update-redaction-rules.md index 75bfc971..574028f2 100644 --- a/proposals/2176-update-redaction-rules.md +++ b/proposals/2176-update-redaction-rules.md @@ -40,7 +40,8 @@ which are preserved: result before or after it is redacted (and therefore may or may not redact the original event). - * `m.room.power_levels` should allow: + * `m.room.power_levels` should allow (in addition to the keys already listed + in the spec): * the `invite` key. Rationale: this is required to authenticate `m.room.member` events with the `invite` membership. Currently, redacting From 6330fff5a4bdb1691898ccd7bfb435cbfda98b11 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 12 Aug 2019 18:13:58 +0100 Subject: [PATCH 130/390] Draft for IS URL in account data --- .../xxxx-identity-server-account-data.md | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 proposals/xxxx-identity-server-account-data.md diff --git a/proposals/xxxx-identity-server-account-data.md b/proposals/xxxx-identity-server-account-data.md new file mode 100644 index 00000000..da539f2a --- /dev/null +++ b/proposals/xxxx-identity-server-account-data.md @@ -0,0 +1,74 @@ +# Store Identity Server in Account Data + +The URL of the Identity Sever to use is currently specified at registration and +login time and then used for the lifetime of a login session. If users wish to +specify a custom one, they must do so each time they log in on every client. +Once they have chosen an Identity Server to advertise their 3PIDs on, it would +be normal that they would wish to continue using this Identity Server for all +Identity requests in their account accross all clients. This proposal aims to +make this easier. + +## Proposal + +The base URL of the Identity Server is to be stored in user account data. It +shall be stored in the same format as in a .well-known file under the key, +`m.identity_server` and shall comprise a single key, `base_url` which is the +base URL of the ID Server to use (that is, the part before `/_matrix`). + +Upon registration or login, a client MUST refrain from performing any requests +to the Identity Server until the account data has been fetched from the server. +Once it has the account data, it MUST check for the presence of the +`m.identity_server` key. If present, the `base_url` in this key MUST be used +as the Identity Server base URL for the duration of the login session. If this +key is not present, the client SHOULD populate it with the ID Server URL +that was or would have been used in the login/registration process. This may +be either from user input, a .well-known lookup, or a default in the client. + +Client MUST listen for changes in the `m.identity_server` account data value +and update the URL that they use for ID Server requests accordingly UNLESS +the login session and choice of ID Server base URL predates this change, in +which case they SHOULD continue to use the value they are currently using. + +Clients MAY offer a way for users to change the ID server being used. If they +do, the client MUST update the value of `m.identity_server` accordingly. + +The `m.identity_server` may be present with a value of `null`. In this case, +clients MUST treat this as no ID Server URL being set and not perform ID +Server requests, disabling any functionality that requires such requests. + +Conversely, if a user wishes to disable ID Server functionality, the client +shall action this by setting the `base_url` of the `m.identity_server` +account data entry to `null`. + +### Transition Period + +Clients currently logged in with a value configured for the ID Server base +URL SHOULD use the value from `m.identity_server` if it exists or is created, +but otherwise continue to use the URL they had previously. They MUST NOT +populate the `m.identity_server` with their current ID Server base URL. + +## Tradeoffs + +There are a number of ways to transition to this new scheme. Clients could +populate the account data with their current ID Server URL as soon as +possible, and immediately use any new value seen on account data. This +would a much faster migration but any users with clients using different +ID Servers would suddenly find all their clients using the ID Server of +whichever client they updated first. + +## Potential issues + +Users will no longer be able to have different clients configured with +different ID Servers. + +## Security considerations + +An attacker would be able to force all a user clients to use a given ID Server +if they gained control of any of a user's logins. + +## Conclusion + +This makes the ID server an account setting which means it persists between +logins. The intention would be to phase out clients ever asking for an ID +Server URL at registration or login: this will be much easier for users to +understand whilst still retaining the flexibilty for those who want it. From 2c8d112089e9ab1290118d72c40b93a28b29e014 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 13 Aug 2019 18:03:43 +0100 Subject: [PATCH 131/390] assign number --- ...ver-account-data.md => 2230-identity-server-account-data.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename proposals/{xxxx-identity-server-account-data.md => 2230-identity-server-account-data.md} (98%) diff --git a/proposals/xxxx-identity-server-account-data.md b/proposals/2230-identity-server-account-data.md similarity index 98% rename from proposals/xxxx-identity-server-account-data.md rename to proposals/2230-identity-server-account-data.md index da539f2a..b863cc59 100644 --- a/proposals/xxxx-identity-server-account-data.md +++ b/proposals/2230-identity-server-account-data.md @@ -1,4 +1,4 @@ -# Store Identity Server in Account Data +# MSC2230: Store Identity Server in Account Data The URL of the Identity Sever to use is currently specified at registration and login time and then used for the lifetime of a login session. If users wish to From 229cb67b01c92925dcc5a00c7660189b82fa318e Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 14 Aug 2019 09:51:27 +0100 Subject: [PATCH 132/390] Apply suggestions from code review Use fewer formal MUST etc in proposal Co-Authored-By: Travis Ralston --- proposals/2230-identity-server-account-data.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/proposals/2230-identity-server-account-data.md b/proposals/2230-identity-server-account-data.md index b863cc59..3a81fa8e 100644 --- a/proposals/2230-identity-server-account-data.md +++ b/proposals/2230-identity-server-account-data.md @@ -15,21 +15,21 @@ shall be stored in the same format as in a .well-known file under the key, `m.identity_server` and shall comprise a single key, `base_url` which is the base URL of the ID Server to use (that is, the part before `/_matrix`). -Upon registration or login, a client MUST refrain from performing any requests +Upon registration or login, a client SHOULD refrain from performing any requests to the Identity Server until the account data has been fetched from the server. -Once it has the account data, it MUST check for the presence of the -`m.identity_server` key. If present, the `base_url` in this key MUST be used +Once it has the account data, it SHOULD check for the presence of the +`m.identity_server` key. If present, the `base_url` in this key SHOULD be used as the Identity Server base URL for the duration of the login session. If this key is not present, the client SHOULD populate it with the ID Server URL -that was or would have been used in the login/registration process. This may +that was or would have been used in the login/registration process. This could be either from user input, a .well-known lookup, or a default in the client. -Client MUST listen for changes in the `m.identity_server` account data value -and update the URL that they use for ID Server requests accordingly UNLESS +Client SHOULD listen for changes in the `m.identity_server` account data value +and update the URL that they use for ID Server requests accordingly unless the login session and choice of ID Server base URL predates this change, in which case they SHOULD continue to use the value they are currently using. -Clients MAY offer a way for users to change the ID server being used. If they +Clients can offer a way for users to change the ID server being used. If they do, the client MUST update the value of `m.identity_server` accordingly. The `m.identity_server` may be present with a value of `null`. In this case, From b9b984ae60172892c13efc5702d68c438ceac0e5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 14 Aug 2019 09:52:13 +0100 Subject: [PATCH 133/390] clarify --- proposals/2230-identity-server-account-data.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/2230-identity-server-account-data.md b/proposals/2230-identity-server-account-data.md index 3a81fa8e..d26de3e2 100644 --- a/proposals/2230-identity-server-account-data.md +++ b/proposals/2230-identity-server-account-data.md @@ -13,7 +13,8 @@ make this easier. The base URL of the Identity Server is to be stored in user account data. It shall be stored in the same format as in a .well-known file under the key, `m.identity_server` and shall comprise a single key, `base_url` which is the -base URL of the ID Server to use (that is, the part before `/_matrix`). +base URL of the ID Server to use (that is, the part before `/_matrix`, including +`https://`). Upon registration or login, a client SHOULD refrain from performing any requests to the Identity Server until the account data has been fetched from the server. From 475c64de8c2827b772372ba0b6c53051b9d94e27 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 16 Aug 2019 19:52:35 -0600 Subject: [PATCH 134/390] Disclose origin story --- proposals/1961-integrations-auth.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proposals/1961-integrations-auth.md b/proposals/1961-integrations-auth.md index 7f735607..1f868e4c 100644 --- a/proposals/1961-integrations-auth.md +++ b/proposals/1961-integrations-auth.md @@ -3,6 +3,9 @@ A set of common APIs needs to be defined for clients to be able to interact with an integration manager. This proposal covers the authentication portion of that API. +**Note**: this proposal is part of a larger "Integrations API" which has not yet been defined. +See [MSC1956](https://github.com/matrix-org/matrix-doc/pull/1956) for details. + ## Proposal From 22c96926848fae3089673c761c0284637bc763d8 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 16 Aug 2019 19:53:28 -0600 Subject: [PATCH 135/390] Disclose origin story better --- proposals/1957-integrations-discovery.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/1957-integrations-discovery.md b/proposals/1957-integrations-discovery.md index cef17098..feac7451 100644 --- a/proposals/1957-integrations-discovery.md +++ b/proposals/1957-integrations-discovery.md @@ -1,6 +1,7 @@ # MSC1957: Integration manager discovery -*Note*: This is a required component of [MSC1956 - Integrations API](https://github.com/matrix-org/matrix-doc/pull/1956) +**Note**: this proposal is part of a larger "Integrations API" which has not yet been defined. +See [MSC1956](https://github.com/matrix-org/matrix-doc/pull/1956) for details. Users should have the freedom to choose which integration manager they want to use in their client, while also accepting suggestions from their homeserver and client. Clients need to know where to find the different From 865d3da0f866cb8afa126df1c56558e6dc353616 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 16 Aug 2019 19:59:32 -0600 Subject: [PATCH 136/390] General clarity improvements --- proposals/1957-integrations-discovery.md | 26 ++++++++++++++---------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/proposals/1957-integrations-discovery.md b/proposals/1957-integrations-discovery.md index feac7451..b305c18f 100644 --- a/proposals/1957-integrations-discovery.md +++ b/proposals/1957-integrations-discovery.md @@ -79,15 +79,16 @@ The user is able to have multiple integration managers through use of multiple w #### Display order of integration managers -Clients which have support for integration managers should display at least 1 manager, but may decide -to display multiple via something like tabs. Clients must prefer to display the user's configured +Clients which have support for integration managers should display at least 1 manager, but should +display multiple via something like tabs. Clients must prefer to display the user's configured integration managers over any defaults, and if only displaying one manager must pick the first -manager after sorting the `state_key`s in lexicographical order. Clients may additionally display -default managers if they so wish, and should preserve the order defined in the various defaults. -If the user has no configured integration managers, the client must prefer to display one or more -of the managers suggested by the homeserver over the managers recommended by the client. +manager after sorting the `state_key`s of the applicable widgets in lexicographical order. Clients +can additionally display default managers if they so wish, and should preserve the order defined in +the various defaults. If the user has no configured integration managers, the client must prefer +to display one or more of the managers suggested by the homeserver over the managers recommended +by the client. -The client may optionally support a way to entirely disable integration manager support, even if the +The client can optionally support a way to entirely disable integration manager support, even if the user and homeserver have managers defined. The rationale for having the client prefer to use the user's integration managers first is so that @@ -157,7 +158,10 @@ Some things which may be desirable in the future are: ## Security considerations -When displaying integration managers, clients should not trust that the input is sanitary. Integration -managers may only be at HTTP(S) endpoints and may still have malicious intent. Ensure any sandboxing -on the manager is appropriate such that it can communicate with the client, but cannot perform unauthorized -actions. +When displaying integration managers, clients should not trust that the input is sanitary. Per the +proposal above, an intergration manager is only permitted to be served from HTTP(S) URIs. A given +integration manager can still have malicious intent however, and clients should ensure any sandboxing +on the manager is appropriate such that it can communicate with the client, but cannot perform +unauthorized actions. Other URI schemes are just as dangerous and could potentially be allowed by +this proposal - use cases are less defined and desirable for schemes like `file://` and are excluded +by this proposal. They can be added in a future proposal if a use case arises. From e4bdc283fd6cdeb2bf90b5a79a180fb19a8e9c63 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 19 Aug 2019 11:45:46 +0100 Subject: [PATCH 137/390] Apply suggestions from code review Typos / spelling Co-Authored-By: Hubert Chathi --- proposals/2140-terms-of-service-2.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 557376cc..78a682d7 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -7,7 +7,7 @@ corresponding method that can be used with Identity Servers and Integration Managers. Requirements for this proposal are: - * ISs and IMs should be able to give multiple documents a user must agree to + * ISes and IMs should be able to give multiple documents a user must agree to abide by * Each document shoud be versioned * ISes and IMs must, for each request that they handle, know that the user @@ -15,7 +15,7 @@ Requirements for this proposal are: absolute proof (we will always have to trust that the client actually showed the document to the user) but it must be reasonably demonstrable that the user has given informed consent for the client to use that service. - * ISs and IMs must be able to prevent users from using the service if they + * ISes and IMs must be able to prevent users from using the service if they have not provided agreement. * A user should only have to agree to each version of each document once for their Matrix ID, ie. having agreed to a set of terms in one client, they @@ -265,7 +265,7 @@ in the current login sessions is: A client uses this client/server API endpoint to request that the Homeserver removes the given 3PID from the given Identity Server, or all Identity Servers. -Takes parameters the same parameters as +Takes the same parameters as `POST /_matrix/client/r0/account/3pid/delete`, ie. `id_server`, `medium`, `address` and the newly added `is_token`. From 12377fbf50515989004bc133071ca56c34ae300f Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 19 Aug 2019 11:53:41 +0100 Subject: [PATCH 138/390] /account/logout not /logout Co-Authored-By: Hubert Chathi --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 78a682d7..c19a1ea9 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -71,7 +71,7 @@ of: * `/_matrix/identity/v2/pubkey/*` * The new `$prefix/account/register` endpoint * The new `GET /_matrix/identity/v2/terms` - * `$prefix/logout` + * `$prefix/account/logout` ...may return an error with `M_UNAUTHORIZED` errcode with HTTP status code 401. This indicates that the user must authenticate with OpenID and supply a valid From 6d0067320c5703d3408c19952d68fb5b41c588f0 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 19 Aug 2019 13:31:20 +0100 Subject: [PATCH 139/390] clarify error proxying --- proposals/2140-terms-of-service-2.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index c19a1ea9..ed8e740e 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -94,7 +94,8 @@ Homeserver to make calls to the IS on its behalf, it must also supply its access token for the Identity Server alongside in the `is_token` key of the same JSON object. That is, in the main request object for a `requestToken` request and in the `threepidCreds` object when supplying 3PID credentials (eg. -in the `m.email.identity` UI auth stage). Exceptions to this are any requests +in the `m.email.identity` UI auth stage). The server must also relay +`M_TERMS_NOT_SIGNED` errors back to the client. Exceptions to this are any requests where the only IS operation the Homeserver may perform is unbinding, ie. `/_matrix/client/r0/account/deactivate` and `/_matrix/client/r0/account/3pid/delete`, in which case the unbind will be From 9e073e9647eef1bfff0dad0a7f0fc46013b66ddc Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 19 Aug 2019 10:42:30 -0600 Subject: [PATCH 140/390] Speeeeeeling Co-Authored-By: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> --- proposals/1957-integrations-discovery.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/1957-integrations-discovery.md b/proposals/1957-integrations-discovery.md index b305c18f..f6ac5aa4 100644 --- a/proposals/1957-integrations-discovery.md +++ b/proposals/1957-integrations-discovery.md @@ -100,7 +100,7 @@ user so they don't get left out. #### Displaying integration managers Clients simply open the `ui_url` (or equivalent) in an `iframe` or similar. In the current ecosystem, -integration managers would receive a `scalar_token` to idenitify the user - this is no longer the case +integration managers would receive a `scalar_token` to identify the user - this is no longer the case and instead integration managers must seek other avenues for determining the user ID. Other proposals cover how to do this in the context of the integrations API. @@ -159,7 +159,7 @@ Some things which may be desirable in the future are: ## Security considerations When displaying integration managers, clients should not trust that the input is sanitary. Per the -proposal above, an intergration manager is only permitted to be served from HTTP(S) URIs. A given +proposal above, an integration manager is only permitted to be served from HTTP(S) URIs. A given integration manager can still have malicious intent however, and clients should ensure any sandboxing on the manager is appropriate such that it can communicate with the client, but cannot perform unauthorized actions. Other URI schemes are just as dangerous and could potentially be allowed by From 9b2ca3cdfe99414e4cf2047c13a20991e32d6aae Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 20 Aug 2019 16:19:15 +0100 Subject: [PATCH 141/390] typoes / clarifications Co-Authored-By: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- proposals/2230-identity-server-account-data.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/2230-identity-server-account-data.md b/proposals/2230-identity-server-account-data.md index d26de3e2..98fcfbf1 100644 --- a/proposals/2230-identity-server-account-data.md +++ b/proposals/2230-identity-server-account-data.md @@ -1,6 +1,6 @@ # MSC2230: Store Identity Server in Account Data -The URL of the Identity Sever to use is currently specified at registration and +The URL of the Identity Server to use is currently specified at registration and login time and then used for the lifetime of a login session. If users wish to specify a custom one, they must do so each time they log in on every client. Once they have chosen an Identity Server to advertise their 3PIDs on, it would @@ -11,7 +11,7 @@ make this easier. ## Proposal The base URL of the Identity Server is to be stored in user account data. It -shall be stored in the same format as in a .well-known file under the key, +shall be stored in the same format as in a .well-known file under the event type `m.identity_server` and shall comprise a single key, `base_url` which is the base URL of the ID Server to use (that is, the part before `/_matrix`, including `https://`). @@ -33,7 +33,7 @@ which case they SHOULD continue to use the value they are currently using. Clients can offer a way for users to change the ID server being used. If they do, the client MUST update the value of `m.identity_server` accordingly. -The `m.identity_server` may be present with a value of `null`. In this case, +The `m.identity_server` may be present with a `base_url` of `null`. In this case, clients MUST treat this as no ID Server URL being set and not perform ID Server requests, disabling any functionality that requires such requests. From 788796e1c6b1f8b57a10530d633a50004f6b27d3 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 20 Aug 2019 09:20:07 -0600 Subject: [PATCH 142/390] Multiple clarifications --- proposals/1957-integrations-discovery.md | 35 ++++++++++++++++-------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/proposals/1957-integrations-discovery.md b/proposals/1957-integrations-discovery.md index f6ac5aa4..201f28c8 100644 --- a/proposals/1957-integrations-discovery.md +++ b/proposals/1957-integrations-discovery.md @@ -45,10 +45,10 @@ As shown, the homeserver is able to suggest multiple integration managers throug must have an `api_url` which must be an `http` or `https` URL. The `ui_url` is optional and if not provided is the same as the `api_url`. Like the `api_url`, the `ui_url` must be `http` or `https` if supplied. -The `ui_url` is ultimately treated the same as a widget, except that the custom `data` is not present and -must not be templated here. Variables like `$matrix_display_name` are able to function, however. Integration -managers should never use the `$matrix_user_id` as authoritative and instead seek other ways to determine the -user ID. This is covered by other proposals. +The `ui_url` is ultimately treated the same as a widget, except that the `data` object from the widget is not +present and must not be templated here. Variables like `$matrix_display_name` are able to function, however. +Integration managers should never use the `$matrix_user_id` as authoritative and instead seek other ways to +determine the user ID. This is covered by other proposals. The `api_url` is the URL clients will use when *not* embedding the integration manager, and instead showing its own purpose-built interface. @@ -56,7 +56,8 @@ its own purpose-built interface. Clients should query the `.well-known` information for the homeserver periodically to update the integration manager settings for that homeserver. The client is not expected to validate or use any other information contained in the response. Current recommendations are to query the configuration when the client starts up -and every 8 hours after that. +and every 8 hours after that. Clients can additionally refresh the configuration whenever they feel is +necessary (such as every time the user opens the integration manager). #### User-configured integration managers @@ -111,10 +112,15 @@ as an account widget rather than a room widget. #### Discovering a manager by only the domain name Clients may wish to ask users for a single canonical domain name so they can find the manager to add -to the user's account transparently. Similar to the .well-known discovery done by servers (and clients -during login), clients which have an integrations domain (eg: "example.org") make a regular HTTPS -request to `https://example.org/.well-known/matrix/integrations` which returns an object which looks -like the following: +to the user's account transparently. This differs from the .well-known discovery which allows homeservers +to recommend their own integration manager: the homeserver is not recommending a default here. The +user has instead opted to pick an integration manager (identified only by domain name) and the client +is expected to resolve that to a set of URLs it can use for the manager. + +Similar to the .well-known discovery done by servers (and clients during login), clients which have an +integrations domain (eg: "example.org") make a regular HTTPS request to +`https://example.org/.well-known/matrix/integrations` which returns an object which looks like the +following: ```json { "m.integrations_widget": { @@ -132,6 +138,13 @@ property, the client should assume there is no integrations manager running on t an integration manager, described above. The client should wrap the object verbatim into the appropriate account data location. +Because the .well-known file would be accessed by web browsers, among other platforms, the server +should be using appropriate CORS headers for the request. The recommended headers are the same as those +which are already recommended for homeserver discovery in the Client-Server API. + +*Note*: this could reuse the client-server mechanic for discovery and just omit the homeserver information +however that conflates many concerns together on the one endpoint. A new endpoint is instead proposed +to keep the concerns isolated. ## Tradeoffs @@ -144,8 +157,8 @@ support this use case, even if it is moderately rare. We could also define the integration managers in a custom account data event rather than defining them as a widget. Doing so just adds clutter to the account data and risks duplicating code in clients as -using widgets gets us URL templating for free (see the section later on in this proposal about account -widgets for more information). +using widgets gets us URL templating for free (see the section earlier on in this proposal about account +widgets for more information: "User-configured integration managers"). ## Future extensions From 1f8cfd57298f68837a315008f8e280eac23e75b4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 21 Aug 2019 14:19:20 +0100 Subject: [PATCH 143/390] Update migration mechanism --- proposals/2230-identity-server-account-data.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/proposals/2230-identity-server-account-data.md b/proposals/2230-identity-server-account-data.md index 98fcfbf1..19fb3062 100644 --- a/proposals/2230-identity-server-account-data.md +++ b/proposals/2230-identity-server-account-data.md @@ -21,14 +21,11 @@ to the Identity Server until the account data has been fetched from the server. Once it has the account data, it SHOULD check for the presence of the `m.identity_server` key. If present, the `base_url` in this key SHOULD be used as the Identity Server base URL for the duration of the login session. If this -key is not present, the client SHOULD populate it with the ID Server URL -that was or would have been used in the login/registration process. This could -be either from user input, a .well-known lookup, or a default in the client. +key is not present, the client SHOULD use whatever value it would have used prior +to this MSC. It should not update the account data in this situation. Client SHOULD listen for changes in the `m.identity_server` account data value -and update the URL that they use for ID Server requests accordingly unless -the login session and choice of ID Server base URL predates this change, in -which case they SHOULD continue to use the value they are currently using. +and update the URL that they use for ID Server requests accordingly. Clients can offer a way for users to change the ID server being used. If they do, the client MUST update the value of `m.identity_server` accordingly. @@ -43,10 +40,9 @@ account data entry to `null`. ### Transition Period -Clients currently logged in with a value configured for the ID Server base -URL SHOULD use the value from `m.identity_server` if it exists or is created, -but otherwise continue to use the URL they had previously. They MUST NOT -populate the `m.identity_server` with their current ID Server base URL. +Clients will continue to use whatever IS URLs they currently use until the +user sets one explicitly, at which point it will be written to account data +and all clients will start using this value. ## Tradeoffs From 4073d940da7d0a3ed25dd30f820dd83692389a47 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 21 Aug 2019 15:11:10 +0100 Subject: [PATCH 144/390] Typo Co-Authored-By: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index ed8e740e..3e2456bd 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -148,7 +148,7 @@ to use the service. Its response is similar to the structure used in the "version": "1.2", "en": { "name": "Privacy Policy", - "url": "https://example.org/somewhere/privaacy-1.2-en.html" + "url": "https://example.org/somewhere/privacy-1.2-en.html" }, "fr": { "name": "Politique de confidentialité", From 69315417b5c576e4e7966fda4f40503f2c2d3ad4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 21 Aug 2019 15:12:25 +0100 Subject: [PATCH 145/390] Typo Co-Authored-By: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 3e2456bd..d256fa88 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -248,7 +248,7 @@ The `/_matrix/identity/v2/3pid/unbind` endpoint must not return either of these errors if the request has a valid signature from a Homeserver, and is being authenticated as such. In summary, the process for using a service that has not previously been used -in the current login sessions is: +in the current login session is: * `GET $prefix/terms` * Compare result with `m.accepted_terms` account data, get set of documents From 8bd9d7caeb2b5185878482eda0c81f50bc873468 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 21 Aug 2019 15:12:45 +0100 Subject: [PATCH 146/390] Add full stop Co-Authored-By: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> --- proposals/2140-terms-of-service-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index d256fa88..49042aaf 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -255,7 +255,7 @@ in the current login session is: pending agreement. * If non-empty, show this set of documents to the user and wait for the user to indicate their agreement. - * Add the newly agreed documents to `m.accepted_terms` + * Add the newly agreed documents to `m.accepted_terms`. * On success, or if there were no documents pending agreement, get an OpenID token from the Homeserver and submit this token to the `register` endpoint. Store the resulting access token. From 4ea8f645d6a6f134a137f86e81c4599e1cd057f7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 21 Aug 2019 18:13:50 +0100 Subject: [PATCH 147/390] is_token -> id_access_token and add invite to proxy list --- proposals/2140-terms-of-service-2.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 49042aaf..9f96a00b 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -91,13 +91,14 @@ API, as specified in [MSC1961](https://github.com/matrix-org/matrix-doc/issues/1 When clients supply an identity server to the Homeserver in order for the Homeserver to make calls to the IS on its behalf, it must also supply its -access token for the Identity Server alongside in the `is_token` key of the -same JSON object. That is, in the main request object for a `requestToken` -request and in the `threepidCreds` object when supplying 3PID credentials (eg. -in the `m.email.identity` UI auth stage). The server must also relay -`M_TERMS_NOT_SIGNED` errors back to the client. Exceptions to this are any requests -where the only IS operation the Homeserver may perform is unbinding, ie. -`/_matrix/client/r0/account/deactivate` and +access token for the Identity Server alongside in the `id_access_token` key of +the same JSON object. That is, in the main request object for `requestToken` +and `/_matrix/client/r0/rooms/{roomId}/invite` requests and in the +`threepidCreds` object when supplying 3PID credentials (eg. in the +`m.email.identity` UI auth stage). The server must also relay +`M_TERMS_NOT_SIGNED` errors back to the client. Exceptions to this are any +requests where the only IS operation the Homeserver may perform is unbinding, +ie. `/_matrix/client/r0/account/deactivate` and `/_matrix/client/r0/account/3pid/delete`, in which case the unbind will be authenticated by a signed request from the Homeserver. From 8b85fda52cc27db4c424d1552a1c3a84fd176e10 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 21 Aug 2019 17:47:55 -0600 Subject: [PATCH 148/390] Add a link to the widget MSC to try and stem questions --- proposals/1957-integrations-discovery.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proposals/1957-integrations-discovery.md b/proposals/1957-integrations-discovery.md index 201f28c8..bb5ada55 100644 --- a/proposals/1957-integrations-discovery.md +++ b/proposals/1957-integrations-discovery.md @@ -3,6 +3,9 @@ **Note**: this proposal is part of a larger "Integrations API" which has not yet been defined. See [MSC1956](https://github.com/matrix-org/matrix-doc/pull/1956) for details. +**Note**: this proposal makes use of the existing Widget API proposed by +[MSC1236](https://github.com/matrix-org/matrix-doc/issues/1236). + Users should have the freedom to choose which integration manager they want to use in their client, while also accepting suggestions from their homeserver and client. Clients need to know where to find the different integration managers and how to contact them. From b6f0e8e8ed663ad018baf233cada68782a426799 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 21 Aug 2019 17:50:18 -0600 Subject: [PATCH 149/390] Clarify that the query string is because they are widgets --- proposals/1957-integrations-discovery.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/proposals/1957-integrations-discovery.md b/proposals/1957-integrations-discovery.md index bb5ada55..85939726 100644 --- a/proposals/1957-integrations-discovery.md +++ b/proposals/1957-integrations-discovery.md @@ -81,6 +81,9 @@ widget rules apply here. The user is able to have multiple integration managers through use of multiple widgets. +The query string shown in the example is to demonstrate that integration managers are widgets and can +make use of the template options provided to widgets. + #### Display order of integration managers Clients which have support for integration managers should display at least 1 manager, but should @@ -149,6 +152,9 @@ which are already recommended for homeserver discovery in the Client-Server API. however that conflates many concerns together on the one endpoint. A new endpoint is instead proposed to keep the concerns isolated. +The query string shown in the example is to demonstrate that integration managers are widgets and can +make use of the template options provided to widgets. + ## Tradeoffs We could limit the user (and by extension, the homeserver and client) to exactly 1 integration manager From ec38013daaa0887f12664523730bfb4036d55291 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 23 Aug 2019 23:07:57 +0300 Subject: [PATCH 150/390] Proposal to allow multiple targets for one redaction event Signed-off-by: Tulir Asokan --- proposals/2244-mass-redactions.md | 66 +++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 proposals/2244-mass-redactions.md diff --git a/proposals/2244-mass-redactions.md b/proposals/2244-mass-redactions.md new file mode 100644 index 00000000..296ffeee --- /dev/null +++ b/proposals/2244-mass-redactions.md @@ -0,0 +1,66 @@ +# Mass redactions +Matrix, like any platform with public chat rooms, has spammers. Currently, +redacting spam essentially requires spamming redaction events in a 1:1 ratio, +which is not optimal[1]. Most clients do not even have any mass +redaction tools, likely in part due to the lack of a mass redaction API. A mass +redaction API on the other hand has not been implemented as it would require +sending lots of events at once. However, this problem could be solved by +allowing a single redaction event to redact many events instead of sending many +redaction events. + +## Proposal +This proposal builds upon [MSC2174] and suggests making the `redacts` field +in the content of `m.room.redaction` events an array of event ID strings +instead of a single event ID string. + +It would be easiest to do this before MSC2174 is written into the spec, as then +only one migration would be needed: from an event-level redacts string to a +content-level redacts array. + +### Number of redactions +Room v4+ event IDs are 44 bytes long, which means the federation event size +limit would cap a single redaction event at a bit less than 1500 targets. +Redactions are not intrinsically heavy, so a separate limit should not be +necessary. + +### Auth rules +The redaction auth rules should change to iterate the array and check if the +sender has the privileges to redact each event. + +There are at least two potential ways to handle targets that are not found or +rejected: soft failing until all targets are found and handling each target +separately. + +#### Soft fail +Soft fail the event until all targets are found, then accept only if the sender +has the privileges to redact every listed event. This is how redactions +currently work. + +This has the downside of requiring servers to fetch all the target events (and +possibly forward them to clients) before being able to process and forward the +redaction event. + +#### Handle each target separately +Handle each target separately: if some targets are not found, remember the +redaction and check auth rules when the target is received. This option brings +some complexities, but might be more optimal in situations such as a spam +attack. + +When receiving a redaction event: +* Ignore illegal targets +* "Remember" targets that can't be found +* Send legal target event IDs to clients in the redaction event. + +When receiving an event that is "remembered" to be possibly redacted by an +earlier redaction, check if the redaction was legal, and if it was, do not +send the event to clients. + +## Tradeoffs + +## Potential issues + +## Security considerations + + +[1]: https://img.mau.lu/hEqqt.png +[MSC2174]: https://github.com/matrix-org/matrix-doc/pull/2174 From 65cd10249cb88a81c4caef4bb2164df183b32661 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 24 Aug 2019 21:56:48 +0900 Subject: [PATCH 151/390] Render enums inside additionalProps as one more table Closes #2242. --- api/client-server/capabilities.yaml | 1 + scripts/templating/matrix_templates/units.py | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/api/client-server/capabilities.yaml b/api/client-server/capabilities.yaml index a50908f7..0d6b0677 100644 --- a/api/client-server/capabilities.yaml +++ b/api/client-server/capabilities.yaml @@ -96,6 +96,7 @@ paths: example: "1" available: type: object + title: AvailableRoomVersions description: |- A detailed description of the room versions the server supports. additionalProperties: diff --git a/scripts/templating/matrix_templates/units.py b/scripts/templating/matrix_templates/units.py index 04e6f8a9..f0d21ed0 100644 --- a/scripts/templating/matrix_templates/units.py +++ b/scripts/templating/matrix_templates/units.py @@ -211,9 +211,17 @@ def get_json_schema_object_fields(obj, enforce_title=False): key_type = additionalProps.get("x-pattern", "string") res = process_data_type(additionalProps) + tables = res["tables"] + val_title = res["title"] + if res.get("enum_desc") and val_title != "enum": + # A map to enum needs another table with enum description + tables.append(TypeTable( + title=val_title, + rows=[TypeTableRow(key="(mapped value)", title="enum", desc=res["desc"])] + )) return { - "title": "{%s: %s}" % (key_type, res["title"]), - "tables": res["tables"], + "title": "{%s: %s}" % (key_type, val_title), + "tables": tables, } if not props: @@ -338,8 +346,8 @@ def process_data_type(prop, required=False, enforce_title=True): prop_title = prop_type if prop.get("enum"): + prop_title = prop.get("title", "enum") if len(prop["enum"]) > 1: - prop_title = "enum" enum_desc = ( "One of: %s" % json.dumps(prop["enum"]) ) From 31c132ec823f8a7f4420c375693716b0e8141996 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 25 Aug 2019 17:39:36 +0900 Subject: [PATCH 152/390] Add changelog --- changelogs/client_server/2245.clarification | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/2245.clarification diff --git a/changelogs/client_server/2245.clarification b/changelogs/client_server/2245.clarification new file mode 100644 index 00000000..67533d15 --- /dev/null +++ b/changelogs/client_server/2245.clarification @@ -0,0 +1 @@ +List available enum values for the room versions capability From 4e2fe124d21a8290b7b2fa01c0200cbc9514dc4f Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Sun, 25 Aug 2019 18:22:23 -0700 Subject: [PATCH 153/390] wording fixes/clarifications --- proposals/1946-secure_server-side_storage.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index 2f269f23..0e6fccb7 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -11,8 +11,8 @@ way of storing such data. Secrets are data that clients need to use and that are sent through or stored on the server, but should not be visible to server operators. Secrets are -plain strings -- if clients need to use more complicated data, it must be -encoded as a string. +plain strings -- if clients need to use more complicated data, they must be +encoded as a string, such as by encoding as JSON. ### Storage @@ -29,10 +29,11 @@ property, which is a human-readable name. The contents will be signed as signed JSON using the user's master cross-signing key. Other properties depend on the encryption algorithm, and are described below. -If a key has the `name` property set to `m.default`, then this key is marked as +If a key has the `name` property set to `m.default`, then this key is treated as the default key for the account. The default key is the one that all secrets -will be encrypted with, and the clients will try to use to decrypt data with, -unless the user specifies otherwise. Only one key can be marked as the default. +will be encrypted with, and that clients will try to use to decrypt data with, +unless the user specifies otherwise. Only one key can be marked as the default +at a time. Encrypted data can be stored using the `account_data` API. The `type` for the `account_data` is defined by the feature that uses the data. For example, @@ -85,7 +86,8 @@ The data is encrypted and MACed as follows: (The key HKDF, AES, and HMAC steps are the same as what are used for encryption in olm and megolm.) -For example, a key using this algorithm could look like: +For example, the `m.secret_storage.key.[key ID]` for a key using this algorithm +could look like: ```json { @@ -159,7 +161,8 @@ that it wishes to retrieve. A device that wishes to share the secret will reply with a `m.secret.send` event, encrypted using olm. When the original client obtains the secret, it sends a `m.secret.request` event with `action` set to `cancel_request` to all devices other than the one that it received the -secret from. +secret from. Clients should ignore `m.secret.send` events received from +devices that it did not send an `m.secret.request` event to. Clients SHOULD ensure that they only share secrets with other devices that are allowed to see them. For example, clients SHOULD only share secrets with devices @@ -213,7 +216,8 @@ to read the information if they manage to get hold of the decryption keys. In particular, if the key is based on a passphrase and the passphrase can be guessed, then the secrets could be compromised. In order to help protect the secrets, clients should provide feedback to the user when their chosen -passphrase is considered weak. +passphrase is considered weak, and may also wish to prevent the user from +reusing their login password. ## Conclusion From a96a2f3fcea9897817f44e4b5194d61e13becf9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 26 Aug 2019 12:11:11 +0200 Subject: [PATCH 154/390] Fix the action of a room key request cancellation. The spec states that the action of a room key request cancellation should be "cancel_request" but every known implementation uses "request_cancellation" instead. This patch fixes the spec to reflect the implementations. --- event-schemas/examples/m.room_key_request$cancel_request | 2 +- event-schemas/schema/m.room_key_request | 2 +- specification/modules/end_to_end_encryption.rst | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/event-schemas/examples/m.room_key_request$cancel_request b/event-schemas/examples/m.room_key_request$cancel_request index c6eb25de..afc1c350 100644 --- a/event-schemas/examples/m.room_key_request$cancel_request +++ b/event-schemas/examples/m.room_key_request$cancel_request @@ -1,6 +1,6 @@ { "content": { - "action": "cancel_request", + "action": "request_cancellation", "requesting_device_id": "RJYKSTBOIE", "request_id": "1495474790150.19" }, diff --git a/event-schemas/schema/m.room_key_request b/event-schemas/schema/m.room_key_request index 007d0086..c08ac0e3 100644 --- a/event-schemas/schema/m.room_key_request +++ b/event-schemas/schema/m.room_key_request @@ -38,7 +38,7 @@ properties: action: enum: - request - - cancel_request + - request_cancellation type: string requesting_device_id: description: ID of the device requesting the key. diff --git a/specification/modules/end_to_end_encryption.rst b/specification/modules/end_to_end_encryption.rst index 329c0170..7758e2c1 100644 --- a/specification/modules/end_to_end_encryption.rst +++ b/specification/modules/end_to_end_encryption.rst @@ -756,8 +756,8 @@ sending `m.room_key_request`_ to-device messages to other devices with device, it can forward the keys to the first device by sending an encrypted `m.forwarded_room_key`_ to-device message. The first device should then send an `m.room_key_request`_ to-device message with ``action`` set to -``cancel_request`` to the other devices that it had originally sent the key -request to; a device that receives a ``cancel_request`` should disregard any +``request_cancellation`` to the other devices that it had originally sent the key +request to; a device that receives a ``request_cancellation`` should disregard any previously-received ``request`` message with the same ``request_id`` and ``requesting_device_id``. From 35eb1993d91d27c8abe350ffc4f756a4b866b23d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 26 Aug 2019 17:40:32 +0200 Subject: [PATCH 155/390] Add a changelog fragment for the room key request action fix. --- changelogs/client_server/newsfragments/2247.clarification | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/2247.clarification diff --git a/changelogs/client_server/newsfragments/2247.clarification b/changelogs/client_server/newsfragments/2247.clarification new file mode 100644 index 00000000..43553399 --- /dev/null +++ b/changelogs/client_server/newsfragments/2247.clarification @@ -0,0 +1 @@ +Fix the ``m.room_key_request`` ``action`` value, setting it from ``cancel_request`` to ``request_cancellation``. From cafe49d36d6152789ac23202af914267a48ca011 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 27 Aug 2019 16:13:18 -0700 Subject: [PATCH 156/390] some clarifications --- proposals/1946-secure_server-side_storage.md | 77 +++++++++++++------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index 0e6fccb7..05c20086 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -2,10 +2,11 @@ Some features may require clients to store encrypted data on the server so that it can be shared securely between clients. Clients may also wish to securely -send such data directly to each other. For example, key backups (MSC-1219) -can store the decryption key for the backups on the server, or cross-signing -(MSC-1756) can store the signing keys. This proposal presents a standardized -way of storing such data. +send such data directly to each other. For example, key backups +([MSC1219](https://github.com/matrix-org/matrix-doc/issues/1219)) can store the +decryption key for the backups on the server, or cross-signing +([MSC1756](https://github.com/matrix-org/matrix-doc/pull/1756)) can store the +signing keys. This proposal presents a standardized way of storing such data. ## Proposal @@ -21,13 +22,29 @@ prevent homeserver administrators from being able to read it. A user can have multiple keys used for encrypting data. This allows the user to selectively decrypt data on clients. For example, the user could have one key that can decrypt everything, and another key that can only decrypt their user-signing -key for cross-signing. Each key has an ID, and a description of the key is -stored in the user's `account_data` using the `type` `m.secret_storage.key.[key -ID]`. The contents of the account data for the key will include an `algorithm` -property, which indicates the encryption algorithm used, as well as a `name` -property, which is a human-readable name. The contents will be signed as -signed JSON using the user's master cross-signing key. Other properties depend -on the encryption algorithm, and are described below. +key for cross-signing. + +Key descriptions and secret data are both stored in the user's `account_data`. + +Each key has an ID, and the description of the key is stored in the +`account_data` using the `type` `m.secret_storage.key.[key ID]`. The contents +of the account data for the key will include an `algorithm` property, which +indicates the encryption algorithm used, as well as a `name` property, which is +a human-readable name. The contents will be signed as signed JSON using the +user's master cross-signing key. Other properties depend on the encryption +algorithm, and are described below. + +Example: + +A key with ID `abcdefg` is stored in `m.secret_storage.key.abcdefg` + +```json +{ + "name": "Some key", + "algorihm": "m.secret_storage.v1.curve25519-aes-sha2", + // ... other properties according to algorithm +} +``` If a key has the `name` property set to `m.default`, then this key is treated as the default key for the account. The default key is the one that all secrets @@ -35,31 +52,39 @@ will be encrypted with, and that clients will try to use to decrypt data with, unless the user specifies otherwise. Only one key can be marked as the default at a time. -Encrypted data can be stored using the `account_data` API. The `type` for the -`account_data` is defined by the feature that uses the data. For example, -decryption keys for key backups could be stored under the type -`m.megolm_backup.v1.recovery_key`, or the self-signing key for cross-signing -could be stored under the type `m.cross_signing.self_signing`. +Encrypted data is stored in the `account_data` using the `type` defined by the +feature that uses the data. For example, decryption keys for key backups could +be stored under the type `m.megolm_backup.v1.recovery_key`, or the self-signing +key for cross-signing could be stored under the type +`m.cross_signing.self_signing`. + +The `account_data` will have an `encrypted` property that is a map from key ID +to an object. The algorithm from the `m.secret_storage.key.[key ID]` data for +the given key defines how the other properties are interpreted, though it's +expected that most encryption schemes would have `ciphertext` and `mac` +properties, where the `ciphertext` property is the unpadded base64-encoded +ciphertext, and the `mac` is used to ensure the integrity of the data. + +Example: -Data will be stored using the following format: +Some secret is encrypted using keys with ID `key_id_1` and `key_id_2`: ```json { "encrypted": { - "key_id": { + "key_id_1": { "ciphertext": "base64+encoded+encrypted+data", - "mac": "base64+encoded+mac" + "mac": "base64+encoded+mac", + // ... other properties according to algorithm property in + // m.secret_storage.key.key_id_1 + }, + "key_id_2": { + // ... } } } ``` -The `encrypted` property is map from key ID to an object. The algorithm for -the given key defines how the other properties are interpreted, though it's -expected that most encryption schemes would have `ciphertext` and `mac` -properties, where the `ciphertext` property is the unpadded base64-encoded -ciphertext, and the `mac` is used to ensure the integrity of the data. - #### Encryption algorithms ##### `m.secret_storage.v1.curve25519-aes-sha2` @@ -181,7 +206,7 @@ unencrypted to-device event. - `name`: (string) Required if `action` is `request`. The name of the secret that is being requested. -- `action`: (enum) Required. One of ["request", "cancel_request"]. +- `action`: (enum) Required. One of ["request", "request_cancellation"]. - `requesting_device_id`: (string) Required. ID of the device requesting the secret. - `request_id`: (string) Required. A random string uniquely identifying the From 9aade7291a79f90f4a4d2fad8d214a5350b57381 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 27 Aug 2019 16:21:00 -0700 Subject: [PATCH 157/390] make it agree with what we actually did with key requests --- proposals/1946-secure_server-side_storage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index 05c20086..0ccde311 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -185,7 +185,7 @@ set to `request` to other devices, and `name` set to the name of the secret that it wishes to retrieve. A device that wishes to share the secret will reply with a `m.secret.send` event, encrypted using olm. When the original client obtains the secret, it sends a `m.secret.request` event with `action` -set to `cancel_request` to all devices other than the one that it received the +set to `request_cancellation` to all devices other than the one that it received the secret from. Clients should ignore `m.secret.send` events received from devices that it did not send an `m.secret.request` event to. From e1b0042e7be20c43908c32845ab0ea513b70b115 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 27 Aug 2019 17:46:45 -0700 Subject: [PATCH 158/390] clarifications, minor fixes, formatting --- proposals/1756-cross-signing.md | 39 ++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index ec3edc33..34789fb8 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -27,8 +27,8 @@ Each user has three sets of key pairs: - a user-signing key pair that is used to sign other users' master keys. When one user (e.g. Alice) verifies another user's (Bob's) identity, Alice will -sign Bob's self-signing key with her user-signing key. (This will mean that -verification methods will need to be modified to pass along the self-signing +sign Bob's master key with her user-signing key. (This will mean that +verification methods will need to be modified to pass along the master identity key.) Alice's device will trust Bob's device if: - Alice's device is using a master key that has signed her user-signing key, @@ -66,14 +66,18 @@ keys, respectively. ### Signature distribution -Currently, users will only be allowed to see signatures made by their own -master, self-signing or user-signing keys, signatures of their own master key -made by their own devices, signatures made by other users' master or -self-signing keys about their own devices, or signatures made of other users' -master keys by their own devices. This is done in order to preserve the -privacy of social connections. Future proposals may define mechanisms for -distributing signatures to other users in order to allow for other web-of-trust -use cases. +Currently, users will only be allowed to see +* signatures made by their own master, self-signing or user-signing keys, +* signatures made by their own devices of their own master key, +* signatures made by other users' self-signing keys about the other users' own + devices, +* signatures made by other users' master keys about the other users' + self-signing key, or +* signatures made by other users' devices about the other users' master keys. + +This is done in order to preserve the privacy of social connections. Future +proposals may define mechanisms for distributing signatures to other users in +order to allow for other web-of-trust use cases. ### Migrating from device verifications @@ -134,8 +138,7 @@ Auth](https://matrix.org/docs/spec/client_server/r0.4.0.html#user-interactive-au } ``` -Cross-signing keys are JSON objects with the following -properties: +Cross-signing keys are JSON objects with the following properties: * `user_id` (string): The user who owns the key * `usage` ([string]): Allowed uses for the key. Must contain `"master"` for @@ -149,10 +152,10 @@ properties: key MAY be signed by a device. In order to ensure that there will be no collisions in the `signatures` -property, the server must respond with an error (FIXME: what error?) if any of +property, the server must respond with an `M_FORBIDDEN` error if any of the uploaded public keys match an existing device ID for the user. Similarly, if a user attempts to log in specifying a device ID matching one of the signing -keys, the server must respond with an error (FIXME: what error?). +keys, the server must respond with an `M_FORBIDDEN` error. If a self-signing or user-signing key is uploaded, it must be signed by the master key that is included in the request, or the current master key if no @@ -211,10 +214,10 @@ response: } ``` -Similarly, the federation endpoints `GET /user/keys/query` and -`POST /user/devices/{userId}` will include the master and self-signing keys. -(It will not include the user-signing key because it is not intended to be -visible to other users.) +Similarly, the federation endpoints `GET /user/keys/query` and `POST +/user/devices/{userId}` will include the master and self-signing keys. (It +will not include the user-signing key because it is not intended to be visible +to other users.) `POST /keys/query` From 03ae5614b0daf45e02f87b92f99753bfae85b3c8 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 27 Aug 2019 17:56:52 -0700 Subject: [PATCH 159/390] remove unnecessary space --- proposals/1756-cross-signing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 34789fb8..1dbef83c 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -58,7 +58,7 @@ the usability of signing users and devices when performing key verification. The private halves of a user's cross-signing keys be stored encrypted on the server so that they may be retrieved by new devices, or shared between devices -using [MSC 1946](https://github.com/matrix-org/matrix-doc/pull/1946). When +using [MSC1946](https://github.com/matrix-org/matrix-doc/pull/1946). When handled in this way, the keys must be base64-encoded, and use the names `m.cross_signing.master`, `m.cross_signing.self_signing`, and `m.cross_signing.user_signing` for the master, self-signing, and user-signing From de3802cd5c3d33c62bbbd50d50e82275011a7711 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 27 Aug 2019 19:24:13 -0600 Subject: [PATCH 160/390] List deprecated endpoints as deprecated Affects the title and the table of contents. We can't realistically alter just the table of contents, but the table of contents is generated from this header. Fixes https://github.com/matrix-org/matrix-doc/issues/1800 --- .../templating/matrix_templates/templates/http-api.tmpl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/templating/matrix_templates/templates/http-api.tmpl b/scripts/templating/matrix_templates/templates/http-api.tmpl index 74836045..d2ee3ff7 100644 --- a/scripts/templating/matrix_templates/templates/http-api.tmpl +++ b/scripts/templating/matrix_templates/templates/http-api.tmpl @@ -1,11 +1,15 @@ {% import 'tables.tmpl' as tables -%} -``{{endpoint.method}} {{endpoint.path}}`` -{{(5 + (endpoint.path | length) + (endpoint.method | length)) * title_kind}} {% if "deprecated" in endpoint and endpoint.deprecated -%} +Deprecated: ``{{endpoint.method}} {{endpoint.path}}`` +{{(17 + (endpoint.path | length) + (endpoint.method | length)) * title_kind}} + .. WARNING:: This API is deprecated and will be removed from a future release. +{% else %} +``{{endpoint.method}} {{endpoint.path}}`` +{{(5 + (endpoint.path | length) + (endpoint.method | length)) * title_kind}} {% endif -%} {{endpoint.desc}} From b8a3f970eebfe6d321587adaced8c75656986e68 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 28 Aug 2019 13:39:42 -0600 Subject: [PATCH 161/390] Add security definition for access token --- api/identity/definitions/security.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 api/identity/definitions/security.yaml diff --git a/api/identity/definitions/security.yaml b/api/identity/definitions/security.yaml new file mode 100644 index 00000000..ef49ff5c --- /dev/null +++ b/api/identity/definitions/security.yaml @@ -0,0 +1,18 @@ +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +accessToken: + type: apiKey + description: The access_token returned by a call to ``/register``. + name: access_token + in: query From 984e0af7b221b480f3bc1bd0c9795a1a6411d515 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 24 Aug 2019 11:58:33 +0300 Subject: [PATCH 162/390] Re-word auth rule section on handling each target separately Co-authored-by: Jason Volk Signed-off-by: Tulir Asokan Signed-off-by: Jason Volk --- proposals/2244-mass-redactions.md | 43 ++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/proposals/2244-mass-redactions.md b/proposals/2244-mass-redactions.md index 296ffeee..68802221 100644 --- a/proposals/2244-mass-redactions.md +++ b/proposals/2244-mass-redactions.md @@ -23,17 +23,21 @@ limit would cap a single redaction event at a bit less than 1500 targets. Redactions are not intrinsically heavy, so a separate limit should not be necessary. -### Auth rules +### Client behavior +Clients shall apply existing `m.room.redaction` target behavior over an array +of event ID strings. + +### Server behavior The redaction auth rules should change to iterate the array and check if the sender has the privileges to redact each event. There are at least two potential ways to handle targets that are not found or -rejected: soft failing until all targets are found and handling each target +rejected: soft failing until all targets are found or handling each target separately. #### Soft fail -Soft fail the event until all targets are found, then accept only if the sender -has the privileges to redact every listed event. This is how redactions +[Soft fail] the event until all targets are found, then accept only if the +sender has the privileges to redact every listed event. This is how redactions currently work. This has the downside of requiring servers to fetch all the target events (and @@ -41,19 +45,27 @@ possibly forward them to clients) before being able to process and forward the redaction event. #### Handle each target separately -Handle each target separately: if some targets are not found, remember the -redaction and check auth rules when the target is received. This option brings -some complexities, but might be more optimal in situations such as a spam -attack. +The target events of an `m.room.redaction` shall no longer be considered when +deciding the authenticity of an `m.room.redaction` event. Any other existing +rules remain unchanged. + +When a server accepts an `m.room.redaction` using the modified auth rules, it +evaluates targets individually for authenticity under the existing auth rules. +Servers MUST NOT include failing and unknown entries to clients. + +> Servers do not know whether redaction targets are authorized at the time they + receive the `m.room.redaction` unless they are in possession of the target + event. Implementations retain entries in the original list which were not + shared with clients to later evaluate the target's redaction status. -When receiving a redaction event: -* Ignore illegal targets -* "Remember" targets that can't be found -* Send legal target event IDs to clients in the redaction event. +When the implementation receives a belated target from an earlier +`m.room.redaction`, it evaluates at that point whether the redaction is +authorized. -When receiving an event that is "remembered" to be possibly redacted by an -earlier redaction, check if the redaction was legal, and if it was, do not -send the event to clients. +> Servers should not send belated target events to clients if their redaction + was found to be authentic, as clients were not made aware of the redaction. + That fact is also used to simply ignore unauthorized targets and send the + events to clients normally. ## Tradeoffs @@ -64,3 +76,4 @@ send the event to clients. [1]: https://img.mau.lu/hEqqt.png [MSC2174]: https://github.com/matrix-org/matrix-doc/pull/2174 +[Soft fail]: https://matrix.org/docs/spec/server_server/r0.1.3#soft-failure From 5b53b3d0b82dcc6c7b140f4e091892cb70b3f4de Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 28 Aug 2019 13:47:06 -0600 Subject: [PATCH 163/390] Clone v1 APIs verbatim --- api/identity/v2_associations.yaml | 300 ++++++++++++++++++++++++ api/identity/v2_email_associations.yaml | 175 ++++++++++++++ api/identity/v2_invitation_signing.yaml | 97 ++++++++ api/identity/v2_phone_associations.yaml | 177 ++++++++++++++ api/identity/v2_ping.yaml | 46 ++++ api/identity/v2_pubkey.yaml | 127 ++++++++++ api/identity/v2_store_invite.yaml | 161 +++++++++++++ 7 files changed, 1083 insertions(+) create mode 100644 api/identity/v2_associations.yaml create mode 100644 api/identity/v2_email_associations.yaml create mode 100644 api/identity/v2_invitation_signing.yaml create mode 100644 api/identity/v2_phone_associations.yaml create mode 100644 api/identity/v2_ping.yaml create mode 100644 api/identity/v2_pubkey.yaml create mode 100644 api/identity/v2_store_invite.yaml diff --git a/api/identity/v2_associations.yaml b/api/identity/v2_associations.yaml new file mode 100644 index 00000000..247e1b4c --- /dev/null +++ b/api/identity/v2_associations.yaml @@ -0,0 +1,300 @@ +# Copyright 2018 New Vector Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Establishing Associations API" + version: "1.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity/api/v1 +consumes: + - application/json +produces: + - application/json +paths: + "/3pid/getValidated3pid": + get: + summary: Check whether ownership of a 3pid was validated. + description: |- + Determines if a given 3pid has been validated by a user. + operationId: getValidated3pid + parameters: + - in: query + type: string + name: sid + description: The Session ID generated by the ``requestToken`` call. + required: true + x-example: 1234 + - in: query + type: string + name: client_secret + description: The client secret passed to the ``requestToken`` call. + required: true + x-example: monkeys_are_GREAT + responses: + 200: + description: Validation information for the session. + examples: + application/json: { + "medium": "email", + "validated_at": 1457622739026, + "address": "louise@bobs.burgers" + } + schema: + type: object + properties: + medium: + type: string + description: The medium type of the 3pid. + address: + type: string + description: The address of the 3pid being looked up. + validated_at: + type: integer + description: |- + Timestamp, in milliseconds, indicating the time that the 3pid + was validated. + required: ['medium', 'address', 'validated_at'] + 400: + description: |- + The session has not been validated. + + If the session has not been validated, then ``errcode`` will be + ``M_SESSION_NOT_VALIDATED``. If the session has timed out, then + ``errcode`` will be ``M_SESSION_EXPIRED``. + examples: + application/json: { + "errcode": "M_SESSION_NOT_VALIDATED", + "error": "This validation session has not yet been completed" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + 404: + description: The Session ID or client secret were not found. + examples: + application/json: { + "errcode": "M_NO_VALID_SESSION", + "error": "No valid session was found matching that sid and client secret" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + "/3pid/bind": + post: + summary: Publish an association between a session and a Matrix user ID. + description: |- + Publish an association between a session and a Matrix user ID. + + Future calls to ``/lookup`` for any of the session\'s 3pids will return + this association. + + Note: for backwards compatibility with previous drafts of this + specification, the parameters may also be specified as + ``application/x-form-www-urlencoded`` data. However, this usage is + deprecated. + operationId: bind + parameters: + - in: body + name: body + schema: + type: object + example: { + "sid": "1234", + "client_secret": "monkeys_are_GREAT", + "mxid": "@ears:matrix.org" + } + properties: + sid: + type: string + description: The Session ID generated by the ``requestToken`` call. + client_secret: + type: string + description: The client secret passed to the ``requestToken`` call. + mxid: + type: string + description: The Matrix user ID to associate with the 3pids. + required: ["sid", "client_secret", "mxid"] + responses: + 200: + description: The association was published. + examples: + application/json: { + "address": "louise@bobs.burgers", + "medium": "email", + "mxid": "@ears:matrix.org", + "not_before": 1428825849161, + "not_after": 4582425849161, + "ts": 1428825849161, + "signatures": { + "matrix.org": { + "ed25519:0": "ENiU2YORYUJgE6WBMitU0mppbQjidDLanAusj8XS2nVRHPu+0t42OKA/r6zV6i2MzUbNQ3c3MiLScJuSsOiVDQ" + } + } + } + schema: + type: object + properties: + address: + type: string + description: The 3pid address of the user being looked up. + medium: + type: string + description: The medium type of the 3pid. + mxid: + type: string + description: The Matrix user ID associated with the 3pid. + not_before: + type: integer + description: A unix timestamp before which the association is not known to be valid. + not_after: + type: integer + description: A unix timestamp after which the association is not known to be valid. + ts: + type: integer + description: The unix timestamp at which the association was verified. + signatures: + type: object + description: |- + The signatures of the verifying identity servers which show that the + association should be trusted, if you trust the verifying identity + services. + $ref: "../../schemas/server-signatures.yaml" + required: + - address + - medium + - mxid + - not_before + - not_after + - ts + - signatures + 400: + description: |- + The association was not published. + + If the session has not been validated, then ``errcode`` will be + ``M_SESSION_NOT_VALIDATED``. If the session has timed out, then + ``errcode`` will be ``M_SESSION_EXPIRED``. + examples: + application/json: { + "errcode": "M_SESSION_NOT_VALIDATED", + "error": "This validation session has not yet been completed" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + 404: + description: The Session ID or client secret were not found + examples: + application/json: { + "errcode": "M_NO_VALID_SESSION", + "error": "No valid session was found matching that sid and client secret" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + "/3pid/unbind": + post: + summary: Remove an association between a session and a Matrix user ID. + description: |- + Remove an association between a session and a Matrix user ID. + + Future calls to ``/lookup`` for any of the session's 3pids will not + return the removed association. + + The identity server should authenticate the request in one of two + ways: + + 1. The request is signed by the homeserver which controls the ``user_id``. + 2. The request includes the ``sid`` and ``client_secret`` parameters, + as per ``/3pid/bind``, which proves ownership of the 3PID. + + If this endpoint returns a JSON Matrix error, that error should be passed + through to the client requesting an unbind through a homeserver, if the + homeserver is acting on behalf of a client. + operationId: unbind + parameters: + - in: body + name: body + schema: + type: object + example: { + "sid": "1234", + "client_secret": "monkeys_are_GREAT", + "mxid": "@ears:example.org", + "threepid": { + "medium": "email", + "address": "monkeys_have_ears@example.org" + } + } + properties: + sid: + type: string + description: The Session ID generated by the ``requestToken`` call. + client_secret: + type: string + description: The client secret passed to the ``requestToken`` call. + mxid: + type: string + description: The Matrix user ID to remove from the 3pids. + threepid: + type: object + title: 3PID + description: |- + The 3PID to remove. Must match the 3PID used to generate the session + if using ``sid`` and ``client_secret`` to authenticate this request. + properties: + medium: + type: string + description: |- + A medium from the `3PID Types`_ Appendix, matching the medium + of the identifier to unbind. + address: + type: string + description: The 3PID address to remove. + required: ['medium', 'address'] + required: ["threepid", "mxid"] + responses: + 200: + description: The association was successfully removed. + examples: + application/json: {} + schema: + type: object + 400: + description: |- + If the response body is not a JSON Matrix error, the identity server + does not support unbinds. If a JSON Matrix error is in the response + body, the requesting party should respect the error. + 404: + description: |- + If the response body is not a JSON Matrix error, the identity server + does not support unbinds. If a JSON Matrix error is in the response + body, the requesting party should respect the error. + 403: + description: |- + The credentials supplied to authenticate the request were invalid. + This may also be returned if the identity server does not support + the chosen authentication method (such as blocking homeservers from + unbinding identifiers). + examples: + application/json: { + "errcode": "M_FORBIDDEN", + "error": "Invalid homeserver signature" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + 501: + description: |- + If the response body is not a JSON Matrix error, the identity server + does not support unbinds. If a JSON Matrix error is in the response + body, the requesting party should respect the error. diff --git a/api/identity/v2_email_associations.yaml b/api/identity/v2_email_associations.yaml new file mode 100644 index 00000000..9911bc5d --- /dev/null +++ b/api/identity/v2_email_associations.yaml @@ -0,0 +1,175 @@ +# Copyright 2018 New Vector Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Email Associations API" + version: "1.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity/api/v1 +consumes: + - application/json +produces: + - application/json +paths: + "/validate/email/requestToken": + post: + summary: Request a token for validating an email address. + description: |- + Create a session for validating an email address. + + The identity server will send an email containing a token. If that + token is presented to the identity server in the future, it indicates + that that user was able to read the email for that email address, and + so we validate ownership of the email address. + + Note that homeservers offer APIs that proxy this API, adding + additional behaviour on top, for example, + ``/register/email/requestToken`` is designed specifically for use when + registering an account and therefore will inform the user if the email + address given is already registered on the server. + + Note: for backwards compatibility with previous drafts of this + specification, the parameters may also be specified as + ``application/x-form-www-urlencoded`` data. However, this usage is + deprecated. + operationId: emailRequestToken + parameters: + - in: body + name: body + schema: + $ref: "definitions/request_email_validation.yaml" + responses: + 200: + description: Session created. + schema: + $ref: "definitions/sid.yaml" + 400: + description: | + An error ocurred. Some possible errors are: + + - ``M_INVALID_EMAIL``: The email address provided was invalid. + - ``M_EMAIL_SEND_ERROR``: The validation email could not be sent. + examples: + application/json: { + "errcode": "M_INVALID_EMAIL", + "error": "The email address is not valid" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + "/validate/email/submitToken": + post: + summary: Validate ownership of an email address. + description: |- + Validate ownership of an email address. + + If the three parameters are consistent with a set generated by a + ``requestToken`` call, ownership of the email address is considered to + have been validated. This does not publish any information publicly, or + associate the email address with any Matrix user ID. Specifically, + calls to ``/lookup`` will not show a binding. + + The identity server is free to match the token case-insensitively, or + carry out other mapping operations such as unicode + normalisation. Whether to do so is an implementation detail for the + identity server. Clients must always pass on the token without + modification. + + Note: for backwards compatibility with previous drafts of this + specification, the parameters may also be specified as + ``application/x-form-www-urlencoded`` data. However, this usage is + deprecated. + operationId: emailSubmitTokenPost + parameters: + - in: body + name: body + schema: + type: object + example: { + "sid": "1234", + "client_secret": "monkeys_are_GREAT", + "token": "atoken" + } + properties: + sid: + type: string + description: The session ID, generated by the ``requestToken`` call. + client_secret: + type: string + description: The client secret that was supplied to the ``requestToken`` call. + token: + type: string + description: The token generated by the ``requestToken`` call and emailed to the user. + required: ["sid", "client_secret", "token"] + responses: + 200: + description: + The success of the validation. + examples: + application/json: { + "success": true + } + schema: + type: object + properties: + success: + type: boolean + description: Whether the validation was successful or not. + required: ['success'] + get: + summary: Validate ownership of an email address. + description: |- + Validate ownership of an email address. + + If the three parameters are consistent with a set generated by a + ``requestToken`` call, ownership of the email address is considered to + have been validated. This does not publish any information publicly, or + associate the email address with any Matrix user ID. Specifically, + calls to ``/lookup`` will not show a binding. + + Note that, in contrast with the POST version, this endpoint will be + used by end-users, and so the response should be human-readable. + operationId: emailSubmitTokenGet + parameters: + - in: query + type: string + name: sid + required: true + description: The session ID, generated by the ``requestToken`` call. + x-example: 1234 + - in: query + type: string + name: client_secret + required: true + description: The client secret that was supplied to the ``requestToken`` call. + x-example: monkeys_are_GREAT + - in: query + type: string + name: token + required: true + description: The token generated by the ``requestToken`` call and emailed to the user. + x-example: atoken + responses: + "200": + description: Email address is validated. + "3xx": + description: |- + Email address is validated, and the ``next_link`` parameter was + provided to the ``requestToken`` call. The user must be redirected + to the URL provided by the ``next_link`` parameter. + "4xx": + description: + Validation failed. diff --git a/api/identity/v2_invitation_signing.yaml b/api/identity/v2_invitation_signing.yaml new file mode 100644 index 00000000..f2d2933d --- /dev/null +++ b/api/identity/v2_invitation_signing.yaml @@ -0,0 +1,97 @@ +# Copyright 2018 New Vector Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Ephemeral Invitation Signing API" + version: "1.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity/api/v1 +consumes: + - application/json +produces: + - application/json +paths: + "/sign-ed25519": + post: + summary: Sign invitation details + description: |- + Sign invitation details. + + The identity server will look up ``token`` which was stored in a call + to ``store-invite``, and fetch the sender of the invite. + operationId: blindlySignStuff + parameters: + - in: body + name: body + schema: + type: object + example: { + "mxid": "@foo:bar.com", + "token": "sometoken", + "private_key": "base64encodedkey" + } + properties: + mxid: + type: string + description: The Matrix user ID of the user accepting the invitation. + token: + type: string + description: The token from the call to ``store-invite``. + private_key: + type: string + description: The private key, encoded as `Unpadded base64`_. + required: ["mxid", "token", "private_key"] + responses: + 200: + description: The signed JSON of the mxid, sender, and token. + schema: + type: object + properties: + mxid: + type: string + description: The Matrix user ID of the user accepting the invitation. + sender: + type: string + description: The Matrix user ID of the user who sent the invitation. + signatures: + type: object + description: The signature of the mxid, sender, and token. + $ref: "../../schemas/server-signatures.yaml" + token: + type: string + description: The token for the invitation. + required: ['mxid', 'sender', 'signatures', 'token'] + examples: + application/json: { + "mxid": "@foo:bar.com", + "sender": "@baz:bar.com", + "signatures": { + "my.id.server": { + "ed25519:0": "def987" + } + }, + "token": "abc123" + } + 404: + description: The token was not found. + examples: + application/json: { + "errcode": "M_UNRECOGNIZED", + "error": "Didn't recognize token" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" diff --git a/api/identity/v2_phone_associations.yaml b/api/identity/v2_phone_associations.yaml new file mode 100644 index 00000000..8d0da628 --- /dev/null +++ b/api/identity/v2_phone_associations.yaml @@ -0,0 +1,177 @@ +# Copyright 2018 New Vector Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Phone Number Associations API" + version: "1.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity/api/v1 +consumes: + - application/json +produces: + - application/json +paths: + "/validate/msisdn/requestToken": + post: + summary: Request a token for validating a phone number. + description: |- + Create a session for validating a phone number. + + The identity server will send an SMS message containing a token. If + that token is presented to the identity server in the future, it + indicates that that user was able to read the SMS for that phone + number, and so we validate ownership of the phone number. + + Note that homeservers offer APIs that proxy this API, adding + additional behaviour on top, for example, + ``/register/msisdn/requestToken`` is designed specifically for use when + registering an account and therefore will inform the user if the phone + number given is already registered on the server. + + Note: for backwards compatibility with previous drafts of this + specification, the parameters may also be specified as + ``application/x-form-www-urlencoded`` data. However, this usage is + deprecated. + operationId: msisdnRequestToken + parameters: + - in: body + name: body + schema: + $ref: "definitions/request_msisdn_validation.yaml" + responses: + 200: + description: Session created. + schema: + $ref: "definitions/sid.yaml" + 400: + description: | + An error ocurred. Some possible errors are: + + - ``M_INVALID_ADDRESS``: The phone number provided was invalid. + - ``M_SEND_ERROR``: The validation SMS could not be sent. + - ``M_DESTINATION_REJECTED``: The identity server cannot deliver an + SMS to the provided country or region. + examples: + application/json: { + "errcode": "M_INVALID_ADDRESS", + "error": "The phone number is not valid" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + "/validate/msisdn/submitToken": + post: + summary: Validate ownership of a phone number. + description: |- + Validate ownership of a phone number. + + If the three parameters are consistent with a set generated by a + ``requestToken`` call, ownership of the phone number is considered to + have been validated. This does not publish any information publicly, or + associate the phone number address with any Matrix user + ID. Specifically, calls to ``/lookup`` will not show a binding. + + The identity server is free to match the token case-insensitively, or + carry out other mapping operations such as unicode + normalisation. Whether to do so is an implementation detail for the + identity server. Clients must always pass on the token without + modification. + + Note: for backwards compatibility with previous drafts of this + specification, the parameters may also be specified as + ``application/x-form-www-urlencoded`` data. However, this usage is + deprecated. + operationId: msisdnSubmitTokenPost + parameters: + - in: body + name: body + schema: + type: object + example: { + "sid": "1234", + "client_secret": "monkeys_are_GREAT", + "token": "atoken" + } + properties: + sid: + type: string + description: The session ID, generated by the ``requestToken`` call. + client_secret: + type: string + description: The client secret that was supplied to the ``requestToken`` call. + token: + type: string + description: The token generated by the ``requestToken`` call and sent to the user. + required: ["sid", "client_secret", "token"] + responses: + 200: + description: + The success of the validation. + examples: + application/json: { + "success": true + } + schema: + type: object + properties: + success: + type: boolean + description: Whether the validation was successful or not. + required: ['success'] + get: + summary: Validate ownership of a phone number. + description: |- + Validate ownership of a phone number. + + If the three parameters are consistent with a set generated by a + ``requestToken`` call, ownership of the phone number address is + considered to have been validated. This does not publish any + information publicly, or associate the phone number with any Matrix + user ID. Specifically, calls to ``/lookup`` will not show a binding. + + Note that, in contrast with the POST version, this endpoint will be + used by end-users, and so the response should be human-readable. + operationId: msisdnSubmitTokenGet + parameters: + - in: query + type: string + name: sid + required: true + description: The session ID, generated by the ``requestToken`` call. + x-example: 1234 + - in: query + type: string + name: client_secret + required: true + description: The client secret that was supplied to the ``requestToken`` call. + x-example: monkeys_are_GREAT + - in: query + type: string + name: token + required: true + description: The token generated by the ``requestToken`` call and sent to the user. + x-example: atoken + responses: + "200": + description: Phone number is validated. + "3xx": + description: |- + Phone number address is validated, and the ``next_link`` parameter + was provided to the ``requestToken`` call. The user must be + redirected to the URL provided by the ``next_link`` parameter. + "4xx": + description: + Validation failed. diff --git a/api/identity/v2_ping.yaml b/api/identity/v2_ping.yaml new file mode 100644 index 00000000..fd81c7c3 --- /dev/null +++ b/api/identity/v2_ping.yaml @@ -0,0 +1,46 @@ +# Copyright 2018 Kamax Sàrl +# Copyright 2018 New Vector Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: "2.0" +info: + title: "Matrix Identity Service Ping API" + version: "1.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity +produces: + - application/json +paths: + "/api/v1": + get: + summary: Checks that an identity server is available at this API endpoint. + description: |- + Checks that an identity server is available at this API endpoint. + + To discover that an identity server is available at a specific URL, + this endpoint can be queried and will return an empty object. + + This is primarly used for auto-discovery and health check purposes + by entities acting as a client for the identity server. + operationId: ping + responses: + 200: + description: An identity server is ready to serve requests. + examples: + application/json: {} + schema: + type: object diff --git a/api/identity/v2_pubkey.yaml b/api/identity/v2_pubkey.yaml new file mode 100644 index 00000000..48446ace --- /dev/null +++ b/api/identity/v2_pubkey.yaml @@ -0,0 +1,127 @@ +# Copyright 2016 OpenMarket Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Public Key API" + version: "1.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity/api/v1 +consumes: + - application/json +produces: + - application/json +paths: + "/pubkey/{keyId}": + get: + summary: Get a public key. + description: |- + Get the public key for the passed key ID. + operationId: getPubKey + parameters: + - in: path + type: string + name: keyId + required: true + description: |- + The ID of the key. This should take the form algorithm:identifier + where algorithm identifies the signing algorithm, and the identifier + is an opaque string. + x-example: "ed25519:0" + responses: + 200: + description: + The public key exists. + examples: + application/json: { + "public_key": "VXuGitF39UH5iRfvbIknlvlAVKgD1BsLDMvBf0pmp7c" + } + schema: + type: object + properties: + public_key: + type: string + description: Unpadded Base64 encoded public key. + required: ['public_key'] + 404: + description: + The public key was not found. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "The public key was not found" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + "/pubkey/isvalid": + get: + summary: Check whether a long-term public key is valid. + description: |- + Check whether a long-term public key is valid. The response should always + be the same, provided the key exists. + operationId: isPubKeyValid + parameters: + - in: query + type: string + name: public_key + required: true + description: |- + The unpadded base64-encoded public key to check. + x-example: "VXuGitF39UH5iRfvbIknlvlAVKgD1BsLDMvBf0pmp7c" + responses: + 200: + description: + The validity of the public key. + examples: + application/json: { + "valid": true + } + schema: + type: object + properties: + valid: + type: boolean + description: Whether the public key is recognised and is currently valid. + required: ['valid'] + "/pubkey/ephemeral/isvalid": + get: + summary: Check whether a short-term public key is valid. + description: |- + Check whether a short-term public key is valid. + operationId: isEphemeralPubKeyValid + parameters: + - in: query + type: string + name: public_key + required: true + description: |- + The unpadded base64-encoded public key to check. + x-example: "VXuGitF39UH5iRfvbIknlvlAVKgD1BsLDMvBf0pmp7c" + responses: + 200: + description: + The validity of the public key. + examples: + application/json: { + "valid": true + } + schema: + type: object + properties: + valid: + type: boolean + description: Whether the public key is recognised and is currently valid. + required: ['valid'] diff --git a/api/identity/v2_store_invite.yaml b/api/identity/v2_store_invite.yaml new file mode 100644 index 00000000..802478dc --- /dev/null +++ b/api/identity/v2_store_invite.yaml @@ -0,0 +1,161 @@ +# Copyright 2018 New Vector Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Store Invitations API" + version: "1.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity/api/v1 +consumes: + - application/json +produces: + - application/json +paths: + "/store-invite": + post: + summary: Store pending invitations to a user's 3pid. + description: |- + Store pending invitations to a user's 3pid. + + In addition to the request parameters specified below, an arbitrary + number of other parameters may also be specified. These may be used in + the invite message generation described below. + + The service will generate a random token and an ephemeral key used for + accepting the invite. + + The service also generates a ``display_name`` for the inviter, which is + a redacted version of ``address`` which does not leak the full contents + of the ``address``. + + The service records persistently all of the above information. + + It also generates an email containing all of this data, sent to the + ``address`` parameter, notifying them of the invitation. + + Also, the generated ephemeral public key will be listed as valid on + requests to ``/_matrix/identity/api/v1/pubkey/ephemeral/isvalid``. + + Currently, invites may only be issued for 3pids of the ``email`` medium. + + Optional fields in the request should be populated to the best of the + server's ability. Identity servers may use these variables when notifying + the ``address`` of the pending invite for display purposes. + operationId: storeInvite + parameters: + - in: body + name: body + schema: + type: object + properties: + medium: + type: string + description: The literal string ``email``. + example: "email" + address: + type: string + description: The email address of the invited user. + example: "foo@example.com" + room_id: + type: string + description: The Matrix room ID to which the user is invited + example: "!something:example.org" + sender: + type: string + description: The Matrix user ID of the inviting user + example: "@bob:example.com" + room_alias: + type: string + description: |- + The Matrix room alias for the room to which the user is + invited. This should be retrieved from the ``m.room.canonical_alias`` + state event. + example: "#somewhere:exmaple.org" + room_avatar_url: + type: string + description: |- + The Content URI for the room to which the user is invited. This should + be retrieved from the ``m.room.avatar`` state event. + example: "mxc://example.org/s0meM3dia" + room_join_rules: + type: string + description: |- + The ``join_rule`` for the room to which the user is invited. This should + be retrieved from the ``m.room.join_rules`` state event. + example: "public" + room_name: + type: string + description: |- + The name of the room to which the user is invited. This should be retrieved + from the ``m.room.name`` state event. + example: "Bob's Emporium of Messages" + sender_display_name: + type: string + description: The display name of the user ID initiating the invite. + example: "Bob Smith" + sender_avatar_url: + type: string + description: The Content URI for the avatar of the user ID initiating the invite. + example: "mxc://example.org/an0th3rM3dia" + required: ["medium", "address", "room_id", "sender"] + responses: + 200: + description: The invitation was stored. + schema: + type: object + properties: + token: + type: string + description: | + The generated token. Must be a string consisting of the + characters ``[0-9a-zA-Z.=_-]``. Its length must not exceed + 255 characters and it must not be empty. + public_keys: + type: array + description: | + A list of [server's long-term public key, generated ephemeral + public key]. + items: + type: string + display_name: + type: string + description: The generated (redacted) display_name. + required: ['token', 'public_keys', 'display_name'] + example: + application/json: { + "token": "sometoken", + "public_keys": [ + "serverpublickey", + "ephemeralpublickey" + ], + "display_name": "f...@b..." + } + 400: + description: | + An error has occured. + + If the 3pid is already bound to a Matrix user ID, the error code + will be ``M_THREEPID_IN_USE``. If the medium is unsupported, the + error code will be ``M_UNRECOGNIZED``. + examples: + application/json: { + "errcode": "M_THREEPID_IN_USE", + "error": "Binding already known", + "mxid": "@alice:example.com" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" From cd75d0f220e8fa2d64d19e04a21a6e4b4bb35854 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 25 Aug 2019 11:29:12 +0300 Subject: [PATCH 164/390] Fix authenticity/authorization terminology Co-authored-by: Kitsune Ral Signed-off-by: Tulir Asokan --- proposals/2244-mass-redactions.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/2244-mass-redactions.md b/proposals/2244-mass-redactions.md index 68802221..1b012cf8 100644 --- a/proposals/2244-mass-redactions.md +++ b/proposals/2244-mass-redactions.md @@ -46,12 +46,12 @@ redaction event. #### Handle each target separately The target events of an `m.room.redaction` shall no longer be considered when -deciding the authenticity of an `m.room.redaction` event. Any other existing -rules remain unchanged. +authorizing of an `m.room.redaction` event. Any other existing rules remain +unchanged. When a server accepts an `m.room.redaction` using the modified auth rules, it -evaluates targets individually for authenticity under the existing auth rules. -Servers MUST NOT include failing and unknown entries to clients. +evaluates individually whether each target can be redacted under the existing +auth rules. Servers MUST NOT include failing and unknown entries to clients. > Servers do not know whether redaction targets are authorized at the time they receive the `m.room.redaction` unless they are in possession of the target @@ -63,7 +63,7 @@ When the implementation receives a belated target from an earlier authorized. > Servers should not send belated target events to clients if their redaction - was found to be authentic, as clients were not made aware of the redaction. + was found to be in effect, as clients were not made aware of the redaction. That fact is also used to simply ignore unauthorized targets and send the events to clients normally. From 238b78bbaf4c6c6b3019be9fc0f93e3021a8c3f2 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 25 Aug 2019 20:41:39 +0300 Subject: [PATCH 165/390] Add potential issue with redacted_because field Signed-off-by: Tulir Asokan --- proposals/2244-mass-redactions.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/2244-mass-redactions.md b/proposals/2244-mass-redactions.md index 1b012cf8..ea0874c3 100644 --- a/proposals/2244-mass-redactions.md +++ b/proposals/2244-mass-redactions.md @@ -70,6 +70,10 @@ authorized. ## Tradeoffs ## Potential issues +A redaction with a thousand targets could mean a thousand events get `unsiged` +-> `redacted_because` containing that redaction event. One potential solution +to this is omitting the list of redacted event IDs from the data in the +`redacted_because` field. ## Security considerations From 79a5663ec3b69c0d26c4cbb56562152778075b37 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 25 Aug 2019 20:50:20 +0300 Subject: [PATCH 166/390] Fix typos, inline links and move image into tree Signed-off-by: Tulir Asokan --- proposals/2244-mass-redactions.md | 31 ++++++++++------------- proposals/images/2244-redaction-spam.png | Bin 0 -> 164158 bytes 2 files changed, 13 insertions(+), 18 deletions(-) create mode 100644 proposals/images/2244-redaction-spam.png diff --git a/proposals/2244-mass-redactions.md b/proposals/2244-mass-redactions.md index ea0874c3..2a24a41e 100644 --- a/proposals/2244-mass-redactions.md +++ b/proposals/2244-mass-redactions.md @@ -1,17 +1,17 @@ # Mass redactions Matrix, like any platform with public chat rooms, has spammers. Currently, redacting spam essentially requires spamming redaction events in a 1:1 ratio, -which is not optimal[1]. Most clients do not even have any mass -redaction tools, likely in part due to the lack of a mass redaction API. A mass -redaction API on the other hand has not been implemented as it would require -sending lots of events at once. However, this problem could be solved by -allowing a single redaction event to redact many events instead of sending many -redaction events. +which is not optimal[1](images/2244-redaction-spam.png). Most +clients do not even have any mass redaction tools, likely in part due to the +lack of a mass redaction API. A mass redaction API on the other hand has not +been implemented as it would require sending lots of events at once. However, +this problem could be solved by allowing a single redaction event to redact +many events instead of sending many redaction events. ## Proposal -This proposal builds upon [MSC2174] and suggests making the `redacts` field -in the content of `m.room.redaction` events an array of event ID strings -instead of a single event ID string. +This proposal builds upon [MSC2174](https://github.com/matrix-org/matrix-doc/pull/2174) +and suggests making the `redacts` field in the content of `m.room.redaction` +events an array of event ID strings instead of a single event ID string. It would be easiest to do this before MSC2174 is written into the spec, as then only one migration would be needed: from an event-level redacts string to a @@ -36,9 +36,9 @@ rejected: soft failing until all targets are found or handling each target separately. #### Soft fail -[Soft fail] the event until all targets are found, then accept only if the -sender has the privileges to redact every listed event. This is how redactions -currently work. +[Soft fail](https://matrix.org/docs/spec/server_server/r0.1.3#soft-failure) the +event until all targets are found, then accept only if the sender has the +privileges to redact every listed event. This is how redactions currently work. This has the downside of requiring servers to fetch all the target events (and possibly forward them to clients) before being able to process and forward the @@ -46,7 +46,7 @@ redaction event. #### Handle each target separately The target events of an `m.room.redaction` shall no longer be considered when -authorizing of an `m.room.redaction` event. Any other existing rules remain +authorizing an `m.room.redaction` event. Any other existing rules remain unchanged. When a server accepts an `m.room.redaction` using the modified auth rules, it @@ -76,8 +76,3 @@ to this is omitting the list of redacted event IDs from the data in the `redacted_because` field. ## Security considerations - - -[1]: https://img.mau.lu/hEqqt.png -[MSC2174]: https://github.com/matrix-org/matrix-doc/pull/2174 -[Soft fail]: https://matrix.org/docs/spec/server_server/r0.1.3#soft-failure diff --git a/proposals/images/2244-redaction-spam.png b/proposals/images/2244-redaction-spam.png new file mode 100644 index 0000000000000000000000000000000000000000..386d95767bd02f2a1e852977b4e479f930887bf9 GIT binary patch literal 164158 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 P>fS3kW!VyGqrm?H8`(gv literal 0 HcmV?d00001 From a47c5aeb426eb5c5be7c50dbdd7c82418e839484 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 28 Aug 2019 14:17:51 -0600 Subject: [PATCH 167/390] v2-ify the v2 api Note that ping and pubkey doesn't require auth - see MSC2140 for more information. --- api/identity/v2_associations.yaml | 18 +++++++++++++----- api/identity/v2_email_associations.yaml | 18 +++++++++++++----- api/identity/v2_invitation_signing.yaml | 10 +++++++--- api/identity/v2_phone_associations.yaml | 18 +++++++++++++----- api/identity/v2_ping.yaml | 6 +++--- api/identity/v2_pubkey.yaml | 10 +++++----- api/identity/v2_store_invite.yaml | 12 ++++++++---- 7 files changed, 62 insertions(+), 30 deletions(-) diff --git a/api/identity/v2_associations.yaml b/api/identity/v2_associations.yaml index 247e1b4c..d1b29a8f 100644 --- a/api/identity/v2_associations.yaml +++ b/api/identity/v2_associations.yaml @@ -15,22 +15,26 @@ swagger: '2.0' info: title: "Matrix Identity Service Establishing Associations API" - version: "1.0.0" + version: "2.0.0" host: localhost:8090 schemes: - https -basePath: /_matrix/identity/api/v1 +basePath: /_matrix/identity/v2 consumes: - application/json produces: - application/json +securityDefinitions: + $ref: definitions/security.yaml paths: "/3pid/getValidated3pid": get: summary: Check whether ownership of a 3pid was validated. description: |- Determines if a given 3pid has been validated by a user. - operationId: getValidated3pid + operationId: getValidated3pidV2 + security: + - accessToken: [] parameters: - in: query type: string @@ -104,7 +108,9 @@ paths: specification, the parameters may also be specified as ``application/x-form-www-urlencoded`` data. However, this usage is deprecated. - operationId: bind + operationId: bindV2 + security: + - accessToken: [] parameters: - in: body name: body @@ -221,7 +227,9 @@ paths: If this endpoint returns a JSON Matrix error, that error should be passed through to the client requesting an unbind through a homeserver, if the homeserver is acting on behalf of a client. - operationId: unbind + operationId: unbindV2 + security: + - accessToken: [] parameters: - in: body name: body diff --git a/api/identity/v2_email_associations.yaml b/api/identity/v2_email_associations.yaml index 9911bc5d..eff18eaf 100644 --- a/api/identity/v2_email_associations.yaml +++ b/api/identity/v2_email_associations.yaml @@ -15,15 +15,17 @@ swagger: '2.0' info: title: "Matrix Identity Service Email Associations API" - version: "1.0.0" + version: "2.0.0" host: localhost:8090 schemes: - https -basePath: /_matrix/identity/api/v1 +basePath: /_matrix/identity/v2 consumes: - application/json produces: - application/json +securityDefinitions: + $ref: definitions/security.yaml paths: "/validate/email/requestToken": post: @@ -46,7 +48,9 @@ paths: specification, the parameters may also be specified as ``application/x-form-www-urlencoded`` data. However, this usage is deprecated. - operationId: emailRequestToken + operationId: emailRequestTokenV2 + security: + - accessToken: [] parameters: - in: body name: body @@ -92,7 +96,9 @@ paths: specification, the parameters may also be specified as ``application/x-form-www-urlencoded`` data. However, this usage is deprecated. - operationId: emailSubmitTokenPost + operationId: emailSubmitTokenPostV2 + security: + - accessToken: [] parameters: - in: body name: body @@ -142,7 +148,9 @@ paths: Note that, in contrast with the POST version, this endpoint will be used by end-users, and so the response should be human-readable. - operationId: emailSubmitTokenGet + operationId: emailSubmitTokenGetV2 + security: + - accessToken: [] parameters: - in: query type: string diff --git a/api/identity/v2_invitation_signing.yaml b/api/identity/v2_invitation_signing.yaml index f2d2933d..c1267bdc 100644 --- a/api/identity/v2_invitation_signing.yaml +++ b/api/identity/v2_invitation_signing.yaml @@ -15,15 +15,17 @@ swagger: '2.0' info: title: "Matrix Identity Service Ephemeral Invitation Signing API" - version: "1.0.0" + version: "2.0.0" host: localhost:8090 schemes: - https -basePath: /_matrix/identity/api/v1 +basePath: /_matrix/identity/v2 consumes: - application/json produces: - application/json +securityDefinitions: + $ref: definitions/security.yaml paths: "/sign-ed25519": post: @@ -33,7 +35,9 @@ paths: The identity server will look up ``token`` which was stored in a call to ``store-invite``, and fetch the sender of the invite. - operationId: blindlySignStuff + operationId: blindlySignStuffV2 + security: + - accessToken: [] parameters: - in: body name: body diff --git a/api/identity/v2_phone_associations.yaml b/api/identity/v2_phone_associations.yaml index 8d0da628..cfaea410 100644 --- a/api/identity/v2_phone_associations.yaml +++ b/api/identity/v2_phone_associations.yaml @@ -15,15 +15,17 @@ swagger: '2.0' info: title: "Matrix Identity Service Phone Number Associations API" - version: "1.0.0" + version: "2.0.0" host: localhost:8090 schemes: - https -basePath: /_matrix/identity/api/v1 +basePath: /_matrix/identity/v2 consumes: - application/json produces: - application/json +securityDefinitions: + $ref: definitions/security.yaml paths: "/validate/msisdn/requestToken": post: @@ -46,7 +48,9 @@ paths: specification, the parameters may also be specified as ``application/x-form-www-urlencoded`` data. However, this usage is deprecated. - operationId: msisdnRequestToken + operationId: msisdnRequestTokenV2 + security: + - accessToken: [] parameters: - in: body name: body @@ -94,7 +98,9 @@ paths: specification, the parameters may also be specified as ``application/x-form-www-urlencoded`` data. However, this usage is deprecated. - operationId: msisdnSubmitTokenPost + operationId: msisdnSubmitTokenPostV2 + security: + - accessToken: [] parameters: - in: body name: body @@ -144,7 +150,9 @@ paths: Note that, in contrast with the POST version, this endpoint will be used by end-users, and so the response should be human-readable. - operationId: msisdnSubmitTokenGet + operationId: msisdnSubmitTokenGetV2 + security: + - accessToken: [] parameters: - in: query type: string diff --git a/api/identity/v2_ping.yaml b/api/identity/v2_ping.yaml index fd81c7c3..61f5d35b 100644 --- a/api/identity/v2_ping.yaml +++ b/api/identity/v2_ping.yaml @@ -17,7 +17,7 @@ swagger: "2.0" info: title: "Matrix Identity Service Ping API" - version: "1.0.0" + version: "2.0.0" host: localhost:8090 schemes: - https @@ -25,7 +25,7 @@ basePath: /_matrix/identity produces: - application/json paths: - "/api/v1": + "/v2": get: summary: Checks that an identity server is available at this API endpoint. description: |- @@ -36,7 +36,7 @@ paths: This is primarly used for auto-discovery and health check purposes by entities acting as a client for the identity server. - operationId: ping + operationId: pingV2 responses: 200: description: An identity server is ready to serve requests. diff --git a/api/identity/v2_pubkey.yaml b/api/identity/v2_pubkey.yaml index 48446ace..68facd68 100644 --- a/api/identity/v2_pubkey.yaml +++ b/api/identity/v2_pubkey.yaml @@ -15,11 +15,11 @@ swagger: '2.0' info: title: "Matrix Identity Service Public Key API" - version: "1.0.0" + version: "2.0.0" host: localhost:8090 schemes: - https -basePath: /_matrix/identity/api/v1 +basePath: /_matrix/identity/v2 consumes: - application/json produces: @@ -30,7 +30,7 @@ paths: summary: Get a public key. description: |- Get the public key for the passed key ID. - operationId: getPubKey + operationId: getPubKeyV2 parameters: - in: path type: string @@ -72,7 +72,7 @@ paths: description: |- Check whether a long-term public key is valid. The response should always be the same, provided the key exists. - operationId: isPubKeyValid + operationId: isPubKeyValidV2 parameters: - in: query type: string @@ -101,7 +101,7 @@ paths: summary: Check whether a short-term public key is valid. description: |- Check whether a short-term public key is valid. - operationId: isEphemeralPubKeyValid + operationId: isEphemeralPubKeyValidV2 parameters: - in: query type: string diff --git a/api/identity/v2_store_invite.yaml b/api/identity/v2_store_invite.yaml index 802478dc..afc41a1c 100644 --- a/api/identity/v2_store_invite.yaml +++ b/api/identity/v2_store_invite.yaml @@ -15,15 +15,17 @@ swagger: '2.0' info: title: "Matrix Identity Service Store Invitations API" - version: "1.0.0" + version: "2.0.0" host: localhost:8090 schemes: - https -basePath: /_matrix/identity/api/v1 +basePath: /_matrix/identity/v2 consumes: - application/json produces: - application/json +securityDefinitions: + $ref: definitions/security.yaml paths: "/store-invite": post: @@ -48,14 +50,16 @@ paths: ``address`` parameter, notifying them of the invitation. Also, the generated ephemeral public key will be listed as valid on - requests to ``/_matrix/identity/api/v1/pubkey/ephemeral/isvalid``. + requests to ``/_matrix/identity/v2/pubkey/ephemeral/isvalid``. Currently, invites may only be issued for 3pids of the ``email`` medium. Optional fields in the request should be populated to the best of the server's ability. Identity servers may use these variables when notifying the ``address`` of the pending invite for display purposes. - operationId: storeInvite + operationId: storeInviteV2 + security: + - accessToken: [] parameters: - in: body name: body From b0acaeddfadb5e7753201e9e0f79dab581b7a3f6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 28 Aug 2019 14:18:11 -0600 Subject: [PATCH 168/390] Reference the v2 API where possible --- specification/identity_service_api.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/specification/identity_service_api.rst b/specification/identity_service_api.rst index 89604644..f8211f70 100644 --- a/specification/identity_service_api.rst +++ b/specification/identity_service_api.rst @@ -57,6 +57,8 @@ The following other versions are also available, in reverse chronological order: General principles ------------------ +.. TODO: TravisR - Define auth for IS v2 API in a future PR + The purpose of an identity server is to validate, store, and answer questions about the identities of users. In particular, it stores associations of the form "identifier X represents the same user as identifier Y", where identities may @@ -176,6 +178,8 @@ Status check {{ping_is_http_api}} +{{v2_ping_is_http_api}} + Key management -------------- @@ -195,11 +199,15 @@ service's long-term keys. {{pubkey_is_http_api}} +{{v2_pubkey_is_http_api}} + Association lookup ------------------ {{lookup_is_http_api}} +.. TODO: TravisR - Add v2 lookup API in future PR + Establishing associations ------------------------- @@ -243,16 +251,22 @@ Email associations {{email_associations_is_http_api}} +{{v2_email_associations_is_http_api}} + Phone number associations ~~~~~~~~~~~~~~~~~~~~~~~~~ {{phone_associations_is_http_api}} +{{v2_phone_associations_is_http_api}} + General ~~~~~~~ {{associations_is_http_api}} +{{v2_associations_is_http_api}} + Invitation storage ------------------ @@ -267,6 +281,8 @@ long-term private key for the identity server. {{store_invite_is_http_api}} +{{v2_store_invite_is_http_api}} + Ephemeral invitation signing ---------------------------- @@ -277,6 +293,8 @@ this isn't possible. {{invitation_signing_is_http_api}} +{{v2_invitation_signing_is_http_api}} + .. _`Unpadded Base64`: ../appendices.html#unpadded-base64 .. _`3PID Types`: ../appendices.html#pid-types .. _`Signing JSON`: ../appendices.html#signing-json From ca4d9d8636a883cfdf122e3df41562786ff823ef Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 28 Aug 2019 14:19:13 -0600 Subject: [PATCH 169/390] Deprecate the v1 IS API --- api/identity/associations.yaml | 3 +++ api/identity/email_associations.yaml | 3 +++ api/identity/invitation_signing.yaml | 1 + api/identity/lookup.yaml | 2 ++ api/identity/phone_associations.yaml | 3 +++ api/identity/ping.yaml | 1 + api/identity/pubkey.yaml | 3 +++ api/identity/store_invite.yaml | 1 + 8 files changed, 17 insertions(+) diff --git a/api/identity/associations.yaml b/api/identity/associations.yaml index 8ff4a9ed..82c70fb8 100644 --- a/api/identity/associations.yaml +++ b/api/identity/associations.yaml @@ -30,6 +30,7 @@ paths: description: |- Determines if a given 3pid has been validated by a user. operationId: getValidated3pid + deprecated: true parameters: - in: query type: string @@ -104,6 +105,7 @@ paths: ``application/x-form-www-urlencoded`` data. However, this usage is deprecated. operationId: bind + deprecated: true parameters: - in: body name: body @@ -221,6 +223,7 @@ paths: through to the client requesting an unbind through a homeserver, if the homeserver is acting on behalf of a client. operationId: unbind + deprecated: true parameters: - in: body name: body diff --git a/api/identity/email_associations.yaml b/api/identity/email_associations.yaml index 38186432..6ac28cd0 100644 --- a/api/identity/email_associations.yaml +++ b/api/identity/email_associations.yaml @@ -46,6 +46,7 @@ paths: ``application/x-form-www-urlencoded`` data. However, this usage is deprecated. operationId: emailRequestToken + deprecated: true parameters: - in: body name: body @@ -92,6 +93,7 @@ paths: ``application/x-form-www-urlencoded`` data. However, this usage is deprecated. operationId: emailSubmitTokenPost + deprecated: true parameters: - in: body name: body @@ -142,6 +144,7 @@ paths: Note that, in contrast with the POST version, this endpoint will be used by end-users, and so the response should be human-readable. operationId: emailSubmitTokenGet + deprecated: true parameters: - in: query type: string diff --git a/api/identity/invitation_signing.yaml b/api/identity/invitation_signing.yaml index 4e10e2b5..e2ac28b0 100644 --- a/api/identity/invitation_signing.yaml +++ b/api/identity/invitation_signing.yaml @@ -33,6 +33,7 @@ paths: The identity server will look up ``token`` which was stored in a call to ``store-invite``, and fetch the sender of the invite. operationId: blindlySignStuff + deprecated: true parameters: - in: body name: body diff --git a/api/identity/lookup.yaml b/api/identity/lookup.yaml index fd50f94b..166b0962 100644 --- a/api/identity/lookup.yaml +++ b/api/identity/lookup.yaml @@ -32,6 +32,7 @@ paths: summary: Look up the Matrix user ID for a 3pid. description: Look up the Matrix user ID for a 3pid. operationId: lookupUser + deprecated: true parameters: - in: query type: string @@ -101,6 +102,7 @@ paths: summary: Lookup Matrix user IDs for a list of 3pids. description: Lookup Matrix user IDs for a list of 3pids. operationId: lookupUsers + deprecated: true parameters: - in: body name: body diff --git a/api/identity/phone_associations.yaml b/api/identity/phone_associations.yaml index b9933f1a..28312a4d 100644 --- a/api/identity/phone_associations.yaml +++ b/api/identity/phone_associations.yaml @@ -46,6 +46,7 @@ paths: ``application/x-form-www-urlencoded`` data. However, this usage is deprecated. operationId: msisdnRequestToken + deprecated: true parameters: - in: body name: body @@ -94,6 +95,7 @@ paths: ``application/x-form-www-urlencoded`` data. However, this usage is deprecated. operationId: msisdnSubmitTokenPost + deprecated: true parameters: - in: body name: body @@ -144,6 +146,7 @@ paths: Note that, in contrast with the POST version, this endpoint will be used by end-users, and so the response should be human-readable. operationId: msisdnSubmitTokenGet + deprecated: true parameters: - in: query type: string diff --git a/api/identity/ping.yaml b/api/identity/ping.yaml index 05e12a87..d7249a77 100644 --- a/api/identity/ping.yaml +++ b/api/identity/ping.yaml @@ -36,6 +36,7 @@ paths: This is primarly used for auto-discovery and health check purposes by entities acting as a client for the identity server. operationId: ping + deprecated: true responses: 200: description: An identity server is ready to serve requests. diff --git a/api/identity/pubkey.yaml b/api/identity/pubkey.yaml index e657c61c..a585e7ec 100644 --- a/api/identity/pubkey.yaml +++ b/api/identity/pubkey.yaml @@ -30,6 +30,7 @@ paths: description: |- Get the public key for the passed key ID. operationId: getPubKey + deprecated: true parameters: - in: path type: string @@ -72,6 +73,7 @@ paths: Check whether a long-term public key is valid. The response should always be the same, provided the key exists. operationId: isPubKeyValid + deprecated: true parameters: - in: query type: string @@ -101,6 +103,7 @@ paths: description: |- Check whether a short-term public key is valid. operationId: isEphemeralPubKeyValid + deprecated: true parameters: - in: query type: string diff --git a/api/identity/store_invite.yaml b/api/identity/store_invite.yaml index bca78d7e..4a5d525d 100644 --- a/api/identity/store_invite.yaml +++ b/api/identity/store_invite.yaml @@ -55,6 +55,7 @@ paths: server's ability. Identity servers may use these variables when notifying the ``address`` of the pending invite for display purposes. operationId: storeInvite + deprecated: true parameters: - in: body name: body From ef5d3b9f302982f04f1decad72c0cdaefbc398e4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 28 Aug 2019 15:12:24 -0600 Subject: [PATCH 170/390] Correct token reference in MSC2140 --- proposals/2140-terms-of-service-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 9f96a00b..e5bcd0ac 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -170,7 +170,7 @@ This endpoint does *not* require authentication. #### `POST $prefix/terms`: Requests to this endpoint have a single key, `user_accepts` whose value is -a list of URLs (given by the `url` field in the GET response) of documents that +a list of URLs (given by the `url` field in the GET response) of documents that the user has agreed to: ```json @@ -269,7 +269,7 @@ A client uses this client/server API endpoint to request that the Homeserver removes the given 3PID from the given Identity Server, or all Identity Servers. Takes the same parameters as `POST /_matrix/client/r0/account/3pid/delete`, ie. `id_server`, `medium`, -`address` and the newly added `is_token`. +`address` and the newly added `id_access_token`. Returns the same as `POST /_matrix/client/r0/account/3pid/delete`. From e40d9e296d098a5a476efaa5e0f845418c017333 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 28 Aug 2019 15:12:47 -0600 Subject: [PATCH 171/390] Split OpenID token info out verbatim --- .../definitions/openid_token.yaml | 36 +++++++++++++++++++ api/client-server/openid.yaml | 23 +----------- 2 files changed, 37 insertions(+), 22 deletions(-) create mode 100644 api/client-server/definitions/openid_token.yaml diff --git a/api/client-server/definitions/openid_token.yaml b/api/client-server/definitions/openid_token.yaml new file mode 100644 index 00000000..8c84c26f --- /dev/null +++ b/api/client-server/definitions/openid_token.yaml @@ -0,0 +1,36 @@ +# Copyright 2018 New Vector Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +properties: + access_token: + type: string + description: |- + An access token the consumer may use to verify the identity of + the person who generated the token. This is given to the federation + API ``GET /openid/userinfo``. + token_type: + type: string + description: The string ``Bearer``. + matrix_server_name: + type: string + description: |- + The homeserver domain the consumer should use when attempting to + verify the user's identity. + expires_in: + type: integer + description: |- + The number of seconds before this token expires and a new one must + be generated. +required: ['access_token', 'token_type', 'matrix_server_name', 'expires_in'] diff --git a/api/client-server/openid.yaml b/api/client-server/openid.yaml index cb982fb3..01fdf68f 100644 --- a/api/client-server/openid.yaml +++ b/api/client-server/openid.yaml @@ -73,28 +73,7 @@ paths: "expires_in": 3600, } schema: - type: object - properties: - access_token: - type: string - description: |- - An access token the consumer may use to verify the identity of - the person who generated the token. This is given to the federation - API ``GET /openid/userinfo``. - token_type: - type: string - description: The string ``Bearer``. - matrix_server_name: - type: string - description: |- - The homeserver domain the consumer should use when attempting to - verify the user's identity. - expires_in: - type: integer - description: |- - The number of seconds before this token expires and a new one must - be generated. - required: ['access_token', 'token_type', 'matrix_server_name', 'expires_in'] + $ref: "definitions/openid_token.yaml" 429: description: This request was rate-limited. schema: From 4d0ea641211e2ea0292a1bc12392b955c903f285 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 28 Aug 2019 15:39:49 -0600 Subject: [PATCH 172/390] Define authentication, ripping off the client-server API description --- .../definitions/openid_token.yaml | 2 +- api/identity/v2_auth.yaml | 109 ++++++++++++++++++ specification/identity_service_api.rst | 29 ++++- 3 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 api/identity/v2_auth.yaml diff --git a/api/client-server/definitions/openid_token.yaml b/api/client-server/definitions/openid_token.yaml index 8c84c26f..b50fcd54 100644 --- a/api/client-server/definitions/openid_token.yaml +++ b/api/client-server/definitions/openid_token.yaml @@ -19,7 +19,7 @@ properties: description: |- An access token the consumer may use to verify the identity of the person who generated the token. This is given to the federation - API ``GET /openid/userinfo``. + API ``GET /openid/userinfo`` to verify the user's identity. token_type: type: string description: The string ``Bearer``. diff --git a/api/identity/v2_auth.yaml b/api/identity/v2_auth.yaml new file mode 100644 index 00000000..16864b8e --- /dev/null +++ b/api/identity/v2_auth.yaml @@ -0,0 +1,109 @@ +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Authentication API" + version: "2.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity/v2 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/account/register": + post: + summary: Exchanges an OpenID token for an access token. + description: |- + Exchanges an OpenID token from the homeserver for an access token to + access the identity server. The request body is the same as the values + returned by ``/openid/request_token`` in the Client-Server API. + operationId: registerAccount + parameters: + - in: body + name: body + schema: + $ref: "../client-server/definitions/openid_token.yaml" + responses: + 200: + description: |- + A token which can be used to authenticate future requests to the + identity server. + examples: + application/json: { + "token": "abc123_OpaqueString" + } + schema: + type: object + properties: + token: + type: string + description: |- + An opaque string representing the token to authenticate future + requests to the identity server with. + required: ['token'] + "/account": + get: + summary: Gets account holder information for a given token. + description: |- + Gets information about what user owns the access token used in the request. + operationId: getAccount + security: + - accessToken: [] + parameters: [] + responses: + 200: + description: The token holder's information. + examples: + application/json: { + "user_id": "@alice:example.org" + } + schema: + type: object + properties: + user_id: + type: string + description: The user ID which registered the token. + required: ['user_id'] + "/account/logout": + post: + summary: Logs out an access token, rendering it unusable. + description: |- + Logs out the access token, preventing it from being used to authenticate + future requests to the server. + operationId: logout + security: + - accessToken: [] + parameters: [] + responses: + 200: + description: The token was successfully logged out. + examples: + application/json: {} + schema: + type: object + 401: + description: |- + The token is not registered or is otherwise unknown to the server. + examples: + application/json: { + "errcode": "M_UNKNOWN_TOKEN", + "error": "Unrecognised access token" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" diff --git a/specification/identity_service_api.rst b/specification/identity_service_api.rst index f8211f70..455e10b4 100644 --- a/specification/identity_service_api.rst +++ b/specification/identity_service_api.rst @@ -57,8 +57,6 @@ The following other versions are also available, in reverse chronological order: General principles ------------------ -.. TODO: TravisR - Define auth for IS v2 API in a future PR - The purpose of an identity server is to validate, store, and answer questions about the identities of users. In particular, it stores associations of the form "identifier X represents the same user as identifier Y", where identities may @@ -173,6 +171,33 @@ to be returned by servers on all requests are:: Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization +Authentication +-------------- + +Most ``v2`` endpoints in the Identity Service API require authentication in order +to ensure that the requesting user has accepted all relevant policies and is otherwise +permitted to make the request. The ``v1`` API (currently deprecated) does not require +this authentication, however using ``v1`` is strongly discouraged as it will be removed +in a future release. + +Identity Servers use a scheme similar to the Client-Server API's concept of access +tokens to authenticate users. The access tokens provided by an Identity Server cannot +be used to authenticate Client-Server API requests. + +An access token is provided to an endpoint in one of two ways: + +1. Via a query string parameter, ``access_token=TheTokenHere``. +2. Via a request header, ``Authorization: Bearer TheTokenHere``. + +Clients are encouraged to the use the ``Authorization`` header where possible to prevent +the access token being leaked in access/HTTP logs. The query string should only be used +in cases where the ``Authorization`` header is inaccessible for the client. + +When credentials are required but missing or invalid, the HTTP call will return with a +status of 401 and the error code ``M_MISSING_TOKEN`` or ``M_UNKNOWN_TOKEN`` respectively. + +{{v2_auth_is_http_api}} + Status check ------------ From 0408373cf93bed99ab8422b84467da539b258b31 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 28 Aug 2019 15:43:47 -0600 Subject: [PATCH 173/390] Litter the client-server API with id_access_token --- api/client-server/administrative_contact.yaml | 11 ++++++++++- api/client-server/create_room.yaml | 5 ++++- .../definitions/request_email_validation.yaml | 5 ++++- .../definitions/request_msisdn_validation.yaml | 5 ++++- api/client-server/registration.yaml | 5 +++++ api/client-server/third_party_membership.yaml | 6 +++++- specification/client_server_api.rst | 6 ++++-- 7 files changed, 36 insertions(+), 7 deletions(-) diff --git a/api/client-server/administrative_contact.yaml b/api/client-server/administrative_contact.yaml index 0e93e4cd..30153bb0 100644 --- a/api/client-server/administrative_contact.yaml +++ b/api/client-server/administrative_contact.yaml @@ -110,10 +110,13 @@ paths: id_server: type: string description: The identity server to use. + id_access_token: + type: string + description: An access token previously registered with the identity server. sid: type: string description: The session identifier given by the identity server. - required: ["client_secret", "id_server", "sid"] + required: ["client_secret", "id_server", "id_access_token", "sid"] bind: type: boolean description: |- @@ -125,6 +128,7 @@ paths: example: { "three_pid_creds": { "id_server": "matrix.org", + "id_access_token": "abc123_OpaqueString", "sid": "abc123987", "client_secret": "d0n'tT3ll" }, @@ -189,6 +193,11 @@ paths: homeserver does not know the original ``id_server``, it MUST return a ``id_server_unbind_result`` of ``no-support``. example: "example.org" + id_access_token: + type: string + description: |- + An access token previously registered with the identity server. Required + if an ``id_server`` is specified. medium: type: string description: The medium of the third party identifier being removed. diff --git a/api/client-server/create_room.yaml b/api/client-server/create_room.yaml index bce61aad..d7a29d99 100644 --- a/api/client-server/create_room.yaml +++ b/api/client-server/create_room.yaml @@ -139,6 +139,9 @@ paths: id_server: type: string description: The hostname+port of the identity server which should be used for third party identifier lookups. + id_access_token: + type: string + description: An access token previously registered with the identity server. medium: type: string # TODO: Link to Identity Service spec when it eixsts @@ -146,7 +149,7 @@ paths: address: type: string description: The invitee's third party identifier. - required: ["id_server", "medium", "address"] + required: ["id_server", "id_access_token", "medium", "address"] room_version: type: string description: |- diff --git a/api/client-server/definitions/request_email_validation.yaml b/api/client-server/definitions/request_email_validation.yaml index 15bc5b3a..63e3ee21 100644 --- a/api/client-server/definitions/request_email_validation.yaml +++ b/api/client-server/definitions/request_email_validation.yaml @@ -23,4 +23,7 @@ allOf: include a port. This parameter is ignored when the homeserver handles 3PID verification. example: "id.example.com" - required: ["id_server"] + id_access_token: + type: string + description: An access token previously registered with the identity server. + required: ["id_server", "id_access_token"] diff --git a/api/client-server/definitions/request_msisdn_validation.yaml b/api/client-server/definitions/request_msisdn_validation.yaml index 370a10cc..43513628 100644 --- a/api/client-server/definitions/request_msisdn_validation.yaml +++ b/api/client-server/definitions/request_msisdn_validation.yaml @@ -23,4 +23,7 @@ allOf: include a port. This parameter is ignored when the homeserver handles 3PID verification. example: "id.example.com" - required: ["id_server"] + id_access_token: + type: string + description: An access token previously registered with the identity server. + required: ["id_server", "id_access_token"] diff --git a/api/client-server/registration.yaml b/api/client-server/registration.yaml index 71177d0c..a747e38b 100644 --- a/api/client-server/registration.yaml +++ b/api/client-server/registration.yaml @@ -542,6 +542,11 @@ paths: it must return an ``id_server_unbind_result`` of ``no-support``. example: "example.org" + id_access_token: + type: string + description: |- + An access token previously registered with the identity server. Required if an + ``id_server`` is supplied. responses: 200: description: The account has been deactivated. diff --git a/api/client-server/third_party_membership.yaml b/api/client-server/third_party_membership.yaml index 077e1f6a..075cd34b 100644 --- a/api/client-server/third_party_membership.yaml +++ b/api/client-server/third_party_membership.yaml @@ -92,6 +92,7 @@ paths: type: object example: { "id_server": "matrix.org", + "id_access_token": "abc123_OpaqueString", "medium": "email", "address": "cheeky@monkey.com" } @@ -99,6 +100,9 @@ paths: id_server: type: string description: The hostname+port of the identity server which should be used for third party identifier lookups. + id_access_token: + type: string + description: An access token previously registered with the identity server. medium: type: string # TODO: Link to Identity Service spec when it eixsts @@ -106,7 +110,7 @@ paths: address: type: string description: The invitee's third party identifier. - required: ["id_server", "medium", "address"] + required: ["id_server", "id_access_token", "medium", "address"] responses: 200: description: The user has been invited to join the room. diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst index 916604a3..5ff710d1 100644 --- a/specification/client_server_api.rst +++ b/specification/client_server_api.rst @@ -802,7 +802,8 @@ To use this authentication type, clients should submit an auth dict as follows: { "sid": "", "client_secret": "", - "id_server": "" + "id_server": "", + "id_access_token": "" } ], "session": "" @@ -830,7 +831,8 @@ To use this authentication type, clients should submit an auth dict as follows: { "sid": "", "client_secret": "", - "id_server": "" + "id_server": "", + "id_access_token": "" } ], "session": "" From aa98137514f121c55a1e4282bb50835fb85b8e82 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 28 Aug 2019 15:45:28 -0600 Subject: [PATCH 174/390] Add changelog --- changelogs/client_server/newsfragments/2255.breaking | 1 + changelogs/identity_service/newsfragments/2255.new | 1 + 2 files changed, 2 insertions(+) create mode 100644 changelogs/client_server/newsfragments/2255.breaking create mode 100644 changelogs/identity_service/newsfragments/2255.new diff --git a/changelogs/client_server/newsfragments/2255.breaking b/changelogs/client_server/newsfragments/2255.breaking new file mode 100644 index 00000000..f9c8c6e1 --- /dev/null +++ b/changelogs/client_server/newsfragments/2255.breaking @@ -0,0 +1 @@ +Add a required ``id_access_token`` to many places which require an ``id_server`` parameter. diff --git a/changelogs/identity_service/newsfragments/2255.new b/changelogs/identity_service/newsfragments/2255.new new file mode 100644 index 00000000..fcb6ba88 --- /dev/null +++ b/changelogs/identity_service/newsfragments/2255.new @@ -0,0 +1 @@ +Add ``/account``, ``/account/register``, and ``/account/logout`` to authenticate with the identity server. From fad99743836862d6651816de0a23d3437fd0987b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 28 Aug 2019 15:40:02 -0600 Subject: [PATCH 175/390] Add missed changelog from prior PR --- changelogs/identity_service/newsfragments/2254.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/identity_service/newsfragments/2254.feature diff --git a/changelogs/identity_service/newsfragments/2254.feature b/changelogs/identity_service/newsfragments/2254.feature new file mode 100644 index 00000000..089d01fe --- /dev/null +++ b/changelogs/identity_service/newsfragments/2254.feature @@ -0,0 +1 @@ +Deprecate the v1 API in favour of an authenticated v2 API. From 91f862d9e8c34ec2b85be955975ae44b41351249 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 28 Aug 2019 20:57:07 -0600 Subject: [PATCH 176/390] Use the right error code for 401 errors --- specification/identity_service_api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/identity_service_api.rst b/specification/identity_service_api.rst index 455e10b4..4afac6ef 100644 --- a/specification/identity_service_api.rst +++ b/specification/identity_service_api.rst @@ -194,7 +194,7 @@ the access token being leaked in access/HTTP logs. The query string should only in cases where the ``Authorization`` header is inaccessible for the client. When credentials are required but missing or invalid, the HTTP call will return with a -status of 401 and the error code ``M_MISSING_TOKEN`` or ``M_UNKNOWN_TOKEN`` respectively. +status of 401 and the error code ``M_UNAUTHORIZED``. {{v2_auth_is_http_api}} From c909a7c423aea546a5d31ea7d424bdda2473f1eb Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 29 Aug 2019 21:18:02 +0300 Subject: [PATCH 177/390] Move omitting redacted_because into proposal and add security consideration Signed-off-by: Tulir Asokan --- proposals/2244-mass-redactions.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/proposals/2244-mass-redactions.md b/proposals/2244-mass-redactions.md index 2a24a41e..38d699e1 100644 --- a/proposals/2244-mass-redactions.md +++ b/proposals/2244-mass-redactions.md @@ -23,11 +23,18 @@ limit would cap a single redaction event at a bit less than 1500 targets. Redactions are not intrinsically heavy, so a separate limit should not be necessary. +Due to the possible large number of redaction targets per redaction event, +servers should omit the list of redaction targets from the `unsigned` -> +`redacted_because` field of redacted events. If clients want to get the list +of targets of a redaction event in `redacted_because`, they should read the +`event_id` field of the `redacted_because` event and use the +`/rooms/{roomId}/event/{eventId}` endpoint to fetch the content. + ### Client behavior Clients shall apply existing `m.room.redaction` target behavior over an array of event ID strings. -### Server behavior +### Server behavior (auth rules) The redaction auth rules should change to iterate the array and check if the sender has the privileges to redact each event. @@ -70,9 +77,7 @@ authorized. ## Tradeoffs ## Potential issues -A redaction with a thousand targets could mean a thousand events get `unsiged` --> `redacted_because` containing that redaction event. One potential solution -to this is omitting the list of redacted event IDs from the data in the -`redacted_because` field. ## Security considerations +Server implementations should ensure that large redaction events do not become +a DoS vector, e.g. by processing redactions in the background. From 1d6501b6ecae194e5b105c513bb412548f1d0ab6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 29 Aug 2019 13:51:38 -0600 Subject: [PATCH 178/390] What if we allowed homeservers to deal with their own business? --- proposals/0000-id_server-now-optional.md | 57 ++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 proposals/0000-id_server-now-optional.md diff --git a/proposals/0000-id_server-now-optional.md b/proposals/0000-id_server-now-optional.md new file mode 100644 index 00000000..a066cf6b --- /dev/null +++ b/proposals/0000-id_server-now-optional.md @@ -0,0 +1,57 @@ +# Proposal to make the `id_server` parameter on client-server APIs optional + +In order to better protect the privacy of of a user, Matrix is wanting to shift to +a model where identity servers have less control over the affairs of the homeserver. +Identity servers are currently used to reset the passwords of users on a given homeserver +as an identity verification technique, however there is no reason why the homeserver +itself can't handle the verification. This proposal allows for a homeserver to verify +the identity of users itself, without the use of an identity server. + +## Proposal + +The `id_server` parameter is to become optional on the following endpoints: + +* `/_matrix/client/:version/account/3pid/:medium/requestToken` +* `/_matrix/client/:version/register/:medium/requestToken` +* `/_matrix/client/:version/account/password/:medium/requestToken` + +The `id_server` parameter is additionally deprecated with intentions of being removed +in a future specification release on the `/register/:medium` and `/account/password/:medium` +endpoints. Once appropriate adoption has been achieved, the specification can safely +remove the parameter as supported. The reason for this deprecation is to completely +remove the identity server's ability to be involved in password resets/registration. +Users wishing to bind their 3rd party identifiers can do so after registration, and +clients can automate this if they so desire. + +Similarly, `bind_email` and `bind_msisdn` are deprecated with intentions to be removed +in a future specification version on `/register`. The flags have no effect if no `id_server` +is being used. + +As per [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140), an `id_access_token` +is required only if an `id_server` is supplied. + +Although not specified as required in the specification currently, the `id_server` +as part of User-Interactive Authentication is also optional if this proposal is accepted. +When the client requests a token without an `id_server`, it should not specify an +`id_server` in UIA. + +Homeservers can reuse HTTP 400 `M_SERVER_NOT_TRUSTED` as an error code on the `/requestToken` +endpoints listed above if they do not trust the identity server the user is supplying. + +In order to allow client implementations to determine if the homeserver they are developed +against supports `id_server` being optional, an unstable feature flag of `m.require_identity_server` +is to be added to `/versions`. When the flag is `false` or not present, clients must assume +that the homeserver requires an `id_server` (ie: it has not yet considered it optional). +If this proposal is accepted, clients are expected to use the supported specification versions +the homeserver advertises instead of the feature flag's presence. + +## Tradeoffs + +Homeservers may have to set up MSISDN/email support to their implementations. This is believed +to be of minimal risk compared to allowing the identity server to continue being involved +with password reset/registration. + +## Security considerations + +The identity server was previously involved with affairs only the homeserver cares about. +This is no longer the case. From 4e43024039e10ade0ec4e40b2396dcfa69b743b6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 29 Aug 2019 13:54:53 -0600 Subject: [PATCH 179/390] Assign number --- ...0-id_server-now-optional.md => 2263-homeserver-pw-resets.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename proposals/{0000-id_server-now-optional.md => 2263-homeserver-pw-resets.md} (96%) diff --git a/proposals/0000-id_server-now-optional.md b/proposals/2263-homeserver-pw-resets.md similarity index 96% rename from proposals/0000-id_server-now-optional.md rename to proposals/2263-homeserver-pw-resets.md index a066cf6b..8db52572 100644 --- a/proposals/0000-id_server-now-optional.md +++ b/proposals/2263-homeserver-pw-resets.md @@ -1,4 +1,4 @@ -# Proposal to make the `id_server` parameter on client-server APIs optional +# MSC2263: Give homeservers the ability to handle their own 3PID registrations/password resets In order to better protect the privacy of of a user, Matrix is wanting to shift to a model where identity servers have less control over the affairs of the homeserver. From 16bb3bd8b522e506501f3bfbcf5dd91046a40a25 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 29 Aug 2019 13:59:48 -0600 Subject: [PATCH 180/390] Add an unstable feature flag to MSC2140 for clients to detect support --- proposals/2140-terms-of-service-2.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 9f96a00b..93259143 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -170,7 +170,7 @@ This endpoint does *not* require authentication. #### `POST $prefix/terms`: Requests to this endpoint have a single key, `user_accepts` whose value is -a list of URLs (given by the `url` field in the GET response) of documents that +a list of URLs (given by the `url` field in the GET response) of documents that the user has agreed to: ```json @@ -277,6 +277,16 @@ Clients may add IS bindings for 3PIDs that already exist on the user's Homeserver account by using the `POST /_matrix/client/r0/account/3pid` to re-add the 3PID. +### Unstable feature flag for transition + +In order to allow client implementations to determine if the homeserver they are developed +against supports `id_access_token`, an unstable feature flag of `m.id_access_token` +is to be added to `/versions`. When the flag is `false` or not present, clients must assume +that the homeserver does not support being given `id_access_token` and may receive an error +for doing so. Clients are expected to use the supported specification versions the homeserver +advertises instead of the feature flag's presence once this proposal is included in a release +of the specification. + ## Tradeoffs The Identity Service API previously did not require authentication, and OpenID From 6d5e90b1d6d01aef3afa2e630222007b4e281bb4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 29 Aug 2019 14:51:32 -0600 Subject: [PATCH 181/390] Apply suggestions from code review Co-Authored-By: Matthew Hodgson --- proposals/2263-homeserver-pw-resets.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/2263-homeserver-pw-resets.md b/proposals/2263-homeserver-pw-resets.md index 8db52572..1bb55da0 100644 --- a/proposals/2263-homeserver-pw-resets.md +++ b/proposals/2263-homeserver-pw-resets.md @@ -1,6 +1,6 @@ # MSC2263: Give homeservers the ability to handle their own 3PID registrations/password resets -In order to better protect the privacy of of a user, Matrix is wanting to shift to +In order to better protect the privacy of a user, Matrix is wanting to shift to a model where identity servers have less control over the affairs of the homeserver. Identity servers are currently used to reset the passwords of users on a given homeserver as an identity verification technique, however there is no reason why the homeserver @@ -15,7 +15,7 @@ The `id_server` parameter is to become optional on the following endpoints: * `/_matrix/client/:version/register/:medium/requestToken` * `/_matrix/client/:version/account/password/:medium/requestToken` -The `id_server` parameter is additionally deprecated with intentions of being removed +The `id_server` parameter is additionally deprecated with intention of being removed in a future specification release on the `/register/:medium` and `/account/password/:medium` endpoints. Once appropriate adoption has been achieved, the specification can safely remove the parameter as supported. The reason for this deprecation is to completely @@ -23,7 +23,7 @@ remove the identity server's ability to be involved in password resets/registrat Users wishing to bind their 3rd party identifiers can do so after registration, and clients can automate this if they so desire. -Similarly, `bind_email` and `bind_msisdn` are deprecated with intentions to be removed +Similarly, `bind_email` and `bind_msisdn` are deprecated with intention to be removed in a future specification version on `/register`. The flags have no effect if no `id_server` is being used. From 1a6eb9a4131a0683f095c142934bc45b8a2a71ad Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 29 Aug 2019 15:19:12 -0600 Subject: [PATCH 182/390] Update proposals/2263-homeserver-pw-resets.md Co-Authored-By: J. Ryan Stinnett --- proposals/2263-homeserver-pw-resets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2263-homeserver-pw-resets.md b/proposals/2263-homeserver-pw-resets.md index 1bb55da0..564d1e45 100644 --- a/proposals/2263-homeserver-pw-resets.md +++ b/proposals/2263-homeserver-pw-resets.md @@ -40,7 +40,7 @@ endpoints listed above if they do not trust the identity server the user is supp In order to allow client implementations to determine if the homeserver they are developed against supports `id_server` being optional, an unstable feature flag of `m.require_identity_server` -is to be added to `/versions`. When the flag is `false` or not present, clients must assume +is to be added to `/versions`. When the flag is `true` or not present, clients must assume that the homeserver requires an `id_server` (ie: it has not yet considered it optional). If this proposal is accepted, clients are expected to use the supported specification versions the homeserver advertises instead of the feature flag's presence. From f8780e23955e5f8a70b108daa0e6428f5ab9e7a6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 30 Aug 2019 04:44:39 -0600 Subject: [PATCH 183/390] add note about edit --- proposals/2140-terms-of-service-2.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 93259143..177da4d6 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -1,5 +1,7 @@ # MSC2140: Terms of Service API for Identity Servers and Integration Managers +*Note*: This MSC was added to in [MSC2264](https://github.com/matrix-org/matrix-doc/pull/2264) + [MSC1692](https://github.com/matrix-org/matrix-doc/issues/1692) introduces a method for homeservers to require that users read and agree to certain documents before being permitted to use the service. This proposal introduces a From 62cc11eee658ead69eb18cad153193a4082daa1f Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 30 Aug 2019 13:49:54 +0100 Subject: [PATCH 185/390] add missing github-labels file --- README.rst | 2 +- meta/github-labels.rst | 91 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 meta/github-labels.rst diff --git a/README.rst b/README.rst index b8847bfb..ace8ebdd 100644 --- a/README.rst +++ b/README.rst @@ -138,4 +138,4 @@ Issue tracking Issues with the Matrix specification are tracked in `GitHub `_. -See `meta/labels.rst `_ for notes on what the labels mean. +See `meta/github-labels.rst `_ for notes on what the labels mean. diff --git a/meta/github-labels.rst b/meta/github-labels.rst new file mode 100644 index 00000000..f674b81b --- /dev/null +++ b/meta/github-labels.rst @@ -0,0 +1,91 @@ +The following labels are used to help categorize issues: + +`spec-omission `_ +-------------------------------------------------------------------------------- + +Things which have been implemented but not currently specified. These may range +from entire API endpoints, to particular options or return parameters. + +Issues with this label will have been implemented in `Synapse +`_. Normally there will be a design +document in Google Docs or similar which describes the feature. + +Examples: + +* `Spec PUT /directory/list `_ +* `Unspec'd server_name request param for /join/{roomIdOrAlias} + `_ + +`clarification `_ +-------------------------------------------------------------------------------- + +An area where the spec could do with being more explicit. + +Examples: + +* `Spec the implicit limit on /syncs + `_ + +* `Clarify the meaning of the currently_active flags in presence events + `_ + +`spec-bug `_ +---------------------------------------------------------------------- + +Something which is in the spec, but is wrong. + +Note: this is *not* for things that are badly designed or don't work well +(for which see 'improvement' or 'feature') - it is for places where the +spec doesn't match reality. + +Examples: + +* `swagger is wrong for directory PUT + `_ + +* `receipts section still refers to initialSync + `_ + +`improvement `_ +---------------------------------------------------------------------------- + +A suggestion for a relatively simple improvement to the protocol. + +Examples: + +* `We need a 'remove 3PID' API so that users can remove mappings + `_ +* `We should mandate that /publicRooms requires an access_token + `_ + +`feature `_ +-------------------------------------------------------------------- + +A suggestion for a significant extension to the matrix protocol which +needs considerable consideration before implementation. + +Examples: + +* `Peer-to-peer Matrix `_ +* `Specify a means for clients to "edit" previous messages + `_ + + +`wart `_ +-------------------------------------------------------------- + +A point where the protocol is inconsistent or inelegant, but which isn't really +causing anybody any problems right now. Might be nice to consider fixing one +day. + + +`question `_ +---------------------------------------------------------------------- + +A thought or idea about the protocol which we aren't really sure whether to +pursue or not. + +Examples: + +* `Should we prepend anti-eval code to our json responses? + `_ From b36fe24f1b719d8650de39187f112ebe0fada041 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 30 Aug 2019 08:27:44 -0600 Subject: [PATCH 186/390] Let's not doubly remove things --- proposals/2263-homeserver-pw-resets.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/proposals/2263-homeserver-pw-resets.md b/proposals/2263-homeserver-pw-resets.md index 564d1e45..3b58af17 100644 --- a/proposals/2263-homeserver-pw-resets.md +++ b/proposals/2263-homeserver-pw-resets.md @@ -23,9 +23,8 @@ remove the identity server's ability to be involved in password resets/registrat Users wishing to bind their 3rd party identifiers can do so after registration, and clients can automate this if they so desire. -Similarly, `bind_email` and `bind_msisdn` are deprecated with intention to be removed -in a future specification version on `/register`. The flags have no effect if no `id_server` -is being used. +Note that `bind_email` and `bind_msisdn` on `/register` have already been removed +by [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140). As per [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140), an `id_access_token` is required only if an `id_server` is supplied. From 5acac5a44e0d4fb04010ed166069e96fc369c3e9 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 30 Aug 2019 09:12:02 -0600 Subject: [PATCH 187/390] Try bumping golang version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 659380b0..b37478bf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -97,7 +97,7 @@ jobs: command: DOCS_URL="${CIRCLE_BUILD_URL}/artifacts/${CIRCLE_NODE_INDEX}/${CIRCLE_WORKING_DIRECTORY/#\~/$HOME}/api/client-server/index.html"; echo $DOCS_URL build-dev-scripts: docker: - - image: golang:1.8 + - image: golang:1.10 steps: - checkout - run: From e6f85cacff43640ca3499b637b581f7973cb9a8d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 31 Aug 2019 00:53:08 +0300 Subject: [PATCH 188/390] Specify that "existing auth rules" means room v5 --- proposals/2244-mass-redactions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/2244-mass-redactions.md b/proposals/2244-mass-redactions.md index 38d699e1..6999b268 100644 --- a/proposals/2244-mass-redactions.md +++ b/proposals/2244-mass-redactions.md @@ -58,7 +58,8 @@ unchanged. When a server accepts an `m.room.redaction` using the modified auth rules, it evaluates individually whether each target can be redacted under the existing -auth rules. Servers MUST NOT include failing and unknown entries to clients. +room v5 auth rules. Servers MUST NOT include failing and unknown entries to +clients. > Servers do not know whether redaction targets are authorized at the time they receive the `m.room.redaction` unless they are in possession of the target From 6d71a41e225f1f0cc227e3528c917f6042d4f148 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 31 Aug 2019 16:01:04 +0100 Subject: [PATCH 189/390] Proposal for ignoring invites --- proposals/2270-ignore-invites.md | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 proposals/2270-ignore-invites.md diff --git a/proposals/2270-ignore-invites.md b/proposals/2270-ignore-invites.md new file mode 100644 index 00000000..c6e51e70 --- /dev/null +++ b/proposals/2270-ignore-invites.md @@ -0,0 +1,47 @@ +# Proposal for ignoring invites + +## Problem + +There is currently no way to ignore an invite in Matrix without explicitly +rejecting it and informing the inviter of the rejection. There are many social +situations where a user may want to silently ignore the invite instead. + +The closest you can currently get is to ignore the user who sent the invite - +but this will then ignore all activity from that user, which may not be +desirable. + +## Solution + +Currently we support ignoring users by maintaining an `m.ignored_user_list` event in +account_data, as per https://matrix.org/docs/spec/client_server/r0.5.0#id189. + +We could do also silently ignore rooms (including invites) with an equivalent +`m.ignored_room_list` event, with precisely the same semantics but listing +room IDs rather than user IDs. + +## Tradeoffs + + * We're limited to ~65KB worth of room IDs (although we could change the + limits for account_data 'events', given they're more freeform JSON than + events) + * We waste initial sync bandwidth with account_data info for ignored rooms + which we may never care about ever again. + * The list will grow unbounded over time (unless the user occasionally + unignores and explicitly rejects the invites), especially if invite spam + accelerates. + * We could instead have a dedicated API for this: + * `PUT /user/{userId}/ignore/{txnId}` + * `PUT /rooms/{roomId}/ignore/{txnId}` + * `GET /user/{userId}/ignore` + * `GET /rooms/{roomId}/ignore` + * `GET /ignore` (for querying the current lists) + * ...and a custom block in `/sync` responses to synchronise ignore changes + * ...except it feels that yet another custom API & custom response block + is even more complicated and clunky than making account_data a bit more + flexible. + * Alternatively, we could extend `/leave` with a `silent` parameter of some kind, + and rely on the invitee's HS to track these 'silent' leaves and ignore the + room on behalf of the invitee. However, it may be nice to let the user track + ignored invites cross-client so they can undo if necessary, which account_data + gives us for free. Plus semantically it seems a bit wrong to use `/leave` + to describe the act of ignoring an invite, when you're not actually leaving it. \ No newline at end of file From a805d2b77961816f6f15ce0a44ee3dfde838fb37 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 31 Aug 2019 16:04:55 +0100 Subject: [PATCH 190/390] oops, premature merge --- proposals/2270-ignore-invites.md | 47 -------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 proposals/2270-ignore-invites.md diff --git a/proposals/2270-ignore-invites.md b/proposals/2270-ignore-invites.md deleted file mode 100644 index c6e51e70..00000000 --- a/proposals/2270-ignore-invites.md +++ /dev/null @@ -1,47 +0,0 @@ -# Proposal for ignoring invites - -## Problem - -There is currently no way to ignore an invite in Matrix without explicitly -rejecting it and informing the inviter of the rejection. There are many social -situations where a user may want to silently ignore the invite instead. - -The closest you can currently get is to ignore the user who sent the invite - -but this will then ignore all activity from that user, which may not be -desirable. - -## Solution - -Currently we support ignoring users by maintaining an `m.ignored_user_list` event in -account_data, as per https://matrix.org/docs/spec/client_server/r0.5.0#id189. - -We could do also silently ignore rooms (including invites) with an equivalent -`m.ignored_room_list` event, with precisely the same semantics but listing -room IDs rather than user IDs. - -## Tradeoffs - - * We're limited to ~65KB worth of room IDs (although we could change the - limits for account_data 'events', given they're more freeform JSON than - events) - * We waste initial sync bandwidth with account_data info for ignored rooms - which we may never care about ever again. - * The list will grow unbounded over time (unless the user occasionally - unignores and explicitly rejects the invites), especially if invite spam - accelerates. - * We could instead have a dedicated API for this: - * `PUT /user/{userId}/ignore/{txnId}` - * `PUT /rooms/{roomId}/ignore/{txnId}` - * `GET /user/{userId}/ignore` - * `GET /rooms/{roomId}/ignore` - * `GET /ignore` (for querying the current lists) - * ...and a custom block in `/sync` responses to synchronise ignore changes - * ...except it feels that yet another custom API & custom response block - is even more complicated and clunky than making account_data a bit more - flexible. - * Alternatively, we could extend `/leave` with a `silent` parameter of some kind, - and rely on the invitee's HS to track these 'silent' leaves and ignore the - room on behalf of the invitee. However, it may be nice to let the user track - ignored invites cross-client so they can undo if necessary, which account_data - gives us for free. Plus semantically it seems a bit wrong to use `/leave` - to describe the act of ignoring an invite, when you're not actually leaving it. \ No newline at end of file From 1f2acbcf29ecfe4495cf53a7d9f749e9ec0f37c3 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 31 Aug 2019 16:45:53 +0100 Subject: [PATCH 191/390] RST is not MD --- specification/proposals_intro.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/proposals_intro.rst b/specification/proposals_intro.rst index 20cfd48d..de65ba27 100644 --- a/specification/proposals_intro.rst +++ b/specification/proposals_intro.rst @@ -79,7 +79,7 @@ their proposed changes to the Matrix protocol: * Pragmatism rather than perfection * Proof rather than conjecture -Please see [MSC1779](https://github.com/matrix-org/matrix-doc/blob/matthew/msc1779/proposals/1779-open-governance.md) +Please see `MSC1779 `_ for full details of the project's Guiding Principles. Technical notes From 7ba4564ac3145bae862069a16cb2c5a1308fbfb4 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 31 Aug 2019 21:04:15 +0300 Subject: [PATCH 192/390] Remove soft fail auth rule option Signed-off-by: Tulir Asokan --- proposals/2244-mass-redactions.md | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/proposals/2244-mass-redactions.md b/proposals/2244-mass-redactions.md index 6999b268..b2d35dff 100644 --- a/proposals/2244-mass-redactions.md +++ b/proposals/2244-mass-redactions.md @@ -35,28 +35,11 @@ Clients shall apply existing `m.room.redaction` target behavior over an array of event ID strings. ### Server behavior (auth rules) -The redaction auth rules should change to iterate the array and check if the -sender has the privileges to redact each event. - -There are at least two potential ways to handle targets that are not found or -rejected: soft failing until all targets are found or handling each target -separately. - -#### Soft fail -[Soft fail](https://matrix.org/docs/spec/server_server/r0.1.3#soft-failure) the -event until all targets are found, then accept only if the sender has the -privileges to redact every listed event. This is how redactions currently work. - -This has the downside of requiring servers to fetch all the target events (and -possibly forward them to clients) before being able to process and forward the -redaction event. - -#### Handle each target separately The target events of an `m.room.redaction` shall no longer be considered when authorizing an `m.room.redaction` event. Any other existing rules remain unchanged. -When a server accepts an `m.room.redaction` using the modified auth rules, it +After a server accepts an `m.room.redaction` using the modified auth rules, it evaluates individually whether each target can be redacted under the existing room v5 auth rules. Servers MUST NOT include failing and unknown entries to clients. From d346099cf70282c1db82ed7821b58c3e8a2bd915 Mon Sep 17 00:00:00 2001 From: Ben Parsons Date: Tue, 3 Sep 2019 18:30:05 +0100 Subject: [PATCH 193/390] deduplicate MSC1779 ref in proposals list --- specification/proposals_intro.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/proposals_intro.rst b/specification/proposals_intro.rst index de65ba27..06bd6dfd 100644 --- a/specification/proposals_intro.rst +++ b/specification/proposals_intro.rst @@ -79,7 +79,7 @@ their proposed changes to the Matrix protocol: * Pragmatism rather than perfection * Proof rather than conjecture -Please see `MSC1779 `_ +Please `see MSC1779 `_ for full details of the project's Guiding Principles. Technical notes From 2b8c8ad5126260ab7a3326f69a7fa881e62c4120 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 3 Sep 2019 13:49:25 -0600 Subject: [PATCH 194/390] MSC1779 is actually merged now --- specification/proposals_intro.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/proposals_intro.rst b/specification/proposals_intro.rst index 06bd6dfd..82a4225b 100644 --- a/specification/proposals_intro.rst +++ b/specification/proposals_intro.rst @@ -79,7 +79,7 @@ their proposed changes to the Matrix protocol: * Pragmatism rather than perfection * Proof rather than conjecture -Please `see MSC1779 `_ +Please `see MSC1779 `_ for full details of the project's Guiding Principles. Technical notes From afd50184940a957b6b0023acd284be07e177147b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 28 Aug 2019 21:56:09 -0600 Subject: [PATCH 195/390] Spec the terms of service handling for identity servers Part of MSC2140 Convert status codes to strings if there is a string status code. Fixes a build error when we mix 4xx and 403 in the same definition. We also have to correct stringified numbers to pass the build. --- api/identity/email_associations.yaml | 2 +- api/identity/phone_associations.yaml | 2 +- api/identity/v2_associations.yaml | 25 +++ api/identity/v2_auth.yaml | 22 +++ api/identity/v2_email_associations.yaml | 35 +++- api/identity/v2_invitation_signing.yaml | 11 ++ api/identity/v2_phone_associations.yaml | 35 +++- api/identity/v2_store_invite.yaml | 11 ++ api/identity/v2_terms.yaml | 149 ++++++++++++++++++ .../identity_service/newsfragments/2258.new | 1 + event-schemas/examples/m.accepted_terms | 10 ++ event-schemas/schema/m.accepted_terms | 23 +++ scripts/templating/matrix_templates/units.py | 16 +- specification/identity_service_api.rst | 27 ++++ 14 files changed, 364 insertions(+), 5 deletions(-) create mode 100644 api/identity/v2_terms.yaml create mode 100644 changelogs/identity_service/newsfragments/2258.new create mode 100644 event-schemas/examples/m.accepted_terms create mode 100644 event-schemas/schema/m.accepted_terms diff --git a/api/identity/email_associations.yaml b/api/identity/email_associations.yaml index 6ac28cd0..69ec7c58 100644 --- a/api/identity/email_associations.yaml +++ b/api/identity/email_associations.yaml @@ -165,7 +165,7 @@ paths: description: The token generated by the ``requestToken`` call and emailed to the user. x-example: atoken responses: - "200": + 200: description: Email address is validated. "3xx": description: |- diff --git a/api/identity/phone_associations.yaml b/api/identity/phone_associations.yaml index 28312a4d..65bbff0c 100644 --- a/api/identity/phone_associations.yaml +++ b/api/identity/phone_associations.yaml @@ -167,7 +167,7 @@ paths: description: The token generated by the ``requestToken`` call and sent to the user. x-example: atoken responses: - "200": + 200: description: Phone number is validated. "3xx": description: |- diff --git a/api/identity/v2_associations.yaml b/api/identity/v2_associations.yaml index d1b29a8f..b9039a18 100644 --- a/api/identity/v2_associations.yaml +++ b/api/identity/v2_associations.yaml @@ -95,6 +95,17 @@ paths: } schema: $ref: "../client-server/definitions/errors/error.yaml" + 403: + description: | + The user must do something in order to use this endpoint. One example + is an ``M_TERMS_NOT_SIGNED`` error where the user must `agree to more terms`_. + examples: + application/json: { + "errcode": "M_TERMS_NOT_SIGNED", + "error": "Please accept our updated terms of service before continuing" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" "/3pid/bind": post: summary: Publish an association between a session and a Matrix user ID. @@ -208,6 +219,17 @@ paths: } schema: $ref: "../client-server/definitions/errors/error.yaml" + 403: + description: | + The user must do something in order to use this endpoint. One example + is an ``M_TERMS_NOT_SIGNED`` error where the user must `agree to more terms`_. + examples: + application/json: { + "errcode": "M_TERMS_NOT_SIGNED", + "error": "Please accept our updated terms of service before continuing" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" "/3pid/unbind": post: summary: Remove an association between a session and a Matrix user ID. @@ -294,6 +316,9 @@ paths: This may also be returned if the identity server does not support the chosen authentication method (such as blocking homeservers from unbinding identifiers). + + Another common error code is ``M_TERMS_NOT_SIGNED`` where the user + needs to `agree to more terms`_ in order to continue. examples: application/json: { "errcode": "M_FORBIDDEN", diff --git a/api/identity/v2_auth.yaml b/api/identity/v2_auth.yaml index 16864b8e..a34f679c 100644 --- a/api/identity/v2_auth.yaml +++ b/api/identity/v2_auth.yaml @@ -80,6 +80,17 @@ paths: type: string description: The user ID which registered the token. required: ['user_id'] + 403: + description: | + The user must do something in order to use this endpoint. One example + is an ``M_TERMS_NOT_SIGNED`` error where the user must `agree to more terms`_. + examples: + application/json: { + "errcode": "M_TERMS_NOT_SIGNED", + "error": "Please accept our updated terms of service before continuing" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" "/account/logout": post: summary: Logs out an access token, rendering it unusable. @@ -107,3 +118,14 @@ paths: } schema: $ref: "../client-server/definitions/errors/error.yaml" + 403: + description: | + The user must do something in order to use this endpoint. One example + is an ``M_TERMS_NOT_SIGNED`` error where the user must `agree to more terms`_. + examples: + application/json: { + "errcode": "M_TERMS_NOT_SIGNED", + "error": "Please accept our updated terms of service before continuing" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" diff --git a/api/identity/v2_email_associations.yaml b/api/identity/v2_email_associations.yaml index eff18eaf..76c3747e 100644 --- a/api/identity/v2_email_associations.yaml +++ b/api/identity/v2_email_associations.yaml @@ -74,6 +74,17 @@ paths: } schema: $ref: "../client-server/definitions/errors/error.yaml" + 403: + description: | + The user must do something in order to use this endpoint. One example + is an ``M_TERMS_NOT_SIGNED`` error where the user must `agree to more terms`_. + examples: + application/json: { + "errcode": "M_TERMS_NOT_SIGNED", + "error": "Please accept our updated terms of service before continuing" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" "/validate/email/submitToken": post: summary: Validate ownership of an email address. @@ -135,6 +146,17 @@ paths: type: boolean description: Whether the validation was successful or not. required: ['success'] + 403: + description: | + The user must do something in order to use this endpoint. One example + is an ``M_TERMS_NOT_SIGNED`` error where the user must `agree to more terms`_. + examples: + application/json: { + "errcode": "M_TERMS_NOT_SIGNED", + "error": "Please accept our updated terms of service before continuing" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" get: summary: Validate ownership of an email address. description: |- @@ -171,7 +193,7 @@ paths: description: The token generated by the ``requestToken`` call and emailed to the user. x-example: atoken responses: - "200": + 200: description: Email address is validated. "3xx": description: |- @@ -181,3 +203,14 @@ paths: "4xx": description: Validation failed. + 403: + description: | + The user must do something in order to use this endpoint. One example + is an ``M_TERMS_NOT_SIGNED`` error where the user must `agree to more terms`_. + examples: + application/json: { + "errcode": "M_TERMS_NOT_SIGNED", + "error": "Please accept our updated terms of service before continuing" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" diff --git a/api/identity/v2_invitation_signing.yaml b/api/identity/v2_invitation_signing.yaml index c1267bdc..0431233a 100644 --- a/api/identity/v2_invitation_signing.yaml +++ b/api/identity/v2_invitation_signing.yaml @@ -99,3 +99,14 @@ paths: } schema: $ref: "../client-server/definitions/errors/error.yaml" + 403: + description: | + The user must do something in order to use this endpoint. One example + is an ``M_TERMS_NOT_SIGNED`` error where the user must `agree to more terms`_. + examples: + application/json: { + "errcode": "M_TERMS_NOT_SIGNED", + "error": "Please accept our updated terms of service before continuing" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" diff --git a/api/identity/v2_phone_associations.yaml b/api/identity/v2_phone_associations.yaml index cfaea410..6d4ad79b 100644 --- a/api/identity/v2_phone_associations.yaml +++ b/api/identity/v2_phone_associations.yaml @@ -76,6 +76,17 @@ paths: } schema: $ref: "../client-server/definitions/errors/error.yaml" + 403: + description: | + The user must do something in order to use this endpoint. One example + is an ``M_TERMS_NOT_SIGNED`` error where the user must `agree to more terms`_. + examples: + application/json: { + "errcode": "M_TERMS_NOT_SIGNED", + "error": "Please accept our updated terms of service before continuing" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" "/validate/msisdn/submitToken": post: summary: Validate ownership of a phone number. @@ -137,6 +148,17 @@ paths: type: boolean description: Whether the validation was successful or not. required: ['success'] + 403: + description: | + The user must do something in order to use this endpoint. One example + is an ``M_TERMS_NOT_SIGNED`` error where the user must `agree to more terms`_. + examples: + application/json: { + "errcode": "M_TERMS_NOT_SIGNED", + "error": "Please accept our updated terms of service before continuing" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" get: summary: Validate ownership of a phone number. description: |- @@ -173,7 +195,7 @@ paths: description: The token generated by the ``requestToken`` call and sent to the user. x-example: atoken responses: - "200": + 200: description: Phone number is validated. "3xx": description: |- @@ -183,3 +205,14 @@ paths: "4xx": description: Validation failed. + 403: + description: | + The user must do something in order to use this endpoint. One example + is an ``M_TERMS_NOT_SIGNED`` error where the user must `agree to more terms`_. + examples: + application/json: { + "errcode": "M_TERMS_NOT_SIGNED", + "error": "Please accept our updated terms of service before continuing" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" diff --git a/api/identity/v2_store_invite.yaml b/api/identity/v2_store_invite.yaml index afc41a1c..9b7a653c 100644 --- a/api/identity/v2_store_invite.yaml +++ b/api/identity/v2_store_invite.yaml @@ -163,3 +163,14 @@ paths: } schema: $ref: "../client-server/definitions/errors/error.yaml" + 403: + description: | + The user must do something in order to use this endpoint. One example + is an ``M_TERMS_NOT_SIGNED`` error where the user must `agree to more terms`_. + examples: + application/json: { + "errcode": "M_TERMS_NOT_SIGNED", + "error": "Please accept our updated terms of service before continuing" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" diff --git a/api/identity/v2_terms.yaml b/api/identity/v2_terms.yaml new file mode 100644 index 00000000..9b831fba --- /dev/null +++ b/api/identity/v2_terms.yaml @@ -0,0 +1,149 @@ +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Terms of Service API" + version: "2.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity/v2 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/terms": + get: + summary: Gets the terms of service offered by the server. + description: |- + Gets all the terms of service offered by the server. The client is expected + to filter through the terms to determine which terms need acceptance from the + user. Note that this endpoint does not require authentication. + operationId: getTerms + parameters: [] + responses: + 200: + description: |- + The terms of service offered by the server. + examples: + application/json: { + "policies": { + "terms_of_service": { + "version": "2.0", + "en": { + "name": "Terms of Service", + "url": "https://example.org/somewhere/terms-2.0-en.html" + }, + "fr": { + "name": "Conditions d'utilisation", + "url": "https://example.org/somewhere/terms-2.0-fr.html" + } + }, + "privacy_policy": { + "version": "1.2", + "en": { + "name": "Privacy Policy", + "url": "https://example.org/somewhere/privacy-1.2-en.html" + }, + "fr": { + "name": "Politique de confidentialité", + "url": "https://example.org/somewhere/privacy-1.2-fr.html" + } + } + } + } + schema: + type: object + properties: + policies: + type: object + title: Policy Map + description: |- + The policies the server offers. Mapped from arbitrary ID (unused in + this version of the specification) to a Policy Object. + additionalProperties: + type: object + title: Policy Object + description: |- + The policy. Includes a map of language (ISO 639-2) to language-specific + policy information. + properties: + version: + type: string + description: |- + The version for the policy. There are no requirements on what this + might be and could be "alpha", semantically versioned, or arbitrary. + required: ['version'] + # TODO: TravisR - Make this render + additionalProperties: + type: object + title: Internationalised Policy + description: |- + The policy information for the specified language. + properties: + name: + type: string + description: The translated name of the policy. + url: + type: string + description: |- + The URL, which should include the policy ID, version, and language + in it, to be presented to the user as the policy. URLs should have + all three criteria to avoid conflicts when the policy is updated + in the future: for example, if this was "https://example.org/terms.html" + then the server would be unable to update it because the client would + have already added that URL to the ``m.accepted_terms`` collection. + required: ['name', 'url'] + required: ['policies'] + post: + summary: Indicates acceptance of terms to the server. + description: |- + Called by a client to indicate that the user has accepted/agreed to the included + set of URLs. Servers MUST NOT assume that the client will be sending all previously + accepted URLs and should therefore append the provided URLs to what the server + already knows has been accepted. + + Clients MUST provide the URL of the policy in the language that was presented + to the user. Servers SHOULD consider acceptance of any one language's URL as + acceptance for all other languages of that policy. + + The server should avoid returning ``M_TERMS_NOT_SIGNED`` because the client + may not be accepting all terms at once. + operationId: agreeToTerms + security: + - accessToken: [] + parameters: + - in: body + name: body + schema: + type: object + properties: + user_accepts: + type: array + items: + type: string + description: The URLs the user is accepting in this request. + example: "https://example.org/somewhere/terms-2.0-en.html" + required: ['user_accepts'] + responses: + 200: + description: |- + The server has considered the user as having accepted the provided URLs. + examples: + application/json: {} + schema: + type: object diff --git a/changelogs/identity_service/newsfragments/2258.new b/changelogs/identity_service/newsfragments/2258.new new file mode 100644 index 00000000..06b9efff --- /dev/null +++ b/changelogs/identity_service/newsfragments/2258.new @@ -0,0 +1 @@ +Add endpoints for accepting and handling terms of service. diff --git a/event-schemas/examples/m.accepted_terms b/event-schemas/examples/m.accepted_terms new file mode 100644 index 00000000..5e8dad16 --- /dev/null +++ b/event-schemas/examples/m.accepted_terms @@ -0,0 +1,10 @@ +{ + "$ref": "core/event.json", + "type": "m.accepted_terms", + "content": { + "accepted": [ + "https://example.org/somewhere/terms-1.2-en.html", + "https://example.org/somewhere/privacy-1.2-en.html" + ] + } +} diff --git a/event-schemas/schema/m.accepted_terms b/event-schemas/schema/m.accepted_terms new file mode 100644 index 00000000..510e741d --- /dev/null +++ b/event-schemas/schema/m.accepted_terms @@ -0,0 +1,23 @@ +--- +allOf: + - $ref: core-event-schema/event.yaml +description: |- + A list of terms URLs the user has previously accepted. Clients SHOULD use this + to avoid presenting the user with terms they have already agreed to. +properties: + content: + type: object + properties: + accepted: + type: array + items: + type: string + description: |- + The list of URLs the user has previously accepted. Should be appended to + when the user agrees to new terms. + type: + enum: + - m.accepted_terms + type: string +title: Accepted Terms of Service URLs +type: object diff --git a/scripts/templating/matrix_templates/units.py b/scripts/templating/matrix_templates/units.py index 04e6f8a9..0c835508 100644 --- a/scripts/templating/matrix_templates/units.py +++ b/scripts/templating/matrix_templates/units.py @@ -586,7 +586,21 @@ class MatrixUnits(Units): raise Exception("Error handling parameter %s" % param_name, e) # endfor[param] good_response = None - for code in sorted(endpoint_swagger.get("responses", {}).keys()): + endpoint_status_codes = endpoint_swagger.get("responses", {}).keys() + # Check to see if any of the status codes are strings ("4xx") and if + # so convert everything to a string to avoid comparing ints and strings. + has_string_status = False + for code in endpoint_status_codes: + if isinstance(code, str): + has_string_status = True + break + if has_string_status: + endpoint_status_codes = [str(i) for i in endpoint_status_codes] + for code in sorted(endpoint_status_codes): + # Convert numeric responses to ints, assuming they got converted + # above. + if isinstance(code, str) and code.isdigit(): + code = int(code) res = endpoint_swagger["responses"][code] if not good_response and code == 200: good_response = res diff --git a/specification/identity_service_api.rst b/specification/identity_service_api.rst index 4afac6ef..c92f737b 100644 --- a/specification/identity_service_api.rst +++ b/specification/identity_service_api.rst @@ -198,6 +198,33 @@ status of 401 and the error code ``M_UNAUTHORIZED``. {{v2_auth_is_http_api}} + +.. _`agree to more terms`: + +Terms of service +---------------- + +Identity Servers are encouraged to have terms of service (or similar policies) to +ensure that users have agreed to their data being processed by the server. To facilitate +this, an identity server can respond to almost any authenticated API endpoint with a +HTTP 403 and the error code ``M_TERMS_NOT_SIGNED``. The error code is used to indicate +that the user must accept new terms of service before being able to continue. + +All endpoints which support authentication can return the ``M_TERMS_NOT_SIGNED`` error. +When clients receive the error, they are expected to make a call to ``GET /terms`` to +find out what terms the server offers. The client compares this to the ``m.accepted_terms`` +account data for the user (described later) and presents the user with option to accept +the still-missing terms of service. After the user has made their selection, if applicable, +the client sends a request to ``POST /terms`` to indicate the user's acceptance. The +server cannot expect that the client will send acceptance for all pending terms, and the +client should not expect that the server will not respond with another ``M_TERMS_NOT_SIGNED`` +on their next request. The terms the user has just accepted are appended to ``m.accepted_terms``. + +{{m_accepted_terms_event}} + +{{v2_terms_is_http_api}} + + Status check ------------ From 52e6868f5095d16bd3c72f73d797b208ebe2d1fc Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 3 Sep 2019 15:03:41 -0600 Subject: [PATCH 196/390] Remove bind_* params on /register as per MSC2140 See https://github.com/matrix-org/matrix-doc/pull/2140 See https://github.com/matrix-org/matrix-doc/issues/2253 --- api/client-server/registration.yaml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/api/client-server/registration.yaml b/api/client-server/registration.yaml index 71177d0c..8bb9a027 100644 --- a/api/client-server/registration.yaml +++ b/api/client-server/registration.yaml @@ -95,18 +95,6 @@ paths: should be authenticated, but is instead used to authenticate the ``register`` call itself. "$ref": "definitions/auth_data.yaml" - bind_email: - type: boolean - description: |- - If true, the server binds the email used for authentication to - the Matrix ID with the identity server. - example: false - bind_msisdn: - type: boolean - description: |- - If true, the server binds the phone number used for authentication - to the Matrix ID with the identity server. - example: false username: type: string description: |- From caf46db6e797cddffd205b1d81d4b1eb3a3c79e9 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 3 Sep 2019 15:04:58 -0600 Subject: [PATCH 197/390] Changelog --- changelogs/client_server/newsfragments/2279.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/2279.feature diff --git a/changelogs/client_server/newsfragments/2279.feature b/changelogs/client_server/newsfragments/2279.feature new file mode 100644 index 00000000..a1fdf168 --- /dev/null +++ b/changelogs/client_server/newsfragments/2279.feature @@ -0,0 +1 @@ +Remove ``bind_msisdn`` and ``bind_email`` from ``/register`` now that the identity server's bind endpoint requires authentication. From 001c51a7401f646d765c07d92593913d90a2ba47 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 3 Sep 2019 15:09:59 -0600 Subject: [PATCH 198/390] /3pid/delete and /deactivate don't take an id_access_token --- api/client-server/administrative_contact.yaml | 9 ++++----- api/client-server/registration.yaml | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/api/client-server/administrative_contact.yaml b/api/client-server/administrative_contact.yaml index 30153bb0..4b8e1d63 100644 --- a/api/client-server/administrative_contact.yaml +++ b/api/client-server/administrative_contact.yaml @@ -176,6 +176,10 @@ paths: description: |- Removes a third party identifier from the user's account. This might not cause an unbind of the identifier from the identity server. + + Unlike other endpoints, this endpoint does not take an ``id_access_token`` + parameter because the homeserver is expected to sign the request to the + identity server instead. operationId: delete3pidFromAccount security: - accessToken: [] @@ -193,11 +197,6 @@ paths: homeserver does not know the original ``id_server``, it MUST return a ``id_server_unbind_result`` of ``no-support``. example: "example.org" - id_access_token: - type: string - description: |- - An access token previously registered with the identity server. Required - if an ``id_server`` is specified. medium: type: string description: The medium of the third party identifier being removed. diff --git a/api/client-server/registration.yaml b/api/client-server/registration.yaml index a747e38b..596dfe34 100644 --- a/api/client-server/registration.yaml +++ b/api/client-server/registration.yaml @@ -519,6 +519,10 @@ paths: The homeserver may change the flows available depending on whether a valid access token is provided. + + Unlike other endpoints, this endpoint does not take an ``id_access_token`` + parameter because the homeserver is expected to sign the request to the + identity server instead. security: - accessToken: [] operationId: deactivateAccount @@ -542,11 +546,6 @@ paths: it must return an ``id_server_unbind_result`` of ``no-support``. example: "example.org" - id_access_token: - type: string - description: |- - An access token previously registered with the identity server. Required if an - ``id_server`` is supplied. responses: 200: description: The account has been deactivated. From 9574f3c8d0efa062f1d077b7590e29ea073d02d9 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Wed, 4 Sep 2019 14:12:00 +0100 Subject: [PATCH 199/390] update to the new deployment of Giles Signed-off-by: Stuart Mumford --- .circleci/config.yml | 2 +- pyproject.toml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b37478bf..bf4404ce 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -121,4 +121,4 @@ workflows: notify: webhooks: - - url: https://giles.cadair.com/circleci + - url: https://giles.cadair.dev/circleci diff --git a/pyproject.toml b/pyproject.toml index b53982b8..060a44fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,9 @@ -[ tool.giles ] +[ tool.gilesbot ] - [ tool.giles.circleci_artifacts.docs ] + [ tool.gilesbot.circleci_artifacts.docs ] url = "gen/index.html" message = "Click details to preview the HTML documentation." - [ tool.giles.circleci_artifacts.swagger ] + [ tool.gilesbot.circleci_artifacts.swagger ] url = "client-server/index.html" message = "Click to preview the swagger build." From 958cffd33085c44070bb400f0912192697fedb3c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 4 Sep 2019 16:17:41 -0600 Subject: [PATCH 200/390] Spec m.identity_server account data As per [MSC2230](https://github.com/matrix-org/matrix-doc/pull/2230) --- event-schemas/examples/m.identity_server | 7 +++++ event-schemas/schema/m.identity_server | 23 ++++++++++++++++ specification/client_server_api.rst | 35 ++++++++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 event-schemas/examples/m.identity_server create mode 100644 event-schemas/schema/m.identity_server diff --git a/event-schemas/examples/m.identity_server b/event-schemas/examples/m.identity_server new file mode 100644 index 00000000..32855e9c --- /dev/null +++ b/event-schemas/examples/m.identity_server @@ -0,0 +1,7 @@ +{ + "$ref": "core/event.json", + "type": "m.identity_server", + "content": { + "base_url": "https://example.org" + } +} diff --git a/event-schemas/schema/m.identity_server b/event-schemas/schema/m.identity_server new file mode 100644 index 00000000..75d2409f --- /dev/null +++ b/event-schemas/schema/m.identity_server @@ -0,0 +1,23 @@ +--- +allOf: + - $ref: core-event-schema/event.yaml +description: |- + Persists the user's preferred identity server, or preference to not use + an identity server at all, in the user's account data. +properties: + content: + type: object + properties: + base_url: + type: string + description: |- + The URL of the identity server the user prefers to use, or ``null`` + if the user does not want to use an identity server. This value is + similar in structure to the ``base_url`` for identity servers in the + ``.well-known/matrix/client`` schema. + type: + enum: + - m.identity_server + type: string +title: Identity Server Preference +type: object diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst index 916604a3..1ea98fef 100644 --- a/specification/client_server_api.rst +++ b/specification/client_server_api.rst @@ -1139,6 +1139,41 @@ Current account information {{whoami_cs_http_api}} +Notes on identity servers ++++++++++++++++++++++++++ + +Identity servers in Matrix store bindings (relationships) between a user's third +party identifier, typically email or phone number, and their user ID. Once a user +has chosen an identity server, that identity server should be used by all clients. + +Clients can see which identity server the user has chosen through the ``m.identity_server`` +account data event, as described below. Clients SHOULD refrain from making requests +to any identity server until the presence of ``m.identity_server`` is confirmed as +(not) present. If present, the client SHOULD check for the presence of the ``base_url`` +property in the event's content. If the ``base_url`` is present, the client SHOULD +use the identity server in that property as the identity server for the user. If the +``base_url`` is missing, or the account data event is not present, the client SHOULD +use whichever default value it normally would for an identity server, if applicable. +Clients SHOULD NOT update the account data with the default identity server when the +user is missing an identity server in their account data. + +Clients SHOULD listen for changes to the ``m.identity_server`` account data event +and update the identity server they are contacting as a result. + +If the client offers a way to set the identity server to use, it MUST update the +value of ``m.identity_server`` accordingly. A ``base_url`` of ``null`` MUST be +treated as though the user does not want to use an identity server, disabling all +related functionality as a result. + +Clients SHOULD refrain from populating the account data as a migration step for users +who are lacking the account data, unless the user sets the identity server within +the client to a value. For example, a user which has no ``m.identity_server`` account +data event should not end up with the client's default identity server in their +account data, unless the user first visits their account settings to set the identity +server. + +{{m_identity_server_event}} + Capabilities negotiation ------------------------ From 6ec7d4cd891e055294b883278c036af3dd6a993c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 4 Sep 2019 16:20:14 -0600 Subject: [PATCH 201/390] changelog --- changelogs/client_server/newsfragments/2281.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/2281.feature diff --git a/changelogs/client_server/newsfragments/2281.feature b/changelogs/client_server/newsfragments/2281.feature new file mode 100644 index 00000000..7c235423 --- /dev/null +++ b/changelogs/client_server/newsfragments/2281.feature @@ -0,0 +1 @@ +Add ``m.identity_server`` account data for tracking the user's preferred identity server. From 185c564a13797f511fc42ae43154fac8f28fe552 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 4 Sep 2019 16:33:54 -0600 Subject: [PATCH 202/390] Spec client-server IS unbind API As per [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140) Note: this modifies the endpoint in MSC2140 to be more in line with the remainder of the proposal. --- api/client-server/administrative_contact.yaml | 64 +++++++++++++++++++ .../client_server/newsfragments/2282.new | 1 + proposals/2140-terms-of-service-2.md | 3 +- 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 changelogs/client_server/newsfragments/2282.new diff --git a/api/client-server/administrative_contact.yaml b/api/client-server/administrative_contact.yaml index 4b8e1d63..2c9a7da7 100644 --- a/api/client-server/administrative_contact.yaml +++ b/api/client-server/administrative_contact.yaml @@ -234,6 +234,70 @@ paths: - id_server_unbind_result tags: - User data + "/account/3pid/unbind": + post: + summary: Removes a user's third party identifier from an identity server. + description: |- + Removes a user's third party identifier from the provided identity server. + This should not cause an unbind from the homeserver (as ``/3pid/delete`` + would) and should only affect the identity server. + + Unlike other endpoints, this endpoint does not take an ``id_access_token`` + parameter because the homeserver is expected to sign the request to the + identity server instead. + operationId: unbind3pidFromAccount + security: + - accessToken: [] + parameters: + - in: body + name: body + schema: + type: object + properties: + id_server: + type: string + description: |- + The identity server to unbind from. If not provided, the homeserver + MUST use the ``id_server`` the identifier was added through. If the + homeserver does not know the original ``id_server``, it MUST return + a ``id_server_unbind_result`` of ``no-support``. + example: "example.org" + medium: + type: string + description: The medium of the third party identifier being removed. + enum: ["email", "msisdn"] + example: "email" + address: + type: string + description: The third party address being removed. + example: "example@example.org" + required: ['medium', 'address'] + responses: + 200: + description: |- + The identity server has disassociated the third party identifier from the + user. + schema: + type: object + properties: + id_server_unbind_result: + type: string + enum: + # XXX: I don't know why, but the order matters here so that "no-support" + # doesn't become "no- support" by the renderer. + - "no-support" + - "success" + description: |- + An indicator as to whether or not the identity server was able to unbind + the 3PID. ``success`` indicates that the identity server has unbound the + identifier whereas ``no-support`` indicates that the identity server + refuses to support the request or the homeserver was not able to determine + an identity server to unbind from. + example: "success" + required: + - id_server_unbind_result + tags: + - User data "/account/3pid/email/requestToken": post: summary: Begins the validation process for an email address for association with the user's account. diff --git a/changelogs/client_server/newsfragments/2282.new b/changelogs/client_server/newsfragments/2282.new new file mode 100644 index 00000000..3395758d --- /dev/null +++ b/changelogs/client_server/newsfragments/2282.new @@ -0,0 +1 @@ +Add ``POST /account/3pid/unbind`` for removing a 3PID from an identity server. diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md index 3767c9b4..6bff8ebb 100644 --- a/proposals/2140-terms-of-service-2.md +++ b/proposals/2140-terms-of-service-2.md @@ -271,7 +271,8 @@ A client uses this client/server API endpoint to request that the Homeserver removes the given 3PID from the given Identity Server, or all Identity Servers. Takes the same parameters as `POST /_matrix/client/r0/account/3pid/delete`, ie. `id_server`, `medium`, -`address` and the newly added `id_access_token`. +`address`. Similar to the other unbind endpoints, this endpoint does not +require an `id_access_token` because the homeserver can only unbind. Returns the same as `POST /_matrix/client/r0/account/3pid/delete`. From 4d835c1753b695d9f77b84415c34a08344f4ccd3 Mon Sep 17 00:00:00 2001 From: Charlie Varley Date: Thu, 5 Sep 2019 10:31:44 +0100 Subject: [PATCH 203/390] Update index.rst --- specification/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/index.rst b/specification/index.rst index bf554e4a..c337d417 100644 --- a/specification/index.rst +++ b/specification/index.rst @@ -106,7 +106,7 @@ The principles that Matrix attempts to follow are: - Empowering the end-user + The user should be able to choose the server and clients they use - + The user should be control how private their communication is + + The user should be able to control how private their communication is + The user should know precisely where their data is stored - Fully decentralised - no single points of control over conversations or the From fc262300070aedbc8a948fc20518eeaa07c6f8e7 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Thu, 5 Sep 2019 14:41:53 +0100 Subject: [PATCH 204/390] Update proposals/1802-standardised-federation-response-format.md Co-Authored-By: Matthew Hodgson --- proposals/1802-standardised-federation-response-format.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/1802-standardised-federation-response-format.md b/proposals/1802-standardised-federation-response-format.md index bd8b1189..abb39361 100644 --- a/proposals/1802-standardised-federation-response-format.md +++ b/proposals/1802-standardised-federation-response-format.md @@ -47,5 +47,5 @@ code), then the sending server should retry the request with the `v1` API. ## Alternative solutions -An alternative solution would be to make the change in the `v1` fedration API, +An alternative solution would be to make the change in the `v1` federation API, but would break backward compatibility, thus would be harder to manage. From cb2b71c0be27346e8cd8c431b57031c692446ee7 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Thu, 5 Sep 2019 14:54:31 +0100 Subject: [PATCH 205/390] Remove /send + rename --- proposals/1802-standardised-federation-response-format.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/1802-standardised-federation-response-format.md b/proposals/1802-standardised-federation-response-format.md index bd8b1189..e874524e 100644 --- a/proposals/1802-standardised-federation-response-format.md +++ b/proposals/1802-standardised-federation-response-format.md @@ -1,4 +1,4 @@ -# Standardised federation response formats +# Remove the '200' value from some federation responses Some responses formats in the federation API specifications use the form `[200, res]` in which `res` is the JSON object containing the actual response for the @@ -13,7 +13,6 @@ This proposal proposes a backward compatible alternative Add a new version of the following endpoints under the prefix `/_matrix/federation/v2`: -* `PUT /_matrix/federation/v2/send/{txnId}` * `PUT /_matrix/federation/v2/send_join/{roomId}/{eventId}` * `PUT /_matrix/federation/v2/send_leave/{roomId}/{eventId}` From 1881a255c2e44b5c7e00bd4ac80555a5fe7502c8 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 5 Sep 2019 13:53:58 -0600 Subject: [PATCH 206/390] Clarify that id_access_token is optional for r0.5 --- api/client-server/administrative_contact.yaml | 5 ++++- api/client-server/create_room.yaml | 5 ++++- api/client-server/definitions/request_email_validation.yaml | 5 ++++- api/client-server/definitions/request_msisdn_validation.yaml | 5 ++++- api/client-server/third_party_membership.yaml | 5 ++++- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/api/client-server/administrative_contact.yaml b/api/client-server/administrative_contact.yaml index 4b8e1d63..cce0b261 100644 --- a/api/client-server/administrative_contact.yaml +++ b/api/client-server/administrative_contact.yaml @@ -112,7 +112,10 @@ paths: description: The identity server to use. id_access_token: type: string - description: An access token previously registered with the identity server. + description: |- + An access token previously registered with the identity server. Servers + can treat this as optional to distinguish between r0.5-compatible clients + and this specification version. sid: type: string description: The session identifier given by the identity server. diff --git a/api/client-server/create_room.yaml b/api/client-server/create_room.yaml index d7a29d99..2487707e 100644 --- a/api/client-server/create_room.yaml +++ b/api/client-server/create_room.yaml @@ -141,7 +141,10 @@ paths: description: The hostname+port of the identity server which should be used for third party identifier lookups. id_access_token: type: string - description: An access token previously registered with the identity server. + description: |- + An access token previously registered with the identity server. Servers + can treat this as optional to distinguish between r0.5-compatible clients + and this specification version. medium: type: string # TODO: Link to Identity Service spec when it eixsts diff --git a/api/client-server/definitions/request_email_validation.yaml b/api/client-server/definitions/request_email_validation.yaml index 63e3ee21..2b270514 100644 --- a/api/client-server/definitions/request_email_validation.yaml +++ b/api/client-server/definitions/request_email_validation.yaml @@ -25,5 +25,8 @@ allOf: example: "id.example.com" id_access_token: type: string - description: An access token previously registered with the identity server. + description: |- + An access token previously registered with the identity server. Servers + can treat this as optional to distinguish between r0.5-compatible clients + and this specification version. required: ["id_server", "id_access_token"] diff --git a/api/client-server/definitions/request_msisdn_validation.yaml b/api/client-server/definitions/request_msisdn_validation.yaml index 43513628..b013a561 100644 --- a/api/client-server/definitions/request_msisdn_validation.yaml +++ b/api/client-server/definitions/request_msisdn_validation.yaml @@ -25,5 +25,8 @@ allOf: example: "id.example.com" id_access_token: type: string - description: An access token previously registered with the identity server. + description: |- + An access token previously registered with the identity server. Servers + can treat this as optional to distinguish between r0.5-compatible clients + and this specification version. required: ["id_server", "id_access_token"] diff --git a/api/client-server/third_party_membership.yaml b/api/client-server/third_party_membership.yaml index 075cd34b..4c5890d1 100644 --- a/api/client-server/third_party_membership.yaml +++ b/api/client-server/third_party_membership.yaml @@ -102,7 +102,10 @@ paths: description: The hostname+port of the identity server which should be used for third party identifier lookups. id_access_token: type: string - description: An access token previously registered with the identity server. + description: |- + An access token previously registered with the identity server. Servers + can treat this as optional to distinguish between r0.5-compatible clients + and this specification version. medium: type: string # TODO: Link to Identity Service spec when it eixsts From 738fa6833e033bbf5e6a58c7003d1dc1904dc404 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 5 Sep 2019 20:24:17 -0600 Subject: [PATCH 207/390] Proposal to make the identity server optional during discovery --- ...0000-optional-identity-server-discovery.md | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 proposals/0000-optional-identity-server-discovery.md diff --git a/proposals/0000-optional-identity-server-discovery.md b/proposals/0000-optional-identity-server-discovery.md new file mode 100644 index 00000000..013a3e77 --- /dev/null +++ b/proposals/0000-optional-identity-server-discovery.md @@ -0,0 +1,22 @@ +# Proposal to make the identity server optional during discovery + +Currently the specification requires that clients `FAIL_ERROR` (hard failure - do not continue) +when the `.well-known` file for `m.identity_server` points to somewhere invalid or is invalid +itself. + +This proposal aims to change that by allowing clients to make a conscious decision to continue +with the invalid identity server configuration, provided the homeserver configuration is valid. + +## Proposal + +Instead of `FAIL_ERROR` for an invalid `m.identity_server` schema/server, clients are to move +to the `FAIL_PROMPT` (inform the user, ask for input if applicable) state. Clients can decide +to show a warning that the identity server is unavailable and allow the user to continue with +the invalid (or client's default) configuration. + +## Tradeoffs + +Clients can end up being configured with an invalid or inoperable identity server. This is +considered a feature by this proposal to allow for less intelligent clients to have their +identity server disabled. Intelligent clients could interpret the lack of identity server +as the homeserver/user asking that identity server functionality be disabled in the client. From 6baeb6c21875f2440f220561af2d069e85749bd9 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 5 Sep 2019 20:25:31 -0600 Subject: [PATCH 208/390] Assign number --- ...-discovery.md => 2284-optional-identity-server-discovery.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename proposals/{0000-optional-identity-server-discovery.md => 2284-optional-identity-server-discovery.md} (94%) diff --git a/proposals/0000-optional-identity-server-discovery.md b/proposals/2284-optional-identity-server-discovery.md similarity index 94% rename from proposals/0000-optional-identity-server-discovery.md rename to proposals/2284-optional-identity-server-discovery.md index 013a3e77..2da7018f 100644 --- a/proposals/0000-optional-identity-server-discovery.md +++ b/proposals/2284-optional-identity-server-discovery.md @@ -1,4 +1,4 @@ -# Proposal to make the identity server optional during discovery +# MSC2284: Making the identity server optional during discovery Currently the specification requires that clients `FAIL_ERROR` (hard failure - do not continue) when the `.well-known` file for `m.identity_server` points to somewhere invalid or is invalid From 642be6c6773bf269e95ba2c268230046e2748913 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 6 Sep 2019 13:06:16 -0600 Subject: [PATCH 209/390] Fix unbind wording --- api/client-server/administrative_contact.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/api/client-server/administrative_contact.yaml b/api/client-server/administrative_contact.yaml index dfb9520e..3581edf7 100644 --- a/api/client-server/administrative_contact.yaml +++ b/api/client-server/administrative_contact.yaml @@ -241,9 +241,8 @@ paths: post: summary: Removes a user's third party identifier from an identity server. description: |- - Removes a user's third party identifier from the provided identity server. - This should not cause an unbind from the homeserver (as ``/3pid/delete`` - would) and should only affect the identity server. + Removes a user's third party identifier from the provided identity server + without removing it from the homeserver. Unlike other endpoints, this endpoint does not take an ``id_access_token`` parameter because the homeserver is expected to sign the request to the From 7644085274e1837b4e5f5701ebcfa8edfb95b425 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Sat, 7 Sep 2019 16:03:11 -0400 Subject: [PATCH 210/390] wording fixes and clarifications --- proposals/1756-cross-signing.md | 50 ++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 1dbef83c..53690b5f 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -2,9 +2,9 @@ ## Background -A user with multiple devices will have a different key for end-to-end -encryption for each device. Other users who want to communicate securely with -this user must then verify each key on each of their devices. If Alice has *n* +If a user has multiple devices, each device will have a different key for +end-to-end encryption. Other users who want to communicate securely with this +user must then verify each key on each of their own devices. If Alice has *n* devices, and Bob has *m* devices, then for Alice to be able to communicate with Bob on any of their devices, this involves *n×m* key verifications. @@ -21,15 +21,15 @@ MSC1680 is presented below. Each user has three sets of key pairs: -- a master cross-signing key pair that is used to identify themselves and to +- a *master* cross-signing key pair that is used to identify themselves and to sign their other cross-signing keys, -- a self-signing key pair that is used to sign their own devices, and -- a user-signing key pair that is used to sign other users' master keys. +- a *self-signing* key pair that is used to sign their own devices, and +- a *user-signing* key pair that is used to sign other users' master keys. When one user (e.g. Alice) verifies another user's (Bob's) identity, Alice will sign Bob's master key with her user-signing key. (This will mean that -verification methods will need to be modified to pass along the master -identity key.) Alice's device will trust Bob's device if: +verification methods will need to be modified to pass along the public part of +the master key.) Alice's device will trust Bob's device if: - Alice's device is using a master key that has signed her user-signing key, - Alice's user-signing key has signed Bob's master key, @@ -41,7 +41,7 @@ identity key.) Alice's device will trust Bob's device if: A user's master key could allow an attacker to impersonate that user to other users, or other users to that user. Thus clients must ensure that the private part of the master key is treated securely. If clients do not have a secure -means of storing the master key (such as an secret storage system provided by +means of storing the master key (such as a secret storage system provided by the operating system), then clients must not store the private part. If a user changes their master key, clients of users that they communicate with must notify their users about the change. @@ -68,7 +68,7 @@ keys, respectively. Currently, users will only be allowed to see * signatures made by their own master, self-signing or user-signing keys, -* signatures made by their own devices of their own master key, +* signatures made by their own devices about their own master key, * signatures made by other users' self-signing keys about the other users' own devices, * signatures made by other users' master keys about the other users' @@ -85,8 +85,8 @@ Users who have verified individual devices may wish to migrate these verifications to use cross-signing instead. In order to aid with this, signatures of a user's master key, made by their own devices, may be uploaded to the server. If another client sees that the user's master key has a valid -signature from a device that was previously verified, then the client MAY -choose to trust and sign the master key. The client SHOULD take precautions to +signature from a device that was previously verified, then the client may +choose to trust and sign the master key. The client should take precautions to ensure that a stolen device cannot be used to cause it to trust a malicious master key. For example, a client could prompt the user before signing the master key, or it could only do this migration on the first master key that it @@ -379,13 +379,15 @@ response: The response contains a `failures` property, which is a map of user ID to device ID to failure reason, if any of the uploaded keys failed. The -homeserver should verify that the signature is correct. If it is not, the -homeserver should set the corresponding entry in `failures` to a JSON object -with the `errcode` property set to `M_INVALID_SIGNATURE`. +homeserver should verify that the signatures on the uploaded keys are valid. +If a signature is not valid, the homeserver should set the corresponding entry +in `failures` to a JSON object with the `errcode` property set to +`M_INVALID_SIGNATURE`. After Alice uploads a signature for her own devices or master key, her signature will be included in the results of the `/keys/query` request when -*anyone* requests her keys: +*anyone* requests her keys. However, signatures made for other users' keys, +made by her user-signing key, will not be included. `POST /keys/query` @@ -455,12 +457,16 @@ response: } ``` -Similarly, the federation endpoints `GET /user/keys/query` and -`POST /user/devices/{userId}` will include the new signature. - -In addition, Alice's server will send an `m.device_list_update` EDU to servers -that have users who share encrypted rooms with Alice, updating her device to -include her new signature. +Similarly, the federation endpoints `GET /user/keys/query` and `POST +/user/devices/{userId}` will include the new signatures for her own devices or +master key, but not signatures made by her user-signing key. + +In addition, when Alice uploads signatures for her own device, Alice's server +will send an `m.device_list_update` EDU to servers that have users who share +encrypted rooms with Alice, updating her device to include her new signature. +And when a signature of a master key is uploaded, Alice's server will send an +`m.signing_key_update` EDU, updating her master key to include her new +signature. After Alice uploads a signature for Bob's user-signing key, her signature will be included in the results of the `/keys/query` request when Alice requests From 60f0ad5b24b8928dff7c246083339c1332b9873e Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Sat, 7 Sep 2019 16:05:46 -0400 Subject: [PATCH 211/390] lowercase --- proposals/1756-cross-signing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 53690b5f..2e4f1b43 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -148,8 +148,8 @@ Cross-signing keys are JSON objects with the following properties: "`ed25519:`" followed by the unpadded base64 encoding of the public key, and whose value is the unpadded base64 encoding of the public key. * `signatures` ({string: {string: string}}): signatures of the key. A - self-signing or user-signing key MUST be signed by the master key. A master - key MAY be signed by a device. + self-signing or user-signing key must be signed by the master key. A master + key may be signed by a device. In order to ensure that there will be no collisions in the `signatures` property, the server must respond with an `M_FORBIDDEN` error if any of From cf953c47fd799af7573bc257fce6fd6db47c5d02 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 9 Sep 2019 17:29:32 -0400 Subject: [PATCH 212/390] clarifications, change "hash" to "etag" --- .../1219-storing-megolm-keys-serverside.md | 112 ++++++++++-------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index dbcf3091..f51ed5f9 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -38,20 +38,26 @@ Proposal -------- This proposal creates new APIs to allow clients to back up room decryption keys -on the server. Decryption keys are encrypted (using public key crypto) before -being sent to the server along with some unencrypted metadata to allow the -server to manage the backups, overwriting backups with "better" versions of the -keys. The user is given a private recovery key to save for recovering the keys -from the backup. - -Clients can create new versions of backups. Aside from the initial backup -creation, a client might start a new version of a backup when, for example, a -user loses a device, and wants to ensure that that device does not get any new -decryption keys. - -Once one client has created a backup version, other clients can fetch the -public key for the backup from the server and add keys to the backup, if they -trust that the backup was not created by a malicious device. +on the server. Room decryption keys are encrypted (using public key crypto) +before being sent to the server along with some unencrypted metadata to allow +the server to manage the backups. If a key for a new megolm session is +uploaded, it is added to the current backup. If a key is uploaded for a megolm +session is that is already present in the backup, the server will use the +metadata to determine which version of the key is "better". The way in which +the server determines which key is "better" is described in the [Storing +Keys](#storing-keys) section. The user is given a private recovery key in +order to recover the keys from the backup in the future. + +Clients can create new key backups (sometimes also referred to in the API as +backup versions) to replace the current backup. Aside from the initial backup +creation, a client might start a new a backup when, for example, a user loses a +device and wants to ensure that that device does not get any new decryption +keys. In this case, the client will then create a new backup using a new key +that the device does not have access to. + +Once one client has created a backup, other clients can fetch the public part +of the recovery key from the server and add keys to the backup, if they trust +that the backup was not created by a malicious device. ### Possible UX for interactive clients @@ -63,31 +69,30 @@ On receipt of encryption keys (1st time): 1. client checks if there is an existing backup: `GET /room_keys/version` 1. if not, ask if the user wants to back up keys 1. if yes: - 1. generate new curve25519 key pair - 2. create new backup version: `POST /room_keys/version` - 3. display private key for user to save (see below for the format) + 1. generate new curve25519 key pair, which will be the recovery key + 2. create new backup: `POST /room_keys/version` + 3. display private key for user to save (see below for the + [format of the recovery key](#recovery-key)) 2. if no, exit and remember decision (user can change their mind later) 3. while prompting, continue to poll `GET /room_keys/versions`, as another device may have created a backup. If so, go to 1.2. - 2. if yes, get public key, prompt user to verify a device that signed the - key¹, or enter recovery key (which can derive the backup key). + 2. if yes, either get the public part of the recovery key and check that it + is signed by the master cross-signing key, or prompt user to enter the + private part of the recovery key (which can derive the public part). 1. User can also decide to create a new backup, in which case, go to 1.1. 2. send key to backup: `PUT /room_keys/keys/${roomId}/${sessionId}?version=$v` 3. continue backing up keys as we receive them (may receive a - `M_WRONG_ROOM_KEYS_VERSION` error if a new backup version has been created: + `M_WRONG_ROOM_KEYS_VERSION` error if a new backup has been created: see below) On `M_WRONG_ROOM_KEYS_VERSION` error when trying to `PUT` keys: 1. get the current version -2. notify the user that there is a new backup version, and display relevant - information +2. notify the user that there is a new backup, and display relevant information 3. confirm with user that they want to use the backup (user may want use the backup, to stop backing up keys, or to create a new backup) -4. verify the device that signed the backup key¹, or enter recovery key - -¹: cross-signing (when that is completed) can be used to verify the device -that signed the key. +4. ensure the backup key is signed by the user's master key, or prompt the user + to enter the recovery key On receipt of undecryptable message: @@ -125,7 +130,7 @@ is 35 bytes. Clients must then remove the first two bytes and the last byte, and use the resulting string as the private key to decrypt backups. If MSC1946 is used to store the key on the server, it must be stored using the -`account_data` `type` `m.megolm_backup.v1`. +`account_data` type `m.megolm_backup.v1`. As a special case, if the recovery key is the same as the curve25519 key used for storing the key, then the contents of the `m.megolm_backup.v1` @@ -160,8 +165,8 @@ Body parameters: - `algorithm` (string): Required. The algorithm used for storing backups. Currently, only `m.megolm_backup.v1.curve25519-aes-sha2` is defined. - `auth_data` (object): Required. algorithm-dependent data. For - `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the definition of - this property. + `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the [definition of + this property](#auth_data-backup-versions). Example: @@ -195,10 +200,10 @@ On success, returns a JSON object with keys: - `auth_data` (object): Required. Same as in the body parameters for `POST /room_keys/version`. - `version` (string): Required. The backup version. -- `hash` (string): Required. The hash value which is an opaque string - representing stored keys in the backup. Client can compare it with the `hash` - value they received in the response of their last key storage request. - If not equal, another matrix client pushed new keys to the backup. +- `etag` (string): Required. The etag value which is an opaque string + representing stored keys in the backup. Clients can compare it with the + `etag` value they received in the response of their last key storage request. + If not equal, another client has pushed new keys to the backup. - `count` (number): Required. The number of keys stored in the backup. Error codes: @@ -214,8 +219,8 @@ Body parameters: - `algorithm` (string): Required. Must be the same as in the body parameters for `GET /room_keys/version`. - `auth_data` (object): Required. algorithm-dependent data. For - `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the definition of - this property. + `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the [definition of + this property](#auth_data-backup-versions). - `version` (string): Required. The backup version. Must be the same as the query parameter or must be the current version. Example: @@ -251,9 +256,15 @@ version. If the server already has a backup in the backup version for the given session and room, then it will keep the "better" one. To determine which one is -"better", key backups are compared first by the `is_verified` flag (`true` is -better than `false`), then by the `first_message_index` (a lower number is better), -and finally by `forwarded_count` (a lower number is better). +"better", keys are compared: + +- first by the `is_verified` flag (`true` is better than `false`), +- then, if `is_verified` is equal, by the `first_message_index` (a lower number is better), +- and finally, is `is_verified` and `first_message_index` are equal, by + `forwarded_count` (a lower number is better). + +If neither key is better than the other (that is, if all three fields are +equal), then the server should keep the existing key. Body parameters: @@ -264,12 +275,12 @@ Body parameters: - `is_verified` (boolean): Required. Whether the device backing up the key has verified the device that the key is from. - `session_data` (object): Required. Algorithm-dependent data. For - `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the definition of - this property. + `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the [definition of + this property](#auth_data-backup-versions). On success, returns a JSON object with keys: -- `hash` (string): Required. The new hash value representing stored keys. See +- `etag` (string): Required. The new etag value representing stored keys. See `GET /room_keys/version/{version}` for more details. - `count` (number): Required. The new count of keys stored in the backup. @@ -299,7 +310,7 @@ Result: ```javascript { - "hash": "abcdefghi", + "etag": "abcdefghi", "count": 10 } ``` @@ -344,7 +355,7 @@ Result: ```javascript { - "hash": "abcdefghi", + "etag": "abcdefghi", "count": 10 } ``` @@ -393,7 +404,7 @@ Result: ```javascript { - "hash": "abcdefghi", + "etag": "abcdefghi", "count": 10 } ``` @@ -401,7 +412,7 @@ Result: #### Retrieving keys When retrieving keys, the `version` parameter is optional, and defaults to -retrieving the latest backup version. +retrieving keys from the latest backup version. ##### `GET /room_keys/keys/${roomId}/${sessionId}?version=$v` @@ -463,7 +474,8 @@ Error codes: Deletes keys from the backup. -On success, returns the empty JSON object. +Returns the same as `PUT +/room_keys/keys/${roomId}/${sessionId}?version=$v`. #### `m.megolm_backup.v1.curve25519-aes-sha2` definitions @@ -479,9 +491,9 @@ following keys: The `auth_data` should be signed by the user's [master cross-signing key](https://github.com/matrix-org/matrix-doc/pull/1756), and may also be -signed by the user's device key. The allows clients to ensure that the public +signed by the user's device key. This allows clients to ensure that the public key is valid, and prevents an attacker from being able to change the backup to -use a public key that have the private key for. +use a public key that they have the private key for. ##### `session_data` for key backups @@ -489,7 +501,9 @@ The `session_data` field in the backups is constructed as follows: 1. Encode the session key to be backed up as a JSON object with the properties: - `algorithm` (string): `m.megolm.v1.aes-sha2` - - `sender_key` (string): base64-encoded device curve25519 key + - `sender_key` (string): base64-encoded device curve25519 key in + [session-sharing + format](https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/megolm.md#session-sharing-format) - `sender_claimed_keys` (object): object containing the identity keys for the sending device - `forwarding_curve25519_key_chain` (array): zero or more curve25519 keys From 6cfd761204b3fe36c76c290afa8463c8256ae951 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 9 Sep 2019 19:49:49 -0600 Subject: [PATCH 213/390] Spec the v2 lookup API Spec for [MSC2134](https://github.com/matrix-org/matrix-doc/pull/2134) --- api/identity/lookup.yaml | 2 +- api/identity/v2_lookup.yaml | 145 ++++++++++++++++++++++++ specification/identity_service_api.rst | 146 ++++++++++++++++++++++++- 3 files changed, 291 insertions(+), 2 deletions(-) create mode 100644 api/identity/v2_lookup.yaml diff --git a/api/identity/lookup.yaml b/api/identity/lookup.yaml index 166b0962..78fa3e3e 100644 --- a/api/identity/lookup.yaml +++ b/api/identity/lookup.yaml @@ -16,7 +16,7 @@ # limitations under the License. swagger: '2.0' info: - title: "Matrix Identity Service Lookup API" + title: "Matrix Identity Service Lookup API" version: "1.0.0" host: localhost:8090 schemes: diff --git a/api/identity/v2_lookup.yaml b/api/identity/v2_lookup.yaml new file mode 100644 index 00000000..561700f4 --- /dev/null +++ b/api/identity/v2_lookup.yaml @@ -0,0 +1,145 @@ +# Copyright 2016 OpenMarket Ltd +# Copyright 2017 Kamax.io +# Copyright 2017 New Vector Ltd +# Copyright 2018 New Vector Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Lookup API" + version: "2.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity/v2 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/hash_details": + get: + summary: Gets hash function information from the server. + description: |- + Gets parameters for hashing identifiers from the server. This can include + any of the algorithms defined in this specification. + operationId: getHashDetails + security: + - accessToken: [] + parameters: [] + responses: + 200: + description: The hash function information. + examples: + application/json: { + "lookup_pepper": "matrixrocks", + "algorithms": ["none", "sha256"] + } + schema: + type: object + properties: + lookup_pepper: + type: string + description: |- + The pepper the client MUST use in hashing identifiers, and MUST + supply to the ``/lookup`` endpoint when performing lookups. + + Servers SHOULD rotate this string often. + algorithms: + type: array + items: + type: string + description: |- + The algorithms the server supports. Must contain at least ``sha256``. + required: ['lookup_pepper', 'algorithms'] + "/lookup": + post: + summary: Look up Matrix User IDs for a set of 3PIDs. + description: |- + Looks up the set of Matrix User IDs which have bound the 3PIDs given, if + bindings are available. Note that the format of the addresses is defined + later in this specification. + operationId: lookupUsersV2 + security: + - accessToken: [] + parameters: + - in: body + name: body + schema: + type: object + properties: + algorithm: + type: string + description: |- + The algorithm the client is using to encode the ``addresses``. This + should be one of the available options from ``/hash_details``. + example: "sha256" + pepper: + type: string + description: |- + The pepper from ``/hash_details``. This is required even when the + ``algorithm`` does not make use of it. + example: "matrixrocks" + addresses: + type: array + items: + type: string + description: |- + The addresses to look up. The format of the entries here depend on + the ``algorithm`` used. Note that queries which have been incorrectly + hashed or formatted will lead to no matches. + example: [ + "4kenr7N9drpCJ4AfalmlGQVsOn3o2RHjkADUpXJWZUc", + "nlo35_T5fzSGZzJApqu8lgIudJvmOQtDaHtr-I4rU7I" + ] + required: ['algorithm', 'pepper', 'addresses'] + responses: + 200: + description: + The associations for any matched ``addresses``. + examples: + application/json: { + "mappings": { + "4kenr7N9drpCJ4AfalmlGQVsOn3o2RHjkADUpXJWZUc": "@alice:example.org" + } + } + schema: + type: object + properties: + mappings: + type: object + description: |- + Any applicable mappings of ``addresses`` to Matrix User IDs. Addresses + which do not have associations will not be included, which can make + this property be an empty object. + title: AssociatedMappings + additionalProperties: + type: string + required: ['mappings'] + 400: + description: + The client's request was invalid in some way. One possible problem could + be the ``pepper`` being invalid after the server has rotated it - this is + presented with the ``M_INVALID_PEPPER`` error code. Clients SHOULD make + a call to ``/hash_details`` to get a new pepper in this scenario, being + careful to avoid retry loops. + examples: + application/json: { + "errcode": "M_INVALID_PEPPER", + "error": "Unknown or invalid pepper - has it been rotated?" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" diff --git a/specification/identity_service_api.rst b/specification/identity_service_api.rst index c92f737b..f389cbc7 100644 --- a/specification/identity_service_api.rst +++ b/specification/identity_service_api.rst @@ -155,6 +155,23 @@ should allow a 3PID to be mapped to a Matrix user identity, but not in the other direction (i.e. one should not be able to get all 3PIDs associated with a Matrix user ID, or get all 3PIDs associated with a 3PID). +Version 1 API deprecation +------------------------- + +.. TODO: Remove this section when the v1 API is removed. + +As described on each of the version 1 endpoints, the v1 API is deprecated in +favour of the v2 API described here. The major difference, with the exception +of a few isolated cases, is that the v2 API requires authentication to ensure +the user has given permission for the identity server to operate on their data. + +The v1 API is planned to be removed from the specification in a future version. + +Clients SHOULD attempt the v2 endpoints first, and if they receive a ``404``, +``400``, or similar error they should try the v1 endpoint or fail the operation. +Clients are strongly encouraged to warn the user of the risks in using the v1 API, +if they are planning on using it. + Web browser clients ------------------- @@ -258,7 +275,134 @@ Association lookup {{lookup_is_http_api}} -.. TODO: TravisR - Add v2 lookup API in future PR +{{v2_lookup_is_http_api}} + +Client behaviour +~~~~~~~~~~~~~~~~ + +.. TODO: Remove this note when v1 is removed completely +.. Note:: + This section only covers the v2 lookup endpoint. The v1 endpoint is described + in isolation above. + +Prior to performing a lookup clients SHOULD make a request to the ``/hash_details`` +endpoint to determine what algorithms the server supports (described in more detail +below). The client then uses this information to form a ``/lookup`` request and +receive known bindings from the server. + +Clients MUST support at least the ``sha256`` algorithm. + +Server behaviour +~~~~~~~~~~~~~~~~ + +.. TODO: Remove this note when v1 is removed completely +.. Note:: + This section only covers the v2 lookup endpoint. The v1 endpoint is described + in isolation above. + +Servers, upon receipt of a ``/lookup`` request, will compare the query against +known bindings it has, hashing the identifiers it knows about as needed to +verify exact matches to the request. + +Servers MUST support at least the ``sha256`` algorithm. + +Algorithms +~~~~~~~~~~ + +Some algorithms are defined as part of the specification, however other formats +can be negotiated between the client and server using ``/hash_details``. + +``sha256`` +++++++++++ + +This algorithm MUST be supported by clients and servers at a minimum. It is +additionally the preferred algorithm for lookups. + +When using this algorithm, the client converts the query first into strings +separated by spaces in the format ``

``. The ```` +is retrieved from ``/hash_details``, the ```` is typically ``email`` or +``msisdn`` (both lowercase), and the ``
`` is the 3PID to search for. +For example, if the client wanted to know about ``alice@example.org``'s bindings, +it would first format the query as ``alice@example.org email ThePepperGoesHere``. + +.. admonition:: Rationale + + Mediums and peppers are appended to the address to prevent a common prefix + for each 3PID, helping prevent attackers from pre-computing the internal state + of the hash function. + +After formatting each query, the string is run through SHA-256 as defined by +`RFC 4634 `_. The resulting bytes are then +encoded using URL-Safe `Unpadded Base64`_ (similar to `room version 4's +event ID format <../../rooms/v4.html#event-ids>`_). + +An example set of queries when using the pepper ``matrixrocks`` would be:: + + "alice@example.com email matrixrocks" -> "4kenr7N9drpCJ4AfalmlGQVsOn3o2RHjkADUpXJWZUc" + "bob@example.com email matrixrocks" -> "LJwSazmv46n0hlMlsb_iYxI0_HXEqy_yj6Jm636cdT8" + "18005552067 msisdn matrixrocks" -> "nlo35_T5fzSGZzJApqu8lgIudJvmOQtDaHtr-I4rU7I" + + +The set of hashes is then given as the ``addresses`` array in ``/lookup``. Note +that the pepper used MUST be supplied as ``pepper`` in the ``/lookup`` request. + +``none`` +++++++++ + +This algorithm performs plaintext lookups on the identity server. Typically this +algorithm should not be used due to the security concerns of unhashed identifiers, +however some scenarios (such as LDAP-backed identity servers) prevent the use of +hashed identifiers. Identity servers (and optionally clients) can use this algorithm +to perform those kinds of lookups. + +Similar to the ``sha256`` algorithm, the client converts the queries into strings +separated by spaces in the format ``
`` - note the lack of ````. +For example, if the client wanted to know about ``alice@example.org``'s bindings, +it would format the query as ``alice@example.org email``. + +The formatted strings are then given as the ``addresses`` in ``/lookup``. Note that +the ``pepper`` is still required, and must be provided to ensure the client has made +an appropriate request to ``/hash_details`` first. + +Security considerations +~~~~~~~~~~~~~~~~~~~~~~~ + +.. Note:: + `MSC2134 `_ has much more + information about the security considerations made for this section of the + specification. This section covers the high-level details for why the specification + is the way it is. + +Typically the lookup endpoint is used when a client has an unknown 3PID it wants to +find a Matrix User ID for. Clients normally do this kind of lookup when inviting new +users to a room or searching a user's address book to find any Matrix users they may +not have discovered yet. Rogue or malicious identity servers could harvest this +unknown information and do nefarious things with it if it were sent in plain text. +In order to protect the privacy of users who might not have a Matrix identifier bound +to their 3PID addresses, the specification attempts to make it difficult to harvest +3PIDs. + +.. admonition:: Rationale + + Hashing identifiers, while not perfect, helps make the effort required to harvest + identifiers significantly higher. Phone numbers in particular are still difficult + to protect with hashing, however hashing is objectively better than not. + + An alternative to hashing would be using bcrypt or similar with many rounds, however + by nature of needing to serve mobile clients and clients on limited hardware the + solution needs be kept relatively lightweight. + +Clients should be cautious of servers not rotating their pepper very often, and +potentially of servers which use a weak pepper - these servers may be attempting to +brute force the identifiers or use rainbow tables to mine the addresses. Similarly, +clients which support the ``none`` algorithm should consider at least warning the user +of the risks in sending identifiers in plain text to the identity server. + +Addresses are still potentially reversable using a calculated rainbow table given +some identifiers, such as phone numbers, common email address domains, and leaked +addresses are easily calculated. For example, phone numbers can have roughly 12 +digits to them, making them an easier target for attack than email addresses. + Establishing associations ------------------------- From b3e2326d8b857fc5c5d944135995c7de5d3e6867 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 9 Sep 2019 19:59:18 -0600 Subject: [PATCH 214/390] changelog --- changelogs/identity_service/newsfragments/2287.new | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/identity_service/newsfragments/2287.new diff --git a/changelogs/identity_service/newsfragments/2287.new b/changelogs/identity_service/newsfragments/2287.new new file mode 100644 index 00000000..7d575bc9 --- /dev/null +++ b/changelogs/identity_service/newsfragments/2287.new @@ -0,0 +1 @@ +Add ``/hash_details`` and a new ``/lookup`` endpoint for performing hashed association lookups. From 8123c4ef0ff499eefbf7ca5becae11511d8835f7 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 9 Sep 2019 22:57:29 -0400 Subject: [PATCH 215/390] additional clarification --- proposals/1219-storing-megolm-keys-serverside.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index f51ed5f9..1103eecf 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -91,8 +91,8 @@ On `M_WRONG_ROOM_KEYS_VERSION` error when trying to `PUT` keys: 2. notify the user that there is a new backup, and display relevant information 3. confirm with user that they want to use the backup (user may want use the backup, to stop backing up keys, or to create a new backup) -4. ensure the backup key is signed by the user's master key, or prompt the user - to enter the recovery key +4. ensure the public part of the recovery key is signed by the user's master + key, or prompt the user to enter the private part of the recovery key On receipt of undecryptable message: From d813b8e12c6e8839d9496c65861ab7ab070bc322 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 10 Sep 2019 09:35:37 -0600 Subject: [PATCH 216/390] Mention M_INVALID_PARAM --- api/identity/v2_lookup.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/identity/v2_lookup.yaml b/api/identity/v2_lookup.yaml index 561700f4..8acafd75 100644 --- a/api/identity/v2_lookup.yaml +++ b/api/identity/v2_lookup.yaml @@ -136,6 +136,9 @@ paths: presented with the ``M_INVALID_PEPPER`` error code. Clients SHOULD make a call to ``/hash_details`` to get a new pepper in this scenario, being careful to avoid retry loops. + + ``M_INVALID_PARAM`` can also be returned to indicate the client supplied + an ``algorithm`` that is unknown to the server. examples: application/json: { "errcode": "M_INVALID_PEPPER", From fdea3e34a864aa55e5fe48f797741e64f6d19c52 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Thu, 12 Sep 2019 13:30:51 +0100 Subject: [PATCH 217/390] wip --- temp | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 temp diff --git a/temp b/temp new file mode 100644 index 00000000..e69de29b From 7096092da901a1e3feda3341c213727f1608a1cb Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Thu, 12 Sep 2019 13:52:52 +0100 Subject: [PATCH 218/390] init --- proposals/2290-separate-threepid-bind-hs.md | 190 ++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 proposals/2290-separate-threepid-bind-hs.md diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md new file mode 100644 index 00000000..a62e5b39 --- /dev/null +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -0,0 +1,190 @@ +# Separate Endpoints for Binding Threepids + +On the Client Server API there is currently a single API for binding a +threepid (an email or a phone number): [POST +/_matrix/client/r0/account/3pid](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid). +Depending on whether the `bind` flag is `true` or `false`, the threepid will +be bound to either a user's account on the homeserver, or both the homeserver +and an identity server. + +A threepid can be bound to an identity server to allow other users to find +their Matrix ID using their email address or phone number. A threepid can +also be bound to a user's account on the homeserver. This allows that +threepid to be used for message notifications, login, password reset, and +other important functions. + +Typically, when using the `POST /_matrix/client/r0/account/3pid` endpoint, +the identity server handles the verification -- either by sending an email to +the email address, or a SMS message to the phone number. Once completed, the +homeserver would check with the identity server that verification had indeed +happened, and if so, the threepid would be bound (again, either to the +homeserver, or the homeserver and identity server simultaneously). + +Now, consider the fact that the identity server used in this process is +provided by the user, using the endpoint's `id_server` parameter. If the user were +to supply a malicious identity server that would immediately answer "yes" to +any threepid validation, then the user could add any threepid to their +account on the homeserver (which is likely not something homeserver admins want). + +To solve this problem, we propose adding a second endpoint that is only used +for binding to an identity server of the user's choice. This endpoint will +not bind the threepid to the user's account on the homeserver, only on the +identity server. + +In addition, the existing binding endpoint will lose the ability to bind +threepids to an identity server, by removing its `bind` flag. Instead, it +will solely be used to bind to the user's account on the homeserver. + +To be clear, the above issue is not a long-standing security issue. Indeed it +is not a problem in any released version of Synapse, as Synapse keeps a list +of "trusted identity servers" that acts a whitelist for what identity servers +a user can specify. + +Synapse is soon to lose this whitelist however, as part of lessening the +reliance of homeservers on identity servers. This cannot be done while the +homeserver is still trusting an identity server for validation of threepids. +If the endpoints are split, the homeserver will handle the validation of +threepids being added to user accounts, and identity servers will validate +threepids being added to their own database. + +One may question why clients don't just contact an identity server directly +to bind a threepid, bypassing the implications of binding through a +homeserver. While this will work, binds should still occur through a +homeserver such that the homeserver can keep track of which binds were made, +which is important when a user wishes to deactivate their account (and remove +all of their bindings made on different identity servers). + +A bind could be made on an identity server, which could then tell the +homeserver that a validation occured, but then there are security +considerations about how to authenticate an identity server in that instance +(and prevent people pretending to be identity servers and telling homeservers +about hundreds of fake binds to a user's account). + +This MSC obseletes +[MSC2229](https://github.com/matrix-org/matrix-doc/pull/2229), which dealt +with changing the rules of the `bind` flag. Since this flag is being removed, +the MSC is no longer relevant. + +## Proposal + +A new endpoint will be added to the Client Server API: `POST /_matrix/client/r0/account/3pid/identity/bind`, and requires authentication. + +The endpoint definition is the same as `POST +/_matrix/client/r0/account/3pid`, minus the `bind` flag. + +An example of binding a threepid to an identity server with this new endpoint: + +First the client must request the threepid be validated by its chosen identity server. + +``` +POST https://identity.server/_matrix/identity/v2/validate/email/requestToken + +{ + "client_secret": "don'tT3ll", + "email": "bob@example.com", + "send_attempt": 1 +} +``` + +Once an email has been sent, the user clicks the link in the email, which +notifies the identity server that the email has been verified. + +Next, the client completes the bind by calling the new endpoint on the homeserver: + +``` +POST https://home.server/_matrix/client/r0/account/3pid/identity/bind + +{ + "three_pid_creds": { + "id_server": "example.org", + "id_access_token": "abc123_OpaqueString", + "sid": "abc123987", + "client_secret": "don'tT3ll" + } +} +``` + +The homeserver will then make a bind request on behalf of the user to the +specified identity server. The homeserver will record if the bind was +successful and notify the user. + +And for completeness, here is an example of binding a threepid to the +homeserver only, using the old endpoint: + +The homeserver is validating the threepid in this instance, so the client +must use the `/requestToken` endpoint of the homeserver: + +``` +POST https://home.server/_matrix/client/r0/account/3pid/email/requestToken + +{ + "client_secret": "don'tT3ll", + "email": "bob@example.com", + "send_attempt": 1, +} +``` + +Once an email has been sent, the user clicks the link in the email, which +notifies the homeserver that the email has been verified. + +The client then sends a request to the old endpoint to bind the threepid to +user's account. + +``` +POST /_matrix/client/r0/account/3pid + +{ + "three_pid_creds": { + "sid": "abc123987", + "client_secret": "don'tT3ll" + } +} +``` + +The threepid will then be bound to the user's account. + +Users will be able to perform binds to an identity server for a threepid even if that threepid has not been bound to the user's account on the homeserver before. + +The achieve the above flow, some changes need to be made to existing +endpoints as well. This MSC requests that the `id_server` and +`id_access_token` parameters be removed from the Client-Server API's [POST +/_matrix/client/r0/account/3pid/email/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-email-requesttoken) +endpoint, as this endpoint is now only intended for the homeserver to send +emails from. Additionally, the same parameters will be removed from the [POST +/_matrix/client/r0/account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid)endpoint's +`three_pid_creds` parameter as an identity server is no longer required to +perform verification. + +This MSC also requests that the text "It is imperative that the homeserver +keep a list of trusted Identity Servers and only proxies to those that it +trusts." be removed from all parts of the spec, as the homeserver should no +longer need to trust any identity servers. + +## Tradeoffs + +It may be possible to reduce the two calls per flow into a single endpoint, +but the current asynchronous approach makes it easy for a client to send a +request, go offline, have the threepid be validated, and then come online +again to finalize the validation afterwards. + +## Backwards compatibility + +TODO + +## Security considerations + +Reducing the homeserver's trust in identity servers should be a boost to security and improve decentralisation in the Matrix ecosystem to boot. + +Caution should be taken for homeserver developers to be sure not to continue +to use user-provided identity servers for any sensitive tasks once it removes +the concept of a trusted identity server. + +## Conclusion + +This MSC helps reduce the homeserver's trust in an identity server even +further to the point where it is only used for binding addresses for lookup - +which was the original intention of the Identity Service API. + +Additionally, by clearly separating the threepid bind endpoint into two +endpoints that each have a clear intention, the concept of threepid binding +becomes a lot easier to reason about. From f5b10c689fc0f83705f161dcae20ce4be9c79715 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Thu, 12 Sep 2019 15:55:50 +0100 Subject: [PATCH 219/390] cleanup --- proposals/2290-separate-threepid-bind-hs.md | 68 +++++++++++---------- temp | 0 2 files changed, 37 insertions(+), 31 deletions(-) delete mode 100644 temp diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index a62e5b39..95e90192 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -1,22 +1,22 @@ # Separate Endpoints for Binding Threepids -On the Client Server API there is currently a single API for binding a +On the Client Server API there is currently a single endpoint for binding a threepid (an email or a phone number): [POST /_matrix/client/r0/account/3pid](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid). Depending on whether the `bind` flag is `true` or `false`, the threepid will be bound to either a user's account on the homeserver, or both the homeserver and an identity server. -A threepid can be bound to an identity server to allow other users to find +For context a threepid can be bound to an identity server to allow other users to find their Matrix ID using their email address or phone number. A threepid can -also be bound to a user's account on the homeserver. This allows that +also be bound to a user's account on the homeserver. This allows the threepid to be used for message notifications, login, password reset, and other important functions. Typically, when using the `POST /_matrix/client/r0/account/3pid` endpoint, the identity server handles the verification -- either by sending an email to -the email address, or a SMS message to the phone number. Once completed, the -homeserver would check with the identity server that verification had indeed +an email address, or a SMS message to a phone number. Once completed, the +homeserver will check with the identity server that verification had indeed happened, and if so, the threepid would be bound (again, either to the homeserver, or the homeserver and identity server simultaneously). @@ -28,7 +28,7 @@ account on the homeserver (which is likely not something homeserver admins want) To solve this problem, we propose adding a second endpoint that is only used for binding to an identity server of the user's choice. This endpoint will -not bind the threepid to the user's account on the homeserver, only on the +not bind the threepid to the user's account on the homeserver, only the identity server. In addition, the existing binding endpoint will lose the ability to bind @@ -67,12 +67,12 @@ the MSC is no longer relevant. ## Proposal -A new endpoint will be added to the Client Server API: `POST /_matrix/client/r0/account/3pid/identity/bind`, and requires authentication. - -The endpoint definition is the same as `POST +A new endpoint will be added to the Client Server API: `POST +/_matrix/client/r0/account/3pid/identity/bind`, and will require +authentication. The endpoint definition is the same as `POST /_matrix/client/r0/account/3pid`, minus the `bind` flag. -An example of binding a threepid to an identity server with this new endpoint: +An example of binding a threepid to **an identity server only** with this new endpoint is as follows: First the client must request the threepid be validated by its chosen identity server. @@ -104,11 +104,15 @@ POST https://home.server/_matrix/client/r0/account/3pid/identity/bind } ``` -The homeserver will then make a bind request on behalf of the user to the -specified identity server. The homeserver will record if the bind was -successful and notify the user. +The homeserver will then make a bind request to the specified identity server +on behalf of the user. The homeserver will record if the bind was successful +and notify the user. + +The threepid has now been binded on the user's identity server without +causing that threepid to be used for password resets or any other +homeserver-related functions. -And for completeness, here is an example of binding a threepid to the +For completeness, here is an example of binding a threepid to the homeserver only, using the old endpoint: The homeserver is validating the threepid in this instance, so the client @@ -125,10 +129,10 @@ POST https://home.server/_matrix/client/r0/account/3pid/email/requestToken ``` Once an email has been sent, the user clicks the link in the email, which -notifies the homeserver that the email has been verified. +notifies the homeserver that the threepid has been verified. -The client then sends a request to the old endpoint to bind the threepid to -user's account. +The client then sends a request to the old endpoint on the homeserver to bind +the threepid to user's account. ``` POST /_matrix/client/r0/account/3pid @@ -143,15 +147,16 @@ POST /_matrix/client/r0/account/3pid The threepid will then be bound to the user's account. -Users will be able to perform binds to an identity server for a threepid even if that threepid has not been bound to the user's account on the homeserver before. - -The achieve the above flow, some changes need to be made to existing -endpoints as well. This MSC requests that the `id_server` and -`id_access_token` parameters be removed from the Client-Server API's [POST +The achieve the above flows, some changes need to be made to existing +endpoints. This MSC requests that the `id_server` and `id_access_token` +parameters be removed from the Client-Server API's [POST /_matrix/client/r0/account/3pid/email/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-email-requesttoken) -endpoint, as this endpoint is now only intended for the homeserver to send -emails from. Additionally, the same parameters will be removed from the [POST -/_matrix/client/r0/account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid)endpoint's +and [POST +/_matrix/client/r0/account/3pid/msisdn/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-msisdn-requesttoken) +endpoints, as these endpoints are now only intended for the homeserver to +send validation requests from. Additionally, the same parameters will be +removed from the [POST +/_matrix/client/r0/account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid) endpoint's `three_pid_creds` parameter as an identity server is no longer required to perform verification. @@ -173,18 +178,19 @@ TODO ## Security considerations -Reducing the homeserver's trust in identity servers should be a boost to security and improve decentralisation in the Matrix ecosystem to boot. +Reducing the homeserver's trust in identity servers should be a boost to +security and improve decentralisation in the Matrix ecosystem to boot. Caution should be taken for homeserver developers to be sure not to continue to use user-provided identity servers for any sensitive tasks once it removes -the concept of a trusted identity server. +the concept of a trusted identity server list. ## Conclusion -This MSC helps reduce the homeserver's trust in an identity server even +This MSC helps to minimize the homeserver's trust in an identity server even further to the point where it is only used for binding addresses for lookup - -which was the original intention of the Identity Service API. +which was the original intention of identity servers to begin with. Additionally, by clearly separating the threepid bind endpoint into two -endpoints that each have a clear intention, the concept of threepid binding -becomes a lot easier to reason about. +endpoints that each have a clear intention, the concept of attaching +threepids to a Matrix user becomes a lot easier to reason about. diff --git a/temp b/temp deleted file mode 100644 index e69de29b..00000000 From 5193c319e7b0250d5a6a74e66d04a9f4cfaa76f1 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 13 Sep 2019 11:49:31 +0100 Subject: [PATCH 220/390] Cleaner API endpoints --- proposals/2290-separate-threepid-bind-hs.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index 95e90192..69ed4827 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -2,7 +2,7 @@ On the Client Server API there is currently a single endpoint for binding a threepid (an email or a phone number): [POST -/_matrix/client/r0/account/3pid](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid). +/account/3pid](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid). Depending on whether the `bind` flag is `true` or `false`, the threepid will be bound to either a user's account on the homeserver, or both the homeserver and an identity server. @@ -13,7 +13,7 @@ also be bound to a user's account on the homeserver. This allows the threepid to be used for message notifications, login, password reset, and other important functions. -Typically, when using the `POST /_matrix/client/r0/account/3pid` endpoint, +Typically, when using the `/account/3pid` endpoint, the identity server handles the verification -- either by sending an email to an email address, or a SMS message to a phone number. Once completed, the homeserver will check with the identity server that verification had indeed @@ -68,9 +68,10 @@ the MSC is no longer relevant. ## Proposal A new endpoint will be added to the Client Server API: `POST -/_matrix/client/r0/account/3pid/identity/bind`, and will require -authentication. The endpoint definition is the same as `POST -/_matrix/client/r0/account/3pid`, minus the `bind` flag. +/account/3pid/identity/bind`, and will require authentication. The endpoint +definition is the same as [POST +/account/3pid](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid), +minus the `bind` flag. An example of binding a threepid to **an identity server only** with this new endpoint is as follows: @@ -150,13 +151,13 @@ The threepid will then be bound to the user's account. The achieve the above flows, some changes need to be made to existing endpoints. This MSC requests that the `id_server` and `id_access_token` parameters be removed from the Client-Server API's [POST -/_matrix/client/r0/account/3pid/email/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-email-requesttoken) +/account/3pid/email/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-email-requesttoken) and [POST -/_matrix/client/r0/account/3pid/msisdn/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-msisdn-requesttoken) +/account/3pid/msisdn/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-msisdn-requesttoken) endpoints, as these endpoints are now only intended for the homeserver to send validation requests from. Additionally, the same parameters will be removed from the [POST -/_matrix/client/r0/account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid) endpoint's +/account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid) endpoint's `three_pid_creds` parameter as an identity server is no longer required to perform verification. From cb7c072edb042ce02046ad4fdaa1af112e85f066 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Mon, 16 Sep 2019 14:42:21 +0100 Subject: [PATCH 221/390] Two new endpoints instead of one --- proposals/2229-rebind-existing-3pid.md | 2 + proposals/2290-separate-threepid-bind-hs.md | 92 ++++++++++++--------- 2 files changed, 55 insertions(+), 39 deletions(-) diff --git a/proposals/2229-rebind-existing-3pid.md b/proposals/2229-rebind-existing-3pid.md index 83883c78..4c709326 100644 --- a/proposals/2229-rebind-existing-3pid.md +++ b/proposals/2229-rebind-existing-3pid.md @@ -1,5 +1,7 @@ # Allowing 3PID Owners to Rebind +## Note: This MSC has been made obselete by MSC2290. + ``` 3PID noun diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index 69ed4827..0d2b9f2d 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -2,7 +2,7 @@ On the Client Server API there is currently a single endpoint for binding a threepid (an email or a phone number): [POST -/account/3pid](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid). +/account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid). Depending on whether the `bind` flag is `true` or `false`, the threepid will be bound to either a user's account on the homeserver, or both the homeserver and an identity server. @@ -26,26 +26,23 @@ to supply a malicious identity server that would immediately answer "yes" to any threepid validation, then the user could add any threepid to their account on the homeserver (which is likely not something homeserver admins want). -To solve this problem, we propose adding a second endpoint that is only used -for binding to an identity server of the user's choice. This endpoint will -not bind the threepid to the user's account on the homeserver, only the -identity server. +To be clear, this is not a long-standing security issue. It is not a problem +in any released version of Synapse, as Synapse keeps a list of "trusted +identity servers" that acts a whitelist for what identity servers a user can +specify. -In addition, the existing binding endpoint will lose the ability to bind -threepids to an identity server, by removing its `bind` flag. Instead, it -will solely be used to bind to the user's account on the homeserver. +The requirement for homeservers to keep this whitelist is soon to be lost +however, as part of lessening the reliance of homeservers on identity +servers. This cannot be done while the homeserver is still trusting an +identity server for validation of threepids. If the endpoints are split, the +homeserver will handle the validation of threepids being added to user +accounts, and identity servers will validate threepids being added to their +own database. -To be clear, the above issue is not a long-standing security issue. Indeed it -is not a problem in any released version of Synapse, as Synapse keeps a list -of "trusted identity servers" that acts a whitelist for what identity servers -a user can specify. - -Synapse is soon to lose this whitelist however, as part of lessening the -reliance of homeservers on identity servers. This cannot be done while the -homeserver is still trusting an identity server for validation of threepids. -If the endpoints are split, the homeserver will handle the validation of -threepids being added to user accounts, and identity servers will validate -threepids being added to their own database. +To solve this problem, we propose adding two new endpoints. One that is only +used for binding to user's account, and another that is only for binding to +an identity server of the user's choice. The existing binding endpoint will +be deprecated. One may question why clients don't just contact an identity server directly to bind a threepid, bypassing the implications of binding through a @@ -62,18 +59,21 @@ about hundreds of fake binds to a user's account). This MSC obseletes [MSC2229](https://github.com/matrix-org/matrix-doc/pull/2229), which dealt -with changing the rules of the `bind` flag. Since this flag is being removed, -the MSC is no longer relevant. +with changing the rules of the `bind` flag on the original endpoint. Since +that endpoint is being deprecated, the MSC is no longer relevant. ## Proposal -A new endpoint will be added to the Client Server API: `POST -/account/3pid/identity/bind`, and will require authentication. The endpoint -definition is the same as [POST -/account/3pid](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid), -minus the `bind` flag. +Two new endpoints will be added to the Client Server API: `POST +/account/3pid/bind` and `POST /account/3pid/add`. Both will require +authentication. The request parameters of `POST /account/3pid/bind` are the +same as [POST +/account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid), +minus the `bind` flag. The request parameters of `POST /account/3pid/add` +will simply consist of a JSON body containing `client_secret` and `sid`. -An example of binding a threepid to **an identity server only** with this new endpoint is as follows: +An example of binding a threepid to **an identity server only** with this new +endpoint is as follows: First the client must request the threepid be validated by its chosen identity server. @@ -93,7 +93,7 @@ notifies the identity server that the email has been verified. Next, the client completes the bind by calling the new endpoint on the homeserver: ``` -POST https://home.server/_matrix/client/r0/account/3pid/identity/bind +POST https://home.server/_matrix/client/r0/account/3pid/bind { "three_pid_creds": { @@ -109,8 +109,8 @@ The homeserver will then make a bind request to the specified identity server on behalf of the user. The homeserver will record if the bind was successful and notify the user. -The threepid has now been binded on the user's identity server without -causing that threepid to be used for password resets or any other +The threepid has now been binded on the user's requested identity server +without causing that threepid to be used for password resets or any other homeserver-related functions. For completeness, here is an example of binding a threepid to the @@ -136,7 +136,7 @@ The client then sends a request to the old endpoint on the homeserver to bind the threepid to user's account. ``` -POST /_matrix/client/r0/account/3pid +POST /_matrix/client/r0/account/3pid/bind { "three_pid_creds": { @@ -151,15 +151,20 @@ The threepid will then be bound to the user's account. The achieve the above flows, some changes need to be made to existing endpoints. This MSC requests that the `id_server` and `id_access_token` parameters be removed from the Client-Server API's [POST -/account/3pid/email/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-email-requesttoken) +/account/3pid/email/requestToken](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid-email-requesttoken) and [POST -/account/3pid/msisdn/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-msisdn-requesttoken) +/account/3pid/msisdn/requestToken](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid-msisdn-requesttoken) endpoints, as these endpoints are now only intended for the homeserver to -send validation requests from. Additionally, the same parameters will be -removed from the [POST -/account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid) endpoint's -`three_pid_creds` parameter as an identity server is no longer required to -perform verification. +send validation requests from. + +Additionally, the [POST +/account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid) +endpoint is deprecated as the two new endpoints replace its functionality. +The `bind` endpoint will also be removed, with the endpoint functioning as if +`bind` was `false`. Allowing an endpoint to add a threepid to both the +identity server and homeserver at the same time requires one to trust the +other, which is the exact behaviour we're trying to eliminate. Doing this +also helps backward compatibility, as explained below. This MSC also requests that the text "It is imperative that the homeserver keep a list of trusted Identity Servers and only proxies to those that it @@ -175,7 +180,16 @@ again to finalize the validation afterwards. ## Backwards compatibility -TODO +Old matrix clients will continue to use the `/account/3pid` endpoint. As this +MSC removes the `bind` parameter and forces `/account/3pid` calls to act as +if `bind` was set to `false`, old clients will still be able to add 3pids, +but they will only be added to the homeserver, not the identity server. New +homeservers will ignore any `id_server` information passed to this endpoint. + +New matrix clients running with old homeservers should try their desired +endpoint (either `/account/3pid/add` or `/account/3pid/bind`) and on +receiving a HTTP `404` error code, should either attempt to use +`/account/3pid` with the `bind` parameter or give up, at their discretion. ## Security considerations From 5b259bfb52e38ecf849ab1ce064efc449a37281f Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Mon, 16 Sep 2019 14:47:38 +0100 Subject: [PATCH 222/390] Fix homeserver binding example --- proposals/2290-separate-threepid-bind-hs.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index 0d2b9f2d..17a04420 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -31,13 +31,12 @@ in any released version of Synapse, as Synapse keeps a list of "trusted identity servers" that acts a whitelist for what identity servers a user can specify. -The requirement for homeservers to keep this whitelist is soon to be lost -however, as part of lessening the reliance of homeservers on identity -servers. This cannot be done while the homeserver is still trusting an -identity server for validation of threepids. If the endpoints are split, the -homeserver will handle the validation of threepids being added to user -accounts, and identity servers will validate threepids being added to their -own database. +Synapse is soon to lose this whitelist however, as part of lessening the +reliance of homeservers on identity servers. This cannot be done while the +homeserver is still trusting an identity server for validation of threepids. +If the endpoints are split, the homeserver will handle the validation of +threepids being added to user accounts, and identity servers will validate +threepids being added to their own database. To solve this problem, we propose adding two new endpoints. One that is only used for binding to user's account, and another that is only for binding to @@ -139,10 +138,8 @@ the threepid to user's account. POST /_matrix/client/r0/account/3pid/bind { - "three_pid_creds": { - "sid": "abc123987", - "client_secret": "don'tT3ll" - } + "sid": "abc123987", + "client_secret": "don'tT3ll" } ``` From 1fc1e3c6cecf7c49753316958696ac5065f45fec Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Mon, 16 Sep 2019 14:49:30 +0100 Subject: [PATCH 223/390] run on sentence --- proposals/2290-separate-threepid-bind-hs.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index 17a04420..5bd5129b 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -135,7 +135,7 @@ The client then sends a request to the old endpoint on the homeserver to bind the threepid to user's account. ``` -POST /_matrix/client/r0/account/3pid/bind +POST https://home.server/_matrix/client/r0/account/3pid/add { "sid": "abc123987", @@ -177,11 +177,11 @@ again to finalize the validation afterwards. ## Backwards compatibility -Old matrix clients will continue to use the `/account/3pid` endpoint. As this +Old matrix clients will continue to use the `/account/3pid` endpoint. This MSC removes the `bind` parameter and forces `/account/3pid` calls to act as -if `bind` was set to `false`, old clients will still be able to add 3pids, -but they will only be added to the homeserver, not the identity server. New -homeservers will ignore any `id_server` information passed to this endpoint. +if `bind` was set to `false`. Old clients will still be able to add 3pids to +the homeserver, but not the identity server. New homeservers must ignore any +`id_server` information passed to this endpoint. New matrix clients running with old homeservers should try their desired endpoint (either `/account/3pid/add` or `/account/3pid/bind`) and on From 196f27efb2ebff9f64e3ebef5c3aa387edc49dfb Mon Sep 17 00:00:00 2001 From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Date: Mon, 16 Sep 2019 15:22:05 +0100 Subject: [PATCH 224/390] Update proposals/2290-separate-threepid-bind-hs.md Co-Authored-By: Matthew Hodgson --- proposals/2290-separate-threepid-bind-hs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index 5bd5129b..d692207d 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -145,7 +145,7 @@ POST https://home.server/_matrix/client/r0/account/3pid/add The threepid will then be bound to the user's account. -The achieve the above flows, some changes need to be made to existing +To achieve the above flows, some changes need to be made to existing endpoints. This MSC requests that the `id_server` and `id_access_token` parameters be removed from the Client-Server API's [POST /account/3pid/email/requestToken](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid-email-requesttoken) From 7b656e9013adeefb4fd70d84b41aed93edc85d1b Mon Sep 17 00:00:00 2001 From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Date: Mon, 16 Sep 2019 15:24:43 +0100 Subject: [PATCH 225/390] Update proposals/2290-separate-threepid-bind-hs.md Co-Authored-By: Matthew Hodgson --- proposals/2290-separate-threepid-bind-hs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index d692207d..6e34f954 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -108,7 +108,7 @@ The homeserver will then make a bind request to the specified identity server on behalf of the user. The homeserver will record if the bind was successful and notify the user. -The threepid has now been binded on the user's requested identity server +The threepid has now been bound on the user's requested identity server without causing that threepid to be used for password resets or any other homeserver-related functions. From f36ed9a27195233c12edb6271207f09935735e01 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Mon, 16 Sep 2019 15:26:07 +0100 Subject: [PATCH 226/390] typos --- proposals/2290-separate-threepid-bind-hs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index 6e34f954..6187efdd 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -131,8 +131,8 @@ POST https://home.server/_matrix/client/r0/account/3pid/email/requestToken Once an email has been sent, the user clicks the link in the email, which notifies the homeserver that the threepid has been verified. -The client then sends a request to the old endpoint on the homeserver to bind -the threepid to user's account. +The client then sends a request to the endpoint on the homeserver to bind +the threepid to a user's account. ``` POST https://home.server/_matrix/client/r0/account/3pid/add From f06ba491fefc9168a869d6ae09734afdded160ad Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Mon, 16 Sep 2019 15:49:31 +0100 Subject: [PATCH 227/390] Assign meaning to bind and add --- proposals/2290-separate-threepid-bind-hs.md | 61 +++++++++++---------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index 6187efdd..de844e47 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -7,18 +7,20 @@ Depending on whether the `bind` flag is `true` or `false`, the threepid will be bound to either a user's account on the homeserver, or both the homeserver and an identity server. -For context a threepid can be bound to an identity server to allow other users to find -their Matrix ID using their email address or phone number. A threepid can -also be bound to a user's account on the homeserver. This allows the -threepid to be used for message notifications, login, password reset, and -other important functions. - -Typically, when using the `/account/3pid` endpoint, -the identity server handles the verification -- either by sending an email to -an email address, or a SMS message to a phone number. Once completed, the -homeserver will check with the identity server that verification had indeed -happened, and if so, the threepid would be bound (again, either to the -homeserver, or the homeserver and identity server simultaneously). +For context a threepid can be bound to an identity server to allow other +users to find their Matrix ID using their email address or phone number. A +threepid can also be added to a user's account on the homeserver. This allows +the threepid to be used for message notifications, login, password reset, and +other important functions. We use the term `add` when talking about adding a +threepid to a homeserver, and `bind` when binding a threepid to an identity +server. This terminology will be used throughout the rest of this proposal. + +Typically, when using the `/account/3pid` endpoint, the identity server +handles the verification -- either by sending an email to an email address, +or a SMS message to a phone number. Once completed, the homeserver will check +with the identity server that verification had indeed happened, and if so, +the threepid would be either added to the homeserver, or added to the +homeserver and bound to the identity server simultaneously. Now, consider the fact that the identity server used in this process is provided by the user, using the endpoint's `id_server` parameter. If the user were @@ -36,12 +38,13 @@ reliance of homeservers on identity servers. This cannot be done while the homeserver is still trusting an identity server for validation of threepids. If the endpoints are split, the homeserver will handle the validation of threepids being added to user accounts, and identity servers will validate -threepids being added to their own database. +threepids being bound to themselves. -To solve this problem, we propose adding two new endpoints. One that is only -used for binding to user's account, and another that is only for binding to -an identity server of the user's choice. The existing binding endpoint will -be deprecated. +To solve this problem, we propose adding two new endpoints. `POST +/account/3pid/add` that is only used for adding to user's account on a +homeserver, and `POST /account/3pid/bind` that is only for binding to an +identity server of the user's choice. The existing binding endpoint (`POST +/account/3pid`) will be deprecated. One may question why clients don't just contact an identity server directly to bind a threepid, bypassing the implications of binding through a @@ -50,11 +53,11 @@ homeserver such that the homeserver can keep track of which binds were made, which is important when a user wishes to deactivate their account (and remove all of their bindings made on different identity servers). -A bind could be made on an identity server, which could then tell the -homeserver that a validation occured, but then there are security +A verification could occur on an identity server, which could then tell the +homeserver that a validation happened, but then there are security considerations about how to authenticate an identity server in that instance (and prevent people pretending to be identity servers and telling homeservers -about hundreds of fake binds to a user's account). +about hundreds of fake threepid additions to a user's account). This MSC obseletes [MSC2229](https://github.com/matrix-org/matrix-doc/pull/2229), which dealt @@ -71,8 +74,8 @@ same as [POST minus the `bind` flag. The request parameters of `POST /account/3pid/add` will simply consist of a JSON body containing `client_secret` and `sid`. -An example of binding a threepid to **an identity server only** with this new -endpoint is as follows: +An example of binding a threepid to an identity server with this new endpoint +is as follows: First the client must request the threepid be validated by its chosen identity server. @@ -112,8 +115,8 @@ The threepid has now been bound on the user's requested identity server without causing that threepid to be used for password resets or any other homeserver-related functions. -For completeness, here is an example of binding a threepid to the -homeserver only, using the old endpoint: +For completeness, here is an example of adding a threepid to the homeserver +only, using the `/account/3pid/add` endpoint: The homeserver is validating the threepid in this instance, so the client must use the `/requestToken` endpoint of the homeserver: @@ -131,7 +134,7 @@ POST https://home.server/_matrix/client/r0/account/3pid/email/requestToken Once an email has been sent, the user clicks the link in the email, which notifies the homeserver that the threepid has been verified. -The client then sends a request to the endpoint on the homeserver to bind +The client then sends a request to the endpoint on the homeserver to add the threepid to a user's account. ``` @@ -143,7 +146,7 @@ POST https://home.server/_matrix/client/r0/account/3pid/add } ``` -The threepid will then be bound to the user's account. +The threepid has now been added to the user's account. To achieve the above flows, some changes need to be made to existing endpoints. This MSC requests that the `id_server` and `id_access_token` @@ -180,8 +183,8 @@ again to finalize the validation afterwards. Old matrix clients will continue to use the `/account/3pid` endpoint. This MSC removes the `bind` parameter and forces `/account/3pid` calls to act as if `bind` was set to `false`. Old clients will still be able to add 3pids to -the homeserver, but not the identity server. New homeservers must ignore any -`id_server` information passed to this endpoint. +the homeserver, but not bind to the identity server. New homeservers must +ignore any `id_server` information passed to this endpoint. New matrix clients running with old homeservers should try their desired endpoint (either `/account/3pid/add` or `/account/3pid/bind`) and on @@ -203,6 +206,6 @@ This MSC helps to minimize the homeserver's trust in an identity server even further to the point where it is only used for binding addresses for lookup - which was the original intention of identity servers to begin with. -Additionally, by clearly separating the threepid bind endpoint into two +Additionally, by clearly separating the original threepid endpoint into two endpoints that each have a clear intention, the concept of attaching threepids to a Matrix user becomes a lot easier to reason about. From 4bc005ac84fa632d6ab933378445142206b6aa65 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Mon, 16 Sep 2019 16:27:16 +0100 Subject: [PATCH 228/390] Remove threepid explanation --- proposals/2290-separate-threepid-bind-hs.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index de844e47..0da60797 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -5,15 +5,10 @@ threepid (an email or a phone number): [POST /account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid). Depending on whether the `bind` flag is `true` or `false`, the threepid will be bound to either a user's account on the homeserver, or both the homeserver -and an identity server. - -For context a threepid can be bound to an identity server to allow other -users to find their Matrix ID using their email address or phone number. A -threepid can also be added to a user's account on the homeserver. This allows -the threepid to be used for message notifications, login, password reset, and -other important functions. We use the term `add` when talking about adding a -threepid to a homeserver, and `bind` when binding a threepid to an identity -server. This terminology will be used throughout the rest of this proposal. +and an identity server. Note that we use the term `add` when talking about +adding a threepid to a homeserver, and `bind` when binding a threepid to an +identity server. This terminology will be used throughout the rest of this +proposal. Typically, when using the `/account/3pid` endpoint, the identity server handles the verification -- either by sending an email to an email address, From af2467606e6a67d418414794df4ecc7a9f9f80b2 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Mon, 16 Sep 2019 18:01:21 +0100 Subject: [PATCH 229/390] parameter, not endpoint --- proposals/2290-separate-threepid-bind-hs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index 0da60797..a28f6d93 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -155,7 +155,7 @@ send validation requests from. Additionally, the [POST /account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid) endpoint is deprecated as the two new endpoints replace its functionality. -The `bind` endpoint will also be removed, with the endpoint functioning as if +The `bind` parameter will also be removed, with the endpoint functioning as if `bind` was `false`. Allowing an endpoint to add a threepid to both the identity server and homeserver at the same time requires one to trust the other, which is the exact behaviour we're trying to eliminate. Doing this From 2a5531075460547b02c6d5c9916f21d8a6d5137a Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Tue, 17 Sep 2019 11:43:56 +0100 Subject: [PATCH 230/390] Clarify why MSC2229 was made obselete --- proposals/2229-rebind-existing-3pid.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/proposals/2229-rebind-existing-3pid.md b/proposals/2229-rebind-existing-3pid.md index 4c709326..1bfb5106 100644 --- a/proposals/2229-rebind-existing-3pid.md +++ b/proposals/2229-rebind-existing-3pid.md @@ -2,6 +2,15 @@ ## Note: This MSC has been made obselete by MSC2290. +MSC2290 provides two separate API endpoints, one for adding a 3PID to the +homeserver, and another for binding to an identity server. These new +endpoints will allow the homeserver to enforce rules on emails that already +exist on the homeserver, only when modifying homeserver email, while only +needing to forward requests when binding to an identity server. This removes +the problem MSC2229 is trying to solve, and it is thus made obselete. + +--- + ``` 3PID noun From c57250b3935c675e3cb0d17c973be50bc32cbd5d Mon Sep 17 00:00:00 2001 From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Date: Tue, 17 Sep 2019 11:48:50 +0100 Subject: [PATCH 231/390] Apply suggestions from code review Co-Authored-By: Travis Ralston --- proposals/2290-separate-threepid-bind-hs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index a28f6d93..8999d143 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -35,7 +35,7 @@ If the endpoints are split, the homeserver will handle the validation of threepids being added to user accounts, and identity servers will validate threepids being bound to themselves. -To solve this problem, we propose adding two new endpoints. `POST +To solve this problem, it is proposed to add two new endpoints. `POST /account/3pid/add` that is only used for adding to user's account on a homeserver, and `POST /account/3pid/bind` that is only for binding to an identity server of the user's choice. The existing binding endpoint (`POST @@ -155,7 +155,7 @@ send validation requests from. Additionally, the [POST /account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid) endpoint is deprecated as the two new endpoints replace its functionality. -The `bind` parameter will also be removed, with the endpoint functioning as if +The `bind` parameter is to be removed, with the endpoint functioning as if `bind` was `false`. Allowing an endpoint to add a threepid to both the identity server and homeserver at the same time requires one to trust the other, which is the exact behaviour we're trying to eliminate. Doing this From 0b67f34578c29e016195dca487a1d949b8561545 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Tue, 17 Sep 2019 12:10:32 +0100 Subject: [PATCH 232/390] Address review comments --- proposals/2290-separate-threepid-bind-hs.md | 117 +++++++++----------- 1 file changed, 53 insertions(+), 64 deletions(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index a28f6d93..644be55b 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -1,8 +1,15 @@ # Separate Endpoints for Binding Threepids +*Note: This MSC obseletes +[MSC2229](https://github.com/matrix-org/matrix-doc/pull/2229), which dealt +with changing the rules of the `bind` flag on [POST +/account/3pid](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid). +That endpoint is being deprecated as part of this MSC, thus MSC2229 is no +longer relevant.* + On the Client Server API there is currently a single endpoint for binding a threepid (an email or a phone number): [POST -/account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid). +/account/3pid](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid). Depending on whether the `bind` flag is `true` or `false`, the threepid will be bound to either a user's account on the homeserver, or both the homeserver and an identity server. Note that we use the term `add` when talking about @@ -28,46 +35,30 @@ in any released version of Synapse, as Synapse keeps a list of "trusted identity servers" that acts a whitelist for what identity servers a user can specify. -Synapse is soon to lose this whitelist however, as part of lessening the -reliance of homeservers on identity servers. This cannot be done while the -homeserver is still trusting an identity server for validation of threepids. -If the endpoints are split, the homeserver will handle the validation of -threepids being added to user accounts, and identity servers will validate -threepids being bound to themselves. - -To solve this problem, we propose adding two new endpoints. `POST -/account/3pid/add` that is only used for adding to user's account on a -homeserver, and `POST /account/3pid/bind` that is only for binding to an -identity server of the user's choice. The existing binding endpoint (`POST -/account/3pid`) will be deprecated. - -One may question why clients don't just contact an identity server directly -to bind a threepid, bypassing the implications of binding through a -homeserver. While this will work, binds should still occur through a -homeserver such that the homeserver can keep track of which binds were made, -which is important when a user wishes to deactivate their account (and remove -all of their bindings made on different identity servers). - -A verification could occur on an identity server, which could then tell the -homeserver that a validation happened, but then there are security -considerations about how to authenticate an identity server in that instance -(and prevent people pretending to be identity servers and telling homeservers -about hundreds of fake threepid additions to a user's account). - -This MSC obseletes -[MSC2229](https://github.com/matrix-org/matrix-doc/pull/2229), which dealt -with changing the rules of the `bind` flag on the original endpoint. Since -that endpoint is being deprecated, the MSC is no longer relevant. +The concept of this whitelist is being removed in this MSC however, as part +of lessening the reliance of homeservers on identity servers. This cannot be +done while the homeserver is still trusting an identity server for validation +of threepids. If the endpoints are split, the homeserver will handle the +validation of threepids being added to user accounts, and identity servers +will validate threepids being bound to themselves. ## Proposal -Two new endpoints will be added to the Client Server API: `POST -/account/3pid/bind` and `POST /account/3pid/add`. Both will require -authentication. The request parameters of `POST /account/3pid/bind` are the -same as [POST +To solve this problem, two new endpoints will be added to the Client Server +API: `POST /account/3pid/bind` and `POST /account/3pid/add`. Both will +require authentication and be rate-limited. The request parameters of `POST +/account/3pid/bind` are the same as [POST /account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid), -minus the `bind` flag. The request parameters of `POST /account/3pid/add` -will simply consist of a JSON body containing `client_secret` and `sid`. +minus the `bind` flag, and the contents of `three_pid_creds` have been +brought to the top level of the request body. The request parameters of `POST +/account/3pid/add` will simply consist of a JSON body containing +`client_secret` and `sid`. + +The homeserver should prevent a threepid being added to a user's account if +it already part of another user's account. However, the homeserver should not +check for existing threepids when binding to an identity server. Identity +servers do not enforce this requirement and neither should the proxying +homeserver. An example of binding a threepid to an identity server with this new endpoint is as follows: @@ -93,12 +84,10 @@ Next, the client completes the bind by calling the new endpoint on the homeserve POST https://home.server/_matrix/client/r0/account/3pid/bind { - "three_pid_creds": { - "id_server": "example.org", - "id_access_token": "abc123_OpaqueString", - "sid": "abc123987", - "client_secret": "don'tT3ll" - } + "id_server": "example.org", + "id_access_token": "abc123_OpaqueString", + "sid": "abc123987", + "client_secret": "don'tT3ll" } ``` @@ -161,17 +150,25 @@ identity server and homeserver at the same time requires one to trust the other, which is the exact behaviour we're trying to eliminate. Doing this also helps backward compatibility, as explained below. -This MSC also requests that the text "It is imperative that the homeserver -keep a list of trusted Identity Servers and only proxies to those that it -trusts." be removed from all parts of the spec, as the homeserver should no -longer need to trust any identity servers. +The text "It is imperative that the homeserver keep a list of trusted +Identity Servers and only proxies to those that it trusts." is to be removed +from all parts of the spec, as the homeserver should no longer need to trust +any identity servers. ## Tradeoffs -It may be possible to reduce the two calls per flow into a single endpoint, -but the current asynchronous approach makes it easy for a client to send a -request, go offline, have the threepid be validated, and then come online -again to finalize the validation afterwards. +One may question why clients don't just contact an identity server directly +to bind a threepid, bypassing the implications of binding through a +homeserver. While this will work, binds should still occur through a +homeserver such that the homeserver can keep track of which binds were made, +which is important when a user wishes to deactivate their account (and remove +all of their bindings made on different identity servers). + +A verification could occur on an identity server, which could then tell the +homeserver that a validation happened, but then there are security +considerations about how to authenticate an identity server in that instance +(and prevent people pretending to be identity servers and telling homeservers +about hundreds of fake threepid additions to a user's account). ## Backwards compatibility @@ -191,16 +188,8 @@ receiving a HTTP `404` error code, should either attempt to use Reducing the homeserver's trust in identity servers should be a boost to security and improve decentralisation in the Matrix ecosystem to boot. -Caution should be taken for homeserver developers to be sure not to continue -to use user-provided identity servers for any sensitive tasks once it removes -the concept of a trusted identity server list. - -## Conclusion - -This MSC helps to minimize the homeserver's trust in an identity server even -further to the point where it is only used for binding addresses for lookup - -which was the original intention of identity servers to begin with. - -Additionally, by clearly separating the original threepid endpoint into two -endpoints that each have a clear intention, the concept of attaching -threepids to a Matrix user becomes a lot easier to reason about. +Some endpoints of the Client Server API allow a user to provide an +`id_server` parameter. Caution should be taken for homeserver developers to +stop using these user-provided identity servers for any sensitive tasks, such +as password reset or account registration, if it removes the concept of a +trusted identity server list. From 1e69a7f3f21e70877248d5168cdca5627b9ea69d Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Tue, 17 Sep 2019 15:02:41 +0100 Subject: [PATCH 233/390] be assertive --- proposals/2290-separate-threepid-bind-hs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index 75fce27a..df60bd83 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -133,8 +133,8 @@ POST https://home.server/_matrix/client/r0/account/3pid/add The threepid has now been added to the user's account. To achieve the above flows, some changes need to be made to existing -endpoints. This MSC requests that the `id_server` and `id_access_token` -parameters be removed from the Client-Server API's [POST +endpoints. The `id_server` and `id_access_token` parameters are to be removed +from the Client-Server API's [POST /account/3pid/email/requestToken](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid-email-requesttoken) and [POST /account/3pid/msisdn/requestToken](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid-msisdn-requesttoken) From 169174e00be3be405a79a2a7b62347c5ff0f0e13 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Tue, 17 Sep 2019 17:01:18 +0100 Subject: [PATCH 234/390] Suggest the use of a unstable flag --- proposals/2290-separate-threepid-bind-hs.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index df60bd83..26cbbeb7 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -172,17 +172,19 @@ about hundreds of fake threepid additions to a user's account). ## Backwards compatibility +A new flag will be added to `/versions`' unstable_features section, +`m.separate_add_and_bind`. If this flag is present and set to `true`, then +clients should use the new API endpoints to carry out threepid adds and +binds. If this flag is not present or set to `false`, clients should use +`/account/3pid`, being aware that they can only bind threepids to the +homeserver, not the identity server. + Old matrix clients will continue to use the `/account/3pid` endpoint. This MSC removes the `bind` parameter and forces `/account/3pid` calls to act as if `bind` was set to `false`. Old clients will still be able to add 3pids to the homeserver, but not bind to the identity server. New homeservers must ignore any `id_server` information passed to this endpoint. -New matrix clients running with old homeservers should try their desired -endpoint (either `/account/3pid/add` or `/account/3pid/bind`) and on -receiving a HTTP `404` error code, should either attempt to use -`/account/3pid` with the `bind` parameter or give up, at their discretion. - ## Security considerations Reducing the homeserver's trust in identity servers should be a boost to From 53519f98d0986371e99812f4e7acba89a552036c Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Tue, 17 Sep 2019 17:01:45 +0100 Subject: [PATCH 235/390] Pin a spec version when we link to it --- proposals/2290-separate-threepid-bind-hs.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index 26cbbeb7..3a15a52c 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -48,7 +48,7 @@ To solve this problem, two new endpoints will be added to the Client Server API: `POST /account/3pid/bind` and `POST /account/3pid/add`. Both will require authentication and be rate-limited. The request parameters of `POST /account/3pid/bind` are the same as [POST -/account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid), +/account/3pid](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid), minus the `bind` flag, and the contents of `three_pid_creds` have been brought to the top level of the request body. The request parameters of `POST /account/3pid/add` will simply consist of a JSON body containing @@ -135,14 +135,14 @@ The threepid has now been added to the user's account. To achieve the above flows, some changes need to be made to existing endpoints. The `id_server` and `id_access_token` parameters are to be removed from the Client-Server API's [POST -/account/3pid/email/requestToken](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid-email-requesttoken) +/account/3pid/email/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-email-requesttoken) and [POST -/account/3pid/msisdn/requestToken](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid-msisdn-requesttoken) +/account/3pid/msisdn/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-msisdn-requesttoken) endpoints, as these endpoints are now only intended for the homeserver to send validation requests from. Additionally, the [POST -/account/3pid](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-account-3pid) +/account/3pid](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid) endpoint is deprecated as the two new endpoints replace its functionality. The `bind` parameter is to be removed, with the endpoint functioning as if `bind` was `false`. Allowing an endpoint to add a threepid to both the From e50bb3df25bedc3eb776888c2eb2ce393482e045 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Tue, 17 Sep 2019 17:04:40 +0100 Subject: [PATCH 236/390] Mention that homeserver's should remember binds done through them --- proposals/2290-separate-threepid-bind-hs.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index 3a15a52c..85803baf 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -93,7 +93,9 @@ POST https://home.server/_matrix/client/r0/account/3pid/bind The homeserver will then make a bind request to the specified identity server on behalf of the user. The homeserver will record if the bind was successful -and notify the user. +and notify the user. The homeserver will remember this bind and the identity +server it occurred on so that it can perform an unbind later if the user +requests it or their account is deactivated. The threepid has now been bound on the user's requested identity server without causing that threepid to be used for password resets or any other From 87d641c7c12c2cdd50a7e197cc438a5da409e05b Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Tue, 17 Sep 2019 17:20:57 +0100 Subject: [PATCH 237/390] Describe what the IS and HS are doing in the examples --- proposals/2290-separate-threepid-bind-hs.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index 85803baf..0e6004ab 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -75,6 +75,10 @@ POST https://identity.server/_matrix/identity/v2/validate/email/requestToken } ``` +The identity server must send an email to the specified address, including a +link to a URL on the identity server which will accept the validation session +ID, the given client_secret, and a randomly-generated token. + Once an email has been sent, the user clicks the link in the email, which notifies the identity server that the email has been verified. @@ -117,6 +121,10 @@ POST https://home.server/_matrix/client/r0/account/3pid/email/requestToken } ``` +Here the homeserver must send an email to the specified address, including a +link to a URL on the homeserver which will accept the validation session ID, +the given client_secret, and a randomly-generated token. + Once an email has been sent, the user clicks the link in the email, which notifies the homeserver that the threepid has been verified. @@ -132,7 +140,9 @@ POST https://home.server/_matrix/client/r0/account/3pid/add } ``` -The threepid has now been added to the user's account. +The homeserver checks the threepid validation session referred to by the +given ID and client_secret was validated, and if so adds the threepid to the +user's account. To achieve the above flows, some changes need to be made to existing endpoints. The `id_server` and `id_access_token` parameters are to be removed From 5d6113db1ec97052d74f271ceb7adb11a9f84787 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Wed, 18 Sep 2019 01:39:40 +0100 Subject: [PATCH 238/390] Update the MSC template (#2296) Tradeoffs->Alternatives, and kill the Conclusions section --- proposals/0000-proposal-template.md | 54 ++++++++++------------------- 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/proposals/0000-proposal-template.md b/proposals/0000-proposal-template.md index 8c8cdcea..cf79ed99 100644 --- a/proposals/0000-proposal-template.md +++ b/proposals/0000-proposal-template.md @@ -41,34 +41,17 @@ The template should have the following sections: * **Introduction** - This should cover the primary problem and broad description of the solution. * **Proposal** - The gory details of the proposal. -* **Tradeoffs** - Any items of the proposal that are less desirable should be listed here. Alternative - solutions to the same problem could also be listed here. * **Potential issues** - This is where problems with the proposal would be listed, such as changes that are not backwards compatible. +* **Alternatives** - This section lists alternative solutions to the same + problem which have been considered and dismsissed. * **Security considerations** - Discussion of what steps were taken to avoid security issues in the future and any potential risks in the proposal. -* **Conclusion** - A repeat of the problem and solution. Furthermore, the template should not be required to be followed. However it is strongly recommended to maintain some sense of consistency between proposals. -## Tradeoffs - -*This is where alternative solutions could be listed. There's almost always another way to do things -and this section gives you the opportunity to highlight why those ways are not as desirable. The -argument made in this example is that all of the text provided by the template could be integrated -into the proposals introduction, although with some risk of losing clarity.* - -Instead of adding a template to the repository, the assistance it provides could be integrated into -the proposal process itself. There is an argument to be had that the proposal process should be as -descriptive as possible, although having even more detail in the proposals introduction could lead to -some confusion or lack of understanding. Not to mention if the document is too large then potential -authors could be scared off as the process suddenly looks a lot more complicated than it is. For those -reasons, this proposal does not consider integrating the template in the proposals introduction a good -idea. - - ## Potential issues *Not all proposals are perfect. Sometimes there's a known disadvantage to implementing the proposal, @@ -84,6 +67,22 @@ is beneficial and not considered a significant problem because it will lead to a can follow. +## Alternatives + +*This is where alternative solutions could be listed. There's almost always another way to do things +and this section gives you the opportunity to highlight why those ways are not as desirable. The +argument made in this example is that all of the text provided by the template could be integrated +into the proposals introduction, although with some risk of losing clarity.* + +Instead of adding a template to the repository, the assistance it provides could be integrated into +the proposal process itself. There is an argument to be had that the proposal process should be as +descriptive as possible, although having even more detail in the proposals introduction could lead to +some confusion or lack of understanding. Not to mention if the document is too large then potential +authors could be scared off as the process suddenly looks a lot more complicated than it is. For those +reasons, this proposal does not consider integrating the template in the proposals introduction a good +idea. + + ## Security considerations *Some proposals may have some security aspect to them that was addressed in the proposed solution. This @@ -94,20 +93,3 @@ of concerns where possible.* By having a template available, people would know what the desired detail for a proposal is. This is not considered a risk because it is important that people understand the proposal process from start to end. - - -## Conclusion - -*Repeating the problem and solution in different words helps reviewers understand the problem a bit more. -This section should wrap up any loose ends left in the document, as well as cover a brief overview of the -content in each section. Note that the example here doesn't touch on the specific implementation details -described in the "Proposal" section - just the high-level points made there.* - -Not having a template for people to follow when making their proposals could lead to large differences -between each MSC. This would make it difficult for reviewers, and there's a potential that some information -could be left out by accident. A template written in the same format the proposal process requires would -give authors the ability to understand how to better explain their own proposal. - -A descriptive template would help potential authors comprehend what the proposal process requires by -demonstrating what is expected of a proposal. Although this is more effort up front, it would lead to more -time saved in the future due to questions about the process. From bd64ffc442127774a9f34631a3960be43dd975d8 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Wed, 18 Sep 2019 11:57:26 +0100 Subject: [PATCH 239/390] Homeservers shouldn't proxy to user-provided identity servers anymore --- proposals/2290-separate-threepid-bind-hs.md | 42 ++++++++++++--------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index 0e6004ab..34712d23 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -145,27 +145,33 @@ given ID and client_secret was validated, and if so adds the threepid to the user's account. To achieve the above flows, some changes need to be made to existing -endpoints. The `id_server` and `id_access_token` parameters are to be removed -from the Client-Server API's [POST -/account/3pid/email/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-email-requesttoken) -and [POST -/account/3pid/msisdn/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-msisdn-requesttoken) -endpoints, as these endpoints are now only intended for the homeserver to -send validation requests from. - -Additionally, the [POST +endpoints. The [POST /account/3pid](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid) endpoint is deprecated as the two new endpoints replace its functionality. The `bind` parameter is to be removed, with the endpoint functioning as if `bind` was `false`. Allowing an endpoint to add a threepid to both the identity server and homeserver at the same time requires one to trust the other, which is the exact behaviour we're trying to eliminate. Doing this -also helps backward compatibility, as explained below. - -The text "It is imperative that the homeserver keep a list of trusted -Identity Servers and only proxies to those that it trusts." is to be removed -from all parts of the spec, as the homeserver should no longer need to trust -any identity servers. +also helps backward compatibility, as explained in [Backwards +compatibility](#backwards-compatibility). + +The `id_server` and `id_access_token` parameters are to be removed +from all of the Client-Server API's `requestToken` endpoints. That is: + +* [POST /account/3pid/email/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-email-requesttoken) +* [POST /account/3pid/msisdn/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-msisdn-requesttoken) +* [POST /register/email/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-register-email-requesttoken) +* [POST /register/msisdn/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-register-msisdn-requesttoken) +* [POST /account/password/email/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-password-email-requesttoken) +* [POST /account/password/msisdn/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-password-msisdn-requesttoken) + +Either the homeserver itself or a service that the homeserver delegates to +should be handling the sending of validation messages, not a user-provided +server. Any mention of the homeserver being able to proxy to an identity +server in the above endpoint descriptions, as well as the text "It is +imperative that the homeserver keep a list of trusted Identity Servers and +only proxies to those that it trusts." is to be removed from all parts of the +spec, as the homeserver should no longer need to trust any identity servers. ## Tradeoffs @@ -204,6 +210,6 @@ security and improve decentralisation in the Matrix ecosystem to boot. Some endpoints of the Client Server API allow a user to provide an `id_server` parameter. Caution should be taken for homeserver developers to -stop using these user-provided identity servers for any sensitive tasks, such -as password reset or account registration, if it removes the concept of a -trusted identity server list. +stop using these user-provided identity servers for any sensitive tasks where +possible, such as password reset or account registration, if it removes the +concept of a trusted identity server list. From 40420d963387bfddf2f8efa6d8b0ad45584e74ec Mon Sep 17 00:00:00 2001 From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Date: Thu, 19 Sep 2019 11:50:16 +0100 Subject: [PATCH 240/390] Update proposals/2290-separate-threepid-bind-hs.md Co-Authored-By: Kitsune Ral --- proposals/2290-separate-threepid-bind-hs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index 34712d23..b361d0e1 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -55,7 +55,7 @@ brought to the top level of the request body. The request parameters of `POST `client_secret` and `sid`. The homeserver should prevent a threepid being added to a user's account if -it already part of another user's account. However, the homeserver should not +it's already part of another user's account. However, the homeserver should not check for existing threepids when binding to an identity server. Identity servers do not enforce this requirement and neither should the proxying homeserver. From eebcaaef02c7c1e0b5e13bcf9a999d2083229d4c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 24 Sep 2019 13:30:24 -0600 Subject: [PATCH 241/390] Point to the new v2 identity endpoint --- specification/modules/third_party_invites.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/modules/third_party_invites.rst b/specification/modules/third_party_invites.rst index 282b06f4..04c3b180 100644 --- a/specification/modules/third_party_invites.rst +++ b/specification/modules/third_party_invites.rst @@ -255,4 +255,4 @@ these is left to the implementer's discretion. -.. _`identity server /isvalid`: ../identity_service/%IDENTITY_RELEASE_LABEL%.html#get-matrix-identity-api-v1-pubkey-isvalid +.. _`identity server /isvalid`: ../identity_service/%IDENTITY_RELEASE_LABEL%.html#get-matrix-identity-v2-pubkey-isvalid From 8d865ca53aebce6a6aa8ac02d9a77c987eb13a05 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 24 Sep 2019 13:36:51 -0600 Subject: [PATCH 242/390] Add s2s POST /publicRooms per MSC2197 See https://github.com/matrix-org/matrix-doc/pull/2197 --- api/server-server/public_rooms.yaml | 160 ++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/api/server-server/public_rooms.yaml b/api/server-server/public_rooms.yaml index b7691023..0216f0c3 100644 --- a/api/server-server/public_rooms.yaml +++ b/api/server-server/public_rooms.yaml @@ -68,3 +68,163 @@ paths: description: The public room list for the homeserver. schema: $ref: "../client-server/definitions/public_rooms_response.yaml" + post: + summary: Gets the public rooms on the server with optional filter. + description: |- + Lists the public rooms on the server, with optional filter. + + This API returns paginated responses. The rooms are ordered by the number + of joined members, with the largest rooms first. + + Note that this endpoint receives and returns the same format that is seen + in the Client-Server API's ``POST /publicRooms`` endpoint. + operationId: queryPublicRooms + security: + - signedRequest: [] + parameters: + - in: body + name: body + required: true + description: |- + Options for which rooms to return. + schema: + type: object + properties: + limit: + type: integer + description: |- + Limit the number of results returned. + since: + type: string + description: |- + A pagination token from a previous request, allowing servers + to get the next (or previous) batch of rooms. The direction + of pagination is specified solely by which token is supplied, + rather than via an explicit flag. + filter: + type: object + title: "Filter" + description: |- + Filter to apply to the results. + properties: + generic_search_term: + type: string + description: |- + A string to search for in the room metadata, e.g. name, + topic, canonical alias etc. (Optional). + include_all_networks: + type: boolean + description: |- + Whether or not to include all known networks/protocols from + application services on the homeserver. Defaults to false. + example: false + third_party_instance_id: + type: string + description: |- + The specific third party network/protocol to request from the + homeserver. Can only be used if ``include_all_networks`` is false. + example: "irc" + example: { + "limit": 10, + "filter": { + "generic_search_term": "foo" + }, + "include_all_networks": false, + "third_party_instance_id": "irc" + } + responses: + 200: + description: A list of the rooms on the server. + schema: + type: object + description: A list of the rooms on the server. + required: ["chunk"] + properties: + chunk: + title: "PublicRoomsChunks" + type: array + description: |- + A paginated chunk of public rooms. + items: + type: object + title: "PublicRoomsChunk" + required: + - room_id + - num_joined_members + - world_readable + - guest_can_join + properties: + aliases: + type: array + description: |- + Aliases of the room. May be empty. + items: + type: string + canonical_alias: + type: string + description: |- + The canonical alias of the room, if any. + name: + type: string + description: |- + The name of the room, if any. + num_joined_members: + type: integer + description: |- + The number of members joined to the room. + room_id: + type: string + description: |- + The ID of the room. + topic: + type: string + description: |- + The topic of the room, if any. + world_readable: + type: boolean + description: |- + Whether the room may be viewed by guest users without joining. + guest_can_join: + type: boolean + description: |- + Whether guest users may join the room and participate in it. + If they can, they will be subject to ordinary power level + rules like any other user. + avatar_url: + type: string + description: The URL for the room's avatar, if one is set. + next_batch: + type: string + description: |- + A pagination token for the response. The absence of this token + means there are no more results to fetch and the client should + stop paginating. + prev_batch: + type: string + description: |- + A pagination token that allows fetching previous results. The + absence of this token means there are no results before this + batch, i.e. this is the first batch. + total_room_count_estimate: + type: integer + description: |- + An estimate on the total number of public rooms, if the + server has an estimate. + examples: + application/json: { + "chunk": [ + { + "aliases": ["#murrays:cheese.bar"], + "avatar_url": "mxc://bleeker.street/CHEDDARandBRIE", + "guest_can_join": false, + "name": "CHEESE", + "num_joined_members": 37, + "room_id": "!ol19s:bleecker.street", + "topic": "Tasty tasty cheese", + "world_readable": true + } + ], + "next_batch": "p190q", + "prev_batch": "p1902", + "total_room_count_estimate": 115 + } From 5cb7599196c4be0f01e41d530fdc85255abd7169 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 24 Sep 2019 13:39:33 -0600 Subject: [PATCH 243/390] Changelog --- changelogs/server_server/newsfragments/2035.new | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/server_server/newsfragments/2035.new diff --git a/changelogs/server_server/newsfragments/2035.new b/changelogs/server_server/newsfragments/2035.new new file mode 100644 index 00000000..6794c4ea --- /dev/null +++ b/changelogs/server_server/newsfragments/2035.new @@ -0,0 +1 @@ +Add new ``POST /publicRooms`` endpoint for filtering the room directory. From 9311e899414401121501b87b0e95bb031542e620 Mon Sep 17 00:00:00 2001 From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Date: Wed, 25 Sep 2019 01:32:35 +0200 Subject: [PATCH 244/390] Update proposals/2229-rebind-existing-3pid.md Co-Authored-By: Hubert Chathi --- proposals/2229-rebind-existing-3pid.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2229-rebind-existing-3pid.md b/proposals/2229-rebind-existing-3pid.md index 1bfb5106..398e6513 100644 --- a/proposals/2229-rebind-existing-3pid.md +++ b/proposals/2229-rebind-existing-3pid.md @@ -1,6 +1,6 @@ # Allowing 3PID Owners to Rebind -## Note: This MSC has been made obselete by MSC2290. +## Note: This MSC has been made obsolete by MSC2290. MSC2290 provides two separate API endpoints, one for adding a 3PID to the homeserver, and another for binding to an identity server. These new From 219ebff6d39837d3982982483f1e114d8f77f832 Mon Sep 17 00:00:00 2001 From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Date: Thu, 26 Sep 2019 10:37:10 +0200 Subject: [PATCH 245/390] typo fix Co-Authored-By: Hubert Chathi --- proposals/2290-separate-threepid-bind-hs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index b361d0e1..fad547c4 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -1,6 +1,6 @@ # Separate Endpoints for Binding Threepids -*Note: This MSC obseletes +*Note: This MSC obsoletes [MSC2229](https://github.com/matrix-org/matrix-doc/pull/2229), which dealt with changing the rules of the `bind` flag on [POST /account/3pid](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid). From 1a51a2476849cc7f08d5d880a6564e74fa10f181 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Thu, 26 Sep 2019 17:16:44 +0100 Subject: [PATCH 246/390] UIAA on /account/3pid/add --- proposals/2290-separate-threepid-bind-hs.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index b361d0e1..a1c7f09e 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -45,8 +45,15 @@ will validate threepids being bound to themselves. ## Proposal To solve this problem, two new endpoints will be added to the Client Server -API: `POST /account/3pid/bind` and `POST /account/3pid/add`. Both will -require authentication and be rate-limited. The request parameters of `POST +API: `POST /account/3pid/bind` and `POST /account/3pid/add`. Binding to an +identity server will require standard authentication, whereas adding a 3pid +to a user account will require [User-Interactive +Authentication](https://matrix.org/docs/spec/client_server/r0.5.0#user-interactive-authentication-api). +The latter is to prevent someone from adding a 3pid (which can be used to +reset passwords) to someone who's left their account open on a public +computer, without needing their password to do so. + +Both endpoints will be rate-limited. The request parameters of `POST /account/3pid/bind` are the same as [POST /account/3pid](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid), minus the `bind` flag, and the contents of `three_pid_creds` have been From ec7e795112ddc5a4d6fb498be996784e9c0ee8b4 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Thu, 26 Sep 2019 17:20:40 +0100 Subject: [PATCH 247/390] reflow --- proposals/2290-separate-threepid-bind-hs.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index 95f810e9..aa71fb35 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -70,7 +70,8 @@ homeserver. An example of binding a threepid to an identity server with this new endpoint is as follows: -First the client must request the threepid be validated by its chosen identity server. +First the client must request the threepid be validated by its chosen +identity server. ``` POST https://identity.server/_matrix/identity/v2/validate/email/requestToken @@ -89,7 +90,8 @@ ID, the given client_secret, and a randomly-generated token. Once an email has been sent, the user clicks the link in the email, which notifies the identity server that the email has been verified. -Next, the client completes the bind by calling the new endpoint on the homeserver: +Next, the client completes the bind by calling the new endpoint on the +homeserver: ``` POST https://home.server/_matrix/client/r0/account/3pid/bind From 46e7137252088f7fe6b12d2ccd713d3b07f4e33b Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Thu, 26 Sep 2019 17:51:51 +0100 Subject: [PATCH 248/390] Don't remove id_server and id_access_token --- proposals/2290-separate-threepid-bind-hs.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/proposals/2290-separate-threepid-bind-hs.md b/proposals/2290-separate-threepid-bind-hs.md index aa71fb35..8c899699 100644 --- a/proposals/2290-separate-threepid-bind-hs.md +++ b/proposals/2290-separate-threepid-bind-hs.md @@ -164,8 +164,10 @@ other, which is the exact behaviour we're trying to eliminate. Doing this also helps backward compatibility, as explained in [Backwards compatibility](#backwards-compatibility). -The `id_server` and `id_access_token` parameters are to be removed -from all of the Client-Server API's `requestToken` endpoints. That is: +Either the homeserver itself or a service that the homeserver delegates to +should be handling the sending of validation messages, not a user-provided +server. Any mention of the homeserver being able to proxy to an identity +server in the below endpoint descriptions: * [POST /account/3pid/email/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-email-requesttoken) * [POST /account/3pid/msisdn/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-msisdn-requesttoken) @@ -174,13 +176,10 @@ from all of the Client-Server API's `requestToken` endpoints. That is: * [POST /account/password/email/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-password-email-requesttoken) * [POST /account/password/msisdn/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-password-msisdn-requesttoken) -Either the homeserver itself or a service that the homeserver delegates to -should be handling the sending of validation messages, not a user-provided -server. Any mention of the homeserver being able to proxy to an identity -server in the above endpoint descriptions, as well as the text "It is -imperative that the homeserver keep a list of trusted Identity Servers and -only proxies to those that it trusts." is to be removed from all parts of the -spec, as the homeserver should no longer need to trust any identity servers. +As well as the text "It is imperative that the homeserver keep a list of +trusted Identity Servers and only proxies to those that it trusts." is to be +removed from all parts of the spec, as the homeserver should no longer need +to trust any identity servers. ## Tradeoffs From 79f16d40a9b40379a6a4bdd2b50500baf6799a47 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 3 Oct 2019 08:28:05 +0900 Subject: [PATCH 249/390] Add a full stop Co-Authored-By: Travis Ralston --- changelogs/client_server/2245.clarification | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/client_server/2245.clarification b/changelogs/client_server/2245.clarification index 67533d15..31e5c2df 100644 --- a/changelogs/client_server/2245.clarification +++ b/changelogs/client_server/2245.clarification @@ -1 +1 @@ -List available enum values for the room versions capability +List available enum values for the room versions capability. From 48b8a95df6b9f344d765bf6a856b8f0b21b75f7e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 3 Oct 2019 09:41:45 -0600 Subject: [PATCH 250/390] Deprecate id_server and make it optional As per [MSC2263](https://github.com/matrix-org/matrix-doc/pull/2263) --- api/client-server/administrative_contact.yaml | 8 ++++++-- .../definitions/request_email_validation.yaml | 6 +++++- .../definitions/request_msisdn_validation.yaml | 6 +++++- api/client-server/registration.yaml | 8 ++++++-- specification/client_server_api.rst | 6 ++++++ 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/api/client-server/administrative_contact.yaml b/api/client-server/administrative_contact.yaml index 3581edf7..41270535 100644 --- a/api/client-server/administrative_contact.yaml +++ b/api/client-server/administrative_contact.yaml @@ -343,7 +343,9 @@ paths: 400: description: |- The third party identifier is already in use on the homeserver, or - the request was invalid. + the request was invalid. The error code ``M_SERVER_NOT_TRUSTED`` + can be returned if the server does not trust/support the identity server + provided in the request. schema: $ref: "definitions/errors/error.yaml" examples: @@ -391,7 +393,9 @@ paths: 400: description: |- The third party identifier is already in use on the homeserver, or - the request was invalid. + the request was invalid. The error code ``M_SERVER_NOT_TRUSTED`` + can be returned if the server does not trust/support the identity server + provided in the request. schema: $ref: "definitions/errors/error.yaml" examples: diff --git a/api/client-server/definitions/request_email_validation.yaml b/api/client-server/definitions/request_email_validation.yaml index 2b270514..2d789d05 100644 --- a/api/client-server/definitions/request_email_validation.yaml +++ b/api/client-server/definitions/request_email_validation.yaml @@ -22,6 +22,9 @@ allOf: The hostname of the identity server to communicate with. May optionally include a port. This parameter is ignored when the homeserver handles 3PID verification. + + This parameter is deprected with a plan to be removed in a future specification + version for ``/account/password`` and ``/register`` requests. example: "id.example.com" id_access_token: type: string @@ -29,4 +32,5 @@ allOf: An access token previously registered with the identity server. Servers can treat this as optional to distinguish between r0.5-compatible clients and this specification version. - required: ["id_server", "id_access_token"] + + Required if an ``id_server`` is supplied. diff --git a/api/client-server/definitions/request_msisdn_validation.yaml b/api/client-server/definitions/request_msisdn_validation.yaml index b013a561..54988fd4 100644 --- a/api/client-server/definitions/request_msisdn_validation.yaml +++ b/api/client-server/definitions/request_msisdn_validation.yaml @@ -22,6 +22,9 @@ allOf: The hostname of the identity server to communicate with. May optionally include a port. This parameter is ignored when the homeserver handles 3PID verification. + + This parameter is deprected with a plan to be removed in a future specification + version for ``/account/password`` and ``/register`` requests. example: "id.example.com" id_access_token: type: string @@ -29,4 +32,5 @@ allOf: An access token previously registered with the identity server. Servers can treat this as optional to distinguish between r0.5-compatible clients and this specification version. - required: ["id_server", "id_access_token"] + + Required if an ``id_server`` is supplied. diff --git a/api/client-server/registration.yaml b/api/client-server/registration.yaml index 733ebe47..a4c5bf71 100644 --- a/api/client-server/registration.yaml +++ b/api/client-server/registration.yaml @@ -425,7 +425,9 @@ paths: 400: description: |- The referenced third party identifier is not recognised by the - homeserver, or the request was invalid + homeserver, or the request was invalid. The error code ``M_SERVER_NOT_TRUSTED`` + can be returned if the server does not trust/support the identity server + provided in the request. schema: $ref: "definitions/errors/error.yaml" examples: @@ -485,7 +487,9 @@ paths: 400: description: |- The referenced third party identifier is not recognised by the - homeserver, or the request was invalid + homeserver, or the request was invalid. The error code ``M_SERVER_NOT_TRUSTED`` + can be returned if the server does not trust/support the identity server + provided in the request. schema: $ref: "definitions/errors/error.yaml" examples: diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst index 8df596be..81fb28a6 100644 --- a/specification/client_server_api.rst +++ b/specification/client_server_api.rst @@ -809,6 +809,9 @@ To use this authentication type, clients should submit an auth dict as follows: "session": "" } +Note that ``id_server`` (and therefore ``id_access_token``) is optional if the +``/requestToken`` request did not include them. + Phone number/MSISDN-based (identity / homeserver) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< :Type: @@ -838,6 +841,9 @@ To use this authentication type, clients should submit an auth dict as follows: "session": "" } +Note that ``id_server`` (and therefore ``id_access_token``) is optional if the +``/requestToken`` request did not include them. + Dummy Auth <<<<<<<<<< :Type: From 2e84465c13e4333f08c518db35d73c710188f6e9 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 3 Oct 2019 09:42:59 -0600 Subject: [PATCH 251/390] Changelog --- changelogs/client_server/newsfragments/2310.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/2310.feature diff --git a/changelogs/client_server/newsfragments/2310.feature b/changelogs/client_server/newsfragments/2310.feature new file mode 100644 index 00000000..0169d23b --- /dev/null +++ b/changelogs/client_server/newsfragments/2310.feature @@ -0,0 +1 @@ +Deprecate ``id_server`` and make it optional in several places. From b59998fd2ba3aa559f05b41441a8d90574e8358e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 3 Oct 2019 09:49:01 -0600 Subject: [PATCH 252/390] Fix client-server typos changelog Note: .misc is not a valid changelog entry. --- changelogs/client_server/newsfragments/2131.clarification | 2 +- changelogs/client_server/newsfragments/2148.clarification | 1 + changelogs/client_server/newsfragments/2148.misc | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 changelogs/client_server/newsfragments/2148.clarification delete mode 100644 changelogs/client_server/newsfragments/2148.misc diff --git a/changelogs/client_server/newsfragments/2131.clarification b/changelogs/client_server/newsfragments/2131.clarification index 3c41fb60..3ccb2333 100644 --- a/changelogs/client_server/newsfragments/2131.clarification +++ b/changelogs/client_server/newsfragments/2131.clarification @@ -1 +1 @@ -Fix typo in key verification framework section. +Fix various typos throughout the specification. diff --git a/changelogs/client_server/newsfragments/2148.clarification b/changelogs/client_server/newsfragments/2148.clarification new file mode 100644 index 00000000..3ccb2333 --- /dev/null +++ b/changelogs/client_server/newsfragments/2148.clarification @@ -0,0 +1 @@ +Fix various typos throughout the specification. diff --git a/changelogs/client_server/newsfragments/2148.misc b/changelogs/client_server/newsfragments/2148.misc deleted file mode 100644 index d5514131..00000000 --- a/changelogs/client_server/newsfragments/2148.misc +++ /dev/null @@ -1 +0,0 @@ -Fix a small duplicated "as". From 54e73e47291712ed250d8ed3482e789ac0c8b9e5 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Fri, 4 Oct 2019 10:41:28 -0400 Subject: [PATCH 253/390] Apply suggestions from code review Co-Authored-By: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- proposals/1219-storing-megolm-keys-serverside.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 1103eecf..23f1e0f5 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -113,7 +113,7 @@ the key is saved directly by the user, then the code is constructed as follows: 1. The 256-bit curve25519 private key is prepended by the bytes `0x8B` and `0x01` -2. All the bytes in the string are above are XORed together to form a parity +2. All the bytes in the string above, including the two header bytes, are XORed together to form a parity byte. This parity byte is appended to the byte string. 3. The byte string is encoded using base58, using the same mapping as is used for Bitcoin addresses. @@ -129,12 +129,14 @@ results in 0, and ensure that the total length of the decoded string is 35 bytes. Clients must then remove the first two bytes and the last byte, and use the resulting string as the private key to decrypt backups. +#### Enconding the recovery key for server-side storage via MSC1946 + If MSC1946 is used to store the key on the server, it must be stored using the `account_data` type `m.megolm_backup.v1`. As a special case, if the recovery key is the same as the curve25519 key used for storing the key, then the contents of the `m.megolm_backup.v1` -`account_data` for that key will be the an object with a `passthrough` property +`account_data` for that key will be an object with a `passthrough` property whose value is `true`. For example, if `m.megolm_backup.v1` is set to: ```json @@ -245,7 +247,7 @@ On success, returns the empty JSON object. Error codes: -- `M_NOT_FOUND`: No backup version found. +- `M_NOT_FOUND`: This backup version was not found. #### Storing keys From eddce003908691e66a312a520dd1e8c30055cef6 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 4 Oct 2019 21:56:02 -0400 Subject: [PATCH 254/390] MSC2313: Ban lists --- proposals/2313-ban-lists.md | 198 ++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 proposals/2313-ban-lists.md diff --git a/proposals/2313-ban-lists.md b/proposals/2313-ban-lists.md new file mode 100644 index 00000000..284ebf23 --- /dev/null +++ b/proposals/2313-ban-lists.md @@ -0,0 +1,198 @@ +# MSC2313: Ban lists + +Matrix is an open network and anyone can participate in it. As a result, a +very wide range of content exists, and it is important to empower users to be +able to select which content they wish to see, and wish they to block. By +extension, room moderators and server admins should also be able to select +which content they do not wish to host in their rooms and servers. + +The protocol's position in this solution should be one of neutrality: it +should not be deciding what content is undesirable for any particular entity and +should instead be empowering those entities to make their own decisions. This +proposal introduces ban lists as a basic mechanism to help users manage this +process, by providing a way of modelling sets of servers, rooms and users +which can then be used to make filtering decisions. This proposal makes no +attempt at interpreting the model and actually making those decisions however. + +To reaffirm: where this proposal says that some content is undesirable it does not intend to +bias the reader into what that could entail. Undesirability is purely in the hands of the +entity perceiving the content. For example, someone who believes birthday cake is undesirable +is perfectly valid in taking that position and is encouraged by this proposal to set up or +use a ban list which prevents birthday cake from coming across their field of view. + +## Proposal + +Ban lists are represented in rooms, allowing for structures and concepts to be reused without +defining a new room version. This proposal does not make any restrictions on how the rooms +are configured, just that the state events described here are represented in the room. For +example, a room which is invite only is just as valid as a room that is not: the important +details are specific state events and not the accessibility, retention, or other aspects +of the room. + +Ban lists are stored as `m.room.rule.` state events, with state keys being arbitrary IDs +assigned by the sender. The `` is currently one of `user`, `room`, and `server`. Three +fields are defined in `content`: + +* `entity` (`string`) - **Required.** The entity/entities affected. Simple globs are supported + for defining entities (`*` and `?` as wildcards, just like `m.room.server_acl`). +* `recommendation` (`enum`) - **Required.** The action subscribed entities should take against + the described entity. Currently only `m.ban` is defined (see below). +* `reason` (`string`) - **Required.** The human-readable description for the recommendation. + +Invalid or missing fields are to be treated as though the rule doesn't exist. This is to +allow for rules to be deleted while state events cannot be deleted in Matrix. + +An example set of minimal state events for banning `@alice:example.org`, `!matrix:example.org`, +`evil.example.org`, and `*.evil.example.org` would be: + +```json +[ + { + "type": "m.room.rule.user", + "state_key": "rule_1", + "content": { + "entity": "@alice:example.org", + "recommendation": "m.ban", + "reason": "undesirable behaviour" + } + }, + { + "type": "m.room.rule.room", + "state_key": "rule_2", + "content": { + "entity": "!matrix:example.org", + "recommendation": "m.ban", + "reason": "undesirable content" + } + }, + { + "type": "m.room.rule.server", + "state_key": "rule_3", + "content": { + "entity": "evil.example.org", + "recommendation": "m.ban", + "reason": "undesirable engagement" + } + }, + { + "type": "m.room.rule.server", + "state_key": "rule_4", + "content": { + "entity": "*.evil.example.org", + "recommendation": "m.ban", + "reason": "undesirable engagement" + } + } +] +``` + +When the entity is a room, it can be a room alias or ID - the subscriber is responsible for +resolving it to a room ID (if it wants to). + +Non-standard recommendations are permitted using the Java package naming convention for +namespacing. A `recommendation` is just a recommendation: how implementations apply the rules +is left as a concern for those implementations. The only standard `recommendation` currently +defined is `m.ban`: The `entity` should be banned from participation where possible. + +The enforcement mechanism of `m.ban` is deliberately left as an implementation detail to avoid the +protocol imposing its opinion on how the lists are interpreted. However, a suggestion for +a simple implementation is as follows: + +* Is a user... + * Applied to a user: The user should be added to the subscriber's ignore list. + * Applied to a room: The user should be banned from the room (either on sight or immediately). + * Applied to a server: The user should not be allowed to send invites to users on the server. +* Is a room... + * Applied to a user: The user should leave the room and not join it (MSC2270-style ignore). + * Applied to a room: No-op because a room cannot ban itself. + * Applied to a server: The server should prevent users from joining the room and from receiving + invites to it (similar to the `shutdown_room` API in Synapse). +* Is a server... + * Applied to a user: The user should not receive events or invites from the server. + * Applied to a room: The server is added as a denied server in the ACLs. + * Applied to a server: The subscriber should avoid federating with the server as much as + possible by blocking invites from the server and not sending traffic unless strictly + required (no outbound invites). + +Other entities could be represented by this recommendation as well, however as per the +introduction to this proposal they are strictly out of scope. An example would be an integration +manager which doesn't want to offer integrations to banned entities - this is an implementation +detail for the integration manager to solve. + +A new event type is introduced here instead of reusing existing events (membership, ACLs, etc) +because the implication of a recommendation/rule is less clear when using the more narrow-scoped +events. For example, a philosophical question arises over what a `membership` of `ban` means to a server +subscribed to the list. More questions get raised when the `membership` of a user isn't `ban`, +but is `join` or similar instead - if the subscriber was mirroring events, it would be inclined +to try and sync membership lists, which this proposal attempts to avoid by using a more generic +and neutral event type. + +How subscriptions to ban lists are handled is also left as an implementation +detail (to avoid unnecessarily blocking progress on this MSC). The subscriber +could be anything that speaks Matrix, therefore this proposal makes no attempt +to describe how this should work for everything. Some ideas for how this could +be implemented include joining the ban list room to watch for updates and +applying them automatically, however there is no requirement that the +subscriber needs to join the room: they could peek or poll at an interval +instead, which is just as valid. + +To ease sharing of these ban list rooms, a system very similar to [MSC1951's sharable URLs]( +https://github.com/matrix-org/matrix-doc/pull/1951/files#diff-4ee6ed0ee1f2df73efac5fa9a9835642R50-R70) +is defined. There are two ways to share the ban list: a link to the room as one would when +sharing any reference to any other room ("please add `#bans:example.org` to your block list"), +or by using plain `http` or `https` URLs. Just like in MSC1951, the URL when approached with +a `Accept: application/json` header or has `.json` appended to the end of the URL should return +a `room_uri` which is a reference to the ban list room. Currently, the reference would be a +`matrix.to` URI, however in the future this could be a `mx://` or similar URL. When not approached +with the intent of JSON, the service could return a user-friendly page describing what is included +in the ban list. + +## Future considerations + +This proposal notably does not define specific behaviour for AI or machine learning applications. +Implementations are currently able to apply AI/ML to their systems if they see fit (for example, +spam detection or undesirable content being uploaded), however no specification is proposed +here to make the interaction standardized. + +This proposal additionally does not describe how a server could subscribe to a ban list: this +is left for the server to figure out (possibly by using a utility user account?) potentially +with the support of other proposals, such as [MSC1777](https://github.com/matrix-org/matrix-doc/pull/1777). + +Further work on reputation systems could enhance ban lists by adding additional metrics to +assert their validity. This proposal assumes social trust ("don't use it if you +don't trust the creator") over verifiable/provable trust - a future proposal can easily add +such systems to these ban lists. + +This proposal intentionally does not handle how a server could assist the user in preventing +undesirable content or subscribing to ban lists. Users already have some tools at their disposal, +such as being able to ignore other users, and are encouraged to make use of those first. Other +proposals are encouraged to specify what additional tools might look like to better handle +ban lists. + +Media (uploaded content) is not handled by this proposal either. This is a concern left for +other proposals like [MSC2278](https://github.com/matrix-org/matrix-doc/pull/2278) and +[MSC701](https://github.com/matrix-org/matrix-doc/issues/701). + +## Security considerations + +Using this solution one can build a social system of shared blacklists, which +may create a divide within established communities if not carefully deployed. +This may well not be a suitable answer for all communities. + +Depending on how the implementations handle subscriptions, user IDs may be linked to ban +lists and therefore expose the views of that user. Using the example from the introduction, +if a user who does not like birthday cake were to join the ban list room for blocking +birthday cake, that user's preference would be exposed to any other observers of that ban +list. Proposals like [MSC1228](https://github.com/matrix-org/matrix-doc/issues/1228) and +[MSC1777](https://github.com/matrix-org/matrix-doc/pull/1777) could help solve this. + +## Implementation notes + +This proposal is partially implemented by [mjolnir](https://github.com/matrix-org/mjolnir) +using the `org.matrix.mjolnir.*` namespace until this becomes stable. This results in +the following mappings: + +* `m.room.rule.user` => `org.matrix.mjolnir.rule.user` +* `m.room.rule.room` => `org.matrix.mjolnir.rule.room` +* `m.room.rule.server` => `org.matrix.mjolnir.rule.server` +* `m.ban` => `org.matrix.mjolnir.ban` From 576177b579699ec7e07e114d338e60db56c2f1a7 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 9 Oct 2019 17:52:53 -0400 Subject: [PATCH 255/390] make version optional in versions update --- proposals/1219-storing-megolm-keys-serverside.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 23f1e0f5..13f16bc9 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -223,7 +223,7 @@ Body parameters: - `auth_data` (object): Required. algorithm-dependent data. For `m.megolm_backup.v1.curve25519-aes-sha2`, see below for the [definition of this property](#auth_data-backup-versions). -- `version` (string): Required. The backup version. Must be the same as the query parameter or must be the current version. +- `version` (string): Optional. The backup version. If present, must be the same as the path parameter. Example: From 5799c433fe992154f1e37921c41debd6f4313319 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 10 Oct 2019 15:03:10 -0400 Subject: [PATCH 256/390] add HTTP status codes for errors and move key format to the right spot --- .../1219-storing-megolm-keys-serverside.md | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index 13f16bc9..ff74eb16 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -210,7 +210,7 @@ On success, returns a JSON object with keys: Error codes: -- `M_NOT_FOUND`: No backup version has been created. +- `M_NOT_FOUND`: No backup version has been created. (with HTTP status code 404) ##### `PUT /room_keys/version/{version}` @@ -247,7 +247,7 @@ On success, returns the empty JSON object. Error codes: -- `M_NOT_FOUND`: This backup version was not found. +- `M_NOT_FOUND`: This backup version was not found. (with HTTP status code 404) #### Storing keys @@ -289,7 +289,8 @@ On success, returns a JSON object with keys: Error codes: - `M_WRONG_ROOM_KEYS_VERSION`: the version specified does not match the current - backup version + backup version (with HTTP status code 403). The current backup version will + be included in the `current_version` field of the HTTP result. Example: @@ -426,7 +427,7 @@ On success, returns a JSON object in the same form as the request body of `PUT Error codes: - M_NOT_FOUND: The session is not present in the backup, or the requested - backup version does not exist. + backup version does not exist. (with HTTP status code 404) ##### `GET /room_keys/keys/${roomId}?version=$v` @@ -446,7 +447,7 @@ a successful response with body: Error codes: -- `M_NOT_FOUND`: The requested backup version does not exist. +- `M_NOT_FOUND`: The requested backup version does not exist. (with HTTP status code 404) ##### `GET /room_keys/keys?version=$v` @@ -466,7 +467,7 @@ a successful response with body: Error codes: -- `M_NOT_FOUND`: The requested backup version does not exist. +- `M_NOT_FOUND`: The requested backup version does not exist. (with HTTP status code 404) #### Deleting keys @@ -503,14 +504,14 @@ The `session_data` field in the backups is constructed as follows: 1. Encode the session key to be backed up as a JSON object with the properties: - `algorithm` (string): `m.megolm.v1.aes-sha2` - - `sender_key` (string): base64-encoded device curve25519 key in - [session-sharing - format](https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/megolm.md#session-sharing-format) + - `sender_key` (string): base64-encoded device curve25519 key - `sender_claimed_keys` (object): object containing the identity keys for the sending device - `forwarding_curve25519_key_chain` (array): zero or more curve25519 keys for devices who forwarded the session key - - `session_key` (string): base64-encoded (unpadded) session key + - `session_key` (string): base64-encoded (unpadded) session key in + [session-sharing + format](https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/megolm.md#session-sharing-format) 2. Generate an ephemeral curve25519 key, and perform an ECDH with the ephemeral key and the backup's public key to generate a shared secret. The public half of the ephemeral key, encoded using base64, becomes the `ephemeral` From 9dfca61ae790e15b7a924955cc93518b4655142f Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 23 Oct 2019 16:13:17 -0400 Subject: [PATCH 257/390] Apply suggestions from code review Co-Authored-By: David Baker Co-Authored-By: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Co-Authored-By: Erik Johnston --- proposals/1946-secure_server-side_storage.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index 0ccde311..2f6460c6 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -41,7 +41,7 @@ A key with ID `abcdefg` is stored in `m.secret_storage.key.abcdefg` ```json { "name": "Some key", - "algorihm": "m.secret_storage.v1.curve25519-aes-sha2", + "algorithm": "m.secret_storage.v1.curve25519-aes-sha2", // ... other properties according to algorithm } ``` @@ -52,7 +52,7 @@ will be encrypted with, and that clients will try to use to decrypt data with, unless the user specifies otherwise. Only one key can be marked as the default at a time. -Encrypted data is stored in the `account_data` using the `type` defined by the +Encrypted data is stored in `account_data` using the `type` defined by the feature that uses the data. For example, decryption keys for key backups could be stored under the type `m.megolm_backup.v1.recovery_key`, or the self-signing key for cross-signing could be stored under the type @@ -180,7 +180,7 @@ and the number of iterations given in the `iterations` parameter. Rather than (or in addition to) storing secrets on the server encrypted by a shared key, devices can send secrets to each other, encrypted using olm. -To request a secret, a client sends a `m.secret.request` event with `action` +To request a secret, a client sends a `m.secret.request` device event with `action` set to `request` to other devices, and `name` set to the name of the secret that it wishes to retrieve. A device that wishes to share the secret will reply with a `m.secret.send` event, encrypted using olm. When the original @@ -229,8 +229,8 @@ also define a secret key mechanism. ## Potential issues -Keeping all the data and keys in account data means that it may clutter up the -`/sync`. However, clients can filter out the data that they are not interested +Keeping all the data and keys in account data means that it may clutter up +`/sync` requests. However, clients can filter out the data that they are not interested in. One possibility for addressing this would be to add a flag to the account data to indicate whether it should come down the `/sync` or not. From b27f6985b2850ceb93ea29112b445ccb821cc82e Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 23 Oct 2019 17:32:36 -0400 Subject: [PATCH 258/390] more clarifications --- proposals/1946-secure_server-side_storage.md | 55 +++++++++++--------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index 2f6460c6..49f115da 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -24,10 +24,12 @@ decrypt data on clients. For example, the user could have one key that can decrypt everything, and another key that can only decrypt their user-signing key for cross-signing. -Key descriptions and secret data are both stored in the user's `account_data`. +Key descriptions and secret data are both stored in the user's account_data. -Each key has an ID, and the description of the key is stored in the -`account_data` using the `type` `m.secret_storage.key.[key ID]`. The contents +#### Key storage + +Each key has an ID, and the description of the key is stored in the user's +account_data using the event type `m.secret_storage.key.[key ID]`. The contents of the account data for the key will include an `algorithm` property, which indicates the encryption algorithm used, as well as a `name` property, which is a human-readable name. The contents will be signed as signed JSON using the @@ -52,13 +54,15 @@ will be encrypted with, and that clients will try to use to decrypt data with, unless the user specifies otherwise. Only one key can be marked as the default at a time. -Encrypted data is stored in `account_data` using the `type` defined by the -feature that uses the data. For example, decryption keys for key backups could -be stored under the type `m.megolm_backup.v1.recovery_key`, or the self-signing -key for cross-signing could be stored under the type +#### Secret storage + +Encrypted data is stored in the user's account_data using the event type +defined by the feature that uses the data. For example, decryption keys for +key backups could be stored under the type `m.megolm_backup.v1.recovery_key`, +or the self-signing key for cross-signing could be stored under the type `m.cross_signing.self_signing`. -The `account_data` will have an `encrypted` property that is a map from key ID +The account_data will have an `encrypted` property that is a map from key ID to an object. The algorithm from the `m.secret_storage.key.[key ID]` data for the given key defines how the other properties are interpreted, though it's expected that most encryption schemes would have `ciphertext` and `mac` @@ -71,17 +75,17 @@ Some secret is encrypted using keys with ID `key_id_1` and `key_id_2`: ```json { - "encrypted": { - "key_id_1": { - "ciphertext": "base64+encoded+encrypted+data", - "mac": "base64+encoded+mac", - // ... other properties according to algorithm property in - // m.secret_storage.key.key_id_1 - }, - "key_id_2": { - // ... - } + "encrypted": { + "key_id_1": { + "ciphertext": "base64+encoded+encrypted+data", + "mac": "base64+encoded+mac", + // ... other properties according to algorithm property in + // m.secret_storage.key.key_id_1 + }, + "key_id_2": { + // ... } + } } ``` @@ -90,7 +94,7 @@ Some secret is encrypted using keys with ID `key_id_1` and `key_id_2`: ##### `m.secret_storage.v1.curve25519-aes-sha2` The public key is stored in the `pubkey` property of the `m.secret_storage.key.[key -ID]` `account_data` as a base64-encoded string. +ID]` account_data as a base64-encoded string. The data is encrypted and MACed as follows: @@ -189,12 +193,13 @@ set to `request_cancellation` to all devices other than the one that it received secret from. Clients should ignore `m.secret.send` events received from devices that it did not send an `m.secret.request` event to. -Clients SHOULD ensure that they only share secrets with other devices that are -allowed to see them. For example, clients SHOULD only share secrets with devices -that are verified and MAY prompt the user to confirm sharing the secret. +Clients MUST ensure that they only share secrets with other devices that are +allowed to see them. For example, clients SHOULD only share secrets with +devices that are verified and MAY prompt the user to confirm sharing the +secret. If a feature allows secrets to be stored or shared, then for consistency it -SHOULD use the same name for both the `account_data` `type` and the `name` in +SHOULD use the same name for both the account_data event type and the `name` in the `m.secret.request`. #### Event definitions @@ -216,8 +221,8 @@ unencrypted to-device event. ##### `m.secret.send` Sent by a client to share a secret with another device, in response to an -`m.secret.request` event. Typically it is encrypted as an `m.room.encrypted` -event, then sent as a to-device event. +`m.secret.request` event. It MUST be encrypted as an `m.room.encrypted` event, +then sent as a to-device event. - `request_id`: (string) Required. The ID of the request that this a response to. - `secret`: (string) Required. The contents of the secret. From f5f12a29e08a197cb183f0ae5b5a36b7f52d63a1 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 8 Oct 2019 15:31:06 +0100 Subject: [PATCH 259/390] Spec MSC2290: 3PID binding split --- api/client-server/administrative_contact.yaml | 149 +++++++++++++++--- api/client-server/registration.yaml | 30 +--- specification/client_server_api.rst | 10 +- 3 files changed, 144 insertions(+), 45 deletions(-) diff --git a/api/client-server/administrative_contact.yaml b/api/client-server/administrative_contact.yaml index 41270535..504e149d 100644 --- a/api/client-server/administrative_contact.yaml +++ b/api/client-server/administrative_contact.yaml @@ -1,4 +1,5 @@ # Copyright 2016 OpenMarket Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -89,8 +90,19 @@ paths: - User data post: summary: Adds contact information to the user's account. - description: Adds contact information to the user's account. + description: |- + Adds contact information to the user's account. + + This endpoint is deprecated in favour of the more specific ``/3pid/add`` + and ``/3pid/bind`` endpoints. + + .. Note:: + Previously this endpoint supported a ``bind`` parameter. This parameter + has been removed, making this endpoint behave as though it was ``false``. + This results in this endpoint being an equivalent to ``/3pid/bind`` rather + than dual-purpose. operationId: post3PIDs + deprecated: true security: - accessToken: [] parameters: @@ -120,13 +132,6 @@ paths: type: string description: The session identifier given by the identity server. required: ["client_secret", "id_server", "id_access_token", "sid"] - bind: - type: boolean - description: |- - Whether the homeserver should also bind this third party - identifier to the account's Matrix ID with the passed identity - server. Default: ``false``. - x-example: true required: ["three_pid_creds"] example: { "three_pid_creds": { @@ -134,8 +139,7 @@ paths: "id_access_token": "abc123_OpaqueString", "sid": "abc123987", "client_secret": "d0n'tT3ll" - }, - "bind": false + } } responses: 200: @@ -173,6 +177,113 @@ paths: "$ref": "definitions/errors/error.yaml" tags: - User data + "/account/3pid/add": + post: + summary: Adds contact information to the user's account. + description: |- + This API endpoint uses the `User-Interactive Authentication API`_. + + Adds contact information to the user's account. Homeservers should use 3PIDs added + through this endpoint for password resets instead of relying on the identity server. + + Homeservers should prevent the caller from adding a 3PID to their account if it has + already been added to another user's account on the homeserver. + operationId: add3PID + security: + - accessToken: [] + parameters: + - in: body + name: body + schema: + type: object + properties: + auth: + description: |- + Additional authentication information for the + user-interactive authentication API. + $ref: "definitions/auth_data.yaml" + client_secret: + type: string + description: The client secret used in the session with the homeserver. + sid: + type: string + description: The session identifier given by the homeserver. + required: ["client_secret", "sid"] + example: { + "sid": "abc123987", + "client_secret": "d0n'tT3ll" + } + responses: + 200: + description: The addition was successful. + examples: + application/json: {} + schema: + type: object + 401: + description: |- + The homeserver requires additional authentication information. + schema: + "$ref": "definitions/auth_response.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + "/account/3pid/bind": + post: + summary: Binds a 3PID to the user's account through an Identity Service. + description: |- + Binds a 3PID to the user's account through the specified identity server. + + Homeservers should not prevent this request from succeeding if another user + has bound the 3PID. Homeservers should simply proxy any errors received by + the identity server to the caller. + + Homeservers should track successful binds so they can be unbound later. + operationId: bind3PID + security: + - accessToken: [] + parameters: + - in: body + name: body + schema: + type: object + properties: + client_secret: + type: string + description: The client secret used in the session with the identity server. + id_server: + type: string + description: The identity server to use. + id_access_token: + type: string + description: |- + An access token previously registered with the identity server. Servers + can treat this as optional to distinguish between r0.5-compatible clients + and this specification version. + sid: + type: string + description: The session identifier given by the identity server. + required: ["client_secret", "id_server", "id_access_token", "sid"] + example: { + "id_server": "example.org", + "id_access_token": "abc123_OpaqueString", + "sid": "abc123987", + "client_secret": "d0n'tT3ll" + } + responses: + 200: + description: The addition was successful. + examples: + application/json: {} + schema: + type: object + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + tags: + - User data "/account/3pid/delete": post: summary: Deletes a third party identifier from the user's account @@ -308,12 +419,9 @@ paths: already associated with an account on this homeserver. This API should be used to request validation tokens when adding an email address to an account. This API's parameters and response are identical to that of - the |/register/email/requestToken|_ endpoint. The homeserver has the - choice of validating the email address itself, or proxying the request - to the ``/validate/email/requestToken`` Identity Service API as - identified by ``id_server``. It is imperative that the - homeserver keep a list of trusted Identity Servers and only proxies to - those that it trusts. + the |/register/email/requestToken|_ endpoint. The homeserver should validate + the email itself, either by sending a validation email itself or by using + a service it has control over. operationId: requestTokenTo3PIDEmail parameters: - in: body @@ -361,12 +469,9 @@ paths: already associated with an account on this homeserver. This API should be used to request validation tokens when adding a phone number to an account. This API's parameters and response are identical to that of - the |/register/msisdn/requestToken|_ endpoint. The homeserver has the - choice of validating the phone number itself, or proxying the request - to the ``/validate/msisdn/requestToken`` Identity Service API as - identified by ``id_server``. It is imperative that the - homeserver keep a list of trusted Identity Servers and only proxies to - those that it trusts. + the |/register/msisdn/requestToken|_ endpoint. The homeserver should validate + the phone number itself, either by sending a validation message itself or by using + a service it has control over. operationId: requestTokenTo3PIDMSISDN parameters: - in: body diff --git a/api/client-server/registration.yaml b/api/client-server/registration.yaml index a4c5bf71..8114299e 100644 --- a/api/client-server/registration.yaml +++ b/api/client-server/registration.yaml @@ -219,11 +219,8 @@ paths: description: |- The homeserver must check that the given email address is **not** already associated with an account on this homeserver. The homeserver - has the choice of validating the email address itself, or proxying the - request to the ``/validate/email/requestToken`` Identity Service API. The - request should be proxied to the domain that is sent by the client in - the ``id_server``. It is imperative that the homeserver keep a list of - trusted Identity Servers and only proxies to those it trusts. + should validate the email itself, either by sending a validation email + itself or by using a service it has control over. operationId: requestTokenToRegisterEmail parameters: - in: body @@ -272,11 +269,8 @@ paths: description: |- The homeserver must check that the given phone number is **not** already associated with an account on this homeserver. The homeserver - has the choice of validating the phone number itself, or proxying the - request to the ``/validate/msisdn/requestToken`` Identity Service API. The - request should be proxied to the domain that is sent by the client in - the ``id_server``. It is imperative that the homeserver keep a list of - trusted Identity Servers and only proxies to those it trusts. + should validate the phone number itself, either by sending a validation + message itself or by using a service it has control over. operationId: requestTokenToRegisterMSISDN parameters: - in: body @@ -388,12 +382,8 @@ paths: email to the given address prompting the user to create an account. ``M_THREEPID_IN_USE`` may not be returned. - The homeserver has the choice of validating the email address itself, - or proxying the request to the ``/validate/email/requestToken`` - Identity Service API. The request should be proxied to the domain that - is sent by the client in the ``id_server``. It is imperative that the - homeserver keep a list of trusted Identity Servers and only proxies to - those that it trusts. + The homeserver should validate the email itself, either by sending a + validation email itself or by using a service it has control over. .. |/register/email/requestToken| replace:: ``/register/email/requestToken`` @@ -451,12 +441,8 @@ paths: to the given phone number prompting the user to create an account. ``M_THREEPID_IN_USE`` may not be returned. - The homeserver has the choice of validating the phone number itself, or - proxying the request to the ``/validate/msisdn/requestToken`` Identity - Service API. The request should be proxied to the domain that is sent - by the client in the ``id_server``. It is imperative that the - homeserver keep a list of trusted Identity Servers and only proxies to - those that it trusts. + The homeserver should validate the phone number itself, either by sending a + validation message itself or by using a service it has control over. .. |/register/msisdn/requestToken| replace:: ``/register/msisdn/requestToken`` diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst index 81fb28a6..d1d5dee4 100644 --- a/specification/client_server_api.rst +++ b/specification/client_server_api.rst @@ -1138,7 +1138,15 @@ Adding Account Administrative Contact Information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A homeserver may keep some contact information for administrative use. -This is independent of any information kept by any identity servers. +This is independent of any information kept by any identity servers, though +can be proxied (bound) to the identity server in many cases. + +.. Note:: + This section deals with two terms: "add" and "bind". Where "add" (or "remove") + is used, it is speaking about an identifier that was not bound to an identity + server. As a result, "bind" (or "unbind") references an identifier that is found + in an identity server. Note that an identifer can be added and bound at the same + time, depending on context. {{administrative_contact_cs_http_api}} From 86eccc3bc8d5c1748a607a2c79067c1dae1b6923 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Fri, 25 Oct 2019 15:00:23 -0400 Subject: [PATCH 260/390] change how we mark the default key, and make sure clients trust keys --- proposals/1946-secure_server-side_storage.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index 49f115da..a2dfb381 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -48,11 +48,17 @@ A key with ID `abcdefg` is stored in `m.secret_storage.key.abcdefg` } ``` -If a key has the `name` property set to `m.default`, then this key is treated as -the default key for the account. The default key is the one that all secrets -will be encrypted with, and that clients will try to use to decrypt data with, -unless the user specifies otherwise. Only one key can be marked as the default -at a time. +A key can be marked as the "default" key by setting the user's account_data +with event type `m.secret_storage.default_key` to the ID of the key. The +default key will be used to encrypet all secrets that the user would expect to +be available on all their clients. Unless the user specifies otherwise, +clients will try to use the default key to decrypt secrets. + +Clients MUST ensure that the key is trusted before using it to encrypt secrets. +One way to do that is to have the client that creates the key sign the key +description (as signed JSON) using the user's master cross-signing key. +Another way to do that is to prompt the user to enter the passphrase and ensure +that the generated private key correponds to the public key. #### Secret storage From 4fadace9c823112122b407db7281e486e347b048 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 28 Oct 2019 12:48:59 -0400 Subject: [PATCH 261/390] Update proposals/1946-secure_server-side_storage.md Co-Authored-By: David Baker --- proposals/1946-secure_server-side_storage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index a2dfb381..832144d1 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -50,7 +50,7 @@ A key with ID `abcdefg` is stored in `m.secret_storage.key.abcdefg` A key can be marked as the "default" key by setting the user's account_data with event type `m.secret_storage.default_key` to the ID of the key. The -default key will be used to encrypet all secrets that the user would expect to +default key will be used to encrypt all secrets that the user would expect to be available on all their clients. Unless the user specifies otherwise, clients will try to use the default key to decrypt secrets. From a6977f19c53e6ccd366b972d95e7ce4bef5eb5f8 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 28 Oct 2019 12:58:27 -0400 Subject: [PATCH 262/390] Update proposals/1219-storing-megolm-keys-serverside.md Co-Authored-By: aditsachde <23707194+aditsachde@users.noreply.github.com> --- proposals/1219-storing-megolm-keys-serverside.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/1219-storing-megolm-keys-serverside.md b/proposals/1219-storing-megolm-keys-serverside.md index ff74eb16..95430fe2 100644 --- a/proposals/1219-storing-megolm-keys-serverside.md +++ b/proposals/1219-storing-megolm-keys-serverside.md @@ -129,7 +129,7 @@ results in 0, and ensure that the total length of the decoded string is 35 bytes. Clients must then remove the first two bytes and the last byte, and use the resulting string as the private key to decrypt backups. -#### Enconding the recovery key for server-side storage via MSC1946 +#### Encoding the recovery key for server-side storage via MSC1946 If MSC1946 is used to store the key on the server, it must be stored using the `account_data` type `m.megolm_backup.v1`. From 2bca44a2e524ef431ab18172c050f7ee7101ff9a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 28 Oct 2019 14:28:48 -0600 Subject: [PATCH 263/390] Remove extraneous backwards compatibility note --- api/client-server/administrative_contact.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/api/client-server/administrative_contact.yaml b/api/client-server/administrative_contact.yaml index 504e149d..9a59cb6b 100644 --- a/api/client-server/administrative_contact.yaml +++ b/api/client-server/administrative_contact.yaml @@ -258,9 +258,7 @@ paths: id_access_token: type: string description: |- - An access token previously registered with the identity server. Servers - can treat this as optional to distinguish between r0.5-compatible clients - and this specification version. + An access token previously registered with the identity server. sid: type: string description: The session identifier given by the identity server. From 6270983b7f0b6d4cba7c679e9dd9b7028d50dfdb Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 30 Oct 2019 14:36:47 -0500 Subject: [PATCH 264/390] MSC2334 - Change default room version to v5 Signed-off-by: Aaron Raimist --- proposals/2334-default-room-version-v5.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 proposals/2334-default-room-version-v5.md diff --git a/proposals/2334-default-room-version-v5.md b/proposals/2334-default-room-version-v5.md new file mode 100644 index 00000000..06ad652b --- /dev/null +++ b/proposals/2334-default-room-version-v5.md @@ -0,0 +1,17 @@ +# MSC2334 - Change default room version to v5 + +This MSC proposes changing the recommended default room version from v4 to v5. This implements steps 5 and 6 of the plan from [MSC2002](https://github.com/matrix-org/matrix-doc/issues/2002). + +Room version 5 enforces the `valid_until_ts` timestamp on signing keys as proposed in [MSC2076](https://github.com/matrix-org/matrix-doc/issues/2076). + +## Proposal + +When [MSC2077](https://github.com/matrix-org/matrix-doc/issues/2077) proposed room version 5, the default version continued to be v4 to allow old servers to continue to chat in newly created rooms. Server-Server API r0.1.2 (which contains the details on room version 5) and Synapse 1.0.0 (with support for room version 5) were both released more than 4 months ago which has given people plenty of time to update. Room version 5 should be the default room version so that newly created rooms enforce key-validity periods. + +## Potential issues + +Servers which do not support room version 5 will not be able to participate in newly created rooms on other servers. Hopefully this will encourage them to update their server. + +## Security considerations + +Room version 5 fixes known security vulnerabilities but that doesn't do much good if newly created rooms aren't using room version 5 by default. \ No newline at end of file From 14c32cc4bfa85a13a6350a4587959ef5a009f042 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 30 Oct 2019 16:20:17 -0500 Subject: [PATCH 265/390] Wrap lines --- proposals/2334-default-room-version-v5.md | 24 +++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/proposals/2334-default-room-version-v5.md b/proposals/2334-default-room-version-v5.md index 06ad652b..e511aafd 100644 --- a/proposals/2334-default-room-version-v5.md +++ b/proposals/2334-default-room-version-v5.md @@ -1,17 +1,29 @@ -# MSC2334 - Change default room version to v5 +# [MSC2334](https://github.com/matrix-org/matrix-doc/pull/2334) - Change default room version to v5 -This MSC proposes changing the recommended default room version from v4 to v5. This implements steps 5 and 6 of the plan from [MSC2002](https://github.com/matrix-org/matrix-doc/issues/2002). +This MSC proposes changing the recommended default room version from v4 to v5. +This implements steps 5 and 6 of the plan from +[MSC2002](https://github.com/matrix-org/matrix-doc/issues/2002). -Room version 5 enforces the `valid_until_ts` timestamp on signing keys as proposed in [MSC2076](https://github.com/matrix-org/matrix-doc/issues/2076). +Room version 5 enforces the `valid_until_ts` timestamp on signing keys as +proposed in [MSC2076](https://github.com/matrix-org/matrix-doc/issues/2076). ## Proposal -When [MSC2077](https://github.com/matrix-org/matrix-doc/issues/2077) proposed room version 5, the default version continued to be v4 to allow old servers to continue to chat in newly created rooms. Server-Server API r0.1.2 (which contains the details on room version 5) and Synapse 1.0.0 (with support for room version 5) were both released more than 4 months ago which has given people plenty of time to update. Room version 5 should be the default room version so that newly created rooms enforce key-validity periods. +When [MSC2077](https://github.com/matrix-org/matrix-doc/issues/2077) proposed +room version 5, the default version continued to be v4 to allow old servers to +continue to chat in newly created rooms. Server-Server API r0.1.2 (which +contains the details on room version 5) and Synapse 1.0.0 (with support for room +version 5) were both released more than 4 months ago which has given people +plenty of time to update. Room version 5 should be the default room version so +that newly created rooms enforce key-validity periods. ## Potential issues -Servers which do not support room version 5 will not be able to participate in newly created rooms on other servers. Hopefully this will encourage them to update their server. +Servers which do not support room version 5 will not be able to participate in +newly created rooms on other servers. Hopefully this will encourage them to +update their server. ## Security considerations -Room version 5 fixes known security vulnerabilities but that doesn't do much good if newly created rooms aren't using room version 5 by default. \ No newline at end of file +Room version 5 fixes known security vulnerabilities but that doesn't do much +good if newly created rooms aren't using room version 5 by default. \ No newline at end of file From ae163ab8180ab2072970f94f6426323a7de69405 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Fri, 1 Nov 2019 14:05:54 +0000 Subject: [PATCH 266/390] remove some lies about markdown being bad (#2337) --- meta/documentation_style.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/meta/documentation_style.rst b/meta/documentation_style.rst index e59ad229..e3d363c4 100644 --- a/meta/documentation_style.rst +++ b/meta/documentation_style.rst @@ -8,10 +8,7 @@ in. Format ------ -We use restructured text throughout all the documentation. You should NOT use -markdown (github-flavored or otherwise). This format was chosen partly because -Sphinx only supports RST. - +Documentation is written either in github-flavored markdown or RST. Sections -------- From 3e42cf528ba65f5025e534fac66cbad98529a5a5 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Fri, 1 Nov 2019 15:38:14 -0400 Subject: [PATCH 267/390] Apply suggestions from code review Co-Authored-By: David Baker --- proposals/1756-cross-signing.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 2e4f1b43..72b74121 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -19,7 +19,7 @@ MSC1680 is presented below. ## Proposal -Each user has three sets of key pairs: +Each user has three key pairs: - a *master* cross-signing key pair that is used to identify themselves and to sign their other cross-signing keys, @@ -29,7 +29,7 @@ Each user has three sets of key pairs: When one user (e.g. Alice) verifies another user's (Bob's) identity, Alice will sign Bob's master key with her user-signing key. (This will mean that verification methods will need to be modified to pass along the public part of -the master key.) Alice's device will trust Bob's device if: +Bob's master key.) Alice's device will trust Bob's device if: - Alice's device is using a master key that has signed her user-signing key, - Alice's user-signing key has signed Bob's master key, @@ -56,7 +56,7 @@ clients should still make efforts to store the private part securely, or not store it at all. Clients will need to balance the security of the keys with the usability of signing users and devices when performing key verification. -The private halves of a user's cross-signing keys be stored encrypted on the +The private halves of a user's cross-signing keys may be stored encrypted on the server so that they may be retrieved by new devices, or shared between devices using [MSC1946](https://github.com/matrix-org/matrix-doc/pull/1946). When handled in this way, the keys must be base64-encoded, and use the names From a51805cfe7f4666726be3642319b85894d287bf2 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Fri, 1 Nov 2019 23:10:14 -0400 Subject: [PATCH 268/390] Apply suggestions from code review Co-Authored-By: Kitsune Ral --- proposals/2313-ban-lists.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/proposals/2313-ban-lists.md b/proposals/2313-ban-lists.md index 284ebf23..ba0273a1 100644 --- a/proposals/2313-ban-lists.md +++ b/proposals/2313-ban-lists.md @@ -1,8 +1,8 @@ # MSC2313: Ban lists -Matrix is an open network and anyone can participate in it. As a result, a +Matrix is an open network and anyone can participate in it. As a result, a very wide range of content exists, and it is important to empower users to be -able to select which content they wish to see, and wish they to block. By +able to select which content they wish to see, and which they wish to block. By extension, room moderators and server admins should also be able to select which content they do not wish to host in their rooms and servers. @@ -11,7 +11,7 @@ should not be deciding what content is undesirable for any particular entity and should instead be empowering those entities to make their own decisions. This proposal introduces ban lists as a basic mechanism to help users manage this process, by providing a way of modelling sets of servers, rooms and users -which can then be used to make filtering decisions. This proposal makes no +which can then be used to make filtering decisions. This proposal makes no attempt at interpreting the model and actually making those decisions however. To reaffirm: where this proposal says that some content is undesirable it does not intend to @@ -22,9 +22,9 @@ use a ban list which prevents birthday cake from coming across their field of vi ## Proposal -Ban lists are represented in rooms, allowing for structures and concepts to be reused without +Ban lists are stored as room state events, allowing for structures and concepts to be reused without defining a new room version. This proposal does not make any restrictions on how the rooms -are configured, just that the state events described here are represented in the room. For +are configured, just that the state events described here are represented in a room. For example, a room which is invite only is just as valid as a room that is not: the important details are specific state events and not the accessibility, retention, or other aspects of the room. @@ -33,7 +33,7 @@ Ban lists are stored as `m.room.rule.` state events, with state keys being assigned by the sender. The `` is currently one of `user`, `room`, and `server`. Three fields are defined in `content`: -* `entity` (`string`) - **Required.** The entity/entities affected. Simple globs are supported +* `entity` (`string`) - **Required.** The entity/entities the recommendation applies to. Simple globs are supported for defining entities (`*` and `?` as wildcards, just like `m.room.server_acl`). * `recommendation` (`enum`) - **Required.** The action subscribed entities should take against the described entity. Currently only `m.ban` is defined (see below). @@ -103,7 +103,8 @@ a simple implementation is as follows: * Applied to a room: The user should be banned from the room (either on sight or immediately). * Applied to a server: The user should not be allowed to send invites to users on the server. * Is a room... - * Applied to a user: The user should leave the room and not join it (MSC2270-style ignore). + * Applied to a user: The user should leave the room and not join it + ([MSC2270](https://github.com/matrix-org/matrix-doc/pull/2270)-style ignore). * Applied to a room: No-op because a room cannot ban itself. * Applied to a server: The server should prevent users from joining the room and from receiving invites to it (similar to the `shutdown_room` API in Synapse). From 30106aaaacf6c2e2c10bcd95963202466a7a1b3b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 4 Nov 2019 21:14:18 +0200 Subject: [PATCH 269/390] Add section about backwards compatibility Signed-off-by: Tulir Asokan --- proposals/2244-mass-redactions.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/proposals/2244-mass-redactions.md b/proposals/2244-mass-redactions.md index b2d35dff..4f410356 100644 --- a/proposals/2244-mass-redactions.md +++ b/proposals/2244-mass-redactions.md @@ -17,6 +17,17 @@ It would be easiest to do this before MSC2174 is written into the spec, as then only one migration would be needed: from an event-level redacts string to a content-level redacts array. +### Backwards compatibility +There is no easy way to stay fully compatible with *older* clients, so the +proposed solution is to not support them. In order to not break old clients +completely, servers should still add a `redacts` string containing one of the +redacted event IDs to the top level of `m.room.redaction` events in *newer* +room versions when serving such events over the Client-Server API. + +Like MSC2174, for improved compatibility with *newer* clients, servers should +add a `redacts` array to the `content` of `m.room.redaction` events in *older* +room versions when serving such events over the Client-Server API. + ### Number of redactions Room v4+ event IDs are 44 bytes long, which means the federation event size limit would cap a single redaction event at a bit less than 1500 targets. From e95eafb2ba6b12d010b2b3c3e651424793ba2e72 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 4 Nov 2019 15:17:51 -0700 Subject: [PATCH 270/390] Clarify that submit_url is without authentication The request is authorized by its parameters, not by an additional access token. Fixes https://github.com/matrix-org/matrix-doc/issues/2298 --- api/client-server/administrative_contact.yaml | 7 ++++--- api/client-server/definitions/request_token_response.yaml | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/api/client-server/administrative_contact.yaml b/api/client-server/administrative_contact.yaml index 9a59cb6b..fc231b60 100644 --- a/api/client-server/administrative_contact.yaml +++ b/api/client-server/administrative_contact.yaml @@ -157,9 +157,10 @@ paths: An optional field containing a URL where the client must submit the validation token to, with identical parameters to the Identity Service API's ``POST - /validate/email/submitToken`` endpoint. The homeserver must - send this token to the user (if applicable), who should - then be prompted to provide it to the client. + /validate/email/submitToken`` endpoint (without the requirement + for an access token). The homeserver must send this token to the + user (if applicable), who should then be prompted to provide it + to the client. If this field is not present, the client can assume that verification will happen without the client's involvement diff --git a/api/client-server/definitions/request_token_response.yaml b/api/client-server/definitions/request_token_response.yaml index e47db8a0..45201a20 100644 --- a/api/client-server/definitions/request_token_response.yaml +++ b/api/client-server/definitions/request_token_response.yaml @@ -25,9 +25,9 @@ properties: description: |- An optional field containing a URL where the client must submit the validation token to, with identical parameters to the Identity Service - API's ``POST /validate/email/submitToken`` endpoint. The homeserver must - send this token to the user (if applicable), who should then be - prompted to provide it to the client. + API's ``POST /validate/email/submitToken`` endpoint (without the requirement + for an access token). The homeserver must send this token to the user (if + applicable), who should then be prompted to provide it to the client. If this field is not present, the client can assume that verification will happen without the client's involvement provided the homeserver From 1dfe2ade0839dd037d8deaa23282518fa253b943 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 4 Nov 2019 15:19:10 -0700 Subject: [PATCH 271/390] Changelog --- changelogs/client_server/newsfragments/2341.clarification | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/2341.clarification diff --git a/changelogs/client_server/newsfragments/2341.clarification b/changelogs/client_server/newsfragments/2341.clarification new file mode 100644 index 00000000..a941db1b --- /dev/null +++ b/changelogs/client_server/newsfragments/2341.clarification @@ -0,0 +1 @@ +Clarify that the ``submit_url`` field is without authentication. From 284b15495b2e0fe7dcefcb77f58643f59edc2952 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 4 Nov 2019 15:25:49 -0700 Subject: [PATCH 272/390] Clarify what format the `country` is on phone number inputs Fixes https://github.com/matrix-org/matrix-doc/issues/1982 --- api/identity/definitions/request_msisdn_validation.yaml | 4 ++-- changelogs/client_server/newsfragments/2342.clarification | 1 + specification/client_server_api.rst | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 changelogs/client_server/newsfragments/2342.clarification diff --git a/api/identity/definitions/request_msisdn_validation.yaml b/api/identity/definitions/request_msisdn_validation.yaml index 018bd733..93cea2ed 100644 --- a/api/identity/definitions/request_msisdn_validation.yaml +++ b/api/identity/definitions/request_msisdn_validation.yaml @@ -30,8 +30,8 @@ properties: country: type: string description: |- - The two-letter uppercase ISO country code that the number in - ``phone_number`` should be parsed as if it were dialled from. + The two-letter uppercase ISO-3166-1 alpha-2 country code that the + number in ``phone_number`` should be parsed as if it were dialled from. example: "GB" phone_number: type: string diff --git a/changelogs/client_server/newsfragments/2342.clarification b/changelogs/client_server/newsfragments/2342.clarification new file mode 100644 index 00000000..09f4b191 --- /dev/null +++ b/changelogs/client_server/newsfragments/2342.clarification @@ -0,0 +1 @@ +Clarify the expected phone number format. diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst index d1d5dee4..7e420b8c 100644 --- a/specification/client_server_api.rst +++ b/specification/client_server_api.rst @@ -1042,6 +1042,9 @@ wishes to canonicalise the phone number, then it can use the "phone": "" } +The ``country`` is the two-letter uppercase ISO-3166-1 alpha-2 country code +that the number in ``phone`` should be parsed as if it were dialled from. + Login ~~~~~ From b662a33081eaf894acf389721d0731cbbbf80f44 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 4 Nov 2019 15:47:25 -0700 Subject: [PATCH 273/390] Recommend that clients don't preview URLs in encrypted rooms Any stronger of a recommendation would probably require a MSC due to the behaviour change. Fixes https://github.com/matrix-org/matrix-doc/issues/2120 --- api/client-server/content-repo.yaml | 8 ++++++++ changelogs/client_server/newsfragments/2343.clarification | 1 + 2 files changed, 9 insertions(+) create mode 100644 changelogs/client_server/newsfragments/2343.clarification diff --git a/api/client-server/content-repo.yaml b/api/client-server/content-repo.yaml index a9a0c2f6..577e63b8 100644 --- a/api/client-server/content-repo.yaml +++ b/api/client-server/content-repo.yaml @@ -340,6 +340,14 @@ paths: "/preview_url": get: summary: "Get information about a URL for a client" + description: |- + Get information about a URL for the client. Typically this is called when a + client sees a URL in a message and wants to render a preview for the user. + + .. Note:: + Clients should consider avoiding this endpoint for URLs posted in encrypted + rooms. + operationId: getUrlPreview produces: ["application/json"] security: diff --git a/changelogs/client_server/newsfragments/2343.clarification b/changelogs/client_server/newsfragments/2343.clarification new file mode 100644 index 00000000..5b16858b --- /dev/null +++ b/changelogs/client_server/newsfragments/2343.clarification @@ -0,0 +1 @@ +Clarify that clients should consider not requesting URL previews in encrypted rooms. From 021c056efc200f968bb1c4c4eb99273c25e8247b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 4 Nov 2019 15:55:20 -0700 Subject: [PATCH 274/390] Add filter query param to /context This was missed as part of lazy-loading. Fixes https://github.com/matrix-org/matrix-doc/issues/2338 --- api/client-server/event_context.yaml | 11 +++++++++++ .../client_server/newsfragments/2344.clarification | 1 + 2 files changed, 12 insertions(+) create mode 100644 changelogs/client_server/newsfragments/2344.clarification diff --git a/api/client-server/event_context.yaml b/api/client-server/event_context.yaml index 3bea351c..e0c74cbf 100644 --- a/api/client-server/event_context.yaml +++ b/api/client-server/event_context.yaml @@ -59,6 +59,17 @@ paths: description: |- The maximum number of events to return. Default: 10. x-example: 3 + - in: query + name: filter + type: string + description: |- + The ID of a filter created using the filter API or a filter JSON + object encoded as a string. The server will detect whether it is + an ID or a JSON object by whether the first character is a ``"{"`` + open brace. + + See `Filtering <#filtering>`_ for more information. + x-example: "66696p746572" responses: 200: description: The events and state surrounding the requested event. diff --git a/changelogs/client_server/newsfragments/2344.clarification b/changelogs/client_server/newsfragments/2344.clarification new file mode 100644 index 00000000..8c92d31e --- /dev/null +++ b/changelogs/client_server/newsfragments/2344.clarification @@ -0,0 +1 @@ +Add missing information on how filters are meant to work with ``/context``. From 3d954f93b5240f84894ec0be555f5e5292f8acef Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 4 Nov 2019 15:57:58 -0700 Subject: [PATCH 275/390] Clarify that the room ID is the object key in /sync responses Fixes https://github.com/matrix-org/matrix-doc/issues/2269 --- api/client-server/sync.yaml | 9 ++++++--- .../client_server/newsfragments/2345.clarification | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 changelogs/client_server/newsfragments/2345.clarification diff --git a/api/client-server/sync.yaml b/api/client-server/sync.yaml index 00a332d9..bf804792 100644 --- a/api/client-server/sync.yaml +++ b/api/client-server/sync.yaml @@ -136,7 +136,8 @@ paths: title: Joined Rooms type: object description: |- - The rooms that the user has joined. + The rooms that the user has joined, mapped as room ID to + room information. additionalProperties: title: Joined Room type: object @@ -249,7 +250,8 @@ paths: title: Invited Rooms type: object description: |- - The rooms that the user has been invited to. + The rooms that the user has been invited to, mapped as room ID to + room information. additionalProperties: title: Invited Room type: object @@ -280,7 +282,8 @@ paths: title: Left rooms type: object description: |- - The rooms that the user has left or been banned from. + The rooms that the user has left or been banned from, mapped as room ID to + room information. additionalProperties: title: Left Room type: object diff --git a/changelogs/client_server/newsfragments/2345.clarification b/changelogs/client_server/newsfragments/2345.clarification new file mode 100644 index 00000000..9ddb8dd1 --- /dev/null +++ b/changelogs/client_server/newsfragments/2345.clarification @@ -0,0 +1 @@ +Clarify what the keys are for rooms in ``/sync``. From f012da17a164165c1f7b91dc13f507835e1a44be Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 5 Nov 2019 16:00:55 -0700 Subject: [PATCH 276/390] Explain why e2e previews are bad --- api/client-server/content-repo.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/client-server/content-repo.yaml b/api/client-server/content-repo.yaml index 577e63b8..d596dbda 100644 --- a/api/client-server/content-repo.yaml +++ b/api/client-server/content-repo.yaml @@ -346,7 +346,9 @@ paths: .. Note:: Clients should consider avoiding this endpoint for URLs posted in encrypted - rooms. + rooms. Encrypted rooms often contain more sensitive information the users + do not want to share with the homeserver, and this can mean that the URLs + being shared should also not be shared with the homeserver. operationId: getUrlPreview produces: ["application/json"] From e59bb20dc36b7620008407fa2d0f86f715ab9e48 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 5 Nov 2019 16:08:15 -0700 Subject: [PATCH 277/390] Try to clarify the filter information on /context --- api/client-server/event_context.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/api/client-server/event_context.yaml b/api/client-server/event_context.yaml index e0c74cbf..b1c9956c 100644 --- a/api/client-server/event_context.yaml +++ b/api/client-server/event_context.yaml @@ -63,10 +63,9 @@ paths: name: filter type: string description: |- - The ID of a filter created using the filter API or a filter JSON - object encoded as a string. The server will detect whether it is - an ID or a JSON object by whether the first character is a ``"{"`` - open brace. + A JSON ``RoomEventFilter`` to filter the returned events with. The + filter can be applied before or/and after the ``limit`` parameter - + whichever the homeserver prefers. See `Filtering <#filtering>`_ for more information. x-example: "66696p746572" From 3e035c917283c559966b6d6f5cc6d1180fa87cc6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 5 Nov 2019 16:14:04 -0700 Subject: [PATCH 278/390] Make room version 5 the default room version. As per [MSC2334](https://github.com/matrix-org/matrix-doc/pull/2334) --- specification/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/index.rst b/specification/index.rst index c337d417..728383ab 100644 --- a/specification/index.rst +++ b/specification/index.rst @@ -540,7 +540,7 @@ some other reason. Versions can switch between stable and unstable periodically for a variety of reasons, including discovered security vulnerabilities and age. Clients should not ask room administrators to upgrade their rooms if the room is -running a stable version. Servers SHOULD use room version 4 as the default room +running a stable version. Servers SHOULD use room version 5 as the default room version when creating new rooms. The available room versions are: From a1e5a96cf8eb00028f9ffb5b97f37c7e45d7fff6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 6 Nov 2019 10:42:08 -0700 Subject: [PATCH 279/390] Update api/client-server/event_context.yaml Co-Authored-By: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- api/client-server/event_context.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/client-server/event_context.yaml b/api/client-server/event_context.yaml index b1c9956c..f76cbff1 100644 --- a/api/client-server/event_context.yaml +++ b/api/client-server/event_context.yaml @@ -64,7 +64,7 @@ paths: type: string description: |- A JSON ``RoomEventFilter`` to filter the returned events with. The - filter can be applied before or/and after the ``limit`` parameter - + filter may be applied before or/and after the ``limit`` parameter - whichever the homeserver prefers. See `Filtering <#filtering>`_ for more information. From 9f01850f7aa6fa6ec96f383716b1b0eff326e309 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 6 Nov 2019 10:43:11 -0700 Subject: [PATCH 280/390] Add some words about where the filter is applied --- api/client-server/event_context.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/api/client-server/event_context.yaml b/api/client-server/event_context.yaml index f76cbff1..5fbfc198 100644 --- a/api/client-server/event_context.yaml +++ b/api/client-server/event_context.yaml @@ -64,8 +64,10 @@ paths: type: string description: |- A JSON ``RoomEventFilter`` to filter the returned events with. The - filter may be applied before or/and after the ``limit`` parameter - - whichever the homeserver prefers. + filter is only applied to ``events_before``, ``events_after``, and + ``state``. It is not applied to the ``event`` itself. The filter may + be applied before or/and after the ``limit`` parameter - whichever the + homeserver prefers. See `Filtering <#filtering>`_ for more information. x-example: "66696p746572" From df202dd493252b053c2c5841dde0431b2ecadbca Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 6 Nov 2019 12:37:46 -0700 Subject: [PATCH 281/390] Identity Service r0.3.0 release --- changelogs/identity_service.rst | 17 +++++++++++++++++ .../identity_service/newsfragments/2254.feature | 1 - .../identity_service/newsfragments/2255.new | 1 - .../identity_service/newsfragments/2258.new | 1 - .../identity_service/newsfragments/2287.new | 1 - 5 files changed, 17 insertions(+), 4 deletions(-) delete mode 100644 changelogs/identity_service/newsfragments/2254.feature delete mode 100644 changelogs/identity_service/newsfragments/2255.new delete mode 100644 changelogs/identity_service/newsfragments/2258.new delete mode 100644 changelogs/identity_service/newsfragments/2287.new diff --git a/changelogs/identity_service.rst b/changelogs/identity_service.rst index cb06709d..7cccc0cf 100644 --- a/changelogs/identity_service.rst +++ b/changelogs/identity_service.rst @@ -1,3 +1,20 @@ +r0.3.0 +====== + +New Endpoints +------------- + +- Add ``/account``, ``/account/register``, and ``/account/logout`` to authenticate with the identity server. (`#2255 `_) +- Add endpoints for accepting and handling terms of service. (`#2258 `_) +- Add ``/hash_details`` and a new ``/lookup`` endpoint for performing hashed association lookups. (`#2287 `_) + + +Backwards Compatible Changes +---------------------------- + +- Deprecate the v1 API in favour of an authenticated v2 API. (`#2254 `_) + + r0.2.1 ====== diff --git a/changelogs/identity_service/newsfragments/2254.feature b/changelogs/identity_service/newsfragments/2254.feature deleted file mode 100644 index 089d01fe..00000000 --- a/changelogs/identity_service/newsfragments/2254.feature +++ /dev/null @@ -1 +0,0 @@ -Deprecate the v1 API in favour of an authenticated v2 API. diff --git a/changelogs/identity_service/newsfragments/2255.new b/changelogs/identity_service/newsfragments/2255.new deleted file mode 100644 index fcb6ba88..00000000 --- a/changelogs/identity_service/newsfragments/2255.new +++ /dev/null @@ -1 +0,0 @@ -Add ``/account``, ``/account/register``, and ``/account/logout`` to authenticate with the identity server. diff --git a/changelogs/identity_service/newsfragments/2258.new b/changelogs/identity_service/newsfragments/2258.new deleted file mode 100644 index 06b9efff..00000000 --- a/changelogs/identity_service/newsfragments/2258.new +++ /dev/null @@ -1 +0,0 @@ -Add endpoints for accepting and handling terms of service. diff --git a/changelogs/identity_service/newsfragments/2287.new b/changelogs/identity_service/newsfragments/2287.new deleted file mode 100644 index 7d575bc9..00000000 --- a/changelogs/identity_service/newsfragments/2287.new +++ /dev/null @@ -1 +0,0 @@ -Add ``/hash_details`` and a new ``/lookup`` endpoint for performing hashed association lookups. From f361c756d2a748d84d6c2f541909e47490a4200e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 6 Nov 2019 12:43:13 -0700 Subject: [PATCH 282/390] Client Server r0.6.0 release --- changelogs/client_server.rst | 44 +++++++++++++++++++ .../newsfragments/2125.clarification | 1 - .../newsfragments/2129.clarification | 1 - .../newsfragments/2131.clarification | 1 - .../newsfragments/2132.clarification | 1 - .../newsfragments/2133.clarification | 1 - .../newsfragments/2136.clarification | 1 - .../newsfragments/2148.clarification | 1 - .../newsfragments/2152.clarification | 1 - .../newsfragments/2157.clarification | 1 - .../newsfragments/2204.clarification | 1 - .../newsfragments/2215.clarification | 1 - .../newsfragments/2223.clarification | 1 - .../client_server/newsfragments/2234.feature | 1 - .../newsfragments/2247.clarification | 1 - .../client_server/newsfragments/2255.breaking | 1 - .../client_server/newsfragments/2279.feature | 1 - .../client_server/newsfragments/2281.feature | 1 - .../client_server/newsfragments/2282.new | 1 - .../client_server/newsfragments/2310.feature | 1 - .../newsfragments/2341.clarification | 1 - .../newsfragments/2342.clarification | 1 - .../newsfragments/2343.clarification | 1 - .../newsfragments/2344.clarification | 1 - .../newsfragments/2345.clarification | 1 - 25 files changed, 44 insertions(+), 24 deletions(-) delete mode 100644 changelogs/client_server/newsfragments/2125.clarification delete mode 100644 changelogs/client_server/newsfragments/2129.clarification delete mode 100644 changelogs/client_server/newsfragments/2131.clarification delete mode 100644 changelogs/client_server/newsfragments/2132.clarification delete mode 100644 changelogs/client_server/newsfragments/2133.clarification delete mode 100644 changelogs/client_server/newsfragments/2136.clarification delete mode 100644 changelogs/client_server/newsfragments/2148.clarification delete mode 100644 changelogs/client_server/newsfragments/2152.clarification delete mode 100644 changelogs/client_server/newsfragments/2157.clarification delete mode 100644 changelogs/client_server/newsfragments/2204.clarification delete mode 100644 changelogs/client_server/newsfragments/2215.clarification delete mode 100644 changelogs/client_server/newsfragments/2223.clarification delete mode 100644 changelogs/client_server/newsfragments/2234.feature delete mode 100644 changelogs/client_server/newsfragments/2247.clarification delete mode 100644 changelogs/client_server/newsfragments/2255.breaking delete mode 100644 changelogs/client_server/newsfragments/2279.feature delete mode 100644 changelogs/client_server/newsfragments/2281.feature delete mode 100644 changelogs/client_server/newsfragments/2282.new delete mode 100644 changelogs/client_server/newsfragments/2310.feature delete mode 100644 changelogs/client_server/newsfragments/2341.clarification delete mode 100644 changelogs/client_server/newsfragments/2342.clarification delete mode 100644 changelogs/client_server/newsfragments/2343.clarification delete mode 100644 changelogs/client_server/newsfragments/2344.clarification delete mode 100644 changelogs/client_server/newsfragments/2345.clarification diff --git a/changelogs/client_server.rst b/changelogs/client_server.rst index c56a1073..47686221 100644 --- a/changelogs/client_server.rst +++ b/changelogs/client_server.rst @@ -1,3 +1,47 @@ +r0.6.0 +====== + +Breaking Changes +---------------- + +- Add a required ``id_access_token`` to many places which require an ``id_server`` parameter. (`#2255 `_) + + +New Endpoints +------------- + +- Add ``POST /account/3pid/unbind`` for removing a 3PID from an identity server. (`#2282 `_) + + +Backwards Compatible Changes +---------------------------- + +- Add ``M_USER_DEACTIVATED`` error code. (`#2234 `_) +- Remove ``bind_msisdn`` and ``bind_email`` from ``/register`` now that the identity server's bind endpoint requires authentication. (`#2279 `_) +- Add ``m.identity_server`` account data for tracking the user's preferred identity server. (`#2281 `_) +- Deprecate ``id_server`` and make it optional in several places. (`#2310 `_) + + +Spec Clarifications +------------------- + +- Add missing format fields to ``m.room.message$m.notice`` schema. (`#2125 `_) +- Remove "required" designation from the ``url`` field of certain ``m.room.message`` msgtypes. (`#2129 `_) +- Fix various typos throughout the specification. (`#2131 `_, `#2136 `_, `#2148 `_, `#2215 `_) +- Clarify the distinction between ``m.key.verification.start`` and its ``m.sas.v1`` variant. (`#2132 `_) +- Fix link to Olm signing specification. (`#2133 `_) +- Clarify the conditions for the ``.m.rule.room_one_to_one`` push rule. (`#2152 `_) +- Clarify the encryption algorithms supported by the device of the device keys example. (`#2157 `_) +- Clarify that ``/rooms/:roomId/event/:eventId`` returns a Matrix error. (`#2204 `_) +- Add a missing ``state_key`` check on ``.m.rule.tombstone``. (`#2223 `_) +- Fix the ``m.room_key_request`` ``action`` value, setting it from ``cancel_request`` to ``request_cancellation``. (`#2247 `_) +- Clarify that the ``submit_url`` field is without authentication. (`#2341 `_) +- Clarify the expected phone number format. (`#2342 `_) +- Clarify that clients should consider not requesting URL previews in encrypted rooms. (`#2343 `_) +- Add missing information on how filters are meant to work with ``/context``. (`#2344 `_) +- Clarify what the keys are for rooms in ``/sync``. (`#2345 `_) + + r0.5.0 ====== diff --git a/changelogs/client_server/newsfragments/2125.clarification b/changelogs/client_server/newsfragments/2125.clarification deleted file mode 100644 index c71cdfff..00000000 --- a/changelogs/client_server/newsfragments/2125.clarification +++ /dev/null @@ -1 +0,0 @@ -Add missing format fields to ``m.room.message$m.notice`` schema. diff --git a/changelogs/client_server/newsfragments/2129.clarification b/changelogs/client_server/newsfragments/2129.clarification deleted file mode 100644 index 9d67deac..00000000 --- a/changelogs/client_server/newsfragments/2129.clarification +++ /dev/null @@ -1 +0,0 @@ -Remove "required" designation from the ``url`` field of certain ``m.room.message`` msgtypes. diff --git a/changelogs/client_server/newsfragments/2131.clarification b/changelogs/client_server/newsfragments/2131.clarification deleted file mode 100644 index 3ccb2333..00000000 --- a/changelogs/client_server/newsfragments/2131.clarification +++ /dev/null @@ -1 +0,0 @@ -Fix various typos throughout the specification. diff --git a/changelogs/client_server/newsfragments/2132.clarification b/changelogs/client_server/newsfragments/2132.clarification deleted file mode 100644 index b8a4cc8a..00000000 --- a/changelogs/client_server/newsfragments/2132.clarification +++ /dev/null @@ -1 +0,0 @@ -Clarify the distinction between ``m.key.verification.start`` and its ``m.sas.v1`` variant. diff --git a/changelogs/client_server/newsfragments/2133.clarification b/changelogs/client_server/newsfragments/2133.clarification deleted file mode 100644 index 3a003179..00000000 --- a/changelogs/client_server/newsfragments/2133.clarification +++ /dev/null @@ -1 +0,0 @@ -Fix link to Olm signing specification. diff --git a/changelogs/client_server/newsfragments/2136.clarification b/changelogs/client_server/newsfragments/2136.clarification deleted file mode 100644 index 3ccb2333..00000000 --- a/changelogs/client_server/newsfragments/2136.clarification +++ /dev/null @@ -1 +0,0 @@ -Fix various typos throughout the specification. diff --git a/changelogs/client_server/newsfragments/2148.clarification b/changelogs/client_server/newsfragments/2148.clarification deleted file mode 100644 index 3ccb2333..00000000 --- a/changelogs/client_server/newsfragments/2148.clarification +++ /dev/null @@ -1 +0,0 @@ -Fix various typos throughout the specification. diff --git a/changelogs/client_server/newsfragments/2152.clarification b/changelogs/client_server/newsfragments/2152.clarification deleted file mode 100644 index 03fde9ff..00000000 --- a/changelogs/client_server/newsfragments/2152.clarification +++ /dev/null @@ -1 +0,0 @@ -Clarify the conditions for the ``.m.rule.room_one_to_one`` push rule. diff --git a/changelogs/client_server/newsfragments/2157.clarification b/changelogs/client_server/newsfragments/2157.clarification deleted file mode 100644 index 5c1d549b..00000000 --- a/changelogs/client_server/newsfragments/2157.clarification +++ /dev/null @@ -1 +0,0 @@ -Clarify the encryption algorithms supported by the device of the device keys example. diff --git a/changelogs/client_server/newsfragments/2204.clarification b/changelogs/client_server/newsfragments/2204.clarification deleted file mode 100644 index 7a66b08b..00000000 --- a/changelogs/client_server/newsfragments/2204.clarification +++ /dev/null @@ -1 +0,0 @@ -Clarify that ``/rooms/:roomId/event/:eventId`` returns a Matrix error. diff --git a/changelogs/client_server/newsfragments/2215.clarification b/changelogs/client_server/newsfragments/2215.clarification deleted file mode 100644 index 3ccb2333..00000000 --- a/changelogs/client_server/newsfragments/2215.clarification +++ /dev/null @@ -1 +0,0 @@ -Fix various typos throughout the specification. diff --git a/changelogs/client_server/newsfragments/2223.clarification b/changelogs/client_server/newsfragments/2223.clarification deleted file mode 100644 index 165b7e13..00000000 --- a/changelogs/client_server/newsfragments/2223.clarification +++ /dev/null @@ -1 +0,0 @@ -Add a missing ``state_key`` check on ``.m.rule.tombstone``. diff --git a/changelogs/client_server/newsfragments/2234.feature b/changelogs/client_server/newsfragments/2234.feature deleted file mode 100644 index bb1883b3..00000000 --- a/changelogs/client_server/newsfragments/2234.feature +++ /dev/null @@ -1 +0,0 @@ -Add ``M_USER_DEACTIVATED`` error code. diff --git a/changelogs/client_server/newsfragments/2247.clarification b/changelogs/client_server/newsfragments/2247.clarification deleted file mode 100644 index 43553399..00000000 --- a/changelogs/client_server/newsfragments/2247.clarification +++ /dev/null @@ -1 +0,0 @@ -Fix the ``m.room_key_request`` ``action`` value, setting it from ``cancel_request`` to ``request_cancellation``. diff --git a/changelogs/client_server/newsfragments/2255.breaking b/changelogs/client_server/newsfragments/2255.breaking deleted file mode 100644 index f9c8c6e1..00000000 --- a/changelogs/client_server/newsfragments/2255.breaking +++ /dev/null @@ -1 +0,0 @@ -Add a required ``id_access_token`` to many places which require an ``id_server`` parameter. diff --git a/changelogs/client_server/newsfragments/2279.feature b/changelogs/client_server/newsfragments/2279.feature deleted file mode 100644 index a1fdf168..00000000 --- a/changelogs/client_server/newsfragments/2279.feature +++ /dev/null @@ -1 +0,0 @@ -Remove ``bind_msisdn`` and ``bind_email`` from ``/register`` now that the identity server's bind endpoint requires authentication. diff --git a/changelogs/client_server/newsfragments/2281.feature b/changelogs/client_server/newsfragments/2281.feature deleted file mode 100644 index 7c235423..00000000 --- a/changelogs/client_server/newsfragments/2281.feature +++ /dev/null @@ -1 +0,0 @@ -Add ``m.identity_server`` account data for tracking the user's preferred identity server. diff --git a/changelogs/client_server/newsfragments/2282.new b/changelogs/client_server/newsfragments/2282.new deleted file mode 100644 index 3395758d..00000000 --- a/changelogs/client_server/newsfragments/2282.new +++ /dev/null @@ -1 +0,0 @@ -Add ``POST /account/3pid/unbind`` for removing a 3PID from an identity server. diff --git a/changelogs/client_server/newsfragments/2310.feature b/changelogs/client_server/newsfragments/2310.feature deleted file mode 100644 index 0169d23b..00000000 --- a/changelogs/client_server/newsfragments/2310.feature +++ /dev/null @@ -1 +0,0 @@ -Deprecate ``id_server`` and make it optional in several places. diff --git a/changelogs/client_server/newsfragments/2341.clarification b/changelogs/client_server/newsfragments/2341.clarification deleted file mode 100644 index a941db1b..00000000 --- a/changelogs/client_server/newsfragments/2341.clarification +++ /dev/null @@ -1 +0,0 @@ -Clarify that the ``submit_url`` field is without authentication. diff --git a/changelogs/client_server/newsfragments/2342.clarification b/changelogs/client_server/newsfragments/2342.clarification deleted file mode 100644 index 09f4b191..00000000 --- a/changelogs/client_server/newsfragments/2342.clarification +++ /dev/null @@ -1 +0,0 @@ -Clarify the expected phone number format. diff --git a/changelogs/client_server/newsfragments/2343.clarification b/changelogs/client_server/newsfragments/2343.clarification deleted file mode 100644 index 5b16858b..00000000 --- a/changelogs/client_server/newsfragments/2343.clarification +++ /dev/null @@ -1 +0,0 @@ -Clarify that clients should consider not requesting URL previews in encrypted rooms. diff --git a/changelogs/client_server/newsfragments/2344.clarification b/changelogs/client_server/newsfragments/2344.clarification deleted file mode 100644 index 8c92d31e..00000000 --- a/changelogs/client_server/newsfragments/2344.clarification +++ /dev/null @@ -1 +0,0 @@ -Add missing information on how filters are meant to work with ``/context``. diff --git a/changelogs/client_server/newsfragments/2345.clarification b/changelogs/client_server/newsfragments/2345.clarification deleted file mode 100644 index 9ddb8dd1..00000000 --- a/changelogs/client_server/newsfragments/2345.clarification +++ /dev/null @@ -1 +0,0 @@ -Clarify what the keys are for rooms in ``/sync``. From 50647e646e108d672eccbccae025791dbb29d038 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 6 Nov 2019 12:44:41 -0700 Subject: [PATCH 283/390] Add r0.6.0 to the list of spec versions. --- specification/client_server_api.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst index 7e420b8c..84cde946 100644 --- a/specification/client_server_api.rst +++ b/specification/client_server_api.rst @@ -45,6 +45,7 @@ Other versions of this specification The following other versions are also available, in reverse chronological order: - `HEAD `_: Includes all changes since the latest versioned release. +- `r0.6.0 `_ - `r0.5.0 `_ - `r0.4.0 `_ - `r0.3.0 `_ From 7351c0cd1e552e691ef338887d7112b72f6849c9 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 6 Nov 2019 12:47:09 -0700 Subject: [PATCH 284/390] Add missing versions to identity spec --- specification/identity_service_api.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specification/identity_service_api.rst b/specification/identity_service_api.rst index f389cbc7..7e5a0f02 100644 --- a/specification/identity_service_api.rst +++ b/specification/identity_service_api.rst @@ -51,6 +51,8 @@ Other versions of this specification The following other versions are also available, in reverse chronological order: - `HEAD `_: Includes all changes since the latest versioned release. +- `r0.3.0 `_ +- `r0.2.1 `_ - `r0.2.0 `_ - `r0.1.0 `_ From 5b6d6fe498196735deed1397f377cc8d67bc65ce Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 7 Nov 2019 08:49:59 -0700 Subject: [PATCH 285/390] Update changelogs/client_server.rst Co-Authored-By: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> --- changelogs/client_server.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/client_server.rst b/changelogs/client_server.rst index 47686221..a5173fd0 100644 --- a/changelogs/client_server.rst +++ b/changelogs/client_server.rst @@ -4,7 +4,7 @@ r0.6.0 Breaking Changes ---------------- -- Add a required ``id_access_token`` to many places which require an ``id_server`` parameter. (`#2255 `_) +- Add ``id_access_token`` as a required request parameter to a few endpoints which require an ``id_server`` parameter as part of `MSC2140 `_. (`#2255 `_) New Endpoints From 5cc5908dd5ca6362314b14637d4fe1a03e6cb3e7 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 7 Nov 2019 18:45:29 -0500 Subject: [PATCH 286/390] Update proposals/1946-secure_server-side_storage.md Co-Authored-By: Matthew Hodgson --- proposals/1946-secure_server-side_storage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index 832144d1..4c5f9b4d 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -201,7 +201,7 @@ devices that it did not send an `m.secret.request` event to. Clients MUST ensure that they only share secrets with other devices that are allowed to see them. For example, clients SHOULD only share secrets with -devices that are verified and MAY prompt the user to confirm sharing the +the user’s own devices that are verified and MAY prompt the user to confirm sharing the secret. If a feature allows secrets to be stored or shared, then for consistency it From e8ce135a411e5fa2715f9d1cbd57f0fc232bc98f Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 7 Nov 2019 19:00:33 -0500 Subject: [PATCH 287/390] add clarification and examples --- proposals/1946-secure_server-side_storage.md | 29 ++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index 4c5f9b4d..aa4fd8f9 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -57,8 +57,9 @@ clients will try to use the default key to decrypt secrets. Clients MUST ensure that the key is trusted before using it to encrypt secrets. One way to do that is to have the client that creates the key sign the key description (as signed JSON) using the user's master cross-signing key. -Another way to do that is to prompt the user to enter the passphrase and ensure -that the generated private key correponds to the public key. +Another way to do that is to prompt the user to enter the passphrase used to +generate the encryption key and ensure that the generated private key +corresponds to the public key. #### Secret storage @@ -79,6 +80,8 @@ Example: Some secret is encrypted using keys with ID `key_id_1` and `key_id_2`: +`org.example.some.secret`: + ```json { "encrypted": { @@ -95,6 +98,28 @@ Some secret is encrypted using keys with ID `key_id_1` and `key_id_2`: } ``` +and the key descriptions for the keys would be: + +`m.secret_storage.key.key_id_1`: + +```json +{ + "name": "Some key", + "algorithm": "m.secret_storage.v1.curve25519-aes-sha2", + // ... other properties according to algorithm +} +``` + +`m.secret_storage.key.key_id_2`: + +```json +{ + "name": "Some other key", + "algorithm": "m.secret_storage.v1.curve25519-aes-sha2", + // ... other properties according to algorithm +} +``` + #### Encryption algorithms ##### `m.secret_storage.v1.curve25519-aes-sha2` From dcbdb94693edcff28efbb813302c90248258cbcc Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 8 Nov 2019 09:03:49 -0700 Subject: [PATCH 288/390] Add explanation --- proposals/2284-optional-identity-server-discovery.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/2284-optional-identity-server-discovery.md b/proposals/2284-optional-identity-server-discovery.md index 2da7018f..940f3381 100644 --- a/proposals/2284-optional-identity-server-discovery.md +++ b/proposals/2284-optional-identity-server-discovery.md @@ -2,7 +2,9 @@ Currently the specification requires that clients `FAIL_ERROR` (hard failure - do not continue) when the `.well-known` file for `m.identity_server` points to somewhere invalid or is invalid -itself. +itself. This can cause problems for clients if they either don't need an identity server to +function (and are forced to validate it anyways) or the client ends up having to disable all +their login UX because the identity server is misconfigured/down. This proposal aims to change that by allowing clients to make a conscious decision to continue with the invalid identity server configuration, provided the homeserver configuration is valid. From daa610659b334c25a68adfd6516ef086fa104013 Mon Sep 17 00:00:00 2001 From: Alexey Murz Korepov Date: Sun, 10 Nov 2019 16:45:01 +0300 Subject: [PATCH 289/390] =?UTF-8?q?Typo:=20later=20user=20=C2=BB=20later?= =?UTF-8?q?=20use?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- specification/modules/content_repo.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/modules/content_repo.rst b/specification/modules/content_repo.rst index 1e3d1866..192250d2 100644 --- a/specification/modules/content_repo.rst +++ b/specification/modules/content_repo.rst @@ -19,7 +19,7 @@ Content repository .. _module:content: The content repository (or "media repository") allows users to upload -files to their homeserver for later user. For example, files which the +files to their homeserver for later use. For example, files which the user wants to send to a room would be uploaded here, as would an avatar the user wants to use. From fc793557f8d7e0a71ff671fb97b86d4529910e43 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 12 Nov 2019 10:48:38 -0500 Subject: [PATCH 290/390] make the default key event an object --- proposals/1946-secure_server-side_storage.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index aa4fd8f9..fd907e53 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -49,10 +49,11 @@ A key with ID `abcdefg` is stored in `m.secret_storage.key.abcdefg` ``` A key can be marked as the "default" key by setting the user's account_data -with event type `m.secret_storage.default_key` to the ID of the key. The -default key will be used to encrypt all secrets that the user would expect to -be available on all their clients. Unless the user specifies otherwise, -clients will try to use the default key to decrypt secrets. +with event type `m.secret_storage.default_key` to an object that has the ID of +the key as its `key` property. The default key will be used to encrypt all +secrets that the user would expect to be available on all their clients. +Unless the user specifies otherwise, clients will try to use the default key to +decrypt secrets. Clients MUST ensure that the key is trusted before using it to encrypt secrets. One way to do that is to have the client that creates the key sign the key From d93d8fe9b5237a787361b80be7c4885c51ca684c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 13 Nov 2019 10:36:41 -0700 Subject: [PATCH 291/390] Add a changelog --- changelogs/client_server/newsfragments/2351.clarification | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/2351.clarification diff --git a/changelogs/client_server/newsfragments/2351.clarification b/changelogs/client_server/newsfragments/2351.clarification new file mode 100644 index 00000000..902a9c3f --- /dev/null +++ b/changelogs/client_server/newsfragments/2351.clarification @@ -0,0 +1 @@ +Fix various spelling errors throughout the specification. From f610235cd65392b1a63d8017cd78758d8f59dbda Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 18 Nov 2019 09:38:17 -0700 Subject: [PATCH 292/390] Rename the MSC to be better targeted --- ...sts.md => 2313-moderation-policy-rooms.md} | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) rename proposals/{2313-ban-lists.md => 2313-moderation-policy-rooms.md} (88%) diff --git a/proposals/2313-ban-lists.md b/proposals/2313-moderation-policy-rooms.md similarity index 88% rename from proposals/2313-ban-lists.md rename to proposals/2313-moderation-policy-rooms.md index ba0273a1..5899a233 100644 --- a/proposals/2313-ban-lists.md +++ b/proposals/2313-moderation-policy-rooms.md @@ -1,4 +1,4 @@ -# MSC2313: Ban lists +# MSC2313: Moderation policies as rooms (ban lists) Matrix is an open network and anyone can participate in it. As a result, a very wide range of content exists, and it is important to empower users to be @@ -9,8 +9,8 @@ which content they do not wish to host in their rooms and servers. The protocol's position in this solution should be one of neutrality: it should not be deciding what content is undesirable for any particular entity and should instead be empowering those entities to make their own decisions. This -proposal introduces ban lists as a basic mechanism to help users manage this -process, by providing a way of modelling sets of servers, rooms and users +proposal introduces "moderation policy rooms" as a basic mechanism to help users +manage this process, by providing a way of modelling sets of servers, rooms and users which can then be used to make filtering decisions. This proposal makes no attempt at interpreting the model and actually making those decisions however. @@ -18,18 +18,18 @@ To reaffirm: where this proposal says that some content is undesirable it does n bias the reader into what that could entail. Undesirability is purely in the hands of the entity perceiving the content. For example, someone who believes birthday cake is undesirable is perfectly valid in taking that position and is encouraged by this proposal to set up or -use a ban list which prevents birthday cake from coming across their field of view. +use a policy room which prevents birthday cake from coming across their field of view. ## Proposal -Ban lists are stored as room state events, allowing for structures and concepts to be reused without -defining a new room version. This proposal does not make any restrictions on how the rooms -are configured, just that the state events described here are represented in a room. For -example, a room which is invite only is just as valid as a room that is not: the important -details are specific state events and not the accessibility, retention, or other aspects -of the room. +Moderation policy lists, also known as ban lists in this proposal, are stored as room state events, +allowing for structures and concepts to be reused without defining a new room version. This +proposal does not make any restrictions on how the rooms are configured, just that the state +events described here are represented in a room. For example, a room which is invite only is +just as valid as a room that is not: the important details are specific state events and not +the accessibility, retention, or other aspects of the room. -Ban lists are stored as `m.room.rule.` state events, with state keys being arbitrary IDs +Ban lists are stored as `m.moderation.rule.` state events, with state keys being arbitrary IDs assigned by the sender. The `` is currently one of `user`, `room`, and `server`. Three fields are defined in `content`: @@ -48,7 +48,7 @@ An example set of minimal state events for banning `@alice:example.org`, `!matri ```json [ { - "type": "m.room.rule.user", + "type": "m.moderation.rule.user", "state_key": "rule_1", "content": { "entity": "@alice:example.org", @@ -57,7 +57,7 @@ An example set of minimal state events for banning `@alice:example.org`, `!matri } }, { - "type": "m.room.rule.room", + "type": "m.moderation.rule.room", "state_key": "rule_2", "content": { "entity": "!matrix:example.org", @@ -66,7 +66,7 @@ An example set of minimal state events for banning `@alice:example.org`, `!matri } }, { - "type": "m.room.rule.server", + "type": "m.moderation.rule.server", "state_key": "rule_3", "content": { "entity": "evil.example.org", @@ -75,7 +75,7 @@ An example set of minimal state events for banning `@alice:example.org`, `!matri } }, { - "type": "m.room.rule.server", + "type": "m.moderation.rule.server", "state_key": "rule_4", "content": { "entity": "*.evil.example.org", @@ -193,7 +193,7 @@ This proposal is partially implemented by [mjolnir](https://github.com/matrix-or using the `org.matrix.mjolnir.*` namespace until this becomes stable. This results in the following mappings: -* `m.room.rule.user` => `org.matrix.mjolnir.rule.user` -* `m.room.rule.room` => `org.matrix.mjolnir.rule.room` -* `m.room.rule.server` => `org.matrix.mjolnir.rule.server` +* `m.moderation.rule.user` => `org.matrix.mjolnir.rule.user` +* `m.moderation.rule.room` => `org.matrix.mjolnir.rule.room` +* `m.moderation.rule.server` => `org.matrix.mjolnir.rule.server` * `m.ban` => `org.matrix.mjolnir.ban` From c7b3d998537d21694a166b4a6a4cf0490ebc0cc2 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 19 Nov 2019 11:53:07 -0700 Subject: [PATCH 293/390] m.policy.rule won the debate --- proposals/2313-moderation-policy-rooms.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/proposals/2313-moderation-policy-rooms.md b/proposals/2313-moderation-policy-rooms.md index 5899a233..0fe9d7b1 100644 --- a/proposals/2313-moderation-policy-rooms.md +++ b/proposals/2313-moderation-policy-rooms.md @@ -29,7 +29,7 @@ events described here are represented in a room. For example, a room which is in just as valid as a room that is not: the important details are specific state events and not the accessibility, retention, or other aspects of the room. -Ban lists are stored as `m.moderation.rule.` state events, with state keys being arbitrary IDs +Ban lists are stored as `m.policy.rule.` state events, with state keys being arbitrary IDs assigned by the sender. The `` is currently one of `user`, `room`, and `server`. Three fields are defined in `content`: @@ -48,7 +48,7 @@ An example set of minimal state events for banning `@alice:example.org`, `!matri ```json [ { - "type": "m.moderation.rule.user", + "type": "m.policy.rule.user", "state_key": "rule_1", "content": { "entity": "@alice:example.org", @@ -57,7 +57,7 @@ An example set of minimal state events for banning `@alice:example.org`, `!matri } }, { - "type": "m.moderation.rule.room", + "type": "m.policy.rule.room", "state_key": "rule_2", "content": { "entity": "!matrix:example.org", @@ -66,7 +66,7 @@ An example set of minimal state events for banning `@alice:example.org`, `!matri } }, { - "type": "m.moderation.rule.server", + "type": "m.policy.rule.server", "state_key": "rule_3", "content": { "entity": "evil.example.org", @@ -75,7 +75,7 @@ An example set of minimal state events for banning `@alice:example.org`, `!matri } }, { - "type": "m.moderation.rule.server", + "type": "m.policy.rule.server", "state_key": "rule_4", "content": { "entity": "*.evil.example.org", @@ -193,7 +193,7 @@ This proposal is partially implemented by [mjolnir](https://github.com/matrix-or using the `org.matrix.mjolnir.*` namespace until this becomes stable. This results in the following mappings: -* `m.moderation.rule.user` => `org.matrix.mjolnir.rule.user` -* `m.moderation.rule.room` => `org.matrix.mjolnir.rule.room` -* `m.moderation.rule.server` => `org.matrix.mjolnir.rule.server` +* `m.policy.rule.user` => `org.matrix.mjolnir.rule.user` +* `m.policy.rule.room` => `org.matrix.mjolnir.rule.room` +* `m.policy.rule.server` => `org.matrix.mjolnir.rule.server` * `m.ban` => `org.matrix.mjolnir.ban` From 3b2f897c7e7d5a5ff40e99228967c06b762861cf Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 20 Nov 2019 11:48:36 -0500 Subject: [PATCH 294/390] Apply suggestions from code review Co-Authored-By: Matthew Hodgson --- proposals/1756-cross-signing.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 72b74121..05e02a4f 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -69,11 +69,11 @@ keys, respectively. Currently, users will only be allowed to see * signatures made by their own master, self-signing or user-signing keys, * signatures made by their own devices about their own master key, -* signatures made by other users' self-signing keys about the other users' own +* signatures made by other users' self-signing keys about their own respective devices, -* signatures made by other users' master keys about the other users' +* signatures made by other users' master keys about their respective self-signing key, or -* signatures made by other users' devices about the other users' master keys. +* signatures made by other users' devices about their respective master keys. This is done in order to preserve the privacy of social connections. Future proposals may define mechanisms for distributing signatures to other users in @@ -84,7 +84,7 @@ order to allow for other web-of-trust use cases. Users who have verified individual devices may wish to migrate these verifications to use cross-signing instead. In order to aid with this, signatures of a user's master key, made by their own devices, may be uploaded -to the server. If another client sees that the user's master key has a valid +to the server. If another user's client sees that that a given user's master key has a valid signature from a device that was previously verified, then the client may choose to trust and sign the master key. The client should take precautions to ensure that a stolen device cannot be used to cause it to trust a malicious From 82260689c946dda84f5dc4231e50755e86226719 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 20 Nov 2019 11:55:10 -0500 Subject: [PATCH 295/390] add link to migrating from device verifications --- proposals/1756-cross-signing.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 05e02a4f..f4ae6011 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -73,7 +73,9 @@ Currently, users will only be allowed to see devices, * signatures made by other users' master keys about their respective self-signing key, or -* signatures made by other users' devices about their respective master keys. +* signatures made by other users' devices about their respective master keys + (these signatures are used for [migrating from device + verifications](#migrating-from-device-verifications)). This is done in order to preserve the privacy of social connections. Future proposals may define mechanisms for distributing signatures to other users in From 29745d04fb669ce2b23c470ca68f164eb05590f6 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Tue, 26 Nov 2019 11:19:57 +0000 Subject: [PATCH 296/390] erikj/msc_membership_reasons --- proposals/2367-membership-reasons.md | 77 ++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 proposals/2367-membership-reasons.md diff --git a/proposals/2367-membership-reasons.md b/proposals/2367-membership-reasons.md new file mode 100644 index 00000000..479fce34 --- /dev/null +++ b/proposals/2367-membership-reasons.md @@ -0,0 +1,77 @@ +# Allowing Reasons in all Membership Events + +Currently users can specify a reason for kicking or banning users in a room +that both the target and other users in a room can see. This is useful to +explain *why* an action without having to send separate messages. + +The proposal extends this to all events, including invites, invite rejections +and leaves for similar reasons. + +## Proposal + +Allow `reason` field to be specified for all of the following APIs: + +``` +POST /_matrix/client/r0/rooms/{roomId}/invite +POST /_matrix/client/r0/rooms/{roomId}/leave +POST /_matrix/client/r0/rooms/{roomId}/kick +POST /_matrix/client/r0/rooms/{roomId}/ban +POST /_matrix/client/r0/rooms/{roomId}/unban +POST /_matrix/client/r0/rooms/{roomId}/join +POST /_matrix/client/r0/join/{roomIdOrAlias} +``` + +If specified the `reason` field will be added to the generated membership +event's content. + +Clients may choose to display the reason for membership events in a room, +though may not do so if e.g. they have collapsed a set of membership changes. + +Clients should not display an invite reason by default to the invitee as this +allows a classic abuse and harassment vector. However, clients may wish to show +invite reasons from known¹ senders, or have a button that allows viewing any +invite reason. + +## Use Cases + +Some basic use cases and examples are given below. + +### Kick/ban + +Kicking and banning already allow specifying a reason to allow giving a reason +for the moderation action (e.g. "Banned for spamming"). + +### Leaving a Room + +A user may wish to leave a room e.g. because the room is no longer relevant +to them, allowing them to specify a reason means they can easily step out of +the room quietly without having to send a message to explain their actions. + +### Invite + +This can be useful to give some context for an invite, e.g. "Alice invites Bob +to get some feedback on the membership reasons MSC". + +### Rejecting an Invite + +If Alice has invited Bob (and many others) to a room to discuss going to a +concert then Bob may wish to simply reject the invite if he can't make it. +Adding a "will be out of town" reason to the rejection helps Alice to know why +her invite was rejected. + +### Joining room + +Adding a reason for joining could be used e.g. by automated bots to say why +they're joining. For example a bridge bot may join a room when asked to bridge +the room to an external network, in which case they may have a reason such as +"BridgeBot joined to bridge the room to RemoteNetwork at the request of Alice". + +## Potential Issues + +The main issue here is ensuring that the invite reason cannot be used as an +abuse vector, however if clients follow the recommendations above this concern +should be mitigated. + +--- + +¹ This is left up to implementations to decide, if they wish to do so. From f054ffe7607e4a15c52385d60b51dc1ffe74b5e2 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Tue, 26 Nov 2019 11:40:38 +0000 Subject: [PATCH 297/390] Add note about using PUT /state/m.room.member/ --- proposals/2367-membership-reasons.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/2367-membership-reasons.md b/proposals/2367-membership-reasons.md index 479fce34..b43c909c 100644 --- a/proposals/2367-membership-reasons.md +++ b/proposals/2367-membership-reasons.md @@ -19,11 +19,15 @@ POST /_matrix/client/r0/rooms/{roomId}/ban POST /_matrix/client/r0/rooms/{roomId}/unban POST /_matrix/client/r0/rooms/{roomId}/join POST /_matrix/client/r0/join/{roomIdOrAlias} +PUT /_matrix/client/r0/rooms/{roomId}/state/m.room.member/{userID} ``` If specified the `reason` field will be added to the generated membership event's content. +*Note: `/state/m.room.member` API currently allows this as clients can specify +arbitrary content already* + Clients may choose to display the reason for membership events in a room, though may not do so if e.g. they have collapsed a set of membership changes. From ad383351c2e5263556ee73ee95082e17c4ff2483 Mon Sep 17 00:00:00 2001 From: Isaiah Inuwa Date: Fri, 29 Nov 2019 10:16:23 -0600 Subject: [PATCH 298/390] Rename nonce to txn_id. Reorganize for clarity. Signed-Off-By: Isaiah Inuwa --- specification/client_server_api.rst | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst index 84cde946..50ffff8f 100644 --- a/specification/client_server_api.rst +++ b/specification/client_server_api.rst @@ -740,22 +740,23 @@ To use this authentication type, clients should submit an auth dict as follows: "session": "" } -The ``nonce`` should be a random string generated by the client for the -request. The same ``nonce`` should be used if retrying the request. A client may receive a login ``token`` via some external service, such as email or SMS. Note that a login token is separate from an access token, the latter providing general authentication to various API endpoints. -The ``txn_id`` may be used by the server to disallow other devices from using -the token, thus providing "single use" tokens while still allowing the device -to retry the request. This would be done by tying the token to the ``txn_id`` +Additionally, the server must encode the user id in the ``token``; there is +therefore no need for the client to submit a separate username. + +The ``txn_id`` should be a random string generated by the client for the +request. The same ``txn_id`` should be used if retrying the request. The +``txn_id`` may be used by the server to disallow other devices from using the +token, thus providing "single use" tokens while still allowing the device to +retry the request. This would be done by tying the token to the ``txn_id`` server side, as well as potentially invalidating the token completely once the device has successfully logged in (e.g. when we receive a request from the newly provisioned access_token). -The server must encode the user id in the ``token``. There is therefore no need -for the client to submit a separate username. OAuth2-based <<<<<<<<<<<< From b7234c7fd34b4788f1a4158cff0148c028a652f2 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Fri, 29 Nov 2019 15:51:50 -0500 Subject: [PATCH 299/390] add unstable prefix section to proposal template --- proposals/0000-proposal-template.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/proposals/0000-proposal-template.md b/proposals/0000-proposal-template.md index cf79ed99..e50aed8b 100644 --- a/proposals/0000-proposal-template.md +++ b/proposals/0000-proposal-template.md @@ -93,3 +93,13 @@ of concerns where possible.* By having a template available, people would know what the desired detail for a proposal is. This is not considered a risk because it is important that people understand the proposal process from start to end. + +## Unstable prefix + +*If a proposal is implemented before it is included in the spec, then implementers must ensure that the +implementation is compatible with the final version that lands in the spec. This generally means that +experimental implementations should use `/unstable` endpoints, and use vendor prefixes where necessary. +For more information, see [MSC2324](https://github.com/matrix-org/matrix-doc/pull/2324). This section +should be used to document things such as what endpoints and names are being used while the feature is +in development, the name of the unstable feature flag to use to detect support for the feature, or what +migration steps are needed to switch to newer versions of the proposal.* From c549bf201bd85ca9630032bb5ae5af32d88cc879 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 2 Dec 2019 14:54:59 +0000 Subject: [PATCH 300/390] Clarify POST instead of GET for federation key query --- proposals/1756-cross-signing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index f4ae6011..98041987 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -216,12 +216,12 @@ response: } ``` -Similarly, the federation endpoints `GET /user/keys/query` and `POST +Similarly, the federation endpoints `POST /user/keys/query` and `POST /user/devices/{userId}` will include the master and self-signing keys. (It will not include the user-signing key because it is not intended to be visible to other users.) -`POST /keys/query` +`POST /user/keys/query` ``` json { From 71faffc9ed9bbdbe020101e3ff19543f034804e6 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 2 Dec 2019 14:59:48 +0000 Subject: [PATCH 301/390] Update example to show keys wrapped by user ID --- proposals/1756-cross-signing.md | 40 ++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 98041987..4b5904bc 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -432,27 +432,31 @@ response: } } }, - "master_key": { - "user_id": "@alice:example.com", - "usage": ["master"], - "keys": { - "ed25519:base64+master+public+key": "base64+master+public+key" - }, - "signatures": { - "@alice:example.com": { - "ed25519:HIJKLMN": "base64+signature+of+master+key" + "master_keys": { + "@alice:example.com": { + "user_id": "@alice:example.com", + "usage": ["master"], + "keys": { + "ed25519:base64+master+public+key": "base64+master+public+key" + }, + "signatures": { + "@alice:example.com": { + "ed25519:HIJKLMN": "base64+signature+of+master+key" + } } } }, - "self_signing_key": { - "user_id": "@alice:example.com", - "usage": ["self_signing"], - "keys": { - "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key" - }, - "signatures": { - "@alice:example.com": { - "ed25519:base64+master+public+key": "base64+signature" + "self_signing_keys": { + "@alice:example.com": { + "user_id": "@alice:example.com", + "usage": ["self_signing"], + "keys": { + "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key" + }, + "signatures": { + "@alice:example.com": { + "ed25519:base64+master+public+key": "base64+signature" + } } } } From 1cf322bc2a546a870a80f03297af020e802e7b43 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 3 Dec 2019 11:43:56 -0700 Subject: [PATCH 302/390] MSC2324: Facilitating early releases of software dependent on spec (#2324) --- proposals/2324-when-to-ship.md | 124 +++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 proposals/2324-when-to-ship.md diff --git a/proposals/2324-when-to-ship.md b/proposals/2324-when-to-ship.md new file mode 100644 index 00000000..1335bfab --- /dev/null +++ b/proposals/2324-when-to-ship.md @@ -0,0 +1,124 @@ +# MSC2324: Facilitating early releases of software dependent on spec + +*Note*: This is a process change MSC, not a change to the spec itself. + +There's currently an unanswered question by the spec process: when is it +safe to start using stable endpoints or to present a feature as "stable"? +Historically this question would receive very different answers depending +on who you asked, so in an effort to come up with a concise answer the +following process change is proposed. + +## Proposal + +The new process, from start to finish, is proposed as: + +1. Have an idea for a feature. +2. Optionally: implement the feature using unstable endpoints, vendor prefixes, + and unstable feature flags as appropriate. + * When using unstable endpoints, they MUST include a vendor prefix. For + example: `/_matrix/client/unstable/com.example/login`. Vendor prefixes + throughout this proposal always use the Java package naming convention. + * Unstable endpoints **do not** inherit from stable (`/r0`) APIs. Previously, + one could access the entirety of the Matrix API through `/unstable` however + this is generally considered a bad practice. Therefore an implementation + can no longer assume that because its feature-specific endpoint exists that + any other endpoint will exist in the same unstable namespace. + * If the client needs to be sure the server supports the feature, an unstable + feature flag that MUST be vendor prefixed is to be used. This kind of flag + shows up in the `unstable_features` field of `/versions` as, for example, + `com.example.new_login`. + * You can ship the feature at *any* time, so long as you are able to accept + the technical debt that results from needing to provide adequate backwards + and forwards compatibility for the vendor prefixed implementation. The + implementation MUST support the flag disappearing and be generally safe for + users. Note that implementations early in the MSC review process may also be + required to provide backwards compatibility with earlier editions of the + proposal. + * If you don't want to support the technical debt (or if it's impossible to + provide adequate backwards/forwards compatibility - e.g. a user authentication + change which can't be safely rolled back), do not implement the feature and + wait for Step 7. + * If at any point the idea changes, the feature flag should also change so + that implementations can adapt as needed. +3. In parallel, or ahead of implementation, open an MSC and solicit review. +4. Before a FCP (Final Comment Period) can be called, the Spec Core Team will + require that evidence to prove the MSC works be presented. A typical example + of this is an implementation of the MSC (which does not necessarily need to have been shipped anywhere). +5. FCP is gone through, and assuming nothing is flagged the MSC lands. +6. A spec PR is written to incorporate the changes into Matrix. +7. A spec release happens. +8. Implementations switch to using stable prefixes (e.g.: `/r0`) if the server + supports the specification version released. If the server doesn't advertise + the specification version, but does have the feature flag, unstable prefixes + should still be used. +9. A transition period of about 2 months starts immediately after the spec release, before + implementations start to loudly encourage other implementations to switch to stable + endpoints. For example, the Synapse team should start asking the Riot team to + support the stable endpoints (as per Step 8) 2 months after the spec release if they + haven't already. + +It's worth repeating that this process generally only applies if the implementation +wants to ship the feature ahead of the spec being available. By doing so, it takes +on the risk that the spec/MSC may change and it must adapt. If the implementation +is unable to take on that risk, or simply doesn't mind waiting, it should go through +the spec process without shipping an unstable implementation. + +To help MSCs get incorporated by implementations as stable features, the spec core +team plans to release the specification more often. How often is undefined and is +largely a case-by-case basis. + +To reiterate: + +* Implementations MUST NOT use stable endpoints before the MSC is in the spec. This + includes NOT using stable endpoints post-FCP. +* Implementations CAN ship features that are exposed by default to users before an + MSC has been merged to the spec, provided they follow the process above. +* Implementations SHOULD be wary of the technical debt they are incurring by moving + faster than the spec. + +To clarify: + +* The vendor prefix is chosen by the developer of the feature, using the Java package + naming convention. For example, `org.matrix` is the foundation's vendor prefix. +* The vendor prefixes, unstable feature flags, and unstable endpoints should be included + in the MSC so other developers can benefit. The MSC MUST still say what the stable + endpoints are to look like. + +### Specific examples outside of the client-server API + +There are some instances where a change might be made outside of the client-server API, +which is where much of this proposal is targetted. The general spirit of the process +should be followed where possible, if implementations decide to work ahead of spec releases. + +#### Room versions + +When a new room version is needed, implementations MUST use vendor-prefixed versions +before using the namespace reserved for Matrix (see https://matrix.org/docs/spec/#room-versions). +A room version is considered released once it is listed as an "available room version" in +the spec. Often a new room version is accompanied with a server-server API release, but +doesn't have to be. + +#### Server-server / Identity / Push / Appservice API + +These APIs don't yet have a `/versions` endpoint or similar. Typically behaviour changes in +these APIs are introduced with backwards compatibility in mind (try X and if that fails fall +back to Y) and therefore don't always need a flag to indicate support. If a flag were to +be required, an `unstable_features` or similar array would need to be exposed somewhere. + +#### Changes to request/response parameters + +Parameters being added to request/response bodies and query strings MUST be vendor-prefixed +per the proposed process. For example, a new JSON field might be `{"org.matrix.example": true}` +with the proposal being for `example` being added. A query string parameter would be prefixed +the same way: `?org.matrix.example=true`. + +If the MSC is simply adding fields to already-versioned endpoints, it does not need to put +the whole endpoint into the `/unstable` namespace provided the new parameters are prefixed +appropriately. + +#### .well-known and other APIs that can't be versioned + +Best effort is appreciated. Typically these endpoints will be receiving minor behavioural +changes or new fields. New fields should be appropriately prefixed, and behaviour changes +should be rolled out cautiously by implementations (waiting until after FCP has concluded +is probably best to ensure there's no major problems with the new behaviour). From c2ffef051f7c67028c41d5a87a25a712fa1e8fcf Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Thu, 5 Dec 2019 09:27:37 -0700 Subject: [PATCH 303/390] Clarify description of user directory Signed off by Stuart Mumford --- api/client-server/users.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/client-server/users.yaml b/api/client-server/users.yaml index 98744719..da8cf03f 100644 --- a/api/client-server/users.yaml +++ b/api/client-server/users.yaml @@ -31,7 +31,7 @@ paths: post: summary: Searches the user directory. description: |- - Performs a search for users on the homeserver. The homeserver may + Performs a search for users. The homeserver may determine which subset of users are searched, however the homeserver MUST at a minimum consider the users the requesting user shares a room with and those who reside in public rooms (known to the homeserver). From 4d0bd5b9e206619ed7f3075d90f13a53be82b109 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sat, 7 Dec 2019 15:12:55 -0700 Subject: [PATCH 304/390] Changelog --- changelogs/client_server/newsfragments/2381.clarification | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/2381.clarification diff --git a/changelogs/client_server/newsfragments/2381.clarification b/changelogs/client_server/newsfragments/2381.clarification new file mode 100644 index 00000000..70bd6d18 --- /dev/null +++ b/changelogs/client_server/newsfragments/2381.clarification @@ -0,0 +1 @@ +Minor clarification for what the user directory searches. From 7cfc4b09a69766c66484771918e804b7fed79e51 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sat, 7 Dec 2019 15:31:42 -0700 Subject: [PATCH 305/390] Changelog --- changelogs/client_server/newsfragments/2369.clarification | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/2369.clarification diff --git a/changelogs/client_server/newsfragments/2369.clarification b/changelogs/client_server/newsfragments/2369.clarification new file mode 100644 index 00000000..c7fd0fd9 --- /dev/null +++ b/changelogs/client_server/newsfragments/2369.clarification @@ -0,0 +1 @@ +Minor clarifications to token-based User-Interactive Authentication. From ccc7bcabbc2625618bd884b641b78de1e52eba90 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sat, 7 Dec 2019 15:32:43 -0700 Subject: [PATCH 306/390] id -> ID while we're here --- specification/client_server_api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst index 50ffff8f..eb32d3b4 100644 --- a/specification/client_server_api.rst +++ b/specification/client_server_api.rst @@ -745,7 +745,7 @@ A client may receive a login ``token`` via some external service, such as email or SMS. Note that a login token is separate from an access token, the latter providing general authentication to various API endpoints. -Additionally, the server must encode the user id in the ``token``; there is +Additionally, the server must encode the user ID in the ``token``; there is therefore no need for the client to submit a separate username. The ``txn_id`` should be a random string generated by the client for the From 47b94b62c48fee542e8c727069046d5bb6f235b2 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sat, 7 Dec 2019 15:57:24 -0700 Subject: [PATCH 307/390] Add some github stuff (PR templates, funding) --- .github/FUNDING.yml | 2 ++ .../PULL_REQUEST_TEMPLATE/ready-proposal.md | 19 ++++++++++++++++++ .github/PULL_REQUEST_TEMPLATE/spec-change.md | 16 +++++++++++++++ .github/PULL_REQUEST_TEMPLATE/wip-proposal.md | 20 +++++++++++++++++++ 4 files changed, 57 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE/ready-proposal.md create mode 100644 .github/PULL_REQUEST_TEMPLATE/spec-change.md create mode 100644 .github/PULL_REQUEST_TEMPLATE/wip-proposal.md diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..afc29f01 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +patreon: matrixdotorg +liberapay: matrixdotorg diff --git a/.github/PULL_REQUEST_TEMPLATE/ready-proposal.md b/.github/PULL_REQUEST_TEMPLATE/ready-proposal.md new file mode 100644 index 00000000..afa808b8 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/ready-proposal.md @@ -0,0 +1,19 @@ +--- +name: Proposal ready for review +about: A proposal that is ready for review by the core team and community. +title: '' +labels: proposal, proposal-in-review +assignees: '' + +--- + + + +### Pull Request Checklist + + + +* [ ] Pull request includes a [changelog file](https://github.com/matrix-org/matrix-doc/blob/master/CONTRIBUTING.rst#adding-to-the-changelog) +* [ ] Pull request includes a [sign off](https://github.com/matrix-org/matrix-doc/blob/master/CONTRIBUTING.rst#sign-off) +* [ ] Pull request includes ['Rendered' link](https://matrix.org/docs/spec/proposals#process) above. +* [ ] Pull request title and file name include this PR's number as the MSC number. diff --git a/.github/PULL_REQUEST_TEMPLATE/spec-change.md b/.github/PULL_REQUEST_TEMPLATE/spec-change.md new file mode 100644 index 00000000..d2f6e874 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/spec-change.md @@ -0,0 +1,16 @@ +--- +name: Spec clarification/not a proposal +about: A change that's not a spec proposal, such as a clarification to the spec itself. +title: '' +labels: '' +assignees: '' + +--- + +### Pull Request Checklist + + + +* [ ] Pull request includes a [changelog file](https://github.com/matrix-org/matrix-doc/blob/master/CONTRIBUTING.rst#adding-to-the-changelog) +* [ ] Pull request includes a [sign off](https://github.com/matrix-org/matrix-doc/blob/master/CONTRIBUTING.rst#sign-off) +* [ ] Pull request is classified as ['other changes'](https://github.com/matrix-org/matrix-doc/blob/master/CONTRIBUTING.rst#other-changes) diff --git a/.github/PULL_REQUEST_TEMPLATE/wip-proposal.md b/.github/PULL_REQUEST_TEMPLATE/wip-proposal.md new file mode 100644 index 00000000..34e26187 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/wip-proposal.md @@ -0,0 +1,20 @@ +--- +name: WIP Proposal +about: A proposal that isn't quite ready for formal review yet. +title: '[WIP] Your Proposal Title' +labels: proposal +assignees: '' + +--- + + + +### Pull Request Checklist + + + +* [ ] Pull request includes a [changelog file](https://github.com/matrix-org/matrix-doc/blob/master/CONTRIBUTING.rst#adding-to-the-changelog) +* [ ] Pull request includes a [sign off](https://github.com/matrix-org/matrix-doc/blob/master/CONTRIBUTING.rst#sign-off) +* [ ] A ['Rendered' link](https://matrix.org/docs/spec/proposals#process) above. +* [ ] Update the title and file name of your proposal to match this PR's number (after opening). +* [ ] Ask in [#matrix-spec:matrix.org](https://matrix.to/#/#matrix-spec:matrix.org) to get this marked as ready for review, once it is ready for review. From 3aba6e3eebc4a75051ab38046cb9b5815d84fc85 Mon Sep 17 00:00:00 2001 From: Carolin Beer Date: Sun, 15 Dec 2019 14:37:10 +0100 Subject: [PATCH 308/390] Change state_type to event_type I couldn't find any other reference to a state_type within the entire specification. I assume this is supposed to be the event_type? This aligns with the description of changes resulting from a state update. --- specification/rooms/v1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/rooms/v1.rst b/specification/rooms/v1.rst index a720b41a..10eee54d 100644 --- a/specification/rooms/v1.rst +++ b/specification/rooms/v1.rst @@ -94,7 +94,7 @@ results of the resolution so far. passes authentication in :math:`R` and add it to :math:`R`. A *conflict* occurs between states where those states have different -``event_ids`` for the same ``(state_type, state_key)``. The events thus +``event_ids`` for the same ``(event_type, state_key)``. The events thus affected are said to be *conflicting* events. From 912b3cbc95734a4b301375afce0241078d4bb89d Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Dec 2019 16:29:56 -0700 Subject: [PATCH 309/390] Move auth events selection to a more appropriate section of the spec This was pretty buried, and feels like a common question. --- .../newsfragments/2392.clarification | 1 + specification/server_server_api.rst | 44 ++++++++++--------- 2 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 changelogs/server_server/newsfragments/2392.clarification diff --git a/changelogs/server_server/newsfragments/2392.clarification b/changelogs/server_server/newsfragments/2392.clarification new file mode 100644 index 00000000..b09420e4 --- /dev/null +++ b/changelogs/server_server/newsfragments/2392.clarification @@ -0,0 +1 @@ +Move auth event selection to a more obvious location. diff --git a/specification/server_server_api.rst b/specification/server_server_api.rst index 62f6916a..a3a57dbf 100644 --- a/specification/server_server_api.rst +++ b/specification/server_server_api.rst @@ -391,28 +391,9 @@ creating a new event in this room should populate the new event's | E4 -.. _`auth events selection`: - -The ``auth_events`` field of a PDU identifies the set of events which give the -sender permission to send the event. The ``auth_events`` for the -``m.room.create`` event in a room is empty; for other events, it should be the -following subset of the room state: - -- The ``m.room.create`` event. -- The current ``m.room.power_levels`` event, if any. -- The sender's current ``m.room.member`` event, if any. -- If type is ``m.room.member``: - - - The target's current ``m.room.member`` event, if any. - - If ``membership`` is ``join`` or ``invite``, the current - ``m.room.join_rules`` event, if any. - - If membership is ``invite`` and ``content`` contains a - ``third_party_invite`` property, the current - ``m.room.third_party_invite`` event with ``state_key`` matching - ``content.third_party_invite.signed.token``, if any. - For a full schema of what a PDU looks like, see the `room version specification`_. + Checks performed on receipt of a PDU ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -466,6 +447,29 @@ specified above. Each room version can have a different algorithm for how the rules work, and which rules are applied. For more detailed information, please see the `room version specification`_. + +Auth events selection +^^^^^^^^^^^^^^^^^^^^^ + +The ``auth_events`` field of a PDU identifies the set of events which give the +sender permission to send the event. The ``auth_events`` for the +``m.room.create`` event in a room is empty; for other events, it should be the +following subset of the room state: + +- The ``m.room.create`` event. +- The current ``m.room.power_levels`` event, if any. +- The sender's current ``m.room.member`` event, if any. +- If type is ``m.room.member``: + + - The target's current ``m.room.member`` event, if any. + - If ``membership`` is ``join`` or ``invite``, the current + ``m.room.join_rules`` event, if any. + - If membership is ``invite`` and ``content`` contains a + ``third_party_invite`` property, the current + ``m.room.third_party_invite`` event with ``state_key`` matching + ``content.third_party_invite.signed.token``, if any. + + Rejection +++++++++ From f59aa563dd6626bbff56bae92d4af82cb56a367f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 1 Jan 2020 11:59:37 -0700 Subject: [PATCH 310/390] Add missing tags to push rules endpoints Without the tags, the endpoints don't end up in the swagger. No changelog for this because it doesn't affect the spec itself. --- api/client-server/pushrules.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/client-server/pushrules.yaml b/api/client-server/pushrules.yaml index a1cbc354..32c3c9a1 100644 --- a/api/client-server/pushrules.yaml +++ b/api/client-server/pushrules.yaml @@ -498,6 +498,8 @@ paths: type: boolean description: Whether the push rule is enabled or not. required: ["enabled"] + tags: + - Push notifications put: summary: "Enable or disable a push rule." description: |- @@ -601,6 +603,8 @@ paths: items: type: string required: ["actions"] + tags: + - Push notifications put: summary: "Set the actions for a push rule." description: |- From 82a626a98a0f6b9ad6cf8db14576c3c10dfa54d2 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Thu, 2 Jan 2020 02:30:24 -0600 Subject: [PATCH 311/390] Fix link to v4 event ID format in identity service spec Signed-off-by: Aaron Raimist --- specification/identity_service_api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/identity_service_api.rst b/specification/identity_service_api.rst index 7e5a0f02..704f763e 100644 --- a/specification/identity_service_api.rst +++ b/specification/identity_service_api.rst @@ -336,7 +336,7 @@ it would first format the query as ``alice@example.org email ThePepperGoesHere`` After formatting each query, the string is run through SHA-256 as defined by `RFC 4634 `_. The resulting bytes are then encoded using URL-Safe `Unpadded Base64`_ (similar to `room version 4's -event ID format <../../rooms/v4.html#event-ids>`_). +event ID format <../rooms/v4.html#event-ids>`_). An example set of queries when using the pepper ``matrixrocks`` would be:: From 8e888b916cdb00fdaed76810bc9a5abc0a9b19bd Mon Sep 17 00:00:00 2001 From: Isaiah Inuwa Date: Sat, 18 Jan 2020 09:09:42 -0600 Subject: [PATCH 312/390] Fix spelling mistakes. --- api/client-server/definitions/request_email_validation.yaml | 2 +- api/client-server/definitions/request_msisdn_validation.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/client-server/definitions/request_email_validation.yaml b/api/client-server/definitions/request_email_validation.yaml index 2d789d05..d24c42b5 100644 --- a/api/client-server/definitions/request_email_validation.yaml +++ b/api/client-server/definitions/request_email_validation.yaml @@ -23,7 +23,7 @@ allOf: include a port. This parameter is ignored when the homeserver handles 3PID verification. - This parameter is deprected with a plan to be removed in a future specification + This parameter is deprecated with a plan to be removed in a future specification version for ``/account/password`` and ``/register`` requests. example: "id.example.com" id_access_token: diff --git a/api/client-server/definitions/request_msisdn_validation.yaml b/api/client-server/definitions/request_msisdn_validation.yaml index 54988fd4..54897e63 100644 --- a/api/client-server/definitions/request_msisdn_validation.yaml +++ b/api/client-server/definitions/request_msisdn_validation.yaml @@ -23,7 +23,7 @@ allOf: include a port. This parameter is ignored when the homeserver handles 3PID verification. - This parameter is deprected with a plan to be removed in a future specification + This parameter is deprecated with a plan to be removed in a future specification version for ``/account/password`` and ``/register`` requests. example: "id.example.com" id_access_token: From 715d2d674b42ba982c3f06543bcbc3fb0cb736f9 Mon Sep 17 00:00:00 2001 From: Isaiah Inuwa Date: Sat, 18 Jan 2020 14:12:24 -0600 Subject: [PATCH 313/390] Add changelog for spelling. --- changelogs/client_server/newsfragments/2415.clarification | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/2415.clarification diff --git a/changelogs/client_server/newsfragments/2415.clarification b/changelogs/client_server/newsfragments/2415.clarification new file mode 100644 index 00000000..d1a7dbfe --- /dev/null +++ b/changelogs/client_server/newsfragments/2415.clarification @@ -0,0 +1 @@ +Fix misspelling of _deprecated_. \ No newline at end of file From b47de28da543f165795812afa366cacd64211a01 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 24 Jan 2020 00:40:33 +0100 Subject: [PATCH 314/390] MSC2422: Allow color on font tag Signed-off-by: Nicolas Werner --- .../2422-allow-color-attribute-on-font-tag.md | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 proposals/2422-allow-color-attribute-on-font-tag.md diff --git a/proposals/2422-allow-color-attribute-on-font-tag.md b/proposals/2422-allow-color-attribute-on-font-tag.md new file mode 100644 index 00000000..4287a696 --- /dev/null +++ b/proposals/2422-allow-color-attribute-on-font-tag.md @@ -0,0 +1,38 @@ +# MSC2422: Allow `color` as attribute for `` in messages + +Currently the spec requires you to use `data-mx-color` instead of the standard +`color` html attribute for the `` tag. This is probably done to make it +consistent with ``, where you may not want to allow a generic style tag for. + +On the other hand the /rainbow command on almost every client just uses the +`color` attribute of the `` tag. While some clients support +`data-mx-color` (i.e. Riot Web), most clients don't. Most clients support +rendering `color` however. + +It would probably be for the best to allow or even prefer `color` on the +`` tag. + +## Proposal + +Add the `color` attribute to the allowed attributes of `` in section +13.2.1.7. Allow only hexcodes as allowed value for this attribute for now. + +## Potential issues + +- We now have a redundant attribute in the spec. While it matches, what the + clients currently do, it may be better to fix each client instead. +- Clients may not sanitize the color attribute and will let other color values + through, increasing compatibility issues again. +- Clients may never support the data-mx-* attributes now. +- Old messages could loose their color +- This proposal doesn't touch span at all, maybe it should? + +## Alternatives + +- fix the clients +- remove the `data-mx-color` and `data-mx-bg-color` attributes entirely, leaving + us just with `color` for `` +- Add a section to tell the clients to prefer `color` over `mx-data-color` +- Spec an entirely different format for messages (that would probably not make + this proposal obsolete) + From 6911171e83fbf322e42565f1f6a217ea24d73924 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Wed, 5 Feb 2020 22:29:56 +0100 Subject: [PATCH 315/390] Fix key export format example Signed-off-by: Nicolas Werner --- .../newsfragments/2430.clarification | 1 + .../modules/end_to_end_encryption.rst | 32 +++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) create mode 100644 changelogs/client_server/newsfragments/2430.clarification diff --git a/changelogs/client_server/newsfragments/2430.clarification b/changelogs/client_server/newsfragments/2430.clarification new file mode 100644 index 00000000..56be0c1a --- /dev/null +++ b/changelogs/client_server/newsfragments/2430.clarification @@ -0,0 +1 @@ +Fix key export format example to match the specification. diff --git a/specification/modules/end_to_end_encryption.rst b/specification/modules/end_to_end_encryption.rst index 7758e2c1..af7b2c59 100644 --- a/specification/modules/end_to_end_encryption.rst +++ b/specification/modules/end_to_end_encryption.rst @@ -844,24 +844,22 @@ Example: .. code:: json - { - "sessions": [ - { - "algorithm": "m.megolm.v1.aes-sha2", - "forwarding_curve25519_key_chain": [ - "hPQNcabIABgGnx3/ACv/jmMmiQHoeFfuLB17tzWp6Hw" - ], - "room_id": "!Cuyf34gef24t:localhost", - "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU", - "sender_claimed_keys": { - "ed25519": "", - }, - "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ", - "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf..." + [ + { + "algorithm": "m.megolm.v1.aes-sha2", + "forwarding_curve25519_key_chain": [ + "hPQNcabIABgGnx3/ACv/jmMmiQHoeFfuLB17tzWp6Hw" + ], + "room_id": "!Cuyf34gef24t:localhost", + "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU", + "sender_claimed_keys": { + "ed25519": "", }, - ... - ] - } + "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ", + "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf..." + }, + ... + ] Messaging Algorithms -------------------- From 6bd4b3c08a7a5137700c8fbf76008b27de92efe0 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 10 Feb 2020 20:29:38 +0000 Subject: [PATCH 316/390] MSC: Updated semantics for publishing room aliases --- proposals/2432-revised-alias-publishing.md | 225 +++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 proposals/2432-revised-alias-publishing.md diff --git a/proposals/2432-revised-alias-publishing.md b/proposals/2432-revised-alias-publishing.md new file mode 100644 index 00000000..8ef08f7d --- /dev/null +++ b/proposals/2432-revised-alias-publishing.md @@ -0,0 +1,225 @@ +# MSC2432: Updated semantics for publishing room aliases + +This MSC offers an alternative to [MSC2260](https://github.com/matrix-org/matrix-doc/issues/2260). + +## Background + +The [`m.room.aliases`](https://matrix.org/docs/spec/client_server/r0.6.0#m-room-aliases) +state event exists to list the available aliases for a given room. This serves +two purposes: + + * It allows existing members of the room to discover alternative aliases, + which may be useful for them to pass this knowledge on to others trying to + join. + + * Secondarily, it helps to educate users about how Matrix works by + illustrating multiple aliases per room and giving a perception of the size + of the network. + +However, it has problems: + + * Any user in the entire ecosystem can create aliases for rooms, which are + then unilaterally added to `m.room.aliases`, and room admins are unable to + remove them. This is an abuse + vector (https://github.com/matrix-org/matrix-doc/issues/625). + + * For various reasons, the `m.room.aliases` event tends to get out of sync + with the actual aliases (https://github.com/matrix-org/matrix-doc/issues/2262). + +## Proposal + +We propose that that room moderators should be able to manually curate a list +of "official" aliases for their room, instead of matrix servers automatically +publishing lists of all room aliases into the room state. No particular +guarantees are offered that this alias list is entirely accurate: it becomes +room moderators' responsibilty to keep it so. + +Meanwhile, the aliases that map to a given room on a given server become +the ultimate responsibility of the administrators of that server. We give them +tools to inspect the alias list and clean it up when necessary, in addition to +the current tools which allow restriction of who can create aliases in the +first place. + +A detailed list of proposed modifications to the Matrix spec follows: + + * `m.room.aliases` loses any special meaning within the spec. In particular: + + * Clients should no longer format it specially in room timelines. + + * Clients and servers should no longer consider `m.room.aliases` when + [calculating the display name for a + room](https://matrix.org/docs/spec/client_server/r0.6.0#calculating-the-display-name-for-a-room). + + (Note: servers follow the room display-name algorithm when calculating + room names for certain types of push notification.) + + * A future room version will remove the special [authorization +rules](https://matrix.org/docs/spec/rooms/v1#authorization-rules) and +[redaction rules](https://matrix.org/docs/spec/client_server/r0.6.0#redactions). + + * [`m.room.canonical_alias`](https://matrix.org/docs/spec/client_server/r0.6.0#m-room-canonical-alias) + is extended to include a new `alt_aliases` property. This, if present, + should be a list of alternative aliases for the room. An example event might + look like: + + ```json + { + "content": { + "alias": "#somewhere:localhost", + "alt_aliases": [ + "#somewhere:overthere.com", + "#somewhereelse:example.com" + ] + }, + "room_id": "!jEsUZKDJdhlrceRyVU:example.org", + "state_key": "", + "type": "m.room.canonical_alias" + } + ``` + + It is valid for `alt_aliases` to be non-empty even if `alias` is absent or + empty. This means that no alias has been picked out as the 'main' alias. + + (Note: although the spec currently claims that `alias` is mandatory, Synapse + generates `m.room.canonical` alias events with no `alias` property when the + main alias is deleted. This change would legitimise that behaviour.) + + (For clarity: it is not proposed that the `alt_aliases` be considered when + calculating the displayname for a room.) + + * [`PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}`](https://matrix.org/docs/spec/client_server/r0.6.0#put-matrix-client-r0-rooms-roomid-state-eventtype-statekey) + is extended to recommend that servers validate any *new* aliases added to + `m.room.canonical_alias` by checking that it is a valid alias according to + the [syntax](https://matrix.org/docs/spec/appendices#room-aliases), and by + looking up the alias and and that it corresponds to the expected room ID. + + (Note: Synapse currently implements this check on the main alias, though + this is unspecced.) + + (TODO: what error code should be returned if the alias is invalid, or if it + points to the wrong room?) + + * Currently, [`PUT /_matrix/client/r0/directory/room/{roomAlias}`](https://matrix.org/docs/spec/client_server/r0.6.0#put-matrix-client-r0-directory-room-roomalias) + attempts to send updated `m.room.aliases` events on the caller's + behalf. (This is implemented in Synapse but does not appear to be explicitly + specced.) This functionality should be removed. + + * Currently, [`DELETE /_matrix/client/r0/directory/room/{roomAlias}`](https://matrix.org/docs/spec/client_server/r0.6.0#delete-matrix-client-r0-directory-room-roomalias), + attempts to send updated `m.room.aliases` and/or `m.room.canonical_alias` + events on the caller's behalf, removing any aliases which have been + deleted. (Again, this is implemented in Synapse but does not appear to be + explicitly specced.) The `m.room.aliases` functionality should be removed, + and the `m.room.canonical_alias` functionality should be extended to cover + `alt_aliases`. As today, no error occurs if the caller does not have + permission to send such an event. + + * A new api endpoint, `GET /_matrix/client/r0/rooms/{roomId}/aliases` is + added, which returns the list of aliases currently defined on the local + server for the given room. The response looks like: + + ```json + { + "aliases": [ + "#somewhere:example.com", + "#somewhereelse:example.com", + "#special_alias:example.com" + ] + } + ``` + + This API can be called by any current member of the room (calls from other + users result in `M_FORBIDDEN`). + + TODO: should this be tied in with `history_visibilty` to allow peeking from + non-members? On the one hand that feels like pointless clutter; on the other + it feels like it makes it more consistent with peekable rooms. + +Various APIs are currently subject to implementation-defined access +restrictions. No change to the specification is changed in this regard +(implementations will continue to be free to impose their own +restrictions). Nevertheless as part of this MSC it is useful to consider some +proposed changes to Synapse's implementation: + + * No change: `PUT /_matrix/client/r0/directory/room/{roomAlias}`: Synapse + only allows access to current members of the room, and also exposes some + configuration options which allow restriction of which users are allowed to + create aliases in general. + + * `DELETE /_matrix/client/r0/directory/room/{roomAlias}`: in this case, + currently Synapse restricts its use to the user that created the alias, and + server admins. + + It is proposed to extend this to local users who have a power-level + sufficient to send an `m.room.canonical_alias` event in the room that the + alias currently points to. + + * [`PUT /_matrix/client/r0/directory/list/room/{roomId}`](https://matrix.org/docs/spec/client_server/r0.6.0#put-matrix-client-r0-directory-list-room-roomid) + and the corresponding unspecced `DELETE` api (both of which set the + visibility of a room in the public directory): currently Synapse restricts + their use to server admins and local users who have a PL sufficient to send + an `m.room.aliases` event in the room (ignoring the special auth + rules). This will be changed to check against the PL required to send an + `m.room.canonical_alias` event. + +It is envisaged that Matrix clients will then change their "Room Settings" user +interface to display the aliases from `m.room.canonical_alias` instead of those +in `m.room.aliases`, as well as giving moderators the ability to update that +list. Clients might also wish to use the new `GET +/_matrix/client/r0/rooms/{roomId}/aliases` endpoint to obtain and display the +currently-available local aliases, though given that this list may be subject +to abuse, it should probably not be shown by default. + +### Future work + +This work isn't considered part of this MSC, but rather a potential extension +for the future. + + * It may be useful to be able to query remote servers for their alias + list. This could be done by extending `GET + /_matrix/client/r0/rooms/{roomId}/aliases` to take a `server_name` + parameter, and defining an API in the server_server spec which will expose + the requested information, subject to the calling homeserver having at least + one user with a right to see it. + + * Similarly, room moderators may wish to be able to delete aliases on a remote + server for their room. We could envisage a fedaration API which allows such + a request to be made, subject to the calling homeserver having at least one + moderator in the room. + +## Potential issues + +The biggest problem with this proposal is that existing clients, which rely on +`m.room.aliases` in one way or another, will lose functionality. In particular, +they may not know about aliases that exist, or they may look at outdated +`m.room.aliases` events that list aliases that no longer exist. However, since +`m.room.aliases` is best-effort anyway, these are both problems that exist to +some extent today. + +## Alternatives + +We considered continuing to use `m.room.aliases` to advertise room aliases +instead of `m.room.canonical_alias`, but the significant changes in semantics +made that seem inappropriate. + +We also considered using separate state events for each advertised alias, +rather than listing them all in one event. This might increase the number of +aliases which can be advertised, and help to reduce races when editing the +list. However, the 64KB limit of an event still allows room for hundreds of +aliases of any sane length, and we don't expect the list to be changing +frequently enough for races to be a practical concern. Ultimately the added +complexity seemed redundant. + +A previous suggestion was +[MSC2260](https://github.com/matrix-org/matrix-doc/issues/2260), which proposed +keeping `m.room.aliases` largely as-is, but giving room moderators tools to +control who can send them via room power-levels. We dismissed it for the +reasons set out at +https://github.com/matrix-org/matrix-doc/pull/2260#issuecomment-584207073. + +## Security considerations + +None currently identified. + +## Unstable prefix + +TBD. From 95ff26679bf4f780509e73d2201cafe9ce40c111 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Tue, 11 Feb 2020 11:16:56 +0000 Subject: [PATCH 317/390] Apply suggestions from code review Co-Authored-By: Matthew Hodgson Co-Authored-By: Hubert Chathi --- proposals/2432-revised-alias-publishing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/2432-revised-alias-publishing.md b/proposals/2432-revised-alias-publishing.md index 8ef08f7d..0afdf195 100644 --- a/proposals/2432-revised-alias-publishing.md +++ b/proposals/2432-revised-alias-publishing.md @@ -32,7 +32,7 @@ We propose that that room moderators should be able to manually curate a list of "official" aliases for their room, instead of matrix servers automatically publishing lists of all room aliases into the room state. No particular guarantees are offered that this alias list is entirely accurate: it becomes -room moderators' responsibilty to keep it so. +room moderators' responsibility to keep it so. Meanwhile, the aliases that map to a given room on a given server become the ultimate responsibility of the administrators of that server. We give them @@ -182,7 +182,7 @@ for the future. one user with a right to see it. * Similarly, room moderators may wish to be able to delete aliases on a remote - server for their room. We could envisage a fedaration API which allows such + server for their room. We could envisage a federation API which allows such a request to be made, subject to the calling homeserver having at least one moderator in the room. From c7942d1cecda30511a10d5b971cb58bb30fa9be3 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 12 Feb 2020 21:58:57 -0700 Subject: [PATCH 318/390] Spec moderation policy rooms (MSC2313) MSC: https://github.com/matrix-org/matrix-doc/pull/2313 --- event-schemas/examples/m.policy.rule.room | 10 ++ event-schemas/examples/m.policy.rule.server | 10 ++ event-schemas/examples/m.policy.rule.user | 10 ++ event-schemas/moderation_policy_rule.yaml | 30 ++++ event-schemas/schema/m.policy.rule.room | 15 ++ event-schemas/schema/m.policy.rule.server | 15 ++ event-schemas/schema/m.policy.rule.user | 15 ++ specification/feature_profiles.rst | 2 + specification/modules/moderation_policies.rst | 129 ++++++++++++++++++ specification/targets.yaml | 1 + 10 files changed, 237 insertions(+) create mode 100644 event-schemas/examples/m.policy.rule.room create mode 100644 event-schemas/examples/m.policy.rule.server create mode 100644 event-schemas/examples/m.policy.rule.user create mode 100644 event-schemas/moderation_policy_rule.yaml create mode 100644 event-schemas/schema/m.policy.rule.room create mode 100644 event-schemas/schema/m.policy.rule.server create mode 100644 event-schemas/schema/m.policy.rule.user create mode 100644 specification/modules/moderation_policies.rst diff --git a/event-schemas/examples/m.policy.rule.room b/event-schemas/examples/m.policy.rule.room new file mode 100644 index 00000000..5a532cb5 --- /dev/null +++ b/event-schemas/examples/m.policy.rule.room @@ -0,0 +1,10 @@ +{ + "$ref": "core/state_event.json", + "type": "m.policy.rule.room", + "state_key": "rule:#*:example.org", + "content": { + "entity": "#*:example.org", + "recommendation": "m.ban", + "reason": "undesirable content" + } +} diff --git a/event-schemas/examples/m.policy.rule.server b/event-schemas/examples/m.policy.rule.server new file mode 100644 index 00000000..3d740a28 --- /dev/null +++ b/event-schemas/examples/m.policy.rule.server @@ -0,0 +1,10 @@ +{ + "$ref": "core/state_event.json", + "type": "m.policy.rule.server", + "state_key": "rule:*.example.org", + "content": { + "entity": "*.example.org", + "recommendation": "m.ban", + "reason": "undesirable engagement" + } +} diff --git a/event-schemas/examples/m.policy.rule.user b/event-schemas/examples/m.policy.rule.user new file mode 100644 index 00000000..eb3832da --- /dev/null +++ b/event-schemas/examples/m.policy.rule.user @@ -0,0 +1,10 @@ +{ + "$ref": "core/state_event.json", + "type": "m.policy.rule.user", + "state_key": "rule:@alice*:example.org", + "content": { + "entity": "@alice*:example.org", + "recommendation": "m.ban", + "reason": "undesirable behaviour" + } +} diff --git a/event-schemas/moderation_policy_rule.yaml b/event-schemas/moderation_policy_rule.yaml new file mode 100644 index 00000000..34ad4d9a --- /dev/null +++ b/event-schemas/moderation_policy_rule.yaml @@ -0,0 +1,30 @@ +# Copyright 2020 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +properties: + entity: + description: |- + The entity affected by this rule. Glob characters ``*`` and ``?`` can be used + to match zero or more and one or more characters respectively. + type: string + recommendation: + description: The suggested action to take. Currently only ``m.ban`` is specified. + type: string + reason: + description: The human-readable description for the ``recommendation``. + type: string +type: object +required: + - entity + - recommendation + - reason diff --git a/event-schemas/schema/m.policy.rule.room b/event-schemas/schema/m.policy.rule.room new file mode 100644 index 00000000..ff81543e --- /dev/null +++ b/event-schemas/schema/m.policy.rule.room @@ -0,0 +1,15 @@ +--- +allOf: + - $ref: core-event-schema/state_event.yaml +description: A moderation policy rule which affects room IDs and room aliases. +properties: + content: + $ref: "../moderation_policy_rule.yaml" + state_key: + description: An arbitrary string decided upon by the sender. + type: string + type: + enum: + - m.policy.rule.room + type: string +type: object diff --git a/event-schemas/schema/m.policy.rule.server b/event-schemas/schema/m.policy.rule.server new file mode 100644 index 00000000..ca37e8ff --- /dev/null +++ b/event-schemas/schema/m.policy.rule.server @@ -0,0 +1,15 @@ +--- +allOf: + - $ref: core-event-schema/state_event.yaml +description: A moderation policy rule which affects servers. +properties: + content: + $ref: "../moderation_policy_rule.yaml" + state_key: + description: An arbitrary string decided upon by the sender. + type: string + type: + enum: + - m.policy.rule.server + type: string +type: object diff --git a/event-schemas/schema/m.policy.rule.user b/event-schemas/schema/m.policy.rule.user new file mode 100644 index 00000000..4fa65ad8 --- /dev/null +++ b/event-schemas/schema/m.policy.rule.user @@ -0,0 +1,15 @@ +--- +allOf: + - $ref: core-event-schema/state_event.yaml +description: A moderation policy rule which affects users. +properties: + content: + $ref: "../moderation_policy_rule.yaml" + state_key: + description: An arbitrary string decided upon by the sender. + type: string + type: + enum: + - m.policy.rule.user + type: string +type: object diff --git a/specification/feature_profiles.rst b/specification/feature_profiles.rst index bb638380..8a3caf87 100644 --- a/specification/feature_profiles.rst +++ b/specification/feature_profiles.rst @@ -61,6 +61,7 @@ Summary `Stickers`_ Optional Optional Optional Optional Optional `Server ACLs`_ Optional Optional Optional Optional Optional `Server Notices`_ Optional Optional Optional Optional Optional + `Moderation policies`_ Optional Optional Optional Optional Optional ===================================== ========== ========== ========== ========== ========== *Please see each module for more details on what clients need to implement.* @@ -94,6 +95,7 @@ Summary .. _Stickers: `module:stickers`_ .. _Server ACLs: `module:server-acls`_ .. Server Notices already has a link elsewhere. +.. _Moderation Policies: `module:moderation-policies`_ Clients ------- diff --git a/specification/modules/moderation_policies.rst b/specification/modules/moderation_policies.rst new file mode 100644 index 00000000..8992ceb1 --- /dev/null +++ b/specification/modules/moderation_policies.rst @@ -0,0 +1,129 @@ +.. Copyright 2020 The Matrix.org Foundation C.I.C. +.. +.. Licensed under the Apache License, Version 2.0 (the "License"); +.. you may not use this file except in compliance with the License. +.. You may obtain a copy of the License at +.. +.. http://www.apache.org/licenses/LICENSE-2.0 +.. +.. Unless required by applicable law or agreed to in writing, software +.. distributed under the License is distributed on an "AS IS" BASIS, +.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +.. See the License for the specific language governing permissions and +.. limitations under the License. + +Moderation policy lists +======================= + +.. _module:moderation-policies: + +With Matrix being an open network where anyone can participate, a very wide +range of content exists and it is important that users are empowered to select +which content they wish to see, and which content they wish to block. By +extension, room moderators and server admins should also be able to select +which content they do not wish to host in their rooms and servers. + +The protocol's position on this is one of neutrality: it should not be deciding +what content is undesirable for any particular entity and should instead be +empowering those entities to make their own decisions. As such, a generic +framework for communicating "moderation policy lists" or "moderation policy rooms" +is described. Note that this module only describes the data structures and not +how they should be interpreting: the entity making the decisions on filtering +is best positioned to interpret the rules how it sees fit. + +Moderation policy lists are stored as room state events. There are no restrictions +on how the rooms can be configured (they could be public, private, encrypted, etc). + +There are currently 3 kinds of entities which can be affected by rules: ``user``, +``server``, and ``room``. All 3 are described with ``m.policy.rule.`` state +events. The ``state_key`` for a policy rule is an arbitrary string decided by the +sender of the rule. + +Rules contain recommendations and reasons for the rule existing. The ``reason`` +is a human-readable string which describes the ``recommendation``. Currently only +one recommendation, ``m.ban``, is specified. + +``m.ban`` recommendation +------------------------ + +When this recommendation is used, the entities affected by the rule should be +banned from participation where possible. The enforcement of this is deliberately +left as an implementation detail to avoid the protocol imposing its opinion on how +the policy list is to be interpreted. However, a suggestion for a simple implementation +is as follows: + +* Is a ``user`` rule... + + * Applied to a user: The user should be added to the subscriber's ignore list. + * Applied to a room: The user should be banned from the room (either on sight or immediately). + * Applied to a server: The user should not be allowed to send invites to users on the server. + +* Is a ``room`` rule... + + * Applied to a user: The user should leave the room and not join it + (`MSC2270 `_-style ignore). + * Applied to a room: No-op because a room cannot ban itself. + * Applied to a server: The server should prevent users from joining the room and from receiving + invites to it. + +* Is a ``server`` rule... + + * Applied to a user: The user should not receive events or invites from the server. + * Applied to a room: The server is added as a denied server in the ACLs. + * Applied to a server: The subscriber should avoid federating with the server as much as + possible by blocking invites from the server and not sending traffic unless strictly + required (no outbound invites). + +Subscribing to policy lists +--------------------------- + +This is deliberatly left as an implementation detail. For implementations using the +Client-Server API, this could be as easy as joining or peeking the room. Joining or peeking +is not required, however: an implementation could poll for updates or use a different +technique for receiving updates to the policy's rules. + +Sharing +------- + +In addition to sharing a direct reference to the room which contains the policy's rules, +plain http or https URLs can be used to share links to the list. When the URL is approached +with a ``Accept: application/json`` header or has ``.json`` appended to the end of the URL, it +should return a JSON object containing a ``room_uri`` property which references the room. +Currently this would be a ``matrix.to`` URI, however in future it could be a Matrix-schemed +URI instead. When not approached with the intent of JSON, the service could return a +user-friendly page describing what is included in the ban list. + +Events +------ + +The ``entity`` described by the state events can contain ``*`` and ``?`` to match zero or more +and one or more characters respectively. Note that rules against rooms can describe a room ID +or room alias - the subscriber is responsible for resolving the alias to a room ID if desired. + +{{m_policy_rule_user_event}} + +{{m_policy_rule_room_event}} + +{{m_policy_rule_server_event}} + +Client behaviour +---------------- +As described above, the client behaviour is deliberatly left undefined. + +Server behaviour +---------------- +Servers have no additional requirements placed on them by this module. + + +Security considerations +----------------------- +This module could be used to build a system of shared blacklists, which may create +a divide within established communities if not carefully deployed. This may well not +be a suitable solution for all communities. + +Depending on how implementations handle subscriptions, user IDs may be linked to +policy lists and therefore expose the views of that user. For example, a client implementation +which joins the user to the policy room would expose the user's ID to observers of the +policy room. In future, `MSC1228 `_ +and `MSC1777 `_ (or similar) could +help solve this concern. diff --git a/specification/targets.yaml b/specification/targets.yaml index 2cd911ba..4e0b068d 100644 --- a/specification/targets.yaml +++ b/specification/targets.yaml @@ -91,6 +91,7 @@ groups: # reusable blobs of files when prefixed with 'group:' - modules/mentions.rst - modules/room_upgrades.rst - modules/server_notices.rst + - modules/moderation_policies.rst title_styles: ["=", "-", "~", "+", "^", "`", "@", ":"] From da047523e32a863c7c0d0704a7c89787920b9310 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 12 Feb 2020 22:00:56 -0700 Subject: [PATCH 319/390] Changelog --- changelogs/client_server/newsfragments/2434.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/2434.feature diff --git a/changelogs/client_server/newsfragments/2434.feature b/changelogs/client_server/newsfragments/2434.feature new file mode 100644 index 00000000..2b8f36f5 --- /dev/null +++ b/changelogs/client_server/newsfragments/2434.feature @@ -0,0 +1 @@ +Added data structures for defining moderation policies in rooms per `MSC2313 `_. From 7a878bb6c34c2f39507de63a8a2416196d90f05b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 12 Feb 2020 22:01:58 -0700 Subject: [PATCH 320/390] Fix location of changelog entries and synergize them --- changelogs/client_server/{ => newsfragments}/2245.clarification | 0 changelogs/client_server/newsfragments/2415.clarification | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename changelogs/client_server/{ => newsfragments}/2245.clarification (100%) diff --git a/changelogs/client_server/2245.clarification b/changelogs/client_server/newsfragments/2245.clarification similarity index 100% rename from changelogs/client_server/2245.clarification rename to changelogs/client_server/newsfragments/2245.clarification diff --git a/changelogs/client_server/newsfragments/2415.clarification b/changelogs/client_server/newsfragments/2415.clarification index d1a7dbfe..902a9c3f 100644 --- a/changelogs/client_server/newsfragments/2415.clarification +++ b/changelogs/client_server/newsfragments/2415.clarification @@ -1 +1 @@ -Fix misspelling of _deprecated_. \ No newline at end of file +Fix various spelling errors throughout the specification. From 6d475ebd5777600e70ed0737845f9b5d21bba59a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 17 Feb 2020 12:56:28 -0700 Subject: [PATCH 321/390] Remove whitespace Co-Authored-By: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> --- specification/modules/moderation_policies.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/specification/modules/moderation_policies.rst b/specification/modules/moderation_policies.rst index 8992ceb1..2e85fb07 100644 --- a/specification/modules/moderation_policies.rst +++ b/specification/modules/moderation_policies.rst @@ -114,7 +114,6 @@ Server behaviour ---------------- Servers have no additional requirements placed on them by this module. - Security considerations ----------------------- This module could be used to build a system of shared blacklists, which may create From 786772f7646f8767c008872abc6fc41b9fe7b809 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Tue, 18 Feb 2020 16:24:46 +0000 Subject: [PATCH 322/390] Update proposals/2432-revised-alias-publishing.md Co-Authored-By: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> --- proposals/2432-revised-alias-publishing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2432-revised-alias-publishing.md b/proposals/2432-revised-alias-publishing.md index 0afdf195..a6887264 100644 --- a/proposals/2432-revised-alias-publishing.md +++ b/proposals/2432-revised-alias-publishing.md @@ -81,7 +81,7 @@ rules](https://matrix.org/docs/spec/rooms/v1#authorization-rules) and empty. This means that no alias has been picked out as the 'main' alias. (Note: although the spec currently claims that `alias` is mandatory, Synapse - generates `m.room.canonical` alias events with no `alias` property when the + generates `m.room.canonical_alias` events with no `alias` property when the main alias is deleted. This change would legitimise that behaviour.) (For clarity: it is not proposed that the `alt_aliases` be considered when From 645dbcc0912624a32d070b2c7ae30d936f1e2f7b Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 19 Feb 2020 07:25:34 +0000 Subject: [PATCH 323/390] make room alias lists peekable --- proposals/2432-revised-alias-publishing.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/proposals/2432-revised-alias-publishing.md b/proposals/2432-revised-alias-publishing.md index a6887264..4a5d95ad 100644 --- a/proposals/2432-revised-alias-publishing.md +++ b/proposals/2432-revised-alias-publishing.md @@ -128,11 +128,8 @@ rules](https://matrix.org/docs/spec/rooms/v1#authorization-rules) and ``` This API can be called by any current member of the room (calls from other - users result in `M_FORBIDDEN`). - - TODO: should this be tied in with `history_visibilty` to allow peeking from - non-members? On the one hand that feels like pointless clutter; on the other - it feels like it makes it more consistent with peekable rooms. + users result in `M_FORBIDDEN`). For rooms with `history_visibility` set to + `world_readable`, it can also be called by users outside the room. Various APIs are currently subject to implementation-defined access restrictions. No change to the specification is changed in this regard From 30d762cc72ad179ceb612dea046418770546714e Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 19 Feb 2020 07:25:49 +0000 Subject: [PATCH 324/390] document unstable prefixes/feature flags --- proposals/2432-revised-alias-publishing.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/proposals/2432-revised-alias-publishing.md b/proposals/2432-revised-alias-publishing.md index 4a5d95ad..81d59e71 100644 --- a/proposals/2432-revised-alias-publishing.md +++ b/proposals/2432-revised-alias-publishing.md @@ -219,4 +219,11 @@ None currently identified. ## Unstable prefix -TBD. +While this feature is in development, the following names will be in use: + +| Proposed final name | Name while in development | +| `GET /_matrix/client/r0/rooms/{roomId}/aliases` | `GET /_matrix/client/unstable/org.matrix.msc2432/rooms/{roomId}/aliases` | + +Servers will indicate support for the new endpoint via a non-empty value for feature flag +`org.matrix.msc2432` in `unstable_features` in the response to `GET +/_matrix/client/versions`. From 98a6cd0f45426beca4eb1e0334573a1ea3748642 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Wed, 19 Feb 2020 07:29:55 +0000 Subject: [PATCH 325/390] fix table --- proposals/2432-revised-alias-publishing.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/2432-revised-alias-publishing.md b/proposals/2432-revised-alias-publishing.md index 81d59e71..c527a14e 100644 --- a/proposals/2432-revised-alias-publishing.md +++ b/proposals/2432-revised-alias-publishing.md @@ -222,6 +222,7 @@ None currently identified. While this feature is in development, the following names will be in use: | Proposed final name | Name while in development | +| --- | --- | | `GET /_matrix/client/r0/rooms/{roomId}/aliases` | `GET /_matrix/client/unstable/org.matrix.msc2432/rooms/{roomId}/aliases` | Servers will indicate support for the new endpoint via a non-empty value for feature flag From 8b9ea10e11bd0a460211e1826543fe17f2667e19 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 19 Feb 2020 07:31:57 +0000 Subject: [PATCH 326/390] server admins can list aliases --- proposals/2432-revised-alias-publishing.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proposals/2432-revised-alias-publishing.md b/proposals/2432-revised-alias-publishing.md index c527a14e..433ce0f6 100644 --- a/proposals/2432-revised-alias-publishing.md +++ b/proposals/2432-revised-alias-publishing.md @@ -131,6 +131,9 @@ rules](https://matrix.org/docs/spec/rooms/v1#authorization-rules) and users result in `M_FORBIDDEN`). For rooms with `history_visibility` set to `world_readable`, it can also be called by users outside the room. + Servers might also choose to allow access to other users such as server + administrators. + Various APIs are currently subject to implementation-defined access restrictions. No change to the specification is changed in this regard (implementations will continue to be free to impose their own From e151ba96acfbd67febc1869c0326b1fb7c4c4dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96rjan=20Fors?= Date: Mon, 24 Feb 2020 09:45:46 +0100 Subject: [PATCH 327/390] Fix typo for upgrade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Örjan Fors --- api/client-server/room_upgrades.yaml | 2 +- changelogs/client_server.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/client-server/room_upgrades.yaml b/api/client-server/room_upgrades.yaml index 55ee14dc..3aaaadcb 100644 --- a/api/client-server/room_upgrades.yaml +++ b/api/client-server/room_upgrades.yaml @@ -90,4 +90,4 @@ paths: schema: "$ref": "definitions/errors/error.yaml" tags: - - Room ugprades + - Room upgrades diff --git a/changelogs/client_server.rst b/changelogs/client_server.rst index a5173fd0..bc12da5c 100644 --- a/changelogs/client_server.rst +++ b/changelogs/client_server.rst @@ -74,7 +74,7 @@ Backwards Compatible Changes - Support optional features by having clients query for capabilities. (`#1829 `_, `#1879 `_) - Add ``M_RESOURCE_LIMIT_EXCEEDED`` as an error code for when homeservers exceed limits imposed on them. (`#1874 `_) - Emit ``M_UNSUPPORTED_ROOM_VERSION`` error codes where applicable on ``/createRoom`` and ``/invite`` APIs. (`#1908 `_) -- Add a ``.m.rule.tombstone`` default push rule for room ugprade notifications. (`#2020 `_) +- Add a ``.m.rule.tombstone`` default push rule for room upgrade notifications. (`#2020 `_) - Add support for sending server notices to clients. (`#2026 `_) - Add MSISDN (phone number) support to User-Interactive Authentication. (`#2030 `_) - Add the option to lazy-load room members for increased client performance. (`#2035 `_) From 4e123ca12113bd0587ac641503f0e8da9f6af4f0 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Sat, 29 Feb 2020 23:29:33 +0000 Subject: [PATCH 328/390] Update proposals/2432-revised-alias-publishing.md Co-Authored-By: Kitsune Ral --- proposals/2432-revised-alias-publishing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2432-revised-alias-publishing.md b/proposals/2432-revised-alias-publishing.md index 433ce0f6..dff72df5 100644 --- a/proposals/2432-revised-alias-publishing.md +++ b/proposals/2432-revised-alias-publishing.md @@ -135,7 +135,7 @@ rules](https://matrix.org/docs/spec/rooms/v1#authorization-rules) and administrators. Various APIs are currently subject to implementation-defined access -restrictions. No change to the specification is changed in this regard +restrictions. No change to the specification is introduced in this regard (implementations will continue to be free to impose their own restrictions). Nevertheless as part of this MSC it is useful to consider some proposed changes to Synapse's implementation: From 420180a69ddd25d44b1c7c048d809b05683978f4 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Tue, 3 Mar 2020 12:30:19 -0500 Subject: [PATCH 329/390] MSC: Remove query_auth federation endpoint. --- .gitignore | 1 + ...x-remove-query_auth-federation-endpoint.md | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 proposals/xxxx-remove-query_auth-federation-endpoint.md diff --git a/.gitignore b/.gitignore index 800be2fa..9cc27b85 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ *.swp _rendered.rst /.vscode/ +/.idea/ diff --git a/proposals/xxxx-remove-query_auth-federation-endpoint.md b/proposals/xxxx-remove-query_auth-federation-endpoint.md new file mode 100644 index 00000000..7930c5f8 --- /dev/null +++ b/proposals/xxxx-remove-query_auth-federation-endpoint.md @@ -0,0 +1,31 @@ +# MSCxxxx: Remove the `query_auth` federation endpoint + +The `query_auth` federation endpoint is unused by Synapse and should be removed. +The current implementation in Synapse is broken and will return a 500 error in +some situations. + +## Proposal + +Remove: + +* [POST `/_matrix/federation/v1/query_auth/{roomId}/{eventId}`](https://matrix.org/docs/spec/server_server/r0.1.3#post-matrix-federation-v1-query-auth-roomid-eventid) + +## Potential issues + +Removing this endpoint impacts backwards compatibility. + +In practice, removing this endpoint should have minimal impact. Since 1.5.0rc1 +of Synapse this endpoint is not called (see [#6214](https://github.com/matrix-org/synapse/pull/6214)). +During removal it was noted that the code to call this endpoint was already +unreachable. + +Note that it seems like this was initially supported in Synapse v0.7.0. It is +not clear at what point it became unused. + +## Alternatives + +The endpoint could be deprecated in removed in a future version of the specification. + +## Security considerations + +None. From c3420770ad3a559217ee5ae7a5998b1c78960055 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Tue, 3 Mar 2020 13:15:32 -0500 Subject: [PATCH 330/390] Clarify history of endpoint. --- ...x-remove-query_auth-federation-endpoint.md | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/proposals/xxxx-remove-query_auth-federation-endpoint.md b/proposals/xxxx-remove-query_auth-federation-endpoint.md index 7930c5f8..387f2efa 100644 --- a/proposals/xxxx-remove-query_auth-federation-endpoint.md +++ b/proposals/xxxx-remove-query_auth-federation-endpoint.md @@ -1,26 +1,39 @@ # MSCxxxx: Remove the `query_auth` federation endpoint The `query_auth` federation endpoint is unused by Synapse and should be removed. -The current implementation in Synapse is broken and will return a 500 error in -some situations. +The current implementation in Synapse is not robust and will return a 500 error +in some situations. ## Proposal -Remove: +Remove the following endpoint: * [POST `/_matrix/federation/v1/query_auth/{roomId}/{eventId}`](https://matrix.org/docs/spec/server_server/r0.1.3#post-matrix-federation-v1-query-auth-roomid-eventid) ## Potential issues -Removing this endpoint impacts backwards compatibility. +Removing this endpoint impacts backwards compatibility, in practice removing +this endpoint should have minimal impact as it was an unused error path in +Synapse. The federation client code to call this endpoint was removed in Synapse +v1.5.0rc1. -In practice, removing this endpoint should have minimal impact. Since 1.5.0rc1 -of Synapse this endpoint is not called (see [#6214](https://github.com/matrix-org/synapse/pull/6214)). -During removal it was noted that the code to call this endpoint was already -unreachable. +Note that dendrite has never implemented this federation endpoint. + +### History + +This endpoint (and the federation client code) to call it was initially +added in Synapse v0.7.0 (see [#43](https://github.com/matrix-org/synapse/pull/43)). +The federation client code was heavily modified for v1.0.0rc1 (see +[#5227](https://github.com/matrix-org/synapse/pull/5227/)), -Note that it seems like this was initially supported in Synapse v0.7.0. It is -not clear at what point it became unused. +The federation client code to call this endpoint was removed in v1.5.0rc1 of +Synapse (see [#6214](https://github.com/matrix-org/synapse/pull/6214). After +that point this endpoint is not called). + +During removal it was noted that the code to call this endpoint was already +unreachable. It seems that this code was never reachable and was meant for an +error situation which was never built out (see `git log -S NOT_ANCESTOR`, the +error condition is never assigned). ## Alternatives From 6754d5ba5f4e98ed67c5771a7176cd7d3ecba582 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Tue, 3 Mar 2020 16:18:59 -0500 Subject: [PATCH 331/390] Move filename based on MSC #. --- ...ndpoint.md => 2451-remove-query_auth-federation-endpoint.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename proposals/{xxxx-remove-query_auth-federation-endpoint.md => 2451-remove-query_auth-federation-endpoint.md} (96%) diff --git a/proposals/xxxx-remove-query_auth-federation-endpoint.md b/proposals/2451-remove-query_auth-federation-endpoint.md similarity index 96% rename from proposals/xxxx-remove-query_auth-federation-endpoint.md rename to proposals/2451-remove-query_auth-federation-endpoint.md index 387f2efa..d5c2af4d 100644 --- a/proposals/xxxx-remove-query_auth-federation-endpoint.md +++ b/proposals/2451-remove-query_auth-federation-endpoint.md @@ -1,4 +1,4 @@ -# MSCxxxx: Remove the `query_auth` federation endpoint +# MSC2451: Remove the `query_auth` federation endpoint The `query_auth` federation endpoint is unused by Synapse and should be removed. The current implementation in Synapse is not robust and will return a 500 error From 68357a7d23420b069308cfbe99244168307230d6 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Wed, 4 Mar 2020 07:31:38 -0500 Subject: [PATCH 332/390] Fix a typo in -> and. Co-Authored-By: Matthew Hodgson --- proposals/2451-remove-query_auth-federation-endpoint.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2451-remove-query_auth-federation-endpoint.md b/proposals/2451-remove-query_auth-federation-endpoint.md index d5c2af4d..0d84069e 100644 --- a/proposals/2451-remove-query_auth-federation-endpoint.md +++ b/proposals/2451-remove-query_auth-federation-endpoint.md @@ -37,7 +37,7 @@ error condition is never assigned). ## Alternatives -The endpoint could be deprecated in removed in a future version of the specification. +The endpoint could be deprecated and removed in a future version of the specification. ## Security considerations From 61715f6452bfda06b273d1fc8a94b9412f4910b6 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Wed, 4 Mar 2020 08:07:05 -0500 Subject: [PATCH 333/390] Update and expand the proposal based on feedback and additional info. --- ...1-remove-query_auth-federation-endpoint.md | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/proposals/2451-remove-query_auth-federation-endpoint.md b/proposals/2451-remove-query_auth-federation-endpoint.md index 0d84069e..b2d8bc9f 100644 --- a/proposals/2451-remove-query_auth-federation-endpoint.md +++ b/proposals/2451-remove-query_auth-federation-endpoint.md @@ -1,8 +1,19 @@ # MSC2451: Remove the `query_auth` federation endpoint -The `query_auth` federation endpoint is unused by Synapse and should be removed. -The current implementation in Synapse is not robust and will return a 500 error -in some situations. +This API was added without sufficient thought nor testing. The endpoint isn't +used in any known implementations, and we do not believe it to be necessary +for federation to work. The only known implementation (in Synapse) was not fully +fleshed out and is broken. + +For background, the idea behind this endpoint was that two homeservers would be +able to share state events with the hope of filling in missing state from one +of homeservers allowing state resolution to complete. This was to protect +against a joining server not providing the full (or providing stale) state. + +In addition to the ideas above not coming to fruition, it is unclear whether the +current design of this endpoint would be sufficient. If this state negotiation +feature is needed in the future it should be redesigned from scratch via the MSC +proposal process. ## Proposal @@ -17,7 +28,8 @@ this endpoint should have minimal impact as it was an unused error path in Synapse. The federation client code to call this endpoint was removed in Synapse v1.5.0rc1. -Note that dendrite has never implemented this federation endpoint. +There is no evidence of other homeserver implementations having implemented this +endpoint. ### History From 438ff8fc1d19f89cbf77583b95a3dbe55d4c6b96 Mon Sep 17 00:00:00 2001 From: "DeepBlueV7.X" Date: Thu, 5 Mar 2020 21:02:09 +0000 Subject: [PATCH 334/390] Update proposals/2422-allow-color-attribute-on-font-tag.md Co-Authored-By: Travis Ralston --- proposals/2422-allow-color-attribute-on-font-tag.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/2422-allow-color-attribute-on-font-tag.md b/proposals/2422-allow-color-attribute-on-font-tag.md index 4287a696..7a87f143 100644 --- a/proposals/2422-allow-color-attribute-on-font-tag.md +++ b/proposals/2422-allow-color-attribute-on-font-tag.md @@ -1,6 +1,6 @@ # MSC2422: Allow `color` as attribute for `` in messages -Currently the spec requires you to use `data-mx-color` instead of the standard +Currently the spec recommends that you to use `data-mx-color` instead of the standard `color` html attribute for the `` tag. This is probably done to make it consistent with ``, where you may not want to allow a generic style tag for. @@ -35,4 +35,3 @@ Add the `color` attribute to the allowed attributes of `` in section - Add a section to tell the clients to prefer `color` over `mx-data-color` - Spec an entirely different format for messages (that would probably not make this proposal obsolete) - From 6b2752c4c2c0fd183224f22ed339226362ddddfd Mon Sep 17 00:00:00 2001 From: "DeepBlueV7.X" Date: Thu, 5 Mar 2020 21:02:23 +0000 Subject: [PATCH 335/390] Update proposals/2422-allow-color-attribute-on-font-tag.md Co-Authored-By: Travis Ralston --- proposals/2422-allow-color-attribute-on-font-tag.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2422-allow-color-attribute-on-font-tag.md b/proposals/2422-allow-color-attribute-on-font-tag.md index 7a87f143..9e4b1645 100644 --- a/proposals/2422-allow-color-attribute-on-font-tag.md +++ b/proposals/2422-allow-color-attribute-on-font-tag.md @@ -15,7 +15,7 @@ It would probably be for the best to allow or even prefer `color` on the ## Proposal Add the `color` attribute to the allowed attributes of `` in section -13.2.1.7. Allow only hexcodes as allowed value for this attribute for now. +13.2.1.7. No changes to the allowable values from the HTML spec are made here. ## Potential issues From e6c7eac023e6b55404fa14d9eebf9e462f097f67 Mon Sep 17 00:00:00 2001 From: Runster Date: Fri, 6 Mar 2020 18:11:28 +0100 Subject: [PATCH 336/390] Correct name of Phil Zimmermann --- specification/modules/end_to_end_encryption.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/modules/end_to_end_encryption.rst b/specification/modules/end_to_end_encryption.rst index af7b2c59..631b182a 100644 --- a/specification/modules/end_to_end_encryption.rst +++ b/specification/modules/end_to_end_encryption.rst @@ -505,7 +505,7 @@ framework outlined above. SAS verification is intended to be a highly interactiv process for users, and as such exposes verfiication methods which are easier for users to use. -The verification process is heavily inspired by Phil Zimmerman's ZRTP key agreement +The verification process is heavily inspired by Phil Zimmermann's ZRTP key agreement handshake. A key part of key agreement in ZRTP is the hash commitment: the party that begins the Diffie-Hellman key sharing sends a hash of their part of the Diffie-Hellman exchange, and does not send their part of the Diffie-Hellman exchange until they have From 7666b9fea71ff2129cc4690ae45e9d2a2e6fc58e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 6 Mar 2020 10:56:19 -0700 Subject: [PATCH 337/390] Add changelog for name correction https://github.com/matrix-org/matrix-doc/pull/2453 --- changelogs/client_server/newsfragments/2453.clarification | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/2453.clarification diff --git a/changelogs/client_server/newsfragments/2453.clarification b/changelogs/client_server/newsfragments/2453.clarification new file mode 100644 index 00000000..902a9c3f --- /dev/null +++ b/changelogs/client_server/newsfragments/2453.clarification @@ -0,0 +1 @@ +Fix various spelling errors throughout the specification. From b760ec2d747281294634bec051064191d4fd3357 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sat, 7 Mar 2020 14:57:32 +0100 Subject: [PATCH 338/390] Add comments why alternatives to MSC2422 weren't chosen --- .../2422-allow-color-attribute-on-font-tag.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/proposals/2422-allow-color-attribute-on-font-tag.md b/proposals/2422-allow-color-attribute-on-font-tag.md index 9e4b1645..3fb03aa5 100644 --- a/proposals/2422-allow-color-attribute-on-font-tag.md +++ b/proposals/2422-allow-color-attribute-on-font-tag.md @@ -29,9 +29,11 @@ Add the `color` attribute to the allowed attributes of `` in section ## Alternatives -- fix the clients -- remove the `data-mx-color` and `data-mx-bg-color` attributes entirely, leaving - us just with `color` for `` -- Add a section to tell the clients to prefer `color` over `mx-data-color` -- Spec an entirely different format for messages (that would probably not make - this proposal obsolete) +- fix the clients + -> This currently seems not feasible. Multiple clients started using color first (i.e. RiotX, Gomuks) and if it isn't spelled out explicitly in the spec, this will probably continue. +- remove the `data-mx-color` and `data-mx-bg-color` attributes entirely, leaving us just with `color` for `` + -> This would break old messages and can be done independently of this proposal at a later date, if it is deemed useful. +- Add a section to tell the clients to prefer `color` over `mx-data-color` + -> I don't really know, why mx-data-* was chosen, but I assume there was a reason, so I don't want to change that. +- Spec an entirely different format for messages (that would probably not make this proposal obsolete) + -> This wouldn't fix the issue, where some client may choose to remove the color tag, since it is dicouraged in the spec. Migration would probably also take a while, so this proposal is a quick solution, that doesn't prevent other solutions at a later date. From 2d85422a2ff500cb79fcf88c82fa5c73fc4f4b1a Mon Sep 17 00:00:00 2001 From: "DeepBlueV7.X" Date: Sat, 7 Mar 2020 18:34:07 +0000 Subject: [PATCH 339/390] Update proposals/2422-allow-color-attribute-on-font-tag.md Co-Authored-By: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- proposals/2422-allow-color-attribute-on-font-tag.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2422-allow-color-attribute-on-font-tag.md b/proposals/2422-allow-color-attribute-on-font-tag.md index 3fb03aa5..11f06bff 100644 --- a/proposals/2422-allow-color-attribute-on-font-tag.md +++ b/proposals/2422-allow-color-attribute-on-font-tag.md @@ -19,7 +19,7 @@ Add the `color` attribute to the allowed attributes of `` in section ## Potential issues -- We now have a redundant attribute in the spec. While it matches, what the +- We now have a redundant attribute in the spec. While it matches what the clients currently do, it may be better to fix each client instead. - Clients may not sanitize the color attribute and will let other color values through, increasing compatibility issues again. From c69a747bae52296f8a7fe930090c64c98227cd73 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Mon, 9 Mar 2020 12:31:06 -0400 Subject: [PATCH 340/390] Convert the initial Google Doc to Markdown. --- proposals/xxxx-ui-interactive-auth-for-sso.md | 262 ++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 proposals/xxxx-ui-interactive-auth-for-sso.md diff --git a/proposals/xxxx-ui-interactive-auth-for-sso.md b/proposals/xxxx-ui-interactive-auth-for-sso.md new file mode 100644 index 00000000..fc7413fe --- /dev/null +++ b/proposals/xxxx-ui-interactive-auth-for-sso.md @@ -0,0 +1,262 @@ +# User-Interactive Auth for SSO-backed homeservers + +## Background + +Certain endpoints, such as `DELETE /_matrix/client/r0/devices/{deviceId}` and +`POST /_matrix/client/r0/account/3pid/add`, require the user to reconfirm their +identity, as a guard against a leaked access token being used to take over an +entire account. + +On a normal homeserver, this is done by prompting the user to enter their +password. However, on a homeserver where users authenticate via a single-sign-on +system, the user doesn't have a password, so this doesn't work. Instead we need +to delegate that check to the SSO system. + +At the protocol level, this means adding support for SSO to +[user-interactive auth](https://matrix.org/docs/spec/client_server/r0.6.0#user-interactive-authentication-api). + +### The current implementation + +Or, "how UI Auth works, in practice": + +When the client calls one of the protected endpoints, it initially returns a 401 +response. For example: + +``` +POST /_matrix/client/r0/delete_devices HTTP/1.1 +Content-Type: application/json + +{ + "devices": ["FSVVTZRRAA"] +} + +HTTP/1.1 401 Unauthorized +Content-Type: application/json + +{ + "flows": [ + { + "stages": [ + "m.login.password" + ] + } + ], + "params": {}, + "session": "dTKfsLHSAJeAhqfxUsvrIVJd" +} +``` + +The client: + +* inspects the "flows" list +* discovers there is a flow it knows how to follow +* carries out the first "stage" of that flow (m.login.password) + +ie, the client prompts the user to enter a password. + +The client then resubmits with an additional 'auth' param, with "type" giving +the name of the authentication type it has just carried out. That completes the +authentication flow, so the server is now happy, and returns a 200: + +``` +POST /_matrix/client/r0/delete_devices HTTP/1.1 +Content-Type: application/json + +{ + "devices": ["FSVVTZRRAA"], + "auth": { + "session":"dTKfsLHSAJeAhqfxUsvrIVJd", + "type":"m.login.password", + "identifier":{"type":"m.id.user", "user":"@userid:matrix.org"}, + "password":"" + } +} + + +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 179 + +{} +``` + +## Proposal + +We add an `m.login.sso` authentication type to the UI auth spec. There are no +additional params as part of this auth type. + +1. If the client wants to complete that authentication type, it opens a browser + window for `/_matrix/client/r0/auth/m.login.sso/fallback/web?session=<...>` + with session set to the UI-Auth session id (from the "auth" dict). + + The homeserver returns a page which says words to the effect of "A client is + trying to remove a device/add an email address/take over your account. To + confirm this action, **re-authenticate with single sign-on**. If you did not + expect this, your account may be compromised!" + + See security section below. +2. The link, once the user clicks on it, goes to the SSO provider's page. +3. The SSO provider validates the user, and redirects the browser back to the + homeserver. +4. The homeserver validates the response from the SSO provider, updates the + user-interactive auth session to show that the SSO has completed, serves the + fallback auth completion page as specced: + https://matrix.org/docs/spec/client_server/r0.6.0#fallback +5. The client resubmits its original request, with its original session id, + which now hopefully completes. + +Note that the post-SSO URL on the homeserver is left up to the homeserver +implementation rather than forming part of the spec. + +* SAML2 servers typically only support one URL per service provider, so in + practice it will need to be the same as that already used for the login flow + (for synapse, it's /_matrix/saml2/authn_response) - and the server needs to + be able to figure out if it's doing SSO for a login attempt or an SSO + attempt. +* CAS doesn't have the same restriction. + +### Example flow: + +0. Client submits the request, which the server says requires SSO: + + ``` + POST /_matrix/client/r0/delete_devices HTTP/1.1 + Content-Type: application/json + Authorization: Bearer xyzzy + + { + "devices": ["FSVVTZRRAA"] + } + + HTTP/1.1 401 Unauthorized + Content-Type: application/json + + { + "flows": [ + { + "stages": [ + "m.login.sso" + ] + } + ], + "params": {}, + "session": "dTKfsLHSAJeAhqfxUsvrIVJd" + } + ``` + +1. Client opens a browser window for the fallback endpoint: + + ``` + GET /_matrix/client/r0/auth/m.login.sso/fallback/web + ?session=dTKfsLHSAJeAhqfxUsvrIVJd HTTP/1.1 + + HTTP/1.1 200 OK + + + can delete device pls? + clicky + + ``` + +2. The user clicks the confirmation link which goes to the SSO provider's site: + + ``` + GET https://sso_provider/validate?SAMLRequest= HTTP/1.1 + + ... SAML/CAS stuff + ``` + +3. The SSO provider validates the user and ends up redirecting the browser back + to the homeserver. (The example below shows a 302 for simplicity but SAML normally uses a 200 with an html
, with javascript to automatically submit it.) + + ``` + HTTP/1.1 302 Moved + Location: https://homeserver/_matrix/saml2/authn_response? + SAMLResponse= + ``` + +4. The browser sends the SSO response to the homeserver, which validates it and + shows the fallback auth completion page: + + ``` + GET /_matrix/saml2/authn_response?SAMLResponse= + + + HTTP/1.1 200 OK + + + +

Thank you.

+

You may now close this window and return to the application.

+ ``` + +5. The client closes the browser popup if it is still open, and resubmits its + original request, which now succeeds: + + ``` + POST /_matrix/client/r0/delete_devices HTTP/1.1 + Content-Type: application/json + Authorization: Bearer xyzzy + + { + "auth": { + "session": "dTKfsLHSAJeAhqfxUsvrIVJd" + } + } + + HTTP/1.1 200 OK + Content-Type: application/json + + {} + ``` + +A few notes: + +* The security of this relies on UI-Auth sessions only being used for the same + request as they were initiated for. I don't think that's currently enforced. +* We might consider an alternative client flow where the fallback auth ends up + redirecting to a given URI, instead of doing javascript postMessage foo. I + think that's an orthogonal change to the fallback auth though. +* In theory, any clients that already implement the fallback process for + unknown authentication types will work fine without modification. + Unfortunately, I don't think Riot (on any platform) is among them. + +## Security considerations + +Recall that the thing we are trying to guard against here is a single leaked +access-token being used to take over an entire account. So let's assume the +attacker has got hold of an access token for your account. What happens if we + skip the confirmation step? + +The attacker, who has your access token, starts a UI-Auth session to add his +email address to your account. + +He then sends you a link "hey, check out this cool video!"; the link leads (via +an innocent-looking url shortener) to +`/_matrix/client/r0/auth/m.login.sso/fallback/web?session=<...>`, with the ID of +the session that he just created. + +Recall that we're skipping the confirmation step, so the server redirects +straight to the SSO provider. + +It's common for SSO providers to redirect straight back to the app if you've +recently authenticated with them; even in the best case, the SSO provider shows +some innocent message along the lines of "Confirm that you want to sign in to +". + +So the SSO completes, and the attacker's session is validated. + +By telling the user what's about to happen as clearly as we can, and making them +confirm, we can mitigate this problem. + +## Unstable prefix + +We should use a vendor prefix here until this hits the spec. + +`org.matrix.login.sso` ? \ No newline at end of file From f48bbd32788ec8734fbaaa85728197632aeffd61 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Mon, 9 Mar 2020 14:00:28 -0400 Subject: [PATCH 341/390] Attempt to clarify the MSC. --- proposals/xxxx-ui-interactive-auth-for-sso.md | 90 ++++++++++--------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/proposals/xxxx-ui-interactive-auth-for-sso.md b/proposals/xxxx-ui-interactive-auth-for-sso.md index fc7413fe..5b6f396d 100644 --- a/proposals/xxxx-ui-interactive-auth-for-sso.md +++ b/proposals/xxxx-ui-interactive-auth-for-sso.md @@ -1,6 +1,4 @@ -# User-Interactive Auth for SSO-backed homeservers - -## Background +# User-Interactive Auth for SSO-backed homeserver Certain endpoints, such as `DELETE /_matrix/client/r0/devices/{deviceId}` and `POST /_matrix/client/r0/account/3pid/add`, require the user to reconfirm their @@ -9,15 +7,17 @@ entire account. On a normal homeserver, this is done by prompting the user to enter their password. However, on a homeserver where users authenticate via a single-sign-on -system, the user doesn't have a password, so this doesn't work. Instead we need -to delegate that check to the SSO system. +system, the user doesn't have a password registered with the homeserver. Instead +we need to delegate that check to the SSO system. At the protocol level, this means adding support for SSO to [user-interactive auth](https://matrix.org/docs/spec/client_server/r0.6.0#user-interactive-authentication-api). -### The current implementation +In theory, any clients that already implement the fallback process for unknown +authentication types will work fine without modification. It is unknown whether +this is widely supported among clients. -Or, "how UI Auth works, in practice": +### UI Auth Overview When the client calls one of the protected endpoints, it initially returns a 401 response. For example: @@ -52,7 +52,7 @@ The client: * discovers there is a flow it knows how to follow * carries out the first "stage" of that flow (m.login.password) -ie, the client prompts the user to enter a password. +In this example, the client prompts the user to enter a password. The client then resubmits with an additional 'auth' param, with "type" giving the name of the authentication type it has just carried out. That completes the @@ -89,24 +89,25 @@ additional params as part of this auth type. window for `/_matrix/client/r0/auth/m.login.sso/fallback/web?session=<...>` with session set to the UI-Auth session id (from the "auth" dict). - The homeserver returns a page which says words to the effect of "A client is - trying to remove a device/add an email address/take over your account. To - confirm this action, **re-authenticate with single sign-on**. If you did not - expect this, your account may be compromised!" + The homeserver returns a page which asks for the user's confirmation before + proceeding. See the security considerations section below for why this is + necessary. For example, the page could say words to the effect of: - See security section below. + > A client is trying to remove a device/add an email address/take over your + > account. To confirm this action, **re-authenticate with single sign-on**. + > If you did not expect this, your account may be compromised! 2. The link, once the user clicks on it, goes to the SSO provider's page. 3. The SSO provider validates the user, and redirects the browser back to the homeserver. 4. The homeserver validates the response from the SSO provider, updates the - user-interactive auth session to show that the SSO has completed, serves the - fallback auth completion page as specced: - https://matrix.org/docs/spec/client_server/r0.6.0#fallback + user-interactive auth session to show that the SSO has completed, + [serves the fallback auth completion page as specced](https://matrix.org/docs/spec/client_server/r0.6.0#fallback). 5. The client resubmits its original request, with its original session id, - which now hopefully completes. + which now should complete. Note that the post-SSO URL on the homeserver is left up to the homeserver -implementation rather than forming part of the spec. +implementation rather than forming part of the specification, choices might be +limited by the chosen SSO implementation, for example: * SAML2 servers typically only support one URL per service provider, so in practice it will need to be the same as that already used for the login flow @@ -216,47 +217,50 @@ implementation rather than forming part of the spec. {} ``` -A few notes: +## Alternatives -* The security of this relies on UI-Auth sessions only being used for the same - request as they were initiated for. I don't think that's currently enforced. -* We might consider an alternative client flow where the fallback auth ends up - redirecting to a given URI, instead of doing javascript postMessage foo. I - think that's an orthogonal change to the fallback auth though. -* In theory, any clients that already implement the fallback process for - unknown authentication types will work fine without modification. - Unfortunately, I don't think Riot (on any platform) is among them. +An alternative client flow where the fallback auth ends up redirecting to a +given URI, instead of doing JavaScript postMessage foo could be considered. +This is probably an orthogonal change to the fallback auth though. ## Security considerations +### Why we need user to confirm before the SSO flow + Recall that the thing we are trying to guard against here is a single leaked access-token being used to take over an entire account. So let's assume the -attacker has got hold of an access token for your account. What happens if we - skip the confirmation step? +attacker has got hold of an access token for your account. What happens if the +confirmation step is skipped? -The attacker, who has your access token, starts a UI-Auth session to add his -email address to your account. +The attacker, who has your access token, starts a UI Authentication session to +add their email address to your account. -He then sends you a link "hey, check out this cool video!"; the link leads (via -an innocent-looking url shortener) to +They then sends you a link "hey, check out this cool video!"; the link leads (via +an innocent-looking URL shortener or some other phishing technique) to `/_matrix/client/r0/auth/m.login.sso/fallback/web?session=<...>`, with the ID of the session that he just created. -Recall that we're skipping the confirmation step, so the server redirects -straight to the SSO provider. +Since there is no confirmation step, the server redirects directly to the SSO +provider. It's common for SSO providers to redirect straight back to the app if you've recently authenticated with them; even in the best case, the SSO provider shows -some innocent message along the lines of "Confirm that you want to sign in to -". +an innocent message along the lines of "Confirm that you want to sign in to +". -So the SSO completes, and the attacker's session is validated. +After redirecting back to the homeserver, the SSO is completed and the +attacker's session is validated. They are now able to make their malicious +change to your account. -By telling the user what's about to happen as clearly as we can, and making them -confirm, we can mitigate this problem. +This problem can be mitigated by clearly telling the user what is about to happen. -## Unstable prefix +### Reusing UI-Auth sessions -We should use a vendor prefix here until this hits the spec. +The security of this relies on UI-Auth sessions only being used for the same +request as they were initiated for. It is not believed that this is currently +enforced. + +## Unstable prefix -`org.matrix.login.sso` ? \ No newline at end of file +A vendor prefix of `org.matrix.login.sso` (instead of `m.login.sso` is proposed +until this is part of the specification. From 4d177753e1e8043b92f9bcec0c4501b9a2802a63 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Mon, 9 Mar 2020 15:05:37 -0400 Subject: [PATCH 342/390] Move proposal to proper proposal number. --- ...active-auth-for-sso.md => 2454-ui-interactive-auth-for-sso.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{xxxx-ui-interactive-auth-for-sso.md => 2454-ui-interactive-auth-for-sso.md} (100%) diff --git a/proposals/xxxx-ui-interactive-auth-for-sso.md b/proposals/2454-ui-interactive-auth-for-sso.md similarity index 100% rename from proposals/xxxx-ui-interactive-auth-for-sso.md rename to proposals/2454-ui-interactive-auth-for-sso.md From 570398e045476fcf4e289367d4856d44b0882b67 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Tue, 10 Mar 2020 10:00:58 -0400 Subject: [PATCH 343/390] Remove section on the how the authentication currently works and just reference the specification. --- proposals/2454-ui-interactive-auth-for-sso.md | 65 +------------------ 1 file changed, 1 insertion(+), 64 deletions(-) diff --git a/proposals/2454-ui-interactive-auth-for-sso.md b/proposals/2454-ui-interactive-auth-for-sso.md index 5b6f396d..bfad25a9 100644 --- a/proposals/2454-ui-interactive-auth-for-sso.md +++ b/proposals/2454-ui-interactive-auth-for-sso.md @@ -11,75 +11,12 @@ system, the user doesn't have a password registered with the homeserver. Instead we need to delegate that check to the SSO system. At the protocol level, this means adding support for SSO to -[user-interactive auth](https://matrix.org/docs/spec/client_server/r0.6.0#user-interactive-authentication-api). +[user-interactive authentication API](https://matrix.org/docs/spec/client_server/r0.6.0#user-interactive-authentication-api). In theory, any clients that already implement the fallback process for unknown authentication types will work fine without modification. It is unknown whether this is widely supported among clients. -### UI Auth Overview - -When the client calls one of the protected endpoints, it initially returns a 401 -response. For example: - -``` -POST /_matrix/client/r0/delete_devices HTTP/1.1 -Content-Type: application/json - -{ - "devices": ["FSVVTZRRAA"] -} - -HTTP/1.1 401 Unauthorized -Content-Type: application/json - -{ - "flows": [ - { - "stages": [ - "m.login.password" - ] - } - ], - "params": {}, - "session": "dTKfsLHSAJeAhqfxUsvrIVJd" -} -``` - -The client: - -* inspects the "flows" list -* discovers there is a flow it knows how to follow -* carries out the first "stage" of that flow (m.login.password) - -In this example, the client prompts the user to enter a password. - -The client then resubmits with an additional 'auth' param, with "type" giving -the name of the authentication type it has just carried out. That completes the -authentication flow, so the server is now happy, and returns a 200: - -``` -POST /_matrix/client/r0/delete_devices HTTP/1.1 -Content-Type: application/json - -{ - "devices": ["FSVVTZRRAA"], - "auth": { - "session":"dTKfsLHSAJeAhqfxUsvrIVJd", - "type":"m.login.password", - "identifier":{"type":"m.id.user", "user":"@userid:matrix.org"}, - "password":"" - } -} - - -HTTP/1.1 200 OK -Content-Type: application/json -Content-Length: 179 - -{} -``` - ## Proposal We add an `m.login.sso` authentication type to the UI auth spec. There are no From 78e08c1987460cd14eba27f503b95b6dc4083a29 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Tue, 10 Mar 2020 10:11:08 -0400 Subject: [PATCH 344/390] Attempt to clarify the proposed changes. --- proposals/2454-ui-interactive-auth-for-sso.md | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/proposals/2454-ui-interactive-auth-for-sso.md b/proposals/2454-ui-interactive-auth-for-sso.md index bfad25a9..d3cab51b 100644 --- a/proposals/2454-ui-interactive-auth-for-sso.md +++ b/proposals/2454-ui-interactive-auth-for-sso.md @@ -1,4 +1,4 @@ -# User-Interactive Auth for SSO-backed homeserver +# User-Interactive Authentication for SSO-backed homeserver Certain endpoints, such as `DELETE /_matrix/client/r0/devices/{deviceId}` and `POST /_matrix/client/r0/account/3pid/add`, require the user to reconfirm their @@ -10,17 +10,21 @@ password. However, on a homeserver where users authenticate via a single-sign-on system, the user doesn't have a password registered with the homeserver. Instead we need to delegate that check to the SSO system. -At the protocol level, this means adding support for SSO to +At the protocol level, this means adding support for SSO to the [user-interactive authentication API](https://matrix.org/docs/spec/client_server/r0.6.0#user-interactive-authentication-api). -In theory, any clients that already implement the fallback process for unknown -authentication types will work fine without modification. It is unknown whether -this is widely supported among clients. +In theory, once SSO is added as a possible flow for authentication any clients +that already implement the [fallback process for unknown authentication types](https://matrix.org/docs/spec/client_server/r0.6.0#fallback) +will work fine without modification. It is unknown whether this is widely +supported among clients. ## Proposal -We add an `m.login.sso` authentication type to the UI auth spec. There are no -additional params as part of this auth type. +An [additional authentication type](https://matrix.org/docs/spec/client_server/r0.6.0#authentication-types) +of `m.login.sso` is added to the user-interactive authentication specification. +There are no additional parameters as part of this authentication type. + +When choosing this authentication flow, the following should occur: 1. If the client wants to complete that authentication type, it opens a browser window for `/_matrix/client/r0/auth/m.login.sso/fallback/web?session=<...>` @@ -91,8 +95,9 @@ limited by the chosen SSO implementation, for example: HTTP/1.1 200 OK - can delete device pls? - clicky + A client is trying to remove a device from your account. To confirm this + action, re-authenticate with single sign-on. + If you did not expect this, your account may be compromised! ``` @@ -101,11 +106,12 @@ limited by the chosen SSO implementation, for example: ``` GET https://sso_provider/validate?SAMLRequest= HTTP/1.1 - ... SAML/CAS stuff + ``` 3. The SSO provider validates the user and ends up redirecting the browser back - to the homeserver. (The example below shows a 302 for simplicity but SAML normally uses a 200 with an html , with javascript to automatically submit it.) + to the homeserver. The example below shows a 302 for simplicity, this might + vary based on SSO implementation. ``` HTTP/1.1 302 Moved @@ -191,13 +197,13 @@ change to your account. This problem can be mitigated by clearly telling the user what is about to happen. -### Reusing UI-Auth sessions +### Reusing User Interactive Authentication sessions -The security of this relies on UI-Auth sessions only being used for the same -request as they were initiated for. It is not believed that this is currently -enforced. +The security of this relies on User Interactive Authentication sessions only +being used for the same request as they were initiated for. It is not believed +that this is currently enforced. ## Unstable prefix -A vendor prefix of `org.matrix.login.sso` (instead of `m.login.sso` is proposed +A vendor prefix of `org.matrix.login.sso` is proposed (instead of `m.login.sso`) until this is part of the specification. From 1facf7ff12834ad06fcaff9ee5e2cdf9d1302837 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Thu, 12 Mar 2020 14:14:20 -0400 Subject: [PATCH 345/390] Add an MSC proposal for how to handle sessions when modifying a password. --- ...sword-modification-invalidating-devices.md | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 proposals/2457-password-modification-invalidating-devices.md diff --git a/proposals/2457-password-modification-invalidating-devices.md b/proposals/2457-password-modification-invalidating-devices.md new file mode 100644 index 00000000..910e7b62 --- /dev/null +++ b/proposals/2457-password-modification-invalidating-devices.md @@ -0,0 +1,53 @@ +# Invalidating devices during password modification + +There are multiple use cases for why a user might want to modify their password: + +* Adopting a password manager (to use a unique password or more secure password). +* Password rotation. +* Re-secure a compromised account. +* ... probably tons of others ... + +These can be summarized into two groups: + +1. "My account has been compromised and I need to re-secure it." +2. "I just want to change my password." + +The [current Matrix specification](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-account-password) +does not provide a way to differentiate between these use cases. It gives no +guidance into what should happen to other sessions / devices when a password is +modified and leaves it up to the implementation. + +It is reasonable for a client to want to specify this behavior to offer two +different workflows: + +1. Modify a password and log all other devices out (for use when an account has + been compromised). +2. Modify a password and do not touch any session data (for use in a + non-malicious situations). + +Alternately a client may default to whichever workflow is best for their users. + +## Proposal + +An optional field is added to the JSON body body of the [password reset endpoint](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-account-password) +called `logout_devices`. This is a boolean flag (defaulting to `true`) that +signals to whether other devices and sessions should be invalidated after +modifying the password. + +## Potential issues + +While Synapse defaults to the behavior of invalidating other devices and +sessions this may not be true of other implementations. Thus, a default of +`true` may not be backwards compatible. It might be more prudent to specify that +the behavior of not specifying the `logout_devices` flag is undefined. + +## Alternatives + +Provide a new endpoint in a future version that supports an additional field (as +above), using a new endpoint would avoid backwards compatibility issues. + +## Security considerations + +By defaulting to invalidating devices and sessions the security considerations +of this endpoint should remain intact. A client will need to be modified to +choose to keep other devices active. From 61b306f8a8a63005f2ab131d32cbf1d5409ddada Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Fri, 13 Mar 2020 07:12:57 -0400 Subject: [PATCH 346/390] Remove concerns about backwards compatibility. --- ...-password-modification-invalidating-devices.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/proposals/2457-password-modification-invalidating-devices.md b/proposals/2457-password-modification-invalidating-devices.md index 910e7b62..9af6e229 100644 --- a/proposals/2457-password-modification-invalidating-devices.md +++ b/proposals/2457-password-modification-invalidating-devices.md @@ -36,15 +36,18 @@ modifying the password. ## Potential issues -While Synapse defaults to the behavior of invalidating other devices and -sessions this may not be true of other implementations. Thus, a default of -`true` may not be backwards compatible. It might be more prudent to specify that -the behavior of not specifying the `logout_devices` flag is undefined. +The specification states: + +> The homeserver SHOULD NOT revoke the access token provided in the request, +> however all other access tokens for the user should be revoked if the request +> succeeds. + +Defaulting `logout_devices` to `true` should be backwards compatible. ## Alternatives -Provide a new endpoint in a future version that supports an additional field (as -above), using a new endpoint would avoid backwards compatibility issues. +A new endpoint could be provided in a future version of the specification that +supports an additional field (as described above). ## Security considerations From 2d2731efff99ddeb67bdb16a716ed1eba8660189 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Mon, 16 Mar 2020 10:42:17 -0400 Subject: [PATCH 347/390] Remove duplicated word. Co-Authored-By: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> --- proposals/2457-password-modification-invalidating-devices.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2457-password-modification-invalidating-devices.md b/proposals/2457-password-modification-invalidating-devices.md index 9af6e229..9b59156c 100644 --- a/proposals/2457-password-modification-invalidating-devices.md +++ b/proposals/2457-password-modification-invalidating-devices.md @@ -29,7 +29,7 @@ Alternately a client may default to whichever workflow is best for their users. ## Proposal -An optional field is added to the JSON body body of the [password reset endpoint](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-account-password) +An optional field is added to the JSON body of the [password reset endpoint](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-account-password) called `logout_devices`. This is a boolean flag (defaulting to `true`) that signals to whether other devices and sessions should be invalidated after modifying the password. From 7917d087f3874f07cfaf0750ed493ded95bc77ac Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 19 Mar 2020 15:21:50 +0000 Subject: [PATCH 348/390] clarifications --- proposals/2432-revised-alias-publishing.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/proposals/2432-revised-alias-publishing.md b/proposals/2432-revised-alias-publishing.md index dff72df5..4c2f010f 100644 --- a/proposals/2432-revised-alias-publishing.md +++ b/proposals/2432-revised-alias-publishing.md @@ -96,8 +96,16 @@ rules](https://matrix.org/docs/spec/rooms/v1#authorization-rules) and (Note: Synapse currently implements this check on the main alias, though this is unspecced.) - (TODO: what error code should be returned if the alias is invalid, or if it - points to the wrong room?) + The following error codes are specified: + + * HTTP 400, with `errcode: M_INVALID_PARAMETER` if an attempt is made to add + an entry which is not a well-formed alias (examples: too long, doesn't + start with `#`, doesn't contain a `:`). + + * HTTP 400, with `errcode: M_BAD_ALIAS` if an added alias does not point at + the given room (either because the alias doesn't exist, because it can't + be resolved due to an unreachable server, or because the alias points at a + different room). * Currently, [`PUT /_matrix/client/r0/directory/room/{roomAlias}`](https://matrix.org/docs/spec/client_server/r0.6.0#put-matrix-client-r0-directory-room-roomalias) attempts to send updated `m.room.aliases` events on the caller's @@ -110,8 +118,14 @@ rules](https://matrix.org/docs/spec/rooms/v1#authorization-rules) and deleted. (Again, this is implemented in Synapse but does not appear to be explicitly specced.) The `m.room.aliases` functionality should be removed, and the `m.room.canonical_alias` functionality should be extended to cover - `alt_aliases`. As today, no error occurs if the caller does not have - permission to send such an event. + `alt_aliases`. + + The behaviour if the calling user has permission to delete the alias but + does not have permission to send `m.room.canonical_alias` events in the room + (for example, by virtue of being a "server administrator", or by being the + user that created the alias) is implementation-defined. It is *recommended* + that in this case, the alias is deleted anyway, and a successful response is + returned to the client. * A new api endpoint, `GET /_matrix/client/r0/rooms/{roomId}/aliases` is added, which returns the list of aliases currently defined on the local From 2b71234f3c8cd39da78ae63553d1c97f9d9aed6e Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 19 Mar 2020 14:39:54 -0400 Subject: [PATCH 349/390] clarify first run of iterative auth checks --- specification/rooms/v2.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specification/rooms/v2.rst b/specification/rooms/v2.rst index 4e8365bf..afc114f8 100644 --- a/specification/rooms/v2.rst +++ b/specification/rooms/v2.rst @@ -148,8 +148,8 @@ The *resolution* of a set of states is obtained as follows: 1. Take all *power events* and any events in their auth chains, recursively, that appear in the *full conflicted set* and order them by the *reverse topological power ordering*. -2. Apply the *iterative auth checks algorithm* on the *unconflicted state map* - and the list of events from the previous step to get a partially resolved +2. Apply the *iterative auth checks algorithm*, starting from the *unconflicted state map*, + to the list of events from the previous step to get a partially resolved state. 3. Take all remaining events that weren't picked in step 1 and order them by the mainline ordering based on the power level in the partially resolved From 28dc6a05153437c76156ea022c7d2a7efe58be04 Mon Sep 17 00:00:00 2001 From: "DeepBlueV7.X" Date: Fri, 20 Mar 2020 20:29:44 +0000 Subject: [PATCH 350/390] Update proposals/2422-allow-color-attribute-on-font-tag.md Apply typo fix as suggested. Co-Authored-By: David Vo --- proposals/2422-allow-color-attribute-on-font-tag.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2422-allow-color-attribute-on-font-tag.md b/proposals/2422-allow-color-attribute-on-font-tag.md index 11f06bff..4e6a9ced 100644 --- a/proposals/2422-allow-color-attribute-on-font-tag.md +++ b/proposals/2422-allow-color-attribute-on-font-tag.md @@ -36,4 +36,4 @@ Add the `color` attribute to the allowed attributes of `` in section - Add a section to tell the clients to prefer `color` over `mx-data-color` -> I don't really know, why mx-data-* was chosen, but I assume there was a reason, so I don't want to change that. - Spec an entirely different format for messages (that would probably not make this proposal obsolete) - -> This wouldn't fix the issue, where some client may choose to remove the color tag, since it is dicouraged in the spec. Migration would probably also take a while, so this proposal is a quick solution, that doesn't prevent other solutions at a later date. + -> This wouldn't fix the issue, where some client may choose to remove the color tag, since it is discouraged in the spec. Migration would probably also take a while, so this proposal is a quick solution, that doesn't prevent other solutions at a later date. From 4d808762da4968594c63068351598dbae7245784 Mon Sep 17 00:00:00 2001 From: Aaron Axvig Date: Mon, 23 Mar 2020 12:11:18 -0400 Subject: [PATCH 351/390] Change formatting from italics to code --- api/client-server/redaction.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/client-server/redaction.yaml b/api/client-server/redaction.yaml index 58141049..907b1d16 100644 --- a/api/client-server/redaction.yaml +++ b/api/client-server/redaction.yaml @@ -37,7 +37,7 @@ paths: This cannot be undone. Users may redact their own events, and any user with a power level - greater than or equal to the `redact` power level of the room may + greater than or equal to the ``redact`` power level of the room may redact events there. operationId: redactEvent security: From fc03f7faa6e1d47ccc8470837beef1f43287929b Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Tue, 24 Mar 2020 12:42:15 -0400 Subject: [PATCH 352/390] Remove query_auth from the specification per MSC2451. --- api/server-server/event_auth.yaml | 134 ---------------------------- specification/server_server_api.rst | 1 - 2 files changed, 135 deletions(-) diff --git a/api/server-server/event_auth.yaml b/api/server-server/event_auth.yaml index 0db0d401..8f545e1d 100644 --- a/api/server-server/event_auth.yaml +++ b/api/server-server/event_auth.yaml @@ -72,137 +72,3 @@ paths: example: $ref: "examples/minimal_pdu.json" required: ['auth_chain'] - "/query_auth/{roomId}/{eventId}": - post: - summary: Compare auth chains with the receiving server - description: |- - Compares the auth chain provided with what the receiving server has for the - room ID and event ID combination. - - The auth difference can be calculated in two parts, where the "remote auth" - is the auth chain provided by the sending server and the "local auth" is the - auth chain the receiving server has. With those lists, the algorithm works - bottom-up after sorting each chain by depth then by event ID. The differences - are then discovered and returned as the response to this API call. - operationId: compareEventAuth - security: - - signedRequest: [] - parameters: - - in: path - name: roomId - type: string - description: The room ID to compare the auth chain in. - required: true - x-example: "!abc123:matrix.org" - - in: path - name: eventId - type: string - description: The event ID to compare the auth chain of. - required: true - x-example: "$helloworld:example.org" - - in: body - name: body - schema: - type: object - properties: - auth_chain: - type: array - description: |- - The auth chain (the "remote auth"). Note that events have a different - format depending on the room version - check the `room version specification`_ - for precise event formats. - items: - type: object - title: PDU - description: |- - The `PDUs <#pdus>`_ contained in the auth chain. The event format - varies depending on the room version - check the `room version specification`_ - for precise event formats. - properties: [] - example: - $ref: "examples/minimal_pdu.json" - missing: - type: array - description: |- - A list of event IDs that the sender thinks the receiver is missing. - items: - type: string - example: [] - rejects: - type: object - description: |- - The set of events that the sending server has rejected from the provided - auth chain. - - The ``string`` key is the event ID that was rejected. - additionalProperties: - type: object - title: Rejection Reason - properties: - reason: - type: enum - enum: ['auth_error', 'replaced', 'not_ancestor'] - description: |- - The reason for the event being rejected. - required: ['reason'] - example: { - "$some_event:example.org": { - "reason": "auth_error" - } - } - required: ['auth_chain'] - responses: - 200: - description: The auth chain differences, as determined by the receiver. - schema: - type: object - properties: - auth_chain: - type: array - description: |- - The auth chain the receiver has, and used to determine the auth - chain differences (the "local auth"). Note that events have a different - format depending on the room version - check the `room version specification`_ - for precise event formats. - items: - type: object - title: PDU - description: |- - The `PDUs <#pdus>`_ contained in the auth chain. The event format - varies depending on the room version - check the `room version specification`_ - for precise event formats. - properties: [] - example: - $ref: "examples/minimal_pdu.json" - missing: - type: array - description: |- - The list of event IDs that the receiver believes it is missing, - after comparing the "remote auth" and "local auth" chains. - items: - type: string - example: ["$a_missing_event:example.org"] - rejects: - type: object - description: |- - The set of events that the receiving server has rejected from the - auth chain, not including events that the sending server is missing - as determined from the difference algorithm. - - The ``string`` key is the event ID that was rejected. - additionalProperties: - type: object - title: Rejection Reason - properties: - reason: - type: enum - enum: ['auth_error', 'replaced', 'not_ancestor'] - description: |- - The reason for the event being rejected. - required: ['reason'] - example: { - "$some_event:example.org": { - "reason": "auth_error" - } - } - required: ['auth_chain', 'missing', 'rejects'] diff --git a/specification/server_server_api.rst b/specification/server_server_api.rst index a3a57dbf..f0a9a563 100644 --- a/specification/server_server_api.rst +++ b/specification/server_server_api.rst @@ -1077,7 +1077,6 @@ The following endpoint prefixes MUST be protected: * ``/_matrix/federation/v1/state_ids`` * ``/_matrix/federation/v1/backfill`` * ``/_matrix/federation/v1/event_auth`` -* ``/_matrix/federation/v1/query_auth`` * ``/_matrix/federation/v1/get_missing_events`` From 9944decacfe58c0a635b34bd30bdf8f3e4a73329 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Tue, 24 Mar 2020 13:17:22 -0400 Subject: [PATCH 353/390] Add changelog entry. --- changelogs/server_server/newsfragments/2470.removal | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/server_server/newsfragments/2470.removal diff --git a/changelogs/server_server/newsfragments/2470.removal b/changelogs/server_server/newsfragments/2470.removal new file mode 100644 index 00000000..3221f5bf --- /dev/null +++ b/changelogs/server_server/newsfragments/2470.removal @@ -0,0 +1 @@ +Remove the unused ``query_auth`` API per [MSC2451](https://github.com/matrix-org/matrix-doc/pull/2451). From 4f0ac741a2350a46231d320cf310718339d14a2d Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 24 Mar 2020 11:22:05 -0600 Subject: [PATCH 354/390] Update changelogs/server_server/newsfragments/2470.removal --- changelogs/server_server/newsfragments/2470.removal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/server_server/newsfragments/2470.removal b/changelogs/server_server/newsfragments/2470.removal index 3221f5bf..51cdadd4 100644 --- a/changelogs/server_server/newsfragments/2470.removal +++ b/changelogs/server_server/newsfragments/2470.removal @@ -1 +1 @@ -Remove the unused ``query_auth`` API per [MSC2451](https://github.com/matrix-org/matrix-doc/pull/2451). +Remove the unused ``query_auth`` API per `MSC2451 `_. From f7ce75d36c2dc09b24cba7c6c8fcdd419992e659 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 24 Mar 2020 11:29:00 -0600 Subject: [PATCH 355/390] Support a 'removed endpoints' changelog type --- changelogs/README.md | 19 ++++++++++++------- changelogs/application_service/pyproject.toml | 15 ++++++++++----- changelogs/client_server/pyproject.toml | 15 ++++++++++----- changelogs/identity_service/pyproject.toml | 5 +++++ changelogs/push_gateway/pyproject.toml | 15 ++++++++++----- changelogs/server_server/pyproject.toml | 15 ++++++++++----- 6 files changed, 57 insertions(+), 27 deletions(-) diff --git a/changelogs/README.md b/changelogs/README.md index a5fb1fb7..5a5b6271 100644 --- a/changelogs/README.md +++ b/changelogs/README.md @@ -2,14 +2,14 @@ # Changelogs -[Towncrier](https://github.com/hawkowl/towncrier) is used to manage the changelog and +[Towncrier](https://github.com/hawkowl/towncrier) is used to manage the changelog and keep it up to date. Because of this, updating a changelog is really easy. ## How to update a changelog when releasing an API 1. Ensure you're in your Python 3 virtual environment 2. `cd` your way to the API you're releasing (eg: `cd changelogs/client_server`) -3. Run `towncrier --version "r0.4.0" --name "client-server" --yes` substituting the +3. Run `towncrier --version "r0.4.0" --name "client-server" --yes` substituting the variables as approprite. Note that `--name` is required although the value is ignored. 4. Commit the changes and finish the release process. @@ -26,27 +26,32 @@ For this example, we're going to pretend that the `server_server` API doesn't ex directory = "newsfragments" issue_format = "`#{issue} `_" title_format = "{version}" - + [[tool.towncrier.type]] directory = "breaking" name = "Breaking Changes" showcontent = true - + [[tool.towncrier.type]] directory = "deprecation" name = "Deprecations" showcontent = true - + [[tool.towncrier.type]] directory = "new" name = "New Endpoints" showcontent = true - + + [[tool.towncrier.type]] + directory = "removal" + name = "Removed Endpoints" + showcontent = true + [[tool.towncrier.type]] directory = "feature" name = "Backwards Compatible Changes" showcontent = true - + [[tool.towncrier.type]] directory = "clarification" name = "Spec Clarifications" diff --git a/changelogs/application_service/pyproject.toml b/changelogs/application_service/pyproject.toml index 44d430e8..278def78 100644 --- a/changelogs/application_service/pyproject.toml +++ b/changelogs/application_service/pyproject.toml @@ -3,27 +3,32 @@ directory = "newsfragments" issue_format = "`#{issue} `_" title_format = "{version}" - + [[tool.towncrier.type]] directory = "breaking" name = "Breaking Changes" showcontent = true - + [[tool.towncrier.type]] directory = "deprecation" name = "Deprecations" showcontent = true - + [[tool.towncrier.type]] directory = "new" name = "New Endpoints" showcontent = true - + + [[tool.towncrier.type]] + directory = "removal" + name = "Removed Endpoints" + showcontent = true + [[tool.towncrier.type]] directory = "feature" name = "Backwards Compatible Changes" showcontent = true - + [[tool.towncrier.type]] directory = "clarification" name = "Spec Clarifications" diff --git a/changelogs/client_server/pyproject.toml b/changelogs/client_server/pyproject.toml index 8fa3f6b5..eb9e7b4e 100644 --- a/changelogs/client_server/pyproject.toml +++ b/changelogs/client_server/pyproject.toml @@ -3,27 +3,32 @@ directory = "newsfragments" issue_format = "`#{issue} `_" title_format = "{version}" - + [[tool.towncrier.type]] directory = "breaking" name = "Breaking Changes" showcontent = true - + [[tool.towncrier.type]] directory = "deprecation" name = "Deprecations" showcontent = true - + [[tool.towncrier.type]] directory = "new" name = "New Endpoints" showcontent = true - + + [[tool.towncrier.type]] + directory = "removal" + name = "Removed Endpoints" + showcontent = true + [[tool.towncrier.type]] directory = "feature" name = "Backwards Compatible Changes" showcontent = true - + [[tool.towncrier.type]] directory = "clarification" name = "Spec Clarifications" diff --git a/changelogs/identity_service/pyproject.toml b/changelogs/identity_service/pyproject.toml index a7fe582d..2e50b9df 100644 --- a/changelogs/identity_service/pyproject.toml +++ b/changelogs/identity_service/pyproject.toml @@ -19,6 +19,11 @@ name = "New Endpoints" showcontent = true + [[tool.towncrier.type]] + directory = "removal" + name = "Removed Endpoints" + showcontent = true + [[tool.towncrier.type]] directory = "feature" name = "Backwards Compatible Changes" diff --git a/changelogs/push_gateway/pyproject.toml b/changelogs/push_gateway/pyproject.toml index dad1bc04..9f2595c9 100644 --- a/changelogs/push_gateway/pyproject.toml +++ b/changelogs/push_gateway/pyproject.toml @@ -3,27 +3,32 @@ directory = "newsfragments" issue_format = "`#{issue} `_" title_format = "{version}" - + [[tool.towncrier.type]] directory = "breaking" name = "Breaking Changes" showcontent = true - + [[tool.towncrier.type]] directory = "deprecation" name = "Deprecations" showcontent = true - + [[tool.towncrier.type]] directory = "new" name = "New Endpoints" showcontent = true - + + [[tool.towncrier.type]] + directory = "removal" + name = "Removed Endpoints" + showcontent = true + [[tool.towncrier.type]] directory = "feature" name = "Backwards Compatible Changes" showcontent = true - + [[tool.towncrier.type]] directory = "clarification" name = "Spec Clarifications" diff --git a/changelogs/server_server/pyproject.toml b/changelogs/server_server/pyproject.toml index 98478527..6a9dca1d 100644 --- a/changelogs/server_server/pyproject.toml +++ b/changelogs/server_server/pyproject.toml @@ -3,27 +3,32 @@ directory = "newsfragments" issue_format = "`#{issue} `_" title_format = "{version}" - + [[tool.towncrier.type]] directory = "breaking" name = "Breaking Changes" showcontent = true - + [[tool.towncrier.type]] directory = "deprecation" name = "Deprecations" showcontent = true - + [[tool.towncrier.type]] directory = "new" name = "New Endpoints" showcontent = true - + + [[tool.towncrier.type]] + directory = "removal" + name = "Removed Endpoints" + showcontent = true + [[tool.towncrier.type]] directory = "feature" name = "Backwards Compatible Changes" showcontent = true - + [[tool.towncrier.type]] directory = "clarification" name = "Spec Clarifications" From eb48863c4044105b86d62997d88a086eda22e690 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Thu, 26 Mar 2020 11:47:32 -0400 Subject: [PATCH 356/390] Markdown formatting. Co-Authored-By: Hubert Chathi --- proposals/2454-ui-interactive-auth-for-sso.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2454-ui-interactive-auth-for-sso.md b/proposals/2454-ui-interactive-auth-for-sso.md index d3cab51b..3532a2fc 100644 --- a/proposals/2454-ui-interactive-auth-for-sso.md +++ b/proposals/2454-ui-interactive-auth-for-sso.md @@ -52,7 +52,7 @@ limited by the chosen SSO implementation, for example: * SAML2 servers typically only support one URL per service provider, so in practice it will need to be the same as that already used for the login flow - (for synapse, it's /_matrix/saml2/authn_response) - and the server needs to + (for synapse, it's `/_matrix/saml2/authn_response`) - and the server needs to be able to figure out if it's doing SSO for a login attempt or an SSO attempt. * CAS doesn't have the same restriction. From 745f8c09df6f948f9083449b2b0848444776855e Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Fri, 27 Mar 2020 13:59:37 -0400 Subject: [PATCH 357/390] Fix incorrect statement about the current spec's guidance. --- .../2457-password-modification-invalidating-devices.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/2457-password-modification-invalidating-devices.md b/proposals/2457-password-modification-invalidating-devices.md index 9b59156c..ccde2ac6 100644 --- a/proposals/2457-password-modification-invalidating-devices.md +++ b/proposals/2457-password-modification-invalidating-devices.md @@ -13,9 +13,9 @@ These can be summarized into two groups: 2. "I just want to change my password." The [current Matrix specification](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-account-password) -does not provide a way to differentiate between these use cases. It gives no -guidance into what should happen to other sessions / devices when a password is -modified and leaves it up to the implementation. +does not provide a way to differentiate between these use cases. It currently +specifies behavior that fits well into the first use-case above: that the +sessions except the current session should be revoked. It is reasonable for a client to want to specify this behavior to offer two different workflows: From edd75703e2e51eab8ccf611a01486eb26e6929e9 Mon Sep 17 00:00:00 2001 From: Aaron Axvig Date: Sat, 28 Mar 2020 18:32:53 -0400 Subject: [PATCH 358/390] Improved steps for building docs on Windows --- README.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.rst b/README.rst index ace8ebdd..d7b75876 100644 --- a/README.rst +++ b/README.rst @@ -66,6 +66,8 @@ The above will write the rendered version of the specification to Windows users ~~~~~~~~~~~~~ +The ``source`` program does not exist on Windows, so instead run one of the +``activate`` files in ``.\env\Scripts\`` to activate the virtual environment. If you're on Windows Vista or higher, be sure that the "Symbolic Links" option was selected when installing Git prior to cloning this repository. If @@ -81,6 +83,24 @@ cloned matrix-doc directory and run the following:: This will delete the file and replace it with a symlink. Git should not detect this as a change, and you should be able to go back to building the project. +Powershell doesn't have ``mklink`` so use cmd. Or, here is the full set of +steps for Powershell: + +.. code-block:: powershell + + virtualenv -p python3 env + .\env\Scripts\pip.exe install -r .\scripts\requirements.txt + + .\env\Scripts\activate.ps1 # Adds a global function 'deactivate' for leaving the env when you are done. + .\Scripts\gendoc.py + + # If you get errors: + cd api\client-server\definitions + del event-schemas + New-Item -ItemType SymbolicLink -Name event-schemas -Value "..\..\..\event-schemas" + cd ..\..\..\ + .\scripts\gendoc.py + Generating the OpenAPI (Swagger) specs -------------------------------------- From 2f5d8a4a271982a786ded1b2dc23a02e35c95583 Mon Sep 17 00:00:00 2001 From: aaronaxvig Date: Mon, 30 Mar 2020 14:32:45 -0400 Subject: [PATCH 359/390] Removed PowerShell stuff Clarified to use Command Prompt as recommended is pull request #2479, removed all PowerShell stuff. --- README.rst | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/README.rst b/README.rst index d7b75876..01ea8e72 100644 --- a/README.rst +++ b/README.rst @@ -73,7 +73,7 @@ If you're on Windows Vista or higher, be sure that the "Symbolic Links" option was selected when installing Git prior to cloning this repository. If you're still seeing errors about files not being found it is likely because the symlink at ``api/client-server/definitions/event-schemas`` looks like a -file. To correct the problem, open an Administrative/Elevated shell in your +file. To correct the problem, open an Administrative/Elevated Command Prompt in your cloned matrix-doc directory and run the following:: cd api\client-server\definitions @@ -83,24 +83,6 @@ cloned matrix-doc directory and run the following:: This will delete the file and replace it with a symlink. Git should not detect this as a change, and you should be able to go back to building the project. -Powershell doesn't have ``mklink`` so use cmd. Or, here is the full set of -steps for Powershell: - -.. code-block:: powershell - - virtualenv -p python3 env - .\env\Scripts\pip.exe install -r .\scripts\requirements.txt - - .\env\Scripts\activate.ps1 # Adds a global function 'deactivate' for leaving the env when you are done. - .\Scripts\gendoc.py - - # If you get errors: - cd api\client-server\definitions - del event-schemas - New-Item -ItemType SymbolicLink -Name event-schemas -Value "..\..\..\event-schemas" - cd ..\..\..\ - .\scripts\gendoc.py - Generating the OpenAPI (Swagger) specs -------------------------------------- From 4cfcda57fb3676d6de6145bb9bd1f9de60645559 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Wed, 1 Apr 2020 10:48:05 -0400 Subject: [PATCH 360/390] Clarify that the only new item here is the new authentication type. --- proposals/2454-ui-interactive-auth-for-sso.md | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/proposals/2454-ui-interactive-auth-for-sso.md b/proposals/2454-ui-interactive-auth-for-sso.md index 3532a2fc..fff96dcf 100644 --- a/proposals/2454-ui-interactive-auth-for-sso.md +++ b/proposals/2454-ui-interactive-auth-for-sso.md @@ -22,11 +22,27 @@ supported among clients. An [additional authentication type](https://matrix.org/docs/spec/client_server/r0.6.0#authentication-types) of `m.login.sso` is added to the user-interactive authentication specification. -There are no additional parameters as part of this authentication type. + +There are no additional parameters as part of this authentication type. As per +the user-interactive authentication specification the only parameter include in +the `auth` dictionary should be the session ID from the homeserver, e.g.: + +```json +{ + "auth": { + "session": "" + } +} +``` + +### Detailed fallback authentication flow: + +The following is a re-iteration of the [fallback authentication flow](https://matrix.org/docs/spec/client_server/r0.6.0#fallback), +but with details filled in for the proposed new authentication type. When choosing this authentication flow, the following should occur: -1. If the client wants to complete that authentication type, it opens a browser +1. If the client wants to complete authentication using SSO, it opens a browser window for `/_matrix/client/r0/auth/m.login.sso/fallback/web?session=<...>` with session set to the UI-Auth session id (from the "auth" dict). @@ -59,6 +75,10 @@ limited by the chosen SSO implementation, for example: ### Example flow: +A more complete example is provided below in which a user attempts to delete +a device and is pushed into the user interactive authentication process with +SSO being the only possible flow. + 0. Client submits the request, which the server says requires SSO: ``` @@ -163,7 +183,7 @@ limited by the chosen SSO implementation, for example: ## Alternatives An alternative client flow where the fallback auth ends up redirecting to a -given URI, instead of doing JavaScript postMessage foo could be considered. +given URI, instead of doing JavaScript `postMessage` foo could be considered. This is probably an orthogonal change to the fallback auth though. ## Security considerations @@ -200,8 +220,9 @@ This problem can be mitigated by clearly telling the user what is about to happe ### Reusing User Interactive Authentication sessions The security of this relies on User Interactive Authentication sessions only -being used for the same request as they were initiated for. It is not believed -that this is currently enforced. +being used for the same request as they were initiated for. This security is not +only a concern for the proposed SSO authentication type. It is not believed +that this is currently enforced in implementations. ## Unstable prefix From 5ae8a8bcfd563ef0d472d0560beba53798897e3c Mon Sep 17 00:00:00 2001 From: Ben Parsons Date: Tue, 7 Apr 2020 14:37:55 +0100 Subject: [PATCH 361/390] fix pagination in scraper --- scripts/proposals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/proposals.py b/scripts/proposals.py index 27cc6cfb..96904ce4 100755 --- a/scripts/proposals.py +++ b/scripts/proposals.py @@ -38,7 +38,7 @@ def getpage(url): pagecount = 1 for link in resp.links.values(): if link['rel'] == 'last': - pagecount = int(re.search('page=(.+?)', link['url']).group(1)) + pagecount = int(re.search('page=(.+)', link['url']).group(1)) val = resp.json() if not isinstance(val, list): From 7c037d2490203d156e9def41f43ce255d863d48b Mon Sep 17 00:00:00 2001 From: Ben Parsons Date: Tue, 7 Apr 2020 15:26:48 +0100 Subject: [PATCH 362/390] improve capture and add example --- scripts/proposals.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/proposals.py b/scripts/proposals.py index 96904ce4..faa10a83 100755 --- a/scripts/proposals.py +++ b/scripts/proposals.py @@ -38,7 +38,10 @@ def getpage(url): pagecount = 1 for link in resp.links.values(): if link['rel'] == 'last': - pagecount = int(re.search('page=(.+)', link['url']).group(1)) + # we extract the pagecount from the `page` param of the last url + # in the response, eg + # 'https://api.github.com/repositories/24998719/issues?state=all&labels=proposal&page=10' + pagecount = int(re.search('page=(\d+)', link['url']).group(1)) val = resp.json() if not isinstance(val, list): From af7cf84083cf17de938102bfac1951caebc89c3e Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Tue, 7 Apr 2020 17:54:09 +0200 Subject: [PATCH 363/390] Fix iv parameter description Signed-off-by: Lukas Lihotzki --- specification/modules/end_to_end_encryption.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/modules/end_to_end_encryption.rst b/specification/modules/end_to_end_encryption.rst index 631b182a..4b433f17 100644 --- a/specification/modules/end_to_end_encryption.rst +++ b/specification/modules/end_to_end_encryption.rst @@ -277,7 +277,7 @@ Parameter Type Description ========= ================ ===================================================== url string **Required.** The URL to the file. key JWK **Required.** A `JSON Web Key`_ object. -iv string **Required.** The Initialisation Vector used by +iv string **Required.** The 128-bit unique counter block used by AES-CTR, encoded as unpadded base64. hashes {string: string} **Required.** A map from an algorithm name to a hash of the ciphertext, encoded as unpadded base64. Clients From b908b8e777840741587d22c6c1bbf25f69ecaeab Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 7 Apr 2020 13:13:52 -0600 Subject: [PATCH 364/390] Add changelog --- changelogs/client_server/newsfragments/2492.clarification | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/2492.clarification diff --git a/changelogs/client_server/newsfragments/2492.clarification b/changelogs/client_server/newsfragments/2492.clarification new file mode 100644 index 00000000..88c478ec --- /dev/null +++ b/changelogs/client_server/newsfragments/2492.clarification @@ -0,0 +1 @@ +Clarify the IV data type for encrypted files. From f6879c897bace91ccfe0cb102f490e9dc5619cde Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Wed, 15 Apr 2020 07:39:00 -0400 Subject: [PATCH 365/390] Fix minor grammatical fixes. Co-Authored-By: Hubert Chathi --- proposals/2454-ui-interactive-auth-for-sso.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/2454-ui-interactive-auth-for-sso.md b/proposals/2454-ui-interactive-auth-for-sso.md index fff96dcf..e39facfb 100644 --- a/proposals/2454-ui-interactive-auth-for-sso.md +++ b/proposals/2454-ui-interactive-auth-for-sso.md @@ -13,7 +13,7 @@ we need to delegate that check to the SSO system. At the protocol level, this means adding support for SSO to the [user-interactive authentication API](https://matrix.org/docs/spec/client_server/r0.6.0#user-interactive-authentication-api). -In theory, once SSO is added as a possible flow for authentication any clients +In theory, once SSO is added as a possible flow for authentication, any clients that already implement the [fallback process for unknown authentication types](https://matrix.org/docs/spec/client_server/r0.6.0#fallback) will work fine without modification. It is unknown whether this is widely supported among clients. @@ -24,7 +24,7 @@ An [additional authentication type](https://matrix.org/docs/spec/client_server/r of `m.login.sso` is added to the user-interactive authentication specification. There are no additional parameters as part of this authentication type. As per -the user-interactive authentication specification the only parameter include in +the user-interactive authentication specification, the only parameter included in the `auth` dictionary should be the session ID from the homeserver, e.g.: ```json @@ -57,7 +57,7 @@ When choosing this authentication flow, the following should occur: 3. The SSO provider validates the user, and redirects the browser back to the homeserver. 4. The homeserver validates the response from the SSO provider, updates the - user-interactive auth session to show that the SSO has completed, + user-interactive auth session to show that the SSO has completed, and [serves the fallback auth completion page as specced](https://matrix.org/docs/spec/client_server/r0.6.0#fallback). 5. The client resubmits its original request, with its original session id, which now should complete. From 9103a0a3980192f78d65f2f9c08f8e6a5d3e726b Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 16 Apr 2020 14:58:27 -0400 Subject: [PATCH 366/390] minor clarifications and corrections --- proposals/1756-cross-signing.md | 4 ++-- proposals/1946-secure_server-side_storage.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md index 4b5904bc..de08422a 100644 --- a/proposals/1756-cross-signing.md +++ b/proposals/1756-cross-signing.md @@ -216,7 +216,7 @@ response: } ``` -Similarly, the federation endpoints `POST /user/keys/query` and `POST +Similarly, the federation endpoints `POST /user/keys/query` and `GET /user/devices/{userId}` will include the master and self-signing keys. (It will not include the user-signing key because it is not intended to be visible to other users.) @@ -463,7 +463,7 @@ response: } ``` -Similarly, the federation endpoints `GET /user/keys/query` and `POST +Similarly, the federation endpoints `POST /user/keys/query` and `GET /user/devices/{userId}` will include the new signatures for her own devices or master key, but not signatures made by her user-signing key. diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index fd907e53..3f386d5e 100644 --- a/proposals/1946-secure_server-side_storage.md +++ b/proposals/1946-secure_server-side_storage.md @@ -66,7 +66,7 @@ corresponds to the public key. Encrypted data is stored in the user's account_data using the event type defined by the feature that uses the data. For example, decryption keys for -key backups could be stored under the type `m.megolm_backup.v1.recovery_key`, +key backups could be stored under the type `m.megolm_backup.v1`, or the self-signing key for cross-signing could be stored under the type `m.cross_signing.self_signing`. From 3c34d83082f445ea7f5bd80720f87bf2fa749721 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 22 Apr 2020 13:37:50 -0600 Subject: [PATCH 367/390] Add some words about what categories are --- specification/proposals_intro.rst | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/specification/proposals_intro.rst b/specification/proposals_intro.rst index 82a4225b..47cb58fc 100644 --- a/specification/proposals_intro.rst +++ b/specification/proposals_intro.rst @@ -341,6 +341,35 @@ Closed proposal-closed A proposal which Obsolete obsolete A proposal which has been made obsolete by another proposal or decision elsewhere. =============================== ============================= ==================================== +Categories +---------- + +We use category labels on MSCs to place them into a track of work. The spec core team +decides which of the tracks they are focusing on for the next while and generally makes +an effort to pull MSCs out of that category when possible. + +The current categories are: + +============ ================= ====================================== +Name Github Label Description +============ ================= ====================================== +Core kind:core Important for the protocol's success. +Feature kind:feature Nice to have additions to the spec. +Maintenance kind:maintenance Fixes or clarifies existing spec. +============ ================= ====================================== + +Some examples of core MSCs would be aggregations, cross-signing, and groups/communities. +These are the sorts of things that if not implemented could cause the protocol to +fail or become second-class. Features would be areas like enhanced media APIs, +new transports, and bookmarks in comparison. Finally, maintenance MSCs would include +improving error codes, clarifying what is required of an API, and adding properties +to an API which makes it easier to use. + +The spec core team assigns a category to each MSC based on the descriptions above. +This can mean that new MSCs get categorized into an area the team isn't focused on, +though that can always change as priorities evolve. We still encourage that MSCs be +opened, even if not the focus for the time being, as they can still make progress and +even be merged without the spec core team focusing on them specifically. Proposal Tracking ----------------- From b5868a59d64e30829ee28ae19f773a565399846f Mon Sep 17 00:00:00 2001 From: Rudi Floren Date: Wed, 22 Apr 2020 22:18:28 +0200 Subject: [PATCH 368/390] Fix typo in Fed. API request auth python example Fixes the typo in the Request Authentication python example. It seems like a copy paste error. Closes: #2509 Signed-off-by: Rudi Floren --- specification/server_server_api.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specification/server_server_api.rst b/specification/server_server_api.rst index f0a9a563..655a8cfc 100644 --- a/specification/server_server_api.rst +++ b/specification/server_server_api.rst @@ -316,8 +316,8 @@ Example python code: "destination": destination_name, } - if content_json is not None: - request["content"] = content + if content is not None: + request_json["content"] = content signed_json = sign_json(request_json, origin_name, origin_signing_key) From 2bc798dcfa9cebed32c31c5083b365d59efadefc Mon Sep 17 00:00:00 2001 From: Rudi Floren Date: Wed, 22 Apr 2020 22:21:36 +0200 Subject: [PATCH 369/390] Add changelog newsfragment --- changelogs/server_server/newsfragments/2510 | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/server_server/newsfragments/2510 diff --git a/changelogs/server_server/newsfragments/2510 b/changelogs/server_server/newsfragments/2510 new file mode 100644 index 00000000..c9cf10be --- /dev/null +++ b/changelogs/server_server/newsfragments/2510 @@ -0,0 +1 @@ +Fix typo in Request Authentication python example From 97b23da9f6fd52368bee002102f7de9f8e246cdd Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 22 Apr 2020 14:27:22 -0600 Subject: [PATCH 370/390] Update changelog entry for 2510 clarification --- changelogs/server_server/newsfragments/2510 | 1 - changelogs/server_server/newsfragments/2510.clarification | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 changelogs/server_server/newsfragments/2510 create mode 100644 changelogs/server_server/newsfragments/2510.clarification diff --git a/changelogs/server_server/newsfragments/2510 b/changelogs/server_server/newsfragments/2510 deleted file mode 100644 index c9cf10be..00000000 --- a/changelogs/server_server/newsfragments/2510 +++ /dev/null @@ -1 +0,0 @@ -Fix typo in Request Authentication python example diff --git a/changelogs/server_server/newsfragments/2510.clarification b/changelogs/server_server/newsfragments/2510.clarification new file mode 100644 index 00000000..9c96c5a5 --- /dev/null +++ b/changelogs/server_server/newsfragments/2510.clarification @@ -0,0 +1 @@ +Fix typo in Request Authentication python example. From 16905a2f5bf3cea32ee0567b8bfd4883d64bccc6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 22 Apr 2020 14:31:34 -0600 Subject: [PATCH 371/390] Make the spec core team the Spec Core Team --- specification/proposals_intro.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specification/proposals_intro.rst b/specification/proposals_intro.rst index 47cb58fc..771d2d6a 100644 --- a/specification/proposals_intro.rst +++ b/specification/proposals_intro.rst @@ -344,7 +344,7 @@ Obsolete obsolete A proposal which Categories ---------- -We use category labels on MSCs to place them into a track of work. The spec core team +We use category labels on MSCs to place them into a track of work. The Spec Core Team decides which of the tracks they are focusing on for the next while and generally makes an effort to pull MSCs out of that category when possible. @@ -365,11 +365,11 @@ new transports, and bookmarks in comparison. Finally, maintenance MSCs would inc improving error codes, clarifying what is required of an API, and adding properties to an API which makes it easier to use. -The spec core team assigns a category to each MSC based on the descriptions above. +The Spec Core Team assigns a category to each MSC based on the descriptions above. This can mean that new MSCs get categorized into an area the team isn't focused on, though that can always change as priorities evolve. We still encourage that MSCs be opened, even if not the focus for the time being, as they can still make progress and -even be merged without the spec core team focusing on them specifically. +even be merged without the Spec Core Team focusing on them specifically. Proposal Tracking ----------------- From 0ae597626af258c133ac285220754ccbd663bae6 Mon Sep 17 00:00:00 2001 From: David Vo Date: Fri, 1 May 2020 00:32:15 +1000 Subject: [PATCH 372/390] Fix .m.rule.contains_user_name push rule to highlight Signed-off-by: David Vo --- specification/modules/push.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specification/modules/push.rst b/specification/modules/push.rst index c9489987..ebf1aef2 100644 --- a/specification/modules/push.rst +++ b/specification/modules/push.rst @@ -473,6 +473,9 @@ Definition (as a ``content`` rule): { "set_tweak": "sound", "value": "default" + }, + { + "set_tweak": "highlight" } ] } From b8efb5ac4701554c5eda7ca7ba3a8f2a880c93b5 Mon Sep 17 00:00:00 2001 From: David Vo Date: Fri, 1 May 2020 15:27:20 +1000 Subject: [PATCH 373/390] Add changelog for 2519 Signed-off-by: David Vo --- changelogs/client_server/newsfragments/2519.clarification | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/2519.clarification diff --git a/changelogs/client_server/newsfragments/2519.clarification b/changelogs/client_server/newsfragments/2519.clarification new file mode 100644 index 00000000..eb3fb6a8 --- /dev/null +++ b/changelogs/client_server/newsfragments/2519.clarification @@ -0,0 +1 @@ +Fix the ``.m.rule.contains_user_name`` default push rule to set the highlight tweak, matching Synapse and users' expectations. From 411b3f432b57e9f1f6d3a170cd159020fb26bd74 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 1 May 2020 10:25:47 -0600 Subject: [PATCH 374/390] Update changelog --- changelogs/client_server/newsfragments/2519.clarification | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/client_server/newsfragments/2519.clarification b/changelogs/client_server/newsfragments/2519.clarification index eb3fb6a8..4b32da1e 100644 --- a/changelogs/client_server/newsfragments/2519.clarification +++ b/changelogs/client_server/newsfragments/2519.clarification @@ -1 +1 @@ -Fix the ``.m.rule.contains_user_name`` default push rule to set the highlight tweak, matching Synapse and users' expectations. +Fix the ``.m.rule.contains_user_name`` default push rule to set the highlight tweak. From 958e1b4a2e2ffd7dd4210818daf88385325ac0c4 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Mon, 4 May 2020 14:36:52 -0400 Subject: [PATCH 375/390] Make the spec changes for MSC 2457. --- api/client-server/registration.yaml | 8 ++++++-- changelogs/client_server/newsfragments/2523.feature | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 changelogs/client_server/newsfragments/2523.feature diff --git a/api/client-server/registration.yaml b/api/client-server/registration.yaml index 8114299e..ecc7a6fa 100644 --- a/api/client-server/registration.yaml +++ b/api/client-server/registration.yaml @@ -328,8 +328,7 @@ paths: The homeserver may change the flows available depending on whether a valid access token is provided. The homeserver SHOULD NOT revoke the - access token provided in the request, however all other access tokens - for the user should be revoked if the request succeeds. + access token provided in the request. security: - accessToken: [] operationId: changePassword @@ -343,6 +342,11 @@ paths: type: string description: The new password for the account. example: "ihatebananas" + logout_devices: + type: boolean + description: |- + Whether other access tokens should be revoked if the request succeeds. Defaults to true. + example: true auth: description: |- Additional authentication information for the user-interactive authentication API. diff --git a/changelogs/client_server/newsfragments/2523.feature b/changelogs/client_server/newsfragments/2523.feature new file mode 100644 index 00000000..6f690ea4 --- /dev/null +++ b/changelogs/client_server/newsfragments/2523.feature @@ -0,0 +1 @@ +Optionally invalidate other access tokens during password modification per `MSC 2457 `_. From 0c582ea8c71658b71e76210d30f2ece5c3ea8644 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Mon, 4 May 2020 16:21:19 -0400 Subject: [PATCH 376/390] Apply suggestions from code review Co-authored-by: Travis Ralston --- api/client-server/registration.yaml | 3 ++- changelogs/client_server/newsfragments/2523.feature | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/api/client-server/registration.yaml b/api/client-server/registration.yaml index ecc7a6fa..c3f544c1 100644 --- a/api/client-server/registration.yaml +++ b/api/client-server/registration.yaml @@ -345,7 +345,8 @@ paths: logout_devices: type: boolean description: |- - Whether other access tokens should be revoked if the request succeeds. Defaults to true. + Whether the other access tokens, and their associated devices, for the user should be + revoked if the request succeeds. Defaults to true. example: true auth: description: |- diff --git a/changelogs/client_server/newsfragments/2523.feature b/changelogs/client_server/newsfragments/2523.feature index 6f690ea4..e45d1c2f 100644 --- a/changelogs/client_server/newsfragments/2523.feature +++ b/changelogs/client_server/newsfragments/2523.feature @@ -1 +1 @@ -Optionally invalidate other access tokens during password modification per `MSC 2457 `_. +Optionally invalidate other access tokens during password modification per `MSC2457 `_. From 1e330c942307259ffdf076940ca70b7ba33da83e Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Mon, 4 May 2020 16:23:31 -0400 Subject: [PATCH 377/390] Clarify revocation behavior. --- api/client-server/registration.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/client-server/registration.yaml b/api/client-server/registration.yaml index c3f544c1..50ce4a96 100644 --- a/api/client-server/registration.yaml +++ b/api/client-server/registration.yaml @@ -328,7 +328,8 @@ paths: The homeserver may change the flows available depending on whether a valid access token is provided. The homeserver SHOULD NOT revoke the - access token provided in the request. + access token provided in the request. Whether other access tokens for + the user are revoked depends on the request parameters. security: - accessToken: [] operationId: changePassword From e89521d1951976cb2a3bca9509e5e501b7fd6325 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 4 May 2020 22:08:22 -0400 Subject: [PATCH 378/390] some fixes (spelling, RST, and naming) --- api/client-server/keys.yaml | 2 +- specification/modules/end_to_end_encryption.rst | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/client-server/keys.yaml b/api/client-server/keys.yaml index 69e39def..e172ea8a 100644 --- a/api/client-server/keys.yaml +++ b/api/client-server/keys.yaml @@ -101,7 +101,7 @@ paths: responses: 200: description: - The provided keys were sucessfully uploaded. + The provided keys were successfully uploaded. schema: type: object properties: diff --git a/specification/modules/end_to_end_encryption.rst b/specification/modules/end_to_end_encryption.rst index 4b433f17..0223d9f5 100644 --- a/specification/modules/end_to_end_encryption.rst +++ b/specification/modules/end_to_end_encryption.rst @@ -565,9 +565,9 @@ The process between Alice and Bob verifying each other would be: they match or not. #. Assuming they match, Alice and Bob's devices calculate the HMAC of their own device keys and a comma-separated sorted list of of the key IDs that they wish the other user - to verify, using SHA-256 as the hash function. HMAC is defined in [RFC 2104](https://tools.ietf.org/html/rfc2104). + to verify, using SHA-256 as the hash function. HMAC is defined in `RFC 2104 `_. The key for the HMAC is different for each item and is calculated by generating - 32 bytes (256 bits) using `the key verification HKDF <#SAS-HKDF>`_. + 32 bytes (256 bits) using `the key verification HKDF <#sas-hkdf>`_. #. Alice's device sends Bob's device a ``m.key.verification.mac`` message containing the MAC of Alice's device keys and the MAC of her key IDs to be verified. Bob's device does the same for Bob's device keys and key IDs concurrently with Alice. @@ -653,14 +653,14 @@ are used in addition to those already specified: {{m_key_verification_mac_event}} -.. _`SAS-HKDF`: +.. _sas-hkdf: HKDF calculation <<<<<<<<<<<<<<<< -In all of the SAS methods, HKDF is as defined in [RFC 5869](https://tools.ietf.org/html/rfc5869) +In all of the SAS methods, HKDF is as defined in `RFC 5869 `_ and uses the previously agreed-upon hash function for the hash function. The shared -secret is supplied as the input keying material. No salt is used, and the input +secret is supplied as the input keying material. No salt is used, and the info parameter is the concatenation of: * The string ``MATRIX_KEY_VERIFICATION_SAS``. @@ -677,7 +677,7 @@ parameter is the concatenation of: For verification of each party's device keys, HKDF is as defined in RFC 5869 and uses SHA-256 as the hash function. The shared secret is supplied as the input keying -material. No salt is used, and in the input parameter is the concatenation of: +material. No salt is used, and in the info parameter is the concatenation of: * The string ``MATRIX_KEY_VERIFICATION_MAC``. * The Matrix ID of the user whose key is being MAC-ed. @@ -691,7 +691,7 @@ material. No salt is used, and in the input parameter is the concatenation of: SAS method: ``decimal`` <<<<<<<<<<<<<<<<<<<<<<< -Generate 5 bytes using `HKDF <#SAS-HKDF>`_ then take sequences of 13 bits to +Generate 5 bytes using `HKDF <#sas-hkdf>`_ then take sequences of 13 bits to convert to decimal numbers (resulting in 3 numbers between 0 and 8191 inclusive each). Add 1000 to each calculated number. @@ -708,7 +708,7 @@ such as dashes, or with the numbers on individual lines. SAS method: ``emoji`` <<<<<<<<<<<<<<<<<<<<< -Generate 6 bytes using `HKDF <#SAS-HKDF>`_ then split the first 42 bits into +Generate 6 bytes using `HKDF <#sas-hkdf>`_ then split the first 42 bits into 7 groups of 6 bits, similar to how one would base64 encode something. Convert each group of 6 bits to a number and use the following table to get the corresponding emoji: From 6e339542562dfc00c77b2abc91d18b0476f48854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 5 May 2020 12:11:51 +0200 Subject: [PATCH 379/390] client-server: Mark the event_id when putting room events as required. --- api/client-server/room_send.yaml | 2 ++ api/client-server/room_state.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/api/client-server/room_send.yaml b/api/client-server/room_send.yaml index 6963f76c..fc8f3339 100644 --- a/api/client-server/room_send.yaml +++ b/api/client-server/room_send.yaml @@ -85,5 +85,7 @@ paths: type: string description: |- A unique identifier for the event. + required: + - event_id tags: - Room participation diff --git a/api/client-server/room_state.yaml b/api/client-server/room_state.yaml index 62168f26..640e2009 100644 --- a/api/client-server/room_state.yaml +++ b/api/client-server/room_state.yaml @@ -92,6 +92,8 @@ paths: type: string description: |- A unique identifier for the event. + required: + - event_id 403: description: |- The sender doesn't have permission to send the event into the room. From 3054ac572fb518499dd53eb21334788d9d3def68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 5 May 2020 12:17:54 +0200 Subject: [PATCH 380/390] changelogs: Fragment for the event id when putting room events clarification. --- changelogs/client_server/newsfragments/2525.clarification | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelogs/client_server/newsfragments/2525.clarification diff --git a/changelogs/client_server/newsfragments/2525.clarification b/changelogs/client_server/newsfragments/2525.clarification new file mode 100644 index 00000000..e93444bf --- /dev/null +++ b/changelogs/client_server/newsfragments/2525.clarification @@ -0,0 +1,2 @@ +Clarification that the event ids are required in the response when putting room +events. From b0e8fd648df7fd9119a0e472710be3e46d3c6483 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 5 May 2020 09:21:58 -0600 Subject: [PATCH 381/390] Apply suggestions from code review --- changelogs/client_server/newsfragments/2525.clarification | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/changelogs/client_server/newsfragments/2525.clarification b/changelogs/client_server/newsfragments/2525.clarification index e93444bf..df1fc538 100644 --- a/changelogs/client_server/newsfragments/2525.clarification +++ b/changelogs/client_server/newsfragments/2525.clarification @@ -1,2 +1 @@ -Clarification that the event ids are required in the response when putting room -events. +Clarify that an ``event_id`` is returned when sending events. From 9980b83dd4bd120a055e8544240121b38cbb628f Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 5 May 2020 15:14:24 -0400 Subject: [PATCH 382/390] add changelog --- changelogs/client_server/newsfragments/2524.clarification | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/2524.clarification diff --git a/changelogs/client_server/newsfragments/2524.clarification b/changelogs/client_server/newsfragments/2524.clarification new file mode 100644 index 00000000..cf36c3ba --- /dev/null +++ b/changelogs/client_server/newsfragments/2524.clarification @@ -0,0 +1 @@ +Fix various wording and markup issues in the end-to-end encryption section. From 6ede023b35e735f262886d8f02dc3523527443e9 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 5 May 2020 16:06:21 -0400 Subject: [PATCH 383/390] make the changelog match others so they get combined --- changelogs/client_server/newsfragments/2524.clarification | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/client_server/newsfragments/2524.clarification b/changelogs/client_server/newsfragments/2524.clarification index cf36c3ba..902a9c3f 100644 --- a/changelogs/client_server/newsfragments/2524.clarification +++ b/changelogs/client_server/newsfragments/2524.clarification @@ -1 +1 @@ -Fix various wording and markup issues in the end-to-end encryption section. +Fix various spelling errors throughout the specification. From 6b8b31ce638d82fed94957bb1b6c33bc71fd92cb Mon Sep 17 00:00:00 2001 From: Gnuxie <50846879+Gnuxie@users.noreply.github.com> Date: Wed, 6 May 2020 17:10:39 +0100 Subject: [PATCH 384/390] correct use of required annotation in json-schema https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.3 --- api/server-server/definitions/keys.yaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/api/server-server/definitions/keys.yaml b/api/server-server/definitions/keys.yaml index 8bc6c563..1e025a52 100644 --- a/api/server-server/definitions/keys.yaml +++ b/api/server-server/definitions/keys.yaml @@ -20,7 +20,6 @@ properties: server_name: type: string description: DNS name of the homeserver. - required: true example: "example.org" verify_keys: type: object @@ -31,7 +30,6 @@ properties: algorithm and ``abc123`` being the version in the example below). Together, this forms the Key ID. The version must have characters matching the regular expression ``[a-zA-Z0-9_]``. - required: true additionalProperties: type: object title: Verify Key @@ -44,8 +42,8 @@ properties: key: type: string description: The `Unpadded Base64`_ encoded key. - required: true example: "VGhpcyBzaG91bGQgYmUgYSByZWFsIGVkMjU1MTkgcGF5bG9hZA" + required: ["key"] old_verify_keys: type: object description: |- @@ -69,13 +67,12 @@ properties: type: integer format: int64 description: POSIX timestamp in milliseconds for when this key expired. - required: true example: 1532645052628 key: type: string description: The `Unpadded Base64`_ encoded key. - required: true example: "VGhpcyBzaG91bGQgYmUgeW91ciBvbGQga2V5J3MgZWQyNTUxOSBwYXlsb2FkLg" + required: ["expired_ts", "key"] signatures: type: object description: Digital signatures for this object signed using the ``verify_keys``. @@ -103,3 +100,4 @@ properties: publishes a key which is valid for a significant amount of time without a way for the homeserver owner to revoke it. example: 1052262000000 +required: ["server_name", "verify_keys"] From 6613cd89a668f400a82a8a9f36b961768d77090f Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Thu, 7 May 2020 15:42:04 +0100 Subject: [PATCH 385/390] 2454-ui-interactive-auth-for-sso.md: markup fix --- proposals/2454-ui-interactive-auth-for-sso.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2454-ui-interactive-auth-for-sso.md b/proposals/2454-ui-interactive-auth-for-sso.md index e39facfb..18112c5f 100644 --- a/proposals/2454-ui-interactive-auth-for-sso.md +++ b/proposals/2454-ui-interactive-auth-for-sso.md @@ -209,7 +209,7 @@ provider. It's common for SSO providers to redirect straight back to the app if you've recently authenticated with them; even in the best case, the SSO provider shows an innocent message along the lines of "Confirm that you want to sign in to -". +\". After redirecting back to the homeserver, the SSO is completed and the attacker's session is validated. They are now able to make their malicious From 9065d1aa143e06a42fd36e59d923d0756ae6225d Mon Sep 17 00:00:00 2001 From: Ben Parsons Date: Thu, 7 May 2020 15:45:33 +0100 Subject: [PATCH 386/390] proposal to use existing events as captions for images --- proposals/2529-text-messages-as-captions.md | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 proposals/2529-text-messages-as-captions.md diff --git a/proposals/2529-text-messages-as-captions.md b/proposals/2529-text-messages-as-captions.md new file mode 100644 index 00000000..df76e325 --- /dev/null +++ b/proposals/2529-text-messages-as-captions.md @@ -0,0 +1,37 @@ +# Use existing m.room.message/m.text events as captions for images + +## Background + +There is a demand to be able to apply a text caption to an image, as is possible in other chat platforms. In Matrix this is not possible, so people will generally send two events: one `m.image`, then a `m.text` event immediately afterward to simulate a caption. + +Better would be to able to explicitly mark an event as a caption. + +## Proposal + +Allow an optional `m.relates_to` field in the `content` field of a text message event. + +Example: + +``` +... + "content": { + "body": "Caption text", + "msgtype": "m.text", + "m.relates_to": { + "event_id": "$(some image event)", + "rel_type": "m.caption" + } + }, +``` + +If a client recognises the `rel_type`, they can render the caption with the image rather than as a separate message in the timeline. + +The benefit of this is that if a client doesn't support or recognise the `m.caption`, it can ignore the relation and just render the message inline. + +This would not require aggregation from the server since there will always be a need to send the event separately anyway. + +## Potential issues + +* Not sure how this relates to the broader questions discussed in MSC1849 +* This is catering to a narrow use-case requirement. There may be a more general solution available +* Would MSC1767 (extensible events) obsolete this? From c704da1449eb18127a4fa4b8028f6dbfb3e1fd9d Mon Sep 17 00:00:00 2001 From: Ben Parsons Date: Thu, 7 May 2020 15:49:18 +0100 Subject: [PATCH 387/390] remove proposal --- proposals/2529-text-messages-as-captions.md | 37 --------------------- 1 file changed, 37 deletions(-) delete mode 100644 proposals/2529-text-messages-as-captions.md diff --git a/proposals/2529-text-messages-as-captions.md b/proposals/2529-text-messages-as-captions.md deleted file mode 100644 index df76e325..00000000 --- a/proposals/2529-text-messages-as-captions.md +++ /dev/null @@ -1,37 +0,0 @@ -# Use existing m.room.message/m.text events as captions for images - -## Background - -There is a demand to be able to apply a text caption to an image, as is possible in other chat platforms. In Matrix this is not possible, so people will generally send two events: one `m.image`, then a `m.text` event immediately afterward to simulate a caption. - -Better would be to able to explicitly mark an event as a caption. - -## Proposal - -Allow an optional `m.relates_to` field in the `content` field of a text message event. - -Example: - -``` -... - "content": { - "body": "Caption text", - "msgtype": "m.text", - "m.relates_to": { - "event_id": "$(some image event)", - "rel_type": "m.caption" - } - }, -``` - -If a client recognises the `rel_type`, they can render the caption with the image rather than as a separate message in the timeline. - -The benefit of this is that if a client doesn't support or recognise the `m.caption`, it can ignore the relation and just render the message inline. - -This would not require aggregation from the server since there will always be a need to send the event separately anyway. - -## Potential issues - -* Not sure how this relates to the broader questions discussed in MSC1849 -* This is catering to a narrow use-case requirement. There may be a more general solution available -* Would MSC1767 (extensible events) obsolete this? From 78f8d1322f220018d51281885ca237a724831366 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Fri, 8 May 2020 11:27:11 -0400 Subject: [PATCH 388/390] Add MSC2454 to the specification. --- .../client_server/newsfragments/2532.feature | 1 + specification/client_server_api.rst | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 changelogs/client_server/newsfragments/2532.feature diff --git a/changelogs/client_server/newsfragments/2532.feature b/changelogs/client_server/newsfragments/2532.feature new file mode 100644 index 00000000..cf74a289 --- /dev/null +++ b/changelogs/client_server/newsfragments/2532.feature @@ -0,0 +1 @@ +Add User-Interactive Authentication for SSO-backed homeserver per `MSC2454 `_. diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst index eb32d3b4..581f0c28 100644 --- a/specification/client_server_api.rst +++ b/specification/client_server_api.rst @@ -643,6 +643,7 @@ This specification defines the following auth types: - ``m.login.password`` - ``m.login.recaptcha`` - ``m.login.oauth2`` + - ``m.login.sso`` - ``m.login.email.identity`` - ``m.login.msisdn`` - ``m.login.token`` @@ -782,6 +783,38 @@ the auth code. Homeservers can choose any path for the ``redirect URI``. Once the OAuth flow has completed, the client retries the request with the session only, as above. +Single Sign-On +<<<<<<<<<<<<<< +:Type: + ``m.login.sso`` +:Description: + Authentication is supported by authorising with an external single sign-on + provider. + +A client wanting to complete authentication using SSO should use the +`Fallback`_ authentication flow by opening a browser window for +``/_matrix/client/r0/auth/m.login.sso/fallback/web?session=<...>`` with the +session parameter set to the session ID provied by the server. + +The homeserver should return a page which asks for the user's confirmation +before proceeding. For example, the page could say words to the effect of: + + A client is trying to remove a device/add an email address/take over your + account. To confirm this action, re-authenticate with single sign-on. If you + did not expect this, your account may be compromised! + +Once the user has confirmed they should be redirected to the single sign-on +provider's login page. Once the provider has validated the user, the browser is +redirected back to the homeserver. + +The homeserver then validates the response from the single sign-on provider and +updates the user-interactive authentication session to mark the single sign-on +stage has been completed. The browser is shown the fallback authentication +completion page. + +Once the flow has completed, the client retries the request with the session +only, as above. + Email-based (identity / homeserver) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< :Type: From e85f6c31a3f6d2f93de402347edae4500d95b383 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Mon, 11 May 2020 15:14:11 -0400 Subject: [PATCH 389/390] Fix a typo found in review. Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> --- specification/client_server_api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst index 581f0c28..fce879a2 100644 --- a/specification/client_server_api.rst +++ b/specification/client_server_api.rst @@ -794,7 +794,7 @@ Single Sign-On A client wanting to complete authentication using SSO should use the `Fallback`_ authentication flow by opening a browser window for ``/_matrix/client/r0/auth/m.login.sso/fallback/web?session=<...>`` with the -session parameter set to the session ID provied by the server. +session parameter set to the session ID provided by the server. The homeserver should return a page which asks for the user's confirmation before proceeding. For example, the page could say words to the effect of: From 3556b8457f5bde619c6e81c9b996c43fc86fb78d Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 11 May 2020 21:05:26 -0600 Subject: [PATCH 390/390] Add changelog for 2527 --- changelogs/server_server/newsfragments/2527.clarification | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/server_server/newsfragments/2527.clarification diff --git a/changelogs/server_server/newsfragments/2527.clarification b/changelogs/server_server/newsfragments/2527.clarification new file mode 100644 index 00000000..329d5da2 --- /dev/null +++ b/changelogs/server_server/newsfragments/2527.clarification @@ -0,0 +1 @@ +Clarify which fields are required on the key server endpoints.