xref: /aosp_15_r20/external/avb/rust/src/descriptor/hash.rs (revision d289c2ba6de359471b23d594623b906876bc48a0)
1 // Copyright 2023, 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 //! Hash descriptors.
16 
17 use super::{
18     util::{parse_descriptor, split_slice, ValidateAndByteswap, ValidationFunc},
19     DescriptorResult,
20 };
21 use avb_bindgen::{avb_hash_descriptor_validate_and_byteswap, AvbHashDescriptor};
22 use core::{ffi::CStr, str::from_utf8};
23 
24 /// `AvbHashDescriptorFlags`; see libavb docs for details.
25 pub use avb_bindgen::AvbHashDescriptorFlags as HashDescriptorFlags;
26 
27 /// Wraps a Hash descriptor stored in a vbmeta image.
28 #[derive(Debug, PartialEq, Eq)]
29 pub struct HashDescriptor<'a> {
30     /// The size of the hashed image.
31     pub image_size: u64,
32 
33     /// Hash algorithm name.
34     pub hash_algorithm: &'a str,
35 
36     /// Flags.
37     pub flags: HashDescriptorFlags,
38 
39     /// Partition name.
40     ///
41     /// Most partition names in this library are passed as `&CStr`, but inside
42     /// descriptors the partition names are not nul-terminated making them
43     /// ineligible for use directly as `&CStr`. If `&CStr` is required, one
44     /// option is to allocate a nul-terminated copy of this string via
45     /// `CString::new()` which can then be converted to `&CStr`.
46     pub partition_name: &'a str,
47 
48     /// Salt used to hash the image.
49     pub salt: &'a [u8],
50 
51     /// Image hash digest.
52     pub digest: &'a [u8],
53 }
54 
55 // SAFETY: `VALIDATE_AND_BYTESWAP_FUNC` is the correct libavb validator for this descriptor type.
56 unsafe impl ValidateAndByteswap for AvbHashDescriptor {
57     const VALIDATE_AND_BYTESWAP_FUNC: ValidationFunc<Self> =
58         avb_hash_descriptor_validate_and_byteswap;
59 }
60 
61 impl<'a> HashDescriptor<'a> {
62     /// Extract a `HashDescriptor` from the given descriptor contents.
63     ///
64     /// # Arguments
65     /// * `contents`: descriptor contents, including the header, in raw big-endian format.
66     ///
67     /// # Returns
68     /// The new descriptor, or `DescriptorError` if the given `contents` aren't a valid
69     /// `AvbHashDescriptor`.
new(contents: &'a [u8]) -> DescriptorResult<Self>70     pub(super) fn new(contents: &'a [u8]) -> DescriptorResult<Self> {
71         // Descriptor contains: header + name + salt + digest.
72         let descriptor = parse_descriptor::<AvbHashDescriptor>(contents)?;
73         let (partition_name, remainder) =
74             split_slice(descriptor.body, descriptor.header.partition_name_len)?;
75         let (salt, remainder) = split_slice(remainder, descriptor.header.salt_len)?;
76         let (digest, _) = split_slice(remainder, descriptor.header.digest_len)?;
77 
78         // Extract the hash algorithm from the original raw header since the temporary
79         // byte-swapped header doesn't live past this function.
80         // The hash algorithm is a nul-terminated UTF-8 string which is identical in the raw
81         // and byteswapped headers.
82         let hash_algorithm =
83             CStr::from_bytes_until_nul(&descriptor.raw_header.hash_algorithm)?.to_str()?;
84 
85         Ok(Self {
86             image_size: descriptor.header.image_size,
87             hash_algorithm,
88             flags: HashDescriptorFlags(descriptor.header.flags),
89             partition_name: from_utf8(partition_name)?,
90             salt,
91             digest,
92         })
93     }
94 }
95 
96 #[cfg(test)]
97 mod tests {
98     use super::*;
99 
100     use crate::DescriptorError;
101     use std::{fs, mem::size_of};
102 
103     /// A valid descriptor that we've pre-generated as test data.
test_contents() -> Vec<u8>104     fn test_contents() -> Vec<u8> {
105         fs::read("testdata/hash_descriptor.bin").unwrap()
106     }
107 
108     #[test]
new_hash_descriptor_success()109     fn new_hash_descriptor_success() {
110         assert!(HashDescriptor::new(&test_contents()).is_ok());
111     }
112 
113     #[test]
new_hash_descriptor_too_short_header_fails()114     fn new_hash_descriptor_too_short_header_fails() {
115         let bad_header_size = size_of::<AvbHashDescriptor>() - 1;
116         assert_eq!(
117             HashDescriptor::new(&test_contents()[..bad_header_size]).unwrap_err(),
118             DescriptorError::InvalidHeader
119         );
120     }
121 
122     #[test]
new_hash_descriptor_too_short_contents_fails()123     fn new_hash_descriptor_too_short_contents_fails() {
124         // The last byte is padding, so we need to drop 2 bytes to trigger an error.
125         let bad_contents_size = test_contents().len() - 2;
126         assert_eq!(
127             HashDescriptor::new(&test_contents()[..bad_contents_size]).unwrap_err(),
128             DescriptorError::InvalidSize
129         );
130     }
131 }
132