Skip to main content

hashiverse_lib/client/key_locker/
mem_key_locker.rs

1//! # In-memory key locker
2//!
3//! Reference implementation of [`crate::client::key_locker::key_locker::KeyLocker`] and
4//! [`crate::client::key_locker::key_locker::KeyLockerManager`] that keeps every account's
5//! plaintext [`crate::tools::keys::Keys`] in a `HashMap` for the lifetime of the process.
6//!
7//! Used by tests and short-lived tools that don't need on-disk persistence. In the
8//! integration-test harness every simulated user gets a `MemKeyLocker`, so switching
9//! accounts, signing, and the guest fallback all exercise the same code paths as a
10//! production locker.
11
12use crate::client::key_locker::key_locker::{KeyLocker, KeyLockerManager, GUEST_CLIENT_ID};
13use crate::tools::client_id::ClientId;
14use crate::tools::keys::Keys;
15use crate::tools::signing;
16use crate::tools::types::Signature;
17use anyhow::anyhow;
18use std::collections::HashMap;
19use std::sync::Arc;
20use parking_lot::RwLock;
21
22pub struct MemKeyLocker {
23    keys: Keys,
24    client_id: ClientId,
25}
26
27#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
28#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
29impl KeyLocker for MemKeyLocker {
30    fn client_id(&self) -> &ClientId {
31        &self.client_id
32    }
33
34    async fn sign(&self, data: &[u8]) -> anyhow::Result<Signature> {
35        Ok(signing::sign(&self.keys.signature_key, data))
36    }
37}
38
39pub struct MemKeyLockerManager {
40    buckets: Arc<RwLock<HashMap<String, Arc<MemKeyLocker>>>>,
41}
42
43impl KeyLockerManager<MemKeyLocker> for MemKeyLockerManager {
44    async fn new() -> anyhow::Result<Arc<Self>> {
45        Ok(Arc::new(Self {
46            buckets: Arc::new(RwLock::new(HashMap::new())),
47        }))
48    }
49
50    async fn list(&self) -> anyhow::Result<Vec<String>> {
51        let buckets = self.buckets.read();
52        let keys = buckets.keys().filter(|k| k.as_str() != GUEST_CLIENT_ID).cloned().collect::<Vec<String>>();
53        Ok(keys)
54    }
55
56    async fn create(&self, key_phrase: String) -> anyhow::Result<Arc<MemKeyLocker>> {
57        let keys = Keys::from_phrase(&key_phrase)?;
58        let client_id = ClientId::new(keys.verification_key_bytes, keys.pq_commitment_bytes)?;
59        let key_public = client_id.id_hex();
60
61        let kml = Arc::new(MemKeyLocker { keys, client_id });
62
63        let mut buckets = self.buckets.write();
64        buckets.insert(key_public, kml.clone());
65
66        Ok(kml)
67    }
68
69    async fn switch(&self, key_public: String) -> anyhow::Result<Arc<MemKeyLocker>> {
70        let buckets = self.buckets.read();
71        let kml = buckets.get(&key_public);
72        match kml {
73            Some(kml) => Ok(kml.clone()),
74            None => Err(anyhow!("Unknown key_public {}", key_public)),
75        }
76    }
77
78    async fn delete(&self, key_public: String) -> anyhow::Result<()> {
79        let mut buckets = self.buckets.write();
80        let kml = buckets.remove(&key_public);
81        match kml {
82            Some(_kml) => Ok(()),
83            None => Err(anyhow!("Unknown key_public {}", key_public)),
84        }
85    }
86
87    async fn reset(&self) -> anyhow::Result<()> {
88        let mut buckets = self.buckets.write();
89        buckets.clear();
90        Ok(())
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use crate::client::key_locker::key_locker;
97    use crate::client::key_locker::mem_key_locker::{MemKeyLocker, MemKeyLockerManager};
98
99    #[tokio::test]
100    async fn add_test() {
101        key_locker::tests::add_test::<MemKeyLocker, MemKeyLockerManager>().await;
102    }
103    #[tokio::test]
104    async fn sign_test() {
105        key_locker::tests::sign_test::<MemKeyLocker, MemKeyLockerManager>().await;
106    }
107    #[tokio::test]
108    async fn guest_client_id_excluded_from_list_test() {
109        key_locker::tests::guest_client_id_excluded_from_list_test::<MemKeyLocker, MemKeyLockerManager>().await;
110    }
111}