use aes::cipher::{KeyIvInit, StreamCipher};
use aes::Aes128;
use bytes::{Bytes, BytesMut};
use hmac::{Hmac, Mac};
use sha1::Sha1;
use crate::srtp::kdf::{CryptoSuite, SessionKeys};
type Aes128Ctr = ctr::Ctr128BE<Aes128>;
type HmacSha1 = Hmac<Sha1>;
const RTP_HEADER_SIZE: usize = 12;
pub struct SrtpContext {
suite: CryptoSuite,
keys: SessionKeys,
sender_roc: u32,
receiver_roc: u32,
highest_seq: u16,
first_packet: bool,
}
impl SrtpContext {
pub fn new(suite: CryptoSuite, master_key: &[u8], master_salt: &[u8]) -> Result<Self, String> {
let keys = SessionKeys::derive(suite, master_key, master_salt)?;
Ok(Self {
suite,
keys,
sender_roc: 0,
receiver_roc: 0,
highest_seq: 0,
first_packet: true,
})
}
pub fn protect(&mut self, rtp: &[u8]) -> Result<Bytes, String> {
if rtp.len() < RTP_HEADER_SIZE {
return Err("RTP packet too short".into());
}
let seq = u16::from_be_bytes([rtp[2], rtp[3]]);
if seq < 0x8000 && self.highest_seq > 0x8000 {
self.sender_roc = self.sender_roc.wrapping_add(1);
}
self.highest_seq = seq;
let ssrc = u32::from_be_bytes([rtp[8], rtp[9], rtp[10], rtp[11]]);
let index = ((self.sender_roc as u64) << 16) | (seq as u64);
let iv = build_iv(&self.keys.srtp_salt, ssrc, index);
let header_len = get_rtp_header_len(rtp)?;
let mut output = BytesMut::with_capacity(rtp.len() + self.suite.auth_tag_len());
output.extend_from_slice(&rtp[..header_len]);
let mut payload = rtp[header_len..].to_vec();
let mut cipher = Aes128Ctr::new((&self.keys.srtp_enc_key[..]).into(), &iv.into());
cipher.apply_keystream(&mut payload);
output.extend_from_slice(&payload);
let tag = self.compute_auth_tag(&output, self.sender_roc);
output.extend_from_slice(&tag[..self.suite.auth_tag_len()]);
Ok(output.freeze())
}
pub fn unprotect(&mut self, srtp: &[u8]) -> Result<Bytes, String> {
let tag_len = self.suite.auth_tag_len();
if srtp.len() < RTP_HEADER_SIZE + tag_len {
return Err("SRTP packet too short".into());
}
let packet = &srtp[..srtp.len() - tag_len];
let received_tag = &srtp[srtp.len() - tag_len..];
let seq = u16::from_be_bytes([packet[2], packet[3]]);
let roc = if self.first_packet {
self.first_packet = false;
self.highest_seq = seq;
0
} else {
estimate_roc(self.receiver_roc, self.highest_seq, seq)
};
let expected_tag = self.compute_auth_tag(packet, roc);
if !constant_time_compare(&expected_tag[..tag_len], received_tag) {
return Err("Authentication failed".into());
}
let index = ((roc as u64) << 16) | (seq as u64);
let current_index = ((self.receiver_roc as u64) << 16) | (self.highest_seq as u64);
if index > current_index {
self.receiver_roc = roc;
self.highest_seq = seq;
}
let ssrc = u32::from_be_bytes([packet[8], packet[9], packet[10], packet[11]]);
let iv = build_iv(&self.keys.srtp_salt, ssrc, index);
let header_len = get_rtp_header_len(packet)?;
let mut output = BytesMut::with_capacity(packet.len());
output.extend_from_slice(&packet[..header_len]);
let mut payload = packet[header_len..].to_vec();
let mut cipher = Aes128Ctr::new((&self.keys.srtp_enc_key[..]).into(), &iv.into());
cipher.apply_keystream(&mut payload);
output.extend_from_slice(&payload);
Ok(output.freeze())
}
fn compute_auth_tag(&self, packet: &[u8], roc: u32) -> [u8; 20] {
let mut mac =
HmacSha1::new_from_slice(&self.keys.srtp_auth_key).expect("HMAC key length is valid");
mac.update(packet);
mac.update(&roc.to_be_bytes());
let result = mac.finalize();
let mut tag = [0u8; 20];
tag.copy_from_slice(&result.into_bytes());
tag
}
}
pub struct SrtcpContext {
suite: CryptoSuite,
keys: SessionKeys,
index: u32,
}
impl SrtcpContext {
pub fn new(suite: CryptoSuite, master_key: &[u8], master_salt: &[u8]) -> Result<Self, String> {
let keys = SessionKeys::derive(suite, master_key, master_salt)?;
Ok(Self {
suite,
keys,
index: 0,
})
}
pub fn protect(&mut self, rtcp: &[u8]) -> Result<Bytes, String> {
if rtcp.len() < 8 {
return Err("RTCP packet too short".into());
}
let ssrc = u32::from_be_bytes([rtcp[4], rtcp[5], rtcp[6], rtcp[7]]);
let srtcp_index = 0x80000000 | self.index;
self.index = (self.index + 1) & 0x7FFFFFFF;
let iv = build_srtcp_iv(&self.keys.srtcp_salt, ssrc, srtcp_index);
let mut output = BytesMut::with_capacity(rtcp.len() + 4 + self.suite.auth_tag_len());
output.extend_from_slice(&rtcp[..8]);
let mut payload = rtcp[8..].to_vec();
let mut cipher = Aes128Ctr::new((&self.keys.srtcp_enc_key[..]).into(), &iv.into());
cipher.apply_keystream(&mut payload);
output.extend_from_slice(&payload);
output.extend_from_slice(&srtcp_index.to_be_bytes());
let tag = self.compute_auth_tag(&output);
output.extend_from_slice(&tag[..self.suite.auth_tag_len()]);
Ok(output.freeze())
}
pub fn unprotect(&mut self, srtcp: &[u8]) -> Result<Bytes, String> {
let tag_len = self.suite.auth_tag_len();
if srtcp.len() < 8 + 4 + tag_len {
return Err("SRTCP packet too short".into());
}
let packet_with_index = &srtcp[..srtcp.len() - tag_len];
let received_tag = &srtcp[srtcp.len() - tag_len..];
let expected_tag = self.compute_auth_tag(packet_with_index);
if !constant_time_compare(&expected_tag[..tag_len], received_tag) {
return Err("Authentication failed".into());
}
let index_offset = packet_with_index.len() - 4;
let srtcp_index = u32::from_be_bytes([
packet_with_index[index_offset],
packet_with_index[index_offset + 1],
packet_with_index[index_offset + 2],
packet_with_index[index_offset + 3],
]);
let is_encrypted = (srtcp_index & 0x80000000) != 0;
let packet = &packet_with_index[..index_offset];
if !is_encrypted {
return Ok(Bytes::copy_from_slice(packet));
}
let ssrc = u32::from_be_bytes([packet[4], packet[5], packet[6], packet[7]]);
let iv = build_srtcp_iv(&self.keys.srtcp_salt, ssrc, srtcp_index);
let mut output = BytesMut::with_capacity(packet.len());
output.extend_from_slice(&packet[..8]);
let mut payload = packet[8..].to_vec();
let mut cipher = Aes128Ctr::new((&self.keys.srtcp_enc_key[..]).into(), &iv.into());
cipher.apply_keystream(&mut payload);
output.extend_from_slice(&payload);
Ok(output.freeze())
}
fn compute_auth_tag(&self, packet: &[u8]) -> [u8; 20] {
let mut mac =
HmacSha1::new_from_slice(&self.keys.srtcp_auth_key).expect("HMAC key length is valid");
mac.update(packet);
let result = mac.finalize();
let mut tag = [0u8; 20];
tag.copy_from_slice(&result.into_bytes());
tag
}
}
fn build_iv(salt: &[u8], ssrc: u32, index: u64) -> [u8; 16] {
let mut iv = [0u8; 16];
iv[..salt.len()].copy_from_slice(salt);
let ssrc_bytes = ssrc.to_be_bytes();
iv[4] ^= ssrc_bytes[0];
iv[5] ^= ssrc_bytes[1];
iv[6] ^= ssrc_bytes[2];
iv[7] ^= ssrc_bytes[3];
let index_bytes = index.to_be_bytes();
iv[8] ^= index_bytes[2];
iv[9] ^= index_bytes[3];
iv[10] ^= index_bytes[4];
iv[11] ^= index_bytes[5];
iv[12] ^= index_bytes[6];
iv[13] ^= index_bytes[7];
iv
}
fn build_srtcp_iv(salt: &[u8], ssrc: u32, index: u32) -> [u8; 16] {
let mut iv = [0u8; 16];
iv[..salt.len()].copy_from_slice(salt);
let ssrc_bytes = ssrc.to_be_bytes();
iv[4] ^= ssrc_bytes[0];
iv[5] ^= ssrc_bytes[1];
iv[6] ^= ssrc_bytes[2];
iv[7] ^= ssrc_bytes[3];
let index_bytes = index.to_be_bytes();
iv[10] ^= index_bytes[0];
iv[11] ^= index_bytes[1];
iv[12] ^= index_bytes[2];
iv[13] ^= index_bytes[3];
iv
}
fn get_rtp_header_len(rtp: &[u8]) -> Result<usize, String> {
if rtp.len() < RTP_HEADER_SIZE {
return Err("RTP packet too short".into());
}
let cc = (rtp[0] & 0x0F) as usize;
let has_extension = (rtp[0] & 0x10) != 0;
let mut header_len = RTP_HEADER_SIZE + cc * 4;
if has_extension {
if rtp.len() < header_len + 4 {
return Err("RTP packet too short for extension".into());
}
let ext_len = u16::from_be_bytes([rtp[header_len + 2], rtp[header_len + 3]]) as usize;
header_len += 4 + ext_len * 4;
}
if rtp.len() < header_len {
return Err("RTP packet too short for header".into());
}
Ok(header_len)
}
fn estimate_roc(current_roc: u32, highest_seq: u16, new_seq: u16) -> u32 {
let v = current_roc;
let s_l = highest_seq;
let seq = new_seq;
if s_l < 0x8000 {
if seq > s_l && seq - s_l > 0x8000 {
v.wrapping_sub(1)
} else {
v
}
} else if seq < s_l && s_l - seq > 0x8000 {
v.wrapping_add(1)
} else {
v
}
}
fn constant_time_compare(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
let mut result = 0u8;
for (x, y) in a.iter().zip(b.iter()) {
result |= x ^ y;
}
result == 0
}
#[cfg(test)]
mod tests {
use super::*;
use crate::srtp::kdf::CryptoSuite;
fn make_test_rtp() -> Vec<u8> {
let mut rtp = vec![0u8; 172];
rtp[0] = 0x80; rtp[1] = 0x00; rtp[2] = 0x00; rtp[3] = 0x01; rtp[4] = 0x00; rtp[5] = 0x00;
rtp[6] = 0x00;
rtp[7] = 0x00;
rtp[8] = 0x12; rtp[9] = 0x34;
rtp[10] = 0x56;
rtp[11] = 0x78;
for (i, byte) in rtp[12..172].iter_mut().enumerate() {
*byte = i as u8;
}
rtp
}
fn make_test_rtp_with_seq(seq: u16) -> Vec<u8> {
let mut rtp = make_test_rtp();
let [hi, lo] = seq.to_be_bytes();
rtp[2] = hi;
rtp[3] = lo;
rtp
}
#[test]
fn test_context_new_invalid_lengths() {
let bad_key = [0u8; 8];
let bad_salt = [0u8; 8];
let _ = SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &bad_key, &bad_salt);
let _ = SrtcpContext::new(CryptoSuite::AesCm128HmacSha1_80, &bad_key, &bad_salt);
}
#[test]
fn test_srtp_protect_unprotect() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx_send =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let mut ctx_recv =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let rtp = make_test_rtp();
let srtp = ctx_send.protect(&rtp).unwrap();
assert_eq!(srtp.len(), rtp.len() + 10);
let decrypted = ctx_recv.unprotect(&srtp).unwrap();
assert_eq!(&decrypted[..], &rtp[..]);
}
#[test]
fn test_srtp_protect_extension_too_short() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let mut rtp = make_test_rtp();
rtp[0] |= 0x10;
rtp.truncate(12);
let _ = ctx.protect(&rtp);
}
#[test]
fn test_srtp_tamper_detection() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx_send =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let mut ctx_recv =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let rtp = make_test_rtp();
let mut srtp = ctx_send.protect(&rtp).unwrap().to_vec();
srtp[20] ^= 0xFF;
let result = ctx_recv.unprotect(&srtp);
assert!(result.is_err());
}
#[test]
fn test_srtp_32bit_tag() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_32, &master_key, &master_salt).unwrap();
let rtp = make_test_rtp();
let srtp = ctx.protect(&rtp).unwrap();
assert_eq!(srtp.len(), rtp.len() + 4);
}
#[test]
fn test_srtcp_protect_unprotect() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx_send =
SrtcpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let mut ctx_recv =
SrtcpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let rtcp = vec![
0x80, 0xC8, 0x00, 0x06, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
let srtcp = ctx_send.protect(&rtcp).unwrap();
assert_eq!(srtcp.len(), rtcp.len() + 4 + 10);
let decrypted = ctx_recv.unprotect(&srtcp).unwrap();
assert_eq!(&decrypted[..], &rtcp[..]);
}
#[test]
fn test_get_rtp_header_len() {
let rtp = vec![0x80; 12];
assert_eq!(get_rtp_header_len(&rtp).unwrap(), 12);
let mut rtp = vec![0x82; 20];
rtp[0] = 0x82; assert_eq!(get_rtp_header_len(&rtp).unwrap(), 20);
let mut rtp = vec![0; 20];
rtp[0] = 0x90; rtp[14] = 0x00; rtp[15] = 0x01; assert_eq!(get_rtp_header_len(&rtp).unwrap(), 20);
}
#[test]
fn test_estimate_roc() {
assert_eq!(estimate_roc(0, 100, 101), 0);
assert_eq!(estimate_roc(0, 0xFFFF, 0), 1);
assert_eq!(estimate_roc(1, 0, 0xFFFF), 0);
}
#[test]
fn test_constant_time_compare() {
let a = [1, 2, 3, 4];
let b = [1, 2, 3, 4];
let c = [1, 2, 3, 5];
assert!(constant_time_compare(&a, &b));
assert!(!constant_time_compare(&a, &c));
assert!(!constant_time_compare(&a, &[1, 2, 3])); }
#[test]
fn test_estimate_roc_more_cases() {
assert_eq!(estimate_roc(5, 0x1000, 0xF000), 4);
assert_eq!(estimate_roc(3, 0x1000, 0x1001), 3);
assert_eq!(estimate_roc(2, 0xFFF0, 0x0010), 3);
assert_eq!(estimate_roc(2, 0x9000, 0x9001), 2);
}
#[test]
fn test_estimate_roc_no_wrap_large_seq_gap() {
assert_eq!(estimate_roc(7, 0x9000, 0x8800), 7);
}
#[test]
fn test_get_rtp_header_len_with_csrc() {
let mut rtp = vec![0x82, 0x00, 0x00, 0x01]; rtp.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); rtp.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]); rtp.extend_from_slice(&[0x00, 0x00, 0x00, 0x02]); rtp.extend_from_slice(&[0x00, 0x00, 0x00, 0x03]); rtp.extend_from_slice(&[0x00]);
let header_len = get_rtp_header_len(&rtp).unwrap();
assert_eq!(header_len, 12 + 8); }
#[test]
fn test_get_rtp_header_len_too_short_for_extension() {
let rtp = vec![
0x90, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
];
let result = get_rtp_header_len(&rtp);
assert!(result.is_err());
}
#[test]
fn test_srtp_protect_short_packet() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let short_rtp = [0u8; 8];
assert!(ctx.protect(&short_rtp).is_err());
}
#[test]
fn test_srtp_unprotect_short_packet() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let short_srtp = [0u8; 16];
assert!(ctx.unprotect(&short_srtp).is_err());
}
#[test]
fn test_srtcp_protect_short_packet() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx =
SrtcpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let short_rtcp = [0u8; 4];
assert!(ctx.protect(&short_rtcp).is_err());
}
#[test]
fn test_srtcp_unprotect_short_packet() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx =
SrtcpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let short_srtcp = [0u8; 10];
assert!(ctx.unprotect(&short_srtcp).is_err());
}
#[test]
fn test_srtp_auth_failure() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let rtp = make_test_rtp();
let srtp = ctx.protect(&rtp).unwrap();
let mut ctx2 =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let mut tampered = srtp.to_vec();
tampered[12] ^= 0xFF;
assert!(ctx2.unprotect(&tampered).is_err());
}
#[test]
fn test_srtp_unprotect_updates_receiver_state() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx_send =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let mut ctx_recv =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let rtp_seq_10 = make_test_rtp_with_seq(10);
let rtp_seq_11 = make_test_rtp_with_seq(11);
let srtp_10 = ctx_send.protect(&rtp_seq_10).unwrap();
let srtp_11 = ctx_send.protect(&rtp_seq_11).unwrap();
ctx_recv.unprotect(&srtp_10).unwrap();
let highest_before = ctx_recv.highest_seq;
ctx_recv.unprotect(&srtp_11).unwrap();
assert!(ctx_recv.highest_seq > highest_before);
}
#[test]
fn test_get_rtp_header_len_too_short() {
let result = get_rtp_header_len(&[0u8; 4]);
assert!(result.is_err());
}
#[test]
fn test_estimate_roc_wraparound_forward() {
let roc = estimate_roc(3, 0x9000, 0x0001);
assert_eq!(roc, 4);
}
#[test]
fn test_estimate_roc_wraparound_forward_large_gap() {
let roc = estimate_roc(1, 0x9001, 0x0001);
assert_eq!(roc, 2);
}
#[test]
fn test_srtcp_auth_failure() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx_send =
SrtcpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let mut ctx_recv =
SrtcpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let rtcp = vec![
0x80, 0xC8, 0x00, 0x06, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let srtcp = ctx_send.protect(&rtcp).unwrap();
let mut tampered = srtcp.to_vec();
let last = tampered.len() - 1;
tampered[last] ^= 0xFF;
assert!(ctx_recv.unprotect(&tampered).is_err());
}
#[test]
fn test_srtp_sender_roc_wraparound() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let high_seq = make_test_rtp_with_seq(0xFFFF);
ctx.protect(&high_seq).unwrap();
assert_eq!(ctx.sender_roc, 0);
let low_seq = make_test_rtp_with_seq(0x0001);
ctx.protect(&low_seq).unwrap();
assert_eq!(ctx.sender_roc, 1);
assert_eq!(ctx.highest_seq, 0x0001);
}
#[test]
fn test_srtp_unprotect_out_of_order_does_not_rewind() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx_send =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let mut ctx_recv =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let rtp_seq_10 = make_test_rtp_with_seq(10);
let rtp_seq_11 = make_test_rtp_with_seq(11);
let srtp_10 = ctx_send.protect(&rtp_seq_10).unwrap();
let srtp_11 = ctx_send.protect(&rtp_seq_11).unwrap();
ctx_recv.unprotect(&srtp_11).unwrap();
let highest_after_11 = ctx_recv.highest_seq;
ctx_recv.unprotect(&srtp_10).unwrap();
assert_eq!(ctx_recv.highest_seq, highest_after_11);
}
#[test]
fn test_srtp_unprotect_header_len_error_after_auth() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx =
SrtpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let mut rtp = vec![0u8; 16];
rtp[0] = 0x90; rtp[1] = 0x00;
rtp[2] = 0x00;
rtp[3] = 0x01;
rtp[8] = 0x12;
rtp[9] = 0x34;
rtp[10] = 0x56;
rtp[11] = 0x78;
rtp[14] = 0x00;
rtp[15] = 0x02;
let tag = ctx.compute_auth_tag(&rtp, 0);
let mut srtp = rtp.clone();
srtp.extend_from_slice(&tag[..ctx.suite.auth_tag_len()]);
let result = ctx.unprotect(&srtp);
assert!(result.is_err());
}
#[test]
fn test_srtcp_unprotect_without_encryption() {
let master_key = [0u8; 16];
let master_salt = [0u8; 14];
let mut ctx =
SrtcpContext::new(CryptoSuite::AesCm128HmacSha1_80, &master_key, &master_salt).unwrap();
let rtcp = vec![
0x80, 0xC8, 0x00, 0x06, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let srtcp_index = 0u32;
let mut packet_with_index = rtcp.clone();
packet_with_index.extend_from_slice(&srtcp_index.to_be_bytes());
let tag = ctx.compute_auth_tag(&packet_with_index);
let mut srtcp = packet_with_index.clone();
srtcp.extend_from_slice(&tag[..ctx.suite.auth_tag_len()]);
let decrypted = ctx.unprotect(&srtcp).unwrap();
assert_eq!(&decrypted[..], &rtcp[..]);
}
}