#![allow(dead_code, unused_imports, unused_qualifications, unreachable_patterns)]
use super::ffi;
use std::collections::HashMap;
use std::sync::{Arc, Mutex, OnceLock};
use std::time::{Duration, Instant};
#[derive(Debug)]
pub(crate) struct LaContextHandle {
token: u64,
created_at: Instant,
ttl: Duration,
}
impl LaContextHandle {
#[allow(unsafe_code)] fn new(reason: &str, ttl: Duration) -> Option<Self> {
let secs = ttl.as_secs_f64();
let reason_len = i32::try_from(reason.len()).ok()?;
let token =
unsafe { ffi::enclaveapp_se_lacontext_create(secs, reason.as_ptr(), reason_len) };
if token == 0 {
None
} else {
Some(LaContextHandle {
token,
created_at: Instant::now(),
ttl,
})
}
}
pub(crate) fn token(&self) -> u64 {
self.token
}
fn is_expired(&self, now: Instant) -> bool {
now.saturating_duration_since(self.created_at) >= self.ttl
}
}
impl Drop for LaContextHandle {
#[allow(unsafe_code)] fn drop(&mut self) {
unsafe { ffi::enclaveapp_se_lacontext_release(self.token) };
}
}
type RegistryKey = (String, String);
fn registry() -> &'static Mutex<HashMap<RegistryKey, Arc<LaContextHandle>>> {
static REGISTRY: OnceLock<Mutex<HashMap<RegistryKey, Arc<LaContextHandle>>>> = OnceLock::new();
REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
}
pub(crate) fn acquire(
app_name: &str,
label: &str,
ttl_secs: u64,
reason: &str,
) -> Option<Arc<LaContextHandle>> {
if ttl_secs == 0 {
return None;
}
let key: RegistryKey = (app_name.to_string(), label.to_string());
let ttl = Duration::from_secs(ttl_secs);
let mut guard = match registry().lock() {
Ok(g) => g,
Err(poisoned) => poisoned.into_inner(),
};
let now = Instant::now();
if let Some(existing) = guard.get(&key) {
if !existing.is_expired(now) {
return Some(Arc::clone(existing));
}
guard.remove(&key);
}
let handle = LaContextHandle::new(reason, ttl)?;
let arc = Arc::new(handle);
guard.insert(key, Arc::clone(&arc));
Some(arc)
}
pub(crate) fn create_once(reason: &str) -> Option<LaContextHandle> {
LaContextHandle::new(reason, Duration::from_secs(5))
}
pub(crate) fn evict(app_name: &str, label: &str) {
let key: RegistryKey = (app_name.to_string(), label.to_string());
let mut guard = match registry().lock() {
Ok(g) => g,
Err(poisoned) => poisoned.into_inner(),
};
guard.remove(&key);
}
pub(crate) fn evict_all() {
let mut guard = match registry().lock() {
Ok(g) => g,
Err(poisoned) => poisoned.into_inner(),
};
guard.clear();
}
pub fn evaluate_presence(reason: &str) -> crate::internal::core::Result<()> {
#[allow(unsafe_code)]
let available = unsafe { ffi::enclaveapp_se_touch_id_available() };
if available == 0 {
return Err(crate::internal::core::Error::NotAvailable);
}
if create_once(reason).is_some() {
Ok(())
} else {
Err(crate::internal::core::Error::UserCancelled {
label: "request_presence".into(),
})
}
}
pub fn evict_all_contexts() {
evict_all();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ttl_zero_returns_none() {
assert!(acquire("test_app", "test_label_zero", 0, "test reason").is_none());
}
}