use crate::ffi::CiphernError;
use crate::fips::{FipsContext, FipsMode};
use crate::key::{KeyLifecycleManager, KeyManager};
use once_cell::sync::Lazy;
use std::sync::{Arc, Mutex, RwLock};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContextState {
Uninitialized,
Initializing,
Ready,
ShuttingDown,
Error,
}
#[derive(Debug, Clone)]
pub struct ContextConfig {
pub enable_fips: bool,
#[allow(dead_code)]
pub max_keys: usize,
#[allow(dead_code)]
pub key_lifecycle_policy: crate::key::KeyLifecyclePolicy,
}
impl Default for ContextConfig {
fn default() -> Self {
Self {
enable_fips: false,
max_keys: 1000,
key_lifecycle_policy: crate::key::KeyLifecyclePolicy::default(),
}
}
}
struct InnerContext {
state: ContextState,
key_manager: Option<Arc<KeyManager>>,
lifecycle_manager: Option<Arc<KeyLifecycleManager>>,
fips_context: Option<Arc<FipsContext>>,
}
pub struct FfiContext {
inner: RwLock<InnerContext>,
config: ContextConfig,
}
impl FfiContext {
pub fn new(config: ContextConfig) -> Self {
Self {
inner: RwLock::new(InnerContext {
state: ContextState::Uninitialized,
key_manager: None,
lifecycle_manager: None,
fips_context: None,
}),
config,
}
}
pub fn initialize(&self) -> std::result::Result<(), CiphernError> {
{
let inner = self.inner.read().unwrap();
match inner.state {
ContextState::Ready => return Ok(()),
ContextState::Initializing => return Err(CiphernError::UnknownError),
ContextState::ShuttingDown | ContextState::Error => {
return Err(CiphernError::UnknownError)
}
_ => {}
}
}
{
let mut inner = self.inner.write().unwrap();
if inner.state == ContextState::Ready {
return Ok(());
}
if inner.state == ContextState::Initializing {
return Err(CiphernError::UnknownError);
}
inner.state = ContextState::Initializing;
}
let result = self.do_initialize_resources();
let mut inner = self.inner.write().unwrap();
match result {
Ok((km, lm, fc)) => {
inner.key_manager = Some(km);
inner.lifecycle_manager = Some(lm);
inner.fips_context = fc;
inner.state = ContextState::Ready;
Ok(())
}
Err(e) => {
inner.state = ContextState::Error;
Err(e)
}
}
}
#[allow(clippy::type_complexity)]
fn do_initialize_resources(
&self,
) -> std::result::Result<
(
Arc<KeyManager>,
Arc<KeyLifecycleManager>,
Option<Arc<FipsContext>>,
),
CiphernError,
> {
crate::init().map_err(|_| CiphernError::UnknownError)?;
let key_manager = KeyManager::new().map_err(|_| CiphernError::MemoryAllocationFailed)?;
let key_manager = Arc::new(key_manager);
let lifecycle_manager =
KeyLifecycleManager::new().map_err(|_| CiphernError::MemoryAllocationFailed)?;
let lifecycle_manager = Arc::new(lifecycle_manager);
let fips_context = if self.config.enable_fips {
FipsContext::enable().map_err(|_| CiphernError::FipsError)?;
let ctx = FipsContext::new(FipsMode::Enabled).map_err(|_| CiphernError::FipsError)?;
Some(Arc::new(ctx))
} else {
None
};
Ok((key_manager, lifecycle_manager, fips_context))
}
pub fn cleanup(&self) {
let mut inner = self.inner.write().unwrap();
inner.state = ContextState::ShuttingDown;
inner.key_manager = None;
inner.lifecycle_manager = None;
inner.fips_context = None;
inner.state = ContextState::Uninitialized;
}
pub fn key_manager(&self) -> std::result::Result<Arc<KeyManager>, CiphernError> {
let inner = self.inner.read().unwrap();
if inner.state != ContextState::Ready {
return Err(CiphernError::UnknownError);
}
inner.key_manager.clone().ok_or(CiphernError::UnknownError)
}
#[allow(dead_code)]
pub fn lifecycle_manager(&self) -> std::result::Result<Arc<KeyLifecycleManager>, CiphernError> {
let inner = self.inner.read().unwrap();
if inner.state != ContextState::Ready {
return Err(CiphernError::UnknownError);
}
inner
.lifecycle_manager
.clone()
.ok_or(CiphernError::UnknownError)
}
#[allow(dead_code)]
pub fn fips_context(&self) -> Option<Arc<FipsContext>> {
let inner = self.inner.read().unwrap();
inner.fips_context.clone()
}
pub fn is_fips_enabled(&self) -> bool {
let inner = self.inner.read().unwrap();
inner.fips_context.is_some() && crate::fips::is_fips_enabled()
}
pub fn set_fips_enabled(&self, enabled: bool) {
let mut inner = self.inner.write().unwrap();
if enabled && inner.fips_context.is_none() {
if let Ok(fips_context) = FipsContext::new(FipsMode::Enabled) {
inner.fips_context = Some(Arc::new(fips_context));
}
} else if !enabled {
inner.fips_context = None;
}
}
#[allow(dead_code)]
pub fn state(&self) -> ContextState {
self.inner.read().unwrap().state
}
}
static GLOBAL_CONTEXT: Lazy<Arc<Mutex<Option<Arc<FfiContext>>>>> =
Lazy::new(|| Arc::new(Mutex::new(None)));
pub fn get_context() -> std::result::Result<Arc<FfiContext>, CiphernError> {
let mut global = GLOBAL_CONTEXT.lock().unwrap();
if let Some(ref context) = *global {
return Ok(context.clone());
}
let config = ContextConfig::default();
let context = Arc::new(FfiContext::new(config));
*global = Some(context.clone());
Ok(context)
}
pub fn initialize_context() -> std::result::Result<(), CiphernError> {
let context = get_context()?;
context.initialize()
}
pub fn cleanup_context() {
if let Ok(context) = get_context() {
context.cleanup();
}
let mut global = GLOBAL_CONTEXT.lock().unwrap();
*global = None;
}
#[allow(dead_code)]
pub fn is_context_ready() -> bool {
if let Ok(global) = GLOBAL_CONTEXT.lock() {
if let Some(ref context) = *global {
return (context as &Arc<FfiContext>).state() == ContextState::Ready;
}
}
false
}
pub fn with_context<F, R>(f: F) -> std::result::Result<R, CiphernError>
where
F: FnOnce(&Arc<FfiContext>) -> std::result::Result<R, CiphernError>,
{
let context = get_context()?;
f(&context)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_context_lifecycle() {
let config = ContextConfig::default();
let context = Arc::new(FfiContext::new(config));
assert_eq!(context.state(), ContextState::Uninitialized);
let init_result = context.initialize();
#[cfg(feature = "gpu")]
{
if init_result.is_err() {
let gpu_enabled = crate::hardware::is_gpu_enabled();
if gpu_enabled && !crate::hardware::is_gpu_initialized() {
return;
}
}
assert!(
init_result.is_ok(),
"Context initialization failed: {:?}",
init_result
);
}
#[cfg(not(feature = "gpu"))]
{
assert!(
init_result.is_ok(),
"Context initialization failed: {:?}",
init_result
);
}
assert_eq!(context.state(), ContextState::Ready);
context.cleanup();
assert_eq!(context.state(), ContextState::Uninitialized);
}
#[test]
fn test_context_config() {
let config = ContextConfig {
enable_fips: true,
max_keys: 500,
key_lifecycle_policy: crate::key::KeyLifecyclePolicy::default(),
};
let context = FfiContext::new(config);
assert_eq!(context.state(), ContextState::Uninitialized);
}
}