use core::mem::size_of;
use crate::digest::{Digest, FixedOutputReset};
use super::FsVerityHashValue;
#[derive(Debug)]
struct FsVerityLayer<H: FsVerityHashValue, const LG_BLKSZ: u8 = 12> {
context: H::Digest,
remaining: usize,
}
impl<H: FsVerityHashValue, const LG_BLKSZ: u8> FsVerityLayer<H, LG_BLKSZ> {
fn new() -> Self {
Self {
context: H::Digest::new(),
remaining: 1 << LG_BLKSZ,
}
}
fn add_data(&mut self, data: &[u8]) {
self.context.update(data);
self.remaining -= data.len();
}
fn complete(&mut self) -> H {
self.context.update([0].repeat(self.remaining));
self.remaining = 1 << LG_BLKSZ;
self.context.finalize_reset().into()
}
}
#[derive(Debug)]
pub struct FsVerityHasher<H: FsVerityHashValue, const LG_BLKSZ: u8 = 12> {
layers: Vec<FsVerityLayer<H, LG_BLKSZ>>,
value: Option<H>,
n_bytes: u64,
partial: Vec<u8>,
wrote_final_block: bool,
}
impl<H: FsVerityHashValue, const LG_BLKSZ: u8> FsVerityHasher<H, LG_BLKSZ> {
pub const BLOCK_SIZE: usize = 1 << LG_BLKSZ;
pub fn hash(buffer: &[u8]) -> H {
use std::io::Write;
let mut hasher = Self::new();
hasher.write_all(buffer).expect("infallible write");
hasher.digest()
}
pub fn new() -> Self {
Self {
layers: vec![],
value: None,
n_bytes: 0,
partial: Vec::with_capacity(Self::BLOCK_SIZE),
wrote_final_block: false,
}
}
fn add_block_from_partial(&mut self) {
let block = std::mem::replace(&mut self.partial, Vec::with_capacity(Self::BLOCK_SIZE));
self.add_block(&block);
}
pub fn add_block(&mut self, data: &[u8]) {
assert!(
!self.wrote_final_block,
"cannot add data after a partial block"
);
if data.len() < Self::BLOCK_SIZE {
self.wrote_final_block = true;
}
if let Some(value) = self.value.take() {
let mut new_layer = FsVerityLayer::new();
new_layer.add_data(value.as_bytes());
self.layers.push(new_layer);
}
let mut context = FsVerityLayer::<H, LG_BLKSZ>::new();
context.add_data(data);
let mut value = context.complete();
self.n_bytes += data.len() as u64;
for layer in self.layers.iter_mut() {
layer.add_data(value.as_bytes());
if layer.remaining != 0 {
return;
}
value = layer.complete();
}
self.value = Some(value);
}
fn root_hash(&mut self) -> H {
if let Some(value) = &self.value {
value.clone()
} else {
let mut value = H::EMPTY;
for layer in self.layers.iter_mut() {
if value != H::EMPTY {
layer.add_data(value.as_bytes());
}
if layer.remaining != (1 << LG_BLKSZ) {
value = layer.complete();
} else {
value = H::EMPTY;
}
}
self.value = Some(value.clone());
value
}
}
pub fn digest(&mut self) -> H {
if !self.partial.is_empty() {
self.add_block_from_partial();
}
let mut context = H::Digest::new();
context.update(1u8.to_le_bytes());
context.update(H::ALGORITHM.kernel_id().to_le_bytes());
context.update(LG_BLKSZ.to_le_bytes());
context.update(0u8.to_le_bytes());
context.update([0; 4]);
context.update(self.n_bytes.to_le_bytes());
context.update(self.root_hash().as_bytes());
context.update([0].repeat(64 - size_of::<H>()));
context.update([0; 32]);
context.update([0; 144]);
context.finalize().into()
}
}
impl<H: FsVerityHashValue, const LG_BLKSZ: u8> std::io::Write for FsVerityHasher<H, LG_BLKSZ> {
fn write(&mut self, data: &[u8]) -> std::io::Result<usize> {
let len = data.len();
let mut remaining = data;
while !remaining.is_empty() {
let space = Self::BLOCK_SIZE - self.partial.len();
let n = remaining.len().min(space);
self.partial.extend_from_slice(&remaining[..n]);
remaining = &remaining[n..];
if self.partial.len() == Self::BLOCK_SIZE {
self.add_block_from_partial();
}
}
Ok(len)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl<H: FsVerityHashValue, const LG_BLKSZ: u8> Default for FsVerityHasher<H, LG_BLKSZ> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use similar_asserts::assert_eq;
use crate::fsverity::{Sha256HashValue, Sha512HashValue};
use super::*;
#[test]
fn test_digest() {
assert_eq!(
FsVerityHasher::<Sha256HashValue, 12>::hash(b"hello world").to_hex(),
"1e2eaa4202d750a41174ee454970b92c1bc2f925b1e35076d8c7d5f56362ba64"
);
assert_eq!(
FsVerityHasher::<Sha512HashValue, 12>::hash(b"hello world").to_hex(),
"18430270729d162d4e469daca123ae61893db4b0583d8f7081e3bf4f92b88ba514e7982f10733fb6aa895195c5ae8fd2eb2c47a8be05513ce5a0c51a6f570409"
);
}
}