Alice Wang | f275286 | 2023-01-18 11:51:25 +0000 | [diff] [blame^] | 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 | //! Structs and functions relating to the descriptors. |
| 16 | |
| 17 | #![warn(unsafe_op_in_unsafe_fn)] |
| 18 | |
| 19 | use crate::error::{AvbIOError, AvbSlotVerifyError}; |
| 20 | use crate::partition::PartitionName; |
| 21 | use crate::utils::{self, is_not_null, to_nonnull, to_usize, usize_checked_add}; |
| 22 | use avb_bindgen::{ |
| 23 | avb_descriptor_foreach, avb_hash_descriptor_validate_and_byteswap, AvbDescriptor, |
| 24 | AvbHashDescriptor, AvbVBMetaData, AVB_SHA256_DIGEST_SIZE, |
| 25 | }; |
| 26 | use core::{ |
| 27 | ffi::c_void, |
| 28 | mem::{size_of, MaybeUninit}, |
| 29 | ops::Range, |
| 30 | slice, |
| 31 | }; |
| 32 | use tinyvec::ArrayVec; |
| 33 | |
| 34 | const DIGEST_SIZE: usize = AVB_SHA256_DIGEST_SIZE as usize; |
| 35 | |
| 36 | /// `HashDescriptors` can have maximum one `HashDescriptor` per known partition. |
| 37 | #[derive(Default)] |
| 38 | pub(crate) struct HashDescriptors( |
| 39 | ArrayVec<[HashDescriptor; PartitionName::NUM_OF_KNOWN_PARTITIONS]>, |
| 40 | ); |
| 41 | |
| 42 | impl 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. |
| 100 | unsafe 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. |
| 114 | unsafe 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)] |
| 133 | pub(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 | |
| 140 | impl 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. |
| 159 | struct AvbHashDescriptorWrap(AvbHashDescriptor); |
| 160 | |
| 161 | impl 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 | } |