//! Recovery domain logic — encrypted recovery bundle CRUD. use std::sync::Arc; use crate::storage::Store; use super::types::DomainError; /// Maximum recovery bundle size: 64 KiB. const MAX_BUNDLE_SIZE: usize = 64 * 1024; /// Default TTL for recovery bundles: 90 days. pub const DEFAULT_TTL_SECS: u64 = 90 * 24 * 60 * 60; /// Domain service for recovery bundle operations. pub struct RecoveryService { pub store: Arc, } impl RecoveryService { /// Store an encrypted recovery bundle. /// /// `token_hash` is the SHA-256 of a recovery token derived from the code. /// `bundle` is the encrypted blob (opaque to server). /// `ttl_secs` is the time-to-live; 0 uses the default (90 days). pub fn store_bundle( &self, token_hash: &[u8], bundle: Vec, ttl_secs: u64, ) -> Result<(), DomainError> { if token_hash.len() != 32 { return Err(DomainError::BadParams(format!( "token_hash must be 32 bytes, got {}", token_hash.len() ))); } if bundle.is_empty() { return Err(DomainError::BadParams("recovery bundle must not be empty".into())); } if bundle.len() > MAX_BUNDLE_SIZE { return Err(DomainError::BadParams(format!( "recovery bundle exceeds max size ({} > {MAX_BUNDLE_SIZE})", bundle.len() ))); } let ttl = if ttl_secs == 0 { DEFAULT_TTL_SECS } else { ttl_secs }; self.store.store_recovery_bundle(token_hash, bundle, ttl)?; Ok(()) } /// Fetch an encrypted recovery bundle by token_hash. pub fn fetch_bundle(&self, token_hash: &[u8]) -> Result>, DomainError> { if token_hash.len() != 32 { return Err(DomainError::BadParams(format!( "token_hash must be 32 bytes, got {}", token_hash.len() ))); } let bundle = self.store.get_recovery_bundle(token_hash)?; Ok(bundle) } /// Delete an encrypted recovery bundle by token_hash. pub fn delete_bundle(&self, token_hash: &[u8]) -> Result { if token_hash.len() != 32 { return Err(DomainError::BadParams(format!( "token_hash must be 32 bytes, got {}", token_hash.len() ))); } let deleted = self.store.delete_recovery_bundle(token_hash)?; Ok(deleted) } }