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