aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Pursell <dpursell@google.com>2024-01-16 12:52:04 -0800
committerDavid Pursell <dpursell@google.com>2024-01-19 14:10:08 -0800
commit0f877b908e456067bdb937582f98503bfc61033f (patch)
tree941954b336f71ec9ab27eaf22702ba549d912dc7
parent4a8e3b12ca51b32ccf98520ef5356c3533416e5c (diff)
downloadavb-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.bp65
-rw-r--r--rust/src/descriptor/chain.rs117
-rw-r--r--rust/src/descriptor/commandline.rs29
-rw-r--r--rust/src/descriptor/hash.rs34
-rw-r--r--rust/src/descriptor/hashtree.rs36
-rw-r--r--rust/src/descriptor/mod.rs11
-rw-r--r--rust/src/descriptor/property.rs24
-rw-r--r--rust/src/lib.rs7
-rw-r--r--rust/testdata/chain_partition_descriptor.binbin0 -> 2160 bytes
-rw-r--r--rust/testdata/hash_descriptor.binbin0 -> 176 bytes
-rw-r--r--rust/testdata/hashtree_descriptor.binbin0 -> 240 bytes
-rw-r--r--rust/testdata/kernel_commandline_descriptor.binbin0 -> 64 bytes
-rw-r--r--rust/testdata/property_descriptor.binbin0 -> 64 bytes
-rw-r--r--rust/tests/verify_tests.rs78
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
new file mode 100644
index 0000000..5331273
--- /dev/null
+++ b/rust/testdata/chain_partition_descriptor.bin
Binary files differ
diff --git a/rust/testdata/hash_descriptor.bin b/rust/testdata/hash_descriptor.bin
new file mode 100644
index 0000000..940732e
--- /dev/null
+++ b/rust/testdata/hash_descriptor.bin
Binary files differ
diff --git a/rust/testdata/hashtree_descriptor.bin b/rust/testdata/hashtree_descriptor.bin
new file mode 100644
index 0000000..cf2b264
--- /dev/null
+++ b/rust/testdata/hashtree_descriptor.bin
Binary files differ
diff --git a/rust/testdata/kernel_commandline_descriptor.bin b/rust/testdata/kernel_commandline_descriptor.bin
new file mode 100644
index 0000000..f6f3fde
--- /dev/null
+++ b/rust/testdata/kernel_commandline_descriptor.bin
Binary files differ
diff --git a/rust/testdata/property_descriptor.bin b/rust/testdata/property_descriptor.bin
new file mode 100644
index 0000000..ea78832
--- /dev/null
+++ b/rust/testdata/property_descriptor.bin
Binary files differ
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)));
+}