[avb] Collect kernel/initrd digests when verification succeeds
Bug: 265897559
Test: m pvmfw_img && atest libpvmfw_avb.integration_test
Change-Id: I6f281090d0f53464824d80e1348f4d099330ad31
diff --git a/pvmfw/avb/Android.bp b/pvmfw/avb/Android.bp
index fb950b7..03670c1 100644
--- a/pvmfw/avb/Android.bp
+++ b/pvmfw/avb/Android.bp
@@ -46,7 +46,9 @@
rustlibs: [
"libanyhow",
"libavb_bindgen",
+ "libhex",
"libpvmfw_avb_nostd",
+ "libopenssl",
],
enabled: false,
arch: {
diff --git a/pvmfw/avb/src/descriptor.rs b/pvmfw/avb/src/descriptor.rs
index b0598de..c54d416 100644
--- a/pvmfw/avb/src/descriptor.rs
+++ b/pvmfw/avb/src/descriptor.rs
@@ -31,7 +31,8 @@
};
use tinyvec::ArrayVec;
-const DIGEST_SIZE: usize = AVB_SHA256_DIGEST_SIZE as usize;
+/// Digest type for kernel and initrd.
+pub type Digest = [u8; AVB_SHA256_DIGEST_SIZE as usize];
/// `HashDescriptors` can have maximum one `HashDescriptor` per known partition.
#[derive(Default)]
@@ -132,9 +133,7 @@
#[derive(Default)]
pub(crate) struct HashDescriptor {
partition_name: PartitionName,
- /// TODO(b/265897559): Pass this digest to DICE.
- #[allow(dead_code)]
- pub(crate) digest: [u8; DIGEST_SIZE],
+ pub(crate) digest: Digest,
}
impl HashDescriptor {
@@ -145,7 +144,7 @@
.try_into()?;
let partition_digest =
data.get(desc.digest_range()?).ok_or(AvbIOError::RangeOutsidePartition)?;
- let mut digest = [0u8; DIGEST_SIZE];
+ let mut digest = [0u8; size_of::<Digest>()];
digest.copy_from_slice(partition_digest);
Ok(Self { partition_name, digest })
}
diff --git a/pvmfw/avb/src/lib.rs b/pvmfw/avb/src/lib.rs
index 065eca5..8fea162 100644
--- a/pvmfw/avb/src/lib.rs
+++ b/pvmfw/avb/src/lib.rs
@@ -25,5 +25,6 @@
mod utils;
mod verify;
+pub use descriptor::Digest;
pub use error::AvbSlotVerifyError;
-pub use verify::{verify_payload, DebugLevel};
+pub use verify::{verify_payload, DebugLevel, VerifiedBootData};
diff --git a/pvmfw/avb/src/partition.rs b/pvmfw/avb/src/partition.rs
index 636bfd3..bc63003 100644
--- a/pvmfw/avb/src/partition.rs
+++ b/pvmfw/avb/src/partition.rs
@@ -18,7 +18,7 @@
use crate::utils::is_not_null;
use core::ffi::{c_char, CStr};
-#[derive(Clone, Debug, Default, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) enum PartitionName {
/// The default `PartitionName` is needed to build the default `HashDescriptor`.
#[default]
diff --git a/pvmfw/avb/src/verify.rs b/pvmfw/avb/src/verify.rs
index 14b0e7e..1a79c83 100644
--- a/pvmfw/avb/src/verify.rs
+++ b/pvmfw/avb/src/verify.rs
@@ -14,15 +14,26 @@
//! This module handles the pvmfw payload verification.
-use crate::descriptor::HashDescriptors;
+use crate::descriptor::{Digest, HashDescriptors};
use crate::error::AvbSlotVerifyError;
use crate::ops::{Ops, Payload};
use crate::partition::PartitionName;
use avb_bindgen::{AvbPartitionData, AvbVBMetaData};
use core::ffi::c_char;
+/// Verified data returned when the payload verification succeeds.
+#[derive(Debug)]
+pub struct VerifiedBootData {
+ /// DebugLevel of the VM.
+ pub debug_level: DebugLevel,
+ /// Kernel digest.
+ pub kernel_digest: Digest,
+ /// Initrd digest if initrd exists.
+ pub initrd_digest: Option<Digest>,
+}
+
/// This enum corresponds to the `DebugLevel` in `VirtualMachineConfig`.
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DebugLevel {
/// Not debuggable at all.
None,
@@ -87,7 +98,7 @@
kernel: &[u8],
initrd: Option<&[u8]>,
trusted_public_key: &[u8],
-) -> Result<DebugLevel, AvbSlotVerifyError> {
+) -> Result<VerifiedBootData, AvbSlotVerifyError> {
let mut payload = Payload::new(kernel, initrd, trusted_public_key);
let mut ops = Ops::from(&mut payload);
let kernel_verify_result = ops.verify_partition(PartitionName::Kernel.as_cstr())?;
@@ -100,12 +111,15 @@
// which is returned by `avb_slot_verify()` when the verification succeeds. It is
// guaranteed by libavb to be non-null and to point to a valid VBMeta structure.
let hash_descriptors = unsafe { HashDescriptors::from_vbmeta(vbmeta_image)? };
- // TODO(b/265897559): Pass the digest in kernel descriptor to DICE.
- let _kernel_descriptor = hash_descriptors.find(PartitionName::Kernel)?;
+ let kernel_descriptor = hash_descriptors.find(PartitionName::Kernel)?;
if initrd.is_none() {
verify_vbmeta_has_only_one_hash_descriptor(&hash_descriptors)?;
- return Ok(DebugLevel::None);
+ return Ok(VerifiedBootData {
+ debug_level: DebugLevel::None,
+ kernel_digest: kernel_descriptor.digest,
+ initrd_digest: None,
+ });
}
let initrd = initrd.unwrap();
@@ -123,5 +137,10 @@
initrd_partition_name,
initrd.len(),
)?;
- Ok(debug_level)
+ let initrd_descriptor = hash_descriptors.find(initrd_partition_name)?;
+ Ok(VerifiedBootData {
+ debug_level,
+ kernel_digest: kernel_descriptor.digest,
+ initrd_digest: Some(initrd_descriptor.digest),
+ })
}
diff --git a/pvmfw/avb/tests/api_test.rs b/pvmfw/avb/tests/api_test.rs
index 4f00f1e..261d8a8 100644
--- a/pvmfw/avb/tests/api_test.rs
+++ b/pvmfw/avb/tests/api_test.rs
@@ -16,9 +16,9 @@
mod utils;
-use anyhow::Result;
+use anyhow::{anyhow, Result};
use avb_bindgen::{AvbFooter, AvbVBMetaImageHeader};
-use pvmfw_avb::{AvbSlotVerifyError, DebugLevel};
+use pvmfw_avb::{verify_payload, AvbSlotVerifyError, DebugLevel};
use std::{fs, mem::size_of, ptr};
use utils::*;
@@ -35,121 +35,125 @@
/// the latest payload can be verified successfully.
#[test]
fn latest_normal_payload_passes_verification() -> Result<()> {
- assert_payload_verification_with_initrd_eq(
- &load_latest_signed_kernel()?,
+ assert_latest_payload_verification_passes(
&load_latest_initrd_normal()?,
- &load_trusted_public_key()?,
- Ok(DebugLevel::None),
+ b"initrd_normal",
+ DebugLevel::None,
)
}
#[test]
fn latest_debug_payload_passes_verification() -> Result<()> {
- assert_payload_verification_with_initrd_eq(
- &load_latest_signed_kernel()?,
+ assert_latest_payload_verification_passes(
&load_latest_initrd_debug()?,
- &load_trusted_public_key()?,
- Ok(DebugLevel::Full),
+ b"initrd_debug",
+ DebugLevel::Full,
)
}
#[test]
fn payload_expecting_no_initrd_passes_verification_with_no_initrd() -> Result<()> {
- assert_payload_verification_eq(
+ let verified_boot_data = verify_payload(
&fs::read(TEST_IMG_WITH_ONE_HASHDESC_PATH)?,
/*initrd=*/ None,
&load_trusted_public_key()?,
- Ok(DebugLevel::None),
)
+ .map_err(|e| anyhow!("Verification failed. Error: {}", e))?;
+
+ assert_eq!(DebugLevel::None, verified_boot_data.debug_level);
+ let digest = hash(&[&hex::decode("1111")?, &fs::read(UNSIGNED_TEST_IMG_PATH)?]);
+ assert_eq!(digest, verified_boot_data.kernel_digest);
+ assert!(verified_boot_data.initrd_digest.is_none());
+ Ok(())
}
#[test]
fn payload_with_non_initrd_descriptor_fails_verification_with_no_initrd() -> Result<()> {
- assert_payload_verification_eq(
+ assert_payload_verification_fails(
&fs::read(TEST_IMG_WITH_NON_INITRD_HASHDESC_PATH)?,
/*initrd=*/ None,
&load_trusted_public_key()?,
- Err(AvbSlotVerifyError::InvalidMetadata),
+ AvbSlotVerifyError::InvalidMetadata,
)
}
#[test]
fn payload_with_non_initrd_descriptor_fails_verification_with_initrd() -> Result<()> {
- assert_payload_verification_with_initrd_eq(
+ assert_payload_verification_with_initrd_fails(
&fs::read(TEST_IMG_WITH_INITRD_AND_NON_INITRD_DESC_PATH)?,
&load_latest_initrd_normal()?,
&load_trusted_public_key()?,
- Err(AvbSlotVerifyError::InvalidMetadata),
+ AvbSlotVerifyError::InvalidMetadata,
)
}
#[test]
fn payload_with_prop_descriptor_fails_verification_with_no_initrd() -> Result<()> {
- assert_payload_verification_eq(
+ assert_payload_verification_fails(
&fs::read(TEST_IMG_WITH_PROP_DESC_PATH)?,
/*initrd=*/ None,
&load_trusted_public_key()?,
- Err(AvbSlotVerifyError::InvalidMetadata),
+ AvbSlotVerifyError::InvalidMetadata,
)
}
#[test]
fn payload_expecting_initrd_fails_verification_with_no_initrd() -> Result<()> {
- assert_payload_verification_eq(
+ assert_payload_verification_fails(
&load_latest_signed_kernel()?,
/*initrd=*/ None,
&load_trusted_public_key()?,
- Err(AvbSlotVerifyError::InvalidMetadata),
+ AvbSlotVerifyError::InvalidMetadata,
)
}
#[test]
fn payload_with_empty_public_key_fails_verification() -> Result<()> {
- assert_payload_verification_with_initrd_eq(
+ assert_payload_verification_with_initrd_fails(
&load_latest_signed_kernel()?,
&load_latest_initrd_normal()?,
/*trusted_public_key=*/ &[0u8; 0],
- Err(AvbSlotVerifyError::PublicKeyRejected),
+ AvbSlotVerifyError::PublicKeyRejected,
)
}
#[test]
fn payload_with_an_invalid_public_key_fails_verification() -> Result<()> {
- assert_payload_verification_with_initrd_eq(
+ assert_payload_verification_with_initrd_fails(
&load_latest_signed_kernel()?,
&load_latest_initrd_normal()?,
/*trusted_public_key=*/ &[0u8; 512],
- Err(AvbSlotVerifyError::PublicKeyRejected),
+ AvbSlotVerifyError::PublicKeyRejected,
)
}
#[test]
fn payload_with_a_different_valid_public_key_fails_verification() -> Result<()> {
- assert_payload_verification_with_initrd_eq(
+ assert_payload_verification_with_initrd_fails(
&load_latest_signed_kernel()?,
&load_latest_initrd_normal()?,
&fs::read(PUBLIC_KEY_RSA2048_PATH)?,
- Err(AvbSlotVerifyError::PublicKeyRejected),
+ AvbSlotVerifyError::PublicKeyRejected,
)
}
#[test]
fn payload_with_an_invalid_initrd_fails_verification() -> Result<()> {
- assert_payload_verification_with_initrd_eq(
+ assert_payload_verification_with_initrd_fails(
&load_latest_signed_kernel()?,
/*initrd=*/ &fs::read(UNSIGNED_TEST_IMG_PATH)?,
&load_trusted_public_key()?,
- Err(AvbSlotVerifyError::Verification),
+ AvbSlotVerifyError::Verification,
)
}
#[test]
fn unsigned_kernel_fails_verification() -> Result<()> {
- assert_payload_verification_with_initrd_eq(
+ assert_payload_verification_with_initrd_fails(
&fs::read(UNSIGNED_TEST_IMG_PATH)?,
&load_latest_initrd_normal()?,
&load_trusted_public_key()?,
- Err(AvbSlotVerifyError::Io),
+ AvbSlotVerifyError::Io,
)
}
@@ -158,11 +162,11 @@
let mut kernel = load_latest_signed_kernel()?;
kernel[1] = !kernel[1]; // Flip the bits
- assert_payload_verification_with_initrd_eq(
+ assert_payload_verification_with_initrd_fails(
&kernel,
&load_latest_initrd_normal()?,
&load_trusted_public_key()?,
- Err(AvbSlotVerifyError::Verification),
+ AvbSlotVerifyError::Verification,
)
}
@@ -191,11 +195,11 @@
// footer is unaligned; copy vbmeta_offset to local variable
let vbmeta_offset = footer.vbmeta_offset;
assert_eq!(wrong_offset, vbmeta_offset);
- assert_payload_verification_with_initrd_eq(
+ assert_payload_verification_with_initrd_fails(
&kernel,
&load_latest_initrd_normal()?,
&load_trusted_public_key()?,
- Err(AvbSlotVerifyError::Io),
+ AvbSlotVerifyError::Io,
)?;
}
Ok(())
@@ -207,11 +211,11 @@
let avb_footer_index = kernel.len() - size_of::<AvbFooter>() + RANDOM_FOOTER_POS;
kernel[avb_footer_index] = !kernel[avb_footer_index];
- assert_payload_verification_with_initrd_eq(
+ assert_payload_verification_with_initrd_fails(
&kernel,
&load_latest_initrd_normal()?,
&load_trusted_public_key()?,
- Err(AvbSlotVerifyError::InvalidMetadata),
+ AvbSlotVerifyError::InvalidMetadata,
)
}
@@ -220,11 +224,11 @@
let mut initrd = load_latest_initrd_normal()?;
initrd.extend(b"androidboot.vbmeta.digest=1111");
- assert_payload_verification_with_initrd_eq(
+ assert_payload_verification_with_initrd_fails(
&load_latest_signed_kernel()?,
&initrd,
&load_trusted_public_key()?,
- Err(AvbSlotVerifyError::Verification),
+ AvbSlotVerifyError::Verification,
)
}
@@ -236,11 +240,11 @@
kernel[vbmeta_index] = !kernel[vbmeta_index]; // Flip the bits
- assert_payload_verification_with_initrd_eq(
+ assert_payload_verification_with_initrd_fails(
&kernel,
&load_latest_initrd_normal()?,
&load_trusted_public_key()?,
- Err(AvbSlotVerifyError::InvalidMetadata),
+ AvbSlotVerifyError::InvalidMetadata,
)
}
@@ -259,17 +263,17 @@
kernel[public_key_offset..(public_key_offset + public_key_size)]
.copy_from_slice(&empty_public_key);
- assert_payload_verification_with_initrd_eq(
+ assert_payload_verification_with_initrd_fails(
&kernel,
&load_latest_initrd_normal()?,
&empty_public_key,
- Err(AvbSlotVerifyError::Verification),
+ AvbSlotVerifyError::Verification,
)?;
- assert_payload_verification_with_initrd_eq(
+ assert_payload_verification_with_initrd_fails(
&kernel,
&load_latest_initrd_normal()?,
&load_trusted_public_key()?,
- Err(AvbSlotVerifyError::Verification),
+ AvbSlotVerifyError::Verification,
)
}
@@ -303,10 +307,10 @@
AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED, vbmeta_header_flags,
"VBMeta verification flag should be disabled now."
);
- assert_payload_verification_with_initrd_eq(
+ assert_payload_verification_with_initrd_fails(
&kernel,
&load_latest_initrd_normal()?,
&load_trusted_public_key()?,
- Err(AvbSlotVerifyError::Verification),
+ AvbSlotVerifyError::Verification,
)
}
diff --git a/pvmfw/avb/tests/utils.rs b/pvmfw/avb/tests/utils.rs
index 0a2eac6..8756d06 100644
--- a/pvmfw/avb/tests/utils.rs
+++ b/pvmfw/avb/tests/utils.rs
@@ -16,12 +16,13 @@
//! Utility functions used by API tests.
-use anyhow::Result;
+use anyhow::{anyhow, Result};
use avb_bindgen::{
avb_footer_validate_and_byteswap, avb_vbmeta_image_header_to_host_byte_order, AvbFooter,
AvbVBMetaImageHeader,
};
-use pvmfw_avb::{verify_payload, AvbSlotVerifyError, DebugLevel};
+use openssl::sha;
+use pvmfw_avb::{verify_payload, AvbSlotVerifyError, DebugLevel, Digest};
use std::{
fs,
mem::{size_of, transmute, MaybeUninit},
@@ -34,22 +35,22 @@
pub const PUBLIC_KEY_RSA2048_PATH: &str = "data/testkey_rsa2048_pub.bin";
-pub fn assert_payload_verification_with_initrd_eq(
+pub fn assert_payload_verification_with_initrd_fails(
kernel: &[u8],
initrd: &[u8],
trusted_public_key: &[u8],
- expected_result: Result<DebugLevel, AvbSlotVerifyError>,
+ expected_error: AvbSlotVerifyError,
) -> Result<()> {
- assert_payload_verification_eq(kernel, Some(initrd), trusted_public_key, expected_result)
+ assert_payload_verification_fails(kernel, Some(initrd), trusted_public_key, expected_error)
}
-pub fn assert_payload_verification_eq(
+pub fn assert_payload_verification_fails(
kernel: &[u8],
initrd: Option<&[u8]>,
trusted_public_key: &[u8],
- expected_result: Result<DebugLevel, AvbSlotVerifyError>,
+ expected_error: AvbSlotVerifyError,
) -> Result<()> {
- assert_eq!(expected_result, verify_payload(kernel, initrd, trusted_public_key));
+ assert_eq!(expected_error, verify_payload(kernel, initrd, trusted_public_key).unwrap_err());
Ok(())
}
@@ -95,3 +96,34 @@
};
Ok(vbmeta_header)
}
+
+pub fn assert_latest_payload_verification_passes(
+ initrd: &[u8],
+ initrd_salt: &[u8],
+ expected_debug_level: DebugLevel,
+) -> Result<()> {
+ let kernel = load_latest_signed_kernel()?;
+ let verified_boot_data = verify_payload(&kernel, Some(initrd), &load_trusted_public_key()?)
+ .map_err(|e| anyhow!("Verification failed. Error: {}", e))?;
+
+ assert_eq!(expected_debug_level, verified_boot_data.debug_level);
+
+ let footer = extract_avb_footer(&kernel)?;
+ assert_eq!(
+ hash(&[&hash(&[b"bootloader"]), &kernel[..usize::try_from(footer.original_image_size)?]]),
+ verified_boot_data.kernel_digest,
+ "Kernel digest is not equal to the expected."
+ );
+ assert_eq!(
+ hash(&[&hash(&[initrd_salt]), initrd,]),
+ verified_boot_data.initrd_digest.unwrap(),
+ "initrd digest is not equal to the expected."
+ );
+ Ok(())
+}
+
+pub fn hash(inputs: &[&[u8]]) -> Digest {
+ let mut digester = sha::Sha256::new();
+ inputs.iter().for_each(|input| digester.update(input));
+ digester.finish()
+}