use bytes::{Buf, BufMut, Bytes, BytesMut};
use std::io::Cursor;
use crate::dtls::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum ExtensionType {
ServerName = 0,
MaxFragmentLength = 1,
ClientCertificateUrl = 2,
TrustedCaKeys = 3,
TruncatedHmac = 4,
StatusRequest = 5,
UserMapping = 6,
ClientAuthz = 7,
ServerAuthz = 8,
CertType = 9,
SupportedGroups = 10,
EcPointFormats = 11,
Srp = 12,
SignatureAlgorithms = 13,
UseSrtp = 14,
Heartbeat = 15,
Alpn = 16,
SignedCertificateTimestamp = 18,
ClientCertificateType = 19,
ServerCertificateType = 20,
Padding = 21,
EncryptThenMac = 22,
ExtendedMasterSecret = 23,
TokenBinding = 24,
CacheInfo = 25,
RenegotiationInfo = 0xff01,
Unknown(u16),
}
impl From<u16> for ExtensionType {
fn from(value: u16) -> Self {
match value {
0 => ExtensionType::ServerName,
1 => ExtensionType::MaxFragmentLength,
2 => ExtensionType::ClientCertificateUrl,
3 => ExtensionType::TrustedCaKeys,
4 => ExtensionType::TruncatedHmac,
5 => ExtensionType::StatusRequest,
6 => ExtensionType::UserMapping,
7 => ExtensionType::ClientAuthz,
8 => ExtensionType::ServerAuthz,
9 => ExtensionType::CertType,
10 => ExtensionType::SupportedGroups,
11 => ExtensionType::EcPointFormats,
12 => ExtensionType::Srp,
13 => ExtensionType::SignatureAlgorithms,
14 => ExtensionType::UseSrtp,
15 => ExtensionType::Heartbeat,
16 => ExtensionType::Alpn,
18 => ExtensionType::SignedCertificateTimestamp,
19 => ExtensionType::ClientCertificateType,
20 => ExtensionType::ServerCertificateType,
21 => ExtensionType::Padding,
22 => ExtensionType::EncryptThenMac,
23 => ExtensionType::ExtendedMasterSecret,
24 => ExtensionType::TokenBinding,
25 => ExtensionType::CacheInfo,
0xff01 => ExtensionType::RenegotiationInfo,
_ => ExtensionType::Unknown(value),
}
}
}
impl From<ExtensionType> for u16 {
fn from(value: ExtensionType) -> Self {
match value {
ExtensionType::ServerName => 0,
ExtensionType::MaxFragmentLength => 1,
ExtensionType::ClientCertificateUrl => 2,
ExtensionType::TrustedCaKeys => 3,
ExtensionType::TruncatedHmac => 4,
ExtensionType::StatusRequest => 5,
ExtensionType::UserMapping => 6,
ExtensionType::ClientAuthz => 7,
ExtensionType::ServerAuthz => 8,
ExtensionType::CertType => 9,
ExtensionType::SupportedGroups => 10,
ExtensionType::EcPointFormats => 11,
ExtensionType::Srp => 12,
ExtensionType::SignatureAlgorithms => 13,
ExtensionType::UseSrtp => 14,
ExtensionType::Heartbeat => 15,
ExtensionType::Alpn => 16,
ExtensionType::SignedCertificateTimestamp => 18,
ExtensionType::ClientCertificateType => 19,
ExtensionType::ServerCertificateType => 20,
ExtensionType::Padding => 21,
ExtensionType::EncryptThenMac => 22,
ExtensionType::ExtendedMasterSecret => 23,
ExtensionType::TokenBinding => 24,
ExtensionType::CacheInfo => 25,
ExtensionType::RenegotiationInfo => 0xff01,
ExtensionType::Unknown(value) => value,
}
}
}
#[derive(Debug, Clone)]
pub enum Extension {
UseSrtp(UseSrtpExtension),
Unknown {
typ: u16,
data: Bytes,
},
}
impl Extension {
pub fn extension_type(&self) -> ExtensionType {
match self {
Self::UseSrtp(_) => ExtensionType::UseSrtp,
Self::Unknown { typ, .. } => ExtensionType::from(*typ),
}
}
pub fn serialize(&self) -> Result<Bytes> {
let mut buf = BytesMut::new();
let typ: u16 = self.extension_type().into();
buf.put_u16(typ);
match self {
Self::UseSrtp(ext) => {
let data = ext.serialize()?;
buf.put_u16(data.len() as u16);
buf.extend_from_slice(&data);
}
Self::Unknown { data, .. } => {
buf.put_u16(data.len() as u16);
buf.extend_from_slice(data);
}
}
Ok(buf.freeze())
}
pub fn parse(data: &[u8]) -> Result<(Self, usize)> {
if data.len() < 4 {
return Err(crate::error::Error::PacketTooShort);
}
let mut cursor = Cursor::new(data);
let typ = cursor.get_u16();
let length = cursor.get_u16() as usize;
if data.len() < 4 + length {
return Err(crate::error::Error::PacketTooShort);
}
let ext_data = &data[4..4 + length];
let extension = match ExtensionType::from(typ) {
ExtensionType::UseSrtp => {
let use_srtp = UseSrtpExtension::parse(ext_data)?;
Extension::UseSrtp(use_srtp)
}
_ => Extension::Unknown {
typ,
data: Bytes::copy_from_slice(ext_data),
},
};
Ok((extension, 4 + length))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum SrtpProtectionProfile {
Aes128CmSha1_80 = 0x0001,
Aes128CmSha1_32 = 0x0002,
AeadAes128Gcm = 0x0007,
AeadAes256Gcm = 0x0008,
Unknown(u16),
}
impl From<u16> for SrtpProtectionProfile {
fn from(value: u16) -> Self {
match value {
0x0001 => SrtpProtectionProfile::Aes128CmSha1_80,
0x0002 => SrtpProtectionProfile::Aes128CmSha1_32,
0x0007 => SrtpProtectionProfile::AeadAes128Gcm,
0x0008 => SrtpProtectionProfile::AeadAes256Gcm,
_ => SrtpProtectionProfile::Unknown(value),
}
}
}
impl From<SrtpProtectionProfile> for u16 {
fn from(value: SrtpProtectionProfile) -> Self {
match value {
SrtpProtectionProfile::Aes128CmSha1_80 => 0x0001,
SrtpProtectionProfile::Aes128CmSha1_32 => 0x0002,
SrtpProtectionProfile::AeadAes128Gcm => 0x0007,
SrtpProtectionProfile::AeadAes256Gcm => 0x0008,
SrtpProtectionProfile::Unknown(value) => value,
}
}
}
#[derive(Debug, Clone)]
pub struct UseSrtpExtension {
pub profiles: Vec<SrtpProtectionProfile>,
pub mki: Bytes,
}
impl UseSrtpExtension {
pub fn new(profiles: Vec<SrtpProtectionProfile>, mki: Bytes) -> Self {
Self { profiles, mki }
}
pub fn with_profiles(profiles: Vec<SrtpProtectionProfile>) -> Self {
Self {
profiles,
mki: Bytes::new(),
}
}
pub fn serialize(&self) -> Result<Bytes> {
let profiles_len = self.profiles.len() * 2;
let total_len = 2 + profiles_len + 1 + self.mki.len();
let mut buf = BytesMut::with_capacity(total_len);
buf.put_u16(profiles_len as u16);
for profile in &self.profiles {
buf.put_u16((*profile).into());
}
buf.put_u8(self.mki.len() as u8);
if !self.mki.is_empty() {
buf.extend_from_slice(&self.mki);
}
Ok(buf.freeze())
}
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < 3 {
return Err(crate::error::Error::PacketTooShort);
}
let mut cursor = Cursor::new(data);
let profiles_len = cursor.get_u16() as usize;
if profiles_len % 2 != 0 {
return Err(crate::error::Error::InvalidPacket(
"SRTP profiles length must be a multiple of 2".to_string(),
));
}
if data.len() < 3 + profiles_len {
return Err(crate::error::Error::PacketTooShort);
}
let mut profiles = Vec::with_capacity(profiles_len / 2);
for _ in 0..(profiles_len / 2) {
let profile_id = cursor.get_u16();
profiles.push(SrtpProtectionProfile::from(profile_id));
}
let mki_len = cursor.get_u8() as usize;
if data.len() < 3 + profiles_len + mki_len {
return Err(crate::error::Error::PacketTooShort);
}
let mki = if mki_len > 0 {
let offset = 3 + profiles_len;
Bytes::copy_from_slice(&data[offset..offset + mki_len])
} else {
Bytes::new()
};
Ok(Self { profiles, mki })
}
}