use super::{Attestation, KeyHandle, SecureKeyProvider, SecurityLevel};
use crate::error::WSError;
use crate::signature::{KeyPair, PublicKey};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use zeroize::Zeroizing;
const KEYRING_SERVICE: &str = "wsc-crypto";
struct KeyCache {
next_handle: u64,
keys: HashMap<u64, (String, KeyPair)>,
id_to_handle: HashMap<String, u64>,
}
impl KeyCache {
fn new() -> Self {
KeyCache {
next_handle: 1,
keys: HashMap::new(),
id_to_handle: HashMap::new(),
}
}
fn insert(&mut self, key_id: String, keypair: KeyPair) -> KeyHandle {
let handle = self.next_handle;
self.next_handle += 1;
self.id_to_handle.insert(key_id.clone(), handle);
self.keys.insert(handle, (key_id, keypair));
KeyHandle::from_raw(handle)
}
fn get(&self, handle: KeyHandle) -> Option<&(String, KeyPair)> {
self.keys.get(&handle.as_raw())
}
fn get_by_id(&self, key_id: &str) -> Option<KeyHandle> {
self.id_to_handle.get(key_id).map(|&h| KeyHandle::from_raw(h))
}
fn remove(&mut self, handle: KeyHandle) -> Option<(String, KeyPair)> {
if let Some((key_id, kp)) = self.keys.remove(&handle.as_raw()) {
self.id_to_handle.remove(&key_id);
Some((key_id, kp))
} else {
None
}
}
fn list(&self) -> Vec<KeyHandle> {
self.keys.keys().map(|&h| KeyHandle::from_raw(h)).collect()
}
}
pub struct KeyringProvider {
app_name: String,
cache: Arc<Mutex<KeyCache>>,
}
impl KeyringProvider {
pub fn new(app_name: &str) -> Result<Self, WSError> {
let service = format!("{}-{}", KEYRING_SERVICE, app_name);
let entry = keyring::Entry::new(&service, "__test__")
.map_err(|e| WSError::InternalError(format!("Keyring unavailable: {}", e)))?;
let _ = entry.delete_credential();
log::info!(
"Keyring provider initialized for '{}' - keys stored in OS credential store",
app_name
);
Ok(KeyringProvider {
app_name: app_name.to_string(),
cache: Arc::new(Mutex::new(KeyCache::new())),
})
}
fn service_name(&self) -> String {
format!("{}-{}", KEYRING_SERVICE, self.app_name)
}
fn store_secret_key(&self, key_id: &str, secret_bytes: &[u8]) -> Result<(), WSError> {
let entry = keyring::Entry::new(&self.service_name(), key_id)
.map_err(|e| WSError::InternalError(format!("Failed to create keyring entry: {}", e)))?;
entry
.set_secret(secret_bytes)
.map_err(|e| WSError::InternalError(format!("Failed to store key in keyring: {}", e)))?;
Ok(())
}
fn load_secret_key(&self, key_id: &str) -> Result<Zeroizing<Vec<u8>>, WSError> {
let entry = keyring::Entry::new(&self.service_name(), key_id)
.map_err(|e| WSError::InternalError(format!("Failed to create keyring entry: {}", e)))?;
let secret = entry.get_secret().map_err(|e| match e {
keyring::Error::NoEntry => {
WSError::InternalError(format!("Key '{}' not found in keyring", key_id))
}
_ => WSError::InternalError(format!("Failed to load key from keyring: {}", e)),
})?;
Ok(Zeroizing::new(secret))
}
fn delete_secret_key(&self, key_id: &str) -> Result<(), WSError> {
let entry = keyring::Entry::new(&self.service_name(), key_id)
.map_err(|e| WSError::InternalError(format!("Failed to create keyring entry: {}", e)))?;
entry.delete_credential().map_err(|e| match e {
keyring::Error::NoEntry => {
WSError::InternalError(format!("Key '{}' not found in keyring", key_id))
}
_ => WSError::InternalError(format!("Failed to delete key from keyring: {}", e)),
})?;
Ok(())
}
fn generate_key_id() -> String {
uuid::Uuid::new_v4().to_string()
}
fn keypair_from_secret(secret_bytes: &[u8]) -> Result<KeyPair, WSError> {
if secret_bytes.len() != 64 {
return Err(WSError::InternalError(format!(
"Invalid secret key length: {} (expected 64)",
secret_bytes.len()
)));
}
let sk = ed25519_compact::SecretKey::from_slice(secret_bytes)
.map_err(|e| WSError::InternalError(format!("Invalid secret key: {}", e)))?;
let pk = sk.public_key();
Ok(KeyPair {
pk: PublicKey { pk, key_id: None },
sk: crate::signature::SecretKey { sk },
})
}
}
impl SecureKeyProvider for KeyringProvider {
fn name(&self) -> &str {
"OS Keyring (Secure Storage)"
}
fn security_level(&self) -> SecurityLevel {
SecurityLevel::HardwareBasic
}
fn health_check(&self) -> Result<(), WSError> {
let entry = keyring::Entry::new(&self.service_name(), "__health_check__")
.map_err(|e| WSError::InternalError(format!("Keyring unavailable: {}", e)))?;
let _ = entry.delete_credential();
Ok(())
}
fn generate_key(&self) -> Result<KeyHandle, WSError> {
let keypair = KeyPair::generate();
let key_id = Self::generate_key_id();
let secret_bytes = keypair.sk.sk.as_ref();
self.store_secret_key(&key_id, secret_bytes)?;
log::debug!("Generated new key '{}' and stored in keyring", key_id);
let mut cache = self
.cache
.lock()
.map_err(|e| WSError::InternalError(format!("Lock poisoned: {}", e)))?;
Ok(cache.insert(key_id, keypair))
}
fn load_key(&self, key_id: &str) -> Result<KeyHandle, WSError> {
{
let cache = self
.cache
.lock()
.map_err(|e| WSError::InternalError(format!("Lock poisoned: {}", e)))?;
if let Some(handle) = cache.get_by_id(key_id) {
return Ok(handle);
}
}
let secret_bytes = self.load_secret_key(key_id)?;
let keypair = Self::keypair_from_secret(&secret_bytes)?;
log::debug!("Loaded key '{}' from keyring", key_id);
let mut cache = self
.cache
.lock()
.map_err(|e| WSError::InternalError(format!("Lock poisoned: {}", e)))?;
Ok(cache.insert(key_id.to_string(), keypair))
}
fn sign(&self, handle: KeyHandle, data: &[u8]) -> Result<Vec<u8>, WSError> {
let cache = self
.cache
.lock()
.map_err(|e| WSError::InternalError(format!("Lock poisoned: {}", e)))?;
let (_, keypair) = cache
.get(handle)
.ok_or_else(|| WSError::InternalError("Invalid key handle".to_string()))?;
let signature = keypair.sk.sk.sign(data, None);
Ok(signature.to_vec())
}
fn get_public_key(&self, handle: KeyHandle) -> Result<PublicKey, WSError> {
let cache = self
.cache
.lock()
.map_err(|e| WSError::InternalError(format!("Lock poisoned: {}", e)))?;
let (_, keypair) = cache
.get(handle)
.ok_or_else(|| WSError::InternalError("Invalid key handle".to_string()))?;
Ok(keypair.pk.clone())
}
fn attestation(&self, _handle: KeyHandle) -> Result<Option<Attestation>, WSError> {
Ok(None)
}
fn delete_key(&self, handle: KeyHandle) -> Result<(), WSError> {
let key_id = {
let mut cache = self
.cache
.lock()
.map_err(|e| WSError::InternalError(format!("Lock poisoned: {}", e)))?;
let (key_id, _) = cache
.remove(handle)
.ok_or_else(|| WSError::InternalError("Invalid key handle".to_string()))?;
key_id
};
self.delete_secret_key(&key_id)?;
log::debug!("Deleted key '{}' from keyring", key_id);
Ok(())
}
fn list_keys(&self) -> Result<Vec<KeyHandle>, WSError> {
let cache = self
.cache
.lock()
.map_err(|e| WSError::InternalError(format!("Lock poisoned: {}", e)))?;
Ok(cache.list())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_provider() -> KeyringProvider {
KeyringProvider::new("test").expect("Failed to create test provider")
}
#[test]
fn test_provider_creation() {
let provider = test_provider();
assert_eq!(provider.name(), "OS Keyring (Secure Storage)");
assert_eq!(provider.security_level(), SecurityLevel::HardwareBasic);
}
#[test]
fn test_health_check() {
let provider = test_provider();
assert!(provider.health_check().is_ok());
}
#[test]
fn test_generate_and_sign() {
let provider = test_provider();
let handle = provider.generate_key().expect("Failed to generate key");
let data = b"test data to sign";
let signature = provider.sign(handle, data).expect("Failed to sign");
assert_eq!(signature.len(), 64);
let public_key = provider
.get_public_key(handle)
.expect("Failed to get public key");
let sig =
ed25519_compact::Signature::from_slice(&signature).expect("Invalid signature format");
assert!(public_key.pk.verify(data, &sig).is_ok());
provider.delete_key(handle).expect("Failed to delete key");
}
#[test]
fn test_key_persistence() {
let provider = test_provider();
let handle1 = provider.generate_key().expect("Failed to generate key");
let key_id = {
let cache = provider.cache.lock().unwrap();
let (key_id, _) = cache.get(handle1).unwrap();
key_id.clone()
};
let data = b"test data";
let sig1 = provider.sign(handle1, data).expect("Failed to sign");
{
let mut cache = provider.cache.lock().unwrap();
cache.keys.clear();
cache.id_to_handle.clear();
}
let handle2 = provider.load_key(&key_id).expect("Failed to load key");
let sig2 = provider.sign(handle2, data).expect("Failed to sign");
assert_eq!(sig1, sig2);
provider.delete_key(handle2).expect("Failed to delete key");
}
#[test]
fn test_delete_key() {
let provider = test_provider();
let handle = provider.generate_key().expect("Failed to generate key");
let key_id = {
let cache = provider.cache.lock().unwrap();
let (key_id, _) = cache.get(handle).unwrap();
key_id.clone()
};
provider.delete_key(handle).expect("Failed to delete key");
let result = provider.load_secret_key(&key_id);
assert!(result.is_err());
}
}