Merge "audit_log.rs: handle Results in LogContext handling"
diff --git a/identity/Android.bp b/identity/Android.bp
index da0df07..007a310 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -22,8 +22,8 @@
 
 }
 
-cc_binary {
-    name: "credstore",
+cc_defaults {
+    name: "credstore_defaults",
     defaults: [
         "identity_defaults",
         "identity_use_latest_hal_aidl_cpp_static",
@@ -31,7 +31,6 @@
         "keymint_use_latest_hal_aidl_cpp_static",
         "android.hardware.identity-support-lib-deps",
     ],
-
     srcs: [
         "Credential.cpp",
         "CredentialData.cpp",
@@ -40,9 +39,7 @@
         "Session.cpp",
         "Util.cpp",
         "WritableCredential.cpp",
-        "main.cpp",
     ],
-    init_rc: ["credstore.rc"],
     shared_libs: [
         "android.hardware.keymaster@4.0",
         "android.security.authorization-ndk",
@@ -68,6 +65,17 @@
     ],
 }
 
+cc_binary {
+    name: "credstore",
+    defaults: [
+        "credstore_defaults",
+    ],
+    srcs: [
+        "main.cpp",
+    ],
+    init_rc: ["credstore.rc"],
+}
+
 filegroup {
     name: "credstore_aidl",
     srcs: [
@@ -112,3 +120,22 @@
         "libbinder",
     ],
 }
+
+cc_fuzz {
+    name: "credstore_service_fuzzer",
+    defaults: [
+        "credstore_defaults",
+        "service_fuzzer_defaults",
+        "fuzzer_disable_leaks",
+    ],
+    srcs: [
+        "fuzzers/credstore_service_fuzzer.cpp",
+    ],
+    fuzz_config: {
+        triage_assignee: "waghpawan@google.com",
+        cc: [
+            "trong@google.com",
+            "zeuthen@google.com",
+        ]
+    },
+}
diff --git a/identity/fuzzers/credstore_service_fuzzer.cpp b/identity/fuzzers/credstore_service_fuzzer.cpp
new file mode 100644
index 0000000..008cb0d
--- /dev/null
+++ b/identity/fuzzers/credstore_service_fuzzer.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <fuzzbinder/libbinder_driver.h>
+#include <sys/stat.h>
+
+#include "CredentialStoreFactory.h"
+
+using android::security::identity::CredentialStoreFactory;
+using namespace android;
+
+void clearDirectory(const char* dirpath, bool recursive) {
+    DIR* dir = opendir(dirpath);
+    CHECK(dir != nullptr);
+    dirent* e;
+    struct stat s;
+    while ((e = readdir(dir)) != nullptr) {
+        if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) {
+            continue;
+        }
+        std::string filename(dirpath);
+        filename.push_back('/');
+        filename.append(e->d_name);
+        int stat_result = lstat(filename.c_str(), &s);
+        CHECK_EQ(0, stat_result) << "unable to stat " << filename;
+        if (S_ISDIR(s.st_mode)) {
+            if (recursive) {
+                clearDirectory(filename.c_str(), true);
+                int rmdir_result = rmdir(filename.c_str());
+                CHECK_EQ(0, rmdir_result) << filename;
+            }
+        } else {
+            int unlink_result = unlink(filename.c_str());
+            CHECK_EQ(0, unlink_result) << filename;
+        }
+    }
+    closedir(dir);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    std::string dataDir = "/data/cred_store_fuzzer";
+    mkdir(dataDir.c_str(), 0700);
+    sp<CredentialStoreFactory> service = sp<CredentialStoreFactory>::make(dataDir);
+    fuzzService(service, FuzzedDataProvider(data, size));
+    clearDirectory(dataDir.c_str(), true);
+    rmdir(dataDir.c_str());
+    return 0;
+}
diff --git a/keystore-engine/Android.bp b/keystore-engine/Android.bp
index cb75cde..52a2aef 100644
--- a/keystore-engine/Android.bp
+++ b/keystore-engine/Android.bp
@@ -36,7 +36,7 @@
     ],
 
     shared_libs: [
-        "android.system.keystore2-V1-ndk",
+        "android.system.keystore2-V3-ndk",
         "libbinder_ndk",
         "libcrypto",
         "libcutils",
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index 1953920..4f2c7bd 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -19,7 +19,6 @@
 use crate::error::anyhow_error_to_cstring;
 use crate::globals::{ENFORCEMENTS, SUPER_KEY, DB, LEGACY_IMPORTER};
 use crate::permission::KeystorePerm;
-use crate::super_key::UserState;
 use crate::utils::{check_keystore_permission, watchdog as wd};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     HardwareAuthToken::HardwareAuthToken,
@@ -158,31 +157,14 @@
                 let mut skm = SUPER_KEY.write().unwrap();
 
                 DB.with(|db| {
-                    skm.unlock_screen_lock_bound_key(
+                    skm.unlock_user(
                         &mut db.borrow_mut(),
+                        &LEGACY_IMPORTER,
                         user_id as u32,
                         &password,
                     )
                 })
-                .context(ks_err!("unlock_screen_lock_bound_key failed"))?;
-
-                // Unlock super key.
-                if let UserState::Uninitialized = DB
-                    .with(|db| {
-                        skm.unlock_and_get_user_state(
-                            &mut db.borrow_mut(),
-                            &LEGACY_IMPORTER,
-                            user_id as u32,
-                            &password,
-                        )
-                    })
-                    .context(ks_err!("Unlock with password."))?
-                {
-                    log::info!(
-                        "In on_lock_screen_event. Trying to unlock when LSKF is uninitialized."
-                    );
-                }
-
+                .context(ks_err!("Unlock with password."))?;
                 Ok(())
             }
             (LockScreenEvent::UNLOCK, None) => {
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 7d61a29..d6bfaf2 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -4653,7 +4653,7 @@
         params
     }
 
-    fn make_test_key_entry(
+    pub fn make_test_key_entry(
         db: &mut KeystoreDB,
         domain: Domain,
         namespace: i64,
@@ -4712,7 +4712,7 @@
         }
     }
 
-    fn make_bootlevel_key_entry(
+    pub fn make_bootlevel_key_entry(
         db: &mut KeystoreDB,
         domain: Domain,
         namespace: i64,
diff --git a/keystore2/src/legacy_importer.rs b/keystore2/src/legacy_importer.rs
index 9eb702d..325c213 100644
--- a/keystore2/src/legacy_importer.rs
+++ b/keystore2/src/legacy_importer.rs
@@ -102,6 +102,11 @@
         }
     }
 
+    #[cfg(test)]
+    pub fn set_empty(&mut self) {
+        self.state = AtomicU8::new(Self::STATE_EMPTY);
+    }
+
     /// The legacy importer must be initialized deferred, because keystore starts very early.
     /// At this time the data partition may not be mounted. So we cannot open database connections
     /// until we get actual key load requests. This sets the function that the legacy loader
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index 5efb798..73dc881 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -83,26 +83,24 @@
             .context(ks_err!("unlock_screen_lock_bound_key failed"))?;
         }
 
-        match DB
-            .with(|db| {
-                skm.reset_or_init_user_and_get_user_state(
-                    &mut db.borrow_mut(),
-                    &LEGACY_IMPORTER,
-                    user_id as u32,
-                    password.as_ref(),
-                )
-            })
-            .context(ks_err!())?
+        if let UserState::LskfLocked = DB
+            .with(|db| skm.get_user_state(&mut db.borrow_mut(), &LEGACY_IMPORTER, user_id as u32))
+            .context(ks_err!("Could not get user state while changing password!"))?
         {
-            UserState::LskfLocked => {
-                // Error - password can not be changed when the device is locked
-                Err(Error::Rc(ResponseCode::LOCKED)).context(ks_err!("Device is locked."))
-            }
-            _ => {
-                // LskfLocked is the only error case for password change
-                Ok(())
-            }
+            // Error - password can not be changed when the device is locked
+            return Err(Error::Rc(ResponseCode::LOCKED)).context(ks_err!("Device is locked."));
         }
+
+        DB.with(|db| match password {
+            Some(pass) => {
+                skm.init_user(&mut db.borrow_mut(), &LEGACY_IMPORTER, user_id as u32, &pass)
+            }
+            None => {
+                // User transitioned to swipe.
+                skm.reset_user(&mut db.borrow_mut(), &LEGACY_IMPORTER, user_id as u32)
+            }
+        })
+        .context(ks_err!("Failed to change user password!"))
     }
 
     fn add_or_remove_user(&self, user_id: i32) -> Result<()> {
@@ -111,11 +109,10 @@
         check_keystore_permission(KeystorePerm::ChangeUser).context(ks_err!())?;
 
         DB.with(|db| {
-            SUPER_KEY.write().unwrap().reset_user(
+            SUPER_KEY.write().unwrap().remove_user(
                 &mut db.borrow_mut(),
                 &LEGACY_IMPORTER,
                 user_id as u32,
-                false,
             )
         })
         .context(ks_err!("Trying to delete keys from db."))?;
diff --git a/keystore2/src/metrics_store.rs b/keystore2/src/metrics_store.rs
index 77cead8..5d311f0 100644
--- a/keystore2/src/metrics_store.rs
+++ b/keystore2/src/metrics_store.rs
@@ -43,9 +43,8 @@
     RkpError::RkpError as MetricsRkpError, RkpErrorStats::RkpErrorStats,
     SecurityLevel::SecurityLevel as MetricsSecurityLevel, Storage::Storage as MetricsStorage,
 };
-use anyhow::{Context, Result};
+use anyhow::{anyhow, Context, Result};
 use lazy_static::lazy_static;
-use rustutils::system_properties::PropertyWatcherError;
 use std::collections::HashMap;
 use std::sync::Mutex;
 
@@ -93,12 +92,15 @@
 
         // Process keystore crash stats.
         if AtomID::CRASH_STATS == atom_id {
-            return Ok(vec![KeystoreAtom {
-                payload: KeystoreAtomPayload::CrashStats(CrashStats {
-                    count_of_crash_events: read_keystore_crash_count()?,
-                }),
-                ..Default::default()
-            }]);
+            return match read_keystore_crash_count()? {
+                Some(count) => Ok(vec![KeystoreAtom {
+                    payload: KeystoreAtomPayload::CrashStats(CrashStats {
+                        count_of_crash_events: count,
+                    }),
+                    ..Default::default()
+                }]),
+                None => Err(anyhow!("Crash count property is not set")),
+            };
         }
 
         // It is safe to call unwrap here since the lock can not be poisoned based on its usage
@@ -564,27 +566,21 @@
 /// If the property is absent, it sets the property with value 0. If the property is present, it
 /// increments the value. This helps tracking keystore crashes internally.
 pub fn update_keystore_crash_sysprop() {
-    let crash_count = read_keystore_crash_count();
-    let new_count = match crash_count {
-        Ok(count) => count + 1,
+    let new_count = match read_keystore_crash_count() {
+        Ok(Some(count)) => count + 1,
+        // If the property is absent, then this is the first start up during the boot.
+        // Proceed to write the system property with value 0.
+        Ok(None) => 0,
         Err(error) => {
-            // If the property is absent, this is the first start up during the boot.
-            // Proceed to write the system property with value 0. Otherwise, log and return.
-            if !matches!(
-                error.root_cause().downcast_ref::<PropertyWatcherError>(),
-                Some(PropertyWatcherError::SystemPropertyAbsent)
-            ) {
-                log::warn!(
-                    concat!(
-                        "In update_keystore_crash_sysprop: ",
-                        "Failed to read the existing system property due to: {:?}.",
-                        "Therefore, keystore crashes will not be logged."
-                    ),
-                    error
-                );
-                return;
-            }
-            0
+            log::warn!(
+                concat!(
+                    "In update_keystore_crash_sysprop: ",
+                    "Failed to read the existing system property due to: {:?}.",
+                    "Therefore, keystore crashes will not be logged."
+                ),
+                error
+            );
+            return;
         }
     };
 
@@ -602,12 +598,12 @@
 }
 
 /// Read the system property: keystore.crash_count.
-pub fn read_keystore_crash_count() -> Result<i32> {
-    rustutils::system_properties::read("keystore.crash_count")
-        .context(ks_err!("Failed read property."))?
-        .context(ks_err!("Property not set."))?
-        .parse::<i32>()
-        .map_err(std::convert::Into::into)
+pub fn read_keystore_crash_count() -> Result<Option<i32>> {
+    match rustutils::system_properties::read("keystore.crash_count") {
+        Ok(Some(count)) => count.parse::<i32>().map(Some).map_err(std::convert::Into::into),
+        Ok(None) => Ok(None),
+        Err(e) => Err(e).context(ks_err!("Failed to read crash count property.")),
+    }
 }
 
 /// Enum defining the bit position for each padding mode. Since padding mode can be repeatable, it
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index cb9960f..2e8b60f 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -26,7 +26,6 @@
     error::ResponseCode,
     key_parameter::{KeyParameter, KeyParameterValue},
     ks_err,
-    legacy_blob::LegacyBlobLoader,
     legacy_importer::LegacyImporter,
     raw_device::KeyMintDevice,
     utils::{watchdog as wd, AesGcm, AID_KEYSTORE},
@@ -400,51 +399,6 @@
         self.data.user_keys.get(&user_id).and_then(|e| e.per_boot.as_ref().cloned())
     }
 
-    /// This function unlocks the super keys for a given user.
-    /// This means the key is loaded from the database, decrypted and placed in the
-    /// super key cache. If there is no such key a new key is created, encrypted with
-    /// a key derived from the given password and stored in the database.
-    pub fn unlock_user_key(
-        &mut self,
-        db: &mut KeystoreDB,
-        user: UserId,
-        pw: &Password,
-        legacy_blob_loader: &LegacyBlobLoader,
-    ) -> Result<()> {
-        let (_, entry) = db
-            .get_or_create_key_with(
-                Domain::APP,
-                user as u64 as i64,
-                USER_SUPER_KEY.alias,
-                crate::database::KEYSTORE_UUID,
-                || {
-                    // For backward compatibility we need to check if there is a super key present.
-                    let super_key = legacy_blob_loader
-                        .load_super_key(user, pw)
-                        .context(ks_err!("Failed to load legacy key blob."))?;
-                    let super_key = match super_key {
-                        None => {
-                            // No legacy file was found. So we generate a new key.
-                            generate_aes256_key()
-                                .context(ks_err!("Failed to generate AES 256 key."))?
-                        }
-                        Some(key) => key,
-                    };
-                    // Regardless of whether we loaded an old AES128 key or generated a new AES256
-                    // key as the super key, we derive a AES256 key from the password and re-encrypt
-                    // the super key before we insert it in the database. The length of the key is
-                    // preserved by the encryption so we don't need any extra flags to inform us
-                    // which algorithm to use it with.
-                    Self::encrypt_with_password(&super_key, pw).context("In create_new_key.")
-                },
-            )
-            .context(ks_err!("Failed to get key id."))?;
-
-        self.populate_cache_from_super_key_blob(user, USER_SUPER_KEY.algorithm, entry, pw)
-            .context(ks_err!())?;
-        Ok(())
-    }
-
     /// Check if a given key is super-encrypted, from its metadata. If so, unwrap the key using
     /// the relevant super key.
     pub fn unwrap_key_if_required<'a>(
@@ -526,83 +480,6 @@
         }
     }
 
-    /// Checks if user has already setup LSKF (i.e. a super key is persisted in the database or the
-    /// legacy database). If not, return Uninitialized state.
-    /// Otherwise, decrypt the super key from the password and return LskfUnlocked state.
-    pub fn check_and_unlock_super_key(
-        &mut self,
-        db: &mut KeystoreDB,
-        legacy_importer: &LegacyImporter,
-        user_id: UserId,
-        pw: &Password,
-    ) -> Result<UserState> {
-        let alias = &USER_SUPER_KEY;
-        let result = legacy_importer
-            .with_try_import_super_key(user_id, pw, || db.load_super_key(alias, user_id))
-            .context(ks_err!("Failed to load super key"))?;
-
-        match result {
-            Some((_, entry)) => {
-                let super_key = self
-                    .populate_cache_from_super_key_blob(user_id, alias.algorithm, entry, pw)
-                    .context(ks_err!())?;
-                Ok(UserState::LskfUnlocked(super_key))
-            }
-            None => Ok(UserState::Uninitialized),
-        }
-    }
-
-    /// Checks if user has already setup LSKF (i.e. a super key is persisted in the database or the
-    /// legacy database). If so, return LskfLocked state.
-    /// If the password is provided, generate a new super key, encrypt with the password,
-    /// store in the database and populate the super key cache for the new user
-    /// and return LskfUnlocked state.
-    /// If the password is not provided, return Uninitialized state.
-    pub fn check_and_initialize_super_key(
-        &mut self,
-        db: &mut KeystoreDB,
-        legacy_importer: &LegacyImporter,
-        user_id: UserId,
-        pw: Option<&Password>,
-    ) -> Result<UserState> {
-        let super_key_exists_in_db = self
-            .super_key_exists_in_db_for_user(db, legacy_importer, user_id)
-            .context(ks_err!("Failed to check if super key exists."))?;
-        if super_key_exists_in_db {
-            Ok(UserState::LskfLocked)
-        } else if let Some(pw) = pw {
-            // Generate a new super key.
-            let super_key =
-                generate_aes256_key().context(ks_err!("Failed to generate AES 256 key."))?;
-            // Derive an AES256 key from the password and re-encrypt the super key
-            // before we insert it in the database.
-            let (encrypted_super_key, blob_metadata) =
-                Self::encrypt_with_password(&super_key, pw).context(ks_err!())?;
-
-            let key_entry = db
-                .store_super_key(
-                    user_id,
-                    &USER_SUPER_KEY,
-                    &encrypted_super_key,
-                    &blob_metadata,
-                    &KeyMetaData::new(),
-                )
-                .context(ks_err!("Failed to store super key."))?;
-
-            let super_key = self
-                .populate_cache_from_super_key_blob(
-                    user_id,
-                    USER_SUPER_KEY.algorithm,
-                    key_entry,
-                    pw,
-                )
-                .context(ks_err!())?;
-            Ok(UserState::LskfUnlocked(super_key))
-        } else {
-            Ok(UserState::Uninitialized)
-        }
-    }
-
     // Helper function to populate super key cache from the super key blob loaded from the database.
     fn populate_cache_from_super_key_blob(
         &mut self,
@@ -613,7 +490,8 @@
     ) -> Result<Arc<SuperKey>> {
         let super_key = Self::extract_super_key_from_key_entry(algorithm, entry, pw, None)
             .context(ks_err!("Failed to extract super key from key entry"))?;
-        self.install_per_boot_key_for_user(user_id, super_key.clone())?;
+        self.install_per_boot_key_for_user(user_id, super_key.clone())
+            .context(ks_err!("Failed to install per boot key for user!"))?;
         Ok(super_key)
     }
 
@@ -683,33 +561,6 @@
         Ok((encrypted_key, metadata))
     }
 
-    // Encrypt the given key blob with the user's super key, if the super key exists and the device
-    // is unlocked. If the super key exists and the device is locked, or LSKF is not setup,
-    // return error. Note that it is out of the scope of this function to check if super encryption
-    // is required. Such check should be performed before calling this function.
-    fn super_encrypt_on_key_init(
-        &self,
-        db: &mut KeystoreDB,
-        legacy_importer: &LegacyImporter,
-        user_id: UserId,
-        key_blob: &[u8],
-    ) -> Result<(Vec<u8>, BlobMetaData)> {
-        match self
-            .get_user_state(db, legacy_importer, user_id)
-            .context(ks_err!("Failed to get user state."))?
-        {
-            UserState::LskfUnlocked(super_key) => {
-                Self::encrypt_with_aes_super_key(key_blob, &super_key)
-                    .context(ks_err!("Failed to encrypt the key."))
-            }
-            UserState::LskfLocked => {
-                Err(Error::Rc(ResponseCode::LOCKED)).context(ks_err!("Device is locked."))
-            }
-            UserState::Uninitialized => Err(Error::Rc(ResponseCode::UNINITIALIZED))
-                .context(ks_err!("LSKF is not setup for the user.")),
-        }
-    }
-
     // Helper function to encrypt a key with the given super key. Callers should select which super
     // key to be used. This is called when a key is super encrypted at its creation as well as at
     // its upgrade.
@@ -729,6 +580,50 @@
         Ok((encrypted_key, metadata))
     }
 
+    // Encrypts a given key_blob using a hybrid approach, which can either use the symmetric super
+    // key or the public super key depending on which is available.
+    //
+    // If the symmetric_key is available, the key_blob is encrypted using symmetric encryption with
+    // the provided symmetric super key.  Otherwise, the function loads the public super key from
+    // the KeystoreDB and encrypts the key_blob using ECDH encryption and marks the keyblob to be
+    // re-encrypted with the symmetric super key on the first use.
+    //
+    // This hybrid scheme allows lock-screen-bound keys to be added when the screen is locked.
+    fn encrypt_with_hybrid_super_key(
+        key_blob: &[u8],
+        symmetric_key: Option<&SuperKey>,
+        public_key_type: &SuperKeyType,
+        db: &mut KeystoreDB,
+        user_id: UserId,
+    ) -> Result<(Vec<u8>, BlobMetaData)> {
+        if let Some(super_key) = symmetric_key {
+            Self::encrypt_with_aes_super_key(key_blob, super_key)
+                .context(ks_err!("Failed to encrypt with ScreenLockBound super key."))
+        } else {
+            // Symmetric key is not available, use public key encryption
+            let loaded = db
+                .load_super_key(public_key_type, user_id)
+                .context(ks_err!("load_super_key failed."))?;
+            let (key_id_guard, key_entry) =
+                loaded.ok_or_else(Error::sys).context(ks_err!("User ECDH super key missing."))?;
+            let public_key = key_entry
+                .metadata()
+                .sec1_public_key()
+                .ok_or_else(Error::sys)
+                .context(ks_err!("sec1_public_key missing."))?;
+            let mut metadata = BlobMetaData::new();
+            let (ephem_key, salt, iv, encrypted_key, aead_tag) =
+                ECDHPrivateKey::encrypt_message(public_key, key_blob)
+                    .context(ks_err!("ECDHPrivateKey::encrypt_message failed."))?;
+            metadata.add(BlobMetaEntry::PublicKey(ephem_key));
+            metadata.add(BlobMetaEntry::Salt(salt));
+            metadata.add(BlobMetaEntry::Iv(iv));
+            metadata.add(BlobMetaEntry::AeadTag(aead_tag));
+            SuperKeyIdentifier::DatabaseId(key_id_guard.id()).add_to_metadata(&mut metadata);
+            Ok((encrypted_key, metadata))
+        }
+    }
+
     /// Check if super encryption is required and if so, super-encrypt the key to be stored in
     /// the database.
     #[allow(clippy::too_many_arguments)]
@@ -744,40 +639,40 @@
     ) -> Result<(Vec<u8>, BlobMetaData)> {
         match Enforcements::super_encryption_required(domain, key_parameters, flags) {
             SuperEncryptionType::None => Ok((key_blob.to_vec(), BlobMetaData::new())),
-            SuperEncryptionType::LskfBound => self
-                .super_encrypt_on_key_init(db, legacy_importer, user_id, key_blob)
-                .context(ks_err!("Failed to super encrypt with LskfBound key.")),
-            SuperEncryptionType::ScreenLockBound => {
-                let entry =
-                    self.data.user_keys.get(&user_id).and_then(|e| e.screen_lock_bound.as_ref());
-                if let Some(super_key) = entry {
-                    Self::encrypt_with_aes_super_key(key_blob, super_key)
-                        .context(ks_err!("Failed to encrypt with ScreenLockBound key."))
-                } else {
-                    // Symmetric key is not available, use public key encryption
-                    let loaded = db
-                        .load_super_key(&USER_SCREEN_LOCK_BOUND_P521_KEY, user_id)
-                        .context(ks_err!("load_super_key failed."))?;
-                    let (key_id_guard, key_entry) =
-                        loaded.ok_or_else(Error::sys).context(ks_err!("User ECDH key missing."))?;
-                    let public_key = key_entry
-                        .metadata()
-                        .sec1_public_key()
-                        .ok_or_else(Error::sys)
-                        .context(ks_err!("sec1_public_key missing."))?;
-                    let mut metadata = BlobMetaData::new();
-                    let (ephem_key, salt, iv, encrypted_key, aead_tag) =
-                        ECDHPrivateKey::encrypt_message(public_key, key_blob)
-                            .context(ks_err!("ECDHPrivateKey::encrypt_message failed."))?;
-                    metadata.add(BlobMetaEntry::PublicKey(ephem_key));
-                    metadata.add(BlobMetaEntry::Salt(salt));
-                    metadata.add(BlobMetaEntry::Iv(iv));
-                    metadata.add(BlobMetaEntry::AeadTag(aead_tag));
-                    SuperKeyIdentifier::DatabaseId(key_id_guard.id())
-                        .add_to_metadata(&mut metadata);
-                    Ok((encrypted_key, metadata))
+            SuperEncryptionType::LskfBound => {
+                // Encrypt the given key blob with the user's per-boot super key.  If the per-boot
+                // super key is not unlocked or the LSKF is not setup, an error is returned.
+                match self
+                    .get_user_state(db, legacy_importer, user_id)
+                    .context(ks_err!("Failed to get user state."))?
+                {
+                    UserState::LskfUnlocked(super_key) => {
+                        Self::encrypt_with_aes_super_key(key_blob, &super_key)
+                            .context(ks_err!("Failed to encrypt with LskfBound key."))
+                    }
+                    UserState::LskfLocked => {
+                        Err(Error::Rc(ResponseCode::LOCKED)).context(ks_err!("Device is locked."))
+                    }
+                    UserState::Uninitialized => Err(Error::Rc(ResponseCode::UNINITIALIZED))
+                        .context(ks_err!("LSKF is not setup for the user.")),
                 }
             }
+            SuperEncryptionType::ScreenLockBound => {
+                let screen_lock_bound_symmetric_key = self
+                    .data
+                    .user_keys
+                    .get(&user_id)
+                    .and_then(|e| e.screen_lock_bound.as_ref())
+                    .map(|arc| arc.as_ref());
+                Self::encrypt_with_hybrid_super_key(
+                    key_blob,
+                    screen_lock_bound_symmetric_key,
+                    &USER_SCREEN_LOCK_BOUND_P521_KEY,
+                    db,
+                    user_id,
+                )
+                .context(ks_err!("Failed to encrypt with ScreenLockBound hybrid scheme."))
+            }
             SuperEncryptionType::BootLevel(level) => {
                 let key_id = SuperKeyIdentifier::BootLevel(level);
                 let super_key = self
@@ -1085,93 +980,147 @@
         }
     }
 
-    /// If the given user is unlocked:
-    /// * and `password` is None, the user is reset, all authentication bound keys are deleted and
-    ///   `Ok(UserState::Uninitialized)` is returned.
-    /// * and `password` is Some, `Ok(UserState::LskfUnlocked)` is returned.
-    /// If the given user is locked:
-    /// * and the user was initialized before, `Ok(UserState::Locked)` is returned.
-    /// * and the user was not initialized before:
-    ///   * and `password` is None, `Ok(Uninitialized)` is returned.
-    ///   * and `password` is Some, super keys are generated and `Ok(UserState::LskfUnlocked)` is
-    ///     returned.
-    pub fn reset_or_init_user_and_get_user_state(
+    /// Deletes all keys and super keys for the given user.
+    /// This is called when a user is deleted.
+    pub fn remove_user(
         &mut self,
         db: &mut KeystoreDB,
         legacy_importer: &LegacyImporter,
         user_id: UserId,
-        password: Option<&Password>,
-    ) -> Result<UserState> {
-        match self.get_per_boot_key_by_user_id_internal(user_id) {
-            Some(_) if password.is_none() => {
-                // Transitioning to swiping, delete only the super key in database and cache,
-                // and super-encrypted keys in database (and in KM).
-                self.reset_user(db, legacy_importer, user_id, true)
-                    .context(ks_err!("Trying to delete keys from the db."))?;
-                // Lskf is now removed in Keystore.
-                Ok(UserState::Uninitialized)
-            }
-            Some(super_key) => {
-                // Keystore won't be notified when changing to a new password when LSKF is
-                // already setup. Therefore, ideally this path wouldn't be reached.
-                Ok(UserState::LskfUnlocked(super_key))
-            }
-            None => {
-                // Check if a super key exists in the database or legacy database.
-                // If so, return LskfLocked state.
-                // Otherwise, i) if the password is provided, initialize the super key and return
-                // LskfUnlocked state ii) if password is not provided, return Uninitialized state.
-                self.check_and_initialize_super_key(db, legacy_importer, user_id, password)
-            }
-        }
+    ) -> Result<()> {
+        // Mark keys created on behalf of the user as unreferenced.
+        legacy_importer
+            .bulk_delete_user(user_id, false)
+            .context(ks_err!("Trying to delete legacy keys."))?;
+        db.unbind_keys_for_user(user_id, false).context(ks_err!("Error in unbinding keys."))?;
+
+        // Delete super key in cache, if exists.
+        self.forget_all_keys_for_user(user_id);
+        Ok(())
     }
 
-    /// Unlocks the given user with the given password. If the key was already unlocked or unlocking
-    /// was successful, `Ok(UserState::LskfUnlocked)` is returned.
-    /// If the user was never initialized `Ok(UserState::Uninitialized)` is returned.
-    pub fn unlock_and_get_user_state(
-        &mut self,
-        db: &mut KeystoreDB,
-        legacy_importer: &LegacyImporter,
-        user_id: UserId,
-        password: &Password,
-    ) -> Result<UserState> {
-        match self.get_per_boot_key_by_user_id_internal(user_id) {
-            Some(super_key) => {
-                log::info!("Trying to unlock when already unlocked.");
-                Ok(UserState::LskfUnlocked(super_key))
-            }
-            None => {
-                // Check if a super key exists in the database or legacy database.
-                // If not, return Uninitialized state.
-                // Otherwise, try to unlock the super key and if successful,
-                // return LskfUnlocked.
-                self.check_and_unlock_super_key(db, legacy_importer, user_id, password)
-                    .context(ks_err!("Failed to unlock super key."))
-            }
-        }
-    }
-
-    /// Delete all the keys created on behalf of the user.
-    /// If 'keep_non_super_encrypted_keys' is set to true, delete only the super key and super
-    /// encrypted keys.
+    /// Deletes all authentication bound keys and super keys for the given user.  The user must be
+    /// unlocked before this function is called.  This function is used to transition a user to
+    /// swipe.
     pub fn reset_user(
         &mut self,
         db: &mut KeystoreDB,
         legacy_importer: &LegacyImporter,
         user_id: UserId,
-        keep_non_super_encrypted_keys: bool,
     ) -> Result<()> {
-        // Mark keys created on behalf of the user as unreferenced.
-        legacy_importer
-            .bulk_delete_user(user_id, keep_non_super_encrypted_keys)
-            .context(ks_err!("Trying to delete legacy keys."))?;
-        db.unbind_keys_for_user(user_id, keep_non_super_encrypted_keys)
-            .context(ks_err!("Error in unbinding keys."))?;
+        match self.get_user_state(db, legacy_importer, user_id)? {
+            UserState::Uninitialized => {
+                Err(Error::sys()).context(ks_err!("Tried to reset an uninitialized user!"))
+            }
+            UserState::LskfLocked => {
+                Err(Error::sys()).context(ks_err!("Tried to reset a locked user's password!"))
+            }
+            UserState::LskfUnlocked(_) => {
+                // Mark keys created on behalf of the user as unreferenced.
+                legacy_importer
+                    .bulk_delete_user(user_id, true)
+                    .context(ks_err!("Trying to delete legacy keys."))?;
+                db.unbind_keys_for_user(user_id, true)
+                    .context(ks_err!("Error in unbinding keys."))?;
 
-        // Delete super key in cache, if exists.
-        self.forget_all_keys_for_user(user_id);
-        Ok(())
+                // Delete super key in cache, if exists.
+                self.forget_all_keys_for_user(user_id);
+                Ok(())
+            }
+        }
+    }
+
+    /// If the user hasn't been initialized yet, then this function generates the user's super keys
+    /// and sets the user's state to LskfUnlocked.  Otherwise this function returns an error.
+    pub fn init_user(
+        &mut self,
+        db: &mut KeystoreDB,
+        legacy_importer: &LegacyImporter,
+        user_id: UserId,
+        password: &Password,
+    ) -> Result<()> {
+        match self.get_user_state(db, legacy_importer, user_id)? {
+            UserState::LskfUnlocked(_) | UserState::LskfLocked => {
+                Err(Error::sys()).context(ks_err!("Tried to re-init an initialized user!"))
+            }
+            UserState::Uninitialized => {
+                // Generate a new super key.
+                let super_key =
+                    generate_aes256_key().context(ks_err!("Failed to generate AES 256 key."))?;
+                // Derive an AES256 key from the password and re-encrypt the super key
+                // before we insert it in the database.
+                let (encrypted_super_key, blob_metadata) =
+                    Self::encrypt_with_password(&super_key, password)
+                        .context(ks_err!("Failed to encrypt super key with password!"))?;
+
+                let key_entry = db
+                    .store_super_key(
+                        user_id,
+                        &USER_SUPER_KEY,
+                        &encrypted_super_key,
+                        &blob_metadata,
+                        &KeyMetaData::new(),
+                    )
+                    .context(ks_err!("Failed to store super key."))?;
+
+                self.populate_cache_from_super_key_blob(
+                    user_id,
+                    USER_SUPER_KEY.algorithm,
+                    key_entry,
+                    password,
+                )
+                .context(ks_err!("Failed to initialize user!"))?;
+                Ok(())
+            }
+        }
+    }
+
+    /// Unlocks the given user with the given password.
+    ///
+    /// If the user is LskfLocked:
+    /// - Unlock the per_boot super key
+    /// - Unlock the screen_lock_bound super key
+    ///
+    /// If the user is LskfUnlocked:
+    /// - Unlock the screen_lock_bound super key only
+    ///
+    pub fn unlock_user(
+        &mut self,
+        db: &mut KeystoreDB,
+        legacy_importer: &LegacyImporter,
+        user_id: UserId,
+        password: &Password,
+    ) -> Result<()> {
+        match self.get_user_state(db, legacy_importer, user_id)? {
+            UserState::LskfUnlocked(_) => self.unlock_screen_lock_bound_key(db, user_id, password),
+            UserState::Uninitialized => {
+                Err(Error::sys()).context(ks_err!("Tried to unlock an uninitialized user!"))
+            }
+            UserState::LskfLocked => {
+                let alias = &USER_SUPER_KEY;
+                let result = legacy_importer
+                    .with_try_import_super_key(user_id, password, || {
+                        db.load_super_key(alias, user_id)
+                    })
+                    .context(ks_err!("Failed to load super key"))?;
+
+                match result {
+                    Some((_, entry)) => {
+                        self.populate_cache_from_super_key_blob(
+                            user_id,
+                            alias.algorithm,
+                            entry,
+                            password,
+                        )
+                        .context(ks_err!("Failed when unlocking user."))?;
+                        self.unlock_screen_lock_bound_key(db, user_id, password)
+                    }
+                    None => {
+                        Err(Error::sys()).context(ks_err!("Locked user does not have a super key!"))
+                    }
+                }
+            }
+        }
     }
 }
 
@@ -1233,3 +1182,390 @@
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::database::tests::make_bootlevel_key_entry;
+    use crate::database::tests::make_test_key_entry;
+    use crate::database::tests::new_test_db;
+    use rand::prelude::*;
+    const USER_ID: u32 = 0;
+    const TEST_KEY_ALIAS: &str = "TEST_KEY";
+    const TEST_BOOT_KEY_ALIAS: &str = "TEST_BOOT_KEY";
+
+    pub fn generate_password_blob() -> Password<'static> {
+        let mut rng = rand::thread_rng();
+        let mut password = vec![0u8; 64];
+        rng.fill_bytes(&mut password);
+
+        let mut zvec = ZVec::new(64).expect("Failed to create ZVec");
+        zvec[..].copy_from_slice(&password[..]);
+
+        Password::Owned(zvec)
+    }
+
+    fn setup_test(pw: &Password) -> (Arc<RwLock<SuperKeyManager>>, KeystoreDB, LegacyImporter) {
+        let mut keystore_db = new_test_db().unwrap();
+        let mut legacy_importer = LegacyImporter::new(Arc::new(Default::default()));
+        legacy_importer.set_empty();
+        let skm: Arc<RwLock<SuperKeyManager>> = Default::default();
+        assert!(skm
+            .write()
+            .unwrap()
+            .init_user(&mut keystore_db, &legacy_importer, USER_ID, pw)
+            .is_ok());
+        (skm, keystore_db, legacy_importer)
+    }
+
+    fn assert_unlocked(
+        skm: &Arc<RwLock<SuperKeyManager>>,
+        keystore_db: &mut KeystoreDB,
+        legacy_importer: &LegacyImporter,
+        user_id: u32,
+        err_msg: &str,
+    ) {
+        let user_state =
+            skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
+        match user_state {
+            UserState::LskfUnlocked(_) => {}
+            _ => panic!("{}", err_msg),
+        }
+    }
+
+    fn assert_locked(
+        skm: &Arc<RwLock<SuperKeyManager>>,
+        keystore_db: &mut KeystoreDB,
+        legacy_importer: &LegacyImporter,
+        user_id: u32,
+        err_msg: &str,
+    ) {
+        let user_state =
+            skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
+        match user_state {
+            UserState::LskfLocked => {}
+            _ => panic!("{}", err_msg),
+        }
+    }
+
+    fn assert_uninitialized(
+        skm: &Arc<RwLock<SuperKeyManager>>,
+        keystore_db: &mut KeystoreDB,
+        legacy_importer: &LegacyImporter,
+        user_id: u32,
+        err_msg: &str,
+    ) {
+        let user_state =
+            skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
+        match user_state {
+            UserState::Uninitialized => {}
+            _ => panic!("{}", err_msg),
+        }
+    }
+
+    #[test]
+    fn test_init_user() {
+        let pw: Password = generate_password_blob();
+        let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+        assert_unlocked(
+            &skm,
+            &mut keystore_db,
+            &legacy_importer,
+            USER_ID,
+            "The user was not unlocked after initialization!",
+        );
+    }
+
+    #[test]
+    fn test_unlock_user() {
+        let pw: Password = generate_password_blob();
+        let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+        assert_unlocked(
+            &skm,
+            &mut keystore_db,
+            &legacy_importer,
+            USER_ID,
+            "The user was not unlocked after initialization!",
+        );
+
+        skm.write().unwrap().data.user_keys.clear();
+        assert_locked(
+            &skm,
+            &mut keystore_db,
+            &legacy_importer,
+            USER_ID,
+            "Clearing the cache did not lock the user!",
+        );
+
+        assert!(skm
+            .write()
+            .unwrap()
+            .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &pw)
+            .is_ok());
+        assert_unlocked(
+            &skm,
+            &mut keystore_db,
+            &legacy_importer,
+            USER_ID,
+            "The user did not unlock!",
+        );
+    }
+
+    #[test]
+    fn test_unlock_wrong_password() {
+        let pw: Password = generate_password_blob();
+        let wrong_pw: Password = generate_password_blob();
+        let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+        assert_unlocked(
+            &skm,
+            &mut keystore_db,
+            &legacy_importer,
+            USER_ID,
+            "The user was not unlocked after initialization!",
+        );
+
+        skm.write().unwrap().data.user_keys.clear();
+        assert_locked(
+            &skm,
+            &mut keystore_db,
+            &legacy_importer,
+            USER_ID,
+            "Clearing the cache did not lock the user!",
+        );
+
+        assert!(skm
+            .write()
+            .unwrap()
+            .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &wrong_pw)
+            .is_err());
+        assert_locked(
+            &skm,
+            &mut keystore_db,
+            &legacy_importer,
+            USER_ID,
+            "The user was unlocked with an incorrect password!",
+        );
+    }
+
+    #[test]
+    fn test_unlock_user_idempotent() {
+        let pw: Password = generate_password_blob();
+        let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+        assert_unlocked(
+            &skm,
+            &mut keystore_db,
+            &legacy_importer,
+            USER_ID,
+            "The user was not unlocked after initialization!",
+        );
+
+        skm.write().unwrap().data.user_keys.clear();
+        assert_locked(
+            &skm,
+            &mut keystore_db,
+            &legacy_importer,
+            USER_ID,
+            "Clearing the cache did not lock the user!",
+        );
+
+        for _ in 0..5 {
+            assert!(skm
+                .write()
+                .unwrap()
+                .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &pw)
+                .is_ok());
+            assert_unlocked(
+                &skm,
+                &mut keystore_db,
+                &legacy_importer,
+                USER_ID,
+                "The user did not unlock!",
+            );
+        }
+    }
+
+    fn test_user_removal(locked: bool) {
+        let pw: Password = generate_password_blob();
+        let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+        assert_unlocked(
+            &skm,
+            &mut keystore_db,
+            &legacy_importer,
+            USER_ID,
+            "The user was not unlocked after initialization!",
+        );
+
+        assert!(make_test_key_entry(
+            &mut keystore_db,
+            Domain::APP,
+            USER_ID.into(),
+            TEST_KEY_ALIAS,
+            None
+        )
+        .is_ok());
+        assert!(make_bootlevel_key_entry(
+            &mut keystore_db,
+            Domain::APP,
+            USER_ID.into(),
+            TEST_BOOT_KEY_ALIAS,
+            false
+        )
+        .is_ok());
+
+        assert!(keystore_db
+            .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
+            .unwrap());
+        assert!(keystore_db
+            .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
+            .unwrap());
+
+        if locked {
+            skm.write().unwrap().data.user_keys.clear();
+            assert_locked(
+                &skm,
+                &mut keystore_db,
+                &legacy_importer,
+                USER_ID,
+                "Clearing the cache did not lock the user!",
+            );
+        }
+
+        assert!(skm
+            .write()
+            .unwrap()
+            .remove_user(&mut keystore_db, &legacy_importer, USER_ID)
+            .is_ok());
+        assert_uninitialized(
+            &skm,
+            &mut keystore_db,
+            &legacy_importer,
+            USER_ID,
+            "The user was not removed!",
+        );
+
+        assert!(!skm
+            .write()
+            .unwrap()
+            .super_key_exists_in_db_for_user(&mut keystore_db, &legacy_importer, USER_ID)
+            .unwrap());
+
+        assert!(!keystore_db
+            .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
+            .unwrap());
+        assert!(!keystore_db
+            .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
+            .unwrap());
+    }
+
+    fn test_user_reset(locked: bool) {
+        let pw: Password = generate_password_blob();
+        let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+        assert_unlocked(
+            &skm,
+            &mut keystore_db,
+            &legacy_importer,
+            USER_ID,
+            "The user was not unlocked after initialization!",
+        );
+
+        assert!(make_test_key_entry(
+            &mut keystore_db,
+            Domain::APP,
+            USER_ID.into(),
+            TEST_KEY_ALIAS,
+            None
+        )
+        .is_ok());
+        assert!(make_bootlevel_key_entry(
+            &mut keystore_db,
+            Domain::APP,
+            USER_ID.into(),
+            TEST_BOOT_KEY_ALIAS,
+            false
+        )
+        .is_ok());
+        assert!(keystore_db
+            .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
+            .unwrap());
+        assert!(keystore_db
+            .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
+            .unwrap());
+
+        if locked {
+            skm.write().unwrap().data.user_keys.clear();
+            assert_locked(
+                &skm,
+                &mut keystore_db,
+                &legacy_importer,
+                USER_ID,
+                "Clearing the cache did not lock the user!",
+            );
+            assert!(skm
+                .write()
+                .unwrap()
+                .reset_user(&mut keystore_db, &legacy_importer, USER_ID)
+                .is_err());
+            assert_locked(
+                &skm,
+                &mut keystore_db,
+                &legacy_importer,
+                USER_ID,
+                "User state should not have changed!",
+            );
+
+            // Keys should still exist.
+            assert!(keystore_db
+                .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
+                .unwrap());
+            assert!(keystore_db
+                .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
+                .unwrap());
+        } else {
+            assert!(skm
+                .write()
+                .unwrap()
+                .reset_user(&mut keystore_db, &legacy_importer, USER_ID)
+                .is_ok());
+            assert_uninitialized(
+                &skm,
+                &mut keystore_db,
+                &legacy_importer,
+                USER_ID,
+                "The user was not reset!",
+            );
+            assert!(!skm
+                .write()
+                .unwrap()
+                .super_key_exists_in_db_for_user(&mut keystore_db, &legacy_importer, USER_ID)
+                .unwrap());
+
+            // Auth bound key should no longer exist.
+            assert!(!keystore_db
+                .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
+                .unwrap());
+            assert!(keystore_db
+                .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
+                .unwrap());
+        }
+    }
+
+    #[test]
+    fn test_remove_unlocked_user() {
+        test_user_removal(false);
+    }
+
+    #[test]
+    fn test_remove_locked_user() {
+        test_user_removal(true);
+    }
+
+    #[test]
+    fn test_reset_unlocked_user() {
+        test_user_reset(false);
+    }
+
+    #[test]
+    fn test_reset_locked_user() {
+        test_user_reset(true);
+    }
+}
diff --git a/keystore2/test_utils/key_generations.rs b/keystore2/test_utils/key_generations.rs
index 02384d9..ff40aa1 100644
--- a/keystore2/test_utils/key_generations.rs
+++ b/keystore2/test_utils/key_generations.rs
@@ -303,6 +303,9 @@
     /// Error code to indicate error in ASN.1 DER-encoded data creation.
     #[error("Failed to create and encode ASN.1 data.")]
     DerEncodeFailed,
+    /// Error code to indicate error while using keystore-engine API.
+    #[error("Failed to perform crypto op using keystore-engine APIs.")]
+    Keystore2EngineOpFailed,
 }
 
 /// Keystore2 error mapping.
diff --git a/keystore2/tests/Android.bp b/keystore2/tests/Android.bp
index 78dd2d7..0c8b0c8 100644
--- a/keystore2/tests/Android.bp
+++ b/keystore2/tests/Android.bp
@@ -57,6 +57,7 @@
         "libkeymaster_portable",
         "libkeymaster_messages",
         "libcppbor_external",
+        "libkeystore-engine",
     ],
     require_root: true,
 }
@@ -80,6 +81,7 @@
         "libkeymaster_portable",
         "libkeymaster_messages",
         "libcppbor_external",
+        "libkeystore-engine",
     ],
 }
 
diff --git a/keystore2/tests/ffi_test_utils.cpp b/keystore2/tests/ffi_test_utils.cpp
index de20d83..47dd7a4 100644
--- a/keystore2/tests/ffi_test_utils.cpp
+++ b/keystore2/tests/ffi_test_utils.cpp
@@ -2,11 +2,12 @@
 
 #include <iostream>
 
+#include <android-base/logging.h>
+
 #include <KeyMintAidlTestBase.h>
 #include <aidl/android/hardware/security/keymint/ErrorCode.h>
 #include <keymaster/UniquePtr.h>
 
-#include <memory>
 #include <vector>
 
 #include <hardware/keymaster_defs.h>
@@ -16,7 +17,6 @@
 #include <keymaster/km_openssl/attestation_record.h>
 #include <keymaster/km_openssl/openssl_err.h>
 #include <keymaster/km_openssl/openssl_utils.h>
-#include <openssl/asn1t.h>
 
 using aidl::android::hardware::security::keymint::ErrorCode;
 
@@ -24,6 +24,9 @@
 #define LENGTH_MASK 0x80
 #define LENGTH_VALUE_MASK 0x7F
 
+/* EVP_PKEY_from_keystore is from system/security/keystore-engine. */
+extern "C" EVP_PKEY* EVP_PKEY_from_keystore(const char* key_id);
+
 /**
  * ASN.1 structure for `KeyDescription` Schema.
  * See `IKeyMintDevice.aidl` for documentation of the `KeyDescription` schema.
@@ -84,6 +87,8 @@
     void operator()(TEST_SECURE_KEY_WRAPPER* p) { TEST_SECURE_KEY_WRAPPER_free(p); }
 };
 
+const std::string keystore2_grant_id_prefix("ks2_keystore-engine_grant_id:");
+
 /* This function extracts a certificate from the certs_chain_buffer at the given
  * offset. Each DER encoded certificate starts with TAG_SEQUENCE followed by the
  * total length of the certificate. The length of the certificate is determined
@@ -364,3 +369,137 @@
 
     return cxx_result;
 }
+
+/**
+ * Perform EC/RSA sign operation using `EVP_PKEY`.
+ */
+bool performSignData(const char* data, size_t data_len, EVP_PKEY* pkey, unsigned char** signature,
+                     size_t* signature_len) {
+    // Create the signing context
+    EVP_MD_CTX* md_ctx = EVP_MD_CTX_new();
+    if (md_ctx == NULL) {
+        LOG(ERROR) << "Failed to create signing context";
+        return false;
+    }
+
+    // Initialize the signing operation
+    if (EVP_DigestSignInit(md_ctx, NULL, EVP_sha256(), NULL, pkey) != 1) {
+        LOG(ERROR) << "Failed to initialize signing operation";
+        EVP_MD_CTX_free(md_ctx);
+        return false;
+    }
+
+    // Sign the data
+    if (EVP_DigestSignUpdate(md_ctx, data, data_len) != 1) {
+        LOG(ERROR) << "Failed to sign data";
+        EVP_MD_CTX_free(md_ctx);
+        return false;
+    }
+
+    // Determine the length of the signature
+    if (EVP_DigestSignFinal(md_ctx, NULL, signature_len) != 1) {
+        LOG(ERROR) << "Failed to determine signature length";
+        EVP_MD_CTX_free(md_ctx);
+        return false;
+    }
+
+    // Allocate memory for the signature
+    *signature = (unsigned char*)malloc(*signature_len);
+    if (*signature == NULL) {
+        LOG(ERROR) << "Failed to allocate memory for the signature";
+        EVP_MD_CTX_free(md_ctx);
+        return false;
+    }
+
+    // Perform the final signing operation
+    if (EVP_DigestSignFinal(md_ctx, *signature, signature_len) != 1) {
+        LOG(ERROR) << "Failed to perform signing operation";
+        free(*signature);
+        EVP_MD_CTX_free(md_ctx);
+        return false;
+    }
+
+    EVP_MD_CTX_free(md_ctx);
+    return true;
+}
+
+/**
+ * Perform EC/RSA verify operation using `EVP_PKEY`.
+ */
+int performVerifySignature(const char* data, size_t data_len, EVP_PKEY* pkey,
+                           const unsigned char* signature, size_t signature_len) {
+    // Create the verification context
+    EVP_MD_CTX* md_ctx = EVP_MD_CTX_new();
+    if (md_ctx == NULL) {
+        LOG(ERROR) << "Failed to create verification context";
+        return false;
+    }
+
+    // Initialize the verification operation
+    if (EVP_DigestVerifyInit(md_ctx, NULL, EVP_sha256(), NULL, pkey) != 1) {
+        LOG(ERROR) << "Failed to initialize verification operation";
+        EVP_MD_CTX_free(md_ctx);
+        return false;
+    }
+
+    // Verify the data
+    if (EVP_DigestVerifyUpdate(md_ctx, data, data_len) != 1) {
+        LOG(ERROR) << "Failed to verify data";
+        EVP_MD_CTX_free(md_ctx);
+        return false;
+    }
+
+    // Perform the verification operation
+    int ret = EVP_DigestVerifyFinal(md_ctx, signature, signature_len);
+    EVP_MD_CTX_free(md_ctx);
+
+    return ret == 1;
+}
+
+/**
+ * Extract the `EVP_PKEY` for the given KeyMint Key and perform Sign/Verify operations
+ * using extracted `EVP_PKEY`.
+ */
+bool performCryptoOpUsingKeystoreEngine(int64_t grant_id) {
+    const int KEY_ID_LEN = 20;
+    char key_id[KEY_ID_LEN] = "";
+    snprintf(key_id, KEY_ID_LEN, "%" PRIx64, grant_id);
+    std::string str_key = std::string(keystore2_grant_id_prefix) + key_id;
+    bool result = false;
+
+#if defined(OPENSSL_IS_BORINGSSL)
+    EVP_PKEY* evp = EVP_PKEY_from_keystore(str_key.c_str());
+    if (!evp) {
+        LOG(ERROR) << "Error while loading a key from keystore-engine";
+        return false;
+    }
+
+    int algo_type = EVP_PKEY_id(evp);
+    if (algo_type != EVP_PKEY_RSA && algo_type != EVP_PKEY_EC) {
+        LOG(ERROR) << "Unsupported Algorithm. Only RSA and EC are allowed.";
+        EVP_PKEY_free(evp);
+        return false;
+    }
+
+    unsigned char* signature = NULL;
+    size_t signature_len = 0;
+    const char* INPUT_DATA = "MY MESSAGE FOR SIGN";
+    size_t data_len = strlen(INPUT_DATA);
+    if (!performSignData(INPUT_DATA, data_len, evp, &signature, &signature_len)) {
+        LOG(ERROR) << "Failed to sign data";
+        EVP_PKEY_free(evp);
+        return false;
+    }
+
+    result = performVerifySignature(INPUT_DATA, data_len, evp, signature, signature_len);
+    if (!result) {
+        LOG(ERROR) << "Signature verification failed";
+    } else {
+        LOG(INFO) << "Signature verification success";
+    }
+
+    free(signature);
+    EVP_PKEY_free(evp);
+#endif
+    return result;
+}
diff --git a/keystore2/tests/ffi_test_utils.hpp b/keystore2/tests/ffi_test_utils.hpp
index b8c7c48..32f29f4 100644
--- a/keystore2/tests/ffi_test_utils.hpp
+++ b/keystore2/tests/ffi_test_utils.hpp
@@ -9,3 +9,4 @@
                               rust::Vec<rust::u8> iv,
                               rust::Vec<rust::u8> tag);
 CxxResult buildAsn1DerEncodedWrappedKeyDescription();
+bool performCryptoOpUsingKeystoreEngine(int64_t grant_id);
diff --git a/keystore2/tests/ffi_test_utils.rs b/keystore2/tests/ffi_test_utils.rs
index 066d4a1..689713a 100644
--- a/keystore2/tests/ffi_test_utils.rs
+++ b/keystore2/tests/ffi_test_utils.rs
@@ -31,6 +31,7 @@
             tag: Vec<u8>,
         ) -> CxxResult;
         fn buildAsn1DerEncodedWrappedKeyDescription() -> CxxResult;
+        fn performCryptoOpUsingKeystoreEngine(grant_id: i64) -> bool;
     }
 }
 
@@ -78,3 +79,11 @@
 pub fn create_wrapped_key_additional_auth_data() -> Result<Vec<u8>, Error> {
     get_result(ffi::buildAsn1DerEncodedWrappedKeyDescription())
 }
+
+pub fn perform_crypto_op_using_keystore_engine(grant_id: i64) -> Result<bool, Error> {
+    if ffi::performCryptoOpUsingKeystoreEngine(grant_id) {
+        return Ok(true);
+    }
+
+    Err(Error::Keystore2EngineOpFailed)
+}
diff --git a/keystore2/tests/keystore2_client_keystore_engine_tests.rs b/keystore2/tests/keystore2_client_keystore_engine_tests.rs
new file mode 100644
index 0000000..1aed8e6
--- /dev/null
+++ b/keystore2/tests/keystore2_client_keystore_engine_tests.rs
@@ -0,0 +1,299 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use nix::unistd::{Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, KeyPurpose::KeyPurpose,
+    PaddingMode::PaddingMode, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
+    IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
+};
+
+use keystore2_test_utils::{authorizations::AuthSetBuilder, get_keystore_service, run_as};
+
+use crate::ffi_test_utils::perform_crypto_op_using_keystore_engine;
+
+use openssl::x509::X509;
+
+fn generate_rsa_key_and_grant_to_user(
+    keystore2: &binder::Strong<dyn IKeystoreService>,
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    alias: &str,
+    grantee_uid: i32,
+    access_vector: i32,
+) -> binder::Result<KeyDescriptor> {
+    let gen_params = AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::RSA)
+        .rsa_public_exponent(65537)
+        .key_size(2048)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .padding_mode(PaddingMode::NONE)
+        .digest(Digest::NONE);
+
+    let key_metadata = sec_level
+        .generateKey(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: -1,
+                alias: Some(alias.to_string()),
+                blob: None,
+            },
+            None,
+            &gen_params,
+            0,
+            b"entropy",
+        )
+        .expect("Failed to generate RSA Key.");
+
+    assert!(key_metadata.certificate.is_some());
+
+    keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
+}
+
+fn generate_ec_key_and_grant_to_user(
+    keystore2: &binder::Strong<dyn IKeystoreService>,
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    alias: &str,
+    grantee_uid: i32,
+    access_vector: i32,
+) -> binder::Result<KeyDescriptor> {
+    let gen_params = AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::EC)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .digest(Digest::NONE)
+        .ec_curve(EcCurve::P_256);
+
+    let key_metadata = sec_level
+        .generateKey(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: -1,
+                alias: Some(alias.to_string()),
+                blob: None,
+            },
+            None,
+            &gen_params,
+            0,
+            b"entropy",
+        )
+        .expect("Failed to generate EC Key.");
+
+    assert!(key_metadata.certificate.is_some());
+
+    keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
+}
+
+fn generate_key_and_grant_to_user(
+    keystore2: &binder::Strong<dyn IKeystoreService>,
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    alias: &str,
+    grantee_uid: u32,
+    algo: Algorithm,
+) -> Result<i64, Box<dyn std::error::Error>> {
+    let access_vector = KeyPermission::GET_INFO.0 | KeyPermission::USE.0 | KeyPermission::DELETE.0;
+
+    assert!(matches!(algo, Algorithm::RSA | Algorithm::EC));
+
+    let grant_key = match algo {
+        Algorithm::RSA => generate_rsa_key_and_grant_to_user(
+            keystore2,
+            sec_level,
+            alias,
+            grantee_uid.try_into().unwrap(),
+            access_vector,
+        )
+        .unwrap(),
+        Algorithm::EC => generate_ec_key_and_grant_to_user(
+            keystore2,
+            sec_level,
+            alias,
+            grantee_uid.try_into().unwrap(),
+            access_vector,
+        )
+        .unwrap(),
+        _ => panic!("Unsupported algorithms"),
+    };
+
+    assert_eq!(grant_key.domain, Domain::GRANT);
+
+    Ok(grant_key.nspace)
+}
+
+fn perform_crypto_op_using_granted_key(
+    keystore2: &binder::Strong<dyn IKeystoreService>,
+    grant_key_nspace: i64,
+) {
+    // Load the granted key from Keystore2-Engine API and perform crypto operations.
+    assert!(perform_crypto_op_using_keystore_engine(grant_key_nspace).unwrap());
+
+    // Delete the granted key.
+    keystore2
+        .deleteKey(&KeyDescriptor {
+            domain: Domain::GRANT,
+            nspace: grant_key_nspace,
+            alias: None,
+            blob: None,
+        })
+        .unwrap();
+}
+
+#[test]
+fn keystore2_perofrm_crypto_op_using_keystore2_engine_rsa_key_success() {
+    static TARGET_SU_CTX: &str = "u:r:su:s0";
+
+    static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+    const USER_ID: u32 = 99;
+    const APPLICATION_ID: u32 = 10001;
+    static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_GID: u32 = GRANTEE_UID;
+
+    // Generate a key and grant it to a user with GET_INFO|USE|DELETE key permissions.
+    let grant_key_nspace = unsafe {
+        run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+            let keystore2 = get_keystore_service();
+            let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+            let alias = "keystore2_engine_rsa_key";
+            generate_key_and_grant_to_user(
+                &keystore2,
+                &sec_level,
+                alias,
+                GRANTEE_UID,
+                Algorithm::RSA,
+            )
+            .unwrap()
+        })
+    };
+
+    // In grantee context load the key and try to perform crypto operation.
+    unsafe {
+        run_as::run_as(
+            GRANTEE_CTX,
+            Uid::from_raw(GRANTEE_UID),
+            Gid::from_raw(GRANTEE_GID),
+            move || {
+                let keystore2 = get_keystore_service();
+                perform_crypto_op_using_granted_key(&keystore2, grant_key_nspace);
+            },
+        )
+    };
+}
+
+#[test]
+fn keystore2_perofrm_crypto_op_using_keystore2_engine_ec_key_success() {
+    static TARGET_SU_CTX: &str = "u:r:su:s0";
+
+    static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+    const USER_ID: u32 = 99;
+    const APPLICATION_ID: u32 = 10001;
+    static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_GID: u32 = GRANTEE_UID;
+
+    // Generate a key and grant it to a user with GET_INFO|USE|DELETE key permissions.
+    let grant_key_nspace = unsafe {
+        run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+            let keystore2 = get_keystore_service();
+            let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+            let alias = "keystore2_engine_ec_test_key";
+            generate_key_and_grant_to_user(
+                &keystore2,
+                &sec_level,
+                alias,
+                GRANTEE_UID,
+                Algorithm::EC,
+            )
+            .unwrap()
+        })
+    };
+
+    // In grantee context load the key and try to perform crypto operation.
+    unsafe {
+        run_as::run_as(
+            GRANTEE_CTX,
+            Uid::from_raw(GRANTEE_UID),
+            Gid::from_raw(GRANTEE_GID),
+            move || {
+                let keystore2 = get_keystore_service();
+                perform_crypto_op_using_granted_key(&keystore2, grant_key_nspace);
+            },
+        )
+    };
+}
+
+#[test]
+fn keystore2_perofrm_crypto_op_using_keystore2_engine_pem_pub_key_success() {
+    static TARGET_SU_CTX: &str = "u:r:su:s0";
+
+    static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+    const USER_ID: u32 = 99;
+    const APPLICATION_ID: u32 = 10001;
+    static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_GID: u32 = GRANTEE_UID;
+
+    // Generate a key and re-encode it's certificate as PEM and update it and
+    // grant it to a user with GET_INFO|USE|DELETE key permissions.
+    let grant_key_nspace = unsafe {
+        run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+            let keystore2 = get_keystore_service();
+            let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+            let alias = "keystore2_engine_rsa_pem_pub_key";
+            let grant_key_nspace = generate_key_and_grant_to_user(
+                &keystore2,
+                &sec_level,
+                alias,
+                GRANTEE_UID,
+                Algorithm::RSA,
+            )
+            .unwrap();
+
+            // Update certificate with encodeed PEM data.
+            let key_entry_response = keystore2
+                .getKeyEntry(&KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: -1,
+                    alias: Some(alias.to_string()),
+                    blob: None,
+                })
+                .unwrap();
+            let cert_bytes = key_entry_response.metadata.certificate.as_ref().unwrap();
+            let cert = X509::from_der(cert_bytes.as_ref()).unwrap();
+            let cert_pem = cert.to_pem().unwrap();
+            keystore2
+                .updateSubcomponent(&key_entry_response.metadata.key, Some(&cert_pem), None)
+                .expect("updateSubcomponent failed.");
+
+            grant_key_nspace
+        })
+    };
+
+    // In grantee context load the key and try to perform crypto operation.
+    unsafe {
+        run_as::run_as(
+            GRANTEE_CTX,
+            Uid::from_raw(GRANTEE_UID),
+            Gid::from_raw(GRANTEE_GID),
+            move || {
+                let keystore2 = get_keystore_service();
+                perform_crypto_op_using_granted_key(&keystore2, grant_key_nspace);
+            },
+        )
+    };
+}
diff --git a/keystore2/tests/keystore2_client_tests.rs b/keystore2/tests/keystore2_client_tests.rs
index 07a298a..9be0bf8 100644
--- a/keystore2/tests/keystore2_client_tests.rs
+++ b/keystore2/tests/keystore2_client_tests.rs
@@ -23,6 +23,7 @@
 pub mod keystore2_client_import_keys_tests;
 pub mod keystore2_client_key_agreement_tests;
 pub mod keystore2_client_key_id_domain_tests;
+pub mod keystore2_client_keystore_engine_tests;
 pub mod keystore2_client_list_entries_tests;
 pub mod keystore2_client_operation_tests;
 pub mod keystore2_client_rsa_key_tests;