1use aes::Aes128;
25use aes::cipher::{KeyIvInit, StreamCipher};
26use hmac::{Hmac, Mac};
27use sha1::Sha1;
28use subtle::ConstantTimeEq;
29
30type Aes128Ctr = ctr::Ctr128BE<Aes128>;
31type HmacSha1 = Hmac<Sha1>;
32
33pub const MASTER_KEY_LEN: usize = 16;
35pub const MASTER_SALT_LEN: usize = 14;
37pub const TAG_LEN: usize = 10;
39
40const SESSION_KEY_LEN: usize = 16;
41const SESSION_SALT_LEN: usize = 14;
42const SESSION_AUTH_LEN: usize = 20;
51
52#[derive(Debug, Clone, Copy)]
54enum Label {
55 RtpEncryption = 0x00,
56 RtpAuthentication = 0x01,
57 RtpSalt = 0x02,
58 RtcpEncryption = 0x03,
59 RtcpAuthentication = 0x04,
60 RtcpSalt = 0x05,
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
65#[non_exhaustive]
66pub enum SrtpError {
67 #[error("{what} must be {expected} octets, not {actual}")]
69 KeyLength {
70 what: &'static str,
72 expected: usize,
74 actual: usize,
76 },
77 #[error("packet is {0} octets; too short to be authenticated")]
79 TooShort(usize),
80 #[error("authentication failed")]
85 NotAuthentic,
86 #[error("replayed or too old: sequence {0}")]
88 Replayed(u16),
89 #[error("replayed or too old SRTCP index {0}")]
91 ReplayedRtcp(u32),
92}
93
94#[derive(Clone)]
96struct Session {
97 rtp_key: [u8; SESSION_KEY_LEN],
98 rtp_salt: [u8; SESSION_SALT_LEN],
99 rtp_auth: [u8; SESSION_AUTH_LEN],
100 rtcp_key: [u8; SESSION_KEY_LEN],
101 rtcp_salt: [u8; SESSION_SALT_LEN],
102 rtcp_auth: [u8; SESSION_AUTH_LEN],
103}
104
105impl std::fmt::Debug for Session {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 f.write_str("Session { .. }")
109 }
110}
111
112fn derive(
119 master_key: &[u8; MASTER_KEY_LEN],
120 master_salt: &[u8; MASTER_SALT_LEN],
121 label: Label,
122 out: &mut [u8],
123) {
124 let mut iv = [0u8; 16];
125 iv[..MASTER_SALT_LEN].copy_from_slice(master_salt);
126 iv[7] ^= label as u8;
127 iv[14] = 0;
131 iv[15] = 0;
132
133 out.fill(0);
134 let mut cipher = Aes128Ctr::new(master_key.into(), (&iv).into());
135 cipher.apply_keystream(out);
136}
137
138#[derive(Debug)]
143pub struct Context {
144 session: Session,
145 roc: u32,
147 highest_seq: Option<u16>,
149 replay: u64,
151 rtcp_index: u32,
153 highest_rtcp_index: Option<u32>,
155 rtcp_replay: u64,
157}
158
159impl Context {
160 pub fn new(master_key: &[u8], master_salt: &[u8]) -> Result<Self, SrtpError> {
162 let key: &[u8; MASTER_KEY_LEN] =
163 master_key.try_into().map_err(|_| SrtpError::KeyLength {
164 what: "master key",
165 expected: MASTER_KEY_LEN,
166 actual: master_key.len(),
167 })?;
168 let salt: &[u8; MASTER_SALT_LEN] =
169 master_salt.try_into().map_err(|_| SrtpError::KeyLength {
170 what: "master salt",
171 expected: MASTER_SALT_LEN,
172 actual: master_salt.len(),
173 })?;
174
175 let mut session = Session {
176 rtp_key: [0; SESSION_KEY_LEN],
177 rtp_salt: [0; SESSION_SALT_LEN],
178 rtp_auth: [0; SESSION_AUTH_LEN],
179 rtcp_key: [0; SESSION_KEY_LEN],
180 rtcp_salt: [0; SESSION_SALT_LEN],
181 rtcp_auth: [0; SESSION_AUTH_LEN],
182 };
183 derive(key, salt, Label::RtpEncryption, &mut session.rtp_key);
184 derive(key, salt, Label::RtpSalt, &mut session.rtp_salt);
185 derive(key, salt, Label::RtpAuthentication, &mut session.rtp_auth);
186 derive(key, salt, Label::RtcpEncryption, &mut session.rtcp_key);
187 derive(key, salt, Label::RtcpSalt, &mut session.rtcp_salt);
188 derive(key, salt, Label::RtcpAuthentication, &mut session.rtcp_auth);
189
190 Ok(Self {
191 session,
192 roc: 0,
193 highest_seq: None,
194 replay: 0,
195 rtcp_index: 0,
196 highest_rtcp_index: None,
197 rtcp_replay: 0,
198 })
199 }
200
201 pub fn protect(&mut self, packet: &[u8]) -> Result<Vec<u8>, SrtpError> {
207 let header_len = rtp_header_len(packet).ok_or(SrtpError::TooShort(packet.len()))?;
208 let (sequence, ssrc) =
209 sequence_and_ssrc(packet).ok_or(SrtpError::TooShort(packet.len()))?;
210
211 let roc = match self.highest_seq {
214 Some(previous) if i32::from(previous) - i32::from(sequence) > 32_768 => {
215 self.roc = self.roc.wrapping_add(1);
216 self.roc
217 }
218 _ => self.roc,
219 };
220 self.highest_seq = Some(sequence);
221
222 let mut out = packet.to_vec();
223 let (_, payload) = out.split_at_mut(header_len);
224 keystream(
225 &self.session.rtp_key,
226 &self.session.rtp_salt,
227 ssrc,
228 index_of(roc, sequence),
229 )
230 .apply_keystream(payload);
231
232 let tag = authenticate(&self.session.rtp_auth, &out, Some(roc));
235 out.extend_from_slice(&tag);
236 Ok(out)
237 }
238
239 pub fn unprotect(&mut self, packet: &[u8]) -> Result<Vec<u8>, SrtpError> {
245 if packet.len() < TAG_LEN {
246 return Err(SrtpError::TooShort(packet.len()));
247 }
248 let (body, tag) = packet.split_at(packet.len() - TAG_LEN);
249 let header_len = rtp_header_len(body).ok_or(SrtpError::TooShort(body.len()))?;
250 let (sequence, ssrc) = sequence_and_ssrc(body).ok_or(SrtpError::TooShort(body.len()))?;
251
252 let roc = self.guess_roc(sequence);
253 let expected = authenticate(&self.session.rtp_auth, body, Some(roc));
254 if expected.ct_eq(tag).unwrap_u8() != 1 {
255 return Err(SrtpError::NotAuthentic);
256 }
257
258 self.check_replay(roc, sequence)?;
260
261 let mut out = body.to_vec();
262 let (_, payload) = out.split_at_mut(header_len);
263 keystream(
264 &self.session.rtp_key,
265 &self.session.rtp_salt,
266 ssrc,
267 index_of(roc, sequence),
268 )
269 .apply_keystream(payload);
270
271 self.accept(roc, sequence);
272 Ok(out)
273 }
274
275 pub fn protect_rtcp(&mut self, packet: &[u8]) -> Result<Vec<u8>, SrtpError> {
277 const RTCP_HEADER_LEN: usize = 8;
279 if packet.len() < RTCP_HEADER_LEN {
280 return Err(SrtpError::TooShort(packet.len()));
281 }
282 let ssrc = u32::from_be_bytes(
283 packet
284 .get(4..8)
285 .and_then(|s| s.try_into().ok())
286 .ok_or(SrtpError::TooShort(packet.len()))?,
287 );
288 let index = self.rtcp_index;
292 self.rtcp_index = self.rtcp_index.wrapping_add(1) & 0x7FFF_FFFF;
293
294 let mut out = packet.to_vec();
295 let (_, payload) = out.split_at_mut(RTCP_HEADER_LEN);
296 keystream(
297 &self.session.rtcp_key,
298 &self.session.rtcp_salt,
299 ssrc,
300 u64::from(index),
301 )
302 .apply_keystream(payload);
303
304 out.extend_from_slice(&(index | 0x8000_0000).to_be_bytes());
306 let tag = authenticate(&self.session.rtcp_auth, &out, None);
307 out.extend_from_slice(&tag);
308 Ok(out)
309 }
310
311 pub fn unprotect_rtcp(&mut self, packet: &[u8]) -> Result<Vec<u8>, SrtpError> {
313 const RTCP_HEADER_LEN: usize = 8;
314 const TRAILER_LEN: usize = 4;
315 if packet.len() < RTCP_HEADER_LEN + TRAILER_LEN + TAG_LEN {
316 return Err(SrtpError::TooShort(packet.len()));
317 }
318 let (body, tag) = packet.split_at(packet.len() - TAG_LEN);
319 let expected = authenticate(&self.session.rtcp_auth, body, None);
320 if expected.ct_eq(tag).unwrap_u8() != 1 {
321 return Err(SrtpError::NotAuthentic);
322 }
323
324 let (payload_and_header, trailer) = body.split_at(body.len() - TRAILER_LEN);
325 let trailer = u32::from_be_bytes(
326 trailer
327 .try_into()
328 .map_err(|_| SrtpError::TooShort(packet.len()))?,
329 );
330 let encrypted = trailer & 0x8000_0000 != 0;
331 let index = trailer & 0x7FFF_FFFF;
332 self.check_rtcp_replay(index)?;
335 let ssrc = u32::from_be_bytes(
336 body.get(4..8)
337 .and_then(|s| s.try_into().ok())
338 .ok_or(SrtpError::TooShort(body.len()))?,
339 );
340
341 let mut out = payload_and_header.to_vec();
342 if encrypted {
343 let (_, payload) = out.split_at_mut(RTCP_HEADER_LEN);
344 keystream(
345 &self.session.rtcp_key,
346 &self.session.rtcp_salt,
347 ssrc,
348 u64::from(index),
349 )
350 .apply_keystream(payload);
351 }
352 self.accept_rtcp(index);
353 Ok(out)
354 }
355
356 fn guess_roc(&self, sequence: u16) -> u32 {
364 let Some(highest) = self.highest_seq else {
365 return self.roc;
366 };
367 let (sequence, highest) = (i32::from(sequence), i32::from(highest));
368
369 if highest < 32_768 {
370 if sequence - highest > 32_768 {
372 return self.roc.wrapping_sub(1);
373 }
374 } else if highest - 32_768 > sequence {
375 return self.roc.wrapping_add(1);
377 }
378 self.roc
379 }
380
381 fn check_replay(&self, roc: u32, sequence: u16) -> Result<(), SrtpError> {
382 let Some(highest) = self.highest_seq else {
383 return Ok(());
384 };
385 let incoming = index_of(roc, sequence);
386 let current = index_of(self.roc, highest);
387
388 if incoming > current {
389 return Ok(());
390 }
391 let behind = current - incoming;
392 if behind >= 64 {
393 return Err(SrtpError::Replayed(sequence));
396 }
397 if self.replay & (1 << behind) != 0 {
398 return Err(SrtpError::Replayed(sequence));
399 }
400 Ok(())
401 }
402
403 fn accept(&mut self, roc: u32, sequence: u16) {
404 let incoming = index_of(roc, sequence);
405 let current = self
406 .highest_seq
407 .map_or(0, |highest| index_of(self.roc, highest));
408
409 if self.highest_seq.is_none() || incoming > current {
410 let advance = if self.highest_seq.is_none() {
411 0
412 } else {
413 incoming - current
414 };
415 self.replay = if advance >= 64 {
416 0
417 } else {
418 self.replay << advance
419 };
420 self.replay |= 1;
421 self.roc = roc;
422 self.highest_seq = Some(sequence);
423 } else {
424 let behind = current - incoming;
425 if behind < 64 {
426 self.replay |= 1 << behind;
427 }
428 }
429 }
430
431 fn check_rtcp_replay(&self, index: u32) -> Result<(), SrtpError> {
432 let Some(highest) = self.highest_rtcp_index else {
433 return Ok(());
434 };
435 if srtcp_forward_distance(highest, index).is_some() {
436 return Ok(());
437 }
438 let behind = highest.wrapping_sub(index) & SRTCP_INDEX_MASK;
439 if behind >= 64 || self.rtcp_replay & (1u64 << behind) != 0 {
440 return Err(SrtpError::ReplayedRtcp(index));
441 }
442 Ok(())
443 }
444
445 fn accept_rtcp(&mut self, index: u32) {
446 let Some(highest) = self.highest_rtcp_index else {
447 self.highest_rtcp_index = Some(index);
448 self.rtcp_replay = 1;
449 return;
450 };
451 if let Some(advance) = srtcp_forward_distance(highest, index) {
452 self.rtcp_replay = if advance >= 64 {
453 0
454 } else {
455 self.rtcp_replay << advance
456 };
457 self.rtcp_replay |= 1;
458 self.highest_rtcp_index = Some(index);
459 } else {
460 let behind = highest.wrapping_sub(index) & SRTCP_INDEX_MASK;
461 if behind < 64 {
462 self.rtcp_replay |= 1u64 << behind;
463 }
464 }
465 }
466}
467
468const SRTCP_INDEX_MASK: u32 = 0x7FFF_FFFF;
469const SRTCP_INDEX_HALF_RANGE: u32 = 0x4000_0000;
470
471fn srtcp_forward_distance(current: u32, incoming: u32) -> Option<u32> {
477 let distance = incoming.wrapping_sub(current) & SRTCP_INDEX_MASK;
478 (distance != 0 && distance < SRTCP_INDEX_HALF_RANGE).then_some(distance)
479}
480
481fn index_of(roc: u32, sequence: u16) -> u64 {
483 (u64::from(roc) << 16) | u64::from(sequence)
484}
485
486fn keystream(
493 key: &[u8; SESSION_KEY_LEN],
494 salt: &[u8; SESSION_SALT_LEN],
495 ssrc: u32,
496 index: u64,
497) -> Aes128Ctr {
498 let mut iv = [0u8; 16];
499 iv[..SESSION_SALT_LEN].copy_from_slice(salt);
500
501 for (slot, byte) in iv.iter_mut().skip(4).zip(ssrc.to_be_bytes()) {
502 *slot ^= byte;
503 }
504 for (slot, byte) in iv
506 .iter_mut()
507 .skip(8)
508 .zip(index.to_be_bytes().into_iter().skip(2))
509 {
510 *slot ^= byte;
511 }
512 Aes128Ctr::new(key.into(), (&iv).into())
513}
514
515fn authenticate(key: &[u8], data: &[u8], roc: Option<u32>) -> [u8; TAG_LEN] {
521 let mut mac = <HmacSha1 as Mac>::new_from_slice(key)
522 .unwrap_or_else(|_| unreachable!("HMAC accepts a key of any length"));
523 mac.update(data);
524 if let Some(roc) = roc {
525 mac.update(&roc.to_be_bytes());
526 }
527 let full = mac.finalize().into_bytes();
528 let mut tag = [0u8; TAG_LEN];
529 tag.copy_from_slice(full.get(..TAG_LEN).unwrap_or(&[0u8; TAG_LEN]));
531 tag
532}
533
534fn sequence_and_ssrc(packet: &[u8]) -> Option<(u16, u32)> {
539 let sequence = u16::from_be_bytes(packet.get(2..4)?.try_into().ok()?);
540 let ssrc = u32::from_be_bytes(packet.get(8..12)?.try_into().ok()?);
541 Some((sequence, ssrc))
542}
543
544fn rtp_header_len(packet: &[u8]) -> Option<usize> {
549 let first = *packet.first()?;
550 if packet.len() < 12 {
551 return None;
552 }
553 let csrc_count = usize::from(first & 0x0F);
554 let mut len = 12 + csrc_count * 4;
555 if first & 0x10 != 0 {
556 let words = usize::from(u16::from_be_bytes([
558 *packet.get(len + 2)?,
559 *packet.get(len + 3)?,
560 ]));
561 len += 4 + words * 4;
562 }
563 (len <= packet.len()).then_some(len)
564}
565
566#[cfg(test)]
567#[allow(
568 clippy::unwrap_used,
569 clippy::expect_used,
570 clippy::panic,
571 clippy::indexing_slicing
572)]
573mod tests {
574 use super::*;
575
576 fn hex(text: &str) -> Vec<u8> {
577 (0..text.len())
578 .step_by(2)
579 .map(|i| u8::from_str_radix(&text[i..i + 2], 16).expect("hex"))
580 .collect()
581 }
582
583 #[test]
596 fn key_derivation_matches_the_rfc() {
597 let master_key: [u8; 16] = hex("E1F97A0D3E018BE0D64FA32C06DE4139").try_into().unwrap();
598 let master_salt: [u8; 14] = hex("0EC675AD498AFEEBB6960B3AABE6").try_into().unwrap();
599
600 let mut cipher_key = [0u8; 16];
601 derive(
602 &master_key,
603 &master_salt,
604 Label::RtpEncryption,
605 &mut cipher_key,
606 );
607 assert_eq!(cipher_key.to_vec(), hex("C61E7A93744F39EE10734AFE3FF7A087"));
608
609 let mut cipher_salt = [0u8; 14];
610 derive(&master_key, &master_salt, Label::RtpSalt, &mut cipher_salt);
611 assert_eq!(cipher_salt.to_vec(), hex("30CBBC08863D8C85D49DB34A9AE1"));
612
613 let mut auth_key = [0u8; 94];
614 derive(
615 &master_key,
616 &master_salt,
617 Label::RtpAuthentication,
618 &mut auth_key,
619 );
620 assert_eq!(
621 auth_key.to_vec(),
622 hex("CEBE321F6FF7716B6FD4AB49AF256A15\
623 6D38BAA48F0A0ACF3C34E2359E6CDBCE\
624 E049646C43D9327AD175578EF7227098\
625 6371C10C9A369AC2F94A8C5FBCDDDC25\
626 6D6E919A48B610EF17C2041E47403576\
627 6B68642C59BBFC2F34DB60DBDFB2")
628 );
629 }
630
631 #[test]
644 fn the_session_authentication_key_is_the_160_bits_the_rfc_fixes() {
645 let context = Context::new(
646 &hex("E1F97A0D3E018BE0D64FA32C06DE4139"),
647 &hex("0EC675AD498AFEEBB6960B3AABE6"),
648 )
649 .expect("a context");
650
651 assert_eq!(
652 context.session.rtp_auth.len(),
653 20,
654 "n_a SHALL be 160 bits (RFC 3711 §5.2, §8.2)"
655 );
656 assert_eq!(
658 context.session.rtp_auth.to_vec(),
659 hex("CEBE321F6FF7716B6FD4AB49AF256A156D38BAA4")
660 );
661 assert_eq!(context.session.rtcp_auth.len(), 20, "and for SRTCP too");
662 }
663
664 #[test]
675 fn the_authentication_tag_is_hmac_sha1_over_the_packet_and_the_roc() {
676 let k_a = hex("CEBE321F6FF7716B6FD4AB49AF256A156D38BAA4");
677 let m = hex("806E5CBA50681DE55C621599");
678
679 assert_eq!(
680 authenticate(&k_a, &m, Some(0xD462_564A)).to_vec(),
681 hex("2E19C5351B7F99278F33"),
682 "SRTP: M = Authenticated Portion || ROC"
683 );
684 assert_eq!(
685 authenticate(&k_a, &m, None).to_vec(),
686 hex("66126DD7550B7E7C90A4"),
687 "SRTCP: M = Authenticated Portion only"
688 );
689 }
690
691 #[test]
699 fn the_first_srtcp_packet_carries_index_zero() {
700 let (mut send, _) = pair();
701 let mut packet = vec![0x80, 201, 0x00, 0x07];
702 packet.extend_from_slice(&0xCAFE_BABEu32.to_be_bytes());
703 packet.extend_from_slice(b"REPORTBODY-REPORTBODY-RE");
704
705 let first = send.protect_rtcp(&packet).expect("protects");
706 let trailer = trailer_of(&first);
707 assert_eq!(trailer & 0x8000_0000, 0x8000_0000, "the E flag is set");
708 assert_eq!(trailer & 0x7FFF_FFFF, 0, "the first index is zero");
709
710 let second = send.protect_rtcp(&packet).expect("protects");
711 assert_eq!(
712 trailer_of(&second) & 0x7FFF_FFFF,
713 1,
714 "and it increments after each packet, not before"
715 );
716 }
717
718 fn trailer_of(protected: &[u8]) -> u32 {
720 let end = protected.len() - TAG_LEN;
721 u32::from_be_bytes(protected[end - 4..end].try_into().expect("four octets"))
722 }
723
724 #[test]
726 fn the_keystream_matches_the_rfc() {
727 let key: [u8; 16] = hex("2B7E151628AED2A6ABF7158809CF4F3C").try_into().unwrap();
728 let salt: [u8; 14] = hex("F0F1F2F3F4F5F6F7F8F9FAFBFCFD").try_into().unwrap();
731
732 let mut out = [0u8; 48];
733 keystream(&key, &salt, 0, 0).apply_keystream(&mut out);
734
735 assert_eq!(out[..16].to_vec(), hex("E03EAD0935C95E80E166B16DD92B4EB4"));
736 assert_eq!(
737 out[16..32].to_vec(),
738 hex("D23513162B02D0F72A43A2FE4A5F97AB")
739 );
740 assert_eq!(out[32..].to_vec(), hex("41E95B3BB0A2E8DD477901E4FCA894C0"));
741 }
742
743 fn rtp(sequence: u16, payload: &[u8]) -> Vec<u8> {
744 let mut packet = vec![0x80, 0x00];
745 packet.extend_from_slice(&sequence.to_be_bytes());
746 packet.extend_from_slice(&(u32::from(sequence) * 160).to_be_bytes());
747 packet.extend_from_slice(&0xDEAD_BEEFu32.to_be_bytes());
748 packet.extend_from_slice(payload);
749 packet
750 }
751
752 fn pair() -> (Context, Context) {
753 let key = [7u8; 16];
754 let salt = [9u8; 14];
755 (
756 Context::new(&key, &salt).expect("a sender"),
757 Context::new(&key, &salt).expect("a receiver"),
758 )
759 }
760
761 #[test]
762 fn a_protected_packet_round_trips() {
763 let (mut send, mut recv) = pair();
764 let plain = rtp(1000, b"the quick brown fox jumps");
765
766 let protected = send.protect(&plain).expect("protects");
767 assert_eq!(protected.len(), plain.len() + TAG_LEN);
768 assert_eq!(recv.unprotect(&protected).expect("unprotects"), plain);
769 }
770
771 #[test]
774 fn the_header_is_readable_and_the_payload_is_not() {
775 let (mut send, _) = pair();
776 let plain = rtp(7, b"SECRET AUDIO SAMPLES HERE");
777 let protected = send.protect(&plain).expect("protects");
778
779 assert_eq!(
780 &protected[..12],
781 &plain[..12],
782 "the header travels in the clear"
783 );
784 assert!(
785 !protected.windows(6).any(|w| w == b"SECRET"),
786 "the payload must not appear on the wire"
787 );
788 }
789
790 #[test]
791 fn an_altered_packet_is_refused() {
792 let (mut send, mut recv) = pair();
793 let mut protected = send.protect(&rtp(1, b"hello")).expect("protects");
794
795 protected[14] ^= 0x01;
797 assert_eq!(recv.unprotect(&protected), Err(SrtpError::NotAuthentic));
798 }
799
800 #[test]
802 fn an_altered_header_is_refused() {
803 let (mut send, mut recv) = pair();
804 let mut protected = send.protect(&rtp(1, b"hello")).expect("protects");
805
806 protected[3] ^= 0x01; assert_eq!(recv.unprotect(&protected), Err(SrtpError::NotAuthentic));
808 }
809
810 #[test]
811 fn a_packet_from_a_different_key_is_refused() {
812 let (mut send, _) = pair();
813 let mut stranger = Context::new(&[1u8; 16], &[2u8; 14]).expect("a context");
814 let protected = send.protect(&rtp(1, b"hello")).expect("protects");
815 assert_eq!(stranger.unprotect(&protected), Err(SrtpError::NotAuthentic));
816 }
817
818 #[test]
821 fn a_replayed_packet_is_refused() {
822 let (mut send, mut recv) = pair();
823 let protected = send.protect(&rtp(100, b"hello")).expect("protects");
824
825 recv.unprotect(&protected).expect("the first time");
826 assert_eq!(recv.unprotect(&protected), Err(SrtpError::Replayed(100)));
827 }
828
829 #[test]
830 fn out_of_order_packets_inside_the_window_are_accepted_once_each() {
831 let (mut send, mut recv) = pair();
832 let packets: Vec<Vec<u8>> = (200..210)
833 .map(|n| send.protect(&rtp(n, b"x")).expect("protects"))
834 .collect();
835
836 for protected in packets.iter().rev() {
838 recv.unprotect(protected).expect("accepted once");
839 }
840 for protected in &packets {
842 assert!(matches!(
843 recv.unprotect(protected),
844 Err(SrtpError::Replayed(_))
845 ));
846 }
847 }
848
849 #[test]
852 fn a_packet_older_than_the_window_is_refused() {
853 let (mut send, mut recv) = pair();
854 let old = send.protect(&rtp(1, b"x")).expect("protects");
855 for n in 2..200 {
856 let p = send.protect(&rtp(n, b"x")).expect("protects");
857 recv.unprotect(&p).expect("accepted");
858 }
859 assert_eq!(recv.unprotect(&old), Err(SrtpError::Replayed(1)));
860 }
861
862 #[test]
866 fn the_stream_survives_the_sequence_number_wrapping() {
867 let (mut send, mut recv) = pair();
868 for n in [65_530u16, 65_533, 65_535, 0, 1, 5] {
869 let plain = rtp(n, b"across the wrap");
870 let protected = send.protect(&plain).expect("protects");
871 assert_eq!(
872 recv.unprotect(&protected).expect("unprotects"),
873 plain,
874 "sequence {n} did not survive"
875 );
876 }
877 assert_eq!(send.roc, 1, "the sender counted one rollover");
878 assert_eq!(recv.roc, 1, "and so did the receiver");
879 }
880
881 #[test]
882 fn rtcp_round_trips_and_is_encrypted() {
883 let (mut send, mut recv) = pair();
884 let mut packet = vec![0x80, 201, 0x00, 0x07];
886 packet.extend_from_slice(&0xCAFE_BABEu32.to_be_bytes());
887 packet.extend_from_slice(b"REPORTBODY-REPORTBODY-RE");
888
889 let protected = send.protect_rtcp(&packet).expect("protects");
890 assert!(
891 !protected.windows(6).any(|w| w == b"REPORT"),
892 "the report body must not appear on the wire"
893 );
894 assert_eq!(recv.unprotect_rtcp(&protected).expect("unprotects"), packet);
895 }
896
897 fn rtcp() -> Vec<u8> {
898 let mut packet = vec![0x80, 201, 0x00, 0x07];
899 packet.extend_from_slice(&0xCAFE_BABEu32.to_be_bytes());
900 packet.extend_from_slice(b"REPORTBODY-REPORTBODY-RE");
901 packet
902 }
903
904 #[test]
907 fn an_authenticated_srtcp_packet_is_accepted_once() {
908 let (mut send, mut recv) = pair();
909 let first = send.protect_rtcp(&rtcp()).expect("protects index zero");
910 let second = send.protect_rtcp(&rtcp()).expect("protects index one");
911
912 recv.unprotect_rtcp(&first).expect("accepted once");
913 assert_eq!(recv.unprotect_rtcp(&first), Err(SrtpError::ReplayedRtcp(0)));
914 recv.unprotect_rtcp(&second)
915 .expect("a distinct authenticated index remains acceptable");
916 }
917
918 #[test]
921 fn srtp_and_srtcp_have_separate_replay_windows() {
922 let (mut send, mut recv) = pair();
923 let media = send.protect(&rtp(0, b"audio")).expect("protects RTP zero");
924 let control = send.protect_rtcp(&rtcp()).expect("protects RTCP zero");
925
926 recv.unprotect(&media).expect("RTP zero is accepted");
927 recv.unprotect_rtcp(&control)
928 .expect("SRTCP zero is independently accepted");
929 assert_eq!(recv.unprotect(&media), Err(SrtpError::Replayed(0)));
930 assert_eq!(
931 recv.unprotect_rtcp(&control),
932 Err(SrtpError::ReplayedRtcp(0))
933 );
934 }
935
936 #[test]
939 fn a_forged_high_srtcp_index_does_not_advance_the_window() {
940 let (mut send, mut recv) = pair();
941 let authentic = send.protect_rtcp(&rtcp()).expect("protects");
942 let mut forged = authentic.clone();
943 let trailer = forged.len() - TAG_LEN - 4;
944 forged[trailer..trailer + 4].copy_from_slice(&0xFFFF_FFFFu32.to_be_bytes());
945
946 assert_eq!(recv.unprotect_rtcp(&forged), Err(SrtpError::NotAuthentic));
947 recv.unprotect_rtcp(&authentic)
948 .expect("the authentic index zero was not made old");
949 }
950
951 #[test]
955 fn the_srtcp_replay_window_holds_exactly_sixty_four_indices() {
956 let (mut send, mut recv) = pair();
957 let oldest_held = send.protect_rtcp(&rtcp()).expect("protects index zero");
958 send.rtcp_index = 63;
959 let newest = send.protect_rtcp(&rtcp()).expect("protects index 63");
960 recv.unprotect_rtcp(&newest).expect("establishes index 63");
961 recv.unprotect_rtcp(&oldest_held)
962 .expect("an unseen packet 63 places behind remains held");
963 assert_eq!(
964 recv.unprotect_rtcp(&oldest_held),
965 Err(SrtpError::ReplayedRtcp(0))
966 );
967
968 let (mut send, mut recv) = pair();
969 let too_old = send.protect_rtcp(&rtcp()).expect("protects index zero");
970 send.rtcp_index = 64;
971 let newest = send.protect_rtcp(&rtcp()).expect("protects index 64");
972 recv.unprotect_rtcp(&newest).expect("establishes index 64");
973 assert_eq!(
974 recv.unprotect_rtcp(&too_old),
975 Err(SrtpError::ReplayedRtcp(0))
976 );
977 }
978
979 #[test]
982 fn the_srtcp_replay_window_crosses_the_index_wrap() {
983 let (mut send, mut recv) = pair();
984 send.rtcp_index = 0x7FFF_FFFE;
985 let before = send.protect_rtcp(&rtcp()).expect("protects max minus one");
986 let last = send.protect_rtcp(&rtcp()).expect("protects max");
987 let wrapped = send.protect_rtcp(&rtcp()).expect("protects zero");
988
989 recv.unprotect_rtcp(&before).expect("accepts max minus one");
990 recv.unprotect_rtcp(&last).expect("accepts max");
991 recv.unprotect_rtcp(&wrapped).expect("accepts wrapped zero");
992 assert_eq!(
993 recv.unprotect_rtcp(&last),
994 Err(SrtpError::ReplayedRtcp(0x7FFF_FFFF))
995 );
996 }
997
998 #[test]
999 fn an_altered_rtcp_packet_is_refused() {
1000 let (mut send, mut recv) = pair();
1001 let mut packet = vec![0x80, 201, 0x00, 0x07];
1002 packet.extend_from_slice(&0xCAFE_BABEu32.to_be_bytes());
1003 packet.extend_from_slice(b"REPORTBODY-REPORTBODY-RE");
1004
1005 let mut protected = send.protect_rtcp(&packet).expect("protects");
1006 protected[10] ^= 0x01;
1007 assert_eq!(
1008 recv.unprotect_rtcp(&protected),
1009 Err(SrtpError::NotAuthentic)
1010 );
1011 }
1012
1013 #[test]
1014 fn a_wrong_length_key_is_refused_by_name() {
1015 let error = Context::new(&[0u8; 8], &[0u8; 14]).expect_err("refused");
1016 assert!(error.to_string().contains("master key"), "{error}");
1017 let error = Context::new(&[0u8; 16], &[0u8; 4]).expect_err("refused");
1018 assert!(error.to_string().contains("master salt"), "{error}");
1019 }
1020
1021 #[test]
1024 fn a_header_with_contributing_sources_is_measured_correctly() {
1025 let mut packet = vec![0x82, 0x00, 0x00, 0x05]; packet.extend_from_slice(&800u32.to_be_bytes());
1027 packet.extend_from_slice(&0xDEAD_BEEFu32.to_be_bytes());
1028 packet.extend_from_slice(&1u32.to_be_bytes());
1029 packet.extend_from_slice(&2u32.to_be_bytes());
1030 packet.extend_from_slice(b"AUDIOAUDIO");
1031
1032 assert_eq!(rtp_header_len(&packet), Some(20));
1033
1034 let (mut send, mut recv) = pair();
1035 let protected = send.protect(&packet).expect("protects");
1036 assert_eq!(&protected[..20], &packet[..20], "the whole header is clear");
1037 assert!(!protected.windows(5).any(|w| w == b"AUDIO"));
1038 assert_eq!(recv.unprotect(&protected).expect("unprotects"), packet);
1039 }
1040
1041 #[test]
1042 fn a_truncated_packet_is_refused_rather_than_indexed() {
1043 let (_, mut recv) = pair();
1044 assert!(matches!(
1045 recv.unprotect(&[0u8; 4]),
1046 Err(SrtpError::TooShort(4))
1047 ));
1048 assert_eq!(rtp_header_len(&[0u8; 8]), None);
1049 }
1050
1051 #[test]
1053 fn debug_output_does_not_leak_key_material() {
1054 let context = Context::new(&[7u8; 16], &[9u8; 14]).expect("a context");
1055 let printed = format!("{context:?}");
1056 assert!(printed.contains("Session { .. }"), "{printed}");
1057 assert!(!printed.contains('7'), "{printed}");
1058 }
1059}