use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::RwLock;
use tracing::{debug, error, info};
use crate::api::common::config::{KeyExchangeMethod, SecurityConfig, SrtpProfile};
use crate::api::common::error::SecurityError;
use crate::srtp::{crypto::SrtpCryptoKey, SrtpContext};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum KeyRotationPolicy {
TimeInterval(Duration),
PacketCount(u64),
Manual,
Never,
Combined(Vec<KeyRotationPolicy>),
}
impl KeyRotationPolicy {
pub fn should_rotate(&self, elapsed: Duration, packet_count: u64) -> bool {
match self {
KeyRotationPolicy::TimeInterval(interval) => elapsed >= *interval,
KeyRotationPolicy::PacketCount(threshold) => packet_count >= *threshold,
KeyRotationPolicy::Manual => false,
KeyRotationPolicy::Never => false,
KeyRotationPolicy::Combined(policies) => policies
.iter()
.any(|policy| policy.should_rotate(elapsed, packet_count)),
}
}
pub fn description(&self) -> String {
match self {
KeyRotationPolicy::TimeInterval(duration) => {
format!("Time interval: {:?}", duration)
}
KeyRotationPolicy::PacketCount(count) => {
format!("Packet count: {}", count)
}
KeyRotationPolicy::Manual => "Manual rotation".to_string(),
KeyRotationPolicy::Never => "No rotation".to_string(),
KeyRotationPolicy::Combined(policies) => {
let descriptions: Vec<String> = policies.iter().map(|p| p.description()).collect();
format!("Combined: [{}]", descriptions.join(", "))
}
}
}
pub fn enterprise_standard() -> Self {
Self::Combined(vec![
Self::TimeInterval(Duration::from_secs(3600)), Self::PacketCount(1_000_000), ])
}
pub fn high_security() -> Self {
Self::Combined(vec![
Self::TimeInterval(Duration::from_secs(900)), Self::PacketCount(100_000), ])
}
pub fn development() -> Self {
Self::TimeInterval(Duration::from_secs(300)) }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StreamType {
Audio,
Video,
Data,
Control,
}
impl StreamType {
pub fn derivation_label(&self) -> &'static str {
match self {
StreamType::Audio => "SRTP_AUDIO",
StreamType::Video => "SRTP_VIDEO",
StreamType::Data => "SRTP_DATA",
StreamType::Control => "SRTP_CONTROL",
}
}
pub fn default_srtp_profile(&self) -> SrtpProfile {
match self {
StreamType::Audio => SrtpProfile::AesCm128HmacSha1_80,
StreamType::Video => SrtpProfile::AesCm128HmacSha1_80,
StreamType::Data => SrtpProfile::AesCm128HmacSha1_32,
StreamType::Control => SrtpProfile::AesCm128HmacSha1_32,
}
}
}
pub struct KeyStore {
master_key: Vec<u8>,
stream_contexts: HashMap<StreamType, SrtpContext>,
created_at: Instant,
last_rotation: Option<Instant>,
packet_count: u64,
generation: u32,
}
impl KeyStore {
pub fn new(master_key: Vec<u8>) -> Result<Self, SecurityError> {
if master_key.len() < 32 {
return Err(SecurityError::Configuration(
"Master key too short".to_string(),
));
}
Ok(Self {
master_key,
stream_contexts: HashMap::new(),
created_at: Instant::now(),
last_rotation: None,
packet_count: 0,
generation: 0,
})
}
pub fn derive_stream_key(
&self,
stream_type: StreamType,
) -> Result<SrtpCryptoKey, SecurityError> {
let label = stream_type.derivation_label();
let mut derived_key = Vec::with_capacity(30);
derived_key.extend_from_slice(&self.master_key[0..16.min(self.master_key.len())]);
let label_hash = self.hash_label(label);
for (i, &byte) in label_hash.iter().take(16).enumerate() {
if i < derived_key.len() {
derived_key[i] ^= byte;
}
}
let gen_bytes = self.generation.to_be_bytes();
for (i, &byte) in gen_bytes.iter().enumerate() {
if i < derived_key.len() {
derived_key[i] ^= byte;
}
}
let mut salt = vec![0u8; 14];
if self.master_key.len() >= 30 {
salt.copy_from_slice(&self.master_key[16..30]);
} else {
salt = vec![
0x9e, 0x7c, 0xa4, 0xf0, 0x85, 0x2d, 0x1c, 0x22, 0xf9, 0x8a, 0x1b, 0x5e, 0x6c, 0x3d,
];
}
for (i, &byte) in gen_bytes.iter().cycle().take(salt.len()).enumerate() {
salt[i] ^= byte;
}
Ok(SrtpCryptoKey::new(derived_key, salt))
}
fn hash_label(&self, label: &str) -> Vec<u8> {
let mut hash = vec![0u8; 16];
let label_bytes = label.as_bytes();
for (i, &byte) in label_bytes.iter().enumerate() {
let hash_len = hash.len();
hash[i % hash_len] ^= byte.wrapping_add(i as u8);
}
hash
}
pub fn setup_stream(&mut self, stream_type: StreamType) -> Result<(), SecurityError> {
let stream_key = self.derive_stream_key(stream_type)?;
let crypto_suite = match stream_type.default_srtp_profile() {
SrtpProfile::AesCm128HmacSha1_80 => crate::srtp::SRTP_AES128_CM_SHA1_80,
SrtpProfile::AesCm128HmacSha1_32 => crate::srtp::SRTP_AES128_CM_SHA1_32,
_ => {
return Err(SecurityError::Configuration(
"Unsupported SRTP profile".to_string(),
))
}
};
let srtp_context = SrtpContext::new(crypto_suite, stream_key).map_err(|e| {
SecurityError::CryptoError(format!("Failed to create SRTP context: {}", e))
})?;
self.stream_contexts.insert(stream_type, srtp_context);
debug!(
"Set up SRTP context for {:?} stream (generation {})",
stream_type, self.generation
);
Ok(())
}
pub fn get_stream_context(&mut self, stream_type: StreamType) -> Option<&mut SrtpContext> {
self.stream_contexts.get_mut(&stream_type)
}
pub fn rotate_keys(&mut self) -> Result<(), SecurityError> {
self.generation += 1;
self.last_rotation = Some(Instant::now());
self.packet_count = 0;
info!("Rotating keys to generation {}", self.generation);
let stream_types: Vec<StreamType> = self.stream_contexts.keys().cloned().collect();
for stream_type in stream_types {
self.setup_stream(stream_type)?;
}
Ok(())
}
pub fn increment_packet_count(&mut self) {
self.packet_count += 1;
}
pub fn elapsed_time(&self) -> Duration {
match self.last_rotation {
Some(last) => last.elapsed(),
None => self.created_at.elapsed(),
}
}
pub fn packet_count(&self) -> u64 {
self.packet_count
}
pub fn generation(&self) -> u32 {
self.generation
}
pub fn configured_streams(&self) -> Vec<StreamType> {
self.stream_contexts.keys().cloned().collect()
}
}
#[derive(Debug, Clone)]
pub struct KeySyndicationConfig {
pub stream_types: Vec<StreamType>,
pub auto_setup_streams: bool,
pub synchronized_rotation: bool,
}
impl Default for KeySyndicationConfig {
fn default() -> Self {
Self {
stream_types: vec![StreamType::Audio, StreamType::Video],
auto_setup_streams: true,
synchronized_rotation: true,
}
}
}
impl KeySyndicationConfig {
pub fn audio_only() -> Self {
Self {
stream_types: vec![StreamType::Audio],
auto_setup_streams: true,
synchronized_rotation: true,
}
}
pub fn multimedia() -> Self {
Self {
stream_types: vec![StreamType::Audio, StreamType::Video, StreamType::Data],
auto_setup_streams: true,
synchronized_rotation: true,
}
}
pub fn full_control() -> Self {
Self {
stream_types: vec![
StreamType::Audio,
StreamType::Video,
StreamType::Data,
StreamType::Control,
],
auto_setup_streams: true,
synchronized_rotation: true,
}
}
}
pub struct KeySyndication {
config: KeySyndicationConfig,
sessions: HashMap<String, KeyStore>,
}
impl KeySyndication {
pub fn new(config: KeySyndicationConfig) -> Self {
Self {
config,
sessions: HashMap::new(),
}
}
pub fn create_session(
&mut self,
session_id: String,
master_key: Vec<u8>,
) -> Result<(), SecurityError> {
let mut key_store = KeyStore::new(master_key)?;
if self.config.auto_setup_streams {
for &stream_type in &self.config.stream_types {
key_store.setup_stream(stream_type)?;
}
}
self.sessions.insert(session_id.clone(), key_store);
info!("Created key syndication session: {}", session_id);
Ok(())
}
pub fn get_session_mut(&mut self, session_id: &str) -> Option<&mut KeyStore> {
self.sessions.get_mut(session_id)
}
pub fn add_stream(
&mut self,
session_id: &str,
stream_type: StreamType,
) -> Result<(), SecurityError> {
let key_store = self
.sessions
.get_mut(session_id)
.ok_or_else(|| SecurityError::NotFound(format!("Session not found: {}", session_id)))?;
key_store.setup_stream(stream_type)?;
info!("Added {:?} stream to session {}", stream_type, session_id);
Ok(())
}
pub fn remove_session(&mut self, session_id: &str) -> bool {
let removed = self.sessions.remove(session_id).is_some();
if removed {
info!("Removed key syndication session: {}", session_id);
}
removed
}
pub fn session_count(&self) -> usize {
self.sessions.len()
}
pub fn session_ids(&self) -> Vec<String> {
self.sessions.keys().cloned().collect()
}
}
#[derive(Debug, Clone)]
pub struct SecurityPolicy {
pub required_methods: Vec<KeyExchangeMethod>,
pub min_rotation_interval: Option<Duration>,
pub max_key_lifetime: Option<Duration>,
pub required_srtp_profiles: Vec<SrtpProfile>,
pub strict_validation: bool,
pub require_pfs: bool,
}
impl Default for SecurityPolicy {
fn default() -> Self {
Self {
required_methods: vec![KeyExchangeMethod::Sdes, KeyExchangeMethod::DtlsSrtp],
min_rotation_interval: Some(Duration::from_secs(3600)), max_key_lifetime: Some(Duration::from_secs(86400)), required_srtp_profiles: vec![SrtpProfile::AesCm128HmacSha1_80],
strict_validation: true,
require_pfs: false,
}
}
}
impl SecurityPolicy {
pub fn enterprise() -> Self {
Self {
required_methods: vec![KeyExchangeMethod::Mikey, KeyExchangeMethod::Sdes],
min_rotation_interval: Some(Duration::from_secs(1800)), max_key_lifetime: Some(Duration::from_secs(7200)), required_srtp_profiles: vec![SrtpProfile::AesCm128HmacSha1_80],
strict_validation: true,
require_pfs: true,
}
}
pub fn high_security() -> Self {
Self {
required_methods: vec![KeyExchangeMethod::Zrtp, KeyExchangeMethod::Mikey],
min_rotation_interval: Some(Duration::from_secs(900)), max_key_lifetime: Some(Duration::from_secs(3600)), required_srtp_profiles: vec![SrtpProfile::AesCm128HmacSha1_80, SrtpProfile::AesGcm128],
strict_validation: true,
require_pfs: true,
}
}
pub fn development() -> Self {
Self {
required_methods: vec![
KeyExchangeMethod::PreSharedKey,
KeyExchangeMethod::Sdes,
KeyExchangeMethod::DtlsSrtp,
],
min_rotation_interval: None,
max_key_lifetime: None,
required_srtp_profiles: vec![
SrtpProfile::AesCm128HmacSha1_80,
SrtpProfile::AesCm128HmacSha1_32,
],
strict_validation: false,
require_pfs: false,
}
}
pub fn validate_config(&self, config: &SecurityConfig) -> Result<(), SecurityError> {
let method = config.mode.key_exchange_method().ok_or_else(|| {
SecurityError::PolicyViolation("No key exchange method configured".to_string())
})?;
if !self.required_methods.contains(&method) {
return Err(SecurityError::PolicyViolation(format!(
"Key exchange method {:?} not allowed by policy",
method
)));
}
for profile in &config.srtp_profiles {
if !self.required_srtp_profiles.contains(profile) {
return Err(SecurityError::PolicyViolation(format!(
"SRTP profile {:?} not allowed by policy",
profile
)));
}
}
Ok(())
}
pub fn validate_rotation_policy(
&self,
policy: &KeyRotationPolicy,
) -> Result<(), SecurityError> {
if let Some(min_interval) = self.min_rotation_interval {
match policy {
KeyRotationPolicy::TimeInterval(interval) => {
if *interval < min_interval {
return Err(SecurityError::PolicyViolation(format!(
"Rotation interval {:?} is less than minimum {:?}",
interval, min_interval
)));
}
}
KeyRotationPolicy::Never => {
return Err(SecurityError::PolicyViolation(
"Policy requires key rotation but 'Never' policy specified".to_string(),
));
}
KeyRotationPolicy::Combined(policies) => {
for sub_policy in policies {
self.validate_rotation_policy(sub_policy)?;
}
}
_ => {} }
}
Ok(())
}
}
pub struct KeyManager {
rotation_policy: Arc<RwLock<KeyRotationPolicy>>,
key_store: Arc<RwLock<Option<KeyStore>>>,
syndication: Arc<RwLock<KeySyndication>>,
security_policy: Arc<RwLock<SecurityPolicy>>,
rotation_task: Arc<RwLock<Option<tokio::task::JoinHandle<()>>>>,
}
impl KeyManager {
pub fn new(
rotation_policy: KeyRotationPolicy,
syndication_config: KeySyndicationConfig,
security_policy: SecurityPolicy,
) -> Self {
Self {
rotation_policy: Arc::new(RwLock::new(rotation_policy)),
key_store: Arc::new(RwLock::new(None)),
syndication: Arc::new(RwLock::new(KeySyndication::new(syndication_config))),
security_policy: Arc::new(RwLock::new(security_policy)),
rotation_task: Arc::new(RwLock::new(None)),
}
}
pub async fn initialize(&self, master_key: Vec<u8>) -> Result<(), SecurityError> {
let key_store = KeyStore::new(master_key)?;
*self.key_store.write().await = Some(key_store);
self.start_rotation_task().await;
Ok(())
}
async fn start_rotation_task(&self) {
let policy = self.rotation_policy.read().await.clone();
match policy {
KeyRotationPolicy::TimeInterval(interval) => {
let key_store = self.key_store.clone();
let _rotation_policy = self.rotation_policy.clone();
let handle = tokio::spawn(async move {
let mut rotation_interval = tokio::time::interval(interval);
rotation_interval
.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
loop {
rotation_interval.tick().await;
if let Some(store) = key_store.write().await.as_mut() {
if let Err(e) = store.rotate_keys() {
error!("Automatic key rotation failed: {}", e);
} else {
info!(
"Automatic key rotation completed (generation {})",
store.generation()
);
}
}
}
});
*self.rotation_task.write().await = Some(handle);
info!(
"Started automatic key rotation task with interval {:?}",
interval
);
}
_ => {
debug!("No automatic rotation task needed for policy: {:?}", policy);
}
}
}
pub async fn stop_rotation_task(&self) {
if let Some(handle) = self.rotation_task.write().await.take() {
handle.abort();
info!("Stopped automatic key rotation task");
}
}
pub async fn rotate_keys(&self) -> Result<(), SecurityError> {
if let Some(key_store) = self.key_store.write().await.as_mut() {
key_store.rotate_keys()?;
info!(
"Manual key rotation completed (generation {})",
key_store.generation()
);
}
Ok(())
}
pub async fn check_and_rotate(&self) -> Result<bool, SecurityError> {
let policy = self.rotation_policy.read().await.clone();
if let Some(key_store) = self.key_store.write().await.as_mut() {
let elapsed = key_store.elapsed_time();
let packet_count = key_store.packet_count();
if policy.should_rotate(elapsed, packet_count) {
key_store.rotate_keys()?;
info!("Key rotation triggered by policy: {}", policy.description());
return Ok(true);
}
}
Ok(false)
}
pub async fn syndication(&self) -> tokio::sync::RwLockReadGuard<'_, KeySyndication> {
self.syndication.read().await
}
pub async fn syndication_mut(&self) -> tokio::sync::RwLockWriteGuard<'_, KeySyndication> {
self.syndication.write().await
}
pub async fn validate_config(&self, config: &SecurityConfig) -> Result<(), SecurityError> {
let policy = self.security_policy.read().await;
policy.validate_config(config)
}
pub async fn get_statistics(&self) -> KeyManagerStatistics {
let mut stats = KeyManagerStatistics::default();
if let Some(key_store) = self.key_store.read().await.as_ref() {
stats.current_generation = key_store.generation();
stats.elapsed_time = key_store.elapsed_time();
stats.packet_count = key_store.packet_count();
stats.configured_streams = key_store.configured_streams().len();
}
let syndication = self.syndication.read().await;
stats.active_sessions = syndication.session_count();
stats
}
}
#[derive(Debug, Default)]
pub struct KeyManagerStatistics {
pub current_generation: u32,
pub elapsed_time: Duration,
pub packet_count: u64,
pub configured_streams: usize,
pub active_sessions: usize,
}
impl Drop for KeyManager {
fn drop(&mut self) {
debug!("KeyManager dropped");
}
}