tests: Remove all binary test data files

In light of the recently discovered backdoor in the xz project where a
part of the malicious code was distributed in the test files, let's
remove all of our test files and generate them at runtime. While our
test files are very simple and consist mostly of zeros, someone who is
not very familiar with these binary formats would have a harder time
examining them and making sure they aren't malicious. With this change,
the data structures are now plainly visible in the test code.

The files generated at runtime are byte-for-byte identical to the test
files being removed. One can verify by comparing the sha512 checksums of
the files with the sha512 checksums hardcoded in the new test code.

Closes: #265

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
This commit is contained in:
Andrew Gunnerson
2024-04-27 14:15:08 -04:00
parent 17162df1e7
commit e22f9f5676
15 changed files with 617 additions and 181 deletions
+283 -105
View File
@@ -1,10 +1,10 @@
/*
* SPDX-FileCopyrightText: 2023 Andrew Gunnerson
* SPDX-FileCopyrightText: 2023-2024 Andrew Gunnerson
* SPDX-License-Identifier: GPL-3.0-only
*/
use std::{
io::{Cursor, Read, Seek, SeekFrom, Write},
io::{Cursor, Read, Seek, Write},
sync::atomic::AtomicBool,
};
@@ -14,8 +14,12 @@ use rsa::RsaPrivateKey;
use avbroot::{
self,
format::avb::{self, AppendedDescriptorMut, AppendedDescriptorRef},
stream::{self, SharedCursor},
format::avb::{
self, AlgorithmType, AppendedDescriptorMut, AppendedDescriptorRef,
ChainPartitionDescriptor, Descriptor, Footer, HashDescriptor, HashTreeDescriptor, Header,
KernelCmdlineDescriptor, PropertyDescriptor,
},
stream::SharedCursor,
};
fn get_test_key() -> RsaPrivateKey {
@@ -31,154 +35,328 @@ fn get_test_key() -> RsaPrivateKey {
RsaPrivateKey::from_pkcs8_encrypted_pem(data, passphrase.trim_end()).unwrap()
}
fn repeat_str(s: &str, max_len: usize) -> String {
assert!(!s.is_empty());
let mut result = s.repeat(max_len / s.len());
result.push_str(&s[..max_len % s.len()]);
result
}
fn repeat_array<const N: usize>(data: &[u8]) -> [u8; N] {
assert!(!data.is_empty());
let mut result = [0u8; N];
for i in 0..N / data.len() {
result[i * data.len()..][..data.len()].copy_from_slice(data);
}
let remain = N % data.len();
result[N - remain..].copy_from_slice(&data[..remain]);
result
}
#[test]
fn round_trip_root_image() {
let data = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/data/vbmeta_root.img",
));
let reader = Cursor::new(data);
let (mut header, footer, _) = avb::load_image(reader).unwrap();
assert_matches!(footer, None);
let mut header = Header {
required_libavb_version_major: 1,
required_libavb_version_minor: 0,
algorithm_type: AlgorithmType::Sha256Rsa4096,
hash: vec![], // autogenerated
signature: vec![], // autogenerated
public_key: vec![], // autogenerated
public_key_metadata: vec![
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f,
],
descriptors: vec![
Descriptor::Property(PropertyDescriptor {
key: "foobar".to_owned(),
value: b"Invalid UTF-8: \xFF".to_vec(),
}),
Descriptor::HashTree(HashTreeDescriptor {
dm_verity_version: 1,
image_size: 4096,
tree_offset: 0,
tree_size: 2048,
data_block_size: 4096,
hash_block_size: 4096,
fec_num_roots: 1,
fec_offset: 2048,
fec_size: 2048,
hash_algorithm: "sha512".to_owned(),
partition_name: "hashtreed_partition".to_owned(),
salt: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef].repeat(8),
root_digest: [0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10].repeat(8),
flags: 0,
reserved: repeat_array(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]),
}),
Descriptor::Hash(HashDescriptor {
image_size: 6,
hash_algorithm: "sha256".to_owned(),
partition_name: "hashed_partition".to_owned(),
salt: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef].repeat(4),
root_digest: [0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10].repeat(4),
flags: 0,
reserved: repeat_array(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]),
}),
Descriptor::KernelCmdline(KernelCmdlineDescriptor {
flags: 1,
cmdline: "foobar".to_owned(),
}),
Descriptor::ChainPartition(ChainPartitionDescriptor {
rollback_index_location: 1,
partition_name: "chained_partition".to_owned(),
public_key: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef].repeat(129),
flags: 0xfedcba98,
reserved: repeat_array(&[0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x98]),
}),
],
rollback_index: 1677974400,
flags: 0,
rollback_index_location: 0,
release_string: repeat_str("MaxLength", 48),
reserved: repeat_array(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]),
};
// Sign the header.
let key = get_test_key();
header.sign(&key).unwrap();
assert_eq!(header.verify().unwrap().unwrap(), key.to_public_key());
// Clear out the signature-related fields and re-sign.
header.hash.clear();
header.signature.clear();
header.public_key.clear();
header.sign(&key).unwrap();
// Write vbmeta structures.
let mut writer = Cursor::new(Vec::new());
avb::write_root_image(&mut writer, &header, 64).unwrap();
let new_data = writer.into_inner();
let data = writer.into_inner();
assert_eq!(data, new_data.as_slice());
// Verify checksum of the output.
assert_eq!(
ring::digest::digest(&ring::digest::SHA512, &data).as_ref(),
[
0xc4, 0xa5, 0xda, 0x3e, 0x09, 0xa2, 0xc8, 0x70, 0xcb, 0xf0, 0x96, 0x79, 0x0e, 0x1e,
0x80, 0xae, 0x5e, 0x37, 0x81, 0x27, 0x24, 0xc3, 0x6c, 0xa9, 0x42, 0x9e, 0x2c, 0xb1,
0x81, 0xad, 0xce, 0xee, 0x8d, 0x4f, 0x76, 0x45, 0x54, 0xc1, 0x31, 0x6a, 0xa7, 0x81,
0x5c, 0x59, 0xa8, 0xe8, 0x76, 0xab, 0xed, 0x5b, 0x07, 0x07, 0x38, 0xdd, 0x09, 0x86,
0x05, 0x39, 0x23, 0x2d, 0x7b, 0xcc, 0x57, 0x06,
],
);
// Parse the generated image.
let mut reader = Cursor::new(&data);
let (new_header, new_footer, new_image_size) = avb::load_image(&mut reader).unwrap();
assert_matches!(new_footer, None);
assert_eq!(new_header, header);
assert_eq!(new_image_size, data.len() as u64);
}
#[test]
fn round_trip_appended_hash_image() {
let data = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/data/vbmeta_appended_hash.img",
));
let mut reader = Cursor::new(data);
let cancel_signal = AtomicBool::new(false);
let (mut header, footer, image_size) = avb::load_image(&mut reader).unwrap();
let mut footer = footer.unwrap();
let key = get_test_key();
assert_eq!(header.verify().unwrap().unwrap(), key.to_public_key());
// Verify the digest.
match header.appended_descriptor().unwrap() {
AppendedDescriptorRef::HashTree(_) => panic!("Expected hash descriptor"),
AppendedDescriptorRef::Hash(d) => {
reader.rewind().unwrap();
d.verify(&mut reader, &cancel_signal).unwrap();
}
}
let image_size = 12288;
let raw_data = b"foobar";
let mut header = Header {
required_libavb_version_major: 1,
required_libavb_version_minor: 0,
algorithm_type: AlgorithmType::Sha256Rsa4096,
hash: vec![], // autogenerated
signature: vec![], // autogenerated
public_key: vec![], // autogenerated
public_key_metadata: vec![
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f,
],
descriptors: vec![
Descriptor::Property(PropertyDescriptor {
key: "foobar".to_owned(),
value: b"Invalid UTF-8: \xFF".to_vec(),
}),
Descriptor::Hash(HashDescriptor {
image_size: raw_data.len() as u64,
hash_algorithm: "sha256".to_owned(),
partition_name: "vbmeta_appended_hash".to_owned(),
salt: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef].repeat(4),
root_digest: vec![], // autogenerated
flags: 0,
reserved: repeat_array(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]),
}),
],
rollback_index: 1677974400,
flags: 0,
rollback_index_location: 0,
release_string: repeat_str("MaxLength", 48),
reserved: repeat_array(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]),
};
let mut footer = Footer {
version_major: 1,
version_minor: 0,
original_image_size: 0, // autogenerated
vbmeta_offset: 0, // autogenerated
vbmeta_size: 0, // autogenerated
reserved: repeat_array(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]),
};
let mut writer = Cursor::new(Vec::new());
let cancel_signal = AtomicBool::new(false);
// Copy the partition data.
reader.seek(SeekFrom::Start(0)).unwrap();
stream::copy_n(
&mut reader,
&mut writer,
footer.original_image_size,
&cancel_signal,
)
.unwrap();
// Write the raw partition data.
writer.write_all(raw_data).unwrap();
// Regenerate the digest.
// Regenerate the raw image digest.
match header.appended_descriptor_mut().unwrap() {
AppendedDescriptorMut::HashTree(_) => panic!("Expected hash descriptor"),
AppendedDescriptorMut::Hash(d) => {
d.root_digest.clear();
writer.rewind().unwrap();
d.update(&mut writer, &cancel_signal).unwrap();
}
}
// Clear out the signature-related fields and re-sign.
header.hash.clear();
header.signature.clear();
header.public_key.clear();
// Verify the raw image digest.
match header.appended_descriptor().unwrap() {
AppendedDescriptorRef::HashTree(_) => panic!("Expected hash descriptor"),
AppendedDescriptorRef::Hash(d) => {
writer.rewind().unwrap();
d.verify(&mut writer, &cancel_signal).unwrap();
}
}
// Sign the header.
let key = get_test_key();
header.sign(&key).unwrap();
assert_eq!(header.verify().unwrap().unwrap(), key.to_public_key());
// Write new vbmeta structures.
// Write vbmeta structures.
avb::write_appended_image(&mut writer, &header, &mut footer, image_size).unwrap();
let new_data = writer.into_inner();
let data = writer.into_inner();
assert_eq!(data, new_data.as_slice());
// Verify checksum of the output.
assert_eq!(
ring::digest::digest(&ring::digest::SHA512, &data).as_ref(),
[
0x09, 0x98, 0x0c, 0x9d, 0x11, 0x50, 0xde, 0xb1, 0x55, 0x3b, 0x00, 0x76, 0xbe, 0x25,
0xfd, 0xe6, 0x46, 0x22, 0xbd, 0x9a, 0x05, 0x86, 0xea, 0x07, 0x4d, 0x8f, 0x7b, 0x15,
0x36, 0x20, 0x0d, 0xf0, 0x7e, 0x96, 0xd2, 0x58, 0xde, 0xf2, 0xa6, 0x91, 0x6d, 0x01,
0x7b, 0x03, 0x96, 0x70, 0xf8, 0x3b, 0x76, 0x74, 0xf0, 0xbf, 0x47, 0xe0, 0xd2, 0xd4,
0x5d, 0xbf, 0xb7, 0x9c, 0xf5, 0xf8, 0xaf, 0x3c,
],
);
// Parse the generated image.
let mut reader = Cursor::new(&data);
let (new_header, new_footer, new_image_size) = avb::load_image(&mut reader).unwrap();
let new_footer = new_footer.unwrap();
assert_eq!(new_header, header);
assert_eq!(new_footer, footer);
assert_eq!(new_image_size, image_size);
}
#[test]
fn round_trip_appended_hash_tree_image() {
let data = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/data/vbmeta_appended_hash_tree.img",
));
let mut reader = SharedCursor::default();
reader.write_all(data).unwrap();
let cancel_signal = AtomicBool::new(false);
let (mut header, footer, image_size) = avb::load_image(&mut reader).unwrap();
let mut footer = footer.unwrap();
let key = get_test_key();
assert_eq!(header.verify().unwrap().unwrap(), key.to_public_key());
// Verify the hash tree and FEC data.
match header.appended_descriptor().unwrap() {
AppendedDescriptorRef::HashTree(d) => d.verify(&reader, &cancel_signal).unwrap(),
AppendedDescriptorRef::Hash(_) => panic!("Expected hash tree descriptor"),
}
let image_size = 28672;
let raw_data: [u8; 8192] = repeat_array(b"foobar");
let mut header = Header {
required_libavb_version_major: 1,
required_libavb_version_minor: 0,
algorithm_type: AlgorithmType::Sha256Rsa4096,
hash: vec![], // autogenerated
signature: vec![], // autogenerated
public_key: vec![], // autogenerated
public_key_metadata: vec![
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f,
],
descriptors: vec![
Descriptor::Property(PropertyDescriptor {
key: "foobar".to_owned(),
value: b"Invalid UTF-8: \xFF".to_vec(),
}),
Descriptor::HashTree(HashTreeDescriptor {
dm_verity_version: 1,
image_size: raw_data.len() as u64,
tree_offset: 0, // autogenerated
tree_size: 0, // autogenerated
data_block_size: 4096,
hash_block_size: 4096,
fec_num_roots: 2,
fec_offset: 0, // autogenerated
fec_size: 0, // autogenerated
hash_algorithm: "sha256".to_owned(),
partition_name: "vbmeta_appended_hash_tree".to_owned(),
salt: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef].repeat(4),
root_digest: vec![], // autogenerated
flags: 0,
reserved: repeat_array(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]),
}),
],
rollback_index: 1677974400,
flags: 0,
rollback_index_location: 0,
release_string: repeat_str("MaxLength", 48),
reserved: repeat_array(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]),
};
let mut footer = Footer {
version_major: 1,
version_minor: 0,
original_image_size: 0, // autogenerated
vbmeta_offset: 0, // autogenerated
vbmeta_size: 0, // autogenerated
reserved: repeat_array(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]),
};
let mut writer = SharedCursor::default();
let cancel_signal = AtomicBool::new(false);
// Copy the partition data, excluding the hash tree and FEC data.
reader.seek(SeekFrom::Start(0)).unwrap();
stream::copy_n(
&mut reader,
&mut writer,
footer.original_image_size,
&cancel_signal,
)
.unwrap();
// Write the raw partition data.
writer.write_all(&raw_data).unwrap();
// Regenerate the hash tree and FEC data.
// Generate and write the hash tree and FEC data.
match header.appended_descriptor_mut().unwrap() {
AppendedDescriptorMut::HashTree(d) => {
d.root_digest.clear();
d.tree_offset = 0;
d.tree_size = 0;
d.fec_offset = 0;
d.fec_size = 0;
d.update(&writer, &writer, None, &cancel_signal).unwrap();
}
AppendedDescriptorMut::Hash(_) => panic!("Expected hash tree descriptor"),
}
// Clear out the signature-related fields and re-sign.
header.hash.clear();
header.signature.clear();
header.public_key.clear();
// Verify the hash tree and FEC data.
match header.appended_descriptor_mut().unwrap() {
AppendedDescriptorMut::HashTree(d) => {
d.verify(&writer, &cancel_signal).unwrap();
}
AppendedDescriptorMut::Hash(_) => panic!("Expected hash tree descriptor"),
}
// Sign the header.
let key = get_test_key();
header.sign(&key).unwrap();
assert_eq!(header.verify().unwrap().unwrap(), key.to_public_key());
// Write new vbmeta structures.
// Write vbmeta structures.
avb::write_appended_image(&mut writer, &header, &mut footer, image_size).unwrap();
let mut new_data = Vec::new();
let mut data = Vec::new();
writer.rewind().unwrap();
writer.read_to_end(&mut new_data).unwrap();
writer.read_to_end(&mut data).unwrap();
assert_eq!(data, new_data.as_slice());
// Verify checksum of the output.
assert_eq!(
ring::digest::digest(&ring::digest::SHA512, &data).as_ref(),
[
0xd6, 0x69, 0x19, 0x6a, 0x36, 0xc8, 0x1c, 0xe9, 0xc4, 0x85, 0xbe, 0xff, 0x43, 0xb1,
0x9f, 0xd4, 0x1d, 0x6c, 0xf9, 0xd2, 0xf3, 0xa6, 0x5f, 0x66, 0x41, 0xd5, 0xf3, 0xfd,
0x28, 0xdb, 0x14, 0x67, 0xc6, 0xa8, 0xef, 0xc4, 0xd4, 0x67, 0x6c, 0xb8, 0x66, 0xbb,
0x56, 0x5a, 0x4a, 0xf5, 0xd8, 0x92, 0x7c, 0x42, 0xbc, 0x47, 0xdb, 0x94, 0x38, 0x15,
0x4b, 0x2d, 0xd0, 0x28, 0x1f, 0xd1, 0x45, 0xa9,
],
);
// Parse the generated image.
let mut reader = Cursor::new(&data);
let (new_header, new_footer, new_image_size) = avb::load_image(&mut reader).unwrap();
let new_footer = new_footer.unwrap();
assert_eq!(new_header, header);
assert_eq!(new_footer, footer);
assert_eq!(new_image_size, image_size);
}
+284 -60
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Andrew Gunnerson
* SPDX-FileCopyrightText: 2023-2024 Andrew Gunnerson
* SPDX-License-Identifier: GPL-3.0-only
*/
@@ -7,7 +7,13 @@ use std::io::Cursor;
use avbroot::{
self,
format::bootimage::{BootImage, BootImageExt},
format::{
avb::{AlgorithmType, Descriptor, HashDescriptor, Header},
bootimage::{
self, BootImage, BootImageExt, BootImageV0Through2, BootImageV3Through4, RamdiskMeta,
V1Extra, V2Extra, V4Extra, VendorBootImageV3Through4, VendorV4Extra,
},
},
stream::{FromReader, ToWriter},
};
use pkcs8::DecodePrivateKey;
@@ -26,100 +32,318 @@ fn get_test_key() -> RsaPrivateKey {
RsaPrivateKey::from_pkcs8_encrypted_pem(data, passphrase.trim_end()).unwrap()
}
fn round_trip(data: &[u8], expected_version: u32) {
let reader = Cursor::new(data);
let mut image = BootImage::from_reader(reader).unwrap();
fn repeat(s: &str, max_len: usize) -> String {
assert!(!s.is_empty());
let mut result = s.repeat(max_len / s.len());
result.push_str(&s[..max_len % s.len()]);
result
}
fn round_trip(image: &BootImage, sha512: &[u8; 64], expected_version: u32) {
assert_eq!(image.header_version(), expected_version);
match &mut image {
BootImage::V3Through4(b) => {
let should_sign = b
.v4_extra
.as_ref()
.map_or(false, |v4| v4.signature.is_some());
let key = get_test_key();
let signed = b.sign(&key).unwrap();
assert_eq!(signed, should_sign);
}
_ => {}
}
let mut writer = Cursor::new(Vec::new());
image.to_writer(&mut writer).unwrap();
let new_data = writer.into_inner();
let data = writer.into_inner();
assert_eq!(data, new_data);
assert_eq!(
ring::digest::digest(&ring::digest::SHA512, &data).as_ref(),
sha512,
);
let reader = Cursor::new(data);
let new_image = BootImage::from_reader(reader).unwrap();
assert_eq!(&new_image, image);
}
#[test]
fn round_trip_v0() {
let data = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/data/boot_v0.img",
));
round_trip(data, 0);
let image = BootImage::V0Through2(BootImageV0Through2 {
kernel_addr: 0x01234567,
ramdisk_addr: 0x89abcdef,
second_addr: 0x02468ace,
tags_addr: 0x13579bdf,
page_size: 4096,
os_version: 0x76543210,
name: repeat("Name", 16),
cmdline: repeat("Cmdline", 512),
id: [
0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff, 0xffeeddcc, 0xbbaa9988, 0x77665544,
0x33221100,
],
extra_cmdline: repeat("ExtraCmdline", 1024),
kernel: b"kernel data".to_vec(),
ramdisk: b"ramdisk data".to_vec(),
second: b"second data".to_vec(),
v1_extra: None,
v2_extra: None,
});
let sha512 = [
0x23, 0x65, 0x0b, 0xfa, 0x7a, 0x09, 0x0a, 0xdf, 0xdd, 0x9a, 0x6c, 0x03, 0xfa, 0xc5, 0xe1,
0xfa, 0x27, 0x65, 0xa0, 0x94, 0xef, 0xa2, 0x0c, 0xc5, 0x3e, 0xd9, 0x67, 0x7d, 0x88, 0x7b,
0xb3, 0x48, 0x39, 0xab, 0x28, 0x77, 0x7b, 0x18, 0xec, 0x60, 0xe0, 0xb7, 0x0d, 0x15, 0x26,
0xb2, 0xd4, 0x27, 0x25, 0x92, 0x5c, 0x7b, 0x0b, 0x5c, 0xf7, 0xed, 0x27, 0x6c, 0x39, 0xb5,
0xb7, 0x44, 0xbb, 0xec,
];
round_trip(&image, &sha512, 0);
}
#[test]
fn round_trip_v1() {
let data = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/data/boot_v1.img",
));
round_trip(data, 1);
let image = BootImage::V0Through2(BootImageV0Through2 {
kernel_addr: 0x01234567,
ramdisk_addr: 0x89abcdef,
second_addr: 0x02468ace,
tags_addr: 0x13579bdf,
page_size: 4096,
os_version: 0x76543210,
name: repeat("Name", 16),
cmdline: repeat("Cmdline", 512),
id: [
0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff, 0xffeeddcc, 0xbbaa9988, 0x77665544,
0x33221100,
],
extra_cmdline: repeat("ExtraCmdline", 1024),
kernel: b"kernel data".to_vec(),
ramdisk: b"ramdisk data".to_vec(),
second: b"second data".to_vec(),
v1_extra: Some(V1Extra {
recovery_dtbo_offset: 0x0123456789abcdef,
recovery_dtbo: b"recovery_dtbo data".to_vec(),
}),
v2_extra: None,
});
let sha512 = [
0x37, 0x8e, 0xf1, 0xf0, 0xb8, 0x44, 0x0f, 0x9e, 0x16, 0xc0, 0x15, 0x98, 0xa2, 0xb5, 0x06,
0x63, 0x59, 0xf4, 0x91, 0xb6, 0x28, 0x03, 0xe6, 0xdc, 0xd2, 0x0d, 0xd7, 0x49, 0x33, 0x63,
0x91, 0xd4, 0xa8, 0x24, 0xff, 0xb0, 0x5f, 0x99, 0x2a, 0x9a, 0xb3, 0x66, 0x81, 0x41, 0x69,
0xb0, 0xbc, 0xe2, 0x5b, 0x33, 0x3f, 0x39, 0x6a, 0xa8, 0xbd, 0xe1, 0x15, 0x3e, 0x51, 0x5a,
0x2a, 0x9d, 0x23, 0x90,
];
round_trip(&image, &sha512, 1);
}
#[test]
fn round_trip_v2() {
let data = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/data/boot_v2.img",
));
round_trip(data, 2);
let image = BootImage::V0Through2(BootImageV0Through2 {
kernel_addr: 0x01234567,
ramdisk_addr: 0x89abcdef,
second_addr: 0x02468ace,
tags_addr: 0x13579bdf,
page_size: 4096,
os_version: 0x76543210,
name: repeat("Name", 16),
cmdline: repeat("Cmdline", 512),
id: [
0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff, 0xffeeddcc, 0xbbaa9988, 0x77665544,
0x33221100,
],
extra_cmdline: repeat("ExtraCmdline", 1024),
kernel: b"kernel data".to_vec(),
ramdisk: b"ramdisk data".to_vec(),
second: b"second data".to_vec(),
v1_extra: Some(V1Extra {
recovery_dtbo_offset: 0x0123456789abcdef,
recovery_dtbo: b"recovery_dtbo data".to_vec(),
}),
v2_extra: Some(V2Extra {
dtb_addr: 0xfedcba9876543210,
dtb: b"dtb data".to_vec(),
}),
});
let sha512 = [
0x04, 0x24, 0x5b, 0xb7, 0x07, 0x82, 0xa9, 0x08, 0x68, 0xb9, 0xc9, 0x65, 0x1f, 0x53, 0xd7,
0x6c, 0xcf, 0xf3, 0x48, 0x58, 0x9a, 0xd4, 0xb1, 0xf3, 0xd8, 0x6f, 0x95, 0x10, 0x70, 0x2f,
0x53, 0x30, 0x60, 0x60, 0xe4, 0x68, 0xd9, 0x84, 0xe8, 0x0a, 0xf3, 0x12, 0xb3, 0xa3, 0x1b,
0x06, 0x88, 0x2f, 0x5d, 0x34, 0x5a, 0xea, 0x8f, 0xbb, 0x54, 0x49, 0x3c, 0xc1, 0x9c, 0xc6,
0x24, 0x80, 0x03, 0xde,
];
round_trip(&image, &sha512, 2);
}
#[test]
fn round_trip_v3() {
let data = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/data/boot_v3.img",
));
round_trip(data, 3);
let image = BootImage::V3Through4(BootImageV3Through4 {
os_version: 0x01234567,
reserved: [0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff],
cmdline: repeat("Cmdline", 1536),
v4_extra: None,
kernel: b"kernel data".to_vec(),
ramdisk: b"ramdisk data".to_vec(),
});
let sha512 = [
0x30, 0xea, 0x77, 0x0a, 0xd3, 0x24, 0x6a, 0x3f, 0xf8, 0xdf, 0xe6, 0xd9, 0x5a, 0xa1, 0xd3,
0xa4, 0x3b, 0x8a, 0x13, 0x39, 0x5e, 0x58, 0x24, 0x3e, 0x71, 0x31, 0x78, 0xa1, 0x2c, 0xad,
0x1d, 0xca, 0x24, 0x12, 0xf5, 0xfb, 0x2c, 0x48, 0xa5, 0x3d, 0xc0, 0x38, 0x55, 0xb6, 0xfd,
0xd3, 0x30, 0xe0, 0x69, 0x11, 0x28, 0xd7, 0x29, 0xda, 0x2e, 0x5a, 0x49, 0x5c, 0x39, 0x1d,
0xb9, 0xdb, 0x53, 0xe1,
];
round_trip(&image, &sha512, 3);
}
#[test]
fn round_trip_v4() {
let data = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/data/boot_v4.img",
));
round_trip(data, 4);
let image = BootImage::V3Through4(BootImageV3Through4 {
os_version: 0x01234567,
reserved: [0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff],
cmdline: repeat("Cmdline", 1536),
v4_extra: Some(V4Extra { signature: None }),
kernel: b"kernel data".to_vec(),
ramdisk: b"ramdisk data".to_vec(),
});
let sha512 = [
0xa8, 0x1d, 0x2b, 0x78, 0x22, 0x45, 0x0b, 0xe7, 0xc2, 0x3a, 0xd8, 0xda, 0x95, 0x49, 0x77,
0x18, 0xd0, 0x7b, 0x9b, 0x7f, 0xc7, 0xf6, 0x48, 0xb4, 0x2d, 0x85, 0x6d, 0xe3, 0x5a, 0xa3,
0x24, 0xb6, 0x94, 0x56, 0xb9, 0x07, 0x84, 0xdb, 0x50, 0x01, 0xca, 0x6c, 0x86, 0x26, 0x32,
0x79, 0x0c, 0xc5, 0x70, 0xcf, 0xcc, 0x7f, 0xc3, 0x5b, 0x96, 0x56, 0x23, 0x5c, 0xd0, 0x50,
0xb0, 0x98, 0xdb, 0x4a,
];
round_trip(&image, &sha512, 4);
}
#[test]
fn round_trip_v4_vts() {
let data = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/data/boot_v4_vts.img",
));
round_trip(data, 4);
let mut header = Header {
required_libavb_version_major: 1,
required_libavb_version_minor: 0,
algorithm_type: AlgorithmType::Sha256Rsa4096,
hash: vec![], // autogenerated
signature: vec![], // autogenerated
public_key: vec![], // autogenerated
public_key_metadata: vec![],
descriptors: vec![Descriptor::Hash(HashDescriptor {
image_size: 12288,
hash_algorithm: "sha256".to_owned(),
partition_name: "boot".to_owned(),
salt: vec![0x64, 0x30, 0x30, 0x64, 0x66, 0x30, 0x30, 0x64],
root_digest: vec![
0xab, 0xd5, 0x48, 0x3e, 0x11, 0xe7, 0x94, 0x0c, 0xb9, 0xbf, 0x38, 0x75, 0x87, 0xa4,
0xa1, 0x65, 0x99, 0x81, 0xa1, 0xb8, 0x39, 0x62, 0xb7, 0xc1, 0xfa, 0xf1, 0xb0, 0xcd,
0x63, 0x07, 0xd4, 0x49,
],
flags: 0,
reserved: [0; 60],
})],
rollback_index: 0,
flags: 0,
rollback_index_location: 0,
release_string: "avbtool 1.2.0".to_owned(),
reserved: [0; 80],
};
let key = get_test_key();
header.sign(&key).unwrap();
assert_eq!(header.verify().unwrap().unwrap(), key.to_public_key());
let image = BootImage::V3Through4(BootImageV3Through4 {
os_version: 0x01234567,
reserved: [0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff],
cmdline: repeat("Cmdline", 1536),
v4_extra: Some(V4Extra {
signature: Some(header),
}),
kernel: b"kernel data".to_vec(),
ramdisk: b"ramdisk data".to_vec(),
});
let sha512 = [
0x19, 0x47, 0x15, 0x3c, 0x1f, 0x62, 0x84, 0xee, 0xbc, 0x16, 0x9e, 0x5a, 0xf2, 0x45, 0x2a,
0xf7, 0x40, 0xc2, 0x18, 0x7f, 0x23, 0xb2, 0xa4, 0x20, 0x10, 0xdf, 0xb1, 0x5c, 0xf2, 0x7f,
0x6f, 0x79, 0x22, 0x1d, 0x29, 0x27, 0x78, 0xea, 0xb3, 0x9e, 0x1f, 0xfe, 0xeb, 0xc8, 0x9f,
0xe6, 0xef, 0xce, 0xa2, 0x28, 0x0b, 0x05, 0x1d, 0x52, 0xff, 0xab, 0xd4, 0x6f, 0x87, 0x11,
0xd9, 0xb6, 0x5f, 0x2e,
];
round_trip(&image, &sha512, 4);
}
#[test]
fn round_trip_vendor_v3() {
let data = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/data/vendor_v3.img",
));
round_trip(data, 3);
let image = BootImage::VendorV3Through4(VendorBootImageV3Through4 {
page_size: 4096,
kernel_addr: 0x01234567,
ramdisk_addr: 0x89abcdef,
cmdline: repeat("Cmdline", 2048),
tags_addr: 0xfedcba98,
name: repeat("Name", 16),
dtb: b"dtb data".to_vec(),
dtb_addr: 0x76543210,
ramdisks: vec![b"ramdisk data".to_vec()],
v4_extra: None,
});
let sha512 = [
0x17, 0x18, 0xb9, 0x67, 0x4c, 0x82, 0x71, 0x98, 0x6a, 0x8a, 0xb8, 0x85, 0x3c, 0x77, 0x9e,
0x27, 0xeb, 0xce, 0x2a, 0x23, 0x04, 0x63, 0x7c, 0x94, 0xd4, 0xad, 0x1f, 0x3c, 0xee, 0x7e,
0x41, 0x8b, 0xa8, 0xd9, 0x35, 0xec, 0xf2, 0xc1, 0x52, 0x3a, 0xd9, 0x5b, 0xbe, 0x63, 0xe8,
0x00, 0xd2, 0x23, 0x4e, 0x37, 0x76, 0x31, 0x5a, 0xfc, 0x63, 0x43, 0x32, 0x34, 0x30, 0xf6,
0x3e, 0x2e, 0x3e, 0x66,
];
round_trip(&image, &sha512, 3);
}
#[test]
fn round_trip_vendor_v4() {
let data = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/data/vendor_v4.img",
));
round_trip(data, 4);
let board_id = [
0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff, 0xffeeddcc, 0xbbaa9988, 0x77665544,
0x33221100, 0x004488cc, 0x115599dd, 0x2266aaee, 0x3377bbff, 0xffbb7733, 0xeeaa6622,
0xdd995511, 0xcc884400,
];
let image = BootImage::VendorV3Through4(VendorBootImageV3Through4 {
page_size: 2048,
kernel_addr: 0x01234567,
ramdisk_addr: 0x89abcdef,
cmdline: repeat("Cmdline", 2048),
tags_addr: 0xfedcba98,
name: repeat("Name", 16),
dtb: b"dtb data".to_vec(),
dtb_addr: 0x76543210,
ramdisks: vec![
b"ramdisk 0 data".to_vec(),
b"ramdisk 1 data".to_vec(),
b"ramdisk 2 data".to_vec(),
b"ramdisk 3 data".to_vec(),
],
v4_extra: Some(VendorV4Extra {
ramdisk_metas: vec![
RamdiskMeta {
ramdisk_type: bootimage::VENDOR_RAMDISK_TYPE_NONE,
ramdisk_name: repeat("None", 32),
board_id,
},
RamdiskMeta {
ramdisk_type: bootimage::VENDOR_RAMDISK_TYPE_PLATFORM,
ramdisk_name: repeat("Platform", 32),
board_id,
},
RamdiskMeta {
ramdisk_type: bootimage::VENDOR_RAMDISK_TYPE_RECOVERY,
ramdisk_name: repeat("Recovery", 32),
board_id,
},
RamdiskMeta {
ramdisk_type: bootimage::VENDOR_RAMDISK_TYPE_DLKM,
ramdisk_name: repeat("Dlkm", 32),
board_id,
},
],
bootconfig: "bootconfig data".to_owned(),
}),
});
let sha512 = [
0x0e, 0x3f, 0x86, 0x9e, 0xad, 0x98, 0xbb, 0x53, 0xc7, 0xc4, 0x3f, 0xb8, 0xc6, 0x06, 0xdc,
0xb2, 0xe5, 0x47, 0x66, 0xe3, 0xaf, 0x2c, 0xa4, 0x91, 0x8d, 0x4b, 0xc5, 0x70, 0x1e, 0x51,
0x19, 0x23, 0x7c, 0xab, 0x40, 0x24, 0x95, 0xef, 0xc8, 0x65, 0xdb, 0x5f, 0x0a, 0x41, 0x93,
0xff, 0x6c, 0x22, 0xb4, 0x9a, 0xe2, 0x20, 0xc1, 0x95, 0xa0, 0x3c, 0xc2, 0x13, 0xdb, 0xc8,
0x24, 0x33, 0x77, 0x75,
];
round_trip(&image, &sha512, 4);
}
+50 -8
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Andrew Gunnerson
* SPDX-FileCopyrightText: 2023-2024 Andrew Gunnerson
* SPDX-License-Identifier: GPL-3.0-only
*/
@@ -7,22 +7,64 @@ use std::io::{self, Cursor};
use avbroot::{
self,
format::cpio::{CpioEntryType, CpioReader, CpioWriter},
format::cpio::{CpioEntry, CpioEntryData, CpioEntryType, CpioReader, CpioWriter},
util,
};
fn generate_archive() -> Vec<u8> {
let writer = Cursor::new(Vec::new());
let mut cpio_writer = CpioWriter::new(writer, false);
for entry in [
CpioEntry::new_symlink(b"symlink", b"target"),
CpioEntry::new_directory(b"directory", 0o755),
CpioEntry::new_file(b"file", 0o644, CpioEntryData::Data(b"foobar".to_vec())),
CpioEntry {
path: b"reserved".to_vec(),
data: CpioEntryData::Size(0),
inode: 12345,
file_type: CpioEntryType::Reserved,
file_mode: 0o4777,
uid: 12345678,
gid: 87654321,
nlink: 2,
mtime: 1700000000,
dev_maj: 2222,
dev_min: 3333,
rdev_maj: 4444,
rdev_min: 5555,
crc32: 0xfedcba09,
},
] {
cpio_writer.start_entry(&entry).unwrap();
}
let writer = cpio_writer.finish().unwrap();
let data = writer.into_inner();
assert_eq!(
ring::digest::digest(&ring::digest::SHA512, &data).as_ref(),
[
0xb0, 0x51, 0xac, 0x28, 0x6f, 0x78, 0xe2, 0xe7, 0x45, 0xa0, 0x52, 0x7c, 0xff, 0x42,
0x30, 0x55, 0xbd, 0x64, 0x7d, 0x4e, 0xb8, 0xe6, 0x95, 0xe5, 0x9b, 0xd1, 0x13, 0xd6,
0x43, 0x0e, 0x32, 0xb2, 0x4e, 0x62, 0xa4, 0x55, 0x64, 0x48, 0xb7, 0x32, 0x26, 0x57,
0x75, 0x07, 0xf5, 0xa6, 0x0f, 0x18, 0xc3, 0x9e, 0x9f, 0x06, 0xdb, 0xa4, 0xf7, 0xeb,
0x5e, 0x8f, 0xce, 0xd0, 0x2b, 0x54, 0x39, 0x57
],
);
data
}
#[test]
fn round_trip_archive() {
let data = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/data/archive.cpio",
));
let data = generate_archive();
assert_ne!(data.len() % 512, 0);
for pad_to_block_size in [false, true] {
println!("Pad to block size: {pad_to_block_size}");
let reader = Cursor::new(data);
let reader = Cursor::new(&data);
let mut cpio_reader = CpioReader::new(reader, false);
let writer = Cursor::new(Vec::new());
@@ -40,7 +82,7 @@ fn round_trip_archive() {
let new_data = writer.get_ref().as_slice();
if pad_to_block_size {
assert!(new_data.starts_with(data));
assert!(new_data.starts_with(&data));
assert!(util::is_zero(&new_data[data.len()..]));
assert_eq!(new_data.len() % 512, 0);
} else {
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.