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