mirror of
https://github.com/matrix-org/matrix-spec
synced 2026-03-07 20:24:08 +01:00
add sharing secrets, and a bunch of cleanups
This commit is contained in:
parent
979827bad3
commit
da5ce919f2
|
|
@ -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
|
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
|
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
|
(MSC-1756) can store the signing keys. This proposal presents a standardized
|
||||||
way of storing such data.
|
way of storing such data.
|
||||||
|
|
||||||
## Proposal
|
## Proposal
|
||||||
|
|
||||||
A user can have multiple keys used for encrypting data. This allows the user
|
Secrets are data that clients need to use and thate are sent through or stored
|
||||||
to selectively decrypt data. For example, the user could have one key that can
|
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
|
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 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`
|
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 indicates the encryption algorithm used, as well as a `name`
|
||||||
property, which is a human-readable name. Other properties depend on the
|
property, which is a human-readable name. The contents will be signed as
|
||||||
encryption algorithm, and are described below.
|
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
|
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,
|
`account_data` is defined by the feature that uses the data. For example,
|
||||||
decryption keys for key backups could be stored under the type
|
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
|
`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:
|
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 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?
|
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:
|
The data is encrypted and MACed as follows:
|
||||||
|
|
||||||
1. Generate an ephemeral curve25519 key, and perform an ECDH with the ephemeral
|
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
|
key and the public key to generate a shared secret. The public half of the
|
||||||
half of the ephemeral key, encoded using base64, becomes the `ephemeral`
|
ephemeral key, encoded using base64, becomes the `ephemeral` property.
|
||||||
property.
|
|
||||||
2. Using the shared secret, generate 80 bytes by performing an HKDF using
|
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
|
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
|
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
|
(The key HKDF, AES, and HMAC steps are the same as what are used for encryption
|
||||||
in olm and megolm.)
|
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):
|
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
|
* 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
|
When decoding a raw key, the process should be reversed, with the exception
|
||||||
that whitespace is insignificant in the user's ASCII input.
|
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
|
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
|
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:
|
ID]` account-data:
|
||||||
|
|
||||||
```json
|
```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`
|
The key is generated using PBKDF2 using the salt given in the `salt`
|
||||||
parameter, and the number of rounds given in the `rounds` parameter.
|
parameter, and the number of rounds given in the `rounds` parameter.
|
||||||
|
|
||||||
## Tradeoffs
|
### Sharing
|
||||||
|
|
||||||
Rather than encrypting data on the server using a static key, clients can
|
Rather than (or in addition to) storing secrets on the server encrypted by a
|
||||||
exchange data by sending to_device messages encrypted using Olm. This allows
|
shared key, devices can send secrets to each other, encrypted using olm.
|
||||||
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
|
To request a secret, a client sends a `m.secret.request` event with `action`
|
||||||
need a way to, for example, recover their key backup, so we must provide a way
|
set to `request` to other devices, and `name` set to the name of the secret
|
||||||
for the data to be stored on the server.
|
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`.
|
||||||
|
|
||||||
|
#### 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
|
Currently, only a public/private key mechanism is defined. It may be useful to
|
||||||
also define a secret key mechanism.
|
also define a secret key mechanism.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue