xref: /aosp_15_r20/external/avb/rust/src/descriptor/property.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 //! Property descriptors.
16 
17 use super::{
18     util::{parse_descriptor, split_slice, ValidateAndByteswap, ValidationFunc},
19     DescriptorError, DescriptorResult,
20 };
21 use avb_bindgen::{avb_property_descriptor_validate_and_byteswap, AvbPropertyDescriptor};
22 use core::str::from_utf8;
23 
24 /// Checks that the first byte is nul and discards it.
25 /// Returns the remainder of `bytes` on success, or `DescriptorError` if the byte wasn't nul.
extract_nul(bytes: &[u8]) -> DescriptorResult<&[u8]>26 fn extract_nul(bytes: &[u8]) -> DescriptorResult<&[u8]> {
27     let (nul, remainder) = split_slice(bytes, 1)?;
28     match nul {
29         b"\0" => Ok(remainder),
30         _ => Err(DescriptorError::InvalidContents),
31     }
32 }
33 
34 /// Wraps an `AvbPropertyDescriptor` stored in a vbmeta image.
35 #[derive(Debug, PartialEq, Eq)]
36 pub struct PropertyDescriptor<'a> {
37     /// Key is always UTF-8.
38     pub key: &'a str,
39 
40     /// Value can be arbitrary bytes.
41     pub value: &'a [u8],
42 }
43 
44 // SAFETY: `VALIDATE_AND_BYTESWAP_FUNC` is the correct libavb validator for this descriptor type.
45 unsafe impl ValidateAndByteswap for AvbPropertyDescriptor {
46     const VALIDATE_AND_BYTESWAP_FUNC: ValidationFunc<Self> =
47         avb_property_descriptor_validate_and_byteswap;
48 }
49 
50 impl<'a> PropertyDescriptor<'a> {
51     /// Extract a `PropertyDescriptor` from the given descriptor contents.
52     ///
53     /// # Arguments
54     /// * `contents`: descriptor contents, including the header, in raw big-endian format.
55     ///
56     /// # Returns
57     /// The new descriptor, or `DescriptorError` if the given `contents` aren't a valid
58     /// `AvbPropertyDescriptor`.
new(contents: &'a [u8]) -> DescriptorResult<Self>59     pub(super) fn new(contents: &'a [u8]) -> DescriptorResult<Self> {
60         // Descriptor contains: header + key + nul + value + nul.
61         let descriptor = parse_descriptor::<AvbPropertyDescriptor>(contents)?;
62         let (key, remainder) = split_slice(descriptor.body, descriptor.header.key_num_bytes)?;
63         let remainder = extract_nul(remainder)?;
64         let (value, remainder) = split_slice(remainder, descriptor.header.value_num_bytes)?;
65         extract_nul(remainder)?;
66 
67         Ok(Self {
68             key: from_utf8(key)?,
69             value,
70         })
71     }
72 }
73 
74 #[cfg(test)]
75 mod tests {
76     use super::*;
77 
78     use std::{fs, mem::size_of};
79 
80     /// A valid descriptor that we've pre-generated as test data.
test_contents() -> Vec<u8>81     fn test_contents() -> Vec<u8> {
82         fs::read("testdata/property_descriptor.bin").unwrap()
83     }
84 
85     #[test]
new_property_descriptor_success()86     fn new_property_descriptor_success() {
87         assert!(PropertyDescriptor::new(&test_contents()).is_ok());
88     }
89 
90     #[test]
new_property_descriptor_too_short_header_fails()91     fn new_property_descriptor_too_short_header_fails() {
92         let bad_header_size = size_of::<AvbPropertyDescriptor>() - 1;
93         assert_eq!(
94             PropertyDescriptor::new(&test_contents()[..bad_header_size]).unwrap_err(),
95             DescriptorError::InvalidHeader
96         );
97     }
98 
99     #[test]
new_property_descriptor_too_short_contents_fails()100     fn new_property_descriptor_too_short_contents_fails() {
101         // The last 2 bytes are padding, so we need to drop 3 bytes to trigger an error.
102         let bad_contents_size = test_contents().len() - 3;
103         assert_eq!(
104             PropertyDescriptor::new(&test_contents()[..bad_contents_size]).unwrap_err(),
105             DescriptorError::InvalidSize
106         );
107     }
108 }
109