• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Hashtree descriptors.
16 
17 use super::{
18     util::{parse_descriptor, split_slice, ValidateAndByteswap, ValidationFunc},
19     DescriptorResult,
20 };
21 use avb_bindgen::{avb_hashtree_descriptor_validate_and_byteswap, AvbHashtreeDescriptor};
22 use core::{ffi::CStr, str::from_utf8};
23 
24 /// `AvbHashtreeDescriptorFlags`; see libavb docs for details.
25 pub use avb_bindgen::AvbHashtreeDescriptorFlags as HashtreeDescriptorFlags;
26 
27 /// Wraps a Hashtree descriptor stored in a vbmeta image.
28 #[derive(Debug, PartialEq, Eq)]
29 pub struct HashtreeDescriptor<'a> {
30     /// DM-Verity version.
31     pub dm_verity_version: u32,
32 
33     /// Hashed image size.
34     pub image_size: u64,
35 
36     /// Offset to the root block of the hash tree.
37     pub tree_offset: u64,
38 
39     /// Hash tree size.
40     pub tree_size: u64,
41 
42     /// Data block size in bytes.
43     pub data_block_size: u32,
44 
45     /// Hash block size in bytes.
46     pub hash_block_size: u32,
47 
48     /// Number of forward error correction roots.
49     pub fec_num_roots: u32,
50 
51     /// Offset to the forward error correction data.
52     pub fec_offset: u64,
53 
54     /// Forward error correction data size.
55     pub fec_size: u64,
56 
57     /// Hash algorithm name.
58     pub hash_algorithm: &'a str,
59 
60     /// Flags.
61     pub flags: HashtreeDescriptorFlags,
62 
63     /// Partition name.
64     pub partition_name: &'a str,
65 
66     /// Salt used to hash the image.
67     pub salt: &'a [u8],
68 
69     /// Image root hash digest.
70     pub root_digest: &'a [u8],
71 }
72 
73 // SAFETY: `VALIDATE_AND_BYTESWAP_FUNC` is the correct libavb validator for this descriptor type.
74 unsafe impl ValidateAndByteswap for AvbHashtreeDescriptor {
75     const VALIDATE_AND_BYTESWAP_FUNC: ValidationFunc<Self> =
76         avb_hashtree_descriptor_validate_and_byteswap;
77 }
78 
79 impl<'a> HashtreeDescriptor<'a> {
80     /// Extract a `HashtreeDescriptor` from the given descriptor contents.
81     ///
82     /// # Arguments
83     /// * `contents`: descriptor contents, including the header, in raw big-endian format.
84     ///
85     /// # Returns
86     /// The new descriptor, or `DescriptorError` if the given `contents` aren't a valid
87     /// `AvbHashtreeDescriptor`.
new(contents: &'a [u8]) -> DescriptorResult<Self>88     pub(super) fn new(contents: &'a [u8]) -> DescriptorResult<Self> {
89         // Descriptor contains: header + name + salt + digest.
90         let descriptor = parse_descriptor::<AvbHashtreeDescriptor>(contents)?;
91         let (partition_name, remainder) =
92             split_slice(descriptor.body, descriptor.header.partition_name_len)?;
93         let (salt, remainder) = split_slice(remainder, descriptor.header.salt_len)?;
94         let (root_digest, _) = split_slice(remainder, descriptor.header.root_digest_len)?;
95 
96         // Extract the hash algorithm from the original raw header since the temporary
97         // byte-swapped header doesn't live past this function.
98         // The hash algorithm is a nul-terminated UTF-8 string which is identical in the raw
99         // and byteswapped headers.
100         let hash_algorithm =
101             CStr::from_bytes_until_nul(&descriptor.raw_header.hash_algorithm)?.to_str()?;
102 
103         Ok(Self {
104             dm_verity_version: descriptor.header.dm_verity_version,
105             image_size: descriptor.header.image_size,
106             tree_offset: descriptor.header.tree_offset,
107             tree_size: descriptor.header.tree_size,
108             data_block_size: descriptor.header.data_block_size,
109             hash_block_size: descriptor.header.hash_block_size,
110             fec_num_roots: descriptor.header.fec_num_roots,
111             fec_offset: descriptor.header.fec_offset,
112             fec_size: descriptor.header.fec_size,
113             hash_algorithm,
114             partition_name: from_utf8(partition_name)?,
115             salt,
116             root_digest,
117             flags: HashtreeDescriptorFlags(descriptor.header.flags),
118         })
119     }
120 }
121 
122 #[cfg(test)]
123 mod tests {
124     use super::*;
125 
126     use crate::DescriptorError;
127     use std::{fs, mem::size_of};
128 
129     /// A valid descriptor that we've pre-generated as test data.
test_contents() -> Vec<u8>130     fn test_contents() -> Vec<u8> {
131         fs::read("testdata/hashtree_descriptor.bin").unwrap()
132     }
133 
134     #[test]
new_hashtree_descriptor_success()135     fn new_hashtree_descriptor_success() {
136         assert!(HashtreeDescriptor::new(&test_contents()).is_ok());
137     }
138 
139     #[test]
new_hashtree_descriptor_too_short_header_fails()140     fn new_hashtree_descriptor_too_short_header_fails() {
141         let bad_header_size = size_of::<AvbHashtreeDescriptor>() - 1;
142         assert_eq!(
143             HashtreeDescriptor::new(&test_contents()[..bad_header_size]).unwrap_err(),
144             DescriptorError::InvalidHeader
145         );
146     }
147 
148     #[test]
new_hashtree_descriptor_too_short_contents_fails()149     fn new_hashtree_descriptor_too_short_contents_fails() {
150         // The last 2 bytes are padding, so we need to drop 3 bytes to trigger an error.
151         let bad_contents_size = test_contents().len() - 3;
152         assert_eq!(
153             HashtreeDescriptor::new(&test_contents()[..bad_contents_size]).unwrap_err(),
154             DescriptorError::InvalidSize
155         );
156     }
157 }
158