Skip to main content

Module dirsidecar

Module dirsidecar 

Source
Expand description

目录名的钥匙包裹边车(.omy-name)。

§为什么目录名要两层结构

原先目录名直接用 HKDF(KEK, info="omy/v1/dirname") 派生的密钥加密。 这个做法有一个致命的结构性限制:目录名只能被一把钥匙解开。

实测过它的后果(probe-tree-multikey):一棵树用两个密码加密,两个 密码都能打开每一个密文文件,但只有第一个能解开目录名,第二个解密整棵 树时报 content hash mismatch——用户看到的是「文件损坏」。恢复码撞的 是同一堵墙。

所以改成与文件一致的两层结构:

目录名密文 = AEAD(DK, 目录名)          DK 是随机的目录密钥
边车       = 每把 KEK 各包一份 DK

这样加一把钥匙只是多一份包裹,与目录名本身的长度无关。

§为什么包裹必须放边车文件,不能塞进目录名

算过了(probe-dirname-length),这不是取舍而是唯一可行解。 包裹塞进目录名时,以 32 字节的明文目录名为例:

钥匙数磁盘名长度上限 255
1196勉强
2292超
4484超
8868超

两把钥匙就破了 255(NTFS / ext4 / APFS 的共同下限)。更糟的是 8 把 钥匙时留给明文目录名的额度是 0 字节——连一个字都放不下。

包裹放边车则与钥匙数量完全无关:同样 32 字节的明文名恒定 100 字符, 明文目录名要超过 127 字节(约 42 个汉字)才触发截断。

§边车固定 8 槽、空槽填随机

与 crate::slot 同样的两条可否认性规则,理由也一样:边车长度固定, 看不出这个目录配了几把钥匙;空槽填随机而非填零,否则「哪些槽在用」 直接可见。

这里还多一层收益:边车现在恒定存在。原先它只在目录名超长时才出现, 那反而泄露了「这个目录的名字很长」。

§丢了边车会怎样

目录名再也解不开——这是必须在 UI 里说清楚的后果。它和目录是一体的, 不是缓存、不是可再生的索引。放在被加密的那个目录内部而不是父目录, 正是为了让它在目录被移动或复制时跟着走。

Structs§

DirKey
随机生成的目录密钥。

Enums§

SidecarPlan
可管理模式下,一个边车槽位要怎么处理。

Constants§

DK_LEN
目录密钥长度。
KEYS_SIDECAR
钥匙包裹边车的文件名。
SIDECAR_FLAG_MANAGED
边车 flags 位:这棵树带槽位目录(可管理模式)。
SIDECAR_LEN
边车文件总长:magic(8) + version(1) + flags(1) + reserved(2) + nonce(24)
SIDECAR_LEN_MANAGED
可管理模式下的边车总长:在基础长度之后追加加密的槽位目录。
SIDECAR_MAGIC
边车魔数,用于快速排除「这不是我们的文件」。
SIDECAR_SLOTS
固定槽位数,与文件的 Key Slot Area 保持一致。
SIDECAR_SLOT_AREA
槽位区总长。
SIDECAR_VERSION
边车格式版本。
WRAP_LEN
包裹后的长度:32 字节 DK + 16 字节 AEAD tag。

Functions§

build_sidecar
构建边车内容。
build_sidecar_managed
构建带槽位目录的边车(可管理模式)。
is_managed
这份边车是不是可管理模式。
open_sidecar
从边车里解出目录密钥。
open_sidecar_at
同 open_sidecar,另外返回命中的槽位下标。
read_slot_directory
从可管理模式的边车里解出槽位目录。
rebuild_sidecar_carrying
在保留原有包裹的前提下,用新的钥匙集合重建边车。
rewrite_sidecar_managed
按计划精确改写边车的槽位(可管理模式)。