1
use openssl::{
2
    bn::BigNum,
3
    dh::Dh,
4
    hash::{Hasher, MessageDigest, hash},
5
    md::Md,
6
    memcmp,
7
    nid::Nid,
8
    pkcs5::pbkdf2_hmac,
9
    pkey::{Id, PKey},
10
    pkey_ctx::PkeyCtx,
11
    rand::rand_bytes,
12
    sign::Signer,
13
    symm::{Cipher, Crypter, Mode},
14
};
15
use zeroize::Zeroizing;
16

            
17
use crate::{Key, Mac, file};
18

            
19
const ENC_ALG: Nid = Nid::AES_128_CBC;
20
const MAC_ALG: Nid = Nid::SHA256;
21

            
22
34
pub fn encrypt(
23
    data: impl AsRef<[u8]>,
24
    key: &Key,
25
    iv: impl AsRef<[u8]>,
26
) -> Result<Vec<u8>, super::Error> {
27
67
    let cipher = Cipher::from_nid(ENC_ALG).unwrap();
28
66
    let mut encryptor = Crypter::new(cipher, Mode::Encrypt, key.as_ref(), Some(iv.as_ref()))
29
        .expect("Invalid key or IV length");
30
33
    encryptor.pad(true);
31

            
32
34
    let mut blob = vec![0; data.as_ref().len() + cipher.block_size()];
33
    // Unwrapping since adding `CIPHER_BLOCK_SIZE` to array is enough space for
34
    // PKCS7
35
69
    let mut encrypted_len = encryptor.update(data.as_ref(), &mut blob)?;
36
35
    encrypted_len += encryptor.finalize(&mut blob[encrypted_len..])?;
37

            
38
34
    blob.truncate(encrypted_len);
39

            
40
35
    Ok(blob)
41
}
42

            
43
24
fn decrypt_with_padding(
44
    blob: impl AsRef<[u8]>,
45
    key: &Key,
46
    iv: impl AsRef<[u8]>,
47
    pad: bool,
48
) -> Result<Zeroizing<Vec<u8>>, super::Error> {
49
46
    let cipher = Cipher::from_nid(ENC_ALG).unwrap();
50
46
    let mut decrypter = Crypter::new(cipher, Mode::Decrypt, key.as_ref(), Some(iv.as_ref()))
51
        .expect("Invalid key or IV length");
52
22
    decrypter.pad(pad);
53

            
54
25
    let mut data = Zeroizing::new(vec![0; blob.as_ref().len() + cipher.block_size()]);
55
47
    let mut decrypted_len = decrypter.update(blob.as_ref(), &mut data)?;
56
24
    decrypted_len += decrypter.finalize(&mut data[decrypted_len..])?;
57

            
58
48
    data.truncate(decrypted_len);
59

            
60
24
    Ok(data)
61
}
62

            
63
20
pub fn decrypt(
64
    blob: impl AsRef<[u8]>,
65
    key: &Key,
66
    iv: impl AsRef<[u8]>,
67
) -> Result<Zeroizing<Vec<u8>>, super::Error> {
68
18
    decrypt_with_padding(blob, key, iv, true)
69
}
70

            
71
4
pub(crate) fn decrypt_no_padding(
72
    blob: impl AsRef<[u8]>,
73
    key: &Key,
74
    iv: impl AsRef<[u8]>,
75
) -> Result<Zeroizing<Vec<u8>>, super::Error> {
76
4
    decrypt_with_padding(blob, key, iv, false)
77
}
78

            
79
15
pub(crate) fn iv_len() -> usize {
80
16
    let cipher = Cipher::from_nid(ENC_ALG).unwrap();
81
15
    cipher.iv_len().unwrap()
82
}
83

            
84
11
pub(crate) fn generate_private_key() -> Result<Zeroizing<Vec<u8>>, super::Error> {
85
11
    let cipher = Cipher::from_nid(ENC_ALG).unwrap();
86
11
    let mut buf = Zeroizing::new(vec![0; cipher.key_len()]);
87
22
    rand_bytes(&mut buf)?;
88
11
    Ok(buf)
89
}
90

            
91
9
pub(crate) fn generate_public_key(private_key: impl AsRef<[u8]>) -> Result<Vec<u8>, super::Error> {
92
18
    let private_key_bn = BigNum::from_slice(private_key.as_ref()).unwrap();
93
    let dh = Dh::from_pqg(
94
18
        BigNum::get_rfc2409_prime_1024().unwrap(),
95
9
        None,
96
18
        BigNum::from_u32(2).unwrap(),
97
    )?;
98
18
    Ok(dh.set_private_key(private_key_bn)?.public_key().to_vec())
99
}
100

            
101
9
pub(crate) fn generate_aes_key(
102
    private_key: impl AsRef<[u8]>,
103
    server_public_key: impl AsRef<[u8]>,
104
) -> Result<Zeroizing<Vec<u8>>, super::Error> {
105
18
    let private_key_bn = BigNum::from_slice(private_key.as_ref()).unwrap();
106
18
    let server_public_key_bn = BigNum::from_slice(server_public_key.as_ref()).unwrap();
107
    let dh = Dh::from_pqg(
108
18
        BigNum::get_rfc2409_prime_1024().unwrap(),
109
9
        None,
110
18
        BigNum::from_u32(2).unwrap(),
111
    )?;
112
27
    let mut common_secret_bytes = dh
113
9
        .set_private_key(private_key_bn)?
114
9
        .compute_key(&server_public_key_bn)?;
115

            
116
9
    let mut common_secret_padded = vec![0; 128 - common_secret_bytes.len()];
117
    // inefficient, but ok for now
118
9
    common_secret_padded.append(&mut common_secret_bytes);
119

            
120
    // hkdf
121
    // input_keying_material
122
9
    let ikm = common_secret_padded;
123

            
124
18
    let mut okm = Zeroizing::new(vec![0; 16]);
125
18
    let mut ctx = PkeyCtx::new_id(Id::HKDF)?;
126
18
    ctx.derive_init()?;
127
9
    ctx.set_hkdf_md(Md::sha256())?;
128
9
    ctx.set_hkdf_key(&ikm)?;
129
9
    ctx.derive(Some(okm.as_mut()))
130
        .expect("hkdf expand should never fail");
131
9
    Ok(okm)
132
}
133

            
134
16
pub fn generate_iv() -> Result<Vec<u8>, super::Error> {
135
17
    let mut buf = vec![0; iv_len()];
136
33
    rand_bytes(&mut buf)?;
137
17
    Ok(buf)
138
}
139

            
140
9
pub(crate) fn mac_len() -> usize {
141
8
    let md = MessageDigest::from_nid(MAC_ALG).unwrap();
142
9
    md.size()
143
}
144

            
145
44
pub(crate) fn compute_mac(data: impl AsRef<[u8]>, key: &Key) -> Result<Mac, super::Error> {
146
83
    let md = MessageDigest::from_nid(MAC_ALG).unwrap();
147
40
    let mac_key = PKey::hmac(key.as_ref())?;
148
82
    let mut signer = Signer::new(md, &mac_key)?;
149
87
    signer.update(data.as_ref())?;
150
48
    signer.sign_to_vec().map_err(From::from).map(Mac::new)
151
}
152

            
153
18
pub(crate) fn verify_mac(
154
    data: impl AsRef<[u8]>,
155
    key: &Key,
156
    expected_mac: impl AsRef<[u8]>,
157
) -> Result<bool, super::Error> {
158
21
    Ok(memcmp::eq(
159
38
        compute_mac(&data, key)?.as_slice(),
160
20
        expected_mac.as_ref(),
161
    ))
162
}
163

            
164
4
pub(crate) fn verify_checksum_md5(digest: impl AsRef<[u8]>, content: impl AsRef<[u8]>) -> bool {
165
    memcmp::eq(
166
8
        &hash(MessageDigest::md5(), content.as_ref()).unwrap(),
167
4
        digest.as_ref(),
168
    )
169
}
170

            
171
20
pub(crate) fn derive_key(
172
    secret: impl AsRef<[u8]>,
173
    key_strength: Result<(), file::WeakKeyError>,
174
    salt: impl AsRef<[u8]>,
175
    iteration_count: usize,
176
) -> Result<Key, super::Error> {
177
33
    let cipher = Cipher::from_nid(ENC_ALG).unwrap();
178
13
    let mut key = Key::new_with_strength(vec![0; cipher.block_size()], key_strength);
179

            
180
36
    let md = MessageDigest::from_nid(MAC_ALG).unwrap();
181
    pbkdf2_hmac(
182
13
        secret.as_ref(),
183
20
        salt.as_ref(),
184
        iteration_count,
185
        md,
186
15
        key.as_mut(),
187
    )?;
188

            
189
17
    Ok(key)
190
}
191

            
192
5
pub(crate) fn legacy_derive_key_and_iv(
193
    secret: impl AsRef<[u8]>,
194
    key_strength: Result<(), file::WeakKeyError>,
195
    salt: impl AsRef<[u8]>,
196
    iteration_count: usize,
197
) -> Result<(Key, Vec<u8>), super::Error> {
198
10
    let cipher = Cipher::from_nid(ENC_ALG).unwrap();
199
5
    let mut buffer = vec![0; cipher.key_len() + cipher.iv_len().unwrap()];
200
10
    let mut hasher = Hasher::new(MessageDigest::sha256())?;
201
5
    let mut pos = 0usize;
202

            
203
    loop {
204
10
        hasher.update(secret.as_ref())?;
205
5
        hasher.update(salt.as_ref())?;
206
5
        let mut digest = hasher.finish()?;
207

            
208
10
        for _ in 1..iteration_count {
209
            // We can't pass an instance, the borrow checker
210
            // would complain about digest being dropped at the end of
211
            // for block
212
            #[allow(clippy::needless_borrows_for_generic_args)]
213
10
            hasher.update(&digest)?;
214
5
            digest = hasher.finish()?;
215
        }
216

            
217
5
        let to_read = usize::min(digest.len(), buffer.len() - pos);
218
5
        buffer[pos..].copy_from_slice(&(&*digest)[..to_read]);
219
5
        pos += to_read;
220

            
221
10
        if pos == buffer.len() {
222
            break;
223
        }
224

            
225
        // We can't pass an instance, the borrow checker
226
        // would complain about digest being dropped at the end of
227
        // for block
228
        #[allow(clippy::needless_borrows_for_generic_args)]
229
        hasher.update(&digest)?;
230
    }
231

            
232
10
    let iv = buffer.split_off(cipher.key_len());
233
10
    Ok((Key::new_with_strength(buffer, key_strength), iv))
234
}