#[cfg(test)]
use crate::api::common::config::SecurityMode;
use std::sync::Arc;
use tokio::sync::RwLock;
use crate::api::common::config::{KeyExchangeMethod, SecurityConfig};
use crate::api::common::error::SecurityError;
use crate::security::SecurityKeyExchange;
use crate::srtp::{crypto::SrtpCryptoKey, SrtpContext, SrtpCryptoSuite};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SecurityState {
Initial,
Negotiating,
Established,
Failed,
Disabled,
}
#[derive(Debug, Clone)]
pub enum KeyExchangeConfig {
DtlsSrtp {
certificate_path: Option<String>,
private_key_path: Option<String>,
fingerprint_algorithm: String,
},
Sdes {
crypto_suites: Vec<SrtpCryptoSuite>,
offer_count: usize,
},
Mikey {
psk: Option<Vec<u8>>,
identity: Option<String>,
mode: MikeyMode,
},
Zrtp {
enable_sas: bool,
cache_expiry: std::time::Duration,
},
PreSharedKey { key: Vec<u8>, salt: Vec<u8> },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MikeyMode {
Psk,
Pke,
}
pub struct UnifiedSecurityContext {
config: SecurityConfig,
method: KeyExchangeMethod,
method_config: KeyExchangeConfig,
state: Arc<RwLock<SecurityState>>,
key_exchange: Arc<RwLock<Option<Box<dyn SecurityKeyExchange + Send + Sync>>>>,
srtp_context: Arc<RwLock<Option<SrtpContext>>>,
}
impl std::fmt::Debug for UnifiedSecurityContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("UnifiedSecurityContext")
.field("method", &self.method)
.field("config", &self.config)
.field("method_config", &self.method_config)
.field("state", &"<RwLock<SecurityState>>")
.field("key_exchange", &"<Box<dyn SecurityKeyExchange>>")
.field("srtp_context", &"<RwLock<Option<SrtpContext>>>")
.finish()
}
}
impl UnifiedSecurityContext {
pub fn new(config: SecurityConfig) -> Result<Self, SecurityError> {
let method = match config.mode.key_exchange_method() {
Some(method) => method,
None => {
return Err(SecurityError::Configuration(
"No key exchange method for security mode".to_string(),
))
}
};
let method_config = Self::create_method_config(&config, method)?;
Ok(Self {
state: Arc::new(RwLock::new(SecurityState::Initial)),
method,
config,
key_exchange: Arc::new(RwLock::new(None)),
srtp_context: Arc::new(RwLock::new(None)),
method_config,
})
}
fn create_method_config(
config: &SecurityConfig,
method: KeyExchangeMethod,
) -> Result<KeyExchangeConfig, SecurityError> {
match method {
KeyExchangeMethod::DtlsSrtp => Ok(KeyExchangeConfig::DtlsSrtp {
certificate_path: config.certificate_path.clone(),
private_key_path: config.private_key_path.clone(),
fingerprint_algorithm: config.fingerprint_algorithm.clone(),
}),
KeyExchangeMethod::Sdes => {
let crypto_suites = config
.srtp_profiles
.iter()
.filter_map(|profile| match profile {
crate::api::common::config::SrtpProfile::AesCm128HmacSha1_80 => {
Some(crate::srtp::SRTP_AES128_CM_SHA1_80)
}
crate::api::common::config::SrtpProfile::AesCm128HmacSha1_32 => {
Some(crate::srtp::SRTP_AES128_CM_SHA1_32)
}
_ => None, })
.collect();
Ok(KeyExchangeConfig::Sdes {
crypto_suites,
offer_count: 2,
})
}
KeyExchangeMethod::Mikey => {
Ok(KeyExchangeConfig::Mikey {
psk: config.srtp_key.clone(),
identity: None,
mode: MikeyMode::Psk, })
}
KeyExchangeMethod::Zrtp => {
Ok(KeyExchangeConfig::Zrtp {
enable_sas: true,
cache_expiry: std::time::Duration::from_secs(3600), })
}
KeyExchangeMethod::PreSharedKey => {
match &config.srtp_key {
Some(key) => {
let salt = if key.len() >= 30 {
key[16..30].to_vec()
} else {
vec![0u8; 14] };
let actual_key = if key.len() >= 16 {
key[0..16].to_vec()
} else {
return Err(SecurityError::Configuration(
"Pre-shared key too short".to_string(),
));
};
Ok(KeyExchangeConfig::PreSharedKey {
key: actual_key,
salt,
})
}
None => Err(SecurityError::Configuration(
"Pre-shared key required for PSK mode".to_string(),
)),
}
}
}
}
pub async fn initialize(&self) -> Result<(), SecurityError> {
let mut state = self.state.write().await;
if *state != SecurityState::Initial {
return Err(SecurityError::InvalidState(
"Context already initialized".to_string(),
));
}
let key_exchange_impl: Box<dyn SecurityKeyExchange + Send + Sync> = match self.method {
KeyExchangeMethod::DtlsSrtp => {
return Err(SecurityError::Configuration(
"DTLS-SRTP should use existing security contexts".to_string(),
));
}
KeyExchangeMethod::Sdes => {
if let KeyExchangeConfig::Sdes {
crypto_suites,
offer_count,
} = &self.method_config
{
let sdes_config = crate::security::sdes::SdesConfig {
crypto_suites: crypto_suites.clone(),
offer_count: *offer_count,
};
let sdes = crate::security::sdes::Sdes::new(
sdes_config,
crate::security::sdes::SdesRole::Offerer,
);
Box::new(sdes)
} else {
return Err(SecurityError::Configuration(
"Invalid SDES configuration".to_string(),
));
}
}
KeyExchangeMethod::Mikey => {
if let KeyExchangeConfig::Mikey {
psk,
identity: _,
mode,
} = &self.method_config
{
match mode {
MikeyMode::Psk => {
let mikey_config = crate::security::mikey::MikeyConfig {
method: crate::security::mikey::MikeyKeyExchangeMethod::Psk,
psk: psk.clone(),
srtp_profile: crate::srtp::SRTP_AES128_CM_SHA1_80,
..Default::default()
};
let mikey = crate::security::mikey::Mikey::new(
mikey_config,
crate::security::mikey::MikeyRole::Initiator,
);
Box::new(mikey)
}
MikeyMode::Pke => {
let mikey_config = crate::security::mikey::MikeyConfig {
method: crate::security::mikey::MikeyKeyExchangeMethod::Pk,
certificate: self.config.certificate_data.clone(),
private_key: self.config.private_key_data.clone(),
peer_certificate: self.config.peer_certificate_data.clone(),
srtp_profile: crate::srtp::SRTP_AES128_CM_SHA1_80,
..Default::default()
};
let mikey = crate::security::mikey::Mikey::new(
mikey_config,
crate::security::mikey::MikeyRole::Initiator,
);
Box::new(mikey)
}
}
} else {
return Err(SecurityError::Configuration(
"Invalid MIKEY configuration".to_string(),
));
}
}
KeyExchangeMethod::Zrtp => {
if let KeyExchangeConfig::Zrtp {
enable_sas,
cache_expiry: _,
} = &self.method_config
{
let zrtp_config = crate::security::zrtp::ZrtpConfig {
ciphers: vec![crate::security::zrtp::ZrtpCipher::Aes1],
hashes: vec![crate::security::zrtp::ZrtpHash::S256],
auth_tags: vec![
crate::security::zrtp::ZrtpAuthTag::HS80,
crate::security::zrtp::ZrtpAuthTag::HS32,
],
key_agreements: vec![crate::security::zrtp::ZrtpKeyAgreement::EC25],
sas_types: if *enable_sas {
vec![crate::security::zrtp::ZrtpSasType::B32]
} else {
vec![]
},
client_id: "RVOIP Unified Security".to_string(),
srtp_profile: crate::srtp::SRTP_AES128_CM_SHA1_80,
};
let zrtp = crate::security::zrtp::Zrtp::new(
zrtp_config,
crate::security::zrtp::ZrtpRole::Initiator,
);
Box::new(zrtp)
} else {
return Err(SecurityError::Configuration(
"Invalid ZRTP configuration".to_string(),
));
}
}
KeyExchangeMethod::PreSharedKey => {
if let KeyExchangeConfig::PreSharedKey { key, salt } = &self.method_config {
let srtp_key = SrtpCryptoKey::new(key.clone(), salt.clone());
let srtp_context = SrtpContext::new(
crate::srtp::SRTP_AES128_CM_SHA1_80, srtp_key,
)
.map_err(|e| {
SecurityError::CryptoError(format!("Failed to create SRTP context: {}", e))
})?;
*self.srtp_context.write().await = Some(srtp_context);
*state = SecurityState::Established;
return Ok(());
} else {
return Err(SecurityError::Configuration(
"Invalid PSK configuration".to_string(),
));
}
}
};
let mut key_exchange_mut = key_exchange_impl;
key_exchange_mut.init().map_err(|e| {
SecurityError::CryptoError(format!("Failed to initialize key exchange: {}", e))
})?;
*self.key_exchange.write().await = Some(key_exchange_mut);
*state = SecurityState::Negotiating;
Ok(())
}
pub async fn process_message(&self, message: &[u8]) -> Result<Option<Vec<u8>>, SecurityError> {
let state = self.state.read().await;
if *state != SecurityState::Negotiating {
return Err(SecurityError::InvalidState(
"Key exchange not in progress".to_string(),
));
}
drop(state);
let mut key_exchange_guard = self.key_exchange.write().await;
let key_exchange = key_exchange_guard.as_mut().ok_or_else(|| {
SecurityError::NotInitialized("Key exchange not initialized".to_string())
})?;
let response = key_exchange
.process_message(message)
.map_err(|e| SecurityError::CryptoError(format!("Key exchange failed: {}", e)))?;
if key_exchange.is_complete() {
if let (Some(srtp_key), Some(srtp_suite)) =
(key_exchange.get_srtp_key(), key_exchange.get_srtp_suite())
{
let srtp_context = SrtpContext::new(srtp_suite, srtp_key).map_err(|e| {
SecurityError::CryptoError(format!("Failed to create SRTP context: {}", e))
})?;
*self.srtp_context.write().await = Some(srtp_context);
*self.state.write().await = SecurityState::Established;
} else {
*self.state.write().await = SecurityState::Failed;
return Err(SecurityError::CryptoError(
"Key exchange completed but no keys available".to_string(),
));
}
}
Ok(response)
}
pub async fn is_established(&self) -> bool {
*self.state.read().await == SecurityState::Established
}
pub async fn get_state(&self) -> SecurityState {
*self.state.read().await
}
pub fn get_method(&self) -> KeyExchangeMethod {
self.method
}
pub async fn get_srtp_context(&self) -> Option<Arc<RwLock<SrtpContext>>> {
let guard = self.srtp_context.read().await;
if guard.is_some() {
None } else {
None
}
}
pub async fn protect_rtp(
&self,
packet: &crate::packet::RtpPacket,
) -> Result<crate::srtp::ProtectedRtpPacket, SecurityError> {
let mut srtp_guard = self.srtp_context.write().await;
let srtp_context = srtp_guard.as_mut().ok_or_else(|| {
SecurityError::NotInitialized("SRTP context not established".to_string())
})?;
srtp_context
.protect(packet)
.map_err(|e| SecurityError::CryptoError(format!("SRTP encryption failed: {}", e)))
}
pub async fn unprotect_rtp(
&self,
data: &[u8],
) -> Result<crate::packet::RtpPacket, SecurityError> {
let mut srtp_guard = self.srtp_context.write().await;
let srtp_context = srtp_guard.as_mut().ok_or_else(|| {
SecurityError::NotInitialized("SRTP context not established".to_string())
})?;
srtp_context
.unprotect(data)
.map_err(|e| SecurityError::CryptoError(format!("SRTP decryption failed: {}", e)))
}
}
pub struct SecurityContextFactory;
impl SecurityContextFactory {
pub fn create_context(config: SecurityConfig) -> Result<UnifiedSecurityContext, SecurityError> {
UnifiedSecurityContext::new(config)
}
pub fn create_sdes_context() -> Result<UnifiedSecurityContext, SecurityError> {
let config = SecurityConfig::sdes_srtp();
Self::create_context(config)
}
pub fn create_mikey_psk_context(psk: Vec<u8>) -> Result<UnifiedSecurityContext, SecurityError> {
let mut config = SecurityConfig::mikey_psk();
config.srtp_key = Some(psk);
Self::create_context(config)
}
pub fn create_zrtp_context() -> Result<UnifiedSecurityContext, SecurityError> {
let config = SecurityConfig::zrtp_p2p();
Self::create_context(config)
}
pub fn create_psk_context(key: Vec<u8>) -> Result<UnifiedSecurityContext, SecurityError> {
let config = SecurityConfig::srtp_with_key(key);
Self::create_context(config)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::api::common::config::{SecurityConfig, SecurityProfile};
fn test_srtp_key() -> Vec<u8> {
vec![
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E,
]
}
#[tokio::test]
async fn test_create_psk_context() {
let key = test_srtp_key();
let context = SecurityContextFactory::create_psk_context(key).unwrap();
assert_eq!(context.get_method(), KeyExchangeMethod::PreSharedKey);
assert_eq!(context.get_state().await, SecurityState::Initial);
}
#[tokio::test]
async fn test_psk_initialization() {
let key = test_srtp_key();
let context = SecurityContextFactory::create_psk_context(key).unwrap();
context.initialize().await.unwrap();
assert!(context.is_established().await);
assert_eq!(context.get_state().await, SecurityState::Established);
}
#[test]
fn test_create_sdes_context() {
let context = SecurityContextFactory::create_sdes_context().unwrap();
assert_eq!(context.get_method(), KeyExchangeMethod::Sdes);
}
#[test]
fn test_create_mikey_context() {
let key = test_srtp_key();
let context = SecurityContextFactory::create_mikey_psk_context(key).unwrap();
assert_eq!(context.get_method(), KeyExchangeMethod::Mikey);
}
#[test]
fn test_create_zrtp_context() {
let context = SecurityContextFactory::create_zrtp_context().unwrap();
assert_eq!(context.get_method(), KeyExchangeMethod::Zrtp);
}
#[test]
fn test_security_config_creation() {
let sdes_config = SecurityConfig::sdes_srtp();
assert_eq!(sdes_config.mode, SecurityMode::SdesSrtp);
assert_eq!(sdes_config.profile, SecurityProfile::SdesSrtp);
let mikey_config = SecurityConfig::mikey_psk();
assert_eq!(mikey_config.mode, SecurityMode::MikeySrtp);
assert_eq!(mikey_config.profile, SecurityProfile::MikeyPsk);
let zrtp_config = SecurityConfig::zrtp_p2p();
assert_eq!(zrtp_config.mode, SecurityMode::ZrtpSrtp);
assert_eq!(zrtp_config.profile, SecurityProfile::ZrtpP2P);
}
#[test]
fn test_key_exchange_method_properties() {
assert!(KeyExchangeMethod::Sdes.requires_network_exchange());
assert!(KeyExchangeMethod::Sdes.uses_signaling_exchange());
assert!(!KeyExchangeMethod::Sdes.uses_media_exchange());
assert!(KeyExchangeMethod::Zrtp.requires_network_exchange());
assert!(!KeyExchangeMethod::Zrtp.uses_signaling_exchange());
assert!(KeyExchangeMethod::Zrtp.uses_media_exchange());
assert!(!KeyExchangeMethod::PreSharedKey.requires_network_exchange());
assert!(!KeyExchangeMethod::PreSharedKey.uses_signaling_exchange());
assert!(!KeyExchangeMethod::PreSharedKey.uses_media_exchange());
}
#[test]
fn test_security_mode_conversions() {
assert_eq!(
SecurityMode::SdesSrtp.key_exchange_method(),
Some(KeyExchangeMethod::Sdes)
);
assert_eq!(
SecurityMode::MikeySrtp.key_exchange_method(),
Some(KeyExchangeMethod::Mikey)
);
assert_eq!(
SecurityMode::ZrtpSrtp.key_exchange_method(),
Some(KeyExchangeMethod::Zrtp)
);
assert_eq!(SecurityMode::None.key_exchange_method(), None);
assert_eq!(
KeyExchangeMethod::Sdes.to_security_mode(),
SecurityMode::SdesSrtp
);
assert_eq!(
KeyExchangeMethod::Mikey.to_security_mode(),
SecurityMode::MikeySrtp
);
assert_eq!(
KeyExchangeMethod::Zrtp.to_security_mode(),
SecurityMode::ZrtpSrtp
);
}
#[test]
fn test_invalid_psk_key() {
let short_key = vec![0x01, 0x02, 0x03]; let result = SecurityContextFactory::create_psk_context(short_key);
assert!(result.is_err());
}
#[tokio::test]
async fn test_sdes_initialization_placeholder() {
let context = SecurityContextFactory::create_sdes_context().unwrap();
let result = context.initialize().await;
assert!(result.is_ok());
assert_eq!(context.get_state().await, SecurityState::Negotiating);
}
#[tokio::test]
async fn test_mikey_initialization_placeholder() {
let key = test_srtp_key();
let context = SecurityContextFactory::create_mikey_psk_context(key).unwrap();
let result = context.initialize().await;
assert!(result.is_ok()); assert_eq!(context.get_state().await, SecurityState::Negotiating);
}
#[tokio::test]
async fn test_zrtp_initialization_success() {
let context = SecurityContextFactory::create_zrtp_context().unwrap();
let result = context.initialize().await;
assert!(result.is_ok()); assert_eq!(context.get_state().await, SecurityState::Negotiating);
}
#[test]
fn test_method_config_creation() {
let key = test_srtp_key();
let config = SecurityConfig::srtp_with_key(key);
let context = UnifiedSecurityContext::new(config).unwrap();
assert_eq!(context.method, KeyExchangeMethod::PreSharedKey);
match &context.method_config {
KeyExchangeConfig::PreSharedKey { key, salt } => {
assert_eq!(key.len(), 16); assert_eq!(salt.len(), 14); }
_ => panic!("Expected PreSharedKey config"),
}
}
#[test]
fn test_sip_scenario_configs() {
let enterprise = SecurityConfig::sip_enterprise();
assert_eq!(enterprise.mode, SecurityMode::MikeySrtp);
let operator = SecurityConfig::sip_operator();
assert_eq!(operator.mode, SecurityMode::SdesSrtp);
let p2p = SecurityConfig::sip_peer_to_peer();
assert_eq!(p2p.mode, SecurityMode::ZrtpSrtp);
let bridge = SecurityConfig::sip_webrtc_bridge();
assert_eq!(bridge.mode, SecurityMode::SdesSrtp); }
#[test]
fn test_multi_method_config() {
let methods = vec![KeyExchangeMethod::Sdes, KeyExchangeMethod::DtlsSrtp];
let config = SecurityConfig::multi_method(methods);
assert_eq!(config.mode, SecurityMode::SdesSrtp); }
}