Data written to instance.img has schema

... so that we can store structured data. Currently the data is encoded
in CBOR.

Bug: 193504400
Test: atest MicrodroidHostTestCases
Change-Id: I271b40f7d2c42fc42d793c36e27ca65cfaa9ba25
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index 0b1bc33..104c04a 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -27,6 +27,7 @@
         "libring",
         "librustutils",
         "libserde",
+        "libserde_cbor",
         "libserde_json",
         "libuuid",
         "libvsock",
diff --git a/microdroid_manager/src/instance.rs b/microdroid_manager/src/instance.rs
index 3ef3b63..51d74b0 100644
--- a/microdroid_manager/src/instance.rs
+++ b/microdroid_manager/src/instance.rs
@@ -37,6 +37,7 @@
 use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
 use ring::aead::{Aad, Algorithm, LessSafeKey, Nonce, UnboundKey, AES_256_GCM};
 use ring::hkdf::{Salt, HKDF_SHA256};
+use serde::{Deserialize, Serialize};
 use std::fs::{File, OpenOptions};
 use std::io::{Read, Seek, SeekFrom, Write};
 use uuid::Uuid;
@@ -104,7 +105,7 @@
     /// Reads the identity data that was written by microdroid manager. The returned data is
     /// plaintext, although it is stored encrypted. In case when the partition for microdroid
     /// manager doesn't exist, which can happen if it's the first boot, `Ok(None)` is returned.
-    pub fn read_microdroid_data(&mut self) -> Result<Option<Box<[u8]>>> {
+    pub fn read_microdroid_data(&mut self) -> Result<Option<MicrodroidData>> {
         let (header, offset) = self.locate_microdroid_header()?;
         if header.is_none() {
             return Ok(None);
@@ -134,14 +135,17 @@
         // Truncate to remove the tag
         data.truncate(plaintext_len);
 
-        Ok(Some(data.into_boxed_slice()))
+        let microdroid_data = serde_cbor::from_slice(data.as_slice())?;
+        Ok(Some(microdroid_data))
     }
 
     /// Writes identity data to the partition for microdroid manager. The partition is appended
     /// if it doesn't exist. The data is stored encrypted.
-    pub fn write_microdroid_data(&mut self, data: &[u8]) -> Result<()> {
+    pub fn write_microdroid_data(&mut self, microdroid_data: &MicrodroidData) -> Result<()> {
         let (header, offset) = self.locate_microdroid_header()?;
 
+        let mut data = serde_cbor::to_vec(microdroid_data)?;
+
         // By encrypting and signing the data, tag will be appended. The tag also becomes part of
         // the encrypted payload which will be written. In addition, a 12-bytes nonce will be
         // prepended (non-encrypted).
@@ -170,7 +174,6 @@
 
         // Then encrypt and sign the data. The non-encrypted input data is copied to a vector
         // because it is encrypted in place, and also the tag is appended.
-        let mut data = data.to_vec();
         get_key().seal_in_place_append_tag(nonce, Aad::from(&header), &mut data)?;
 
         // Persist the encrypted payload data
@@ -307,3 +310,17 @@
 
     ret
 }
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct MicrodroidData {
+    pub apk_data: ApkData,
+    // TODO(b/197053593) add data for APEXes
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct ApkData {
+    pub root_hash: Box<RootHash>,
+    // TODO(b/199143508) add cert
+}
+
+pub type RootHash = [u8];
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 9cbcaf1..4f192dc 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -18,7 +18,7 @@
 mod ioutil;
 mod metadata;
 
-use crate::instance::InstanceDisk;
+use crate::instance::{ApkData, InstanceDisk, MicrodroidData, RootHash};
 use anyhow::{anyhow, bail, Context, Result};
 use apkverify::verify;
 use binder::unstable_api::{new_spibinder, AIBinder};
@@ -92,27 +92,24 @@
 
     let metadata = metadata::load()?;
 
-    // Try to read roothash from the instance disk.
-    // TODO(jiyong): the data should have an internal structure.
     let mut instance = InstanceDisk::new()?;
-    let saved_roothash = instance.read_microdroid_data().context("Failed to read identity data")?;
-    let saved_roothash = saved_roothash.as_deref();
+    let data = instance.read_microdroid_data().context("Failed to read identity data")?;
+    let saved_root_hash: Option<&[u8]> =
+        if let Some(data) = data.as_ref() { Some(&data.apk_data.root_hash) } else { None };
 
     // Verify the payload before using it.
-    let verified_roothash =
-        verify_payload(saved_roothash).context("Payload verification failed")?;
-    if let Some(saved_roothash) = saved_roothash {
-        if saved_roothash == verified_roothash.as_ref() {
-            info!("Saved roothash is verified.");
+    let verified_root_hash =
+        verify_payload(saved_root_hash).context("Payload verification failed")?;
+    if let Some(saved_root_hash) = saved_root_hash {
+        if saved_root_hash == verified_root_hash.as_ref() {
+            info!("Saved root_hash is verified.");
         } else {
             bail!("Detected an update of the APK which isn't supported yet.");
         }
     } else {
-        info!("Saving APK roothash: {}", to_hex_string(verified_roothash.as_ref()));
-        // TODO(jiyong): the data should have an internal structure.
-        instance
-            .write_microdroid_data(verified_roothash.as_ref())
-            .context("Failed to write identity data")?;
+        info!("Saving APK root_hash: {}", to_hex_string(verified_root_hash.as_ref()));
+        let data = MicrodroidData { apk_data: ApkData { root_hash: verified_root_hash } };
+        instance.write_microdroid_data(&data).context("Failed to write identity data")?;
     }
 
     wait_for_apex_config_done()?;
@@ -141,42 +138,40 @@
     Ok(())
 }
 
-type Roothash = [u8];
-
-// Verify payload before executing it. Full verification (which is slow) is done when the roothash
+// Verify payload before executing it. Full verification (which is slow) is done when the root_hash
 // values from the idsig file and the instance disk are different. This function returns the
-// verified roothash that can be saved to the instance disk.
-fn verify_payload(roothash: Option<&Roothash>) -> Result<Box<Roothash>> {
+// verified root hash that can be saved to the instance disk.
+fn verify_payload(root_hash: Option<&RootHash>) -> Result<Box<RootHash>> {
     let start_time = SystemTime::now();
 
-    let roothash_from_idsig = get_apk_roothash_from_idsig()?;
-    let roothash_trustful = roothash == Some(&roothash_from_idsig);
+    let root_hash_from_idsig = get_apk_root_hash_from_idsig()?;
+    let root_hash_trustful = root_hash == Some(&root_hash_from_idsig);
 
-    // If roothash can be trusted, pass it to apkdmverity so that it uses the passed roothash
+    // If root_hash can be trusted, pass it to apkdmverity so that it uses the passed root_hash
     // instead of the value read from the idsig file.
-    if roothash_trustful {
-        let roothash = to_hex_string(roothash.unwrap());
-        system_properties::write("microdroid_manager.apk_roothash", &roothash)?;
+    if root_hash_trustful {
+        let root_hash = to_hex_string(root_hash.unwrap());
+        system_properties::write("microdroid_manager.apk_root_hash", &root_hash)?;
     }
 
     // Start apkdmverity and wait for the dm-verify block
     system_properties::write("ctl.start", "apkdmverity")?;
     ioutil::wait_for_file(DM_MOUNTED_APK_PATH, WAIT_TIMEOUT)?;
 
-    // Do the full verification if the roothash is un-trustful. This requires the full scanning of
+    // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
     // the APK file and therefore can be very slow if the APK is large. Note that this step is
-    // taken only when the roothash is un-trustful which can be either when this is the first boot
+    // 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
-    if !roothash_trustful {
+    if !root_hash_trustful {
         verify(DM_MOUNTED_APK_PATH).context(format!("failed to verify {}", DM_MOUNTED_APK_PATH))?;
     }
 
     info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
 
-    // At this point, we can ensure that the roothash from the idsig file is trusted, either by
-    // fully verifying the APK or by comparing it with the saved roothash.
-    Ok(roothash_from_idsig)
+    // 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(root_hash_from_idsig)
 }
 
 // Waits until linker config is generated
@@ -192,7 +187,7 @@
     Ok(())
 }
 
-fn get_apk_roothash_from_idsig() -> Result<Box<Roothash>> {
+fn get_apk_root_hash_from_idsig() -> Result<Box<RootHash>> {
     let mut idsig = File::open("/dev/block/by-name/microdroid-apk-idsig")?;
     let idsig = V4Signature::from(&mut idsig)?;
     Ok(idsig.hashing_info.raw_root_hash)