Merge changes I4fae6a46,I60506ae1,I0c6fc14e
* changes:
Implement authorize_update_or_finish.
Definition and initialization of data structures for enforcements.
Implement AuthTokenHandler enum.
diff --git a/keystore2/src/auth_token_handler.rs b/keystore2/src/auth_token_handler.rs
new file mode 100644
index 0000000..8c10442
--- /dev/null
+++ b/keystore2/src/auth_token_handler.rs
@@ -0,0 +1,83 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This module defines the AuthTokenHandler enum and its methods. AuthTokenHandler enum represents
+//! the different states an auth token and an associated verification 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,
+};
+use anyhow::{Context, Result};
+use std::sync::mpsc::Receiver;
+
+/// AuthTokenHandler enum has five different variants which are described by the comments above
+// each variant.
+pub enum AuthTokenHandler {
+ /// Used when an operation does not require an auth token for authorization.
+ NoAuthRequired,
+ /// Used to represent the intermediate state between the time the operation is found to be
+ /// requiring per-op auth and the time the auth token for the operation is found.
+ OpAuthRequired,
+ /// Used to represent the intermediate state between the time the operation is found to be
+ /// using a time_out key with STRONGBOX keymint, and the time a verficiation token is requested
+ /// from the worker thread which obtains 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)>),
+ /// 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
+ /// required) is found.
+ Token(HardwareAuthToken, Option<VerificationToken>),
+}
+
+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 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(())
+ } else {
+ Err(KeystoreError::sys()).context(
+ "In receive_verification_token: Wrong variant found in the authorization object.",
+ )
+ }
+ }
+}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 79944eb..f9b320f 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -12,6 +12,9 @@
// 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
@@ -47,7 +50,10 @@
use crate::permission::KeyPermSet;
use anyhow::{anyhow, Context, Result};
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType,
+ SecurityLevel::SecurityLevel,
+};
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, KeyDescriptor::KeyDescriptor,
};
@@ -244,6 +250,42 @@
conn: Connection,
}
+/// This struct encapsulates the information to be stored in the database about the auth tokens
+/// received by keystore.
+pub struct AuthTokenEntry {
+ auth_token: HardwareAuthToken,
+ time_received: i32,
+}
+
+impl AuthTokenEntry {
+ fn new(auth_token: HardwareAuthToken, time_received: i32) -> Self {
+ AuthTokenEntry { auth_token, time_received }
+ }
+
+ /// Checks if this auth token satisfies the given authentication information.
+ pub fn satisfies_auth(
+ auth_token: &HardwareAuthToken,
+ user_secure_ids: &[i64],
+ auth_type: HardwareAuthenticatorType,
+ ) -> bool {
+ user_secure_ids.iter().any(|&sid| {
+ (sid == auth_token.userId || sid == auth_token.authenticatorId)
+ && (((auth_type.0 as i32) & (auth_token.authenticatorType.0 as i32)) != 0)
+ })
+ }
+
+ 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 > other.time_received
+ }
+
+ /// Returns the auth token wrapped by the AuthTokenEntry
+ pub fn get_auth_token(self) -> HardwareAuthToken {
+ self.auth_token
+ }
+}
+
impl KeystoreDB {
/// This will create a new database connection connecting the two
/// files persistent.sqlite and perboot.sqlite in the given directory.
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
new file mode 100644
index 0000000..dfd89b5
--- /dev/null
+++ b/keystore2/src/enforcements.rs
@@ -0,0 +1,140 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//TODO: remove 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::database::AuthTokenEntry;
+use crate::error::Error as KeystoreError;
+use crate::key_parameter::{KeyParameter, KeyParameterValue};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
+ HardwareAuthenticatorType::HardwareAuthenticatorType,
+};
+use android_system_keystore2::aidl::android::system::keystore2::OperationChallenge::OperationChallenge;
+use anyhow::{Context, Result};
+use std::collections::{HashMap, HashSet};
+use std::sync::Mutex;
+
+/// Enforcements data structure
+pub struct Enforcements {
+ // This hash set contains the user ids for whom the device is currently unlocked. If a user id
+ // is not in the set, it implies that the device is locked for the user.
+ device_unlocked_set: Mutex<HashSet<i32>>,
+ // This maps the operation challenge to an optional auth token, to maintain op-auth tokens
+ // in-memory, until they are picked up and given to the operation by authorise_update_finish().
+ op_auth_map: Mutex<HashMap<i64, Option<HardwareAuthToken>>>,
+}
+
+impl Enforcements {
+ /// Creates an enforcement object with the two data structures it holds.
+ pub fn new() -> Self {
+ Enforcements {
+ device_unlocked_set: Mutex::new(HashSet::new()),
+ op_auth_map: Mutex::new(HashMap::new()),
+ }
+ }
+
+ /// 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.
+ pub fn authorize_update_or_finish(
+ &self,
+ key_params: &[KeyParameter],
+ op_challenge: Option<OperationChallenge>,
+ ) -> Result<AuthTokenHandler> {
+ let mut user_auth_type: Option<HardwareAuthenticatorType> = None;
+ let mut user_secure_ids = Vec::<i64>::new();
+ let mut is_timeout_key = false;
+
+ for key_param in key_params.iter() {
+ match key_param.key_parameter_value() {
+ KeyParameterValue::NoAuthRequired => {
+ // unlike in authorize_create, we do not check if both NoAuthRequired and user
+ // secure id are present, because that is already checked in authorize_create.
+ return Ok(AuthTokenHandler::NoAuthRequired);
+ }
+ KeyParameterValue::AuthTimeout(_) => {
+ is_timeout_key = true;
+ }
+ KeyParameterValue::HardwareAuthenticatorType(a) => {
+ user_auth_type = Some(*a);
+ }
+ KeyParameterValue::UserSecureID(u) => {
+ user_secure_ids.push(*u);
+ }
+ _ => {}
+ }
+ }
+
+ // If either of auth_type or secure_id is present and the other is not present,
+ // authorize_create would have already returned error.
+ // At this point, if UserSecureID is present and AuthTimeout is not present in
+ // key parameters, per-op auth is required.
+ // Obtain and validate the auth token.
+ if !is_timeout_key && !user_secure_ids.is_empty() {
+ let challenge =
+ op_challenge.ok_or(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
+ "In authorize_update_or_finish: Auth required, but operation challenge is not
+ present.",
+ )?;
+ let auth_type =
+ user_auth_type.ok_or(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
+ "In authorize_update_or_finish: Auth required, but authenticator type is not
+ present.",
+ )?;
+ // It is ok to unwrap here, because there is no way this lock can get poisoned and
+ // and there is no way to recover if it is poisoned.
+ let mut op_auth_map_guard = self.op_auth_map.lock().unwrap();
+ let auth_entry = op_auth_map_guard.remove(&(challenge.challenge));
+
+ match auth_entry {
+ Some(Some(auth_token)) => {
+ if AuthTokenEntry::satisfies_auth(&auth_token, &user_secure_ids, auth_type) {
+ return Ok(AuthTokenHandler::Token(auth_token, None));
+ } else {
+ return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
+ .context("In authorize_update_or_finish: Auth token does not match.");
+ }
+ }
+ _ => {
+ // there was no auth token
+ return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
+ "In authorize_update_or_finish: Auth required, but an auth token
+ is not found for the given operation challenge, in the op_auth_map.",
+ );
+ }
+ }
+ }
+
+ // If we don't find HardwareAuthenticatorType and UserSecureID, we assume that
+ // authentication is not required, because in legacy keys, authentication related
+ // key parameters may not present.
+ // TODO: METRICS: count how many times (if any) this code path is executed, in order
+ // to identify if any such keys are in use
+ Ok(AuthTokenHandler::NoAuthRequired)
+ }
+}
+
+impl Default for Enforcements {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+// TODO: Add tests to enforcement module (b/175578618).
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index f75ec77..45447a9 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -14,7 +14,9 @@
//! This crate implements the Android Keystore 2.0 service.
+pub mod auth_token_handler;
pub mod database;
+pub mod enforcements;
pub mod error;
pub mod globals;
/// Internal Representation of Key Parameter and convenience functions.