mumble_protocol_2x/
crypt.rs

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