use crate::tools::client_id::ClientId;
use crate::tools::types::Signature;
use std::sync::Arc;
pub const GUEST_CLIENT_ID: &str = "fe050cc21479a93d00fdb825c98c8489ea47dd6ce180b6f8b72665f284842e41";
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
pub trait KeyLocker: Send + Sync {
fn client_id(&self) -> &ClientId;
async fn sign(&self, data: &[u8]) -> anyhow::Result<Signature>;
}
pub trait KeyLockerManager<TKeyLocker: KeyLocker> {
async fn new() -> anyhow::Result<Arc<Self>>;
async fn list(&self) -> anyhow::Result<Vec<String>>;
async fn create(&self, key_phrase: String) -> anyhow::Result<Arc<TKeyLocker>>;
async fn switch(&self, client_id: String) -> anyhow::Result<Arc<TKeyLocker>>;
async fn delete(&self, client_id: String) -> anyhow::Result<()>;
async fn reset(&self) -> anyhow::Result<()>;
}
#[cfg(any(test, feature = "generic-tests"))]
pub mod tests {
use crate::client::key_locker::key_locker::{KeyLocker, KeyLockerManager, GUEST_CLIENT_ID};
use crate::tools::types::VerificationKey;
use crate::tools::{signing, tools};
pub async fn add_test<TKeyLocker: KeyLocker, TKeyLockerManager: KeyLockerManager<TKeyLocker>>() {
let result = try {
let key_locker_manager = TKeyLockerManager::new().await?;
key_locker_manager.reset().await?;
assert_eq!(0, key_locker_manager.list().await?.len());
let key_locker_1 = key_locker_manager.create("key_phrase_1".to_string()).await?;
let key_locker_2 = key_locker_manager.create("key_phrase_2".to_string()).await?;
assert_eq!(2, key_locker_manager.list().await?.len());
let client_id_1 = key_locker_1.client_id().id.to_hex_str();
let client_id_2 = key_locker_2.client_id().id.to_hex_str();
assert_eq!(key_locker_1.client_id(), key_locker_manager.switch(client_id_1).await?.client_id());
assert_eq!(key_locker_2.client_id(), key_locker_manager.switch(client_id_2).await?.client_id());
key_locker_manager.reset().await?;
assert_eq!(0, key_locker_manager.list().await?.len());
};
if let Err(e) = result {
panic!("Test failed: {}", e);
}
}
pub async fn guest_client_id_excluded_from_list_test<TKeyLocker: KeyLocker, TKeyLockerManager: KeyLockerManager<TKeyLocker>>() {
let result: anyhow::Result<()> = try {
let key_locker_manager = TKeyLockerManager::new().await?;
key_locker_manager.reset().await?;
let _guest_key_locker = key_locker_manager.create("".to_string()).await?;
let real_key_locker = key_locker_manager.create("real_keyphrase".to_string()).await?;
let real_client_id = real_key_locker.client_id().id.to_hex_str();
let listed_client_ids = key_locker_manager.list().await?;
assert_eq!(listed_client_ids.len(), 1, "list() should return exactly 1 key, got {}", listed_client_ids.len());
assert_eq!(listed_client_ids[0], real_client_id);
assert!(!listed_client_ids.contains(&GUEST_CLIENT_ID.to_string()), "list() must not return the guest client ID");
key_locker_manager.reset().await?;
};
if let Err(e) = result {
panic!("Test failed: {}", e);
}
}
pub async fn sign_test<TKeyLocker: KeyLocker, TKeyLockerManager: KeyLockerManager<TKeyLocker>>() {
let result = try {
let key_locker_manager = TKeyLockerManager::new().await?;
key_locker_manager.reset().await?;
assert_eq!(0, key_locker_manager.list().await?.len());
let key_locker_1 = key_locker_manager.create("key_phrase_1".to_string()).await?;
{
let mut message_to_sign = [0u8; 1024];
tools::random_fill_bytes(&mut message_to_sign);
let signature = key_locker_1.sign(&message_to_sign).await?;
let verification_key = VerificationKey::from_bytes(&key_locker_1.client_id().verification_key_bytes)?;
let result = signing::verify(&verification_key, &signature, &message_to_sign);
assert!(result.is_ok())
}
};
if let Err(e) = result {
panic!("Test failed: {}", e);
}
}
#[test]
fn guest_client_id_constant_test() {
use crate::tools::client_id::ClientId;
use crate::tools::keys::Keys;
let keys = Keys::from_phrase("").expect("empty keyphrase should always work");
let client_id = ClientId::new(keys.verification_key_bytes, keys.pq_commitment_bytes).expect("client id creation should always work");
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");
}
}