composefs/fsverity/
digest.rs1use core::{cmp::min, mem::size_of};
7
8use sha2::Digest;
9
10use super::FsVerityHashValue;
11
12#[derive(Debug)]
13struct FsVerityLayer<H: FsVerityHashValue, const LG_BLKSZ: u8 = 12> {
14 context: H::Digest,
15 remaining: usize,
16}
17
18impl<H: FsVerityHashValue, const LG_BLKSZ: u8> FsVerityLayer<H, LG_BLKSZ> {
19 fn new() -> Self {
20 Self {
21 context: H::Digest::new(),
22 remaining: 1 << LG_BLKSZ,
23 }
24 }
25
26 fn add_data(&mut self, data: &[u8]) {
27 self.context.update(data);
28 self.remaining -= data.len();
29 }
30
31 fn complete(&mut self) -> H {
32 self.context.update([0].repeat(self.remaining));
33 self.remaining = 1 << LG_BLKSZ;
34 self.context.finalize_reset().into()
35 }
36}
37
38#[derive(Debug)]
53pub struct FsVerityHasher<H: FsVerityHashValue, const LG_BLKSZ: u8 = 12> {
54 layers: Vec<FsVerityLayer<H, LG_BLKSZ>>,
55 value: Option<H>,
56 n_bytes: u64,
57}
58
59impl<H: FsVerityHashValue, const LG_BLKSZ: u8> FsVerityHasher<H, LG_BLKSZ> {
60 pub const BLOCK_SIZE: usize = 1 << LG_BLKSZ;
62
63 pub fn hash(buffer: &[u8]) -> H {
65 let mut hasher = Self::new();
66
67 let mut start = 0;
68 while start < buffer.len() {
69 let end = min(start + Self::BLOCK_SIZE, buffer.len());
70 hasher.add_block(&buffer[start..end]);
71 start = end;
72 }
73
74 hasher.digest()
75 }
76
77 pub fn new() -> Self {
79 Self {
80 layers: vec![],
81 value: None,
82 n_bytes: 0,
83 }
84 }
85
86 pub fn add_block(&mut self, data: &[u8]) {
91 if let Some(value) = self.value.take() {
92 let mut new_layer = FsVerityLayer::new();
95 new_layer.add_data(value.as_bytes());
96 self.layers.push(new_layer);
97 }
98
99 let mut context = FsVerityLayer::<H, LG_BLKSZ>::new();
101 context.add_data(data);
102 let mut value = context.complete();
103 self.n_bytes += data.len() as u64;
104
105 for layer in self.layers.iter_mut() {
106 layer.add_data(value.as_bytes());
108 if layer.remaining != 0 {
109 return;
110 }
111 value = layer.complete();
113 }
114
115 self.value = Some(value);
117 }
118
119 fn root_hash(&mut self) -> H {
120 if let Some(value) = &self.value {
121 value.clone()
122 } else {
123 let mut value = H::EMPTY;
124
125 for layer in self.layers.iter_mut() {
126 if value != H::EMPTY {
128 layer.add_data(value.as_bytes());
129 }
130 if layer.remaining != (1 << LG_BLKSZ) {
131 value = layer.complete();
133 } else {
134 value = H::EMPTY;
135 }
136 }
137
138 self.value = Some(value.clone());
139
140 value
141 }
142 }
143
144 pub fn digest(&mut self) -> H {
148 let mut context = H::Digest::new();
171 context.update(1u8.to_le_bytes()); context.update(H::ALGORITHM.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());
177 context.update(self.root_hash().as_bytes());
178 context.update([0].repeat(64 - size_of::<H>()));
179 context.update([0; 32]); context.update([0; 144]); context.finalize().into()
182 }
183}
184
185impl<H: FsVerityHashValue, const LG_BLKSZ: u8> Default for FsVerityHasher<H, LG_BLKSZ> {
186 fn default() -> Self {
187 Self::new()
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use similar_asserts::assert_eq;
194
195 use crate::fsverity::{Sha256HashValue, Sha512HashValue};
196
197 use super::*;
198
199 #[test]
200 fn test_digest() {
201 assert_eq!(
202 FsVerityHasher::<Sha256HashValue, 12>::hash(b"hello world").to_hex(),
203 "1e2eaa4202d750a41174ee454970b92c1bc2f925b1e35076d8c7d5f56362ba64"
204 );
205
206 assert_eq!(
207 FsVerityHasher::<Sha512HashValue, 12>::hash(b"hello world").to_hex(),
208 "18430270729d162d4e469daca123ae61893db4b0583d8f7081e3bf4f92b88ba514e7982f10733fb6aa895195c5ae8fd2eb2c47a8be05513ce5a0c51a6f570409"
209 );
210 }
211}