Merge changes I58337a5b,Ic1fd923d into main
* changes:
pvmfw: Use AVB VM name as DICE component_name
pvmfw: Support com.android.virt.name property
diff --git a/guest/pvmfw/README.md b/guest/pvmfw/README.md
index 766a923..652ca90 100644
--- a/guest/pvmfw/README.md
+++ b/guest/pvmfw/README.md
@@ -461,7 +461,12 @@
- `secretkeeper_protection`: pvmfw defers rollback protection to the guest
- `supports_uefi_boot`: pvmfw boots the VM as a EFI payload (experimental)
- `trusty_security_vm`: pvmfw skips rollback protection
-- `"com.android.virt.page_size"`: the guest page size in KiB (optional, defaults to 4)
+- `"com.android.virt.page_size"`: (optional) the guest page size in KiB, defaults to 4
+- `"com.android.virt.name"`: (optional) VM name, used as the
+ [`component_name`][dice-comp-name] (defaults to `"vm_entry"`) in the guest
+ DICE certificate and to identify special VMs
+
+[dice-comp-name]: https://cs.android.com/android/platform/superproject/main/+/main:external/open-dice/docs/android.md;l=81;drc=6d511e9533eac05d64d47fcd78ac5d881e72c3de
## Development
diff --git a/guest/pvmfw/avb/Android.bp b/guest/pvmfw/avb/Android.bp
index 141c1d2..0d55d7c 100644
--- a/guest/pvmfw/avb/Android.bp
+++ b/guest/pvmfw/avb/Android.bp
@@ -37,6 +37,7 @@
":test_image_with_one_hashdesc",
":test_image_with_non_initrd_hashdesc",
":test_image_with_initrd_and_non_initrd_desc",
+ ":test_image_with_name",
":test_image_with_invalid_page_size",
":test_image_with_negative_page_size",
":test_image_with_overflow_page_size",
@@ -123,6 +124,20 @@
}
avb_add_hash_footer {
+ name: "test_image_with_name",
+ src: ":unsigned_test_image",
+ partition_name: "boot",
+ private_key: ":pvmfw_sign_key",
+ salt: "2134",
+ props: [
+ {
+ name: "com.android.virt.name",
+ value: "test_vm_name",
+ },
+ ],
+}
+
+avb_add_hash_footer {
name: "test_image_with_invalid_page_size",
src: ":unsigned_test_image",
partition_name: "boot",
diff --git a/guest/pvmfw/avb/src/error.rs b/guest/pvmfw/avb/src/error.rs
index 1307e15..eb82837 100644
--- a/guest/pvmfw/avb/src/error.rs
+++ b/guest/pvmfw/avb/src/error.rs
@@ -30,6 +30,8 @@
UnknownVbmetaProperty,
/// VBMeta has invalid page_size property.
InvalidPageSize,
+ /// VBMeta has invalid VM name property.
+ InvalidVmName,
}
impl From<SlotVerifyError<'_>> for PvmfwVerifyError {
@@ -54,6 +56,7 @@
}
Self::UnknownVbmetaProperty => write!(f, "Unknown vbmeta property"),
Self::InvalidPageSize => write!(f, "Invalid page_size property"),
+ Self::InvalidVmName => write!(f, "Invalid name property"),
}
}
}
diff --git a/guest/pvmfw/avb/src/verify.rs b/guest/pvmfw/avb/src/verify.rs
index 8810696..6a3d7de 100644
--- a/guest/pvmfw/avb/src/verify.rs
+++ b/guest/pvmfw/avb/src/verify.rs
@@ -17,7 +17,7 @@
use crate::ops::{Ops, Payload};
use crate::partition::PartitionName;
use crate::PvmfwVerifyError;
-use alloc::vec::Vec;
+use alloc::{string::String, vec::Vec};
use avb::{
Descriptor, DescriptorError, DescriptorResult, HashDescriptor, PartitionData, SlotVerifyError,
SlotVerifyNoDataResult, VbmetaData,
@@ -47,6 +47,8 @@
pub rollback_index: u64,
/// Page size of kernel, if present.
pub page_size: Option<usize>,
+ /// Name of the guest payload, if present.
+ pub name: Option<String>,
}
impl VerifiedBootData<'_> {
@@ -238,6 +240,18 @@
Ok(Some(size))
}
+/// Returns the indicated payload name, if present.
+fn read_name(vbmeta_data: &VbmetaData) -> Result<Option<String>, PvmfwVerifyError> {
+ let Some(property) = vbmeta_data.get_property_value("com.android.virt.name") else {
+ return Ok(None);
+ };
+ let name = str::from_utf8(property).map_err(|_| PvmfwVerifyError::InvalidVmName)?;
+ if name.is_empty() {
+ return Err(PvmfwVerifyError::InvalidVmName);
+ }
+ Ok(Some(name.into()))
+}
+
/// Verifies the given initrd partition, and checks that the resulting contents looks like expected.
fn verify_initrd(
ops: &mut Ops,
@@ -275,6 +289,7 @@
let hash_descriptors = HashDescriptors::get(&descriptors)?;
let capabilities = Capability::get_capabilities(vbmeta_image)?;
let page_size = read_page_size(vbmeta_image)?;
+ let name = read_name(vbmeta_image)?;
if initrd.is_none() {
hash_descriptors.verify_no_initrd()?;
@@ -286,6 +301,7 @@
capabilities,
rollback_index,
page_size,
+ name,
});
}
@@ -307,5 +323,6 @@
capabilities,
rollback_index,
page_size,
+ name,
})
}
diff --git a/guest/pvmfw/avb/tests/api_test.rs b/guest/pvmfw/avb/tests/api_test.rs
index 3027c47..b3899d9 100644
--- a/guest/pvmfw/avb/tests/api_test.rs
+++ b/guest/pvmfw/avb/tests/api_test.rs
@@ -27,6 +27,7 @@
use utils::*;
const TEST_IMG_WITH_ONE_HASHDESC_PATH: &str = "test_image_with_one_hashdesc.img";
+const TEST_IMG_WITH_NAME_PATH: &str = "test_image_with_name.img";
const TEST_IMG_WITH_INVALID_PAGE_SIZE_PATH: &str = "test_image_with_invalid_page_size.img";
const TEST_IMG_WITH_NEGATIVE_PAGE_SIZE_PATH: &str = "test_image_with_negative_page_size.img";
const TEST_IMG_WITH_OVERFLOW_PAGE_SIZE_PATH: &str = "test_image_with_overflow_page_size.img";
@@ -102,6 +103,7 @@
capabilities: vec![],
rollback_index: 0,
page_size: None,
+ name: None,
};
assert_eq!(expected_boot_data, verified_boot_data);
@@ -147,6 +149,7 @@
capabilities: vec![Capability::RemoteAttest],
rollback_index: 0,
page_size: None,
+ name: None,
};
assert_eq!(expected_boot_data, verified_boot_data);
@@ -247,6 +250,18 @@
}
#[test]
+fn kernel_has_expected_valid_name() {
+ let kernel = fs::read(TEST_IMG_WITH_NAME_PATH).unwrap();
+ assert_eq!(read_name(&kernel), Ok(Some("test_vm_name".to_owned())));
+}
+
+#[test]
+fn kernel_has_expected_missing_name() {
+ let kernel = fs::read(TEST_IMG_WITH_ONE_HASHDESC_PATH).unwrap();
+ assert_eq!(read_name(&kernel), Ok(None));
+}
+
+#[test]
fn kernel_has_expected_page_size_invalid() {
let kernel = fs::read(TEST_IMG_WITH_INVALID_PAGE_SIZE_PATH).unwrap();
assert_eq!(read_page_size(&kernel), Err(PvmfwVerifyError::InvalidPageSize));
@@ -483,6 +498,7 @@
capabilities: vec![],
rollback_index: 5,
page_size: None,
+ name: None,
};
assert_eq!(expected_boot_data, verified_boot_data);
Ok(())
diff --git a/guest/pvmfw/avb/tests/utils.rs b/guest/pvmfw/avb/tests/utils.rs
index 7282f3e..227daa2 100644
--- a/guest/pvmfw/avb/tests/utils.rs
+++ b/guest/pvmfw/avb/tests/utils.rs
@@ -28,6 +28,7 @@
use std::{
fs,
mem::{size_of, transmute, MaybeUninit},
+ string::String,
};
const MICRODROID_KERNEL_IMG_PATH: &str = "microdroid_kernel";
@@ -134,6 +135,7 @@
capabilities,
rollback_index: 1,
page_size,
+ name: None,
};
assert_eq!(expected_boot_data, verified_boot_data);
@@ -166,12 +168,23 @@
capabilities,
rollback_index: expected_rollback_index,
page_size,
+ name: None,
};
assert_eq!(expected_boot_data, verified_boot_data);
Ok(())
}
+pub fn read_name(kernel: &[u8]) -> Result<Option<String>, PvmfwVerifyError> {
+ let public_key = load_trusted_public_key().unwrap();
+ let verified_boot_data = verify_payload(
+ kernel,
+ None, // initrd
+ &public_key,
+ )?;
+ Ok(verified_boot_data.name)
+}
+
pub fn read_page_size(kernel: &[u8]) -> Result<Option<usize>, PvmfwVerifyError> {
let public_key = load_trusted_public_key().unwrap();
let verified_boot_data = verify_payload(
diff --git a/guest/pvmfw/src/dice.rs b/guest/pvmfw/src/dice.rs
index f49fedb..49a3807 100644
--- a/guest/pvmfw/src/dice.rs
+++ b/guest/pvmfw/src/dice.rs
@@ -16,6 +16,7 @@
extern crate alloc;
use alloc::format;
+use alloc::string::String;
use alloc::vec::Vec;
use ciborium::cbor;
use ciborium::Value;
@@ -83,6 +84,7 @@
pub mode: DiceMode,
pub security_version: u64,
pub rkp_vm_marker: bool,
+ component_name: String,
}
impl PartialInputs {
@@ -90,12 +92,13 @@
let code_hash = to_dice_hash(data)?;
let auth_hash = hash(data.public_key)?;
let mode = to_dice_mode(data.debug_level);
+ let component_name = data.name.clone().unwrap_or(String::from("vm_entry"));
// We use rollback_index from vbmeta as the security_version field in dice certificate.
let security_version = data.rollback_index;
let rkp_vm_marker = data.has_capability(Capability::RemoteAttest)
|| data.has_capability(Capability::TrustySecurityVm);
- Ok(Self { code_hash, auth_hash, mode, security_version, rkp_vm_marker })
+ Ok(Self { code_hash, auth_hash, mode, security_version, rkp_vm_marker, component_name })
}
pub fn write_next_bcc(
@@ -156,7 +159,7 @@
fn generate_config_descriptor(&self, instance_hash: Option<Hash>) -> Result<Vec<u8>> {
let mut config = Vec::with_capacity(4);
- config.push((cbor!(COMPONENT_NAME_KEY)?, cbor!("vm_entry")?));
+ config.push((cbor!(COMPONENT_NAME_KEY)?, cbor!(self.component_name.as_str())?));
config.push((cbor!(SECURITY_VERSION_KEY)?, cbor!(self.security_version)?));
if self.rkp_vm_marker {
config.push((cbor!(RKP_VM_MARKER_KEY)?, Value::Null))
@@ -200,6 +203,7 @@
kernel_digest: [1u8; size_of::<Digest>()],
initrd_digest: Some([2u8; size_of::<Digest>()]),
public_key: b"public key",
+ name: None,
capabilities: vec![],
rollback_index: 42,
page_size: None,
@@ -249,12 +253,13 @@
}
#[test]
- fn rkp_vm_config_descriptor_has_rkp_vm_marker() {
+ fn rkp_vm_config_descriptor_has_rkp_vm_marker_and_component_name() {
let vb_data =
VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
let inputs = PartialInputs::new(&vb_data).unwrap();
let config_map = decode_config_descriptor(&inputs, Some(HASH));
+ assert_eq!(config_map.get(&COMPONENT_NAME_KEY).unwrap().as_text().unwrap(), "vm_entry");
assert!(config_map.get(&RKP_VM_MARKER_KEY).unwrap().is_null());
}