fitgirl_decrypt/
crypto.rs1use std::num::NonZero;
2
3use aes_gcm::aead::Aead;
4use aes_gcm::{Aes256Gcm, Key, KeyInit, Nonce};
5use base64::prelude::BASE64_STANDARD;
6use base64::Engine as _;
7
8use crate::{types::CompressionType, Error};
9use crate::{Attachment, Cipher, CipherInfo};
10
11pub fn decrypt_with_key(
13 master_key: impl AsRef<[u8]>,
14 cipher: impl AsRef<CipherInfo>,
15) -> crate::Result<Attachment> {
16 let CipherInfo { adata, ct } = cipher.as_ref();
17
18 let Cipher {
19 cipher_iv,
20 kdf_salt,
21 kdf_iterations,
22 compression_type,
23 ..
24 } = &adata.cipher;
25
26 let ct = BASE64_STANDARD.decode(ct)?;
27 let cipher_iv = BASE64_STANDARD.decode(cipher_iv)?;
28 let kdf_salt = BASE64_STANDARD.decode(kdf_salt)?;
29 let iterations = NonZero::new(*kdf_iterations).ok_or(Error::ZeroIterations)?;
30 let algorithm = ring::pbkdf2::PBKDF2_HMAC_SHA256;
31
32 let mut derived_key = [0u8; 32];
33 ring::pbkdf2::derive(
34 algorithm,
35 iterations,
36 &kdf_salt,
37 master_key.as_ref(),
38 &mut derived_key,
39 );
40
41 let adata_json = serde_json::to_string(&adata)?;
42
43 let data = decrypt_aes_256_gcm(&ct, &derived_key, cipher_iv, &adata_json, compression_type)?;
44 Ok(serde_json::from_slice(&data)?)
45}
46
47fn decrypt_aes_256_gcm(
48 ct: &[u8],
49 derived_key: &[u8; 32],
50 iv: Vec<u8>,
51 adata_json: &str,
52 compression_type: &CompressionType,
53) -> crate::Result<Vec<u8>> {
54 type Cipher = aes_gcm::AesGcm<aes_gcm::aes::Aes256, typenum::U16>;
55
56 let cipher = Cipher::new(Key::<Aes256Gcm>::from_slice(derived_key));
57 let payload = aes_gcm::aead::Payload {
58 msg: ct,
59 aad: adata_json.as_bytes(),
60 };
61 let data = cipher
62 .decrypt(Nonce::from_slice(&iv), payload)
63 .map_err(|_| Error::AesGcm)?;
64
65 let decompressed = match compression_type {
66 CompressionType::None => data,
67 CompressionType::Zlib => {
68 miniz_oxide::inflate::decompress_to_vec(&data).map_err(|_| Error::DecompressError)?
69 }
70 };
71
72 Ok(decompressed)
73}