1 // Copyright 2024, 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 //! Chain partition descriptors. 16 17 use super::{ 18 util::{parse_descriptor, split_slice, ValidateAndByteswap, ValidationFunc}, 19 DescriptorResult, 20 }; 21 use avb_bindgen::{ 22 avb_chain_partition_descriptor_validate_and_byteswap, AvbChainPartitionDescriptor, 23 }; 24 use core::str::from_utf8; 25 26 /// `AvbChainPartitionDescriptorFlags`; see libavb docs for details. 27 pub use avb_bindgen::AvbChainPartitionDescriptorFlags as ChainPartitionDescriptorFlags; 28 29 /// Wraps a chain partition descriptor stored in a vbmeta image. 30 #[derive(Debug, PartialEq, Eq)] 31 pub struct ChainPartitionDescriptor<'a> { 32 /// Chained partition rollback index location. 33 pub rollback_index_location: u32, 34 35 /// Chained partition name. 36 /// 37 /// Most partition names in this library are passed as `&CStr`, but inside 38 /// descriptors the partition names are not nul-terminated making them 39 /// ineligible for use directly as `&CStr`. If `&CStr` is required, one 40 /// option is to allocate a nul-terminated copy of this string via 41 /// `CString::new()` which can then be converted to `&CStr`. 42 pub partition_name: &'a str, 43 44 /// Chained partition public key. 45 pub public_key: &'a [u8], 46 47 /// Flags. 48 pub flags: ChainPartitionDescriptorFlags, 49 } 50 51 // SAFETY: `VALIDATE_AND_BYTESWAP_FUNC` is the correct libavb validator for this descriptor type. 52 unsafe impl ValidateAndByteswap for AvbChainPartitionDescriptor { 53 const VALIDATE_AND_BYTESWAP_FUNC: ValidationFunc<Self> = 54 avb_chain_partition_descriptor_validate_and_byteswap; 55 } 56 57 impl<'a> ChainPartitionDescriptor<'a> { 58 /// Extract a `ChainPartitionDescriptor` from the given descriptor contents. 59 /// 60 /// # Arguments 61 /// * `contents`: descriptor contents, including the header, in raw big-endian format. 62 /// 63 /// # Returns 64 /// The new descriptor, or `DescriptorError` if the given `contents` aren't a valid 65 /// `AvbChainPartitionDescriptor`. new(contents: &'a [u8]) -> DescriptorResult<Self>66 pub(super) fn new(contents: &'a [u8]) -> DescriptorResult<Self> { 67 // Descriptor contains: header + partition name + public key. 68 let descriptor = parse_descriptor::<AvbChainPartitionDescriptor>(contents)?; 69 let (partition_name, remainder) = 70 split_slice(descriptor.body, descriptor.header.partition_name_len)?; 71 let (public_key, _) = split_slice(remainder, descriptor.header.public_key_len)?; 72 73 Ok(Self { 74 flags: ChainPartitionDescriptorFlags(descriptor.header.flags), 75 partition_name: from_utf8(partition_name)?, 76 rollback_index_location: descriptor.header.rollback_index_location, 77 public_key, 78 }) 79 } 80 } 81 82 #[cfg(test)] 83 mod tests { 84 use super::*; 85 86 use crate::DescriptorError; 87 use std::{fs, mem::size_of}; 88 89 /// A valid descriptor that we've pre-generated as test data. test_contents() -> Vec<u8>90 fn test_contents() -> Vec<u8> { 91 fs::read("testdata/chain_partition_descriptor.bin").unwrap() 92 } 93 94 #[test] new_chain_partition_descriptor_success()95 fn new_chain_partition_descriptor_success() { 96 assert!(ChainPartitionDescriptor::new(&test_contents()).is_ok()); 97 } 98 99 #[test] new_chain_partition_descriptor_too_short_header_fails()100 fn new_chain_partition_descriptor_too_short_header_fails() { 101 let bad_header_size = size_of::<AvbChainPartitionDescriptor>() - 1; 102 assert_eq!( 103 ChainPartitionDescriptor::new(&test_contents()[..bad_header_size]).unwrap_err(), 104 DescriptorError::InvalidHeader 105 ); 106 } 107 108 #[test] new_chain_partition_descriptor_too_short_contents_fails()109 fn new_chain_partition_descriptor_too_short_contents_fails() { 110 // The last byte is padding, so we need to drop 2 bytes to trigger an error. 111 let bad_contents_size = test_contents().len() - 2; 112 assert_eq!( 113 ChainPartitionDescriptor::new(&test_contents()[..bad_contents_size]).unwrap_err(), 114 DescriptorError::InvalidSize 115 ); 116 } 117 } 118