229 lines
No EOL
12 KiB
Markdown
229 lines
No EOL
12 KiB
Markdown
### 机密信息
|
||
|
||
{{% added-in v="1.1" %}}
|
||
|
||
客户端可能拥有希望授权给其他客户端访问但不希望服务器知晓的机密信息,因此这些信息在通过服务器传递时必须进行加密。这可以通过异步方式(将加密后的数据存储在服务器供之后获取)或同步方式(客户端之间互发消息)来实现。
|
||
|
||
每个机密信息都有一个标识符,客户端在存储、获取、请求或共享机密信息时会通过该标识符进行引用。机密信息是普通字符串;如需存储结构化数据,可将其编码为字符串形式。
|
||
|
||
本节所描述的机制被称为“安全机密信息存储与共享”(secure secret storage and sharing)、简称“SSSS”或“4S”。
|
||
|
||
#### 存储
|
||
|
||
当机密信息存储在服务器上时,会以[账户数据](#client-config)的形式存储在用户的账户数据中,事件类型等于机密信息的标识符。用于加密机密信息的密钥,其描述也会存储在用户的账户数据中。用户可以拥有多个密钥,从而根据分配给客户端的密钥控制其可访问哪些机密信息。
|
||
|
||
##### 密钥存储
|
||
|
||
每个密钥都有一个 ID,其描述以事件类型 `m.secret_storage.key.[key ID]` 存储在用户的账户数据中。密钥的账户数据内容包含一个 `algorithm` 属性,表示所用加密算法,以及一个 `name` 属性,为该密钥的可读名称。密钥描述中还可以包含一个 `passphrase` 属性,该属性用于根据用户输入的口令生成密钥,详见[从口令派生密钥](#deriving-keys-from-passphrases)。
|
||
|
||
`KeyDescription`
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|-------------|-----------|--------------------------------------------------------------------------------------------------------------------------------------------|
|
||
| name | string | 可选。密钥名称。如果未提供,客户端可使用“未命名密钥”之类的通用名称;若该密钥被标记为默认密钥(见下文),则可用“默认密钥”。 |
|
||
| algorithm | string | **必需。** 此密钥使用的加密算法。目前仅支持 `m.secret_storage.v1.aes-hmac-sha2`。 |
|
||
| passphrase | string | 详见[从口令派生密钥](#deriving-keys-from-passphrases)一节。 |
|
||
|
||
其他属性依赖于加密算法,详见下文。
|
||
|
||
如需将某个密钥标记为“默认”密钥,应在用户的账户数据中,设置事件类型为 `m.secret_storage.default_key` 的对象,其 `key` 属性为该密钥 ID。默认密钥将用于加密用户期望在所有客户端均可用的所有机密信息。除非用户另行指定,客户端将尝试使用默认密钥解密机密信息。
|
||
|
||
希望为用户提供简化界面的客户端可只支持默认密钥。若未指定默认密钥,客户端可视为不存在任何密钥。当此类客户端创建密钥时,应将其标为默认密钥。
|
||
|
||
`DefaultKey`
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|------|--------|----------------------------|
|
||
| key | string | **必需。** 默认密钥的ID。 |
|
||
|
||
|
||
###### `m.secret_storage.v1.aes-hmac-sha2`
|
||
|
||
为便于客户端检查用户输入密钥的正确性,`m.secret_storage.v1.aes-hmac-sha2` 算法使用的密钥会存储附加数据。
|
||
|
||
存储密钥时,客户端应:
|
||
|
||
1. 以机密信息存储密钥为基础,使用 SHA-256 为哈希值,32字节0为salt,空字符串为info执行 HKDF,生成64字节数据。前32字节为AES密钥,后32字节为MAC密钥。
|
||
|
||
2. 生成16字节随机数,将第63位设为0(为兼容不同 AES-CTR 实现),作为AES初始化向量(IV)。
|
||
|
||
3. 用第1步所得的AES密钥和IV,以 AES-CTR-256 加密32字节的零组成的消息。
|
||
|
||
4. 用第1步所得的MAC密钥,对第3步所得的原始加密数据执行 HMAC-SHA-256。
|
||
|
||
5. 将第2步生成的IV与第4步生成的 MAC,使用[无填充 base64](/appendices/#unpadded-base64) 编码,并分别存储在 `iv` 和 `mac` 属性中,属性位置为 `m.secret_storage.key.[key ID]` 账户数据。(第3步得到的密文仅用于MAC计算后即丢弃。)
|
||
|
||
客户端在检查密钥正确性时可重复此过程:若MAC值匹配,则密钥正确。但需注意,这些属性为**可选**。若不存在此类属性,客户端必须假定密钥有效。
|
||
|
||
还需注意,虽然建议客户端应当如上采用无填充base64进行编码,但部分现有实现使用标准[RFC4648规范base64](https://datatracker.ietf.org/doc/html/rfc4648#section-4)并含有填充,因此客户端必须同时兼容两种编码。
|
||
|
||
因此,面向该算法的 `m.secret_storage.key.[key ID]` 账户数据结构如下:
|
||
|
||
`AesHmacSha2KeyDescription`
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|-----------|--------|---------------------------------------------------------------------------------------------------|
|
||
| name | string | 可选。密钥名称。 |
|
||
| algorithm | string | **必需。** 此密钥使用的加密算法:`m.secret_storage.v1.aes-hmac-sha2`。 |
|
||
| passphrase| object | 详见[从口令派生密钥](#deriving-keys-from-passphrases)一节。 |
|
||
| iv | string | 可选。校验用的16字节初始化向量,base64编码。 |
|
||
| mac | string | 可选。对32字节零加密结果的MAC,base64编码。 |
|
||
|
||
示例:
|
||
|
||
```json
|
||
{
|
||
"name": "m.default",
|
||
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
||
"iv": "random+data",
|
||
"mac": "mac+of+encrypted+zeros"
|
||
}
|
||
```
|
||
|
||
##### 机密信息存储
|
||
|
||
加密后的数据以具体功能定义的事件类型,存储在用户账户数据中。账户数据会有一个 `encrypted` 属性,为从密钥ID到对象的映射。特定密钥的 `m.secret_storage.key.[key ID]` 数据中的算法定义了解释其他属性的方式,但大多数加密方案应包含 `ciphertext` 和 `mac` 两个属性,其中 `ciphertext` 属性为无填充base64编码的密文,`mac` 用于保证数据完整性。
|
||
|
||
`Secret`
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|-----------|--------------------|----------------------------------------------------------------------------------------------------------------|
|
||
| encrypted | {string: object} | **必需。** 密钥ID到加密数据的映射。加密数据的确切格式取决于密钥算法。参见[m.secret_storage.v1.aes-hmac-sha2](#msecret_storagev1aes-hmac-sha2-1) 节中的 `AesHmacSha2EncryptedData` 定义。 |
|
||
|
||
示例:
|
||
|
||
某机密信息使用 ID 为 `key_id_1` 和 `key_id_2` 的密钥加密:
|
||
|
||
`org.example.some.secret`:
|
||
|
||
```
|
||
{
|
||
"encrypted": {
|
||
"key_id_1": {
|
||
"ciphertext": "base64+encoded+encrypted+data",
|
||
"mac": "base64+encoded+mac",
|
||
// ... 其他属性,见 m.secret_storage.key.key_id_1 的 algorithm 属性
|
||
},
|
||
"key_id_2": {
|
||
// ...
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
相应密钥的描述:
|
||
|
||
`m.secret_storage.key.key_id_1`:
|
||
|
||
```
|
||
{
|
||
"name": "Some key",
|
||
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
||
// ... 其他属性,见 algorithm
|
||
}
|
||
```
|
||
|
||
`m.secret_storage.key.key_id_2`:
|
||
|
||
```
|
||
{
|
||
"name": "Some other key",
|
||
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
||
// ... 其他属性,见 algorithm
|
||
}
|
||
```
|
||
|
||
若 `key_id_1` 为默认密钥,则还应有:
|
||
|
||
`m.secret_storage.default_key`:
|
||
|
||
```
|
||
{
|
||
"key": "key_id_1"
|
||
}
|
||
```
|
||
|
||
###### `m.secret_storage.v1.aes-hmac-sha2`
|
||
|
||
采用 `m.secret_storage.v1.aes-hmac-sha2` 算法加密的机密信息,使用 AES-CTR-256 进行加密,并用 HMAC-SHA-256 进行认证,加密过程如下:
|
||
|
||
1. 以机密信息存储密钥为基础,使用 SHA-256 作为哈希,32字节0为salt,以机密名称为info,执行HKDF生成64字节。前32字节为AES密钥,后32字节为MAC密钥。
|
||
|
||
2. 生成16字节随机数,将第63位设为0(为兼容不同AES-CTR实现),用作AES初始化向量(IV)。
|
||
|
||
3. 用第1步获得的AES密钥和IV,采用 AES-CTR-256 加密数据。
|
||
|
||
4. 用第1步获得的MAC密钥,对第3步获得的原始加密数据执行HMAC-SHA-256。
|
||
|
||
5. 将第2步的 IV、第3步的密文和第4步的 MAC 用[无填充 base64](/appendices/#unpadded-base64) 编码,分别存为账户数据对象的 `iv`、`ciphertext` 和 `mac` 属性。
|
||
|
||
**注意**:部分现有实现使用标准[RFC4648规范base64](https://datatracker.ietf.org/doc/html/rfc4648#section-4)带填充进行编码,因此客户端必须两种编码均可接受。
|
||
|
||
采用该算法加密的账户数据对象的 `encrypted` 属性结构如下:
|
||
|
||
`AesHmacSha2EncryptedData`
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|-----------|---------|------------------------------------------------------------------|
|
||
| iv | string | **必需。** 16字节初始化向量,base64编码。 |
|
||
| ciphertext| string | **必需。** AES-CTR加密的数据,base64编码。 |
|
||
| mac | string | **必需。** MAC,base64编码。 |
|
||
|
||
示例,加密后数据形态如下:
|
||
|
||
```json
|
||
{
|
||
"encrypted": {
|
||
"key_id": {
|
||
"iv": "16+bytes+base64",
|
||
"ciphertext": "base64+encoded+encrypted+data",
|
||
"mac": "base64+encoded+mac"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
##### 密钥表示
|
||
|
||
用户获得 `m.secret_storage.v1.aes-hmac-sha2` 的原始密钥时,应以[通用加密密钥表示方法](/appendices/#cryptographic-key-representation)的字符串形式呈现密钥。
|
||
|
||
##### 从口令派生密钥
|
||
|
||
用户可能希望使用自选口令而非随机生成的密钥。在这种情况下,如何从口令生成密钥的信息会保存在 `m.secret_storage.key.[key ID]` 账户数据的 `passphrase` 属性中。`passphrase` 属性包含一个 `algorithm` 属性,表示如何据口令生成密钥。`passphrase` 的其他属性由指定的算法定义。
|
||
|
||
当前只定义了 `m.pbkdf2` 算法。对于 `m.pbkdf2`,`passphrase` 属性包括:
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|-----------|---------|---------------------------------------------|
|
||
| algorithm | string | **必需。** 必须为 `m.pbkdf2` |
|
||
| salt | string | **必需。** PBKDF2 算法使用的 salt。 |
|
||
| iterations| integer | **必需。** PBKDF2 算法使用的迭代次数。 |
|
||
| bits | integer | 可选。生成密钥的位数,默认256。 |
|
||
|
||
密钥将使用 PBKDF2 算法,以 SHA-512 为哈希,使用 `salt` 属性作为盐值,`iterations` 属性指明迭代次数生成。
|
||
|
||
示例:
|
||
|
||
```
|
||
{
|
||
"passphrase": {
|
||
"algorithm": "m.pbkdf2",
|
||
"salt": "MmMsAlty",
|
||
"iterations": 100000,
|
||
"bits": 256
|
||
},
|
||
...
|
||
}
|
||
```
|
||
|
||
#### 共享
|
||
|
||
客户端如需向其他设备请求机密信息,可发送 `m.secret.request` 设备事件,`action` 设为 `request`,`name` 设为机密信息标识符。有意愿分享机密信息的设备将用Olm加密,发送 `m.secret.send` 事件回复。当原始客户端获取到机密信息后,应向所有除获得机密信息那台设备外的其他设备发送 `m.secret.request` 事件,`action` 设置为 `request_cancellation`。客户端应忽略未针对本机发送 `m.secret.request` 事件的设备所收到的 `m.secret.send` 事件。
|
||
|
||
客户端必须确保仅将机密信息共享给被授权查看的其他设备。例如,客户端应仅与自己已验证的设备共享机密信息,必要时还可提示用户确认共享操作。
|
||
|
||
##### 事件定义
|
||
|
||
{{% event event="m.secret.request" %}}
|
||
|
||
{{% event event="m.secret.send" %}} |