systemslibrarian/shadow-vault

GitHub: systemslibrarian/shadow-vault

一个基于浏览器的可否认加密演示工具,通过双密码机制让同一容器可解密出真实或诱饵两条消息,且无法从结构上判断哪个才是真的。

Stars: 0 | Forks: 0

# 影子金库 **可否认加密 — 两条消息,一个容器,无痕可循。** **[在线演示](https://systemslibrarian.github.io/shadow-vault/)** 你被扣留了。他们要求你提供密码。你照做了。他们解密出一条看似合理的信息 — 一份购物清单、一篇日记、一张给朋友的便条。他们无法证明、无法检测、甚至无法测试的是:在同一容器的不同偏移位置隐藏着第二条加密信息,只有使用另一个他们不知道存在的不同密码才能解密。 影子金库是一个基于浏览器的可否认加密演示,使用固定大小的容器存放两条独立加密的信息。真实密码解密真实信息。诱饵密码解密诱饵信息。容器在结构上与随机字节无法区分 — 没有头部、没有魔数、没有长度字段、没有取证指纹。 ## 工作原理 ``` Passphrase A → Argon2id (64 bytes) → key[0..31] + nonce[32..43] + offset[44..47] ↓ ↓ ChaCha20-Poly1305 encrypt position in container Passphrase B → Argon2id (64 bytes) → key[0..31] + nonce[32..43] + offset[44..47] ↓ ↓ ChaCha20-Poly1305 encrypt position in container Container = CSPRNG random bytes (fixed size: 4KB–32KB) + real ciphertext at offset A + decoy ciphertext at offset B + everything else remains random padding ``` 1. 固定大小的容器完全由加密随机字节填充 2. 每个密码通过 Argon2id 独立派生加密密钥、随机数和槽位偏移量 3. 每条信息使用 ChaCha20-Poly1305 加密并写入其派生的偏移位置 4. 结果看起来像随机数据 — 与 CSPRNG 填充无法区分 ## 为什么 Argon2id 是关键 Argon2id 不是偶然的选择 — 它是可否认加密模型的安全基础。 如果没有内存硬密钥派生,获得容器的攻击者可以同时暴力破解两个密码,恢复两个偏移量和两条信息。使用高内存参数(每次派生 64MB+,按 RFC 9106)使这在计算上毫无希望。成本随每个密码尝试递增 — 攻击两个独立密码需要两倍的成本。 UI 明确展示了这一点:调低 Argon2id 参数,观察派生时间下降到危险水平。 ## 密码学技术栈 | 组件 | 实现 | 参考 | |-----------|---------------|-----------| | 密钥派生 | Argon2id (Rust → WASM, `argon2` crate) | [RFC 9106](https://datatracker.ietf.org/doc/html/rfc9106) | | 对称加密 | ChaCha20-Poly1305 AEAD (Rust → WASM, `chacha20poly1305` crate) | [RFC 8439](https://datatracker.ietf.org/doc/html/rfc8439) | | 内存清零 | 通过 `zeroize` crate 保证(编译器屏障) | [zeroize](https://docs.rs/zeroize) | | 随机生成 | `getrandom` crate(由 Web Crypto API 在 WASM 中支持) | [W3C Web Crypto](https://www.w3.org/TR/WebCryptoAPI/) | | 盐值派生 | SHA-256(从角色确定性派生,`sha2` crate) | [FIPS 180-4](https://csrc.nist.gov/publications/detail/fips/180/4/final) | **所有密码操作都在 Web Worker 中运行** — 密钥材料永远不会离开 WASM 线性内存。RustCrypto crates(`argon2`、`chacha20poly1305`)是经过社区审计的实现。`zeroize` crate 使用 `volatile` 写入来保证敏感内存在释放前被清零,这是 JavaScript 的 `fill(0)` 无法保证的。 **Argon2id 参数(默认):** - 内存:64 MB(65536 KiB)— RFC 9106 交互式使用最低要求 - 迭代次数:3 — RFC 9106 推荐值 - 并行度:4 - 输出:64 字节(密钥 + 随机数 + 偏移量种子) **ChaCha20-Poly1305:** 通过 `chacha20poly1305` crate(RustCrypto)在 Rust 中实现。在每次应用加载时根据 RFC 8439 测试向量进行验证。所有密码操作通过 WASM 在 Web Worker 中运行 — 主线程从不处理密钥材料。 ## 此工具无法防护的情况 - 本演示代码中的**实现漏洞** - **键盘记录器**或被入侵的设备捕获密码 - **暴力胁迫**(酷刑逼供) - **容器外的元数据** — 文件名、时间戳、访问日志、浏览器历史 - **浏览器内存** — 密码以 JavaScript 字符串(不可变、被垃圾回收)形式输入后才传到 WASM。Rust/WASM 确定性地清零所有密钥材料,但 JavaScript 中的密码字符串是不可避免的。 - **流量分析**(如果容器在网络上传输) ## 诚实的局限性 影子金库使用**经审计的 RustCrypto crates** 编译为 WASM,但集成和容器格式尚未经过正式的安全审计。基于浏览器的密码学继承浏览器的所有安全模型限制(扩展、开发者工具、操作系统级攻击)。 密码仍然通过 HTML `` 元素作为不可变的 JavaScript 字符串输入 — Rust/WASM 无法清零这些。其他所有内容(密钥、盐值、明文槽)通过 `zeroize` crate 确定性地清零。 **真实信息和诱饵信息都需要强且唯一的密码。** 可否认性取决于攻击者无法暴力破解任一密码。无论 Argon2id 参数如何设置,弱密码都会使整个安全模型崩溃。 解密后的信息会在 2 分钟后自动清除。敏感输入在空闲和页面卸载时清除。 此工具在严重威胁模型下比原生应用程序弱。对于现实世界的可否认加密,请使用带隐藏卷的 [VeraCrypt](
标签:Argon2id, ChaCha20-Poly1305, CMS安全, CSPRNG, JavaScript, meg, Poly1305, Web Crypto API, 信息安全, 加密容器, 可否认加密, 可视化界面, 密码学, 密钥派生函数, 对称加密, 手动系统调用, 抗审查, 数据隐藏, 浏览器加密, 端到端加密, 网络安全, 自动化审计, 通知系统, 随机数生成, 隐写术, 隐私保护, 零知识证明