diff --git a/api/client-server/definitions/key_backup_data.yaml b/api/client-server/definitions/key_backup_data.yaml new file mode 100644 index 00000000..6a3b4042 --- /dev/null +++ b/api/client-server/definitions/key_backup_data.yaml @@ -0,0 +1,50 @@ +# 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 +title: KeyBackupData +description: "The key data" +properties: + first_message_index: + description: |- + The index of the first message in the session that the key can decrypt. + type: integer + example: 1 + forwarded_count: + description: |- + The number of times this key has been forwarded via key-sharing between devices. + type: integer + example: 0 + is_verified: + description: |- + Whether the device backing up the key verified the device that the key + is from. + type: boolean + example: false + session_data: + description: |- + Algorithm-dependent data. See the documentation for the backup + algorithms in `Server-side key backups`_ for more information on the + expected format of the data. + type: object + example: { + "ephemeral": "base64+ephemeral+key", + "ciphertext": "base64+ciphertext+of+JSON+data", + "mac": "base64+mac+of+ciphertext" + } +required: + - first_message_index + - forwarded_count + - is_verified + - session_data diff --git a/api/client-server/key_backup.yaml b/api/client-server/key_backup.yaml new file mode 100644 index 00000000..34dce118 --- /dev/null +++ b/api/client-server/key_backup.yaml @@ -0,0 +1,940 @@ +# Copyright 2019-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. +swagger: '2.0' +info: + title: "Matrix Client-Server Key Backup API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https + - http +basePath: /_matrix/client/%CLIENT_MAJOR_VERSION% +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/room_keys/version": + post: + summary: Create a new backup. + description: |- + Creates a new backup. + operationId: postRoomKeysVersion + security: + - accessToken: [] + parameters: + - in: body + name: version + description: "The backup configuration." + schema: + type: object + properties: + algorithm: + description: The algorithm used for storing backups. + type: string + enum: ["m.megolm_backup.v1.curve25519-aes-sha2"] + example: "m.megolm_backup.v1.curve25519-aes-sha2" + auth_data: + description: |- + Algorithm-dependent data. See the documentation for the backup + algorithms in `Server-side key backups`_ for more information on the + expected format of the data. + type: object + example: { + "public_key": "abcdefg", + "signatures": { + "@alice:example.org": { + "ed25519:deviceid": "signature" + } + } + } + required: + - algorithm + - auth_data + responses: + 200: + description: + The version id of the new backup. + schema: + type: object + properties: + version: + type: string + description: The backup version. This is an opaque string. + example: "1" + required: + - version + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + tags: + - End-to-end encryption + get: + summary: Get information about the latest backup version. + description: |- + Get information about the latest backup version. + operationId: getRoomKeysVersionCurrent + security: + - accessToken: [] + responses: + 200: + description: + The information about the backup. + schema: + type: object + properties: + algorithm: + type: string + description: The algorithm used for storing backups. + enum: ["m.megolm_backup.v1.curve25519-aes-sha2"] + example: "m.megolm_backup.v1.curve25519-aes-sha2" + auth_data: + description: |- + Algorithm-dependent data. See the documentation for the backup + algorithms in `Server-side key backups`_ for more information on the + expected format of the data. + type: object + example: { + "public_key": "abcdefg", + "signatures": { + "@alice:example.org": { + "ed25519:deviceid": "signature" + } + } + } + count: + description: The number of keys stored in the backup. + type: integer + example: 42 + etag: + description: |- + An opaque string representing stored keys in the backup. + Clients can compare it with the ``etag`` value they received + in the request of their last key storage request. If not + equal, another client has modified the backup. + type: string + example: "anopaquestring" + version: + type: string + description: The backup version + example: "1" + required: + - algorithm + - auth_data + - count + - etag + - version + 404: + description: + No backup exists. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "No current backup version" + } + schema: + "$ref": "definitions/errors/error.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + tags: + - End-to-end encryption + "/room_keys/version/{version}": + get: + summary: Get information about an existing backup. + description: |- + Get information about an existing backup. + operationId: getRoomKeysVersion + security: + - accessToken: [] + parameters: + - in: path + type: string + name: version + description: |- + The backup version to get, as returned in the ``version`` parameter + of the response in `POST /_matrix/client/r0/room_keys/version`_ or + this endpoint. + required: true + x-example: "1" + responses: + 200: + description: + The information about the requested backup. + schema: + type: object + properties: + algorithm: + type: string + description: The algorithm used for storing backups. + enum: ["m.megolm_backup.v1.curve25519-aes-sha2"] + example: "m.megolm_backup.v1.curve25519-aes-sha2" + auth_data: + description: |- + Algorithm-dependent data. See the documentation for the backup + algorithms in `Server-side key backups`_ for more information on the + expected format of the data. + type: object + example: { + "public_key": "abcdefg", + "signatures": { + "@alice:example.org": { + "ed25519:deviceid": "signature" + } + } + } + count: + description: The number of keys stored in the backup. + type: integer + example: 42 + etag: + description: |- + An opaque string representing stored keys in the backup. + Clients can compare it with the ``etag`` value they received + in the request of their last key storage request. If not + equal, another client has modified the backup. + type: string + example: "anopaquestring" + version: + type: string + description: The backup version + example: "1" + required: + - algorithm + - auth_data + - count + - etag + - version + 404: + description: + The backup specified does not exist. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Unknown backup version" + } + schema: + "$ref": "definitions/errors/error.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + tags: + - End-to-end encryption + put: + summary: Update information about an existing backup. + description: |- + Update information about an existing backup. Only ``auth_data`` can be modified. + operationId: putRoomKeysVersion + security: + - accessToken: [] + parameters: + - in: path + type: string + name: version + description: |- + The backup version to update, as returned in the ``version`` + parameter in the response of `POST + /_matrix/client/r0/room_keys/version`_ or `GET + /_matrix/client/r0/room_keys/version/{version}`_. + required: true + x-example: "1" + - in: body + name: version + description: "The backup configuration" + schema: + type: object + properties: + algorithm: + description: |- + The algorithm used for storing backups. Must be the same as + the algorithm currently used by the backup. + type: string + enum: ["m.megolm_backup.v1.curve25519-aes-sha2"] + example: "m.megolm_backup.v1.curve25519-aes-sha2" + auth_data: + description: |- + Algorithm-dependent data. See the documentation for the backup + algorithms in `Server-side key backups`_ for more information on the + expected format of the data. + type: object + example: { + "public_key": "abcdefg", + "signatures": { + "@alice:example.org": { + "ed25519:deviceid": "signature" + } + } + } + version: + description: |- + The backup version. If present, must be the same as the + version in the path parameter. + type: string + example: "1" + required: + - algorithm + - auth_data + responses: + 200: + description: The update succeeded. + schema: + type: object + properties: {} + 400: + description: |- + A parameter was incorrect. For example, the ``algorithm`` does not + match the current backup algorithm, or the ``version`` in the body + does not match the ``version`` in the path. + examples: + application/json: { + "errcode": "M_INVALID_PARAM", + "error": "Algorithm does not match" + } + schema: + "$ref": "definitions/errors/error.yaml" + 404: + description: The backup specified does not exist. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Unknown backup version" + } + schema: + "$ref": "definitions/errors/error.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + tags: + - End-to-end encryption + delete: + summary: Delete an existing key backup. + description: |- + Delete an existing key backup. Both the information about the backup, + as well as all key data related to the backup will be deleted. + operationId: deleteRoomKeysVersion + security: + - accessToken: [] + parameters: + - in: path + type: string + name: version + description: |- + The backup version to delete, as returned in the ``version`` + parameter in the response of `POST + /_matrix/client/r0/room_keys/version`_ or `GET + /_matrix/client/r0/room_keys/version/{version}`_. + required: true + x-example: "1" + responses: + 200: + description: |- + The delete succeeded, or the specified backup was previously + deleted. + schema: + type: object + properties: {} + 404: + description: |- + The backup specified does not exist. If the backup was previously + deleted, the call should succeed rather than returning an error. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Unknown backup version" + } + schema: + "$ref": "definitions/errors/error.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + tags: + - End-to-end encryption + "/room_keys/keys/{roomId}/{sessionId}": + put: + summary: Store a key in the backup. + description: |- + Store a key in the backup. + operationId: postRoomKeysKeyRoomIdSessionId + security: + - accessToken: [] + parameters: + - in: query + type: string + name: version + description: |- + The backup in which to store the key. Must be the current backup. + required: true + x-example: "1" + - in: path + type: string + name: roomId + description: The ID of the room that the key is for. + required: true + x-example: "!roomid:example.org" + - in: path + type: string + name: sessionId + description: The ID of the megolm session that the key is for. + required: true + x-example: "sessionid" + - in: body + name: data + description: "The key data." + schema: + "$ref": "definitions/key_backup_data.yaml" + responses: + 200: + description: The update succeeded. + schema: + type: object + properties: + etag: + description: |- + The new etag value representing stored keys in the backup. + See ``GET /room_keys/version/{version}`` for more details. + type: string + example: "abcdefg" + count: + description: The number of keys stored in the backup + type: integer + example: 10 + required: + - etag + - count + 403: + description: |- + The version specified does not match the current backup version. + The current version will be included in the ``current_version`` + field. + examples: + application/json: { + "errcode": "M_WRONG_ROOM_KEYS_VERSION", + "error": "Wrong backup version.", + "current_version": "42" + } + schema: + "$ref": "definitions/errors/error.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + tags: + - End-to-end encryption + get: + summary: Retrieve a key from the backup + description: |- + Retrieve a key from the backup. + operationId: getRoomKeysKeyRoomIdSessionId + security: + - accessToken: [] + parameters: + - in: query + type: string + name: version + description: |- + The backup from which to retrieve the key + required: true + x-example: "1" + - in: path + type: string + name: roomId + description: The ID of the room that the requested key is for. + required: true + x-example: "!roomid:example.org" + - in: path + type: string + name: sessionId + description: The ID of the megolm session whose key is requested. + required: true + x-example: "sessionid" + responses: + 200: + description: The key data + schema: + "$ref": "definitions/key_backup_data.yaml" + 404: + description: The key or backup was not found. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Key not found." + } + schema: + "$ref": "definitions/errors/error.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + delete: + summary: Delete a key from the backup + description: |- + Delete a key from the backup. + operationId: deleteRoomKeysKeyRoomIdSessionId + security: + - accessToken: [] + parameters: + - in: query + type: string + name: version + description: |- + The backup from which to delete the key + required: true + x-example: "1" + - in: path + type: string + name: roomId + description: The ID of the room that the specified key is for. + required: true + x-example: "!roomid:example.org" + - in: path + type: string + name: sessionId + description: The ID of the megolm session whose key is to be deleted. + required: true + x-example: "sessionid" + responses: + 200: + description: The update succeeded + schema: + type: object + properties: + etag: + description: |- + The new etag value representing stored keys in the backup. + See ``GET /room_keys/version/{version}`` for more details. + type: string + example: "abcdefg" + count: + description: The number of keys stored in the backup + type: integer + example: 10 + required: + - etag + - count + 404: + description: |- + The backup was not found. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Unknown backup version" + } + schema: + "$ref": "definitions/errors/error.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + "/room_keys/keys/{roomId}": + put: + summary: Store several keys in the backup for a given room. + description: |- + Store a key in the backup. + operationId: postRoomKeysKeyRoomId + security: + - accessToken: [] + parameters: + - in: query + type: string + name: version + description: |- + The backup in which to store the keys. Must be the current backup. + required: true + x-example: "1" + - in: path + type: string + name: roomId + description: The ID of the room that the keys are for. + required: true + x-example: "!roomid:example.org" + - in: body + description: "The backup data" + name: backupData + schema: + type: object + properties: + sessions: + type: object + description: |- + A map of session IDs to key data. + additionalProperties: + allOf: + - $ref: "definitions/key_backup_data.yaml" + example: { + "sessionid1": { + "ephemeral": "base64+ephemeral+key", + "ciphertext": "base64+ciphertext+of+JSON+data", + "mac": "base64+mac+of+ciphertext" + } + } + responses: + 200: + description: The update succeeded + schema: + type: object + properties: + etag: + description: |- + The new etag value representing stored keys in the backup. + See ``GET /room_keys/version/{version}`` for more details. + type: string + example: "abcdefg" + count: + description: The number of keys stored in the backup + type: integer + example: 10 + required: + - etag + - count + 403: + description: |- + The version specified does not match the current backup version. + The current version will be included in the ``current_version`` + field. + examples: + application/json: { + "errcode": "M_WRONG_ROOM_KEYS_VERSION", + "error": "Wrong backup version.", + "current_version": "42" + } + schema: + "$ref": "definitions/errors/error.yaml" + 404: + description: |- + The backup was not found. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Unknown backup version" + } + schema: + "$ref": "definitions/errors/error.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + tags: + - End-to-end encryption + get: + summary: Retrieve the keys from the backup for a given room + description: |- + Retrieve the keys from the backup for a given room + operationId: getRoomKeysKeyRoomId + security: + - accessToken: [] + parameters: + - in: query + type: string + name: version + description: |- + The backup from which to retrieve the key + required: true + x-example: "1" + - in: path + type: string + name: roomId + description: The ID of the room that the requested key is for. + required: true + x-example: "!roomid:example.org" + responses: + 200: + description: |- + The key data. If no keys are found, then an object with an empty + ``sessions`` property will be returned (``{"sessions": {}}``). + schema: + type: object + properties: + sessions: + type: object + description: |- + A map of session IDs to key data. + additionalProperties: + allOf: + - $ref: "definitions/key_backup_data.yaml" + example: { + "sessionid1": { + "ephemeral": "base64+ephemeral+key", + "ciphertext": "base64+ciphertext+of+JSON+data", + "mac": "base64+mac+of+ciphertext" + } + } + 404: + description: |- + The backup was not found. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Unknown backup version" + } + schema: + "$ref": "definitions/errors/error.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + delete: + summary: Delete a key from the backup + description: |- + Delete a key from the backup. + operationId: deleteRoomKeysKeyRoomId + security: + - accessToken: [] + parameters: + - in: query + type: string + name: version + description: |- + The backup from which to delete the key + required: true + x-example: "1" + - in: path + type: string + name: roomId + description: The ID of the room that the specified key is for. + required: true + x-example: "!roomid:example.org" + responses: + 200: + description: The update succeeded + schema: + type: object + properties: + etag: + description: |- + The new etag value representing stored keys in the backup. + See ``GET /room_keys/version/{version}`` for more details. + type: string + example: "abcdefg" + count: + description: The number of keys stored in the backup + type: integer + example: 10 + required: + - etag + - count + 404: + description: |- + The backup was not found. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Unknown backup version" + } + schema: + "$ref": "definitions/errors/error.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + "/room_keys/keys": + put: + summary: Store several keys in the backup. + description: |- + Store several keys in the backup. + operationId: postRoomKeysKey + security: + - accessToken: [] + parameters: + - in: query + type: string + name: version + description: |- + The backup in which to store the keys. Must be the current backup. + required: true + x-example: "1" + - in: body + description: "The backup data" + name: backupData + schema: + type: object + properties: + rooms: + type: object + description: |- + A map of room IDs to session IDs to key data. + additionalProperties: + type: object + additionalProperties: + allOf: + - $ref: "definitions/key_backup_data.yaml" + example: { + "!room:example.org": { + "sessions": { + "sessionid1": { + "ephemeral": "base64+ephemeral+key", + "ciphertext": "base64+ciphertext+of+JSON+data", + "mac": "base64+mac+of+ciphertext" + } + } + } + } + responses: + 200: + description: The update succeeded + schema: + type: object + properties: + etag: + description: |- + The new etag value representing stored keys in the backup. + See ``GET /room_keys/version/{version}`` for more details. + type: string + example: "abcdefg" + count: + description: The number of keys stored in the backup + type: integer + example: 10 + required: + - etag + - count + 403: + description: |- + The version specified does not match the current backup version. + The current version will be included in the ``current_version`` + field. + examples: + application/json: { + "errcode": "M_WRONG_ROOM_KEYS_VERSION", + "error": "Wrong backup version.", + "current_version": "42" + } + schema: + "$ref": "definitions/errors/error.yaml" + 404: + description: |- + The backup was not found. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Unknown backup version" + } + schema: + "$ref": "definitions/errors/error.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + tags: + - End-to-end encryption + get: + summary: Retrieve the keys from the backup for a given room + description: |- + Retrieve the keys from the backup for a given room + operationId: getRoomKeysKeyRoomId + security: + - accessToken: [] + parameters: + - in: query + type: string + name: version + description: |- + The backup from which to retrieve the keys. If omitted, the keys are + retrieved from the current backup. + x-example: "1" + responses: + 200: + description: |- + The key data. If no keys are found, then an object with an empty + ``rooms`` property will be returned (``{"rooms": {}}``). + schema: + type: object + properties: + rooms: + type: object + description: |- + A map of room IDs to session IDs to key data. + additionalProperties: + type: object + additionalProperties: + allOf: + - $ref: "definitions/key_backup_data.yaml" + example: { + "!room:example.org": { + "sessions": { + "sessionid1": { + "ephemeral": "base64+ephemeral+key", + "ciphertext": "base64+ciphertext+of+JSON+data", + "mac": "base64+mac+of+ciphertext" + } + } + } + } + 404: + description: The backup was not found. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Unknown backup version." + } + schema: + "$ref": "definitions/errors/error.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + delete: + summary: Delete a key from the backup + description: |- + Delete a key from the backup. + operationId: deleteRoomKeysKeyRoomId + security: + - accessToken: [] + parameters: + - in: query + type: string + name: version + description: |- + The backup from which to delete the key + required: true + x-example: "1" + responses: + 200: + description: The update succeeded + schema: + type: object + properties: + etag: + description: |- + The new etag value representing stored keys in the backup. + See ``GET /room_keys/version/{version}`` for more details. + type: string + example: "abcdefg" + count: + description: The number of keys stored in the backup + type: integer + example: 10 + required: + - etag + - count + 404: + description: |- + The backup was not found. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Unknown backup version" + } + schema: + "$ref": "definitions/errors/error.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" diff --git a/changelogs/client_server/2387.new b/changelogs/client_server/2387.new new file mode 100644 index 00000000..a709a5fa --- /dev/null +++ b/changelogs/client_server/2387.new @@ -0,0 +1 @@ +Add key backup (``/room_keys/*``) endpoints. diff --git a/specification/modules/end_to_end_encryption.rst b/specification/modules/end_to_end_encryption.rst index a357944b..d3b6070d 100644 --- a/specification/modules/end_to_end_encryption.rst +++ b/specification/modules/end_to_end_encryption.rst @@ -781,6 +781,136 @@ previously-received ``request`` message with the same ``request_id`` and A reasonable strategy is for a user's client to only send keys requested by the verified devices of the same user. +Server-side key backups +~~~~~~~~~~~~~~~~~~~~~~~ + +Devices may upload encrypted copies of keys to the server. When a device tries +to read a message that it does not have keys for, it may request the key from +the server and decrypt it. Backups are per-user, and users may replace backups +with new backups. + +In contrast with `Key requests`_, Server-side key backups do not require another +device to be online from which to request keys. However, as the session keys are +stored on the server encrypted, it requires users to enter a decryption key to +decrypt the session keys. + +To create a backup, a client will call `POST +/_matrix/client/r0/room_keys/version`_ and define how the keys are to be +encrypted through the backup's ``auth_data``; other clients can discover the +backup by calling `GET /_matrix/client/r0/room_keys/version`_. Keys are +encrypted according to the backup's ``auth_data`` and added to the backup by +calling `PUT /_matrix/client/r0/room_keys/keys`_ or one of its variants, and +can be retrieved by calling `GET /_matrix/client/r0/room_keys/keys`_ or one of +its variants. Keys can only be written to the most recently created version of +the backup. Backups can also be deleted using `DELETE +/_matrix/client/r0/room_keys/version/{version}`_, or individual keys can be +deleted using `DELETE /_matrix/client/r0/room_keys/keys`_ or one of its +variants. + +Clients must only store keys in backups after they have ensured that the +``auth_data`` is trusted, either by checking the signatures on it, or by +deriving the public key from a private key that it obtained from a trusted +source. + +When a client uploads a key for a session that the server already has a key +for, the server will choose to either keep the existing key or replace it with +the new key based on the key metadata as follows: + +- if the keys have different values for ``is_verified``, then it will keep the + key that has ``is_verified`` set to ``true``; +- if they have the same values for ``is_verified``, then it will keep the key + with a lower ``first_message_index``; +- and finally, is ``is_verified`` and ``first_message_index`` are equal, then + it will keep the key with a lower ``forwarded_count``. + +Recovery key +<<<<<<<<<<<< + +If the recovery key (the private half of the backup encryption key) is +presented to the user to save, it is presented as a string 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 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 + `_, + that is, using the alphabet + ``123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz``. +4. A space should be added after every 4th character. + +When reading in a recovery key, clients must disregard whitespace, and perform +the reverse of steps 1 through 3. + +Backup algorithm: ``m.megolm_backup.v1.curve25519-aes-sha2`` +<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +When a backup is created with the ``algorithm`` set to +``m.megolm_backup.v1.curve25519-aes-sha2``, the ``auth_data`` should have the +following format: + +``AuthData`` + +.. table:: + :widths: auto + + ========== =========== ====================================================== + Parameter Type Description + ========== =========== ====================================================== + public_key string **Required.** The curve25519 public key used to encrypt + the backups, encoded in unpadded base64. + signatures Signatures Optional. Signatures of the ``auth_data``, as Signed + JSON + ========== =========== ====================================================== + +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: + + .. table:: + :widths: auto + + =============================== ======== ========================================= + Parameter Type Description + =============================== ======== ========================================= + algorithm string **Required.** The end-to-end message + encryption algorithm that the key is + for. Must be ``m.megolm.v1.aes-sha2``. + forwarding_curve25519_key_chain [string] **Required.** Chain of Curve25519 keys + through which this session was + forwarded, via + `m.forwarded_room_key`_ events. + sender_key string **Required.** Unpadded base64-encoded + device curve25519 key. + sender_claimed_keys {string: **Required.** A map from algorithm name + string} (``ed25519``) to the identity key + for the sending device. + session_key string **Required.** Unpadded base64-encoded + session key in `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 unpadded 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 unpadded 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``. + +{{key_backup_cs_http_api}} + Key exports ~~~~~~~~~~~ @@ -854,6 +984,9 @@ described as follows: session_key string Required. The key for the session. =============================== =========== ==================================== +This is similar to the format before encryption used for the session keys in +`Server-side key backups`_ but adds the ``room_id`` and ``session_id`` fields. + Example: .. code:: json