RFC-0157:Fxfs 加密和多卷支持 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 介绍 Fxfs 中的加密和多卷支持。 |
问题 | |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2022-04-07 |
审核日期(年-月-日) | 2022-04-28 |
摘要
此 RFC 介绍了 Fxfs 中的加密和多卷支持。
设计初衷
Fuchsia 需要能够支持使用 绑定到不同用户的密码。
利益相关方
审核者:abarth@google.com、enoharemaien@google.com、palmer@google.com、 jfsulliv@google.com、jsankey@google.com、zarvox@google.com、
已咨询:Fuchsia 的安全、存储和隐私团队。
社交化:此设计已由 Storage 团队传达和审核 和上述审核人员,然后再开始此 RFC 流程。
设计
RFC-0136 中介绍了 Fxfs。
要求
应该可以创建、枚举和删除卷。
每个卷应使用不同的密钥进行加密。
文件名、大小和时间戳等对象元数据 加密。
必须能够在没有密钥时删除卷。
无需大量资源就能支持按键滚动 迁移工作
卷的大小应该是有限制的(尽管此卷的设计 留待将来使用)。
您应该可以查询卷的大小,而无需访问 键。
应基于屏蔽提供针对指纹攻击的保护措施 计数。这是一种攻击方式,要求知道加密大小(向上舍入为 最近的文件块)来判断 这组文件存在于文件系统中
不在范围内
此设计未涵盖以下方面:
在设备(包括主机和目标设备)之间传输加密映像 设备),而此操作有助于进行调试 这种做法非常有用,但这在生产设备上 而且也没有先例。
加密服务的实现。我们将在其他地方介绍此设计。
设计应支持按键滚动,但支持使用的精确 API 它们不在讨论范围内。
概览
Fxfs 加密
Fxfs 内置加密功能支持按文件使用单独的密钥。谷歌文件极客 通常只有一个加密密钥,但为了支持密钥滚动和文件存储, 该格式支持每个文件使用多个密钥。密钥将被封装 并通过 Fxfs 将与之通信的加密服务解封。
根父存储区和根存储区中的以下对象将具有某种形式 加密:
该日志(存在于根父级存储区中)将发生变更 针对子存储区中的元数据使用流加密。通过 此加密的密钥将按商店进行。
子存储区(位于根存储区中)的图层文件将是 加密。对这些文件进行加密的机制与所有 其他文件。该密钥将使用按商店划分的密钥进行封装,并使用 其他未加密商店信息
根存储区和根存储区中的其他对象不会加密,即 超级块、与分配器相关的所有对象、后备的层文件 根对象存储区(用于存储根存储区对象的元数据)和 包含有关子商店的基本信息的对象。以上均不是 构成用户数据。
最终结果是:
- 用户数据会被加密
- 所有元数据,包括文件名、文件大小、范围信息和 目录信息将被加密
部分信息不会加密:
- 卷中的文件数。
- 分配给卷的范围集。
对象 ID
在 Fxfs 中,对象 ID 目前以单调递增的形式分配 可用作边信道。为解决这一问题, 对象 ID 的最小有效 32 位会被加密(在分配时 (例如,使用 ff1 加密)。密钥将在循环 32 秒后轮替 对象 ID 的位数。该密钥以封装方式存储 未加密商店信息的剩余部分对象 ID 的前 32 位 键每次滚动都会单调递增。
通过指定卷内使用的对象数量和空间, 边信道(基本上是通过 statvfs 获取的所有信息)。寻址 这超出了此设计的范围(对 Fxfs 而言并不新)。
密钥管理
密钥封装和解封分别由单独的“加密”负责,服务 它将提供如下协议:
/// Designates the purpose of a key.
type KeyPurpose = flexible enum {
/// The key will be used to encrypt metadata.
METADATA = 1;
/// The key will be used to encrypt data.
DATA = 2;
};
protocol Crypt {
/// Creates a new wrapped key. `owner`, effectively a nonce, identifies the
/// owner (an object ID) of the key and must be supplied to `UnwrapKey`.
/// `metadata` indicates that the key will be used to encrypt metadata which
/// might influence the choice of wrapping key used by the service. Returns
/// the wrapping key ID used to wrap this key along with the wrapped and
/// unwrapped key. Errors:
/// ZX_ERR_INVALID_ARGS: purpose is not recognised.
/// ZX_ERR_BAD_STATE: the crypt service has not been correctly initialized
/// with a wrapping key for the specified purpose.
CreateKey(struct {
owner uint64;
purpose KeyPurpose;
}) -> (struct {
wrapping_key_id uint64;
wrapped_key bytes:48;
unwrapped_key bytes:32;
}) error zx.status;
/// Unwraps keys that are wrapped by the key identified by `wrapping_key_id`.
/// `owner` must be the same as that passed to `CreateKey`. This can fail due
/// to permission reasons or if an incorrect key is supplied.
UnwrapKey(struct {
wrapping_key_id uint64;
owner uint64;
key bytes:48;
}) -> (struct {
unwrapped_key bytes:32;
}) error zx.status;
};
wrapping_key_id
的含义由加密算法的实现者决定 服务。Fxfs 不会对它的值应用任何显著性。每个已加密的 Fxfs 卷,从而将服务器置于不同的进程中,
目前将支持 256 位密钥(与 Zxcrypt 一致)。
加密服务预计将使用 AEAD 来封装密钥 也就是说,封装的密钥可能是 48 个字节。
purpose
用于分隔元数据所用的键与数据所用的键, 并且旨在促进按键滚动(见下文)。
加密服务的精确实现以及密钥管理政策 超出了本设计的范围。
磁盘格式
每个文件都包含如下内容:
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum EncryptionKeys {
None,
AES256XTS(WrappedKeys),
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct WrappedKeys {
/// The keys (wrapped). To support key rolling and clones, there can be more
/// than one key. Each of the keys is given an identifier. The identifier is
/// unique to the object. AES256-XTS requires a 512 bit key, which is made
/// of two 256 bit keys, one for the data and one for the tweak. Both those
/// keys are derived from the single 256 bit key we have here.
pub keys: Vec<(/* wrapping_key_id= */ u64, /* id= */ u64, [u8; 48])>,
}
每个范围都大致如下:
pub enum ExtentValue {
/// Indicates a deleted extent; that is, the logical range described by the
/// extent key is considered to be deleted.
None,
/// The location of the extent and other related information. `key_id`
/// identifies which of the object's keys should be used. `checksums` hold
/// post-encryption checksums.
Some { device_offset: u64, checksums: Checksums, key_id: u64 },
}
数据块将使用 AES-XTS-256 进行加密(与 Zxcrypt)。文件中的逻辑偏移量将用于调整。
元数据
对象存储区会维护日志结构合并 (LSM) 树,以保存其元数据 这些文件的加密方式与存储区中包含的文件相同。通过 创建用于图层文件的密钥,并将用途设置为 “metadata”。
事务被提交时,系统会将变更写入日志。这些 变更会应用于 LSM 树的内存层。一段时间 内存中层将刷新为持久层。任何变更 都需要在将数据写入 日志。为了支持这一点,Fxfs 使用了一项新的变更:
EncryptedObjectStore(Box<[u8]>),
流加密(不适合使用 AES-XTS-256 等分块加密)、Chacha20、 将用于加密这些变更。该密钥会被封装 与其他未加密存储数据一起存储的数据。
在重放时,键可能不可用,在这种情况下,这些变更 将以加密形式保存在内存中如果需要清空内存中 数据(以释放日志的空间),这些加密的更改将被 写入到根存储区中的对象。
当密钥可用时,加密的更改(可能是 或存在于前述文件中,可以解密和应用。接收者 解密变更,则需要加密流中的偏移量;这是 存储的未加密信息。
这些是添加到 StoreInfo 中的新字段(未加密的 商店):
// The (wrapped) key that encrypted mutations should use.
mutations_key: Option<Box<[u8]>>,
// Mutations for the store are encrypted using a stream cipher. To decrypt the
// mutations, we need to know the offset in the cipher stream to start it.
mutations_cipher_offset: u64,
// If we have to flush the store whilst we do not have the key, we need to
// write the encrypted mutations to an object. This is the object ID of that
// file if it exists.
encrypted_mutations_object_id: u64,
删除卷
为了支持删除卷,分配元数据将包含 拥有元数据的商店 ID:
pub struct AllocatorKey {
pub device_range: Range<u64>,
}
pub enum AllocationRefCount {
// Tombstone variant indicating an extent is no longer allocated.
None,
// Used when we know there are no possible allocations below us in the stack.
// (e.g. on the first allocation of an extent.)
// This variant also tracks the owning ObjectStore for the extent.
Abs { count: u64, owner_object_id: u64 },
}
pub struct AllocatorValue {
/// Reference count for the allocated range.
/// (A zero reference count is treated as a 'tombstone', indicating that older
/// values in the LSM Tree for this range should be ignored).
pub refs: AllocationRefCount,
}
删除卷涉及更改分配器,使其体现所有 属于已删除卷的记录。经过重大压缩后, 确保这些记录已不复存在,这样我们就会忘记 卷。分配器会保留已删除卷列表的相关信息 与其元数据的其余部分相关联
内嵌数据
Fxfs 目前不支持内嵌数据,但支持后就会 密钥与元数据的加密密钥相同。这些键 在写入新的图层文件时滚动。
安全擦除
若要安全地清除文件,必须确保文件不可恢复,即使密钥 (硬件中的容器除外)随后会遭到破坏。鉴于此, 文件系统通常在闪存设备上运行 收集),擦除所有出现的数据非常重要。唯一实用的 解决方案是滚动封装密钥(不应存储在闪存中)。
安全擦除并非计划要实现的功能,但应该 按照以下步骤操作:
开始使用新的封装密钥封装新的元数据密钥。
Fxfs 会使用旧的元数据键重写所有对象。
旧的元数据封装密钥会被销毁(通常使用 TPM 功能) (直接或间接),这超出了本设计的范围)。
通过执行一个主要步骤,第 2 步可以在 Fxfs 中 压缩。由于这是自然发生的,因此这是一个程序, 可以定期进行,也可按需进行。可以 安排 Fxfs 来保证每次(比如每周)进行一次重大压缩, 具体取决于可用的键。
此过程要求使用不同 将密钥封装到数据(否则,系统会强制重写所有数据, 这会造成限制),因此便是提供给 create_key 的元数据参数 方法。
请注意,此过程需要重写卷的所有元数据,这样做 因此不应频繁执行
按键操作
为安全起见,上文中概述了元数据封装密钥的滚动密钥 擦除内容。应该可以使用 以下程序:
开始使用新的封装密钥封装新密钥。返回新的 wrapping_key_id 。
要求 Fxfs 重新封装与给定 wrapping_key_id 匹配的所有密钥。用于实现以下目的的 API: 则留给后续设计使用:应该不需要任何磁盘格式 更改。
按照安全擦除程序封装元数据密钥。由于键是 只写入到元数据文件,则应确保封装数据 旧封装密钥被粉碎后,成功滚动该密钥。
请注意,您可以合并第 2 步和第 3 步,应该能够 主要压缩和重新封装密钥。
与安全擦除一样,并未计划实现密钥滚动支持 。
Fsck
没有密钥的 Fsck 将执行一组有限的检查。该 显然是不可行的, 范围和一致性。
在密钥可用的情况下,将可以执行完全一致性 或者只是检查单个卷的元数据。
多卷支持
fshost
将导出一个新的目录:volume。目录中的节点
将代表 fshost 导出的卷,并且将支持新的卷协议
(节点上将不支持其他协议,即这些节点不支持
fuchsia.io Node 协议,因此无法克隆)。协议将
例如:
type MountOptions = table {
// A handle to the crypt service. Unencrypted volumes will ignore this option.
crypt client_end:Crypt;
}
protocol Volume {
// Mounts the volume and starts serving the filesystem. An error will be
// returned if the volume is currently being served from a previous call
// to Mount.
Mount(resource struct {
export_root server_end:Directory;
options MountOptions;
}) -> () error zx.status;
}
如果提供了错误的加密服务,装载将失败,但这应该 极好;用户应该在调用 Mount 时 succeed;不应将其用作测试凭据的方法。
导出根目录现在与文件系统公开的根目录类似。通过 系统将公开 fs.Admin 服务,并且可以使用 关停方法。如果所有连接到该卷的设备,该卷也会被锁定。 书卷已关闭。锁定音量时,请务必小心谨慎,确保 所有解封装的密钥都会被舍弃。
枚举和移除操作将通过 fuchsia.io 目录协议在 存储卷目录移除不一定会异步执行,但操作会成功 则 不会将其返回 已移除。该名称可以立即重新使用,但可能需要一些时间 以供使用
新卷无法使用目录协议的 open 方法,因为我们需要 提供加密服务和其他选项而是会添加新的协议:
type CreateVolumeOptions = table {
// Reserved for future use.
}
protocol Manager {
// Creates a new data volume.
CreateVolume(resource struct {
name string:MAX_FILENAME;
crypt client_end:Crypt;
export_root server_end: Directory;
options CreateVolumeOptions;
}) -> () error zx.status;;
}
最初,实施将继续进行,假设 Fxfs 将管理所有 但将来应该可以提供 支持由 Zxcrypt 支持的文件系统。
卷上可能存在的卷的名称和确切的卷集将 ,因此不在本 RFC 的讨论范围之内。
实现
对多卷和加密的支持将在典型的 。
目前还没有计划支持密钥滚动、安全清除和 inline-data。
AES-XTS-256、ChaCha20 和 ff1 加密将由第三方 crate 提供。
必须接受安全审核。
性能
加密会影响性能。现有文件系统基准 将用于评估效果。实现 Zxcrypt 后 这应该会抵消由于这种实施造成的任何损失。不限 以及当前性能的回归问题。
向后兼容性
这将是对 Fxfs 的重大更改,并且需要重新格式化。
安全注意事项
此 RFC 已由安全团队审核。
以下要求由安全性驱动:
每个卷应使用不同的密钥进行加密。
文件数据和元数据应加密。
应该很难将对象 ID 用作旁路。
实现将需要接受安全审核。
隐私注意事项
此 RFC 已由隐私权团队审核。
我们在隐私保护方面主要考虑的因素是,以下数据不会 加密:
- 卷中的文件数。
- 分配给卷的范围集(包括 分配给卷的空间)。
- 卷的名称。
所有其他数据和元数据都应加密。这应该足以 以满足包括指纹攻击的要求。
实现的安全审核应验证设计是否符合以下要求 要求。
测试
将使用单元、集成和 和端到端测试Fxfs 将用于许多 在 CQ 下运行,因此可以得到曝光。
文档
在某些阶段,需要提供文档以帮助系统设计人员选择 指定产品的不同存储选项之间具有无限制。不过, 具体配置尚未确定,并且不在 此 RFC 范围。
此 RFC 中引入的 API 最初会(并且可能无限期) 在树内,并且暂时将记录为 FIDL 的一部分。
缺点、替代方案和未知问题
此 RFC 中概述的加密设计要比 基于分区的加密,这与 Zxcrypt 所用的加密机制相同。
系统设计人员仍然可以使用基于分区的方法 但是对于卷之间的空间共享存在限制: 基于分区的方案,该方案非常灵活,有空间,需要 Volume-manager(这会导致性能下降,因为 并且可能遭遇碎片化问题(移动空间 分区可能需要进行碎片整理)。同样,采用自动化技术的 支持对基于分区的方案进行安全清空和密钥滚动。
先验技术和参考资料
Zxcrypt 使用 AES-256 XTS 以与此 RFC 类似的方式对分块进行加密 所用的调整方法有所不同。
此处的设计主要与 Android 12 的加密功能兼容 要求。其中一个显著区别在于,通过 Fxfs 的日志需要通过流加密方式进行加密, 明确提到可以接受。