Merge "Add APK details to the DICE chain" into main
diff --git a/libs/apkmanifest/src/apkmanifest.rs b/libs/apkmanifest/src/apkmanifest.rs
index 6766b21..b92aa74 100644
--- a/libs/apkmanifest/src/apkmanifest.rs
+++ b/libs/apkmanifest/src/apkmanifest.rs
@@ -28,7 +28,7 @@
 use std::path::Path;
 
 /// Information extracted from the Android manifest inside an APK.
-#[derive(Debug)]
+#[derive(Debug, Default, Eq, PartialEq)]
 pub struct ApkManifestInfo {
     /// The package name of the app.
     pub package: String,
diff --git a/libs/dice/open_dice/src/error.rs b/libs/dice/open_dice/src/error.rs
index 53ffd2d..bef9a9c 100644
--- a/libs/dice/open_dice/src/error.rs
+++ b/libs/dice/open_dice/src/error.rs
@@ -38,11 +38,11 @@
 impl fmt::Display for DiceError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
-            Self::InvalidInput => write!(f, "invalid input"),
+            Self::InvalidInput => write!(f, "Invalid input"),
             Self::BufferTooSmall(buffer_required_size) => {
-                write!(f, "buffer too small. Required {buffer_required_size} bytes.")
+                write!(f, "Buffer too small; need {buffer_required_size} bytes")
             }
-            Self::PlatformError => write!(f, "platform error"),
+            Self::PlatformError => write!(f, "Platform error"),
         }
     }
 }
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index db65193..8710e54 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -24,6 +24,7 @@
         "libbyteorder",
         "libcap_rust",
         "libciborium",
+        "libcoset",
         "libdiced_open_dice",
         "libdiced_sample_inputs",
         "libglob",
diff --git a/microdroid_manager/src/dice.rs b/microdroid_manager/src/dice.rs
index a576416..6b0775a 100644
--- a/microdroid_manager/src/dice.rs
+++ b/microdroid_manager/src/dice.rs
@@ -13,12 +13,15 @@
 // limitations under the License.
 
 use crate::dice_driver::DiceDriver;
+use crate::instance::ApkData;
 use crate::{is_debuggable, MicrodroidData};
 use anyhow::{bail, Context, Result};
-use ciborium::{cbor, ser};
+use ciborium::{cbor, Value};
+use coset::CborSerializable;
 use diced_open_dice::OwnedDiceArtifacts;
 use microdroid_metadata::PayloadMetadata;
-use openssl::sha::Sha512;
+use openssl::sha::{sha512, Sha512};
+use std::iter::once;
 
 /// Perform an open DICE derivation for the payload.
 pub fn dice_derivation(
@@ -26,6 +29,11 @@
     verified_data: &MicrodroidData,
     payload_metadata: &PayloadMetadata,
 ) -> Result<OwnedDiceArtifacts> {
+    let subcomponents = build_subcomponent_list(verified_data);
+
+    let config_descriptor = format_payload_config_descriptor(payload_metadata, &subcomponents)
+        .context("Building config descriptor")?;
+
     // Calculate compound digests of code and authorities
     let mut code_hash_ctx = Sha512::new();
     let mut authority_hash_ctx = Sha512::new();
@@ -42,8 +50,6 @@
     let code_hash = code_hash_ctx.finish();
     let authority_hash = authority_hash_ctx.finish();
 
-    let config_descriptor = format_payload_config_descriptor(payload_metadata)?;
-
     // Check debuggability, conservatively assuming it is debuggable
     let debuggable = is_debuggable()?;
 
@@ -52,35 +58,71 @@
     dice.derive(code_hash, &config_descriptor, authority_hash, debuggable, hidden)
 }
 
-/// Returns a configuration descriptor of the given payload following the BCC's specification:
-/// https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl
-/// {
-///   -70002: "Microdroid payload",
-///   ? -71000: tstr ; payload_config_path
-///   ? -71001: PayloadConfig
-/// }
-/// PayloadConfig = {
-///   1: tstr ; payload_binary_name
-/// }
-fn format_payload_config_descriptor(payload: &PayloadMetadata) -> Result<Vec<u8>> {
-    const MICRODROID_PAYLOAD_COMPONENT_NAME: &str = "Microdroid payload";
+struct Subcomponent<'a> {
+    name: String,
+    version: u64,
+    code_hash: &'a [u8],
+    authority_hash: Box<[u8]>,
+}
 
-    let config_descriptor_cbor_value = match payload {
-        PayloadMetadata::ConfigPath(payload_config_path) => cbor!({
-            -70002 => MICRODROID_PAYLOAD_COMPONENT_NAME,
-            -71000 => payload_config_path
-        }),
-        PayloadMetadata::Config(payload_config) => cbor!({
-            -70002 => MICRODROID_PAYLOAD_COMPONENT_NAME,
-            -71001 => {1 => payload_config.payload_binary_name}
-        }),
-        _ => bail!("Failed to match the payload against a config type: {:?}", payload),
+impl<'a> Subcomponent<'a> {
+    fn to_value(&self) -> Result<Value> {
+        Ok(cbor!({
+           1 => self.name,
+           2 => self.version,
+           3 => self.code_hash,
+           4 => self.authority_hash
+        })?)
     }
-    .context("Failed to build a CBOR Value from payload metadata")?;
-    let mut config_descriptor = Vec::new();
 
-    ser::into_writer(&config_descriptor_cbor_value, &mut config_descriptor)?;
-    Ok(config_descriptor)
+    fn for_apk(apk: &'a ApkData) -> Self {
+        Self {
+            name: format!("apk:{}", apk.package_name),
+            version: apk.version_code,
+            code_hash: &apk.root_hash,
+            authority_hash:
+                // TODO(b/305925597): Hash the certificate not the pubkey
+                Box::new(sha512(&apk.pubkey)),
+        }
+    }
+}
+
+fn build_subcomponent_list(verified_data: &MicrodroidData) -> Vec<Subcomponent> {
+    if !cfg!(dice_changes) {
+        return vec![];
+    }
+
+    once(&verified_data.apk_data)
+        .chain(&verified_data.extra_apks_data)
+        .map(Subcomponent::for_apk)
+        .collect()
+}
+
+// Returns a configuration descriptor of the given payload. See vm_config.cddl for a definition
+// of the format.
+fn format_payload_config_descriptor(
+    payload: &PayloadMetadata,
+    subcomponents: &[Subcomponent],
+) -> Result<Vec<u8>> {
+    let mut map = Vec::new();
+    map.push((cbor!(-70002)?, cbor!("Microdroid payload")?));
+    map.push(match payload {
+        PayloadMetadata::ConfigPath(payload_config_path) => {
+            (cbor!(-71000)?, cbor!(payload_config_path)?)
+        }
+        PayloadMetadata::Config(payload_config) => {
+            (cbor!(-71001)?, cbor!({1 => payload_config.payload_binary_name})?)
+        }
+        _ => bail!("Failed to match the payload against a config type: {:?}", payload),
+    });
+
+    if !subcomponents.is_empty() {
+        let values =
+            subcomponents.iter().map(Subcomponent::to_value).collect::<Result<Vec<_>>>()?;
+        map.push((cbor!(-71002)?, cbor!(values)?));
+    }
+
+    Ok(Value::Map(map).to_vec()?)
 }
 
 #[cfg(test)]
@@ -88,17 +130,30 @@
     use super::*;
     use microdroid_metadata::PayloadConfig;
 
+    const NO_SUBCOMPONENTS: [Subcomponent; 0] = [];
+
+    fn assert_eq_bytes(expected: &[u8], actual: &[u8]) {
+        assert_eq!(
+            expected,
+            actual,
+            "Expected {}, got {}",
+            hex::encode(expected),
+            hex::encode(actual)
+        )
+    }
+
     #[test]
     fn payload_metadata_with_path_formats_correctly() -> Result<()> {
         let payload_metadata = PayloadMetadata::ConfigPath("/config_path".to_string());
-        let config_descriptor = format_payload_config_descriptor(&payload_metadata)?;
+        let config_descriptor =
+            format_payload_config_descriptor(&payload_metadata, &NO_SUBCOMPONENTS)?;
         static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
             0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
             0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01,
             0x15, 0x57, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x61, 0x74,
             0x68,
         ];
-        assert_eq!(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
+        assert_eq_bytes(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
         Ok(())
     }
 
@@ -109,14 +164,48 @@
             ..Default::default()
         };
         let payload_metadata = PayloadMetadata::Config(payload_config);
-        let config_descriptor = format_payload_config_descriptor(&payload_metadata)?;
+        let config_descriptor =
+            format_payload_config_descriptor(&payload_metadata, &NO_SUBCOMPONENTS)?;
         static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
             0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
             0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01,
             0x15, 0x58, 0xa1, 0x01, 0x6e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62,
             0x69, 0x6e, 0x61, 0x72, 0x79,
         ];
-        assert_eq!(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
+        assert_eq_bytes(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
+        Ok(())
+    }
+
+    #[test]
+    fn payload_metadata_with_subcomponents_formats_correctly() -> Result<()> {
+        let payload_metadata = PayloadMetadata::ConfigPath("/config_path".to_string());
+        let subcomponents = [
+            Subcomponent {
+                name: "apk1".to_string(),
+                version: 1,
+                code_hash: &[42u8],
+                authority_hash: Box::new([17u8]),
+            },
+            Subcomponent {
+                name: "apk2".to_string(),
+                version: 0x1000_0000_0001,
+                code_hash: &[43u8],
+                authority_hash: Box::new([19u8]),
+            },
+        ];
+        let config_descriptor =
+            format_payload_config_descriptor(&payload_metadata, &subcomponents)?;
+        // Verified using cbor.me.
+        static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
+            0xa3, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
+            0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01,
+            0x15, 0x57, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x61, 0x74,
+            0x68, 0x3a, 0x00, 0x01, 0x15, 0x59, 0x82, 0xa4, 0x01, 0x64, 0x61, 0x70, 0x6b, 0x31,
+            0x02, 0x01, 0x03, 0x81, 0x18, 0x2a, 0x04, 0x81, 0x11, 0xa4, 0x01, 0x64, 0x61, 0x70,
+            0x6b, 0x32, 0x02, 0x1b, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x81,
+            0x18, 0x2b, 0x04, 0x81, 0x13,
+        ];
+        assert_eq_bytes(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
         Ok(())
     }
 }
diff --git a/microdroid_manager/src/instance.rs b/microdroid_manager/src/instance.rs
index 2ff04f1..6c9e245 100644
--- a/microdroid_manager/src/instance.rs
+++ b/microdroid_manager/src/instance.rs
@@ -289,6 +289,8 @@
 pub struct ApkData {
     pub root_hash: Box<RootHash>,
     pub pubkey: Box<[u8]>,
+    pub package_name: String,
+    pub version_code: u64,
 }
 
 impl ApkData {
diff --git a/microdroid_manager/src/verify.rs b/microdroid_manager/src/verify.rs
index 06b15f7..22f3414 100644
--- a/microdroid_manager/src/verify.rs
+++ b/microdroid_manager/src/verify.rs
@@ -145,19 +145,26 @@
     // taken only when the root_hash is un-trustful which can be either when this is the first boot
     // of the VM or APK was updated in the host.
     // TODO(jooyung): consider multithreading to make this faster
-    let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
+
+    let main_apk_data =
+        get_data_from_apk(DM_MOUNTED_APK_PATH, root_hash_from_idsig, root_hash_trustful)?;
+
     let extra_apks_data = extra_root_hashes_from_idsig
         .into_iter()
         .enumerate()
         .map(|(i, extra_root_hash)| {
             let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
-            let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
-            Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
+            get_data_from_apk(&mount_path, extra_root_hash, extra_root_hashes_trustful[i])
         })
         .collect::<Result<Vec<_>>>()?;
 
     info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
 
+    // At this point, we can ensure that the root hashes from the idsig files are trusted, either
+    // because we have fully verified the APK signature (and apkdmverity checks all the data we
+    // verified is consistent with the root hash) or because we have the saved APK data which will
+    // be checked as identical to the data we have verified.
+
     // Use the salt from a verified instance, or generate a salt for a new instance.
     let salt = if let Some(saved_data) = saved_data {
         saved_data.salt.clone()
@@ -170,16 +177,36 @@
         salt
     };
 
-    // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
-    // fully verifying the APK or by comparing it with the saved root_hash.
     Ok(MicrodroidData {
         salt,
-        apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
+        apk_data: main_apk_data,
         extra_apks_data,
         apex_data: apex_data_from_payload,
     })
 }
 
+fn get_data_from_apk(
+    apk_path: &str,
+    root_hash: Box<RootHash>,
+    root_hash_trustful: bool,
+) -> Result<ApkData> {
+    let pubkey = get_public_key_from_apk(apk_path, root_hash_trustful)?;
+    // Read package name etc from the APK manifest. In the unlikely event that they aren't present
+    // we use the default values. We simply put these values in the DICE node for the payload, and
+    // users of that can decide how to handle blank information - there's no reason for us
+    // to fail starting a VM even with such a weird APK.
+    let manifest_info = get_manifest_info(apk_path)
+        .map_err(|e| warn!("Failed to read manifest info from APK: {e:?}"))
+        .unwrap_or_default();
+
+    Ok(ApkData {
+        root_hash,
+        pubkey,
+        package_name: manifest_info.package,
+        version_code: manifest_info.version_code,
+    })
+}
+
 fn get_apk_root_hash_from_idsig<P: AsRef<Path>>(idsig_path: P) -> Result<Box<RootHash>> {
     Ok(V4Signature::from_idsig_path(idsig_path)?.hashing_info.raw_root_hash)
 }
@@ -187,24 +214,14 @@
 fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
     let current_sdk = get_current_sdk()?;
 
-    let public_key_der = if !root_hash_trustful {
+    if !root_hash_trustful {
         verify(apk, current_sdk).context(MicrodroidError::PayloadVerificationFailed(format!(
             "failed to verify {}",
             apk
-        )))?
+        )))
     } else {
-        get_public_key_der(apk, current_sdk)?
-    };
-
-    match get_manifest_info(apk) {
-        Ok(manifest_info) => {
-            // TODO (b/299591171): Do something with this info
-            info!("Manifest info is {manifest_info:?}")
-        }
-        Err(e) => warn!("Failed to read manifest info from APK: {e:?}"),
-    };
-
-    Ok(public_key_der)
+        get_public_key_der(apk, current_sdk)
+    }
 }
 
 fn get_current_sdk() -> Result<u32> {
diff --git a/microdroid_manager/src/vm_config.cddl b/microdroid_manager/src/vm_config.cddl
new file mode 100644
index 0000000..052262d
--- /dev/null
+++ b/microdroid_manager/src/vm_config.cddl
@@ -0,0 +1,31 @@
+; Configuration Descriptor used in the DICE node that describes the payload of a Microdroid virtual
+; machine.
+;
+; See the Open DICE specification
+; https://pigweed.googlesource.com/open-dice/+/HEAD/docs/specification.md,
+; and the Android Profile for DICE
+; https://pigweed.googlesource.com/open-dice/+/HEAD/docs/android.md.
+;
+; CDDL for the normal Configuration Descriptor can be found at
+; https://cs.android.com/android/platform/superproject/main/+/main:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl
+
+; The configuration descriptor node for a Microdroid VM, with extensions to describe the contents
+; of the VM payload.
+VmConfigDescriptor = {
+    -70002 : "Microdroid payload",      ; Component name
+    (? -71000: tstr //                  ; Path to the payload config file
+    ? -71001: PayloadConfig),
+    ? -71002: [+ SubcomponentDescriptor],
+}
+
+PayloadConfig = {
+    1: tstr                             ; Path to the binary file where payload execution starts
+}
+
+; Describes a unit of code (e.g. an APK or an APEX) present inside the VM.
+SubcomponentDescriptor = {
+  1: tstr,                              ; Component name
+  2: uint,                              ; Security version
+  ? 3: bstr,                            ; Code hash
+  4: bstr,                              ; Authority hash
+}