dpp_crypto/keystore/
rotation.rs1use aes_gcm::{
2 Nonce,
3 aead::{Aead, OsRng},
4};
5use anyhow::Result;
6use rand::RngCore;
7use sha2::{Digest, Sha256};
8use zeroize::Zeroize;
9
10use super::{KeyRecord, KeyStore, default_algorithm};
11use ed25519_dalek::SigningKey;
12
13use crate::keystore::KeyEntry;
14
15impl KeyStore {
16 pub fn archive_key(&self, key_id: &str) -> Result<()> {
19 if *self.needs_migration.read().expect("lock") {
20 anyhow::bail!(
21 "key store requires KDF migration before writes are allowed — \
22 call migrate_if_needed() first"
23 );
24 }
25 let mut map = self.records.write().expect("key store write lock poisoned");
26 if let Some(record) = map.get(key_id) {
27 let ts = std::time::SystemTime::now()
28 .duration_since(std::time::UNIX_EPOCH)
29 .unwrap_or_default()
30 .as_nanos();
31 let archive_key = format!("{key_id}#archived-{ts}");
32 let archived = KeyRecord {
33 encrypted_signing_key: record.encrypted_signing_key.clone(),
34 nonce: record.nonce.clone(),
35 fingerprint: record.fingerprint.clone(),
36 verifying_key_hex: record.verifying_key_hex.clone(),
37 revoked: record.revoked,
38 algorithm: record.algorithm.clone(),
39 };
40 map.insert(archive_key, archived);
41 self.persist_envelope(&map)?;
42 }
43 Ok(())
44 }
45
46 pub fn rotate_key(&self, key_id: &str) -> Result<KeyEntry> {
54 self.rotate_inner(key_id, false)
55 }
56
57 pub fn revoke_and_rotate(&self, key_id: &str) -> Result<KeyEntry> {
63 self.rotate_inner(key_id, true)
64 }
65
66 fn rotate_inner(&self, key_id: &str, revoke_old: bool) -> Result<KeyEntry> {
67 if *self.needs_migration.read().expect("lock") {
68 anyhow::bail!(
69 "key store requires KDF migration before writes are allowed — \
70 call migrate_if_needed() first"
71 );
72 }
73
74 let signing_key = SigningKey::generate(&mut OsRng);
77 let verifying_key = signing_key.verifying_key();
78 let fingerprint = hex::encode(Sha256::digest(verifying_key.as_bytes()));
79 let verifying_key_hex = hex::encode(verifying_key.as_bytes());
80 let mut nonce_bytes = [0u8; 12];
81 rand::rngs::OsRng.fill_bytes(&mut nonce_bytes);
82 let nonce = Nonce::from_slice(&nonce_bytes);
83 let mut raw = signing_key.to_bytes();
84 let encrypted = self
85 .cipher
86 .encrypt(nonce, raw.as_ref())
87 .map_err(|_| anyhow::anyhow!("AES-GCM encrypt failed"))?;
88 raw.zeroize();
89 let new_record = KeyRecord {
90 encrypted_signing_key: encrypted,
91 nonce: nonce_bytes.to_vec(),
92 fingerprint: fingerprint.clone(),
93 verifying_key_hex,
94 revoked: false,
95 algorithm: default_algorithm(),
96 };
97
98 {
99 let mut map = self.records.write().expect("key store write lock poisoned");
100 if let Some(record) = map.get(key_id) {
103 let ts = std::time::SystemTime::now()
104 .duration_since(std::time::UNIX_EPOCH)
105 .unwrap_or_default()
106 .as_nanos();
107 let archive_name = format!("{key_id}#archived-{ts}");
108 let archived = KeyRecord {
109 encrypted_signing_key: record.encrypted_signing_key.clone(),
110 nonce: record.nonce.clone(),
111 fingerprint: record.fingerprint.clone(),
112 verifying_key_hex: record.verifying_key_hex.clone(),
113 revoked: revoke_old,
114 algorithm: record.algorithm.clone(),
115 };
116 map.insert(archive_name, archived);
117 }
118 map.insert(key_id.to_owned(), new_record);
119 self.persist_envelope(&map)?;
120 }
121
122 Ok(KeyEntry {
123 signing_key,
124 verifying_key,
125 fingerprint,
126 revoked: false,
127 })
128 }
129}