backup_suite/crypto/
key_management.rs

1//! # キー管理システム
2//!
3//! パスワードからの安全な鍵導出とマスターキー管理を提供します。
4
5use crate::error::{BackupError, Result};
6use argon2::password_hash::rand_core::{OsRng, RngCore};
7use argon2::password_hash::SaltString;
8use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
9use zeroize::{Zeroize, ZeroizeOnDrop};
10
11/// マスターキー(32バイト)
12#[derive(Clone, Zeroize, ZeroizeOnDrop)]
13pub struct MasterKey {
14    key: [u8; 32],
15}
16
17impl MasterKey {
18    /// 新しいマスターキーを生成
19    #[must_use]
20    pub fn generate() -> Self {
21        let mut key = [0u8; 32];
22        OsRng.fill_bytes(&mut key);
23        Self { key }
24    }
25
26    /// バイト配列からマスターキーを作成
27    #[must_use]
28    pub fn from_bytes(bytes: [u8; 32]) -> Self {
29        Self { key: bytes }
30    }
31
32    /// キーのバイト配列を取得
33    #[must_use]
34    pub fn as_bytes(&self) -> &[u8; 32] {
35        &self.key
36    }
37}
38
39/// キー導出設定
40#[derive(Debug, Clone)]
41pub struct KeyDerivationConfig {
42    /// メモリ使用量(KB)
43    pub memory_cost: u32,
44    /// 反復回数
45    pub time_cost: u32,
46    /// 並列度
47    pub parallelism: u32,
48}
49
50impl Default for KeyDerivationConfig {
51    fn default() -> Self {
52        Self {
53            memory_cost: 131_072, // 128MB(OWASP推奨)
54            time_cost: 4,         // 4回反復(OWASP推奨)
55            parallelism: 2,       // 並列度2(セキュリティと性能のバランス)
56        }
57    }
58}
59
60/// キー導出エンジン
61pub struct KeyDerivation {
62    config: KeyDerivationConfig,
63}
64
65impl KeyDerivation {
66    /// 新しいキー導出エンジンを作成
67    #[must_use]
68    pub fn new(config: KeyDerivationConfig) -> Self {
69        Self { config }
70    }
71
72    /// パスワードからマスターキーを導出
73    ///
74    /// # Errors
75    ///
76    /// 以下の場合にエラーを返します:
77    /// - Argon2パラメータが無効な場合 (`BackupError::EncryptionError`)
78    /// - ソルトのBase64エンコードに失敗した場合 (`BackupError::EncryptionError`)
79    /// - パスワードハッシュ生成に失敗した場合 (`BackupError::EncryptionError`)
80    /// - ハッシュの生成に失敗した場合 (`BackupError::EncryptionError`)
81    /// - 生成されたキーの長さが32バイトでない場合 (`BackupError::EncryptionError`)
82    pub fn derive_key(&self, password: &str, salt: &[u8]) -> Result<MasterKey> {
83        let argon2 = Argon2::new(
84            argon2::Algorithm::Argon2id,
85            argon2::Version::V0x13,
86            argon2::Params::new(
87                self.config.memory_cost,
88                self.config.time_cost,
89                self.config.parallelism,
90                Some(32),
91            )
92            .map_err(|e| BackupError::EncryptionError(format!("Argon2パラメータエラー: {e}")))?,
93        );
94
95        let salt_string = SaltString::encode_b64(salt)
96            .map_err(|e| BackupError::EncryptionError(format!("Salt エンコードエラー: {e}")))?;
97
98        let password_hash = argon2
99            .hash_password(password.as_bytes(), &salt_string)
100            .map_err(|e| BackupError::EncryptionError(format!("パスワードハッシュエラー: {e}")))?;
101
102        let hash = password_hash
103            .hash
104            .ok_or_else(|| BackupError::EncryptionError("ハッシュ生成に失敗".to_string()))?;
105        let hash_bytes = hash.as_bytes();
106
107        if hash_bytes.len() != 32 {
108            return Err(BackupError::EncryptionError("無効なキー長".to_string()));
109        }
110
111        let mut key = [0u8; 32];
112        key.copy_from_slice(hash_bytes);
113        Ok(MasterKey::from_bytes(key))
114    }
115
116    /// ランダムなソルトを生成
117    #[must_use]
118    pub fn generate_salt() -> [u8; 16] {
119        let mut salt = [0u8; 16];
120        OsRng.fill_bytes(&mut salt);
121        salt
122    }
123
124    /// パスワードを検証
125    ///
126    /// # Errors
127    ///
128    /// 以下の場合にエラーを返します:
129    /// - ハッシュ文字列のパースに失敗した場合 (`BackupError::EncryptionError`)
130    ///   - 無効なPHC文字列形式の場合
131    ///   - 破損したハッシュデータの場合
132    pub fn verify_password(&self, password: &str, hash: &str) -> Result<bool> {
133        let parsed_hash = PasswordHash::new(hash)
134            .map_err(|e| BackupError::EncryptionError(format!("ハッシュ解析エラー: {e}")))?;
135
136        let argon2 = Argon2::default();
137        Ok(argon2
138            .verify_password(password.as_bytes(), &parsed_hash)
139            .is_ok())
140    }
141}
142
143impl Default for KeyDerivation {
144    fn default() -> Self {
145        Self::new(KeyDerivationConfig::default())
146    }
147}
148
149/// キーマネージャー
150#[derive(Default)]
151pub struct KeyManager {
152    derivation: KeyDerivation,
153}
154
155impl KeyManager {
156    /// 新しいキーマネージャーを作成
157    #[must_use]
158    pub fn new(config: KeyDerivationConfig) -> Self {
159        Self {
160            derivation: KeyDerivation::new(config),
161        }
162    }
163
164    /// パスワードからマスターキーを生成(新しいソルト付き)
165    ///
166    /// # Errors
167    ///
168    /// 以下の場合にエラーを返します:
169    /// - `derive_key` 関数内で発生するエラー(詳細は `KeyDerivation::derive_key` を参照)
170    ///   - Argon2パラメータエラー
171    ///   - パスワードハッシュ生成エラー
172    ///   - 無効なキー長エラー
173    pub fn create_master_key(&self, password: &str) -> Result<(MasterKey, [u8; 16])> {
174        let salt = KeyDerivation::generate_salt();
175        let key = self.derivation.derive_key(password, &salt)?;
176        Ok((key, salt))
177    }
178
179    /// 既存のソルトでマスターキーを復元
180    ///
181    /// # Errors
182    ///
183    /// 以下の場合にエラーを返します:
184    /// - `derive_key` 関数内で発生するエラー(詳細は `KeyDerivation::derive_key` を参照)
185    ///   - Argon2パラメータエラー
186    ///   - パスワードハッシュ生成エラー
187    ///   - 無効なキー長エラー
188    pub fn restore_master_key(&self, password: &str, salt: &[u8]) -> Result<MasterKey> {
189        self.derivation.derive_key(password, salt)
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196
197    #[test]
198    fn test_master_key_generation() {
199        let key1 = MasterKey::generate();
200        let key2 = MasterKey::generate();
201
202        // キーは異なる値になる
203        assert_ne!(key1.as_bytes(), key2.as_bytes());
204
205        // キー長は32バイト
206        assert_eq!(key1.as_bytes().len(), 32);
207    }
208
209    #[test]
210    fn test_key_derivation() {
211        let kd = KeyDerivation::default();
212        let password = "test_password_123";
213        let salt = KeyDerivation::generate_salt();
214
215        let key1 = kd.derive_key(password, &salt).unwrap();
216        let key2 = kd.derive_key(password, &salt).unwrap();
217
218        // 同じパスワード・ソルトからは同じキーが生成される
219        assert_eq!(key1.as_bytes(), key2.as_bytes());
220
221        // 異なるソルトからは異なるキーが生成される
222        let salt2 = KeyDerivation::generate_salt();
223        let key3 = kd.derive_key(password, &salt2).unwrap();
224        assert_ne!(key1.as_bytes(), key3.as_bytes());
225    }
226
227    #[test]
228    fn test_key_manager() {
229        let km = KeyManager::default();
230        let password = "secure_password_456";
231
232        let (key1, salt) = km.create_master_key(password).unwrap();
233        let key2 = km.restore_master_key(password, &salt).unwrap();
234
235        // 作成したキーと復元したキーは同じ
236        assert_eq!(key1.as_bytes(), key2.as_bytes());
237    }
238
239    // ========== verify_password 認証テスト(CRITICAL - Mutation Testing対策) ==========
240
241    #[test]
242    fn test_verify_password_with_correct_password() {
243        let kd = KeyDerivation::default();
244        let password = "correct_password_123";
245
246        // パスワードをハッシュ化(Argon2でPHC文字列形式を生成)
247        let salt = KeyDerivation::generate_salt();
248        let argon2 = Argon2::default();
249        let salt_string = SaltString::encode_b64(&salt).unwrap();
250        let password_hash = argon2
251            .hash_password(password.as_bytes(), &salt_string)
252            .unwrap();
253        let hash_str = password_hash.to_string();
254
255        // 正しいパスワードで検証 → true を返すべき
256        let result = kd.verify_password(password, &hash_str).unwrap();
257        assert!(result, "正しいパスワードは true を返すべき");
258    }
259
260    #[test]
261    fn test_verify_password_with_wrong_password() {
262        let kd = KeyDerivation::default();
263        let correct_password = "correct_password_123";
264        let wrong_password = "wrong_password_456";
265
266        // 正しいパスワードでハッシュを生成
267        let salt = KeyDerivation::generate_salt();
268        let argon2 = Argon2::default();
269        let salt_string = SaltString::encode_b64(&salt).unwrap();
270        let password_hash = argon2
271            .hash_password(correct_password.as_bytes(), &salt_string)
272            .unwrap();
273        let hash_str = password_hash.to_string();
274
275        // 間違ったパスワードで検証 → false を返すべき
276        let result = kd.verify_password(wrong_password, &hash_str).unwrap();
277        assert!(!result, "間違ったパスワードは false を返すべき");
278    }
279
280    #[test]
281    fn test_verify_password_edge_cases() {
282        let kd = KeyDerivation::default();
283        let password = "test_password";
284
285        // ハッシュ生成
286        let salt = KeyDerivation::generate_salt();
287        let argon2 = Argon2::default();
288        let salt_string = SaltString::encode_b64(&salt).unwrap();
289        let password_hash = argon2
290            .hash_password(password.as_bytes(), &salt_string)
291            .unwrap();
292        let hash_str = password_hash.to_string();
293
294        // 空パスワード → false
295        let result = kd.verify_password("", &hash_str).unwrap();
296        assert!(!result, "空パスワードは拒否されるべき");
297
298        // 大文字小文字の違い → false(パスワードは大文字小文字を区別)
299        let result = kd.verify_password("TEST_PASSWORD", &hash_str).unwrap();
300        assert!(!result, "大文字小文字の違いは拒否されるべき");
301
302        // 部分一致 → false
303        let result = kd.verify_password("test_pass", &hash_str).unwrap();
304        assert!(!result, "部分一致パスワードは拒否されるべき");
305    }
306
307    #[test]
308    fn test_verify_password_must_return_boolean() {
309        let kd = KeyDerivation::default();
310        let password = "any_password";
311
312        // ハッシュ生成
313        let salt = KeyDerivation::generate_salt();
314        let argon2 = Argon2::default();
315        let salt_string = SaltString::encode_b64(&salt).unwrap();
316        let password_hash = argon2
317            .hash_password(password.as_bytes(), &salt_string)
318            .unwrap();
319        let hash_str = password_hash.to_string();
320
321        // 正しいパスワード → 必ず true
322        let result_correct = kd.verify_password(password, &hash_str).unwrap();
323        assert!(result_correct, "verify_password(correct) must return true");
324
325        // 間違ったパスワード → 必ず false
326        let result_wrong = kd.verify_password("different_password", &hash_str).unwrap();
327        assert!(!result_wrong, "verify_password(wrong) must return false");
328
329        // 両方の結果が異なることを確認(常に同じ値を返す変異を検出)
330        assert_ne!(
331            result_correct, result_wrong,
332            "正しいパスワードと間違ったパスワードで結果が異なるべき"
333        );
334    }
335
336    #[test]
337    fn test_verify_password_invalid_hash_format() {
338        let kd = KeyDerivation::default();
339        let password = "any_password";
340
341        // 無効なハッシュ形式 → エラーを返すべき
342        let result = kd.verify_password(password, "invalid_hash_format");
343        assert!(result.is_err(), "無効なハッシュ形式はエラーを返すべき");
344
345        // 空文字列 → エラーを返すべき
346        let result = kd.verify_password(password, "");
347        assert!(result.is_err(), "空のハッシュ文字列はエラーを返すべき");
348    }
349}