dpp_crypto/keystore/
migration.rs1use aes_gcm::{
2 Aes256Gcm, Nonce,
3 aead::{Aead, KeyInit, OsRng},
4};
5use anyhow::{Context, Result};
6use rand::RngCore;
7use zeroize::Zeroize;
8
9use super::{KeyRecord, KeyStore, derive_aes_key_argon2};
10
11impl KeyStore {
12 pub fn open_and_migrate(path: impl AsRef<std::path::Path>, passphrase: &str) -> Result<Self> {
19 let store = Self::open(path.as_ref(), passphrase)?;
20 if store.migrate_if_needed(passphrase)? {
21 Self::open(path, passphrase)
23 } else {
24 Ok(store)
25 }
26 }
27
28 pub fn migrate_if_needed(&self, passphrase: &str) -> Result<bool> {
36 let needs = *self.needs_migration.read().expect("lock");
37 if !needs {
38 return Ok(false);
39 }
40
41 tracing::info!("migrating key store from SHA-256 to Argon2id KDF");
42
43 let new_key = derive_aes_key_argon2(passphrase, &self.salt)?;
45 let new_cipher = Aes256Gcm::new(&new_key);
46
47 let mut map = self.records.write().expect("key store write lock");
48 let mut migrated = super::KeyRecordMap::with_capacity(map.len());
49
50 for (id, record) in map.iter() {
51 let nonce = Nonce::from_slice(&record.nonce);
53 let mut raw = self
54 .cipher
55 .decrypt(nonce, record.encrypted_signing_key.as_ref())
56 .map_err(|_| {
57 anyhow::anyhow!("AES-GCM decrypt failed during migration for key {id}")
58 })?;
59
60 let mut nonce_bytes = [0u8; 12];
62 OsRng.fill_bytes(&mut nonce_bytes);
63 let new_nonce = Nonce::from_slice(&nonce_bytes);
64 let encrypted = new_cipher
65 .encrypt(new_nonce, raw.as_ref())
66 .map_err(|_| anyhow::anyhow!("AES-GCM encrypt failed during migration"))?;
67 raw.zeroize();
68
69 migrated.insert(
70 id.clone(),
71 KeyRecord {
72 encrypted_signing_key: encrypted,
73 nonce: nonce_bytes.to_vec(),
74 fingerprint: record.fingerprint.clone(),
75 verifying_key_hex: record.verifying_key_hex.clone(),
76 revoked: record.revoked,
77 algorithm: record.algorithm.clone(),
78 },
79 );
80 }
81
82 *map = migrated;
83 self.persist_envelope(&map)
84 .context("Failed to persist migrated key store")?;
85
86 drop(map);
87 *self.needs_migration.write().expect("lock") = false;
88
89 tracing::info!("key store migrated from SHA-256 to Argon2id");
92 Ok(true)
93 }
94}