1
use std::{
2
    ops::{Mul, Rem, Shr},
3
    sync::LazyLock,
4
};
5

            
6
use cipher::{
7
    BlockDecryptMut, BlockEncryptMut, BlockSizeUser, IvSizeUser, KeyIvInit, KeySizeUser,
8
    block_padding::{NoPadding, Pkcs7},
9
};
10
use digest::{Digest, FixedOutput, Mac, Output, OutputSizeUser};
11
use hkdf::Hkdf;
12
use md5::Md5;
13
use num::{FromPrimitive, Integer, One, Zero};
14
use num_bigint_dig::BigUint;
15
use sha2::Sha256;
16
use subtle::ConstantTimeEq;
17
use zeroize::{Zeroize, Zeroizing};
18

            
19
use crate::{Key, file};
20

            
21
type EncAlg = cbc::Encryptor<aes::Aes128>;
22
type DecAlg = cbc::Decryptor<aes::Aes128>;
23
type MacAlg = hmac::Hmac<sha2::Sha256>;
24

            
25
7
pub fn encrypt(
26
    data: impl AsRef<[u8]>,
27
    key: &Key,
28
    iv: impl AsRef<[u8]>,
29
) -> Result<Vec<u8>, super::Error> {
30
14
    let mut blob = vec![0; data.as_ref().len() + EncAlg::block_size()];
31

            
32
    // Unwrapping since adding `CIPHER_BLOCK_SIZE` to array is enough space for
33
    // PKCS7
34
28
    let encrypted_len = EncAlg::new_from_slices(key.as_ref(), iv.as_ref())
35
        .expect("Invalid key length")
36
14
        .encrypt_padded_b2b_mut::<Pkcs7>(data.as_ref(), &mut blob)?
37
        .len();
38

            
39
7
    blob.truncate(encrypted_len);
40

            
41
7
    Ok(blob)
42
}
43

            
44
6
pub fn decrypt(
45
    blob: impl AsRef<[u8]>,
46
    key: &Key,
47
    iv: impl AsRef<[u8]>,
48
) -> Result<Zeroizing<Vec<u8>>, super::Error> {
49
12
    let mut data = blob.as_ref().to_vec();
50

            
51
30
    Ok(DecAlg::new_from_slices(key.as_ref(), iv.as_ref())
52
6
        .expect("Invalid key length")
53
12
        .decrypt_padded_mut::<Pkcs7>(&mut data)?
54
6
        .to_vec()
55
6
        .into())
56
}
57

            
58
2
pub(crate) fn decrypt_no_padding(
59
    blob: impl AsRef<[u8]>,
60
    key: &Key,
61
    iv: impl AsRef<[u8]>,
62
) -> Result<Zeroizing<Vec<u8>>, super::Error> {
63
4
    let mut data = blob.as_ref().to_vec();
64

            
65
10
    Ok(DecAlg::new_from_slices(key.as_ref(), iv.as_ref())
66
2
        .expect("Invalid key length")
67
4
        .decrypt_padded_mut::<NoPadding>(&mut data)?
68
2
        .to_vec()
69
2
        .into())
70
}
71

            
72
2
pub(crate) fn iv_len() -> usize {
73
2
    DecAlg::iv_size()
74
}
75

            
76
2
pub(crate) fn generate_private_key() -> Result<Zeroizing<Vec<u8>>, super::Error> {
77
2
    let generic_array = EncAlg::generate_key(cipher::rand_core::OsRng);
78
2
    Ok(Zeroizing::new(generic_array.to_vec()))
79
}
80

            
81
2
pub(crate) fn generate_public_key(private_key: impl AsRef<[u8]>) -> Result<Vec<u8>, super::Error> {
82
4
    let private_key_uint = BigUint::from_bytes_be(private_key.as_ref());
83
4
    static DH_GENERATOR: LazyLock<BigUint> = LazyLock::new(|| BigUint::from_u64(0x2).unwrap());
84
4
    let public_key_uint = powm(&DH_GENERATOR, private_key_uint);
85

            
86
4
    Ok(public_key_uint.to_bytes_be())
87
}
88

            
89
2
pub(crate) fn generate_aes_key(
90
    private_key: impl AsRef<[u8]>,
91
    server_public_key: impl AsRef<[u8]>,
92
) -> Result<Zeroizing<Vec<u8>>, super::Error> {
93
4
    let server_public_key_uint = BigUint::from_bytes_be(server_public_key.as_ref());
94
4
    let private_key_uint = BigUint::from_bytes_be(private_key.as_ref());
95
2
    let common_secret = powm(&server_public_key_uint, private_key_uint);
96

            
97
2
    let mut common_secret_bytes = common_secret.to_bytes_be();
98
4
    let mut common_secret_padded = vec![0; 128 - common_secret_bytes.len()];
99
    // inefficient, but ok for now
100
2
    common_secret_padded.append(&mut common_secret_bytes);
101

            
102
    // hkdf
103
    // input_keying_material
104
2
    let ikm = common_secret_padded;
105
2
    let salt = None;
106
    let info = [];
107

            
108
    // output keying material
109
4
    let mut okm = Zeroizing::new(vec![0; 16]);
110

            
111
4
    let (_, hk) = Hkdf::<Sha256>::extract(salt, &ikm);
112
2
    hk.expand(&info, okm.as_mut())
113
        .expect("hkdf expand should never fail");
114

            
115
2
    Ok(okm)
116
}
117

            
118
2
pub fn generate_iv() -> Result<Vec<u8>, super::Error> {
119
2
    Ok(EncAlg::generate_iv(cipher::rand_core::OsRng).to_vec())
120
}
121

            
122
2
pub(crate) fn mac_len() -> usize {
123
2
    MacAlg::output_size()
124
}
125

            
126
4
pub(crate) fn compute_mac(data: impl AsRef<[u8]>, key: &Key) -> Result<crate::Mac, super::Error> {
127
8
    let mut mac = MacAlg::new_from_slice(key.as_ref()).unwrap();
128
4
    mac.update(data.as_ref());
129
4
    Ok(crate::Mac::new(mac.finalize().into_bytes().to_vec()))
130
}
131

            
132
4
pub(crate) fn verify_mac(
133
    data: impl AsRef<[u8]>,
134
    key: &Key,
135
    expected_mac: impl AsRef<[u8]>,
136
) -> Result<bool, super::Error> {
137
8
    let mut mac = MacAlg::new_from_slice(key.as_ref()).unwrap();
138
4
    mac.update(data.as_ref());
139
4
    Ok(mac.verify_slice(expected_mac.as_ref()).is_ok())
140
}
141

            
142
2
pub(crate) fn verify_checksum_md5(digest: impl AsRef<[u8]>, content: impl AsRef<[u8]>) -> bool {
143
2
    let mut hasher = Md5::new();
144
2
    hasher.update(content.as_ref());
145
2
    hasher.finalize_fixed().ct_eq(digest.as_ref()).into()
146
}
147

            
148
3
pub(crate) fn derive_key(
149
    secret: impl AsRef<[u8]>,
150
    key_strength: Result<(), file::WeakKeyError>,
151
    salt: impl AsRef<[u8]>,
152
    iteration_count: usize,
153
) -> Result<Key, super::Error> {
154
6
    let mut key = Key::new_with_strength(vec![0; EncAlg::block_size()], key_strength);
155

            
156
    pbkdf2::pbkdf2::<hmac::Hmac<sha2::Sha256>>(
157
3
        secret.as_ref(),
158
3
        salt.as_ref(),
159
3
        iteration_count.try_into().unwrap(),
160
3
        key.as_mut(),
161
    )
162
    .expect("HMAC can be initialized with any key length");
163

            
164
2
    Ok(key)
165
}
166

            
167
3
pub(crate) fn legacy_derive_key_and_iv(
168
    secret: impl AsRef<[u8]>,
169
    key_strength: Result<(), file::WeakKeyError>,
170
    salt: impl AsRef<[u8]>,
171
    iteration_count: usize,
172
) -> Result<(Key, Vec<u8>), super::Error> {
173
6
    let mut buffer = vec![0; EncAlg::key_size() + EncAlg::iv_size()];
174
3
    let mut hasher = Sha256::new();
175
3
    let mut digest_buffer = vec![0; <Sha256 as Digest>::output_size()];
176
    #[allow(deprecated)]
177
6
    let digest = Output::<Sha256>::from_mut_slice(digest_buffer.as_mut_slice());
178

            
179
3
    let mut pos = 0usize;
180

            
181
    loop {
182
3
        hasher.update(secret.as_ref());
183
3
        hasher.update(salt.as_ref());
184
3
        hasher.finalize_into_reset(digest);
185

            
186
3
        for _ in 1..iteration_count {
187
            // We can't pass an instance, the borrow checker
188
            // would complain about digest being dropped at the end of
189
            // for block
190
            #[allow(clippy::needless_borrows_for_generic_args)]
191
3
            hasher.update(&digest);
192
3
            hasher.finalize_into_reset(digest);
193
        }
194

            
195
3
        let to_read = usize::min(digest.len(), buffer.len() - pos);
196
3
        buffer[pos..].copy_from_slice(&digest[..to_read]);
197
3
        pos += to_read;
198

            
199
6
        if pos == buffer.len() {
200
            break;
201
        }
202

            
203
        // We can't pass an instance, the borrow checker
204
        // would complain about digest being dropped at the end of
205
        // loop block
206
        #[allow(clippy::needless_borrows_for_generic_args)]
207
        hasher.update(&digest);
208
    }
209

            
210
6
    let iv = buffer.split_off(EncAlg::key_size());
211
6
    Ok((Key::new_with_strength(buffer, key_strength), iv))
212
}
213

            
214
/// from https://github.com/plietar/librespot/blob/master/core/src/util/mod.rs#L53
215
2
fn powm(base: &BigUint, mut exp: BigUint) -> BigUint {
216
    // for key exchange
217
2
    static DH_PRIME: LazyLock<BigUint> = LazyLock::new(|| {
218
2
        BigUint::from_bytes_be(&[
219
            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68,
220
            0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08,
221
            0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A,
222
            0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
223
            0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51,
224
            0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
225
            0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38,
226
            0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
227
            0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
228
            0xFF, 0xFF,
229
        ])
230
    });
231

            
232
2
    let mut base = base.clone();
233
2
    let mut result: BigUint = One::one();
234

            
235
6
    while !exp.is_zero() {
236
6
        if exp.is_odd() {
237
2
            result = result.mul(&base).rem(&*DH_PRIME);
238
        }
239
4
        exp = exp.shr(1);
240
2
        base = (&base).mul(&base).rem(&*DH_PRIME);
241
    }
242
2
    exp.zeroize();
243

            
244
2
    result
245
}