Support CompOS Key Service in a VM
Add the ability to start the service in a VM, and to then communicate
with it from the host via RPC Binder. Update command-line syntax.
Also revert my recent client naming change, since one tool that can
handle both host & VM seems better than two different ones.
Bug: 193603140
Test: Manual: start service in VM, connect to it, generate & verify keys.
Change-Id: I3cdb25395537e29bbfaa957eeac0c16ba4de93de
diff --git a/compos/src/compos_key_host_main.rs b/compos/src/compos_key_host_main.rs
deleted file mode 100644
index 28b069a..0000000
--- a/compos/src/compos_key_host_main.rs
+++ /dev/null
@@ -1,45 +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.
-
-//! Run the CompOS key management service in the host, using normal Binder.
-
-mod compos_key_service;
-
-use crate::compos_key_service::CompOsKeyService;
-use anyhow::{Context, Result};
-use compos_aidl_interface::aidl::com::android::compos::ICompOsKeyService::BnCompOsKeyService;
-use compos_aidl_interface::binder::{add_service, BinderFeatures, ProcessState};
-use log::{info, Level};
-
-const LOG_TAG: &str = "CompOsKeyService";
-const OUR_SERVICE_NAME: &str = "android.system.composkeyservice";
-
-fn main() -> Result<()> {
- android_logger::init_once(
- android_logger::Config::default().with_tag(LOG_TAG).with_min_level(Level::Trace),
- );
-
- // We need to start the thread pool for Binder to work properly.
- ProcessState::start_thread_pool();
-
- let service = CompOsKeyService::new()?;
- let service = BnCompOsKeyService::new_binder(service, BinderFeatures::default());
-
- add_service(OUR_SERVICE_NAME, service.as_binder()).context("Adding service failed")?;
- info!("It's alive!");
-
- ProcessState::join_thread_pool();
-
- Ok(())
-}
diff --git a/compos/src/compos_key_main.rs b/compos/src/compos_key_main.rs
new file mode 100644
index 0000000..9135c18
--- /dev/null
+++ b/compos/src/compos_key_main.rs
@@ -0,0 +1,72 @@
+// 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.
+
+//! Run the CompOS key management service, either in the host using normal Binder or in the
+//! VM using RPC Binder.
+
+mod compos_key_service;
+
+use crate::compos_key_service::{CompOsKeyService, KeystoreNamespace};
+use anyhow::{bail, Context, Result};
+use binder::unstable_api::AsNative;
+use compos_aidl_interface::aidl::com::android::compos::ICompOsKeyService::BnCompOsKeyService;
+use compos_aidl_interface::binder::{add_service, BinderFeatures, ProcessState};
+use log::{info, Level};
+
+const LOG_TAG: &str = "CompOsKeyService";
+const OUR_SERVICE_NAME: &str = "android.system.composkeyservice";
+const OUR_VSOCK_PORT: u32 = 3142;
+
+fn main() -> Result<()> {
+ android_logger::init_once(
+ android_logger::Config::default().with_tag(LOG_TAG).with_min_level(Level::Info),
+ );
+
+ let matches = clap::App::new("compos_key_main")
+ .arg(clap::Arg::with_name("rpc_binder").long("rpc-binder"))
+ .get_matches();
+
+ let rpc_binder = matches.is_present("rpc_binder");
+
+ let key_namespace =
+ if rpc_binder { KeystoreNamespace::VmPayload } else { KeystoreNamespace::Odsign };
+ let service = CompOsKeyService::new(key_namespace)?;
+ let mut service =
+ BnCompOsKeyService::new_binder(service, BinderFeatures::default()).as_binder();
+
+ if rpc_binder {
+ info!("Starting RPC service");
+ // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+ // Plus the binder objects are threadsafe.
+ let retval = unsafe {
+ binder_rpc_unstable_bindgen::RunRpcServer(
+ service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder,
+ OUR_VSOCK_PORT,
+ )
+ };
+ if retval {
+ info!("RPC server has shut down gracefully");
+ } else {
+ bail!("Premature termination of RPC server");
+ }
+ } else {
+ info!("Starting binder service");
+ add_service(OUR_SERVICE_NAME, service).context("Adding service failed")?;
+ info!("It's alive!");
+
+ ProcessState::join_thread_pool();
+ }
+
+ Ok(())
+}
diff --git a/compos/src/compos_key_service.rs b/compos/src/compos_key_service.rs
index 654eedd..66451b3 100644
--- a/compos/src/compos_key_service.rs
+++ b/compos/src/compos_key_service.rs
@@ -38,8 +38,17 @@
use scopeguard::ScopeGuard;
use std::ffi::CString;
+/// Keystore2 namespace IDs, used for access control to keys.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum KeystoreNamespace {
+ /// In the host we re-use the ID assigned to odsign. See system/sepolicy/private/keystore2_key_contexts.
+ // TODO(alanstokes): Remove this.
+ Odsign = 101,
+ /// In a VM we can use the generic ID allocated for payloads. See microdroid's keystore2_key_contexts.
+ VmPayload = 140,
+}
+
const KEYSTORE_SERVICE_NAME: &str = "android.system.keystore2.IKeystoreService/default";
-const COMPOS_NAMESPACE: i64 = 101;
const PURPOSE_SIGN: KeyParameter =
KeyParameter { tag: Tag::PURPOSE, value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN) };
const ALGORITHM: KeyParameter =
@@ -57,10 +66,11 @@
const NO_AUTH_REQUIRED: KeyParameter =
KeyParameter { tag: Tag::NO_AUTH_REQUIRED, value: KeyParameterValue::BoolValue(true) };
-const KEY_DESCRIPTOR: KeyDescriptor =
- KeyDescriptor { domain: Domain::BLOB, nspace: COMPOS_NAMESPACE, alias: None, blob: None };
+const BLOB_KEY_DESCRIPTOR: KeyDescriptor =
+ KeyDescriptor { domain: Domain::BLOB, nspace: 0, alias: None, blob: None };
pub struct CompOsKeyService {
+ namespace: KeystoreNamespace,
random: SystemRandom,
security_level: Strong<dyn IKeystoreSecurityLevel>,
}
@@ -94,11 +104,12 @@
}
impl CompOsKeyService {
- pub fn new() -> Result<Self> {
+ pub fn new(namespace: KeystoreNamespace) -> Result<Self> {
let keystore_service = wait_for_interface::<dyn IKeystoreService>(KEYSTORE_SERVICE_NAME)
.context("No Keystore service")?;
Ok(Self {
+ namespace,
random: SystemRandom::new(),
security_level: keystore_service
.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT)
@@ -107,6 +118,7 @@
}
fn do_generate(&self) -> Result<CompOsKeyData> {
+ let key_descriptor = KeyDescriptor { nspace: self.namespace as i64, ..BLOB_KEY_DESCRIPTOR };
let key_parameters =
[PURPOSE_SIGN, ALGORITHM, PADDING, DIGEST, KEY_SIZE, EXPONENT, NO_AUTH_REQUIRED];
let attestation_key = None;
@@ -115,7 +127,7 @@
let key_metadata = self
.security_level
- .generateKey(&KEY_DESCRIPTOR, attestation_key, &key_parameters, flags, &entropy)
+ .generateKey(&key_descriptor, attestation_key, &key_parameters, flags, &entropy)
.context("Generating key failed")?;
if let (Some(certificate), Some(blob)) = (key_metadata.certificate, key_metadata.key.blob) {
@@ -139,7 +151,11 @@
}
fn do_sign(&self, key_blob: &[u8], data: &[u8]) -> Result<Vec<u8>> {
- let key_descriptor = KeyDescriptor { blob: Some(key_blob.to_vec()), ..KEY_DESCRIPTOR };
+ let key_descriptor = KeyDescriptor {
+ nspace: self.namespace as i64,
+ blob: Some(key_blob.to_vec()),
+ ..BLOB_KEY_DESCRIPTOR
+ };
let operation_parameters = [PURPOSE_SIGN, ALGORITHM, PADDING, DIGEST];
let forced = false;