blob: b0598dec3d751ab7e064e3083e19320cf7cf48d0 [file] [log] [blame]
Alice Wangf2752862023-01-18 11:51:25 +00001// 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//! Structs and functions relating to the descriptors.
16
17#![warn(unsafe_op_in_unsafe_fn)]
18
19use crate::error::{AvbIOError, AvbSlotVerifyError};
20use crate::partition::PartitionName;
21use crate::utils::{self, is_not_null, to_nonnull, to_usize, usize_checked_add};
22use avb_bindgen::{
23 avb_descriptor_foreach, avb_hash_descriptor_validate_and_byteswap, AvbDescriptor,
24 AvbHashDescriptor, AvbVBMetaData, AVB_SHA256_DIGEST_SIZE,
25};
26use core::{
27 ffi::c_void,
28 mem::{size_of, MaybeUninit},
29 ops::Range,
30 slice,
31};
32use tinyvec::ArrayVec;
33
34const DIGEST_SIZE: usize = AVB_SHA256_DIGEST_SIZE as usize;
35
36/// `HashDescriptors` can have maximum one `HashDescriptor` per known partition.
37#[derive(Default)]
38pub(crate) struct HashDescriptors(
39 ArrayVec<[HashDescriptor; PartitionName::NUM_OF_KNOWN_PARTITIONS]>,
40);
41
42impl HashDescriptors {
43 /// Builds `HashDescriptors` from `AvbVBMetaData`.
44 /// Returns an error if the given `AvbVBMetaData` contains non-hash descriptor, hash
45 /// descriptor of unknown `PartitionName` or duplicated hash descriptors.
46 ///
47 /// # Safety
48 ///
49 /// Behavior is undefined if any of the following conditions are violated:
50 /// * `vbmeta.vbmeta_data` must be non-null and points to a valid VBMeta.
51 /// * `vbmeta.vbmeta_data` must be valid for reading `vbmeta.vbmeta_size` bytes.
52 pub(crate) unsafe fn from_vbmeta(vbmeta: AvbVBMetaData) -> Result<Self, AvbSlotVerifyError> {
53 is_not_null(vbmeta.vbmeta_data).map_err(|_| AvbSlotVerifyError::Io)?;
54 let mut descriptors = Self::default();
55 // SAFETY: It is safe as the raw pointer `vbmeta.vbmeta_data` is a non-null pointer and
56 // points to a valid VBMeta structure.
57 if !unsafe {
58 avb_descriptor_foreach(
59 vbmeta.vbmeta_data,
60 vbmeta.vbmeta_size,
61 Some(check_and_save_descriptor),
62 &mut descriptors as *mut _ as *mut c_void,
63 )
64 } {
65 return Err(AvbSlotVerifyError::InvalidMetadata);
66 }
67 Ok(descriptors)
68 }
69
70 fn push(&mut self, descriptor: HashDescriptor) -> utils::Result<()> {
71 if self.0.iter().any(|d| d.partition_name_eq(&descriptor)) {
72 return Err(AvbIOError::Io);
73 }
74 self.0.push(descriptor);
75 Ok(())
76 }
77
78 pub(crate) fn len(&self) -> usize {
79 self.0.len()
80 }
81
82 /// Finds the `HashDescriptor` for the given `PartitionName`.
83 /// Throws an error if no corresponding descriptor found.
84 pub(crate) fn find(
85 &self,
86 partition_name: PartitionName,
87 ) -> Result<&HashDescriptor, AvbSlotVerifyError> {
88 self.0
89 .iter()
90 .find(|d| d.partition_name == partition_name)
91 .ok_or(AvbSlotVerifyError::InvalidMetadata)
92 }
93}
94
95/// # Safety
96///
97/// Behavior is undefined if any of the following conditions are violated:
98/// * The `descriptor` pointer must be non-null and points to a valid `AvbDescriptor` struct.
99/// * The `user_data` pointer must be non-null and points to a valid `HashDescriptors` struct.
100unsafe extern "C" fn check_and_save_descriptor(
101 descriptor: *const AvbDescriptor,
102 user_data: *mut c_void,
103) -> bool {
104 // SAFETY: It is safe because the caller must ensure that the `descriptor` pointer and
105 // the `user_data` are non-null and valid.
106 unsafe { try_check_and_save_descriptor(descriptor, user_data).is_ok() }
107}
108
109/// # Safety
110///
111/// Behavior is undefined if any of the following conditions are violated:
112/// * The `descriptor` pointer must be non-null and points to a valid `AvbDescriptor` struct.
113/// * The `user_data` pointer must be non-null and points to a valid `HashDescriptors` struct.
114unsafe fn try_check_and_save_descriptor(
115 descriptor: *const AvbDescriptor,
116 user_data: *mut c_void,
117) -> utils::Result<()> {
118 is_not_null(descriptor)?;
119 // SAFETY: It is safe because the caller ensures that `descriptor` is a non-null pointer
120 // pointing to a valid struct.
121 let desc = unsafe { AvbHashDescriptorWrap::from_descriptor_ptr(descriptor)? };
122 // SAFETY: It is safe because the caller ensures that `descriptor` is a non-null pointer
123 // pointing to a valid struct.
124 let data = unsafe { slice::from_raw_parts(descriptor as *const u8, desc.len()?) };
125 let mut descriptors = to_nonnull(user_data as *mut HashDescriptors)?;
126 // SAFETY: It is safe because the caller ensures that `user_data` is a non-null pointer
127 // pointing to a valid struct.
128 let descriptors = unsafe { descriptors.as_mut() };
129 descriptors.push(HashDescriptor::new(&desc, data)?)
130}
131
132#[derive(Default)]
133pub(crate) struct HashDescriptor {
134 partition_name: PartitionName,
135 /// TODO(b/265897559): Pass this digest to DICE.
136 #[allow(dead_code)]
137 pub(crate) digest: [u8; DIGEST_SIZE],
138}
139
140impl HashDescriptor {
141 fn new(desc: &AvbHashDescriptorWrap, data: &[u8]) -> utils::Result<Self> {
142 let partition_name = data
143 .get(desc.partition_name_range()?)
144 .ok_or(AvbIOError::RangeOutsidePartition)?
145 .try_into()?;
146 let partition_digest =
147 data.get(desc.digest_range()?).ok_or(AvbIOError::RangeOutsidePartition)?;
148 let mut digest = [0u8; DIGEST_SIZE];
149 digest.copy_from_slice(partition_digest);
150 Ok(Self { partition_name, digest })
151 }
152
153 fn partition_name_eq(&self, other: &HashDescriptor) -> bool {
154 self.partition_name == other.partition_name
155 }
156}
157
158/// `AvbHashDescriptor` contains the metadata for the given descriptor.
159struct AvbHashDescriptorWrap(AvbHashDescriptor);
160
161impl AvbHashDescriptorWrap {
162 /// # Safety
163 ///
164 /// Behavior is undefined if any of the following conditions are violated:
165 /// * The `descriptor` pointer must be non-null and point to a valid `AvbDescriptor`.
166 unsafe fn from_descriptor_ptr(descriptor: *const AvbDescriptor) -> utils::Result<Self> {
167 is_not_null(descriptor)?;
168 // SAFETY: It is safe as the raw pointer `descriptor` is non-null and points to
169 // a valid `AvbDescriptor`.
170 let desc = unsafe {
171 let mut desc = MaybeUninit::uninit();
172 if !avb_hash_descriptor_validate_and_byteswap(
173 descriptor as *const AvbHashDescriptor,
174 desc.as_mut_ptr(),
175 ) {
176 return Err(AvbIOError::Io);
177 }
178 desc.assume_init()
179 };
180 Ok(Self(desc))
181 }
182
183 fn len(&self) -> utils::Result<usize> {
184 usize_checked_add(
185 size_of::<AvbDescriptor>(),
186 to_usize(self.0.parent_descriptor.num_bytes_following)?,
187 )
188 }
189
190 fn partition_name_end(&self) -> utils::Result<usize> {
191 usize_checked_add(size_of::<AvbHashDescriptor>(), to_usize(self.0.partition_name_len)?)
192 }
193
194 fn partition_name_range(&self) -> utils::Result<Range<usize>> {
195 let start = size_of::<AvbHashDescriptor>();
196 Ok(start..(self.partition_name_end()?))
197 }
198
199 fn digest_range(&self) -> utils::Result<Range<usize>> {
200 let start = usize_checked_add(self.partition_name_end()?, to_usize(self.0.salt_len)?)?;
201 let end = usize_checked_add(start, to_usize(self.0.digest_len)?)?;
202 Ok(start..end)
203 }
204}