Authenticate file w/ fs-verity digest in hex
An input directory is configured by a build manifest file. With this
change, authfs starts to use the fs-verity digest in the manifest to
authenticate the corresponding file.
Although we're not using it, this change also makes it possible to
specify a known fs-verity digest to a remote file from the command line
(and deprecate the fake support of signature verification by the given
certificate).
Since we no longer derive fs-verity digest from the first chunk of a
Merkle tree, some relevant functions are deleted.
Bug: 206869687
Test: atest AuthFsHostTest ComposHostTestCases
Test: atest authfs_device_test_src_lib
Change-Id: Ibb5c246fb0d29aeafde187555f8d72c0282a65c7
diff --git a/authfs/src/fsverity/metadata/Android.bp b/authfs/src/fsverity/metadata/Android.bp
index b155224..af3729f 100644
--- a/authfs/src/fsverity/metadata/Android.bp
+++ b/authfs/src/fsverity/metadata/Android.bp
@@ -18,6 +18,7 @@
],
rustlibs: [
"libauthfs_fsverity_metadata_bindgen",
+ "libring",
],
edition: "2018",
apex_available: ["com.android.virt"],
diff --git a/authfs/src/fsverity/metadata/metadata.hpp b/authfs/src/fsverity/metadata/metadata.hpp
index 7bbd3da..f05740e 100644
--- a/authfs/src/fsverity/metadata/metadata.hpp
+++ b/authfs/src/fsverity/metadata/metadata.hpp
@@ -47,6 +47,9 @@
const uint64_t CHUNK_SIZE = 4096;
+// Give the macro value a name to export.
+const uint8_t FSVERITY_HASH_ALG_SHA256 = FS_VERITY_HASH_ALG_SHA256;
+
enum class FSVERITY_SIGNATURE_TYPE : __le32 {
NONE = 0,
PKCS7 = 1,
diff --git a/authfs/src/fsverity/metadata/metadata.rs b/authfs/src/fsverity/metadata/metadata.rs
index 073e044..8bc0617 100644
--- a/authfs/src/fsverity/metadata/metadata.rs
+++ b/authfs/src/fsverity/metadata/metadata.rs
@@ -16,18 +16,31 @@
//! Rust bindgen interface for FSVerity Metadata file (.fsv_meta)
use authfs_fsverity_metadata_bindgen::{
- fsverity_metadata_header, FSVERITY_SIGNATURE_TYPE_NONE, FSVERITY_SIGNATURE_TYPE_PKCS7,
- FSVERITY_SIGNATURE_TYPE_RAW,
+ fsverity_descriptor, fsverity_metadata_header, FSVERITY_HASH_ALG_SHA256,
+ FSVERITY_SIGNATURE_TYPE_NONE, FSVERITY_SIGNATURE_TYPE_PKCS7, FSVERITY_SIGNATURE_TYPE_RAW,
};
+use ring::digest::{Context, SHA256};
use std::cmp::min;
-use std::os::unix::fs::MetadataExt;
+use std::ffi::OsString;
+use std::fs::File;
+use std::io::{self, Read, Seek};
+use std::mem::{size_of, zeroed};
+use std::os::unix::fs::{FileExt, MetadataExt};
+use std::path::{Path, PathBuf};
+use std::slice::from_raw_parts_mut;
+
+/// Offset of `descriptor` in `struct fsverity_metadatata_header`.
+const DESCRIPTOR_OFFSET: usize = 4;
/// Structure for parsed metadata.
pub struct FSVerityMetadata {
/// Header for the metadata.
pub header: fsverity_metadata_header,
+ /// fs-verity digest of the file, with hash algorithm defined in the fs-verity descriptor.
+ pub digest: Vec<u8>,
+
/// Optional signature for the metadata.
pub signature: Option<Vec<u8>>,
@@ -54,14 +67,6 @@
}
}
-use std::ffi::OsString;
-use std::fs::File;
-use std::io::{self, Read, Seek};
-use std::mem::{size_of, zeroed};
-use std::os::unix::fs::FileExt;
-use std::path::{Path, PathBuf};
-use std::slice::from_raw_parts_mut;
-
/// Common block and page size in Linux.
pub const CHUNK_SIZE: u64 = authfs_fsverity_metadata_bindgen::CHUNK_SIZE;
@@ -75,23 +80,42 @@
/// Parse metadata from given file, and returns a structure for the metadata.
pub fn parse_fsverity_metadata(mut metadata_file: File) -> io::Result<Box<FSVerityMetadata>> {
- let header_size = size_of::<fsverity_metadata_header>();
+ let (header, digest) = {
+ // SAFETY: The header doesn't include any pointers.
+ let mut header: fsverity_metadata_header = unsafe { zeroed() };
- // SAFETY: the header doesn't include any pointers
- let header: fsverity_metadata_header = unsafe {
- let mut header: fsverity_metadata_header = zeroed();
- let buffer = from_raw_parts_mut(
- &mut header as *mut fsverity_metadata_header as *mut u8,
- header_size,
- );
- metadata_file.read_exact(buffer)?;
+ // SAFETY: fsverity_metadata_header is packed, so reading/write from/to the back_buffer
+ // won't overflow.
+ let back_buffer = unsafe {
+ from_raw_parts_mut(
+ &mut header as *mut fsverity_metadata_header as *mut u8,
+ size_of::<fsverity_metadata_header>(),
+ )
+ };
+ metadata_file.read_exact(back_buffer)?;
+
+ // Digest needs to be calculated with the raw value (without changing the endianness).
+ let digest = match header.descriptor.hash_algorithm {
+ FSVERITY_HASH_ALG_SHA256 => {
+ let mut context = Context::new(&SHA256);
+ context.update(
+ &back_buffer
+ [DESCRIPTOR_OFFSET..DESCRIPTOR_OFFSET + size_of::<fsverity_descriptor>()],
+ );
+ Ok(context.finish().as_ref().to_owned())
+ }
+ alg => Err(io::Error::new(
+ io::ErrorKind::Other,
+ format!("Unsupported hash algorithm {}, continue (likely failing soon)", alg),
+ )),
+ }?;
// TODO(inseob): This doesn't seem ideal. Maybe we can consider nom?
header.version = u32::from_le(header.version);
header.descriptor.data_size = u64::from_le(header.descriptor.data_size);
header.signature_type = u32::from_le(header.signature_type);
header.signature_size = u32::from_le(header.signature_size);
- header
+ (header, digest)
};
if header.version != 1 {
@@ -113,5 +137,5 @@
let merkle_tree_offset =
(metadata_file.stream_position()? + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE;
- Ok(Box::new(FSVerityMetadata { header, signature, metadata_file, merkle_tree_offset }))
+ Ok(Box::new(FSVerityMetadata { header, digest, signature, metadata_file, merkle_tree_offset }))
}