use super::error::AidError;
use super::verifier::AIdCredentialVerifier;
use crate::aid::key_cache::KeyCache;
use actr_protocol::{AIdCredential, IdentityClaims};
use ed25519_dalek::VerifyingKey;
use once_cell::sync::OnceCell;
use std::sync::Arc;
static KEY_CACHE: OnceCell<Arc<KeyCache>> = OnceCell::new();
pub struct AIdCredentialValidator;
impl AIdCredentialValidator {
pub async fn init(sqlite_path: &std::path::Path) -> Result<(), AidError> {
if KEY_CACHE.get().is_some() {
return Ok(());
}
let cache_db = sqlite_path.join("signaling_key_cache.db");
let cache = KeyCache::new(&cache_db).await?;
let _ = KEY_CACHE.set(Arc::new(cache));
Ok(())
}
fn get_cache() -> Result<Arc<KeyCache>, AidError> {
KEY_CACHE.get().cloned().ok_or(AidError::InvalidFormat)
}
pub async fn check(
credential: &AIdCredential,
realm_id: u32,
) -> Result<(IdentityClaims, bool), AidError> {
let cache = Self::get_cache()?;
let key_id = credential.key_id;
let (verifying_key, _expires_at) = cache
.get_cached_key(key_id)
.await?
.ok_or(AidError::InvalidFormat)?;
let claims = AIdCredentialVerifier::verify(credential, &verifying_key)?;
if claims.realm_id != realm_id {
return Err(AidError::InvalidFormat);
}
Ok((claims, false))
}
pub async fn get_key_bytes(key_id: u32) -> Result<Option<Vec<u8>>, AidError> {
let cache = Self::get_cache()?;
let result = cache.get_cached_key(key_id).await?;
Ok(result.map(|(verifying_key, _expires_at)| verifying_key.as_bytes().to_vec()))
}
pub async fn populate_key(
key_id: u32,
verifying_key: &VerifyingKey,
expires_at: u64,
) -> Result<(), AidError> {
let cache = Self::get_cache()?;
cache.cache_key(key_id, verifying_key, expires_at).await
}
pub async fn persist_key(
sqlite_path: &std::path::Path,
key_id: u32,
verifying_key: &VerifyingKey,
expires_at: u64,
) -> Result<(), AidError> {
let cache_db = sqlite_path.join("signaling_key_cache.db");
let cache = KeyCache::new(&cache_db).await?;
cache.cache_key(key_id, verifying_key, expires_at).await?;
if let Some(global_cache) = KEY_CACHE.get() {
let _ = global_cache
.cache_key(key_id, verifying_key, expires_at)
.await;
}
Ok(())
}
pub fn check_sync(
credential: &AIdCredential,
realm_id: u32,
) -> Result<IdentityClaims, AidError> {
tokio::task::block_in_place(|| {
let handle =
tokio::runtime::Handle::try_current().map_err(|_| AidError::InvalidFormat)?;
let (claims, _) = handle.block_on(Self::check(credential, realm_id))?;
Ok(claims)
})
}
}