Merge changes I28f673b6,I146f7cfd into main

* changes:
  Flag for import of previously-emulated keys
  Cope with previously-emulated keys
diff --git a/keystore/tests/fuzzer/Android.bp b/keystore/tests/fuzzer/Android.bp
index 5df5c7a..d459f75 100644
--- a/keystore/tests/fuzzer/Android.bp
+++ b/keystore/tests/fuzzer/Android.bp
@@ -40,9 +40,17 @@
     ],
     fuzz_config: {
         cc: [
-            "android-media-fuzzing-reports@google.com",
+            "android-hardware-security@google.com",
         ],
-        componentid: 155276,
+        componentid: 1084732,
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of libkeystore-wifi-hidl",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
     },
 }
 
@@ -62,9 +70,17 @@
     ],
     fuzz_config: {
         cc: [
-            "android-media-fuzzing-reports@google.com",
+            "android-hardware-security@google.com",
         ],
-        componentid: 155276,
+        componentid: 1084732,
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of libkeystore-attestation-application-id",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
     },
 }
 
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index e59b6f2..c378b42 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -58,6 +58,7 @@
         "liblog_rust",
         "libmessage_macro",
         "librand",
+        "librkpd_client",
         "librustutils",
         "libserde",
         "libserde_cbor",
diff --git a/keystore2/TEST_MAPPING b/keystore2/TEST_MAPPING
index 1038bea..57ce78c 100644
--- a/keystore2/TEST_MAPPING
+++ b/keystore2/TEST_MAPPING
@@ -35,6 +35,9 @@
       "name": "keystore2_client_tests"
     },
     {
+      "name": "librkpd_client.test"
+    },
+    {
       "name": "libwatchdog_rs.test"
     }
   ]
diff --git a/keystore2/aconfig/flags.aconfig b/keystore2/aconfig/flags.aconfig
index 02716da..41e1a92 100644
--- a/keystore2/aconfig/flags.aconfig
+++ b/keystore2/aconfig/flags.aconfig
@@ -9,9 +9,9 @@
 }
 
 flag {
-  name: "deprecate_legacy_keystore"
+  name: "disable_legacy_keystore_put"
   namespace: "hardware_backed_security"
-  description: "This flag rolls out legacy keystore deprecation and makes it so that the put command returns a deprecation error"
+  description: "This flag disables legacy keystore put and makes it so that command returns an error"
   bug: "307460850"
   is_fixed_read_only: true
 }
diff --git a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
index 86d38d7..8275e8c 100644
--- a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
+++ b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
@@ -39,6 +39,20 @@
     void onUserAdded(in int userId);
 
     /**
+     * Allows LockSettingsService to tell Keystore to create a user's superencryption keys and store
+     * them encrypted by the given secret.  Requires 'ChangeUser' permission.
+     *
+     * ## Error conditions:
+     * `ResponseCode::PERMISSION_DENIED` - if caller does not have the 'ChangeUser' permission
+     * `ResponseCode::SYSTEM_ERROR` - if failed to initialize the user's super keys
+     *
+     * @param userId - Android user id
+     * @param password - a secret derived from the synthetic password of the user
+     * @param allowExisting - if true, then the keys already existing is not considered an error
+     */
+    void initUserSuperKeys(in int userId, in byte[] password, in boolean allowExisting);
+
+    /**
      * Allows LockSettingsService to inform keystore about removing a user.
      * Callers require 'ChangeUser' permission.
      *
@@ -51,6 +65,18 @@
     void onUserRemoved(in int userId);
 
     /**
+     * Allows LockSettingsService to tell Keystore that a user's LSKF is being removed, ie the
+     * user's lock screen is changing to Swipe or None.  Requires 'ChangePassword' permission.
+     *
+     * ## Error conditions:
+     * `ResponseCode::PERMISSION_DENIED` - if caller does not have the 'ChangePassword' permission
+     * `ResponseCode::SYSTEM_ERROR` - if failed to delete the user's auth-bound keys
+     *
+     * @param userId - Android user id
+     */
+    void onUserLskfRemoved(in int userId);
+
+    /**
      * Allows LockSettingsService to inform keystore about password change of a user.
      * Callers require 'ChangePassword' permission.
      *
diff --git a/keystore2/rkpd_client/Android.bp b/keystore2/rkpd_client/Android.bp
new file mode 100644
index 0000000..d6a911f
--- /dev/null
+++ b/keystore2/rkpd_client/Android.bp
@@ -0,0 +1,55 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+rust_defaults {
+    name: "librkpd_client_defaults",
+    crate_name: "rkpd_client",
+    srcs: ["src/lib.rs"],
+    rustlibs: [
+        "android.security.rkp_aidl-rust",
+        "libanyhow",
+        "libbinder_rs",
+        "liblog_rust",
+        "libmessage_macro",
+        "libthiserror",
+        "libtokio",
+    ],
+}
+
+rust_library {
+    name: "librkpd_client",
+    defaults: ["librkpd_client_defaults"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.virt",
+    ],
+}
+
+rust_test {
+    name: "librkpd_client.test",
+    defaults: ["librkpd_client_defaults"],
+    test_suites: ["general-tests"],
+    rustlibs: [
+        "librand",
+    ],
+}
diff --git a/keystore2/src/rkpd_client.rs b/keystore2/rkpd_client/src/lib.rs
similarity index 84%
rename from keystore2/src/rkpd_client.rs
rename to keystore2/rkpd_client/src/lib.rs
index fe64150..d8a5276 100644
--- a/keystore2/src/rkpd_client.rs
+++ b/keystore2/rkpd_client/src/lib.rs
@@ -14,7 +14,6 @@
 
 //! Helper wrapper around RKPD interface.
 
-use crate::error::{map_binder_status_code, Error, ResponseCode};
 use android_security_rkp_aidl::aidl::android::security::rkp::{
     IGetKeyCallback::BnGetKeyCallback, IGetKeyCallback::ErrorCode::ErrorCode as GetKeyErrorCode,
     IGetKeyCallback::IGetKeyCallback, IGetRegistrationCallback::BnGetRegistrationCallback,
@@ -24,8 +23,8 @@
     IStoreUpgradedKeyCallback::IStoreUpgradedKeyCallback,
     RemotelyProvisionedKey::RemotelyProvisionedKey,
 };
-use android_security_rkp_aidl::binder::{BinderFeatures, Interface, Strong};
 use anyhow::{Context, Result};
+use binder::{BinderFeatures, Interface, StatusCode, Strong};
 use message_macro::source_location_msg;
 use std::sync::Mutex;
 use std::time::Duration;
@@ -41,6 +40,44 @@
     tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap()
 }
 
+/// Errors occurred during the interaction with RKPD.
+#[derive(Debug, Clone, Copy, thiserror::Error, PartialEq, Eq)]
+pub enum Error {
+    /// An RKPD request gets cancelled.
+    #[error("An RKPD request gets cancelled")]
+    RequestCancelled,
+
+    /// Failed to get registration.
+    #[error("Failed to get registration")]
+    GetRegistrationFailed,
+
+    /// Failed to get key.
+    #[error("Failed to get key: {0:?}")]
+    GetKeyFailed(GetKeyErrorCode),
+
+    /// Failed to store upgraded key.
+    #[error("Failed to store upgraded key")]
+    StoreUpgradedKeyFailed,
+
+    /// Retryable timeout when waiting for a callback.
+    #[error("Retryable timeout when waiting for a callback")]
+    RetryableTimeout,
+
+    /// Timeout when waiting for a callback.
+    #[error("Timeout when waiting for a callback")]
+    Timeout,
+
+    /// Wraps a Binder status code.
+    #[error("Binder transaction error {0:?}")]
+    BinderTransaction(StatusCode),
+}
+
+impl From<StatusCode> for Error {
+    fn from(s: StatusCode) -> Self {
+        Self::BinderTransaction(s)
+    }
+}
+
 /// Thread-safe channel for sending a value once and only once. If a value has
 /// already been send, subsequent calls to send will noop.
 struct SafeSender<T> {
@@ -87,17 +124,17 @@
     fn onCancel(&self) -> binder::Result<()> {
         log::warn!("IGetRegistrationCallback cancelled");
         self.registration_tx.send(
-            Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
+            Err(Error::RequestCancelled)
                 .context(source_location_msg!("GetRegistrationCallback cancelled.")),
         );
         Ok(())
     }
     fn onError(&self, description: &str) -> binder::Result<()> {
         log::error!("IGetRegistrationCallback failed: '{description}'");
-        self.registration_tx
-            .send(Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)).context(
-                source_location_msg!("GetRegistrationCallback failed: {:?}", description),
-            ));
+        self.registration_tx.send(
+            Err(Error::GetRegistrationFailed)
+                .context(source_location_msg!("GetRegistrationCallback failed: {:?}", description)),
+        );
         Ok(())
     }
 }
@@ -105,7 +142,8 @@
 /// Make a new connection to a IRegistration service.
 async fn get_rkpd_registration(rpc_name: &str) -> Result<binder::Strong<dyn IRegistration>> {
     let remote_provisioning: Strong<dyn IRemoteProvisioning> =
-        map_binder_status_code(binder::get_interface("remote_provisioning"))
+        binder::get_interface("remote_provisioning")
+            .map_err(Error::from)
             .context(source_location_msg!("Trying to connect to IRemoteProvisioning service."))?;
 
     let (tx, rx) = oneshot::channel();
@@ -116,8 +154,7 @@
         .context(source_location_msg!("Trying to get registration."))?;
 
     match timeout(RKPD_TIMEOUT, rx).await {
-        Err(e) => Err(Error::Rc(ResponseCode::SYSTEM_ERROR))
-            .context(source_location_msg!("Waiting for RKPD: {:?}", e)),
+        Err(e) => Err(Error::Timeout).context(source_location_msg!("Waiting for RKPD: {:?}", e)),
         Ok(v) => v.unwrap(),
     }
 }
@@ -148,28 +185,13 @@
     fn onCancel(&self) -> binder::Result<()> {
         log::warn!("IGetKeyCallback cancelled");
         self.key_tx.send(
-            Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
-                .context(source_location_msg!("GetKeyCallback cancelled.")),
+            Err(Error::RequestCancelled).context(source_location_msg!("GetKeyCallback cancelled.")),
         );
         Ok(())
     }
     fn onError(&self, error: GetKeyErrorCode, description: &str) -> binder::Result<()> {
         log::error!("IGetKeyCallback failed: {description}");
-        let rc = match error {
-            GetKeyErrorCode::ERROR_UNKNOWN => ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
-            GetKeyErrorCode::ERROR_PERMANENT => ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
-            GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY => {
-                ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY
-            }
-            GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH => {
-                ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE
-            }
-            _ => {
-                log::error!("Unexpected error from rkpd: {error:?}");
-                ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR
-            }
-        };
-        self.key_tx.send(Err(Error::Rc(rc)).context(source_location_msg!(
+        self.key_tx.send(Err(Error::GetKeyFailed(error)).context(source_location_msg!(
             "GetKeyCallback failed: {:?} {:?}",
             error,
             description
@@ -195,7 +217,7 @@
             if let Err(e) = registration.cancelGetKey(&cb) {
                 log::error!("IRegistration::cancelGetKey failed: {:?}", e);
             }
-            Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
+            Err(Error::RetryableTimeout)
                 .context(source_location_msg!("Waiting for RKPD key timed out: {:?}", e))
         }
         Ok(v) => v.unwrap(),
@@ -234,9 +256,9 @@
     }
 
     fn onError(&self, error: &str) -> binder::Result<()> {
-        log::error!("IGetRegistrationCallback failed: {error}");
+        log::error!("IStoreUpgradedKeyCallback failed: {error}");
         self.completer.send(
-            Err(Error::Rc(ResponseCode::SYSTEM_ERROR))
+            Err(Error::StoreUpgradedKeyFailed)
                 .context(source_location_msg!("Failed to store upgraded key: {:?}", error)),
         );
         Ok(())
@@ -256,7 +278,7 @@
         .context(source_location_msg!("Failed to store upgraded blob with RKPD."))?;
 
     match timeout(RKPD_TIMEOUT, rx).await {
-        Err(e) => Err(Error::Rc(ResponseCode::SYSTEM_ERROR))
+        Err(e) => Err(Error::Timeout)
             .context(source_location_msg!("Waiting for RKPD to complete storing key: {:?}", e)),
         Ok(v) => v.unwrap(),
     }
@@ -291,7 +313,6 @@
 mod tests {
     use super::*;
     use android_security_rkp_aidl::aidl::android::security::rkp::IRegistration::BnRegistration;
-    use std::collections::HashMap;
     use std::sync::atomic::{AtomicU32, Ordering};
     use std::sync::{Arc, Mutex};
 
@@ -415,10 +436,7 @@
         assert!(cb.onCancel().is_ok());
 
         let result = tokio_rt().block_on(rx).unwrap();
-        assert_eq!(
-            result.unwrap_err().downcast::<Error>().unwrap(),
-            Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
-        );
+        assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::RequestCancelled);
     }
 
     #[test]
@@ -428,10 +446,7 @@
         assert!(cb.onError("error").is_ok());
 
         let result = tokio_rt().block_on(rx).unwrap();
-        assert_eq!(
-            result.unwrap_err().downcast::<Error>().unwrap(),
-            Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
-        );
+        assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::GetRegistrationFailed);
     }
 
     #[test]
@@ -453,29 +468,11 @@
         assert!(cb.onCancel().is_ok());
 
         let result = tokio_rt().block_on(rx).unwrap();
-        assert_eq!(
-            result.unwrap_err().downcast::<Error>().unwrap(),
-            Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
-        );
+        assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::RequestCancelled);
     }
 
     #[test]
     fn test_get_key_cb_error() {
-        let error_mapping = HashMap::from([
-            (GetKeyErrorCode::ERROR_UNKNOWN, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
-            (GetKeyErrorCode::ERROR_PERMANENT, ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR),
-            (
-                GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY,
-                ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY,
-            ),
-            (
-                GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH,
-                ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE,
-            ),
-        ]);
-
-        // Loop over the generated list of enum values to better ensure this test stays in
-        // sync with the AIDL.
         for get_key_error in GetKeyErrorCode::enum_values() {
             let (tx, rx) = oneshot::channel();
             let cb = GetKeyCallback::new_native_binder(tx);
@@ -484,7 +481,7 @@
             let result = tokio_rt().block_on(rx).unwrap();
             assert_eq!(
                 result.unwrap_err().downcast::<Error>().unwrap(),
-                Error::Rc(error_mapping[&get_key_error]),
+                Error::GetKeyFailed(get_key_error),
             );
         }
     }
@@ -505,10 +502,7 @@
         assert!(cb.onError("oh no! it failed").is_ok());
 
         let result = tokio_rt().block_on(rx).unwrap();
-        assert_eq!(
-            result.unwrap_err().downcast::<Error>().unwrap(),
-            Error::Rc(ResponseCode::SYSTEM_ERROR)
-        );
+        assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::StoreUpgradedKeyFailed);
     }
 
     #[test]
@@ -532,10 +526,7 @@
 
         let result =
             tokio_rt().block_on(get_rkpd_attestation_key_from_registration_async(&registration, 0));
-        assert_eq!(
-            result.unwrap_err().downcast::<Error>().unwrap(),
-            Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
-        );
+        assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::RetryableTimeout);
     }
 
     #[test]
@@ -560,10 +551,7 @@
             &[],
             &[],
         ));
-        assert_eq!(
-            result.unwrap_err().downcast::<Error>().unwrap(),
-            Error::Rc(ResponseCode::SYSTEM_ERROR)
-        );
+        assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::Timeout);
     }
 
     #[test]
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 83963f9..63dbf7f 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -47,7 +47,7 @@
 
 use crate::gc::Gc;
 use crate::impl_metadata; // This is in db_utils.rs
-use crate::key_parameter::{KeyParameter, Tag};
+use crate::key_parameter::{KeyParameter, KeyParameterValue, Tag};
 use crate::ks_err;
 use crate::permission::KeyPermSet;
 use crate::utils::{get_current_time_in_milliseconds, watchdog as wd, AID_USER_OFFSET};
@@ -2544,6 +2544,70 @@
         .context(ks_err!())
     }
 
+    /// Deletes all auth-bound keys, i.e. keys that require user authentication, for the given user.
+    /// This runs when the user's lock screen is being changed to Swipe or None.
+    ///
+    /// This intentionally does *not* delete keys that require that the device be unlocked, unless
+    /// such keys also require user authentication.  Keystore's concept of user authentication is
+    /// fairly strong, and it requires that keys that require authentication be deleted as soon as
+    /// authentication is no longer possible.  In contrast, keys that just require that the device
+    /// be unlocked should remain usable when the lock screen is set to Swipe or None, as the device
+    /// is always considered "unlocked" in that case.
+    pub fn unbind_auth_bound_keys_for_user(&mut self, user_id: u32) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::unbind_auth_bound_keys_for_user", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let mut stmt = tx
+                .prepare(&format!(
+                    "SELECT id from persistent.keyentry
+                     WHERE key_type = ?
+                     AND domain = ?
+                     AND cast ( (namespace/{aid_user_offset}) as int) = ?
+                     AND state = ?;",
+                    aid_user_offset = AID_USER_OFFSET
+                ))
+                .context(concat!(
+                    "In unbind_auth_bound_keys_for_user. ",
+                    "Failed to prepare the query to find the keys created by apps."
+                ))?;
+
+            let mut rows = stmt
+                .query(params![KeyType::Client, Domain::APP.0 as u32, user_id, KeyLifeCycle::Live,])
+                .context(ks_err!("Failed to query the keys created by apps."))?;
+
+            let mut key_ids: Vec<i64> = Vec::new();
+            db_utils::with_rows_extract_all(&mut rows, |row| {
+                key_ids
+                    .push(row.get(0).context("Failed to read key id of a key created by an app.")?);
+                Ok(())
+            })
+            .context(ks_err!())?;
+
+            let mut notify_gc = false;
+            let mut num_unbound = 0;
+            for key_id in key_ids {
+                // Load the key parameters and filter out non-auth-bound keys.  To identify
+                // auth-bound keys, use the presence of UserSecureID.  The absence of NoAuthRequired
+                // could also be used, but UserSecureID is what Keystore treats as authoritative
+                // when actually enforcing the key parameters (it might not matter, though).
+                let params = Self::load_key_parameters(key_id, tx)
+                    .context("Failed to load key parameters.")?;
+                let is_auth_bound_key = params.iter().any(|kp| {
+                    matches!(kp.key_parameter_value(), KeyParameterValue::UserSecureID(_))
+                });
+                if is_auth_bound_key {
+                    notify_gc = Self::mark_unreferenced(tx, key_id)
+                        .context("In unbind_auth_bound_keys_for_user.")?
+                        || notify_gc;
+                    num_unbound += 1;
+                }
+            }
+            log::info!("Deleting {num_unbound} auth-bound keys for user {user_id}");
+            Ok(()).do_gc(notify_gc)
+        })
+        .context(ks_err!())
+    }
+
     fn load_key_components(
         tx: &Transaction,
         load_bits: KeyEntryLoadBits,
@@ -4752,6 +4816,53 @@
         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 = db.create_key_entry(&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, &params)?;
+
+        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));
@@ -4955,6 +5066,71 @@
         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()?;
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 95e8837..43147e8 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -603,6 +603,44 @@
             }
         }
 
+        if android_security_flags::fix_unlocked_device_required_keys() {
+            let (hat, state) = if user_secure_ids.is_empty() {
+                (None, DeferredAuthState::NoAuthRequired)
+            } else if let Some(key_time_out) = key_time_out {
+                let (hat, last_off_body) =
+                    Self::find_auth_token(|hat: &AuthTokenEntry| match user_auth_type {
+                        Some(auth_type) => hat.satisfies(&user_secure_ids, auth_type),
+                        None => false, // not reachable due to earlier check
+                    })
+                    .ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
+                    .context(ks_err!("No suitable auth token found."))?;
+                let now = MonotonicRawTime::now();
+                let token_age = now
+                    .checked_sub(&hat.time_received())
+                    .ok_or_else(Error::sys)
+                    .context(ks_err!(
+                        "Overflow while computing Auth token validity. \
+                    Validity cannot be established."
+                    ))?;
+
+                let on_body_extended = allow_while_on_body && last_off_body < hat.time_received();
+
+                if token_age.seconds() > key_time_out && !on_body_extended {
+                    return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
+                        .context(ks_err!("matching auth token is expired."));
+                }
+                let state = if requires_timestamp {
+                    DeferredAuthState::TimeStampRequired(hat.auth_token().clone())
+                } else {
+                    DeferredAuthState::NoAuthRequired
+                };
+                (Some(hat.take_auth_token()), state)
+            } else {
+                (None, DeferredAuthState::OpAuthRequired)
+            };
+            return Ok((hat, AuthInfo { state, key_usage_limited, confirmation_token_receiver }));
+        }
+
         if !unlocked_device_required && no_auth_required {
             return Ok((
                 None,
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index 1a048b6..b4c57fb 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -28,11 +28,13 @@
 //! be added every time an error is forwarded.
 
 pub use android_hardware_security_keymint::aidl::android::hardware::security::keymint::ErrorCode::ErrorCode;
+use android_security_rkp_aidl::aidl::android::security::rkp::IGetKeyCallback::ErrorCode::ErrorCode as GetKeyErrorCode;
 pub use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
 use android_system_keystore2::binder::{
     ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode,
 };
 use keystore2_selinux as selinux;
+use rkpd_client::Error as RkpdError;
 use std::cmp::PartialEq;
 use std::ffi::CString;
 
@@ -66,6 +68,49 @@
     }
 }
 
+impl From<RkpdError> for Error {
+    fn from(e: RkpdError) -> Self {
+        match e {
+            RkpdError::RequestCancelled | RkpdError::GetRegistrationFailed => {
+                Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
+            }
+            RkpdError::GetKeyFailed(e) => {
+                let response_code = match e {
+                    GetKeyErrorCode::ERROR_UNKNOWN => ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
+                    GetKeyErrorCode::ERROR_PERMANENT => ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
+                    GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY => {
+                        ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY
+                    }
+                    GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH => {
+                        ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE
+                    }
+                    _ => {
+                        log::error!("Unexpected get key error from rkpd: {e:?}");
+                        ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR
+                    }
+                };
+                Error::Rc(response_code)
+            }
+            RkpdError::RetryableTimeout => Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+            RkpdError::StoreUpgradedKeyFailed | RkpdError::Timeout => {
+                Error::Rc(ResponseCode::SYSTEM_ERROR)
+            }
+            RkpdError::BinderTransaction(s) => Error::BinderTransaction(s),
+        }
+    }
+}
+
+/// Maps an `rkpd_client::Error` that is wrapped with an `anyhow::Error` to a keystore2 `Error`.
+pub fn wrapped_rkpd_error_to_ks_error(e: &anyhow::Error) -> Error {
+    match e.downcast_ref::<RkpdError>() {
+        Some(e) => Error::from(*e),
+        None => {
+            log::error!("Failed to downcast the anyhow::Error to rkpd_client::Error: {e:?}");
+            Error::Rc(ResponseCode::SYSTEM_ERROR)
+        }
+    }
+}
+
 /// Helper function to map the binder status we get from calls into KeyMint
 /// to a Keystore Error. We don't create an anyhow error here to make
 /// it easier to evaluate KeyMint errors, which we must do in some cases, e.g.,
@@ -409,4 +454,35 @@
             expected_error_string
         );
     }
+
+    #[test]
+    fn rkpd_error_is_in_sync_with_response_code() {
+        let error_mapping = [
+            (RkpdError::RequestCancelled, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+            (RkpdError::GetRegistrationFailed, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+            (
+                RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_UNKNOWN),
+                ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
+            ),
+            (
+                RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PERMANENT),
+                ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
+            ),
+            (
+                RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY),
+                ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY,
+            ),
+            (
+                RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH),
+                ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE,
+            ),
+            (RkpdError::StoreUpgradedKeyFailed, ResponseCode::SYSTEM_ERROR),
+            (RkpdError::RetryableTimeout, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+            (RkpdError::Timeout, ResponseCode::SYSTEM_ERROR),
+        ];
+        for (rkpd_error, expected_response_code) in error_mapping {
+            let e: Error = rkpd_error.into();
+            assert_eq!(e, Error::Rc(expected_response_code));
+        }
+    }
 } // mod tests
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index e51a319..c0eecd8 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -37,7 +37,6 @@
 pub mod permission;
 pub mod raw_device;
 pub mod remote_provisioning;
-pub mod rkpd_client;
 pub mod security_level;
 pub mod service;
 pub mod shared_secret_negotiation;
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index ea48f4d..74858de 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -120,6 +120,41 @@
             .context(ks_err!("While invoking the delete listener."))
     }
 
+    fn init_user_super_keys(
+        &self,
+        user_id: i32,
+        password: Password,
+        allow_existing: bool,
+    ) -> Result<()> {
+        // Permission check. Must return on error. Do not touch the '?'.
+        check_keystore_permission(KeystorePerm::ChangeUser).context(ks_err!())?;
+
+        let mut skm = SUPER_KEY.write().unwrap();
+        DB.with(|db| {
+            skm.initialize_user(
+                &mut db.borrow_mut(),
+                &LEGACY_IMPORTER,
+                user_id as u32,
+                &password,
+                allow_existing,
+            )
+        })
+        .context(ks_err!("Failed to initialize user super keys"))
+    }
+
+    // Deletes all auth-bound keys when the user's LSKF is removed.
+    fn on_user_lskf_removed(user_id: i32) -> Result<()> {
+        // Permission check. Must return on error. Do not touch the '?'.
+        check_keystore_permission(KeystorePerm::ChangePassword).context(ks_err!())?;
+
+        LEGACY_IMPORTER
+            .bulk_delete_user(user_id as u32, true)
+            .context(ks_err!("Failed to delete legacy keys."))?;
+
+        DB.with(|db| db.borrow_mut().unbind_auth_bound_keys_for_user(user_id as u32))
+            .context(ks_err!("Failed to delete auth-bound keys."))
+    }
+
     fn clear_namespace(&self, domain: Domain, nspace: i64) -> Result<()> {
         // Permission check. Must return on error. Do not touch the '?'.
         check_keystore_permission(KeystorePerm::ClearUID).context("In clear_namespace.")?;
@@ -272,12 +307,29 @@
         map_or_log_err(self.add_or_remove_user(user_id), Ok)
     }
 
+    fn initUserSuperKeys(
+        &self,
+        user_id: i32,
+        password: &[u8],
+        allow_existing: bool,
+    ) -> BinderResult<()> {
+        log::info!("initUserSuperKeys(user={user_id}, allow_existing={allow_existing})");
+        let _wp = wd::watch_millis("IKeystoreMaintenance::initUserSuperKeys", 500);
+        map_or_log_err(self.init_user_super_keys(user_id, password.into(), allow_existing), Ok)
+    }
+
     fn onUserRemoved(&self, user_id: i32) -> BinderResult<()> {
         log::info!("onUserRemoved(user={user_id})");
         let _wp = wd::watch_millis("IKeystoreMaintenance::onUserRemoved", 500);
         map_or_log_err(self.add_or_remove_user(user_id), Ok)
     }
 
+    fn onUserLskfRemoved(&self, user_id: i32) -> BinderResult<()> {
+        log::info!("onUserLskfRemoved(user={user_id})");
+        let _wp = wd::watch_millis("IKeystoreMaintenance::onUserLskfRemoved", 500);
+        map_or_log_err(Self::on_user_lskf_removed(user_id), Ok)
+    }
+
     fn clearNamespace(&self, domain: Domain, nspace: i64) -> BinderResult<()> {
         log::info!("clearNamespace({domain:?}, nspace={nspace})");
         let _wp = wd::watch_millis("IKeystoreMaintenance::clearNamespace", 500);
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index 3f7833e..a386d96 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -31,12 +31,13 @@
 use keystore2_crypto::parse_subject_from_certificate;
 
 use crate::database::Uuid;
+use crate::error::wrapped_rkpd_error_to_ks_error;
 use crate::globals::get_remotely_provisioned_component_name;
 use crate::ks_err;
 use crate::metrics_store::log_rkp_error_stats;
-use crate::rkpd_client::get_rkpd_attestation_key;
 use crate::watchdog_helper::watchdog as wd;
 use android_security_metrics::aidl::android::security::metrics::RkpError::RkpError as MetricsRkpError;
+use rkpd_client::get_rkpd_attestation_key;
 
 /// Contains helper functions to check if remote provisioning is enabled on the system and, if so,
 /// to assign and retrieve attestation keys and certificate chains.
@@ -102,7 +103,7 @@
                 Err(e) => {
                     if self.is_rkp_only() {
                         log::error!("Error occurred: {:?}", e);
-                        return Err(e);
+                        return Err(wrapped_rkpd_error_to_ks_error(&e)).context(format!("{e:?}"));
                     }
                     log::warn!("Error occurred: {:?}", e);
                     log_rkp_error_stats(
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 8abff77..6fb0eb2 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -19,7 +19,9 @@
     log_key_deleted, log_key_generated, log_key_imported, log_key_integrity_violation,
 };
 use crate::database::{BlobInfo, CertificateInfo, KeyIdGuard};
-use crate::error::{self, map_km_error, map_or_log_err, Error, ErrorCode};
+use crate::error::{
+    self, map_km_error, map_or_log_err, wrapped_rkpd_error_to_ks_error, Error, ErrorCode,
+};
 use crate::globals::{
     get_remotely_provisioned_component_name, DB, ENFORCEMENTS, LEGACY_IMPORTER, SUPER_KEY,
 };
@@ -28,7 +30,6 @@
 use crate::ks_err;
 use crate::metrics_store::log_key_creation_event_stats;
 use crate::remote_provisioning::RemProvState;
-use crate::rkpd_client::store_rkpd_attestation_key;
 use crate::super_key::{KeyBlob, SuperKeyManager};
 use crate::utils::{
     check_device_attestation_permissions, check_key_permission,
@@ -62,6 +63,7 @@
     KeyMetadata::KeyMetadata, KeyParameters::KeyParameters, ResponseCode::ResponseCode,
 };
 use anyhow::{anyhow, Context, Result};
+use rkpd_client::store_rkpd_attestation_key;
 use std::convert::TryInto;
 use std::time::SystemTime;
 
@@ -896,8 +898,11 @@
             f,
             |upgraded_blob| {
                 let _wp = wd::watch_millis("Calling store_rkpd_attestation_key()", 500);
-                store_rkpd_attestation_key(&rpc_name, key_blob, upgraded_blob)
-                    .context(ks_err!("Failed store_rkpd_attestation_key()."))
+                if let Err(e) = store_rkpd_attestation_key(&rpc_name, key_blob, upgraded_blob) {
+                    Err(wrapped_rkpd_error_to_ks_error(&e)).context(format!("{e:?}"))
+                } else {
+                    Ok(())
+                }
             },
         )
         .context(ks_err!())
@@ -1064,13 +1069,13 @@
     use super::*;
     use crate::error::map_km_error;
     use crate::globals::get_keymint_device;
-    use crate::rkpd_client::{get_rkpd_attestation_key, store_rkpd_attestation_key};
     use crate::utils::upgrade_keyblob_if_required_with;
     use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
         Algorithm::Algorithm, AttestationKey::AttestationKey, KeyParameter::KeyParameter,
         KeyParameterValue::KeyParameterValue, Tag::Tag,
     };
     use keystore2_crypto::parse_subject_from_certificate;
+    use rkpd_client::get_rkpd_attestation_key;
 
     #[test]
     // This is a helper for a manual test. We want to check that after a system upgrade RKPD
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index 898a8c2..3992eb2 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -248,11 +248,12 @@
 
 #[derive(Default)]
 struct UserSuperKeys {
-    /// The AfterFirstUnlock super key is used for LSKF binding of authentication bound keys. There
-    /// is one key per android user. The key is stored on flash encrypted with a key derived from a
-    /// secret, that is itself derived from the user's lock screen knowledge factor (LSKF). When the
-    /// user unlocks the device for the first time, this key is unlocked, i.e., decrypted, and stays
-    /// memory resident until the device reboots.
+    /// The AfterFirstUnlock super key is used for synthetic password binding of authentication
+    /// bound keys. There is one key per android user. The key is stored on flash encrypted with a
+    /// key derived from a secret, that is itself derived from the user's synthetic password. (In
+    /// most cases, the user's synthetic password can, in turn, only be decrypted using the user's
+    /// Lock Screen Knowledge Factor or LSKF.) When the user unlocks the device for the first time,
+    /// this key is unlocked, i.e., decrypted, and stays memory resident until the device reboots.
     after_first_unlock: Option<Arc<SuperKey>>,
     /// The UnlockedDeviceRequired symmetric super key works like the AfterFirstUnlock super key
     /// with the distinction that it is cleared from memory when the device is locked.
@@ -474,7 +475,7 @@
         }
     }
 
-    /// Checks if user has setup LSKF, even when super key cache is empty for the user.
+    /// Checks if the user's AfterFirstUnlock super key exists in the database (or legacy database).
     /// The reference to self is unused but it is required to prevent calling this function
     /// concurrently with skm state database changes.
     fn super_key_exists_in_db_for_user(
@@ -662,7 +663,8 @@
             SuperEncryptionType::None => Ok((key_blob.to_vec(), BlobMetaData::new())),
             SuperEncryptionType::AfterFirstUnlock => {
                 // Encrypt the given key blob with the user's AfterFirstUnlock super key. If the
-                // user has not unlocked the device since boot or has no LSKF, an error is returned.
+                // user has not unlocked the device since boot or the super keys were never
+                // initialized for the user for some reason, an error is returned.
                 match self
                     .get_user_state(db, legacy_importer, user_id)
                     .context(ks_err!("Failed to get user state for user {user_id}"))?
@@ -676,7 +678,7 @@
                         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 user {user_id}")),
+                        .context(ks_err!("User {user_id} does not have super keys")),
                 }
             }
             SuperEncryptionType::UnlockedDeviceRequired => {
@@ -1131,6 +1133,37 @@
         }
     }
 
+    /// Initializes the given user by creating their super keys, both AfterFirstUnlock and
+    /// UnlockedDeviceRequired. If allow_existing is true, then the user already being initialized
+    /// is not considered an error.
+    pub fn initialize_user(
+        &mut self,
+        db: &mut KeystoreDB,
+        legacy_importer: &LegacyImporter,
+        user_id: UserId,
+        password: &Password,
+        allow_existing: bool,
+    ) -> Result<()> {
+        // Create the AfterFirstUnlock super key.
+        if self.super_key_exists_in_db_for_user(db, legacy_importer, user_id)? {
+            log::info!("AfterFirstUnlock super key already exists");
+            if !allow_existing {
+                return Err(Error::sys()).context(ks_err!("Tried to re-init an initialized user!"));
+            }
+        } else {
+            let super_key = self
+                .create_super_key(db, user_id, &USER_AFTER_FIRST_UNLOCK_SUPER_KEY, password, None)
+                .context(ks_err!("Failed to create AfterFirstUnlock super key"))?;
+
+            self.install_after_first_unlock_key_for_user(user_id, super_key)
+                .context(ks_err!("Failed to install AfterFirstUnlock super key for user"))?;
+        }
+
+        // Create the UnlockedDeviceRequired super keys.
+        self.unlock_unlocked_device_required_keys(db, user_id, password)
+            .context(ks_err!("Failed to create UnlockedDeviceRequired super keys"))
+    }
+
     /// Unlocks the given user with the given password.
     ///
     /// If the user state is BeforeFirstUnlock:
@@ -1186,15 +1219,15 @@
 /// This enum represents different states of the user's life cycle in the device.
 /// For now, only three states are defined. More states may be added later.
 pub enum UserState {
-    // The user has registered LSKF and has unlocked the device by entering PIN/Password,
-    // and hence the AfterFirstUnlock super key is available in the cache.
+    // The user's super keys exist, and the user has unlocked the device at least once since boot.
+    // Hence, the AfterFirstUnlock super key is available in the cache.
     AfterFirstUnlock(Arc<SuperKey>),
-    // The user has registered LSKF, but has not unlocked the device using password, after reboot.
-    // Hence the AfterFirstUnlock and UnlockedDeviceRequired super keys are not available in the
-    // cache. However, they exist in the database in encrypted form.
+    // The user's super keys exist, but the user hasn't unlocked the device at least once since
+    // boot. Hence, the AfterFirstUnlock and UnlockedDeviceRequired super keys are not available in
+    // the cache. However, they exist in the database in encrypted form.
     BeforeFirstUnlock,
-    // There's no user in the device for the given user id, or the user with the user id has not
-    // setup LSKF.
+    // The user's super keys don't exist. I.e., there's no user with the given user ID, or the user
+    // is in the process of being created or destroyed.
     Uninitialized,
 }
 
diff --git a/keystore2/test_utils/authorizations.rs b/keystore2/test_utils/authorizations.rs
index 02ceb83..2cb2aaf 100644
--- a/keystore2/test_utils/authorizations.rs
+++ b/keystore2/test_utils/authorizations.rs
@@ -335,6 +335,31 @@
         self.0.push(KeyParameter { tag: Tag::APPLICATION_ID, value: KeyParameterValue::Blob(b) });
         self
     }
+
+    /// Set device-unique-attestation.
+    pub fn device_unique_attestation(mut self) -> Self {
+        self.0.push(KeyParameter {
+            tag: Tag::DEVICE_UNIQUE_ATTESTATION,
+            value: KeyParameterValue::BoolValue(true),
+        });
+        self
+    }
+
+    /// Add certificate serial number.
+    pub fn cert_serial(mut self, b: Vec<u8>) -> Self {
+        self.0
+            .push(KeyParameter { tag: Tag::CERTIFICATE_SERIAL, value: KeyParameterValue::Blob(b) });
+        self
+    }
+
+    /// Add certificate subject name.
+    pub fn cert_subject_name(mut self, b: Vec<u8>) -> Self {
+        self.0.push(KeyParameter {
+            tag: Tag::CERTIFICATE_SUBJECT,
+            value: KeyParameterValue::Blob(b),
+        });
+        self
+    }
 }
 
 impl Deref for AuthSetBuilder {
diff --git a/keystore2/test_utils/ffi_test_utils.rs b/keystore2/test_utils/ffi_test_utils.rs
index 5d6bf46..1ccdcc8 100644
--- a/keystore2/test_utils/ffi_test_utils.rs
+++ b/keystore2/test_utils/ffi_test_utils.rs
@@ -50,7 +50,19 @@
 
 /// Validate given certificate chain.
 pub fn validate_certchain(cert_buf: &[u8]) -> Result<bool, Error> {
-    if ffi::validateCertChain(cert_buf.to_vec(), cert_buf.len().try_into().unwrap(), true) {
+    validate_certchain_with_strict_issuer_check(cert_buf, true)
+}
+
+/// Validate given certificate chain with an option to validate the issuer.
+pub fn validate_certchain_with_strict_issuer_check(
+    cert_buf: &[u8],
+    strict_issuer_check: bool,
+) -> Result<bool, Error> {
+    if ffi::validateCertChain(
+        cert_buf.to_vec(),
+        cert_buf.len().try_into().unwrap(),
+        strict_issuer_check,
+    ) {
         return Ok(true);
     }
 
diff --git a/keystore2/test_utils/key_generations.rs b/keystore2/test_utils/key_generations.rs
index badc480..9ddc87a 100644
--- a/keystore2/test_utils/key_generations.rs
+++ b/keystore2/test_utils/key_generations.rs
@@ -40,7 +40,7 @@
 
 use crate::ffi_test_utils::{
     get_os_patchlevel, get_os_version, get_value_from_attest_record, get_vendor_patchlevel,
-    validate_certchain,
+    validate_certchain_with_strict_issuer_check,
 };
 
 /// Shell namespace.
@@ -1426,7 +1426,10 @@
             let mut cert_chain: Vec<u8> = Vec::new();
             cert_chain.extend(key_metadata.certificate.as_ref().unwrap());
             cert_chain.extend(key_metadata.certificateChain.as_ref().unwrap());
-            validate_certchain(&cert_chain).expect("Error while validating cert chain");
+            let strict_issuer_check =
+                !(gen_params.iter().any(|kp| kp.tag == Tag::DEVICE_UNIQUE_ATTESTATION));
+            validate_certchain_with_strict_issuer_check(&cert_chain, strict_issuer_check)
+                .expect("Error while validating cert chain");
         }
 
         if let Some(challenge_param) =
diff --git a/keystore2/tests/keystore2_client_attest_key_tests.rs b/keystore2/tests/keystore2_client_attest_key_tests.rs
index c9ef298..3532a35 100644
--- a/keystore2/tests/keystore2_client_attest_key_tests.rs
+++ b/keystore2/tests/keystore2_client_attest_key_tests.rs
@@ -488,12 +488,12 @@
 
 fn get_attestation_ids(keystore2: &binder::Strong<dyn IKeystoreService>) -> Vec<(Tag, Vec<u8>)> {
     let attest_ids = vec![
-        (Tag::ATTESTATION_ID_BRAND, "ro.product.brand_for_attestation"),
-        (Tag::ATTESTATION_ID_DEVICE, "ro.product.device"),
-        (Tag::ATTESTATION_ID_PRODUCT, "ro.product.name_for_attestation"),
-        (Tag::ATTESTATION_ID_SERIAL, "ro.serialno"),
-        (Tag::ATTESTATION_ID_MANUFACTURER, "ro.product.manufacturer"),
-        (Tag::ATTESTATION_ID_MODEL, "ro.product.model_for_attestation"),
+        (Tag::ATTESTATION_ID_BRAND, "brand"),
+        (Tag::ATTESTATION_ID_DEVICE, "device"),
+        (Tag::ATTESTATION_ID_PRODUCT, "name"),
+        (Tag::ATTESTATION_ID_SERIAL, "serialno"),
+        (Tag::ATTESTATION_ID_MANUFACTURER, "manufacturer"),
+        (Tag::ATTESTATION_ID_MODEL, "model"),
         (Tag::ATTESTATION_ID_IMEI, ""), //Get this value from Telephony service.
         (Tag::ATTESTATION_ID_SECOND_IMEI, ""), //Get this value from Telephony service.
     ];
diff --git a/keystore2/tests/keystore2_client_authorizations_tests.rs b/keystore2/tests/keystore2_client_authorizations_tests.rs
index 279ecd7..2291a08 100644
--- a/keystore2/tests/keystore2_client_authorizations_tests.rs
+++ b/keystore2/tests/keystore2_client_authorizations_tests.rs
@@ -14,6 +14,9 @@
 
 use std::time::SystemTime;
 
+use openssl::bn::{BigNum, MsbOption};
+use openssl::x509::X509NameBuilder;
+
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
     ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
@@ -39,7 +42,8 @@
 
 use crate::keystore2_client_test_utils::{
     delete_app_key, perform_sample_asym_sign_verify_op, perform_sample_hmac_sign_verify_op,
-    perform_sample_sym_key_decrypt_op, perform_sample_sym_key_encrypt_op, SAMPLE_PLAIN_TEXT,
+    perform_sample_sym_key_decrypt_op, perform_sample_sym_key_encrypt_op,
+    verify_certificate_serial_num, verify_certificate_subject_name, SAMPLE_PLAIN_TEXT,
 };
 
 use keystore2_test_utils::ffi_test_utils::get_value_from_attest_record;
@@ -964,3 +968,39 @@
         keystore_auth.getLastAuthTime(0, &[HardwareAuthenticatorType::FINGERPRINT]).unwrap() > 0
     );
 }
+
+/// Generate a key with specifying `CERTIFICATE_SUBJECT and CERTIFICATE_SERIAL`. Test should
+/// generate a key successfully and verify the specified key parameters.
+#[test]
+fn keystore2_gen_key_auth_serial_number_subject_test_success() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let cert_subject = "test cert subject";
+    let mut x509_name = X509NameBuilder::new().unwrap();
+    x509_name.append_entry_by_text("CN", cert_subject).unwrap();
+    let x509_name = x509_name.build().to_der().unwrap();
+
+    let mut serial = BigNum::new().unwrap();
+    serial.rand(159, MsbOption::MAYBE_ZERO, false).unwrap();
+
+    let gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::EC)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .digest(Digest::SHA_2_256)
+        .ec_curve(EcCurve::P_256)
+        .attestation_challenge(b"foo".to_vec())
+        .cert_subject_name(x509_name)
+        .cert_serial(serial.to_vec());
+
+    let alias = "ks_test_auth_tags_test";
+    let key_metadata = key_generations::generate_key(&sec_level, &gen_params, alias).unwrap();
+    verify_certificate_subject_name(
+        key_metadata.certificate.as_ref().unwrap(),
+        cert_subject.as_bytes(),
+    );
+    verify_certificate_serial_num(key_metadata.certificate.as_ref().unwrap(), &serial);
+    delete_app_key(&keystore2, alias).unwrap();
+}
diff --git a/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs b/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs
new file mode 100644
index 0000000..cf88fc5
--- /dev/null
+++ b/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs
@@ -0,0 +1,406 @@
+// 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 android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, ErrorCode::ErrorCode,
+    KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, SecurityLevel::SecurityLevel, Tag::Tag,
+};
+
+use keystore2_test_utils::{
+    authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+use keystore2_test_utils::ffi_test_utils::get_value_from_attest_record;
+
+use crate::keystore2_client_test_utils::{
+    delete_app_key, get_attest_id_value, is_second_imei_id_attestation_required,
+    perform_sample_asym_sign_verify_op,
+};
+
+/// This macro is used for generating device unique attested EC key with device id attestation.
+macro_rules! test_ec_key_device_unique_attestation_id {
+    ( $test_name:ident, $tag:expr, $prop_name:expr ) => {
+        #[test]
+        fn $test_name() {
+            generate_ec_key_device_unique_attested_with_id_attest($tag, $prop_name);
+        }
+    };
+}
+
+/// This macro is used for generating device unique attested RSA key with device id attestation.
+macro_rules! test_rsa_key_device_unique_attestation_id {
+    ( $test_name:ident, $tag:expr, $prop_name:expr ) => {
+        #[test]
+        fn $test_name() {
+            generate_rsa_key_device_unique_attested_with_id_attest($tag, $prop_name);
+        }
+    };
+}
+
+fn generate_ec_key_device_unique_attested_with_id_attest(attest_id_tag: Tag, prop_name: &str) {
+    let gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::EC)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .digest(Digest::SHA_2_256)
+        .ec_curve(EcCurve::P_256)
+        .attestation_challenge(b"foo".to_vec())
+        .device_unique_attestation();
+    generate_device_unique_attested_key_with_device_attest_ids(
+        gen_params,
+        attest_id_tag,
+        prop_name,
+    );
+}
+
+fn generate_rsa_key_device_unique_attested_with_id_attest(attest_id_tag: Tag, prop_name: &str) {
+    let gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::RSA)
+        .rsa_public_exponent(65537)
+        .key_size(2048)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .digest(Digest::SHA_2_256)
+        .padding_mode(PaddingMode::RSA_PKCS1_1_5_SIGN)
+        .attestation_challenge(b"foo".to_vec())
+        .device_unique_attestation();
+    generate_device_unique_attested_key_with_device_attest_ids(
+        gen_params,
+        attest_id_tag,
+        prop_name,
+    );
+}
+
+fn add_attest_id_auth(
+    gen_params: authorizations::AuthSetBuilder,
+    attest_id_tag: Tag,
+    value: Vec<u8>,
+) -> authorizations::AuthSetBuilder {
+    match attest_id_tag {
+        Tag::ATTESTATION_ID_BRAND => gen_params.attestation_device_brand(value),
+        Tag::ATTESTATION_ID_DEVICE => gen_params.attestation_device_name(value),
+        Tag::ATTESTATION_ID_PRODUCT => gen_params.attestation_device_product_name(value),
+        Tag::ATTESTATION_ID_SERIAL => gen_params.attestation_device_serial(value),
+        Tag::ATTESTATION_ID_MANUFACTURER => gen_params.attestation_device_manufacturer(value),
+        Tag::ATTESTATION_ID_MODEL => gen_params.attestation_device_model(value),
+        Tag::ATTESTATION_ID_IMEI => gen_params.attestation_device_imei(value),
+        Tag::ATTESTATION_ID_SECOND_IMEI => gen_params.attestation_device_second_imei(value),
+        _ => {
+            panic!("Unknown attestation id");
+        }
+    }
+}
+
+/// Generate a device unique attested key with attestation of the device's identifiers. Test should
+/// succeed in generating a attested key with attestation of device identifiers. Test might fail on
+/// devices which don't support device id attestation with error response code `CANNOT_ATTEST_IDS`.
+fn generate_device_unique_attested_key_with_device_attest_ids(
+    gen_params: authorizations::AuthSetBuilder,
+    attest_id: Tag,
+    prop_name: &str,
+) {
+    let keystore2 = get_keystore_service();
+    let result =
+        key_generations::map_ks_error(keystore2.getSecurityLevel(SecurityLevel::STRONGBOX));
+    if result.is_err() {
+        assert_eq!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE), result.unwrap_err());
+        return;
+    }
+    let sec_level = result.unwrap();
+
+    if attest_id == Tag::ATTESTATION_ID_SECOND_IMEI
+        && !is_second_imei_id_attestation_required(&keystore2)
+    {
+        return;
+    }
+
+    if let Some(value) = get_attest_id_value(attest_id, prop_name) {
+        if value.is_empty() {
+            return;
+        }
+        let gen_params = add_attest_id_auth(gen_params, attest_id, value.clone());
+        let alias = "ks_test_device_unique_attest_id_test";
+        match key_generations::map_ks_error(key_generations::generate_key(
+            &sec_level,
+            &gen_params,
+            alias,
+        )) {
+            Ok(key_metadata) => {
+                let attest_id_value = get_value_from_attest_record(
+                    key_metadata.certificate.as_ref().unwrap(),
+                    attest_id,
+                    key_metadata.keySecurityLevel,
+                )
+                .expect("Attest id verification failed.");
+                assert_eq!(attest_id_value, value);
+                delete_app_key(&keystore2, alias).unwrap();
+            }
+            Err(e) => {
+                assert_eq!(e, Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
+            }
+        }
+    }
+}
+
+/// Try generate a key with `DEVICE_UNIQUE_ATTESTATION` using `TRUSTED_ENVIRONMENT` security level.
+/// Test should fail to generate a key with error code `INVALID_ARGUMENT`
+#[test]
+fn keystore2_gen_key_device_unique_attest_with_default_sec_level_unimplemented() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::EC)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .digest(Digest::SHA_2_256)
+        .ec_curve(EcCurve::P_256)
+        .attestation_challenge(b"foo".to_vec())
+        .device_unique_attestation();
+
+    let alias = "ks_test_auth_tags_test";
+    let result = key_generations::map_ks_error(key_generations::generate_key(
+        &sec_level,
+        &gen_params,
+        alias,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INVALID_ARGUMENT), result.unwrap_err());
+}
+
+/// Generate a EC key with `DEVICE_UNIQUE_ATTESTATION` using `STRONGBOX` security level.
+/// Test should create a key successfully, verify key characteristics, cert-chain signatures and
+/// use it for performing an operation.
+#[test]
+fn keystore2_gen_ec_key_device_unique_attest_with_strongbox_sec_level_test_success() {
+    let keystore2 = get_keystore_service();
+    let result =
+        key_generations::map_ks_error(keystore2.getSecurityLevel(SecurityLevel::STRONGBOX));
+    if result.is_err() {
+        assert_eq!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE), result.unwrap_err());
+        return;
+    }
+
+    let sec_level = result.unwrap();
+    let gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::EC)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .digest(Digest::SHA_2_256)
+        .ec_curve(EcCurve::P_256)
+        .attestation_challenge(b"foo".to_vec())
+        .device_unique_attestation();
+
+    let alias = "ks_device_unique_ec_key_attest_test";
+    match key_generations::map_ks_error(key_generations::generate_key(
+        &sec_level,
+        &gen_params,
+        alias,
+    )) {
+        Ok(key_metadata) => {
+            perform_sample_asym_sign_verify_op(
+                &sec_level,
+                &key_metadata,
+                None,
+                Some(Digest::SHA_2_256),
+            );
+            delete_app_key(&keystore2, alias).unwrap();
+        }
+        Err(e) => {
+            assert_eq!(e, Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
+        }
+    }
+}
+
+/// Generate a RSA key with `DEVICE_UNIQUE_ATTESTATION` using `STRONGBOX` security level.
+/// Test should create a key successfully, verify key characteristics, cert-chain signatures and
+/// use it for performing an operation.
+#[test]
+fn keystore2_gen_rsa_key_device_unique_attest_with_strongbox_sec_level_test_success() {
+    let keystore2 = get_keystore_service();
+    let result =
+        key_generations::map_ks_error(keystore2.getSecurityLevel(SecurityLevel::STRONGBOX));
+    if result.is_err() {
+        assert_eq!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE), result.unwrap_err());
+        return;
+    }
+
+    let sec_level = result.unwrap();
+    let gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::RSA)
+        .rsa_public_exponent(65537)
+        .key_size(2048)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .digest(Digest::SHA_2_256)
+        .padding_mode(PaddingMode::RSA_PKCS1_1_5_SIGN)
+        .attestation_challenge(b"foo".to_vec())
+        .device_unique_attestation();
+
+    let alias = "ks_device_unique_rsa_key_attest_test";
+    match key_generations::map_ks_error(key_generations::generate_key(
+        &sec_level,
+        &gen_params,
+        alias,
+    )) {
+        Ok(key_metadata) => {
+            perform_sample_asym_sign_verify_op(
+                &sec_level,
+                &key_metadata,
+                Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
+                Some(Digest::SHA_2_256),
+            );
+            delete_app_key(&keystore2, alias).unwrap();
+        }
+        Err(e) => {
+            assert_eq!(e, Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
+        }
+    }
+}
+
+/// Try to generate a device unique attested key with attestation of invalid device's identifiers.
+/// Test should fail with error response code `CANNOT_ATTEST_IDS`.
+#[test]
+fn keystore2_device_unique_attest_key_fails_with_invalid_attestation_id() {
+    let keystore2 = get_keystore_service();
+    let result =
+        key_generations::map_ks_error(keystore2.getSecurityLevel(SecurityLevel::STRONGBOX));
+    if result.is_err() {
+        assert_eq!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE), result.unwrap_err());
+        return;
+    }
+
+    let sec_level = result.unwrap();
+    let attest_id_params = vec![
+        (Tag::ATTESTATION_ID_BRAND, b"invalid-brand".to_vec()),
+        (Tag::ATTESTATION_ID_DEVICE, b"invalid-device-name".to_vec()),
+        (Tag::ATTESTATION_ID_PRODUCT, b"invalid-product-name".to_vec()),
+        (Tag::ATTESTATION_ID_SERIAL, b"invalid-ro-serial".to_vec()),
+        (Tag::ATTESTATION_ID_MANUFACTURER, b"invalid-ro-product-manufacturer".to_vec()),
+        (Tag::ATTESTATION_ID_MODEL, b"invalid-ro-product-model".to_vec()),
+        (Tag::ATTESTATION_ID_IMEI, b"invalid-imei".to_vec()),
+    ];
+
+    for (attest_id, value) in attest_id_params {
+        let gen_params = authorizations::AuthSetBuilder::new()
+            .no_auth_required()
+            .algorithm(Algorithm::EC)
+            .purpose(KeyPurpose::SIGN)
+            .purpose(KeyPurpose::VERIFY)
+            .digest(Digest::SHA_2_256)
+            .ec_curve(EcCurve::P_256)
+            .attestation_challenge(b"foo".to_vec())
+            .device_unique_attestation();
+        let alias = "ks_ec_device_unique_attested_test_key_fail";
+        let gen_params = add_attest_id_auth(gen_params, attest_id, value.clone());
+
+        let result = key_generations::map_ks_error(key_generations::generate_key(
+            &sec_level,
+            &gen_params,
+            alias,
+        ));
+        assert!(result.is_err());
+        assert!(matches!(result.unwrap_err(), Error::Km(ErrorCode::CANNOT_ATTEST_IDS)));
+    }
+}
+
+// Below macros generate tests for generating device unique attested EC keys with attestation
+// of the device's identifiers.
+test_ec_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_ecdsa_attest_id_brand,
+    Tag::ATTESTATION_ID_BRAND,
+    "ro.product.brand_for_attestation"
+);
+test_ec_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_ecdsa_attest_id_device,
+    Tag::ATTESTATION_ID_DEVICE,
+    "ro.product.device"
+);
+test_ec_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_ecdsa_attest_id_product,
+    Tag::ATTESTATION_ID_PRODUCT,
+    "ro.product.name_for_attestation"
+);
+test_ec_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_ecdsa_attest_id_serial,
+    Tag::ATTESTATION_ID_SERIAL,
+    "ro.serialno"
+);
+test_ec_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_ecdsa_attest_id_manufacturer,
+    Tag::ATTESTATION_ID_MANUFACTURER,
+    "ro.product.manufacturer"
+);
+test_ec_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_ecdsa_attest_id_model,
+    Tag::ATTESTATION_ID_MODEL,
+    "ro.product.model_for_attestation"
+);
+test_ec_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_ecdsa_attest_id_imei,
+    Tag::ATTESTATION_ID_IMEI,
+    ""
+);
+test_ec_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_ecdsa_attest_id_second_imei,
+    Tag::ATTESTATION_ID_SECOND_IMEI,
+    ""
+);
+
+// Below macros generate tests for generating device unique attested RSA keys with attestation
+// of the device's identifiers.
+test_rsa_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_rsa_attest_id_brand,
+    Tag::ATTESTATION_ID_BRAND,
+    "ro.product.brand_for_attestation"
+);
+test_rsa_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_rsa_attest_id_device,
+    Tag::ATTESTATION_ID_DEVICE,
+    "ro.product.device"
+);
+test_rsa_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_rsa_attest_id_product,
+    Tag::ATTESTATION_ID_PRODUCT,
+    "ro.product.name_for_attestation"
+);
+test_rsa_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_rsa_attest_id_serial,
+    Tag::ATTESTATION_ID_SERIAL,
+    "ro.serialno"
+);
+test_rsa_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_rsa_attest_id_manufacturer,
+    Tag::ATTESTATION_ID_MANUFACTURER,
+    "ro.product.manufacturer"
+);
+test_rsa_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_rsa_attest_id_model,
+    Tag::ATTESTATION_ID_MODEL,
+    "ro.product.model_for_attestation"
+);
+test_rsa_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_rsa_attest_id_imei,
+    Tag::ATTESTATION_ID_IMEI,
+    ""
+);
+test_rsa_key_device_unique_attestation_id!(
+    keystore2_device_unique_attest_rsa_attest_id_second_imei,
+    Tag::ATTESTATION_ID_SECOND_IMEI,
+    ""
+);
diff --git a/keystore2/tests/keystore2_client_test_utils.rs b/keystore2/tests/keystore2_client_test_utils.rs
index e76c64b..037482a 100644
--- a/keystore2/tests/keystore2_client_test_utils.rs
+++ b/keystore2/tests/keystore2_client_test_utils.rs
@@ -17,9 +17,11 @@
 
 use std::process::{Command, Output};
 
+use openssl::bn::BigNum;
 use openssl::encrypt::Encrypter;
 use openssl::error::ErrorStack;
 use openssl::hash::MessageDigest;
+use openssl::nid::Nid;
 use openssl::pkey::PKey;
 use openssl::pkey::Public;
 use openssl::rsa::Padding;
@@ -517,30 +519,33 @@
     match attest_id {
         Tag::ATTESTATION_ID_IMEI => get_imei(0),
         Tag::ATTESTATION_ID_SECOND_IMEI => get_imei(1),
-        Tag::ATTESTATION_ID_BRAND => {
-            let prop_val = get_system_prop(prop_name);
-            if prop_val.is_empty() {
-                Some(get_system_prop("ro.product.brand"))
-            } else {
+        Tag::ATTESTATION_ID_SERIAL => Some(get_system_prop(format!("ro.{}", prop_name).as_str())),
+        _ => {
+            let prop_val =
+                get_system_prop(format!("ro.product.{}_for_attestation", prop_name).as_str());
+            if !prop_val.is_empty() {
                 Some(prop_val)
+            } else {
+                let prop_val = get_system_prop(format!("ro.product.vendor.{}", prop_name).as_str());
+                if !prop_val.is_empty() {
+                    Some(prop_val)
+                } else {
+                    Some(get_system_prop(format!("ro.product.{}", prop_name).as_str()))
+                }
             }
         }
-        Tag::ATTESTATION_ID_PRODUCT => {
-            let prop_val = get_system_prop(prop_name);
-            if prop_val.is_empty() {
-                Some(get_system_prop("ro.product.name"))
-            } else {
-                Some(prop_val)
-            }
-        }
-        Tag::ATTESTATION_ID_MODEL => {
-            let prop_val = get_system_prop(prop_name);
-            if prop_val.is_empty() {
-                Some(get_system_prop("ro.product.model"))
-            } else {
-                Some(prop_val)
-            }
-        }
-        _ => Some(get_system_prop(prop_name)),
     }
 }
+
+pub fn verify_certificate_subject_name(cert_bytes: &[u8], expected_subject: &[u8]) {
+    let cert = X509::from_der(cert_bytes).unwrap();
+    let subject = cert.subject_name();
+    let cn = subject.entries_by_nid(Nid::COMMONNAME).next().unwrap();
+    assert_eq!(cn.data().as_slice(), expected_subject);
+}
+
+pub fn verify_certificate_serial_num(cert_bytes: &[u8], expected_serial_num: &BigNum) {
+    let cert = X509::from_der(cert_bytes).unwrap();
+    let serial_num = cert.serial_number();
+    assert_eq!(serial_num.to_bn().as_ref().unwrap(), expected_serial_num);
+}
diff --git a/keystore2/tests/keystore2_client_tests.rs b/keystore2/tests/keystore2_client_tests.rs
index ac7f19f..a0c140a 100644
--- a/keystore2/tests/keystore2_client_tests.rs
+++ b/keystore2/tests/keystore2_client_tests.rs
@@ -17,6 +17,7 @@
 pub mod keystore2_client_attest_key_tests;
 pub mod keystore2_client_authorizations_tests;
 pub mod keystore2_client_delete_key_tests;
+pub mod keystore2_client_device_unique_attestation_tests;
 pub mod keystore2_client_ec_key_tests;
 pub mod keystore2_client_grant_key_tests;
 pub mod keystore2_client_hmac_key_tests;
diff --git a/provisioner/Android.bp b/provisioner/Android.bp
index 0bf687d..605abb4 100644
--- a/provisioner/Android.bp
+++ b/provisioner/Android.bp
@@ -30,6 +30,7 @@
     ],
     shared_libs: [
         "libbinder_ndk",
+        "libcrypto",
         "liblog",
     ],
     static_libs: [
@@ -39,7 +40,6 @@
         "libbase",
         "libcppbor_external",
         "libcppcose_rkp",
-        "libcrypto_static",
         "libjsoncpp",
         "libkeymint_remote_prov_support",
         "libmediadrmrkp",
diff --git a/provisioner/support/test.cpp b/provisioner/support/test.cpp
index 418eab9..0e6e2f4 100644
--- a/provisioner/support/test.cpp
+++ b/provisioner/support/test.cpp
@@ -34,6 +34,10 @@
   public:
     virtual void SetUp() override {
         auto rpcName = String16(GetParam().c_str());
+        String16 avfName = String16(IRemotelyProvisionedComponent::descriptor) + String16("/avf");
+        if (avfName == rpcName) {
+            GTEST_SKIP() << "Skipping test for avf";
+        }
         rpc_ = android::waitForService<IRemotelyProvisionedComponent>(rpcName);
         ASSERT_NE(rpc_, nullptr);
     }