use crate::Error;
use aes::cipher::{AsyncStreamCipher, KeyIvInit};
use byteorder::{ByteOrder, LE};
pub type Decipher = cfb_mode::Decryptor<aes::Aes128>;
pub struct MmkvCrcHeader {
pub crc32: u32,
pub iv: [u8; 16],
pub real_size: usize,
}
impl MmkvCrcHeader {
pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, Error> {
let bytes = bytes.as_ref();
if bytes.len() < 32 {
return Err(Error::UnexpectedEof);
}
let crc32 = LE::read_u32(&bytes[0..4]);
let iv = bytes[0x0C..0x1C].try_into().unwrap();
let real_size = LE::read_u32(&bytes[0x1C..0x20]) as usize;
Ok(Self {
crc32,
iv,
real_size,
})
}
}
pub fn decrypt(
crc_header: &MmkvCrcHeader,
key: &[u8],
mmkv_file_body: &mut [u8],
) -> Result<usize, Error> {
let real_size = crc_header.real_size;
if mmkv_file_body.len() < real_size + 4 {
Err(Error::BufferTooSmall(real_size))?;
}
let expected_size = LE::read_u32(&mmkv_file_body) as usize;
if expected_size != real_size {
Err(Error::FileSizeMismatch)?;
}
let mut file_slice = &mut mmkv_file_body[4..real_size + 4];
if crc32fast::hash(file_slice) != crc_header.crc32 {
Err(Error::ChecksumMismatch)?;
}
let decipher = Decipher::new(key.into(), &crc_header.iv.into());
decipher.decrypt(&mut file_slice);
Ok(real_size)
}