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)