hashiverse_lib/client/key_locker/
key_locker.rs1use crate::tools::client_id::ClientId;
23use crate::tools::types::Signature;
24use std::sync::Arc;
25
26pub const GUEST_CLIENT_ID: &str = "fe050cc21479a93d00fdb825c98c8489ea47dd6ce180b6f8b72665f284842e41";
29
30#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
45#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
46pub trait KeyLocker: Send + Sync {
47 fn client_id(&self) -> &ClientId;
48 async fn sign(&self, data: &[u8]) -> anyhow::Result<Signature>;
49}
50
51pub trait KeyLockerManager<TKeyLocker: KeyLocker> {
64 async fn new() -> anyhow::Result<Arc<Self>>;
65 async fn list(&self) -> anyhow::Result<Vec<String>>;
66 async fn create(&self, key_phrase: String) -> anyhow::Result<Arc<TKeyLocker>>;
67 async fn switch(&self, key_public: String) -> anyhow::Result<Arc<TKeyLocker>>;
68 async fn delete(&self, key_public: String) -> anyhow::Result<()>;
69 async fn reset(&self) -> anyhow::Result<()>;
70}
71
72#[cfg(any(test, feature = "generic-tests"))]
73pub mod tests {
74 use crate::client::key_locker::key_locker::{KeyLocker, KeyLockerManager, GUEST_CLIENT_ID};
75 use crate::tools::types::VerificationKey;
76 use crate::tools::{signing, tools};
77
78 pub async fn add_test<TKeyLocker: KeyLocker, TKeyLockerManager: KeyLockerManager<TKeyLocker>>() {
79 let result = try {
80 let key_locker_manager = TKeyLockerManager::new().await?;
81
82 key_locker_manager.reset().await?;
84 assert_eq!(0, key_locker_manager.list().await?.len());
85
86 let key_locker_1 = key_locker_manager.create("key_phrase_1".to_string()).await?;
88 let key_locker_2 = key_locker_manager.create("key_phrase_2".to_string()).await?;
89 assert_eq!(2, key_locker_manager.list().await?.len());
90
91 let public_key_1 = key_locker_1.client_id().id.to_hex_str();
93 let public_key_2 = key_locker_2.client_id().id.to_hex_str();
94 assert_eq!(key_locker_1.client_id(), key_locker_manager.switch(public_key_1).await?.client_id());
95 assert_eq!(key_locker_2.client_id(), key_locker_manager.switch(public_key_2).await?.client_id());
96
97 key_locker_manager.reset().await?;
99 assert_eq!(0, key_locker_manager.list().await?.len());
100 };
101
102 if let Err(e) = result {
103 panic!("Test failed: {}", e);
104 }
105 }
106 pub async fn guest_client_id_excluded_from_list_test<TKeyLocker: KeyLocker, TKeyLockerManager: KeyLockerManager<TKeyLocker>>() {
108 let result: anyhow::Result<()> = try {
109 let key_locker_manager = TKeyLockerManager::new().await?;
110 key_locker_manager.reset().await?;
111
112 let _guest_key_locker = key_locker_manager.create("".to_string()).await?;
114 let real_key_locker = key_locker_manager.create("real_keyphrase".to_string()).await?;
115 let real_public_key = real_key_locker.client_id().id.to_hex_str();
116
117 let listed_keys = key_locker_manager.list().await?;
119 assert_eq!(listed_keys.len(), 1, "list() should return exactly 1 key, got {}", listed_keys.len());
120 assert_eq!(listed_keys[0], real_public_key);
121 assert!(!listed_keys.contains(&GUEST_CLIENT_ID.to_string()), "list() must not return the guest client ID");
122
123 key_locker_manager.reset().await?;
124 };
125
126 if let Err(e) = result {
127 panic!("Test failed: {}", e);
128 }
129 }
130
131 pub async fn sign_test<TKeyLocker: KeyLocker, TKeyLockerManager: KeyLockerManager<TKeyLocker>>() {
132 let result = try {
133 let key_locker_manager = TKeyLockerManager::new().await?;
134
135 key_locker_manager.reset().await?;
137 assert_eq!(0, key_locker_manager.list().await?.len());
138
139 let key_locker_1 = key_locker_manager.create("key_phrase_1".to_string()).await?;
141
142 {
144 let mut message_to_sign = [0u8; 1024];
145 tools::random_fill_bytes(&mut message_to_sign);
146 let signature = key_locker_1.sign(&message_to_sign).await?;
147 let verification_key = VerificationKey::from_bytes(&key_locker_1.client_id().verification_key_bytes)?;
148 let result = signing::verify(&verification_key, &signature, &message_to_sign);
149 assert!(result.is_ok())
150 }
151 };
152
153 if let Err(e) = result {
154 panic!("Test failed: {}", e);
155 }
156 }
157
158 #[test]
161 fn guest_client_id_constant_test() {
162 use crate::tools::client_id::ClientId;
163 use crate::tools::keys::Keys;
164 let keys = Keys::from_phrase("").expect("empty keyphrase should always work");
165 let client_id = ClientId::new(keys.verification_key_bytes, keys.pq_commitment_bytes).expect("client id creation should always work");
166 assert_eq!(client_id.id.to_hex_str(), GUEST_CLIENT_ID, "GUEST_CLIENT_ID constant is stale — update it to match the current empty-keyphrase derivation");
167 }
168}