use crate::guardian::error::Result;
use crate::log::identity::Identity;
use async_trait::async_trait;
use ed25519_dalek::{Signature, Signer, Verifier, VerifyingKey};
use iroh::{EndpointId as NodeId, SecretKey};
use std::sync::Arc;
pub struct CreateIdentityOptions {
pub identity_keys_path: String,
pub id_type: String,
pub keystore: Arc<dyn Keystore>,
pub id: String,
}
#[async_trait]
pub trait Keystore: Send + Sync {
async fn put(&self, key: &str, value: &[u8]) -> Result<()>;
async fn get(&self, key: &str) -> Result<Option<Vec<u8>>>;
async fn has(&self, key: &str) -> Result<bool>;
async fn delete(&self, key: &str) -> Result<()>;
fn enumerate_keys(&self) -> Result<Vec<String>>;
fn public_key(&self, key_id: &str) -> Result<Option<String>>;
fn generate_key(&self, key_id: &str) -> Result<String>;
fn key_meta(&self, _key_id: &str) -> Result<Option<KeyMeta>> {
Ok(None)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct KeyMeta {
pub created_at: u64,
pub updated_at: u64,
pub kind: String,
pub rotated_count: u32,
}
impl KeyMeta {
pub(crate) fn now_secs() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0)
}
pub(crate) fn to_json(&self) -> Vec<u8> {
format!(
r#"{{"created_at":{},"updated_at":{},"kind":"{}","rotated_count":{}}}"#,
self.created_at, self.updated_at, self.kind, self.rotated_count
)
.into_bytes()
}
pub(crate) fn from_json(bytes: &[u8]) -> Option<Self> {
let v: serde_json::Value = serde_json::from_slice(bytes).ok()?;
Some(KeyMeta {
created_at: v.get("created_at")?.as_u64()?,
updated_at: v.get("updated_at").and_then(|x| x.as_u64()).unwrap_or(0),
kind: v
.get("kind")
.and_then(|x| x.as_str())
.unwrap_or("ed25519")
.to_string(),
rotated_count: v.get("rotated_count").and_then(|x| x.as_u64()).unwrap_or(0) as u32,
})
}
}
pub(crate) fn derive_public(bytes: &[u8]) -> Option<String> {
if bytes.len() != 32 {
return None;
}
iroh::SecretKey::try_from(&bytes[..32])
.ok()
.map(|sk| sk.public().to_string())
}
pub(crate) fn new_secret_bytes() -> ([u8; 32], String) {
let sk = iroh::SecretKey::generate();
(sk.to_bytes(), sk.public().to_string())
}
#[async_trait]
pub trait IdentityProvider: Send + Sync {
async fn get_id(&self, opts: &CreateIdentityOptions) -> Result<String>;
async fn sign_identity(&self, data: &[u8], id: &str) -> Result<Vec<u8>>;
fn get_type(&self) -> String;
async fn verify_identity(&self, identity: &Identity) -> Result<()>;
async fn sign(&self, identity: &Identity, bytes: &[u8]) -> Result<Vec<u8>>;
fn unmarshal_public_key(&self, data: &[u8]) -> Result<VerifyingKey>;
}
pub struct GuardianDBIdentityProvider {
secret_key: SecretKey,
provider_type: String,
}
impl Default for GuardianDBIdentityProvider {
fn default() -> Self {
Self::new()
}
}
impl GuardianDBIdentityProvider {
pub fn new() -> Self {
Self {
secret_key: SecretKey::generate(),
provider_type: "GuardianDB".to_string(),
}
}
pub fn new_with_secret_key(secret_key: SecretKey) -> Self {
Self {
secret_key,
provider_type: "GuardianDB".to_string(),
}
}
pub fn public_key(&self) -> NodeId {
self.secret_key.public()
}
#[cfg(test)]
pub fn new_for_testing() -> Self {
Self::new()
}
fn get_signing_key(&self) -> ed25519_dalek::SigningKey {
let bytes = self.secret_key.to_bytes();
ed25519_dalek::SigningKey::from_bytes(&bytes)
}
}
#[async_trait]
impl IdentityProvider for GuardianDBIdentityProvider {
async fn get_id(&self, _opts: &CreateIdentityOptions) -> Result<String> {
let node_id = self.secret_key.public();
Ok(node_id.to_string())
}
async fn sign_identity(&self, data: &[u8], _id: &str) -> Result<Vec<u8>> {
let signing_key = self.get_signing_key();
let signature = signing_key.sign(data);
Ok(signature.to_bytes().to_vec())
}
fn get_type(&self) -> String {
self.provider_type.clone()
}
async fn verify_identity(&self, identity: &Identity) -> Result<()> {
let public_key = identity.public_key().ok_or_else(|| {
crate::guardian::error::GuardianError::Store("Identity missing public key".to_string())
})?;
let signatures_map = identity.signatures_map();
let signature_bytes = signatures_map.get("publicKey").ok_or_else(|| {
crate::guardian::error::GuardianError::Store(
"Identity missing publicKey signature".to_string(),
)
})?;
let signature = Signature::from_slice(signature_bytes).map_err(|e| {
crate::guardian::error::GuardianError::Store(format!("Invalid signature format: {}", e))
})?;
let signed_data = format!("{}{}", identity.id(), identity.get_type());
public_key
.verify(signed_data.as_bytes(), &signature)
.map_err(|e| {
crate::guardian::error::GuardianError::Store(format!(
"Invalid identity signature: {}",
e
))
})
}
async fn sign(&self, _identity: &Identity, bytes: &[u8]) -> Result<Vec<u8>> {
let signing_key = self.get_signing_key();
let signature = signing_key.sign(bytes);
Ok(signature.to_bytes().to_vec())
}
fn unmarshal_public_key(&self, data: &[u8]) -> Result<VerifyingKey> {
if data.len() != 32 {
return Err(crate::guardian::error::GuardianError::Store(
"Invalid public key length".to_string(),
));
}
VerifyingKey::from_bytes(data.try_into().map_err(|_| {
crate::guardian::error::GuardianError::Store("Failed to convert bytes".to_string())
})?)
.map_err(|e| {
crate::guardian::error::GuardianError::Store(format!(
"Failed to unmarshal public key: {}",
e
))
})
}
}
use std::collections::HashMap;
use tokio::sync::RwLock;
pub struct InMemoryKeystore {
store: RwLock<HashMap<String, Vec<u8>>>,
meta: RwLock<HashMap<String, KeyMeta>>,
}
impl InMemoryKeystore {
pub fn new() -> Self {
Self {
store: RwLock::new(HashMap::new()),
meta: RwLock::new(HashMap::new()),
}
}
}
impl Default for InMemoryKeystore {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl Keystore for InMemoryKeystore {
async fn put(&self, key: &str, value: &[u8]) -> Result<()> {
let mut store = self.store.write().await;
store.insert(key.to_string(), value.to_vec());
Ok(())
}
async fn get(&self, key: &str) -> Result<Option<Vec<u8>>> {
let store = self.store.read().await;
Ok(store.get(key).cloned())
}
async fn has(&self, key: &str) -> Result<bool> {
let store = self.store.read().await;
Ok(store.contains_key(key))
}
async fn delete(&self, key: &str) -> Result<()> {
let mut store = self.store.write().await;
store.remove(key);
Ok(())
}
fn enumerate_keys(&self) -> Result<Vec<String>> {
Ok(self
.store
.try_read()
.map(|s| s.keys().cloned().collect())
.unwrap_or_default())
}
fn public_key(&self, key_id: &str) -> Result<Option<String>> {
Ok(self
.store
.try_read()
.ok()
.and_then(|s| s.get(key_id).and_then(|b| derive_public(b))))
}
fn generate_key(&self, key_id: &str) -> Result<String> {
use zeroize::Zeroize;
let (mut bytes, public) = new_secret_bytes();
let result = match self.store.try_write() {
Ok(mut s) => {
s.insert(key_id.to_string(), bytes.to_vec());
if let Ok(mut m) = self.meta.try_write() {
let now = KeyMeta::now_secs();
match m.get_mut(key_id) {
Some(existing) => {
existing.rotated_count = existing.rotated_count.saturating_add(1);
existing.updated_at = now;
}
None => {
m.insert(
key_id.to_string(),
KeyMeta {
created_at: now,
updated_at: now,
kind: "ed25519".to_string(),
rotated_count: 0,
},
);
}
}
}
Ok(public)
}
Err(_) => Err(crate::guardian::error::GuardianError::Other(
"keystore busy".to_string(),
)),
};
bytes.zeroize();
result
}
fn key_meta(&self, key_id: &str) -> Result<Option<KeyMeta>> {
Ok(self
.meta
.try_read()
.ok()
.and_then(|m| m.get(key_id).cloned()))
}
}