Merge "rpc_binder: Refactor users of RpcServer to use new API"
diff --git a/encryptedstore/Android.bp b/encryptedstore/Android.bp
new file mode 100644
index 0000000..13ef1b9
--- /dev/null
+++ b/encryptedstore/Android.bp
@@ -0,0 +1,31 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+    name: "encryptedstore.defaults",
+    srcs: ["src/main.rs"],
+    edition: "2021",
+    prefer_rlib: true,
+    rustlibs: [
+        "libandroid_logger",
+        "libanyhow",
+        "liblibc",
+        "libclap",
+        "libhex",
+        "liblog_rust",
+        "libnix",
+        "libdm_rust",
+    ],
+    multilib: {
+        lib32: {
+            enabled: false,
+        },
+    },
+}
+
+rust_binary {
+    name: "encryptedstore",
+    defaults: ["encryptedstore.defaults"],
+    bootstrap: true,
+}
diff --git a/encryptedstore/src/main.rs b/encryptedstore/src/main.rs
new file mode 100644
index 0000000..d7d2382
--- /dev/null
+++ b/encryptedstore/src/main.rs
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! `encryptedstore` is a program that (as the name indicates) provides encrypted storage
+//! solution in a VM. This is based on dm-crypt & requires the (64 bytes') key & the backing device.
+//! It uses dm_rust lib.
+
+use anyhow::{ensure, Context, Result};
+use clap::{arg, App};
+use dm::{crypt::CipherType, util};
+use log::info;
+use std::ffi::CString;
+use std::fs::{create_dir_all, OpenOptions};
+use std::io::{Error, Read, Write};
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::fs::FileTypeExt;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+const MK2FS_BIN: &str = "/system/bin/mke2fs";
+const UNFORMATTED_STORAGE_MAGIC: &str = "UNFORMATTED-STORAGE";
+
+fn main() -> Result<()> {
+    android_logger::init_once(
+        android_logger::Config::default()
+            .with_tag("encryptedstore")
+            .with_min_level(log::Level::Info),
+    );
+    info!("Starting encryptedstore binary");
+
+    let matches = App::new("encryptedstore")
+        .args(&[
+            arg!(--blkdevice <FILE> "the block device backing the encrypted storage")
+                .required(true),
+            arg!(--key <KEY> "key (in hex) equivalent to 64 bytes)").required(true),
+            arg!(--mountpoint <MOUNTPOINT> "mount point for the storage").required(true),
+        ])
+        .get_matches();
+
+    let blkdevice = Path::new(matches.value_of("blkdevice").unwrap());
+    let key = matches.value_of("key").unwrap();
+    let mountpoint = Path::new(matches.value_of("mountpoint").unwrap());
+    encryptedstore_init(blkdevice, key, mountpoint).context(format!(
+        "Unable to initialize encryptedstore on {:?} & mount at {:?}",
+        blkdevice, mountpoint
+    ))?;
+    Ok(())
+}
+
+fn encryptedstore_init(blkdevice: &Path, key: &str, mountpoint: &Path) -> Result<()> {
+    ensure!(
+        std::fs::metadata(&blkdevice)
+            .context(format!("Failed to get metadata of {:?}", blkdevice))?
+            .file_type()
+            .is_block_device(),
+        "The path:{:?} is not of a block device",
+        blkdevice
+    );
+
+    let needs_formatting =
+        needs_formatting(blkdevice).context("Unable to check if formatting is required")?;
+    let crypt_device =
+        enable_crypt(blkdevice, key, "cryptdev").context("Unable to map crypt device")?;
+
+    // We might need to format it with filesystem if this is a "seen-for-the-first-time" device.
+    if needs_formatting {
+        info!("Freshly formatting the crypt device");
+        format_ext4(&crypt_device)?;
+    }
+    mount(&crypt_device, mountpoint).context(format!("Unable to mount {:?}", crypt_device))?;
+    Ok(())
+}
+
+fn enable_crypt(data_device: &Path, key: &str, name: &str) -> Result<PathBuf> {
+    let dev_size = util::blkgetsize64(data_device)?;
+    let key = hex::decode(key).context("Unable to decode hex key")?;
+    ensure!(key.len() == 64, "We need 64 bytes' key for aes-xts cipher for block encryption");
+
+    // Create the dm-crypt spec
+    let target = dm::crypt::DmCryptTargetBuilder::default()
+        .data_device(data_device, dev_size)
+        .cipher(CipherType::AES256XTS) // TODO(b/259253336) Move to HCTR2 based encryption.
+        .key(&key)
+        .build()
+        .context("Couldn't build the DMCrypt target")?;
+    let dm = dm::DeviceMapper::new()?;
+    dm.create_crypt_device(name, &target).context("Failed to create dm-crypt device")
+}
+
+// The disk contains UNFORMATTED_STORAGE_MAGIC to indicate we need to format the crypt device.
+// This function looks for it, zeroing it, if present.
+fn needs_formatting(data_device: &Path) -> Result<bool> {
+    let mut file = OpenOptions::new()
+        .read(true)
+        .write(true)
+        .open(data_device)
+        .with_context(|| format!("Failed to open {:?}", data_device))?;
+
+    let mut buf = [0; UNFORMATTED_STORAGE_MAGIC.len()];
+    file.read_exact(&mut buf)?;
+
+    if buf == UNFORMATTED_STORAGE_MAGIC.as_bytes() {
+        buf.fill(0);
+        file.write_all(&buf)?;
+        return Ok(true);
+    }
+    Ok(false)
+}
+
+fn format_ext4(device: &Path) -> Result<()> {
+    let mkfs_options = [
+        "-j",               // Create appropriate sized journal
+        "-O metadata_csum", // Metadata checksum for filesystem integrity
+    ];
+    let mut cmd = Command::new(MK2FS_BIN);
+    let status = cmd
+        .args(mkfs_options)
+        .arg(device)
+        .status()
+        .context(format!("failed to execute {}", MK2FS_BIN))?;
+    ensure!(status.success(), "mkfs failed with {:?}", status);
+    Ok(())
+}
+
+fn mount(source: &Path, mountpoint: &Path) -> Result<()> {
+    create_dir_all(mountpoint).context(format!("Failed to create {:?}", &mountpoint))?;
+    let mount_options = CString::new("").unwrap();
+    let source = CString::new(source.as_os_str().as_bytes())?;
+    let mountpoint = CString::new(mountpoint.as_os_str().as_bytes())?;
+    let fstype = CString::new("ext4").unwrap();
+
+    let ret = unsafe {
+        libc::mount(
+            source.as_ptr(),
+            mountpoint.as_ptr(),
+            fstype.as_ptr(),
+            libc::MS_NOSUID | libc::MS_NODEV | libc::MS_NOEXEC,
+            mount_options.as_ptr() as *const std::ffi::c_void,
+        )
+    };
+    if ret < 0 {
+        Err(Error::last_os_error()).context("mount failed")
+    } else {
+        Ok(())
+    }
+}
diff --git a/javalib/Android.bp b/javalib/Android.bp
index 2982a32..8421231 100644
--- a/javalib/Android.bp
+++ b/javalib/Android.bp
@@ -59,9 +59,6 @@
     impl_library_visibility: [
         "//packages/modules/Virtualization:__subpackages__",
     ],
-
-    // TODO(b/243512044): remove once we have API tracking files in prebuilts/sdk
-    unsafe_ignore_missing_latest_api: true,
 }
 
 prebuilt_apis {
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 91801ff..f2b223d 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -84,6 +84,7 @@
         "microdroid_plat_sepolicy_and_mapping.sha256",
         "microdroid_property_contexts",
         "microdroid_service_contexts",
+        "mke2fs",
 
         // TODO(b/195425111) these should be added automatically
         "libcrypto", // used by many (init_second_stage, microdroid_manager, toybox, etc)
@@ -107,6 +108,7 @@
                 "apkdmverity",
                 "authfs",
                 "authfs_service",
+                "encryptedstore",
                 "microdroid_crashdump_kernel",
                 "microdroid_kexec",
                 "microdroid_manager",
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 ef8f5f5..a53b401 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -83,6 +83,13 @@
 // 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;
+const ENCRYPTEDSTORE_MOUNTPOINT: &str = "/mnt/encryptedstore";
+
 #[derive(thiserror::Error, Debug)]
 enum MicrodroidError {
     #[error("Cannot connect to virtualization service: {0}")]
@@ -365,7 +372,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(
@@ -419,7 +434,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")
@@ -780,3 +800,28 @@
 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))
+        .args(["--mountpoint", ENCRYPTEDSTORE_MOUNTPOINT])
+        .spawn()
+        .context("encryptedstore failed")
+}
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
index b4b3795..ac37ee0 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
@@ -42,7 +42,6 @@
 
 public abstract class MicrodroidHostTestCaseBase extends BaseHostJUnit4Test {
     protected static final String TEST_ROOT = "/data/local/tmp/virt/";
-    protected static final String VIRT_APEX = "/apex/com.android.virt/";
     protected static final String LOG_PATH = TEST_ROOT + "log.txt";
     protected static final String CONSOLE_PATH = TEST_ROOT + "console.txt";
     private static final int TEST_VM_ADB_PORT = 8000;
@@ -191,14 +190,6 @@
         return pathLine.substring("package:".length());
     }
 
-    public static void shutdownMicrodroid(ITestDevice androidDevice, String cid)
-            throws DeviceNotAvailableException {
-        CommandRunner android = new CommandRunner(androidDevice);
-
-        // Shutdown the VM
-        android.run(VIRT_APEX + "bin/vm", "stop", cid);
-    }
-
     // Establish an adb connection to microdroid by letting Android forward the connection to
     // microdroid. Wait until the connection is established and microdroid is booted.
     public static void adbConnectToMicrodroid(ITestDevice androidDevice, String cid) {
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 4e9c501..cffaae1 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -85,6 +85,7 @@
     private static final String APK_NAME = "MicrodroidTestApp.apk";
     private static final String PACKAGE_NAME = "com.android.microdroid.test";
     private static final String SHELL_PACKAGE_NAME = "com.android.shell";
+    private static final String VIRT_APEX = "/apex/com.android.virt/";
 
     private static final int MIN_MEM_ARM64 = 145;
     private static final int MIN_MEM_X86_64 = 196;
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/PartitionType.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/PartitionType.aidl
index f25e674..774681a 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/PartitionType.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/PartitionType.aidl
@@ -29,4 +29,8 @@
      * The partition is initialized as an instance image which is formatted to hold per-VM secrets
      */
     ANDROID_VM_INSTANCE = 1,
+    /**
+     * The partition is initialized to back encryptedstore disk image formatted to indicate intent
+     */
+    ENCRYPTEDSTORE = 2,
 }
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index cfcfa2b..578960c 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -98,6 +98,8 @@
 
 const MICRODROID_OS_NAME: &str = "microdroid";
 
+const UNFORMATTED_STORAGE_MAGIC: &str = "UNFORMATTED-STORAGE";
+
 /// Singleton service for allocating globally-unique VM resources, such as the CID, and running
 /// singleton servers, like tombstone receiver.
 #[derive(Debug, Default)]
@@ -261,6 +263,7 @@
         match partition_type {
             PartitionType::RAW => Ok(()),
             PartitionType::ANDROID_VM_INSTANCE => format_as_android_vm_instance(&mut part),
+            PartitionType::ENCRYPTEDSTORE => format_as_encryptedstore(&mut part),
             _ => Err(Error::new(
                 ErrorKind::Unsupported,
                 format!("Unsupported partition type {:?}", partition_type),
@@ -593,6 +596,11 @@
     part.flush()
 }
 
+fn format_as_encryptedstore(part: &mut dyn Write) -> std::io::Result<()> {
+    part.write_all(UNFORMATTED_STORAGE_MAGIC.as_bytes())?;
+    part.flush()
+}
+
 fn prepare_ramdump_file(ramdump_path: &Path) -> Result<File> {
     File::create(ramdump_path).context(format!("Failed to create ramdump file {:?}", &ramdump_path))
 }
diff --git a/virtualizationservice/src/payload.rs b/virtualizationservice/src/payload.rs
index 233e74b..f6e8a7b 100644
--- a/virtualizationservice/src/payload.rs
+++ b/virtualizationservice/src/payload.rs
@@ -420,7 +420,7 @@
 
     if let Some(file) = storage_image {
         writable_partitions.push(Partition {
-            label: "encrypted-storage".to_owned(),
+            label: "encryptedstore".to_owned(),
             image: Some(ParcelFileDescriptor::new(file)),
             writable: true,
         });
diff --git a/vm/src/run.rs b/vm/src/run.rs
index 1f0433d..01b916b 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -103,7 +103,7 @@
                 service,
                 path,
                 storage_size.unwrap_or(10 * 1024 * 1024),
-                PartitionType::RAW,
+                PartitionType::ENCRYPTEDSTORE,
             )?;
         }
         Some(open_parcel_file(path, true)?)