pub mod auth;
pub mod crypto;
pub mod key_derivation;
pub use auth::{SrtpAuthenticator, SrtpReplayProtection};
pub use crypto::SrtpCryptoKey;
pub use key_derivation::{
create_srtp_iv, srtp_kdf, KeyDerivationLabel, KeyRotationFrequency, SrtpKeyDerivationParams,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SrtpEncryptionAlgorithm {
AesCm,
AesF8,
Null,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SrtpAuthenticationAlgorithm {
HmacSha1_80,
HmacSha1_32,
Null,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SrtpCryptoSuite {
pub encryption: SrtpEncryptionAlgorithm,
pub authentication: SrtpAuthenticationAlgorithm,
pub key_length: usize,
pub tag_length: usize,
}
pub const SRTP_AES128_CM_SHA1_80: SrtpCryptoSuite = SrtpCryptoSuite {
encryption: SrtpEncryptionAlgorithm::AesCm,
authentication: SrtpAuthenticationAlgorithm::HmacSha1_80,
key_length: 16, tag_length: 10, };
pub const SRTP_AES128_CM_SHA1_32: SrtpCryptoSuite = SrtpCryptoSuite {
encryption: SrtpEncryptionAlgorithm::AesCm,
authentication: SrtpAuthenticationAlgorithm::HmacSha1_32,
key_length: 16, tag_length: 4, };
pub const SRTP_AES256_CM_SHA1_80: SrtpCryptoSuite = SrtpCryptoSuite {
encryption: SrtpEncryptionAlgorithm::AesCm,
authentication: SrtpAuthenticationAlgorithm::HmacSha1_80,
key_length: 32, tag_length: 10, };
pub const SRTP_AES256_CM_SHA1_32: SrtpCryptoSuite = SrtpCryptoSuite {
encryption: SrtpEncryptionAlgorithm::AesCm,
authentication: SrtpAuthenticationAlgorithm::HmacSha1_32,
key_length: 32, tag_length: 4, };
pub const SRTP_NULL_SHA1_80: SrtpCryptoSuite = SrtpCryptoSuite {
encryption: SrtpEncryptionAlgorithm::Null,
authentication: SrtpAuthenticationAlgorithm::HmacSha1_80,
key_length: 16, tag_length: 10, };
pub const SRTP_NULL_NULL: SrtpCryptoSuite = SrtpCryptoSuite {
encryption: SrtpEncryptionAlgorithm::Null,
authentication: SrtpAuthenticationAlgorithm::Null,
key_length: 16, tag_length: 0,
};
pub const SRTP_AEAD_AES_128_GCM: SrtpCryptoSuite = SrtpCryptoSuite {
encryption: SrtpEncryptionAlgorithm::AesCm, authentication: SrtpAuthenticationAlgorithm::Null, key_length: 16, tag_length: 16, };
pub const SRTP_AEAD_AES_256_GCM: SrtpCryptoSuite = SrtpCryptoSuite {
encryption: SrtpEncryptionAlgorithm::AesCm, authentication: SrtpAuthenticationAlgorithm::Null, key_length: 32, tag_length: 16, };
pub struct SrtpContext {
enabled: bool,
crypto: crypto::SrtpCrypto,
key_rotation: key_derivation::KeyRotationFrequency,
packet_index: u64,
}
pub struct ProtectedRtpPacket {
pub packet: crate::packet::RtpPacket,
pub auth_tag: Option<Vec<u8>>,
}
impl ProtectedRtpPacket {
pub fn serialize(&self) -> Result<bytes::Bytes, crate::Error> {
let packet_bytes = self.packet.serialize()?;
if let Some(tag) = &self.auth_tag {
let mut buffer = bytes::BytesMut::with_capacity(packet_bytes.len() + tag.len());
buffer.extend_from_slice(&packet_bytes);
buffer.extend_from_slice(tag);
Ok(buffer.freeze())
} else {
Ok(packet_bytes)
}
}
pub fn serialize_into(
&self,
buffer: &mut bytes::BytesMut,
) -> Result<bytes::Bytes, crate::Error> {
buffer.clear();
buffer.reserve(self.packet.size() + self.auth_tag.as_ref().map_or(0, Vec::len));
self.packet.header.serialize(buffer)?;
buffer.extend_from_slice(&self.packet.payload);
if let Some(tag) = &self.auth_tag {
buffer.extend_from_slice(tag);
}
Ok(buffer.split().freeze())
}
}
impl SrtpContext {
pub fn new(suite: SrtpCryptoSuite, key: crypto::SrtpCryptoKey) -> Result<Self, crate::Error> {
let crypto = crypto::SrtpCrypto::new(suite, key)?;
Ok(Self {
enabled: true,
crypto,
key_rotation: key_derivation::KeyRotationFrequency::None,
packet_index: 0,
})
}
pub fn new_from_keys(
local_key: Vec<u8>,
_remote_key: Vec<u8>,
local_salt: Vec<u8>,
_remote_salt: Vec<u8>,
profile: SrtpCryptoSuite,
) -> Result<Self, crate::Error> {
let combined_key = crypto::SrtpCryptoKey::new(local_key, local_salt);
Self::new(profile, combined_key)
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
pub fn set_key_rotation(&mut self, frequency: key_derivation::KeyRotationFrequency) {
self.key_rotation = frequency;
}
pub fn protect(
&mut self,
packet: &crate::packet::RtpPacket,
) -> Result<ProtectedRtpPacket, crate::Error> {
if !self.enabled {
return Ok(ProtectedRtpPacket {
packet: packet.clone(),
auth_tag: None,
});
}
if self.key_rotation.should_rotate(self.packet_index) {
}
self.packet_index += 1;
let (encrypted, auth_tag) = self.crypto.encrypt_rtp(packet)?;
Ok(ProtectedRtpPacket {
packet: encrypted,
auth_tag,
})
}
pub fn unprotect(&mut self, data: &[u8]) -> Result<crate::packet::RtpPacket, crate::Error> {
if !self.enabled {
return crate::packet::RtpPacket::parse(data);
}
self.crypto.decrypt_rtp(data)
}
pub fn protect_rtcp(&mut self, data: &[u8]) -> Result<bytes::Bytes, crate::Error> {
if !self.enabled {
return Ok(bytes::Bytes::copy_from_slice(data));
}
let (encrypted, auth_tag) = self.crypto.encrypt_rtcp(data)?;
if let Some(tag) = auth_tag {
let mut buffer = bytes::BytesMut::with_capacity(encrypted.len() + tag.len());
buffer.extend_from_slice(&encrypted);
buffer.extend_from_slice(&tag);
Ok(buffer.freeze())
} else {
Ok(encrypted)
}
}
pub fn unprotect_rtcp(&mut self, data: &[u8]) -> Result<bytes::Bytes, crate::Error> {
if !self.enabled {
return Ok(bytes::Bytes::copy_from_slice(data));
}
self.crypto.decrypt_rtcp(data)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_srtp_context_creation() {
let key = SrtpCryptoKey::new(vec![0; 16], vec![0; 14]);
let context = SrtpContext::new(SRTP_NULL_NULL, key);
assert!(context.is_ok());
}
}
#[cfg(test)]
mod integration_tests;