1use crate::{
2 errors::{SrtpError, SrtpResult},
3 rtp::RtpPacket,
4};
5use aes::Aes128;
6use aes_gcm::{
7 Aes128Gcm, Nonce,
8 aead::{Aead, KeyInit, Payload},
9};
10use bytes::Bytes;
11use ctr::cipher::{KeyIvInit, StreamCipher};
12use hmac::{Hmac, Mac};
13use sha1::Sha1;
14use std::collections::HashMap;
15use std::collections::hash_map::Entry;
16use std::fmt;
17
18type Aes128Ctr = ctr::Ctr128BE<Aes128>;
19type HmacSha1 = Hmac<Sha1>;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
22pub enum SrtpProfile {
23 #[default]
24 Aes128Sha1_80,
25 Aes128Sha1_32,
26 AeadAes128Gcm,
27 NullCipherHmac,
28}
29
30impl SrtpProfile {
31 fn tag_len(&self) -> usize {
32 match self {
33 Self::Aes128Sha1_80 | Self::NullCipherHmac => 10,
34 Self::Aes128Sha1_32 => 4,
35 Self::AeadAes128Gcm => 16,
36 }
37 }
38
39 fn salt_len(&self) -> usize {
40 match self {
41 Self::AeadAes128Gcm => 12,
42 _ => 14,
43 }
44 }
45
46 fn key_len(&self) -> usize {
47 16
48 }
49
50 fn auth_key_len(&self) -> usize {
51 match self {
52 Self::Aes128Sha1_80 | Self::NullCipherHmac => 20,
53 Self::Aes128Sha1_32 => 20,
54 Self::AeadAes128Gcm => 0, }
56 }
57}
58
59#[derive(Debug, Clone)]
60pub struct SrtpKeyingMaterial {
61 pub master_key: Vec<u8>,
62 pub master_salt: Vec<u8>,
63}
64
65impl SrtpKeyingMaterial {
66 pub fn new(master_key: Vec<u8>, master_salt: Vec<u8>) -> Self {
67 Self {
68 master_key,
69 master_salt,
70 }
71 }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub enum SrtpDirection {
76 Sender,
77 Receiver,
78}
79
80pub struct SrtpSession {
81 profile: SrtpProfile,
82 tx_keying: SrtpKeyingMaterial,
83 rx_keying: SrtpKeyingMaterial,
84 tx_contexts: HashMap<u32, SrtpContext>,
85 rx_contexts: HashMap<u32, SrtpContext>,
86}
87
88const SSRC_CONTEXT_HIGH_WATERMARK: usize = 32;
93const SSRC_INACTIVITY_EVICT: std::time::Duration = std::time::Duration::from_secs(60);
97
98impl SrtpSession {
99 pub fn new(
100 profile: SrtpProfile,
101 tx_keying: SrtpKeyingMaterial,
102 rx_keying: SrtpKeyingMaterial,
103 ) -> Result<Self, SrtpError> {
104 Ok(Self {
105 profile,
106 tx_keying,
107 rx_keying,
108 tx_contexts: HashMap::new(),
109 rx_contexts: HashMap::new(),
110 })
111 }
112
113 pub fn protect_rtp(&mut self, packet: &mut RtpPacket) -> SrtpResult<()> {
114 let ssrc = packet.header.ssrc;
115 self.evict_stale_tx(ssrc);
116 let ctx = match self.tx_contexts.entry(ssrc) {
117 Entry::Occupied(e) => e.into_mut(),
118 Entry::Vacant(e) => e.insert(SrtpContext::new(
119 ssrc,
120 self.profile,
121 self.tx_keying.clone(),
122 SrtpDirection::Sender,
123 )?),
124 };
125 ctx.last_used = std::time::Instant::now();
126 ctx.protect(packet)
127 }
128
129 pub fn unprotect_rtp(&mut self, packet: &mut RtpPacket) -> SrtpResult<()> {
130 let ssrc = packet.header.ssrc;
131 self.evict_stale_rx(ssrc);
132 let ctx = match self.rx_contexts.entry(ssrc) {
133 Entry::Occupied(e) => e.into_mut(),
134 Entry::Vacant(e) => e.insert(SrtpContext::new(
135 ssrc,
136 self.profile,
137 self.rx_keying.clone(),
138 SrtpDirection::Receiver,
139 )?),
140 };
141 ctx.last_used = std::time::Instant::now();
142 ctx.unprotect(packet)
143 }
144
145 pub fn protect_rtcp(&mut self, packet: &mut Vec<u8>) -> SrtpResult<()> {
146 if packet.len() < 8 {
147 return Err(SrtpError::PacketTooShort);
148 }
149 let ssrc = u32::from_be_bytes([packet[4], packet[5], packet[6], packet[7]]);
150
151 self.evict_stale_tx(ssrc);
152 let ctx = match self.tx_contexts.entry(ssrc) {
153 Entry::Occupied(e) => e.into_mut(),
154 Entry::Vacant(e) => e.insert(SrtpContext::new(
155 ssrc,
156 self.profile,
157 self.tx_keying.clone(),
158 SrtpDirection::Sender,
159 )?),
160 };
161 ctx.last_used = std::time::Instant::now();
162 ctx.protect_rtcp(packet)
163 }
164
165 pub fn unprotect_rtcp(&mut self, packet: &mut Vec<u8>) -> SrtpResult<()> {
166 if packet.len() < 14 {
167 return Err(SrtpError::PacketTooShort);
169 }
170 let ssrc = u32::from_be_bytes([packet[4], packet[5], packet[6], packet[7]]);
171
172 self.evict_stale_rx(ssrc);
173 let ctx = match self.rx_contexts.entry(ssrc) {
174 Entry::Occupied(e) => e.into_mut(),
175 Entry::Vacant(e) => e.insert(SrtpContext::new(
176 ssrc,
177 self.profile,
178 self.rx_keying.clone(),
179 SrtpDirection::Receiver,
180 )?),
181 };
182 ctx.last_used = std::time::Instant::now();
183 ctx.unprotect_rtcp(packet)
184 }
185
186 fn evict_stale_tx(&mut self, keep_ssrc: u32) {
190 if self.tx_contexts.len() <= SSRC_CONTEXT_HIGH_WATERMARK {
191 return;
192 }
193 let now = std::time::Instant::now();
194 self.tx_contexts.retain(|s, c| {
195 *s == keep_ssrc || now.duration_since(c.last_used) < SSRC_INACTIVITY_EVICT
196 });
197 }
198
199 fn evict_stale_rx(&mut self, keep_ssrc: u32) {
201 if self.rx_contexts.len() <= SSRC_CONTEXT_HIGH_WATERMARK {
202 return;
203 }
204 let now = std::time::Instant::now();
205 self.rx_contexts.retain(|s, c| {
206 *s == keep_ssrc || now.duration_since(c.last_used) < SSRC_INACTIVITY_EVICT
207 });
208 }
209}
210
211#[derive(Debug, Clone)]
212struct SessionKeys {
213 cipher_key: Vec<u8>,
214 auth_key: Vec<u8>,
215 salt: Vec<u8>,
216}
217
218#[derive(Clone)]
219pub struct SrtpContext {
220 ssrc: u32,
221 _profile: SrtpProfile,
222 rtp_keys: SessionKeys,
223 rtcp_keys: SessionKeys,
224 rtp_gcm_cipher: Option<Aes128Gcm>,
225 rtcp_gcm_cipher: Option<Aes128Gcm>,
226 rtp_auth_prototype: Option<HmacSha1>,
227 direction: SrtpDirection,
228 rollover_counter: u32,
229 last_sequence: Option<u16>,
230 rtcp_index: u32,
231 last_used: std::time::Instant,
235}
236
237impl fmt::Debug for SrtpContext {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 f.debug_struct("SrtpContext")
240 .field("ssrc", &self.ssrc)
241 .field("_profile", &self._profile)
242 .field("direction", &self.direction)
243 .field("rollover_counter", &self.rollover_counter)
244 .finish()
245 }
246}
247
248impl SrtpContext {
249 pub fn new(
250 ssrc: u32,
251 profile: SrtpProfile,
252 keying: SrtpKeyingMaterial,
253 direction: SrtpDirection,
254 ) -> SrtpResult<Self> {
255 if keying.master_key.len() < profile.key_len()
256 || keying.master_salt.len() < profile.salt_len()
257 {
258 return Err(SrtpError::UnsupportedProfile);
259 }
260
261 let (rtp_keys, rtcp_keys) = Self::derive_keys(profile, &keying)?;
262
263 let rtp_gcm_cipher = if let SrtpProfile::AeadAes128Gcm = profile {
264 Some(
265 Aes128Gcm::new_from_slice(&rtp_keys.cipher_key)
266 .map_err(|_| SrtpError::UnsupportedProfile)?,
267 )
268 } else {
269 None
270 };
271
272 let rtcp_gcm_cipher = if let SrtpProfile::AeadAes128Gcm = profile {
273 Some(
274 Aes128Gcm::new_from_slice(&rtcp_keys.cipher_key)
275 .map_err(|_| SrtpError::UnsupportedProfile)?,
276 )
277 } else {
278 None
279 };
280
281 let rtp_auth_prototype = if !rtp_keys.auth_key.is_empty() {
282 Some(
283 <HmacSha1 as hmac::digest::KeyInit>::new_from_slice(&rtp_keys.auth_key)
284 .map_err(|_| SrtpError::UnsupportedProfile)?,
285 )
286 } else {
287 None
288 };
289
290 Ok(Self {
291 ssrc,
292 _profile: profile,
293 rtp_keys,
294 rtcp_keys,
295 rtp_gcm_cipher,
296 rtcp_gcm_cipher,
297 rtp_auth_prototype,
298 direction,
299 rollover_counter: 0,
300 last_sequence: None,
301 rtcp_index: 0,
302 last_used: std::time::Instant::now(),
303 })
304 }
305
306 fn derive_keys(
307 profile: SrtpProfile,
308 keying: &SrtpKeyingMaterial,
309 ) -> SrtpResult<(SessionKeys, SessionKeys)> {
310 let key_len = profile.key_len();
311 let salt_len = profile.salt_len();
312 let auth_len = profile.auth_key_len();
313
314 let rtp_cipher = Self::kdf(key_len, 0x00, &keying.master_key, &keying.master_salt)?;
316 let rtp_auth = if auth_len > 0 {
317 Self::kdf(auth_len, 0x01, &keying.master_key, &keying.master_salt)?
318 } else {
319 Vec::new()
320 };
321 let rtp_salt = Self::kdf(salt_len, 0x02, &keying.master_key, &keying.master_salt)?;
322
323 let rtcp_cipher = Self::kdf(key_len, 0x03, &keying.master_key, &keying.master_salt)?;
325 let rtcp_auth = if auth_len > 0 {
326 Self::kdf(auth_len, 0x04, &keying.master_key, &keying.master_salt)?
327 } else {
328 Vec::new()
329 };
330 let rtcp_salt = Self::kdf(salt_len, 0x05, &keying.master_key, &keying.master_salt)?;
331
332 Ok((
333 SessionKeys {
334 cipher_key: rtp_cipher,
335 auth_key: rtp_auth,
336 salt: rtp_salt,
337 },
338 SessionKeys {
339 cipher_key: rtcp_cipher,
340 auth_key: rtcp_auth,
341 salt: rtcp_salt,
342 },
343 ))
344 }
345
346 fn kdf(len: usize, label: u8, master_key: &[u8], master_salt: &[u8]) -> SrtpResult<Vec<u8>> {
347 let mut iv = [0u8; 16];
353 for (i, &b) in master_salt.iter().take(14).enumerate() {
355 iv[i] = b;
356 }
357
358 iv[7] ^= label;
361
362 let mut out = vec![0u8; len];
364 let mut cipher = Aes128Ctr::new_from_slices(&master_key[..16], &iv)
365 .map_err(|_| SrtpError::UnsupportedProfile)?;
366 cipher.apply_keystream(&mut out);
367
368 Ok(out)
369 }
370
371 pub fn protect_rtcp(&mut self, packet: &mut Vec<u8>) -> SrtpResult<()> {
372 self.rtcp_index += 1;
373 let index = self.rtcp_index;
374 let index_with_e = index | 0x8000_0000;
376
377 if let SrtpProfile::AeadAes128Gcm = self._profile {
378 let nonce = self.build_gcm_rtcp_nonce(index);
379 let cipher = self
380 .rtcp_gcm_cipher
381 .as_ref()
382 .ok_or(SrtpError::UnsupportedProfile)?;
383
384 let mut aad = Vec::with_capacity(12);
386 aad.extend_from_slice(&packet[..8]);
387 aad.extend_from_slice(&index_with_e.to_be_bytes());
388
389 let payload_data = &packet[8..];
391
392 let payload = Payload {
393 msg: payload_data,
394 aad: &aad,
395 };
396
397 let ciphertext = cipher
398 .encrypt(Nonce::from_slice(&nonce), payload)
399 .map_err(|_| SrtpError::AuthenticationFailed)?;
400
401 packet.truncate(8);
403 packet.extend_from_slice(&ciphertext);
404 packet.extend_from_slice(&index_with_e.to_be_bytes());
405
406 return Ok(());
407 }
408
409 if packet.len() > 8 {
412 self.cipher_rtcp(packet, index)?;
413 }
414
415 packet.extend_from_slice(&index_with_e.to_be_bytes());
417
418 let tag = self.auth_tag_rtcp(packet)?;
420 packet.extend_from_slice(&tag);
421
422 Ok(())
423 }
424
425 pub fn unprotect_rtcp(&mut self, packet: &mut Vec<u8>) -> SrtpResult<()> {
426 let tag_len = self._profile.tag_len();
427 if packet.len() < tag_len + 4 {
428 return Err(SrtpError::PacketTooShort);
429 }
430
431 if let SrtpProfile::AeadAes128Gcm = self._profile {
432 let index_bytes = &packet[packet.len() - 4..];
434 let index_with_e = u32::from_be_bytes([
435 index_bytes[0],
436 index_bytes[1],
437 index_bytes[2],
438 index_bytes[3],
439 ]);
440 let index = index_with_e & 0x7FFF_FFFF;
441
442 if index > self.rtcp_index {
444 self.rtcp_index = index;
445 }
446
447 let nonce = self.build_gcm_rtcp_nonce(index);
448 let cipher = self
449 .rtcp_gcm_cipher
450 .as_ref()
451 .ok_or(SrtpError::UnsupportedProfile)?;
452
453 let mut aad = Vec::with_capacity(12);
455 aad.extend_from_slice(&packet[..8]);
456 aad.extend_from_slice(&index_with_e.to_be_bytes());
457
458 let ciphertext_and_tag = &packet[8..packet.len() - 4];
462
463 let payload = Payload {
464 msg: ciphertext_and_tag,
465 aad: &aad,
466 };
467
468 let plaintext = cipher
469 .decrypt(Nonce::from_slice(&nonce), payload)
470 .map_err(|_| SrtpError::AuthenticationFailed)?;
471
472 packet.truncate(8);
474 packet.extend_from_slice(&plaintext);
475
476 return Ok(());
477 }
478
479 let split = packet.len() - tag_len;
481 let tag = packet[split..].to_vec();
482 packet.truncate(split);
483
484 let expected = self.auth_tag_rtcp(packet)?;
486 if !constant_time_eq(&tag, &expected) {
487 return Err(SrtpError::AuthenticationFailed);
488 }
489
490 let index_bytes = &packet[packet.len() - 4..];
492 let index_with_e = u32::from_be_bytes([
493 index_bytes[0],
494 index_bytes[1],
495 index_bytes[2],
496 index_bytes[3],
497 ]);
498 packet.truncate(packet.len() - 4);
499
500 let e_bit = (index_with_e & 0x8000_0000) != 0;
501 let index = index_with_e & 0x7FFF_FFFF;
502
503 if index > self.rtcp_index {
506 self.rtcp_index = index;
507 }
508
509 if e_bit && packet.len() > 8 {
510 self.cipher_rtcp(packet, index)?;
511 }
512
513 Ok(())
514 }
515
516 fn cipher_rtcp(&self, packet: &mut [u8], index: u32) -> SrtpResult<()> {
517 let mut iv = [0u8; 16];
522 for (i, &b) in self.rtcp_keys.salt.iter().take(14).enumerate() {
523 iv[i] = b;
524 }
525
526 let mut block = [0u8; 16];
527 block[4..8].copy_from_slice(&self.ssrc.to_be_bytes());
528 block[10..14].copy_from_slice(&index.to_be_bytes());
529
530 for i in 0..16 {
531 iv[i] ^= block[i];
532 }
533
534 let mut cipher = Aes128Ctr::new_from_slices(&self.rtcp_keys.cipher_key[..16], &iv)
536 .map_err(|_| SrtpError::UnsupportedProfile)?;
537
538 cipher.apply_keystream(&mut packet[8..]);
540
541 Ok(())
542 }
543
544 fn auth_tag_rtcp(&self, data: &[u8]) -> SrtpResult<Vec<u8>> {
545 let mut mac = <HmacSha1 as hmac::digest::KeyInit>::new_from_slice(&self.rtcp_keys.auth_key)
546 .map_err(|_| SrtpError::UnsupportedProfile)?;
547 mac.update(data);
548 let result = mac.finalize().into_bytes();
549 let tag_len = self._profile.tag_len();
550 Ok(result[..tag_len].to_vec())
551 }
552
553 pub fn protect(&mut self, packet: &mut RtpPacket) -> SrtpResult<()> {
554 let roc = self.estimate_roc(packet.header.sequence_number);
555
556 if let SrtpProfile::AeadAes128Gcm = self._profile {
557 let nonce = self.build_gcm_nonce(packet.header.sequence_number, roc);
558 let cipher = self
559 .rtp_gcm_cipher
560 .as_ref()
561 .ok_or(SrtpError::UnsupportedProfile)?;
562
563 let original_payload = std::mem::take(&mut packet.payload);
565 let aad = packet.marshal()?;
566 packet.payload = original_payload;
567
568 let payload = Payload {
569 msg: packet.payload.as_ref(),
570 aad: &aad,
571 };
572
573 let ciphertext = cipher
574 .encrypt(Nonce::from_slice(&nonce), payload)
575 .map_err(|_| SrtpError::AuthenticationFailed)?;
576
577 packet.payload = Bytes::from(ciphertext);
578 self.update(packet.header.sequence_number, roc);
579 return Ok(());
580 }
581
582 self.cipher_payload(packet, roc)?;
583 let auth_input = packet.marshal()?;
584 let tag = self.auth_tag(&auth_input, roc)?;
585 let mut with_tag = packet.payload.to_vec();
586 with_tag.extend_from_slice(&tag);
587 packet.payload = Bytes::from(with_tag);
588 self.update(packet.header.sequence_number, roc);
589 Ok(())
590 }
591
592 pub fn unprotect(&mut self, packet: &mut RtpPacket) -> SrtpResult<()> {
593 let tag_len = self._profile.tag_len();
594 if packet.payload.len() < tag_len {
595 return Err(SrtpError::PacketTooShort);
596 }
597
598 let roc = self.estimate_roc(packet.header.sequence_number);
599
600 if let SrtpProfile::AeadAes128Gcm = self._profile {
601 let nonce = self.build_gcm_nonce(packet.header.sequence_number, roc);
602 let cipher = self
603 .rtp_gcm_cipher
604 .as_ref()
605 .ok_or(SrtpError::UnsupportedProfile)?;
606
607 let original_payload = std::mem::take(&mut packet.payload);
609 let aad = packet.marshal()?;
610 packet.payload = original_payload;
611
612 let payload = Payload {
613 msg: packet.payload.as_ref(),
614 aad: &aad,
615 };
616
617 let plaintext = cipher
618 .decrypt(Nonce::from_slice(&nonce), payload)
619 .map_err(|_| SrtpError::AuthenticationFailed)?;
620
621 packet.payload = Bytes::from(plaintext);
622 self.update(packet.header.sequence_number, roc);
623 return Ok(());
624 }
625
626 let split = packet.payload.len() - tag_len;
627 let tag = packet.payload[split..].to_vec();
628 packet.payload = packet.payload.slice(..split);
630 let auth_input = packet.marshal()?;
631 let expected = self.auth_tag(&auth_input, roc)?;
632 if !constant_time_eq(&tag, &expected) {
633 return Err(SrtpError::AuthenticationFailed);
634 }
635 self.cipher_payload(packet, roc)?;
636 self.update(packet.header.sequence_number, roc);
637 Ok(())
638 }
639
640 fn cipher_payload(&self, packet: &mut RtpPacket, roc: u32) -> SrtpResult<()> {
641 if packet.payload.is_empty() {
642 return Ok(());
643 }
644 match self._profile {
645 SrtpProfile::NullCipherHmac => Ok(()),
646 SrtpProfile::Aes128Sha1_80 | SrtpProfile::Aes128Sha1_32 => {
647 let iv = self.build_iv(packet.header.sequence_number, roc);
648 let mut cipher = Aes128Ctr::new_from_slices(&self.rtp_keys.cipher_key[..16], &iv)
649 .map_err(|_| SrtpError::UnsupportedProfile)?;
650 let mut buf = packet.payload.to_vec();
654 cipher.apply_keystream(&mut buf);
655 packet.payload = Bytes::from(buf);
656 Ok(())
657 }
658 _ => Err(SrtpError::UnsupportedProfile),
659 }
660 }
661
662 fn auth_tag(&self, data: &[u8], roc: u32) -> SrtpResult<Vec<u8>> {
663 let mut mac = self
664 .rtp_auth_prototype
665 .as_ref()
666 .ok_or(SrtpError::UnsupportedProfile)?
667 .clone();
668 mac.update(data);
669 mac.update(&roc.to_be_bytes());
670 let result = mac.finalize().into_bytes();
671 let tag_len = self._profile.tag_len();
672 Ok(result[..tag_len].to_vec())
673 }
674
675 fn build_gcm_rtcp_nonce(&self, index: u32) -> [u8; 12] {
676 let mut iv = [0u8; 12];
677 iv.copy_from_slice(&self.rtcp_keys.salt[..12]);
678
679 let mut block = [0u8; 12];
680 block[2..6].copy_from_slice(&self.ssrc.to_be_bytes());
681 block[8..12].copy_from_slice(&index.to_be_bytes());
682
683 for i in 0..12 {
684 iv[i] ^= block[i];
685 }
686 iv
687 }
688
689 fn build_gcm_nonce(&self, sequence: u16, roc: u32) -> [u8; 12] {
690 let mut iv = [0u8; 12];
691 iv.copy_from_slice(&self.rtp_keys.salt[..12]);
692
693 let mut block = [0u8; 12];
694 block[2..6].copy_from_slice(&self.ssrc.to_be_bytes());
695 block[6..10].copy_from_slice(&roc.to_be_bytes());
696 block[10..12].copy_from_slice(&sequence.to_be_bytes());
697
698 for i in 0..12 {
699 iv[i] ^= block[i];
700 }
701 iv
702 }
703
704 fn build_iv(&self, sequence: u16, roc: u32) -> [u8; 16] {
705 let index = ((roc as u64) << 16) | sequence as u64;
706 let mut iv = [0u8; 16];
707 for (i, byte) in self.rtp_keys.salt.iter().enumerate().take(14) {
708 iv[i] = *byte;
709 }
710 let mut block = [0u8; 16];
711 block[4..8].copy_from_slice(&self.ssrc.to_be_bytes());
712
713 let iv_part = index << 16;
716 block[8..16].copy_from_slice(&iv_part.to_be_bytes());
717
718 for i in 0..16 {
719 iv[i] ^= block[i];
720 }
721 iv
722 }
723
724 fn estimate_roc(&self, sequence: u16) -> u32 {
725 let Some(last_seq) = self.last_sequence else {
726 return self.rollover_counter;
727 };
728
729 let roc = self.rollover_counter;
730 let diff = (sequence as i32) - (last_seq as i32);
731
732 if diff < -32768 {
733 roc.wrapping_add(1)
734 } else if diff > 32768 {
735 roc.wrapping_sub(1)
736 } else {
737 roc
738 }
739 }
740
741 fn update(&mut self, sequence: u16, roc: u32) {
742 if self.last_sequence.is_none() {
743 self.last_sequence = Some(sequence);
744 self.rollover_counter = roc;
745 return;
746 }
747
748 let current_index =
749 ((self.rollover_counter as u64) << 16) | (self.last_sequence.unwrap() as u64);
750 let new_index = ((roc as u64) << 16) | (sequence as u64);
751
752 if new_index > current_index {
753 self.rollover_counter = roc;
754 self.last_sequence = Some(sequence);
755 }
756 }
757
758 pub fn ssrc(&self) -> u32 {
759 self.ssrc
760 }
761
762 pub fn direction(&self) -> SrtpDirection {
763 self.direction
764 }
765}
766
767#[cfg(test)]
768mod tests {
769 use super::*;
770 use crate::rtp::{RtpHeader, RtpPacket};
771
772 fn sample_packet(seq: u16) -> RtpPacket {
773 let header = RtpHeader::new(96, seq, 1234, 0xdead_beef);
774 RtpPacket::new(header, vec![1, 2, 3])
775 }
776
777 fn material() -> SrtpKeyingMaterial {
778 SrtpKeyingMaterial::new(vec![0; 16], vec![0; 14])
779 }
780
781 #[test]
782 fn protect_and_unprotect_roundtrip() {
783 let mut session =
784 SrtpSession::new(SrtpProfile::Aes128Sha1_80, material(), material()).unwrap();
785 let mut packet = sample_packet(1);
786 let original = packet.payload.clone();
787 session.protect_rtp(&mut packet).unwrap();
788 assert_eq!(packet.payload.len(), original.len() + 10);
789 assert_ne!(packet.payload[..original.len()], original[..]);
790 session.unprotect_rtp(&mut packet).unwrap();
791 assert_eq!(packet.payload, original);
792 }
793
794 #[test]
795 fn protect_and_unprotect_roundtrip_gcm() {
796 let mut session =
797 SrtpSession::new(SrtpProfile::AeadAes128Gcm, material(), material()).unwrap();
798 let mut packet = sample_packet(1);
799 let original = packet.payload.clone();
800 session.protect_rtp(&mut packet).unwrap();
801 assert_eq!(packet.payload.len(), original.len() + 16);
802 assert_ne!(packet.payload[..original.len()], original[..]);
803 session.unprotect_rtp(&mut packet).unwrap();
804 assert_eq!(packet.payload, original);
805 }
806
807 #[test]
808 fn authentication_failure_returns_error() {
809 let mut ctx = SrtpContext::new(
810 42,
811 SrtpProfile::Aes128Sha1_80,
812 material(),
813 SrtpDirection::Receiver,
814 )
815 .unwrap();
816 let mut packet = sample_packet(1);
817 ctx.protect(&mut packet).unwrap();
818 let mut tampered = packet.payload.to_vec();
820 tampered[0] ^= 0xFF;
821 packet.payload = Bytes::from(tampered);
822 let err = ctx.unprotect(&mut packet).unwrap_err();
823 assert!(matches!(err, SrtpError::AuthenticationFailed));
824 }
825
826 #[test]
827 fn null_cipher_still_authenticates() {
828 let mut ctx = SrtpContext::new(
829 7,
830 SrtpProfile::NullCipherHmac,
831 material(),
832 SrtpDirection::Sender,
833 )
834 .unwrap();
835 let mut packet = sample_packet(10);
836 ctx.protect(&mut packet).unwrap();
837 assert_eq!(packet.payload.len(), 3 + 10);
838 }
839
840 #[test]
841 fn roc_rollover_handling() {
842 let mut sender =
843 SrtpSession::new(SrtpProfile::Aes128Sha1_80, material(), material()).unwrap();
844 let mut receiver =
845 SrtpSession::new(SrtpProfile::Aes128Sha1_80, material(), material()).unwrap();
846
847 let mut p1 = sample_packet(65535);
849 sender.protect_rtp(&mut p1).unwrap();
850
851 let mut p2 = sample_packet(0);
853 sender.protect_rtp(&mut p2).unwrap();
854
855 receiver.unprotect_rtp(&mut p1).unwrap();
857 receiver.unprotect_rtp(&mut p2).unwrap();
858 }
859
860 #[test]
861 fn roc_rollover_reordered() {
862 let mut sender =
863 SrtpSession::new(SrtpProfile::Aes128Sha1_80, material(), material()).unwrap();
864 let mut receiver =
865 SrtpSession::new(SrtpProfile::Aes128Sha1_80, material(), material()).unwrap();
866
867 let mut p0 = sample_packet(50000);
869 sender.protect_rtp(&mut p0).unwrap();
870 receiver.unprotect_rtp(&mut p0).unwrap();
871
872 let mut p1 = sample_packet(65535);
874 sender.protect_rtp(&mut p1).unwrap();
875
876 let mut p2 = sample_packet(0);
878 sender.protect_rtp(&mut p2).unwrap();
879
880 let mut p1_recv = p1.clone();
883 let mut p2_recv = p2.clone();
884
885 receiver.unprotect_rtp(&mut p2_recv).unwrap();
886 receiver.unprotect_rtp(&mut p1_recv).unwrap();
887 }
888}
889
890fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
891 if a.len() != b.len() {
892 return false;
893 }
894 let mut diff = 0u8;
895 for (x, y) in a.iter().zip(b.iter()) {
896 diff |= x ^ y;
897 }
898 diff == 0
899}
900
901#[cfg(test)]
902mod security_tests {
903 use super::*;
904 use crate::rtp::{RtpHeader, RtpPacket};
905
906 fn sample_packet(seq: u16) -> RtpPacket {
907 let header = RtpHeader::new(96, seq, 1234, 0xdead_beef);
908 RtpPacket::new(header, vec![1, 2, 3])
909 }
910
911 #[test]
912 fn default_profile_is_encrypting_not_null() {
913 let default_profile = SrtpProfile::default();
915 assert_eq!(
916 default_profile,
917 SrtpProfile::Aes128Sha1_80,
918 "Default SRTP profile must be Aes128Sha1_80 (encrypting), not NullCipherHmac"
919 );
920 }
921
922 #[test]
923 fn null_cipher_must_be_explicit() {
924 let profiles = [
926 SrtpProfile::default(),
927 SrtpProfile::Aes128Sha1_80,
928 SrtpProfile::Aes128Sha1_32,
929 SrtpProfile::AeadAes128Gcm,
930 ];
931 for p in &profiles {
932 assert_ne!(
933 *p,
934 SrtpProfile::NullCipherHmac,
935 "Production profiles must exclude NullCipherHmac: {:?}",
936 p
937 );
938 }
939 }
940
941 #[test]
942 fn null_cipher_protect_is_transparent_but_authenticates() {
943 let mut ctx = SrtpContext::new(
945 42,
946 SrtpProfile::NullCipherHmac,
947 SrtpKeyingMaterial::new(vec![0; 16], vec![0; 14]),
948 SrtpDirection::Sender,
949 )
950 .unwrap();
951 let mut packet = sample_packet(100);
952 let original_payload = packet.payload.clone();
953 ctx.protect(&mut packet).unwrap();
954 assert_eq!(packet.payload.len(), original_payload.len() + 10);
956 assert_eq!(
957 &packet.payload[..original_payload.len()],
958 &original_payload[..]
959 );
960 let mut rx_ctx = SrtpContext::new(
962 42,
963 SrtpProfile::NullCipherHmac,
964 SrtpKeyingMaterial::new(vec![0; 16], vec![0; 14]),
965 SrtpDirection::Receiver,
966 )
967 .unwrap();
968 rx_ctx.unprotect(&mut packet).unwrap();
969 assert_eq!(packet.payload, original_payload);
970 }
971}