Merge changes from topic "vm-sharedlibs-apexes"
* changes:
Pass sharedlibs APEXes to VM
Pass active APEXes to VM
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/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/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/src/compilation.rs b/compos/src/compilation.rs
index e8f55f8..7e3834a 100644
--- a/compos/src/compilation.rs
+++ b/compos/src/compilation.rs
@@ -18,6 +18,7 @@
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;
@@ -35,11 +36,13 @@
IAuthFsService::IAuthFsService,
};
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
pub struct OdrefreshContext<'a> {
+ compilation_mode: CompilationMode,
system_dir_fd: i32,
output_dir_fd: i32,
staging_dir_fd: i32,
@@ -50,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,
@@ -57,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");
}
@@ -75,6 +86,7 @@
// CompOS.
Ok(Self {
+ compilation_mode,
system_dir_fd,
output_dir_fd,
staging_dir_fd,
@@ -143,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, &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);
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/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..6f27ce1 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",
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 0c294e9..cb27a24 100644
--- a/microdroid/microdroid.json
+++ b/microdroid/microdroid.json
@@ -37,6 +37,5 @@
"writable": true
}
],
- "memory_mib": 256,
- "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_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 5a77198..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>,
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/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/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 238b625..7b8cb7f 100644
--- a/virtualizationservice/src/payload.rs
+++ b/virtualizationservice/src/payload.rs
@@ -117,7 +117,6 @@
}
struct PackageManager {
- // TODO(b/199146189) use IPackageManagerNative
apex_info_list: &'static ApexInfoList,
}
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,