librespot_audio/
decrypt.rs

1use std::io;
2
3use aes::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek};
4
5type Aes128Ctr = ctr::Ctr128BE<aes::Aes128>;
6
7use librespot_core::audio_key::AudioKey;
8
9const AUDIO_AESIV: [u8; 16] = [
10    0x72, 0xe0, 0x67, 0xfb, 0xdd, 0xcb, 0xcf, 0x77, 0xeb, 0xe8, 0xbc, 0x64, 0x3f, 0x63, 0x0d, 0x93,
11];
12
13pub struct AudioDecrypt<T: io::Read> {
14    // a `None` cipher is a convenience to make `AudioDecrypt` pass files unaltered
15    cipher: Option<Aes128Ctr>,
16    reader: T,
17}
18
19impl<T: io::Read> AudioDecrypt<T> {
20    pub fn new(key: Option<AudioKey>, reader: T) -> AudioDecrypt<T> {
21        let cipher = if let Some(key) = key {
22            Aes128Ctr::new_from_slices(&key.0, &AUDIO_AESIV).ok()
23        } else {
24            // some files are unencrypted
25            None
26        };
27
28        AudioDecrypt { cipher, reader }
29    }
30}
31
32impl<T: io::Read> io::Read for AudioDecrypt<T> {
33    fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
34        let len = self.reader.read(output)?;
35
36        if let Some(ref mut cipher) = self.cipher {
37            cipher.apply_keystream(&mut output[..len]);
38        }
39
40        Ok(len)
41    }
42}
43
44impl<T: io::Read + io::Seek> io::Seek for AudioDecrypt<T> {
45    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
46        let newpos = self.reader.seek(pos)?;
47
48        if let Some(ref mut cipher) = self.cipher {
49            cipher.seek(newpos);
50        }
51
52        Ok(newpos)
53    }
54}