载荷格式
载荷分为 A区(文件元数据+内容)和 B区(Manifest+自定义数据),两区完全独立。
A区/B区分离设计
| 区域 | 内容 | 加密时 | 未加密时 |
|---|---|---|---|
| A区 | 文件数量 + 路径条目 + 文件内容 | AES-256 加密 | 压缩 + S-Box |
| B区 | Manifest + CustomData | 压缩 + S-Box(明文) | 压缩 + S-Box(明文) |
注意:B区仅在
HasManifest=1或HasCustom=1时存在。如果两者均为 0,文件在 A区数据结束后即终止。
文件布局
未加密模式(Encrypted = 0)
┌──────────────────────────────────────────────────┐
│ TTP 头部 (26 字节) │
├──────────────────────────────────────────────────┤
│ A区长度 (4 bytes, uint32 LE) │
├──────────────────────────────────────────────────┤
│ [A区] 压缩 + S-Box 后的数据 │
├──────────────────────────────────────────────────┤
│ [B区] 压缩 + S-Box 后的数据(可选) │
└──────────────────────────────────────────────────┘加密模式(Encrypted = 1)
┌──────────────────────────────────────────────────┐
│ TTP 头部 (26 字节) │
├──────────────────────────────────────────────────┤
│ A区长度 (4 bytes, uint32 LE) │
├──────────────────────────────────────────────────┤
│ [加密区] AES-256 加密的 A区数据 │
│ 内部 = Salt(16B) + IV(12B) + A区密文 │
├──────────────────────────────────────────────────┤
│ [B区] 压缩 + S-Box 后的数据(可选) │
└──────────────────────────────────────────────────┘A区明文内容
文件数量 (4 bytes, uint32 LE)
├── 文件条目 1:
│ - 路径长度 (2 bytes, uint16 LE)
│ - 路径 (UTF-8, 如 "folder/sub/file.txt")
│ - 内容长度 (8 bytes, uint64 LE)
│ - 内容 (原始字节)
├── 文件条目 2:
│ ...- 文件数量最大约 42.9 亿(uint32)
- 路径最长 65535 字节(uint16)
- 文件内容最大 2^64 字节(uint64)
B区明文内容
[HasManifest = 1 时]
Manifest 长度 (4 bytes, uint32 LE)
Manifest 文本 (UTF-8 JSON)
[HasCustom = 1 时]
自定义数据长度 (8 bytes, uint64 LE)
自定义数据 (原始字节)Manifest 语义:Manifest 仅用于存储应用自定义信息(如版本号、作者、项目名)。文件目录结构完全由二进制路径条目还原,不再依赖 Manifest。
加密参数(加密区内部前置)
Salt (16 bytes) — PBKDF2 盐值
IV (12 bytes) — AES-256-GCM 初始向量
Ciphertext — AES-256-GCM 加密的 A区数据(含 Auth Tag)密钥派生:PBKDF2-HMAC-SHA256,100000 次迭代,从 password + Salt 派生 32 字节密钥。
路径规范
- 路径分隔符必须使用
/(无论打包系统) - 解包时由解包器转换为当前系统的路径分隔符
- 路径长度单位为字节数(非字符数)
- 不允许包含
\0字节 - 路径最长 65535 字节(uint16)
边界情况
- 空目录:
collect_files()静默跳过空目录,不保留在载荷中 - 空载荷:允许打包空目录(文件数量 = 0)
- 路径安全:解包器必须检测路径穿越攻击(
../)
