Implement and integrate the verification token handler.
This CL implements a background task handler which for now, runs
a thread to retrieve verification tokens. This CL also integrates
this handler with the enforcement module, in order to allow operations
to receive verification tokens.
Bug: 171503362, 171503128
Test: TBD
Change-Id: I2ed0742043095dafb3b5cb7581ca3a2a70929ecc
diff --git a/keystore2/src/background_task_handler.rs b/keystore2/src/background_task_handler.rs
new file mode 100644
index 0000000..fbb6778
--- /dev/null
+++ b/keystore2/src/background_task_handler.rs
@@ -0,0 +1,144 @@
+// 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, IKeyMintDevice::IKeyMintDevice,
+ SecurityLevel::SecurityLevel, VerificationToken::VerificationToken,
+};
+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};
+/// This is the struct encapsulating the thread which handles background tasks such as
+/// obtaining verification 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, VerificationToken)>)),
+ ///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 verification 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.",
+ ) {
+ // TODO: call the timestamp service/old TEE keymaster to get
+ // timestamp/verification tokens and pass it down the sender that is
+ // coupled with a particular operation's receiver.
+ // If none of the services are available, pass the authtoken and a default
+ // verification token down the channel.
+ let km_dev: Box<dyn IKeyMintDevice> =
+ crate::globals::get_keymint_device(SecurityLevel::TRUSTED_ENVIRONMENT)
+ .expect("A TEE Keymint must be present.")
+ .get_interface()
+ .expect("Fatal: The keymint device does not implement IKeyMintDevice.");
+ let result = km_dev.verifyAuthorization(op_challenge.challenge, &auth_token);
+ match result {
+ Ok(verification_token) => {
+ // this can fail if the operation is dropped and hence the channel
+ // is hung up.
+ op_sender.send((auth_token, verification_token)).unwrap_or_else(|e| {
+ error!(
+ "In background task handler thread. Failed to send
+ verification token to operation {} due to error {:?}.",
+ op_challenge.challenge, e
+ )
+ });
+ }
+ Err(e) => {
+ // log error
+ error!(
+ "In background task handler thread. Failed to receive
+ verification token for operation {} due to error {:?}.",
+ op_challenge.challenge, e
+ );
+ // send default verification token
+ // this can fail if the operation is dropped and the channel is
+ // hung up.
+ op_sender.send((auth_token, VerificationToken::default())).unwrap_or_else(
+ |e| {
+ error!(
+ "In background task handler thread. Failed to send default
+ verification 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);
+ });
+ }
+ }
+}