draft of secure server-side storage
parent
f1280ef305
commit
63c6d030fd
@ -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.
|
Loading…
Reference in New Issue