Implement add auth token functionality.

This CL implements the functionality related to adding an auth token
to auth token table in keystore.

Bug: 171503352
Test: TBD
Change-Id: Ide4150ffcb4bc51b062afa44c82cb0a89b0ee689
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index f9b320f..03523ff 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -48,7 +48,7 @@
 use crate::error::{Error as KsError, ResponseCode};
 use crate::key_parameter::{KeyParameter, Tag};
 use crate::permission::KeyPermSet;
-use anyhow::{anyhow, Context, Result};
+use crate::utils::get_current_time_in_seconds;
 
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType,
@@ -57,13 +57,15 @@
 use android_system_keystore2::aidl::android::system::keystore2::{
     Domain::Domain, KeyDescriptor::KeyDescriptor,
 };
+use anyhow::{anyhow, Context, Result};
 
 use lazy_static::lazy_static;
 #[cfg(not(test))]
 use rand::prelude::random;
 use rusqlite::{
-    params, types::FromSql, types::FromSqlResult, types::ToSqlOutput, types::ValueRef, Connection,
-    OptionalExtension, ToSql, Transaction, TransactionBehavior, NO_PARAMS,
+    params, types::FromSql, types::FromSqlResult, types::ToSqlOutput, types::Value,
+    types::ValueRef, Connection, OptionalExtension, ToSql, Transaction, TransactionBehavior,
+    NO_PARAMS,
 };
 use std::{
     collections::HashSet,
@@ -250,15 +252,44 @@
     conn: Connection,
 }
 
+/// Database representation of the monotonic time retrieved from the system call clock_gettime with
+/// CLOCK_MONOTONIC_RAW. Stores monotonic time as i64 in seconds.
+#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)]
+pub struct MonotonicRawTime(i64);
+
+impl MonotonicRawTime {
+    /// Constructs a new MonotonicRawTime
+    pub fn now() -> Self {
+        Self(get_current_time_in_seconds())
+    }
+
+    /// Returns the integer value of MonotonicRawTime as i64
+    pub fn seconds(&self) -> i64 {
+        self.0
+    }
+}
+
+impl ToSql for MonotonicRawTime {
+    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
+        Ok(ToSqlOutput::Owned(Value::Integer(self.0)))
+    }
+}
+
+impl FromSql for MonotonicRawTime {
+    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
+        Ok(Self(i64::column_result(value)?))
+    }
+}
+
 /// 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,
+    time_received: MonotonicRawTime,
 }
 
 impl AuthTokenEntry {
-    fn new(auth_token: HardwareAuthToken, time_received: i32) -> Self {
+    fn new(auth_token: HardwareAuthToken, time_received: MonotonicRawTime) -> Self {
         AuthTokenEntry { auth_token, time_received }
     }
 
@@ -277,7 +308,7 @@
     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
+        self.time_received.seconds() > other.time_received.seconds()
     }
 
     /// Returns the auth token wrapped by the AuthTokenEntry
@@ -361,6 +392,38 @@
         )
         .context("Failed to initialize \"grant\" table.")?;
 
+        //TODO: only drop the following two perboot tables if this is the first start up
+        //during the boot (b/175716626).
+        // conn.execute("DROP TABLE IF EXISTS perboot.authtoken;", NO_PARAMS)
+        //     .context("Failed to drop perboot.authtoken table")?;
+        conn.execute(
+            "CREATE TABLE IF NOT EXISTS perboot.authtoken (
+                        id INTEGER PRIMARY KEY,
+                        challenge INTEGER,
+                        user_id INTEGER,
+                        auth_id INTEGER,
+                        authenticator_type INTEGER,
+                        timestamp INTEGER,
+                        mac BLOB,
+                        time_received INTEGER,
+                        UNIQUE(user_id, auth_id, authenticator_type));",
+            NO_PARAMS,
+        )
+        .context("Failed to initialize \"authtoken\" table.")?;
+
+        // conn.execute("DROP TABLE IF EXISTS perboot.metadata;", NO_PARAMS)
+        //     .context("Failed to drop perboot.metadata table")?;
+        // metadata table stores certain miscellaneous information required for keystore functioning
+        // during a boot cycle, as key-value pairs.
+        conn.execute(
+            "CREATE TABLE IF NOT EXISTS perboot.metadata (
+                        key TEXT,
+                        value BLOB,
+                        UNIQUE(key));",
+            NO_PARAMS,
+        )
+        .context("Failed to initialize \"metadata\" table.")?;
+
         Ok(())
     }
 
@@ -965,6 +1028,26 @@
             }
         }
     }
+
+    /// Insert or replace the auth token based on the UNIQUE constraint of the auth token table
+    pub fn insert_auth_token(&mut self, auth_token: &HardwareAuthToken) -> Result<()> {
+        self.conn
+            .execute(
+                "INSERT OR REPLACE INTO perboot.authtoken (challenge, user_id, auth_id,
+            authenticator_type, timestamp, mac, time_received) VALUES(?, ?, ?, ?, ?, ?, ?);",
+                params![
+                    auth_token.challenge,
+                    auth_token.userId,
+                    auth_token.authenticatorId,
+                    auth_token.authenticatorType.0 as i32,
+                    auth_token.timestamp.milliSeconds as i64,
+                    auth_token.mac,
+                    MonotonicRawTime::now(),
+                ],
+            )
+            .context("In insert_auth_token: failed to insert auth token into the database")?;
+        Ok(())
+    }
 }
 
 #[cfg(test)]
@@ -978,6 +1061,12 @@
     use crate::key_perm_set;
     use crate::permission::{KeyPerm, KeyPermSet};
     use crate::test::utils::TempDir;
+    use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+        HardwareAuthToken::HardwareAuthToken,
+        HardwareAuthenticatorType::HardwareAuthenticatorType as kmhw_authenticator_type,
+        Timestamp::Timestamp,
+    };
+    use rusqlite::Error;
     use rusqlite::NO_PARAMS;
     use std::cell::RefCell;
     use std::sync::atomic::{AtomicU8, Ordering};
@@ -1024,11 +1113,88 @@
             .prepare("SELECT name from perboot.sqlite_master WHERE type='table' ORDER BY name;")?
             .query_map(params![], |row| row.get(0))?
             .collect::<rusqlite::Result<Vec<String>>>()?;
-        assert_eq!(tables.len(), 0);
+
+        assert_eq!(tables.len(), 2);
+        assert_eq!(tables[0], "authtoken");
+        assert_eq!(tables[1], "metadata");
         Ok(())
     }
 
     #[test]
+    fn test_auth_token_table_invariant() -> Result<()> {
+        let mut db = new_test_db()?;
+        let auth_token1 = HardwareAuthToken {
+            challenge: i64::MAX,
+            userId: 200,
+            authenticatorId: 200,
+            authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+            timestamp: Timestamp { milliSeconds: 500 },
+            mac: String::from("mac").into_bytes(),
+        };
+        db.insert_auth_token(&auth_token1)?;
+        let auth_tokens_returned = get_auth_tokens(&mut db)?;
+        assert_eq!(auth_tokens_returned.len(), 1);
+
+        // insert another auth token with the same values for the columns in the UNIQUE constraint
+        // of the auth token table and different value for timestamp
+        let auth_token2 = HardwareAuthToken {
+            challenge: i64::MAX,
+            userId: 200,
+            authenticatorId: 200,
+            authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+            timestamp: Timestamp { milliSeconds: 600 },
+            mac: String::from("mac").into_bytes(),
+        };
+
+        db.insert_auth_token(&auth_token2)?;
+        let mut auth_tokens_returned = get_auth_tokens(&mut db)?;
+        assert_eq!(auth_tokens_returned.len(), 1);
+
+        if let Some(auth_token) = auth_tokens_returned.pop() {
+            assert_eq!(auth_token.auth_token.timestamp.milliSeconds, 600);
+        }
+
+        // insert another auth token with the different values for the columns in the UNIQUE
+        // constraint of the auth token table
+        let auth_token3 = HardwareAuthToken {
+            challenge: i64::MAX,
+            userId: 201,
+            authenticatorId: 200,
+            authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+            timestamp: Timestamp { milliSeconds: 600 },
+            mac: String::from("mac").into_bytes(),
+        };
+
+        db.insert_auth_token(&auth_token3)?;
+        let auth_tokens_returned = get_auth_tokens(&mut db)?;
+        assert_eq!(auth_tokens_returned.len(), 2);
+
+        Ok(())
+    }
+
+    // utility function for test_auth_token_table_invariant()
+    fn get_auth_tokens(db: &mut KeystoreDB) -> Result<Vec<AuthTokenEntry>> {
+        let mut stmt = db.conn.prepare("SELECT * from perboot.authtoken;")?;
+
+        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)?,
+                ))
+            })?
+            .collect::<Result<Vec<AuthTokenEntry>, Error>>()?;
+        Ok(auth_token_entries)
+    }
+
+    #[test]
     fn test_persistence_for_files() -> Result<()> {
         let temp_dir = TempDir::new("persistent_db_test")?;
         let db = KeystoreDB::new(temp_dir.path())?;
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 1c64833..473686c 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -20,11 +20,12 @@
 use crate::auth_token_handler::AuthTokenHandler;
 use crate::database::AuthTokenEntry;
 use crate::error::Error as KeystoreError;
+use crate::globals::DB;
 use crate::key_parameter::{KeyParameter, KeyParameterValue};
 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,
+    SecurityLevel::SecurityLevel, Tag::Tag, Timestamp::Timestamp,
 };
 use android_system_keystore2::aidl::android::system::keystore2::OperationChallenge::OperationChallenge;
 use anyhow::{Context, Result};
@@ -337,6 +338,45 @@
         let set = self.device_unlocked_set.lock().unwrap();
         !set.contains(&user_id)
     }
+
+    /// Sets the device locked status for the user. This method is called externally.
+    pub fn set_device_locked(&self, user_id: i32, device_locked_status: bool) {
+        // unwrap here because there's no way this mutex guard can be poisoned and
+        // because there's no way to recover, even if it is poisoned.
+        let mut set = self.device_unlocked_set.lock().unwrap();
+        if device_locked_status {
+            set.remove(&user_id);
+        } else {
+            set.insert(user_id);
+        }
+    }
+
+    /// Add this auth token to the database.
+    /// Then check if there is an entry in the op_auth_map, indexed by the challenge of this
+    /// auth token (which could have been inserted during create_operation of an operation on a
+    /// per-op-auth key). If so, add a copy of this auth token to the map indexed by the
+    /// challenge.
+    pub fn add_auth_token(&self, auth_token: HardwareAuthToken) -> Result<()> {
+        //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();
+
+        if op_auth_map_guard.contains_key(&auth_token.challenge) {
+            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(),
+            };
+            op_auth_map_guard.insert(auth_token.challenge, Some(auth_token_copy));
+        }
+
+        DB.with(|db| db.borrow_mut().insert_auth_token(&auth_token))
+            .context("In add_auth_token.")?;
+        Ok(())
+    }
 }
 
 impl Default for Enforcements {
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index 66f3db6..c168486 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -12,6 +12,10 @@
 // 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.
 
@@ -26,6 +30,7 @@
 };
 use anyhow::{anyhow, Context};
 use binder::{FromIBinder, SpIBinder, ThreadState};
+use std::convert::TryFrom;
 use std::sync::Mutex;
 
 /// This function uses its namesake in the permission module and in
@@ -136,3 +141,15 @@
 ) -> Vec<Authorization> {
     parameters.into_iter().map(|p| p.into_authorization()).collect()
 }
+
+/// This returns the current time (in seconds) as an instance of a monotonic clock, by invoking the
+/// system call since Rust does not support getting monotonic time instance as an integer.
+pub fn get_current_time_in_seconds() -> i64 {
+    let mut current_time = libc::timespec { tv_sec: 0, tv_nsec: 0 };
+    // Following unsafe block includes one system call to get monotonic time.
+    // Therefore, it is not considered harmful.
+    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).
+    i64::try_from(current_time.tv_sec).unwrap()
+}