1use aes::cipher::{KeyIvInit, StreamCipher};
26use hmac::{Hmac, Mac};
27use sha1::Sha1;
28
29use crate::error::{Error, Result};
30
31type Aes128Ctr = ctr::Ctr128BE<aes::Aes128>;
32type HmacSha1 = Hmac<Sha1>;
33
34const AUTH_TAG_LEN: usize = 10;
35const MASTER_KEY_LEN: usize = 16;
36const MASTER_SALT_LEN: usize = 14;
37
38pub const KEYING_MATERIAL_LEN: usize = MASTER_KEY_LEN + MASTER_SALT_LEN;
40
41const SESSION_KEY_LEN: usize = 16;
42const SESSION_SALT_LEN: usize = 14;
43const SESSION_AUTH_KEY_LEN: usize = 20;
44
45const LABEL_RTP_CIPHER: u8 = 0x00;
46const LABEL_RTP_AUTH: u8 = 0x01;
47const LABEL_RTP_SALT: u8 = 0x02;
48const LABEL_RTCP_CIPHER: u8 = 0x03;
49const LABEL_RTCP_AUTH: u8 = 0x04;
50const LABEL_RTCP_SALT: u8 = 0x05;
51
52pub struct SrtpContext {
56 session_key: [u8; SESSION_KEY_LEN],
57 session_salt: [u8; SESSION_SALT_LEN],
58 session_auth_key: [u8; SESSION_AUTH_KEY_LEN],
59 rtcp_session_key: [u8; SESSION_KEY_LEN],
60 rtcp_session_salt: [u8; SESSION_SALT_LEN],
61 rtcp_session_auth_key: [u8; SESSION_AUTH_KEY_LEN],
62 roc: u32,
63 s_l: u16,
64 initialized: bool,
65 srtcp_index: u32,
66}
67
68impl SrtpContext {
69 pub fn new(master_key: &[u8], master_salt: &[u8]) -> Result<Self> {
74 if master_key.len() != MASTER_KEY_LEN {
75 return Err(Error::srtp(format!(
76 "master key must be {} bytes, got {}",
77 MASTER_KEY_LEN,
78 master_key.len()
79 )));
80 }
81 if master_salt.len() != MASTER_SALT_LEN {
82 return Err(Error::srtp(format!(
83 "master salt must be {} bytes, got {}",
84 MASTER_SALT_LEN,
85 master_salt.len()
86 )));
87 }
88
89 let session_key = derive_session_key(master_key, master_salt, LABEL_RTP_CIPHER)?;
90 let session_salt = derive_session_salt(master_key, master_salt, LABEL_RTP_SALT)?;
91 let session_auth_key = derive_session_auth_key(master_key, master_salt, LABEL_RTP_AUTH)?;
92
93 let rtcp_session_key = derive_session_key(master_key, master_salt, LABEL_RTCP_CIPHER)?;
94 let rtcp_session_salt = derive_session_salt(master_key, master_salt, LABEL_RTCP_SALT)?;
95 let rtcp_session_auth_key =
96 derive_session_auth_key(master_key, master_salt, LABEL_RTCP_AUTH)?;
97
98 Ok(Self {
99 session_key,
100 session_salt,
101 session_auth_key,
102 rtcp_session_key,
103 rtcp_session_salt,
104 rtcp_session_auth_key,
105 roc: 0,
106 s_l: 0,
107 initialized: false,
108 srtcp_index: 0,
109 })
110 }
111
112 pub fn from_base64(b64_key_material: &str) -> Result<Self> {
114 use base64::Engine;
115 let decoded = base64::engine::general_purpose::STANDARD
116 .decode(b64_key_material.trim())
117 .map_err(|e| Error::srtp(format!("base64 decode error: {}", e)))?;
118 if decoded.len() < KEYING_MATERIAL_LEN {
119 return Err(Error::srtp(format!(
120 "keying material too short: {} bytes, need {}",
121 decoded.len(),
122 KEYING_MATERIAL_LEN
123 )));
124 }
125 Self::new(
126 &decoded[..MASTER_KEY_LEN],
127 &decoded[MASTER_KEY_LEN..KEYING_MATERIAL_LEN],
128 )
129 }
130
131 pub fn generate() -> Result<(Self, String)> {
133 use base64::Engine;
134 let material: [u8; KEYING_MATERIAL_LEN] = rand::random();
135 let b64 = base64::engine::general_purpose::STANDARD.encode(material);
136 let ctx = Self::new(&material[..MASTER_KEY_LEN], &material[MASTER_KEY_LEN..])?;
137 Ok((ctx, b64))
138 }
139
140 pub fn protect_rtp(&mut self, rtp_packet: &[u8]) -> Result<Vec<u8>> {
142 if rtp_packet.len() < 12 {
143 return Err(Error::rtp("RTP packet too short"));
144 }
145
146 let cc = (rtp_packet[0] & 0x0F) as usize;
147 let header_len = 12 + cc * 4;
148 if rtp_packet.len() < header_len {
149 return Err(Error::rtp("RTP packet truncated"));
150 }
151
152 let seq = u16::from_be_bytes([rtp_packet[2], rtp_packet[3]]);
153 let ssrc =
154 u32::from_be_bytes([rtp_packet[8], rtp_packet[9], rtp_packet[10], rtp_packet[11]]);
155
156 if !self.initialized {
158 self.s_l = seq;
159 self.initialized = true;
160 } else if seq == 0 && self.s_l == 0xFFFF {
161 self.roc = self.roc.wrapping_add(1);
162 }
163 self.s_l = seq;
164
165 let iv = build_iv(&self.session_salt, ssrc, self.roc, seq);
166
167 let mut output = rtp_packet.to_vec();
168 aes_128_cm_encrypt(&self.session_key, &iv, &mut output[header_len..])?;
169
170 let tag = compute_rtp_auth_tag(&self.session_auth_key, &output, self.roc)?;
171 output.extend_from_slice(&tag);
172
173 Ok(output)
174 }
175
176 pub fn unprotect_rtp(&mut self, srtp_packet: &[u8]) -> Result<Vec<u8>> {
178 if srtp_packet.len() < 12 + AUTH_TAG_LEN {
179 return Err(Error::srtp("SRTP packet too short"));
180 }
181
182 let cc = (srtp_packet[0] & 0x0F) as usize;
183 let header_len = 12 + cc * 4;
184 if srtp_packet.len() < header_len + AUTH_TAG_LEN {
185 return Err(Error::srtp("SRTP packet truncated"));
186 }
187
188 let seq = u16::from_be_bytes([srtp_packet[2], srtp_packet[3]]);
189 let ssrc = u32::from_be_bytes([
190 srtp_packet[8],
191 srtp_packet[9],
192 srtp_packet[10],
193 srtp_packet[11],
194 ]);
195
196 let estimated_roc = self.estimate_roc(seq);
197
198 let auth_boundary = srtp_packet.len() - AUTH_TAG_LEN;
199 let authenticated_portion = &srtp_packet[..auth_boundary];
200 let received_tag = &srtp_packet[auth_boundary..];
201
202 let computed_tag =
203 compute_rtp_auth_tag(&self.session_auth_key, authenticated_portion, estimated_roc)?;
204 if !constant_time_eq(&computed_tag, received_tag) {
205 return Err(Error::srtp("SRTP authentication failed"));
206 }
207
208 self.update_roc(seq);
209
210 let iv = build_iv(&self.session_salt, ssrc, estimated_roc, seq);
211 let mut output = authenticated_portion.to_vec();
212 aes_128_cm_encrypt(&self.session_key, &iv, &mut output[header_len..])?;
213
214 Ok(output)
215 }
216
217 pub fn protect_rtcp(&mut self, rtcp_packet: &[u8]) -> Result<Vec<u8>> {
219 if rtcp_packet.len() < 8 {
220 return Err(Error::rtcp("RTCP packet too short"));
221 }
222
223 let ssrc = u32::from_be_bytes([
224 rtcp_packet[4],
225 rtcp_packet[5],
226 rtcp_packet[6],
227 rtcp_packet[7],
228 ]);
229
230 let index = self.srtcp_index;
231 self.srtcp_index = self.srtcp_index.wrapping_add(1) & 0x7FFF_FFFF;
232
233 let iv = build_rtcp_iv(&self.rtcp_session_salt, ssrc, index);
234
235 let mut output = rtcp_packet.to_vec();
236 if output.len() > 8 {
237 aes_128_cm_encrypt(&self.rtcp_session_key, &iv, &mut output[8..])?;
238 }
239
240 let e_index = index | 0x8000_0000;
241 output.extend_from_slice(&e_index.to_be_bytes());
242
243 let tag = compute_rtcp_auth_tag(&self.rtcp_session_auth_key, &output)?;
244 output.extend_from_slice(&tag);
245
246 Ok(output)
247 }
248
249 pub fn unprotect_rtcp(&mut self, srtcp_packet: &[u8]) -> Result<Vec<u8>> {
251 if srtcp_packet.len() < 8 + 4 + AUTH_TAG_LEN {
252 return Err(Error::srtp("SRTCP packet too short"));
253 }
254
255 let auth_boundary = srtcp_packet.len() - AUTH_TAG_LEN;
256 let authenticated_portion = &srtcp_packet[..auth_boundary];
257 let received_tag = &srtcp_packet[auth_boundary..];
258
259 let computed_tag =
260 compute_rtcp_auth_tag(&self.rtcp_session_auth_key, authenticated_portion)?;
261 if !constant_time_eq(&computed_tag, received_tag) {
262 return Err(Error::srtp("SRTCP authentication failed"));
263 }
264
265 let index_offset = authenticated_portion.len() - 4;
266 let e_index = u32::from_be_bytes([
267 authenticated_portion[index_offset],
268 authenticated_portion[index_offset + 1],
269 authenticated_portion[index_offset + 2],
270 authenticated_portion[index_offset + 3],
271 ]);
272 let encrypted = (e_index & 0x8000_0000) != 0;
273 let index = e_index & 0x7FFF_FFFF;
274
275 let mut output = authenticated_portion[..index_offset].to_vec();
276
277 if encrypted && output.len() > 8 {
278 let ssrc = u32::from_be_bytes([output[4], output[5], output[6], output[7]]);
279 let iv = build_rtcp_iv(&self.rtcp_session_salt, ssrc, index);
280 aes_128_cm_encrypt(&self.rtcp_session_key, &iv, &mut output[8..])?;
281 }
282
283 Ok(output)
284 }
285
286 fn estimate_roc(&self, seq: u16) -> u32 {
291 if !self.initialized {
292 return 0;
293 }
294
295 let diff = (seq as i32) - (self.s_l as i32);
298
299 if diff > 0x8000 {
300 self.roc.wrapping_sub(1)
303 } else if diff < -0x8000 {
304 self.roc.wrapping_add(1)
307 } else {
308 self.roc
310 }
311 }
312
313 fn update_roc(&mut self, seq: u16) {
315 if !self.initialized {
316 self.s_l = seq;
317 self.initialized = true;
318 return;
319 }
320
321 let estimated = self.estimate_roc(seq);
322
323 if estimated != self.roc {
325 self.roc = estimated;
326 }
327
328 let diff = (seq as i32) - (self.s_l as i32);
331 if diff > 0 || diff < -0x8000 {
332 self.s_l = seq;
334 }
335 }
336}
337
338impl std::fmt::Debug for SrtpContext {
339 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
340 f.debug_struct("SrtpContext")
341 .field("initialized", &self.initialized)
342 .field("roc", &self.roc)
343 .field("s_l", &self.s_l)
344 .finish()
345 }
346}
347
348fn build_iv(session_salt: &[u8; SESSION_SALT_LEN], ssrc: u32, roc: u32, seq: u16) -> [u8; 16] {
349 let mut iv = [0u8; 16];
350 iv[4..8].copy_from_slice(&ssrc.to_be_bytes());
351 iv[8..12].copy_from_slice(&roc.to_be_bytes());
352 iv[12..14].copy_from_slice(&seq.to_be_bytes());
353 for i in 0..SESSION_SALT_LEN {
354 iv[2 + i] ^= session_salt[i];
355 }
356 iv
357}
358
359fn build_rtcp_iv(session_salt: &[u8; SESSION_SALT_LEN], ssrc: u32, index: u32) -> [u8; 16] {
360 let mut iv = [0u8; 16];
361 iv[4..8].copy_from_slice(&ssrc.to_be_bytes());
362 iv[10..14].copy_from_slice(&index.to_be_bytes());
363 for i in 0..SESSION_SALT_LEN {
364 iv[2 + i] ^= session_salt[i];
365 }
366 iv
367}
368
369fn aes_128_cm_encrypt(key: &[u8; 16], iv: &[u8; 16], data: &mut [u8]) -> Result<()> {
370 let mut cipher = Aes128Ctr::new(key.into(), iv.into());
371 cipher.apply_keystream(data);
372 Ok(())
373}
374
375fn compute_rtp_auth_tag(
376 auth_key: &[u8; SESSION_AUTH_KEY_LEN],
377 authenticated_portion: &[u8],
378 roc: u32,
379) -> Result<[u8; AUTH_TAG_LEN]> {
380 let mut mac = HmacSha1::new_from_slice(auth_key)
381 .map_err(|e| Error::srtp(format!("HMAC init error: {}", e)))?;
382 mac.update(authenticated_portion);
383 mac.update(&roc.to_be_bytes());
384 let result = mac.finalize().into_bytes();
385 let mut tag = [0u8; AUTH_TAG_LEN];
386 tag.copy_from_slice(&result[..AUTH_TAG_LEN]);
387 Ok(tag)
388}
389
390fn compute_rtcp_auth_tag(
391 auth_key: &[u8; SESSION_AUTH_KEY_LEN],
392 authenticated_portion: &[u8],
393) -> Result<[u8; AUTH_TAG_LEN]> {
394 let mut mac = HmacSha1::new_from_slice(auth_key)
395 .map_err(|e| Error::srtp(format!("HMAC init error: {}", e)))?;
396 mac.update(authenticated_portion);
397 let result = mac.finalize().into_bytes();
398 let mut tag = [0u8; AUTH_TAG_LEN];
399 tag.copy_from_slice(&result[..AUTH_TAG_LEN]);
400 Ok(tag)
401}
402
403fn derive_session_key(
404 master_key: &[u8],
405 master_salt: &[u8],
406 label: u8,
407) -> Result<[u8; SESSION_KEY_LEN]> {
408 let mut output = [0u8; SESSION_KEY_LEN];
409 prf_aes_cm(master_key, master_salt, label, &mut output)?;
410 Ok(output)
411}
412
413fn derive_session_salt(
414 master_key: &[u8],
415 master_salt: &[u8],
416 label: u8,
417) -> Result<[u8; SESSION_SALT_LEN]> {
418 let mut output = [0u8; SESSION_SALT_LEN];
419 prf_aes_cm(master_key, master_salt, label, &mut output)?;
420 Ok(output)
421}
422
423fn derive_session_auth_key(
424 master_key: &[u8],
425 master_salt: &[u8],
426 label: u8,
427) -> Result<[u8; SESSION_AUTH_KEY_LEN]> {
428 let mut output = [0u8; SESSION_AUTH_KEY_LEN];
429 prf_aes_cm(master_key, master_salt, label, &mut output)?;
430 Ok(output)
431}
432
433fn prf_aes_cm(master_key: &[u8], master_salt: &[u8], label: u8, output: &mut [u8]) -> Result<()> {
434 let mut x = [0u8; 14];
435 x[7] = label;
436
437 for i in 0..14 {
438 x[i] ^= master_salt[i];
439 }
440
441 let mut iv = [0u8; 16];
442 iv[..14].copy_from_slice(&x);
443
444 output.fill(0);
445 let mut cipher = Aes128Ctr::new(master_key.into(), (&iv).into());
446 cipher.apply_keystream(output);
447
448 Ok(())
449}
450
451fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
452 if a.len() != b.len() {
453 return false;
454 }
455 let mut diff = 0u8;
456 for (x, y) in a.iter().zip(b.iter()) {
457 diff |= x ^ y;
458 }
459 diff == 0
460}
461
462pub fn parse_sdp_crypto(sdp: &str) -> Option<String> {
464 for line in sdp.lines() {
465 let line = line.trim();
466 if let Some(rest) = line.strip_prefix("a=crypto:") {
467 let parts: Vec<&str> = rest.splitn(3, ' ').collect();
468 if parts.len() >= 3
469 && parts[1] == "AES_CM_128_HMAC_SHA1_80"
470 && parts[2].starts_with("inline:")
471 {
472 let key_material = &parts[2]["inline:".len()..];
473 let key_material = key_material.split('|').next().unwrap_or(key_material);
474 return Some(key_material.to_string());
475 }
476 }
477 }
478 None
479}
480
481pub fn build_sdp_crypto_line(b64_key: &str) -> String {
483 format!("a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:{}\r\n", b64_key)
484}
485
486#[cfg(test)]
487mod tests {
488 use super::*;
489
490 #[test]
491 fn test_roundtrip_rtp() {
492 let (mut ctx_send, b64) = SrtpContext::generate().unwrap();
493 let mut ctx_recv = SrtpContext::from_base64(&b64).unwrap();
494
495 let mut rtp = vec![0x80, 0x00];
496 rtp.extend_from_slice(&1u16.to_be_bytes());
497 rtp.extend_from_slice(&160u32.to_be_bytes());
498 rtp.extend_from_slice(&0x12345678u32.to_be_bytes());
499 rtp.extend_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]);
500
501 let srtp = ctx_send.protect_rtp(&rtp).unwrap();
502 assert_ne!(&srtp[12..12 + 4], &[0xDE, 0xAD, 0xBE, 0xEF]);
503 assert_eq!(srtp.len(), rtp.len() + AUTH_TAG_LEN);
504
505 let decrypted = ctx_recv.unprotect_rtp(&srtp).unwrap();
506 assert_eq!(decrypted, rtp);
507 }
508
509 #[test]
510 fn test_roundtrip_rtcp() {
511 let (mut ctx_send, b64) = SrtpContext::generate().unwrap();
512 let mut ctx_recv = SrtpContext::from_base64(&b64).unwrap();
513
514 let mut rtcp = vec![0x80, 200];
515 rtcp.extend_from_slice(&6u16.to_be_bytes());
516 rtcp.extend_from_slice(&0xAABBCCDDu32.to_be_bytes());
517 rtcp.extend_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
518
519 let srtcp = ctx_send.protect_rtcp(&rtcp).unwrap();
520 let decrypted = ctx_recv.unprotect_rtcp(&srtcp).unwrap();
521 assert_eq!(decrypted, rtcp);
522 }
523
524 #[test]
525 fn test_auth_failure() {
526 let (mut ctx_send, _) = SrtpContext::generate().unwrap();
527 let (_, b64_other) = SrtpContext::generate().unwrap();
528 let mut ctx_recv = SrtpContext::from_base64(&b64_other).unwrap();
529
530 let mut rtp = vec![0x80, 0x00];
531 rtp.extend_from_slice(&1u16.to_be_bytes());
532 rtp.extend_from_slice(&160u32.to_be_bytes());
533 rtp.extend_from_slice(&0x12345678u32.to_be_bytes());
534 rtp.extend_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]);
535
536 let srtp = ctx_send.protect_rtp(&rtp).unwrap();
537 assert!(ctx_recv.unprotect_rtp(&srtp).is_err());
538 }
539
540 #[test]
541 fn test_parse_sdp_crypto() {
542 let sdp = "v=0\r\n\
543 a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:dGVzdGtleXRlc3RrZXkxMjM0NTY3ODkwMTI=\r\n";
544 let key = parse_sdp_crypto(sdp);
545 assert_eq!(
546 key,
547 Some("dGVzdGtleXRlc3RrZXkxMjM0NTY3ODkwMTI=".to_string())
548 );
549 }
550
551 #[test]
552 fn test_parse_sdp_crypto_with_lifetime() {
553 let sdp = "v=0\r\n\
555 a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:YUJjRGVmZ0hpSktsbU5PUHF|2^31\r\n";
556 let key = parse_sdp_crypto(sdp);
557 assert_eq!(key, Some("YUJjRGVmZ0hpSktsbU5PUHF".to_string()));
558 }
559
560 #[test]
561 fn test_parse_sdp_crypto_no_crypto_line() {
562 let sdp = "v=0\r\nm=audio 5004 RTP/AVP 0\r\n";
563 assert!(parse_sdp_crypto(sdp).is_none());
564 }
565
566 #[test]
567 fn test_parse_sdp_crypto_wrong_suite() {
568 let sdp = "v=0\r\na=crypto:1 AES_CM_256_HMAC_SHA1_80 inline:key=\r\n";
569 assert!(parse_sdp_crypto(sdp).is_none());
570 }
571
572 #[test]
573 fn test_build_sdp_crypto_line() {
574 let key = "YUJjRGVmZ0hpSktsbU5PUHF";
575 let line = build_sdp_crypto_line(key);
576 assert_eq!(
577 line,
578 "a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:YUJjRGVmZ0hpSktsbU5PUHF\r\n"
579 );
580 }
581
582 #[test]
583 fn test_srtp_sequence_rollover() {
584 let (mut ctx_send, b64) = SrtpContext::generate().unwrap();
585 let mut ctx_recv = SrtpContext::from_base64(&b64).unwrap();
586
587 let start_seq = 65530u16;
590 for i in 0u16..12 {
591 let seq = start_seq.wrapping_add(i);
592 let ts = (start_seq as u32 + i as u32) * 160;
593
594 let mut rtp = vec![0x80, 0x00];
595 rtp.extend_from_slice(&seq.to_be_bytes());
596 rtp.extend_from_slice(&ts.to_be_bytes());
597 rtp.extend_from_slice(&0x12345678u32.to_be_bytes());
598 rtp.extend_from_slice(&[0xAA, 0xBB, 0xCC, 0xDD]);
599
600 let srtp = ctx_send.protect_rtp(&rtp).unwrap();
601 let decrypted = ctx_recv.unprotect_rtp(&srtp).unwrap();
602 assert_eq!(decrypted, rtp, "Failed at seq {} (i={})", seq, i);
603 }
604 }
605
606 #[test]
607 fn test_srtp_continuous_through_rollover() {
608 let (mut ctx_send, b64) = SrtpContext::generate().unwrap();
609 let mut ctx_recv = SrtpContext::from_base64(&b64).unwrap();
610
611 let start = 65520u16;
614 for i in 0u16..32 {
615 let seq = start.wrapping_add(i);
616 let ts = (start as u32 + i as u32) * 160;
617
618 let mut rtp = vec![0x80, 0x00];
619 rtp.extend_from_slice(&seq.to_be_bytes());
620 rtp.extend_from_slice(&ts.to_be_bytes());
621 rtp.extend_from_slice(&0x12345678u32.to_be_bytes());
622 rtp.extend_from_slice(&[(i & 0xFF) as u8; 4]);
623
624 let srtp = ctx_send.protect_rtp(&rtp).unwrap();
625 let decrypted = ctx_recv.unprotect_rtp(&srtp).unwrap();
626 assert_eq!(decrypted, rtp, "Failed at seq {} (i={})", seq, i);
627 }
628
629 assert_eq!(ctx_send.roc, 1, "Sender ROC should be 1 after rollover");
631 assert_eq!(ctx_recv.roc, 1, "Receiver ROC should be 1 after rollover");
632 }
633
634 #[test]
635 fn test_srtp_second_rollover() {
636 let (mut ctx_send, b64) = SrtpContext::generate().unwrap();
637 let mut ctx_recv = SrtpContext::from_base64(&b64).unwrap();
638
639 let start1 = 65530u16;
641 for i in 0u16..12 {
642 let seq = start1.wrapping_add(i);
643 let ts = (start1 as u32 + i as u32) * 160;
644
645 let mut rtp = vec![0x80, 0x00];
646 rtp.extend_from_slice(&seq.to_be_bytes());
647 rtp.extend_from_slice(&ts.to_be_bytes());
648 rtp.extend_from_slice(&0x12345678u32.to_be_bytes());
649 rtp.extend_from_slice(&[0xAA; 4]);
650
651 let srtp = ctx_send.protect_rtp(&rtp).unwrap();
652 let decrypted = ctx_recv.unprotect_rtp(&srtp).unwrap();
653 assert_eq!(decrypted, rtp, "First rollover failed at seq {}", seq);
654 }
655
656 assert_eq!(ctx_send.roc, 1);
657 assert_eq!(ctx_recv.roc, 1);
658
659 let current_seq = start1.wrapping_add(12); let start2 = 65530u16;
663
664 for seq in current_seq..start2 {
667 let ts = (65530u32 + 12 + (seq - current_seq) as u32) * 160;
668
669 let mut rtp = vec![0x80, 0x00];
670 rtp.extend_from_slice(&seq.to_be_bytes());
671 rtp.extend_from_slice(&ts.to_be_bytes());
672 rtp.extend_from_slice(&0x12345678u32.to_be_bytes());
673 rtp.extend_from_slice(&[0xBB; 4]);
674
675 let srtp = ctx_send.protect_rtp(&rtp).unwrap();
676 let decrypted = ctx_recv.unprotect_rtp(&srtp).unwrap();
677 assert_eq!(decrypted, rtp, "Gap packet failed at seq {}", seq);
678 }
679
680 assert_eq!(ctx_send.roc, 1);
682 assert_eq!(ctx_recv.roc, 1);
683
684 for i in 0u16..12 {
686 let seq = start2.wrapping_add(i);
687 let ts = (65530u32 * 2 + i as u32) * 160;
688
689 let mut rtp = vec![0x80, 0x00];
690 rtp.extend_from_slice(&seq.to_be_bytes());
691 rtp.extend_from_slice(&ts.to_be_bytes());
692 rtp.extend_from_slice(&0x12345678u32.to_be_bytes());
693 rtp.extend_from_slice(&[0xCC; 4]);
694
695 let srtp = ctx_send.protect_rtp(&rtp).unwrap();
696 let decrypted = ctx_recv.unprotect_rtp(&srtp).unwrap();
697 assert_eq!(decrypted, rtp, "Second rollover failed at seq {}", seq);
698 }
699
700 assert_eq!(
702 ctx_send.roc, 2,
703 "Sender ROC should be 2 after second rollover"
704 );
705 assert_eq!(
706 ctx_recv.roc, 2,
707 "Receiver ROC should be 2 after second rollover"
708 );
709 }
710
711 #[test]
712 fn test_srtp_out_of_order_near_rollover() {
713 let (mut ctx_send, b64) = SrtpContext::generate().unwrap();
714 let mut ctx_recv = SrtpContext::from_base64(&b64).unwrap();
715
716 let sequences = [65534u16, 65535, 0, 1, 2, 3];
718 let mut packets: Vec<(u16, Vec<u8>)> = Vec::new();
719
720 for (i, &seq) in sequences.iter().enumerate() {
721 let ts = (65534u32 + i as u32) * 160;
722 let mut rtp = vec![0x80, 0x00];
723 rtp.extend_from_slice(&seq.to_be_bytes());
724 rtp.extend_from_slice(&ts.to_be_bytes());
725 rtp.extend_from_slice(&0x12345678u32.to_be_bytes());
726 rtp.extend_from_slice(&[i as u8; 4]);
727
728 let srtp = ctx_send.protect_rtp(&rtp).unwrap();
729 packets.push((seq, srtp));
730 }
731
732 let receive_order = [0, 1, 3, 2, 4, 5];
735 for &idx in &receive_order {
736 let (seq, ref srtp) = packets[idx];
737 let result = ctx_recv.unprotect_rtp(srtp);
738 assert!(
739 result.is_ok(),
740 "Failed to decrypt seq {} (idx {})",
741 seq,
742 idx
743 );
744 }
745 }
746
747 #[test]
748 fn test_srtp_multiple_packets() {
749 let (mut ctx_send, b64) = SrtpContext::generate().unwrap();
750 let mut ctx_recv = SrtpContext::from_base64(&b64).unwrap();
751
752 for seq in 0u16..100 {
754 let mut rtp = vec![0x80, 0x00];
755 rtp.extend_from_slice(&seq.to_be_bytes());
756 rtp.extend_from_slice(&((seq as u32) * 160).to_be_bytes());
757 rtp.extend_from_slice(&0x12345678u32.to_be_bytes());
758 rtp.extend_from_slice(&[seq as u8; 160]);
759
760 let srtp = ctx_send.protect_rtp(&rtp).unwrap();
761 let decrypted = ctx_recv.unprotect_rtp(&srtp).unwrap();
762 assert_eq!(decrypted, rtp);
763 }
764 }
765
766 #[test]
767 fn test_srtp_invalid_key_length() {
768 let result = SrtpContext::new(&[0u8; 15], &[0u8; 14]);
770 assert!(result.is_err());
771
772 let result = SrtpContext::new(&[0u8; 16], &[0u8; 13]);
774 assert!(result.is_err());
775 }
776
777 #[test]
778 fn test_srtp_invalid_base64() {
779 let result = SrtpContext::from_base64("not-valid-base64!!!");
780 assert!(result.is_err());
781 }
782
783 #[test]
784 fn test_srtp_too_short_base64() {
785 let result = SrtpContext::from_base64("YWJjZA=="); assert!(result.is_err());
787 }
788
789 #[test]
790 fn test_srtp_packet_too_short() {
791 let (mut ctx, _) = SrtpContext::generate().unwrap();
792
793 let short_rtp = vec![0x80, 0x00, 0x00, 0x01];
795 assert!(ctx.protect_rtp(&short_rtp).is_err());
796
797 let short_srtp = vec![
799 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA0, 0x12, 0x34, 0x56, 0x78,
800 ];
801 assert!(ctx.unprotect_rtp(&short_srtp).is_err());
802 }
803
804 #[test]
805 fn test_srtp_tampered_packet() {
806 let (mut ctx_send, b64) = SrtpContext::generate().unwrap();
807 let mut ctx_recv = SrtpContext::from_base64(&b64).unwrap();
808
809 let mut rtp = vec![0x80, 0x00];
810 rtp.extend_from_slice(&1u16.to_be_bytes());
811 rtp.extend_from_slice(&160u32.to_be_bytes());
812 rtp.extend_from_slice(&0x12345678u32.to_be_bytes());
813 rtp.extend_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]);
814
815 let mut srtp = ctx_send.protect_rtp(&rtp).unwrap();
816
817 srtp[12] ^= 0xFF;
819
820 assert!(ctx_recv.unprotect_rtp(&srtp).is_err());
821 }
822
823 #[test]
824 fn test_srtcp_multiple_packets() {
825 let (mut ctx_send, b64) = SrtpContext::generate().unwrap();
826 let mut ctx_recv = SrtpContext::from_base64(&b64).unwrap();
827
828 for i in 0..10 {
830 let mut rtcp = vec![0x80, 200];
831 rtcp.extend_from_slice(&6u16.to_be_bytes());
832 rtcp.extend_from_slice(&(0xAABBCC00u32 + i).to_be_bytes());
833 rtcp.extend_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
834
835 let srtcp = ctx_send.protect_rtcp(&rtcp).unwrap();
836 let decrypted = ctx_recv.unprotect_rtcp(&srtcp).unwrap();
837 assert_eq!(decrypted, rtcp);
838 }
839 }
840
841 #[test]
842 fn test_rtcp_too_short() {
843 let (mut ctx, _) = SrtpContext::generate().unwrap();
844
845 let short_rtcp = vec![0x80, 200, 0x00, 0x01];
847 assert!(ctx.protect_rtcp(&short_rtcp).is_err());
848 }
849
850 #[test]
851 fn test_srtp_context_debug() {
852 let (ctx, _) = SrtpContext::generate().unwrap();
853 let debug_str = format!("{:?}", ctx);
854 assert!(debug_str.contains("SrtpContext"));
855 assert!(debug_str.contains("initialized"));
856 }
857
858 #[test]
859 fn test_constant_time_eq() {
860 assert!(constant_time_eq(&[1, 2, 3], &[1, 2, 3]));
861 assert!(!constant_time_eq(&[1, 2, 3], &[1, 2, 4]));
862 assert!(!constant_time_eq(&[1, 2, 3], &[1, 2]));
863 assert!(constant_time_eq(&[], &[]));
864 }
865}