Implement authorize_update_or_finish.

This CL implements authorize_update_or_finish method and helper
structs and methods for that.

Bug: 159461976
Test: Unit tests
Change-Id: I4fae6a464ca66fc632fc2f6a9662a425ad7046bf
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 9086faf..4dd60bc 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
index 7f9569f..dfd89b5 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -17,7 +17,16 @@
 
 //! This is the Keystore 2.0 Enforcements module.
 // TODO: more description to follow.
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::HardwareAuthToken::HardwareAuthToken;
+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;
 
@@ -39,6 +48,87 @@
             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 {
@@ -47,4 +137,4 @@
     }
 }
 
-//TODO: Add tests to enforcement module (b/175578618).
+// TODO: Add tests to enforcement module (b/175578618).