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 =
54 <&Nonce<U12>>::try_from(record.nonce.as_slice()).expect("nonce must be 12 bytes");
55 let mut raw = self
56 .cipher
57 .decrypt(nonce, record.encrypted_signing_key.as_ref())
58 .map_err(|_| {
59 anyhow::anyhow!("AES-GCM decrypt failed during migration for key {id}")
60 })?;
61
62 let mut nonce_bytes = [0u8; 12];
64 crate::os_rng().fill_bytes(&mut nonce_bytes);
65 let new_nonce = <&Nonce<U12>>::from(&nonce_bytes);
66 let encrypted = new_cipher
67 .encrypt(new_nonce, raw.as_ref())
68 .map_err(|_| anyhow::anyhow!("AES-GCM encrypt failed during migration"))?;
69 raw.zeroize();
70
71 migrated.insert(
72 id.clone(),
73 KeyRecord {
74 encrypted_signing_key: encrypted,
75 nonce: nonce_bytes.to_vec(),
76 fingerprint: record.fingerprint.clone(),
77 verifying_key_hex: record.verifying_key_hex.clone(),
78 revoked: record.revoked,
79 algorithm: record.algorithm.clone(),
80 },
81 );
82 }
83
84 *map = migrated;
85 self.persist_envelope(&map)
86 .context("Failed to persist migrated key store")?;
87
88 drop(map);
89 *self.needs_migration.write().expect("lock") = false;
90
91 tracing::info!("key store migrated from SHA-256 to Argon2id");
94 Ok(true)
95 }
96}