Microdroid: Map a dm-crypt dev on (virtio-blk)disk
1. microdroid_manager, on seeing a (named) block device dedicated for
storage, will run encryptedstore binary.
2. The key derived for the encryption will be derived from the dice
using the CDIs of *payload* as hashes.
3. encryptedstore binary will create the dm-crypt device using the
libdm_rust library.
Note: The salt used for the key is deterministic but I randomly got from
/dev/urandom. This ensures the key & payload secret are different.
Test: Run bin/vm run-app using --storage & --storage-size flag
Test: Write into the crypt device & check persistence by running another
VM with same instance image.
Bug: 241541860
Change-Id: I11d00343a040935dd90a232fe7c5ab4e06b6d145
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index 4b06b3e..44b4c01 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -23,6 +23,7 @@
"libdiced_sample_inputs",
"libdiced_utils",
"libglob",
+ "libhex",
"libitertools",
"libkernlog",
"libkeystore2_crypto_rust",
diff --git a/microdroid_manager/src/dice.rs b/microdroid_manager/src/dice.rs
index 3881db3..499835f 100644
--- a/microdroid_manager/src/dice.rs
+++ b/microdroid_manager/src/dice.rs
@@ -38,6 +38,16 @@
pub bcc: Vec<u8>,
}
+impl DiceContext {
+ pub fn get_sealing_key(&self, salt: &[u8], identifier: &[u8], keysize: u32) -> Result<ZVec> {
+ // Deterministically derive a key to use for sealing data based on salt. Use different salt
+ // for different keys.
+ let mut key = ZVec::new(keysize as usize)?;
+ hkdf(&mut key, Md::sha256(), &self.cdi_seal, salt, identifier)?;
+ Ok(key)
+ }
+}
+
/// Artifacts that are mapped into the process address space from the driver.
pub enum DiceDriver<'a> {
Real {
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index c18dd26..cc4b9dc 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -80,6 +80,12 @@
// SYNC WITH virtualizationservice/src/crosvm.rs
const FAILURE_SERIAL_DEVICE: &str = "/dev/ttyS1";
+/// Identifier for the key used for encrypted store.
+const ENCRYPTEDSTORE_BACKING_DEVICE: &str = "/dev/block/by-name/encryptedstore";
+const ENCRYPTEDSTORE_BIN: &str = "/system/bin/encryptedstore";
+const ENCRYPTEDSTORE_KEY_IDENTIFIER: &str = "encryptedstore_key";
+const ENCRYPTEDSTORE_KEYSIZE: u32 = 64;
+
#[derive(thiserror::Error, Debug)]
enum MicrodroidError {
#[error("Cannot connect to virtualization service: {0}")]
@@ -350,7 +356,15 @@
// To minimize the exposure to untrusted data, derive dice profile as soon as possible.
info!("DICE derivation for payload");
- let dice = dice_derivation(dice, &verified_data, &payload_metadata)?;
+ let dice_context = dice_derivation(dice, &verified_data, &payload_metadata)?;
+
+ // Run encryptedstore binary to prepare the storage
+ let encryptedstore_child = if Path::new(ENCRYPTEDSTORE_BACKING_DEVICE).exists() {
+ info!("Preparing encryptedstore ...");
+ Some(prepare_encryptedstore(&dice_context).context("encryptedstore run")?)
+ } else {
+ None
+ };
// Before reading a file from the APK, start zipfuse
run_zipfuse(
@@ -404,7 +418,12 @@
// Wait until zipfuse has mounted the APK so we can access the payload
wait_for_property_true(APK_MOUNT_DONE_PROP).context("Failed waiting for APK mount done")?;
- register_vm_payload_service(allow_restricted_apis, service.clone(), dice)?;
+ register_vm_payload_service(allow_restricted_apis, service.clone(), dice_context)?;
+
+ if let Some(mut child) = encryptedstore_child {
+ let exitcode = child.wait().context("Wait for encryptedstore child")?;
+ ensure!(exitcode.success(), "Unable to prepare encrypted storage. Exitcode={}", exitcode);
+ }
system_properties::write("dev.bootcomplete", "1").context("set dev.bootcomplete")?;
exec_task(task, service).context("Failed to run payload")
@@ -802,3 +821,27 @@
fn to_hex_string(buf: &[u8]) -> String {
buf.iter().map(|b| format!("{:02X}", b)).collect()
}
+
+fn prepare_encryptedstore(dice: &DiceContext) -> Result<Child> {
+ // Use a fixed salt to scope the derivation to this API.
+ // Generated using hexdump -vn32 -e'14/1 "0x%02X, " 1 "\n"' /dev/urandom
+ // TODO(b/241541860) : Move this (& other salts) to a salt container, i.e. a global enum
+ let salt = [
+ 0xFC, 0x1D, 0x35, 0x7B, 0x96, 0xF3, 0xEF, 0x17, 0x78, 0x7D, 0x70, 0xED, 0xEA, 0xFE, 0x1D,
+ 0x6F, 0xB3, 0xF9, 0x40, 0xCE, 0xDD, 0x99, 0x40, 0xAA, 0xA7, 0x0E, 0x92, 0x73, 0x90, 0x86,
+ 0x4A, 0x75,
+ ];
+ let key = dice.get_sealing_key(
+ &salt,
+ ENCRYPTEDSTORE_KEY_IDENTIFIER.as_bytes(),
+ ENCRYPTEDSTORE_KEYSIZE,
+ )?;
+
+ let mut cmd = Command::new(ENCRYPTEDSTORE_BIN);
+ cmd.arg("--blkdevice")
+ .arg(ENCRYPTEDSTORE_BACKING_DEVICE)
+ .arg("--key")
+ .arg(hex::encode(&*key))
+ .spawn()
+ .context("encryptedstore failed")
+}