Move large test modules into separate files
This complies better with the Android Rust style guide, and makes it
easier to navigate the code.
Test: keystore2_test libwatchdog_rs.test librkpd_client.test
Change-Id: Iceb49e309af66ec16d31da66b328936b0312061a
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index a9a1c4b..754dd9c 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -45,6 +45,9 @@
pub(crate) mod utils;
mod versioning;
+#[cfg(test)]
+pub mod tests;
+
use crate::gc::Gc;
use crate::impl_metadata; // This is in database/utils.rs
use crate::key_parameter::{KeyParameter, KeyParameterValue, Tag};
@@ -2864,2620 +2867,3 @@
Ok(app_uids_vec)
}
}
-
-#[cfg(test)]
-pub mod tests {
-
- use super::*;
- use crate::key_parameter::{
- Algorithm, BlockMode, Digest, EcCurve, HardwareAuthenticatorType, KeyOrigin, KeyParameter,
- KeyParameterValue, KeyPurpose, PaddingMode, SecurityLevel,
- };
- use crate::key_perm_set;
- use crate::permission::{KeyPerm, KeyPermSet};
- use crate::super_key::{SuperKeyManager, USER_AFTER_FIRST_UNLOCK_SUPER_KEY, SuperEncryptionAlgorithm, SuperKeyType};
- use keystore2_test_utils::TempDir;
- use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- HardwareAuthToken::HardwareAuthToken,
- HardwareAuthenticatorType::HardwareAuthenticatorType as kmhw_authenticator_type,
- };
- use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
- Timestamp::Timestamp,
- };
- use std::cell::RefCell;
- use std::collections::BTreeMap;
- use std::fmt::Write;
- use std::sync::atomic::{AtomicU8, Ordering};
- use std::sync::Arc;
- use std::thread;
- use std::time::{Duration, SystemTime};
- use crate::utils::AesGcm;
- #[cfg(disabled)]
- use std::time::Instant;
-
- pub fn new_test_db() -> Result<KeystoreDB> {
- let conn = KeystoreDB::make_connection("file::memory:")?;
-
- let mut db = KeystoreDB { conn, gc: None, perboot: Arc::new(perboot::PerbootDB::new()) };
- db.with_transaction(Immediate("TX_new_test_db"), |tx| {
- KeystoreDB::init_tables(tx).context("Failed to initialize tables.").no_gc()
- })?;
- Ok(db)
- }
-
- fn rebind_alias(
- db: &mut KeystoreDB,
- newid: &KeyIdGuard,
- alias: &str,
- domain: Domain,
- namespace: i64,
- ) -> Result<bool> {
- db.with_transaction(Immediate("TX_rebind_alias"), |tx| {
- KeystoreDB::rebind_alias(tx, newid, alias, &domain, &namespace, KeyType::Client).no_gc()
- })
- .context(ks_err!())
- }
-
- #[test]
- fn datetime() -> Result<()> {
- let conn = Connection::open_in_memory()?;
- conn.execute("CREATE TABLE test (ts DATETIME);", [])?;
- let now = SystemTime::now();
- let duration = Duration::from_secs(1000);
- let then = now.checked_sub(duration).unwrap();
- let soon = now.checked_add(duration).unwrap();
- conn.execute(
- "INSERT INTO test (ts) VALUES (?), (?), (?);",
- params![DateTime::try_from(now)?, DateTime::try_from(then)?, DateTime::try_from(soon)?],
- )?;
- let mut stmt = conn.prepare("SELECT ts FROM test ORDER BY ts ASC;")?;
- let mut rows = stmt.query([])?;
- assert_eq!(DateTime::try_from(then)?, rows.next()?.unwrap().get(0)?);
- assert_eq!(DateTime::try_from(now)?, rows.next()?.unwrap().get(0)?);
- assert_eq!(DateTime::try_from(soon)?, rows.next()?.unwrap().get(0)?);
- assert!(rows.next()?.is_none());
- assert!(DateTime::try_from(then)? < DateTime::try_from(now)?);
- assert!(DateTime::try_from(then)? < DateTime::try_from(soon)?);
- assert!(DateTime::try_from(now)? < DateTime::try_from(soon)?);
- Ok(())
- }
-
- // Ensure that we're using the "injected" random function, not the real one.
- #[test]
- fn test_mocked_random() {
- let rand1 = random();
- let rand2 = random();
- let rand3 = random();
- if rand1 == rand2 {
- assert_eq!(rand2 + 1, rand3);
- } else {
- assert_eq!(rand1 + 1, rand2);
- assert_eq!(rand2, rand3);
- }
- }
-
- // Test that we have the correct tables.
- #[test]
- fn test_tables() -> Result<()> {
- let db = new_test_db()?;
- let tables = db
- .conn
- .prepare("SELECT name from persistent.sqlite_master WHERE type='table' ORDER BY name;")?
- .query_map(params![], |row| row.get(0))?
- .collect::<rusqlite::Result<Vec<String>>>()?;
- assert_eq!(tables.len(), 6);
- assert_eq!(tables[0], "blobentry");
- assert_eq!(tables[1], "blobmetadata");
- assert_eq!(tables[2], "grant");
- assert_eq!(tables[3], "keyentry");
- assert_eq!(tables[4], "keymetadata");
- assert_eq!(tables[5], "keyparameter");
- 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(&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(&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(&db);
- assert_eq!(auth_tokens_returned.len(), 2);
-
- Ok(())
- }
-
- // utility function for test_auth_token_table_invariant()
- fn get_auth_tokens(db: &KeystoreDB) -> Vec<AuthTokenEntry> {
- db.perboot.get_all_auth_token_entries()
- }
-
- fn create_key_entry(
- db: &mut KeystoreDB,
- domain: &Domain,
- namespace: &i64,
- key_type: KeyType,
- km_uuid: &Uuid,
- ) -> Result<KeyIdGuard> {
- db.with_transaction(Immediate("TX_create_key_entry"), |tx| {
- KeystoreDB::create_key_entry_internal(tx, domain, namespace, key_type, km_uuid).no_gc()
- })
- }
-
- #[test]
- fn test_persistence_for_files() -> Result<()> {
- let temp_dir = TempDir::new("persistent_db_test")?;
- let mut db = KeystoreDB::new(temp_dir.path(), None)?;
-
- create_key_entry(&mut db, &Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
- let entries = get_keyentry(&db)?;
- assert_eq!(entries.len(), 1);
-
- let db = KeystoreDB::new(temp_dir.path(), None)?;
-
- let entries_new = get_keyentry(&db)?;
- assert_eq!(entries, entries_new);
- Ok(())
- }
-
- #[test]
- fn test_create_key_entry() -> Result<()> {
- fn extractor(ke: &KeyEntryRow) -> (Domain, i64, Option<&str>, Uuid) {
- (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref(), ke.km_uuid.unwrap())
- }
-
- let mut db = new_test_db()?;
-
- create_key_entry(&mut db, &Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
- create_key_entry(&mut db, &Domain::SELINUX, &101, KeyType::Client, &KEYSTORE_UUID)?;
-
- let entries = get_keyentry(&db)?;
- assert_eq!(entries.len(), 2);
- assert_eq!(extractor(&entries[0]), (Domain::APP, 100, None, KEYSTORE_UUID));
- assert_eq!(extractor(&entries[1]), (Domain::SELINUX, 101, None, KEYSTORE_UUID));
-
- // Test that we must pass in a valid Domain.
- check_result_is_error_containing_string(
- create_key_entry(&mut db, &Domain::GRANT, &102, KeyType::Client, &KEYSTORE_UUID),
- &format!("Domain {:?} must be either App or SELinux.", Domain::GRANT),
- );
- check_result_is_error_containing_string(
- create_key_entry(&mut db, &Domain::BLOB, &103, KeyType::Client, &KEYSTORE_UUID),
- &format!("Domain {:?} must be either App or SELinux.", Domain::BLOB),
- );
- check_result_is_error_containing_string(
- create_key_entry(&mut db, &Domain::KEY_ID, &104, KeyType::Client, &KEYSTORE_UUID),
- &format!("Domain {:?} must be either App or SELinux.", Domain::KEY_ID),
- );
-
- Ok(())
- }
-
- #[test]
- fn test_rebind_alias() -> Result<()> {
- fn extractor(
- ke: &KeyEntryRow,
- ) -> (Option<Domain>, Option<i64>, Option<&str>, Option<Uuid>) {
- (ke.domain, ke.namespace, ke.alias.as_deref(), ke.km_uuid)
- }
-
- let mut db = new_test_db()?;
- create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
- create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
- let entries = get_keyentry(&db)?;
- assert_eq!(entries.len(), 2);
- assert_eq!(
- extractor(&entries[0]),
- (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID))
- );
- assert_eq!(
- extractor(&entries[1]),
- (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID))
- );
-
- // Test that the first call to rebind_alias sets the alias.
- rebind_alias(&mut db, &KEY_ID_LOCK.get(entries[0].id), "foo", Domain::APP, 42)?;
- let entries = get_keyentry(&db)?;
- assert_eq!(entries.len(), 2);
- assert_eq!(
- extractor(&entries[0]),
- (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
- );
- assert_eq!(
- extractor(&entries[1]),
- (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID))
- );
-
- // Test that the second call to rebind_alias also empties the old one.
- rebind_alias(&mut db, &KEY_ID_LOCK.get(entries[1].id), "foo", Domain::APP, 42)?;
- let entries = get_keyentry(&db)?;
- assert_eq!(entries.len(), 2);
- assert_eq!(extractor(&entries[0]), (None, None, None, Some(KEYSTORE_UUID)));
- assert_eq!(
- extractor(&entries[1]),
- (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
- );
-
- // Test that we must pass in a valid Domain.
- check_result_is_error_containing_string(
- rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::GRANT, 42),
- &format!("Domain {:?} must be either App or SELinux.", Domain::GRANT),
- );
- check_result_is_error_containing_string(
- rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::BLOB, 42),
- &format!("Domain {:?} must be either App or SELinux.", Domain::BLOB),
- );
- check_result_is_error_containing_string(
- rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::KEY_ID, 42),
- &format!("Domain {:?} must be either App or SELinux.", Domain::KEY_ID),
- );
-
- // Test that we correctly handle setting an alias for something that does not exist.
- check_result_is_error_containing_string(
- rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::SELINUX, 42),
- "Expected to update a single entry but instead updated 0",
- );
- // Test that we correctly abort the transaction in this case.
- let entries = get_keyentry(&db)?;
- assert_eq!(entries.len(), 2);
- assert_eq!(extractor(&entries[0]), (None, None, None, Some(KEYSTORE_UUID)));
- assert_eq!(
- extractor(&entries[1]),
- (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
- );
-
- Ok(())
- }
-
- #[test]
- fn test_grant_ungrant() -> Result<()> {
- const CALLER_UID: u32 = 15;
- const GRANTEE_UID: u32 = 12;
- const SELINUX_NAMESPACE: i64 = 7;
-
- let mut db = new_test_db()?;
- db.conn.execute(
- "INSERT INTO persistent.keyentry (id, key_type, domain, namespace, alias, state, km_uuid)
- VALUES (1, 0, 0, 15, 'key', 1, ?), (2, 0, 2, 7, 'yek', 1, ?);",
- params![KEYSTORE_UUID, KEYSTORE_UUID],
- )?;
- let app_key = KeyDescriptor {
- domain: super::Domain::APP,
- nspace: 0,
- alias: Some("key".to_string()),
- blob: None,
- };
- const PVEC1: KeyPermSet = key_perm_set![KeyPerm::Use, KeyPerm::GetInfo];
- const PVEC2: KeyPermSet = key_perm_set![KeyPerm::Use];
-
- // Reset totally predictable random number generator in case we
- // are not the first test running on this thread.
- reset_random();
- let next_random = 0i64;
-
- let app_granted_key = db
- .grant(&app_key, CALLER_UID, GRANTEE_UID, PVEC1, |k, a| {
- assert_eq!(*a, PVEC1);
- assert_eq!(
- *k,
- KeyDescriptor {
- domain: super::Domain::APP,
- // namespace must be set to the caller_uid.
- nspace: CALLER_UID as i64,
- alias: Some("key".to_string()),
- blob: None,
- }
- );
- Ok(())
- })
- .unwrap();
-
- assert_eq!(
- app_granted_key,
- KeyDescriptor {
- domain: super::Domain::GRANT,
- // The grantid is next_random due to the mock random number generator.
- nspace: next_random,
- alias: None,
- blob: None,
- }
- );
-
- let selinux_key = KeyDescriptor {
- domain: super::Domain::SELINUX,
- nspace: SELINUX_NAMESPACE,
- alias: Some("yek".to_string()),
- blob: None,
- };
-
- let selinux_granted_key = db
- .grant(&selinux_key, CALLER_UID, 12, PVEC1, |k, a| {
- assert_eq!(*a, PVEC1);
- assert_eq!(
- *k,
- KeyDescriptor {
- domain: super::Domain::SELINUX,
- // namespace must be the supplied SELinux
- // namespace.
- nspace: SELINUX_NAMESPACE,
- alias: Some("yek".to_string()),
- blob: None,
- }
- );
- Ok(())
- })
- .unwrap();
-
- assert_eq!(
- selinux_granted_key,
- KeyDescriptor {
- domain: super::Domain::GRANT,
- // The grantid is next_random + 1 due to the mock random number generator.
- nspace: next_random + 1,
- alias: None,
- blob: None,
- }
- );
-
- // This should update the existing grant with PVEC2.
- let selinux_granted_key = db
- .grant(&selinux_key, CALLER_UID, 12, PVEC2, |k, a| {
- assert_eq!(*a, PVEC2);
- assert_eq!(
- *k,
- KeyDescriptor {
- domain: super::Domain::SELINUX,
- // namespace must be the supplied SELinux
- // namespace.
- nspace: SELINUX_NAMESPACE,
- alias: Some("yek".to_string()),
- blob: None,
- }
- );
- Ok(())
- })
- .unwrap();
-
- assert_eq!(
- selinux_granted_key,
- KeyDescriptor {
- domain: super::Domain::GRANT,
- // Same grant id as before. The entry was only updated.
- nspace: next_random + 1,
- alias: None,
- blob: None,
- }
- );
-
- {
- // Limiting scope of stmt, because it borrows db.
- let mut stmt = db
- .conn
- .prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
- let mut rows = stmt.query_map::<(i64, u32, i64, KeyPermSet), _, _>([], |row| {
- Ok((row.get(0)?, row.get(1)?, row.get(2)?, KeyPermSet::from(row.get::<_, i32>(3)?)))
- })?;
-
- let r = rows.next().unwrap().unwrap();
- assert_eq!(r, (next_random, GRANTEE_UID, 1, PVEC1));
- let r = rows.next().unwrap().unwrap();
- assert_eq!(r, (next_random + 1, GRANTEE_UID, 2, PVEC2));
- assert!(rows.next().is_none());
- }
-
- debug_dump_keyentry_table(&mut db)?;
- println!("app_key {:?}", app_key);
- println!("selinux_key {:?}", selinux_key);
-
- db.ungrant(&app_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
- db.ungrant(&selinux_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
-
- Ok(())
- }
-
- static TEST_KEY_BLOB: &[u8] = b"my test blob";
- static TEST_CERT_BLOB: &[u8] = b"my test cert";
- static TEST_CERT_CHAIN_BLOB: &[u8] = b"my test cert_chain";
-
- #[test]
- fn test_set_blob() -> Result<()> {
- let key_id = KEY_ID_LOCK.get(3000);
- let mut db = new_test_db()?;
- let mut blob_metadata = BlobMetaData::new();
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
- db.set_blob(
- &key_id,
- SubComponentType::KEY_BLOB,
- Some(TEST_KEY_BLOB),
- Some(&blob_metadata),
- )?;
- 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)?;
- drop(key_id);
-
- let mut stmt = db.conn.prepare(
- "SELECT subcomponent_type, keyentryid, blob, id FROM persistent.blobentry
- ORDER BY subcomponent_type ASC;",
- )?;
- let mut rows = stmt
- .query_map::<((SubComponentType, i64, Vec<u8>), i64), _, _>([], |row| {
- Ok(((row.get(0)?, row.get(1)?, row.get(2)?), row.get(3)?))
- })?;
- let (r, id) = rows.next().unwrap().unwrap();
- assert_eq!(r, (SubComponentType::KEY_BLOB, 3000, TEST_KEY_BLOB.to_vec()));
- let (r, _) = rows.next().unwrap().unwrap();
- assert_eq!(r, (SubComponentType::CERT, 3000, TEST_CERT_BLOB.to_vec()));
- let (r, _) = rows.next().unwrap().unwrap();
- assert_eq!(r, (SubComponentType::CERT_CHAIN, 3000, TEST_CERT_CHAIN_BLOB.to_vec()));
-
- drop(rows);
- drop(stmt);
-
- assert_eq!(
- db.with_transaction(Immediate("TX_test"), |tx| {
- BlobMetaData::load_from_db(id, tx).no_gc()
- })
- .expect("Should find blob metadata."),
- blob_metadata
- );
- Ok(())
- }
-
- static TEST_ALIAS: &str = "my super duper key";
-
- #[test]
- fn test_insert_and_load_full_keyentry_domain_app() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_domain_app")?
- .0;
- let (_key_guard, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap();
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- db.unbind_key(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- 1,
- |_, _| Ok(()),
- )
- .unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- #[test]
- fn test_insert_and_load_certificate_entry_domain_app() -> Result<()> {
- let mut db = new_test_db()?;
-
- db.store_new_certificate(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- TEST_CERT_BLOB,
- &KEYSTORE_UUID,
- )
- .expect("Trying to insert cert.");
-
- let (_key_guard, mut key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::PUBLIC,
- 1,
- |_k, _av| Ok(()),
- )
- .expect("Trying to read certificate entry.");
-
- assert!(key_entry.pure_cert());
- assert!(key_entry.cert().is_none());
- assert_eq!(key_entry.take_cert_chain(), Some(TEST_CERT_BLOB.to_vec()));
-
- db.unbind_key(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- 1,
- |_, _| Ok(()),
- )
- .unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- #[test]
- fn test_insert_and_load_full_keyentry_domain_selinux() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_domain_selinux")?
- .0;
- let (_key_guard, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap();
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- db.unbind_key(
- &KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- 1,
- |_, _| Ok(()),
- )
- .unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- #[test]
- fn test_insert_and_load_full_keyentry_domain_key_id() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_domain_key_id")?
- .0;
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- db.unbind_key(
- &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
- KeyType::Client,
- 1,
- |_, _| Ok(()),
- )
- .unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- #[test]
- fn test_check_and_update_key_usage_count_with_limited_use_key() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(123))
- .context("test_check_and_update_key_usage_count_with_limited_use_key")?
- .0;
- // Update the usage count of the limited use key.
- db.check_and_update_key_usage_count(key_id)?;
-
- let (_key_guard, key_entry) = db.load_key_entry(
- &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 1,
- |_k, _av| Ok(()),
- )?;
-
- // The usage count is decremented now.
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, Some(122)));
-
- Ok(())
- }
-
- #[test]
- fn test_check_and_update_key_usage_count_with_exhausted_limited_use_key() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(1))
- .context("test_check_and_update_key_usage_count_with_exhausted_limited_use_key")?
- .0;
- // Update the usage count of the limited use key.
- db.check_and_update_key_usage_count(key_id).expect(concat!(
- "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
- "This should succeed."
- ));
-
- // Try to update the exhausted limited use key.
- let e = db.check_and_update_key_usage_count(key_id).expect_err(concat!(
- "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
- "This should fail."
- ));
- assert_eq!(
- &KsError::Km(ErrorCode::INVALID_KEY_BLOB),
- e.root_cause().downcast_ref::<KsError>().unwrap()
- );
-
- Ok(())
- }
-
- #[test]
- fn test_insert_and_load_full_keyentry_from_grant() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant")?
- .0;
-
- let granted_key = db
- .grant(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- 1,
- 2,
- key_perm_set![KeyPerm::Use],
- |_k, _av| Ok(()),
- )
- .unwrap();
-
- debug_dump_grant_table(&mut db)?;
-
- let (_key_guard, key_entry) = db
- .load_key_entry(&granted_key, KeyType::Client, KeyEntryLoadBits::BOTH, 2, |k, av| {
- assert_eq!(Domain::GRANT, k.domain);
- assert!(av.unwrap().includes(KeyPerm::Use));
- Ok(())
- })
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- db.unbind_key(&granted_key, KeyType::Client, 2, |_, _| Ok(())).unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &granted_key,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- 2,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- // This test attempts to load a key by key id while the caller is not the owner
- // but a grant exists for the given key and the caller.
- #[test]
- fn test_insert_and_load_full_keyentry_from_grant_by_key_id() -> Result<()> {
- let mut db = new_test_db()?;
- const OWNER_UID: u32 = 1u32;
- const GRANTEE_UID: u32 = 2u32;
- const SOMEONE_ELSE_UID: u32 = 3u32;
- let key_id = make_test_key_entry(&mut db, Domain::APP, OWNER_UID as i64, TEST_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?
- .0;
-
- db.grant(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- OWNER_UID,
- GRANTEE_UID,
- key_perm_set![KeyPerm::Use],
- |_k, _av| Ok(()),
- )
- .unwrap();
-
- debug_dump_grant_table(&mut db)?;
-
- let id_descriptor =
- KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, ..Default::default() };
-
- let (_, key_entry) = db
- .load_key_entry(
- &id_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- GRANTEE_UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(OWNER_UID as i64, k.nspace);
- assert!(av.unwrap().includes(KeyPerm::Use));
- Ok(())
- },
- )
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- let (_, key_entry) = db
- .load_key_entry(
- &id_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- SOMEONE_ELSE_UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(OWNER_UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- db.unbind_key(&id_descriptor, KeyType::Client, OWNER_UID, |_, _| Ok(())).unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &id_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- GRANTEE_UID,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- // Creates a key migrates it to a different location and then tries to access it by the old
- // and new location.
- #[test]
- fn test_migrate_key_app_to_app() -> Result<()> {
- let mut db = new_test_db()?;
- const SOURCE_UID: u32 = 1u32;
- const DESTINATION_UID: u32 = 2u32;
- static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
- static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
- let key_id_guard =
- make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
-
- let source_descriptor: KeyDescriptor = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(SOURCE_ALIAS.to_string()),
- blob: None,
- };
-
- let destination_descriptor: KeyDescriptor = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(DESTINATION_ALIAS.to_string()),
- blob: None,
- };
-
- let key_id = key_id_guard.id();
-
- db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| {
- Ok(())
- })
- .unwrap();
-
- let (_, key_entry) = db
- .load_key_entry(
- &destination_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- DESTINATION_UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(DESTINATION_UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &source_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- SOURCE_UID,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- // Creates a key migrates it to a different location and then tries to access it by the old
- // and new location.
- #[test]
- fn test_migrate_key_app_to_selinux() -> Result<()> {
- let mut db = new_test_db()?;
- const SOURCE_UID: u32 = 1u32;
- const DESTINATION_UID: u32 = 2u32;
- const DESTINATION_NAMESPACE: i64 = 1000i64;
- static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
- static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
- let key_id_guard =
- make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
-
- let source_descriptor: KeyDescriptor = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(SOURCE_ALIAS.to_string()),
- blob: None,
- };
-
- let destination_descriptor: KeyDescriptor = KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: DESTINATION_NAMESPACE,
- alias: Some(DESTINATION_ALIAS.to_string()),
- blob: None,
- };
-
- let key_id = key_id_guard.id();
-
- db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| {
- Ok(())
- })
- .unwrap();
-
- let (_, key_entry) = db
- .load_key_entry(
- &destination_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- DESTINATION_UID,
- |k, av| {
- assert_eq!(Domain::SELINUX, k.domain);
- assert_eq!(DESTINATION_NAMESPACE, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &source_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- SOURCE_UID,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- // Creates two keys and tries to migrate the first to the location of the second which
- // is expected to fail.
- #[test]
- fn test_migrate_key_destination_occupied() -> Result<()> {
- let mut db = new_test_db()?;
- const SOURCE_UID: u32 = 1u32;
- const DESTINATION_UID: u32 = 2u32;
- static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
- static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
- let key_id_guard =
- make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
- make_test_key_entry(&mut db, Domain::APP, DESTINATION_UID as i64, DESTINATION_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
-
- let destination_descriptor: KeyDescriptor = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(DESTINATION_ALIAS.to_string()),
- blob: None,
- };
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::INVALID_ARGUMENT)),
- db.migrate_key_namespace(
- key_id_guard,
- &destination_descriptor,
- DESTINATION_UID,
- |_k| Ok(())
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- #[test]
- fn test_upgrade_0_to_1() {
- const ALIAS1: &str = "test_upgrade_0_to_1_1";
- const ALIAS2: &str = "test_upgrade_0_to_1_2";
- const ALIAS3: &str = "test_upgrade_0_to_1_3";
- const UID: u32 = 33;
- let temp_dir = Arc::new(TempDir::new("test_upgrade_0_to_1").unwrap());
- let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
- let key_id_untouched1 =
- make_test_key_entry(&mut db, Domain::APP, UID as i64, ALIAS1, None).unwrap().id();
- let key_id_untouched2 =
- make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS2, false).unwrap().id();
- let key_id_deleted =
- make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS3, true).unwrap().id();
-
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS1.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS2.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
- assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS3.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
- assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_deleted, true));
-
- db.with_transaction(Immediate("TX_test"), |tx| KeystoreDB::from_0_to_1(tx).no_gc())
- .unwrap();
-
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS1.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS2.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
- assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS3.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
- }
-
- static KEY_LOCK_TEST_ALIAS: &str = "my super duper locked key";
-
- #[test]
- fn test_insert_and_load_full_keyentry_domain_app_concurrently() -> Result<()> {
- let handle = {
- let temp_dir = Arc::new(TempDir::new("id_lock_test")?);
- let temp_dir_clone = temp_dir.clone();
- let mut db = KeystoreDB::new(temp_dir.path(), None)?;
- let key_id = make_test_key_entry(&mut db, Domain::APP, 33, KEY_LOCK_TEST_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_domain_app")?
- .0;
- let (_key_guard, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(KEY_LOCK_TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 33,
- |_k, _av| Ok(()),
- )
- .unwrap();
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
- let state = Arc::new(AtomicU8::new(1));
- let state2 = state.clone();
-
- // Spawning a second thread that attempts to acquire the key id lock
- // for the same key as the primary thread. The primary thread then
- // waits, thereby forcing the secondary thread into the second stage
- // of acquiring the lock (see KEY ID LOCK 2/2 above).
- // The test succeeds if the secondary thread observes the transition
- // of `state` from 1 to 2, despite having a whole second to overtake
- // the primary thread.
- let handle = thread::spawn(move || {
- let temp_dir = temp_dir_clone;
- let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
- assert!(db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(KEY_LOCK_TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 33,
- |_k, _av| Ok(()),
- )
- .is_ok());
- // We should only see a 2 here because we can only return
- // from load_key_entry when the `_key_guard` expires,
- // which happens at the end of the scope.
- assert_eq!(2, state2.load(Ordering::Relaxed));
- });
-
- thread::sleep(std::time::Duration::from_millis(1000));
-
- assert_eq!(Ok(1), state.compare_exchange(1, 2, Ordering::Relaxed, Ordering::Relaxed));
-
- // Return the handle from this scope so we can join with the
- // secondary thread after the key id lock has expired.
- handle
- // This is where the `_key_guard` goes out of scope,
- // which is the reason for concurrent load_key_entry on the same key
- // to unblock.
- };
- // Join with the secondary thread and unwrap, to propagate failing asserts to the
- // main test thread. We will not see failing asserts in secondary threads otherwise.
- handle.join().unwrap();
- Ok(())
- }
-
- #[test]
- fn test_database_busy_error_code() {
- let temp_dir =
- TempDir::new("test_database_busy_error_code_").expect("Failed to create temp dir.");
-
- let mut db1 = KeystoreDB::new(temp_dir.path(), None).expect("Failed to open database1.");
- let mut db2 = KeystoreDB::new(temp_dir.path(), None).expect("Failed to open database2.");
-
- let _tx1 = db1
- .conn
- .transaction_with_behavior(rusqlite::TransactionBehavior::Immediate)
- .expect("Failed to create first transaction.");
-
- let error = db2
- .conn
- .transaction_with_behavior(rusqlite::TransactionBehavior::Immediate)
- .context("Transaction begin failed.")
- .expect_err("This should fail.");
- let root_cause = error.root_cause();
- if let Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseBusy, .. }) =
- root_cause.downcast_ref::<rusqlite::ffi::Error>()
- {
- return;
- }
- panic!(
- "Unexpected error {:?} \n{:?} \n{:?}",
- error,
- root_cause,
- root_cause.downcast_ref::<rusqlite::ffi::Error>()
- )
- }
-
- #[cfg(disabled)]
- #[test]
- fn test_large_number_of_concurrent_db_manipulations() -> Result<()> {
- let temp_dir = Arc::new(
- TempDir::new("test_large_number_of_concurrent_db_manipulations_")
- .expect("Failed to create temp dir."),
- );
-
- let test_begin = Instant::now();
-
- const KEY_COUNT: u32 = 500u32;
- let mut db =
- new_test_db_with_gc(temp_dir.path(), |_, _| Ok(())).expect("Failed to open database.");
- const OPEN_DB_COUNT: u32 = 50u32;
-
- let mut actual_key_count = KEY_COUNT;
- // First insert KEY_COUNT keys.
- for count in 0..KEY_COUNT {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(15) {
- actual_key_count = count;
- break;
- }
- let alias = format!("test_alias_{}", count);
- make_test_key_entry(&mut db, Domain::APP, 1, &alias, None)
- .expect("Failed to make key entry.");
- }
-
- // Insert more keys from a different thread and into a different namespace.
- let temp_dir1 = temp_dir.clone();
- let handle1 = thread::spawn(move || {
- let mut db = new_test_db_with_gc(temp_dir1.path(), |_, _| Ok(()))
- .expect("Failed to open database.");
-
- for count in 0..actual_key_count {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
- return;
- }
- let alias = format!("test_alias_{}", count);
- make_test_key_entry(&mut db, Domain::APP, 2, &alias, None)
- .expect("Failed to make key entry.");
- }
-
- // then unbind them again.
- for count in 0..actual_key_count {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
- return;
- }
- let key = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(format!("test_alias_{}", count)),
- blob: None,
- };
- db.unbind_key(&key, KeyType::Client, 2, |_, _| Ok(())).expect("Unbind Failed.");
- }
- });
-
- // And start unbinding the first set of keys.
- let temp_dir2 = temp_dir.clone();
- let handle2 = thread::spawn(move || {
- let mut db = new_test_db_with_gc(temp_dir2.path(), |_, _| Ok(()))
- .expect("Failed to open database.");
-
- for count in 0..actual_key_count {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
- return;
- }
- let key = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(format!("test_alias_{}", count)),
- blob: None,
- };
- db.unbind_key(&key, KeyType::Client, 1, |_, _| Ok(())).expect("Unbind Failed.");
- }
- });
-
- // While a lot of inserting and deleting is going on we have to open database connections
- // successfully and use them.
- // This clone is not redundant, because temp_dir needs to be kept alive until db goes
- // out of scope.
- #[allow(clippy::redundant_clone)]
- let temp_dir4 = temp_dir.clone();
- let handle4 = thread::spawn(move || {
- for count in 0..OPEN_DB_COUNT {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
- return;
- }
- let mut db = new_test_db_with_gc(temp_dir4.path(), |_, _| Ok(()))
- .expect("Failed to open database.");
-
- let alias = format!("test_alias_{}", count);
- make_test_key_entry(&mut db, Domain::APP, 3, &alias, None)
- .expect("Failed to make key entry.");
- let key = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(alias),
- blob: None,
- };
- db.unbind_key(&key, KeyType::Client, 3, |_, _| Ok(())).expect("Unbind Failed.");
- }
- });
-
- handle1.join().expect("Thread 1 panicked.");
- handle2.join().expect("Thread 2 panicked.");
- handle4.join().expect("Thread 4 panicked.");
-
- Ok(())
- }
-
- #[test]
- fn list() -> Result<()> {
- let temp_dir = TempDir::new("list_test")?;
- let mut db = KeystoreDB::new(temp_dir.path(), None)?;
- static LIST_O_ENTRIES: &[(Domain, i64, &str)] = &[
- (Domain::APP, 1, "test1"),
- (Domain::APP, 1, "test2"),
- (Domain::APP, 1, "test3"),
- (Domain::APP, 1, "test4"),
- (Domain::APP, 1, "test5"),
- (Domain::APP, 1, "test6"),
- (Domain::APP, 1, "test7"),
- (Domain::APP, 2, "test1"),
- (Domain::APP, 2, "test2"),
- (Domain::APP, 2, "test3"),
- (Domain::APP, 2, "test4"),
- (Domain::APP, 2, "test5"),
- (Domain::APP, 2, "test6"),
- (Domain::APP, 2, "test8"),
- (Domain::SELINUX, 100, "test1"),
- (Domain::SELINUX, 100, "test2"),
- (Domain::SELINUX, 100, "test3"),
- (Domain::SELINUX, 100, "test4"),
- (Domain::SELINUX, 100, "test5"),
- (Domain::SELINUX, 100, "test6"),
- (Domain::SELINUX, 100, "test9"),
- ];
-
- let list_o_keys: Vec<(i64, i64)> = LIST_O_ENTRIES
- .iter()
- .map(|(domain, ns, alias)| {
- let entry =
- make_test_key_entry(&mut db, *domain, *ns, alias, None).unwrap_or_else(|e| {
- panic!("Failed to insert {:?} {} {}. Error {:?}", domain, ns, alias, e)
- });
- (entry.id(), *ns)
- })
- .collect();
-
- for (domain, namespace) in
- &[(Domain::APP, 1i64), (Domain::APP, 2i64), (Domain::SELINUX, 100i64)]
- {
- let mut list_o_descriptors: Vec<KeyDescriptor> = LIST_O_ENTRIES
- .iter()
- .filter_map(|(domain, ns, alias)| match ns {
- ns if *ns == *namespace => Some(KeyDescriptor {
- domain: *domain,
- nspace: *ns,
- alias: Some(alias.to_string()),
- blob: None,
- }),
- _ => None,
- })
- .collect();
- list_o_descriptors.sort();
- let mut list_result = db.list_past_alias(*domain, *namespace, KeyType::Client, None)?;
- list_result.sort();
- assert_eq!(list_o_descriptors, list_result);
-
- let mut list_o_ids: Vec<i64> = list_o_descriptors
- .into_iter()
- .map(|d| {
- let (_, entry) = db
- .load_key_entry(
- &d,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- *namespace as u32,
- |_, _| Ok(()),
- )
- .unwrap();
- entry.id()
- })
- .collect();
- list_o_ids.sort_unstable();
- let mut loaded_entries: Vec<i64> = list_o_keys
- .iter()
- .filter_map(|(id, ns)| match ns {
- ns if *ns == *namespace => Some(*id),
- _ => None,
- })
- .collect();
- loaded_entries.sort_unstable();
- assert_eq!(list_o_ids, loaded_entries);
- }
- assert_eq!(
- Vec::<KeyDescriptor>::new(),
- db.list_past_alias(Domain::SELINUX, 101, KeyType::Client, None)?
- );
-
- Ok(())
- }
-
- // Helpers
-
- // Checks that the given result is an error containing the given string.
- fn check_result_is_error_containing_string<T>(result: Result<T>, target: &str) {
- let error_str = format!(
- "{:#?}",
- result.err().unwrap_or_else(|| panic!("Expected the error: {}", target))
- );
- assert!(
- error_str.contains(target),
- "The string \"{}\" should contain \"{}\"",
- error_str,
- target
- );
- }
-
- #[derive(Debug, PartialEq)]
- struct KeyEntryRow {
- id: i64,
- key_type: KeyType,
- domain: Option<Domain>,
- namespace: Option<i64>,
- alias: Option<String>,
- state: KeyLifeCycle,
- km_uuid: Option<Uuid>,
- }
-
- fn get_keyentry(db: &KeystoreDB) -> Result<Vec<KeyEntryRow>> {
- db.conn
- .prepare("SELECT * FROM persistent.keyentry;")?
- .query_map([], |row| {
- Ok(KeyEntryRow {
- id: row.get(0)?,
- key_type: row.get(1)?,
- domain: row.get::<_, Option<_>>(2)?.map(Domain),
- namespace: row.get(3)?,
- alias: row.get(4)?,
- state: row.get(5)?,
- km_uuid: row.get(6)?,
- })
- })?
- .map(|r| r.context("Could not read keyentry row."))
- .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_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(
- KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::Algorithm(Algorithm::RSA),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::KeySize(1024), SecurityLevel::TRUSTED_ENVIRONMENT),
- KeyParameter::new(
- KeyParameterValue::BlockMode(BlockMode::ECB),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::BlockMode(BlockMode::GCM),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::Digest(Digest::NONE), SecurityLevel::STRONGBOX),
- KeyParameter::new(
- KeyParameterValue::Digest(Digest::MD5),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::Digest(Digest::SHA_2_224),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::Digest(Digest::SHA_2_256),
- SecurityLevel::STRONGBOX,
- ),
- KeyParameter::new(
- KeyParameterValue::PaddingMode(PaddingMode::NONE),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::PaddingMode(PaddingMode::RSA_OAEP),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS),
- SecurityLevel::STRONGBOX,
- ),
- KeyParameter::new(
- KeyParameterValue::PaddingMode(PaddingMode::RSA_PKCS1_1_5_SIGN),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::TRUSTED_ENVIRONMENT),
- KeyParameter::new(KeyParameterValue::MinMacLength(256), SecurityLevel::STRONGBOX),
- KeyParameter::new(
- KeyParameterValue::EcCurve(EcCurve::P_224),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::EcCurve(EcCurve::P_256), SecurityLevel::STRONGBOX),
- KeyParameter::new(
- KeyParameterValue::EcCurve(EcCurve::P_384),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::EcCurve(EcCurve::P_521),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::RSAPublicExponent(3),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::IncludeUniqueID,
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::BootLoaderOnly, SecurityLevel::STRONGBOX),
- KeyParameter::new(KeyParameterValue::RollbackResistance, SecurityLevel::STRONGBOX),
- KeyParameter::new(
- KeyParameterValue::ActiveDateTime(1234567890),
- SecurityLevel::STRONGBOX,
- ),
- KeyParameter::new(
- KeyParameterValue::OriginationExpireDateTime(1234567890),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::UsageExpireDateTime(1234567890),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::MinSecondsBetweenOps(1234567890),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::MaxUsesPerBoot(1234567890),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::UserID(1), SecurityLevel::STRONGBOX),
- KeyParameter::new(
- KeyParameterValue::NoAuthRequired,
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType::PASSWORD),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::AuthTimeout(1234567890), SecurityLevel::SOFTWARE),
- KeyParameter::new(KeyParameterValue::AllowWhileOnBody, SecurityLevel::SOFTWARE),
- KeyParameter::new(
- KeyParameterValue::TrustedUserPresenceRequired,
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::TrustedConfirmationRequired,
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::UnlockedDeviceRequired,
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::ApplicationID(vec![1u8, 2u8, 3u8, 4u8]),
- SecurityLevel::SOFTWARE,
- ),
- KeyParameter::new(
- KeyParameterValue::ApplicationData(vec![4u8, 3u8, 2u8, 1u8]),
- SecurityLevel::SOFTWARE,
- ),
- KeyParameter::new(
- KeyParameterValue::CreationDateTime(12345677890),
- SecurityLevel::SOFTWARE,
- ),
- KeyParameter::new(
- KeyParameterValue::KeyOrigin(KeyOrigin::GENERATED),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::RootOfTrust(vec![3u8, 2u8, 1u8, 4u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::OSVersion(1), SecurityLevel::TRUSTED_ENVIRONMENT),
- KeyParameter::new(KeyParameterValue::OSPatchLevel(2), SecurityLevel::SOFTWARE),
- KeyParameter::new(
- KeyParameterValue::UniqueID(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::SOFTWARE,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationChallenge(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationApplicationID(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdBrand(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdDevice(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdProduct(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdSerial(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdIMEI(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdSecondIMEI(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdMEID(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdManufacturer(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdModel(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::VendorPatchLevel(3),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::BootPatchLevel(4),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AssociatedData(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::Nonce(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::MacLength(256),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::ResetSinceIdRotation,
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::ConfirmationToken(vec![5u8, 5u8, 5u8, 5u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- ];
- if let Some(value) = max_usage_count {
- params.push(KeyParameter::new(
- KeyParameterValue::UsageCountLimit(value),
- SecurityLevel::SOFTWARE,
- ));
- }
-
- for sid in user_secure_ids.iter() {
- params.push(KeyParameter::new(
- KeyParameterValue::UserSecureID(*sid),
- SecurityLevel::STRONGBOX,
- ));
- }
- params
- }
-
- pub fn make_test_key_entry(
- db: &mut KeystoreDB,
- domain: Domain,
- namespace: i64,
- 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 = create_key_entry(db, &domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
- let mut blob_metadata = BlobMetaData::new();
- blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
- blob_metadata.add(BlobMetaEntry::Salt(vec![1, 2, 3]));
- blob_metadata.add(BlobMetaEntry::Iv(vec![2, 3, 1]));
- blob_metadata.add(BlobMetaEntry::AeadTag(vec![3, 1, 2]));
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
-
- db.set_blob(
- &key_id,
- SubComponentType::KEY_BLOB,
- Some(TEST_KEY_BLOB),
- Some(&blob_metadata),
- )?;
- 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_with_sids(max_usage_count, sids);
- db.insert_keyparameter(&key_id, ¶ms)?;
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
- db.insert_key_metadata(&key_id, &metadata)?;
- rebind_alias(db, &key_id, alias, domain, namespace)?;
- Ok(key_id)
- }
-
- fn make_test_key_entry_test_vector(key_id: i64, max_usage_count: Option<i32>) -> KeyEntry {
- let params = make_test_params(max_usage_count);
-
- let mut blob_metadata = BlobMetaData::new();
- blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
- blob_metadata.add(BlobMetaEntry::Salt(vec![1, 2, 3]));
- blob_metadata.add(BlobMetaEntry::Iv(vec![2, 3, 1]));
- blob_metadata.add(BlobMetaEntry::AeadTag(vec![3, 1, 2]));
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
-
- KeyEntry {
- id: key_id,
- key_blob_info: Some((TEST_KEY_BLOB.to_vec(), blob_metadata)),
- cert: Some(TEST_CERT_BLOB.to_vec()),
- cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
- km_uuid: KEYSTORE_UUID,
- parameters: params,
- metadata,
- pure_cert: false,
- }
- }
-
- pub fn make_bootlevel_key_entry(
- db: &mut KeystoreDB,
- domain: Domain,
- namespace: i64,
- alias: &str,
- logical_only: bool,
- ) -> Result<KeyIdGuard> {
- let key_id = create_key_entry(db, &domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
- let mut blob_metadata = BlobMetaData::new();
- if !logical_only {
- blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
- }
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
-
- db.set_blob(
- &key_id,
- SubComponentType::KEY_BLOB,
- Some(TEST_KEY_BLOB),
- Some(&blob_metadata),
- )?;
- 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 mut params = make_test_params(None);
- params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
-
- db.insert_keyparameter(&key_id, ¶ms)?;
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
- db.insert_key_metadata(&key_id, &metadata)?;
- rebind_alias(db, &key_id, alias, domain, namespace)?;
- Ok(key_id)
- }
-
- // Creates an app key that is marked as being superencrypted by the given
- // super key ID and that has the given authentication and unlocked device
- // parameters. This does not actually superencrypt the key blob.
- fn make_superencrypted_key_entry(
- db: &mut KeystoreDB,
- namespace: i64,
- alias: &str,
- requires_authentication: bool,
- requires_unlocked_device: bool,
- super_key_id: i64,
- ) -> Result<KeyIdGuard> {
- let domain = Domain::APP;
- let key_id = create_key_entry(db, &domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
-
- let mut blob_metadata = BlobMetaData::new();
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
- blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key_id)));
- db.set_blob(
- &key_id,
- SubComponentType::KEY_BLOB,
- Some(TEST_KEY_BLOB),
- Some(&blob_metadata),
- )?;
-
- let mut params = vec![];
- if requires_unlocked_device {
- params.push(KeyParameter::new(
- KeyParameterValue::UnlockedDeviceRequired,
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ));
- }
- if requires_authentication {
- params.push(KeyParameter::new(
- KeyParameterValue::UserSecureID(42),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ));
- }
- db.insert_keyparameter(&key_id, ¶ms)?;
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
- db.insert_key_metadata(&key_id, &metadata)?;
-
- rebind_alias(db, &key_id, alias, domain, namespace)?;
- Ok(key_id)
- }
-
- fn make_bootlevel_test_key_entry_test_vector(key_id: i64, logical_only: bool) -> KeyEntry {
- let mut params = make_test_params(None);
- params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
-
- let mut blob_metadata = BlobMetaData::new();
- if !logical_only {
- blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
- }
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
-
- KeyEntry {
- id: key_id,
- key_blob_info: Some((TEST_KEY_BLOB.to_vec(), blob_metadata)),
- cert: Some(TEST_CERT_BLOB.to_vec()),
- cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
- km_uuid: KEYSTORE_UUID,
- parameters: params,
- metadata,
- pure_cert: false,
- }
- }
-
- fn debug_dump_keyentry_table(db: &mut KeystoreDB) -> Result<()> {
- let mut stmt = db.conn.prepare(
- "SELECT id, key_type, domain, namespace, alias, state, km_uuid FROM persistent.keyentry;",
- )?;
- let rows = stmt.query_map::<(i64, KeyType, i32, i64, String, KeyLifeCycle, Uuid), _, _>(
- [],
- |row| {
- Ok((
- row.get(0)?,
- row.get(1)?,
- row.get(2)?,
- row.get(3)?,
- row.get(4)?,
- row.get(5)?,
- row.get(6)?,
- ))
- },
- )?;
-
- println!("Key entry table rows:");
- for r in rows {
- let (id, key_type, domain, namespace, alias, state, km_uuid) = r.unwrap();
- println!(
- " id: {} KeyType: {:?} Domain: {} Namespace: {} Alias: {} State: {:?} KmUuid: {:?}",
- id, key_type, domain, namespace, alias, state, km_uuid
- );
- }
- Ok(())
- }
-
- fn debug_dump_grant_table(db: &mut KeystoreDB) -> Result<()> {
- let mut stmt = db
- .conn
- .prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
- let rows = stmt.query_map::<(i64, i64, i64, i64), _, _>([], |row| {
- Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?))
- })?;
-
- println!("Grant table rows:");
- for r in rows {
- let (id, gt, ki, av) = r.unwrap();
- println!(" id: {} grantee: {} key_id: {} access_vector: {}", id, gt, ki, av);
- }
- Ok(())
- }
-
- // Use a custom random number generator that repeats each number once.
- // This allows us to test repeated elements.
-
- thread_local! {
- static RANDOM_COUNTER: RefCell<i64> = const { RefCell::new(0) };
- }
-
- fn reset_random() {
- RANDOM_COUNTER.with(|counter| {
- *counter.borrow_mut() = 0;
- })
- }
-
- pub fn random() -> i64 {
- RANDOM_COUNTER.with(|counter| {
- let result = *counter.borrow() / 2;
- *counter.borrow_mut() += 1;
- result
- })
- }
-
- #[test]
- fn test_unbind_keys_for_user() -> Result<()> {
- let mut db = new_test_db()?;
- db.unbind_keys_for_user(1)?;
-
- make_test_key_entry(&mut db, Domain::APP, 210000, TEST_ALIAS, None)?;
- make_test_key_entry(&mut db, Domain::APP, 110000, TEST_ALIAS, None)?;
- db.unbind_keys_for_user(2)?;
-
- assert_eq!(1, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
- assert_eq!(0, db.list_past_alias(Domain::APP, 210000, KeyType::Client, None)?.len());
-
- db.unbind_keys_for_user(1)?;
- assert_eq!(0, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
-
- Ok(())
- }
-
- #[test]
- fn test_unbind_keys_for_user_removes_superkeys() -> Result<()> {
- let mut db = new_test_db()?;
- let super_key = keystore2_crypto::generate_aes256_key()?;
- let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
- let (encrypted_super_key, metadata) =
- SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
-
- let key_name_enc = SuperKeyType {
- alias: "test_super_key_1",
- algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
- name: "test_super_key_1",
- };
-
- let key_name_nonenc = SuperKeyType {
- alias: "test_super_key_2",
- algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
- name: "test_super_key_2",
- };
-
- // Install two super keys.
- db.store_super_key(
- 1,
- &key_name_nonenc,
- &super_key,
- &BlobMetaData::new(),
- &KeyMetaData::new(),
- )?;
- db.store_super_key(1, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
-
- // Check that both can be found in the database.
- assert!(db.load_super_key(&key_name_enc, 1)?.is_some());
- assert!(db.load_super_key(&key_name_nonenc, 1)?.is_some());
-
- // Install the same keys for a different user.
- db.store_super_key(
- 2,
- &key_name_nonenc,
- &super_key,
- &BlobMetaData::new(),
- &KeyMetaData::new(),
- )?;
- db.store_super_key(2, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
-
- // Check that the second pair of keys can be found in the database.
- assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
- assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
-
- // Delete all keys for user 1.
- db.unbind_keys_for_user(1)?;
-
- // All of user 1's keys should be gone.
- assert!(db.load_super_key(&key_name_enc, 1)?.is_none());
- assert!(db.load_super_key(&key_name_nonenc, 1)?.is_none());
-
- // User 2's keys should not have been touched.
- assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
- assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
-
- Ok(())
- }
-
- fn app_key_exists(db: &mut KeystoreDB, nspace: i64, alias: &str) -> Result<bool> {
- db.key_exists(Domain::APP, nspace, alias, KeyType::Client)
- }
-
- // Tests the unbind_auth_bound_keys_for_user() function.
- #[test]
- fn test_unbind_auth_bound_keys_for_user() -> Result<()> {
- let mut db = new_test_db()?;
- let user_id = 1;
- let nspace: i64 = (user_id * AID_USER_OFFSET).into();
- let other_user_id = 2;
- let other_user_nspace: i64 = (other_user_id * AID_USER_OFFSET).into();
- let super_key_type = &USER_AFTER_FIRST_UNLOCK_SUPER_KEY;
-
- // Create a superencryption key.
- let super_key = keystore2_crypto::generate_aes256_key()?;
- let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
- let (encrypted_super_key, blob_metadata) =
- SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
- db.store_super_key(
- user_id,
- super_key_type,
- &encrypted_super_key,
- &blob_metadata,
- &KeyMetaData::new(),
- )?;
- let super_key_id = db.load_super_key(super_key_type, user_id)?.unwrap().0 .0;
-
- // Store 4 superencrypted app keys, one for each possible combination of
- // (authentication required, unlocked device required).
- make_superencrypted_key_entry(&mut db, nspace, "noauth_noud", false, false, super_key_id)?;
- make_superencrypted_key_entry(&mut db, nspace, "noauth_ud", false, true, super_key_id)?;
- make_superencrypted_key_entry(&mut db, nspace, "auth_noud", true, false, super_key_id)?;
- make_superencrypted_key_entry(&mut db, nspace, "auth_ud", true, true, super_key_id)?;
- assert!(app_key_exists(&mut db, nspace, "noauth_noud")?);
- assert!(app_key_exists(&mut db, nspace, "noauth_ud")?);
- assert!(app_key_exists(&mut db, nspace, "auth_noud")?);
- assert!(app_key_exists(&mut db, nspace, "auth_ud")?);
-
- // Also store a key for a different user that requires authentication.
- make_superencrypted_key_entry(
- &mut db,
- other_user_nspace,
- "auth_ud",
- true,
- true,
- super_key_id,
- )?;
-
- db.unbind_auth_bound_keys_for_user(user_id)?;
-
- // Verify that only the user's app keys that require authentication were
- // deleted. Keys that require an unlocked device but not authentication
- // should *not* have been deleted, nor should the super key have been
- // deleted, nor should other users' keys have been deleted.
- assert!(db.load_super_key(super_key_type, user_id)?.is_some());
- assert!(app_key_exists(&mut db, nspace, "noauth_noud")?);
- assert!(app_key_exists(&mut db, nspace, "noauth_ud")?);
- assert!(!app_key_exists(&mut db, nspace, "auth_noud")?);
- assert!(!app_key_exists(&mut db, nspace, "auth_ud")?);
- assert!(app_key_exists(&mut db, other_user_nspace, "auth_ud")?);
-
- Ok(())
- }
-
- #[test]
- fn test_store_super_key() -> Result<()> {
- let mut db = new_test_db()?;
- let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
- let super_key = keystore2_crypto::generate_aes256_key()?;
- let secret_bytes = b"keystore2 is great.";
- let (encrypted_secret, iv, tag) =
- keystore2_crypto::aes_gcm_encrypt(secret_bytes, &super_key)?;
-
- let (encrypted_super_key, metadata) =
- SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
- db.store_super_key(
- 1,
- &USER_AFTER_FIRST_UNLOCK_SUPER_KEY,
- &encrypted_super_key,
- &metadata,
- &KeyMetaData::new(),
- )?;
-
- // Check if super key exists.
- assert!(db.key_exists(
- Domain::APP,
- 1,
- USER_AFTER_FIRST_UNLOCK_SUPER_KEY.alias,
- KeyType::Super
- )?);
-
- let (_, key_entry) = db.load_super_key(&USER_AFTER_FIRST_UNLOCK_SUPER_KEY, 1)?.unwrap();
- let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(
- USER_AFTER_FIRST_UNLOCK_SUPER_KEY.algorithm,
- key_entry,
- &pw,
- None,
- )?;
-
- let decrypted_secret_bytes = loaded_super_key.decrypt(&encrypted_secret, &iv, &tag)?;
- assert_eq!(secret_bytes, &*decrypted_secret_bytes);
-
- Ok(())
- }
-
- fn get_valid_statsd_storage_types() -> Vec<MetricsStorage> {
- vec![
- MetricsStorage::KEY_ENTRY,
- MetricsStorage::KEY_ENTRY_ID_INDEX,
- MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
- MetricsStorage::BLOB_ENTRY,
- MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
- MetricsStorage::KEY_PARAMETER,
- MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX,
- MetricsStorage::KEY_METADATA,
- MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX,
- MetricsStorage::GRANT,
- MetricsStorage::AUTH_TOKEN,
- MetricsStorage::BLOB_METADATA,
- MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
- ]
- }
-
- /// Perform a simple check to ensure that we can query all the storage types
- /// that are supported by the DB. Check for reasonable values.
- #[test]
- fn test_query_all_valid_table_sizes() -> Result<()> {
- const PAGE_SIZE: i32 = 4096;
-
- let mut db = new_test_db()?;
-
- for t in get_valid_statsd_storage_types() {
- let stat = db.get_storage_stat(t)?;
- // AuthToken can be less than a page since it's in a btree, not sqlite
- // TODO(b/187474736) stop using if-let here
- if let MetricsStorage::AUTH_TOKEN = t {
- } else {
- assert!(stat.size >= PAGE_SIZE);
- }
- assert!(stat.size >= stat.unused_size);
- }
-
- Ok(())
- }
-
- fn get_storage_stats_map(db: &mut KeystoreDB) -> BTreeMap<i32, StorageStats> {
- get_valid_statsd_storage_types()
- .into_iter()
- .map(|t| (t.0, db.get_storage_stat(t).unwrap()))
- .collect()
- }
-
- fn assert_storage_increased(
- db: &mut KeystoreDB,
- increased_storage_types: Vec<MetricsStorage>,
- baseline: &mut BTreeMap<i32, StorageStats>,
- ) {
- for storage in increased_storage_types {
- // Verify the expected storage increased.
- let new = db.get_storage_stat(storage).unwrap();
- let old = &baseline[&storage.0];
- assert!(new.size >= old.size, "{}: {} >= {}", storage.0, new.size, old.size);
- assert!(
- new.unused_size <= old.unused_size,
- "{}: {} <= {}",
- storage.0,
- new.unused_size,
- old.unused_size
- );
-
- // Update the baseline with the new value so that it succeeds in the
- // later comparison.
- baseline.insert(storage.0, new);
- }
-
- // Get an updated map of the storage and verify there were no unexpected changes.
- let updated_stats = get_storage_stats_map(db);
- assert_eq!(updated_stats.len(), baseline.len());
-
- for &k in baseline.keys() {
- let stringify = |map: &BTreeMap<i32, StorageStats>| -> String {
- let mut s = String::new();
- for &k in map.keys() {
- writeln!(&mut s, " {}: {}, {}", &k, map[&k].size, map[&k].unused_size)
- .expect("string concat failed");
- }
- s
- };
-
- assert!(
- updated_stats[&k].size == baseline[&k].size
- && updated_stats[&k].unused_size == baseline[&k].unused_size,
- "updated_stats:\n{}\nbaseline:\n{}",
- stringify(&updated_stats),
- stringify(baseline)
- );
- }
- }
-
- #[test]
- fn test_verify_key_table_size_reporting() -> Result<()> {
- let mut db = new_test_db()?;
- let mut working_stats = get_storage_stats_map(&mut db);
-
- let key_id = create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
- assert_storage_increased(
- &mut db,
- vec![
- MetricsStorage::KEY_ENTRY,
- MetricsStorage::KEY_ENTRY_ID_INDEX,
- MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
- ],
- &mut working_stats,
- );
-
- let mut blob_metadata = BlobMetaData::new();
- blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
- db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), None)?;
- assert_storage_increased(
- &mut db,
- vec![
- MetricsStorage::BLOB_ENTRY,
- MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
- MetricsStorage::BLOB_METADATA,
- MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
- ],
- &mut working_stats,
- );
-
- let params = make_test_params(None);
- db.insert_keyparameter(&key_id, ¶ms)?;
- assert_storage_increased(
- &mut db,
- vec![MetricsStorage::KEY_PARAMETER, MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX],
- &mut working_stats,
- );
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
- db.insert_key_metadata(&key_id, &metadata)?;
- assert_storage_increased(
- &mut db,
- vec![MetricsStorage::KEY_METADATA, MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX],
- &mut working_stats,
- );
-
- let mut sum = 0;
- for stat in working_stats.values() {
- sum += stat.size;
- }
- let total = db.get_storage_stat(MetricsStorage::DATABASE)?.size;
- assert!(sum <= total, "Expected sum <= total. sum: {}, total: {}", sum, total);
-
- Ok(())
- }
-
- #[test]
- fn test_verify_auth_table_size_reporting() -> Result<()> {
- let mut db = new_test_db()?;
- let mut working_stats = get_storage_stats_map(&mut db);
- db.insert_auth_token(&HardwareAuthToken {
- challenge: 123,
- userId: 456,
- authenticatorId: 789,
- authenticatorType: kmhw_authenticator_type::ANY,
- timestamp: Timestamp { milliSeconds: 10 },
- mac: b"mac".to_vec(),
- });
- assert_storage_increased(&mut db, vec![MetricsStorage::AUTH_TOKEN], &mut working_stats);
- Ok(())
- }
-
- #[test]
- fn test_verify_grant_table_size_reporting() -> Result<()> {
- const OWNER: i64 = 1;
- let mut db = new_test_db()?;
- make_test_key_entry(&mut db, Domain::APP, OWNER, TEST_ALIAS, None)?;
-
- let mut working_stats = get_storage_stats_map(&mut db);
- db.grant(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- OWNER as u32,
- 123,
- key_perm_set![KeyPerm::Use],
- |_, _| Ok(()),
- )?;
-
- assert_storage_increased(&mut db, vec![MetricsStorage::GRANT], &mut working_stats);
-
- Ok(())
- }
-
- #[test]
- fn find_auth_token_entry_returns_latest() -> Result<()> {
- let mut db = new_test_db()?;
- db.insert_auth_token(&HardwareAuthToken {
- challenge: 123,
- userId: 456,
- authenticatorId: 789,
- authenticatorType: kmhw_authenticator_type::ANY,
- timestamp: Timestamp { milliSeconds: 10 },
- mac: b"mac0".to_vec(),
- });
- std::thread::sleep(std::time::Duration::from_millis(1));
- db.insert_auth_token(&HardwareAuthToken {
- challenge: 123,
- userId: 457,
- authenticatorId: 789,
- authenticatorType: kmhw_authenticator_type::ANY,
- timestamp: Timestamp { milliSeconds: 12 },
- mac: b"mac1".to_vec(),
- });
- std::thread::sleep(std::time::Duration::from_millis(1));
- db.insert_auth_token(&HardwareAuthToken {
- challenge: 123,
- userId: 458,
- authenticatorId: 789,
- authenticatorType: kmhw_authenticator_type::ANY,
- timestamp: Timestamp { milliSeconds: 3 },
- mac: b"mac2".to_vec(),
- });
- // All three entries are in the database
- assert_eq!(db.perboot.auth_tokens_len(), 3);
- // It selected the most recent timestamp
- assert_eq!(db.find_auth_token_entry(|_| true).unwrap().auth_token.mac, b"mac2".to_vec());
- Ok(())
- }
-
- #[test]
- fn test_load_key_descriptor() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)?.0;
-
- let key = db.load_key_descriptor(key_id)?.unwrap();
-
- assert_eq!(key.domain, Domain::APP);
- assert_eq!(key.nspace, 1);
- assert_eq!(key.alias, Some(TEST_ALIAS.to_string()));
-
- // No such id
- 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(())
- }
-}