pvmfw: Support com.android.virt.name property

Teach pvmfw to recognize a new AVB property that will be used to gate
features specific to particular VMs, such as the way we currently
support the RKP VM with special rollback protection and its DICE chain
marker. For now, only add the infrastructure and unit-tests.

Note: No functional change intended.

Bug: 378673494
Bug: 377276983
Test: m pvmfw_bin
Test: atest libpvmfw_avb.integration_test libpvmfw.dice.test
Change-Id: Ic1fd923df361e19b2d0cd323aa6a0ca866a281a6
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..32ccda2 100644
--- a/guest/pvmfw/src/dice.rs
+++ b/guest/pvmfw/src/dice.rs
@@ -200,6 +200,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,