List apps affected by secure user ID
Add a method to the Keystore maintenance interface to list the UIDs of
apps that are affected by a given secure user ID.
With this method, it would be possible to tell if removing a given
user's LSKF or enrolling new biometrics will invalidate Keystore keys,
thus affecting some apps.
Bug: 302109605
Test: atest keystore2_test
Change-Id: If5888506e0c72a56eca3339778889c7d8038acc5
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 15ceed6..0a1c547 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -2910,6 +2910,75 @@
})
.context(ks_err!())
}
+
+ /// Returns a list of app UIDs that have keys authenticated by the given secure_user_id
+ /// (for the given user_id).
+ /// This is helpful for finding out which apps will have their keys invalidated when
+ /// the user changes biometrics enrollment or removes their LSKF.
+ pub fn get_app_uids_affected_by_sid(
+ &mut self,
+ user_id: i32,
+ secure_user_id: i64,
+ ) -> Result<Vec<i64>> {
+ let _wp = wd::watch_millis("KeystoreDB::get_app_uids_affected_by_sid", 500);
+
+ let key_ids_and_app_uids = self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ let mut stmt = tx
+ .prepare(&format!(
+ "SELECT id, namespace from persistent.keyentry
+ WHERE key_type = ?
+ AND domain = ?
+ AND cast ( (namespace/{AID_USER_OFFSET}) as int) = ?
+ AND state = ?;",
+ ))
+ .context(concat!(
+ "In get_app_uids_affected_by_sid, ",
+ "failed to prepare the query to find the keys created by apps."
+ ))?;
+
+ let mut rows = stmt
+ .query(params![KeyType::Client, Domain::APP.0 as u32, user_id, KeyLifeCycle::Live,])
+ .context(ks_err!("Failed to query the keys created by apps."))?;
+
+ let mut key_ids_and_app_uids: HashMap<i64, i64> = Default::default();
+ db_utils::with_rows_extract_all(&mut rows, |row| {
+ key_ids_and_app_uids.insert(
+ row.get(0).context("Failed to read key id of a key created by an app.")?,
+ row.get(1).context("Failed to read the app uid")?,
+ );
+ Ok(())
+ })?;
+ Ok(key_ids_and_app_uids).no_gc()
+ })?;
+ let mut app_uids_affected_by_sid: HashSet<i64> = Default::default();
+ for (key_id, app_uid) in key_ids_and_app_uids {
+ // Read the key parameters for each key in its own transaction. It is OK to ignore
+ // an error to get the properties of a particular key since it might have been deleted
+ // under our feet after the previous transaction concluded. If the key was deleted
+ // then it is no longer applicable if it was auth-bound or not.
+ if let Ok(is_key_bound_to_sid) =
+ self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ let params = Self::load_key_parameters(key_id, tx)
+ .context("Failed to load key parameters.")?;
+ // Check if the key is bound to this secure user ID.
+ let is_key_bound_to_sid = params.iter().any(|kp| {
+ matches!(
+ kp.key_parameter_value(),
+ KeyParameterValue::UserSecureID(sid) if *sid == secure_user_id
+ )
+ });
+ Ok(is_key_bound_to_sid).no_gc()
+ })
+ {
+ if is_key_bound_to_sid {
+ app_uids_affected_by_sid.insert(app_uid);
+ }
+ }
+ }
+
+ let app_uids_vec: Vec<i64> = app_uids_affected_by_sid.into_iter().collect();
+ Ok(app_uids_vec)
+ }
}
#[cfg(test)]
@@ -4492,10 +4561,17 @@
.collect::<Result<Vec<_>>>()
}
+ fn make_test_params(max_usage_count: Option<i32>) -> Vec<KeyParameter> {
+ make_test_params_with_sids(max_usage_count, &[42])
+ }
+
// Note: The parameters and SecurityLevel associations are nonsensical. This
// collection is only used to check if the parameters are preserved as expected by the
// database.
- fn make_test_params(max_usage_count: Option<i32>) -> Vec<KeyParameter> {
+ fn make_test_params_with_sids(
+ max_usage_count: Option<i32>,
+ user_secure_ids: &[i64],
+ ) -> Vec<KeyParameter> {
let mut params = vec![
KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::TRUSTED_ENVIRONMENT),
KeyParameter::new(
@@ -4594,7 +4670,6 @@
SecurityLevel::TRUSTED_ENVIRONMENT,
),
KeyParameter::new(KeyParameterValue::UserID(1), SecurityLevel::STRONGBOX),
- KeyParameter::new(KeyParameterValue::UserSecureID(42), SecurityLevel::STRONGBOX),
KeyParameter::new(
KeyParameterValue::NoAuthRequired,
SecurityLevel::TRUSTED_ENVIRONMENT,
@@ -4722,6 +4797,13 @@
SecurityLevel::SOFTWARE,
));
}
+
+ for sid in user_secure_ids.iter() {
+ params.push(KeyParameter::new(
+ KeyParameterValue::UserSecureID(*sid),
+ SecurityLevel::STRONGBOX,
+ ));
+ }
params
}
@@ -4732,6 +4814,17 @@
alias: &str,
max_usage_count: Option<i32>,
) -> Result<KeyIdGuard> {
+ make_test_key_entry_with_sids(db, domain, namespace, alias, max_usage_count, &[42])
+ }
+
+ pub fn make_test_key_entry_with_sids(
+ db: &mut KeystoreDB,
+ domain: Domain,
+ namespace: i64,
+ alias: &str,
+ max_usage_count: Option<i32>,
+ sids: &[i64],
+ ) -> Result<KeyIdGuard> {
let key_id = db.create_key_entry(&domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
let mut blob_metadata = BlobMetaData::new();
blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
@@ -4749,7 +4842,7 @@
db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
- let params = make_test_params(max_usage_count);
+ let params = make_test_params_with_sids(max_usage_count, sids);
db.insert_keyparameter(&key_id, ¶ms)?;
let mut metadata = KeyMetaData::new();
@@ -5418,4 +5511,111 @@
assert_eq!(db.load_key_descriptor(key_id + 1)?, None);
Ok(())
}
+
+ #[test]
+ fn test_get_list_app_uids_for_sid() -> Result<()> {
+ let uid: i32 = 1;
+ let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
+ let first_sid = 667;
+ let second_sid = 669;
+ let first_app_id: i64 = 123 + uid_offset;
+ let second_app_id: i64 = 456 + uid_offset;
+ let third_app_id: i64 = 789 + uid_offset;
+ let unrelated_app_id: i64 = 1011 + uid_offset;
+ let mut db = new_test_db()?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ first_app_id,
+ TEST_ALIAS,
+ None,
+ &[first_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ second_app_id,
+ "alias2",
+ None,
+ &[first_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ second_app_id,
+ TEST_ALIAS,
+ None,
+ &[second_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ third_app_id,
+ "alias3",
+ None,
+ &[second_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ unrelated_app_id,
+ TEST_ALIAS,
+ None,
+ &[],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+
+ let mut first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
+ first_sid_apps.sort();
+ assert_eq!(first_sid_apps, vec![first_app_id, second_app_id]);
+ let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
+ second_sid_apps.sort();
+ assert_eq!(second_sid_apps, vec![second_app_id, third_app_id]);
+ Ok(())
+ }
+
+ #[test]
+ fn test_get_list_app_uids_with_multiple_sids() -> Result<()> {
+ let uid: i32 = 1;
+ let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
+ let first_sid = 667;
+ let second_sid = 669;
+ let third_sid = 772;
+ let first_app_id: i64 = 123 + uid_offset;
+ let second_app_id: i64 = 456 + uid_offset;
+ let mut db = new_test_db()?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ first_app_id,
+ TEST_ALIAS,
+ None,
+ &[first_sid, second_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ second_app_id,
+ "alias2",
+ None,
+ &[second_sid, third_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+
+ let first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
+ assert_eq!(first_sid_apps, vec![first_app_id]);
+
+ let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
+ second_sid_apps.sort();
+ assert_eq!(second_sid_apps, vec![first_app_id, second_app_id]);
+
+ let third_sid_apps = db.get_app_uids_affected_by_sid(uid, third_sid)?;
+ assert_eq!(third_sid_apps, vec![second_app_id]);
+ Ok(())
+ }
}