Skip to main content

rtc_srtp/context/
mod.rs

1#[cfg(test)]
2mod context_test;
3#[cfg(test)]
4mod srtcp_test;
5#[cfg(test)]
6mod srtp_test;
7
8use std::collections::HashMap;
9
10use crypto::RTCCrypto;
11use shared::replay_detector::*;
12
13use crate::cipher::cipher_aead_aes_gcm::*;
14use crate::cipher::cipher_aes_cm_hmac_sha1::*;
15use crate::cipher::*;
16use crate::option::*;
17use crate::protection_profile::*;
18use shared::error::{Error, Result};
19
20/// SRTCP protection and unprotection.
21pub mod srtcp;
22/// SRTP protection and unprotection.
23pub mod srtp;
24
25const MAX_ROC: u32 = u32::MAX;
26const SEQ_NUM_MEDIAN: u16 = 1 << 15;
27const SEQ_NUM_MAX: u16 = u16::MAX;
28
29/// Encrypt/Decrypt state for a single SRTP SSRC
30#[derive(Default)]
31pub(crate) struct SrtpSsrcState {
32    ssrc: u32,
33    index: u64,
34    rollover_has_processed: bool,
35    replay_detector: Option<Box<dyn ReplayDetector>>,
36}
37
38/// Encrypt/Decrypt state for a single SRTCP SSRC
39#[derive(Default)]
40pub(crate) struct SrtcpSsrcState {
41    srtcp_index: usize,
42    ssrc: u32,
43    replay_detector: Option<Box<dyn ReplayDetector>>,
44}
45
46impl SrtpSsrcState {
47    pub fn next_rollover_count(&self, sequence_number: u16) -> (u32, i32, bool) {
48        let local_roc = (self.index >> 16) as u32;
49        let local_seq = self.index as u16;
50
51        let mut guess_roc = local_roc;
52
53        let diff = if self.rollover_has_processed {
54            let seq = (sequence_number as i32).wrapping_sub(local_seq as i32);
55            // When local_roc is equal to 0, and entering seq-local_seq > SEQ_NUM_MEDIAN
56            // judgment, it will cause guess_roc calculation error
57            if self.index > SEQ_NUM_MEDIAN as _ {
58                if local_seq < SEQ_NUM_MEDIAN {
59                    if seq > SEQ_NUM_MEDIAN as i32 {
60                        guess_roc = local_roc.wrapping_sub(1);
61                        seq.wrapping_sub(SEQ_NUM_MAX as i32 + 1)
62                    } else {
63                        seq
64                    }
65                } else if local_seq - SEQ_NUM_MEDIAN > sequence_number {
66                    guess_roc = local_roc.wrapping_add(1);
67                    seq.wrapping_add(SEQ_NUM_MAX as i32 + 1)
68                } else {
69                    seq
70                }
71            } else {
72                // local_roc is equal to 0
73                seq
74            }
75        } else {
76            0i32
77        };
78
79        (guess_roc, diff, (guess_roc == 0 && local_roc == MAX_ROC))
80    }
81
82    /// https://tools.ietf.org/html/rfc3550#appendix-A.1
83    pub fn update_rollover_count(&mut self, sequence_number: u16, diff: i32) {
84        if !self.rollover_has_processed {
85            self.index |= sequence_number as u64;
86            self.rollover_has_processed = true;
87        } else {
88            self.index = self.index.wrapping_add(diff as _);
89        }
90    }
91}
92
93/// Context represents a SRTP cryptographic context
94/// Context can only be used for one-way operations
95/// it must either used ONLY for encryption or ONLY for decryption
96pub struct Context {
97    cipher: Box<dyn Cipher>,
98
99    srtp_ssrc_states: HashMap<u32, SrtpSsrcState>,
100    srtcp_ssrc_states: HashMap<u32, SrtcpSsrcState>,
101
102    new_srtp_replay_detector: ContextOption,
103    new_srtcp_replay_detector: ContextOption,
104}
105
106impl Context {
107    /// Creates an SRTP context.
108    ///
109    /// The crypto implementation is supplied by the caller; this crate never resolves a default.
110    /// Borrowed, not owned: it is only needed to build the keyed cipher/MAC objects below, which
111    /// are what the context retains.
112    pub fn new(
113        master_key: &[u8],
114        master_salt: &[u8],
115        profile: ProtectionProfile,
116        srtp_ctx_opt: Option<ContextOption>,
117        srtcp_ctx_opt: Option<ContextOption>,
118        crypto: &dyn RTCCrypto,
119    ) -> Result<Context> {
120        let key_len = profile.key_len();
121        let salt_len = profile.salt_len();
122
123        if master_key.len() != key_len {
124            return Err(Error::SrtpMasterKeyLength(key_len, master_key.len()));
125        } else if master_salt.len() != salt_len {
126            return Err(Error::SrtpSaltLength(salt_len, master_salt.len()));
127        }
128        profile.ensure_crypto_supported(crypto)?;
129
130        let cipher: Box<dyn Cipher> = match profile {
131            ProtectionProfile::Aes128CmHmacSha1_32
132            | ProtectionProfile::Aes128CmHmacSha1_80
133            | ProtectionProfile::Aes256CmHmacSha1_80
134            | ProtectionProfile::Aes256CmHmacSha1_32 => Box::new(CipherAesCmHmacSha1::new(
135                profile,
136                master_key,
137                master_salt,
138                crypto,
139            )?),
140
141            ProtectionProfile::AeadAes128Gcm | ProtectionProfile::AeadAes256Gcm => {
142                // `CipherAeadAesGcm::new` selects AES-128 vs AES-256 from the
143                // profile itself, so both GCM profiles share one arm.
144                Box::new(CipherAeadAesGcm::new(
145                    profile,
146                    master_key,
147                    master_salt,
148                    crypto,
149                )?)
150            }
151        };
152
153        let srtp_ctx_opt = if let Some(ctx_opt) = srtp_ctx_opt {
154            ctx_opt
155        } else {
156            srtp_no_replay_protection()
157        };
158
159        let srtcp_ctx_opt = if let Some(ctx_opt) = srtcp_ctx_opt {
160            ctx_opt
161        } else {
162            srtcp_no_replay_protection()
163        };
164
165        Ok(Context {
166            cipher,
167            srtp_ssrc_states: HashMap::new(),
168            srtcp_ssrc_states: HashMap::new(),
169            new_srtp_replay_detector: srtp_ctx_opt,
170            new_srtcp_replay_detector: srtcp_ctx_opt,
171        })
172    }
173
174    fn get_srtp_ssrc_state(&mut self, ssrc: u32) -> &mut SrtpSsrcState {
175        // `or_insert_with` so the replay detector (a boxed sliding-window
176        // detector backed by a heap-allocated bit buffer) is only constructed on
177        // the first packet for a given SSRC. `or_insert` would build — and then
178        // immediately drop — a fresh detector on every decrypted packet.
179        let new_srtp_replay_detector = &self.new_srtp_replay_detector;
180        self.srtp_ssrc_states
181            .entry(ssrc)
182            .or_insert_with(|| SrtpSsrcState {
183                ssrc,
184                replay_detector: Some(new_srtp_replay_detector()),
185                ..Default::default()
186            })
187    }
188
189    fn get_srtcp_ssrc_state(&mut self, ssrc: u32) -> &mut SrtcpSsrcState {
190        let new_srtcp_replay_detector = &self.new_srtcp_replay_detector;
191        self.srtcp_ssrc_states
192            .entry(ssrc)
193            .or_insert_with(|| SrtcpSsrcState {
194                ssrc,
195                replay_detector: Some(new_srtcp_replay_detector()),
196                ..Default::default()
197            })
198    }
199
200    /// roc returns SRTP rollover counter value of specified SSRC.
201    fn get_roc(&self, ssrc: u32) -> Option<u32> {
202        self.srtp_ssrc_states
203            .get(&ssrc)
204            .map(|s| (s.index >> 16) as _)
205    }
206
207    /// set_roc sets SRTP rollover counter value of specified SSRC.
208    fn set_roc(&mut self, ssrc: u32, roc: u32) {
209        let state = self.get_srtp_ssrc_state(ssrc);
210        state.index = (roc as u64) << 16;
211        state.rollover_has_processed = false;
212    }
213
214    /// index returns SRTCP index value of specified SSRC.
215    fn get_index(&self, ssrc: u32) -> Option<usize> {
216        self.srtcp_ssrc_states.get(&ssrc).map(|s| s.srtcp_index)
217    }
218
219    /// set_index sets SRTCP index value of specified SSRC.
220    fn set_index(&mut self, ssrc: u32, index: usize) {
221        self.get_srtcp_ssrc_state(ssrc).srtcp_index = index % (MAX_SRTCP_INDEX + 1);
222    }
223}