Merge "Add Keystore 2.0 service." am: 0078542aa1
Original change: https://android-review.googlesource.com/c/platform/system/security/+/1395708
Change-Id: I8b30f67ee5943ce28ac55928906bb9e93c657e04
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index f4b153c..a1ee969 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -53,6 +53,18 @@
],
}
+rust_binary {
+ name: "keystore2",
+ srcs: ["src/keystore2_main.rs"],
+ rustlibs: [
+ "libandroid_logger",
+ "libbinder_rs",
+ "libkeystore2",
+ "liblog_rust",
+ ],
+ init_rc: ["keystore2.rc"],
+}
+
// This is a placeholder for the libraries that will be generated from the AIDL specs
// eventually.
rust_library {
diff --git a/keystore2/keystore2.rc b/keystore2/keystore2.rc
new file mode 100644
index 0000000..139c94b
--- /dev/null
+++ b/keystore2/keystore2.rc
@@ -0,0 +1,12 @@
+# Start the keystore2 service.
+# Keystore 2.0 changes its working directory to the first positional
+# command line option, i.e., /data/misc/keystore, where it stores its
+# database.
+# Keystore shall run as user keystore and groups keystore, readproc, and log.
+#
+# See system/core/init/README.md for information on the init.rc language.
+service keystore2 /system/bin/keystore2 /data/misc/keystore
+ class main
+ user keystore
+ group keystore readproc log
+ writepid /dev/cpuset/foreground/tasks
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
new file mode 100644
index 0000000..dea0a93
--- /dev/null
+++ b/keystore2/src/keystore2_main.rs
@@ -0,0 +1,59 @@
+// Copyright 2020, 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.
+
+//! This crate implements Keystore 2.0.
+
+use binder::Interface;
+use keystore2::service::KeystoreService;
+use log::{error, info};
+use std::panic;
+
+static KS2_SERVICE_NAME: &str = "android.system.keystore2";
+
+/// Keystore 2.0 takes one argument which is a path indicating its designated working directory.
+fn main() {
+ // Initialize android logging.
+ android_logger::init_once(
+ android_logger::Config::default().with_tag("keystore2").with_min_level(log::Level::Debug),
+ );
+ // Redirect panic messages to logcat.
+ panic::set_hook(Box::new(|panic_info| {
+ error!("{}", panic_info);
+ }));
+
+ // Saying hi.
+ info!("Keystore2 is starting.");
+
+ let mut args = std::env::args();
+ args.next().expect("That's odd. How is there not even a first argument?");
+ if let Some(dir) = args.next() {
+ if std::env::set_current_dir(dir.clone()).is_err() {
+ panic!("Failed to set working directory {}.", dir)
+ }
+ } else {
+ panic!("Must specify a working directory.");
+ }
+
+ let ks_service = KeystoreService::new_native_binder().unwrap_or_else(|e| {
+ panic!("Failed to create service {} because of {:?}.", KS2_SERVICE_NAME, e);
+ });
+ binder::add_service(KS2_SERVICE_NAME, ks_service.as_binder()).unwrap_or_else(|e| {
+ panic!("Failed to register service {} because of {:?}.", KS2_SERVICE_NAME, e);
+ });
+
+ info!("Successfully registered Keystore 2.0 service.");
+ info!("Joining threadpool now.");
+
+ binder::ProcessState::join_thread_pool();
+}
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 7439a5b..067399e 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -19,5 +19,8 @@
pub mod globals;
/// Internal Representation of Key Parameter and convenience functions.
pub mod key_parameter;
+pub mod operation;
pub mod permission;
+pub mod security_level;
+pub mod service;
pub mod utils;
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
new file mode 100644
index 0000000..f987188
--- /dev/null
+++ b/keystore2/src/operation.rs
@@ -0,0 +1,768 @@
+// Copyright 2020, 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.
+
+//! This crate implements the `IKeystoreOperation` AIDL interface, which represents
+//! an ongoing key operation, as well as the operation database, which is mainly
+//! required for tracking operations for the purpose of pruning.
+//! This crate also implements an operation pruning strategy.
+//!
+//! Operations implement the API calls update, finish, and abort.
+//! Additionally, an operation can be dropped and pruned. The former
+//! happens if the client deletes a binder to the operation object.
+//! An existing operation may get pruned when running out of operation
+//! slots and a new operation takes precedence.
+//!
+//! ## Operation Lifecycle
+//! An operation gets created when the client calls `IKeystoreSecurityLevel::create`.
+//! It may receive zero or more update request. The lifecycle ends when:
+//! * `update` yields an error.
+//! * `finish` is called.
+//! * `abort` is called.
+//! * The operation gets dropped.
+//! * The operation gets pruned.
+//! `Operation` has an `Outcome` member. While the outcome is `Outcome::Unknown`,
+//! the operation is active and in a good state. Any of the above conditions may
+//! change the outcome to one of the defined outcomes Success, Abort, Dropped,
+//! Pruned, or ErrorCode. The latter is chosen in the case of an unexpected error, during
+//! `update` or `finish`. `Success` is chosen iff `finish` completes without error.
+//! Note that all operations get dropped eventually in the sense that they lose
+//! their last reference and get destroyed. At that point, the fate of the operation
+//! gets logged. However, an operation will transition to `Outcome::Dropped` iff
+//! the operation was still active (`Outcome::Unknown`) at that time.
+//!
+//! ## Operation Dropping
+//! To observe the dropping of an operation, we have to make sure that there
+//! are no strong references to the IBinder representing this operation.
+//! This would be simple enough if the operation object would need to be accessed
+//! only by transactions. But to perform pruning, we have to retain a reference to the
+//! original operation object.
+//!
+//! ## Operation Pruning
+//! Pruning an operation happens during the creation of a new operation.
+//! We have to iterate through the operation database to find a suitable
+//! candidate. Then we abort and finalize this operation setting its outcome to
+//! `Outcome::Pruned`. The corresponding KeyMint operation slot will have been freed
+//! up at this point, but the `Operation` object lingers. When the client
+//! attempts to use the operation again they will receive
+//! ErrorCode::INVALID_OPERATION_HANDLE indicating that the operation no longer
+//! exits. This should be the cue for the client to destroy its binder.
+//! At that point the operation gets dropped.
+//!
+//! ## Architecture
+//! The `IKeystoreOperation` trait is implemented by `KeystoreOperation`.
+//! This acts as a proxy object holding a strong reference to actual operation
+//! implementation `Operation`.
+//!
+//! ```
+//! struct KeystoreOperation {
+//! operation: Mutex<Option<Arc<Operation>>>,
+//! }
+//! ```
+//!
+//! The `Mutex` serves two purposes. It provides interior mutability allowing
+//! us to set the Option to None. We do this when the life cycle ends during
+//! a call to `update`, `finish`, or `abort`. As a result most of the Operation
+//! related resources are freed. The `KeystoreOperation` proxy object still
+//! lingers until dropped by the client.
+//! The second purpose is to protect operations against concurrent usage.
+//! Failing to lock this mutex yields `ResponseCode::OPERATION_BUSY` and indicates
+//! a programming error in the client.
+//!
+//! Note that the Mutex only protects the operation against concurrent client calls.
+//! We still retain weak references to the operation in the operation database:
+//!
+//! ```
+//! struct OperationDb {
+//! operations: Mutex<Vec<Weak<Operation>>>
+//! }
+//! ```
+//!
+//! This allows us to access the operations for the purpose of pruning.
+//! We do this in three phases.
+//! 1. We gather the pruning information. Besides non mutable information,
+//! we access `last_usage` which is protected by a mutex.
+//! We only lock this mutex for single statements at a time. During
+//! this phase we hold the operation db lock.
+//! 2. We choose a pruning candidate by computing the pruning resistance
+//! of each operation. We do this entirely with information we now
+//! have on the stack without holding any locks.
+//! (See `OperationDb::prune` for more details on the pruning strategy.)
+//! 3. During pruning we briefly lock the operation database again to get the
+//! the pruning candidate by index. We then attempt to abort the candidate.
+//! If the candidate was touched in the meantime or is currently fulfilling
+//! a request (i.e., the client calls update, finish, or abort),
+//! we go back to 1 and try again.
+//!
+//! So the outer Mutex in `KeystoreOperation::operation` only protects
+//! operations against concurrent client calls but not against concurrent
+//! pruning attempts. This is what the `Operation::outcome` mutex is used for.
+//!
+//! ```
+//! struct Operation {
+//! ...
+//! outcome: Mutex<Outcome>,
+//! ...
+//! }
+//! ```
+//!
+//! Any request that can change the outcome, i.e., `update`, `finish`, `abort`,
+//! `drop`, and `prune` has to take the outcome lock and check if the outcome
+//! is still `Outcome::Unknown` before entering. `prune` is special in that
+//! it will `try_lock`, because we don't want to be blocked on a potentially
+//! long running request at another operation. If it fails to get the lock
+//! the operation is either being touched, which changes its pruning resistance,
+//! or it transitions to its end-of-life, which means we may get a free slot.
+//! Either way, we have to revaluate the pruning scores.
+
+use std::{
+ collections::HashMap,
+ sync::{Arc, Mutex, MutexGuard, Weak},
+ time::Duration,
+ time::Instant,
+};
+
+use crate::error::{map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
+use crate::utils::Asp;
+use android_hardware_keymint::aidl::android::hardware::keymint::{
+ IKeyMintOperation::IKeyMintOperation, KeyParameter::KeyParameter as KmParam, Tag::Tag,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ IKeystoreOperation::BnKeystoreOperation, IKeystoreOperation::IKeystoreOperation,
+};
+use anyhow::{anyhow, Context, Result};
+use binder::{IBinder, Interface};
+
+/// Operations have `Outcome::Unknown` as long as they are active. They transition
+/// to one of the other variants exactly once. The distinction in outcome is mainly
+/// for the statistic.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+enum Outcome {
+ Unknown,
+ Success,
+ Abort,
+ Dropped,
+ Pruned,
+ ErrorCode(ErrorCode),
+}
+
+/// Operation bundles all of the operation related resources and tracks the operation's
+/// outcome.
+#[derive(Debug)]
+pub struct Operation {
+ // The index of this operation in the OperationDb.
+ index: usize,
+ km_op: Asp,
+ last_usage: Mutex<Instant>,
+ outcome: Mutex<Outcome>,
+ owner: u32, // Uid of the operation's owner.
+}
+
+struct PruningInfo {
+ last_usage: Instant,
+ owner: u32,
+ index: usize,
+}
+
+static EMPTY_BLOB: &[u8] = &[];
+// We don't except more than 32KiB of data in `update`, `updateAad`, and `finish`.
+const MAX_RECEIVE_DATA: usize = 0x8000;
+
+impl Operation {
+ /// Constructor
+ pub fn new(index: usize, km_op: Box<dyn IKeyMintOperation>, owner: u32) -> Self {
+ Self {
+ index,
+ km_op: Asp::new(km_op.as_binder()),
+ last_usage: Mutex::new(Instant::now()),
+ outcome: Mutex::new(Outcome::Unknown),
+ owner,
+ }
+ }
+
+ fn get_pruning_info(&self) -> PruningInfo {
+ // Expect safety:
+ // `last_usage` is locked only for primitive single line statements.
+ // There is no chance to panic and poison the mutex.
+ PruningInfo {
+ last_usage: *self.last_usage.lock().expect("In get_pruning_info."),
+ owner: self.owner,
+ index: self.index,
+ }
+ }
+
+ fn prune(&self, last_usage: Instant) -> Result<(), Error> {
+ let mut locked_outcome = match self.outcome.try_lock() {
+ Ok(guard) => match *guard {
+ Outcome::Unknown => guard,
+ _ => return Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)),
+ },
+ Err(_) => return Err(Error::Rc(ResponseCode::OPERATION_BUSY)),
+ };
+
+ // In `OperationDb::prune`, which is our caller, we first gather the pruning
+ // information including the last usage. When we select a candidate
+ // we call `prune` on that candidate passing the last_usage
+ // that we gathered earlier. If the actual last usage
+ // has changed since than, it means the operation was busy in the
+ // meantime, which means that we have to reevaluate the pruning score.
+ //
+ // Expect safety:
+ // `last_usage` is locked only for primitive single line statements.
+ // There is no chance to panic and poison the mutex.
+ if *self.last_usage.lock().expect("In Operation::prune()") != last_usage {
+ return Err(Error::Rc(ResponseCode::OPERATION_BUSY));
+ }
+ *locked_outcome = Outcome::Pruned;
+
+ let km_op: Box<dyn IKeyMintOperation> = match self.km_op.get_interface() {
+ Ok(km_op) => km_op,
+ Err(e) => {
+ log::error!("In prune: Failed to get KeyMintOperation interface.\n {:?}", e);
+ return Err(Error::sys());
+ }
+ };
+
+ // We abort the operation. If there was an error we log it but ignore it.
+ if let Err(e) = map_km_error(km_op.abort()) {
+ log::error!("In prune: KeyMint::abort failed with {:?}.", e);
+ }
+
+ Ok(())
+ }
+
+ // This function takes a Result from a KeyMint call and inspects it for errors.
+ // If an error was found it updates the given `locked_outcome` accordingly.
+ // It forwards the Result unmodified.
+ // The precondition to this call must be *locked_outcome == Outcome::Unknown.
+ // Ideally the `locked_outcome` came from a successful call to `check_active`
+ // see below.
+ fn update_outcome<T>(
+ &self,
+ locked_outcome: &mut Outcome,
+ err: Result<T, Error>,
+ ) -> Result<T, Error> {
+ match &err {
+ Err(Error::Km(e)) => *locked_outcome = Outcome::ErrorCode(*e),
+ Err(_) => *locked_outcome = Outcome::ErrorCode(ErrorCode::UNKNOWN_ERROR),
+ Ok(_) => (),
+ }
+ err
+ }
+
+ // This function grabs the outcome lock and checks the current outcome state.
+ // If the outcome is still `Outcome::Unknown`, this function returns
+ // the locked outcome for further updates. In any other case it returns
+ // ErrorCode::INVALID_OPERATION_HANDLE indicating that this operation has
+ // been finalized and is no longer active.
+ fn check_active(&self) -> Result<MutexGuard<Outcome>> {
+ let guard = self.outcome.lock().expect("In check_active.");
+ match *guard {
+ Outcome::Unknown => Ok(guard),
+ _ => Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)).context(format!(
+ "In check_active: Call on finalized operation with outcome: {:?}.",
+ *guard
+ )),
+ }
+ }
+
+ // This function checks the amount of input data sent to us. We reject any buffer
+ // exceeding MAX_RECEIVE_DATA bytes as input to `update`, `update_aad`, and `finish`
+ // in order to force clients into using reasonable limits.
+ fn check_input_length(data: &[u8]) -> Result<()> {
+ if data.len() > MAX_RECEIVE_DATA {
+ // This error code is unique, no context required here.
+ return Err(anyhow!(Error::Rc(ResponseCode::TOO_MUCH_DATA)));
+ }
+ Ok(())
+ }
+
+ // Update the last usage to now.
+ fn touch(&self) {
+ // Expect safety:
+ // `last_usage` is locked only for primitive single line statements.
+ // There is no chance to panic and poison the mutex.
+ *self.last_usage.lock().expect("In touch.") = Instant::now();
+ }
+
+ /// Implementation of `IKeystoreOperation::updateAad`.
+ /// Refer to the AIDL spec at system/hardware/interfaces/keystore2 for details.
+ fn update_aad(&self, aad_input: &[u8]) -> Result<()> {
+ let mut outcome = self.check_active().context("In update_aad")?;
+ Self::check_input_length(aad_input).context("In update_aad")?;
+ self.touch();
+
+ let params =
+ [KmParam { tag: Tag::ASSOCIATED_DATA, blob: aad_input.into(), ..Default::default() }];
+
+ let mut out_params: Vec<KmParam> = Vec::new();
+ let mut output: Vec<u8> = Vec::new();
+
+ let km_op: Box<dyn IKeyMintOperation> =
+ self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
+
+ self.update_outcome(
+ &mut *outcome,
+ map_km_error(km_op.update(
+ ¶ms,
+ &[],
+ // TODO HardwareAuthtoken missing
+ &Default::default(),
+ &mut out_params,
+ &mut output,
+ )),
+ )
+ .context("In update_aad: KeyMint::update failed.")?;
+
+ Ok(())
+ }
+
+ /// Implementation of `IKeystoreOperation::update`.
+ /// Refer to the AIDL spec at system/hardware/interfaces/keystore2 for details.
+ fn update(&self, input: &[u8]) -> Result<Option<Vec<u8>>> {
+ let mut outcome = self.check_active().context("In update")?;
+ Self::check_input_length(input).context("In update")?;
+ self.touch();
+
+ let mut out_params: Vec<KmParam> = Vec::new();
+ let mut output: Vec<u8> = Vec::new();
+
+ let km_op: Box<dyn IKeyMintOperation> =
+ self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
+
+ self.update_outcome(
+ &mut *outcome,
+ map_km_error(km_op.update(
+ &[],
+ input,
+ // TODO HardwareAuthtoken missing
+ &Default::default(),
+ &mut out_params,
+ &mut output,
+ )),
+ )
+ .context("In update: KeyMint::update failed.")?;
+
+ if output.is_empty() {
+ Ok(None)
+ } else {
+ Ok(Some(output))
+ }
+ }
+
+ /// Implementation of `IKeystoreOperation::finish`.
+ /// Refer to the AIDL spec at system/hardware/interfaces/keystore2 for details.
+ fn finish(&self, input: Option<&[u8]>, signature: Option<&[u8]>) -> Result<Option<Vec<u8>>> {
+ let mut outcome = self.check_active().context("In finish")?;
+ if let Some(input) = input {
+ Self::check_input_length(input).context("In finish")?;
+ }
+ self.touch();
+ let input = input.unwrap_or(EMPTY_BLOB);
+ let signature = signature.unwrap_or(EMPTY_BLOB);
+
+ let mut out_params: Vec<KmParam> = Vec::new();
+ let mut output: Vec<u8> = Vec::new();
+
+ let km_op: Box<dyn IKeyMintOperation> =
+ self.km_op.get_interface().context("In finish: Failed to get KeyMintOperation.")?;
+
+ self.update_outcome(
+ &mut *outcome,
+ map_km_error(km_op.finish(
+ &[],
+ &input,
+ &signature,
+ &Default::default(),
+ &Default::default(),
+ &mut out_params,
+ &mut output,
+ )),
+ )
+ .context("In finish: KeyMint::finish failed.")?;
+
+ // At this point the operation concluded successfully.
+ *outcome = Outcome::Success;
+
+ if output.is_empty() {
+ Ok(None)
+ } else {
+ Ok(Some(output))
+ }
+ }
+
+ /// Aborts the operation if it is active. IFF the operation is aborted the outcome is
+ /// set to `outcome`. `outcome` must reflect the reason for the abort. Since the operation
+ /// gets aborted `outcome` must not be `Operation::Success` or `Operation::Unknown`.
+ fn abort(&self, outcome: Outcome) -> Result<()> {
+ let mut locked_outcome = self.check_active().context("In abort")?;
+ *locked_outcome = outcome;
+ let km_op: Box<dyn IKeyMintOperation> =
+ self.km_op.get_interface().context("In abort: Failed to get KeyMintOperation.")?;
+
+ map_km_error(km_op.abort()).context("In abort: KeyMint::abort failed.")
+ }
+}
+
+impl Drop for Operation {
+ fn drop(&mut self) {
+ if let Ok(Outcome::Unknown) = self.outcome.get_mut() {
+ // If the operation was still active we call abort, setting
+ // the outcome to `Outcome::Dropped`
+ if let Err(e) = self.abort(Outcome::Dropped) {
+ log::error!("While dropping Operation: abort failed:\n {:?}", e);
+ }
+ }
+ }
+}
+
+/// The OperationDb holds weak references to all ongoing operations.
+/// Its main purpose is to facilitate operation pruning.
+#[derive(Debug, Default)]
+pub struct OperationDb {
+ // TODO replace Vec with WeakTable when the weak_table crate becomes
+ // available.
+ operations: Mutex<Vec<Weak<Operation>>>,
+}
+
+impl OperationDb {
+ /// Creates a new OperationDb.
+ pub fn new() -> Self {
+ Self { operations: Mutex::new(Vec::new()) }
+ }
+
+ /// Creates a new operation.
+ /// This function takes a KeyMint operation and an associated
+ /// owner uid and returns a new Operation wrapped in a `std::sync::Arc`.
+ pub fn create_operation(
+ &self,
+ km_op: Box<dyn IKeyMintOperation>,
+ owner: u32,
+ ) -> Arc<Operation> {
+ // We use unwrap because we don't allow code that can panic while locked.
+ let mut operations = self.operations.lock().expect("In create_operation.");
+
+ let mut index: usize = 0;
+ // First we iterate through the operation slots to try and find an unused
+ // slot. If we don't find one, we append the new entry instead.
+ match (*operations).iter_mut().find(|s| {
+ index += 1;
+ s.upgrade().is_none()
+ }) {
+ Some(free_slot) => {
+ let new_op = Arc::new(Operation::new(index - 1, km_op, owner));
+ *free_slot = Arc::downgrade(&new_op);
+ new_op
+ }
+ None => {
+ let new_op = Arc::new(Operation::new(operations.len(), km_op, owner));
+ operations.push(Arc::downgrade(&new_op));
+ new_op
+ }
+ }
+ }
+
+ fn get(&self, index: usize) -> Option<Arc<Operation>> {
+ self.operations.lock().expect("In OperationDb::get.").get(index).and_then(|op| op.upgrade())
+ }
+
+ /// Attempts to prune an operation.
+ ///
+ /// This function is used during operation creation, i.e., by
+ /// `KeystoreSecurityLevel::create_operation`, to try and free up an operation slot
+ /// if it got `ErrorCode::TOO_MANY_OPERATIONS` from the KeyMint backend. It is not
+ /// guaranteed that an operation slot is available after this call successfully
+ /// returned for various reasons. E.g., another thread may have snatched up the newly
+ /// available slot. Callers may have to call prune multiple times before they get a
+ /// free operation slot. Prune may also return `Err(Error::Rc(ResponseCode::BACKEND_BUSY))`
+ /// which indicates that no prunable operation was found.
+ ///
+ /// To find a suitable candidate we compute the malus for the caller and each existing
+ /// operation. The malus is the inverse of the pruning power (caller) or pruning
+ /// resistance (existing operation).
+ /// The malus is based on the number of sibling operations and age. Sibling
+ /// operations are operations that have the same owner (UID).
+ /// Every operation, existing or new, starts with a malus of 1. Every sibling
+ /// increases the malus by one. The age is the time since an operation was last touched.
+ /// It increases the malus by log6(<age in seconds> + 1) rounded down to the next
+ /// integer. So the malus increases stepwise after 5s, 35s, 215s, ...
+ /// Of two operations with the same malus the least recently used one is considered
+ /// weaker.
+ /// For the caller to be able to prune an operation it must find an operation
+ /// with a malus higher than its own.
+ ///
+ /// The malus can be expressed as
+ /// ```
+ /// malus = 1 + no_of_siblings + floor(log6(age_in_seconds + 1))
+ /// ```
+ /// where the constant `1` accounts for the operation under consideration.
+ /// In reality we compute it as
+ /// ```
+ /// caller_malus = 1 + running_siblings
+ /// ```
+ /// because the new operation has no age and is not included in the `running_siblings`,
+ /// and
+ /// ```
+ /// running_malus = running_siblings + floor(log6(age_in_seconds + 1))
+ /// ```
+ /// because a running operation is included in the `running_siblings` and it has
+ /// an age.
+ ///
+ /// ## Example
+ /// A caller with no running operations has a malus of 1. Young (age < 5s) operations
+ /// also with no siblings have a malus of one and cannot be pruned by the caller.
+ /// We have to find an operation that has at least one sibling or is older than 5s.
+ ///
+ /// A caller with one running operation has a malus of 2. Now even young siblings
+ /// or single child aging (5s <= age < 35s) operations are off limit. An aging
+ /// sibling of two, however, would have a malus of 3 and would be fair game.
+ ///
+ /// ## Rationale
+ /// Due to the limitation of KeyMint operation slots, we cannot get around pruning or
+ /// a single app could easily DoS KeyMint.
+ /// Keystore 1.0 used to always prune the least recently used operation. This at least
+ /// guaranteed that new operations can always be started. With the increased usage
+ /// of Keystore we saw increased pruning activity which can lead to a livelock
+ /// situation in the worst case.
+ /// With the new pruning strategy we want to provide well behaved clients with
+ /// progress assurances while punishing DoS attempts. As a result of this
+ /// strategy we can be in the situation where no operation can be pruned and the
+ /// creation of a new operation fails. This allows single child operations which
+ /// are frequently updated to complete, thereby breaking up livelock situations
+ /// and facilitating system wide progress.
+ pub fn prune(&self, caller: u32) -> Result<(), Error> {
+ loop {
+ // Maps the uid of the owner to the number of operations that owner has
+ // (running_siblings). More operations per owner lowers the pruning
+ // resistance of the operations of that owner. Whereas the number of
+ // ongoing operations of the caller lowers the pruning power of the caller.
+ let mut owners: HashMap<u32, u64> = HashMap::new();
+ let mut pruning_info: Vec<PruningInfo> = Vec::new();
+
+ let now = Instant::now();
+ self.operations
+ .lock()
+ .expect("In OperationDb::prune: Trying to lock self.operations.")
+ .iter()
+ .for_each(|op| {
+ if let Some(op) = op.upgrade() {
+ let p_info = op.get_pruning_info();
+ let owner = p_info.owner;
+ pruning_info.push(p_info);
+ // Count operations per owner.
+ *owners.entry(owner).or_insert(0) += 1;
+ }
+ });
+
+ let caller_malus = 1u64 + *owners.entry(caller).or_default();
+
+ // We iterate through all operations computing the malus and finding
+ // the candidate with the highest malus which must also be higher
+ // than the caller_malus.
+ struct CandidateInfo {
+ index: usize,
+ malus: u64,
+ last_usage: Instant,
+ age: Duration,
+ }
+ let candidate = pruning_info.iter().fold(
+ None,
+ |acc: Option<CandidateInfo>, &PruningInfo { last_usage, owner, index }| {
+ // Compute the age of the current operation.
+ let age = now
+ .checked_duration_since(last_usage)
+ .unwrap_or_else(|| Duration::new(0, 0));
+
+ // Compute the malus of the current operation.
+ // Expect safety: Every owner in pruning_info was counted in
+ // the owners map. So this unwrap cannot panic.
+ let malus = *owners
+ .get(&owner)
+ .expect("This is odd. We should have counted every owner in pruning_info.")
+ + ((age.as_secs() + 1) as f64).log(6.0).floor() as u64;
+
+ // Now check if the current operation is a viable/better candidate
+ // the one currently stored in the accumulator.
+ match acc {
+ // First we have to find any operation that is prunable by the caller.
+ None => {
+ if caller_malus < malus {
+ Some(CandidateInfo { index, malus, last_usage, age })
+ } else {
+ None
+ }
+ }
+ // If we have found one we look for the operation with the worst score.
+ // If there is a tie, the older operation is considered weaker.
+ Some(CandidateInfo { index: i, malus: m, last_usage: l, age: a }) => {
+ if malus > m || (malus == m && age > a) {
+ Some(CandidateInfo { index, malus, last_usage, age })
+ } else {
+ Some(CandidateInfo { index: i, malus: m, last_usage: l, age: a })
+ }
+ }
+ }
+ },
+ );
+
+ match candidate {
+ Some(CandidateInfo { index, malus: _, last_usage, age: _ }) => {
+ match self.get(index) {
+ Some(op) => {
+ match op.prune(last_usage) {
+ // We successfully freed up a slot.
+ Ok(()) => break Ok(()),
+ // This means the operation we tried to prune was on its way
+ // out. It also means that the slot it had occupied was freed up.
+ Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)) => break Ok(()),
+ // This means the operation we tried to prune was currently
+ // servicing a request. There are two options.
+ // * Assume that it was touched, which means that its
+ // pruning resistance increased. In that case we have
+ // to start over and find another candidate.
+ // * Assume that the operation is transitioning to end-of-life.
+ // which means that we got a free slot for free.
+ // If we assume the first but the second is true, we prune
+ // a good operation without need (aggressive approach).
+ // If we assume the second but the first is true, our
+ // caller will attempt to create a new KeyMint operation,
+ // fail with `ErrorCode::TOO_MANY_OPERATIONS`, and call
+ // us again (conservative approach).
+ Err(Error::Rc(ResponseCode::OPERATION_BUSY)) => {
+ // We choose the conservative approach, because
+ // every needlessly pruned operation can impact
+ // the user experience.
+ // To switch to the aggressive approach replace
+ // the following line with `continue`.
+ break Ok(());
+ }
+
+ // The candidate may have been touched so the score
+ // has changed since our evaluation.
+ _ => continue,
+ }
+ }
+ // This index does not exist any more. The operation
+ // in this slot was dropped. Good news, a slot
+ // has freed up.
+ None => break Ok(()),
+ }
+ }
+ // We did not get a pruning candidate.
+ None => break Err(Error::Rc(ResponseCode::BACKEND_BUSY)),
+ }
+ }
+ }
+}
+
+/// Implementation of IKeystoreOperation.
+pub struct KeystoreOperation {
+ operation: Mutex<Option<Arc<Operation>>>,
+}
+
+impl KeystoreOperation {
+ /// Creates a new operation instance wrapped in a
+ /// BnKeystoreOperation proxy object. It also
+ /// calls `IBinder::set_requesting_sid` on the new interface, because
+ /// we need it for checking Keystore permissions.
+ pub fn new_native_binder(operation: Arc<Operation>) -> impl IKeystoreOperation + Send {
+ let result =
+ BnKeystoreOperation::new_binder(Self { operation: Mutex::new(Some(operation)) });
+ result.as_binder().set_requesting_sid(true);
+ result
+ }
+
+ /// Grabs the outer operation mutex and calls `f` on the locked operation.
+ /// The function also deletes the operation if it returns with an error or if
+ /// `delete_op` is true.
+ fn with_locked_operation<T, F>(&self, f: F, delete_op: bool) -> Result<T>
+ where
+ for<'a> F: FnOnce(&'a Operation) -> Result<T>,
+ {
+ let mut delete_op: bool = delete_op;
+ match self.operation.try_lock() {
+ Ok(mut mutex_guard) => {
+ let result = match &*mutex_guard {
+ Some(op) => {
+ let result = f(&*op);
+ // Any error here means we can discard the operation.
+ if result.is_err() {
+ delete_op = true;
+ }
+ result
+ }
+ None => Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE))
+ .context("In KeystoreOperation::with_locked_operation"),
+ };
+
+ if delete_op {
+ // We give up our reference to the Operation, thereby freeing up our
+ // internal resources and ending the wrapped KeyMint operation.
+ // This KeystoreOperation object will still be owned by an SpIBinder
+ // until the client drops its remote reference.
+ *mutex_guard = None;
+ }
+ result
+ }
+ Err(_) => Err(Error::Rc(ResponseCode::OPERATION_BUSY))
+ .context("In KeystoreOperation::with_locked_operation"),
+ }
+ }
+}
+
+impl binder::Interface for KeystoreOperation {}
+
+impl IKeystoreOperation for KeystoreOperation {
+ fn updateAad(&self, aad_input: &[u8]) -> binder::public_api::Result<()> {
+ map_or_log_err(
+ self.with_locked_operation(
+ |op| op.update_aad(aad_input).context("In KeystoreOperation::updateAad"),
+ false,
+ ),
+ Ok,
+ )
+ }
+
+ fn update(&self, input: &[u8]) -> binder::public_api::Result<Option<Vec<u8>>> {
+ map_or_log_err(
+ self.with_locked_operation(
+ |op| op.update(input).context("In KeystoreOperation::update"),
+ false,
+ ),
+ Ok,
+ )
+ }
+ fn finish(
+ &self,
+ input: Option<&[u8]>,
+ signature: Option<&[u8]>,
+ ) -> binder::public_api::Result<Option<Vec<u8>>> {
+ map_or_log_err(
+ self.with_locked_operation(
+ |op| op.finish(input, signature).context("In KeystoreOperation::finish"),
+ true,
+ ),
+ Ok,
+ )
+ }
+
+ fn abort(&self) -> binder::public_api::Result<()> {
+ map_or_log_err(
+ self.with_locked_operation(
+ |op| op.abort(Outcome::Abort).context("In KeystoreOperation::abort"),
+ true,
+ ),
+ Ok,
+ )
+ }
+}
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
new file mode 100644
index 0000000..287d626
--- /dev/null
+++ b/keystore2/src/security_level.rs
@@ -0,0 +1,553 @@
+// Copyright 2020, 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.
+
+#![allow(unused_variables)]
+
+//! This crate implements the IKeystoreSecurityLevel interface.
+
+use android_hardware_keymint::aidl::android::hardware::keymint::{
+ Algorithm::Algorithm, Certificate::Certificate as KmCertificate,
+ IKeyMintDevice::IKeyMintDevice, KeyCharacteristics::KeyCharacteristics, KeyFormat::KeyFormat,
+ KeyParameter::KeyParameter as KmParam, KeyPurpose::KeyPurpose, Tag::Tag,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ AuthenticatorSpec::AuthenticatorSpec, AuthenticatorType::AuthenticatorType,
+ Certificate::Certificate, CertificateChain::CertificateChain, Domain::Domain,
+ IKeystoreOperation::IKeystoreOperation, IKeystoreSecurityLevel::BnKeystoreSecurityLevel,
+ IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+ KeyParameter::KeyParameter, OperationChallenge::OperationChallenge,
+ SecurityLevel::SecurityLevel,
+};
+
+use crate::error::{self, map_km_error, map_or_log_err, Error, ErrorCode};
+use crate::globals::DB;
+use crate::permission::KeyPerm;
+use crate::utils::{check_key_permission, keyparam_ks_to_km, Asp};
+use crate::{
+ database::{KeyEntry, KeyEntryLoadBits, SubComponentType},
+ operation::KeystoreOperation,
+ operation::OperationDb,
+};
+use anyhow::{anyhow, Context, Result};
+use binder::{IBinder, Interface, ThreadState};
+
+/// Implementation of the IKeystoreSecurityLevel Interface.
+pub struct KeystoreSecurityLevel {
+ security_level: SecurityLevel,
+ keymint: Asp,
+ operation_db: OperationDb,
+}
+
+static KEYMINT_SERVICE_NAME: &str = "android.hardware.keymint.IKeyMintDevice";
+
+// Blob of 32 zeroes used as empty masking key.
+static ZERO_BLOB_32: &[u8] = &[0; 32];
+
+impl KeystoreSecurityLevel {
+ /// Creates a new security level instance wrapped in a
+ /// BnKeystoreSecurityLevel proxy object. It also
+ /// calls `IBinder::set_requesting_sid` on the new interface, because
+ /// we need it for checking keystore permissions.
+ pub fn new_native_binder(
+ security_level: SecurityLevel,
+ ) -> Result<impl IKeystoreSecurityLevel + Send> {
+ let service_name = format!("{}/default", KEYMINT_SERVICE_NAME);
+ let keymint: Box<dyn IKeyMintDevice> =
+ binder::get_interface(&service_name).map_err(|e| {
+ anyhow!(format!(
+ "Could not get KeyMint instance: {} failed with error code {:?}",
+ service_name, e
+ ))
+ })?;
+
+ let result = BnKeystoreSecurityLevel::new_binder(Self {
+ security_level,
+ keymint: Asp::new(keymint.as_binder()),
+ operation_db: OperationDb::new(),
+ });
+ result.as_binder().set_requesting_sid(true);
+ Ok(result)
+ }
+
+ fn store_new_key(
+ &self,
+ key: KeyDescriptor,
+ km_cert_chain: Option<Vec<KmCertificate>>,
+ blob: Vec<u8>,
+ ) -> Result<(KeyDescriptor, Option<Certificate>, Option<CertificateChain>)> {
+ let (cert, cert_chain) = match km_cert_chain {
+ Some(mut chain) => (
+ match chain.len() {
+ 0 => None,
+ _ => Some(Certificate { data: chain.remove(0).encodedCertificate }),
+ },
+ match chain.len() {
+ 0 => None,
+ _ => Some(CertificateChain {
+ data: chain
+ .iter()
+ .map(|c| c.encodedCertificate.iter())
+ .flatten()
+ .copied()
+ .collect(),
+ }),
+ },
+ ),
+ None => (None, None),
+ };
+
+ let key = match key.domain {
+ Domain::BLOB => {
+ KeyDescriptor { domain: Domain::BLOB, blob: Some(blob), ..Default::default() }
+ }
+ _ => DB
+ .with(|db| {
+ let mut db = db.borrow_mut();
+ let key_id = db
+ .create_key_entry(key.domain, key.nspace)
+ .context("Trying to create a key entry.")?;
+ db.insert_blob(key_id, SubComponentType::KM_BLOB, &blob, self.security_level)
+ .context("Trying to insert km blob.")?;
+ if let Some(c) = &cert {
+ db.insert_blob(
+ key_id,
+ SubComponentType::CERT,
+ &c.data,
+ self.security_level,
+ )
+ .context("Trying to insert cert blob.")?;
+ }
+ if let Some(c) = &cert_chain {
+ db.insert_blob(
+ key_id,
+ SubComponentType::CERT_CHAIN,
+ &c.data,
+ self.security_level,
+ )
+ .context("Trying to insert cert chain blob.")?;
+ }
+ match &key.alias {
+ Some(alias) => db
+ .rebind_alias(key_id, alias, key.domain, key.nspace)
+ .context("Failed to rebind alias.")?,
+ None => {
+ return Err(error::Error::sys()).context(
+ "Alias must be specified. (This should have been checked earlier.)",
+ )
+ }
+ }
+ Ok(KeyDescriptor {
+ domain: Domain::KEY_ID,
+ nspace: key_id,
+ ..Default::default()
+ })
+ })
+ .context("In store_new_key.")?,
+ };
+
+ Ok((key, cert, cert_chain))
+ }
+
+ fn create(
+ &self,
+ key: &KeyDescriptor,
+ operation_parameters: &[KeyParameter],
+ forced: bool,
+ ) -> Result<(Box<dyn IKeystoreOperation>, Option<OperationChallenge>)> {
+ let caller_uid = ThreadState::get_calling_uid();
+ // We use `scoping_blob` to extend the life cycle of the blob loaded from the database,
+ // so that we can use it by reference like the blob provided by the key descriptor.
+ // Otherwise, we would have to clone the blob from the key descriptor.
+ let scoping_blob: Vec<u8>;
+ let (km_blob, key_id) =
+ match key.domain {
+ Domain::BLOB => {
+ check_key_permission(KeyPerm::use_(), key, &None)
+ .context("In create: checking use permission for Domain::BLOB.")?;
+ (
+ match &key.blob {
+ Some(blob) => blob,
+ None => return Err(Error::sys()).context(
+ "In create: Key blob must be specified when using Domain::BLOB.",
+ ),
+ },
+ None,
+ )
+ }
+ _ => {
+ let mut key_entry = DB
+ .with::<_, Result<KeyEntry>>(|db| {
+ db.borrow_mut().load_key_entry(
+ key.clone(),
+ KeyEntryLoadBits::KM,
+ caller_uid,
+ |k, av| check_key_permission(KeyPerm::use_(), k, &av),
+ )
+ })
+ .context("In create: Failed to load key blob.")?;
+ scoping_blob = match key_entry.take_km_blob() {
+ Some(blob) => blob,
+ None => return Err(Error::sys()).context(
+ "In create: Successfully loaded key entry, but KM blob was missing.",
+ ),
+ };
+ (&scoping_blob, Some(key_entry.id()))
+ }
+ };
+
+ // TODO Authorize begin operation.
+ // Check if we need an authorization token.
+ // Lookup authorization token and request VerificationToken if required.
+
+ let purpose = operation_parameters.iter().find(|p| p.tag == Tag::PURPOSE.0).map_or(
+ Err(Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("In create: No operation purpose specified."),
+ |kp| Ok(KeyPurpose(kp.integer)),
+ )?;
+
+ let km_params =
+ operation_parameters.iter().map(|p| keyparam_ks_to_km(p)).collect::<Vec<KmParam>>();
+
+ let km_dev: Box<dyn IKeyMintDevice> =
+ self.keymint.get_interface().context("In create: Failed to get KeyMint device")?;
+
+ let (begin_result, upgraded_blob) = loop {
+ match map_km_error(km_dev.begin(purpose, &km_blob, &km_params, &Default::default())) {
+ Ok(result) => break (result, None),
+ Err(Error::Km(ErrorCode::TOO_MANY_OPERATIONS)) => {
+ self.operation_db.prune(caller_uid).context("In create: Outer loop.")?;
+ continue;
+ }
+ Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
+ let upgraded_blob = map_km_error(km_dev.upgradeKey(&km_blob, &km_params))
+ .context("In create: Upgrade failed.")?;
+ break loop {
+ match map_km_error(km_dev.begin(
+ purpose,
+ &upgraded_blob,
+ &km_params,
+ &Default::default(),
+ )) {
+ Ok(result) => break (result, Some(upgraded_blob)),
+ // If Keystore 2.0 is multi threaded another request may have
+ // snatched up our previously pruned operation slot. So we might
+ // need to prune again.
+ Err(Error::Km(ErrorCode::TOO_MANY_OPERATIONS)) => {
+ self.operation_db
+ .prune(caller_uid)
+ .context("In create: Inner loop.")?;
+ continue;
+ }
+ Err(e) => {
+ return Err(e)
+ .context("In create: Begin operation failed after upgrade.")
+ }
+ }
+ };
+ }
+ Err(e) => return Err(e).context("In create: Begin operation failed."),
+ };
+ };
+
+ if let Some(upgraded_blob) = upgraded_blob {
+ if let Some(key_id) = key_id {
+ DB.with(|db| {
+ db.borrow_mut().insert_blob(
+ key_id,
+ SubComponentType::KM_BLOB,
+ &upgraded_blob,
+ self.security_level,
+ )
+ })
+ .context("In create: Failed to insert upgraded blob into the database.")?;
+ }
+ }
+
+ let operation = match begin_result.operation {
+ Some(km_op) => self.operation_db.create_operation(km_op, caller_uid),
+ None => return Err(Error::sys()).context("In create: Begin operation returned successfully, but did not return a valid operation."),
+ };
+
+ let op_binder: Box<dyn IKeystoreOperation> =
+ KeystoreOperation::new_native_binder(operation)
+ .as_binder()
+ .into_interface()
+ .context("In create: Failed to create IKeystoreOperation.")?;
+
+ // TODO find out what to do with the returned parameters.
+
+ // TODO we need to the enforcement module to determine if we need to return the challenge.
+ // We return None for now because we don't support auth bound keys yet.
+ Ok((op_binder, None))
+ }
+
+ fn generate_key(
+ &self,
+ key: &KeyDescriptor,
+ params: &[KeyParameter],
+ entropy: &[u8],
+ ) -> Result<(KeyDescriptor, Option<Certificate>, Option<CertificateChain>)> {
+ if key.domain != Domain::BLOB && key.alias.is_none() {
+ return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("In generate_key: Alias must be specified");
+ }
+
+ let key = match key.domain {
+ Domain::APP => KeyDescriptor {
+ domain: key.domain,
+ nspace: ThreadState::get_calling_uid() as i64,
+ alias: key.alias.clone(),
+ blob: None,
+ },
+ _ => key.clone(),
+ };
+
+ // generate_key requires the rebind permission.
+ check_key_permission(KeyPerm::rebind(), &key, &None).context("In generate_key.")?;
+
+ let km_dev: Box<dyn IKeyMintDevice> = self.keymint.get_interface()?;
+ map_km_error(km_dev.addRngEntropy(entropy))?;
+ let mut blob: Vec<u8> = Default::default();
+ let mut key_characteristics: KeyCharacteristics = Default::default();
+ let mut certificate_chain: Vec<KmCertificate> = Default::default();
+ map_km_error(km_dev.generateKey(
+ ¶ms.iter().map(|p| keyparam_ks_to_km(p)).collect::<Vec<KmParam>>(),
+ &mut blob,
+ &mut key_characteristics,
+ &mut certificate_chain,
+ ))?;
+
+ self.store_new_key(key, Some(certificate_chain), blob).context("In generate_key.")
+ }
+
+ fn import_key(
+ &self,
+ key: &KeyDescriptor,
+ params: &[KeyParameter],
+ key_data: &[u8],
+ ) -> Result<(KeyDescriptor, Option<Certificate>, Option<CertificateChain>)> {
+ if key.domain != Domain::BLOB && key.alias.is_none() {
+ return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("In import_key: Alias must be specified");
+ }
+
+ let key = match key.domain {
+ Domain::APP => KeyDescriptor {
+ domain: key.domain,
+ nspace: ThreadState::get_calling_uid() as i64,
+ alias: key.alias.clone(),
+ blob: None,
+ },
+ _ => key.clone(),
+ };
+
+ // import_key requires the rebind permission.
+ check_key_permission(KeyPerm::rebind(), &key, &None).context("In import_key.")?;
+
+ let mut blob: Vec<u8> = Default::default();
+ let mut key_characteristics: KeyCharacteristics = Default::default();
+ let mut certificate_chain: Vec<KmCertificate> = Default::default();
+
+ let format = params
+ .iter()
+ .find(|p| p.tag == Tag::ALGORITHM.0)
+ .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("No KeyParameter 'Algorithm'.")
+ .and_then(|p| match Algorithm(p.integer) {
+ Algorithm::AES | Algorithm::HMAC | Algorithm::TRIPLE_DES => Ok(KeyFormat::RAW),
+ Algorithm::RSA | Algorithm::EC => Ok(KeyFormat::PKCS8),
+ algorithm => Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context(format!("Unknown Algorithm {:?}.", algorithm)),
+ })
+ .context("In import_key.")?;
+
+ let km_dev: Box<dyn IKeyMintDevice> = self.keymint.get_interface()?;
+ map_km_error(km_dev.importKey(
+ ¶ms.iter().map(|p| keyparam_ks_to_km(p)).collect::<Vec<KmParam>>(),
+ format,
+ key_data,
+ &mut blob,
+ &mut key_characteristics,
+ &mut certificate_chain,
+ ))?;
+
+ self.store_new_key(key, Some(certificate_chain), blob).context("In import_key.")
+ }
+
+ fn import_wrapped_key(
+ &self,
+ key: &KeyDescriptor,
+ wrapping_key: &KeyDescriptor,
+ masking_key: Option<&[u8]>,
+ params: &[KeyParameter],
+ authenticators: &[AuthenticatorSpec],
+ ) -> Result<(KeyDescriptor, Option<Certificate>, Option<CertificateChain>)> {
+ if key.domain != Domain::BLOB && key.alias.is_none() {
+ return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("In import_wrapped_key: Alias must be specified.");
+ }
+
+ let wrapped_data = match &key.blob {
+ Some(d) => d,
+ None => {
+ return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context(
+ "In import_wrapped_key: Blob must be specified and hold wrapped key data.",
+ )
+ }
+ };
+
+ let key = match key.domain {
+ Domain::APP => KeyDescriptor {
+ domain: key.domain,
+ nspace: ThreadState::get_calling_uid() as i64,
+ alias: key.alias.clone(),
+ blob: None,
+ },
+ _ => key.clone(),
+ };
+
+ // import_wrapped_key requires the rebind permission for the new key.
+ check_key_permission(KeyPerm::rebind(), &key, &None).context("In import_wrapped_key.")?;
+
+ let wrapping_key_entry = DB
+ .with(|db| {
+ db.borrow_mut().load_key_entry(
+ wrapping_key.clone(),
+ KeyEntryLoadBits::KM,
+ ThreadState::get_calling_uid(),
+ |k, av| check_key_permission(KeyPerm::use_(), k, &av),
+ )
+ })
+ .context("Failed to load wrapping key.")?;
+ let wrapping_key_blob = match wrapping_key_entry.km_blob() {
+ Some(blob) => blob,
+ None => {
+ return Err(error::Error::sys()).context(concat!(
+ "No km_blob after successfully loading key.",
+ " This should never happen."
+ ))
+ }
+ };
+
+ let mut blob: Vec<u8> = Default::default();
+ let mut key_characteristics: KeyCharacteristics = Default::default();
+ // km_dev.importWrappedKey does not return a certificate chain.
+ // TODO Do we assume that all wrapped keys are symmetric?
+ // let certificate_chain: Vec<KmCertificate> = Default::default();
+
+ let pw_sid = authenticators
+ .iter()
+ .find_map(|a| match a.authenticatorType {
+ AuthenticatorType::PASSWORD => Some(a.authenticatorId),
+ _ => None,
+ })
+ .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("A password authenticator SID must be specified.")?;
+
+ let fp_sid = authenticators
+ .iter()
+ .find_map(|a| match a.authenticatorType {
+ AuthenticatorType::FINGERPRINT => Some(a.authenticatorId),
+ _ => None,
+ })
+ .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("A fingerprint authenticator SID must be specified.")?;
+
+ let masking_key = masking_key.unwrap_or(ZERO_BLOB_32);
+
+ let km_dev: Box<dyn IKeyMintDevice> = self.keymint.get_interface()?;
+ map_km_error(km_dev.importWrappedKey(
+ wrapped_data,
+ wrapping_key_blob,
+ masking_key,
+ ¶ms.iter().map(|p| keyparam_ks_to_km(p)).collect::<Vec<KmParam>>(),
+ pw_sid,
+ fp_sid,
+ &mut blob,
+ &mut key_characteristics,
+ ))?;
+
+ self.store_new_key(key, None, blob).context("In import_wrapped_key.")
+ }
+}
+
+impl binder::Interface for KeystoreSecurityLevel {}
+
+impl IKeystoreSecurityLevel for KeystoreSecurityLevel {
+ fn create(
+ &self,
+ key: &KeyDescriptor,
+ operation_parameters: &[KeyParameter],
+ forced: bool,
+ challenge: &mut Option<OperationChallenge>,
+ ) -> binder::public_api::Result<Box<dyn IKeystoreOperation>> {
+ map_or_log_err(self.create(key, operation_parameters, forced), |v| {
+ *challenge = v.1;
+ Ok(v.0)
+ })
+ }
+ fn generateKey(
+ &self,
+ key: &KeyDescriptor,
+ params: &[KeyParameter],
+ entropy: &[u8],
+ result_key: &mut KeyDescriptor,
+ public_cert: &mut Option<Certificate>,
+ certificate_chain: &mut Option<CertificateChain>,
+ ) -> binder::public_api::Result<()> {
+ map_or_log_err(self.generate_key(key, params, entropy), |v| {
+ *result_key = v.0;
+ *public_cert = v.1;
+ *certificate_chain = v.2;
+ Ok(())
+ })
+ }
+ fn importKey(
+ &self,
+ key: &KeyDescriptor,
+ params: &[KeyParameter],
+ key_data: &[u8],
+ result_key: &mut KeyDescriptor,
+ public_cert: &mut Option<Certificate>,
+ certificate_chain: &mut Option<CertificateChain>,
+ ) -> binder::public_api::Result<()> {
+ map_or_log_err(self.import_key(key, params, key_data), |v| {
+ *result_key = v.0;
+ *public_cert = v.1;
+ *certificate_chain = v.2;
+ Ok(())
+ })
+ }
+ fn importWrappedKey(
+ &self,
+ key: &KeyDescriptor,
+ wrapping_key: &KeyDescriptor,
+ masking_key: Option<&[u8]>,
+ params: &[KeyParameter],
+ authenticators: &[AuthenticatorSpec],
+ result_key: &mut KeyDescriptor,
+ public_cert: &mut Option<Certificate>,
+ certificate_chain: &mut Option<CertificateChain>,
+ ) -> binder::public_api::Result<()> {
+ map_or_log_err(
+ self.import_wrapped_key(key, wrapping_key, masking_key, params, authenticators),
+ |v| {
+ *result_key = v.0;
+ *public_cert = v.1;
+ *certificate_chain = v.2;
+ Ok(())
+ },
+ )
+ }
+}
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
new file mode 100644
index 0000000..ea17766
--- /dev/null
+++ b/keystore2/src/service.rs
@@ -0,0 +1,254 @@
+// Copyright 2020, 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.
+
+// TODO remove when fully implemented.
+#![allow(unused_variables)]
+
+//! This crate implement the core Keystore 2.0 service API as defined by the Keystore 2.0
+//! AIDL spec.
+
+use crate::database::{KeyEntry, KeyEntryLoadBits, SubComponentType};
+use crate::error::{self, map_or_log_err, ErrorCode};
+use crate::globals::DB;
+use crate::permission;
+use crate::permission::KeyPerm;
+use crate::security_level::KeystoreSecurityLevel;
+use crate::utils::{check_grant_permission, check_key_permission, Asp};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Certificate::Certificate, CertificateChain::CertificateChain, Domain::Domain,
+ IKeystoreSecurityLevel::IKeystoreSecurityLevel, IKeystoreService::BnKeystoreService,
+ IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor, KeyMetadata::KeyMetadata,
+ SecurityLevel::SecurityLevel,
+};
+use anyhow::{anyhow, Context, Result};
+use binder::{IBinder, Interface, ThreadState};
+
+/// Implementation of the IKeystoreService.
+pub struct KeystoreService {
+ sec_level: Asp,
+}
+
+impl KeystoreService {
+ /// Create a new instance of the Keystore 2.0 service.
+ pub fn new_native_binder() -> Result<impl IKeystoreService> {
+ let result = BnKeystoreService::new_binder(Self {
+ sec_level: Asp::new({
+ let sec_level =
+ KeystoreSecurityLevel::new_native_binder(SecurityLevel::TRUSTED_ENVIRONMENT)
+ .context("While trying to create IKeystoreSecurityLevel")?;
+ sec_level.as_binder()
+ }),
+ });
+ result.as_binder().set_requesting_sid(true);
+ Ok(result)
+ }
+
+ fn get_security_level(
+ &self,
+ security_level: SecurityLevel,
+ ) -> Result<Box<dyn IKeystoreSecurityLevel>> {
+ match security_level {
+ SecurityLevel::TRUSTED_ENVIRONMENT => self
+ .sec_level
+ .get_interface()
+ .context("In get_security_level: Failed to get IKeystoreSecurityLevel."),
+ _ => Err(anyhow!(error::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))),
+ }
+ }
+
+ fn get_key_entry(
+ &self,
+ key: &KeyDescriptor,
+ ) -> Result<(
+ KeyMetadata,
+ Option<Certificate>,
+ Option<CertificateChain>,
+ Box<dyn IKeystoreSecurityLevel>,
+ )> {
+ let mut key_entry: KeyEntry = DB
+ .with(|db| {
+ db.borrow_mut().load_key_entry(
+ key.clone(),
+ KeyEntryLoadBits::PUBLIC,
+ ThreadState::get_calling_uid(),
+ |k, av| check_key_permission(KeyPerm::get_info(), k, &av),
+ )
+ })
+ .context("In get_key_entry, while trying to load key info.")?;
+
+ let i_sec_level = match key_entry.sec_level() {
+ SecurityLevel::TRUSTED_ENVIRONMENT => self
+ .sec_level
+ .get_interface()
+ .context("In get_key_entry: Failed to get IKeystoreSecurityLevel.")?,
+ _ => return Err(anyhow!(error::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))),
+ };
+
+ Ok((
+ KeyMetadata {
+ key: KeyDescriptor {
+ domain: Domain::KEY_ID,
+ nspace: key_entry.id(),
+ ..Default::default()
+ },
+ securityLevel: key_entry.sec_level(),
+ // TODO add key characteristics here.
+ ..Default::default()
+ },
+ key_entry.take_cert().map(|v| Certificate { data: v }),
+ key_entry.take_cert_chain().map(|v| CertificateChain { data: v }),
+ i_sec_level,
+ ))
+ }
+
+ fn update_subcomponent(
+ &self,
+ key: &KeyDescriptor,
+ public_cert: Option<&Certificate>,
+ certificate_chain: Option<&CertificateChain>,
+ ) -> Result<()> {
+ DB.with::<_, Result<()>>(|db| {
+ let mut db = db.borrow_mut();
+ let key_entry = db
+ .load_key_entry(
+ key.clone(),
+ KeyEntryLoadBits::NONE,
+ ThreadState::get_calling_uid(),
+ |k, av| {
+ check_key_permission(KeyPerm::update(), k, &av)
+ .context("In update_subcomponent.")
+ },
+ )
+ .context("Failed to load key_entry.")?;
+
+ if let Some(cert) = public_cert {
+ db.insert_blob(
+ key_entry.id(),
+ SubComponentType::CERT,
+ &cert.data,
+ key_entry.sec_level(),
+ )
+ .context("Failed to update cert subcomponent.")?;
+ }
+
+ if let Some(cert_chain) = certificate_chain {
+ db.insert_blob(
+ key_entry.id(),
+ SubComponentType::CERT_CHAIN,
+ &cert_chain.data,
+ key_entry.sec_level(),
+ )
+ .context("Failed to update cert chain subcomponent.")?;
+ }
+ Ok(())
+ })
+ .context("In update_subcomponent.")
+ }
+
+ fn list_entries(&self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
+ // TODO implement.
+ Err(anyhow!(error::Error::sys()))
+ }
+
+ fn delete_key(&self, key: &KeyDescriptor) -> Result<()> {
+ // TODO implement.
+ Err(anyhow!(error::Error::sys()))
+ }
+
+ fn grant(
+ &self,
+ key: &KeyDescriptor,
+ grantee_uid: i32,
+ access_vector: permission::KeyPermSet,
+ ) -> Result<KeyDescriptor> {
+ DB.with(|db| {
+ db.borrow_mut().grant(
+ key.clone(),
+ ThreadState::get_calling_uid(),
+ grantee_uid as u32,
+ access_vector,
+ |k, av| check_grant_permission(*av, k).context("During grant."),
+ )
+ })
+ .context("In KeystoreService::grant.")
+ }
+
+ fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> Result<()> {
+ DB.with(|db| {
+ db.borrow_mut().ungrant(
+ key.clone(),
+ ThreadState::get_calling_uid(),
+ grantee_uid as u32,
+ |k| check_key_permission(KeyPerm::grant(), k, &None),
+ )
+ })
+ .context("In KeystoreService::ungrant.")
+ }
+}
+
+impl binder::Interface for KeystoreService {}
+
+// Implementation of IKeystoreService. See AIDL spec at
+// system/security/keystore2/binder/android/security/keystore2/IKeystoreService.aidl
+impl IKeystoreService for KeystoreService {
+ fn getSecurityLevel(
+ &self,
+ security_level: SecurityLevel,
+ ) -> binder::public_api::Result<Box<dyn IKeystoreSecurityLevel>> {
+ map_or_log_err(self.get_security_level(security_level), Ok)
+ }
+ fn getKeyEntry(
+ &self,
+ key: &KeyDescriptor,
+ metadata: &mut KeyMetadata,
+ public_cert: &mut Option<Certificate>,
+ certificate_chain: &mut Option<CertificateChain>,
+ ) -> binder::public_api::Result<Box<dyn IKeystoreSecurityLevel>> {
+ map_or_log_err(self.get_key_entry(key), |v| {
+ *metadata = v.0;
+ *public_cert = v.1;
+ *certificate_chain = v.2;
+ Ok(v.3)
+ })
+ }
+ fn updateSubcomponent(
+ &self,
+ key: &KeyDescriptor,
+ public_cert: Option<&Certificate>,
+ certificate_chain: Option<&CertificateChain>,
+ ) -> binder::public_api::Result<()> {
+ map_or_log_err(self.update_subcomponent(key, public_cert, certificate_chain), Ok)
+ }
+ fn listEntries(
+ &self,
+ domain: Domain,
+ namespace: i64,
+ ) -> binder::public_api::Result<Vec<KeyDescriptor>> {
+ map_or_log_err(self.list_entries(domain, namespace), Ok)
+ }
+ fn deleteKey(&self, key: &KeyDescriptor) -> binder::public_api::Result<()> {
+ map_or_log_err(self.delete_key(key), Ok)
+ }
+ fn grant(
+ &self,
+ key: &KeyDescriptor,
+ grantee_uid: i32,
+ access_vector: i32,
+ ) -> binder::public_api::Result<KeyDescriptor> {
+ map_or_log_err(self.grant(key, grantee_uid, access_vector.into()), Ok)
+ }
+ fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> binder::public_api::Result<()> {
+ map_or_log_err(self.ungrant(key, grantee_uid), Ok)
+ }
+}