srt_protocol/protocol/encryption/
key.rs

1use std::{
2    array::TryFromSliceError,
3    convert::TryInto,
4    fmt::{self, Debug, Formatter},
5    ops::Deref,
6};
7
8use aes::{Aes128, Aes192, Aes256};
9use cipher::KeyInit;
10use hmac::Hmac;
11use rand::{rngs::OsRng, RngCore};
12use sha1::Sha1;
13
14use crate::{
15    packet::SeqNumber,
16    settings::{KeySettings, KeySize, Passphrase},
17};
18
19use super::wrap;
20
21#[derive(Clone, Eq, PartialEq)]
22pub struct Salt([u8; 16]);
23
24impl Salt {
25    pub fn new_random() -> Self {
26        let mut salt = [0; 16];
27        OsRng.fill_bytes(&mut salt[..]);
28        Self(salt)
29    }
30
31    pub fn try_from(bytes: &[u8]) -> Result<Salt, TryFromSliceError> {
32        Ok(Salt(bytes[..].try_into()?))
33    }
34
35    pub fn generate_strean_iv_for(&self, seq_number: SeqNumber) -> StreamInitializationVector {
36        let salt = self.0;
37        /* HaiCrypt-TP CTR mode IV (128-bit): (all these are in bytes)
38         *    0   1   2   3   4   5  6   7   8   9   10  11  12  13  14  15
39         * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
40         * |                   0s                  |      pki      |  ctr  |
41         * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
42         *                            XOR
43         * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
44         * |                         nonce                         +
45         * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
46         *
47         * pki    (32-bit): packet index (sequence number)
48         * ctr    (16-bit): block counter
49         * nonce (112-bit): number used once (first 12 bytes of salt)
50         */
51        let mut out = [0; 16];
52        out[0..14].copy_from_slice(&salt[..14]);
53
54        for (i, b) in seq_number.0.to_be_bytes().iter().enumerate() {
55            out[i + 10] ^= *b;
56        }
57
58        // TODO: the ref impl doesn't put ctr in here....
59        // https://github.com/Haivision/srt/blob/9f7068d4f45eb3276e30fcc6e920f82b387c6852/haicrypt/hcrypt.h#L136-L136
60
61        StreamInitializationVector(out)
62    }
63
64    pub fn as_slice(&self) -> &[u8] {
65        &self.0
66    }
67}
68
69impl Debug for Salt {
70    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
71        write!(f, "Salt(0x{})", hex::encode_upper(self.0))
72    }
73}
74
75#[derive(Clone, Eq, PartialEq)]
76pub struct StreamInitializationVector([u8; 16]);
77
78impl StreamInitializationVector {
79    pub fn try_from(slice: &[u8]) -> Result<Self, TryFromSliceError> {
80        Ok(StreamInitializationVector(slice[..].try_into()?))
81    }
82
83    pub fn as_bytes(&self) -> &[u8] {
84        &self.0
85    }
86}
87
88impl Debug for StreamInitializationVector {
89    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
90        write!(f, "StreamIV(0x{})", hex::encode_upper(self.0))
91    }
92}
93
94#[derive(Clone, Eq, PartialEq)]
95pub struct WrapInitializationVector([u8; 8]);
96
97impl WrapInitializationVector {
98    pub fn try_from(slice: &[u8]) -> Result<Self, TryFromSliceError> {
99        Ok(WrapInitializationVector(slice[..].try_into()?))
100    }
101}
102
103impl Debug for WrapInitializationVector {
104    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
105        write!(f, "KeyIV(0x{})", hex::encode_upper(self.0))
106    }
107}
108
109#[derive(Clone, Eq, PartialEq)]
110pub enum EncryptionKey {
111    Bytes16([u8; 16]),
112    Bytes24([u8; 24]),
113    Bytes32([u8; 32]),
114}
115
116impl EncryptionKey {
117    pub fn new_random(size: KeySize) -> Self {
118        use EncryptionKey::*;
119        fn new_key<const N: usize>() -> [u8; N] {
120            let mut key = [0u8; N];
121            OsRng.fill_bytes(&mut key[..]);
122            key
123        }
124        match size {
125            KeySize::AES128 => Bytes16(new_key()),
126            KeySize::AES192 => Bytes24(new_key()),
127            KeySize::AES256 => Bytes32(new_key()),
128            KeySize::Unspecified => Bytes16(new_key()),
129        }
130    }
131
132    pub fn try_from(bytes: &[u8]) -> Result<EncryptionKey, TryFromSliceError> {
133        use EncryptionKey::*;
134        match bytes.len() {
135            16 => Ok(Bytes16(bytes[..].try_into()?)),
136            24 => Ok(Bytes24(bytes[..].try_into()?)),
137            _ => Ok(Bytes32(bytes[..].try_into()?)),
138        }
139    }
140
141    #[allow(clippy::len_without_is_empty)]
142    pub fn len(&self) -> usize {
143        use EncryptionKey::*;
144        match self {
145            Bytes16(key) => key.len(),
146            Bytes24(key) => key.len(),
147            Bytes32(key) => key.len(),
148        }
149    }
150
151    pub fn as_bytes(&self) -> &[u8] {
152        use EncryptionKey::*;
153        match self {
154            Bytes16(key) => &key[..],
155            Bytes24(key) => &key[..],
156            Bytes32(key) => &key[..],
157        }
158    }
159}
160
161impl fmt::Debug for EncryptionKey {
162    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
163        use EncryptionKey::*;
164        match self {
165            Bytes16(_) => f.debug_struct("EncryptionKey::Bytes16"),
166            Bytes24(_) => f.debug_struct("EncryptionKey::Bytes24"),
167            Bytes32(_) => f.debug_struct("EncryptionKey::Bytes32"),
168        }
169        .finish()
170    }
171}
172
173#[derive(Clone, Eq, PartialEq)]
174pub struct KeyEncryptionKey(EncryptionKey);
175
176impl KeyEncryptionKey {
177    pub fn new(key_settings: &KeySettings, salt: &Salt) -> Self {
178        let key_size = key_settings.key_size;
179        let passphrase = &key_settings.passphrase;
180
181        // Generate the key encrypting key from the passphrase, caching it in the struct
182        // https://github.com/Haivision/srt/blob/2ef4ef003c2006df1458de6d47fbe3d2338edf69/haicrypt/hcrypt_sa.c#L69-L103
183
184        // the reference implementation uses the last 8 (at max) bytes of the salt. Sources:
185        // https://github.com/Haivision/srt/blob/2ef4ef003c2006df1458de6d47fbe3d2338edf69/haicrypt/haicrypt.h#L72
186        // https://github.com/Haivision/srt/blob/2ef4ef003c2006df1458de6d47fbe3d2338edf69/haicrypt/hcrypt_sa.c#L77-L85
187
188        fn calculate_pbkdf2(passphrase: &Passphrase, salt: &Salt, key: &mut [u8]) {
189            let salt = salt.0;
190            // is what the reference implementation uses.https://github.com/Haivision/srt/blob/2ef4ef003c2006df1458de6d47fbe3d2338edf69/haicrypt/haicrypt.h#L73
191            const ROUNDS: u32 = 2048;
192            let salt_len = usize::min(8, salt.len());
193            pbkdf2::pbkdf2::<Hmac<Sha1>>(
194                passphrase.as_bytes(),
195                &salt[salt.len() - salt_len..], // last salt_len bytes
196                ROUNDS,
197                &mut *key,
198            )
199            .unwrap();
200        }
201
202        fn new_key<const N: usize>(passphrase: &Passphrase, salt: &Salt) -> [u8; N] {
203            let mut key = [0u8; N];
204            calculate_pbkdf2(passphrase, salt, &mut key);
205            key
206        }
207
208        use EncryptionKey::*;
209        let key = match key_size {
210            KeySize::AES128 => Bytes16(new_key(passphrase, salt)),
211            KeySize::AES192 => Bytes24(new_key(passphrase, salt)),
212            KeySize::AES256 => Bytes32(new_key(passphrase, salt)),
213            KeySize::Unspecified => Bytes16(new_key(passphrase, salt)),
214        };
215
216        KeyEncryptionKey(key)
217    }
218
219    pub fn encrypt_wrapped_keys(&self, keys: &[u8]) -> Vec<u8> {
220        let mut encrypted_keys = vec![0; keys.len() + 8];
221        use EncryptionKey::*;
222        match &self.0 {
223            Bytes16(key) => wrap::aes_wrap(
224                &Aes128::new(key[..].into()),
225                None,
226                &mut encrypted_keys,
227                keys,
228            ),
229            Bytes24(key) => wrap::aes_wrap(
230                &Aes192::new(key[..].into()),
231                None,
232                &mut encrypted_keys,
233                keys,
234            ),
235            Bytes32(key) => wrap::aes_wrap(
236                &Aes256::new(key[..].into()),
237                None,
238                &mut encrypted_keys,
239                keys,
240            ),
241        }
242        encrypted_keys
243    }
244
245    pub fn decrypt_wrapped_keys(
246        &self,
247        wrapped_keys: &[u8],
248    ) -> Result<Vec<u8>, WrapInitializationVector> {
249        use EncryptionKey::*;
250        let mut keys = vec![0; wrapped_keys.len() - 8];
251        let mut iv = [0; 8];
252        match &self.0 {
253            Bytes16(key) => wrap::aes_unwrap(
254                &Aes128::new(key[..].into()),
255                &mut iv,
256                &mut keys,
257                wrapped_keys,
258            ),
259            Bytes24(key) => wrap::aes_unwrap(
260                &Aes192::new(key[..].into()),
261                &mut iv,
262                &mut keys,
263                wrapped_keys,
264            ),
265            Bytes32(key) => wrap::aes_unwrap(
266                &Aes256::new(key[..].into()),
267                &mut iv,
268                &mut keys,
269                wrapped_keys,
270            ),
271        }
272        if iv != wrap::DEFAULT_IV {
273            return Err(WrapInitializationVector(iv));
274        }
275        Ok(keys)
276    }
277}
278
279impl Deref for KeyEncryptionKey {
280    type Target = EncryptionKey;
281
282    fn deref(&self) -> &Self::Target {
283        &self.0
284    }
285}
286
287impl fmt::Debug for KeyEncryptionKey {
288    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
289        use EncryptionKey::*;
290        match &self.0 {
291            Bytes16(_) => f.debug_struct("KeyEncryptionKey::Bytes16"),
292            Bytes24(_) => f.debug_struct("KeyEncryptionKey::Bytes24"),
293            Bytes32(_) => f.debug_struct("KeyEncryptionKey::Bytes32"),
294        }
295        .finish()
296    }
297}
298
299#[cfg(test)]
300mod test {
301    use super::*;
302
303    #[test]
304    fn kek_generate() {
305        // this is an example taken from the reference impl
306        let key_settings = KeySettings {
307            key_size: KeySize::AES128,
308            passphrase: "password123".into(),
309        };
310        let expected_kek = &hex::decode(b"08F2758F41E4244D00057C9CEBEB95FC").unwrap()[..];
311        let salt =
312            Salt::try_from(&hex::decode(b"7D59759C2B1A3F0B06C7028790C81C7D").unwrap()[..]).unwrap();
313
314        let kek = KeyEncryptionKey::new(&key_settings, &salt);
315
316        assert_eq!(kek.0.as_bytes(), expected_kek);
317
318        // ensure that secrets don't make it into any logs
319        assert_eq!(format!("{kek:?}"), "KeyEncryptionKey::Bytes16");
320        assert_eq!(format!("{:?}", kek.deref()), "EncryptionKey::Bytes16");
321
322        assert_ne!(Salt::new_random(), Salt::new_random());
323    }
324
325    #[test]
326    fn generate_iv() {
327        // example from the reference implementation
328        let salt =
329            Salt::try_from(&hex::decode(b"87647f8a2361fb1a9e692de576985949").unwrap()[..]).unwrap();
330        let expected_iv = StreamInitializationVector::try_from(
331            &hex::decode(b"87647f8a2361fb1a9e6907af1b810000").unwrap()[..],
332        )
333        .unwrap();
334
335        let iv = salt.generate_strean_iv_for(SeqNumber(709520665));
336
337        assert_eq!(iv, expected_iv);
338
339        assert_eq!(
340            format!("{iv:?}"),
341            "StreamIV(0x87647F8A2361FB1A9E6907AF1B810000)"
342        );
343        assert_eq!(
344            format!("{salt:?}"),
345            "Salt(0x87647F8A2361FB1A9E692DE576985949)"
346        );
347
348        assert_ne!(Salt::new_random(), Salt::new_random());
349    }
350}