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