mumble_protocol/
crypt.rs

1//! Implementation of the cryptography used for Mumble's voice channel
2
3use bytes::BytesMut;
4use openssl::memcmp;
5use openssl::rand::rand_bytes;
6use std::convert::TryInto;
7use std::io;
8use tokio_util::codec::Decoder;
9use tokio_util::codec::Encoder;
10
11use crate::voice::Clientbound;
12use crate::voice::Serverbound;
13use crate::voice::VoiceCodec;
14use crate::voice::VoicePacket;
15use crate::voice::VoicePacketDst;
16
17/// Maximum size of an encrypted Mumble packet.
18/// Note that larger packets can be produced if there is sufficient voice data in one packet but
19/// there's no guarantee that the remote end will not just drop it.
20pub const MAX_PACKET_SIZE: usize = 1024;
21/// Size in bytes of the AES key used in `CryptState`.
22pub const KEY_SIZE: usize = 16;
23/// Size in bytes of blocks for the AES primitive.
24pub const BLOCK_SIZE: usize = std::mem::size_of::<u128>();
25
26/// Implements OCB2-AES128 for encryption and authentication of the voice packets
27/// when transmitted over UDP.
28/// Also provides statistics about good, late and lost packets.
29///
30/// Implements a `Codec` which parses a stream of encrypted data chunks into [VoicePacket]s.
31///
32/// Note that OCB is covered by patents, however a license has been granted for use in "most"
33/// software. See: http://web.cs.ucdavis.edu/~rogaway/ocb/license.htm
34///
35/// Based on https://github.com/mumble-voip/mumble/blob/e31d267a11b4ed0597ad41309a7f6b715837141f/src/CryptState.cpp
36pub struct CryptState<EncodeDst: VoicePacketDst, DecodeDst: VoicePacketDst> {
37    codec: VoiceCodec<EncodeDst, DecodeDst>,
38
39    key: [u8; KEY_SIZE],
40    // internally as native endianness, externally as little endian and during ocb_* as big endian
41    encrypt_nonce: u128,
42    decrypt_nonce: u128,
43    decrypt_history: [u8; 0x100],
44
45    good: u32,
46    late: u32,
47    lost: u32,
48}
49/// The [CryptState] used on the server side.
50pub type ServerCryptState = CryptState<Clientbound, Serverbound>;
51/// The [CryptState] used on the client side.
52pub type ClientCryptState = CryptState<Serverbound, Clientbound>;
53
54/// The reason a decrypt operation failed.
55#[derive(Clone, Copy, Debug, PartialEq, Eq)]
56pub enum DecryptError {
57    /// The packet is too short to be decrypted
58    Eof,
59    /// The packet has already been decrypted previously.
60    Repeat,
61    /// The packet was far too late.
62    Late,
63    /// The MAC of the decrypted packet did not match.
64    ///
65    /// This may also indicate a substantial de-sync of the decryption nonce.
66    Mac,
67}
68
69impl<EncodeDst: VoicePacketDst, DecodeDst: VoicePacketDst> CryptState<EncodeDst, DecodeDst> {
70    /// Creates a new CryptState with randomly generated key and initial encrypt- and decrypt-nonce.
71    pub fn generate_new() -> Self {
72        let mut key = [0; KEY_SIZE];
73        rand_bytes(&mut key).unwrap();
74
75        CryptState {
76            codec: VoiceCodec::new(),
77
78            key,
79            encrypt_nonce: 0,
80            decrypt_nonce: 1 << 127,
81            decrypt_history: [0; 0x100],
82
83            good: 0,
84            late: 0,
85            lost: 0,
86        }
87    }
88
89    /// Creates a new CryptState from previously generated key, encrypt- and decrypt-nonce.
90    pub fn new_from(
91        key: [u8; KEY_SIZE],
92        encrypt_nonce: [u8; BLOCK_SIZE],
93        decrypt_nonce: [u8; BLOCK_SIZE],
94    ) -> Self {
95        CryptState {
96            codec: VoiceCodec::new(),
97
98            key,
99            encrypt_nonce: u128::from_le_bytes(encrypt_nonce),
100            decrypt_nonce: u128::from_le_bytes(decrypt_nonce),
101            decrypt_history: [0; 0x100],
102
103            good: 0,
104            late: 0,
105            lost: 0,
106        }
107    }
108
109    /// Returns the amount of packets transmitted without issues.
110    pub fn get_good(&self) -> u32 {
111        self.good
112    }
113
114    /// Returns the amount of packets which were transmitted successfully but arrived late.
115    pub fn get_late(&self) -> u32 {
116        self.late
117    }
118
119    /// Returns the amount of packets which were lost.
120    pub fn get_lost(&self) -> u32 {
121        self.lost
122    }
123
124    /// Returns the shared, **private** key.
125    pub fn get_key(&self) -> &[u8; KEY_SIZE] {
126        &self.key
127    }
128
129    /// Returns the nonce used for encrypting.
130    pub fn get_encrypt_nonce(&self) -> [u8; BLOCK_SIZE] {
131        self.encrypt_nonce.to_le_bytes()
132    }
133
134    /// Returns the nonce used for decrypting.
135    pub fn get_decrypt_nonce(&self) -> [u8; BLOCK_SIZE] {
136        self.decrypt_nonce.to_le_bytes()
137    }
138
139    /// Updates the nonce used for decrypting.
140    pub fn set_decrypt_nonce(&mut self, nonce: &[u8; BLOCK_SIZE]) {
141        self.decrypt_nonce = u128::from_le_bytes(*nonce);
142    }
143
144    /// Encrypts an encoded voice packet and returns the resulting bytes.
145    pub fn encrypt(&mut self, packet: VoicePacket<EncodeDst>, dst: &mut BytesMut) {
146        self.encrypt_nonce = self.encrypt_nonce.wrapping_add(1);
147
148        // Leave four bytes for header
149        dst.resize(4, 0);
150        let mut inner = dst.split_off(4);
151
152        self.codec
153            .encode(packet, &mut inner)
154            .expect("VoiceEncoder is infallible");
155
156        let tag = self.ocb_encrypt(inner.as_mut());
157        dst.unsplit(inner);
158
159        dst[0] = self.encrypt_nonce as u8;
160        dst[1..4].copy_from_slice(&tag.to_be_bytes()[0..3]);
161    }
162
163    /// Decrypts a voice packet and (if successful) returns the `Result` of parsing the packet.
164    pub fn decrypt(
165        &mut self,
166        buf: &mut BytesMut,
167    ) -> Result<Result<VoicePacket<DecodeDst>, io::Error>, DecryptError> {
168        if buf.len() < 4 {
169            return Err(DecryptError::Eof);
170        }
171        let header = buf.split_to(4);
172        let nonce_0 = header[0];
173
174        // If we update our decrypt_nonce and the tag check fails or we've been processing late
175        // packets, we need to revert it
176        let saved_nonce = self.decrypt_nonce;
177        let mut late = false; // will always restore nonce if this is the case
178        let mut lost = 0; // for stats only
179
180        if self.decrypt_nonce.wrapping_add(1) as u8 == nonce_0 {
181            // in order
182            self.decrypt_nonce = self.decrypt_nonce.wrapping_add(1);
183        } else {
184            // packet is late or repeated, or we lost a few packets in between
185            let diff = nonce_0.wrapping_sub(self.decrypt_nonce as u8) as i8;
186            self.decrypt_nonce = self.decrypt_nonce.wrapping_add(diff as u128);
187            if diff > 0 {
188                lost = i32::from(diff - 1); // lost a few packets in between this and the last one
189            } else if diff > -30 {
190                if self.decrypt_history[nonce_0 as usize] == (self.decrypt_nonce >> 8) as u8 {
191                    self.decrypt_nonce = saved_nonce;
192                    return Err(DecryptError::Repeat);
193                }
194                // just late
195                late = true;
196                lost = -1;
197            } else {
198                return Err(DecryptError::Late); // late by more than 30 packets
199            }
200        }
201
202        let tag = self.ocb_decrypt(buf.as_mut());
203        if !memcmp::eq(&tag.to_be_bytes()[0..3], &header[1..4]) {
204            self.decrypt_nonce = saved_nonce;
205            return Err(DecryptError::Mac);
206        }
207
208        self.decrypt_history[nonce_0 as usize] = (self.decrypt_nonce >> 8) as u8;
209
210        self.good += 1;
211        if late {
212            self.late += 1;
213            self.decrypt_nonce = saved_nonce;
214        }
215        self.lost = (self.lost as i32 + lost as i32) as u32;
216
217        Ok(self
218            .codec
219            .decode(buf)
220            .map(|it| it.expect("VoiceCodec is stateless")))
221    }
222
223    /// Encrypt the provided buffer using AES-OCB, returning the tag.
224    fn ocb_encrypt(&self, mut buf: &mut [u8]) -> u128 {
225        let mut offset = self.aes_encrypt(self.encrypt_nonce.to_be());
226        let mut checksum = 0u128;
227
228        while buf.len() > BLOCK_SIZE {
229            let (chunk, remainder) = buf.split_at_mut(BLOCK_SIZE);
230            buf = remainder;
231            let chunk: &mut [u8; BLOCK_SIZE] = chunk.try_into().expect("split_at works");
232
233            offset = s2(offset);
234
235            let plain = u128::from_be_bytes(*chunk);
236            let encrypted = self.aes_encrypt(offset ^ plain) ^ offset;
237            chunk.copy_from_slice(&encrypted.to_be_bytes());
238
239            checksum ^= plain;
240        }
241
242        offset = s2(offset);
243
244        let len = buf.len();
245        assert!(len <= BLOCK_SIZE);
246        let pad = self.aes_encrypt((len * 8) as u128 ^ offset);
247        let mut block = pad.to_be_bytes();
248        block[..len].copy_from_slice(buf);
249        let plain = u128::from_be_bytes(block);
250        let encrypted = pad ^ plain;
251        buf.copy_from_slice(&encrypted.to_be_bytes()[..len]);
252
253        checksum ^= plain;
254
255        self.aes_encrypt(offset ^ s2(offset) ^ checksum)
256    }
257
258    /// Decrypt the provided buffer using AES-OCB, returning the tag.
259    /// **Make sure to verify that the tag matches!**
260    fn ocb_decrypt(&self, mut buf: &mut [u8]) -> u128 {
261        let mut offset = self.aes_encrypt(self.decrypt_nonce.to_be());
262        let mut checksum = 0u128;
263
264        while buf.len() > BLOCK_SIZE {
265            let (chunk, remainder) = buf.split_at_mut(BLOCK_SIZE);
266            buf = remainder;
267            let chunk: &mut [u8; BLOCK_SIZE] = chunk.try_into().expect("split_at works");
268
269            offset = s2(offset);
270
271            let encrypted = u128::from_be_bytes(*chunk);
272            let plain = self.aes_decrypt(offset ^ encrypted) ^ offset;
273            chunk.copy_from_slice(&plain.to_be_bytes());
274
275            checksum ^= plain;
276        }
277
278        offset = s2(offset);
279
280        let len = buf.len();
281        assert!(len <= BLOCK_SIZE);
282        let pad = self.aes_encrypt((len * 8) as u128 ^ offset);
283        let mut block = [0; BLOCK_SIZE];
284        block[..len].copy_from_slice(buf);
285        let plain = u128::from_be_bytes(block) ^ pad;
286        buf.copy_from_slice(&plain.to_be_bytes()[..len]);
287
288        checksum ^= plain;
289
290        self.aes_encrypt(offset ^ s2(offset) ^ checksum)
291    }
292
293    /// AES-128 encryption primitive.
294    fn aes_encrypt(&self, block: u128) -> u128 {
295        // TODO is there no better way to do this (and aes_decrypt)?
296        let mut result = [0u8; BLOCK_SIZE * 2];
297        let mut crypter = openssl::symm::Crypter::new(
298            openssl::symm::Cipher::aes_128_ecb(),
299            openssl::symm::Mode::Encrypt,
300            &self.key,
301            None,
302        )
303        .unwrap();
304        crypter.pad(false);
305        crypter.update(&block.to_be_bytes(), &mut result).unwrap();
306        crypter.finalize(&mut result).unwrap();
307        u128::from_be_bytes((&result[..BLOCK_SIZE]).try_into().unwrap())
308    }
309
310    /// AES-128 decryption primitive.
311    fn aes_decrypt(&self, block: u128) -> u128 {
312        let mut result = [0u8; BLOCK_SIZE * 2];
313        let mut crypter = openssl::symm::Crypter::new(
314            openssl::symm::Cipher::aes_128_ecb(),
315            openssl::symm::Mode::Decrypt,
316            &self.key,
317            None,
318        )
319        .unwrap();
320        crypter.pad(false);
321        crypter.update(&block.to_be_bytes(), &mut result).unwrap();
322        crypter.finalize(&mut result).unwrap();
323        u128::from_be_bytes((&result[..BLOCK_SIZE]).try_into().unwrap())
324    }
325}
326
327fn s2(block: u128) -> u128 {
328    let rot = block.rotate_left(1);
329    let carry = rot & 1;
330    rot ^ (carry * 0x86)
331}
332
333impl<EncodeDst: VoicePacketDst, DecodeDst: VoicePacketDst> Decoder
334    for CryptState<EncodeDst, DecodeDst>
335{
336    type Item = VoicePacket<DecodeDst>;
337    type Error = io::Error;
338
339    fn decode(&mut self, buf_mut: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
340        if buf_mut.is_empty() {
341            return Ok(None);
342        }
343        self.decrypt(buf_mut)
344            .unwrap_or_else(|_| {
345                Err(io::Error::new(
346                    io::ErrorKind::InvalidData,
347                    "failed to decrypt",
348                ))
349            })
350            .map(Some)
351    }
352}
353
354impl<EncodeDst: VoicePacketDst, DecodeDst: VoicePacketDst> Encoder<VoicePacket<EncodeDst>>
355    for CryptState<EncodeDst, DecodeDst>
356{
357    type Error = io::Error; // never
358
359    fn encode(
360        &mut self,
361        item: VoicePacket<EncodeDst>,
362        dst: &mut BytesMut,
363    ) -> Result<(), Self::Error> {
364        self.encrypt(item, dst);
365        Ok(())
366    }
367}
368
369#[cfg(test)]
370mod test {
371    use bytes::BufMut;
372
373    use super::*;
374    use crate::voice::VoicePacketPayload;
375
376    fn u128hex(src: &str) -> u128 {
377        u128::from_str_radix(src, 16).unwrap()
378    }
379
380    fn bytes_from_hex(src: &str) -> BytesMut {
381        let mut buf = BytesMut::new();
382        hex_to_bytes(src, &mut buf);
383        buf
384    }
385
386    fn hex_to_bytes(src: &str, dst: &mut BytesMut) {
387        dst.clear();
388        dst.reserve(src.len() / 2);
389        let mut iter = src.chars();
390        while !iter.as_str().is_empty() {
391            dst.put_u8(u8::from_str_radix(&iter.as_str()[..2], 16).unwrap());
392            iter.next();
393            iter.next();
394        }
395    }
396
397    #[test]
398    fn aes_test_vectors() {
399        let key = u128hex("E8E9EAEBEDEEEFF0F2F3F4F5F7F8F9FA");
400        let state =
401            ClientCryptState::new_from(key.to_be_bytes(), Default::default(), Default::default());
402        assert_eq!(
403            u128hex("6743C3D1519AB4F2CD9A78AB09A511BD"),
404            state.aes_encrypt(u128hex("014BAF2278A69D331D5180103643E99A"))
405        );
406        assert_eq!(
407            u128hex("014BAF2278A69D331D5180103643E99A"),
408            state.aes_decrypt(u128hex("6743C3D1519AB4F2CD9A78AB09A511BD"))
409        );
410    }
411
412    // Test vectors from http://web.cs.ucdavis.edu/~rogaway/papers/draft-krovetz-ocb-00.txt
413    // (excluding ones with headers since those aren't implemented here)
414    #[test]
415    #[allow(clippy::cognitive_complexity)] // all macro-generated
416    fn ocb_test_vectors() {
417        macro_rules! test_cases {
418            ($(
419                T : $name:expr,
420                M : $plain:expr,
421                C : $cipher:expr,
422                T : $tag:expr,
423            )*) => {$(
424                let key = u128hex("000102030405060708090a0b0c0d0e0f");
425                let nonce = u128hex("000102030405060708090a0b0c0d0e0f");
426                let state = ClientCryptState::new_from(
427                    key.to_be_bytes(),
428                    nonce.to_be_bytes(),
429                    nonce.to_be_bytes(),
430                );
431
432                let mut result = BytesMut::new();
433                hex_to_bytes($plain.as_ref(), &mut result);
434                let tag = state.ocb_encrypt(&mut result);
435                assert_eq!(bytes_from_hex($cipher), result, concat!("ENCRYPT-RESULT-", $name));
436                assert_eq!(u128hex($tag), tag, concat!("ENCRYPT-TAG-", $name));
437
438                hex_to_bytes($cipher.as_ref(), &mut result);
439                let tag = state.ocb_decrypt(&mut result);
440                assert_eq!(bytes_from_hex($plain), result, concat!("DECRYPT-RESULT-", $name));
441                assert_eq!(u128hex($tag), tag, concat!("DECRYPT-TAG-", $name));
442            )*};
443        }
444
445        test_cases! {
446            T : "OCB-AES-128-0B",
447            M : "",
448            C : "",
449            T : "BF3108130773AD5EC70EC69E7875A7B0",
450
451            T : "OCB-AES-128-8B",
452            M : "0001020304050607",
453            C : "C636B3A868F429BB",
454            T : "A45F5FDEA5C088D1D7C8BE37CABC8C5C",
455
456            T : "OCB-AES-128-16B",
457            M : "000102030405060708090A0B0C0D0E0F",
458            C : "52E48F5D19FE2D9869F0C4A4B3D2BE57",
459            T : "F7EE49AE7AA5B5E6645DB6B3966136F9",
460
461            T : "OCB-AES-128-24B",
462            M : "000102030405060708090A0B0C0D0E0F1011121314151617",
463            C : "F75D6BC8B4DC8D66B836A2B08B32A636CC579E145D323BEB",
464            T : "A1A50F822819D6E0A216784AC24AC84C",
465
466            T : "OCB-AES-128-32B",
467            M : "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
468            C : "F75D6BC8B4DC8D66B836A2B08B32A636CEC3C555037571709DA25E1BB0421A27",
469            T : "09CA6C73F0B5C6C5FD587122D75F2AA3",
470
471            T : "OCB-AES-128-40B",
472            M : "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
473            C : "F75D6BC8B4DC8D66B836A2B08B32A6369F1CD3C5228D79FD6C267F5F6AA7B231C7DFB9D59951AE9C",
474            T : "9DB0CDF880F73E3E10D4EB3217766688",
475        }
476    }
477
478    #[test]
479    fn encrypt_and_decrypt_are_inverse() {
480        let mut server_state =
481            ServerCryptState::new_from(Default::default(), Default::default(), Default::default());
482        let mut client_state =
483            ClientCryptState::new_from(Default::default(), Default::default(), Default::default());
484
485        let packet = VoicePacket::Audio {
486            _dst: std::marker::PhantomData,
487            target: 13,
488            session_id: 42,
489            seq_num: 123_567,
490            payload: VoicePacketPayload::Opus(BytesMut::from("test").freeze(), true),
491            position_info: None,
492        };
493
494        let mut buf = BytesMut::new();
495        server_state.encrypt(packet.clone(), &mut buf);
496        let result = client_state
497            .decrypt(&mut buf)
498            .expect("Failed to decrypt")
499            .expect("Failed to decode");
500
501        assert_eq!(packet, result);
502    }
503}