Keystore 2.0: Revisit Enforcements.
This patch revisits the Keystore 2.0 enforcements module to support
KM4.1 hardware enforced device locked keys.
* Consolidate the background handler into async_task.
* The auth token handler became AuthInfo and was moved into
enforcements.rs.
* The auth token validity check moved from database.rs to
enforcements.rs.
Bug: 171503362
Test: Keystore CTS tests
Change-Id: If37d38183901b356242079af812c7a0e1e79abf3
diff --git a/keystore2/selinux/src/lib.rs b/keystore2/selinux/src/lib.rs
index 932c30e..2b5091d 100644
--- a/keystore2/selinux/src/lib.rs
+++ b/keystore2/selinux/src/lib.rs
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// #![allow(missing_docs)]
-
//! This crate provides some safe wrappers around the libselinux API. It is currently limited
//! to the API surface that Keystore 2.0 requires to perform permission checks against
//! the SEPolicy. Notably, it provides wrappers for:
diff --git a/keystore2/src/auth_token_handler.rs b/keystore2/src/auth_token_handler.rs
deleted file mode 100644
index bedec50..0000000
--- a/keystore2/src/auth_token_handler.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-// 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 module defines the AuthTokenHandler enum and its methods. AuthTokenHandler enum represents
-//! the different states an auth token and an associated timestamp token can be expressed during
-//! the operation life cycle.
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- HardwareAuthToken::HardwareAuthToken,
-};
-use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
- TimeStampToken::TimeStampToken,
-};
-
-use anyhow::{Context, Result};
-use std::sync::mpsc::Receiver;
-
-/// AuthTokenHandler enum has five different variants which are described by the comments above
-// each variant, as follows.
-#[derive(Debug)]
-pub enum AuthTokenHandler {
- /// Used when an operation does not require an auth token for authorization.
- NoAuthRequired,
- /// Used to represent the intermediate state between the time the operation is found to be
- /// requiring per-op auth and the time the auth token for the operation is found.
- OpAuthRequired,
- /// Used to represent the intermediate state between the time the operation is found to be
- /// using a time_out key with STRONGBOX keymint, and the time a verficiation token is requested
- /// from the worker thread which obtains timestamp tokens from the TEE KeyMint.
- TimestampRequired(HardwareAuthToken),
- /// Used to represent the intermediate state between the time a timestamp token is requested
- /// from the worker thread which obtains timestamp tokens from the TEE KeyMint and the time
- /// the timestamp token is received from the worker thread.
- Channel(Receiver<(HardwareAuthToken, TimeStampToken)>),
- /// Used to represent the final state for all operations requiring an auth token for
- /// authorization, after the matching auth token (and the associated timestamp token if
- /// required) is found.
- Token(HardwareAuthToken, Option<TimeStampToken>),
-}
-
-impl AuthTokenHandler {
- /// If Channel variant, block on it until the timestamp token is sent by the
- /// keystore2 worker thread which obtains timestamp tokens from TEE Keymint and converts the
- /// object from Channel variant to Token variant.
- /// Retrieve auth token and timestamp token from the Token variant of an AuthTokenHandler
- /// instance.
- pub fn retrieve_auth_and_timestamp_tokens(
- &mut self,
- ) -> Result<(Option<&HardwareAuthToken>, Option<&TimeStampToken>)> {
- // Converts to Token variant if Channel variant found, after retrieving the
- // TimeStampToken
- if let AuthTokenHandler::Channel(recv) = self {
- let (auth_token, timestamp_token) =
- recv.recv().context("In receive_timestamp_token: sender disconnected.")?;
- *self = AuthTokenHandler::Token(auth_token, Some(timestamp_token));
- }
- // get the tokens from the Token variant
- if let AuthTokenHandler::Token(auth_token, optional_time_stamp_token) = self {
- Ok((Some(auth_token), optional_time_stamp_token.as_ref()))
- } else {
- Ok((None, None))
- }
- }
-
- /// Retrieve auth token from TimestampRequired and Token variants of an
- /// AuthTokenHandler instance. This method is useful when we only expect an auth token and
- /// do not expect a timestamp token.
- pub fn get_auth_token(&self) -> Option<&HardwareAuthToken> {
- match self {
- AuthTokenHandler::TimestampRequired(auth_token) => Some(auth_token),
- AuthTokenHandler::Token(auth_token, _) => Some(auth_token),
- _ => None,
- }
- }
-}
diff --git a/keystore2/src/background_task_handler.rs b/keystore2/src/background_task_handler.rs
deleted file mode 100644
index b039506..0000000
--- a/keystore2/src/background_task_handler.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-// 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 module implements the handling of background tasks such as obtaining timestamp tokens from
-//! the timestamp service (or TEE KeyMint in legacy devices), via a separate thread.
-
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- HardwareAuthToken::HardwareAuthToken,
-};
-use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
- ISecureClock::ISecureClock, TimeStampToken::TimeStampToken,
-};
-use android_system_keystore2::aidl::android::system::keystore2::OperationChallenge::OperationChallenge;
-use anyhow::Result;
-use log::error;
-use std::sync::mpsc::{Receiver, Sender};
-use std::sync::Mutex;
-use std::thread::{spawn, JoinHandle};
-
-use crate::globals::get_timestamp_service;
-
-/// This is the struct encapsulating the thread which handles background tasks such as
-/// obtaining timestamp tokens.
-pub struct BackgroundTaskHandler {
- task_handler: Mutex<Option<JoinHandle<()>>>,
-}
-
-/// This enum defines the two variants of a message that can be passed down to the
-/// BackgroundTaskHandler via the channel.
-pub enum Message {
- ///This variant represents a message sent down the channel when requesting a timestamp token.
- Inputs((HardwareAuthToken, OperationChallenge, Sender<(HardwareAuthToken, TimeStampToken)>)),
- ///This variant represents a message sent down the channel when signalling the thread to stop.
- Shutdown,
-}
-
-impl BackgroundTaskHandler {
- /// Initialize the BackgroundTaskHandler with the task_handler field set to None.
- /// The thread is not started during initialization, as it needs the receiver end of a channel
- /// to function.
- pub fn new() -> Self {
- BackgroundTaskHandler { task_handler: Mutex::new(None) }
- }
-
- /// Start the background task handler (bth) by passing in the receiver end of a channel, through
- /// which the enforcement module can send messages to the bth thread.
- pub fn start_bth(&self, receiver: Receiver<Message>) -> Result<()> {
- let task_handler = Self::start_thread(receiver)?;
- // it is ok to unwrap here because there is no way that this lock can get poisoned.
- let mut thread_guard = self.task_handler.lock().unwrap();
- *thread_guard = Some(task_handler);
- Ok(())
- }
-
- fn start_thread(receiver: Receiver<Message>) -> Result<JoinHandle<()>> {
- // TODO: initialize timestamp service/keymint instances.
- // First lookup timestamp token service, if this is not a legacy device.
- // Otherwise, lookup keymaster 4.1 or 4.0 (previous ones are not relevant, because strongbox
- // was introduced with keymaster 4.0).
- // If either a timestamp service or a keymint instance is expected to be found and neither
- // is found, an error is returned.
- // If neither is expected to be found, make timestamp_service field None, and in the thread,
- // send a default timestamp token down the channel to the operation.
- // Until timestamp service is available and proper probing of legacy keymaster devices are
- // done, the keymint service is initialized here as it is done in security_level module.
- Ok(spawn(move || {
- while let Message::Inputs((auth_token, op_challenge, op_sender)) =
- receiver.recv().expect(
- "In background task handler thread. Failed to
- receive message over the channel.",
- )
- {
- let dev: Box<dyn ISecureClock> = get_timestamp_service()
- .expect(concat!(
- "Secure Clock service must be present ",
- "if TimeStampTokens are required."
- ))
- .get_interface()
- .expect("Fatal: Timestamp service does not implement ISecureClock.");
- let result = dev.generateTimeStamp(op_challenge.challenge);
- match result {
- Ok(timestamp_token) => {
- // this can fail if the operation is dropped and hence the channel
- // is hung up.
- op_sender.send((auth_token, timestamp_token)).unwrap_or_else(|e| {
- error!(
- "In background task handler thread. Failed to send
- timestamp token to operation {} due to error {:?}.",
- op_challenge.challenge, e
- )
- });
- }
- Err(e) => {
- // log error
- error!(
- "In background task handler thread. Failed to receive
- timestamp token for operation {} due to error {:?}.",
- op_challenge.challenge, e
- );
- // send default timestamp token
- // this can fail if the operation is dropped and the channel is
- // hung up.
- op_sender.send((auth_token, TimeStampToken::default())).unwrap_or_else(
- |e| {
- error!(
- "In background task handler thread. Failed to send default
- timestamp token to operation {} due to error {:?}.",
- op_challenge.challenge, e
- )
- },
- );
- }
- }
- }
- }))
- }
-}
-
-impl Default for BackgroundTaskHandler {
- fn default() -> Self {
- Self::new()
- }
-}
-
-// TODO: Verify if we want the thread to finish the requests they are working on, during drop.
-impl Drop for BackgroundTaskHandler {
- fn drop(&mut self) {
- // it is ok to unwrap here as there is no way this lock can get poisoned.
- let mut thread_guard = self.task_handler.lock().unwrap();
- if let Some(thread) = (*thread_guard).take() {
- // TODO: Verify how best to handle the error in this case.
- thread.join().unwrap_or_else(|e| {
- panic!("Failed to join the background task handling thread because of {:?}.", e);
- });
- }
- }
-}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index e25fea2..f70b0df 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -56,7 +56,7 @@
use std::{convert::TryFrom, convert::TryInto, time::SystemTimeError};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
+ HardwareAuthToken::HardwareAuthToken,
HardwareAuthenticatorType::HardwareAuthenticatorType, SecurityLevel::SecurityLevel,
};
use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
@@ -75,7 +75,7 @@
types::FromSqlResult,
types::ToSqlOutput,
types::{FromSqlError, Value, ValueRef},
- Connection, Error, OptionalExtension, ToSql, Transaction, TransactionBehavior, NO_PARAMS,
+ Connection, OptionalExtension, ToSql, Transaction, TransactionBehavior, NO_PARAMS,
};
use std::{
collections::{HashMap, HashSet},
@@ -547,6 +547,11 @@
pub fn seconds(&self) -> i64 {
self.0
}
+
+ /// Like i64::checked_sub.
+ pub fn checked_sub(&self, other: &Self) -> Option<Self> {
+ self.0.checked_sub(other.0).map(Self)
+ }
}
impl ToSql for MonotonicRawTime {
@@ -574,21 +579,27 @@
}
/// Checks if this auth token satisfies the given authentication information.
- pub fn satisfies_auth(
- auth_token: &HardwareAuthToken,
- user_secure_ids: &[i64],
- auth_type: HardwareAuthenticatorType,
- ) -> bool {
+ pub fn satisfies(&self, user_secure_ids: &[i64], auth_type: HardwareAuthenticatorType) -> bool {
user_secure_ids.iter().any(|&sid| {
- (sid == auth_token.userId || sid == auth_token.authenticatorId)
- && (((auth_type.0 as i32) & (auth_token.authenticatorType.0 as i32)) != 0)
+ (sid == self.auth_token.userId || sid == self.auth_token.authenticatorId)
+ && (((auth_type.0 as i32) & (self.auth_token.authenticatorType.0 as i32)) != 0)
})
}
/// Returns the auth token wrapped by the AuthTokenEntry
- pub fn get_auth_token(self) -> HardwareAuthToken {
+ pub fn auth_token(&self) -> &HardwareAuthToken {
+ &self.auth_token
+ }
+
+ /// Returns the auth token wrapped by the AuthTokenEntry
+ pub fn take_auth_token(self) -> HardwareAuthToken {
self.auth_token
}
+
+ /// Returns the time that this auth token was received.
+ pub fn time_received(&self) -> MonotonicRawTime {
+ self.time_received
+ }
}
impl KeystoreDB {
@@ -699,9 +710,6 @@
NO_PARAMS,
)
.context("Failed to initialize \"metadata\" table.")?;
- // TODO: Add the initial entry on last_off_body to the metadata table during the boot of the
- // device (i.e. during the first startup of the keystore during a boot cycle).
- Self::insert_last_off_body(conn, MonotonicRawTime::now()).context("In init-tables.")?;
Ok(())
}
@@ -1683,69 +1691,23 @@
Ok(())
}
- /// find the auth token entry issued for a time-out token
- pub fn find_timed_auth_token_entry(
+ /// Find the newest auth token matching the given predicate.
+ pub fn find_auth_token_entry<F>(
&mut self,
- user_secure_ids: &[i64],
- auth_type: HardwareAuthenticatorType,
- key_time_out: i64,
- allow_while_on_body: bool,
- ) -> Result<AuthTokenEntry> {
- let tx = self
- .conn
- .transaction_with_behavior(TransactionBehavior::Immediate)
- .context("In find_timed_auth_token_entry: failed to initialize transaction.")?;
- let auth_token_entries =
- Self::load_auth_token_entries(&tx).context("In find_timed_auth_token_entry.")?;
- // NOTE: Although in legacy keystore both timestamp and time_received are used when finding
- // the newest match, we decided to only consider time_received in keystore2 code.
- // TODO: verify that the iter().find() preserves the order.
- let newest_match: Option<AuthTokenEntry> = auth_token_entries.into_iter().find(|entry| {
- AuthTokenEntry::satisfies_auth(&entry.auth_token, user_secure_ids, auth_type)
- });
+ p: F,
+ ) -> Result<Option<(AuthTokenEntry, MonotonicRawTime)>>
+ where
+ F: Fn(&AuthTokenEntry) -> bool,
+ {
+ self.with_transaction(TransactionBehavior::Deferred, |tx| {
+ let mut stmt = tx
+ .prepare("SELECT * from perboot.authtoken ORDER BY time_received DESC;")
+ .context("Prepare statement failed.")?;
- // Tag::ALLOW_WHILE_ON_BODY specifies that the key may be used after authentication
- // timeout if device is still on-body. So we return error only if both checks fail.
- if let Some(newest_match_entry) = newest_match {
- let current_time = MonotonicRawTime::now();
- let time_since_received_plus_time_out =
- match newest_match_entry.time_received.seconds().checked_add(key_time_out) {
- Some(t) => t,
- None => {
- // we do not expect this behavior, so we need to log this error if it ever
- // happens.
- error!("In find_timed_auth_token_entry: overflow occurred.");
- return Err(KsError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
- "In find_timed_auth_token_entry: matching auth token is expired.",
- );
- }
- };
- if (time_since_received_plus_time_out < current_time.seconds())
- && (!allow_while_on_body
- || (newest_match_entry.time_received.seconds()
- < Self::get_last_off_body(&tx)?.seconds()))
- {
- return Err(KsError::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
- .context("In find_timed_auth_token_entry: matching auth token is expired.");
- }
- tx.commit().context("In find_timed_auth_token_entry, failed to commit transaction.")?;
- Ok(newest_match_entry)
- } else {
- Err(KsError::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
- .context("In find_timed_auth_token_entry: no matching auth token found.")
- }
- }
+ let mut rows = stmt.query(NO_PARAMS).context("Failed to query.")?;
- /// load the existing auth token entries in perboot.authtoken table into a vector.
- /// return None if the table is empty.
- fn load_auth_token_entries(tx: &Transaction) -> Result<Vec<AuthTokenEntry>> {
- let mut stmt = tx
- .prepare("SELECT * from perboot.authtoken ORDER BY time_received DESC;")
- .context("In load_auth_token_entries: select prepare statement failed.")?;
-
- let auth_token_entries: Vec<AuthTokenEntry> = stmt
- .query_map(NO_PARAMS, |row| {
- Ok(AuthTokenEntry::new(
+ while let Some(row) = rows.next().context("Failed to get next row.")? {
+ let entry = AuthTokenEntry::new(
HardwareAuthToken {
challenge: row.get(1)?,
userId: row.get(2)?,
@@ -1755,28 +1717,32 @@
mac: row.get(6)?,
},
row.get(7)?,
- ))
- })
- .context("In load_auth_token_entries: query_map failed.")?
- .collect::<Result<Vec<AuthTokenEntry>, Error>>()
- .context(
- "In load_auth_token_entries: failed to create a vector of auth token entries
- from mapped rows.",
- )?;
- Ok(auth_token_entries)
+ );
+ if p(&entry) {
+ return Ok(Some((
+ entry,
+ Self::get_last_off_body(tx)
+ .context("In find_auth_token_entry: Trying to get last off body")?,
+ )));
+ }
+ }
+ Ok(None)
+ })
+ .context("In find_auth_token_entry.")
}
- /// insert last_off_body into the metadata table at the initialization of auth token table
- pub fn insert_last_off_body(conn: &Connection, last_off_body: MonotonicRawTime) -> Result<()> {
- conn.execute(
- "INSERT OR REPLACE INTO perboot.metadata (key, value) VALUES (?, ?);",
- params!["last_off_body", last_off_body],
- )
- .context("In insert_last_off_body: failed to insert.")?;
+ /// Insert last_off_body into the metadata table at the initialization of auth token table
+ pub fn insert_last_off_body(&self, last_off_body: MonotonicRawTime) -> Result<()> {
+ self.conn
+ .execute(
+ "INSERT OR REPLACE INTO perboot.metadata (key, value) VALUES (?, ?);",
+ params!["last_off_body", last_off_body],
+ )
+ .context("In insert_last_off_body: failed to insert.")?;
Ok(())
}
- /// update last_off_body when on_device_off_body is called
+ /// Update last_off_body when on_device_off_body is called
pub fn update_last_off_body(&self, last_off_body: MonotonicRawTime) -> Result<()> {
self.conn
.execute(
@@ -1787,15 +1753,14 @@
Ok(())
}
- /// get last_off_body time when finding auth tokens
+ /// Get last_off_body time when finding auth tokens
fn get_last_off_body(tx: &Transaction) -> Result<MonotonicRawTime> {
- let mut stmt = tx
- .prepare("SELECT value from perboot.metadata WHERE key = ?;")
- .context("In get_last_off_body: select prepare statement failed.")?;
- let last_off_body: Result<MonotonicRawTime> = stmt
- .query_row(params!["last_off_body"], |row| Ok(row.get(0)?))
- .context("In get_last_off_body: query_row failed.");
- last_off_body
+ tx.query_row(
+ "SELECT value from perboot.metadata WHERE key = ?;",
+ params!["last_off_body"],
+ |row| Ok(row.get(0)?),
+ )
+ .context("In get_last_off_body: query_row failed.")
}
}
@@ -3013,7 +2978,7 @@
#[test]
fn test_last_off_body() -> Result<()> {
let mut db = new_test_db()?;
- KeystoreDB::insert_last_off_body(&db.conn, MonotonicRawTime::now())?;
+ db.insert_last_off_body(MonotonicRawTime::now())?;
let tx = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?;
let last_off_body_1 = KeystoreDB::get_last_off_body(&tx)?;
tx.commit()?;
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 4ff3950..22b4bed 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -14,39 +14,292 @@
//! This is the Keystore 2.0 Enforcements module.
// TODO: more description to follow.
-use crate::auth_token_handler::AuthTokenHandler;
-use crate::background_task_handler::Message;
-use crate::database::AuthTokenEntry;
-use crate::error::Error as KeystoreError;
-use crate::globals::DB;
+use crate::database::{AuthTokenEntry, MonotonicRawTime};
+use crate::error::{map_binder_status, Error, ErrorCode};
+use crate::globals::{get_timestamp_service, ASYNC_TASK, DB, ENFORCEMENTS};
use crate::key_parameter::{KeyParameter, KeyParameterValue};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
- HardwareAuthenticatorType::HardwareAuthenticatorType, KeyPurpose::KeyPurpose,
- SecurityLevel::SecurityLevel, Tag::Tag,
+ HardwareAuthenticatorType::HardwareAuthenticatorType,
+ KeyParameter::KeyParameter as KmKeyParameter, KeyPurpose::KeyPurpose, Tag::Tag,
};
use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
- TimeStampToken::TimeStampToken, Timestamp::Timestamp,
+ ISecureClock::ISecureClock, TimeStampToken::TimeStampToken,
};
use android_system_keystore2::aidl::android::system::keystore2::OperationChallenge::OperationChallenge;
use anyhow::{Context, Result};
use std::collections::{HashMap, HashSet};
-use std::sync::mpsc::{channel, Sender};
-use std::sync::Mutex;
+use std::sync::{
+ mpsc::{channel, Receiver, Sender},
+ Arc, Mutex, Weak,
+};
use std::time::SystemTime;
+#[derive(Debug)]
+enum AuthRequestState {
+ /// An outstanding per operation authorization request.
+ OpAuth,
+ /// An outstanding request for per operation authorization and secure timestamp.
+ TimeStampedOpAuth(Receiver<Result<TimeStampToken, Error>>),
+ /// An outstanding request for a timestamp token.
+ TimeStamp(Receiver<Result<TimeStampToken, Error>>),
+}
+
+#[derive(Debug)]
+struct AuthRequest {
+ state: AuthRequestState,
+ /// This need to be set to Some to fulfill a AuthRequestState::OpAuth or
+ /// AuthRequestState::TimeStampedOpAuth.
+ hat: Option<HardwareAuthToken>,
+}
+
+impl AuthRequest {
+ fn op_auth() -> Arc<Mutex<Self>> {
+ Arc::new(Mutex::new(Self { state: AuthRequestState::OpAuth, hat: None }))
+ }
+
+ fn timestamped_op_auth(receiver: Receiver<Result<TimeStampToken, Error>>) -> Arc<Mutex<Self>> {
+ Arc::new(Mutex::new(Self {
+ state: AuthRequestState::TimeStampedOpAuth(receiver),
+ hat: None,
+ }))
+ }
+
+ fn timestamp(
+ hat: HardwareAuthToken,
+ receiver: Receiver<Result<TimeStampToken, Error>>,
+ ) -> Arc<Mutex<Self>> {
+ Arc::new(Mutex::new(Self { state: AuthRequestState::TimeStamp(receiver), hat: Some(hat) }))
+ }
+
+ fn add_auth_token(&mut self, hat: HardwareAuthToken) {
+ self.hat = Some(hat)
+ }
+
+ fn get_auth_tokens(&mut self) -> Result<(HardwareAuthToken, Option<TimeStampToken>)> {
+ match (&self.state, self.hat.is_some()) {
+ (AuthRequestState::OpAuth, true) => Ok((self.hat.take().unwrap(), None)),
+ (AuthRequestState::TimeStampedOpAuth(recv), true)
+ | (AuthRequestState::TimeStamp(recv), true) => {
+ let result = recv.recv().context("In get_auth_tokens: Sender disconnected.")?;
+ let tst = result.context(concat!(
+ "In get_auth_tokens: Worker responded with error ",
+ "from generating timestamp token."
+ ))?;
+ Ok((self.hat.take().unwrap(), Some(tst)))
+ }
+ (_, false) => Err(Error::Km(ErrorCode::KEY_USER_NOT_AUTHENTICATED))
+ .context("In get_auth_tokens: No operation auth token received."),
+ }
+ }
+}
+
+/// DeferredAuthState describes how auth tokens and timestamp tokens need to be provided when
+/// updating and finishing an operation.
+#[derive(Debug)]
+enum DeferredAuthState {
+ /// Used when an operation does not require further authorization.
+ NoAuthRequired,
+ /// Indicates that the operation requires an operation specific token. This means we have
+ /// to return an operation challenge to the client which should reward us with an
+ /// operation specific auth token. If it is not provided before the client calls update
+ /// or finish, the operation fails as not authorized.
+ OpAuthRequired,
+ /// Indicates that the operation requires a time stamp token. The auth token was already
+ /// loaded from the database, but it has to be accompanied by a time stamp token to inform
+ /// the target KM with a different clock about the time on the authenticators.
+ TimeStampRequired(HardwareAuthToken),
+ /// Indicates that both an operation bound auth token and a verification token are
+ /// before the operation can commence.
+ TimeStampedOpAuthRequired,
+ /// In this state the auth info is waiting for the deferred authorizations to come in.
+ /// We block on timestamp tokens, because we can always make progress on these requests.
+ /// The per-op auth tokens might never come, which means we fail if the client calls
+ /// update or finish before we got a per-op auth token.
+ Waiting(Arc<Mutex<AuthRequest>>),
+ /// In this state we have gotten all of the required tokens, we just cache them to
+ /// be used when the operation progresses.
+ Token(HardwareAuthToken, Option<TimeStampToken>),
+}
+
+/// Auth info hold all of the authorization related information of an operation. It is stored
+/// in and owned by the operation. It is constructed by authorize_create and stays with the
+/// operation until it completes.
+#[derive(Debug)]
+pub struct AuthInfo {
+ state: DeferredAuthState,
+}
+
+struct TokenReceiverMap {
+ /// The map maps an outstanding challenge to a TokenReceiver. If an incoming Hardware Auth
+ /// Token (HAT) has the map key in its challenge field, it gets passed to the TokenReceiver
+ /// and the entry is removed from the map. In the case where no HAT is received before the
+ /// corresponding operation gets dropped, the entry goes stale. So every time the cleanup
+ /// counter (second field in the tuple) turns 0, the map is cleaned from stale entries.
+ /// The cleanup counter is decremented every time a new receiver is added.
+ /// and reset to TokenReceiverMap::CLEANUP_PERIOD + 1 after each cleanup.
+ map_and_cleanup_counter: Mutex<(HashMap<i64, TokenReceiver>, u8)>,
+}
+
+impl Default for TokenReceiverMap {
+ fn default() -> Self {
+ Self { map_and_cleanup_counter: Mutex::new((HashMap::new(), Self::CLEANUP_PERIOD + 1)) }
+ }
+}
+
+impl TokenReceiverMap {
+ /// There is a chance that receivers may become stale because their operation is dropped
+ /// without ever being authorized. So occasionally we iterate through the map and throw
+ /// out obsolete entries.
+ /// This is the number of calls to add_receiver between cleanups.
+ const CLEANUP_PERIOD: u8 = 25;
+
+ pub fn add_auth_token(&self, hat: HardwareAuthToken) {
+ let mut map = self.map_and_cleanup_counter.lock().unwrap();
+ let (ref mut map, _) = *map;
+ if let Some((_, recv)) = map.remove_entry(&hat.challenge) {
+ recv.add_auth_token(hat);
+ }
+ }
+
+ pub fn add_receiver(&self, challenge: i64, recv: TokenReceiver) {
+ let mut map = self.map_and_cleanup_counter.lock().unwrap();
+ let (ref mut map, ref mut cleanup_counter) = *map;
+ map.insert(challenge, recv);
+
+ *cleanup_counter -= 1;
+ if *cleanup_counter == 0 {
+ map.retain(|_, v| !v.is_obsolete());
+ map.shrink_to_fit();
+ *cleanup_counter = Self::CLEANUP_PERIOD + 1;
+ }
+ }
+}
+
+#[derive(Debug)]
+struct TokenReceiver(Weak<Mutex<AuthRequest>>);
+
+impl TokenReceiver {
+ fn is_obsolete(&self) -> bool {
+ self.0.upgrade().is_none()
+ }
+
+ fn add_auth_token(&self, hat: HardwareAuthToken) {
+ if let Some(state_arc) = self.0.upgrade() {
+ let mut state = state_arc.lock().unwrap();
+ state.add_auth_token(hat);
+ }
+ }
+}
+
+fn get_timestamp_token(challenge: i64) -> Result<TimeStampToken, Error> {
+ let dev: Box<dyn ISecureClock> = get_timestamp_service()
+ .expect(concat!(
+ "Secure Clock service must be present ",
+ "if TimeStampTokens are required."
+ ))
+ .get_interface()
+ .expect("Fatal: Timestamp service does not implement ISecureClock.");
+ map_binder_status(dev.generateTimeStamp(challenge))
+}
+
+fn timestamp_token_request(challenge: i64, sender: Sender<Result<TimeStampToken, Error>>) {
+ if let Err(e) = sender.send(get_timestamp_token(challenge)) {
+ log::info!(
+ concat!(
+ "In timestamp_token_request: Operation hung up ",
+ "before timestamp token could be delivered. {:?}"
+ ),
+ e
+ );
+ }
+}
+
+impl AuthInfo {
+ /// This function gets called after an operation was successfully created.
+ /// It makes all the preparations required, so that the operation has all the authentication
+ /// related artifacts to advance on update and finish.
+ pub fn finalize_create_authorization(&mut self, challenge: i64) -> Option<OperationChallenge> {
+ match &self.state {
+ DeferredAuthState::OpAuthRequired => {
+ let auth_request = AuthRequest::op_auth();
+ let token_receiver = TokenReceiver(Arc::downgrade(&auth_request));
+ ENFORCEMENTS.register_op_auth_receiver(challenge, token_receiver);
+
+ self.state = DeferredAuthState::Waiting(auth_request);
+ Some(OperationChallenge { challenge })
+ }
+ DeferredAuthState::TimeStampedOpAuthRequired => {
+ let (sender, receiver) = channel::<Result<TimeStampToken, Error>>();
+ let auth_request = AuthRequest::timestamped_op_auth(receiver);
+ let token_receiver = TokenReceiver(Arc::downgrade(&auth_request));
+ ENFORCEMENTS.register_op_auth_receiver(challenge, token_receiver);
+
+ ASYNC_TASK.queue_hi(move || timestamp_token_request(challenge, sender));
+ self.state = DeferredAuthState::Waiting(auth_request);
+ Some(OperationChallenge { challenge })
+ }
+ DeferredAuthState::TimeStampRequired(hat) => {
+ let hat = (*hat).clone();
+ let (sender, receiver) = channel::<Result<TimeStampToken, Error>>();
+ let auth_request = AuthRequest::timestamp(hat, receiver);
+ ASYNC_TASK.queue_hi(move || timestamp_token_request(challenge, sender));
+ self.state = DeferredAuthState::Waiting(auth_request);
+ None
+ }
+ _ => None,
+ }
+ }
+
+ /// This function returns the auth tokens as needed by the ongoing operation or fails
+ /// with ErrorCode::KEY_USER_NOT_AUTHENTICATED. If this was called for the first time
+ /// after a deferred authorization was requested by finalize_create_authorization, this
+ /// function may block on the generation of a time stamp token. It then moves the
+ /// tokens into the DeferredAuthState::Token state for future use.
+ pub fn get_auth_tokens(
+ &mut self,
+ ) -> Result<(Option<HardwareAuthToken>, Option<TimeStampToken>)> {
+ let deferred_tokens = if let DeferredAuthState::Waiting(ref auth_request) = self.state {
+ let mut state = auth_request.lock().unwrap();
+ Some(state.get_auth_tokens().context("In AuthInfo::get_auth_tokens.")?)
+ } else {
+ None
+ };
+
+ if let Some((hat, tst)) = deferred_tokens {
+ self.state = DeferredAuthState::Token(hat, tst);
+ }
+
+ match &self.state {
+ DeferredAuthState::NoAuthRequired => Ok((None, None)),
+ DeferredAuthState::Token(hat, tst) => Ok((Some((*hat).clone()), (*tst).clone())),
+ DeferredAuthState::OpAuthRequired
+ | DeferredAuthState::TimeStampedOpAuthRequired
+ | DeferredAuthState::TimeStampRequired(_) => {
+ Err(Error::Km(ErrorCode::KEY_USER_NOT_AUTHENTICATED)).context(concat!(
+ "In AuthInfo::get_auth_tokens: No operation auth token requested??? ",
+ "This should not happen."
+ ))
+ }
+ // This should not be reachable, because it should have been handled above.
+ DeferredAuthState::Waiting(_) => {
+ Err(Error::sys()).context("In AuthInfo::get_auth_tokens: Cannot be reached.")
+ }
+ }
+ }
+}
+
/// Enforcements data structure
pub struct Enforcements {
- // This hash set contains the user ids for whom the device is currently unlocked. If a user id
- // is not in the set, it implies that the device is locked for the user.
+ /// This hash set contains the user ids for whom the device is currently unlocked. If a user id
+ /// is not in the set, it implies that the device is locked for the user.
device_unlocked_set: Mutex<HashSet<i32>>,
- // This maps the operation challenge to an optional auth token, to maintain op-auth tokens
- // in-memory, until they are picked up and given to the operation by authorise_update_finish().
- op_auth_map: Mutex<HashMap<i64, Option<HardwareAuthToken>>>,
- // sender end of the channel via which the enforcement module communicates with the
- // background task handler (bth). This is of type Mutex in an Option because it is initialized
- // after the global enforcement object is created.
- sender_to_bth: Mutex<Option<Sender<Message>>>,
+ /// This field maps outstanding auth challenges to their operations. When an auth token
+ /// with the right challenge is received it is passed to the map using
+ /// TokenReceiverMap::add_auth_token() which removes the entry from the map. If an entry goes
+ /// stale, because the operation gets dropped before an auth token is received, the map
+ /// is cleaned up in regular intervals.
+ op_auth_map: TokenReceiverMap,
}
impl Enforcements {
@@ -54,127 +307,40 @@
pub fn new() -> Self {
Enforcements {
device_unlocked_set: Mutex::new(HashSet::new()),
- op_auth_map: Mutex::new(HashMap::new()),
- sender_to_bth: Mutex::new(None),
+ op_auth_map: Default::default(),
}
}
- /// Initialize the sender_to_bth field, using the given sender end of a channel.
- pub fn set_sender_to_bth(&self, sender: Sender<Message>) {
- // It is ok to unwrap here because there is no chance of poisoning this mutex.
- let mut sender_guard = self.sender_to_bth.lock().unwrap();
- *sender_guard = Some(sender);
- }
-
- /// Checks if update or finish calls are authorized. If the operation is based on per-op key,
- /// try to receive the auth token from the op_auth_map. We assume that by the time update/finish
- /// is called, the auth token has been delivered to keystore. Therefore, we do not wait for it
- /// and if the auth token is not found in the map, an error is returned.
- /// This method is called only during the first call to update or if finish is called right
- /// after create operation, because the operation caches the authorization decisions and tokens
- /// from previous calls to enforcement module.
- pub fn authorize_update_or_finish(
- &self,
- key_params: &[KeyParameter],
- op_challenge: Option<&OperationChallenge>,
- ) -> Result<AuthTokenHandler> {
- let mut user_auth_type: Option<HardwareAuthenticatorType> = None;
- let mut user_secure_ids = Vec::<i64>::new();
- let mut is_timeout_key = false;
-
- for key_param in key_params.iter() {
- match key_param.key_parameter_value() {
- KeyParameterValue::NoAuthRequired => {
- // unlike in authorize_create, we do not check if both NoAuthRequired and user
- // secure id are present, because that is already checked in authorize_create.
- return Ok(AuthTokenHandler::NoAuthRequired);
- }
- KeyParameterValue::AuthTimeout(_) => {
- is_timeout_key = true;
- }
- KeyParameterValue::HardwareAuthenticatorType(a) => {
- user_auth_type = Some(*a);
- }
- KeyParameterValue::UserSecureID(u) => {
- user_secure_ids.push(*u);
- }
- _ => {}
- }
- }
-
- // If either of auth_type or secure_id is present and the other is not present,
- // authorize_create would have already returned error.
- // At this point, if UserSecureID is present and AuthTimeout is not present in
- // key parameters, per-op auth is required.
- // Obtain and validate the auth token.
- if !is_timeout_key && !user_secure_ids.is_empty() {
- let challenge =
- op_challenge.ok_or(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
- "In authorize_update_or_finish: Auth required, but operation challenge is not
- present.",
- )?;
- let auth_type =
- user_auth_type.ok_or(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
- "In authorize_update_or_finish: Auth required, but authenticator type is not
- present.",
- )?;
- // It is ok to unwrap here, because there is no way this lock can get poisoned and
- // and there is no way to recover if it is poisoned.
- let mut op_auth_map_guard = self.op_auth_map.lock().unwrap();
- let auth_entry = op_auth_map_guard.remove(&(challenge.challenge));
-
- match auth_entry {
- Some(Some(auth_token)) => {
- if AuthTokenEntry::satisfies_auth(&auth_token, &user_secure_ids, auth_type) {
- return Ok(AuthTokenHandler::Token(auth_token, None));
- } else {
- return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
- .context("In authorize_update_or_finish: Auth token does not match.");
- }
- }
- _ => {
- // there was no auth token
- return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
- "In authorize_update_or_finish: Auth required, but an auth token
- is not found for the given operation challenge, in the op_auth_map.",
- );
- }
- }
- }
-
- // If we don't find HardwareAuthenticatorType and UserSecureID, we assume that
- // authentication is not required, because in legacy keys, authentication related
- // key parameters may not present.
- // TODO: METRICS: count how many times (if any) this code path is executed, in order
- // to identify if any such keys are in use
- Ok(AuthTokenHandler::NoAuthRequired)
- }
-
/// Checks if a create call is authorized, given key parameters and operation parameters.
+ /// It returns an optional immediate auth token which can be presented to begin, and an
+ /// AuthInfo object which stays with the authorized operation and is used to obtain
+ /// auth tokens and timestamp tokens as required by the operation.
/// With regard to auth tokens, the following steps are taken:
+ ///
+ /// If no key parameters are given (typically when the client is self managed
+ /// (see Domain.Blob)) nothing is enforced.
/// If the key is time-bound, find a matching auth token from the database.
- /// If the above step is successful, and if the security level is STRONGBOX, return a
- /// TimestampRequired variant of the AuthTokenHandler with the found auth token to signal
- /// the operation that it may need to obtain a timestamp token from TEE KeyMint.
- /// If the security level is not STRONGBOX, return a Token variant of the AuthTokenHandler with
- /// the found auth token to signal the operation that no more authorization required.
- /// If the key is per-op, return an OpAuthRequired variant of the AuthTokenHandler to signal
- /// create_operation() that it needs to add the operation challenge to the op_auth_map, once it
- /// is received from the keymint, and that operation needs to be authorized before update/finish
- /// is called.
+ /// If the above step is successful, and if requires_timestamp is given, the returned
+ /// AuthInfo will provide a Timestamp token as appropriate.
pub fn authorize_create(
&self,
purpose: KeyPurpose,
- key_params: &[KeyParameter],
- op_params: &[KeyParameter],
- security_level: SecurityLevel,
- ) -> Result<AuthTokenHandler> {
+ key_params: Option<&[KeyParameter]>,
+ op_params: &[KmKeyParameter],
+ requires_timestamp: bool,
+ ) -> Result<(Option<HardwareAuthToken>, AuthInfo)> {
+ let key_params = if let Some(k) = key_params {
+ k
+ } else {
+ return Ok((None, AuthInfo { state: DeferredAuthState::NoAuthRequired }));
+ };
+
match purpose {
// Allow SIGN, DECRYPT for both symmetric and asymmetric keys.
KeyPurpose::SIGN | KeyPurpose::DECRYPT => {}
// Rule out WRAP_KEY purpose
KeyPurpose::WRAP_KEY => {
- return Err(KeystoreError::Km(Ec::INCOMPATIBLE_PURPOSE))
+ return Err(Error::Km(Ec::INCOMPATIBLE_PURPOSE))
.context("In authorize_create: WRAP_KEY purpose is not allowed here.");
}
KeyPurpose::VERIFY | KeyPurpose::ENCRYPT => {
@@ -184,7 +350,7 @@
match *kp.key_parameter_value() {
KeyParameterValue::Algorithm(Algorithm::RSA)
| KeyParameterValue::Algorithm(Algorithm::EC) => {
- return Err(KeystoreError::Km(Ec::UNSUPPORTED_PURPOSE)).context(
+ return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE)).context(
"In authorize_create: public operations on asymmetric keys are not
supported.",
);
@@ -194,7 +360,7 @@
}
}
_ => {
- return Err(KeystoreError::Km(Ec::UNSUPPORTED_PURPOSE))
+ return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE))
.context("In authorize_create: specified purpose is not supported.");
}
}
@@ -203,7 +369,6 @@
// There is only one additional variable than what legacy keystore has, but this helps
// reduce the number of for loops on key parameters from 3 to 1, compared to legacy keystore
let mut key_purpose_authorized: bool = false;
- let mut is_time_out_key: bool = false;
let mut user_auth_type: Option<HardwareAuthenticatorType> = None;
let mut no_auth_required: bool = false;
let mut caller_nonce_allowed = false;
@@ -221,7 +386,6 @@
no_auth_required = true;
}
KeyParameterValue::AuthTimeout(t) => {
- is_time_out_key = true;
key_time_out = Some(*t as i64);
}
KeyParameterValue::HardwareAuthenticatorType(a) => {
@@ -241,7 +405,7 @@
}
KeyParameterValue::ActiveDateTime(a) => {
if !Enforcements::is_given_time_passed(*a, true) {
- return Err(KeystoreError::Km(Ec::KEY_NOT_YET_VALID))
+ return Err(Error::Km(Ec::KEY_NOT_YET_VALID))
.context("In authorize_create: key is not yet active.");
}
}
@@ -249,7 +413,7 @@
if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN)
&& Enforcements::is_given_time_passed(*o, false)
{
- return Err(KeystoreError::Km(Ec::KEY_EXPIRED))
+ return Err(Error::Km(Ec::KEY_EXPIRED))
.context("In authorize_create: key is expired.");
}
}
@@ -257,7 +421,7 @@
if (purpose == KeyPurpose::DECRYPT || purpose == KeyPurpose::VERIFY)
&& Enforcements::is_given_time_passed(*u, false)
{
- return Err(KeystoreError::Km(Ec::KEY_EXPIRED))
+ return Err(Error::Km(Ec::KEY_EXPIRED))
.context("In authorize_create: key is expired.");
}
}
@@ -276,7 +440,7 @@
// NOTE: as per offline discussion, sanitizing key parameters and rejecting
// create operation if any non-allowed tags are present, is not done in
// authorize_create (unlike in legacy keystore where AuthorizeBegin is rejected if
- // a subset of non-allowed tags are present). Because santizing key parameters
+ // a subset of non-allowed tags are present). Because sanitizing key parameters
// should have been done during generate/import key, by KeyMint.
_ => { /*Do nothing on all the other key parameters, as in legacy keystore*/ }
}
@@ -284,13 +448,13 @@
// authorize the purpose
if !key_purpose_authorized {
- return Err(KeystoreError::Km(Ec::INCOMPATIBLE_PURPOSE))
+ return Err(Error::Km(Ec::INCOMPATIBLE_PURPOSE))
.context("In authorize_create: the purpose is not authorized.");
}
// if both NO_AUTH_REQUIRED and USER_SECURE_ID tags are present, return error
if !user_secure_ids.is_empty() && no_auth_required {
- return Err(KeystoreError::Km(Ec::INVALID_KEY_BLOB)).context(
+ return Err(Error::Km(Ec::INVALID_KEY_BLOB)).context(
"In authorize_create: key has both NO_AUTH_REQUIRED
and USER_SECURE_ID tags.",
);
@@ -300,17 +464,18 @@
if (user_auth_type.is_some() && user_secure_ids.is_empty())
|| (user_auth_type.is_none() && !user_secure_ids.is_empty())
{
- return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
+ return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
"In authorize_create: Auth required, but either auth type or secure ids
are not present.",
);
}
+
// validate caller nonce for origination purposes
if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN)
&& !caller_nonce_allowed
- && op_params.iter().any(|kp| kp.get_tag() == Tag::NONCE)
+ && op_params.iter().any(|kp| kp.tag == Tag::NONCE)
{
- return Err(KeystoreError::Km(Ec::CALLER_NONCE_PROHIBITED)).context(
+ return Err(Error::Km(Ec::CALLER_NONCE_PROHIBITED)).context(
"In authorize_create, NONCE is present,
although CALLER_NONCE is not present",
);
@@ -319,50 +484,99 @@
if unlocked_device_required {
// check the device locked status. If locked, operations on the key are not
// allowed.
- log::info!("Checking for lockd device of user {}.", user_id);
if self.is_device_locked(user_id) {
- return Err(KeystoreError::Km(Ec::DEVICE_LOCKED))
+ return Err(Error::Km(Ec::DEVICE_LOCKED))
.context("In authorize_create: device is locked.");
}
}
- if !user_secure_ids.is_empty() {
- // key requiring authentication per operation
- if !is_time_out_key {
- return Ok(AuthTokenHandler::OpAuthRequired);
- } else {
- // key requiring time-out based authentication
- let auth_token = DB
- .with::<_, Result<HardwareAuthToken>>(|db| {
- let mut db = db.borrow_mut();
- match (user_auth_type, key_time_out) {
- (Some(auth_type), Some(key_time_out)) => {
- let matching_entry = db
- .find_timed_auth_token_entry(
- &user_secure_ids,
- auth_type,
- key_time_out,
- allow_while_on_body,
- )
- .context("Failed to find timed auth token.")?;
- Ok(matching_entry.get_auth_token())
- }
- (_, _) => Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
- .context("Authenticator type and/or key time out is not given."),
- }
- })
- .context("In authorize_create.")?;
-
- if security_level == SecurityLevel::STRONGBOX {
- return Ok(AuthTokenHandler::TimestampRequired(auth_token));
- } else {
- return Ok(AuthTokenHandler::Token(auth_token, None));
- }
- }
+ if !unlocked_device_required && no_auth_required {
+ return Ok((None, AuthInfo { state: DeferredAuthState::NoAuthRequired }));
}
- // If we reach here, all authorization enforcements have passed and no auth token required.
- Ok(AuthTokenHandler::NoAuthRequired)
+ let has_sids = !user_secure_ids.is_empty();
+
+ let timeout_bound = key_time_out.is_some() && has_sids;
+
+ let per_op_bound = key_time_out.is_none() && has_sids;
+
+ let need_auth_token = timeout_bound || unlocked_device_required;
+
+ let hat_and_last_off_body = if need_auth_token {
+ let hat_and_last_off_body = Self::find_auth_token(|hat: &AuthTokenEntry| {
+ if let (Some(auth_type), true) = (user_auth_type, has_sids) {
+ hat.satisfies(&user_secure_ids, auth_type)
+ } else {
+ unlocked_device_required
+ }
+ })
+ .context("In authorize_create: Trying to get required auth token.")?;
+ Some(
+ hat_and_last_off_body
+ .ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
+ .context("In authorize_create: No suitable auth token found.")?,
+ )
+ } else {
+ None
+ };
+
+ // Now check the validity of the auth token if the key is timeout bound.
+ let hat = match (hat_and_last_off_body, key_time_out) {
+ (Some((hat, last_off_body)), Some(key_time_out)) => {
+ let now = MonotonicRawTime::now();
+ let token_age = now
+ .checked_sub(&hat.time_received())
+ .ok_or_else(Error::sys)
+ .context(concat!(
+ "In authorize_create: Overflow while computing Auth token validity. ",
+ "Validity cannot be established."
+ ))?;
+
+ let on_body_extended = allow_while_on_body && last_off_body < hat.time_received();
+
+ if token_age.seconds() > key_time_out && !on_body_extended {
+ return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
+ .context("In authorize_create: matching auth token is expired.");
+ }
+ Some(hat)
+ }
+ (Some((hat, _)), None) => Some(hat),
+ // If timeout_bound is true, above code must have retrieved a HAT or returned with
+ // KEY_USER_NOT_AUTHENTICATED. This arm should not be reachable.
+ (None, Some(_)) => panic!("Logical error."),
+ _ => None,
+ };
+
+ Ok(match (hat, requires_timestamp, per_op_bound) {
+ // Per-op-bound and Some(hat) can only happen if we are both per-op bound and unlocked
+ // device required. In addition, this KM instance needs a timestamp token.
+ // So the HAT cannot be presented on create. So on update/finish we present both
+ // an per-op-bound auth token and a timestamp token.
+ (Some(_), true, true) => (None, DeferredAuthState::TimeStampedOpAuthRequired),
+ (Some(hat), true, false) => {
+ (None, DeferredAuthState::TimeStampRequired(hat.take_auth_token()))
+ }
+ (Some(hat), false, true) => {
+ (Some(hat.take_auth_token()), DeferredAuthState::OpAuthRequired)
+ }
+ (Some(hat), false, false) => {
+ (Some(hat.take_auth_token()), DeferredAuthState::NoAuthRequired)
+ }
+ (None, _, true) => (None, DeferredAuthState::OpAuthRequired),
+ (None, _, false) => (None, DeferredAuthState::NoAuthRequired),
+ })
+ .map(|(hat, state)| (hat, AuthInfo { state }))
+ }
+
+ fn find_auth_token<F>(p: F) -> Result<Option<(AuthTokenEntry, MonotonicRawTime)>>
+ where
+ F: Fn(&AuthTokenEntry) -> bool,
+ {
+ DB.with(|db| {
+ let mut db = db.borrow_mut();
+ db.find_auth_token_entry(p).context("Trying to find auth token.")
+ })
+ .context("In find_auth_token.")
}
/// Checks if the time now since epoch is greater than (or equal, if is_given_time_inclusive is
@@ -404,67 +618,21 @@
}
/// Add this auth token to the database.
- /// Then check if there is an entry in the op_auth_map, indexed by the challenge of this
- /// auth token (which could have been inserted during create_operation of an operation on a
- /// per-op-auth key). If so, add a copy of this auth token to the map indexed by the
- /// challenge.
- pub fn add_auth_token(&self, auth_token: HardwareAuthToken) -> Result<()> {
- //it is ok to unwrap here, because there is no way this lock can get poisoned and
- //and there is no way to recover if it is poisoned.
- let mut op_auth_map_guard = self.op_auth_map.lock().unwrap();
+ /// Then present the auth token to the op auth map. If an operation is waiting for this
+ /// auth token this fulfills the request and removes the receiver from the map.
+ pub fn add_auth_token(&self, hat: HardwareAuthToken) -> Result<()> {
+ DB.with(|db| db.borrow_mut().insert_auth_token(&hat)).context("In add_auth_token.")?;
- if op_auth_map_guard.contains_key(&auth_token.challenge) {
- let auth_token_copy = HardwareAuthToken {
- challenge: auth_token.challenge,
- userId: auth_token.userId,
- authenticatorId: auth_token.authenticatorId,
- authenticatorType: HardwareAuthenticatorType(auth_token.authenticatorType.0),
- timestamp: Timestamp { milliSeconds: auth_token.timestamp.milliSeconds },
- mac: auth_token.mac.clone(),
- };
- op_auth_map_guard.insert(auth_token.challenge, Some(auth_token_copy));
- }
-
- DB.with(|db| db.borrow_mut().insert_auth_token(&auth_token))
- .context("In add_auth_token.")?;
+ self.op_auth_map.add_auth_token(hat);
Ok(())
}
/// This allows adding an entry to the op_auth_map, indexed by the operation challenge.
/// This is to be called by create_operation, once it has received the operation challenge
/// from keymint for an operation whose authorization decision is OpAuthRequired, as signalled
- /// by the AuthTokenHandler.
- pub fn insert_to_op_auth_map(&self, op_challenge: i64) {
- let mut op_auth_map_guard = self.op_auth_map.lock().unwrap();
- op_auth_map_guard.insert(op_challenge, None);
- }
-
- /// Requests a timestamp token from the background task handler which will retrieve it from
- /// Timestamp Service or TEE KeyMint.
- /// Once the create_operation receives an operation challenge from KeyMint, if it has
- /// previously received a TimestampRequired variant of AuthTokenHandler during
- /// authorize_create_operation, it calls this method to obtain a TimeStampToken.
- pub fn request_timestamp_token(
- &self,
- auth_token: HardwareAuthToken,
- op_challenge: OperationChallenge,
- ) -> Result<AuthTokenHandler> {
- // create a channel for this particular operation
- let (op_sender, op_receiver) = channel::<(HardwareAuthToken, TimeStampToken)>();
- // it is ok to unwrap here because there is no way this mutex gets poisoned.
- let sender_guard = self.sender_to_bth.lock().unwrap();
- if let Some(sender) = &*sender_guard {
- let sender_cloned = sender.clone();
- drop(sender_guard);
- sender_cloned
- .send(Message::Inputs((auth_token, op_challenge, op_sender)))
- .map_err(|_| KeystoreError::sys())
- .context(
- "In request_timestamp_token. Sending a request for a timestamp token
- failed.",
- )?;
- }
- Ok(AuthTokenHandler::Channel(op_receiver))
+ /// by the DeferredAuthState.
+ fn register_op_auth_receiver(&self, challenge: i64, recv: TokenReceiver) {
+ self.op_auth_map.add_receiver(challenge, recv);
}
}
@@ -474,21 +642,4 @@
}
}
-impl Drop for Enforcements {
- fn drop(&mut self) {
- let sender_guard = self.sender_to_bth.lock().unwrap();
- if let Some(sender) = &*sender_guard {
- let sender_cloned = sender.clone();
- drop(sender_guard);
- // TODO: Verify how best to handle the error in this case.
- sender_cloned.send(Message::Shutdown).unwrap_or_else(|e| {
- panic!(
- "Failed to send shutdown message to background task handler because of {:?}.",
- e
- );
- });
- }
- }
-}
-
// TODO: Add tests to enforcement module (b/175578618).
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index 7ceff26..50ec26c 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -16,13 +16,12 @@
//! database connections and connections to services that Keystore needs
//! to talk to.
-use crate::async_task::AsyncTask;
-use crate::background_task_handler::BackgroundTaskHandler;
use crate::enforcements::Enforcements;
use crate::gc::Gc;
use crate::legacy_blob::LegacyBlobLoader;
use crate::super_key::SuperKeyManager;
use crate::utils::Asp;
+use crate::{async_task::AsyncTask, database::MonotonicRawTime};
use crate::{
database::KeystoreDB,
error::{map_binder_status, map_binder_status_code, Error, ErrorCode},
@@ -51,6 +50,8 @@
.expect("Failed to open database.");
DB_INIT.call_once(|| {
log::info!("Touching Keystore 2.0 database for this first time since boot.");
+ db.insert_last_off_body(MonotonicRawTime::now())
+ .expect("Could not initialize database with last off body.");
log::info!("Calling cleanup leftovers.");
let n = db.cleanup_leftovers().expect("Failed to cleanup database on startup.");
if n != 0 {
@@ -86,12 +87,8 @@
/// A single on-demand worker thread that handles deferred tasks with two different
/// priorities.
pub static ref ASYNC_TASK: AsyncTask = Default::default();
- /// Singeleton for enforcements.
+ /// Singleton for enforcements.
pub static ref ENFORCEMENTS: Enforcements = Enforcements::new();
- /// Background task handler is initialized and exists globally.
- /// The other modules (e.g. enforcements) communicate with it via a channel initialized during
- /// keystore startup.
- pub static ref BACKGROUND_TASK_HANDLER: BackgroundTaskHandler = BackgroundTaskHandler::new();
/// LegacyBlobLoader is initialized and exists globally.
/// The same directory used by the database is used by the LegacyBlobLoader as well.
pub static ref LEGACY_BLOB_LOADER: LegacyBlobLoader = LegacyBlobLoader::new(
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index c75cfc8..2ea41aa 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -17,12 +17,9 @@
use binder::Interface;
use keystore2::apc::ApcManager;
use keystore2::authorization::AuthorizationManager;
-use keystore2::background_task_handler::Message;
-use keystore2::globals::{BACKGROUND_TASK_HANDLER, ENFORCEMENTS};
use keystore2::service::KeystoreService;
use log::{error, info};
use std::panic;
-use std::sync::mpsc::channel;
static KS2_SERVICE_NAME: &str = "android.system.keystore2";
static APC_SERVICE_NAME: &str = "android.security.apc";
@@ -55,14 +52,6 @@
panic!("Must specify a working directory.");
}
- // initialize the channel via which the enforcement module and background task handler module
- // communicate, and hand over the sender and receiver ends to the respective objects.
- let (sender, receiver) = channel::<Message>();
- ENFORCEMENTS.set_sender_to_bth(sender);
- BACKGROUND_TASK_HANDLER.start_bth(receiver).unwrap_or_else(|e| {
- panic!("Failed to start background task handler because of {:?}.", e);
- });
-
info!("Starting thread pool now.");
binder::ProcessState::start_thread_pool();
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 240998e..0475d6f 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -16,9 +16,7 @@
#![recursion_limit = "256"]
pub mod apc;
-pub mod auth_token_handler;
pub mod authorization;
-pub mod background_task_handler;
pub mod database;
pub mod enforcements;
pub mod error;
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
index 8e4f800..30e6d55 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -125,23 +125,16 @@
//! 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 crate::auth_token_handler::AuthTokenHandler;
+use crate::enforcements::AuthInfo;
use crate::error::{map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
-use crate::globals::ENFORCEMENTS;
-use crate::key_parameter::KeyParameter;
use crate::utils::Asp;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- ByteArray::ByteArray, HardwareAuthToken::HardwareAuthToken,
- IKeyMintOperation::IKeyMintOperation, KeyParameter::KeyParameter as KmParam,
- KeyParameterArray::KeyParameterArray, KeyParameterValue::KeyParameterValue as KmParamValue,
- Tag::Tag,
-};
-use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
- TimeStampToken::TimeStampToken,
+ ByteArray::ByteArray, IKeyMintOperation::IKeyMintOperation,
+ KeyParameter::KeyParameter as KmParam, KeyParameterArray::KeyParameterArray,
+ KeyParameterValue::KeyParameterValue as KmParamValue, Tag::Tag,
};
use android_system_keystore2::aidl::android::system::keystore2::{
IKeystoreOperation::BnKeystoreOperation, IKeystoreOperation::IKeystoreOperation,
- OperationChallenge::OperationChallenge,
};
use anyhow::{anyhow, Context, Result};
use binder::{IBinder, Interface};
@@ -175,11 +168,7 @@
last_usage: Mutex<Instant>,
outcome: Mutex<Outcome>,
owner: u32, // Uid of the operation's owner.
- auth_token_handler: Mutex<AuthTokenHandler>,
- // optional because in create_operation, there is a case in which we might not load
- // key parameters
- key_params: Option<Vec<KeyParameter>>,
- op_challenge: Option<OperationChallenge>,
+ auth_info: Mutex<AuthInfo>,
}
struct PruningInfo {
@@ -197,9 +186,7 @@
index: usize,
km_op: Box<dyn IKeyMintOperation>,
owner: u32,
- auth_token_handler: AuthTokenHandler,
- key_params: Option<Vec<KeyParameter>>,
- op_challenge: Option<OperationChallenge>,
+ auth_info: AuthInfo,
) -> Self {
Self {
index,
@@ -207,9 +194,7 @@
last_usage: Mutex::new(Instant::now()),
outcome: Mutex::new(Outcome::Unknown),
owner,
- auth_token_handler: Mutex::new(auth_token_handler),
- key_params,
- op_challenge,
+ auth_info: Mutex::new(auth_info),
}
}
@@ -352,15 +337,20 @@
let km_op: Box<dyn IKeyMintOperation> =
self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
+ let (hat, tst) = self
+ .auth_info
+ .lock()
+ .unwrap()
+ .get_auth_tokens()
+ .context("In update_aad: Trying to get auth tokens.")?;
+
self.update_outcome(
&mut *outcome,
map_km_error(km_op.update(
Some(¶ms),
None,
- // TODO Get auth token from enforcement module if required.
- None,
- // TODO Get timestamp token from enforcement module if required.
- None,
+ hat.as_ref(),
+ tst.as_ref(),
&mut out_params,
&mut output,
)),
@@ -370,43 +360,6 @@
Ok(())
}
- /// Based on the authorization information stored in the operation during create_operation(),
- /// and any previous calls to update(), this function returns appropriate auth token and
- /// timestamp token to be passed to keymint.
- /// Note that the call to the global enforcement object happens only during the first call to
- /// update or if finish() is called right after create_opertation.
- fn handle_authorization<'a>(
- auth_token_handler: &'a mut AuthTokenHandler,
- key_params: Option<&Vec<KeyParameter>>,
- op_challenge: Option<&OperationChallenge>,
- ) -> Result<(Option<&'a HardwareAuthToken>, Option<&'a TimeStampToken>)> {
- // keystore performs authorization only if key parameters have been loaded during
- // create_operation()
- if let Some(key_parameters) = key_params {
- match *auth_token_handler {
- // this variant is found only in a first call to update or if finish is called
- // right after create_operation.
- AuthTokenHandler::OpAuthRequired => {
- *auth_token_handler = ENFORCEMENTS
- .authorize_update_or_finish(key_parameters.as_slice(), op_challenge)
- .context("In handle_authorization.")?;
- Ok((auth_token_handler.get_auth_token(), None))
- }
- // this variant is found only in a first call to update or if finish is called
- // right after create_operation.
- AuthTokenHandler::Channel(_)|
- // this variant is found in every subsequent call to update/finish,
- // unless the authorization is not required for the key
- AuthTokenHandler::Token(_, _) => {
- auth_token_handler.retrieve_auth_and_timestamp_tokens()
- }
- _ => Ok((None, None))
- }
- } else {
- Ok((None, None))
- }
- }
-
/// 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>>> {
@@ -420,21 +373,20 @@
let km_op: Box<dyn IKeyMintOperation> =
self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
- let mut auth_handler = self.auth_token_handler.lock().unwrap();
- let (auth_token_for_km, timestamp_token_for_km) = Self::handle_authorization(
- &mut auth_handler,
- self.key_params.as_ref(),
- self.op_challenge.as_ref(),
- )
- .context("In update.")?;
+ let (hat, tst) = self
+ .auth_info
+ .lock()
+ .unwrap()
+ .get_auth_tokens()
+ .context("In update: Trying to get auth tokens.")?;
self.update_outcome(
&mut *outcome,
map_km_error(km_op.update(
None,
Some(input),
- auth_token_for_km,
- timestamp_token_for_km,
+ hat.as_ref(),
+ tst.as_ref(),
&mut out_params,
&mut output,
)),
@@ -467,13 +419,12 @@
let km_op: Box<dyn IKeyMintOperation> =
self.km_op.get_interface().context("In finish: Failed to get KeyMintOperation.")?;
- let mut auth_handler = self.auth_token_handler.lock().unwrap();
- let (auth_token_for_km, timestamp_token_for_km) = Self::handle_authorization(
- &mut auth_handler,
- self.key_params.as_ref(),
- self.op_challenge.as_ref(),
- )
- .context("In finish.")?;
+ let (hat, tst) = self
+ .auth_info
+ .lock()
+ .unwrap()
+ .get_auth_tokens()
+ .context("In finish: Trying to get auth tokens.")?;
let output = self
.update_outcome(
@@ -482,8 +433,8 @@
None,
input,
signature,
- auth_token_for_km,
- timestamp_token_for_km,
+ hat.as_ref(),
+ tst.as_ref(),
&mut out_params,
)),
)
@@ -546,9 +497,7 @@
&self,
km_op: Box<dyn IKeyMintOperation>,
owner: u32,
- auth_token_handler: AuthTokenHandler,
- key_params: Option<Vec<KeyParameter>>,
- op_challenge: Option<OperationChallenge>,
+ auth_info: AuthInfo,
) -> 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.");
@@ -561,26 +510,12 @@
s.upgrade().is_none()
}) {
Some(free_slot) => {
- let new_op = Arc::new(Operation::new(
- index - 1,
- km_op,
- owner,
- auth_token_handler,
- key_params,
- op_challenge,
- ));
+ let new_op = Arc::new(Operation::new(index - 1, km_op, owner, auth_info));
*free_slot = Arc::downgrade(&new_op);
new_op
}
None => {
- let new_op = Arc::new(Operation::new(
- operations.len(),
- km_op,
- owner,
- auth_token_handler,
- key_params,
- op_challenge,
- ));
+ let new_op = Arc::new(Operation::new(operations.len(), km_op, owner, auth_info));
operations.push(Arc::downgrade(&new_op));
new_op
}
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 943f69f..7f41ae8 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -17,20 +17,19 @@
//! This crate implements the IKeystoreSecurityLevel interface.
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, HardwareAuthToken::HardwareAuthToken,
- HardwareAuthenticatorType::HardwareAuthenticatorType, IKeyMintDevice::IKeyMintDevice,
- KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat, KeyParameter::KeyParameter,
- KeyParameterValue::KeyParameterValue, SecurityLevel::SecurityLevel, Tag::Tag,
+ Algorithm::Algorithm, HardwareAuthenticatorType::HardwareAuthenticatorType,
+ IKeyMintDevice::IKeyMintDevice, KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat,
+ KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue, SecurityLevel::SecurityLevel,
+ Tag::Tag,
};
use android_system_keystore2::aidl::android::system::keystore2::{
AuthenticatorSpec::AuthenticatorSpec, CreateOperationResponse::CreateOperationResponse,
Domain::Domain, IKeystoreOperation::IKeystoreOperation,
IKeystoreSecurityLevel::BnKeystoreSecurityLevel,
IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
- KeyMetadata::KeyMetadata, KeyParameters::KeyParameters, OperationChallenge::OperationChallenge,
+ KeyMetadata::KeyMetadata, KeyParameters::KeyParameters,
};
-use crate::auth_token_handler::AuthTokenHandler;
use crate::globals::ENFORCEMENTS;
use crate::key_parameter::KeyParameter as KsKeyParam;
use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
@@ -223,34 +222,19 @@
},
)?;
- let mut auth_token_for_km: &HardwareAuthToken = &Default::default();
- let mut auth_token_handler = AuthTokenHandler::NoAuthRequired;
+ let (immediate_hat, mut auth_info) = ENFORCEMENTS
+ .authorize_create(
+ purpose,
+ key_parameters.as_deref(),
+ operation_parameters,
+ // TODO b/178222844 Replace this with the configuration returned by
+ // KeyMintDevice::getHardwareInfo.
+ // For now we assume that strongbox implementations need secure timestamps.
+ self.security_level == SecurityLevel::STRONGBOX,
+ )
+ .context("In create_operation.")?;
- // keystore performs authorizations only if the key parameters are loaded above
- if let Some(ref key_params) = key_parameters {
- // Note: although currently only one operation parameter is checked in authorizing the
- // operation, the whole operation_parameter vector is converted into the internal
- // representation of key parameter because we might need to sanitize operation
- // parameters (b/175792701)
- let mut op_params: Vec<KsKeyParam> = Vec::new();
- for op_param in operation_parameters.iter() {
- op_params.push(KsKeyParam::new(op_param.into(), self.security_level));
- }
- // authorize the operation, and receive an AuthTokenHandler, if authorized, else
- // propagate the error
- auth_token_handler = ENFORCEMENTS
- .authorize_create(
- purpose,
- key_params.as_slice(),
- op_params.as_slice(),
- self.security_level,
- )
- .context("In create_operation.")?;
- // if an auth token was found, pass it to keymint
- if let Some(auth_token) = auth_token_handler.get_auth_token() {
- auth_token_for_km = auth_token;
- }
- }
+ let immediate_hat = immediate_hat.unwrap_or_default();
let km_dev: Box<dyn IKeyMintDevice> = self
.keymint
@@ -268,7 +252,7 @@
purpose,
blob,
&operation_parameters,
- auth_token_for_km,
+ &immediate_hat,
)) {
Err(Error::Km(ErrorCode::TOO_MANY_OPERATIONS)) => {
self.operation_db.prune(caller_uid)?;
@@ -280,35 +264,11 @@
)
.context("In create_operation: Failed to begin operation.")?;
- let mut operation_challenge: Option<OperationChallenge> = None;
-
- // take actions based on the authorization decision (if any) received via auth token handler
- match auth_token_handler {
- AuthTokenHandler::OpAuthRequired => {
- operation_challenge =
- Some(OperationChallenge { challenge: begin_result.challenge });
- ENFORCEMENTS.insert_to_op_auth_map(begin_result.challenge);
- }
- AuthTokenHandler::TimestampRequired(auth_token) => {
- //request a timestamp token, given the auth token and the challenge
- auth_token_handler = ENFORCEMENTS
- .request_timestamp_token(
- auth_token,
- OperationChallenge { challenge: begin_result.challenge },
- )
- .context("In create_operation.")?;
- }
- _ => {}
- }
+ let operation_challenge = auth_info.finalize_create_authorization(begin_result.challenge);
let operation = match begin_result.operation {
Some(km_op) => {
- let mut op_challenge_copy: Option<OperationChallenge> = None;
- if let Some(ref op_challenge) = operation_challenge {
- op_challenge_copy = Some(OperationChallenge{challenge: op_challenge.challenge});
- }
- self.operation_db.create_operation(km_op, caller_uid,
- auth_token_handler, key_parameters, op_challenge_copy)
+ self.operation_db.create_operation(km_op, caller_uid, auth_info)
},
None => return Err(Error::sys()).context("In create_operation: Begin operation returned successfully, but did not return a valid operation."),
};
@@ -319,8 +279,6 @@
.into_interface()
.context("In create_operation: Failed to create IKeystoreOperation.")?;
- // 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(CreateOperationResponse {
iOperation: Some(op_binder),
operationChallenge: operation_challenge,