urwithajit9/evnx-crypto

GitHub: urwithajit9/evnx-crypto

一个纯 Rust 实现的零知识加密原语库,为 evnx 生态系统提供端到端的密钥派生、vault 加密、非对称密钥管理和 SRP 认证能力。

Stars: 0 | Forks: 0

# evnx-crypto [![Crates.io](https://img.shields.io/crates/v/evnx-crypto)](https://crates.io/crates/evnx-crypto) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/dd45d95be8154829.svg)](https://github.com/urwithajit9/evnx-crypto/actions) [![Security Audit](https://github.com/urwithajit9/evnx-crypto/actions/workflows/audit.yml/badge.svg)](https://github.com/urwithajit9/evnx-crypto/actions) [![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue)](LICENSE) 一个纯 Rust 加密库,为 evnx 实现 ZKE (Zero-Knowledge Encryption) 协议。服务器**绝不**可见您的密码、主密钥或明文 `.env` 内容 —— 所有的敏感操作均在此库中本地运行。 ## 本库功能 ``` User Password │ ├─[Argon2id + argon2_salt]──→ Master Key (local only, never transmitted) │ └──→ encrypt Ed25519 private key │ └──→ wrap VaultKey (solo vaults) │ └─[Argon2id + srp_salt]────→ SRP Password Input └──→ SRP Verifier (sent to server) └──→ SRP Login Proof (sent to server) Server stores: argon2_salt, srp_salt, SRP verifier, encrypted private key Server NEVER sees: password, master key, VaultKey, .env plaintext ``` ## 模块 | 模块 | 用途 | 状态 | |--------|---------|--------| | [`kdf`](#kdf) | Argon2id 密钥派生 — 主密钥 + SRP 密码 | ✅ v0.1.0 | | [`vault`](#vault) | AES-256-GCM vault 加密 + XChaCha20 密钥封装 | ✅ v0.1.0 | | [`keypair`](#keypair) | Ed25519 + X25519 密钥对,私钥加密,ECDH vault 共享 | ✅ v0.1.0 | | [`srp`](#srp) | SRP-6a 客户端 — 验证器、临时值、证明、验证 | ✅ v0.1.0 | | [`zeroize`](#zeroize) | 用于堆分配秘密的安全内存类型 | ✅ v0.1.0 | | [`errors`](#errors) | `CryptoError` — 所有失败变体 | ✅ v0.1.0 | ## 安装 此 crate 是 evnx 项目的内部库。作为 Git 依赖项添加: ``` [dependencies] evnx-crypto = { git = "https://github.com/urwithajit9/evnx-crypto", tag = "v0.1.0" } ``` 用于与 evnx-server 或 evnx CLI 并行本地开发: ``` [dependencies] evnx-crypto = { path = "../evnx-crypto" } ``` ## 快速开始 ### 注册 (客户端) ``` use evnx_crypto::{ kdf::{generate_salt, derive_master_key, derive_srp_password}, keypair::{generate_keypair, encrypt_private_key}, srp::compute_verifier, }; // 1. Generate two independent salts — NEVER share or reuse let argon2_salt = generate_salt(); // for master key derivation let srp_salt = generate_salt(); // for SRP authentication // 2. Derive master key — LOCAL ONLY, never transmitted let master_key = derive_master_key(password.as_bytes(), &argon2_salt)?; // 3. Generate asymmetric keypair let keypair = generate_keypair(); // 4. Encrypt private key with master key (safe to store on server) let enc_private_key = encrypt_private_key(&keypair, &master_key)?; // 5. Derive SRP verifier (password-equivalent stored on server) let srp_password = derive_srp_password(password.as_bytes(), &srp_salt)?; let verifier = compute_verifier(srp_password, srp_salt)?; // 6. Send to server (master_key and private key NEVER leave the client): // { email, verifier, srp_salt, argon2_salt, // keypair.ed25519_public, keypair.x25519_public, enc_private_key } ``` ### 登录 (客户端) ``` use evnx_crypto::{ kdf::{derive_master_key, derive_srp_password}, keypair::decrypt_private_key, srp::{generate_client_ephemeral, compute_client_proof, verify_server_proof}, }; // After server returns { argon2_salt, srp_salt, enc_private_key }: // 1. Re-derive master key and decrypt private key let master_key = derive_master_key(password.as_bytes(), &argon2_salt)?; let keypair = decrypt_private_key(&enc_private_key, &master_key)?; // 2. SRP Step 1 — generate ephemeral, send A to server let eph = generate_client_ephemeral()?; // POST /auth/srp/init { email, client_public: eph.public_a } // server responds with { server_public: B, srp_salt, session_id } // 3. SRP Step 2 — compute proof, send M1 to server let srp_password = derive_srp_password(password.as_bytes(), &srp_salt)?; let proof = compute_client_proof(&email, srp_password, &srp_salt, &server_b, &eph)?; // POST /auth/srp/verify { session_id, client_proof: proof.client_proof } // server responds with { server_proof: M2 } // 4. SRP Step 3 — verify M2 (mutual authentication) verify_server_proof(&server_m2, &proof)?; // If Ok(()), the server is legitimate. Proceed with session. ``` ### 推送 (加密 .env) ``` use evnx_crypto::{ vault::{encrypt_vault}, keypair::unwrap_vault_key, }; // After fetching { encrypted_vault_key, eph_pub_key } from server: let vault_key = unwrap_vault_key(&wrapped, keypair.x25519_private_bytes())?; let blob = encrypt_vault(env_file_bytes, &vault_key)?; // Send { nonce: blob.nonce, ciphertext: blob.ciphertext } to server for S3 upload ``` ### 拉取 (解密 .env) ``` use evnx_crypto::{ vault::decrypt_vault, keypair::unwrap_vault_key, }; // After fetching { nonce, ciphertext } from server: let vault_key = unwrap_vault_key(&wrapped, keypair.x25519_private_bytes())?; let plaintext = decrypt_vault(&blob, &vault_key)?; // Write plaintext to .env ``` ### 与协作者共享 vault ``` use evnx_crypto::keypair::{unwrap_vault_key, wrap_vault_key_for_user}; // After fetching collaborator's x25519_public from server: let my_vault_key = unwrap_vault_key(&my_wrapped, keypair.x25519_private_bytes())?; let wrapped_for_them = wrap_vault_key_for_user(&my_vault_key, &collab_x25519_pub)?; // POST /vaults/{id}/members { encrypted_vault_key, eph_pub_key } ``` ## 模块参考 ### `kdf` {#kdf} 基于 Argon2id 的密钥派生。所有派生均在客户端进行。 ``` // Generate a random 32-byte salt (call separately for argon2 and SRP salts) pub fn generate_salt() -> [u8; 32]; // Derive 256-bit master key from password + salt // Returns MasterKey (ZeroizeOnDrop) pub fn derive_master_key(password: &[u8], salt: &[u8; 32]) -> Result; // Derive SRP password input (different Argon2id call, different salt) // Returns Zeroizing> pub fn derive_srp_password(password: &[u8], srp_salt: &[u8; 32]) -> Result>, CryptoError>; ``` **参数 (OWASP 2024 默认值):** m=64MB, t=3 次迭代, p=4 个线程。请根据您的硬件在 `src/kdf.rs` 中进行调整。目标:300–500ms。 ### `vault` {#vault} 用于 .env 加密的 AES-256-GCM。用于密钥封装的 XChaCha20-Poly1305。 ``` // Generate a fresh random 256-bit vault key pub fn VaultKey::generate() -> VaultKey; // Encrypt plaintext → EncryptedBlob { nonce, ciphertext } // Fresh random nonce per call. GCM auth tag embedded in ciphertext. pub fn encrypt_vault(plaintext: &[u8], key: &VaultKey) -> Result; // Decrypt EncryptedBlob → plaintext // Returns Err(Decryption) on wrong key or tampered ciphertext. Never panics. pub fn decrypt_vault(blob: &EncryptedBlob, key: &VaultKey) -> Result, CryptoError>; // Wrap VaultKey with user's MasterKey (solo vaults / backup) pub fn wrap_vault_key_with_master_key(vk: &VaultKey, mk: &MasterKey) -> Result, CryptoError>; pub fn unwrap_vault_key_with_master_key(wrapped: &[u8], mk: &MasterKey) -> Result; ``` ### `keypair` {#keypair} Ed25519 (签名) + X25519 (密钥协商) 密钥对管理。 ``` // Generate a fresh Ed25519 + X25519 keypair // X25519 private key is deterministically derived from Ed25519 seed via HKDF. // One EncryptedPrivateKey covers both. pub fn generate_keypair() -> UserKeypair; // Encrypt Ed25519 seed with MasterKey (XChaCha20-Poly1305) pub fn encrypt_private_key(keypair: &UserKeypair, mk: &MasterKey) -> Result; // Decrypt and reconstruct full keypair from EncryptedPrivateKey pub fn decrypt_private_key(enc: &EncryptedPrivateKey, mk: &MasterKey) -> Result; // ECDH wrap: encrypt VaultKey for a specific recipient // Uses ephemeral X25519 + HKDF-SHA256 + XChaCha20-Poly1305 // Ephemeral private key is zeroized immediately after ECDH. pub fn wrap_vault_key_for_user(vk: &VaultKey, recipient_pub: &X25519PublicKeyBytes) -> Result; // ECDH unwrap: decrypt VaultKey using your X25519 private key pub fn unwrap_vault_key(wrapped: &WrappedVaultKey, my_x25519_private: &[u8; 32]) -> Result; ``` ### `srp` {#srp} SRP-6a 客户端 — RFC 5054,使用 2048 位群和 SHA-256。 ``` // Registration: compute SRP verifier from Argon2id-derived SRP password // srp_password_bytes = output of derive_srp_password(), NOT raw password pub fn compute_verifier(srp_password: Zeroizing>, srp_salt: [u8; 32]) -> Result; // Login Step 1: generate ephemeral A (send public_a to server) pub fn generate_client_ephemeral() -> Result; // Login Step 2: compute client proof M1 (send client_proof to server) // Keep session_key to verify M2 in Step 3. pub fn compute_client_proof( email: &str, srp_password: Zeroizing>, srp_salt: &[u8], server_public_b: &[u8], ephemeral: &SrpClientEphemeral, ) -> Result; // Login Step 3: verify server proof M2 (mutual authentication) // Returns Err if server cannot prove it knows the verifier. pub fn verify_server_proof(server_m2: &[u8], proof: &SrpClientProof) -> Result<(), CryptoError>; ``` ### `zeroize` {#zeroize} 用于堆分配秘密的安全内存封装器。 ``` // Variable-length heap secret pub struct SecretBytes(Zeroizing>); // Password input before conversion to bytes pub struct SecretString(Zeroizing); // Fixed-size stack secret with ZeroizeOnDrop pub struct SecretArray([u8; N]); // Explicit zeroize of a mutable byte slice pub fn zeroize_slice(buf: &mut [u8]); ``` 所有类型实现的 `Debug` 和 `Display` 均显示为 `[REDACTED]` —— 可以安全地记录日志。 ### `errors` {#errors} ``` pub enum CryptoError { Kdf(String), // Argon2 derivation failure Encryption, // AES-GCM encrypt failed Decryption, // AES-GCM decrypt failed (wrong key or tampered) KeyWrap, // XChaCha20 wrap failed KeyUnwrap, // XChaCha20 unwrap failed (wrong key or tampered) InvalidInput(String), // Malformed input (e.g., slice too short) Srp(String), // SRP protocol error } ``` 所有库函数均返回 `Result`。库代码中无 panic。 ## 安全属性 | 保证 | 实现方式 | |-----------|-----| | 密码从不传输 | 所有派生在任何网络调用之前均在客户端进行 | | 主密钥从不存储 | 每次会话从密码重新派生,ZeroizeOnDrop | | SRP 验证器泄露 ≠ vault 访问权限 | argon2_salt 和 srp_salt 是独立的;不同的 Argon2id 调用 | | AES-GCM nonce 永不重复 | 每次 `encrypt_vault()` 调用使用全新的 OsRng nonce | | 篡改检测 | AES-GCM 和 XChaCha20-Poly1305 认证标签;失败时返回 `Err(Decryption)` | | ECDH 临时密钥擦除 | `EphemeralSecret` (x25519-dalek) 在 drop 时自动擦除 | | 密钥材料从堆中擦除 | `MasterKey`, `VaultKey`, `UserKeypair`, 证明类型上的 `ZeroizeOnDrop` | | 不跨域重用密钥 | HKDF 针对每个操作使用不同的 `info` 字符串 | ## 运行测试 ``` # 所有测试 cargo test # 特定模块 cargo test --test kdf cargo test --test vault cargo test --test keypair cargo test --test srp cargo test --test zeroize cargo test --test integration # 详细输出(查看计时、println! 输出) cargo test --test integration -- --nocapture # 更多 proptest 迭代(慢但彻底) PROPTEST_CASES=1000 cargo test # KDF 计时基准 cargo test --test kdf benchmark_derive_master_key -- --ignored --nocapture # 安全审计 cargo audit # Lint cargo clippy -- -D warnings ``` ## 密码学选择 | 决策 | 选择 | 理由 | |----------|--------|-----------| | KDF | Argon2id | PHC 获胜者;内存困难;`id` 变体可抵抗侧信道和 GPU 攻击 | | Vault 加密 | AES-256-GCM | FIPS 140-2;x86/ARM 上的硬件加速;AEAD | | 密钥封装 | XChaCha20-Poly1305 | 192 位 nonce 可安全用于大规模随机生成;无硬件要求 | | 非对称签名 | Ed25519 | 快速、密钥/签名小、恒定时间、没有容易出错的参数选择 | | 密钥协商 | X25519 | RFC 7748;恒定时间;与 Ed25519 天然配对 (同曲线族) | | 用于 ECDH 的 KDF | HKDF-SHA256 | 标准;通过 `info` 参数进行域分离 | | SRP 群 | RFC 5054 G_2048 | 2048 位 DH 群;兼顾安全性和性能 | | SRP 哈希 | SHA-256 | 抗碰撞性;标准 | ## 本库不执行的操作 - **网络 I/O** — 无 HTTP、无 TLS、无套接字操作 - **文件 I/O** — 不读取或写入 `.env` 文件 - **服务端 SRP** — 仅客户端;`evnx-server` 实现服务端 - **密钥存储** — 不管理 OS 钥匙串或配置文件 - **Token 生成** — JWT 和 API Token 逻辑位于 `evnx-server` ## 许可证 根据您的选择,许可于 MIT 或 Apache-2.0。 ## 相关仓库 | 仓库 | 角色 | |------|------| | [`evnx`](https://github.com/urwithajit9/evnx) | CLI —— 消费此库以进行本地加密 | | [`evnx-server`](https://github.com/urwithajit9/evnx-server) | 后端 —— 消费此库以获取 KDF 参数和错误类型 | ## 🔐 密钥派生函数 (KDF) KDF 模块提供安全的、基于 Argon2id 的密钥派生,用于密码到密钥的转换,实施了零知识加密的行业最佳实践。 ### 概述 我们的 KDF 实现使用 **Argon2id** (Password Hashing Competition 获胜者),其参数符合 **OWASP 2024 建议**。它服务于两个关键的、隔离的目的: 1. **主密钥派生**:派生一个 32 字节的加密密钥,用于保护 Ed25519 私钥 2. **SRP 密码派生**:为安全远程密码 (SRP) 认证生成单独的输入 ### 🔑 安全架构 ``` graph TB subgraph Client["🔒 Client-Side (Never Transmitted)"] P[User Password] AS[Argon2 Salt
32 bytes] SS[SRP Salt
32 bytes] subgraph KDF["Key Derivation Functions"] MDK[derive_master_key
Argon2id] DSP[derive_srp_password
Argon2id] end MK[Master Key
32 bytes] SP[SRP Password
32 bytes] subgraph Crypto["Cryptographic Operations"] ENC[Encrypt Ed25519
Private Key] SRP[Generate SRP
Verifier/Proof] end end subgraph Server["🖥️ Server Storage"] AS_STORE[Argon2 Salt
Stored] SS_STORE[SRP Salt
Stored] V[SRP Verifier
Stored] ENC_KEY[Encrypted
Ed25519 Key] end P --> MDK P --> DSP AS --> MDK SS --> DSP MDK --> MK DSP --> SP MK --> ENC SP --> SRP AS -.-> AS_STORE SS -.-> SS_STORE ENC --> ENC_KEY SRP --> V ENC_KEY -.-> Server V -.-> Server AS_STORE -.-> Client SS_STORE -.-> Client style MK fill:#f96,stroke:#333,stroke-width:3px style SP fill:#f96,stroke:#333,stroke-width:2px style P fill:#bbf,stroke:#333,stroke-width:2px style KDF fill:#ffebcd,stroke:#333,stroke-width:2px ``` ### 🔒 安全保证 | 特性 | 实现 | 益处 | |---------|----------------|---------| | **算法** | Argon2id v0x13 | 内存困难,抵抗 GPU/ASIC 攻击 | | **内存开销** | 64 MB (可配置) | 防止并行暴力破解攻击 | | **时间开销** | 3 次迭代 | 增加每次猜测的计算成本 | | **并行度** | 4 个线程 | 针对现代 CPU 优化 | | **输出长度** | 32 字节 (256 位) | 足以用于对称加密 | | **盐值唯一性** | 每用户 32 字节随机值 | 防止彩虹表攻击 | | **密钥分离** | 主密钥/SRP 使用独立盐值 | 秘密隔离 | | **归零化** | 自动内存清除 | 防止密钥材料泄露 | ### 🛡️ 威胁模型防护 ``` graph LR subgraph Threats["🎯 Attack Vectors"] T1[Dictionary Attack] T2[Rainbow Tables] T3[GPU/ASIC Cracking] T4[Side-Channel] T5[Memory Dump] T6[Verifier Compromise] end subgraph Defenses["🛡️ Mitigations"] D1[High iteration count
3 rounds] D2[Unique random salts
32 bytes each] D3[Memory-hard function
64 MB RAM] D4[Constant-time ops
Argon2 library] D5[ZeroizeOnDrop
Secure memory] D6[Separate derivation
paths] end T1 --> D1 T2 --> D2 T3 --> D3 T4 --> D4 T5 --> D5 T6 --> D6 style Defenses fill:#d4edda,stroke:#28a745,stroke-width:2px style Threats fill:#f8d7da,stroke:#dc3545,stroke-width:2px ``` ### 📊 性能特征 **目标硬件:** 现代服务器 (Intel Xeon / AMD EPYC) **目标派生时间:** 300–500ms | 参数 | 值 | 影响 | |-----------|-------|--------| | 内存 (m_cost) | 64 MB | 每次派生的 RAM 需求 | | 迭代 (t_cost) | 3 | CPU 时间乘数 | | 并行度 (p_cost) | 4 | 线程利用率 | | **实际时间** (Mac Mini M1) | ~1175ms | ⚠️ 可能需要调整 | ### 🔄 密钥派生流程 #### 注册流程 ``` sequenceDiagram participant U as User participant C as Client App participant S as Server U->>C: Enter password C->>C: Generate argon2_salt (random) C->>C: Generate srp_salt (random) C->>C: derive_master_key(password, argon2_salt) C->>C: Generate Ed25519 keypair C->>C: Encrypt private key with master_key C->>C: derive_srp_password(password, srp_salt) C->>C: Generate SRP verifier C->>S: Register(argon2_salt, srp_salt, verifier, encrypted_key) S->>S: Store salts + verifier S-->>C: Registration successful Note over C: Password & master_key
NEVER leave client Note over U: Only encrypted data
sent to server ``` #### 登录流程 ``` sequenceDiagram participant U as User participant C as Client App participant S as Server U->>C: Enter password C->>S: Request login (username) S->>S: Lookup user S-->>C: Return argon2_salt, srp_salt C->>C: derive_master_key(password, argon2_salt) C->>C: Fetch encrypted private key C->>C: Decrypt with master_key C->>C: derive_srp_password(password, srp_salt) C->>C: Generate SRP proof C->>S: SRP proof S->>S: Verify proof S-->>C: Authenticated! Note over C: Master key derived
locally each time Note over U: Password never
transmitted ``` ### 🔧 配置 编辑 `src/kdf.rs` 以调整参数: ``` // OWASP 2024 minimums - ADJUST FOR YOUR HARDWARE const ARGON2_M_COST: u32 = 65536; // Memory: 64 MB const ARGON2_T_COST: u32 = 3; // Iterations: 3 const ARGON2_P_COST: u32 = 4; // Parallelism: 4 lanes // Target: 300-500ms derivation time on production server ``` **调整指南:** - **增加 `M_COST`** 如果服务器有剩余 RAM (更安全) - **增加 `T_COST`** 如果 CPU 快但 RAM 有限 - **减少两者** 如果派生对于用户体验来说太慢 (安全性降低) - **运行基准测试:** `cargo test --test kdf benchmark_derive_master_key -- --ignored` ### 🧪 测试 ``` # 运行所有 KDF 测试 cargo test --test kdf # 运行并输出计时 cargo test --test kdf -- --nocapture # 运行 benchmark(默认忽略) cargo test --test kdf benchmark_derive_master_key -- --ignored --nocapture # 测试所有模块 cargo test ``` ### 📦 依赖项 ``` [dependencies] argon2 = "0.5" # Argon2 implementation rand = "0.8" # Cryptographic RNG zeroize = { version = "1.7", features = ["derive"] } # Secure memory ``` ### 🔍 安全审计清单 - ✅ Argon2id (不是 Argon2i 或 Argon2d) - ✅ 每个用户每个目的使用唯一的盐值 - ✅ 不向服务器传输密码 - ✅ 主密钥永不离开客户端 - ✅ 敏感内存的归零化 - ✅ 恒定时间比较 (通过 Argon2 库) - ✅ 密码学安全的 RNG (OsRng) - ✅ MasterKey 结构体上无 Copy/Clone # 🔐 Vault 加密模块 ## 概述 `vault` 模块为敏感配置数据提供认证加密。它专为以下目的设计: - ✅ 在存储或传输前加密 `.env` 文件 - ✅ 使用用户主密钥封装 vault 密钥以进行安全的密钥管理 - ✅ 通过 AEAD (关联数据的认证加密) 确保完整性 - ✅ 在 drop 时从内存中归零化敏感密钥 ## 密码学原语 | 组成部分 | 算法 | 用途 | |-----------|-----------|---------| | **数据加密** | AES-256-GCM | 加密 `.env` 明文并附带认证标签 | | **密钥封装** | XChaCha20-Poly1305 | 使用用户 `MasterKey` 封装 `VaultKey` | | **KDF** | Argon2id (通过 `kdf` 模块) | 从用户密码派生 `MasterKey` | | **随机性** | `rand::OsRng` | 密码学安全的 nonce/密钥生成 | | **内存安全** | `zeroize::ZeroizeOnDrop` | 在 drop 时安全擦除密钥 | ## 常量 ``` pub const VAULT_KEY_LEN: usize = 32; // 256-bit key pub const NONCE_LEN: usize = 12; // 96-bit nonce for AES-GCM pub const XCHACHA_NONCE_LEN: usize = 24; // 192-bit nonce for XChaCha20 ``` ## 核心类型 ### `VaultKey` ``` #[derive(ZeroizeOnDrop)] pub struct VaultKey(pub [u8; VAULT_KEY_LEN]); ``` 用于加密 vault 数据的 32 字节对称密钥。在 drop 时自动归零化。 #### 方法 ``` impl VaultKey { /// Generate a new cryptographically random vault key pub fn generate() -> Self; } ``` ### `EncryptedBlob` ``` pub struct EncryptedBlob { pub nonce: [u8; NONCE_LEN], // 12-byte nonce for AES-GCM pub ciphertext: Vec, // Ciphertext + 16-byte GCM auth tag } ``` 表示加密后的 `.env` 文件。nonce 以明文存储;认证标签附加在密文后。 ## 公共 API ### 加密/解密 `.env` 数据 ``` /// Encrypt plaintext with VaultKey using AES-256-GCM pub fn encrypt_vault( plaintext: &[u8], vault_key: &VaultKey ) -> Result; /// Decrypt EncryptedBlob with VaultKey pub fn decrypt_vault( blob: &EncryptedBlob, vault_key: &VaultKey ) -> Result, CryptoError>; ``` #### 示例 ``` use evnx_crypto::vault::{VaultKey, encrypt_vault, decrypt_vault}; let vault_key = VaultKey::generate(); let env_content = b"DATABASE_URL=postgres://user:pass@localhost/db\n"; // Encrypt let blob = encrypt_vault(env_content, &vault_key)?; // Store blob.nonce and blob.ciphertext securely... // Later, decrypt let decrypted = decrypt_vault(&blob, &vault_key)?; assert_eq!(env_content, &decrypted[..]); ``` ### 封装/解封 Vault 密钥 ``` /// Wrap VaultKey with user's MasterKey using XChaCha20-Poly1305 /// Returns: [24-byte nonce || ciphertext] pub fn wrap_vault_key_with_master_key( vault_key: &VaultKey, master_key: &MasterKey ) -> Result, CryptoError>; /// Unwrap VaultKey from wrapped blob pub fn unwrap_vault_key_with_master_key( wrapped: &[u8], master_key: &MasterKey ) -> Result; ``` #### 示例 ``` use evnx_crypto::vault::{VaultKey, wrap_vault_key_with_master_key, unwrap_vault_key_with_master_key}; use evnx_crypto::kdf::MasterKey; let master_key = MasterKey::derive_from_password("user_password", b"salt123")?; let vault_key = VaultKey::generate(); // Wrap for storage let wrapped = wrap_vault_key_with_master_key(&vault_key, &master_key)?; // Store `wrapped` in database (e.g., vault_members row) // Later, unwrap let unwrapped = unwrap_vault_key_with_master_key(&wrapped, &master_key)?; assert_eq!(vault_key.0, unwrapped.0); ``` ## 错误处理 所有函数返回 `Result`: ``` pub enum CryptoError { Encryption, // AES-GCM encryption failure Decryption, // Auth tag mismatch, wrong key, or tampering KeyWrap, // XChaCha20-Poly1305 wrap failure KeyUnwrap, // Auth failure or invalid input during unwrap InvalidInput(String), // Malformed input (e.g., too short) KdfError(String), // Argon2id derivation failure } ``` ✅ **绝不 panic** —— 所有错误均被优雅处理。 ## 安全属性 | 属性 | 保证 | |----------|-----------| | **机密性** | AES-256-GCM / XChaCha20-Poly1305 提供 IND-CCA2 安全性 | | **完整性** | AEAD 认证标签检测任何篡改 (1 位改变 → 解密失败) | | **Nonce 安全** | 每次加密调用生成全新的随机 nonce (OsRng) | | **密钥隔离** | `VaultKey` 和 `MasterKey` 是独立的;泄露其中一个 ≠ 泄露另一个 | | **内存安全** | `ZeroizeOnDrop` 确保密钥在 drop 时从堆/栈中擦除 | | **无密钥重用** | 每次 `encrypt_vault()` 调使用唯一的 nonce;永不重用 (密钥, nonce) 对 | ## ⚠️ 关键警告 ``` // ❌ NEVER reuse a nonce with the same key // ❌ NEVER store VaultKey in plaintext // ❌ NEVER log ciphertext, keys, or passwords // ❌ NEVER use static/test keys in production // ✅ ALWAYS generate fresh VaultKey per vault // ✅ ALWAYS wrap VaultKey with user's MasterKey before storage // ✅ ALWAYS verify decryption errors before proceeding // ✅ ALWAYS use constant-time comparison for passwords (handled by Argon2) ``` ## 测试 运行测试套件: ``` # 所有 vault 测试 cargo test --test vault # 带详细输出 cargo test --test vault -- --nocapture # 仅基于属性的测试 cargo test --test vault proptest # 增加 CI 的 proptest 迭代 PROPTEST_CASES=1000 cargo test --test vault ``` ### 测试覆盖范围 - ✅ 往返加密/解密 (空、小、大、二进制数据) - ✅ nonce 唯一性 (100+ 次加密 → 所有 nonce 均不相同) - ✅ 错误密钥 → `Err(Decryption)`,而非 panic - ✅ 篡改 (密文、nonce、截断、追加) → 认证失败 - ✅ 密钥封装往返 + 跨密钥失败 - ✅ 边缘情况:全零、全一、空密文 - ✅ 完整工作流:加密 → 封装 → 解封 → 解密 ## 依赖项 ``` # Cargo.toml [dependencies] aes-gcm = "0.10" chacha20poly1305 = "0.10" rand = "0.8" zeroize = { version = "1.7", features = ["derive"] } [dev-dependencies] proptest = "1.4" ``` ## 另见 - [`src/kdf.rs`](../kdf.rs) — 用于 `MasterKey` 的 Argon2id 密钥派生 - [`src/errors.rs`](../errors.rs) — `CryptoError` 定义 - [`tests/vault.rs`](../../tests/vault.rs) — 综合测试套件
标签:AES-256-GCM, Argon2id, ECDH, Ed25519, Rust, SRP-6a, X25519, XChaCha20, ZKE, 内存安全, 加密库, 可视化界面, 安全管理, 安全远程密码协议, 密码学, 密钥封装, 密钥派生, 开发库, 手动系统调用, 抗暴力破解, 环境变量管理, 端到端加密, 网络安全, 网络流量审计, 通知系统, 隐私保护, 零知识加密, 零知识证明