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};
12use ed25519_dalek::SigningKey;
13
14fn archived_key_name(key_id: &str) -> String {
24 format!("{key_id}#archived-{}", uuid::Uuid::now_v7())
25}
26
27impl KeyStore {
28 pub fn archive_key(&self, key_id: &str) -> Result<()> {
31 if *self.needs_migration.read().expect("lock") {
32 anyhow::bail!(
33 "key store requires KDF migration before writes are allowed — \
34 call migrate_if_needed() first"
35 );
36 }
37 let mut map = self.records.write().expect("key store write lock poisoned");
38 if let Some(record) = map.get(key_id) {
39 let archived = record.clone();
40 let archive_key = archived_key_name(key_id);
41 map.insert(archive_key, archived);
42 self.persist_envelope(&map)?;
43 }
44 Ok(())
45 }
46
47 pub fn rotate_key(&self, key_id: &str) -> Result<KeyEntry> {
55 self.rotate_inner(key_id, false)
56 }
57
58 pub fn revoke_and_rotate(&self, key_id: &str) -> Result<KeyEntry> {
64 self.rotate_inner(key_id, true)
65 }
66
67 fn rotate_inner(&self, key_id: &str, revoke_old: bool) -> Result<KeyEntry> {
68 if *self.needs_migration.read().expect("lock") {
69 anyhow::bail!(
70 "key store requires KDF migration before writes are allowed — \
71 call migrate_if_needed() first"
72 );
73 }
74
75 let signing_key = SigningKey::generate(&mut crate::os_rng());
78 let verifying_key = signing_key.verifying_key();
79 let fingerprint = hex::encode(Sha256::digest(verifying_key.as_bytes()));
80 let verifying_key_hex = hex::encode(verifying_key.as_bytes());
81 let mut nonce_bytes = [0u8; 12];
82 crate::os_rng().fill_bytes(&mut nonce_bytes);
83 let nonce = <&Nonce<U12>>::from(&nonce_bytes);
84 let mut raw = signing_key.to_bytes();
85 let encrypted = self
86 .cipher
87 .encrypt(nonce, raw.as_ref())
88 .map_err(|_| anyhow::anyhow!("AES-GCM encrypt failed"))?;
89 raw.zeroize();
90 let new_record = KeyRecord::new(
91 encrypted,
92 nonce_bytes.to_vec(),
93 fingerprint.clone(),
94 verifying_key_hex,
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 archive_name = archived_key_name(key_id);
103 let archived = KeyRecord {
104 revoked: revoke_old,
105 ..record.clone()
106 };
107 map.insert(archive_name, archived);
108 }
109 map.insert(key_id.to_owned(), new_record);
110 self.persist_envelope(&map)?;
111 }
112
113 Ok(KeyEntry {
114 signing_key,
115 verifying_key,
116 fingerprint,
117 revoked: false,
118 algorithm: super::store::default_algorithm(),
119 })
120 }
121}