use core::convert::TryFrom;
use core::mem;
use plain::Plain;
use crate::{Entry, Error, PublicKey};
#[derive(Clone, Copy)]
#[repr(packed)]
pub struct Header {
pub signature: [u8; 64],
pub public_key: [u8; 32],
pub blake3: [u8; 32],
pub count: u64,
}
unsafe impl Plain for Header {}
impl Header {
pub fn new<'a>(data: &'a [u8], public_key: &PublicKey) -> Result<&'a Header, Error> {
let signed = data.get(..mem::size_of::<Header>())
.ok_or(Error::Plain(plain::Error::TooShort))?;
let mut verified = [0; mem::size_of::<Header>()];
let count = sodalite::sign_attached_open(&mut verified, signed, public_key.as_data())
.map_err(|_err| Error::InvalidSignature)?;
if &verified[..count] != &signed[64..] {
return Err(Error::InvalidData);
}
let header: &Header = unsafe { Header::new_unchecked(signed)? };
if &header.public_key != &public_key.as_data()[..] {
return Err(Error::InvalidKey);
}
Ok(header)
}
pub unsafe fn new_unchecked<'a>(data: &'a [u8]) -> Result<&'a Header, Error> {
plain::from_bytes(data)
.map_err(Error::Plain)
}
pub fn entries_size(&self) -> Result<u64, Error> {
let entry_size = u64::try_from(mem::size_of::<Entry>())
.map_err(Error::TryFromInt)?;
self.count
.checked_mul(entry_size)
.ok_or(Error::Overflow)
}
pub fn total_size(&self) -> Result<u64, Error> {
let header_size = u64::try_from(mem::size_of::<Header>())
.map_err(Error::TryFromInt)?;
self.entries_size()?
.checked_add(header_size)
.ok_or(Error::Overflow)
}
pub fn entries<'a>(&self, data: &'a [u8]) -> Result<&'a [Entry], Error> {
let entries_size = usize::try_from(self.entries_size()?)
.map_err(Error::TryFromInt)?;
let entries_data = data.get(..entries_size)
.ok_or(Error::Plain(plain::Error::TooShort))?;
let hash = {
let mut hasher = blake3::Hasher::new();
hasher.update_with_join::<blake3::join::RayonJoin>(&entries_data);
hasher.finalize()
};
if &self.blake3 != hash.as_bytes() {
return Err(Error::InvalidBlake3);
}
unsafe { Self::entries_unchecked(entries_data) }
}
pub unsafe fn entries_unchecked<'a>(data: &'a [u8]) -> Result<&'a [Entry], Error> {
plain::slice_from_bytes(data)
.map_err(Error::Plain)
}
}