mmkv_parser/
cipher.rs

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    /// CRC32 checksum of the enciphered file content, to the `hdr.real_size` bytes.
9    pub crc32: u32,
10
11    /// AES-128-CFB IV
12    pub iv: [u8; 16],
13
14    /// The actual mmkv store file size (without the 4-byte header).
15    pub real_size: usize,
16}
17
18impl MmkvCrcHeader {
19    /// Parse mmkv crc file from bytes.
20    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
38/// Decrypt a given mmkv file using the header
39pub 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    // First 4 bytes is also the mmkv body size.
50    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}