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 //! Hash descriptors. 16 17 use super::{ 18 util::{parse_descriptor, split_slice, ValidateAndByteswap, ValidationFunc}, 19 DescriptorResult, 20 }; 21 use avb_bindgen::{avb_hash_descriptor_validate_and_byteswap, AvbHashDescriptor}; 22 use core::{ffi::CStr, str::from_utf8}; 23 24 /// `AvbHashDescriptorFlags`; see libavb docs for details. 25 pub use avb_bindgen::AvbHashDescriptorFlags as HashDescriptorFlags; 26 27 /// Wraps a Hash descriptor stored in a vbmeta image. 28 #[derive(Debug, PartialEq, Eq)] 29 pub struct HashDescriptor<'a> { 30 /// The size of the hashed image. 31 pub image_size: u64, 32 33 /// Hash algorithm name. 34 pub hash_algorithm: &'a str, 35 36 /// Flags. 37 pub flags: HashDescriptorFlags, 38 39 /// Partition name. 40 /// 41 /// Most partition names in this library are passed as `&CStr`, but inside 42 /// descriptors the partition names are not nul-terminated making them 43 /// ineligible for use directly as `&CStr`. If `&CStr` is required, one 44 /// option is to allocate a nul-terminated copy of this string via 45 /// `CString::new()` which can then be converted to `&CStr`. 46 pub partition_name: &'a str, 47 48 /// Salt used to hash the image. 49 pub salt: &'a [u8], 50 51 /// Image hash digest. 52 pub digest: &'a [u8], 53 } 54 55 // SAFETY: `VALIDATE_AND_BYTESWAP_FUNC` is the correct libavb validator for this descriptor type. 56 unsafe impl ValidateAndByteswap for AvbHashDescriptor { 57 const VALIDATE_AND_BYTESWAP_FUNC: ValidationFunc<Self> = 58 avb_hash_descriptor_validate_and_byteswap; 59 } 60 61 impl<'a> HashDescriptor<'a> { 62 /// Extract a `HashDescriptor` from the given descriptor contents. 63 /// 64 /// # Arguments 65 /// * `contents`: descriptor contents, including the header, in raw big-endian format. 66 /// 67 /// # Returns 68 /// The new descriptor, or `DescriptorError` if the given `contents` aren't a valid 69 /// `AvbHashDescriptor`. new(contents: &'a [u8]) -> DescriptorResult<Self>70 pub(super) fn new(contents: &'a [u8]) -> DescriptorResult<Self> { 71 // Descriptor contains: header + name + salt + digest. 72 let descriptor = parse_descriptor::<AvbHashDescriptor>(contents)?; 73 let (partition_name, remainder) = 74 split_slice(descriptor.body, descriptor.header.partition_name_len)?; 75 let (salt, remainder) = split_slice(remainder, descriptor.header.salt_len)?; 76 let (digest, _) = split_slice(remainder, descriptor.header.digest_len)?; 77 78 // Extract the hash algorithm from the original raw header since the temporary 79 // byte-swapped header doesn't live past this function. 80 // The hash algorithm is a nul-terminated UTF-8 string which is identical in the raw 81 // and byteswapped headers. 82 let hash_algorithm = 83 CStr::from_bytes_until_nul(&descriptor.raw_header.hash_algorithm)?.to_str()?; 84 85 Ok(Self { 86 image_size: descriptor.header.image_size, 87 hash_algorithm, 88 flags: HashDescriptorFlags(descriptor.header.flags), 89 partition_name: from_utf8(partition_name)?, 90 salt, 91 digest, 92 }) 93 } 94 } 95 96 #[cfg(test)] 97 mod tests { 98 use super::*; 99 100 use crate::DescriptorError; 101 use std::{fs, mem::size_of}; 102 103 /// A valid descriptor that we've pre-generated as test data. test_contents() -> Vec<u8>104 fn test_contents() -> Vec<u8> { 105 fs::read("testdata/hash_descriptor.bin").unwrap() 106 } 107 108 #[test] new_hash_descriptor_success()109 fn new_hash_descriptor_success() { 110 assert!(HashDescriptor::new(&test_contents()).is_ok()); 111 } 112 113 #[test] new_hash_descriptor_too_short_header_fails()114 fn new_hash_descriptor_too_short_header_fails() { 115 let bad_header_size = size_of::<AvbHashDescriptor>() - 1; 116 assert_eq!( 117 HashDescriptor::new(&test_contents()[..bad_header_size]).unwrap_err(), 118 DescriptorError::InvalidHeader 119 ); 120 } 121 122 #[test] new_hash_descriptor_too_short_contents_fails()123 fn new_hash_descriptor_too_short_contents_fails() { 124 // The last byte is padding, so we need to drop 2 bytes to trigger an error. 125 let bad_contents_size = test_contents().len() - 2; 126 assert_eq!( 127 HashDescriptor::new(&test_contents()[..bad_contents_size]).unwrap_err(), 128 DescriptorError::InvalidSize 129 ); 130 } 131 } 132