pub struct Kek(/* private fields */);Expand description
库主密钥(Key Encryption Key)。
由 Argon2id(密码, vault_salt) 派生,每密码每会话只应派生一次,
之后用 Kek::derive_slot_key 等方法为每个文件微秒级派生子密钥。
Implementations§
Source§impl Kek
impl Kek
Sourcepub fn duplicate(&self) -> Self
pub fn duplicate(&self) -> Self
显式复制。
命名与 SecretKey::duplicate 一致,刻意不实现 Clone——这样审计时
能搜出所有 KEK 复制点。扫描器需要它把会话缓存中的 KEK 传给解包函数。
Sourcepub fn from_password(
password: &[u8],
vault_salt: &[u8],
params: Argon2Params,
) -> Result<Self>
pub fn from_password( password: &[u8], vault_salt: &[u8], params: Argon2Params, ) -> Result<Self>
用 Argon2id 从密码派生 KEK。
§开销
这是故意慢的操作(默认参数约 180 ms)。调用方必须缓存结果, 不得在循环中对每个文件重复调用。
§Errors
Argon2 参数非法(如内存成本过小)时返回 Error::KeyDerivation。
Sourcepub fn derive_dirname_key(
&self,
vault_salt: &[u8; 16],
info: &[u8],
) -> SecretKey
pub fn derive_dirname_key( &self, vault_salt: &[u8; 16], info: &[u8], ) -> SecretKey
派生 vault 级的目录名加密密钥(树形模式,文档 05 §3.3)。
salt = vault_salt,info 由调用方给出(见
crate::dirname::DirnameKey::derive)。
§为什么 salt 是 vault_salt 而不是 file_uuid
目录名不属于任何单个文件,没有 file_uuid 可用。用 vault_salt 的 副作用是目录结构的可见性绑定整个 vault:能解开 vault 的任一 密码都能看到完整目录树,无法按 slot 区分权限。这是树形模式的固有 约束,UI 必须告知用户。
Sourcepub fn derive_slot_key(
&self,
file_uuid: &[u8; 16],
slot_index: u16,
) -> SecretKey
pub fn derive_slot_key( &self, file_uuid: &[u8; 16], slot_index: u16, ) -> SecretKey
派生第 slot_index 个 key slot 的包裹密钥。
info = "omy/v1/slot" || u16le(slot_index),salt = file_uuid
Sourcepub fn fingerprint(&self, vault_salt: &[u8; 16]) -> [u8; 32]
pub fn fingerprint(&self, vault_salt: &[u8; 16]) -> [u8; 32]
派生该 KEK 的会话内指纹,用于判断两个 KEK 是否相同。
§为什么需要它
会话要同时装多个密码,就必须回答「刚输的这个,是不是已经装过的那个」。
直接比较 KEK 字节是可以的,但那要求把密钥本身搬来搬去做相等比较;
而按名字比较又不成立——同一个密码可以被起两个名字,不同密码也可以
重名(上一轮「label 让同一个密码被算成两条凭据」正是后者)。
§为什么不能落盘
指纹是密码的稳定标识:同一密码在同一 vault 下恒为同一值。写进 磁盘就等于给攻击者一个离线可比对的口令哈希——他能据此判断两个库是不是 同一个密码、能拿字典逐个比对候选密码,而 Argon2 那几百毫秒的代价 在这里完全不起作用(KEK 已经是派生结果,指纹只是一次 HKDF)。
所以它只允许在内存里存在,不得写入任何文件、日志或 IPC 响应。
§为什么 salt 是 vault_salt
让指纹天然绑定 vault。同一密码在不同 vault 下 KEK 本就不同,指纹也就
不同——这与缓存键必须含 vault_salt 是同一个道理,避免跨库误判成
「已经装过了」。