Implement get auth token from database.

This CL implements the functionality related to retrieving an auth token
from the auth token table in keystore perboot database.

Bug: 171503352
Test: TBD
Change-Id: Iefa988cfbe6b01f65dacb029ffdf986216ff9895
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index df1c24c..ab8b05f 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,15 @@
 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,
+    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 +73,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 +583,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 +697,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 +1680,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)]
@@ -1704,13 +1813,13 @@
         HardwareAuthenticatorType::HardwareAuthenticatorType as kmhw_authenticator_type,
         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 +3005,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..1f82e57 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -12,9 +12,6 @@
 // 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;
@@ -150,8 +147,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 +184,13 @@
         // 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;
 
         // iterate through key parameters, recording information we need for authorization
         // enforcements later, or enforcing authorizations in place, where applicable
@@ -201,11 +199,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),
@@ -255,6 +254,9 @@
                             .context("In authorize_create: device is locked.");
                     }
                 }
+                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
                 // authorize_create (unlike in legacy keystore where AuthorizeBegin is rejected if
@@ -279,8 +281,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
@@ -299,13 +301,37 @@
         }
 
         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::VerificationRequired(auth_token));
+                } else {
+                    return Ok(AuthTokenHandler::Token(auth_token, None));
+                }
             }
         }
 
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index abbc162..b0f4885 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.
 
@@ -157,6 +153,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()
 }