srt_protocol/options/
encryption.rs

1use std::{
2    convert::TryFrom,
3    fmt::{self, Debug, Display, Formatter},
4};
5
6use super::*;
7
8// https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-6
9// https://github.com/Haivision/srt/blob/master/docs/features/encryption.md
10#[derive(Clone, Default, Debug, Eq, PartialEq)]
11pub struct Encryption {
12    // TODO: support unspecified key length
13    //  also check to ensure we implement key negotiation algorithm correctly
14    /// SRTO_PBKEYLEN
15    ///
16    /// Encryption key length.
17    ///
18    /// Possible values:
19    ///
20    ///  0  = PBKEYLEN (default value)
21    ///  16 = AES-128 (effective value)
22    ///  24 = AES-192
23    ///  32 = AES-256
24    ///
25    /// The use is slightly different in 1.2.0 (HSv4), and since 1.3.0 (HSv5):
26    ///
27    /// HSv4: This is set on the sender and enables encryption, if not 0. The receiver shall not set
28    /// it and will agree on the length as defined by the sender.
29    ///
30    /// HSv5: The "default value" for PBKEYLEN is 0, which means that the PBKEYLEN won't be
31    /// advertised. The "effective value" for PBKEYLEN is 16, but this applies only when neither
32    /// party has set the value explicitly (i.e. when both are initially at the default value of 0).
33    /// If any party has set an explicit value (16, 24, 32) it will be advertised in the handshake.
34    /// If the other party remains at the default 0, it will accept the peer's value. The situation
35    /// where both parties set a value should be treated carefully. Actually there are three
36    /// intended methods of defining it, and all other uses are considered undefined behavior:
37    ///
38    /// Unidirectional: the sender shall set PBKEYLEN and the receiver shall not alter the default
39    /// value 0. The effective PBKEYLEN will be the one set on the sender. The receiver need not
40    /// know the sender's PBKEYLEN, just the passphrase, PBKEYLEN will be correctly passed.
41    ///
42    /// Bidirectional in Caller-Listener arrangement: it is recommended to use a rule whereby you
43    /// will be setting the PBKEYLEN exclusively either on the Listener or on the Caller. The value
44    /// set on the Listener will win, if set on both parties.
45    ///
46    /// Bidirectional in Rendezvous arrangement: you have to know the passphrases for both parties,
47    /// as well as PBKEYLEN. Set PBKEYLEN to the same value on both parties (or leave the default
48    /// value on both parties, which will result in 16)
49    ///
50    /// Unwanted behavior cases: if both parties set PBKEYLEN and the value on both sides is
51    /// different, the effective PBKEYLEN will be the one that is set on the Responder party, which
52    /// may also override the PBKEYLEN 32 set by the sender to value 16 if such value was used by
53    /// the receiver. The Responder party is the Listener in a Caller-Listener arrangement. In
54    /// Rendezvous it's a matter of luck which party becomes the Responder.
55    pub key_size: KeySize,
56
57    /// SRTO_PASSPHRASE
58    /// Sets the passphrase for encryption. This enables encryption on this party (or disables it, if
59    /// an empty passphrase is passed). The password must be minimum 10 and maximum 79 characters
60    /// long.
61    ///
62    /// The passphrase is the shared secret between the sender and the receiver. It is used to
63    /// generate the Key Encrypting Key using PBKDF2 (Password-Based Key Derivation Function 2).
64    ///
65    /// When a socket with configured passphrase is being connected, the peer must have the same
66    /// password set, or the connection is rejected. This behavior can be changed by
67    /// SRTO_ENFORCEDENCRYPTION.
68    ///
69    /// Note that since the introduction of bidirectional support, there's only one initial
70    /// encryption key to encrypt the stream (new keys after refreshing will be updated
71    /// independently), and there's no distinction between "service party that defines the password"
72    /// and "client party that is required to set matching password" - both parties are equivalent,
73    /// and in order to have a working encrypted connection, they have to simply set the same
74    /// passphrase.
75    pub passphrase: Option<Passphrase>,
76
77    pub km_refresh: KeyMaterialRefresh,
78}
79
80#[derive(Clone, Debug, Eq, PartialEq)]
81pub struct KeyMaterialRefresh {
82    /// SRTO_KMREFRESHRATE
83    /// KM Refresh Period specifies the number of packets to be sent
84    /// before switching to the new SEK
85    ///
86    /// The recommended KM Refresh Period is after 2^25 packets encrypted
87    /// with the same SEK are sent.
88    ///
89    /// The number of packets to be transmitted after which the Stream Encryption Key (SEK), used to
90    /// encrypt packets, will be switched to the new one. Note that the old and new keys live in
91    /// parallel for a certain period of time (see SRTO_KMPREANNOUNCE) before and after the
92    /// switchover.
93    ///
94    /// Having a preannounce period before switchover ensures the new SEK is installed at the
95    /// receiver before the first packet encrypted with the new SEK is received. The old key remains
96    /// active after switchover in order to decrypt packets that might still be in flight, or
97    /// packets that have to be retransmitted.
98    ///
99    /// Default value: 0 - corresponds to 16777216 packets (2^24 or 0x1000000).
100    pub period: PacketCount,
101
102    /// SRTO_KMPREANNOUNCE
103    /// KM Pre-Announcement Period specifies when a new key is announced
104    /// in a number of packets before key switchover.  The same value is
105    /// used to determine when to decommission the old key after
106    /// switchover.
107    ///
108    /// The recommended KM Pre-Announcement Period is 4000 packets (i.e.
109    /// a new key is generated, wrapped, and sent at 2^25 minus 4000
110    /// packets; the old key is decommissioned at 2^25 plus 4000
111    /// packets).
112    ///
113    /// The interval (defined in packets) between when a new Stream Encrypting Key (SEK) is sent and
114    /// when switchover occurs. This value also applies to the subsequent interval between when
115    /// switchover occurs and when the old SEK is decommissioned.
116    ///
117    /// At SRTO_KMPREANNOUNCE packets before switchover the new key is sent (repeatedly, if
118    /// necessary, until it is confirmed by the receiver).
119    ///
120    /// At the switchover point (see SRTO_KMREFRESHRATE), the sender starts encrypting and sending
121    /// packets using the new key. The old key persists in case it is needed to decrypt packets that
122    /// were in the flight window, or retransmitted packets.
123    ///
124    /// The old key is decommissioned at SRTO_KMPREANNOUNCE packets after switchover.
125    ///
126    /// The allowed range for this value is between 1 and half of the current value of
127    /// SRTO_KMREFRESHRATE. The minimum value should never be less than the flight window SRTO_FC
128    /// (i.e. the number of packets that have already left the sender but have not yet arrived at
129    /// the receiver).
130    ///
131    /// The value of SRTO_KMPREANNOUNCE must not exceed (SRTO_KMREFRESHRATE - 1) / 2`.
132    ///
133    /// Default value: 2^12
134    pub pre_announcement_period: PacketCount,
135}
136
137impl Default for KeyMaterialRefresh {
138    fn default() -> Self {
139        Self {
140            period: PacketCount(1u64 << 24),                  // 2^25
141            pre_announcement_period: PacketCount(1u64 << 12), // 2^12,
142        }
143    }
144}
145
146impl Validation for Encryption {
147    type Error = OptionsError;
148
149    fn is_valid(&self) -> Result<(), Self::Error> {
150        let period: u64 = self.km_refresh.period.into();
151        let pre_announcement_period: u64 = self.km_refresh.pre_announcement_period.into();
152
153        if period == 0 || pre_announcement_period > period.saturating_sub(1) / 2 {
154            Err(OptionsError::KeyMaterialRefresh(
155                PacketCount(period),
156                PacketCount(pre_announcement_period),
157            ))
158        } else {
159            Ok(())
160        }
161    }
162}
163
164// https://github.com/Haivision/srt/blob/master/docs/API/API-socket-options.md#srto_passphrase
165#[derive(Clone, Eq, PartialEq)]
166pub struct Passphrase(String);
167
168impl<'a> From<&'a str> for Passphrase {
169    fn from(value: &'a str) -> Self {
170        Self::try_from(value.to_string()).unwrap()
171    }
172}
173
174impl TryFrom<String> for Passphrase {
175    type Error = OptionsError;
176
177    fn try_from(value: String) -> Result<Self, Self::Error> {
178        if !(10..=79).contains(&value.len()) {
179            return Err(OptionsError::PassphraseLength(value.len()));
180        }
181        Ok(Passphrase(value))
182    }
183}
184
185impl Passphrase {
186    pub fn as_bytes(&self) -> &[u8] {
187        self.0.as_bytes()
188    }
189}
190
191impl Display for Passphrase {
192    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
193        Debug::fmt(self, f)
194    }
195}
196
197impl Debug for Passphrase {
198    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
199        f.debug_struct("Passphrase").finish()
200    }
201}
202
203// https://github.com/Haivision/srt/blob/master/docs/API/API-socket-options.md#srto_pbkeylen
204#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
205pub enum KeySize {
206    #[default]
207    Unspecified,
208    AES128,
209    AES192,
210    AES256,
211}
212
213impl KeySize {
214    pub fn as_raw(self) -> u8 {
215        use KeySize::*;
216        match self {
217            Unspecified => 0,
218            AES128 => 16,
219            AES192 => 24,
220            AES256 => 32,
221        }
222    }
223
224    pub fn as_usize(self) -> usize {
225        use KeySize::*;
226        match self {
227            Unspecified => 16,
228            AES128 => 16,
229            AES192 => 24,
230            AES256 => 32,
231        }
232    }
233}
234
235impl TryFrom<u16> for KeySize {
236    type Error = OptionsError;
237
238    fn try_from(value: u16) -> Result<Self, OptionsError> {
239        use KeySize::*;
240        match value {
241            0 => Ok(Unspecified),
242            16 => Ok(AES128),
243            24 => Ok(AES192),
244            32 => Ok(AES256),
245            value => Err(OptionsError::InvalidKeySize(value)),
246        }
247    }
248}
249
250#[cfg(test)]
251mod tests {
252    use super::*;
253
254    #[test]
255    fn formatting() {
256        assert_eq!(
257            format!("{:?}", Passphrase::from("1234567890")),
258            "Passphrase"
259        );
260    }
261
262    #[test]
263    fn try_from() {
264        use OptionsError::*;
265        assert_eq!(
266            Passphrase::try_from("123456789".to_string()),
267            Err(PassphraseLength(9))
268        );
269        assert_eq!(
270            Passphrase::try_from(String::from_utf8_lossy(&[b'X'; 80]).to_string()),
271            Err(PassphraseLength(80))
272        );
273    }
274}