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