diff --git a/proposals/1946-secure_server-side_storage.md b/proposals/1946-secure_server-side_storage.md index 603fe64e0..c44cd57eb 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.