Keystore 2.0: Revisit Enforcements.

This patch revisits the Keystore 2.0 enforcements module to support
KM4.1 hardware enforced device locked keys.
* Consolidate the background handler into async_task.
* The auth token handler became AuthInfo and was moved into
  enforcements.rs.
* The auth token validity check moved from database.rs to
  enforcements.rs.

Bug: 171503362
Test: Keystore CTS tests
Change-Id: If37d38183901b356242079af812c7a0e1e79abf3
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index e25fea2..f70b0df 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -56,7 +56,7 @@
 use std::{convert::TryFrom, convert::TryInto, time::SystemTimeError};
 
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
+    HardwareAuthToken::HardwareAuthToken,
     HardwareAuthenticatorType::HardwareAuthenticatorType, SecurityLevel::SecurityLevel,
 };
 use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
@@ -75,7 +75,7 @@
     types::FromSqlResult,
     types::ToSqlOutput,
     types::{FromSqlError, Value, ValueRef},
-    Connection, Error, OptionalExtension, ToSql, Transaction, TransactionBehavior, NO_PARAMS,
+    Connection, OptionalExtension, ToSql, Transaction, TransactionBehavior, NO_PARAMS,
 };
 use std::{
     collections::{HashMap, HashSet},
@@ -547,6 +547,11 @@
     pub fn seconds(&self) -> i64 {
         self.0
     }
+
+    /// Like i64::checked_sub.
+    pub fn checked_sub(&self, other: &Self) -> Option<Self> {
+        self.0.checked_sub(other.0).map(Self)
+    }
 }
 
 impl ToSql for MonotonicRawTime {
@@ -574,21 +579,27 @@
     }
 
     /// 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 {
+    pub fn satisfies(&self, 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)
+            (sid == self.auth_token.userId || sid == self.auth_token.authenticatorId)
+                && (((auth_type.0 as i32) & (self.auth_token.authenticatorType.0 as i32)) != 0)
         })
     }
 
     /// Returns the auth token wrapped by the AuthTokenEntry
-    pub fn get_auth_token(self) -> HardwareAuthToken {
+    pub fn auth_token(&self) -> &HardwareAuthToken {
+        &self.auth_token
+    }
+
+    /// Returns the auth token wrapped by the AuthTokenEntry
+    pub fn take_auth_token(self) -> HardwareAuthToken {
         self.auth_token
     }
+
+    /// Returns the time that this auth token was received.
+    pub fn time_received(&self) -> MonotonicRawTime {
+        self.time_received
+    }
 }
 
 impl KeystoreDB {
@@ -699,9 +710,6 @@
             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(())
     }
 
@@ -1683,69 +1691,23 @@
         Ok(())
     }
 
-    /// find the auth token entry issued for a time-out token
-    pub fn find_timed_auth_token_entry(
+    /// Find the newest auth token matching the given predicate.
+    pub fn find_auth_token_entry<F>(
         &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)
-        });
+        p: F,
+    ) -> Result<Option<(AuthTokenEntry, MonotonicRawTime)>>
+    where
+        F: Fn(&AuthTokenEntry) -> bool,
+    {
+        self.with_transaction(TransactionBehavior::Deferred, |tx| {
+            let mut stmt = tx
+                .prepare("SELECT * from perboot.authtoken ORDER BY time_received DESC;")
+                .context("Prepare statement failed.")?;
 
-        // 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.")
-        }
-    }
+            let mut rows = stmt.query(NO_PARAMS).context("Failed to query.")?;
 
-    /// 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(
+            while let Some(row) = rows.next().context("Failed to get next row.")? {
+                let entry = AuthTokenEntry::new(
                     HardwareAuthToken {
                         challenge: row.get(1)?,
                         userId: row.get(2)?,
@@ -1755,28 +1717,32 @@
                         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)
+                );
+                if p(&entry) {
+                    return Ok(Some((
+                        entry,
+                        Self::get_last_off_body(tx)
+                            .context("In find_auth_token_entry: Trying to get last off body")?,
+                    )));
+                }
+            }
+            Ok(None)
+        })
+        .context("In find_auth_token_entry.")
     }
 
-    /// 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.")?;
+    /// Insert last_off_body into the metadata table at the initialization of auth token table
+    pub fn insert_last_off_body(&self, last_off_body: MonotonicRawTime) -> Result<()> {
+        self.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
+    /// 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(
@@ -1787,15 +1753,14 @@
         Ok(())
     }
 
-    /// get last_off_body time when finding auth tokens
+    /// 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
+        tx.query_row(
+            "SELECT value from perboot.metadata WHERE key = ?;",
+            params!["last_off_body"],
+            |row| Ok(row.get(0)?),
+        )
+        .context("In get_last_off_body: query_row failed.")
     }
 }
 
@@ -3013,7 +2978,7 @@
     #[test]
     fn test_last_off_body() -> Result<()> {
         let mut db = new_test_db()?;
-        KeystoreDB::insert_last_off_body(&db.conn, MonotonicRawTime::now())?;
+        db.insert_last_off_body(MonotonicRawTime::now())?;
         let tx = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?;
         let last_off_body_1 = KeystoreDB::get_last_off_body(&tx)?;
         tx.commit()?;