xref: /aosp_15_r20/external/avb/rust/src/descriptor/chain.rs (revision d289c2ba6de359471b23d594623b906876bc48a0)
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 //! Chain partition descriptors.
16 
17 use super::{
18     util::{parse_descriptor, split_slice, ValidateAndByteswap, ValidationFunc},
19     DescriptorResult,
20 };
21 use avb_bindgen::{
22     avb_chain_partition_descriptor_validate_and_byteswap, AvbChainPartitionDescriptor,
23 };
24 use core::str::from_utf8;
25 
26 /// `AvbChainPartitionDescriptorFlags`; see libavb docs for details.
27 pub use avb_bindgen::AvbChainPartitionDescriptorFlags as ChainPartitionDescriptorFlags;
28 
29 /// Wraps a chain partition descriptor stored in a vbmeta image.
30 #[derive(Debug, PartialEq, Eq)]
31 pub struct ChainPartitionDescriptor<'a> {
32     /// Chained partition rollback index location.
33     pub rollback_index_location: u32,
34 
35     /// Chained partition name.
36     ///
37     /// Most partition names in this library are passed as `&CStr`, but inside
38     /// descriptors the partition names are not nul-terminated making them
39     /// ineligible for use directly as `&CStr`. If `&CStr` is required, one
40     /// option is to allocate a nul-terminated copy of this string via
41     /// `CString::new()` which can then be converted to `&CStr`.
42     pub partition_name: &'a str,
43 
44     /// Chained partition public key.
45     pub public_key: &'a [u8],
46 
47     /// Flags.
48     pub flags: ChainPartitionDescriptorFlags,
49 }
50 
51 // SAFETY: `VALIDATE_AND_BYTESWAP_FUNC` is the correct libavb validator for this descriptor type.
52 unsafe impl ValidateAndByteswap for AvbChainPartitionDescriptor {
53     const VALIDATE_AND_BYTESWAP_FUNC: ValidationFunc<Self> =
54         avb_chain_partition_descriptor_validate_and_byteswap;
55 }
56 
57 impl<'a> ChainPartitionDescriptor<'a> {
58     /// Extract a `ChainPartitionDescriptor` from the given descriptor contents.
59     ///
60     /// # Arguments
61     /// * `contents`: descriptor contents, including the header, in raw big-endian format.
62     ///
63     /// # Returns
64     /// The new descriptor, or `DescriptorError` if the given `contents` aren't a valid
65     /// `AvbChainPartitionDescriptor`.
new(contents: &'a [u8]) -> DescriptorResult<Self>66     pub(super) fn new(contents: &'a [u8]) -> DescriptorResult<Self> {
67         // Descriptor contains: header + partition name + public key.
68         let descriptor = parse_descriptor::<AvbChainPartitionDescriptor>(contents)?;
69         let (partition_name, remainder) =
70             split_slice(descriptor.body, descriptor.header.partition_name_len)?;
71         let (public_key, _) = split_slice(remainder, descriptor.header.public_key_len)?;
72 
73         Ok(Self {
74             flags: ChainPartitionDescriptorFlags(descriptor.header.flags),
75             partition_name: from_utf8(partition_name)?,
76             rollback_index_location: descriptor.header.rollback_index_location,
77             public_key,
78         })
79     }
80 }
81 
82 #[cfg(test)]
83 mod tests {
84     use super::*;
85 
86     use crate::DescriptorError;
87     use std::{fs, mem::size_of};
88 
89     /// A valid descriptor that we've pre-generated as test data.
test_contents() -> Vec<u8>90     fn test_contents() -> Vec<u8> {
91         fs::read("testdata/chain_partition_descriptor.bin").unwrap()
92     }
93 
94     #[test]
new_chain_partition_descriptor_success()95     fn new_chain_partition_descriptor_success() {
96         assert!(ChainPartitionDescriptor::new(&test_contents()).is_ok());
97     }
98 
99     #[test]
new_chain_partition_descriptor_too_short_header_fails()100     fn new_chain_partition_descriptor_too_short_header_fails() {
101         let bad_header_size = size_of::<AvbChainPartitionDescriptor>() - 1;
102         assert_eq!(
103             ChainPartitionDescriptor::new(&test_contents()[..bad_header_size]).unwrap_err(),
104             DescriptorError::InvalidHeader
105         );
106     }
107 
108     #[test]
new_chain_partition_descriptor_too_short_contents_fails()109     fn new_chain_partition_descriptor_too_short_contents_fails() {
110         // The last byte is padding, so we need to drop 2 bytes to trigger an error.
111         let bad_contents_size = test_contents().len() - 2;
112         assert_eq!(
113             ChainPartitionDescriptor::new(&test_contents()[..bad_contents_size]).unwrap_err(),
114             DescriptorError::InvalidSize
115         );
116     }
117 }
118