1use crate::Error;
2use aes::cipher::{AsyncStreamCipher, KeyIvInit};
3use byteorder::{ByteOrder, LE};
4
5pub type Decipher = cfb_mode::Decryptor<aes::Aes128>;
6
7pub struct MmkvCrcHeader {
8 pub crc32: u32,
10
11 pub iv: [u8; 16],
13
14 pub real_size: usize,
16}
17
18impl MmkvCrcHeader {
19 pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, Error> {
21 let bytes = bytes.as_ref();
22 if bytes.len() < 32 {
23 return Err(Error::UnexpectedEof);
24 }
25
26 let crc32 = LE::read_u32(&bytes[0..4]);
27 let iv = bytes[0x0C..0x1C].try_into().unwrap();
28 let real_size = LE::read_u32(&bytes[0x1C..0x20]) as usize;
29
30 Ok(Self {
31 crc32,
32 iv,
33 real_size,
34 })
35 }
36}
37
38pub fn decrypt(
40 crc_header: &MmkvCrcHeader,
41 key: &[u8],
42 mmkv_file_body: &mut [u8],
43) -> Result<usize, Error> {
44 let real_size = crc_header.real_size;
45 if mmkv_file_body.len() < real_size + 4 {
46 Err(Error::BufferTooSmall(real_size))?;
47 }
48
49 let expected_size = LE::read_u32(&mmkv_file_body) as usize;
51 if expected_size != real_size {
52 Err(Error::FileSizeMismatch)?;
53 }
54
55 let mut file_slice = &mut mmkv_file_body[4..real_size + 4];
56 if crc32fast::hash(file_slice) != crc_header.crc32 {
57 Err(Error::ChecksumMismatch)?;
58 }
59
60 let decipher = Decipher::new(key.into(), &crc_header.iv.into());
61 decipher.decrypt(&mut file_slice);
62
63 Ok(real_size)
64}