use crate::guardian::error::{GuardianError, Result};
use crate::log::identity_provider::Keystore as KeystoreInterface;
use async_trait::async_trait;
use iroh::SecretKey;
use sled::Db;
use std::sync::Arc;
#[derive(Clone, Debug)]
pub struct SledKeystore {
db: Db,
}
impl SledKeystore {
pub fn new(path: Option<std::path::PathBuf>) -> Result<Self> {
let db = match path {
Some(p) => sled::open(p)
.map_err(|e| GuardianError::Other(format!("Erro ao abrir sled: {}", e)))?,
None => sled::Config::new().temporary(true).open().map_err(|e| {
GuardianError::Other(format!("Erro ao criar sled temporário: {}", e))
})?,
};
Ok(Self { db })
}
pub fn temporary() -> Result<Self> {
Self::new(None)
}
pub async fn put_keypair(&self, key: &str, secret_key: &SecretKey) -> Result<()> {
let encoded = secret_key.to_bytes();
self.put(key, &encoded).await
}
pub async fn get_keypair(&self, key: &str) -> Result<Option<SecretKey>> {
match self.get(key).await? {
Some(bytes) => {
if bytes.len() != 32 {
return Err(GuardianError::Other(
"Tamanho inválido de chave secreta".to_string(),
));
}
let secret_key = SecretKey::try_from(&bytes[..32]).map_err(|e| {
GuardianError::Other(format!("Erro ao decodificar secret key: {}", e))
})?;
Ok(Some(secret_key))
}
None => Ok(None),
}
}
pub async fn list_keys(&self) -> Result<Vec<String>> {
let keys: std::result::Result<Vec<String>, sled::Error> = self
.db
.iter()
.keys()
.map(|k| k.map(|key| String::from_utf8_lossy(&key).to_string()))
.collect();
keys.map_err(|e| GuardianError::Other(format!("Erro ao listar chaves: {}", e)))
}
pub async fn close(&self) -> Result<()> {
self.db
.flush_async()
.await
.map_err(|e| GuardianError::Other(format!("Erro ao fechar keystore: {}", e)))?;
Ok(())
}
}
#[async_trait]
impl KeystoreInterface for SledKeystore {
async fn put(&self, key: &str, value: &[u8]) -> Result<()> {
self.db
.insert(key, value)
.map_err(|e| GuardianError::Other(format!("Erro ao inserir no keystore: {}", e)))?;
Ok(())
}
async fn get(&self, key: &str) -> Result<Option<Vec<u8>>> {
match self
.db
.get(key)
.map_err(|e| GuardianError::Other(format!("Erro ao recuperar do keystore: {}", e)))?
{
Some(bytes) => Ok(Some(bytes.to_vec())),
None => Ok(None),
}
}
async fn has(&self, key: &str) -> Result<bool> {
Ok(self.db.contains_key(key).map_err(|e| {
GuardianError::Other(format!("Erro ao verificar chave no keystore: {}", e))
})?)
}
async fn delete(&self, key: &str) -> Result<()> {
self.db
.remove(key)
.map_err(|e| GuardianError::Other(format!("Erro ao remover do keystore: {}", e)))?;
Ok(())
}
}
pub fn create_keystore(
directory: Option<std::path::PathBuf>,
) -> Result<Arc<dyn KeystoreInterface + Send + Sync>> {
let keystore = SledKeystore::new(directory)?;
Ok(Arc::new(keystore))
}
pub fn create_temp_keystore() -> Result<Arc<dyn KeystoreInterface + Send + Sync>> {
let keystore = SledKeystore::temporary()?;
Ok(Arc::new(keystore))
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_sled_keystore_basic_operations() {
let keystore = SledKeystore::temporary().unwrap();
let key = "test_key";
let value = b"test_value";
assert!(!keystore.has(key).await.unwrap());
keystore.put(key, value).await.unwrap();
assert!(keystore.has(key).await.unwrap());
let retrieved = keystore.get(key).await.unwrap().unwrap();
assert_eq!(retrieved, value);
keystore.delete(key).await.unwrap();
assert!(!keystore.has(key).await.unwrap());
}
#[tokio::test]
async fn test_keypair_storage() {
use rand_core::OsRng;
let keystore = SledKeystore::temporary().unwrap();
let key_name = "test_keypair";
let original_secret = SecretKey::generate(OsRng);
keystore
.put_keypair(key_name, &original_secret)
.await
.unwrap();
let retrieved_secret = keystore.get_keypair(key_name).await.unwrap().unwrap();
assert_eq!(original_secret.public(), retrieved_secret.public());
}
#[tokio::test]
async fn test_list_keys() {
let keystore = SledKeystore::temporary().unwrap();
keystore.put("key1", b"value1").await.unwrap();
keystore.put("key2", b"value2").await.unwrap();
keystore.put("key3", b"value3").await.unwrap();
let mut keys = keystore.list_keys().await.unwrap();
keys.sort();
assert_eq!(keys, vec!["key1", "key2", "key3"]);
}
}