Merge "Remove custom AndroidTest.xml"
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index bd08bc8..cf37fca 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -19,7 +19,9 @@
rustlibs: [
"android.hardware.security.keymint-rust",
+ "android.hardware.security.secureclock-rust",
"android.security.apc-rust",
+ "android.security.authorization-rust",
"android.security.compat-rust",
"android.system.keystore2-rust",
"libanyhow",
@@ -46,10 +48,11 @@
auto_gen_config: true,
rustlibs: [
"android.hardware.security.keymint-rust",
+ "android.hardware.security.secureclock-rust",
"android.security.apc-rust",
+ "android.security.authorization-rust",
"android.security.compat-rust",
"android.system.keystore2-rust",
- "android.hardware.security.keymint-rust",
"libandroid_logger",
"libanyhow",
"libbinder_rs",
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
index 0d05dfe..fac36e5 100644
--- a/keystore2/aidl/Android.bp
+++ b/keystore2/aidl/Android.bp
@@ -28,9 +28,12 @@
}
aidl_interface {
- name: "android.security.authorizations",
- srcs: [ "android/security/authorizations/*.aidl" ],
- imports: [ "android.hardware.security.keymint" ],
+ name: "android.security.authorization",
+ srcs: [ "android/security/authorization/*.aidl" ],
+ imports: [
+ "android.hardware.security.keymint",
+ "android.hardware.security.secureclock",
+ ],
unstable: true,
backend: {
java: {
@@ -62,7 +65,11 @@
aidl_interface {
name: "android.security.compat",
srcs: [ "android/security/compat/*.aidl" ],
- imports: [ "android.hardware.security.keymint" ],
+ imports: [
+ "android.hardware.security.keymint",
+ "android.hardware.security.secureclock",
+ "android.hardware.security.sharedsecret",
+ ],
unstable: true,
backend: {
java: {
diff --git a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
new file mode 100644
index 0000000..df64401
--- /dev/null
+++ b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
@@ -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.
+
+package android.security.authorization;
+
+import android.hardware.security.keymint.HardwareAuthToken;
+import android.security.authorization.LockScreenEvent;
+
+// TODO: mark the interface with @SensitiveData when the annotation is ready (b/176110256).
+
+/**
+ * IKeystoreAuthorization interface exposes the methods for other system components to
+ * provide keystore with the information required to enforce authorizations on key usage.
+ */
+interface IKeystoreAuthorization {
+
+ /**
+ * Allows the Android authenticators to hand over an auth token to Keystore.
+ * Callers require 'AddAuth' permission.
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'AddAuth' permission.
+ * `ResponseCode::SYSTEM_ERROR` - if failed to store the auth token in the database or if failed
+ * to add the auth token to the operation, if it is a per-op auth token.
+ *
+ * @param authToken The auth token created by an authenticator, upon user authentication.
+ */
+ void addAuthToken(in HardwareAuthToken authToken);
+
+ /**
+ * Unlocks the keystore for the given user id.
+ * Callers require 'Unlock' permission.
+ * If a password was set, a password must be given on unlock or the operation fails.
+ *
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'Unlock' permission.
+ * `ResponseCode::SYSTEM_ERROR` - if failed to perform lock/unlock operations due to various
+ *
+ * @lockScreenEvent - Indicates what happened.
+ * * LockScreenEvent.UNLOCK if the screen was unlocked.
+ * * LockScreenEvent.LOCK if the screen was locked.
+ *
+ * @param userId - Android user id
+ *
+ * @param password - synthetic password derived by the user denoted by the user id
+ */
+ void onLockScreenEvent(in LockScreenEvent lockScreenEvent, in int userId,
+ in @nullable byte[] password);
+}
diff --git a/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl b/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl
new file mode 100644
index 0000000..877a916
--- /dev/null
+++ b/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl
@@ -0,0 +1,21 @@
+// 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.
+
+package android.security.authorization;
+
+@Backing(type="int")
+enum LockScreenEvent {
+ UNLOCK = 0,
+ LOCK = 1,
+}
diff --git a/keystore2/aidl/android/security/authorizations/IKeystoreAuthorization.aidl b/keystore2/aidl/android/security/authorizations/IKeystoreAuthorization.aidl
deleted file mode 100644
index d3e80ee..0000000
--- a/keystore2/aidl/android/security/authorizations/IKeystoreAuthorization.aidl
+++ /dev/null
@@ -1,35 +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.
-
-package android.security.authorizations;
-
-import android.hardware.security.keymint.HardwareAuthToken;
-
-/**
-* IKeystoreAuthorization interface exposes the methods for other system components to
-* provide keystore with the information required to enforce authorizations on key usage.
-*/
-interface IKeystoreAuthorization {
-
- /**
- * Allows the Android authenticators to hand over an auth token to Keystore.
- * Callers require 'AddAuth' permission.
- * ## Error conditions:
- * `ResponseCode::SYSTEM_ERROR` - if failed to store the auth token in the database or if failed
- * to add the auth token to the operation, if it is a per-op auth token.
- *
- * @param authToken The auth token created by an authenticator, upon user authentication.
- */
- void addAuthToken(in HardwareAuthToken authToken);
-}
diff --git a/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
index 6a72c75..4b6a93b 100644
--- a/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
+++ b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
@@ -18,11 +18,30 @@
import android.hardware.security.keymint.IKeyMintDevice;
import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.secureclock.ISecureClock;
+import android.hardware.security.sharedsecret.ISharedSecret;
/**
+ * The compatibility service allows Keystore 2.0 to connect to legacy wrapper implementations that
+ * it hosts itself without registering them as a service. Keystore 2.0 would not be allowed to
+ * register a HAL service, so instead it registers this service which it can then connect to.
*/
interface IKeystoreCompatService {
/**
+ * Return an implementation of IKeyMintDevice, that it implemented by Keystore 2.0 itself
+ * by means of Keymaster 4.1 or lower.
*/
IKeyMintDevice getKeyMintDevice (SecurityLevel securityLevel);
+
+ /**
+ * Returns an implementation of ISecureClock, that is implemented by Keystore 2.0 itself
+ * by means of Keymaster 4.x.
+ */
+ ISecureClock getSecureClock ();
+
+ /**
+ * Returns an implementation of ISharedSecret, that is implemented by Keystore 2.0 itself
+ * by means of Keymaster 4.x.
+ */
+ ISharedSecret getSharedSecret (SecurityLevel securityLevel);
}
diff --git a/keystore2/src/auth_token_handler.rs b/keystore2/src/auth_token_handler.rs
index 8c10442..bedec50 100644
--- a/keystore2/src/auth_token_handler.rs
+++ b/keystore2/src/auth_token_handler.rs
@@ -13,17 +13,21 @@
// 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 verification token can be expressed during
+//! the different states an auth token and an associated timestamp token can be expressed during
//! the operation life cycle.
-use crate::error::Error as KeystoreError;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- HardwareAuthToken::HardwareAuthToken, VerificationToken::VerificationToken,
+ 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.
+// each variant, as follows.
+#[derive(Debug)]
pub enum AuthTokenHandler {
/// Used when an operation does not require an auth token for authorization.
NoAuthRequired,
@@ -32,52 +36,50 @@
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 verification tokens from the TEE KeyMint.
- VerificationRequired(HardwareAuthToken),
- /// Used to represent the intermediate state between the time a verification token is requested
- /// from the worker thread which obtains verification tokens from the TEE KeyMint and the time
- /// the verification token is received from the worker thread.
- Channel(Receiver<(HardwareAuthToken, VerificationToken)>),
+ /// 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 verification token if
+ /// authorization, after the matching auth token (and the associated timestamp token if
/// required) is found.
- Token(HardwareAuthToken, Option<VerificationToken>),
+ Token(HardwareAuthToken, Option<TimeStampToken>),
}
impl AuthTokenHandler {
- /// Retrieve auth token and verification token from the Token variant of an AuthTokenHandler
- /// instance
- pub fn get_auth_and_verification_tokens(
- &self,
- ) -> Option<(&HardwareAuthToken, &VerificationToken)> {
- if let AuthTokenHandler::Token(auth_token, Some(verification_token)) = self {
- Some((auth_token, verification_token))
- } else {
- None
- }
- }
-
- /// Retrieve auth token from the Token variant of an AuthTokenHandler instance
- pub fn get_auth_token(&self) -> Option<&HardwareAuthToken> {
- if let AuthTokenHandler::Token(auth_token, _) = self {
- Some(auth_token)
- } else {
- None
- }
- }
-
- /// If Channel variant, block on it until the verification token is sent by the
- /// keystore2 worker thread which obtains verification tokens from TEE Keymint
- pub fn receive_verification_token(&mut self) -> Result<()> {
+ /// 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, verification_token) =
- recv.recv().context("In receive_verification_token: sender disconnected.")?;
- *self = AuthTokenHandler::Token(auth_token, Some(verification_token));
- Ok(())
+ 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 {
- Err(KeystoreError::sys()).context(
- "In receive_verification_token: Wrong variant found in the authorization object.",
- )
+ 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/authorization.rs b/keystore2/src/authorization.rs
new file mode 100644
index 0000000..ba27df8
--- /dev/null
+++ b/keystore2/src/authorization.rs
@@ -0,0 +1,135 @@
+// 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 IKeyAuthorization AIDL interface.
+
+use crate::error::Error as KeystoreError;
+use crate::error::map_or_log_err;
+use crate::globals::{DB, ENFORCEMENTS, LEGACY_BLOB_LOADER, SUPER_KEY};
+use crate::permission::KeystorePerm;
+use crate::utils::check_keystore_permission;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType,
+};
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
+ Timestamp::Timestamp,
+};
+use android_security_authorization::binder::{Interface, Result as BinderResult};
+use android_security_authorization:: aidl::android::security::authorization::IKeystoreAuthorization::{
+ BnKeystoreAuthorization, IKeystoreAuthorization,
+};
+use android_security_authorization:: aidl::android::security::authorization::LockScreenEvent::LockScreenEvent;
+use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
+use anyhow::{Context, Result};
+use binder::IBinder;
+
+/// This struct is defined to implement the aforementioned AIDL interface.
+/// As of now, it is an empty struct.
+pub struct AuthorizationManager;
+
+impl AuthorizationManager {
+ /// Create a new instance of Keystore Authorization service.
+ pub fn new_native_binder() -> Result<impl IKeystoreAuthorization> {
+ let result = BnKeystoreAuthorization::new_binder(Self);
+ result.as_binder().set_requesting_sid(true);
+ Ok(result)
+ }
+
+ fn add_auth_token(&self, auth_token: &HardwareAuthToken) -> Result<()> {
+ //check keystore permission
+ check_keystore_permission(KeystorePerm::add_auth()).context("In add_auth_token.")?;
+
+ //TODO: Keymint's HardwareAuthToken aidl needs to implement Copy/Clone
+ 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(),
+ };
+ ENFORCEMENTS.add_auth_token(auth_token_copy)?;
+ Ok(())
+ }
+
+ fn on_lock_screen_event(
+ &self,
+ lock_screen_event: LockScreenEvent,
+ user_id: i32,
+ password: Option<&[u8]>,
+ ) -> Result<()> {
+ match (lock_screen_event, password) {
+ (LockScreenEvent::UNLOCK, Some(user_password)) => {
+ //This corresponds to the unlock() method in legacy keystore API.
+ //check permission
+ check_keystore_permission(KeystorePerm::unlock())
+ .context("In on_lock_screen_event: Unlock with password.")?;
+ ENFORCEMENTS.set_device_locked(user_id, false);
+ // Unlock super key.
+ DB.with::<_, Result<()>>(|db| {
+ let mut db = db.borrow_mut();
+ //TODO - b/176123105 - Once the user management API is implemented, unlock is
+ //allowed only if the user is added. Then the two tasks handled by the
+ //unlock_user_key will be split into two methods. For now, unlock_user_key
+ //method is used as it is, which created a super key for the user if one does
+ //not exists, in addition to unlocking the existing super key of the user/
+ SUPER_KEY.unlock_user_key(
+ user_id as u32,
+ user_password,
+ &mut db,
+ &LEGACY_BLOB_LOADER,
+ )?;
+ Ok(())
+ })
+ .context("In on_lock_screen_event.")?;
+
+ Ok(())
+ }
+ (LockScreenEvent::UNLOCK, None) => {
+ check_keystore_permission(KeystorePerm::unlock())
+ .context("In on_lock_screen_event: Unlock.")?;
+ ENFORCEMENTS.set_device_locked(user_id, false);
+ Ok(())
+ }
+ (LockScreenEvent::LOCK, None) => {
+ check_keystore_permission(KeystorePerm::lock())
+ .context("In on_lock_screen_event: Lock")?;
+ ENFORCEMENTS.set_device_locked(user_id, true);
+ Ok(())
+ }
+ _ => {
+ // Any other combination is not supported.
+ Err(KeystoreError::Rc(ResponseCode::INVALID_ARGUMENT))
+ .context("In on_lock_screen_event: Unknown event.")
+ }
+ }
+ }
+}
+
+impl Interface for AuthorizationManager {}
+
+impl IKeystoreAuthorization for AuthorizationManager {
+ fn addAuthToken(&self, auth_token: &HardwareAuthToken) -> BinderResult<()> {
+ map_or_log_err(self.add_auth_token(auth_token), Ok)
+ }
+
+ fn onLockScreenEvent(
+ &self,
+ lock_screen_event: LockScreenEvent,
+ user_id: i32,
+ password: Option<&[u8]>,
+ ) -> BinderResult<()> {
+ map_or_log_err(self.on_lock_screen_event(lock_screen_event, user_id, password), Ok)
+ }
+}
diff --git a/keystore2/src/background_task_handler.rs b/keystore2/src/background_task_handler.rs
new file mode 100644
index 0000000..b039506
--- /dev/null
+++ b/keystore2/src/background_task_handler.rs
@@ -0,0 +1,148 @@
+// 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 df1c24c..e25fea2 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -12,9 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//TODO: remove this in the future CLs in the stack.
-#![allow(dead_code)]
-
//! This is the Keystore 2.0 database module.
//! The database module provides a connection to the backing SQLite store.
//! We have two databases one for persistent key blob storage and one for
@@ -59,14 +56,17 @@
use std::{convert::TryFrom, convert::TryInto, time::SystemTimeError};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType,
- SecurityLevel::SecurityLevel,
+ ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
+ HardwareAuthenticatorType::HardwareAuthenticatorType, SecurityLevel::SecurityLevel,
+};
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
+ Timestamp::Timestamp,
};
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, KeyDescriptor::KeyDescriptor,
};
-
use lazy_static::lazy_static;
+use log::error;
#[cfg(not(test))]
use rand::prelude::random;
use rusqlite::{
@@ -75,7 +75,7 @@
types::FromSqlResult,
types::ToSqlOutput,
types::{FromSqlError, Value, ValueRef},
- Connection, OptionalExtension, ToSql, Transaction, TransactionBehavior, NO_PARAMS,
+ Connection, Error, OptionalExtension, ToSql, Transaction, TransactionBehavior, NO_PARAMS,
};
use std::{
collections::{HashMap, HashSet},
@@ -585,12 +585,6 @@
})
}
- fn is_newer_than(&self, other: &AuthTokenEntry) -> bool {
- // NOTE: Although in legacy keystore both timestamp and time_received are involved in this
- // check, we decided to only consider time_received in keystore2 code.
- self.time_received.seconds() > other.time_received.seconds()
- }
-
/// Returns the auth token wrapped by the AuthTokenEntry
pub fn get_auth_token(self) -> HardwareAuthToken {
self.auth_token
@@ -705,7 +699,9 @@
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(())
}
@@ -1686,6 +1682,121 @@
.context("In insert_auth_token: failed to insert auth token into the database")?;
Ok(())
}
+
+ /// find the auth token entry issued for a time-out token
+ pub fn find_timed_auth_token_entry(
+ &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)
+ });
+
+ // 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.")
+ }
+ }
+
+ /// 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(
+ HardwareAuthToken {
+ challenge: row.get(1)?,
+ userId: row.get(2)?,
+ authenticatorId: row.get(3)?,
+ authenticatorType: HardwareAuthenticatorType(row.get(4)?),
+ timestamp: Timestamp { milliSeconds: row.get(5)? },
+ 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)
+ }
+
+ /// 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.")?;
+ Ok(())
+ }
+
+ /// 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(
+ "UPDATE perboot.metadata SET value = ? WHERE key = ?;",
+ params![last_off_body, "last_off_body"],
+ )
+ .context("In update_last_off_body: failed to update.")?;
+ Ok(())
+ }
+
+ /// 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
+ }
}
#[cfg(test)]
@@ -1702,15 +1813,17 @@
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
HardwareAuthToken::HardwareAuthToken,
HardwareAuthenticatorType::HardwareAuthenticatorType as kmhw_authenticator_type,
+ };
+ use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
Timestamp::Timestamp,
};
- use rusqlite::Error;
use rusqlite::NO_PARAMS;
+ use rusqlite::{Error, TransactionBehavior};
use std::cell::RefCell;
use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::Arc;
use std::thread;
- use std::time::SystemTime;
+ use std::time::{Duration, SystemTime};
fn new_test_db() -> Result<KeystoreDB> {
let conn = KeystoreDB::make_connection("file::memory:", "file::memory:")?;
@@ -2896,4 +3009,21 @@
result
})
}
+
+ #[test]
+ fn test_last_off_body() -> Result<()> {
+ let mut db = new_test_db()?;
+ KeystoreDB::insert_last_off_body(&db.conn, MonotonicRawTime::now())?;
+ let tx = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?;
+ let last_off_body_1 = KeystoreDB::get_last_off_body(&tx)?;
+ tx.commit()?;
+ let one_second = Duration::from_secs(1);
+ thread::sleep(one_second);
+ db.update_last_off_body(MonotonicRawTime::now())?;
+ let tx2 = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?;
+ let last_off_body_2 = KeystoreDB::get_last_off_body(&tx2)?;
+ tx2.commit()?;
+ assert!(last_off_body_1.seconds() < last_off_body_2.seconds());
+ Ok(())
+ }
}
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 473686c..4ff3950 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -12,12 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//TODO: remove this after implementing the methods.
-#![allow(dead_code)]
-
//! 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;
@@ -25,11 +23,15 @@
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, Timestamp::Timestamp,
+ SecurityLevel::SecurityLevel, Tag::Tag,
+};
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
+ TimeStampToken::TimeStampToken, Timestamp::Timestamp,
};
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::time::SystemTime;
@@ -41,25 +43,40 @@
// 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>>>,
}
impl Enforcements {
- /// Creates an enforcement object with the two data structures it holds.
+ /// Creates an enforcement object with the two data structures it holds and the sender as None.
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),
}
}
+ /// 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>,
+ op_challenge: Option<&OperationChallenge>,
) -> Result<AuthTokenHandler> {
let mut user_auth_type: Option<HardwareAuthenticatorType> = None;
let mut user_secure_ids = Vec::<i64>::new();
@@ -137,8 +154,8 @@
/// With regard to auth tokens, the following steps are taken:
/// 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
- /// VerificationRequired variant of the AuthTokenHandler with the found auth token to signal
- /// the operation that it may need to obtain a verification token from TEE KeyMint.
+ /// 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
@@ -150,8 +167,7 @@
purpose: KeyPurpose,
key_params: &[KeyParameter],
op_params: &[KeyParameter],
- // security_level will be used in the next CL
- _security_level: SecurityLevel,
+ security_level: SecurityLevel,
) -> Result<AuthTokenHandler> {
match purpose {
// Allow SIGN, DECRYPT for both symmetric and asymmetric keys.
@@ -188,11 +204,14 @@
// 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 auth_type: Option<HardwareAuthenticatorType> = None;
+ let mut user_auth_type: Option<HardwareAuthenticatorType> = None;
let mut no_auth_required: bool = false;
let mut caller_nonce_allowed = false;
let mut user_id: i32 = -1;
let mut user_secure_ids = Vec::<i64>::new();
+ let mut key_time_out: Option<i64> = None;
+ let mut allow_while_on_body = false;
+ let mut unlocked_device_required = false;
// iterate through key parameters, recording information we need for authorization
// enforcements later, or enforcing authorizations in place, where applicable
@@ -201,11 +220,12 @@
KeyParameterValue::NoAuthRequired => {
no_auth_required = true;
}
- KeyParameterValue::AuthTimeout(_) => {
+ KeyParameterValue::AuthTimeout(t) => {
is_time_out_key = true;
+ key_time_out = Some(*t as i64);
}
KeyParameterValue::HardwareAuthenticatorType(a) => {
- auth_type = Some(*a);
+ user_auth_type = Some(*a);
}
KeyParameterValue::KeyPurpose(p) => {
// Note: if there can be multiple KeyPurpose key parameters (TODO: confirm this),
@@ -248,12 +268,10 @@
user_id = *u;
}
KeyParameterValue::UnlockedDeviceRequired => {
- // check the device locked status. If locked, operations on the key are not
- // allowed.
- if self.is_device_locked(user_id) {
- return Err(KeystoreError::Km(Ec::DEVICE_LOCKED))
- .context("In authorize_create: device is locked.");
- }
+ unlocked_device_required = true;
+ }
+ KeyParameterValue::AllowWhileOnBody => {
+ allow_while_on_body = true;
}
// NOTE: as per offline discussion, sanitizing key parameters and rejecting
// create operation if any non-allowed tags are present, is not done in
@@ -279,8 +297,8 @@
}
// if either of auth_type or secure_id is present and the other is not present, return error
- if (auth_type.is_some() && user_secure_ids.is_empty())
- || (auth_type.is_none() && !user_secure_ids.is_empty())
+ 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(
"In authorize_create: Auth required, but either auth type or secure ids
@@ -298,14 +316,48 @@
);
}
+ 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))
+ .context("In authorize_create: device is locked.");
+ }
+ }
+
if !user_secure_ids.is_empty() {
- // per op auth token
+ // key requiring authentication per operation
if !is_time_out_key {
return Ok(AuthTokenHandler::OpAuthRequired);
} else {
- //time out token
- // TODO: retrieve it from the database
- // - in an upcoming CL
+ // 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));
+ }
}
}
@@ -377,6 +429,43 @@
.context("In add_auth_token.")?;
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))
+ }
}
impl Default for Enforcements {
@@ -385,4 +474,21 @@
}
}
+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 18bed9a..7ceff26 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -17,7 +17,10 @@
//! 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::{
@@ -78,9 +81,21 @@
pub static ref SUPER_KEY: SuperKeyManager = Default::default();
/// Map of KeyMint devices.
static ref KEY_MINT_DEVICES: Mutex<HashMap<SecurityLevel, Asp>> = Default::default();
+ /// Timestamp service.
+ static ref TIME_STAMP_DEVICE: Mutex<Option<Asp>> = Default::default();
/// 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.
+ 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(
+ &std::env::current_dir().expect("Could not get the current working directory."));
}
static KEYMINT_SERVICE_NAME: &str = "android.hardware.security.keymint.IKeyMintDevice";
@@ -132,11 +147,58 @@
if let Some(dev) = devices_map.get(&security_level) {
Ok(dev.clone())
} else {
- let dev = connect_keymint(security_level).map_err(|e| {
- anyhow::anyhow!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
- .context(format!("In get_keymint_device: {:?}", e))
- })?;
+ let dev = connect_keymint(security_level).context("In get_keymint_device.")?;
devices_map.insert(security_level, dev.clone());
Ok(dev)
}
}
+
+static TIME_STAMP_SERVICE_NAME: &str = "android.hardware.security.secureclock.ISecureClock";
+
+/// Make a new connection to a secure clock service.
+/// If no native SecureClock device can be found brings up the compatibility service and attempts
+/// to connect to the legacy wrapper.
+fn connect_secureclock() -> Result<Asp> {
+ let secureclock = map_binder_status_code(binder::get_interface(TIME_STAMP_SERVICE_NAME))
+ .context("In connect_secureclock: Trying to connect to genuine secure clock service.")
+ .or_else(|e| {
+ match e.root_cause().downcast_ref::<Error>() {
+ Some(Error::BinderTransaction(StatusCode::NAME_NOT_FOUND)) => {
+ // This is a no-op if it was called before.
+ keystore2_km_compat::add_keymint_device_service();
+
+ let keystore_compat_service: Box<dyn IKeystoreCompatService> =
+ map_binder_status_code(binder::get_interface("android.security.compat"))
+ .context(
+ "In connect_secureclock: Trying to connect to compat service.",
+ )?;
+
+ // Legacy secure clock services were only implemented by TEE.
+ map_binder_status(keystore_compat_service.getSecureClock())
+ .map_err(|e| match e {
+ Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
+ Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
+ }
+ e => e,
+ })
+ .context("In connect_secureclock: Trying to get Legacy wrapper.")
+ }
+ _ => Err(e),
+ }
+ })?;
+
+ Ok(Asp::new(secureclock.as_binder()))
+}
+
+/// Get the timestamp service that verifies auth token timeliness towards security levels with
+/// different clocks.
+pub fn get_timestamp_service() -> Result<Asp> {
+ let mut ts_device = TIME_STAMP_DEVICE.lock().unwrap();
+ if let Some(dev) = &*ts_device {
+ Ok(dev.clone())
+ } else {
+ let dev = connect_secureclock().context("In get_timestamp_service.")?;
+ *ts_device = Some(dev.clone());
+ Ok(dev)
+ }
+}
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index 7391f37..c75cfc8 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -16,12 +16,17 @@
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";
+static AUTHORIZATION_SERVICE_NAME: &str = "android.security.authorization";
/// Keystore 2.0 takes one argument which is a path indicating its designated working directory.
fn main() {
@@ -50,6 +55,14 @@
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();
@@ -67,6 +80,14 @@
panic!("Failed to register service {} because of {:?}.", APC_SERVICE_NAME, e);
});
+ let authorization_service = AuthorizationManager::new_native_binder().unwrap_or_else(|e| {
+ panic!("Failed to create service {} because of {:?}.", AUTHORIZATION_SERVICE_NAME, e);
+ });
+ binder::add_service(AUTHORIZATION_SERVICE_NAME, authorization_service.as_binder())
+ .unwrap_or_else(|e| {
+ panic!("Failed to register service {} because of {:?}.", AUTHORIZATION_SERVICE_NAME, e);
+ });
+
info!("Successfully registered Keystore 2.0 service.");
info!("Joining thread pool now.");
diff --git a/keystore2/src/km_compat/Android.bp b/keystore2/src/km_compat/Android.bp
index 7ca6a63..a5da5a6 100644
--- a/keystore2/src/km_compat/Android.bp
+++ b/keystore2/src/km_compat/Android.bp
@@ -49,6 +49,8 @@
"android.hardware.keymaster@4.0",
"android.hardware.keymaster@4.1",
"android.hardware.security.keymint-unstable-ndk_platform",
+ "android.hardware.security.secureclock-unstable-ndk_platform",
+ "android.hardware.security.sharedsecret-unstable-ndk_platform",
"android.security.compat-ndk_platform",
"android.system.keystore2-ndk_platform",
"libbase",
@@ -66,6 +68,9 @@
name: "libkm_compat_service",
srcs: ["km_compat_service.cpp"],
shared_libs: [
+ "android.hardware.security.keymint-unstable-ndk_platform",
+ "android.hardware.security.secureclock-unstable-ndk_platform",
+ "android.hardware.security.sharedsecret-unstable-ndk_platform",
"android.security.compat-ndk_platform",
"libbinder_ndk",
"libcrypto",
@@ -93,6 +98,8 @@
"android.hardware.keymaster@4.0",
"android.hardware.keymaster@4.1",
"android.hardware.security.keymint-unstable-ndk_platform",
+ "android.hardware.security.secureclock-unstable-ndk_platform",
+ "android.hardware.security.sharedsecret-unstable-ndk_platform",
"android.security.compat-ndk_platform",
"android.system.keystore2-ndk_platform",
"libbase",
diff --git a/keystore2/src/km_compat/certificate_test.cpp b/keystore2/src/km_compat/certificate_test.cpp
index 9307569..d6bece7 100644
--- a/keystore2/src/km_compat/certificate_test.cpp
+++ b/keystore2/src/km_compat/certificate_test.cpp
@@ -49,14 +49,12 @@
if (!device) {
return ScopedAStatus::fromStatus(STATUS_NAME_NOT_FOUND);
}
- ByteArray blob;
- KeyCharacteristics characteristics;
- std::vector<Certificate> certChain;
- auto status = device->generateKey(keyParams, &blob, &characteristics, &certChain);
+ KeyCreationResult creationResult;
+ auto status = device->generateKey(keyParams, &creationResult);
if (!status.isOk()) {
return status;
}
- return certChain;
+ return creationResult.certificateChain;
}
static void ensureCertChainSize(const std::variant<std::vector<Certificate>, ScopedAStatus>& result,
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index c9af80d..d965922 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -35,12 +35,12 @@
using ::aidl::android::hardware::security::keymint::Digest;
using ::aidl::android::hardware::security::keymint::PaddingMode;
using ::aidl::android::hardware::security::keymint::Tag;
-using ::aidl::android::hardware::security::keymint::VerificationToken;
using ::aidl::android::system::keystore2::ResponseCode;
using ::android::hardware::hidl_vec;
using ::android::hardware::keymaster::V4_0::TagType;
using ::android::hidl::manager::V1_2::IServiceManager;
using V4_0_HardwareAuthToken = ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+using V4_0_HmacSharingParameters = ::android::hardware::keymaster::V4_0::HmacSharingParameters;
using V4_0_KeyCharacteristics = ::android::hardware::keymaster::V4_0::KeyCharacteristics;
using V4_0_KeyFormat = ::android::hardware::keymaster::V4_0::KeyFormat;
using V4_0_KeyParameter = ::android::hardware::keymaster::V4_0::KeyParameter;
@@ -73,12 +73,13 @@
return kps;
}
-static KeyCharacteristics
-convertKeyCharacteristicsFromLegacy(const V4_0_KeyCharacteristics& legacyKc) {
+static std::vector<KeyCharacteristics>
+convertKeyCharacteristicsFromLegacy(KeyMintSecurityLevel securityLevel,
+ const V4_0_KeyCharacteristics& legacyKc) {
KeyCharacteristics kc;
- kc.softwareEnforced = convertKeyParametersFromLegacy(legacyKc.softwareEnforced);
- kc.hardwareEnforced = convertKeyParametersFromLegacy(legacyKc.hardwareEnforced);
- return kc;
+ kc.securityLevel = securityLevel;
+ kc.authorizations = convertKeyParametersFromLegacy(legacyKc.hardwareEnforced);
+ return {kc};
}
static V4_0_KeyFormat convertKeyFormatToLegacy(const KeyFormat& kf) {
@@ -98,16 +99,32 @@
return legacyAt;
}
-static V4_0_VerificationToken convertVerificationTokenToLegacy(const VerificationToken& vt) {
+static V4_0_VerificationToken convertTimestampTokenToLegacy(const TimeStampToken& tst) {
V4_0_VerificationToken legacyVt;
- legacyVt.challenge = vt.challenge;
- legacyVt.timestamp = vt.timestamp.milliSeconds;
- legacyVt.securityLevel =
- static_cast<::android::hardware::keymaster::V4_0::SecurityLevel>(vt.securityLevel);
- legacyVt.mac = vt.mac;
+ legacyVt.challenge = tst.challenge;
+ legacyVt.timestamp = tst.timestamp.milliSeconds;
+ // Legacy verification tokens were always minted by TEE.
+ legacyVt.securityLevel = V4_0::SecurityLevel::TRUSTED_ENVIRONMENT;
+ legacyVt.mac = tst.mac;
return legacyVt;
}
+static V4_0_HmacSharingParameters
+convertSharedSecretParameterToLegacy(const SharedSecretParameters& ssp) {
+ V4_0_HmacSharingParameters legacyHsp;
+ legacyHsp.seed = ssp.seed;
+ std::copy(ssp.nonce.begin(), ssp.nonce.end(), legacyHsp.nonce.data());
+ return legacyHsp;
+}
+
+static std::vector<V4_0_HmacSharingParameters>
+convertSharedSecretParametersToLegacy(const std::vector<SharedSecretParameters>& legacySsps) {
+ std::vector<V4_0_HmacSharingParameters> ssps(legacySsps.size());
+ std::transform(legacySsps.begin(), legacySsps.end(), ssps.begin(),
+ convertSharedSecretParameterToLegacy);
+ return ssps;
+}
+
void OperationSlots::setNumFreeSlots(uint8_t numFreeSlots) {
std::lock_guard<std::mutex> lock(mNumFreeSlotsMutex);
mNumFreeSlots = numFreeSlots;
@@ -140,8 +157,10 @@
// TODO: What do I do about the version number? Is it the version of the device I get?
auto result = mDevice->getHardwareInfo([&](auto securityLevel, const auto& keymasterName,
const auto& keymasterAuthorName) {
- _aidl_return->securityLevel =
+ securityLevel_ =
static_cast<::aidl::android::hardware::security::keymint::SecurityLevel>(securityLevel);
+
+ _aidl_return->securityLevel = securityLevel_;
_aidl_return->keyMintName = keymasterName;
_aidl_return->keyMintAuthorName = keymasterAuthorName;
});
@@ -152,48 +171,38 @@
return ScopedAStatus::ok();
}
-// We're not implementing this.
-ScopedAStatus KeyMintDevice::verifyAuthorization(int64_t in_challenge ATTRIBUTE_UNUSED,
- const HardwareAuthToken& in_token ATTRIBUTE_UNUSED,
- VerificationToken* _aidl_return ATTRIBUTE_UNUSED) {
- return ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(V4_0_ErrorCode::UNIMPLEMENTED));
-}
-
ScopedAStatus KeyMintDevice::addRngEntropy(const std::vector<uint8_t>& in_data) {
V4_0_ErrorCode errorCode = mDevice->addRngEntropy(in_data);
return convertErrorCode(errorCode);
}
ScopedAStatus KeyMintDevice::generateKey(const std::vector<KeyParameter>& in_keyParams,
- ByteArray* out_generatedKeyBlob,
- KeyCharacteristics* out_generatedKeyCharacteristics,
- std::vector<Certificate>* out_outCertChain) {
+ KeyCreationResult* out_creationResult) {
auto legacyKeyParams = convertKeyParametersToLegacy(in_keyParams);
V4_0_ErrorCode errorCode;
auto result = mDevice->generateKey(
legacyKeyParams, [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
const V4_0_KeyCharacteristics& keyCharacteristics) {
errorCode = error;
- out_generatedKeyBlob->data = keyBlob;
- *out_generatedKeyCharacteristics =
- convertKeyCharacteristicsFromLegacy(keyCharacteristics);
+ out_creationResult->keyBlob = keyBlob;
+ out_creationResult->keyCharacteristics =
+ convertKeyCharacteristicsFromLegacy(securityLevel_, keyCharacteristics);
});
if (!result.isOk()) {
return ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
}
if (errorCode == V4_0_ErrorCode::OK) {
- auto cert = getCertificate(in_keyParams, out_generatedKeyBlob->data);
+ auto cert = getCertificate(in_keyParams, out_creationResult->keyBlob);
if (std::holds_alternative<V4_0_ErrorCode>(cert)) {
auto code = std::get<V4_0_ErrorCode>(cert);
// We return OK in successful cases that do not generate a certificate.
if (code != V4_0_ErrorCode::OK) {
errorCode = code;
- deleteKey(out_generatedKeyBlob->data);
+ deleteKey(out_creationResult->keyBlob);
}
} else {
- *out_outCertChain = std::get<std::vector<Certificate>>(cert);
+ out_creationResult->certificateChain = std::get<std::vector<Certificate>>(cert);
}
}
return convertErrorCode(errorCode);
@@ -202,36 +211,34 @@
ScopedAStatus KeyMintDevice::importKey(const std::vector<KeyParameter>& in_inKeyParams,
KeyFormat in_inKeyFormat,
const std::vector<uint8_t>& in_inKeyData,
- ByteArray* out_outImportedKeyBlob,
- KeyCharacteristics* out_outImportedKeyCharacteristics,
- std::vector<Certificate>* out_outCertChain) {
+ KeyCreationResult* out_creationResult) {
auto legacyKeyParams = convertKeyParametersToLegacy(in_inKeyParams);
auto legacyKeyFormat = convertKeyFormatToLegacy(in_inKeyFormat);
V4_0_ErrorCode errorCode;
- auto result =
- mDevice->importKey(legacyKeyParams, legacyKeyFormat, in_inKeyData,
- [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
- const V4_0_KeyCharacteristics& keyCharacteristics) {
- errorCode = error;
- out_outImportedKeyBlob->data = keyBlob;
- *out_outImportedKeyCharacteristics =
- convertKeyCharacteristicsFromLegacy(keyCharacteristics);
- });
+ auto result = mDevice->importKey(legacyKeyParams, legacyKeyFormat, in_inKeyData,
+ [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
+ const V4_0_KeyCharacteristics& keyCharacteristics) {
+ errorCode = error;
+ out_creationResult->keyBlob = keyBlob;
+ out_creationResult->keyCharacteristics =
+ convertKeyCharacteristicsFromLegacy(
+ securityLevel_, keyCharacteristics);
+ });
if (!result.isOk()) {
return ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
}
if (errorCode == V4_0_ErrorCode::OK) {
- auto cert = getCertificate(in_inKeyParams, out_outImportedKeyBlob->data);
+ auto cert = getCertificate(in_inKeyParams, out_creationResult->keyBlob);
if (std::holds_alternative<V4_0_ErrorCode>(cert)) {
auto code = std::get<V4_0_ErrorCode>(cert);
// We return OK in successful cases that do not generate a certificate.
if (code != V4_0_ErrorCode::OK) {
errorCode = code;
- deleteKey(out_outImportedKeyBlob->data);
+ deleteKey(out_creationResult->keyBlob);
}
} else {
- *out_outCertChain = std::get<std::vector<Certificate>>(cert);
+ out_creationResult->certificateChain = std::get<std::vector<Certificate>>(cert);
}
}
return convertErrorCode(errorCode);
@@ -241,20 +248,19 @@
const std::vector<uint8_t>& in_inWrappedKeyData,
const std::vector<uint8_t>& in_inWrappingKeyBlob, const std::vector<uint8_t>& in_inMaskingKey,
const std::vector<KeyParameter>& in_inUnwrappingParams, int64_t in_inPasswordSid,
- int64_t in_inBiometricSid, ByteArray* out_outImportedKeyBlob,
- KeyCharacteristics* out_outImportedKeyCharacteristics) {
+ int64_t in_inBiometricSid, KeyCreationResult* out_creationResult) {
auto legacyUnwrappingParams = convertKeyParametersToLegacy(in_inUnwrappingParams);
V4_0_ErrorCode errorCode;
- auto result =
- mDevice->importWrappedKey(in_inWrappedKeyData, in_inWrappingKeyBlob, in_inMaskingKey,
- legacyUnwrappingParams, in_inPasswordSid, in_inBiometricSid,
- [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
- const V4_0_KeyCharacteristics& keyCharacteristics) {
- errorCode = error;
- out_outImportedKeyBlob->data = keyBlob;
- *out_outImportedKeyCharacteristics =
- convertKeyCharacteristicsFromLegacy(keyCharacteristics);
- });
+ auto result = mDevice->importWrappedKey(
+ in_inWrappedKeyData, in_inWrappingKeyBlob, in_inMaskingKey, legacyUnwrappingParams,
+ in_inPasswordSid, in_inBiometricSid,
+ [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
+ const V4_0_KeyCharacteristics& keyCharacteristics) {
+ errorCode = error;
+ out_creationResult->keyBlob = keyBlob;
+ out_creationResult->keyCharacteristics =
+ convertKeyCharacteristicsFromLegacy(securityLevel_, keyCharacteristics);
+ });
if (!result.isOk()) {
return ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
@@ -331,13 +337,13 @@
return convertErrorCode(errorCode);
}
-ScopedAStatus
-KeyMintOperation::update(const std::optional<KeyParameterArray>& in_inParams,
- const std::optional<std::vector<uint8_t>>& in_input,
- const std::optional<HardwareAuthToken>& in_inAuthToken,
- const std::optional<VerificationToken>& in_inVerificationToken,
- std::optional<KeyParameterArray>* out_outParams,
- std::optional<ByteArray>* out_output, int32_t* _aidl_return) {
+ScopedAStatus KeyMintOperation::update(const std::optional<KeyParameterArray>& in_inParams,
+ const std::optional<std::vector<uint8_t>>& in_input,
+ const std::optional<HardwareAuthToken>& in_inAuthToken,
+ const std::optional<TimeStampToken>& in_inTimeStampToken,
+ std::optional<KeyParameterArray>* out_outParams,
+ std::optional<ByteArray>* out_output,
+ int32_t* _aidl_return) {
std::vector<V4_0_KeyParameter> legacyParams;
if (in_inParams.has_value()) {
legacyParams = convertKeyParametersToLegacy(in_inParams.value().params);
@@ -348,8 +354,8 @@
authToken = convertAuthTokenToLegacy(in_inAuthToken.value());
}
V4_0_VerificationToken verificationToken;
- if (in_inVerificationToken.has_value()) {
- verificationToken = convertVerificationTokenToLegacy(in_inVerificationToken.value());
+ if (in_inTimeStampToken.has_value()) {
+ verificationToken = convertTimestampTokenToLegacy(in_inTimeStampToken.value());
}
V4_0_ErrorCode errorCode;
auto result = mDevice->update(
@@ -374,14 +380,13 @@
return convertErrorCode(errorCode);
}
-ScopedAStatus
-KeyMintOperation::finish(const std::optional<KeyParameterArray>& in_inParams,
- const std::optional<std::vector<uint8_t>>& in_input,
- const std::optional<std::vector<uint8_t>>& in_inSignature,
- const std::optional<HardwareAuthToken>& in_authToken,
- const std::optional<VerificationToken>& in_inVerificationToken,
- std::optional<KeyParameterArray>* out_outParams,
- std::vector<uint8_t>* _aidl_return) {
+ScopedAStatus KeyMintOperation::finish(const std::optional<KeyParameterArray>& in_inParams,
+ const std::optional<std::vector<uint8_t>>& in_input,
+ const std::optional<std::vector<uint8_t>>& in_inSignature,
+ const std::optional<HardwareAuthToken>& in_authToken,
+ const std::optional<TimeStampToken>& in_inTimeStampToken,
+ std::optional<KeyParameterArray>* out_outParams,
+ std::vector<uint8_t>* _aidl_return) {
V4_0_ErrorCode errorCode;
std::vector<V4_0_KeyParameter> legacyParams;
if (in_inParams.has_value()) {
@@ -394,8 +399,8 @@
authToken = convertAuthTokenToLegacy(in_authToken.value());
}
V4_0_VerificationToken verificationToken;
- if (in_inVerificationToken.has_value()) {
- verificationToken = convertVerificationTokenToLegacy(in_inVerificationToken.value());
+ if (in_inTimeStampToken.has_value()) {
+ verificationToken = convertTimestampTokenToLegacy(in_inTimeStampToken.value());
}
auto result = mDevice->finish(
mOperationHandle, legacyParams, input, signature, authToken, verificationToken,
@@ -429,6 +434,60 @@
}
}
+// SecureClock implementation
+
+ScopedAStatus SecureClock::generateTimeStamp(int64_t in_challenge, TimeStampToken* _aidl_return) {
+ V4_0_ErrorCode errorCode;
+ auto result = mDevice->verifyAuthorization(
+ in_challenge, {}, V4_0_HardwareAuthToken(),
+ [&](V4_0_ErrorCode error, const V4_0_VerificationToken& token) {
+ errorCode = error;
+ _aidl_return->challenge = token.challenge;
+ _aidl_return->timestamp.milliSeconds = token.timestamp;
+ _aidl_return->mac = token.mac;
+ });
+ if (!result.isOk()) {
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+ }
+ return convertErrorCode(errorCode);
+}
+
+// SharedSecret implementation
+
+ScopedAStatus SharedSecret::getSharedSecretParameters(SharedSecretParameters* _aidl_return) {
+ V4_0_ErrorCode errorCode;
+ auto result = mDevice->getHmacSharingParameters(
+ [&](V4_0_ErrorCode error, const V4_0_HmacSharingParameters& params) {
+ errorCode = error;
+ _aidl_return->seed = params.seed;
+ std::copy(params.nonce.data(), params.nonce.data() + params.nonce.elementCount(),
+ std::back_inserter(_aidl_return->nonce));
+ });
+ if (!result.isOk()) {
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+ }
+ return convertErrorCode(errorCode);
+}
+
+ScopedAStatus
+SharedSecret::computeSharedSecret(const std::vector<SharedSecretParameters>& in_params,
+ std::vector<uint8_t>* _aidl_return) {
+ V4_0_ErrorCode errorCode;
+ auto legacyParams = convertSharedSecretParametersToLegacy(in_params);
+ auto result = mDevice->computeSharedHmac(
+ legacyParams, [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& sharingCheck) {
+ errorCode = error;
+ *_aidl_return = sharingCheck;
+ });
+ if (!result.isOk()) {
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+ }
+ return convertErrorCode(errorCode);
+}
+
// Certificate implementation
template <KMV1::Tag tag, KMV1::TagType type>
@@ -607,8 +666,8 @@
std::optional<KeyParameterArray> outParams;
std::optional<ByteArray> outByte;
int32_t status;
- beginResult.operation->update(std::nullopt, dataVec, HardwareAuthToken(),
- VerificationToken(), &outParams, &outByte, &status);
+ beginResult.operation->update(std::nullopt, dataVec, std::nullopt, std::nullopt,
+ &outParams, &outByte, &status);
if (!status) {
return std::vector<uint8_t>();
}
@@ -832,7 +891,11 @@
return result;
}
-// KeyMintDevice implementation
+void KeyMintDevice::setNumFreeSlots(uint8_t numFreeSlots) {
+ mOperationSlots.setNumFreeSlots(numFreeSlots);
+}
+
+// Constructors and helpers.
KeyMintDevice::KeyMintDevice(sp<Keymaster> device, KeyMintSecurityLevel securityLevel)
: mDevice(device) {
@@ -843,37 +906,88 @@
}
}
-void KeyMintDevice::setNumFreeSlots(uint8_t numFreeSlots) {
- mOperationSlots.setNumFreeSlots(numFreeSlots);
+sp<Keymaster> getDevice(KeyMintSecurityLevel securityLevel) {
+ static std::mutex mutex;
+ static sp<Keymaster> teeDevice;
+ static sp<Keymaster> sbDevice;
+ std::lock_guard<std::mutex> lock(mutex);
+ if (!teeDevice) {
+ auto devices = initializeKeymasters();
+ teeDevice = devices[V4_0::SecurityLevel::TRUSTED_ENVIRONMENT];
+ sbDevice = devices[V4_0::SecurityLevel::STRONGBOX];
+ }
+ switch (securityLevel) {
+ case KeyMintSecurityLevel::TRUSTED_ENVIRONMENT:
+ return teeDevice;
+ case KeyMintSecurityLevel::STRONGBOX:
+ return sbDevice;
+ default:
+ return {};
+ }
}
std::shared_ptr<KeyMintDevice>
KeyMintDevice::createKeyMintDevice(KeyMintSecurityLevel securityLevel) {
- static std::mutex mutex;
- std::lock_guard<std::mutex> lock(mutex);
- static std::shared_ptr<KeyMintDevice> device_ptr;
- if (!device_ptr) {
- auto secLevel = static_cast<SecurityLevel>(securityLevel);
- auto devices = initializeKeymasters();
- auto device = devices[secLevel];
- if (!device) {
- return {};
- }
- device_ptr = ndk::SharedRefBase::make<KeyMintDevice>(std::move(device), securityLevel);
+ if (auto dev = getDevice(securityLevel)) {
+ return ndk::SharedRefBase::make<KeyMintDevice>(std::move(dev), securityLevel);
}
- return device_ptr;
+ return {};
+}
+
+std::shared_ptr<SharedSecret> SharedSecret::createSharedSecret(KeyMintSecurityLevel securityLevel) {
+ auto device = getDevice(securityLevel);
+ if (!device) {
+ return {};
+ }
+ return ndk::SharedRefBase::make<SharedSecret>(std::move(device));
+}
+
+std::shared_ptr<SecureClock> SecureClock::createSecureClock(KeyMintSecurityLevel securityLevel) {
+ auto device = getDevice(securityLevel);
+ if (!device) {
+ return {};
+ }
+ return ndk::SharedRefBase::make<SecureClock>(std::move(device));
}
ScopedAStatus
KeystoreCompatService::getKeyMintDevice(KeyMintSecurityLevel in_securityLevel,
std::shared_ptr<IKeyMintDevice>* _aidl_return) {
- if (mDeviceCache.find(in_securityLevel) == mDeviceCache.end()) {
+ auto i = mDeviceCache.find(in_securityLevel);
+ if (i == mDeviceCache.end()) {
auto device = KeyMintDevice::createKeyMintDevice(in_securityLevel);
if (!device) {
return ScopedAStatus::fromStatus(STATUS_NAME_NOT_FOUND);
}
- mDeviceCache[in_securityLevel] = std::move(device);
+ bool inserted = false;
+ std::tie(i, inserted) = mDeviceCache.insert({in_securityLevel, std::move(device)});
}
- *_aidl_return = mDeviceCache[in_securityLevel];
+ *_aidl_return = i->second;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus KeystoreCompatService::getSharedSecret(KeyMintSecurityLevel in_securityLevel,
+ std::shared_ptr<ISharedSecret>* _aidl_return) {
+ if (!mSharedSecret) {
+ auto secret = SharedSecret::createSharedSecret(in_securityLevel);
+ if (!secret) {
+ return ScopedAStatus::fromStatus(STATUS_NAME_NOT_FOUND);
+ }
+ mSharedSecret = std::move(secret);
+ }
+ *_aidl_return = mSharedSecret;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus KeystoreCompatService::getSecureClock(std::shared_ptr<ISecureClock>* _aidl_return) {
+ if (!mSharedSecret) {
+ // The legacy verification service was always provided by the TEE variant.
+ auto clock = SecureClock::createSecureClock(KeyMintSecurityLevel::TRUSTED_ENVIRONMENT);
+ if (!clock) {
+ return ScopedAStatus::fromStatus(STATUS_NAME_NOT_FOUND);
+ }
+ mSecureClock = std::move(clock);
+ }
+ *_aidl_return = mSecureClock;
return ScopedAStatus::ok();
}
diff --git a/keystore2/src/km_compat/km_compat.h b/keystore2/src/km_compat/km_compat.h
index 904d391..481481a 100644
--- a/keystore2/src/km_compat/km_compat.h
+++ b/keystore2/src/km_compat/km_compat.h
@@ -18,6 +18,8 @@
#include <aidl/android/hardware/security/keymint/BnKeyMintDevice.h>
#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
+#include <aidl/android/hardware/security/secureclock/BnSecureClock.h>
+#include <aidl/android/hardware/security/sharedsecret/BnSharedSecret.h>
#include <aidl/android/security/compat/BnKeystoreCompatService.h>
#include <keymasterV4_1/Keymaster4.h>
#include <unordered_map>
@@ -30,15 +32,19 @@
using ::aidl::android::hardware::security::keymint::Certificate;
using ::aidl::android::hardware::security::keymint::HardwareAuthToken;
using ::aidl::android::hardware::security::keymint::KeyCharacteristics;
+using ::aidl::android::hardware::security::keymint::KeyCreationResult;
using ::aidl::android::hardware::security::keymint::KeyFormat;
using ::aidl::android::hardware::security::keymint::KeyMintHardwareInfo;
using ::aidl::android::hardware::security::keymint::KeyParameter;
using ::aidl::android::hardware::security::keymint::KeyParameterArray;
using ::aidl::android::hardware::security::keymint::KeyPurpose;
-using ::aidl::android::hardware::security::keymint::VerificationToken;
using KeyMintSecurityLevel = ::aidl::android::hardware::security::keymint::SecurityLevel;
using V4_0_ErrorCode = ::android::hardware::keymaster::V4_0::ErrorCode;
using ::aidl::android::hardware::security::keymint::IKeyMintDevice;
+using ::aidl::android::hardware::security::secureclock::ISecureClock;
+using ::aidl::android::hardware::security::secureclock::TimeStampToken;
+using ::aidl::android::hardware::security::sharedsecret::ISharedSecret;
+using ::aidl::android::hardware::security::sharedsecret::SharedSecretParameters;
using ::aidl::android::security::compat::BnKeystoreCompatService;
using ::android::hardware::keymaster::V4_1::support::Keymaster;
using ::ndk::ScopedAStatus;
@@ -80,25 +86,18 @@
static std::shared_ptr<KeyMintDevice> createKeyMintDevice(KeyMintSecurityLevel securityLevel);
ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* _aidl_return) override;
- ScopedAStatus verifyAuthorization(int64_t in_challenge, const HardwareAuthToken& in_token,
- VerificationToken* _aidl_return) override;
ScopedAStatus addRngEntropy(const std::vector<uint8_t>& in_data) override;
ScopedAStatus generateKey(const std::vector<KeyParameter>& in_keyParams,
- ByteArray* out_generatedKeyBlob,
- KeyCharacteristics* out_generatedKeyCharacteristics,
- std::vector<Certificate>* out_outCertChain) override;
+ KeyCreationResult* out_creationResult) override;
ScopedAStatus importKey(const std::vector<KeyParameter>& in_inKeyParams,
KeyFormat in_inKeyFormat, const std::vector<uint8_t>& in_inKeyData,
- ByteArray* out_outImportedKeyBlob,
- KeyCharacteristics* out_outImportedKeyCharacteristics,
- std::vector<Certificate>* out_outCertChain) override;
+ KeyCreationResult* out_creationResult) override;
ScopedAStatus importWrappedKey(const std::vector<uint8_t>& in_inWrappedKeyData,
const std::vector<uint8_t>& in_inWrappingKeyBlob,
const std::vector<uint8_t>& in_inMaskingKey,
const std::vector<KeyParameter>& in_inUnwrappingParams,
int64_t in_inPasswordSid, int64_t in_inBiometricSid,
- ByteArray* out_outImportedKeyBlob,
- KeyCharacteristics* out_outImportedKeyCharacteristics) override;
+ KeyCreationResult* out_creationResult) override;
ScopedAStatus upgradeKey(const std::vector<uint8_t>& in_inKeyBlobToUpgrade,
const std::vector<KeyParameter>& in_inUpgradeParams,
std::vector<uint8_t>* _aidl_return) override;
@@ -121,6 +120,7 @@
private:
std::optional<V4_0_ErrorCode> signCertificate(const std::vector<KeyParameter>& keyParams,
const std::vector<uint8_t>& keyBlob, X509* cert);
+ KeyMintSecurityLevel securityLevel_;
};
class KeyMintOperation : public aidl::android::hardware::security::keymint::BnKeyMintOperation {
@@ -138,25 +138,54 @@
ScopedAStatus update(const std::optional<KeyParameterArray>& in_inParams,
const std::optional<std::vector<uint8_t>>& in_input,
const std::optional<HardwareAuthToken>& in_inAuthToken,
- const std::optional<VerificationToken>& in_inVerificationToken,
+ const std::optional<TimeStampToken>& in_inTimestampToken,
std::optional<KeyParameterArray>* out_outParams,
std::optional<ByteArray>* out_output, int32_t* _aidl_return);
ScopedAStatus finish(const std::optional<KeyParameterArray>& in_inParams,
const std::optional<std::vector<uint8_t>>& in_input,
const std::optional<std::vector<uint8_t>>& in_inSignature,
const std::optional<HardwareAuthToken>& in_authToken,
- const std::optional<VerificationToken>& in_inVerificationToken,
+ const std::optional<TimeStampToken>& in_inTimestampToken,
std::optional<KeyParameterArray>* out_outParams,
std::vector<uint8_t>* _aidl_return);
ScopedAStatus abort();
};
+class SharedSecret : public aidl::android::hardware::security::sharedsecret::BnSharedSecret {
+ private:
+ ::android::sp<Keymaster> mDevice;
+
+ public:
+ SharedSecret(::android::sp<Keymaster> device) : mDevice(device) {}
+ static std::shared_ptr<SharedSecret> createSharedSecret(KeyMintSecurityLevel securityLevel);
+
+ virtual ScopedAStatus getSharedSecretParameters(SharedSecretParameters* _aidl_return) override;
+ virtual ScopedAStatus computeSharedSecret(const std::vector<SharedSecretParameters>& in_params,
+ std::vector<uint8_t>* _aidl_return) override;
+};
+
+class SecureClock : public aidl::android::hardware::security::secureclock::BnSecureClock {
+ private:
+ ::android::sp<Keymaster> mDevice;
+
+ public:
+ SecureClock(::android::sp<Keymaster> device) : mDevice(device) {}
+ static std::shared_ptr<SecureClock> createSecureClock(KeyMintSecurityLevel securityLevel);
+
+ ScopedAStatus generateTimeStamp(int64_t in_challenge, TimeStampToken* _aidl_return) override;
+};
+
class KeystoreCompatService : public BnKeystoreCompatService {
private:
std::unordered_map<KeyMintSecurityLevel, std::shared_ptr<IKeyMintDevice>> mDeviceCache;
+ std::shared_ptr<ISharedSecret> mSharedSecret;
+ std::shared_ptr<ISecureClock> mSecureClock;
public:
KeystoreCompatService() {}
ScopedAStatus getKeyMintDevice(KeyMintSecurityLevel in_securityLevel,
std::shared_ptr<IKeyMintDevice>* _aidl_return) override;
+ ScopedAStatus getSharedSecret(KeyMintSecurityLevel in_securityLevel,
+ std::shared_ptr<ISharedSecret>* _aidl_return) override;
+ ScopedAStatus getSecureClock(std::shared_ptr<ISecureClock>* _aidl_return) override;
};
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
index 36f1303..aed0e7e 100644
--- a/keystore2/src/km_compat/lib.rs
+++ b/keystore2/src/km_compat/lib.rs
@@ -28,20 +28,21 @@
use super::*;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, BeginResult::BeginResult, BlockMode::BlockMode, ByteArray::ByteArray,
- Certificate::Certificate, Digest::Digest, ErrorCode::ErrorCode,
- HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::IKeyMintDevice,
- KeyCharacteristics::KeyCharacteristics, KeyFormat::KeyFormat, KeyParameter::KeyParameter,
+ Algorithm::Algorithm, BeginResult::BeginResult, BlockMode::BlockMode, Digest::Digest,
+ ErrorCode::ErrorCode, HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::IKeyMintDevice,
+ KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat, KeyParameter::KeyParameter,
KeyParameterArray::KeyParameterArray, KeyParameterValue::KeyParameterValue,
KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, SecurityLevel::SecurityLevel, Tag::Tag,
};
use android_hardware_security_keymint::binder;
use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
+ static COMPAT_NAME: &str = "android.security.compat";
+
fn get_device() -> Box<dyn IKeyMintDevice> {
add_keymint_device_service();
let compat_service: Box<dyn IKeystoreCompatService> =
- binder::get_interface("android.security.compat").unwrap();
+ binder::get_interface(COMPAT_NAME).unwrap();
compat_service.getKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap()
}
@@ -56,15 +57,6 @@
}
#[test]
- fn test_verify_authorization() {
- use android_hardware_security_keymint::aidl::android::hardware::security::keymint::HardwareAuthToken::HardwareAuthToken;
- let legacy = get_device();
- let result = legacy.verifyAuthorization(0, &HardwareAuthToken::default());
- assert!(result.is_err());
- assert_eq!(result.unwrap_err().service_specific_error(), ErrorCode::UNIMPLEMENTED.0,);
- }
-
- #[test]
fn test_add_rng_entropy() {
let legacy = get_device();
let result = legacy.addRngEntropy(&[42; 16]);
@@ -72,17 +64,10 @@
}
// TODO: If I only need the key itself, don't return the other things.
- fn generate_key(
- legacy: &dyn IKeyMintDevice,
- kps: Vec<KeyParameter>,
- ) -> (ByteArray, KeyCharacteristics, Vec<Certificate>) {
- let mut blob = ByteArray { data: vec![] };
- let mut characteristics = KeyCharacteristics::default();
- let mut cert_chain = vec![];
- let result = legacy.generateKey(&kps, &mut blob, &mut characteristics, &mut cert_chain);
- assert!(result.is_ok(), "{:?}", result);
- assert_ne!(blob.data.len(), 0);
- (blob, characteristics, cert_chain)
+ fn generate_key(legacy: &dyn IKeyMintDevice, kps: Vec<KeyParameter>) -> KeyCreationResult {
+ let creation_result = legacy.generateKey(&kps).expect("Failed to generate key");
+ assert_ne!(creation_result.keyBlob.len(), 0);
+ creation_result
}
fn generate_rsa_key(legacy: &dyn IKeyMintDevice, encrypt: bool, attest: bool) -> Vec<u8> {
@@ -123,14 +108,14 @@
value: KeyParameterValue::Blob(vec![42; 8]),
});
}
- let (blob, _, cert_chain) = generate_key(legacy, kps);
+ let creation_result = generate_key(legacy, kps);
if attest {
// TODO: Will this always be greater than 1?
- assert!(cert_chain.len() > 1);
+ assert!(creation_result.certificateChain.len() > 1);
} else {
- assert_eq!(cert_chain.len(), 1);
+ assert_eq!(creation_result.certificateChain.len(), 1);
}
- blob.data
+ creation_result.keyBlob
}
#[test]
@@ -160,23 +145,15 @@
}];
let kf = KeyFormat::RAW;
let kd = [0; 16];
- let mut blob = ByteArray { data: vec![] };
- let mut characteristics = KeyCharacteristics::default();
- let mut cert_chain = vec![];
- let result =
- legacy.importKey(&kps, kf, &kd, &mut blob, &mut characteristics, &mut cert_chain);
- assert!(result.is_ok(), "{:?}", result);
- assert_ne!(blob.data.len(), 0);
- assert_eq!(cert_chain.len(), 0);
+ let creation_result = legacy.importKey(&kps, kf, &kd).expect("Failed to import key");
+ assert_ne!(creation_result.keyBlob.len(), 0);
+ assert_eq!(creation_result.certificateChain.len(), 0);
}
#[test]
fn test_import_wrapped_key() {
let legacy = get_device();
- let mut blob = ByteArray { data: vec![] };
- let mut characteristics = KeyCharacteristics::default();
- let result =
- legacy.importWrappedKey(&[], &[], &[], &[], 0, 0, &mut blob, &mut characteristics);
+ let result = legacy.importWrappedKey(&[], &[], &[], &[], 0, 0);
// TODO: This test seems to fail on cuttlefish. How should I test it?
assert!(result.is_err());
}
@@ -238,9 +215,9 @@
value: KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT),
},
];
- let (blob, _, cert_chain) = generate_key(legacy, kps);
- assert_eq!(cert_chain.len(), 0);
- blob.data
+ let creation_result = generate_key(legacy, kps);
+ assert_eq!(creation_result.certificateChain.len(), 0);
+ creation_result.keyBlob
}
fn begin(
@@ -322,4 +299,36 @@
assert!(result.is_ok(), "{:?}", result);
assert!(out_params.is_some());
}
+
+ #[test]
+ fn test_secure_clock() {
+ add_keymint_device_service();
+ let compat_service: Box<dyn IKeystoreCompatService> =
+ binder::get_interface(COMPAT_NAME).unwrap();
+ let secure_clock = compat_service.getSecureClock().unwrap();
+
+ let challenge = 42;
+ let result = secure_clock.generateTimeStamp(challenge);
+ assert!(result.is_ok(), "{:?}", result);
+ let result = result.unwrap();
+ assert_eq!(result.challenge, challenge);
+ assert_eq!(result.mac.len(), 32);
+ }
+
+ #[test]
+ fn test_shared_secret() {
+ add_keymint_device_service();
+ let compat_service: Box<dyn IKeystoreCompatService> =
+ binder::get_interface(COMPAT_NAME).unwrap();
+ let shared_secret =
+ compat_service.getSharedSecret(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let result = shared_secret.getSharedSecretParameters();
+ assert!(result.is_ok(), "{:?}", result);
+ let params = result.unwrap();
+
+ let result = shared_secret.computeSharedSecret(&[params]);
+ assert!(result.is_ok(), "{:?}", result);
+ assert_ne!(result.unwrap().len(), 0);
+ }
}
diff --git a/keystore2/src/km_compat/slot_test.cpp b/keystore2/src/km_compat/slot_test.cpp
index e56fb37..0859ddf 100644
--- a/keystore2/src/km_compat/slot_test.cpp
+++ b/keystore2/src/km_compat/slot_test.cpp
@@ -46,14 +46,12 @@
KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::ENCRYPT),
KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::DECRYPT),
});
- ByteArray blob;
- KeyCharacteristics characteristics;
- std::vector<Certificate> cert;
- auto status = device->generateKey(keyParams, &blob, &characteristics, &cert);
+ KeyCreationResult creationResult;
+ auto status = device->generateKey(keyParams, &creationResult);
if (!status.isOk()) {
return {};
}
- return blob.data;
+ return creationResult.keyBlob;
}
static std::variant<BeginResult, ScopedAStatus> begin(std::shared_ptr<KeyMintDevice> device,
@@ -151,16 +149,14 @@
KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::SIGN),
KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED, true),
});
- ByteArray blob;
- KeyCharacteristics characteristics;
- std::vector<Certificate> cert;
- status = device->generateKey(kps, &blob, &characteristics, &cert);
+ KeyCreationResult creationResult;
+ status = device->generateKey(kps, &creationResult);
ASSERT_TRUE(!status.isOk());
ASSERT_EQ(status.getServiceSpecificError(),
static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
// But generating a certificate with signCert does not use a slot.
kps.pop_back();
- status = device->generateKey(kps, &blob, &characteristics, &cert);
+ status = device->generateKey(kps, &creationResult);
ASSERT_TRUE(status.isOk());
// Destructing operations should free up their slots.
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 29b3992..240998e 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -17,6 +17,8 @@
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 9a35154..8e4f800 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -125,6 +125,26 @@
//! 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::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,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ IKeystoreOperation::BnKeystoreOperation, IKeystoreOperation::IKeystoreOperation,
+ OperationChallenge::OperationChallenge,
+};
+use anyhow::{anyhow, Context, Result};
+use binder::{IBinder, Interface};
use std::{
collections::HashMap,
sync::{Arc, Mutex, MutexGuard, Weak},
@@ -132,19 +152,6 @@
time::Instant,
};
-use crate::error::{map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
-use crate::utils::Asp;
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- 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,
-};
-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.
@@ -168,6 +175,11 @@
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>,
}
struct PruningInfo {
@@ -181,13 +193,23 @@
impl Operation {
/// Constructor
- pub fn new(index: usize, km_op: Box<dyn IKeyMintOperation>, owner: u32) -> Self {
+ pub fn new(
+ index: usize,
+ km_op: Box<dyn IKeyMintOperation>,
+ owner: u32,
+ auth_token_handler: AuthTokenHandler,
+ key_params: Option<Vec<KeyParameter>>,
+ op_challenge: Option<OperationChallenge>,
+ ) -> Self {
Self {
index,
km_op: Asp::new(km_op.as_binder()),
last_usage: Mutex::new(Instant::now()),
outcome: Mutex::new(Outcome::Unknown),
owner,
+ auth_token_handler: Mutex::new(auth_token_handler),
+ key_params,
+ op_challenge,
}
}
@@ -337,7 +359,7 @@
None,
// TODO Get auth token from enforcement module if required.
None,
- // TODO Get verification token from enforcement module if required.
+ // TODO Get timestamp token from enforcement module if required.
None,
&mut out_params,
&mut output,
@@ -348,6 +370,43 @@
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>>> {
@@ -361,15 +420,21 @@
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.")?;
+
self.update_outcome(
&mut *outcome,
map_km_error(km_op.update(
None,
Some(input),
- // TODO Get auth token from enforcement module if required.
- None,
- // TODO Get verification token from enforcement module if required.
- None,
+ auth_token_for_km,
+ timestamp_token_for_km,
&mut out_params,
&mut output,
)),
@@ -402,6 +467,14 @@
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 output = self
.update_outcome(
&mut *outcome,
@@ -409,10 +482,8 @@
None,
input,
signature,
- // TODO Get auth token from enforcement module if required.
- None,
- // TODO Get verification token from enforcement module if required.
- None,
+ auth_token_for_km,
+ timestamp_token_for_km,
&mut out_params,
)),
)
@@ -475,6 +546,9 @@
&self,
km_op: Box<dyn IKeyMintOperation>,
owner: u32,
+ auth_token_handler: AuthTokenHandler,
+ key_params: Option<Vec<KeyParameter>>,
+ op_challenge: Option<OperationChallenge>,
) -> 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.");
@@ -487,12 +561,26 @@
s.upgrade().is_none()
}) {
Some(free_slot) => {
- let new_op = Arc::new(Operation::new(index - 1, km_op, owner));
+ let new_op = Arc::new(Operation::new(
+ index - 1,
+ km_op,
+ owner,
+ auth_token_handler,
+ key_params,
+ op_challenge,
+ ));
*free_slot = Arc::downgrade(&new_op);
new_op
}
None => {
- let new_op = Arc::new(Operation::new(operations.len(), km_op, owner));
+ let new_op = Arc::new(Operation::new(
+ operations.len(),
+ km_op,
+ owner,
+ auth_token_handler,
+ key_params,
+ op_challenge,
+ ));
operations.push(Arc::downgrade(&new_op));
new_op
}
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index af59f79..943f69f 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -17,9 +17,9 @@
//! This crate implements the IKeystoreSecurityLevel interface.
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, ByteArray::ByteArray, Certificate::Certificate as KmCertificate,
+ Algorithm::Algorithm, HardwareAuthToken::HardwareAuthToken,
HardwareAuthenticatorType::HardwareAuthenticatorType, IKeyMintDevice::IKeyMintDevice,
- KeyCharacteristics::KeyCharacteristics, KeyFormat::KeyFormat, KeyParameter::KeyParameter,
+ KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat, KeyParameter::KeyParameter,
KeyParameterValue::KeyParameterValue, SecurityLevel::SecurityLevel, Tag::Tag,
};
use android_system_keystore2::aidl::android::system::keystore2::{
@@ -27,9 +27,13 @@
Domain::Domain, IKeystoreOperation::IKeystoreOperation,
IKeystoreSecurityLevel::BnKeystoreSecurityLevel,
IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
- KeyMetadata::KeyMetadata, KeyParameters::KeyParameters,
+ KeyMetadata::KeyMetadata, KeyParameters::KeyParameters, OperationChallenge::OperationChallenge,
};
+use crate::auth_token_handler::AuthTokenHandler;
+use crate::globals::ENFORCEMENTS;
+use crate::key_parameter::KeyParameter as KsKeyParam;
+use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
use crate::utils::{check_key_permission, Asp};
use crate::{database::KeyIdGuard, globals::DB};
use crate::{
@@ -44,6 +48,7 @@
use crate::{
error::{self, map_km_error, map_or_log_err, Error, ErrorCode},
utils::key_characteristics_to_internal,
+ utils::uid_to_android_user,
};
use anyhow::{Context, Result};
use binder::{IBinder, Interface, ThreadState};
@@ -79,39 +84,45 @@
fn store_new_key(
&self,
key: KeyDescriptor,
- key_characteristics: KeyCharacteristics,
- km_cert_chain: Option<Vec<KmCertificate>>,
- blob: ByteArray,
+ creation_result: KeyCreationResult,
+ user_id: u32,
) -> Result<KeyMetadata> {
- let (cert, cert_chain): (Option<Vec<u8>>, Option<Vec<u8>>) = match km_cert_chain {
- Some(mut chain) => (
- match chain.len() {
- 0 => None,
- _ => Some(chain.remove(0).encodedCertificate),
- },
- match chain.len() {
- 0 => None,
- _ => Some(
- chain
- .iter()
- .map(|c| c.encodedCertificate.iter())
- .flatten()
- .copied()
- .collect(),
- ),
- },
- ),
- None => (None, None),
- };
+ let KeyCreationResult {
+ keyBlob: key_blob,
+ keyCharacteristics: key_characteristics,
+ certificateChain: mut certificate_chain,
+ } = creation_result;
- let key_parameters =
- key_characteristics_to_internal(key_characteristics, self.security_level);
+ let (cert, cert_chain): (Option<Vec<u8>>, Option<Vec<u8>>) = (
+ match certificate_chain.len() {
+ 0 => None,
+ _ => Some(certificate_chain.remove(0).encodedCertificate),
+ },
+ match certificate_chain.len() {
+ 0 => None,
+ _ => Some(
+ certificate_chain
+ .iter()
+ .map(|c| c.encodedCertificate.iter())
+ .flatten()
+ .copied()
+ .collect(),
+ ),
+ },
+ );
+
+ let mut key_parameters = key_characteristics_to_internal(key_characteristics);
+
+ key_parameters.push(KsKeyParam::new(
+ KsKeyParamValue::UserID(user_id as i32),
+ SecurityLevel::SOFTWARE,
+ ));
let creation_date = DateTime::now().context("Trying to make creation time.")?;
let key = match key.domain {
Domain::BLOB => {
- KeyDescriptor { domain: Domain::BLOB, blob: Some(blob.data), ..Default::default() }
+ KeyDescriptor { domain: Domain::BLOB, blob: Some(key_blob), ..Default::default() }
}
_ => DB
.with::<_, Result<KeyDescriptor>>(|db| {
@@ -123,7 +134,7 @@
.store_new_key(
key,
&key_parameters,
- &blob.data,
+ &key_blob,
cert.as_deref(),
cert_chain.as_deref(),
&metadata,
@@ -159,7 +170,7 @@
// 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_guard) = match key.domain {
+ let (km_blob, key_id_guard, key_parameters) = match key.domain {
Domain::BLOB => {
check_key_permission(KeyPerm::use_(), key, &None)
.context("In create_operation: checking use permission for Domain::BLOB.")?;
@@ -174,6 +185,7 @@
}
},
None,
+ None,
)
}
_ => {
@@ -197,14 +209,10 @@
))
}
};
- (&scoping_blob, Some(key_id_guard))
+ (&scoping_blob, Some(key_id_guard), Some(key_entry.into_key_parameters()))
}
};
- // 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).map_or(
Err(Error::Km(ErrorCode::INVALID_ARGUMENT))
.context("In create_operation: No operation purpose specified."),
@@ -215,6 +223,35 @@
},
)?;
+ let mut auth_token_for_km: &HardwareAuthToken = &Default::default();
+ let mut auth_token_handler = AuthTokenHandler::NoAuthRequired;
+
+ // 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 km_dev: Box<dyn IKeyMintDevice> = self
.keymint
.get_interface()
@@ -231,7 +268,7 @@
purpose,
blob,
&operation_parameters,
- &Default::default(),
+ auth_token_for_km,
)) {
Err(Error::Km(ErrorCode::TOO_MANY_OPERATIONS)) => {
self.operation_db.prune(caller_uid)?;
@@ -243,8 +280,36 @@
)
.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 = match begin_result.operation {
- Some(km_op) => self.operation_db.create_operation(km_op, caller_uid),
+ 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)
+ },
None => return Err(Error::sys()).context("In create_operation: Begin operation returned successfully, but did not return a valid operation."),
};
@@ -258,7 +323,7 @@
// We return None for now because we don't support auth bound keys yet.
Ok(CreateOperationResponse {
iOperation: Some(op_binder),
- operationChallenge: None,
+ operationChallenge: operation_challenge,
parameters: match begin_result.params.len() {
0 => None,
_ => Some(KeyParameters { keyParameter: begin_result.params }),
@@ -278,11 +343,12 @@
return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
.context("In generate_key: Alias must be specified");
}
+ let caller_uid = ThreadState::get_calling_uid();
let key = match key.domain {
Domain::APP => KeyDescriptor {
domain: key.domain,
- nspace: ThreadState::get_calling_uid() as i64,
+ nspace: caller_uid as i64,
alias: key.alias.clone(),
blob: None,
},
@@ -294,18 +360,10 @@
let km_dev: Box<dyn IKeyMintDevice> = self.keymint.get_interface()?;
map_km_error(km_dev.addRngEntropy(entropy))?;
- let mut blob: ByteArray = Default::default();
- let mut key_characteristics: KeyCharacteristics = Default::default();
- let mut certificate_chain: Vec<KmCertificate> = Default::default();
- map_km_error(km_dev.generateKey(
- ¶ms,
- &mut blob,
- &mut key_characteristics,
- &mut certificate_chain,
- ))?;
+ let creation_result = map_km_error(km_dev.generateKey(¶ms))?;
- self.store_new_key(key, key_characteristics, Some(certificate_chain), blob)
- .context("In generate_key.")
+ let user_id = uid_to_android_user(caller_uid);
+ self.store_new_key(key, creation_result, user_id).context("In generate_key.")
}
fn import_key(
@@ -320,11 +378,12 @@
return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
.context("In import_key: Alias must be specified");
}
+ let caller_uid = ThreadState::get_calling_uid();
let key = match key.domain {
Domain::APP => KeyDescriptor {
domain: key.domain,
- nspace: ThreadState::get_calling_uid() as i64,
+ nspace: caller_uid as i64,
alias: key.alias.clone(),
blob: None,
},
@@ -334,10 +393,6 @@
// import_key requires the rebind permission.
check_key_permission(KeyPerm::rebind(), &key, &None).context("In import_key.")?;
- let mut blob: ByteArray = 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)
@@ -355,17 +410,10 @@
.context("In import_key.")?;
let km_dev: Box<dyn IKeyMintDevice> = self.keymint.get_interface()?;
- map_km_error(km_dev.importKey(
- ¶ms,
- format,
- key_data,
- &mut blob,
- &mut key_characteristics,
- &mut certificate_chain,
- ))?;
+ let creation_result = map_km_error(km_dev.importKey(¶ms, format, key_data))?;
- self.store_new_key(key, key_characteristics, Some(certificate_chain), blob)
- .context("In import_key.")
+ let user_id = uid_to_android_user(caller_uid);
+ self.store_new_key(key, creation_result, user_id).context("In import_key.")
}
fn import_wrapped_key(
@@ -396,10 +444,11 @@
}
};
+ let caller_uid = ThreadState::get_calling_uid();
let key = match key.domain {
Domain::APP => KeyDescriptor {
domain: key.domain,
- nspace: ThreadState::get_calling_uid() as i64,
+ nspace: caller_uid as i64,
alias: key.alias.clone(),
blob: None,
},
@@ -415,7 +464,7 @@
wrapping_key.clone(),
KeyType::Client,
KeyEntryLoadBits::KM,
- ThreadState::get_calling_uid(),
+ caller_uid,
|k, av| check_key_permission(KeyPerm::use_(), k, &av),
)
})
@@ -455,29 +504,26 @@
let masking_key = masking_key.unwrap_or(ZERO_BLOB_32);
let km_dev: Box<dyn IKeyMintDevice> = self.keymint.get_interface()?;
- let ((blob, key_characteristics), _) = self.upgrade_keyblob_if_required_with(
+ let (creation_result, _) = self.upgrade_keyblob_if_required_with(
&*km_dev,
Some(wrapping_key_id_guard),
wrapping_key_blob,
&[],
|wrapping_blob| {
- let mut blob: ByteArray = Default::default();
- let mut key_characteristics: KeyCharacteristics = Default::default();
- map_km_error(km_dev.importWrappedKey(
+ let creation_result = map_km_error(km_dev.importWrappedKey(
wrapped_data,
wrapping_key_blob,
masking_key,
¶ms,
pw_sid,
fp_sid,
- &mut blob,
- &mut key_characteristics,
))?;
- Ok((blob, key_characteristics))
+ Ok(creation_result)
},
)?;
- self.store_new_key(key, key_characteristics, None, blob).context("In import_wrapped_key.")
+ let user_id = uid_to_android_user(caller_uid);
+ self.store_new_key(key, creation_result, user_id).context("In import_wrapped_key.")
}
fn upgrade_keyblob_if_required_with<T, F>(
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index 86a86dd..080348c 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -12,10 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// This suppresses the compiler's complaint about converting tv_sec to i64 in method
-// get_current_time_in_seconds.
-#![allow(clippy::useless_conversion)]
-
//! This module implements utility functions used by the Keystore 2.0 service
//! implementation.
@@ -23,7 +19,7 @@
use crate::permission;
use crate::permission::{KeyPerm, KeyPermSet, KeystorePerm};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- KeyCharacteristics::KeyCharacteristics, SecurityLevel::SecurityLevel,
+ KeyCharacteristics::KeyCharacteristics, SecurityLevel::SecurityLevel, Tag::Tag,
};
use android_security_apc::aidl::android::security::apc::{
IProtectedConfirmation::{FLAG_UI_OPTION_INVERTED, FLAG_UI_OPTION_MAGNIFIED},
@@ -126,19 +122,21 @@
/// Converts a set of key characteristics as returned from KeyMint into the internal
/// representation of the keystore service.
-/// The parameter `hw_security_level` indicates which security level shall be used for
-/// parameters found in the hardware enforced parameter list.
pub fn key_characteristics_to_internal(
- key_characteristics: KeyCharacteristics,
- hw_security_level: SecurityLevel,
+ key_characteristics: Vec<KeyCharacteristics>,
) -> Vec<crate::key_parameter::KeyParameter> {
key_characteristics
- .hardwareEnforced
.into_iter()
- .map(|aidl_kp| crate::key_parameter::KeyParameter::new(aidl_kp.into(), hw_security_level))
- .chain(key_characteristics.softwareEnforced.into_iter().map(|aidl_kp| {
- crate::key_parameter::KeyParameter::new(aidl_kp.into(), SecurityLevel::SOFTWARE)
- }))
+ .flat_map(|aidl_key_char| {
+ let sec_level = aidl_key_char.securityLevel;
+ aidl_key_char.authorizations.into_iter().map(move |aidl_kp| {
+ let sec_level = match (aidl_kp.tag, sec_level) {
+ (Tag::ORIGIN, SecurityLevel::SOFTWARE) => SecurityLevel::TRUSTED_ENVIRONMENT,
+ _ => sec_level,
+ };
+ crate::key_parameter::KeyParameter::new(aidl_kp.into(), sec_level)
+ })
+ })
.collect()
}
@@ -159,6 +157,9 @@
unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_RAW, &mut current_time) };
// It is safe to unwrap here because try_from() returns std::convert::Infallible, which is
// defined to be an error that can never happen (i.e. the result is always ok).
+ // This suppresses the compiler's complaint about converting tv_sec to i64 in method
+ // get_current_time_in_seconds.
+ #[allow(clippy::useless_conversion)]
i64::try_from(current_time.tv_sec).unwrap()
}