diff options
author | David Pursell <dpursell@google.com> | 2024-01-16 12:52:04 -0800 |
---|---|---|
committer | David Pursell <dpursell@google.com> | 2024-01-19 14:10:08 -0800 |
commit | 0f877b908e456067bdb937582f98503bfc61033f (patch) | |
tree | 941954b336f71ec9ab27eaf22702ba549d912dc7 | |
parent | 4a8e3b12ca51b32ccf98520ef5356c3533416e5c (diff) | |
download | avb-0f877b908e456067bdb937582f98503bfc61033f.tar.gz |
libavb_rs: add chain partition descriptors
Adds the ChainPartitionDescriptor struct which provides a safe API to
access chain partition descriptors.
Bug: b/290110273
Test: atest libavb_rs_test libavb_rs_uuid_test libavb_rs_unittest libavb_rs_uuid_unittest
Change-Id: I1fb08e71515a97192a1c0246b90e94b62d3e65af
-rw-r--r-- | rust/Android.bp | 65 | ||||
-rw-r--r-- | rust/src/descriptor/chain.rs | 117 | ||||
-rw-r--r-- | rust/src/descriptor/commandline.rs | 29 | ||||
-rw-r--r-- | rust/src/descriptor/hash.rs | 34 | ||||
-rw-r--r-- | rust/src/descriptor/hashtree.rs | 36 | ||||
-rw-r--r-- | rust/src/descriptor/mod.rs | 11 | ||||
-rw-r--r-- | rust/src/descriptor/property.rs | 24 | ||||
-rw-r--r-- | rust/src/lib.rs | 7 | ||||
-rw-r--r-- | rust/testdata/chain_partition_descriptor.bin | bin | 0 -> 2160 bytes | |||
-rw-r--r-- | rust/testdata/hash_descriptor.bin | bin | 0 -> 176 bytes | |||
-rw-r--r-- | rust/testdata/hashtree_descriptor.bin | bin | 0 -> 240 bytes | |||
-rw-r--r-- | rust/testdata/kernel_commandline_descriptor.bin | bin | 0 -> 64 bytes | |||
-rw-r--r-- | rust/testdata/property_descriptor.bin | bin | 0 -> 64 bytes | |||
-rw-r--r-- | rust/tests/verify_tests.rs | 78 |
14 files changed, 290 insertions, 111 deletions
diff --git a/rust/Android.bp b/rust/Android.bp index 27c5fce..d9fbd7d 100644 --- a/rust/Android.bp +++ b/rust/Android.bp @@ -225,13 +225,6 @@ rust_library { defaults: ["libavb_rs.defaults"], } -// Unit tests: std, no features. -rust_test { - name: "libavb_rs_unittest", - defaults: ["libavb_rs.defaults"], - test_suites: ["general-tests"], -} - // lib: std, UUID feature. rust_library { name: "libavb_rs_uuid", @@ -241,14 +234,39 @@ rust_library { ], } +// "libavb_rs.defaults" plus additional unit test defaults. +rust_defaults { + name: "libavb_rs_unittest.defaults", + defaults: ["libavb_rs.defaults"], + data: [":libavb_rs_example_descriptors"], + test_suites: ["general-tests"], +} + +// Unit tests: std, no features. +rust_test { + name: "libavb_rs_unittest", + defaults: ["libavb_rs_unittest.defaults"], +} + // Unit tests: std, UUID feature. rust_test { name: "libavb_rs_uuid_unittest", defaults: [ - "libavb_rs.defaults", + "libavb_rs_unittest.defaults", "libavb_rs.uuid.defaults", ], - test_suites: ["general-tests"], +} + +// Example descriptors in binary format. +filegroup { + name: "libavb_rs_example_descriptors", + srcs: [ + "testdata/chain_partition_descriptor.bin", + "testdata/hash_descriptor.bin", + "testdata/hashtree_descriptor.bin", + "testdata/kernel_commandline_descriptor.bin", + "testdata/property_descriptor.bin", + ], } // Integration test defaults. @@ -257,12 +275,15 @@ rust_defaults { srcs: ["tests/tests.rs"], data: [ ":avb_testkey_rsa4096_pub_bin", + ":avb_testkey_rsa8192_pub_bin", ":avbrs_test_image", ":avbrs_test_image_with_vbmeta_footer", ":avbrs_test_image_with_vbmeta_footer_for_boot", + ":avbrs_test_image_with_vbmeta_footer_for_test_part_2", ":avbrs_test_vbmeta", ":avbrs_test_vbmeta_2_parts", ":avbrs_test_vbmeta_persistent_digest", + ":avbrs_test_vbmeta_with_chained_partition", ":avbrs_test_vbmeta_with_commandline", ":avbrs_test_vbmeta_with_hashtree", ":avbrs_test_vbmeta_with_property", @@ -414,6 +435,20 @@ genrule { cmd: "$(location avbtool) make_vbmeta_image --kernel_cmdline test_cmdline_key=test_cmdline_value --key $(location :avb_testkey_rsa4096) --algorithm SHA512_RSA4096 --include_descriptors_from_image $(location :avbrs_test_image_descriptor) --output $(out)", } +// Standalone vbmeta image with chain descriptor to "test_part_2" with rollback +// index 4, signed by avb_testkey_rsa8192. +genrule { + name: "avbrs_test_vbmeta_with_chained_partition", + tools: ["avbtool"], + srcs: [ + ":avbrs_test_image_descriptor", + ":avb_testkey_rsa4096", + ":avb_testkey_rsa8192_pub_bin", + ], + out: ["test_vbmeta_with_chained_partition.img"], + cmd: "$(location avbtool) make_vbmeta_image --chain_partition test_part_2:4:$(location :avb_testkey_rsa8192_pub_bin) --key $(location :avb_testkey_rsa4096) --algorithm SHA512_RSA4096 --include_descriptors_from_image $(location :avbrs_test_image_descriptor) --output $(out)", +} + // Combined test image + signed vbmeta footer for "test_part". avb_add_hash_footer { name: "avbrs_test_image_with_vbmeta_footer", @@ -431,3 +466,15 @@ avb_add_hash_footer { private_key: ":avb_testkey_rsa4096", salt: "A001", } + +// Combined test image + signed vbmeta footer for "test_part_2" signed by +// avb_testkey_rsa8192 with rollback index = 7. +avb_add_hash_footer { + name: "avbrs_test_image_with_vbmeta_footer_for_test_part_2", + src: ":avbrs_test_image", + partition_name: "test_part_2", + private_key: ":avb_testkey_rsa8192", + algorithm: "SHA256_RSA8192", + salt: "A002", + rollback_index: 7, +} diff --git a/rust/src/descriptor/chain.rs b/rust/src/descriptor/chain.rs new file mode 100644 index 0000000..c579992 --- /dev/null +++ b/rust/src/descriptor/chain.rs @@ -0,0 +1,117 @@ +// Copyright 2024, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Chain partition descriptors. + +use super::{ + util::{parse_descriptor, split_slice, ValidateAndByteswap, ValidationFunc}, + DescriptorResult, +}; +use avb_bindgen::{ + avb_chain_partition_descriptor_validate_and_byteswap, AvbChainPartitionDescriptor, +}; +use core::str::from_utf8; + +/// `AvbChainPartitionDescriptorFlags`; see libavb docs for details. +pub use avb_bindgen::AvbChainPartitionDescriptorFlags as ChainPartitionDescriptorFlags; + +/// Wraps a chain partition descriptor stored in a vbmeta image. +#[derive(Debug, PartialEq, Eq)] +pub struct ChainPartitionDescriptor<'a> { + /// Chained partition rollback index location. + pub rollback_index_location: u32, + + /// Chained partition name. + /// + /// Most partition names in this library are passed as `&CStr`, but inside + /// descriptors the partition names are not nul-terminated making them + /// ineligible for use directly as `&CStr`. If `&CStr` is required, one + /// option is to allocate a nul-terminated copy of this string via + /// `CString::new()` which can then be converted to `&CStr`. + pub partition_name: &'a str, + + /// Chained partition public key. + pub public_key: &'a [u8], + + /// Flags. + pub flags: ChainPartitionDescriptorFlags, +} + +// SAFETY: `VALIDATE_AND_BYTESWAP_FUNC` is the correct libavb validator for this descriptor type. +unsafe impl ValidateAndByteswap for AvbChainPartitionDescriptor { + const VALIDATE_AND_BYTESWAP_FUNC: ValidationFunc<Self> = + avb_chain_partition_descriptor_validate_and_byteswap; +} + +impl<'a> ChainPartitionDescriptor<'a> { + /// Extract a `ChainPartitionDescriptor` from the given descriptor contents. + /// + /// # Arguments + /// * `contents`: descriptor contents, including the header, in raw big-endian format. + /// + /// # Returns + /// The new descriptor, or `DescriptorError` if the given `contents` aren't a valid + /// `AvbChainPartitionDescriptor`. + pub(super) fn new(contents: &'a [u8]) -> DescriptorResult<Self> { + // Descriptor contains: header + partition name + public key. + let descriptor = parse_descriptor::<AvbChainPartitionDescriptor>(contents)?; + let (partition_name, remainder) = + split_slice(descriptor.body, descriptor.header.partition_name_len)?; + let (public_key, _) = split_slice(remainder, descriptor.header.public_key_len)?; + + Ok(Self { + flags: ChainPartitionDescriptorFlags(descriptor.header.flags), + partition_name: from_utf8(partition_name)?, + rollback_index_location: descriptor.header.rollback_index_location, + public_key, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::DescriptorError; + use std::{fs, mem::size_of}; + + /// A valid descriptor that we've pre-generated as test data. + fn test_contents() -> Vec<u8> { + fs::read("testdata/chain_partition_descriptor.bin").unwrap() + } + + #[test] + fn new_chain_partition_descriptor_success() { + assert!(ChainPartitionDescriptor::new(&test_contents()).is_ok()); + } + + #[test] + fn new_chain_partition_descriptor_too_short_header_fails() { + let bad_header_size = size_of::<AvbChainPartitionDescriptor>() - 1; + assert_eq!( + ChainPartitionDescriptor::new(&test_contents()[..bad_header_size]).unwrap_err(), + DescriptorError::InvalidHeader + ); + } + + #[test] + fn new_chain_partition_descriptor_too_short_contents_fails() { + // The last byte is padding, so we need to drop 2 bytes to trigger an error. + let bad_contents_size = test_contents().len() - 2; + assert_eq!( + ChainPartitionDescriptor::new(&test_contents()[..bad_contents_size]).unwrap_err(), + DescriptorError::InvalidSize + ); + } +} diff --git a/rust/src/descriptor/commandline.rs b/rust/src/descriptor/commandline.rs index 4f45c3b..a9afbb4 100644 --- a/rust/src/descriptor/commandline.rs +++ b/rust/src/descriptor/commandline.rs @@ -69,31 +69,23 @@ mod tests { use super::*; use crate::DescriptorError; - use std::mem::size_of; + use std::{fs, mem::size_of}; - /// A valid kernel commandline descriptor in raw big-endian format. - const TEST_KERNEL_COMMANDLINE_DESCRIPTOR: &[u8] = &[ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x74, 0x65, 0x73, 0x74, 0x5F, 0x63, - 0x6D, 0x64, 0x6C, 0x69, 0x6E, 0x65, 0x5F, 0x6B, 0x65, 0x79, 0x3D, 0x74, 0x65, 0x73, 0x74, - 0x5F, 0x63, 0x6D, 0x64, 0x6C, 0x69, 0x6E, 0x65, 0x5F, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x00, - 0x00, 0x00, 0x00, 0x00, - ]; + /// A valid descriptor that we've pre-generated as test data. + fn test_contents() -> Vec<u8> { + fs::read("testdata/kernel_commandline_descriptor.bin").unwrap() + } #[test] fn new_commandline_descriptor_success() { - let descriptor = KernelCommandlineDescriptor::new(TEST_KERNEL_COMMANDLINE_DESCRIPTOR); - assert!(descriptor.is_ok()); + assert!(KernelCommandlineDescriptor::new(&test_contents()).is_ok()); } #[test] fn new_commandline_descriptor_too_short_header_fails() { let bad_header_size = size_of::<KernelCommandlineDescriptor>() - 1; assert_eq!( - KernelCommandlineDescriptor::new( - &TEST_KERNEL_COMMANDLINE_DESCRIPTOR[..bad_header_size] - ) - .unwrap_err(), + KernelCommandlineDescriptor::new(&test_contents()[..bad_header_size]).unwrap_err(), DescriptorError::InvalidHeader ); } @@ -101,12 +93,9 @@ mod tests { #[test] fn new_commandline_descriptor_too_short_contents_fails() { // The last 5 bytes are padding, so we need to drop 6 bytes to trigger an error. - let bad_contents_size = TEST_KERNEL_COMMANDLINE_DESCRIPTOR.len() - 6; + let bad_contents_size = test_contents().len() - 6; assert_eq!( - KernelCommandlineDescriptor::new( - &TEST_KERNEL_COMMANDLINE_DESCRIPTOR[..bad_contents_size] - ) - .unwrap_err(), + KernelCommandlineDescriptor::new(&test_contents()[..bad_contents_size]).unwrap_err(), DescriptorError::InvalidSize ); } diff --git a/rust/src/descriptor/hash.rs b/rust/src/descriptor/hash.rs index 6bd47c0..be7fb5b 100644 --- a/rust/src/descriptor/hash.rs +++ b/rust/src/descriptor/hash.rs @@ -98,39 +98,23 @@ mod tests { use super::*; use crate::DescriptorError; - use std::mem::size_of; + use std::{fs, mem::size_of}; - /// A valid hash descriptor in raw big-endian format. - /// - /// It's fairly complicated to generate a descriptor programmatically, but for the purposes - /// of these tests we don't care about the specific values, so this is just hardcoded. - /// Actually extracting data from a descriptor is checked in the integration tests. - const TEST_HASH_DESCRIPTOR: &[u8] = &[ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, - 0x74, 0x5F, 0x70, 0x61, 0x72, 0x74, 0x10, 0x00, 0x89, 0xE6, 0xFD, 0x31, 0x42, 0x91, 0x7B, - 0x8C, 0x34, 0xAC, 0x7D, 0x30, 0x89, 0x7A, 0x90, 0x7A, 0x71, 0xBD, 0x3B, 0xF5, 0xD9, 0xB3, - 0x9D, 0x00, 0xBF, 0x93, 0x8B, 0x41, 0xDC, 0xF3, 0xB8, 0x4F, 0x00, - ]; + /// A valid descriptor that we've pre-generated as test data. + fn test_contents() -> Vec<u8> { + fs::read("testdata/hash_descriptor.bin").unwrap() + } #[test] fn new_hash_descriptor_success() { - let descriptor = HashDescriptor::new(TEST_HASH_DESCRIPTOR); - assert!(descriptor.is_ok()); + assert!(HashDescriptor::new(&test_contents()).is_ok()); } #[test] fn new_hash_descriptor_too_short_header_fails() { let bad_header_size = size_of::<AvbHashDescriptor>() - 1; assert_eq!( - HashDescriptor::new(&TEST_HASH_DESCRIPTOR[..bad_header_size]).unwrap_err(), + HashDescriptor::new(&test_contents()[..bad_header_size]).unwrap_err(), DescriptorError::InvalidHeader ); } @@ -138,9 +122,9 @@ mod tests { #[test] fn new_hash_descriptor_too_short_contents_fails() { // The last byte is padding, so we need to drop 2 bytes to trigger an error. - let bad_contents_size = TEST_HASH_DESCRIPTOR.len() - 2; + let bad_contents_size = test_contents().len() - 2; assert_eq!( - HashDescriptor::new(&TEST_HASH_DESCRIPTOR[..bad_contents_size]).unwrap_err(), + HashDescriptor::new(&test_contents()[..bad_contents_size]).unwrap_err(), DescriptorError::InvalidSize ); } diff --git a/rust/src/descriptor/hashtree.rs b/rust/src/descriptor/hashtree.rs index a811cc5..cc2ee8c 100644 --- a/rust/src/descriptor/hashtree.rs +++ b/rust/src/descriptor/hashtree.rs @@ -124,39 +124,23 @@ mod tests { use super::*; use crate::DescriptorError; - use std::mem::size_of; - - /// A valid hashtree descriptor in raw big-endian format. - const TEST_HASHTREE_DESCRIPTOR: &[u8] = &[ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xE0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x68, 0x61, - 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x74, 0x65, 0x73, 0x74, 0x5F, 0x70, 0x61, 0x72, 0x74, 0x5F, 0x68, 0x61, 0x73, 0x68, 0x74, - 0x72, 0x65, 0x65, 0x99, 0xCE, 0xC4, 0x29, 0x60, 0x61, 0xCF, 0xBD, 0xE7, 0xD2, 0x17, 0xE2, - 0x88, 0x99, 0x05, 0x39, 0xAB, 0x70, 0x6D, 0xD0, 0x4C, 0x77, 0x76, 0xF8, 0xFD, 0xD2, 0x2B, - 0xF4, 0xC4, 0x7F, 0x31, 0x1B, 0x7B, 0x7B, 0xA5, 0xEF, 0x42, 0x8D, 0x7B, 0xE8, 0x00, 0x00, - ]; + use std::{fs, mem::size_of}; + + /// A valid descriptor that we've pre-generated as test data. + fn test_contents() -> Vec<u8> { + fs::read("testdata/hashtree_descriptor.bin").unwrap() + } #[test] fn new_hashtree_descriptor_success() { - let descriptor = HashtreeDescriptor::new(TEST_HASHTREE_DESCRIPTOR); - assert!(descriptor.is_ok()); + assert!(HashtreeDescriptor::new(&test_contents()).is_ok()); } #[test] fn new_hashtree_descriptor_too_short_header_fails() { let bad_header_size = size_of::<AvbHashtreeDescriptor>() - 1; assert_eq!( - HashtreeDescriptor::new(&TEST_HASHTREE_DESCRIPTOR[..bad_header_size]).unwrap_err(), + HashtreeDescriptor::new(&test_contents()[..bad_header_size]).unwrap_err(), DescriptorError::InvalidHeader ); } @@ -164,9 +148,9 @@ mod tests { #[test] fn new_hashtree_descriptor_too_short_contents_fails() { // The last 2 bytes are padding, so we need to drop 3 bytes to trigger an error. - let bad_contents_size = TEST_HASHTREE_DESCRIPTOR.len() - 3; + let bad_contents_size = test_contents().len() - 3; assert_eq!( - HashtreeDescriptor::new(&TEST_HASHTREE_DESCRIPTOR[..bad_contents_size]).unwrap_err(), + HashtreeDescriptor::new(&test_contents()[..bad_contents_size]).unwrap_err(), DescriptorError::InvalidSize ); } diff --git a/rust/src/descriptor/mod.rs b/rust/src/descriptor/mod.rs index 929ec2f..488401e 100644 --- a/rust/src/descriptor/mod.rs +++ b/rust/src/descriptor/mod.rs @@ -19,6 +19,7 @@ extern crate alloc; +mod chain; mod commandline; mod hash; mod hashtree; @@ -37,13 +38,13 @@ use core::{ str::Utf8Error, }; +pub use chain::{ChainPartitionDescriptor, ChainPartitionDescriptorFlags}; pub use commandline::{KernelCommandlineDescriptor, KernelCommandlineDescriptorFlags}; pub use hash::{HashDescriptor, HashDescriptorFlags}; pub use hashtree::{HashtreeDescriptor, HashtreeDescriptorFlags}; pub use property::PropertyDescriptor; /// A single descriptor. -// TODO(b/290110273): add support for full descriptor contents. #[derive(Debug, PartialEq, Eq)] pub enum Descriptor<'a> { /// Wraps `AvbPropertyDescriptor`. @@ -55,7 +56,7 @@ pub enum Descriptor<'a> { /// Wraps `AvbKernelCmdlineDescriptor`. KernelCommandline(KernelCommandlineDescriptor<'a>), /// Wraps `AvbChainPartitionDescriptor`. - ChainPartition(&'a [u8]), + ChainPartition(ChainPartitionDescriptor<'a>), /// Unknown descriptor type. Unknown(&'a [u8]), } @@ -140,9 +141,9 @@ impl<'a> Descriptor<'a> { Ok(AvbDescriptorTag::AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE) => Ok( Descriptor::KernelCommandline(KernelCommandlineDescriptor::new(contents)?), ), - Ok(AvbDescriptorTag::AVB_DESCRIPTOR_TAG_CHAIN_PARTITION) => { - Ok(Descriptor::ChainPartition(contents)) - } + Ok(AvbDescriptorTag::AVB_DESCRIPTOR_TAG_CHAIN_PARTITION) => Ok( + Descriptor::ChainPartition(ChainPartitionDescriptor::new(contents)?), + ), _ => Ok(Descriptor::Unknown(contents)), } } diff --git a/rust/src/descriptor/property.rs b/rust/src/descriptor/property.rs index ce24bea..c97f48f 100644 --- a/rust/src/descriptor/property.rs +++ b/rust/src/descriptor/property.rs @@ -74,28 +74,24 @@ impl<'a> PropertyDescriptor<'a> { #[cfg(test)] mod tests { use super::*; - use std::mem::size_of; - /// A valid property descriptor in raw big-endian format. - const TEST_PROPERTY_DESCRIPTOR: &[u8] = &[ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0F, 0x74, 0x65, 0x73, 0x74, 0x5F, 0x70, 0x72, 0x6F, 0x70, 0x5F, 0x6B, 0x65, 0x79, - 0x00, 0x74, 0x65, 0x73, 0x74, 0x5F, 0x70, 0x72, 0x6F, 0x70, 0x5F, 0x76, 0x61, 0x6C, 0x75, - 0x65, 0x00, 0x00, 0x00, - ]; + use std::{fs, mem::size_of}; + + /// A valid descriptor that we've pre-generated as test data. + fn test_contents() -> Vec<u8> { + fs::read("testdata/property_descriptor.bin").unwrap() + } #[test] fn new_property_descriptor_success() { - let descriptor = PropertyDescriptor::new(TEST_PROPERTY_DESCRIPTOR); - assert!(descriptor.is_ok()); + assert!(PropertyDescriptor::new(&test_contents()).is_ok()); } #[test] fn new_property_descriptor_too_short_header_fails() { let bad_header_size = size_of::<AvbPropertyDescriptor>() - 1; assert_eq!( - PropertyDescriptor::new(&TEST_PROPERTY_DESCRIPTOR[..bad_header_size]).unwrap_err(), + PropertyDescriptor::new(&test_contents()[..bad_header_size]).unwrap_err(), DescriptorError::InvalidHeader ); } @@ -103,9 +99,9 @@ mod tests { #[test] fn new_property_descriptor_too_short_contents_fails() { // The last 2 bytes are padding, so we need to drop 3 bytes to trigger an error. - let bad_contents_size = TEST_PROPERTY_DESCRIPTOR.len() - 3; + let bad_contents_size = test_contents().len() - 3; assert_eq!( - PropertyDescriptor::new(&TEST_PROPERTY_DESCRIPTOR[..bad_contents_size]).unwrap_err(), + PropertyDescriptor::new(&test_contents()[..bad_contents_size]).unwrap_err(), DescriptorError::InvalidSize ); } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index c41b80a..c96d4e5 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -32,9 +32,10 @@ mod ops; mod verify; pub use descriptor::{ - Descriptor, DescriptorError, DescriptorResult, HashDescriptor, HashDescriptorFlags, - HashtreeDescriptor, HashtreeDescriptorFlags, KernelCommandlineDescriptor, - KernelCommandlineDescriptorFlags, PropertyDescriptor, + ChainPartitionDescriptor, ChainPartitionDescriptorFlags, Descriptor, DescriptorError, + DescriptorResult, HashDescriptor, HashDescriptorFlags, HashtreeDescriptor, + HashtreeDescriptorFlags, KernelCommandlineDescriptor, KernelCommandlineDescriptorFlags, + PropertyDescriptor, }; pub use error::{ IoError, IoResult, SlotVerifyError, SlotVerifyNoDataResult, SlotVerifyResult, diff --git a/rust/testdata/chain_partition_descriptor.bin b/rust/testdata/chain_partition_descriptor.bin Binary files differnew file mode 100644 index 0000000..5331273 --- /dev/null +++ b/rust/testdata/chain_partition_descriptor.bin diff --git a/rust/testdata/hash_descriptor.bin b/rust/testdata/hash_descriptor.bin Binary files differnew file mode 100644 index 0000000..940732e --- /dev/null +++ b/rust/testdata/hash_descriptor.bin diff --git a/rust/testdata/hashtree_descriptor.bin b/rust/testdata/hashtree_descriptor.bin Binary files differnew file mode 100644 index 0000000..cf2b264 --- /dev/null +++ b/rust/testdata/hashtree_descriptor.bin diff --git a/rust/testdata/kernel_commandline_descriptor.bin b/rust/testdata/kernel_commandline_descriptor.bin Binary files differnew file mode 100644 index 0000000..f6f3fde --- /dev/null +++ b/rust/testdata/kernel_commandline_descriptor.bin diff --git a/rust/testdata/property_descriptor.bin b/rust/testdata/property_descriptor.bin Binary files differnew file mode 100644 index 0000000..ea78832 --- /dev/null +++ b/rust/testdata/property_descriptor.bin diff --git a/rust/tests/verify_tests.rs b/rust/tests/verify_tests.rs index 14d2b79..015bc9f 100644 --- a/rust/tests/verify_tests.rs +++ b/rust/tests/verify_tests.rs @@ -16,10 +16,10 @@ use crate::test_ops::TestOps; use avb::{ - slot_verify, Descriptor, HashDescriptor, HashDescriptorFlags, HashtreeDescriptor, - HashtreeDescriptorFlags, HashtreeErrorMode, IoError, KernelCommandlineDescriptor, - KernelCommandlineDescriptorFlags, PropertyDescriptor, SlotVerifyData, SlotVerifyError, - SlotVerifyFlags, SlotVerifyResult, + slot_verify, ChainPartitionDescriptor, ChainPartitionDescriptorFlags, Descriptor, + HashDescriptor, HashDescriptorFlags, HashtreeDescriptor, HashtreeDescriptorFlags, + HashtreeErrorMode, IoError, KernelCommandlineDescriptor, KernelCommandlineDescriptorFlags, + PropertyDescriptor, SlotVerifyData, SlotVerifyError, SlotVerifyFlags, SlotVerifyResult, }; use hex::decode; use std::{ffi::CString, fs}; @@ -37,10 +37,14 @@ const TEST_VBMETA_PERSISTENT_DIGEST_PATH: &str = "test_vbmeta_persistent_digest. const TEST_VBMETA_WITH_PROPERTY_PATH: &str = "test_vbmeta_with_property.img"; const TEST_VBMETA_WITH_HASHTREE_PATH: &str = "test_vbmeta_with_hashtree.img"; const TEST_VBMETA_WITH_COMMANDLINE_PATH: &str = "test_vbmeta_with_commandline.img"; +const TEST_VBMETA_WITH_CHAINED_PARTITION_PATH: &str = "test_vbmeta_with_chained_partition.img"; const TEST_IMAGE_WITH_VBMETA_FOOTER_PATH: &str = "avbrs_test_image_with_vbmeta_footer.img"; const TEST_IMAGE_WITH_VBMETA_FOOTER_FOR_BOOT_PATH: &str = "avbrs_test_image_with_vbmeta_footer_for_boot.img"; +const TEST_IMAGE_WITH_VBMETA_FOOTER_FOR_TEST_PART_2: &str = + "avbrs_test_image_with_vbmeta_footer_for_test_part_2.img"; const TEST_PUBLIC_KEY_PATH: &str = "data/testkey_rsa4096_pub.bin"; +const TEST_PUBLIC_KEY_RSA8192_PATH: &str = "data/testkey_rsa8192_pub.bin"; const TEST_PARTITION_NAME: &str = "test_part"; const TEST_PARTITION_SLOT_C_NAME: &str = "test_part_c"; const TEST_PARTITION_2_NAME: &str = "test_part_2"; @@ -50,6 +54,8 @@ const TEST_VBMETA_ROLLBACK_LOCATION: usize = 0; // Default value, we don't expli const TEST_PROPERTY_KEY: &str = "test_prop_key"; const TEST_PROPERTY_VALUE: &[u8] = b"test_prop_value"; const TEST_KERNEL_COMMANDLINE: &str = "test_cmdline_key=test_cmdline_value"; +const TEST_CHAINED_PARTITION_ROLLBACK_LOCATION: usize = 4; +const TEST_CHAINED_PARTITION_ROLLBACK_INDEX: u64 = 7; // Expected values determined by examining the vbmeta image with `avbtool info_image`. // Images can be found in <out>/soong/.intermediates/external/avb/rust/. @@ -92,8 +98,8 @@ fn test_ops_two_images_one_vbmeta() -> TestOps { ops } -/// Calls `slot_verify()` using standard args for `test_ops_two_images_one_vbmeta()` setup. -fn verify_two_images_one_vbmeta(ops: &mut TestOps) -> SlotVerifyResult<SlotVerifyData> { +/// Calls `slot_verify()` for both test partitions. +fn verify_two_images(ops: &mut TestOps) -> SlotVerifyResult<SlotVerifyData> { slot_verify( ops, &[ @@ -254,7 +260,7 @@ fn slotted_partition_passes_verification() { fn two_images_one_vbmeta_passes_verification() { let mut ops = test_ops_two_images_one_vbmeta(); - let result = verify_two_images_one_vbmeta(&mut ops); + let result = verify_two_images(&mut ops); // We should still only have 1 `VbmetaData` since we only used 1 vbmeta image, but it // signed 2 partitions so we should have 2 `PartitionData` objects. @@ -590,7 +596,7 @@ fn preloaded_image_verification_data_display() { fn two_images_one_vbmeta_verification_data_display() { let mut ops = test_ops_two_images_one_vbmeta(); - let result = verify_two_images_one_vbmeta(&mut ops); + let result = verify_two_images(&mut ops); let data = result.unwrap(); assert_eq!( @@ -637,7 +643,7 @@ fn one_image_gives_single_descriptor() { fn two_images_gives_two_descriptors() { let mut ops = test_ops_two_images_one_vbmeta(); - let result = verify_two_images_one_vbmeta(&mut ops); + let result = verify_two_images(&mut ops); let data = result.unwrap(); assert_eq!(data.vbmeta_data()[0].descriptors().unwrap().len(), 2); @@ -724,3 +730,57 @@ fn verify_kernel_commandline_descriptor() { }), ); } + +#[test] +fn verify_chain_partition_descriptor() { + let mut ops = test_ops_two_images_one_vbmeta(); + + // Set up the fake ops to contain: + // * the default test image in TEST_PARTITION_NAME + // * a signed test image with vbmeta footer in TEST_PARTITION_2_NAME + // * a vbmeta image in "vbmeta" which: + // * signs the default TEST_PARTITION_NAME image + // * chains to TEST_PARTITION_2_NAME + // + // Since this is an unusual configuration, it's simpler to just set it up manually here + // rather than try to adapt `verify_and_find_descriptor()` for this one case. + ops.add_partition( + "vbmeta", + fs::read(TEST_VBMETA_WITH_CHAINED_PARTITION_PATH).unwrap(), + ); + // Replace the chained partition with the combined image + vbmeta footer. + ops.add_partition( + TEST_PARTITION_2_NAME, + fs::read(TEST_IMAGE_WITH_VBMETA_FOOTER_FOR_TEST_PART_2).unwrap(), + ); + // Add the rollback index for the chained partition's location. + ops.rollbacks.insert( + TEST_CHAINED_PARTITION_ROLLBACK_LOCATION, + TEST_CHAINED_PARTITION_ROLLBACK_INDEX, + ); + + let result = verify_two_images(&mut ops); + + let data = result.unwrap(); + // We should have two vbmeta images - one from the "vbmeta" partition, the other embedded + // in the footer of TEST_PARTITION_2_NAME. + let vbmetas = data.vbmeta_data(); + assert_eq!(vbmetas.len(), 2); + // Search for the main vbmeta so we don't assume any particular order. + let main_vbmeta = vbmetas + .iter() + .find(|v| v.partition_name().to_str().unwrap() == "vbmeta") + .unwrap(); + + // The main vbmeta should contain the chain descriptor. + let expected = ChainPartitionDescriptor { + rollback_index_location: TEST_CHAINED_PARTITION_ROLLBACK_LOCATION as u32, + partition_name: TEST_PARTITION_2_NAME, + public_key: &fs::read(TEST_PUBLIC_KEY_RSA8192_PATH).unwrap(), + flags: ChainPartitionDescriptorFlags(0), + }; + assert!(main_vbmeta + .descriptors() + .unwrap() + .contains(&Descriptor::ChainPartition(expected))); +} |