xbxbxb462-cpu/sherd

GitHub: xbxbxb462-cpu/sherd

一款面向极端威胁模型的离线命令行加密工具,提供合理推诿、Shamir 秘密共享和全面的内存防护能力。

Stars: 0 | Forks: 0

sherd

sherd

为真正需要保密的人提供的离线、单二进制文件加密工具。

CI Releases License Rust MSRV Platform LOC Status

Sherd 是一款命令行加密工具,其构建基于一个假设:对手具有专业能力。它使用通过 **Argon2id** 和 **HKDF-SHA256** 派生的密钥,或用于非对称交换的 **X25519 接收者密钥**,通过 **AES-256-GCM** 加密消息和文件。它通过不可区分的诱饵槽位支持**合理推诿**,在 GF(256) 上支持 **Shamir Secret Sharing**,以及一种会掩盖明文长度的**偏执模式**。 每个密钥缓冲区都被包装在 `Zeroizing<…>` 中并在 drop 时被擦除。整个进程地址空间通过 `mlockall(MCL_CURRENT | MCL_FUTURE)` 被锁定以防止交换。Core dump 已被禁用。解密在各个槽位和数据块之间以统一的时间运行。没有配置文件,没有网络,没有遥测,没有插件加载器。 ## 为什么选择 Sherd? 大多数加密工具都以便利性为目标进行优化。Sherd 则针对对手具有专业能力和明确动机的情况进行了优化。 - **内存被锁定,而不仅仅是归零。** `mlockall(MCL_CURRENT | MCL_FUTURE)` 防止内核将进程的任何页面交换到磁盘。Core dump 通过 `setrlimit(RLIMIT_CORE, 0)` 和 `prctl(PR_SET_DUMPABLE, 0)` 被禁用。冷重启攻击者什么也找不到。 - **合理推诿是真实的,而不是虚张声势。** 每个密码加密的文件都有两个不可区分的槽位。在胁迫下,你只需透露诱饵密码;对手无法证明第二个槽位的存在。两个槽位都是带有有效提交标签的有效 AES-256-GCM 密文,并被填充到相同的随机化长度。 - **解密采用统一时间。** 无论提交标签是否匹配,每个槽位的每个数据块都会被处理。错误的密码与正确的密码花费相同的实际时间。这关闭了否则会破坏合理推诿的时间侧信道。 - **Shamir 共享不会泄露任何信息。** 无论密钥长度如何,共享份额都是固定的 4098 字节。阈值 K 和总数 N 不会存储在任何共享份额中。截获其中一个份额的人无法得知法定人数或密钥大小。 - **没有配置攻击面。** 没有配置文件,没有插件加载器,没有网络。恶意的环境变量无法降级 KDF 或禁用填充。每个与安全相关的参数都是硬编码的,或者在命令行中明确指定。 - **用于非对称交换的接收者模式。** 加密到 X25519 公钥(`sherd1...`)。没有密码,没有 Argon2id,即时加密和解密。每个接收者都会获得一个独立的 stanza;任何单个接收者的身份都可以进行解密。 有关完整的协议规范,请参阅 [`docs/protocol.md`](docs/protocol.md)。 ## 目录 - [快速入门](#quickstart) - [安装](#install) - [使用方法](#usage) - [密码加密](#passphrase-encryption) - [接收者加密 (X25519)](#recipient-encryption-x25519) - [合理推诿](#plausible-deniability) - [Shamir 共享](#shamir-secret-sharing) - [不解密进行检查](#inspect-without-decrypting) - [安全模型](#security-model) - [文件格式](#file-format) - [常见问题](#faq) - [贡献](#contributing) - [许可证](#license) ## 快速入门 ``` # 从源码构建(需要 Rust 1.74+) git clone https://github.com/xbxbxb462-cpu/sherd.git cd sherd cargo install --path . # 允许无 root 下进行内存锁定 sudo setcap cap_ipc_lock=ep "$(which sherd)" # 使用密码加密消息(stdin → stdout) echo "top secret" | sherd encrypt > msg.shrd.asc # 解密 sherd decrypt -i msg.shrd.asc ``` 对于基于接收者的加密(没有密码,只需公钥): ``` sherd keygen -o alice.key # writes SHERD-SECRET-KEY-1... (mode 0600) sherd keygen -y -i alice.key # prints: sherd1... echo "for alice" | sherd encrypt -r sherd1... > for-alice.shrd.asc sherd decrypt -I alice.key -i for-alice.shrd.asc ``` ## 安装 ### 从源码构建 需要 Rust 1.74 或更高版本。 ``` git clone https://github.com/xbxbxb462-cpu/sherd.git cd sherd cargo install --path . ``` 生成的二进制文件位于 `~/.cargo/bin/sherd`。 ### 内存锁定 Sherd 在 release 模式下运行的前提是能够锁定其地址空间以防止交换。选择以下一种方式: ``` # 选项 A:授予一次 capability sudo setcap cap_ipc_lock=ep "$(which sherd)" # 选项 B:为所有用户提高 memlock rlimit echo '* soft memlock unlimited' | sudo tee -a /etc/security/limits.conf echo '* hard memlock unlimited' | sudo tee -a /etc/security/limits.conf # 注销并重新登录 ``` 在 debug 构建中,设置 `SHERD_ALLOW_NO_MLOCK=1` 会被接受,但会发出明显的警告。在 release 构建中,该变量会被拒绝。 ### 验证二进制文件 ``` sherd hash # prints the SHA-256 of the running binary sherd selftest # runs Argon2id / HKDF / HMAC / AES-GCM known-answer tests ``` ## 使用方法 Sherd 有九个子命令。运行 `sherd --help` 获取完整列表,或运行 `sherd --help` 了解详细信息。 ### 密码加密 使用密码进行加密。密码通过 Argon2id(内存 64–256 MiB,迭代次数 3–5,并行度 4)进行拉伸,并绑定到提交标签中,该标签会在释放任何明文之前进行验证。 ``` # stdin → stdout,ASCII-armored echo "top secret" | sherd encrypt --kdf standard > msg.shrd.asc # file → file,binary sherd encrypt-file -i report.pdf # 写入 report.pdf.shrd(mode 0600) ``` 解密: ``` sherd decrypt -i msg.shrd.asc sherd decrypt-file -i report.pdf.shrd -o report.pdf ``` KDF 预设: | 预设 | 内存 | 迭代次数 | 通道 | 用例 | |--------|-------:|-----------:|------:|----------| | `standard` | 64 MiB | 3 | 4 | 默认,交互式 | | `paranoid` | 128 MiB | 4 | 4 | 敏感数据,添加长度抖动填充 | | `extreme` | 256 MiB | 5 | 4 | 离线主密钥 | 密码源(按安全性排序): ``` # file descriptor(永远不会出现在 /proc/PID/cmdline 中) sherd encrypt --pass-fd 3 3 for-alice.shrd.asc # 加密给多个接收者(每个人都可以解密) echo "for both" | sherd encrypt \ -r sherd1HVDKgCR/RXkQCN1iVr7mejRHHMdg/0nOKzOlP37OtUo= \ -r sherd1XqjsrbgszkY/XZ3LJku/PH1ZjyrqANYDQs05sP4aZG8= \ > for-both.shrd.asc # 加密给文件中列出的接收者(每行一个,允许 # 注释) echo "for the team" | sherd encrypt -R recipients.txt > team.shrd # 使用任何匹配的 identity 解密 sherd decrypt -I alice.key -i for-both.shrd.asc ``` 身份文件包含一行或多行 `SHERD-SECRET-KEY-1...`,每行一个。`#` 开头的行是注释。你可以多次传递 `-I` 来尝试多个身份。 ### 合理推诿 每个密码加密的文件都有两个不可区分的槽位。如果你提供诱饵密码和诱饵明文,第二个槽位将携带诱饵;在胁迫下,你只需透露诱饵密码,而对手无法证明第二个槽位的存在。 ``` sherd encrypt \ --decoy decoy.txt \ --decoy-pass-file decoy-pass.txt \ --pass-file real-pass.txt \ -i real.txt -o real.shrd.asc ``` 两个槽位都被填充到相同的随机化目标长度,因此文件大小不会泄露哪个槽位更大。 ### Shamir 共享 将密钥拆分为 N 个共享份额,其中任意 K 个即可重构它。无论密钥长度如何,共享份额都是固定的 4098 字节。阈值 K 和总数 N **不会**存储在任何共享份额中——截获其中一个份额的人对法定人数一无所知。 ``` # 3-of-5 split sherd share-split -i master.key -k 3 -n 5 > shares.txt # 将每个 === SHERD Share === block 通过单独的 channel 分发 # 使用任意 3 个重建 sherd share-combine -k 3 -s share1.txt -s share2.txt -s share3.txt -o master.key ``` 阈值 `-k` 由调用者在合并时提供。它不是从共享份额中恢复出来的。 ### 不解密进行检查 `sherd inspect` 报告文件元数据,而不会运行 KDF 或触碰密文: ``` sherd inspect -i file.shrd ``` 输出内容包括:格式版本、模式(密码 vs 接收者)、加密算法、KDF 参数 (v1) 或接收者数量 (v2)、数据块数量、密文大小、每个槽位 / 每个 stanza 的大小。这对于解密前的筛选非常有用。 ### Shell 自动补全 为 bash、zsh、fish 或 PowerShell 生成补全脚本: ``` # bash(添加到 ~/.bashrc) sherd completion bash > ~/.local/share/bash-completion/completions/sherd # zsh(添加到 ~/.zshrc) sherd completion zsh > "${fpath[1]}/_sherd" # fish sherd completion fish > ~/.config/fish/completions/sherd.fish # powershell sherd completion powershell | Out-String | Invoke-Expression ``` ## 安全模型 ### Sherd 可以防御什么 - **纯密文攻击者。** 使用带有基于每个数据块 HKDF 派生密钥的 AES-256-GCM;nonce 重用在结构上是不可能的(每个槽位使用随机的 `base_iv`,数据块索引被 XOR 到 nonce 中)。 - **头部篡改。** 固定的头部被绑定到提交标签 (HMAC-SHA256-truncated-128) 以及每个数据块的 AEAD AAD 中。 - **提交标签伪造。** 提交标签还绑定了第一个数据块密文的 SHA-256,防止了“隐形蝾螈”密文替换攻击。 - **数据块泄露。** 每个数据块都有自己通过 HKDF 派生的密钥;一个被破解的数据块不会泄露相邻数据块的信息。 - **时间侧信道。** 无论提交标签是否匹配,每个槽位的每个数据块都会被处理。错误的密码与正确的密码花费相同的实际时间(受 Argon2id 方差影响除外)。 - **长度侧信道。** 输出大小是随机化的:4 字节长度前缀(已验证)+ 至少 32 字节填充 + 统一的 0–8 KiB 抖动 + 偏执模式添加 1–4 × 4 KiB 块。诱饵槽位被填充到相同的目标长度。 - **胁迫。** 诱饵槽位与真实槽位无法区分。两者都是带有有效提交标签的有效 AES-256-GCM 密文。 - **内存取证。** `mlockall(MCL_CURRENT | MCL_FUTURE)` 锁定整个地址空间。`prctl(PR_SET_DUMPABLE, 0)` 和 `setrlimit(RLIMIT_CORE, 0)` 禁用 core dump。每个密钥缓冲区(密码、主密钥、PRK、提交密钥、基于数据块的密钥、填充明文、解密输出、Shamir 重构的密钥、X25519 身份、文件密钥)都被包装在 `Zeroizing<…>` 中并在 drop 时被擦除。 - **递归加密隐患。** `sherd encrypt` 会拒绝以 `SHR1` 魔数开头的内容,除非你传递 `--force`。 - **路径遍历。** `decrypt-file` 中的嵌入式文件名已进行无害化处理;输出路径会根据输入路径进行检查。 - **文件覆盖。** `decrypt-file` 会拒绝覆盖现有的输出文件,除非指定了 `--force`。 - **输入上的 TOCTOU。** 文件只打开一次,在文件描述符上执行 `fstat`,并从同一个 fd 读取。 ### Sherd 无法防御什么 - **受损的操作系统或硬件植入物。** 如果内核是恶意的,`mlockall` 就只是一个建议。在进行高风险操作时,请使用运行 Tails 的物理隔离机器。 - **冷启动攻击。** 如果此威胁在考虑范围内,请在敏感操作前后进行冷重启。 - **浏览器或操作系统的 0-day 漏洞。** 超出考虑范围。 - **除时间以外的侧信道。** 功耗、电磁、声学——超出考虑范围。 ### 量子对手 AES-256 能够抵抗 Grover 算法降至 2^128 的工作量。X25519 则不能(量子对手可以破解 ECDH)。如果量子对手在考虑范围内,请使用带有高熵密码的密码模式和 `--kdf extreme`。 有关完整的披露政策,请参阅 [`SECURITY.md`](SECURITY.md)。 ## 文件格式 ### v1 — 密码 ``` +-------------------+ | magic "SHR1" | 4 bytes | version = 1 | 1 byte | flags | 1 byte (FLAG_PARANOID always set) | cipher_id | 1 byte (AES-256-GCM) | kdf_id | 1 byte (Argon2id) | commit_id | 1 byte (HMAC-SHA256-trunc-128) | kdf_mem_kib | 4 bytes (u32 BE) | kdf_iters | 1 byte (u8) | kdf_par | 1 byte (u8) | slot_count | 1 byte (always 2) +-------------------+ | slot 0 | salt[16] + iv[12] + commit_tag[16] | | + chunk_count[4] + ct_total_len[4] | | + ciphertext[ct_total_len] +-------------------+ | slot 1 | (same layout; real or decoy) +-------------------+ ``` ### v2 — 接收者 ``` +-------------------+ | magic "SHR1" | 4 bytes | version = 2 | 1 byte | recipient_count | 1 byte (1..=255) | base_iv | 12 bytes | chunk_count | 4 bytes (u32 BE) | ct_total_len | 4 bytes (u32 BE) +-------------------+ | stanza 0 | ephemeral_pub[32] + wrapped_key[48] | ... | | stanza N-1 | ephemeral_pub[32] + wrapped_key[48] +-------------------+ | ciphertext | ct_total_len bytes +-------------------+ ``` 数据块大小为 1 MiB;最大密文为 256 MiB(256 个数据块)。每个数据块都有一个独立的 AES-256-GCM 密钥,该密钥通过 `HKDF-Expand(file_key, "sherd-v1/chunk/{i}/{count}")` 派生。 ASCII armor 将任一格式包装为: ``` -----BEGIN SHERD MESSAGE----- -----END SHERD MESSAGE----- ``` ## 常见问题 **为什么强制锁定内存?** 如果操作系统能够将你的密码或主密钥交换到磁盘,任何具有物理访问权限的人都可以在事后将其恢复。`mlockall` 可以防止这种情况。如果你无法授予 `CAP_IPC_LOCK` 或提高 `RLIMIT_MEMLOCK`,Sherd 将拒绝在 release 模式下运行。 **为什么没有配置文件?** 配置文件是一个攻击面。恶意的配置可能会降级 KDF 预设、禁用填充或更改加密算法。每个与安全相关的参数要么是硬编码的(加密算法、KDF 算法),要么是在命令行中明确指定的(KDF 预设、诱饵)。 **为什么密码模式下有两个槽位?** 第二个槽位是合理推诿通道。如果你不提供诱饵,第二个槽位就是一个在结构上完全相同的、带有随机密文的虚拟槽位——观察者无法辨别诱饵是否存在。 **我可以同时加密给密码和接收者吗?** 不能在同一个文件中实现。请使用两个文件:一个用密码加密,另一个用接收者加密,且具有相同的明文。或者先将明文加密给接收者,然后再用密码加密接收者的身份文件。 **如果我丢失了身份文件会怎样?** 文件密钥被包装到了你的 X25519 公钥中。如果没有私钥,就无法恢复它。这里没有托管,没有恢复,也没有后门。 **格式稳定吗?** 从 sherd 1.0 开始,v1 和 v2 格式是稳定的。未来的版本将添加新的版本字节,而不是破坏现有的版本。ASCII armor 标签(`SHERD MESSAGE`、`SHERD FILE`、`SHERD SHARE)是固定的。 **为什么不用 ChaCha20-Poly1305?** AES-256-GCM 在 x86_64 和 ARM64 上具有硬件加速,这使得常量时间实现更容易验证。ChaCha20-Poly1305 是一种很好的加密算法;这只是一个偏好,而不是关于安全性的声明。 ## 许可证 双重授权,可选择以下任一: - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE)) - MIT License ([LICENSE-MIT](LICENSE-MIT)) 由你选择。贡献遵循相同的双重许可证。
标签:Rust, 加密工具, 可视化界面, 密码学, 手动系统调用, 文件加密, 网络安全, 网络流量审计, 通知系统, 隐私保护