Tenant APK: Validate apks against TenancyConfig

MM should ensure that the tenant apks presented by Android are a subset
of those present in TenancyConfig - which is a signed declaration of
tenancy & part of DICE based secret derivation in VM. This ensures
Android is not able to plug arbitrary tenant packages into the VM.

Test: MicrodroidTests.tenantApk
Bug: 429639517
Flag: build.RELEASE_AVF_ENABLE_ADVANCE_MULTITENANCY
Change-Id: Id1480bcc146b1e6be4130bc7fbeba85b9671281c
diff --git a/guest/microdroid_manager/src/main.rs b/guest/microdroid_manager/src/main.rs
index 739d9c8..9659950 100644
--- a/guest/microdroid_manager/src/main.rs
+++ b/guest/microdroid_manager/src/main.rs
@@ -45,7 +45,9 @@
 use crate::dice::dice_derivation;
 use crate::encrypted_store_kek::{decrypt_kek, encrypt_kek};
 use crate::instance::{EncryptedStoreMode, InstanceDisk, MicrodroidData};
-use crate::verify::{integrity_protect_tenant_apks, verify_payload};
+use crate::verify::{
+    integrity_protect_tenant_apks, validate_tenant_apks_against_tenant_config, verify_payload,
+};
 use crate::vm_internal_service::VmInternalService;
 use crate::vm_payload_service::VmPayloadService;
 use anyhow::{anyhow, bail, ensure, Context, Error, Result};
@@ -579,7 +581,11 @@
         .context("Failed to mount extra apks")?;
 
     // TODO(b/429639517): Verify the tenant packages against`VmPayloadConfig` from main_apk
-    integrity_protect_tenant_apks()?;
+    let tenant_apk_data_extracted_from_manifest = integrity_protect_tenant_apks()?;
+    validate_tenant_apks_against_tenant_config(
+        &tenant_apk_data_extracted_from_manifest,
+        &config.tenants,
+    )?;
     let tenant_apk_count =
         config.tenants.iter().filter(|t| matches!(t, TenantConfig::Apk(_))).count();
     mount_additional_apks(&mut zipfuse, tenant_apk_count, AdditionalApkType::TenantApk)
diff --git a/guest/microdroid_manager/src/verify.rs b/guest/microdroid_manager/src/verify.rs
index 3a1dacf..1623382 100644
--- a/guest/microdroid_manager/src/verify.rs
+++ b/guest/microdroid_manager/src/verify.rs
@@ -22,9 +22,12 @@
 use itertools::sorted;
 use log::{info, warn};
 use microdroid_metadata::{write_metadata, Metadata};
+use microdroid_payload_config::TenantConfig;
 use openssl::sha::sha512;
 use rustutils::system_properties;
+use std::collections::HashSet;
 use std::fs::OpenOptions;
+use std::hash::Hash;
 use std::path::Path;
 use std::process::{Child, Command};
 use std::str;
@@ -175,7 +178,7 @@
     })
 }
 
-pub fn integrity_protect_tenant_apks() -> Result<()> {
+pub(crate) fn integrity_protect_tenant_apks() -> Result<Vec<ApkData>> {
     // sort globbed paths to match apks (tenant-{idx}) and idsigs (tenant-{idx})
     // e.g. "tenant-0" corresponds to "tenant-idsig-0"
     let tenant_apks =
@@ -188,8 +191,14 @@
         tenant_apks.len(),
         tenant_idsigs.len()
     );
+    let tenant_hashes_from_idsig: Vec<_> = tenant_idsigs
+        .iter()
+        .map(|idsig| {
+            get_apk_root_hash_from_idsig(idsig).expect("Can't find root hash from tenant idsig")
+        })
+        .collect();
 
-    let tenant_apk_names: Vec<_> =
+    let tenant_apk_block_dev: Vec<_> =
         (0..tenant_apks.len()).map(|i| format!("tenant-apk-{}", i)).collect();
     let mut apkdmverity_arguments: Vec<ApkDmverityArgument> = vec![];
     for (i, tenant_apk) in tenant_apks.iter().enumerate() {
@@ -197,7 +206,7 @@
             ApkDmverityArgument {
                 apk: tenant_apk.to_str().unwrap(),
                 idsig: tenant_idsigs[i].to_str().unwrap(),
-                name: &tenant_apk_names[i],
+                name: &tenant_apk_block_dev[i],
                 saved_root_hash: None,
             }
         });
@@ -206,9 +215,50 @@
     let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
 
     apkdmverity_child.wait()?;
+    let tenant_apks_data = tenant_hashes_from_idsig
+        .into_iter()
+        .enumerate()
+        .map(|(i, root_hash)| {
+            let mount_path = format!("/dev/block/mapper/{}", &tenant_apk_block_dev[i]);
+            get_data_from_apk(&mount_path, root_hash, false)
+        })
+        .collect::<Result<Vec<_>>>()?;
+
+    Ok(tenant_apks_data)
+}
+
+// Validation logic includes:
+// 1. The tenant_apk a subset of apks described in tenant_config (comparison is by package name)
+// 2. The order of description in tenant_config is irrelevant.
+pub(crate) fn validate_tenant_apks_against_tenant_config(
+    tenant_apk: &[ApkData], // data extracted from the apk passed from host
+    tenant_config: &[TenantConfig],
+) -> Result<()> {
+    let apk_names_in_config = tenant_config
+        .iter()
+        .filter_map(|config| {
+            if let TenantConfig::Apk(config) = config {
+                Some(config.name.clone())
+            } else {
+                None
+            }
+        })
+        .collect::<Vec<_>>();
+    let tenant_apk = tenant_apk.iter().map(|apk| apk.package_name.clone()).collect::<Vec<_>>();
+    ensure!(
+        is_subset(&tenant_apk, &apk_names_in_config),
+        MicrodroidError::PayloadVerificationFailed(format!(
+            "Tenant apk {tenant_apk:?} must be subset of TenancyConfig {tenant_config:?}"
+        ))
+    );
     Ok(())
 }
 
+fn is_subset<T: Eq + Hash + Clone>(a: &[T], b: &[T]) -> bool {
+    let b_set: HashSet<_> = b.iter().collect();
+    a.iter().all(|item| b_set.contains(item))
+}
+
 fn validate_manifest_info(info: &ApkManifestInfo) -> Result<()> {
     ensure!(
         info.has_relaxed_rollback_protection_permission == info.rollback_index.is_some(),
diff --git a/libs/libmicrodroid_payload_metadata/config/src/lib.rs b/libs/libmicrodroid_payload_metadata/config/src/lib.rs
index cf338aa..50555ca 100644
--- a/libs/libmicrodroid_payload_metadata/config/src/lib.rs
+++ b/libs/libmicrodroid_payload_metadata/config/src/lib.rs
@@ -133,6 +133,8 @@
     Apk(TenantConfiguration),
 }
 
+// TODO: More (optional) configuration need to be added for tenant
+// such as min_version, expected signer. Ensure as they are added, verification code matches these.
 /// Tenant config
 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
 pub struct TenantConfiguration {