use super::KeyManagerOperations;
use crate::audit::AuditLogger;
use crate::error::{CryptoError, Result};
use crate::random::SecureRandom;
use crate::types::{Algorithm, KeyState};
use chrono::{DateTime, Duration, Utc};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
#[derive(Debug, Clone)]
pub struct KeyLifecyclePolicy {
pub key_lifetime: Duration,
pub rotation_interval: Duration,
pub rotation_warning_period: Duration,
pub max_key_usage: Option<usize>,
pub auto_rotation_enabled: bool,
pub version_management_enabled: bool,
}
impl Default for KeyLifecyclePolicy {
fn default() -> Self {
Self {
key_lifetime: Duration::days(365), rotation_interval: Duration::days(90), rotation_warning_period: Duration::days(30), max_key_usage: Some(1_000_000), auto_rotation_enabled: true,
version_management_enabled: true,
}
}
}
#[derive(Debug, Clone)]
pub struct KeyVersion {
pub version_id: String,
pub key_id: String,
pub algorithm: Algorithm,
pub created_at: DateTime<Utc>,
pub expires_at: DateTime<Utc>,
pub is_active: bool,
pub usage_count: usize,
pub state: KeyState,
}
pub struct KeyLifecycleManager {
policies: Arc<RwLock<HashMap<Algorithm, KeyLifecyclePolicy>>>,
key_versions: Arc<RwLock<HashMap<String, Vec<KeyVersion>>>>,
rotation_schedule: Arc<RwLock<HashMap<String, DateTime<Utc>>>>,
_rng: SecureRandom,
}
impl KeyLifecycleManager {
pub fn new() -> Result<Self> {
Ok(Self {
policies: Arc::new(RwLock::new(HashMap::new())),
key_versions: Arc::new(RwLock::new(HashMap::new())),
rotation_schedule: Arc::new(RwLock::new(HashMap::new())),
_rng: SecureRandom::new()?,
})
}
pub fn set_policy(&self, algorithm: Algorithm, policy: KeyLifecyclePolicy) -> Result<()> {
let mut policies = self
.policies
.write()
.map_err(|_| CryptoError::MemoryProtectionFailed("Lock poisoned".into()))?;
policies.insert(algorithm, policy);
AuditLogger::log("KEY_POLICY_SET", Some(algorithm), None, Ok(()));
Ok(())
}
pub fn get_policy(&self, algorithm: Algorithm) -> Option<KeyLifecyclePolicy> {
let policies = self.policies.read().ok()?;
policies.get(&algorithm).cloned()
}
pub fn create_key_version(
&self,
key_manager: &dyn KeyManagerOperations,
algorithm: Algorithm,
) -> Result<String> {
let policy = self.get_policy(algorithm).unwrap_or_default();
let key_id = key_manager.generate_key_operation(algorithm)?;
let version = KeyVersion {
version_id: format!("v_{}", chrono::Utc::now().timestamp_millis()),
key_id: key_id.clone(),
algorithm,
created_at: Utc::now(),
expires_at: Utc::now() + policy.key_lifetime,
is_active: true,
usage_count: 0,
state: KeyState::Active,
};
let mut versions = self
.key_versions
.write()
.map_err(|_| CryptoError::MemoryProtectionFailed("Lock poisoned".into()))?;
let entry = versions.entry(key_id.clone()).or_insert_with(Vec::new);
for old_version in entry.iter_mut() {
if old_version.is_active {
old_version.is_active = false;
}
}
entry.push(version);
if policy.auto_rotation_enabled {
let mut schedule = self
.rotation_schedule
.write()
.map_err(|_| CryptoError::MemoryProtectionFailed("Lock poisoned".into()))?;
schedule.insert(key_id.clone(), Utc::now() + policy.rotation_interval);
}
AuditLogger::log(
"KEY_VERSION_CREATED",
Some(algorithm),
Some(&key_id),
Ok(()),
);
Ok(key_id)
}
pub fn get_active_version(&self, key_id: &str) -> Result<KeyVersion> {
let versions = self
.key_versions
.read()
.map_err(|_| CryptoError::MemoryProtectionFailed("Lock poisoned".into()))?;
let key_versions = versions
.get(key_id)
.ok_or_else(|| CryptoError::KeyNotFound(key_id.to_string()))?;
key_versions
.iter()
.find(|v| v.is_active && v.state == KeyState::Active)
.cloned()
.ok_or_else(|| CryptoError::KeyNotFound("No active version found".into()))
}
pub fn needs_rotation(&self, key_id: &str) -> Result<bool> {
let versions = self
.key_versions
.read()
.map_err(|_| CryptoError::MemoryProtectionFailed("Lock poisoned".into()))?;
let key_versions = versions
.get(key_id)
.ok_or_else(|| CryptoError::KeyNotFound(key_id.to_string()))?;
if let Some(active_version) = key_versions.iter().find(|v| v.is_active) {
let policy = self
.get_policy(active_version.algorithm)
.unwrap_or_default();
let now = Utc::now();
if active_version.expires_at < now {
return Ok(true);
}
if let Some(max_usage) = policy.max_key_usage {
if active_version.usage_count >= max_usage {
return Ok(true);
}
}
let schedule = self
.rotation_schedule
.read()
.map_err(|_| CryptoError::MemoryProtectionFailed("Lock poisoned".into()))?;
if let Some(&rotation_time) = schedule.get(key_id) {
if now >= rotation_time {
return Ok(true);
}
}
}
Ok(false)
}
pub fn rotate_key(
&self,
key_manager: &dyn KeyManagerOperations,
key_id: &str,
algorithm: Algorithm,
) -> Result<String> {
if !self.needs_rotation(key_id)? {
return Err(CryptoError::KeyError("Key does not need rotation".into()));
}
let new_key_id = self.create_key_version(key_manager, algorithm)?;
let policy = self.get_policy(algorithm).unwrap_or_default();
if policy.auto_rotation_enabled {
let mut schedule = self
.rotation_schedule
.write()
.map_err(|_| CryptoError::MemoryProtectionFailed("Lock poisoned".into()))?;
schedule.insert(new_key_id.clone(), Utc::now() + policy.rotation_interval);
}
AuditLogger::log("KEY_ROTATED", Some(algorithm), Some(&new_key_id), Ok(()));
Ok(new_key_id)
}
pub fn increment_key_usage(&self, key_id: &str) -> Result<()> {
let mut versions = self
.key_versions
.write()
.map_err(|_| CryptoError::MemoryProtectionFailed("Lock poisoned".into()))?;
let key_versions = versions
.get_mut(key_id)
.ok_or_else(|| CryptoError::KeyNotFound(key_id.to_string()))?;
if let Some(active_version) = key_versions.iter_mut().find(|v| v.is_active) {
active_version.usage_count += 1;
}
Ok(())
}
pub fn get_rotation_warning(&self, key_id: &str) -> Result<Option<String>> {
let versions = self
.key_versions
.read()
.map_err(|_| CryptoError::MemoryProtectionFailed("Lock poisoned".into()))?;
let key_versions = versions
.get(key_id)
.ok_or_else(|| CryptoError::KeyNotFound(key_id.to_string()))?;
let policy = self.get_policy(Algorithm::AES256GCM).unwrap_or_default();
let now = Utc::now();
if let Some(active_version) = key_versions.iter().find(|v| v.is_active) {
if let Some(max_usage) = policy.max_key_usage {
if active_version.usage_count >= max_usage {
return Ok(Some(format!(
"Key {} has reached usage limit ({} uses). Consider rotating it.",
key_id, active_version.usage_count
)));
}
}
let warning_time = active_version.expires_at - policy.rotation_warning_period;
if now >= warning_time {
let days_until_expiry = (active_version.expires_at - now).num_days();
return Ok(Some(format!(
"Key {} will expire in {} days. Consider rotating it.",
key_id, days_until_expiry
)));
}
}
Ok(None)
}
pub fn get_rotation_warning_for_key(
&self,
key_manager: &dyn KeyManagerOperations,
key_id: &str,
) -> Result<Option<String>> {
key_manager.get_key_operation(key_id)?;
let policy = self.get_policy(Algorithm::AES256GCM).unwrap_or_default();
if policy.rotation_warning_period > Duration::zero() {
let schedule = self
.rotation_schedule
.read()
.map_err(|_| CryptoError::MemoryProtectionFailed("Lock poisoned".into()))?;
if let Some(next_rotation) = schedule.get(key_id) {
let now = Utc::now();
let duration = *next_rotation - now;
if duration > Duration::zero() {
if duration < policy.rotation_warning_period {
return Ok(Some(format!(
"Key {} is expiring in less than {:?}. Please rotate soon.",
key_id, duration
)));
}
} else {
return Ok(Some(format!(
"Key {} has expired. Please rotate immediately.",
key_id
)));
}
} else {
return Ok(Some(format!(
"Key {} exists but has no rotation schedule. It may be unmanaged.",
key_id
)));
}
}
Ok(None)
}
pub fn destroy_key_with_versions(
&self,
key_manager: &dyn KeyManagerOperations,
key_id: &str,
) -> Result<()> {
let mut versions = self
.key_versions
.write()
.map_err(|_| CryptoError::MemoryProtectionFailed("Lock poisoned".into()))?;
if let Some(key_versions) = versions.remove(key_id) {
for version in key_versions {
key_manager.destroy_key_operation(&version.key_id)?;
}
}
let mut schedule = self
.rotation_schedule
.write()
.map_err(|_| CryptoError::MemoryProtectionFailed("Lock poisoned".into()))?;
schedule.remove(key_id);
AuditLogger::log("KEY_DESTROYED_WITH_VERSIONS", None, Some(key_id), Ok(()));
Ok(())
}
pub fn get_keys_needing_rotation(&self) -> Result<Vec<String>> {
let versions = self
.key_versions
.read()
.map_err(|_| CryptoError::MemoryProtectionFailed("Lock poisoned".into()))?;
let mut keys_needing_rotation = Vec::new();
for (key_id, _) in versions.iter() {
if self.needs_rotation(key_id)? {
keys_needing_rotation.push(key_id.clone());
}
}
Ok(keys_needing_rotation)
}
pub fn rotate_all_expired_keys(
&self,
key_manager: &dyn KeyManagerOperations,
) -> Result<Vec<String>> {
let keys_needing_rotation = self.get_keys_needing_rotation()?;
let mut rotated_keys = Vec::with_capacity(keys_needing_rotation.len());
let versions = self
.key_versions
.read()
.map_err(|_| CryptoError::MemoryProtectionFailed("Lock poisoned".into()))?;
for key_id in &keys_needing_rotation {
if let Some(key_versions) = versions.get(key_id) {
if let Some(active_version) = key_versions.iter().find(|v| v.is_active) {
if let Ok(new_key_id) =
self.rotate_key(key_manager, key_id, active_version.algorithm)
{
rotated_keys.push(new_key_id);
}
}
}
}
Ok(rotated_keys)
}
}
#[allow(dead_code)]
pub trait KeyManagerLifecycleExt: KeyManagerOperations {
#[allow(dead_code)]
fn generate_key_with_lifecycle(
&self,
algorithm: Algorithm,
lifecycle_manager: &KeyLifecycleManager,
) -> Result<String>;
#[allow(dead_code)]
fn rotate_key(
&self,
key_id: &str,
algorithm: Algorithm,
lifecycle_manager: &KeyLifecycleManager,
) -> Result<String>;
#[allow(dead_code)]
fn get_key_lifecycle_status(
&self,
key_id: &str,
lifecycle_manager: &KeyLifecycleManager,
) -> Result<String>;
}