blob: 8429caab721d4edbfcf8a90d136d2abac0b77071 [file] [log] [blame]
Victor Hsiehec184562020-11-06 13:50:31 -08001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Victor Hsiehf77c5722020-11-13 14:30:05 -080017use libc::EIO;
Victor Hsiehec184562020-11-06 13:50:31 -080018use std::io;
19use thiserror::Error;
20
Victor Hsiehf77c5722020-11-13 14:30:05 -080021use crate::auth::Authenticator;
Victor Hsiehda3fbc42021-02-23 16:12:49 -080022use crate::common::{divide_roundup, CHUNK_SIZE};
Victor Hsiehec184562020-11-06 13:50:31 -080023use crate::crypto::{CryptoError, Sha256Hasher};
24use crate::reader::ReadOnlyDataByChunk;
25
Victor Hsiehda3fbc42021-02-23 16:12:49 -080026const ZEROS: [u8; CHUNK_SIZE as usize] = [0u8; CHUNK_SIZE as usize];
Victor Hsiehec184562020-11-06 13:50:31 -080027
Victor Hsiehf77c5722020-11-13 14:30:05 -080028// The size of `struct fsverity_formatted_digest` in Linux with SHA-256.
29const SIZE_OF_FSVERITY_FORMATTED_DIGEST_SHA256: usize = 12 + Sha256Hasher::HASH_SIZE;
30
Victor Hsiehec184562020-11-06 13:50:31 -080031#[derive(Error, Debug)]
32pub enum FsverityError {
Victor Hsiehf77c5722020-11-13 14:30:05 -080033 #[error("Cannot verify a signature")]
34 BadSignature,
35 #[error("Insufficient data, only got {0}")]
36 InsufficientData(usize),
Victor Hsiehec184562020-11-06 13:50:31 -080037 #[error("Cannot verify a block")]
38 CannotVerify,
39 #[error("I/O error")]
40 Io(#[from] io::Error),
41 #[error("Crypto")]
42 UnexpectedCryptoError(#[from] CryptoError),
43}
44
45type HashBuffer = [u8; Sha256Hasher::HASH_SIZE];
46
Victor Hsiehec184562020-11-06 13:50:31 -080047fn hash_with_padding(chunk: &[u8], pad_to: usize) -> Result<HashBuffer, CryptoError> {
48 let padding_size = pad_to - chunk.len();
49 Sha256Hasher::new()?.update(&chunk)?.update(&ZEROS[..padding_size])?.finalize()
50}
51
Victor Hsiehec184562020-11-06 13:50:31 -080052fn verity_check<T: ReadOnlyDataByChunk>(
53 chunk: &[u8],
54 chunk_index: u64,
55 file_size: u64,
56 merkle_tree: &T,
57) -> Result<HashBuffer, FsverityError> {
58 // The caller should not be able to produce a chunk at the first place if `file_size` is 0. The
59 // current implementation expects to crash when a `ReadOnlyDataByChunk` implementation reads
60 // beyone the file size, including empty file.
61 assert_ne!(file_size, 0);
62
Victor Hsiehda3fbc42021-02-23 16:12:49 -080063 let chunk_hash = hash_with_padding(&chunk, CHUNK_SIZE as usize)?;
Victor Hsiehec184562020-11-06 13:50:31 -080064
65 fsverity_walk(chunk_index, file_size, merkle_tree)?.try_fold(
66 chunk_hash,
67 |actual_hash, result| {
68 let (merkle_chunk, hash_offset_in_chunk) = result?;
69 let expected_hash =
70 &merkle_chunk[hash_offset_in_chunk..hash_offset_in_chunk + Sha256Hasher::HASH_SIZE];
71 if actual_hash != expected_hash {
72 return Err(FsverityError::CannotVerify);
73 }
Victor Hsiehda3fbc42021-02-23 16:12:49 -080074 Ok(hash_with_padding(&merkle_chunk, CHUNK_SIZE as usize)?)
Victor Hsiehec184562020-11-06 13:50:31 -080075 },
76 )
77}
78
79fn log128_ceil(num: u64) -> Option<u64> {
80 match num {
81 0 => None,
82 n => Some(divide_roundup(64 - (n - 1).leading_zeros() as u64, 7)),
83 }
84}
85
86/// Given a chunk index and the size of the file, returns an iterator that walks the Merkle tree
87/// from the leaf to the root. The iterator carries the slice of the chunk/node as well as the
88/// offset of the child node's hash. It is up to the iterator user to use the node and hash,
89/// e.g. for the actual verification.
90#[allow(clippy::needless_collect)]
ThiƩbaud Weksteen3f5f2362021-01-12 11:37:51 +010091fn fsverity_walk<T: ReadOnlyDataByChunk>(
Victor Hsiehec184562020-11-06 13:50:31 -080092 chunk_index: u64,
93 file_size: u64,
ThiƩbaud Weksteen3f5f2362021-01-12 11:37:51 +010094 merkle_tree: &T,
95) -> Result<impl Iterator<Item = Result<([u8; 4096], usize), FsverityError>> + '_, FsverityError> {
Victor Hsiehda3fbc42021-02-23 16:12:49 -080096 let hashes_per_node = CHUNK_SIZE / Sha256Hasher::HASH_SIZE as u64;
97 let hash_pages = divide_roundup(file_size, hashes_per_node * CHUNK_SIZE);
Victor Hsiehec184562020-11-06 13:50:31 -080098 debug_assert_eq!(hashes_per_node, 128u64);
99 let max_level = log128_ceil(hash_pages).expect("file should not be empty") as u32;
100 let root_to_leaf_steps = (0..=max_level)
101 .rev()
102 .map(|x| {
103 let leaves_per_hash = hashes_per_node.pow(x);
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800104 let leaves_size_per_hash = CHUNK_SIZE * leaves_per_hash;
Victor Hsiehec184562020-11-06 13:50:31 -0800105 let leaves_size_per_node = leaves_size_per_hash * hashes_per_node;
106 let nodes_at_level = divide_roundup(file_size, leaves_size_per_node);
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800107 let level_size = nodes_at_level * CHUNK_SIZE;
Victor Hsiehec184562020-11-06 13:50:31 -0800108 let offset_in_level = (chunk_index / leaves_per_hash) * Sha256Hasher::HASH_SIZE as u64;
109 (level_size, offset_in_level)
110 })
111 .scan(0, |level_offset, (level_size, offset_in_level)| {
112 let this_level_offset = *level_offset;
113 *level_offset += level_size;
114 let global_hash_offset = this_level_offset + offset_in_level;
115 Some(global_hash_offset)
116 })
117 .map(|global_hash_offset| {
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800118 let chunk_index = global_hash_offset / CHUNK_SIZE;
119 let hash_offset_in_chunk = (global_hash_offset % CHUNK_SIZE) as usize;
Victor Hsiehec184562020-11-06 13:50:31 -0800120 (chunk_index, hash_offset_in_chunk)
121 })
122 .collect::<Vec<_>>();
123
124 Ok(root_to_leaf_steps.into_iter().rev().map(move |(chunk_index, hash_offset_in_chunk)| {
125 let mut merkle_chunk = [0u8; 4096];
126 let _ = merkle_tree.read_chunk(chunk_index, &mut merkle_chunk)?;
127 Ok((merkle_chunk, hash_offset_in_chunk))
128 }))
129}
130
Victor Hsiehf77c5722020-11-13 14:30:05 -0800131fn build_fsverity_formatted_digest(
132 root_hash: &HashBuffer,
133 file_size: u64,
134) -> Result<[u8; SIZE_OF_FSVERITY_FORMATTED_DIGEST_SHA256], CryptoError> {
135 let desc_hash = Sha256Hasher::new()?
136 .update(&1u8.to_le_bytes())? // version
137 .update(&1u8.to_le_bytes())? // hash_algorithm
138 .update(&12u8.to_le_bytes())? // log_blocksize
139 .update(&0u8.to_le_bytes())? // salt_size
140 .update(&0u32.to_le_bytes())? // sig_size
141 .update(&file_size.to_le_bytes())? // data_size
142 .update(root_hash)? // root_hash, first 32 bytes
143 .update(&[0u8; 32])? // root_hash, last 32 bytes
144 .update(&[0u8; 32])? // salt
145 .update(&[0u8; 32])? // reserved
146 .update(&[0u8; 32])? // reserved
147 .update(&[0u8; 32])? // reserved
148 .update(&[0u8; 32])? // reserved
149 .update(&[0u8; 16])? // reserved
150 .finalize()?;
151
152 let mut fsverity_digest = [0u8; SIZE_OF_FSVERITY_FORMATTED_DIGEST_SHA256];
153 fsverity_digest[0..8].copy_from_slice(b"FSVerity");
154 fsverity_digest[8..10].copy_from_slice(&1u16.to_le_bytes());
155 fsverity_digest[10..12].copy_from_slice(&32u16.to_le_bytes());
156 fsverity_digest[12..].copy_from_slice(&desc_hash);
157 Ok(fsverity_digest)
158}
159
160pub struct FsverityChunkedFileReader<F: ReadOnlyDataByChunk, M: ReadOnlyDataByChunk> {
161 chunked_file: F,
162 file_size: u64,
163 merkle_tree: M,
164 root_hash: HashBuffer,
165}
166
167impl<F: ReadOnlyDataByChunk, M: ReadOnlyDataByChunk> FsverityChunkedFileReader<F, M> {
Victor Hsiehf77c5722020-11-13 14:30:05 -0800168 pub fn new<A: Authenticator>(
169 authenticator: &A,
170 chunked_file: F,
171 file_size: u64,
172 sig: Vec<u8>,
173 merkle_tree: M,
174 ) -> Result<FsverityChunkedFileReader<F, M>, FsverityError> {
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800175 let mut buf = [0u8; CHUNK_SIZE as usize];
Victor Hsiehf77c5722020-11-13 14:30:05 -0800176 let size = merkle_tree.read_chunk(0, &mut buf)?;
177 if buf.len() != size {
178 return Err(FsverityError::InsufficientData(size));
179 }
180 let root_hash = Sha256Hasher::new()?.update(&buf[..])?.finalize()?;
181 let fsverity_digest = build_fsverity_formatted_digest(&root_hash, file_size)?;
182 let valid = authenticator.verify(&sig, &fsverity_digest)?;
183 if valid {
184 Ok(FsverityChunkedFileReader { chunked_file, file_size, merkle_tree, root_hash })
185 } else {
186 Err(FsverityError::BadSignature)
187 }
188 }
189}
190
191impl<F: ReadOnlyDataByChunk, M: ReadOnlyDataByChunk> ReadOnlyDataByChunk
192 for FsverityChunkedFileReader<F, M>
193{
194 fn read_chunk(&self, chunk_index: u64, buf: &mut [u8]) -> io::Result<usize> {
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800195 debug_assert!(buf.len() as u64 >= CHUNK_SIZE);
Victor Hsiehf77c5722020-11-13 14:30:05 -0800196 let size = self.chunked_file.read_chunk(chunk_index, buf)?;
197 let root_hash = verity_check(&buf[..size], chunk_index, self.file_size, &self.merkle_tree)
198 .map_err(|_| io::Error::from_raw_os_error(EIO))?;
199 if root_hash != self.root_hash {
200 Err(io::Error::from_raw_os_error(EIO))
201 } else {
202 Ok(size)
203 }
204 }
205}
206
Victor Hsiehec184562020-11-06 13:50:31 -0800207#[cfg(test)]
208mod tests {
209 use super::*;
Victor Hsiehf77c5722020-11-13 14:30:05 -0800210 use crate::auth::FakeAuthenticator;
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800211 use crate::reader::{ChunkedFileReader, ReadOnlyDataByChunk};
Victor Hsiehec184562020-11-06 13:50:31 -0800212 use anyhow::Result;
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800213 use std::fs::File;
214 use std::io::Read;
215
216 type LocalFsverityChunkedFileReader =
217 FsverityChunkedFileReader<ChunkedFileReader, ChunkedFileReader>;
Victor Hsiehec184562020-11-06 13:50:31 -0800218
219 fn total_chunk_number(file_size: u64) -> u64 {
220 (file_size + 4095) / 4096
221 }
222
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800223 // Returns a reader with fs-verity verification and the file size.
224 fn new_reader_with_fsverity(
225 content_path: &str,
226 merkle_tree_path: &str,
227 signature_path: &str,
228 ) -> Result<(LocalFsverityChunkedFileReader, u64)> {
229 let file_reader = ChunkedFileReader::new(File::open(content_path)?)?;
230 let file_size = file_reader.len();
231 let merkle_tree = ChunkedFileReader::new(File::open(merkle_tree_path)?)?;
232 let mut sig = Vec::new();
233 let _ = File::open(signature_path)?.read_to_end(&mut sig)?;
234 let authenticator = FakeAuthenticator::always_succeed();
235 Ok((
236 FsverityChunkedFileReader::new(
237 &authenticator,
238 file_reader,
239 file_size,
240 sig,
241 merkle_tree,
242 )?,
243 file_size,
244 ))
245 }
246
Victor Hsiehec184562020-11-06 13:50:31 -0800247 #[test]
248 fn fsverity_verify_full_read_4k() -> Result<()> {
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800249 let (file_reader, file_size) = new_reader_with_fsverity(
250 "testdata/input.4k",
251 "testdata/input.4k.merkle_dump",
252 "testdata/input.4k.fsv_sig",
Victor Hsiehf77c5722020-11-13 14:30:05 -0800253 )?;
Victor Hsiehec184562020-11-06 13:50:31 -0800254
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800255 for i in 0..total_chunk_number(file_size) {
Victor Hsiehf77c5722020-11-13 14:30:05 -0800256 let mut buf = [0u8; 4096];
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800257 assert!(file_reader.read_chunk(i, &mut buf[..]).is_ok());
Victor Hsiehec184562020-11-06 13:50:31 -0800258 }
259 Ok(())
260 }
261
262 #[test]
263 fn fsverity_verify_full_read_4k1() -> Result<()> {
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800264 let (file_reader, file_size) = new_reader_with_fsverity(
265 "testdata/input.4k1",
266 "testdata/input.4k1.merkle_dump",
267 "testdata/input.4k1.fsv_sig",
Victor Hsiehf77c5722020-11-13 14:30:05 -0800268 )?;
Victor Hsiehec184562020-11-06 13:50:31 -0800269
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800270 for i in 0..total_chunk_number(file_size) {
Victor Hsiehf77c5722020-11-13 14:30:05 -0800271 let mut buf = [0u8; 4096];
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800272 assert!(file_reader.read_chunk(i, &mut buf[..]).is_ok());
Victor Hsiehec184562020-11-06 13:50:31 -0800273 }
274 Ok(())
275 }
276
277 #[test]
278 fn fsverity_verify_full_read_4m() -> Result<()> {
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800279 let (file_reader, file_size) = new_reader_with_fsverity(
280 "testdata/input.4m",
281 "testdata/input.4m.merkle_dump",
282 "testdata/input.4m.fsv_sig",
Victor Hsiehf77c5722020-11-13 14:30:05 -0800283 )?;
Victor Hsiehec184562020-11-06 13:50:31 -0800284
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800285 for i in 0..total_chunk_number(file_size) {
Victor Hsiehf77c5722020-11-13 14:30:05 -0800286 let mut buf = [0u8; 4096];
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800287 assert!(file_reader.read_chunk(i, &mut buf[..]).is_ok());
Victor Hsiehec184562020-11-06 13:50:31 -0800288 }
289 Ok(())
290 }
291
292 #[test]
293 fn fsverity_verify_bad_merkle_tree() -> Result<()> {
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800294 let (file_reader, _) = new_reader_with_fsverity(
295 "testdata/input.4m",
296 "testdata/input.4m.merkle_dump.bad", // First leaf node is corrupted.
297 "testdata/input.4m.fsv_sig",
Victor Hsiehf77c5722020-11-13 14:30:05 -0800298 )?;
Victor Hsiehec184562020-11-06 13:50:31 -0800299
300 // A lowest broken node (a 4K chunk that contains 128 sha256 hashes) will fail the read
301 // failure of the underlying chunks, but not before or after.
302 let mut buf = [0u8; 4096];
303 let num_hashes = 4096 / 32;
304 let last_index = num_hashes;
305 for i in 0..last_index {
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800306 assert!(file_reader.read_chunk(i, &mut buf[..]).is_err());
Victor Hsiehec184562020-11-06 13:50:31 -0800307 }
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800308 assert!(file_reader.read_chunk(last_index, &mut buf[..]).is_ok());
Victor Hsiehf77c5722020-11-13 14:30:05 -0800309 Ok(())
310 }
311
312 #[test]
313 fn invalid_signature() -> Result<()> {
314 let authenticator = FakeAuthenticator::always_fail();
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800315 let file_reader = ChunkedFileReader::new(File::open("testdata/input.4m")?)?;
316 let file_size = file_reader.len();
317 let merkle_tree = ChunkedFileReader::new(File::open("testdata/input.4m.merkle_dump")?)?;
Victor Hsiehf77c5722020-11-13 14:30:05 -0800318 let sig = include_bytes!("../testdata/input.4m.fsv_sig").to_vec();
319 assert!(FsverityChunkedFileReader::new(
320 &authenticator,
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800321 file_reader,
322 file_size,
Victor Hsiehf77c5722020-11-13 14:30:05 -0800323 sig,
324 merkle_tree
325 )
326 .is_err());
Victor Hsiehec184562020-11-06 13:50:31 -0800327 Ok(())
328 }
329}