Add getLastAuthTime() to IKeystoreAuthorization

This returns the time (from CLOCK_MONOTONIC_RAW) that the specified user
last authenticated using the given authenticator.

Bug: 303839446
Test: atest keystore2_client_tests
Change-Id: Idd4c477365ffa556b7985d1d926dfa554680ff28
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 1c7eebe..271f94d 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -41,6 +41,7 @@
         "android.security.maintenance-rust",
         "android.security.metrics-rust",
         "android.security.rkp_aidl-rust",
+        "libaconfig_android_hardware_biometrics_rust",
         "libanyhow",
         "libbinder_rs",
         "libkeystore2_aaid-rust",
@@ -161,3 +162,9 @@
     crate_name: "keystore2_flags",
     aconfig_declarations: "keystore2_flags",
 }
+
+rust_aconfig_library {
+    name: "libaconfig_android_hardware_biometrics_rust",
+    crate_name: "aconfig_android_hardware_biometrics_rust",
+    aconfig_declarations: "android.hardware.biometrics.flags-aconfig",
+}
diff --git a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
index e3b7d11..b31a51e 100644
--- a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
+++ b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
@@ -15,6 +15,7 @@
 package android.security.authorization;
 
 import android.hardware.security.keymint.HardwareAuthToken;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
 import android.security.authorization.LockScreenEvent;
 import android.security.authorization.AuthorizationTokens;
 
@@ -108,4 +109,13 @@
      */
     AuthorizationTokens getAuthTokensForCredStore(in long challenge, in long secureUserId,
      in long authTokenMaxAgeMillis);
+
+    /**
+     * Returns the last successful authentication time since boot for the given user with any of the
+     * given authenticator types. This is determined by inspecting the cached auth tokens.
+     *
+     * ## Error conditions:
+     * `ResponseCode::NO_AUTH_TOKEN_FOUND` - if there is no matching authentication token found
+     */
+    long getLastAuthTime(in long secureUserId, in HardwareAuthenticatorType[] authTypes);
 }
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index f840189..7416b7f 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -14,27 +14,30 @@
 
 //! This module implements IKeystoreAuthorization AIDL interface.
 
-use crate::ks_err;
-use crate::error::Error as KeystoreError;
 use crate::error::anyhow_error_to_cstring;
-use crate::globals::{ENFORCEMENTS, SUPER_KEY, DB, LEGACY_IMPORTER};
+use crate::error::Error as KeystoreError;
+use crate::globals::{DB, ENFORCEMENTS, LEGACY_IMPORTER, SUPER_KEY};
+use crate::ks_err;
 use crate::permission::KeystorePerm;
 use crate::utils::{check_keystore_permission, watchdog as wd};
+use aconfig_android_hardware_biometrics_rust;
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    HardwareAuthToken::HardwareAuthToken,
+    HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType,
 };
-use android_security_authorization::binder::{BinderFeatures, ExceptionCode, Interface, Result as BinderResult,
-    Strong, Status as BinderStatus};
 use android_security_authorization::aidl::android::security::authorization::{
-    IKeystoreAuthorization::BnKeystoreAuthorization, IKeystoreAuthorization::IKeystoreAuthorization,
-    LockScreenEvent::LockScreenEvent, AuthorizationTokens::AuthorizationTokens,
+    AuthorizationTokens::AuthorizationTokens, IKeystoreAuthorization::BnKeystoreAuthorization,
+    IKeystoreAuthorization::IKeystoreAuthorization, LockScreenEvent::LockScreenEvent,
     ResponseCode::ResponseCode,
 };
-use android_system_keystore2::aidl::android::system::keystore2::{
-    ResponseCode::ResponseCode as KsResponseCode};
+use android_security_authorization::binder::{
+    BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status as BinderStatus,
+    Strong,
+};
+use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode as KsResponseCode;
 use anyhow::{Context, Result};
 use keystore2_crypto::Password;
 use keystore2_selinux as selinux;
+use std::ffi::CString;
 
 /// This is the Authorization error type, it wraps binder exceptions and the
 /// Authorization ResponseCode
@@ -226,6 +229,31 @@
             ENFORCEMENTS.get_auth_tokens(challenge, secure_user_id, auth_token_max_age_millis)?;
         Ok(AuthorizationTokens { authToken: auth_token, timestampToken: ts_token })
     }
+
+    fn get_last_auth_time(
+        &self,
+        secure_user_id: i64,
+        auth_types: &[HardwareAuthenticatorType],
+    ) -> Result<i64> {
+        // Check keystore permission.
+        check_keystore_permission(KeystorePerm::GetLastAuthTime).context(ks_err!())?;
+
+        let mut max_time: i64 = -1;
+        for auth_type in auth_types.iter() {
+            if let Some(time) = ENFORCEMENTS.get_last_auth_time(secure_user_id, *auth_type) {
+                if time.milliseconds() > max_time {
+                    max_time = time.milliseconds();
+                }
+            }
+        }
+
+        if max_time >= 0 {
+            Ok(max_time)
+        } else {
+            Err(Error::Rc(ResponseCode::NO_AUTH_TOKEN_FOUND))
+                .context(ks_err!("No auth token found"))
+        }
+    }
 }
 
 impl Interface for AuthorizationManager {}
@@ -274,4 +302,19 @@
             Ok,
         )
     }
+
+    fn getLastAuthTime(
+        &self,
+        secure_user_id: i64,
+        auth_types: &[HardwareAuthenticatorType],
+    ) -> binder::Result<i64> {
+        if aconfig_android_hardware_biometrics_rust::last_authentication_time() {
+            map_or_log_err(self.get_last_auth_time(secure_user_id, auth_types), Ok)
+        } else {
+            Err(BinderStatus::new_service_specific_error(
+                ResponseCode::PERMISSION_DENIED.0,
+                Some(CString::new("Feature is not enabled.").unwrap().as_c_str()),
+            ))
+        }
+    }
 }
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index bb23b82..95e8837 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -845,6 +845,24 @@
             get_timestamp_token(challenge).context(ks_err!("Error in getting timestamp token."))?;
         Ok((auth_token, tst))
     }
+
+    /// Finds the most recent received time for an auth token that matches the given secure user id and authenticator
+    pub fn get_last_auth_time(
+        &self,
+        secure_user_id: i64,
+        auth_type: HardwareAuthenticatorType,
+    ) -> Option<MonotonicRawTime> {
+        let sids: Vec<i64> = vec![secure_user_id];
+
+        let result =
+            Self::find_auth_token(|entry: &AuthTokenEntry| entry.satisfies(&sids, auth_type));
+
+        if let Some((auth_token_entry, _)) = result {
+            Some(auth_token_entry.time_received())
+        } else {
+            None
+        }
+    }
 }
 
 // TODO: Add tests to enforcement module (b/175578618).
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index 35d6988..bc73744 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -149,6 +149,9 @@
         /// Checked on calls to IRemotelyProvisionedKeyPool::getAttestationKey
         #[selinux(name = get_attestation_key)]
         GetAttestationKey,
+        /// Checked on IKeystoreAuthorization::getLastAuthTime() is called.
+        #[selinux(name = get_last_auth_time)]
+        GetLastAuthTime,
     }
 );
 
diff --git a/keystore2/test_utils/Android.bp b/keystore2/test_utils/Android.bp
index a3c40cb..c16aa12 100644
--- a/keystore2/test_utils/Android.bp
+++ b/keystore2/test_utils/Android.bp
@@ -39,6 +39,7 @@
         "libserde",
         "libserde_cbor",
         "libthiserror",
+        "android.security.authorization-rust",
     ],
     static_libs: [
         "libkeystore2_ffi_test_utils",
diff --git a/keystore2/test_utils/lib.rs b/keystore2/test_utils/lib.rs
index a373a2f..8394ca1 100644
--- a/keystore2/test_utils/lib.rs
+++ b/keystore2/test_utils/lib.rs
@@ -20,6 +20,7 @@
 use std::{env::temp_dir, ops::Deref};
 
 use android_system_keystore2::aidl::android::system::keystore2::IKeystoreService::IKeystoreService;
+use android_security_authorization::aidl::android::security::authorization::IKeystoreAuthorization::IKeystoreAuthorization;
 
 pub mod authorizations;
 pub mod ffi_test_utils;
@@ -27,6 +28,7 @@
 pub mod run_as;
 
 static KS2_SERVICE_NAME: &str = "android.system.keystore2.IKeystoreService/default";
+static AUTH_SERVICE_NAME: &str = "android.security.authorization";
 
 /// Represents the lifecycle of a temporary directory for testing.
 #[derive(Debug)]
@@ -116,3 +118,8 @@
 pub fn get_keystore_service() -> binder::Strong<dyn IKeystoreService> {
     binder::get_interface(KS2_SERVICE_NAME).unwrap()
 }
+
+/// Get Keystore auth service.
+pub fn get_keystore_auth_service() -> binder::Strong<dyn IKeystoreAuthorization> {
+    binder::get_interface(AUTH_SERVICE_NAME).unwrap()
+}
diff --git a/keystore2/tests/Android.bp b/keystore2/tests/Android.bp
index d33bf9f..e09b224 100644
--- a/keystore2/tests/Android.bp
+++ b/keystore2/tests/Android.bp
@@ -35,6 +35,9 @@
     test_config: "AndroidTest.xml",
 
     rustlibs: [
+        "android.hardware.security.secureclock-V1-rust",
+        "android.security.authorization-rust",
+        "libaconfig_android_hardware_biometrics_rust",
         "libbinder_rs",
         "libkeystore2_test_utils",
         "libnix",
diff --git a/keystore2/tests/keystore2_client_authorizations_tests.rs b/keystore2/tests/keystore2_client_authorizations_tests.rs
index 22a23c8..279ecd7 100644
--- a/keystore2/tests/keystore2_client_authorizations_tests.rs
+++ b/keystore2/tests/keystore2_client_authorizations_tests.rs
@@ -25,8 +25,16 @@
     KeyMetadata::KeyMetadata, ResponseCode::ResponseCode,
 };
 
+use aconfig_android_hardware_biometrics_rust;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    HardwareAuthToken::HardwareAuthToken,
+    HardwareAuthenticatorType::HardwareAuthenticatorType
+};
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::Timestamp::Timestamp;
+
 use keystore2_test_utils::{
-    authorizations, get_keystore_service, key_generations, key_generations::Error,
+    authorizations, get_keystore_auth_service, get_keystore_service, key_generations,
+    key_generations::Error,
 };
 
 use crate::keystore2_client_test_utils::{
@@ -902,3 +910,57 @@
     assert_eq!(Error::Km(ErrorCode::INVALID_KEY_BLOB), result.unwrap_err());
     delete_app_key(&keystore2, attest_alias).unwrap();
 }
+
+fn add_hardware_token(auth_type: HardwareAuthenticatorType) {
+    let keystore_auth = get_keystore_auth_service();
+
+    let token = HardwareAuthToken {
+        challenge: 0,
+        userId: 0,
+        authenticatorId: 0,
+        authenticatorType: auth_type,
+        timestamp: Timestamp { milliSeconds: 500 },
+        mac: vec![],
+    };
+    keystore_auth.addAuthToken(&token).unwrap();
+}
+
+#[test]
+fn keystore2_flagged_off_get_last_auth_password_permission_denied() {
+    if aconfig_android_hardware_biometrics_rust::last_authentication_time() {
+        return;
+    }
+
+    let keystore_auth = get_keystore_auth_service();
+
+    let result = keystore_auth.getLastAuthTime(0, &[HardwareAuthenticatorType::PASSWORD]);
+
+    assert!(result.is_err());
+    assert_eq!(result.unwrap_err().service_specific_error(), ResponseCode::PERMISSION_DENIED.0);
+}
+
+#[test]
+fn keystore2_flagged_on_get_last_auth_password_success() {
+    if !aconfig_android_hardware_biometrics_rust::last_authentication_time() {
+        return;
+    }
+
+    let keystore_auth = get_keystore_auth_service();
+
+    add_hardware_token(HardwareAuthenticatorType::PASSWORD);
+    assert!(keystore_auth.getLastAuthTime(0, &[HardwareAuthenticatorType::PASSWORD]).unwrap() > 0);
+}
+
+#[test]
+fn keystore2_flagged_on_get_last_auth_fingerprint_success() {
+    if !aconfig_android_hardware_biometrics_rust::last_authentication_time() {
+        return;
+    }
+
+    let keystore_auth = get_keystore_auth_service();
+
+    add_hardware_token(HardwareAuthenticatorType::FINGERPRINT);
+    assert!(
+        keystore_auth.getLastAuthTime(0, &[HardwareAuthenticatorType::FINGERPRINT]).unwrap() > 0
+    );
+}