use crate::crypt;
use crate::result::{DatabaseIntegrityError, Result};
use byteorder::{ByteOrder, LittleEndian};
pub(crate) fn read_hmac_block_stream(
data: &[u8],
key: &crypt::GenericArray<u8, crypt::U64>,
) -> Result<Vec<u8>> {
let mut out = Vec::new();
let mut pos = 0;
let mut block_index = 0;
while pos < data.len() {
let hmac = &data[pos..(pos + 32)];
let size_bytes = &data[(pos + 32)..(pos + 36)];
let size = LittleEndian::read_u32(size_bytes) as usize;
let block = &data[(pos + 36)..(pos + 36 + size)];
let hmac_block_key = get_hmac_block_key(block_index, key)?;
let mut block_index_buf = [0u8; 8];
LittleEndian::write_u64(&mut block_index_buf, block_index as u64);
if hmac
!= crypt::calculate_hmac(&[&block_index_buf, size_bytes, &block], &hmac_block_key)?
.as_slice()
{
return Err(DatabaseIntegrityError::BlockHashMismatch { block_index }.into());
}
pos += 36 + size;
block_index += 1;
out.extend_from_slice(block);
}
Ok(out)
}
pub(crate) fn get_hmac_block_key(
block_index: usize,
key: &crypt::GenericArray<u8, crypt::U64>,
) -> Result<crypt::GenericArray<u8, crypt::U64>> {
let mut buf = [0u8; 8];
LittleEndian::write_u64(&mut buf, block_index as u64);
crate::crypt::calculate_sha512(&[&buf, key])
}