Snap for 8140651 from 64246ce4327d266631524e5f649ad4663fa92fa4 to sdk-release
Change-Id: I50e1eadd7c9fad58aa74a7846d3f9d73ee85402e
diff --git a/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb b/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
new file mode 100644
index 0000000..4199b2b
--- /dev/null
+++ b/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
@@ -0,0 +1,12 @@
+drops {
+ android_build_drop {
+ build_id: "8134799"
+ target: "u-boot_pvmfw"
+ source_file: "pvmfw.img"
+ }
+ dest_file: "pvmfw/pvmfw.img"
+ version: ""
+ version_group: ""
+ git_project: "platform/packages/modules/Virtualization"
+ git_branch: "master"
+}
diff --git a/apex/sign_virt_apex.py b/apex/sign_virt_apex.py
index 9a0fe1a..8fe3403 100644
--- a/apex/sign_virt_apex.py
+++ b/apex/sign_virt_apex.py
@@ -294,6 +294,8 @@
input_dir, 'etc', 'microdroid_bootconfig.app_debuggable')
bootconfig_full_debuggable = os.path.join(
input_dir, 'etc', 'microdroid_bootconfig.full_debuggable')
+ uboot_env_img = os.path.join(
+ input_dir, 'etc', 'uboot_env.img')
# Key(pubkey) for bootloader should match with the one used to make VBmeta below
# while it's okay to use different keys for other image files.
@@ -330,17 +332,21 @@
MakeVbmetaImage(args, key, vbmeta_img, images=[
boot_img, vendor_boot_img, init_boot_img, system_a_img, vendor_a_img])
- # Re-sign bootconfigs with the same key
+ # Re-sign bootconfigs and the uboot_env with the same key
bootconfig_sign_key = key
AddHashFooter(args, bootconfig_sign_key, bootconfig_normal)
AddHashFooter(args, bootconfig_sign_key, bootconfig_app_debuggable)
AddHashFooter(args, bootconfig_sign_key, bootconfig_full_debuggable)
+ AddHashFooter(args, bootconfig_sign_key, uboot_env_img)
- # Re-sign vbmeta_bootconfig with a chained_partition to "bootconfig"
- # Note that, for now, `key` and `bootconfig_sign_key` are the same, but technically they
- # can be different. Vbmeta records pubkeys which signed chained partitions.
+ # Re-sign vbmeta_bootconfig with chained_partitions to "bootconfig" and
+ # "uboot_env". Note that, for now, `key` and `bootconfig_sign_key` are the
+ # same, but technically they can be different. Vbmeta records pubkeys which
+ # signed chained partitions.
MakeVbmetaImage(args, key, vbmeta_bootconfig_img, chained_partitions={
- 'bootconfig': bootconfig_sign_key})
+ 'bootconfig': bootconfig_sign_key,
+ 'uboot_env': bootconfig_sign_key,
+ })
def VerifyVirtApex(args):
diff --git a/authfs/tests/Android.bp b/authfs/tests/Android.bp
index 6b3a474..ca403e8 100644
--- a/authfs/tests/Android.bp
+++ b/authfs/tests/Android.bp
@@ -14,7 +14,7 @@
"VirtualizationTestHelper",
],
test_suites: ["general-tests"],
- target_required: ["open_then_run_module"],
+ data_device_bins: ["open_then_run"],
data: [
":authfs_test_files",
":MicrodroidTestApp.signed",
@@ -22,16 +22,7 @@
}
rust_test {
- // PushFilePreparer can sometimes push the directory (if named "open_then_run", which contains
- // the actual executable in a per-architecture sub-directory) instead of the executable. This
- // makes it harder to use because the host Java test have to detect the executable path
- // dynamically, e.g. if it's a directory, append the device's architecture to build the actual
- // executable path. By simply renaming the module (thus the host directory), this forces
- // PushFilePreparer to always push the executable to the destination, so that the Java test can
- // easily locate the executable with a constant path.
- name: "open_then_run_module",
- stem: "open_then_run",
-
+ name: "open_then_run",
crate_name: "open_then_run",
srcs: ["open_then_run.rs"],
edition: "2018",
diff --git a/compos/Android.bp b/compos/Android.bp
index c54348a..0bcbcdd 100644
--- a/compos/Android.bp
+++ b/compos/Android.bp
@@ -28,6 +28,7 @@
"libprotobuf",
"libregex",
"libring",
+ "librustutils",
"libscopeguard",
],
prefer_rlib: true,
diff --git a/compos/aidl/com/android/compos/ICompOsService.aidl b/compos/aidl/com/android/compos/ICompOsService.aidl
index cead5d0..18e163e 100644
--- a/compos/aidl/com/android/compos/ICompOsService.aidl
+++ b/compos/aidl/com/android/compos/ICompOsService.aidl
@@ -21,6 +21,17 @@
/** {@hide} */
interface ICompOsService {
/**
+ * What type of compilation to perform.
+ */
+ @Backing(type="int")
+ enum CompilationMode {
+ /** Compile artifacts required by the current set of APEXes for use on reboot. */
+ NORMAL_COMPILE = 0,
+ /** Compile a full set of artifacts for test purposes. */
+ TEST_COMPILE = 1,
+ }
+
+ /**
* Initializes the service with the supplied encrypted private key blob. The key cannot be
* changed once initialized, so once initiailzed, a repeated call will fail with
* EX_ILLEGAL_STATE.
@@ -37,6 +48,7 @@
* through systemDirFd over AuthFS), and *CLASSPATH derived in the VM, to generate the same
* odrefresh output artifacts to the output directory (through outputDirFd).
*
+ * @param compilationMode The type of compilation to be performed
* @param systemDirFd An fd referring to /system
* @param outputDirFd An fd referring to the output directory, ART_APEX_DATA
* @param stagingDirFd An fd referring to the staging directory, e.g. ART_APEX_DATA/staging
@@ -46,8 +58,9 @@
* @param systemServerCompilerFilter The compiler filter used to compile system server
* @return odrefresh exit code
*/
- byte odrefresh(int systemDirFd, int outputDirFd, int stagingDirFd, String targetDirName,
- String zygoteArch, String systemServerCompilerFilter);
+ byte odrefresh(CompilationMode compilation_mode, int systemDirFd, int outputDirFd,
+ int stagingDirFd, String targetDirName, String zygoteArch,
+ String systemServerCompilerFilter);
/**
* Generate a new public/private key pair suitable for signing CompOs output files.
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index f4b3440..6a35fb0 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -122,6 +122,7 @@
configPath: config_path.to_owned(),
debugLevel: debug_level,
extraIdsigs: vec![idsig_manifest_apk_fd],
+ protectedVm: false,
memoryMib: VM_MEMORY_MIB,
numCpus: parameters.cpus.map_or(1, NonZeroU32::get) as i32,
cpuAffinity: parameters.cpu_set.clone(),
diff --git a/compos/composd/src/instance_manager.rs b/compos/composd/src/instance_manager.rs
index e8c1d9a..2f15cb5 100644
--- a/compos/composd/src/instance_manager.rs
+++ b/compos/composd/src/instance_manager.rs
@@ -43,22 +43,14 @@
}
pub fn start_pending_instance(&self) -> Result<Arc<CompOsInstance>> {
- let config_path = Some(PREFER_STAGED_VM_CONFIG_PATH.to_owned());
- let mut vm_parameters = VmParameters { config_path, ..Default::default() };
- vm_parameters.cpus = match system_properties::read(DEX2OAT_THREADS_PROP_NAME) {
- Ok(s) => Some(NonZeroU32::from_str(&s)?),
- Err(_) => {
- // dex2oat uses all CPUs by default. To match the behavior, give the VM all CPUs by
- // default.
- NonZeroU32::new(num_cpus::get() as u32)
- }
- };
- vm_parameters.cpu_set = system_properties::read(DEX2OAT_CPU_SET_PROP_NAME).ok();
+ let mut vm_parameters = new_vm_parameters()?;
+ vm_parameters.config_path = Some(PREFER_STAGED_VM_CONFIG_PATH.to_owned());
self.start_instance(PENDING_INSTANCE_DIR, vm_parameters)
}
pub fn start_test_instance(&self) -> Result<Arc<CompOsInstance>> {
- let vm_parameters = VmParameters { debug_mode: true, ..Default::default() };
+ let mut vm_parameters = new_vm_parameters()?;
+ vm_parameters.debug_mode = true;
self.start_instance(TEST_INSTANCE_DIR, vm_parameters)
}
@@ -90,6 +82,19 @@
}
}
+fn new_vm_parameters() -> Result<VmParameters> {
+ let cpus = match system_properties::read(DEX2OAT_THREADS_PROP_NAME) {
+ Ok(s) => Some(NonZeroU32::from_str(&s)?),
+ Err(_) => {
+ // dex2oat uses all CPUs by default. To match the behavior, give the VM all CPUs by
+ // default.
+ NonZeroU32::new(num_cpus::get() as u32)
+ }
+ };
+ let cpu_set = system_properties::read(DEX2OAT_CPU_SET_PROP_NAME).ok();
+ Ok(VmParameters { cpus, cpu_set, ..Default::default() })
+}
+
// Ensures we only run one instance at a time.
// Valid states:
// Starting: is_starting is true, running_instance is None.
diff --git a/compos/composd/src/odrefresh_task.rs b/compos/composd/src/odrefresh_task.rs
index 330f0ab..47ff590 100644
--- a/compos/composd/src/odrefresh_task.rs
+++ b/compos/composd/src/odrefresh_task.rs
@@ -23,7 +23,9 @@
};
use android_system_composd::binder::{Interface, Result as BinderResult, Strong};
use anyhow::{Context, Result};
-use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
+use compos_aidl_interface::aidl::com::android::compos::ICompOsService::{
+ CompilationMode::CompilationMode, ICompOsService,
+};
use compos_common::odrefresh::ExitCode;
use log::{error, warn};
use rustutils::system_properties;
@@ -68,6 +70,7 @@
pub fn start(
comp_os: Arc<CompOsInstance>,
+ compilation_mode: CompilationMode,
target_dir_name: String,
callback: &Strong<dyn ICompilationTaskCallback>,
) -> Result<OdrefreshTask> {
@@ -75,14 +78,19 @@
let task = RunningTask { comp_os, callback: callback.clone() };
let task = OdrefreshTask { running_task: Arc::new(Mutex::new(Some(task))) };
- task.clone().start_thread(service, target_dir_name);
+ task.clone().start_thread(service, compilation_mode, target_dir_name);
Ok(task)
}
- fn start_thread(self, service: Strong<dyn ICompOsService>, target_dir_name: String) {
+ fn start_thread(
+ self,
+ service: Strong<dyn ICompOsService>,
+ compilation_mode: CompilationMode,
+ target_dir_name: String,
+ ) {
thread::spawn(move || {
- let exit_code = run_in_vm(service, &target_dir_name);
+ let exit_code = run_in_vm(service, compilation_mode, &target_dir_name);
let task = self.take();
// We don't do the callback if cancel has already happened.
@@ -106,7 +114,11 @@
}
}
-fn run_in_vm(service: Strong<dyn ICompOsService>, target_dir_name: &str) -> Result<ExitCode> {
+fn run_in_vm(
+ service: Strong<dyn ICompOsService>,
+ compilation_mode: CompilationMode,
+ target_dir_name: &str,
+) -> Result<ExitCode> {
let output_root = Path::new(ART_APEX_DATA);
// We need to remove the target directory because odrefresh running in compos will create it
@@ -134,6 +146,7 @@
let system_server_compiler_filter =
system_properties::read("dalvik.vm.systemservercompilerfilter").unwrap_or_default();
let exit_code = service.odrefresh(
+ compilation_mode,
system_dir.as_raw_fd(),
output_dir.as_raw_fd(),
staging_dir.as_raw_fd(),
diff --git a/compos/composd/src/service.rs b/compos/composd/src/service.rs
index 6cdcd85..f4121e7 100644
--- a/compos/composd/src/service.rs
+++ b/compos/composd/src/service.rs
@@ -28,6 +28,7 @@
self, BinderFeatures, ExceptionCode, Interface, Status, Strong, ThreadState,
};
use anyhow::{Context, Result};
+use compos_aidl_interface::aidl::com::android::compos::ICompOsService::CompilationMode::CompilationMode;
use compos_common::binder::to_binder_result;
use rustutils::{users::AID_ROOT, users::AID_SYSTEM};
use std::sync::Arc;
@@ -72,7 +73,12 @@
let comp_os = self.instance_manager.start_pending_instance().context("Starting CompOS")?;
let target_dir_name = "compos-pending".to_owned();
- let task = OdrefreshTask::start(comp_os, target_dir_name, callback)?;
+ let task = OdrefreshTask::start(
+ comp_os,
+ CompilationMode::NORMAL_COMPILE,
+ target_dir_name,
+ callback,
+ )?;
Ok(BnCompilationTask::new_binder(task, BinderFeatures::default()))
}
@@ -84,7 +90,12 @@
let comp_os = self.instance_manager.start_test_instance().context("Starting CompOS")?;
let target_dir_name = "test-artifacts".to_owned();
- let task = OdrefreshTask::start(comp_os, target_dir_name, callback)?;
+ let task = OdrefreshTask::start(
+ comp_os,
+ CompilationMode::TEST_COMPILE,
+ target_dir_name,
+ callback,
+ )?;
Ok(BnCompilationTask::new_binder(task, BinderFeatures::default()))
}
diff --git a/compos/native/compos_native.cpp b/compos/native/compos_native.cpp
index f529421..81280fd 100644
--- a/compos/native/compos_native.cpp
+++ b/compos/native/compos_native.cpp
@@ -42,7 +42,7 @@
bssl::UniquePtr<RSA> key_pair(RSA_new());
// This function specifies that the public exponent is always 65537, which is good because
- // that's what odsign is expecting.
+ // that's what odsign is expecting.
if (!RSA_generate_key_fips(key_pair.get(), KEY_BITS, /*callback=*/nullptr)) {
return make_key_error("Failed to generate key pair");
}
diff --git a/compos/src/compilation.rs b/compos/src/compilation.rs
index 850a0a8..7e3834a 100644
--- a/compos/src/compilation.rs
+++ b/compos/src/compilation.rs
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-use anyhow::{bail, Context, Result};
+use anyhow::{anyhow, bail, Context, Result};
use log::{debug, info, warn};
use minijail::{self, Minijail};
use regex::Regex;
+use rustutils::system_properties;
+use std::collections::HashMap;
use std::env;
use std::ffi::OsString;
use std::fs::read_dir;
-use std::os::unix::io::AsRawFd;
use std::path::{self, Path, PathBuf};
use std::process::Command;
@@ -34,16 +35,14 @@
},
IAuthFsService::IAuthFsService,
};
-use authfs_aidl_interface::binder::{ParcelFileDescriptor, Strong};
+use authfs_aidl_interface::binder::Strong;
+use compos_aidl_interface::aidl::com::android::compos::ICompOsService::CompilationMode::CompilationMode;
use compos_common::odrefresh::ExitCode;
const FD_SERVER_PORT: i32 = 3264; // TODO: support dynamic port
-/// The number that represents the file descriptor number expecting by the task. The number may be
-/// meaningless in the current process.
-pub type PseudoRawFd = i32;
-
pub struct OdrefreshContext<'a> {
+ compilation_mode: CompilationMode,
system_dir_fd: i32,
output_dir_fd: i32,
staging_dir_fd: i32,
@@ -54,6 +53,7 @@
impl<'a> OdrefreshContext<'a> {
pub fn new(
+ compilation_mode: CompilationMode,
system_dir_fd: i32,
output_dir_fd: i32,
staging_dir_fd: i32,
@@ -61,6 +61,13 @@
zygote_arch: &'a str,
system_server_compiler_filter: &'a str,
) -> Result<Self> {
+ if compilation_mode != CompilationMode::NORMAL_COMPILE {
+ let debuggable = system_properties::read_bool("ro.boot.microdroid.debuggable", false)?;
+ if !debuggable {
+ bail!("Requested compilation mode only available in debuggable VMs");
+ }
+ }
+
if system_dir_fd < 0 || output_dir_fd < 0 || staging_dir_fd < 0 {
bail!("The remote FDs are expected to be non-negative");
}
@@ -79,6 +86,7 @@
// CompOS.
Ok(Self {
+ compilation_mode,
system_dir_fd,
output_dir_fd,
staging_dir_fd,
@@ -114,19 +122,22 @@
let authfs = authfs_service.mount(&authfs_config)?;
let mountpoint = PathBuf::from(authfs.getMountPoint()?);
+ // Make a copy of our environment as the basis of the one we will give odrefresh
+ let mut odrefresh_vars = EnvMap::from_current_env();
+
let mut android_root = mountpoint.clone();
android_root.push(context.system_dir_fd.to_string());
android_root.push("system");
- env::set_var("ANDROID_ROOT", &android_root);
+ odrefresh_vars.set("ANDROID_ROOT", path_to_str(&android_root)?);
debug!("ANDROID_ROOT={:?}", &android_root);
let art_apex_data = mountpoint.join(context.output_dir_fd.to_string());
- env::set_var("ART_APEX_DATA", &art_apex_data);
+ odrefresh_vars.set("ART_APEX_DATA", path_to_str(&art_apex_data)?);
debug!("ART_APEX_DATA={:?}", &art_apex_data);
let staging_dir = mountpoint.join(context.staging_dir_fd.to_string());
- set_classpaths(&android_root)?;
+ set_classpaths(&mut odrefresh_vars, &android_root)?;
let mut args = vec![
"odrefresh".to_string(),
@@ -144,18 +155,21 @@
));
}
- args.push("--compile".to_string());
+ let compile_flag = match context.compilation_mode {
+ CompilationMode::NORMAL_COMPILE => "--compile",
+ CompilationMode::TEST_COMPILE => "--force-compile",
+ other => bail!("Unknown compilation mode {:?}", other),
+ };
+ args.push(compile_flag.to_string());
debug!("Running odrefresh with args: {:?}", &args);
- let jail = spawn_jailed_task(odrefresh_path, &args, Vec::new() /* fd_mapping */)
+ let jail = spawn_jailed_task(odrefresh_path, &args, &odrefresh_vars.into_env())
.context("Spawn odrefresh")?;
let exit_code = match jail.wait() {
- Ok(_) => Result::<u8>::Ok(0),
- Err(minijail::Error::ReturnCode(exit_code)) => Ok(exit_code),
- Err(e) => {
- bail!("Unexpected minijail error: {}", e)
- }
- }?;
+ Ok(_) => 0,
+ Err(minijail::Error::ReturnCode(exit_code)) => exit_code,
+ Err(e) => bail!("Unexpected minijail error: {}", e),
+ };
let exit_code = ExitCode::from_i32(exit_code.into())?;
info!("odrefresh exited with {:?}", exit_code);
@@ -173,9 +187,13 @@
Ok(exit_code)
}
-fn set_classpaths(android_root: &Path) -> Result<()> {
+fn path_to_str(path: &Path) -> Result<&str> {
+ path.to_str().ok_or_else(|| anyhow!("Bad path {:?}", path))
+}
+
+fn set_classpaths(odrefresh_vars: &mut EnvMap, android_root: &Path) -> Result<()> {
let export_lines = run_derive_classpath(android_root)?;
- load_classpath_vars(&export_lines)
+ load_classpath_vars(odrefresh_vars, &export_lines)
}
fn run_derive_classpath(android_root: &Path) -> Result<String> {
@@ -203,15 +221,14 @@
String::from_utf8(result.stdout).context("Converting derive_classpath output")
}
-fn load_classpath_vars(export_lines: &str) -> Result<()> {
+fn load_classpath_vars(odrefresh_vars: &mut EnvMap, export_lines: &str) -> Result<()> {
// Each line should be in the format "export <var name> <value>"
let pattern = Regex::new(r"^export ([^ ]+) ([^ ]+)$").context("Failed to construct Regex")?;
for line in export_lines.lines() {
if let Some(captures) = pattern.captures(line) {
let name = &captures[1];
let value = &captures[2];
- // TODO(b/213416778) Don't modify our env, construct a fresh one for odrefresh
- env::set_var(name, value);
+ odrefresh_vars.set(name, value);
} else {
warn!("Malformed line from derive_classpath: {}", line);
}
@@ -238,14 +255,28 @@
Ok(())
}
-fn spawn_jailed_task(
- executable: &Path,
- args: &[String],
- fd_mapping: Vec<(ParcelFileDescriptor, PseudoRawFd)>,
-) -> Result<Minijail> {
+fn spawn_jailed_task(executable: &Path, args: &[String], env_vars: &[String]) -> Result<Minijail> {
// TODO(b/185175567): Run in a more restricted sandbox.
let jail = Minijail::new()?;
- let preserve_fds: Vec<_> = fd_mapping.iter().map(|(f, id)| (f.as_raw_fd(), *id)).collect();
- let _pid = jail.run_remap(executable, preserve_fds.as_slice(), args)?;
+ let keep_fds = [];
+ let command = minijail::Command::new_for_path(executable, &keep_fds, args, Some(env_vars))?;
+ let _pid = jail.run_command(command)?;
Ok(jail)
}
+
+struct EnvMap(HashMap<String, String>);
+
+impl EnvMap {
+ fn from_current_env() -> Self {
+ Self(env::vars().collect())
+ }
+
+ fn set(&mut self, key: &str, value: &str) {
+ self.0.insert(key.to_owned(), value.to_owned());
+ }
+
+ fn into_env(self) -> Vec<String> {
+ // execve() expects an array of "k=v" strings, rather than a list of (k, v) pairs.
+ self.0.into_iter().map(|(k, v)| k + "=" + &v).collect()
+ }
+}
diff --git a/compos/src/compsvc.rs b/compos/src/compsvc.rs
index 422f271..9d754a7 100644
--- a/compos/src/compsvc.rs
+++ b/compos/src/compsvc.rs
@@ -31,7 +31,7 @@
use authfs_aidl_interface::aidl::com::android::virt::fs::IAuthFsService::IAuthFsService;
use compos_aidl_interface::aidl::com::android::compos::{
CompOsKeyData::CompOsKeyData,
- ICompOsService::{BnCompOsService, ICompOsService},
+ ICompOsService::{BnCompOsService, CompilationMode::CompilationMode, ICompOsService},
};
use compos_aidl_interface::binder::{
BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Strong,
@@ -82,6 +82,7 @@
fn odrefresh(
&self,
+ compilation_mode: CompilationMode,
system_dir_fd: i32,
output_dir_fd: i32,
staging_dir_fd: i32,
@@ -90,6 +91,7 @@
system_server_compiler_filter: &str,
) -> BinderResult<i8> {
let context = to_binder_result(OdrefreshContext::new(
+ compilation_mode,
system_dir_fd,
output_dir_fd,
staging_dir_fd,
diff --git a/compos/tests/java/android/compos/test/ComposKeyTestCase.java b/compos/tests/java/android/compos/test/ComposKeyTestCase.java
index d59d3d9..49235fe 100644
--- a/compos/tests/java/android/compos/test/ComposKeyTestCase.java
+++ b/compos/tests/java/android/compos/test/ComposKeyTestCase.java
@@ -38,13 +38,25 @@
@RunWith(DeviceJUnit4ClassRunner.class)
public final class ComposKeyTestCase extends VirtualizationTestCaseBase {
- /** Wait time for service to be ready on boot */
+ /**
+ * Wait time for service to be ready on boot
+ */
private static final int READY_LATENCY_MS = 10 * 1000; // 10 seconds
- /** Path to compos_key_cmd tool */
+ /**
+ * Path to compos_key_cmd tool
+ */
private static final String COMPOS_KEY_CMD_BIN = "/apex/com.android.compos/bin/compos_key_cmd";
- /** Config of the test VM. This is a path inside the APK. */
+ /**
+ * Path to the com.android.compos.payload APK
+ */
+ private static final String COMPOS_PAYLOAD_APK_PATH =
+ "/apex/com.android.compos/app/CompOSPayloadApp/CompOSPayloadApp.apk";
+
+ /**
+ * Config of the test VM. This is a path inside the APK.
+ */
private static final String VM_TEST_CONFIG_PATH = "assets/vm_test_config.json";
private String mCid;
@@ -134,7 +146,9 @@
getDevice(),
getBuild(),
/* apkName, no need to install */ null,
- packageName,
+ COMPOS_PAYLOAD_APK_PATH,
+ /* packageName - not needed, we know the path */ null,
+ /* extraIdSigPaths */ null,
VM_TEST_CONFIG_PATH,
/* debug */ true,
/* use default memoryMib */ 0,
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index 7d1f9b0..04a90e0 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -51,6 +51,7 @@
private static final String KEY_APKPATH = "apkPath";
private static final String KEY_PAYLOADCONFIGPATH = "payloadConfigPath";
private static final String KEY_DEBUGLEVEL = "debugLevel";
+ private static final String KEY_PROTECTED_VM = "protectedVm";
private static final String KEY_MEMORY_MIB = "memoryMib";
private static final String KEY_NUM_CPUS = "numCpus";
private static final String KEY_CPU_AFFINITY = "cpuAffinity";
@@ -83,6 +84,11 @@
private final DebugLevel mDebugLevel;
/**
+ * Whether to run the VM in protected mode, so the host can't access its memory.
+ */
+ private final boolean mProtectedVm;
+
+ /**
* The amount of RAM to give the VM, in MiB. If this is 0 or negative the default will be used.
*/
private final int mMemoryMib;
@@ -111,6 +117,7 @@
@NonNull Signature[] certs,
@NonNull String payloadConfigPath,
DebugLevel debugLevel,
+ boolean protectedVm,
int memoryMib,
int numCpus,
String cpuAffinity) {
@@ -118,6 +125,7 @@
mCerts = certs;
mPayloadConfigPath = payloadConfigPath;
mDebugLevel = debugLevel;
+ mProtectedVm = protectedVm;
mMemoryMib = memoryMib;
mNumCpus = numCpus;
mCpuAffinity = cpuAffinity;
@@ -149,11 +157,12 @@
throw new VirtualMachineException("No payloadConfigPath");
}
final DebugLevel debugLevel = DebugLevel.values()[b.getInt(KEY_DEBUGLEVEL)];
+ final boolean protectedVm = b.getBoolean(KEY_PROTECTED_VM);
final int memoryMib = b.getInt(KEY_MEMORY_MIB);
final int numCpus = b.getInt(KEY_NUM_CPUS);
final String cpuAffinity = b.getString(KEY_CPU_AFFINITY);
- return new VirtualMachineConfig(apkPath, certs, payloadConfigPath, debugLevel, memoryMib,
- numCpus, cpuAffinity);
+ return new VirtualMachineConfig(apkPath, certs, payloadConfigPath, debugLevel, protectedVm,
+ memoryMib, numCpus, cpuAffinity);
}
/** Persists this config to a stream, for example a file. */
@@ -169,6 +178,8 @@
b.putStringArray(KEY_CERTS, certs);
b.putString(KEY_PAYLOADCONFIGPATH, mPayloadConfigPath);
b.putInt(KEY_DEBUGLEVEL, mDebugLevel.ordinal());
+ b.putBoolean(KEY_PROTECTED_VM, mProtectedVm);
+ b.putInt(KEY_NUM_CPUS, mNumCpus);
if (mMemoryMib > 0) {
b.putInt(KEY_MEMORY_MIB, mMemoryMib);
}
@@ -219,6 +230,7 @@
parcel.debugLevel = VirtualMachineAppConfig.DebugLevel.FULL;
break;
}
+ parcel.protectedVm = mProtectedVm;
parcel.memoryMib = mMemoryMib;
parcel.numCpus = mNumCpus;
parcel.cpuAffinity = mCpuAffinity;
@@ -230,16 +242,17 @@
private Context mContext;
private String mPayloadConfigPath;
private DebugLevel mDebugLevel;
+ private boolean mProtectedVm;
private int mMemoryMib;
private int mNumCpus;
private String mCpuAffinity;
- // TODO(jiyong): add more items like # of cpu, size of ram, debuggability, etc.
/** Creates a builder for the given context (APK), and the payload config file in APK. */
public Builder(@NonNull Context context, @NonNull String payloadConfigPath) {
mContext = context;
mPayloadConfigPath = payloadConfigPath;
mDebugLevel = DebugLevel.NONE;
+ mProtectedVm = false;
mNumCpus = 1;
mCpuAffinity = null;
}
@@ -250,6 +263,12 @@
return this;
}
+ /** Sets whether to protect the VM memory from the host. Defaults to false. */
+ public Builder protectedVm(boolean protectedVm) {
+ mProtectedVm = protectedVm;
+ return this;
+ }
+
/**
* Sets the amount of RAM to give the VM. If this is zero or negative then the default will
* be used.
@@ -309,8 +328,8 @@
}
return new VirtualMachineConfig(
- apkPath, certs, mPayloadConfigPath, mDebugLevel, mMemoryMib, mNumCpus,
- mCpuAffinity);
+ apkPath, certs, mPayloadConfigPath, mDebugLevel, mProtectedVm, mMemoryMib,
+ mNumCpus, mCpuAffinity);
}
}
}
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index e078108..3566bd2 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -73,7 +73,6 @@
"apexd",
"debuggerd",
"diced.microdroid",
- "keystore2_microdroid",
"linker",
"linkerconfig",
"servicemanager.microdroid",
@@ -81,15 +80,10 @@
"cgroups.json",
"public.libraries.android.txt",
- // TODO(b/185767624): remove hidl after full keymint support
- "hwservicemanager",
-
"microdroid_plat_sepolicy_and_mapping.sha256",
"microdroid_file_contexts",
- "microdroid_hwservice_contexts",
"microdroid_property_contexts",
"microdroid_service_contexts",
- "microdroid_keystore2_key_contexts",
"microdroid_compatibility_matrix",
"microdroid_manifest",
@@ -179,7 +173,6 @@
use_avb: true,
deps: [
"android.hardware.security.dice-service.microdroid",
- "android.hardware.security.keymint-service.microdroid",
"microdroid_fstab",
"microdroid_precompiled_sepolicy.plat_sepolicy_and_mapping.sha256",
"microdroid_vendor_manifest",
@@ -355,6 +348,10 @@
name: "bootconfig",
private_key: ":microdroid_sign_key",
},
+ {
+ name: "uboot_env",
+ private_key: ":microdroid_sign_key",
+ },
],
}
@@ -531,18 +528,42 @@
genrule {
name: "microdroid_uboot_env_gen",
- tools: ["mkenvimage_host"],
- srcs: ["uboot-env.txt"],
+ tools: [
+ "mkenvimage_host",
+ "avbtool",
+ ],
+ srcs: [
+ "uboot-env.txt",
+ ":microdroid_sign_key",
+ ],
out: ["output.img"],
- cmd: "$(location mkenvimage_host) -s 4096 -o $(out) $(in)",
+ cmd: "$(location mkenvimage_host) -s 4096 -o $(out) $(location uboot-env.txt) && " +
+ "$(location avbtool) add_hash_footer " +
+ "--algorithm SHA256_RSA4096 " +
+ "--partition_name uboot_env " +
+ "--key $(location :microdroid_sign_key) " +
+ "--partition_size $$(( " + avb_hash_footer_kb + " * 1024 + ( $$(stat --format=%s $(out)) + 4096 - 1 ) / 4096 * 4096 )) " +
+ "--image $(out)",
}
genrule {
name: "microdroid_uboot_env_gen_x86_64",
- tools: ["mkenvimage_host"],
- srcs: ["uboot-env-x86_64.txt"],
+ tools: [
+ "mkenvimage_host",
+ "avbtool",
+ ],
+ srcs: [
+ "uboot-env-x86_64.txt",
+ ":microdroid_sign_key",
+ ],
out: ["output.img"],
- cmd: "$(location mkenvimage_host) -s 4096 -o $(out) $(in)",
+ cmd: "$(location mkenvimage_host) -s 4096 -o $(out) $(location uboot-env-x86_64.txt) && " +
+ "$(location avbtool) add_hash_footer " +
+ "--algorithm SHA256_RSA4096 " +
+ "--partition_name uboot_env " +
+ "--key $(location :microdroid_sign_key) " +
+ "--partition_size $$(( " + avb_hash_footer_kb + " * 1024 + ( $$(stat --format=%s $(out)) + 4096 - 1 ) / 4096 * 4096 )) " +
+ "--image $(out)",
}
// Note that keys can be different for filesystem images even though we're using the same key
diff --git a/microdroid/init.rc b/microdroid/init.rc
index e76260e..ebe2464 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -83,9 +83,6 @@
setprop ro.debuggable ${ro.boot.microdroid.debuggable:-0}
- # TODO(b/185767624): remove hidl after full keymint support
- start hwservicemanager
-
on init && property:ro.boot.logd.enabled=1
# Start logd before any other services run to ensure we capture all of their logs.
start logd
@@ -138,21 +135,15 @@
# We restorecon /data in case the userdata partition has been reset.
restorecon /data
- # set up keystore directory structure first so that we can end early boot
+ # set up misc directory structure first so that we can end early boot
# and start apexd
mkdir /data/misc 01771 system misc
- mkdir /data/misc/keystore 0700 keystore keystore
# work around b/183668221
- restorecon /data/misc /data/misc/keystore
-
- start keystore2
+ restorecon /data/misc
mkdir /data/misc/authfs 0700 root root
start authfs_service
-on late-fs
- start vendor.keymint-microdroid
-
on post-fs-data
mark_post_data
@@ -169,12 +160,6 @@
start tombstoned
- # Boot level 30
- # odsign signing keys have MAX_BOOT_LEVEL=30
- # This is currently the earliest boot level, but we start at 30
- # to leave room for earlier levels.
- setprop keystore.boot_level 30
-
# For security reasons, /data/local/tmp should always be empty.
# Do not place files or directories in /data/local/tmp
mkdir /data/local 0751 root root
diff --git a/microdroid/keymint/Android.bp b/microdroid/keymint/Android.bp
deleted file mode 100644
index 7915ada..0000000
--- a/microdroid/keymint/Android.bp
+++ /dev/null
@@ -1,41 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_binary {
- name: "android.hardware.security.keymint-service.microdroid",
- relative_install_path: "hw",
- init_rc: ["android.hardware.security.keymint-service.microdroid.rc"],
- vintf_fragments: [
- "android.hardware.security.keymint-service.microdroid.xml",
- ],
- vendor: true,
- cflags: [
- "-Wall",
- "-Wextra",
- ],
- defaults: [
- "keymint_use_latest_hal_aidl_ndk_shared",
- ],
- shared_libs: [
- "lib_android_keymaster_keymint_utils",
- "libbase",
- "libbinder_ndk",
- "libcppbor_external",
- "libcrypto",
- "libkeymaster_portable",
- "libkeymint",
- "liblog",
- "libpuresoftkeymasterdevice",
- "libsoft_attestation_cert",
- "libutils",
- ],
- local_include_dirs: [
- "include",
- ],
- srcs: [
- "MicrodroidKeyMintDevice.cpp",
- "MicrodroidKeymasterContext.cpp",
- "service.cpp",
- ],
-}
diff --git a/microdroid/keymint/MicrodroidKeyMintDevice.cpp b/microdroid/keymint/MicrodroidKeyMintDevice.cpp
deleted file mode 100644
index c2f01f2..0000000
--- a/microdroid/keymint/MicrodroidKeyMintDevice.cpp
+++ /dev/null
@@ -1,431 +0,0 @@
-/*
- * Copyright 2021, 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.
- */
-
-#define LOG_TAG "android.hardware.security.keymint-impl"
-#include "MicrodroidKeyMintDevice.h"
-
-#include <AndroidKeyMintOperation.h>
-#include <KeyMintUtils.h>
-#include <aidl/android/hardware/security/keymint/ErrorCode.h>
-#include <android-base/logging.h>
-#include <keymaster/android_keymaster.h>
-#include <keymaster/contexts/pure_soft_keymaster_context.h>
-#include <keymaster/keymaster_configuration.h>
-
-#include "MicrodroidKeyMintDevice.h"
-#include "MicrodroidKeymasterContext.h"
-
-namespace aidl::android::hardware::security::keymint {
-
-using namespace keymaster; // NOLINT(google-build-using-namespace)
-
-using km_utils::authToken2AidlVec;
-using km_utils::kmBlob2vector;
-using km_utils::kmError2ScopedAStatus;
-using km_utils::kmParam2Aidl;
-using km_utils::KmParamSet;
-using km_utils::kmParamSet2Aidl;
-using km_utils::legacy_enum_conversion;
-using secureclock::TimeStampToken;
-
-namespace {
-
-vector<KeyCharacteristics> convertKeyCharacteristics(const AuthorizationSet& requestParams,
- const AuthorizationSet& sw_enforced,
- const AuthorizationSet& hw_enforced,
- bool include_keystore_enforced = true) {
- KeyCharacteristics keyMintEnforced{SecurityLevel::SOFTWARE, {}};
- KeyCharacteristics keystoreEnforced{SecurityLevel::KEYSTORE, {}};
- CHECK(hw_enforced.empty()) << "Hardware-enforced list is non-empty for pure SW KeyMint";
-
- // This is a pure software implementation, so all tags are in sw_enforced.
- // We need to walk through the SW-enforced list and figure out which tags to
- // return in the software list and which in the keystore list.
-
- for (auto& entry : sw_enforced) {
- switch (entry.tag) {
- /* Invalid and unused */
- case KM_TAG_ECIES_SINGLE_HASH_MODE:
- case KM_TAG_INVALID:
- case KM_TAG_KDF:
- case KM_TAG_ROLLBACK_RESISTANCE:
- CHECK(false) << "We shouldn't see tag " << entry.tag;
- break;
-
- /* Unimplemented */
- case KM_TAG_ALLOW_WHILE_ON_BODY:
- case KM_TAG_BOOTLOADER_ONLY:
- case KM_TAG_EARLY_BOOT_ONLY:
- case KM_TAG_ROLLBACK_RESISTANT:
- case KM_TAG_STORAGE_KEY:
- case KM_TAG_TRUSTED_CONFIRMATION_REQUIRED:
- case KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED:
- break;
-
- /* Keystore-enforced if not locally generated. */
- case KM_TAG_CREATION_DATETIME:
- // A KeyMaster implementation is required to add this tag to generated/imported
- // keys. A KeyMint implementation is not required to create this tag, only to echo
- // it back if it was included in the key generation/import request.
- if (requestParams.Contains(KM_TAG_CREATION_DATETIME)) {
- keystoreEnforced.authorizations.push_back(kmParam2Aidl(entry));
- }
- break;
-
- /* Disallowed in KeyCharacteristics */
- case KM_TAG_APPLICATION_DATA:
- case KM_TAG_ATTESTATION_APPLICATION_ID:
- break;
-
- /* Not key characteristics */
- case KM_TAG_ASSOCIATED_DATA:
- case KM_TAG_ATTESTATION_CHALLENGE:
- case KM_TAG_ATTESTATION_ID_BRAND:
- case KM_TAG_ATTESTATION_ID_DEVICE:
- case KM_TAG_ATTESTATION_ID_IMEI:
- case KM_TAG_ATTESTATION_ID_MANUFACTURER:
- case KM_TAG_ATTESTATION_ID_MEID:
- case KM_TAG_ATTESTATION_ID_MODEL:
- case KM_TAG_ATTESTATION_ID_PRODUCT:
- case KM_TAG_ATTESTATION_ID_SERIAL:
- case KM_TAG_AUTH_TOKEN:
- case KM_TAG_CERTIFICATE_SERIAL:
- case KM_TAG_CERTIFICATE_SUBJECT:
- case KM_TAG_CERTIFICATE_NOT_AFTER:
- case KM_TAG_CERTIFICATE_NOT_BEFORE:
- case KM_TAG_CONFIRMATION_TOKEN:
- case KM_TAG_DEVICE_UNIQUE_ATTESTATION:
- case KM_TAG_IDENTITY_CREDENTIAL_KEY:
- case KM_TAG_MAC_LENGTH:
- case KM_TAG_NONCE:
- case KM_TAG_RESET_SINCE_ID_ROTATION:
- case KM_TAG_ROOT_OF_TRUST:
- case KM_TAG_UNIQUE_ID:
- break;
-
- /* KeyMint-enforced */
- case KM_TAG_ALGORITHM:
- case KM_TAG_APPLICATION_ID:
- case KM_TAG_AUTH_TIMEOUT:
- case KM_TAG_BLOB_USAGE_REQUIREMENTS:
- case KM_TAG_BLOCK_MODE:
- case KM_TAG_BOOT_PATCHLEVEL:
- case KM_TAG_CALLER_NONCE:
- case KM_TAG_DIGEST:
- case KM_TAG_EC_CURVE:
- case KM_TAG_EXPORTABLE:
- case KM_TAG_INCLUDE_UNIQUE_ID:
- case KM_TAG_KEY_SIZE:
- case KM_TAG_MAX_USES_PER_BOOT:
- case KM_TAG_MIN_MAC_LENGTH:
- case KM_TAG_MIN_SECONDS_BETWEEN_OPS:
- case KM_TAG_NO_AUTH_REQUIRED:
- case KM_TAG_ORIGIN:
- case KM_TAG_OS_PATCHLEVEL:
- case KM_TAG_OS_VERSION:
- case KM_TAG_PADDING:
- case KM_TAG_PURPOSE:
- case KM_TAG_RSA_OAEP_MGF_DIGEST:
- case KM_TAG_RSA_PUBLIC_EXPONENT:
- case KM_TAG_UNLOCKED_DEVICE_REQUIRED:
- case KM_TAG_USER_AUTH_TYPE:
- case KM_TAG_USER_SECURE_ID:
- case KM_TAG_VENDOR_PATCHLEVEL:
- keyMintEnforced.authorizations.push_back(kmParam2Aidl(entry));
- break;
-
- /* Keystore-enforced */
- case KM_TAG_ACTIVE_DATETIME:
- case KM_TAG_ALL_APPLICATIONS:
- case KM_TAG_ALL_USERS:
- case KM_TAG_MAX_BOOT_LEVEL:
- case KM_TAG_ORIGINATION_EXPIRE_DATETIME:
- case KM_TAG_USAGE_EXPIRE_DATETIME:
- case KM_TAG_USER_ID:
- case KM_TAG_USAGE_COUNT_LIMIT:
- keystoreEnforced.authorizations.push_back(kmParam2Aidl(entry));
- break;
- }
- }
-
- vector<KeyCharacteristics> retval;
- retval.reserve(2);
- if (!keyMintEnforced.authorizations.empty()) retval.push_back(std::move(keyMintEnforced));
- if (include_keystore_enforced && !keystoreEnforced.authorizations.empty()) {
- retval.push_back(std::move(keystoreEnforced));
- }
-
- return retval;
-}
-
-Certificate convertCertificate(const keymaster_blob_t& cert) {
- return {std::vector<uint8_t>(cert.data, cert.data + cert.data_length)};
-}
-
-vector<Certificate> convertCertificateChain(const CertificateChain& chain) {
- vector<Certificate> retval;
- retval.reserve(chain.entry_count);
- std::transform(chain.begin(), chain.end(), std::back_inserter(retval), convertCertificate);
- return retval;
-}
-
-void addClientAndAppData(const std::vector<uint8_t>& appId, const std::vector<uint8_t>& appData,
- ::keymaster::AuthorizationSet* params) {
- params->Clear();
- if (appId.size()) {
- params->push_back(::keymaster::TAG_APPLICATION_ID, appId.data(), appId.size());
- }
- if (appData.size()) {
- params->push_back(::keymaster::TAG_APPLICATION_DATA, appData.data(), appData.size());
- }
-}
-
-} // namespace
-
-constexpr size_t kOperationTableSize = 16;
-
-MicrodroidKeyMintDevice::MicrodroidKeyMintDevice(::keymaster::KeymasterKeyBlob& rootKey)
- : impl_(new ::keymaster::AndroidKeymaster(
- [&]() -> auto {
- auto context = new MicrodroidKeymasterContext(KmVersion::KEYMINT_1, rootKey);
- context->SetSystemVersion(::keymaster::GetOsVersion(),
- ::keymaster::GetOsPatchlevel());
- return context;
- }(),
- kOperationTableSize)) {}
-
-MicrodroidKeyMintDevice::~MicrodroidKeyMintDevice() {}
-
-ScopedAStatus MicrodroidKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) {
- info->versionNumber = 1;
- info->securityLevel = SecurityLevel::SOFTWARE;
- info->keyMintName = "MicrodroidKeyMintDevice";
- info->keyMintAuthorName = "Google";
- info->timestampTokenRequired = false;
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::addRngEntropy(const vector<uint8_t>& data) {
- if (data.size() == 0) {
- return ScopedAStatus::ok();
- }
-
- AddEntropyRequest request(impl_->message_version());
- request.random_data.Reinitialize(data.data(), data.size());
-
- AddEntropyResponse response(impl_->message_version());
- impl_->AddRngEntropy(request, &response);
-
- return kmError2ScopedAStatus(response.error);
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::generateKey(const vector<KeyParameter>& keyParams,
- const optional<AttestationKey>& attestationKey,
- KeyCreationResult* creationResult) {
- GenerateKeyRequest request(impl_->message_version());
- request.key_description.Reinitialize(KmParamSet(keyParams));
- if (attestationKey) {
- request.attestation_signing_key_blob =
- KeymasterKeyBlob(attestationKey->keyBlob.data(), attestationKey->keyBlob.size());
- request.attest_key_params.Reinitialize(KmParamSet(attestationKey->attestKeyParams));
- request.issuer_subject = KeymasterBlob(attestationKey->issuerSubjectName.data(),
- attestationKey->issuerSubjectName.size());
- }
-
- GenerateKeyResponse response(impl_->message_version());
- impl_->GenerateKey(request, &response);
-
- if (response.error != KM_ERROR_OK) {
- // Note a key difference between this current aidl and previous hal, is
- // that hal returns void where as aidl returns the error status. If
- // aidl returns error, then aidl will not return any change you may make
- // to the out parameters. This is quite different from hal where all
- // output variable can be modified due to hal returning void.
- //
- // So the caller need to be aware not to expect aidl functions to clear
- // the output variables for you in case of error. If you left some
- // wrong data set in the out parameters, they will stay there.
- return kmError2ScopedAStatus(response.error);
- }
-
- creationResult->keyBlob = kmBlob2vector(response.key_blob);
- creationResult->keyCharacteristics =
- convertKeyCharacteristics(request.key_description, response.unenforced,
- response.enforced);
- creationResult->certificateChain = convertCertificateChain(response.certificate_chain);
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::importKey(const vector<KeyParameter>& keyParams,
- KeyFormat keyFormat,
- const vector<uint8_t>& keyData,
- const optional<AttestationKey>& attestationKey,
- KeyCreationResult* creationResult) {
- ImportKeyRequest request(impl_->message_version());
- request.key_description.Reinitialize(KmParamSet(keyParams));
- request.key_format = legacy_enum_conversion(keyFormat);
- request.key_data = KeymasterKeyBlob(keyData.data(), keyData.size());
- if (attestationKey) {
- request.attestation_signing_key_blob =
- KeymasterKeyBlob(attestationKey->keyBlob.data(), attestationKey->keyBlob.size());
- request.attest_key_params.Reinitialize(KmParamSet(attestationKey->attestKeyParams));
- request.issuer_subject = KeymasterBlob(attestationKey->issuerSubjectName.data(),
- attestationKey->issuerSubjectName.size());
- }
-
- ImportKeyResponse response(impl_->message_version());
- impl_->ImportKey(request, &response);
-
- if (response.error != KM_ERROR_OK) {
- return kmError2ScopedAStatus(response.error);
- }
-
- creationResult->keyBlob = kmBlob2vector(response.key_blob);
- creationResult->keyCharacteristics =
- convertKeyCharacteristics(request.key_description, response.unenforced,
- response.enforced);
- creationResult->certificateChain = convertCertificateChain(response.certificate_chain);
-
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::importWrappedKey(
- const vector<uint8_t>& wrappedKeyData, const vector<uint8_t>& wrappingKeyBlob,
- const vector<uint8_t>& maskingKey, const vector<KeyParameter>& unwrappingParams,
- int64_t passwordSid, int64_t biometricSid, KeyCreationResult* creationResult) {
- ImportWrappedKeyRequest request(impl_->message_version());
- request.SetWrappedMaterial(wrappedKeyData.data(), wrappedKeyData.size());
- request.SetWrappingMaterial(wrappingKeyBlob.data(), wrappingKeyBlob.size());
- request.SetMaskingKeyMaterial(maskingKey.data(), maskingKey.size());
- request.additional_params.Reinitialize(KmParamSet(unwrappingParams));
- request.password_sid = static_cast<uint64_t>(passwordSid);
- request.biometric_sid = static_cast<uint64_t>(biometricSid);
-
- ImportWrappedKeyResponse response(impl_->message_version());
- impl_->ImportWrappedKey(request, &response);
-
- if (response.error != KM_ERROR_OK) {
- return kmError2ScopedAStatus(response.error);
- }
-
- creationResult->keyBlob = kmBlob2vector(response.key_blob);
- creationResult->keyCharacteristics =
- convertKeyCharacteristics(request.additional_params, response.unenforced,
- response.enforced);
- creationResult->certificateChain = convertCertificateChain(response.certificate_chain);
-
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,
- const vector<KeyParameter>& upgradeParams,
- vector<uint8_t>* keyBlob) {
- UpgradeKeyRequest request(impl_->message_version());
- request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
- request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
-
- UpgradeKeyResponse response(impl_->message_version());
- impl_->UpgradeKey(request, &response);
-
- if (response.error != KM_ERROR_OK) {
- return kmError2ScopedAStatus(response.error);
- }
-
- *keyBlob = kmBlob2vector(response.upgraded_key);
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::deleteKey(const vector<uint8_t>&) {
- // There's nothing to be done to delete software key blobs.
- return kmError2ScopedAStatus(KM_ERROR_OK);
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::deleteAllKeys() {
- // There's nothing to be done to delete software key blobs.
- return kmError2ScopedAStatus(KM_ERROR_OK);
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::destroyAttestationIds() {
- return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,
- const vector<KeyParameter>& params,
- const optional<HardwareAuthToken>& authToken,
- BeginResult* result) {
- BeginOperationRequest request(impl_->message_version());
- request.purpose = legacy_enum_conversion(purpose);
- request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
- request.additional_params.Reinitialize(KmParamSet(params));
-
- vector<uint8_t> vector_token = authToken2AidlVec(authToken);
- request.additional_params.push_back(TAG_AUTH_TOKEN,
- reinterpret_cast<uint8_t*>(vector_token.data()),
- vector_token.size());
-
- BeginOperationResponse response(impl_->message_version());
- impl_->BeginOperation(request, &response);
-
- if (response.error != KM_ERROR_OK) {
- return kmError2ScopedAStatus(response.error);
- }
-
- result->params = kmParamSet2Aidl(response.output_params);
- result->challenge = response.op_handle;
- result->operation =
- ndk::SharedRefBase::make<AndroidKeyMintOperation>(impl_, response.op_handle);
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::deviceLocked(
- bool, const std::optional<secureclock::TimeStampToken>&) {
- // Microdroid doesn't yet have a concept of a locked device.
- return kmError2ScopedAStatus(KM_ERROR_OK);
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::earlyBootEnded() {
- return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::convertStorageKeyToEphemeral(
- const std::vector<uint8_t>& /* storageKeyBlob */,
- std::vector<uint8_t>* /* ephemeralKeyBlob */) {
- return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::getKeyCharacteristics(
- const std::vector<uint8_t>& keyBlob, const std::vector<uint8_t>& appId,
- const std::vector<uint8_t>& appData, std::vector<KeyCharacteristics>* keyCharacteristics) {
- GetKeyCharacteristicsRequest request(impl_->message_version());
- request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
- addClientAndAppData(appId, appData, &request.additional_params);
-
- GetKeyCharacteristicsResponse response(impl_->message_version());
- impl_->GetKeyCharacteristics(request, &response);
-
- if (response.error != KM_ERROR_OK) {
- return kmError2ScopedAStatus(response.error);
- }
-
- AuthorizationSet emptySet;
- *keyCharacteristics =
- convertKeyCharacteristics(emptySet, response.unenforced, response.enforced,
- /* include_keystore_enforced = */ false);
-
- return ScopedAStatus::ok();
-}
-
-} // namespace aidl::android::hardware::security::keymint
diff --git a/microdroid/keymint/MicrodroidKeymasterContext.cpp b/microdroid/keymint/MicrodroidKeymasterContext.cpp
deleted file mode 100644
index 1d1346b..0000000
--- a/microdroid/keymint/MicrodroidKeymasterContext.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright 2021, 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.
- */
-
-#include "MicrodroidKeymasterContext.h"
-
-#include <android-base/logging.h>
-#include <keymaster/key.h>
-#include <keymaster/key_blob_utils/auth_encrypted_key_blob.h>
-#include <keymaster/key_blob_utils/software_keyblobs.h>
-
-using namespace ::keymaster;
-
-// This value is used for the ROOT_OF_TRUST tag which is only used in
-// attestation records which aren't supported in this implementation so a
-// constant doesn't cause any hard. MicroDroid SoftWare root-of-trust.
-static uint8_t SWROT[] = {'M', 'D', 'S', 'W'};
-static const KeymasterBlob microdroidSoftwareRootOfTrust(SWROT);
-
-keymaster_error_t MicrodroidKeymasterContext::CreateKeyBlob(const AuthorizationSet& key_description,
- keymaster_key_origin_t origin,
- const KeymasterKeyBlob& key_material,
- KeymasterKeyBlob* blob,
- AuthorizationSet* hw_enforced,
- AuthorizationSet* sw_enforced) const {
- keymaster_error_t error;
-
- if (key_description.GetTagValue(TAG_ROLLBACK_RESISTANCE)) {
- return KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE;
- }
-
- error = SetKeyBlobAuthorizations(key_description, origin, os_version_, os_patchlevel_,
- hw_enforced, sw_enforced);
- if (error != KM_ERROR_OK) return error;
-
- AuthorizationSet hidden;
- error = BuildHiddenAuthorizations(key_description, &hidden, microdroidSoftwareRootOfTrust);
- if (error != KM_ERROR_OK) return error;
-
- CHECK(hw_enforced->empty());
-
- // Note that the authorizations included in the blob are not encrypted. This
- // doesn't pose a problem for the current applications but may be a
- // candidate for hardening.
- auto encrypted_key = EncryptKey(key_material, AES_GCM_WITH_SW_ENFORCED, *hw_enforced,
- *sw_enforced, hidden, SecureDeletionData{}, root_key_, random_);
- if (!encrypted_key) return encrypted_key.error();
-
- auto serialized = SerializeAuthEncryptedBlob(*encrypted_key, *hw_enforced, *sw_enforced,
- 0 /* key_slot */);
- if (!serialized) return serialized.error();
- *blob = *serialized;
- return KM_ERROR_OK;
-}
-
-keymaster_error_t MicrodroidKeymasterContext::ParseKeyBlob(
- const KeymasterKeyBlob& blob, const AuthorizationSet& additional_params,
- UniquePtr<Key>* key) const {
- keymaster_error_t error;
-
- AuthorizationSet hidden;
- error = BuildHiddenAuthorizations(additional_params, &hidden, microdroidSoftwareRootOfTrust);
- if (error != KM_ERROR_OK) return error;
-
- auto deserialized_key = DeserializeAuthEncryptedBlob(blob);
- if (!deserialized_key) return deserialized_key.error();
-
- keymaster_algorithm_t algorithm;
- if (!deserialized_key->sw_enforced.GetTagValue(TAG_ALGORITHM, &algorithm)) {
- return KM_ERROR_INVALID_ARGUMENT;
- }
-
- auto key_material = DecryptKey(*deserialized_key, hidden, SecureDeletionData{}, root_key_);
- if (!key_material) return key_material.error();
-
- auto factory = GetKeyFactory(algorithm);
- return factory->LoadKey(move(*key_material), additional_params,
- move(deserialized_key->hw_enforced),
- move(deserialized_key->sw_enforced), key);
-}
-
-static bool UpgradeIntegerTag(keymaster_tag_t tag, uint32_t value, AuthorizationSet* set) {
- int index = set->find(tag);
- if (index == -1) {
- keymaster_key_param_t param;
- param.tag = tag;
- param.integer = value;
- set->push_back(param);
- return true;
- }
-
- if (set->params[index].integer > value) return false;
-
- if (set->params[index].integer != value) {
- set->params[index].integer = value;
- }
- return true;
-}
-
-keymaster_error_t MicrodroidKeymasterContext::UpgradeKeyBlob(const KeymasterKeyBlob& key_to_upgrade,
- const AuthorizationSet& upgrade_params,
- KeymasterKeyBlob* upgraded_key) const {
- UniquePtr<Key> key;
- keymaster_error_t error = ParseKeyBlob(key_to_upgrade, upgrade_params, &key);
- if (error != KM_ERROR_OK) return error;
-
- if (os_version_ == 0) {
- // We need to allow "upgrading" OS version to zero, to support upgrading from proper
- // numbered releases to unnumbered development and preview releases.
-
- int key_os_version_pos = key->sw_enforced().find(TAG_OS_VERSION);
- if (key_os_version_pos != -1) {
- uint32_t key_os_version = key->sw_enforced()[key_os_version_pos].integer;
- if (key_os_version != 0) {
- key->sw_enforced()[key_os_version_pos].integer = os_version_;
- }
- }
- }
-
- if (!UpgradeIntegerTag(TAG_OS_VERSION, os_version_, &key->sw_enforced()) ||
- !UpgradeIntegerTag(TAG_OS_PATCHLEVEL, os_patchlevel_, &key->sw_enforced()))
- // One of the version fields would have been a downgrade. Not allowed.
- return KM_ERROR_INVALID_ARGUMENT;
-
- AuthorizationSet hidden;
- error = BuildHiddenAuthorizations(upgrade_params, &hidden, microdroidSoftwareRootOfTrust);
- if (error != KM_ERROR_OK) return error;
-
- auto encrypted_key =
- EncryptKey(key->key_material(), AES_GCM_WITH_SW_ENFORCED, key->hw_enforced(),
- key->sw_enforced(), hidden, SecureDeletionData{}, root_key_, random_);
- if (!encrypted_key) return encrypted_key.error();
-
- auto serialized = SerializeAuthEncryptedBlob(*encrypted_key, key->hw_enforced(),
- key->sw_enforced(), 0 /* key_slot */);
- if (!serialized) return serialized.error();
-
- *upgraded_key = std::move(*serialized);
- return error;
-}
diff --git a/microdroid/keymint/android.hardware.security.keymint-service.microdroid.rc b/microdroid/keymint/android.hardware.security.keymint-service.microdroid.rc
deleted file mode 100644
index d6851bd..0000000
--- a/microdroid/keymint/android.hardware.security.keymint-service.microdroid.rc
+++ /dev/null
@@ -1,3 +0,0 @@
-service vendor.keymint-microdroid /vendor/bin/hw/android.hardware.security.keymint-service.microdroid
- class early_hal
- user nobody
diff --git a/microdroid/keymint/android.hardware.security.keymint-service.microdroid.xml b/microdroid/keymint/android.hardware.security.keymint-service.microdroid.xml
deleted file mode 100644
index 73d15a8..0000000
--- a/microdroid/keymint/android.hardware.security.keymint-service.microdroid.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<manifest version="1.0" type="device">
- <hal format="aidl">
- <name>android.hardware.security.keymint</name>
- <fqname>IKeyMintDevice/default</fqname>
- </hal>
-</manifest>
diff --git a/microdroid/keymint/include/MicrodroidKeyMintDevice.h b/microdroid/keymint/include/MicrodroidKeyMintDevice.h
deleted file mode 100644
index dec7baa..0000000
--- a/microdroid/keymint/include/MicrodroidKeyMintDevice.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2021, 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.
- */
-
-#pragma once
-
-#include <aidl/android/hardware/security/keymint/BnKeyMintDevice.h>
-#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
-#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
-#include <keymaster/android_keymaster_utils.h>
-
-namespace keymaster {
-class AndroidKeymaster;
-}
-
-namespace aidl::android::hardware::security::keymint {
-using ::ndk::ScopedAStatus;
-using std::optional;
-using std::shared_ptr;
-using std::vector;
-
-using secureclock::TimeStampToken;
-
-class MicrodroidKeyMintDevice : public BnKeyMintDevice {
-public:
- explicit MicrodroidKeyMintDevice(::keymaster::KeymasterKeyBlob& rootKey);
- virtual ~MicrodroidKeyMintDevice();
-
- ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* info) override;
-
- ScopedAStatus addRngEntropy(const vector<uint8_t>& data) override;
-
- ScopedAStatus generateKey(const vector<KeyParameter>& keyParams,
- const optional<AttestationKey>& attestationKey,
- KeyCreationResult* creationResult) override;
-
- ScopedAStatus importKey(const vector<KeyParameter>& keyParams, KeyFormat keyFormat,
- const vector<uint8_t>& keyData,
- const optional<AttestationKey>& attestationKey,
- KeyCreationResult* creationResult) override;
-
- ScopedAStatus importWrappedKey(const vector<uint8_t>& wrappedKeyData,
- const vector<uint8_t>& wrappingKeyBlob,
- const vector<uint8_t>& maskingKey,
- const vector<KeyParameter>& unwrappingParams,
- int64_t passwordSid, int64_t biometricSid,
- KeyCreationResult* creationResult) override;
-
- ScopedAStatus upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,
- const vector<KeyParameter>& upgradeParams,
- vector<uint8_t>* keyBlob) override;
-
- ScopedAStatus deleteKey(const vector<uint8_t>& keyBlob) override;
- ScopedAStatus deleteAllKeys() override;
- ScopedAStatus destroyAttestationIds() override;
-
- ScopedAStatus begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,
- const vector<KeyParameter>& params,
- const optional<HardwareAuthToken>& authToken, BeginResult* result) override;
-
- ScopedAStatus deviceLocked(bool passwordOnly,
- const optional<TimeStampToken>& timestampToken) override;
- ScopedAStatus earlyBootEnded() override;
-
- ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob,
- std::vector<uint8_t>* ephemeralKeyBlob) override;
-
- ScopedAStatus getKeyCharacteristics(
- const std::vector<uint8_t>& keyBlob, const std::vector<uint8_t>& appId,
- const std::vector<uint8_t>& appData,
- std::vector<KeyCharacteristics>* keyCharacteristics) override;
-
- shared_ptr<::keymaster::AndroidKeymaster>& getKeymasterImpl() { return impl_; }
-
-protected:
- std::shared_ptr<::keymaster::AndroidKeymaster> impl_;
-};
-
-} // namespace aidl::android::hardware::security::keymint
diff --git a/microdroid/keymint/include/MicrodroidKeymasterContext.h b/microdroid/keymint/include/MicrodroidKeymasterContext.h
deleted file mode 100644
index 636d240..0000000
--- a/microdroid/keymint/include/MicrodroidKeymasterContext.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2021, 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.
- */
-
-#include <keymaster/contexts/pure_soft_keymaster_context.h>
-#include <keymaster/km_openssl/software_random_source.h>
-
-class MicrodroidKeymasterContext : public ::keymaster::PureSoftKeymasterContext {
-public:
- explicit MicrodroidKeymasterContext(::keymaster::KmVersion version,
- ::keymaster::KeymasterKeyBlob& root_key)
- : PureSoftKeymasterContext(version, KM_SECURITY_LEVEL_SOFTWARE), root_key_(root_key) {}
-
- keymaster_error_t CreateKeyBlob(const ::keymaster::AuthorizationSet& auths,
- keymaster_key_origin_t origin,
- const ::keymaster::KeymasterKeyBlob& key_material,
- ::keymaster::KeymasterKeyBlob* blob,
- ::keymaster::AuthorizationSet* hw_enforced,
- ::keymaster::AuthorizationSet* sw_enforced) const override;
-
- keymaster_error_t ParseKeyBlob(const ::keymaster::KeymasterKeyBlob& blob,
- const ::keymaster::AuthorizationSet& additional_params,
- ::keymaster::UniquePtr<::keymaster::Key>* key) const override;
-
- keymaster_error_t UpgradeKeyBlob(const ::keymaster::KeymasterKeyBlob& key_to_upgrade,
- const ::keymaster::AuthorizationSet& upgrade_params,
- ::keymaster::KeymasterKeyBlob* upgraded_key) const override;
-
-private:
- ::keymaster::SoftwareRandomSource random_;
- ::keymaster::KeymasterKeyBlob root_key_;
-};
diff --git a/microdroid/keymint/service.cpp b/microdroid/keymint/service.cpp
deleted file mode 100644
index 5fc0bd2..0000000
--- a/microdroid/keymint/service.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright 2021, 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.
- */
-
-#define LOG_TAG "android.hardware.security.keymint-service"
-
-#include <AndroidKeyMintDevice.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/result.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-#include <keymaster/android_keymaster_utils.h>
-#include <keymaster/mem.h>
-#include <keymaster/soft_keymaster_logger.h>
-#include <openssl/digest.h>
-#include <openssl/hkdf.h>
-#include <openssl/is_boringssl.h>
-#include <openssl/sha.h>
-
-#include "MicrodroidKeyMintDevice.h"
-
-using aidl::android::hardware::security::keymint::MicrodroidKeyMintDevice;
-using aidl::android::hardware::security::keymint::SecurityLevel;
-
-using android::base::Error;
-using android::base::GetProperty;
-using android::base::Result;
-
-using keymaster::KeymasterBlob;
-using keymaster::KeymasterKeyBlob;
-using keymaster::memset_s;
-
-namespace {
-
-template <typename T, class... Args>
-std::shared_ptr<T> addService(Args&&... args) {
- std::shared_ptr<T> ser = ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);
- auto instanceName = std::string(T::descriptor) + "/default";
- LOG(INFO) << "adding keymint service instance: " << instanceName;
- binder_status_t status =
- AServiceManager_addService(ser->asBinder().get(), instanceName.c_str());
- CHECK(status == STATUS_OK);
- return ser;
-}
-
-Result<KeymasterKeyBlob> getRootKey() {
- const std::string prop = "ro.vmsecret.keymint";
- const std::chrono::seconds timeout(15);
- while (!android::base::WaitForPropertyCreation(prop, timeout)) {
- LOG(WARNING) << "waited " << timeout.count() << "seconds for " << prop
- << ", still waiting...";
- }
-
- // In a small effort to avoid spreading the secret around too widely in
- // memory, move the secert into a buffer that will wipe itself and clear
- // the original string.
- std::string secretProp = GetProperty(prop, "");
- KeymasterBlob secret(reinterpret_cast<const uint8_t*>(secretProp.data()), secretProp.size());
- memset_s(secretProp.data(), 0, secretProp.size());
- if (secret.size() < 64u) return Error() << "secret is too small";
-
- // Derive the root key from the secret to avoid getting locked into using
- // the secret directly.
- KeymasterKeyBlob rootKey(SHA512_DIGEST_LENGTH);
- const uint8_t kRootKeyIkm[] = "keymint_root_key";
- const uint8_t* kNoSalt = nullptr;
- const size_t kNoSaltLen = 0;
- if (!HKDF(rootKey.writable_data(), rootKey.size(), EVP_sha512(), (uint8_t*)secret.begin(),
- secret.size(), kNoSalt, kNoSaltLen, kRootKeyIkm, sizeof(kRootKeyIkm))) {
- return Error() << "Failed to derive a key";
- }
- if (rootKey.size() < 64u) return Error() << "root key is too small";
-
- LOG(INFO) << "root key obtained";
- return rootKey;
-}
-
-} // namespace
-
-int main() {
- auto rootKey = getRootKey();
- if (!rootKey.ok()) {
- LOG(FATAL) << "Failed to get root key: " << rootKey.error();
- }
-
- // Zero threads seems like a useless pool, but below we'll join this thread
- // to it, increasing the pool size to 1.
- ABinderProcess_setThreadPoolMaxThreadCount(0);
-
- // Add Keymint Service
- std::shared_ptr<MicrodroidKeyMintDevice> keyMint =
- ndk::SharedRefBase::make<MicrodroidKeyMintDevice>(*rootKey);
- auto instanceName = std::string(MicrodroidKeyMintDevice::descriptor) + "/default";
- LOG(INFO) << "adding keymint service instance: " << instanceName;
- binder_status_t status =
- AServiceManager_addService(keyMint->asBinder().get(), instanceName.c_str());
- CHECK(status == STATUS_OK);
-
- ABinderProcess_joinThreadPool();
- return EXIT_FAILURE; // should not reach
-}
diff --git a/microdroid/microdroid.json b/microdroid/microdroid.json
index c6c743d..cb27a24 100644
--- a/microdroid/microdroid.json
+++ b/microdroid/microdroid.json
@@ -37,6 +37,5 @@
"writable": true
}
],
- "memory_mib": 2048,
- "protected": false
+ "memory_mib": 256
}
diff --git a/microdroid/microdroid_compatibility_matrix.xml b/microdroid/microdroid_compatibility_matrix.xml
index dbc12a8..a345e30 100644
--- a/microdroid/microdroid_compatibility_matrix.xml
+++ b/microdroid/microdroid_compatibility_matrix.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<compatibility-matrix version="1.0" type="framework">
<hal format="aidl" optional="true">
- <name>android.hardware.security.keymint</name>
+ <name>android.hardware.security.dice</name>
<version>1</version>
<interface>
- <name>IKeyMintDevice</name>
+ <name>IDiceDevice</name>
<instance>default</instance>
</interface>
</hal>
diff --git a/microdroid/microdroid_manifest.xml b/microdroid/microdroid_manifest.xml
index 28a374f..b84ba8f 100644
--- a/microdroid/microdroid_manifest.xml
+++ b/microdroid/microdroid_manifest.xml
@@ -1,24 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<manifest version="1.0" type="framework">
- <!--TODO(b/185767624): remove hidl after full keymint support-->
- <hal format="hidl">
- <name>android.hidl.manager</name>
- <transport>hwbinder</transport>
- <version>1.2</version>
- <interface>
- <name>IServiceManager</name>
- <instance>default</instance>
- </interface>
- <fqname>@1.2::IServiceManager/default</fqname>
- </hal>
- <hal format="hidl">
- <name>android.hidl.token</name>
- <transport>hwbinder</transport>
- <version>1.0</version>
- <interface>
- <name>ITokenManager</name>
- <instance>default</instance>
- </interface>
- <fqname>@1.0::ITokenManager/default</fqname>
- </hal>
+ <!-- empty -->
</manifest>
diff --git a/microdroid/microdroid_vendor_compatibility_matrix.xml b/microdroid/microdroid_vendor_compatibility_matrix.xml
index efa1c98..44735d8 100644
--- a/microdroid/microdroid_vendor_compatibility_matrix.xml
+++ b/microdroid/microdroid_vendor_compatibility_matrix.xml
@@ -1,27 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<compatibility-matrix version="1.0" type="device">
- <hal format="aidl">
- <name>android.system.keystore2</name>
- <interface>
- <name>IKeystoreService</name>
- <instance>default</instance>
- </interface>
- </hal>
- <!--TODO(b/185767624): remove hidl after full keymint support-->
- <hal format="hidl" optional="true">
- <name>android.hidl.manager</name>
- <version>1.0</version>
- <interface>
- <name>IServiceManager</name>
- <instance>default</instance>
- </interface>
- </hal>
- <hal format="hidl" optional="true">
- <name>android.hidl.token</name>
- <version>1.0</version>
- <interface>
- <name>ITokenManager</name>
- <instance>default</instance>
- </interface>
- </hal>
+ <!-- empty -->
</compatibility-matrix>
diff --git a/microdroid/payload/metadata.proto b/microdroid/payload/metadata.proto
index 9e60b38..2e92f55 100644
--- a/microdroid/payload/metadata.proto
+++ b/microdroid/payload/metadata.proto
@@ -43,6 +43,10 @@
// The timestamp in seconds when the APEX was last updated. This should match the value in
// apex-info-list.xml.
uint64 last_update_seconds = 5;
+
+ // Required.
+ // Whether the APEX is a factory version or not.
+ bool is_factory = 6;
}
message ApkPayload {
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index f888b80..1878c87 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -9,6 +9,7 @@
edition: "2018",
prefer_rlib: true,
rustlibs: [
+ "android.hardware.security.dice-V1-rust",
"android.security.dice-rust",
"android.system.virtualizationservice-rust",
"android.system.virtualmachineservice-rust",
@@ -18,6 +19,7 @@
"libbinder_rpc_unstable_bindgen",
"libbinder_rs",
"libbyteorder",
+ "libdiced_utils",
"libglob",
"libidsig",
"libitertools",
diff --git a/microdroid_manager/src/instance.rs b/microdroid_manager/src/instance.rs
index 1068792..f3bbf16 100644
--- a/microdroid_manager/src/instance.rs
+++ b/microdroid_manager/src/instance.rs
@@ -316,6 +316,7 @@
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct MicrodroidData {
+ pub salt: Vec<u8>, // Should be [u8; 64] but that isn't serializable.
pub apk_data: ApkData,
pub extra_apks_data: Vec<ApkData>,
pub apex_data: Vec<ApexData>,
@@ -336,4 +337,5 @@
pub public_key: Vec<u8>,
pub root_digest: Vec<u8>,
pub last_update_seconds: u64,
+ pub is_factory: bool,
}
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 4420a49..827f9ff 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -19,20 +19,28 @@
mod payload;
use crate::instance::{ApkData, InstanceDisk, MicrodroidData, RootHash};
+use android_hardware_security_dice::aidl::android::hardware::security::dice::{
+ Config::Config, InputValues::InputValues, Mode::Mode,
+};
+use android_security_dice::aidl::android::security::dice::IDiceMaintenance::IDiceMaintenance;
use anyhow::{anyhow, bail, ensure, Context, Error, Result};
use apkverify::{get_public_key_der, verify};
use binder::unstable_api::{new_spibinder, AIBinder};
-use binder::{FromIBinder, Strong};
+use binder::{wait_for_interface, FromIBinder, Strong};
+use diced_utils::cbor::encode_header;
use glob::glob;
use idsig::V4Signature;
use itertools::sorted;
-use log::{error, info, warn};
+use log::{error, info};
use microdroid_metadata::{write_metadata, Metadata};
use microdroid_payload_config::{Task, TaskType, VmPayloadConfig};
use once_cell::sync::OnceCell;
use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
+use rand::Fill;
+use ring::digest;
use rustutils::system_properties;
use rustutils::system_properties::PropertyWatcher;
+use std::convert::TryInto;
use std::fs::{self, create_dir, File, OpenOptions};
use std::io::BufRead;
use std::os::unix::io::{FromRawFd, IntoRawFd};
@@ -61,6 +69,8 @@
const APEX_CONFIG_DONE_PROP: &str = "apex_config.done";
const LOGD_ENABLED_PROP: &str = "ro.boot.logd.enabled";
+const ADBD_ENABLED_PROP: &str = "ro.boot.adb.enabled";
+const DEBUGGABLE_PROP: &str = "ro.boot.microdroid.debuggable";
#[derive(thiserror::Error, Debug)]
enum MicrodroidError {
@@ -137,6 +147,61 @@
}
}
+fn is_debuggable() -> Result<bool> {
+ // Read all the properties so the behaviour is most similar between debug and non-debug boots.
+ // Defensively default to debug enabled for unrecognised values.
+ let adb = system_properties::read_bool(ADBD_ENABLED_PROP, true)?;
+ let logd = system_properties::read_bool(LOGD_ENABLED_PROP, true)?;
+ let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
+ Ok(adb || logd || debuggable)
+}
+
+fn dice_derivation(verified_data: MicrodroidData, payload_config_path: &str) -> Result<()> {
+ // Calculate compound digests of code and authorities
+ let mut code_hash_ctx = digest::Context::new(&digest::SHA512);
+ let mut authority_hash_ctx = digest::Context::new(&digest::SHA512);
+ code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
+ authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
+ for extra_apk in verified_data.extra_apks_data {
+ code_hash_ctx.update(extra_apk.root_hash.as_ref());
+ authority_hash_ctx.update(extra_apk.pubkey.as_ref());
+ }
+ for apex in verified_data.apex_data {
+ code_hash_ctx.update(apex.root_digest.as_ref());
+ authority_hash_ctx.update(apex.public_key.as_ref());
+ }
+ let code_hash = code_hash_ctx.finish().as_ref().try_into().unwrap();
+ let authority_hash = authority_hash_ctx.finish().as_ref().try_into().unwrap();
+
+ // {
+ // -70002: "Microdroid payload",
+ // -71000: payload_config_path
+ // }
+ let mut config_desc = vec![
+ 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,
+ ];
+ let config_path_bytes = payload_config_path.as_bytes();
+ encode_header(3, config_path_bytes.len().try_into().unwrap(), &mut config_desc)?;
+ config_desc.extend_from_slice(config_path_bytes);
+
+ // Send the details to diced
+ let diced =
+ wait_for_interface::<dyn IDiceMaintenance>("android.security.dice.IDiceMaintenance")
+ .context("IDiceMaintenance service not found")?;
+ diced
+ .demoteSelf(&[InputValues {
+ codeHash: code_hash,
+ config: Config { desc: config_desc },
+ authorityHash: authority_hash,
+ authorityDescriptor: None,
+ mode: if is_debuggable()? { Mode::DEBUG } else { Mode::NORMAL },
+ hidden: verified_data.salt.try_into().unwrap(),
+ }])
+ .context("IDiceMaintenance::demoteSelf failed")?;
+ Ok(())
+}
+
fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
let metadata = load_metadata().context("Failed to load payload metadata")?;
@@ -182,10 +247,8 @@
}
mount_extra_apks(&config)?;
- let fake_secret = "This is a placeholder for a value that is derived from the images that are loaded in the VM.";
- if let Err(err) = rustutils::system_properties::write("ro.vmsecret.keymint", fake_secret) {
- warn!("failed to set ro.vmsecret.keymint: {}", err);
- }
+ info!("DICE derivation for payload");
+ dice_derivation(verified_data, &metadata.payload_config_path)?;
// Wait until apex config is done. (e.g. linker configuration for apexes)
// TODO(jooyung): wait until sys.boot_completed?
@@ -376,9 +439,19 @@
info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
+ // 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()
+ } else {
+ let mut salt = vec![0u8; 64];
+ salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
+ 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 },
extra_apks_data,
apex_data: apex_data_from_payload,
@@ -409,8 +482,7 @@
let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
loop {
prop.wait()?;
- let val = system_properties::read(APEX_CONFIG_DONE_PROP)?;
- if val == "true" {
+ if system_properties::read_bool(APEX_CONFIG_DONE_PROP, false)? {
break;
}
}
@@ -480,7 +552,7 @@
// Start logging if enabled
// TODO(b/200914564) set filterspec if debug_level is app_only
- if system_properties::read(LOGD_ENABLED_PROP)? == "1" {
+ if system_properties::read_bool(LOGD_ENABLED_PROP, false)? {
system_properties::write("ctl.start", "seriallogging")?;
}
diff --git a/microdroid_manager/src/payload.rs b/microdroid_manager/src/payload.rs
index 661af5f..48535f3 100644
--- a/microdroid_manager/src/payload.rs
+++ b/microdroid_manager/src/payload.rs
@@ -48,6 +48,7 @@
public_key: result.public_key,
root_digest: result.root_digest,
last_update_seconds: apex.last_update_seconds,
+ is_factory: apex.is_factory,
})
})
.collect()
@@ -63,6 +64,7 @@
public_key: data.public_key.clone(),
root_digest: data.root_digest.clone(),
last_update_seconds: data.last_update_seconds,
+ is_factory: data.is_factory,
..Default::default()
})
.collect(),
diff --git a/pvmfw/pvmfw.img b/pvmfw/pvmfw.img
index 510b2c4..a2541a3 100644
--- a/pvmfw/pvmfw.img
+++ b/pvmfw/pvmfw.img
Binary files differ
diff --git a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
index 208d61f..f15036c 100644
--- a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
+++ b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
@@ -24,4 +24,7 @@
/* read a system property. */
String readProperty(String prop);
+
+ /* get the VM's stable secret. */
+ byte[] insecurelyExposeSecret();
}
diff --git a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
index 678fe84..e15f1ae 100644
--- a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
+++ b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
@@ -204,6 +204,24 @@
Optional<Integer> numCpus,
Optional<String> cpuAffinity)
throws DeviceNotAvailableException {
+ return startMicrodroid(androidDevice, buildInfo, apkName, null, packageName,
+ extraIdsigPaths, configPath, debug,
+ memoryMib, numCpus, cpuAffinity);
+ }
+
+ public static String startMicrodroid(
+ ITestDevice androidDevice,
+ IBuildInfo buildInfo,
+ String apkName,
+ String apkPath,
+ String packageName,
+ String[] extraIdsigPaths,
+ String configPath,
+ boolean debug,
+ int memoryMib,
+ Optional<Integer> numCpus,
+ Optional<String> cpuAffinity)
+ throws DeviceNotAvailableException {
CommandRunner android = new CommandRunner(androidDevice);
// Install APK if necessary
@@ -215,9 +233,11 @@
// Get the path to the installed apk. Note that
// getDevice().getAppPackageInfo(...).getCodePath() doesn't work due to the incorrect
// parsing of the "=" character. (b/190975227). So we use the `pm path` command directly.
- String apkPath = android.run("pm", "path", packageName);
- assertTrue(apkPath.startsWith("package:"));
- apkPath = apkPath.substring("package:".length());
+ if (apkPath == null) {
+ apkPath = android.run("pm", "path", packageName);
+ assertTrue(apkPath.startsWith("package:"));
+ apkPath = apkPath.substring("package:".length());
+ }
android.run("mkdir", "-p", TEST_ROOT);
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 6cd16c2..40d72fe 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -10,6 +10,7 @@
"androidx.test.runner",
"androidx.test.ext.junit",
"com.android.microdroid.testservice-java",
+ "truth-prebuilt",
],
libs: ["android.system.virtualmachine"],
jni_libs: ["MicrodroidTestNativeLib"],
@@ -22,7 +23,7 @@
name: "MicrodroidTestNativeLib",
srcs: ["src/native/testbinary.cpp"],
shared_libs: [
- "android.system.keystore2-V1-ndk",
+ "android.security.dice-ndk",
"android.system.virtualmachineservice-ndk",
"com.android.microdroid.testservice-ndk",
"libbase",
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index 032ecfd..803bdc6 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -15,14 +15,14 @@
*/
package com.android.microdroid.test;
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsNot.not;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeNoException;
-import static org.junit.Assume.assumeThat;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
@@ -52,6 +52,7 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@@ -176,9 +177,6 @@
testService.readProperty("debug.microdroid.app.sublib.run"),
"true");
assertEquals(
- testService.readProperty("debug.microdroid.test.keystore"),
- "PASS");
- assertEquals(
testService.readProperty("debug.microdroid.test.extra_apk"),
"PASS");
} catch (Exception e) {
@@ -215,8 +213,10 @@
@Test
public void changingDebugLevelInvalidatesVmIdentity()
throws VirtualMachineException, InterruptedException, IOException {
- assumeThat("Skip on Cuttlefish. b/195765441",
- android.os.Build.DEVICE, is(not("vsoc_x86_64")));
+ assume()
+ .withMessage("Skip on Cuttlefish. b/195765441")
+ .that(android.os.Build.DEVICE)
+ .isNotEqualTo("vsoc_x86_64");
VirtualMachineConfig.Builder builder =
new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config.json");
@@ -272,4 +272,64 @@
};
listener.runToFinish(mInner.mVm);
}
+
+ private byte[] launchVmAndGetSecret(String instanceName)
+ throws VirtualMachineException, InterruptedException {
+ VirtualMachineConfig.Builder builder =
+ new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config.json");
+ VirtualMachineConfig normalConfig = builder.debugLevel(DebugLevel.NONE).build();
+ mInner.mVm = mInner.mVmm.getOrCreate(instanceName, normalConfig);
+ final CompletableFuture<byte[]> secret = new CompletableFuture<>();
+ VmEventListener listener =
+ new VmEventListener() {
+ @Override
+ public void onPayloadReady(VirtualMachine vm) {
+ try {
+ ITestService testService = ITestService.Stub.asInterface(
+ vm.connectToVsockServer(ITestService.SERVICE_PORT).get());
+ secret.complete(testService.insecurelyExposeSecret());
+ } catch (Exception e) {
+ fail("Exception while connecting to service: " + e.toString());
+ }
+ // TODO(b/208639280): remove this sleep. For now, we need to wait for a few
+ // seconds so that crosvm can actually persist instance.img.
+ try {
+ Thread.sleep(30 * 1000);
+ } catch (InterruptedException e) { }
+ forceStop(vm);
+ }
+ };
+ listener.runToFinish(mInner.mVm);
+ return secret.getNow(null);
+ }
+
+ @Test
+ public void instancesOfSameVmHaveDifferentSecrets()
+ throws VirtualMachineException, InterruptedException {
+ assume()
+ .withMessage("Skip on Cuttlefish. b/195765441")
+ .that(android.os.Build.DEVICE)
+ .isNotEqualTo("vsoc_x86_64");
+
+ byte[] vm_a_secret = launchVmAndGetSecret("test_vm_a");
+ byte[] vm_b_secret = launchVmAndGetSecret("test_vm_b");
+ assertThat(vm_a_secret).isNotNull();
+ assertThat(vm_b_secret).isNotNull();
+ assertThat(vm_a_secret).isNotEqualTo(vm_b_secret);
+ }
+
+ @Test
+ public void sameInstanceKeepsSameSecrets()
+ throws VirtualMachineException, InterruptedException {
+ assume()
+ .withMessage("Skip on Cuttlefish. b/195765441")
+ .that(android.os.Build.DEVICE)
+ .isNotEqualTo("vsoc_x86_64");
+
+ byte[] vm_secret_first_boot = launchVmAndGetSecret("test_vm");
+ byte[] vm_secret_second_boot = launchVmAndGetSecret("test_vm");
+ assertThat(vm_secret_first_boot).isNotNull();
+ assertThat(vm_secret_second_boot).isNotNull();
+ assertThat(vm_secret_first_boot).isEqualTo(vm_secret_second_boot);
+ }
}
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index c748b2a..417ff4a 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -13,13 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include <aidl/android/system/keystore2/IKeystoreService.h>
+#include <aidl/android/security/dice/IDiceNode.h>
#include <aidl/android/system/virtualmachineservice/IVirtualMachineService.h>
#include <aidl/com/android/microdroid/testservice/BnTestService.h>
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/result.h>
-#include <android-base/unique_fd.h>
#include <android/binder_auto_utils.h>
#include <android/binder_manager.h>
#include <fcntl.h>
@@ -34,159 +33,19 @@
#include <binder_rpc_unstable.hpp>
#include <string>
-using aidl::android::hardware::security::keymint::Algorithm;
-using aidl::android::hardware::security::keymint::Digest;
-using aidl::android::hardware::security::keymint::KeyParameter;
-using aidl::android::hardware::security::keymint::KeyParameterValue;
-using aidl::android::hardware::security::keymint::KeyPurpose;
-using aidl::android::hardware::security::keymint::SecurityLevel;
-using aidl::android::hardware::security::keymint::Tag;
-
-using aidl::android::system::keystore2::CreateOperationResponse;
-using aidl::android::system::keystore2::Domain;
-using aidl::android::system::keystore2::IKeystoreSecurityLevel;
-using aidl::android::system::keystore2::IKeystoreService;
-using aidl::android::system::keystore2::KeyDescriptor;
-using aidl::android::system::keystore2::KeyMetadata;
+using aidl::android::hardware::security::dice::BccHandover;
+using aidl::android::security::dice::IDiceNode;
using aidl::android::system::virtualmachineservice::IVirtualMachineService;
using android::base::ErrnoError;
using android::base::Error;
using android::base::Result;
-using android::base::unique_fd;
extern void testlib_sub();
namespace {
-Result<void> test_keystore() {
- // Connect to Keystore.
- ndk::SpAIBinder binder(
- AServiceManager_waitForService("android.system.keystore2.IKeystoreService/default"));
- auto service = IKeystoreService::fromBinder(binder);
- if (service == nullptr) {
- return Error() << "Failed to find Keystore";
- }
- std::shared_ptr<IKeystoreSecurityLevel> securityLevel;
- auto status = service->getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT, &securityLevel);
- if (!status.isOk()) {
- return Error() << "Failed to get security level";
- }
-
- // Create a signing key.
- std::vector<KeyParameter> params;
-
- KeyParameter algo;
- algo.tag = Tag::ALGORITHM;
- algo.value = KeyParameterValue::make<KeyParameterValue::algorithm>(Algorithm::HMAC);
- params.push_back(algo);
-
- KeyParameter key_size;
- key_size.tag = Tag::KEY_SIZE;
- key_size.value = KeyParameterValue::make<KeyParameterValue::integer>(256);
- params.push_back(key_size);
-
- KeyParameter min_mac_length;
- min_mac_length.tag = Tag::MIN_MAC_LENGTH;
- min_mac_length.value = KeyParameterValue::make<KeyParameterValue::integer>(256);
- params.push_back(min_mac_length);
-
- KeyParameter digest;
- digest.tag = Tag::DIGEST;
- digest.value = KeyParameterValue::make<KeyParameterValue::digest>(Digest::SHA_2_256);
- params.push_back(digest);
-
- KeyParameter purposeSign;
- purposeSign.tag = Tag::PURPOSE;
- purposeSign.value = KeyParameterValue::make<KeyParameterValue::keyPurpose>(KeyPurpose::SIGN);
- params.push_back(purposeSign);
-
- KeyParameter purposeVerify;
- purposeVerify.tag = Tag::PURPOSE;
- purposeVerify.value =
- KeyParameterValue::make<KeyParameterValue::keyPurpose>(KeyPurpose::VERIFY);
- params.push_back(purposeVerify);
-
- KeyParameter auth;
- auth.tag = Tag::NO_AUTH_REQUIRED;
- auth.value = KeyParameterValue::make<KeyParameterValue::boolValue>(true);
- params.push_back(auth);
-
- KeyDescriptor descriptor;
- descriptor.domain = Domain::SELINUX;
- descriptor.alias = "payload-test-key";
- descriptor.nspace = 140; // vm_payload_key
-
- KeyMetadata metadata;
- status = securityLevel->generateKey(descriptor, {}, params, 0, {}, &metadata);
- if (!status.isOk()) {
- return Error() << "Failed to create new HMAC key";
- }
-
- // Sign something.
- params.clear();
- params.push_back(algo);
- params.push_back(digest);
- params.push_back(purposeSign);
-
- KeyParameter mac_length;
- mac_length.tag = Tag::MAC_LENGTH;
- mac_length.value = KeyParameterValue::make<KeyParameterValue::integer>(256);
- params.push_back(mac_length);
-
- CreateOperationResponse opResponse;
- status = securityLevel->createOperation(descriptor, params, false, &opResponse);
- if (!status.isOk()) {
- return Error() << "Failed to create keystore signing operation: "
- << status.getServiceSpecificError();
- }
- auto operation = opResponse.iOperation;
-
- std::string message = "This is the message to sign";
- std::optional<std::vector<uint8_t>> out;
- status = operation->update({message.begin(), message.end()}, &out);
- if (!status.isOk()) {
- return Error() << "Failed to call keystore update operation.";
- }
-
- std::optional<std::vector<uint8_t>> signature;
- status = operation->finish({}, {}, &signature);
- if (!status.isOk()) {
- return Error() << "Failed to call keystore finish operation.";
- }
-
- if (!signature.has_value()) {
- return Error() << "Didn't receive a signature from keystore finish operation.";
- }
-
- // Verify the signature.
- params.clear();
- params.push_back(algo);
- params.push_back(digest);
- params.push_back(purposeVerify);
-
- status = securityLevel->createOperation(descriptor, params, false, &opResponse);
- if (!status.isOk()) {
- return Error() << "Failed to create keystore verification operation: "
- << status.getServiceSpecificError();
- }
- operation = opResponse.iOperation;
-
- status = operation->update({message.begin(), message.end()}, &out);
- if (!status.isOk()) {
- return Error() << "Failed to call keystore update operation.";
- }
-
- std::optional<std::vector<uint8_t>> out_signature;
- status = operation->finish({}, signature.value(), &out_signature);
- if (!status.isOk()) {
- return Error() << "Failed to call keystore finish operation.";
- }
-
- return {};
-}
-
template <typename T>
Result<T> report_test(std::string name, Result<T> result) {
auto property = "debug.microdroid.test." + name;
@@ -219,6 +78,23 @@
return ndk::ScopedAStatus::ok();
}
+
+ ndk::ScopedAStatus insecurelyExposeSecret(std::vector<uint8_t>* out) override {
+ ndk::SpAIBinder binder(AServiceManager_getService("android.security.dice.IDiceNode"));
+ auto service = IDiceNode::fromBinder(binder);
+ if (service == nullptr) {
+ return ndk::ScopedAStatus::
+ fromServiceSpecificErrorWithMessage(0, "Failed to find diced");
+ }
+ BccHandover handover;
+ auto deriveStatus = service->derive({}, &handover);
+ if (!deriveStatus.isOk()) {
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(0,
+ "Failed call diced");
+ }
+ *out = {handover.cdiSeal.begin(), handover.cdiSeal.end()};
+ return ndk::ScopedAStatus::ok();
+ }
};
auto testService = ndk::SharedRefBase::make<TestService>();
@@ -283,7 +159,6 @@
report_test("extra_apk", verify_apk());
__system_property_set("debug.microdroid.app.run", "true");
- if (!report_test("keystore", test_keystore()).ok()) return 1;
if (auto res = start_test_service(); res.ok()) {
return 0;
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 653524e..b82064b 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -31,7 +31,6 @@
"libcommand_fds",
"libdisk",
"libidsig",
- "libkvm",
"liblog_rust",
"libmicrodroid_metadata",
"libmicrodroid_payload_config",
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl
index 6562159..6f3d4f0 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl
@@ -28,9 +28,6 @@
/**
* Register a Binder object to get callbacks when the state of the VM changes, such as if it
* dies.
- *
- * TODO(jiyong): this should be registered when IVirtualizationService.run is called. Otherwise,
- * we might miss some events that happen before the registration is done.
*/
void registerCallback(IVirtualMachineCallback callback);
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index 8265f96..c36e561 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -47,6 +47,9 @@
/** Debug level of the VM */
DebugLevel debugLevel;
+ /** Whether the VM should be a protected VM. */
+ boolean protectedVm;
+
/**
* The amount of RAM to give the VM, in MiB. If this is 0 or negative then it will default to
* the value in microdroid.json, if any, or the crosvm default.
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 42eb1e6..5b0c9b7 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -50,7 +50,6 @@
use binder_common::{lazy_service::LazyServiceGuard, new_binder_exception};
use disk::QcowFile;
use idsig::{HashAlgorithm, V4Signature};
-use kvm::{Kvm, Cap};
use log::{debug, error, info, warn};
use microdroid_payload_config::VmPayloadConfig;
use rustutils::system_properties;
@@ -190,9 +189,7 @@
VirtualMachineConfig::AppConfig(config) => BorrowedOrOwned::Owned(
load_app_config(config, &temporary_directory).map_err(|e| {
error!("Failed to load app config from {}: {}", &config.configPath, e);
- // At this point, we do not know the protected status of Vm
- // setting it to false, though this may not be correct.
- write_vm_creation_stats(false, false);
+ write_vm_creation_stats(config.protectedVm, false);
new_binder_exception(
ExceptionCode::SERVICE_SPECIFIC,
format!("Failed to load app config from {}: {}", &config.configPath, e),
@@ -202,7 +199,15 @@
VirtualMachineConfig::RawConfig(config) => BorrowedOrOwned::Borrowed(config),
};
let config = config.as_ref();
- let protected_vm = config.protectedVm;
+ let protected = config.protectedVm;
+
+ // Debug level FULL is only supported for non-protected VMs.
+ if is_debug_level_full && protected {
+ return Err(new_binder_exception(
+ ExceptionCode::SERVICE_SPECIFIC,
+ "FULL debug level not supported for protected VMs.",
+ ));
+ };
// Check if partition images are labeled incorrectly. This is to prevent random images
// which are not protected by the Android Verified Boot (e.g. bits downloaded by apps) from
@@ -226,7 +231,7 @@
let zero_filler_path = temporary_directory.join("zero.img");
write_zero_filler(&zero_filler_path).map_err(|e| {
error!("Failed to make composite image: {}", e);
- write_vm_creation_stats(protected_vm, false);
+ write_vm_creation_stats(protected, false);
new_binder_exception(
ExceptionCode::SERVICE_SPECIFIC,
format!("Failed to make composite image: {}", e),
@@ -248,24 +253,6 @@
})
.collect::<Result<Vec<DiskFile>, _>>()?;
- let protected_vm_supported = Kvm::new()
- .map_err(|e| new_binder_exception(ExceptionCode::SERVICE_SPECIFIC, e.to_string()))?
- .check_extension(Cap::ArmProtectedVm);
- let protected = config.protectedVm && protected_vm_supported;
- if config.protectedVm && !protected_vm_supported {
- warn!("Protected VM was requested, but it isn't supported on this machine. Ignored.");
- }
-
- // And force run in non-protected mode when debug level is FULL
- let protected = if is_debug_level_full {
- if protected {
- warn!("VM will run in FULL debug level. Running in non-protected mode");
- }
- false
- } else {
- protected
- };
-
// Actually start the VM.
let crosvm_config = CrosvmConfig {
cid,
@@ -292,7 +279,7 @@
)
.map_err(|e| {
error!("Failed to create VM with config {:?}: {}", config, e);
- write_vm_creation_stats(protected_vm, false);
+ write_vm_creation_stats(protected, false);
new_binder_exception(
ExceptionCode::SERVICE_SPECIFIC,
format!("Failed to create VM: {}", e),
@@ -300,7 +287,7 @@
})?,
);
state.add_vm(Arc::downgrade(&instance));
- write_vm_creation_stats(protected_vm, true);
+ write_vm_creation_stats(protected, true);
Ok(VirtualMachine::create(instance))
}
@@ -587,6 +574,7 @@
vm_config.memoryMib = config.memoryMib;
}
+ vm_config.protectedVm = config.protectedVm;
vm_config.numCpus = config.numCpus;
vm_config.cpuAffinity = config.cpuAffinity.clone();
diff --git a/virtualizationservice/src/payload.rs b/virtualizationservice/src/payload.rs
index 65adc25..7b8cb7f 100644
--- a/virtualizationservice/src/payload.rs
+++ b/virtualizationservice/src/payload.rs
@@ -53,7 +53,7 @@
list: Vec<ApexInfo>,
}
-#[derive(Clone, Debug, Deserialize)]
+#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
struct ApexInfo {
#[serde(rename = "moduleName")]
name: String,
@@ -66,6 +66,15 @@
// The field claims to be milliseconds but is actually seconds.
#[serde(rename = "lastUpdateMillis")]
last_update_seconds: u64,
+
+ #[serde(rename = "isFactory")]
+ is_factory: bool,
+
+ #[serde(rename = "isActive")]
+ is_active: bool,
+
+ #[serde(rename = "provideSharedApexLibs")]
+ provide_shared_apex_libs: bool,
}
impl ApexInfoList {
@@ -91,26 +100,23 @@
Ok(apex_info_list)
})
}
+}
- /// Returns the list of apex names matching with the predicate
- fn get_matching(&self, predicate: fn(&ApexInfo) -> bool) -> Vec<String> {
- self.list.iter().filter(|info| predicate(info)).map(|info| info.name.clone()).collect()
- }
-
- fn get(&self, apex_name: &str) -> Result<&ApexInfo> {
- self.list
- .iter()
- .find(|apex| apex.name == apex_name)
- .ok_or_else(|| anyhow!("{} not found.", apex_name))
- }
-
- fn get_path_for(&self, apex_name: &str) -> Result<PathBuf> {
- Ok(self.get(apex_name)?.path.clone())
+impl ApexInfo {
+ fn matches(&self, apex_config: &ApexConfig) -> bool {
+ // Match with pseudo name "{CLASSPATH}" which represents APEXes contributing
+ // to any derive_classpath environment variable
+ if apex_config.name == "{CLASSPATH}" && self.has_classpath_jar {
+ return true;
+ }
+ if apex_config.name == self.name {
+ return true;
+ }
+ false
}
}
struct PackageManager {
- // TODO(b/199146189) use IPackageManagerNative
apex_info_list: &'static ApexInfoList,
}
@@ -137,6 +143,8 @@
let metadata = metadata(&apex_info.path)?;
apex_info.last_update_seconds =
metadata.modified()?.duration_since(SystemTime::UNIX_EPOCH)?.as_secs();
+ // by definition, staged apex can't be a factory apex.
+ apex_info.is_factory = false;
}
}
}
@@ -147,21 +155,21 @@
fn make_metadata_file(
config_path: &str,
- apex_names: &[String],
+ apex_infos: &[&ApexInfo],
temporary_directory: &Path,
- apex_list: &ApexInfoList,
) -> Result<ParcelFileDescriptor> {
let metadata_path = temporary_directory.join("metadata");
let metadata = Metadata {
version: 1,
- apexes: apex_names
+ apexes: apex_infos
.iter()
.enumerate()
- .map(|(i, apex_name)| {
+ .map(|(i, apex_info)| {
Ok(ApexPayload {
- name: apex_name.clone(),
+ name: apex_info.name.clone(),
partition_name: format!("microdroid-apex-{}", i),
- last_update_seconds: apex_list.get(apex_name)?.last_update_seconds,
+ last_update_seconds: apex_info.last_update_seconds,
+ is_factory: apex_info.is_factory,
..Default::default()
})
})
@@ -220,12 +228,13 @@
let pm = PackageManager::new()?;
let apex_list = pm.get_apex_list(vm_payload_config.prefer_staged)?;
- // collect APEX names from config
- let apexes = collect_apex_names(&apex_list, &vm_payload_config.apexes, app_config.debugLevel);
- info!("Microdroid payload APEXes: {:?}", apexes);
+ // collect APEXes from config
+ let apex_infos =
+ collect_apex_infos(&apex_list, &vm_payload_config.apexes, app_config.debugLevel);
+ info!("Microdroid payload APEXes: {:?}", apex_infos.iter().map(|ai| &ai.name));
let metadata_file =
- make_metadata_file(&app_config.configPath, &apexes, temporary_directory, &apex_list)?;
+ make_metadata_file(&app_config.configPath, &apex_infos, temporary_directory)?;
// put metadata at the first partition
let mut partitions = vec![Partition {
label: "payload-metadata".to_owned(),
@@ -233,9 +242,8 @@
writable: false,
}];
- for (i, apex) in apexes.iter().enumerate() {
- let apex_path = apex_list.get_path_for(apex)?;
- let apex_file = open_parcel_file(&apex_path, false)?;
+ for (i, apex_info) in apex_infos.iter().enumerate() {
+ let apex_file = open_parcel_file(&apex_info.path, false)?;
partitions.push(Partition {
label: format!("microdroid-apex-{}", i),
image: Some(apex_file),
@@ -310,30 +318,26 @@
Ok(apexes)
}
-// Collect APEX names from config
-fn collect_apex_names(
- apex_list: &ApexInfoList,
- apexes: &[ApexConfig],
+// Collect ApexInfos from VM config
+fn collect_apex_infos<'a>(
+ apex_list: &'a ApexInfoList,
+ apex_configs: &[ApexConfig],
debug_level: DebugLevel,
-) -> Vec<String> {
- // Process pseudo names like "{CLASSPATH}".
- // For now we have following pseudo APEX names:
- // - {CLASSPATH}: represents APEXes contributing to any derive_classpath environment variable
- let mut apex_names: Vec<String> = apexes
- .iter()
- .flat_map(|apex| match apex.name.as_str() {
- "{CLASSPATH}" => apex_list.get_matching(|apex| apex.has_classpath_jar),
- _ => vec![apex.name.clone()],
- })
- .collect();
- // Add required APEXes
- apex_names.extend(MICRODROID_REQUIRED_APEXES.iter().map(|name| name.to_string()));
+) -> Vec<&'a ApexInfo> {
+ let mut additional_apexes: Vec<&str> = MICRODROID_REQUIRED_APEXES.to_vec();
if debug_level != DebugLevel::NONE {
- apex_names.extend(MICRODROID_REQUIRED_APEXES_DEBUG.iter().map(|name| name.to_string()));
+ additional_apexes.extend(MICRODROID_REQUIRED_APEXES_DEBUG.to_vec());
}
- apex_names.sort();
- apex_names.dedup();
- apex_names
+
+ apex_list
+ .list
+ .iter()
+ .filter(|ai| {
+ apex_configs.iter().any(|cfg| ai.matches(cfg) && ai.is_active)
+ || additional_apexes.iter().any(|name| name == &ai.name && ai.is_active)
+ || ai.provide_shared_apex_libs
+ })
+ .collect()
}
pub fn add_microdroid_images(
@@ -403,34 +407,125 @@
}
#[test]
- fn test_collect_apex_names() {
- let apex_list = ApexInfoList {
+ fn test_collect_apexes() {
+ let apex_info_list = ApexInfoList {
list: vec![
ApexInfo {
- name: "hasnt_classpath".to_string(),
- path: PathBuf::from("path0"),
+ // 0
+ name: "com.android.adbd".to_string(),
+ path: PathBuf::from("adbd"),
has_classpath_jar: false,
last_update_seconds: 12345678,
+ is_factory: true,
+ is_active: true,
+ ..Default::default()
},
ApexInfo {
+ // 1
+ name: "com.android.os.statsd".to_string(),
+ path: PathBuf::from("statsd"),
+ has_classpath_jar: false,
+ last_update_seconds: 12345678,
+ is_factory: true,
+ is_active: false,
+ ..Default::default()
+ },
+ ApexInfo {
+ // 2
+ name: "com.android.os.statsd".to_string(),
+ path: PathBuf::from("statsd/updated"),
+ has_classpath_jar: false,
+ last_update_seconds: 12345678 + 1,
+ is_factory: false,
+ is_active: true,
+ ..Default::default()
+ },
+ ApexInfo {
+ // 3
+ name: "no_classpath".to_string(),
+ path: PathBuf::from("no_classpath"),
+ has_classpath_jar: false,
+ last_update_seconds: 12345678,
+ is_factory: true,
+ is_active: true,
+ ..Default::default()
+ },
+ ApexInfo {
+ // 4
name: "has_classpath".to_string(),
- path: PathBuf::from("path1"),
+ path: PathBuf::from("has_classpath"),
has_classpath_jar: true,
last_update_seconds: 87654321,
+ is_factory: true,
+ is_active: false,
+ ..Default::default()
+ },
+ ApexInfo {
+ // 5
+ name: "has_classpath".to_string(),
+ path: PathBuf::from("has_classpath/updated"),
+ has_classpath_jar: true,
+ last_update_seconds: 87654321 + 1,
+ is_factory: false,
+ is_active: true,
+ ..Default::default()
+ },
+ ApexInfo {
+ // 6
+ name: "apex-foo".to_string(),
+ path: PathBuf::from("apex-foo"),
+ has_classpath_jar: false,
+ last_update_seconds: 87654321,
+ is_factory: true,
+ is_active: false,
+ ..Default::default()
+ },
+ ApexInfo {
+ // 7
+ name: "apex-foo".to_string(),
+ path: PathBuf::from("apex-foo/updated"),
+ has_classpath_jar: false,
+ last_update_seconds: 87654321 + 1,
+ is_factory: false,
+ is_active: true,
+ ..Default::default()
+ },
+ ApexInfo {
+ // 8
+ name: "sharedlibs".to_string(),
+ path: PathBuf::from("apex-foo"),
+ last_update_seconds: 87654321,
+ is_factory: true,
+ provide_shared_apex_libs: true,
+ ..Default::default()
+ },
+ ApexInfo {
+ // 9
+ name: "sharedlibs".to_string(),
+ path: PathBuf::from("apex-foo/updated"),
+ last_update_seconds: 87654321 + 1,
+ is_active: true,
+ provide_shared_apex_libs: true,
+ ..Default::default()
},
],
};
- let apexes = vec![
- ApexConfig { name: "config_name".to_string() },
+ let apex_configs = vec![
+ ApexConfig { name: "apex-foo".to_string() },
ApexConfig { name: "{CLASSPATH}".to_string() },
];
assert_eq!(
- collect_apex_names(&apex_list, &apexes, DebugLevel::FULL),
+ collect_apex_infos(&apex_info_list, &apex_configs, DebugLevel::FULL),
vec![
- "com.android.adbd".to_string(),
- "com.android.os.statsd".to_string(),
- "config_name".to_string(),
- "has_classpath".to_string(),
+ // Pass active/required APEXes
+ &apex_info_list.list[0],
+ &apex_info_list.list[2],
+ // Pass active APEXes specified in the config
+ &apex_info_list.list[5],
+ &apex_info_list.list[7],
+ // Pass both preinstalled(inactive) and updated(active) for "sharedlibs" APEXes
+ &apex_info_list.list[8],
+ &apex_info_list.list[9],
]
);
}
diff --git a/vm/src/main.rs b/vm/src/main.rs
index ad8c201..25f9bfb 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -72,6 +72,10 @@
#[structopt(long, default_value = "none", parse(try_from_str=parse_debug_level))]
debug: DebugLevel,
+ /// Run VM in protected mode.
+ #[structopt(short, long)]
+ protected: bool,
+
/// Memory size (in MiB) of the VM. If unspecified, defaults to the value of `memory_mib`
/// in the VM config file.
#[structopt(short, long)]
@@ -174,6 +178,7 @@
console,
log,
debug,
+ protected,
mem,
cpus,
cpu_affinity,
@@ -188,6 +193,7 @@
console.as_deref(),
log.as_deref(),
debug,
+ protected,
mem,
cpus,
cpu_affinity,
diff --git a/vm/src/run.rs b/vm/src/run.rs
index 8583fe2..d558add 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -50,6 +50,7 @@
console_path: Option<&Path>,
log_path: Option<&Path>,
debug_level: DebugLevel,
+ protected: bool,
mem: Option<u32>,
cpus: Option<u32>,
cpu_affinity: Option<String>,
@@ -100,6 +101,7 @@
instanceImage: open_parcel_file(instance, true /* writable */)?.into(),
configPath: config_path.to_owned(),
debugLevel: debug_level,
+ protectedVm: protected,
memoryMib: mem.unwrap_or(0) as i32, // 0 means use the VM default
numCpus: cpus.unwrap_or(1) as i32,
cpuAffinity: cpu_affinity,