Make user authentication tests more realistic

- Use different SIDs for each test case to reduce the chances of HATs
  being re-used between test cases.
- Make the auth-per-op failure case closer to the success case.
- Fix auth-type for fake biometric HAT.
- Allow more error codes on old devices.

Test: keystore2_client_tests
Bug: 374935444
Change-Id: Ib5bc1238fb090501b7e527cb624e69b93686f778
diff --git a/keystore2/tests/user_auth.rs b/keystore2/tests/user_auth.rs
index 187256b..789f54b 100644
--- a/keystore2/tests/user_auth.rs
+++ b/keystore2/tests/user_auth.rs
@@ -14,7 +14,9 @@
 
 //! Tests for user authentication interactions (via `IKeystoreAuthorization`).
 
-use crate::keystore2_client_test_utils::{BarrierReached, BarrierReachedWithData};
+use crate::keystore2_client_test_utils::{
+    BarrierReached, BarrierReachedWithData, get_vsr_api_level
+};
 use android_security_authorization::aidl::android::security::authorization::{
     IKeystoreAuthorization::IKeystoreAuthorization
 };
@@ -59,12 +61,13 @@
 ];
 /// Gatekeeper password.
 static GK_PASSWORD: &[u8] = b"correcthorsebatterystaple";
-/// Fake SID value corresponding to Gatekeeper.
-static GK_FAKE_SID: i64 = 123456;
-/// Fake SID value corresponding to a biometric authenticator.
-static BIO_FAKE_SID1: i64 = 345678;
-/// Fake SID value corresponding to a biometric authenticator.
-static BIO_FAKE_SID2: i64 = 456789;
+/// Fake SID base value corresponding to Gatekeeper.  Individual tests use different SIDs to reduce
+/// the chances of cross-contamination, calculated statically (because each test is forked into a
+/// separate process).
+static GK_FAKE_SID_BASE: i64 = 123400;
+/// Fake SID base value corresponding to a biometric authenticator.  Individual tests use different
+/// SIDs to reduce the chances of cross-contamination.
+static BIO_FAKE_SID_BASE: i64 = 345600;
 
 const WEAK_UNLOCK_ENABLED: bool = true;
 const WEAK_UNLOCK_DISABLED: bool = false;
@@ -171,6 +174,8 @@
 
 #[test]
 fn test_auth_bound_timeout_with_gk() {
+    let bio_fake_sid1 = BIO_FAKE_SID_BASE + 1;
+    let bio_fake_sid2 = BIO_FAKE_SID_BASE + 2;
     type Barrier = BarrierReachedWithData<Option<i64>>;
     android_logger::init_once(
         android_logger::Config::default()
@@ -199,8 +204,8 @@
             ks2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).context("no TEE")?;
         let params = AuthSetBuilder::new()
             .user_secure_id(gk_sid)
-            .user_secure_id(BIO_FAKE_SID1)
-            .user_secure_id(BIO_FAKE_SID2)
+            .user_secure_id(bio_fake_sid1)
+            .user_secure_id(bio_fake_sid2)
             .user_auth_type(HardwareAuthenticatorType::ANY)
             .auth_timeout(3)
             .algorithm(Algorithm::EC)
@@ -214,7 +219,7 @@
                 &KeyDescriptor {
                     domain: Domain::APP,
                     nspace: -1,
-                    alias: Some("auth-bound-timeout".to_string()),
+                    alias: Some("auth-bound-timeout-1".to_string()),
                     blob: None,
                 },
                 None,
@@ -223,7 +228,7 @@
                 b"entropy",
             )
             .context("key generation failed")?;
-        info!("A: created auth-timeout key {key:?}");
+        info!("A: created auth-timeout key {key:?} bound to sids {gk_sid}, {bio_fake_sid1}, {bio_fake_sid2}");
 
         // No HATs so cannot create an operation using the key.
         let params = AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256);
@@ -279,7 +284,7 @@
 
     // Lock and unlock to ensure super keys are already created.
     auth_service
-        .onDeviceLocked(user_id, &[BIO_FAKE_SID1, BIO_FAKE_SID2], WEAK_UNLOCK_DISABLED)
+        .onDeviceLocked(user_id, &[bio_fake_sid1, bio_fake_sid2], WEAK_UNLOCK_DISABLED)
         .unwrap();
     auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
 
@@ -304,6 +309,9 @@
 
 #[test]
 fn test_auth_bound_timeout_failure() {
+    let gk_fake_sid = GK_FAKE_SID_BASE + 1;
+    let bio_fake_sid1 = BIO_FAKE_SID_BASE + 3;
+    let bio_fake_sid2 = BIO_FAKE_SID_BASE + 4;
     android_logger::init_once(
         android_logger::Config::default()
             .with_tag("keystore2_client_tests")
@@ -323,8 +331,8 @@
         let sec_level =
             ks2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).context("no TEE")?;
         let params = AuthSetBuilder::new()
-            .user_secure_id(BIO_FAKE_SID1)
-            .user_secure_id(BIO_FAKE_SID2)
+            .user_secure_id(bio_fake_sid1)
+            .user_secure_id(bio_fake_sid2)
             .user_auth_type(HardwareAuthenticatorType::ANY)
             .auth_timeout(3)
             .algorithm(Algorithm::EC)
@@ -338,7 +346,7 @@
                 &KeyDescriptor {
                     domain: Domain::APP,
                     nspace: -1,
-                    alias: Some("auth-bound-timeout".to_string()),
+                    alias: Some("auth-bound-timeout-2".to_string()),
                     blob: None,
                 },
                 None,
@@ -347,7 +355,7 @@
                 b"entropy",
             )
             .context("key generation failed")?;
-        info!("A: created auth-timeout key {key:?}");
+        info!("A: created auth-timeout key {key:?} bound to sids {bio_fake_sid1}, {bio_fake_sid2}");
 
         // No HATs so cannot create an operation using the key.
         let params = AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256);
@@ -361,7 +369,12 @@
         reader.recv();
 
         let result = sec_level.createOperation(&key, &params, UNFORCED);
-        expect_km_error!(&result, ErrorCode::KEY_USER_NOT_AUTHENTICATED);
+        expect!(result.is_err());
+        if get_vsr_api_level() >= 35 {
+            // Older devices may report an incorrect error code when presented with an invalid auth
+            // token.
+            expect_km_error!(&result, ErrorCode::KEY_USER_NOT_AUTHENTICATED);
+        }
         info!("B: failed auth-bound operation (HAT is invalid) as expected {result:?}");
 
         writer.send(&BarrierReached {}); // B done.
@@ -395,10 +408,10 @@
 
     // Lock and unlock to ensure super keys are already created.
     auth_service
-        .onDeviceLocked(user_id, &[BIO_FAKE_SID1, BIO_FAKE_SID2], WEAK_UNLOCK_DISABLED)
+        .onDeviceLocked(user_id, &[bio_fake_sid1, bio_fake_sid2], WEAK_UNLOCK_DISABLED)
         .unwrap();
     auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
-    auth_service.addAuthToken(&fake_lskf_token(GK_FAKE_SID)).unwrap();
+    auth_service.addAuthToken(&fake_lskf_token(gk_fake_sid)).unwrap();
 
     info!("trigger child process action A and wait for completion");
     child_handle.send(&BarrierReached {});
@@ -406,7 +419,7 @@
 
     // Unlock with password and a fake auth token that matches the key
     auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
-    auth_service.addAuthToken(&fake_bio_lskf_token(GK_FAKE_SID, BIO_FAKE_SID1)).unwrap();
+    auth_service.addAuthToken(&fake_bio_lskf_token(gk_fake_sid, bio_fake_sid1)).unwrap();
 
     info!("trigger child process action B and wait for completion");
     child_handle.send(&BarrierReached {});
@@ -421,6 +434,8 @@
 
 #[test]
 fn test_auth_bound_per_op_with_gk() {
+    let bio_fake_sid1 = BIO_FAKE_SID_BASE + 5;
+    let bio_fake_sid2 = BIO_FAKE_SID_BASE + 6;
     type Barrier = BarrierReachedWithData<Option<i64>>;
     android_logger::init_once(
         android_logger::Config::default()
@@ -449,7 +464,7 @@
             ks2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).context("no TEE")?;
         let params = AuthSetBuilder::new()
             .user_secure_id(gk_sid)
-            .user_secure_id(BIO_FAKE_SID1)
+            .user_secure_id(bio_fake_sid1)
             .user_auth_type(HardwareAuthenticatorType::ANY)
             .algorithm(Algorithm::EC)
             .purpose(KeyPurpose::SIGN)
@@ -462,7 +477,7 @@
                 &KeyDescriptor {
                     domain: Domain::APP,
                     nspace: -1,
-                    alias: Some("auth-per-op".to_string()),
+                    alias: Some("auth-per-op-1".to_string()),
                     blob: None,
                 },
                 None,
@@ -471,7 +486,7 @@
                 b"entropy",
             )
             .context("key generation failed")?;
-        info!("A: created auth-per-op key {key:?}");
+        info!("A: created auth-per-op key {key:?} bound to sids {gk_sid}, {bio_fake_sid1}");
 
         // We can create an operation using the key...
         let params = AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256);
@@ -530,7 +545,7 @@
 
     // Lock and unlock to ensure super keys are already created.
     auth_service
-        .onDeviceLocked(user_id, &[BIO_FAKE_SID1, BIO_FAKE_SID2], WEAK_UNLOCK_DISABLED)
+        .onDeviceLocked(user_id, &[bio_fake_sid1, bio_fake_sid2], WEAK_UNLOCK_DISABLED)
         .unwrap();
     auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
 
@@ -554,8 +569,10 @@
 }
 
 #[test]
-fn test_auth_bound_per_op_failure() {
-    type Barrier = BarrierReachedWithData<i64>;
+fn test_auth_bound_per_op_with_gk_failure() {
+    let bio_fake_sid1 = BIO_FAKE_SID_BASE + 7;
+    let bio_fake_sid2 = BIO_FAKE_SID_BASE + 8;
+    type Barrier = BarrierReachedWithData<Option<i64>>;
     android_logger::init_once(
         android_logger::Config::default()
             .with_tag("keystore2_client_tests")
@@ -566,17 +583,24 @@
                          writer: &mut ChannelWriter<Barrier>|
           -> Result<(), run_as::Error> {
         // Now we're in a new process, wait to be notified before starting.
-        reader.recv();
+        let gk_sid: i64 = match reader.recv().0 {
+            Some(sid) => sid,
+            None => {
+                // There is no AIDL Gatekeeper available, so abandon the test.  It would be nice to
+                // know this before starting the child process, but finding it out requires Binder,
+                // which can't be used until after the child has forked.
+                return Ok(());
+            }
+        };
 
         // Action A: create a new auth-bound key which requires auth-per-operation (because
         // AUTH_TIMEOUT is not specified), and fail to finish an operation using it.
         let ks2 = get_keystore_service();
-
         let sec_level =
             ks2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).context("no TEE")?;
         let params = AuthSetBuilder::new()
-            .user_secure_id(GK_FAKE_SID)
-            .user_secure_id(BIO_FAKE_SID1)
+            .user_secure_id(gk_sid)
+            .user_secure_id(bio_fake_sid1)
             .user_auth_type(HardwareAuthenticatorType::ANY)
             .algorithm(Algorithm::EC)
             .purpose(KeyPurpose::SIGN)
@@ -589,7 +613,7 @@
                 &KeyDescriptor {
                     domain: Domain::APP,
                     nspace: -1,
-                    alias: Some("auth-per-op".to_string()),
+                    alias: Some("auth-per-op-2".to_string()),
                     blob: None,
                 },
                 None,
@@ -598,7 +622,7 @@
                 b"entropy",
             )
             .context("key generation failed")?;
-        info!("A: created auth-per-op key {key:?}");
+        info!("A: created auth-per-op key {key:?} bound to sids {gk_sid}, {bio_fake_sid1}");
 
         // We can create an operation using the key...
         let params = AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256);
@@ -613,7 +637,7 @@
         expect_km_error!(&result, ErrorCode::KEY_USER_NOT_AUTHENTICATED);
         info!("A: failed auth-per-op op (no HAT) as expected {result:?}");
 
-        writer.send(&Barrier::new(0)); // A done.
+        writer.send(&Barrier::new(None)); // A done.
 
         // Action B: fail again when an irrelevant HAT is available.
         reader.recv();
@@ -629,7 +653,7 @@
         expect_km_error!(&result, ErrorCode::KEY_USER_NOT_AUTHENTICATED);
         info!("B: failed auth-per-op op (HAT is not per-op) as expected {result:?}");
 
-        writer.send(&Barrier::new(0)); // B done.
+        writer.send(&Barrier::new(None)); // B done.
 
         // Action C: start an operation and pass out the challenge
         reader.recv();
@@ -638,7 +662,7 @@
             .expect("failed to create auth-per-op operation");
         let op = result.iOperation.context("no operation in result")?;
         info!("C: created auth-per-op operation, got challenge {:?}", result.operationChallenge);
-        writer.send(&Barrier::new(result.operationChallenge.unwrap().challenge)); // C done.
+        writer.send(&Barrier::new(Some(result.operationChallenge.unwrap().challenge))); // C done.
 
         // Action D: finishing the operation still fails because the per-op HAT
         // is invalid (the HMAC signature is faked and so the secure world
@@ -647,7 +671,7 @@
         let result = op.finish(Some(b"data"), None);
         expect_km_error!(&result, ErrorCode::KEY_USER_NOT_AUTHENTICATED);
         info!("D: failed auth-per-op op (HAT is per-op but invalid) as expected {result:?}");
-        writer.send(&Barrier::new(0)); // D done.
+        writer.send(&Barrier::new(None)); // D done.
 
         Ok(())
     };
@@ -664,37 +688,43 @@
     // user.
     let _ks2 = get_keystore_service();
     let user = TestUser::new();
+    if user.gk.is_none() {
+        // Can't run this test if there's no AIDL Gatekeeper.
+        child_handle.send(&Barrier::new(None));
+        assert_eq!(child_handle.get_result(), Ok(()), "child process failed");
+        return;
+    }
     let user_id = user.id;
     let auth_service = get_authorization();
 
     // Lock and unlock to ensure super keys are already created.
     auth_service
-        .onDeviceLocked(user_id, &[BIO_FAKE_SID1, BIO_FAKE_SID2], WEAK_UNLOCK_DISABLED)
+        .onDeviceLocked(user_id, &[bio_fake_sid1, bio_fake_sid2], WEAK_UNLOCK_DISABLED)
         .unwrap();
     auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
-    auth_service.addAuthToken(&fake_lskf_token(GK_FAKE_SID)).unwrap();
 
     info!("trigger child process action A and wait for completion");
-    child_handle.send(&Barrier::new(0));
+    let gk_sid = user.gk_sid.unwrap();
+    child_handle.send(&Barrier::new(Some(gk_sid)));
     child_handle.recv_or_die();
 
     // Unlock with password and a fake auth token.
     auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
-    auth_service.addAuthToken(&fake_lskf_token(GK_FAKE_SID)).unwrap();
+    auth_service.addAuthToken(&fake_lskf_token(gk_sid)).unwrap();
 
     info!("trigger child process action B and wait for completion");
-    child_handle.send(&Barrier::new(0));
+    child_handle.send(&Barrier::new(None));
     child_handle.recv_or_die();
 
     info!("trigger child process action C and wait for completion");
-    child_handle.send(&Barrier::new(0));
-    let challenge = child_handle.recv_or_die().0;
+    child_handle.send(&Barrier::new(None));
+    let challenge = child_handle.recv_or_die().0.expect("no challenge");
 
     // Add a fake auth token with the challenge value.
-    auth_service.addAuthToken(&fake_lskf_token_with_challenge(GK_FAKE_SID, challenge)).unwrap();
+    auth_service.addAuthToken(&fake_lskf_token_with_challenge(gk_sid, challenge)).unwrap();
 
     info!("trigger child process action D and wait for completion");
-    child_handle.send(&Barrier::new(0));
+    child_handle.send(&Barrier::new(None));
     child_handle.recv_or_die();
 
     assert_eq!(child_handle.get_result(), Ok(()), "child process failed");
@@ -702,6 +732,9 @@
 
 #[test]
 fn test_unlocked_device_required() {
+    let gk_fake_sid = GK_FAKE_SID_BASE + 3;
+    let bio_fake_sid1 = BIO_FAKE_SID_BASE + 9;
+    let bio_fake_sid2 = BIO_FAKE_SID_BASE + 10;
     android_logger::init_once(
         android_logger::Config::default()
             .with_tag("keystore2_client_tests")
@@ -804,10 +837,10 @@
 
     // Lock and unlock to ensure super keys are already created.
     auth_service
-        .onDeviceLocked(user_id, &[BIO_FAKE_SID1, BIO_FAKE_SID2], WEAK_UNLOCK_DISABLED)
+        .onDeviceLocked(user_id, &[bio_fake_sid1, bio_fake_sid2], WEAK_UNLOCK_DISABLED)
         .unwrap();
     auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
-    auth_service.addAuthToken(&fake_lskf_token(GK_FAKE_SID)).unwrap();
+    auth_service.addAuthToken(&fake_lskf_token(gk_fake_sid)).unwrap();
 
     info!("trigger child process action A while unlocked and wait for completion");
     child_handle.send(&BarrierReached {});
@@ -815,7 +848,7 @@
 
     // Move to locked and don't allow weak unlock, so super keys are wiped.
     auth_service
-        .onDeviceLocked(user_id, &[BIO_FAKE_SID1, BIO_FAKE_SID2], WEAK_UNLOCK_DISABLED)
+        .onDeviceLocked(user_id, &[bio_fake_sid1, bio_fake_sid2], WEAK_UNLOCK_DISABLED)
         .unwrap();
 
     info!("trigger child process action B while locked and wait for completion");
@@ -824,7 +857,7 @@
 
     // Unlock with password => loads super key from database.
     auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
-    auth_service.addAuthToken(&fake_lskf_token(GK_FAKE_SID)).unwrap();
+    auth_service.addAuthToken(&fake_lskf_token(gk_fake_sid)).unwrap();
 
     info!("trigger child process action C while lskf-unlocked and wait for completion");
     child_handle.send(&BarrierReached {});
@@ -832,7 +865,7 @@
 
     // Move to locked and allow weak unlock, then do a weak unlock.
     auth_service
-        .onDeviceLocked(user_id, &[BIO_FAKE_SID1, BIO_FAKE_SID2], WEAK_UNLOCK_ENABLED)
+        .onDeviceLocked(user_id, &[bio_fake_sid1, bio_fake_sid2], WEAK_UNLOCK_ENABLED)
         .unwrap();
     auth_service.onDeviceUnlocked(user_id, None).unwrap();
 
@@ -866,7 +899,7 @@
         challenge: 0,
         userId: gk_sid,
         authenticatorId: bio_sid,
-        authenticatorType: HardwareAuthenticatorType::PASSWORD,
+        authenticatorType: HardwareAuthenticatorType::FINGERPRINT,
         timestamp: Timestamp { milliSeconds: 123 },
         mac: vec![1, 2, 3],
     }