Biometric support for UNLOCKED_DEVICE_REQUIRED

When the device is locked, keystore is passed a list of biometric
SIDs which should allow unlock of UNLOCKED_DEVICE_REQUIRED keys.
It creates a KM key protected by these SIDs and uses it to encrypt
the UNLOCKED_DEVICE_REQUIRED secrets, and uses this key to recover
those secrets when the device is unlocked.

Test: aosp/1686345
Bug: 163866361
Change-Id: Ic73ed0089cd9567a83c38aed61e20215862aa0be
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index ec1edff..cac75c0 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -130,7 +130,15 @@
         lock_screen_event: LockScreenEvent,
         user_id: i32,
         password: Option<Password>,
+        unlocking_sids: Option<&[i64]>,
     ) -> Result<()> {
+        log::info!(
+            "on_lock_screen_event({:?}, user_id={:?}, password.is_some()={}, unlocking_sids={:?})",
+            lock_screen_event,
+            user_id,
+            password.is_some(),
+            unlocking_sids
+        );
         match (lock_screen_event, password) {
             (LockScreenEvent::UNLOCK, Some(password)) => {
                 // This corresponds to the unlock() method in legacy keystore API.
@@ -172,14 +180,23 @@
                 check_keystore_permission(KeystorePerm::unlock())
                     .context("In on_lock_screen_event: Unlock.")?;
                 ENFORCEMENTS.set_device_locked(user_id, false);
+                DB.with(|db| {
+                    SUPER_KEY.try_unlock_user_with_biometric(&mut db.borrow_mut(), user_id as u32)
+                })
+                .context("In on_lock_screen_event: try_unlock_user_with_biometric failed")?;
                 Ok(())
             }
             (LockScreenEvent::LOCK, None) => {
                 check_keystore_permission(KeystorePerm::lock())
                     .context("In on_lock_screen_event: Lock")?;
                 ENFORCEMENTS.set_device_locked(user_id, true);
-                SUPER_KEY.lock_screen_lock_bound_key(user_id as u32);
-
+                DB.with(|db| {
+                    SUPER_KEY.lock_screen_lock_bound_key(
+                        &mut db.borrow_mut(),
+                        user_id as u32,
+                        unlocking_sids.unwrap_or(&[]),
+                    );
+                });
                 Ok(())
             }
             _ => {
@@ -225,9 +242,15 @@
         lock_screen_event: LockScreenEvent,
         user_id: i32,
         password: Option<&[u8]>,
+        unlocking_sids: Option<&[i64]>,
     ) -> BinderResult<()> {
         map_or_log_err(
-            self.on_lock_screen_event(lock_screen_event, user_id, password.map(|pw| pw.into())),
+            self.on_lock_screen_event(
+                lock_screen_event,
+                user_id,
+                password.map(|pw| pw.into()),
+                unlocking_sids,
+            ),
             Ok,
         )
     }
diff --git a/keystore2/src/boot_level_keys.rs b/keystore2/src/boot_level_keys.rs
index a658c02..3084195 100644
--- a/keystore2/src/boot_level_keys.rs
+++ b/keystore2/src/boot_level_keys.rs
@@ -17,28 +17,17 @@
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Algorithm::Algorithm, Digest::Digest, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
 };
-use android_system_keystore2::aidl::android::system::keystore2::{
-    Domain::Domain, KeyDescriptor::KeyDescriptor,
-};
 use anyhow::{Context, Result};
 use keystore2_crypto::{hkdf_expand, ZVec, AES_256_KEY_LENGTH};
 use std::{collections::VecDeque, convert::TryFrom};
 
-use crate::{
-    database::KeystoreDB, key_parameter::KeyParameterValue, raw_device::KeyMintDevice,
-    utils::AID_KEYSTORE,
-};
+use crate::{database::KeystoreDB, key_parameter::KeyParameterValue, raw_device::KeyMintDevice};
 
 /// This is not thread safe; caller must hold a lock before calling.
 /// In practice the caller is SuperKeyManager and the lock is the
 /// Mutex on its internal state.
 pub fn get_level_zero_key(db: &mut KeystoreDB) -> Result<ZVec> {
-    let key_desc = KeyDescriptor {
-        domain: Domain::APP,
-        nspace: AID_KEYSTORE as i64,
-        alias: Some("boot_level_key".to_string()),
-        blob: None,
-    };
+    let key_desc = KeyMintDevice::internal_descriptor("boot_level_key".to_string());
     let params = [
         KeyParameterValue::Algorithm(Algorithm::HMAC).into(),
         KeyParameterValue::Digest(Digest::SHA_2_256).into(),
@@ -60,10 +49,11 @@
     let level_zero_key = km_dev
         .use_key_in_one_step(
             db,
-            key_id_guard,
+            &key_id_guard,
             &key_entry,
             KeyPurpose::SIGN,
             &params,
+            None,
             b"Create boot level key",
         )
         .context("In get_level_zero_key: use_key_in_one_step failed")?;
diff --git a/keystore2/src/raw_device.rs b/keystore2/src/raw_device.rs
index cdec79b..06432fe 100644
--- a/keystore2/src/raw_device.rs
+++ b/keystore2/src/raw_device.rs
@@ -25,12 +25,13 @@
     utils::{key_characteristics_to_internal, Asp, AID_KEYSTORE},
 };
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    BeginResult::BeginResult, ErrorCode::ErrorCode, IKeyMintDevice::IKeyMintDevice,
-    IKeyMintOperation::IKeyMintOperation, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
+    BeginResult::BeginResult, ErrorCode::ErrorCode, HardwareAuthToken::HardwareAuthToken,
+    IKeyMintDevice::IKeyMintDevice, IKeyMintOperation::IKeyMintOperation,
+    KeyCreationResult::KeyCreationResult, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
     SecurityLevel::SecurityLevel,
 };
 use android_system_keystore2::aidl::android::system::keystore2::{
-    KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+    Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
 };
 use anyhow::{Context, Result};
 use binder::Strong;
@@ -57,23 +58,26 @@
         Ok(KeyMintDevice { asp, km_uuid })
     }
 
-    /// Generate a KM key and store in the database.
-    fn generate_and_store_key(
+    /// Create a KM key and store in the database.
+    pub fn create_and_store_key<F>(
         &self,
         db: &mut KeystoreDB,
         key_desc: &KeyDescriptor,
-        params: &[KeyParameter],
-    ) -> Result<()> {
+        creator: F,
+    ) -> Result<()>
+    where
+        F: FnOnce(Strong<dyn IKeyMintDevice>) -> Result<KeyCreationResult, binder::Status>,
+    {
         let km_dev: Strong<dyn IKeyMintDevice> = self
             .asp
             .get_interface()
-            .context("In generate_and_store_key: Failed to get KeyMint device")?;
-        let creation_result = map_km_error(km_dev.generateKey(params, None))
-            .context("In generate_and_store_key: generateKey failed")?;
+            .context("In create_and_store_key: Failed to get KeyMint device")?;
+        let creation_result =
+            map_km_error(creator(km_dev)).context("In create_and_store_key: creator failed")?;
         let key_parameters = key_characteristics_to_internal(creation_result.keyCharacteristics);
 
         let creation_date =
-            DateTime::now().context("In generate_and_store_key: DateTime::now() failed")?;
+            DateTime::now().context("In create_and_store_key: DateTime::now() failed")?;
 
         let mut key_metadata = KeyMetaData::new();
         key_metadata.add(KeyMetaEntry::CreationDate(creation_date));
@@ -88,10 +92,44 @@
             &key_metadata,
             &self.km_uuid,
         )
-        .context("In generate_and_store_key: store_new_key failed")?;
+        .context("In create_and_store_key: store_new_key failed")?;
         Ok(())
     }
 
+    /// Generate a KeyDescriptor for internal-use keys.
+    pub fn internal_descriptor(alias: String) -> KeyDescriptor {
+        KeyDescriptor {
+            domain: Domain::APP,
+            nspace: AID_KEYSTORE as i64,
+            alias: Some(alias),
+            blob: None,
+        }
+    }
+
+    /// Look up an internal-use key in the database given a key descriptor.
+    pub fn lookup_from_desc(
+        db: &mut KeystoreDB,
+        key_desc: &KeyDescriptor,
+    ) -> Result<(KeyIdGuard, KeyEntry)> {
+        db.load_key_entry(&key_desc, KeyType::Client, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| {
+            Ok(())
+        })
+        .context("In lookup_from_desc: load_key_entry failed")
+    }
+
+    /// Look up the key in the database, and return None if it is absent.
+    pub fn not_found_is_none(
+        lookup: Result<(KeyIdGuard, KeyEntry)>,
+    ) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
+        match lookup {
+            Ok(result) => Ok(Some(result)),
+            Err(e) => match e.root_cause().downcast_ref::<Error>() {
+                Some(&Error::Rc(ResponseCode::KEY_NOT_FOUND)) => Ok(None),
+                _ => Err(e),
+            },
+        }
+    }
+
     /// This does the lookup and store in separate transactions; caller must
     /// hold a lock before calling.
     pub fn lookup_or_generate_key(
@@ -105,26 +143,16 @@
         // - because the caller needs to hold a lock in any case
         // - because it avoids holding database locks during slow
         //   KeyMint operations
-        let lookup = db.load_key_entry(
-            &key_desc,
-            KeyType::Client,
-            KeyEntryLoadBits::KM,
-            AID_KEYSTORE,
-            |_, _| Ok(()),
-        );
-        match lookup {
-            Ok(result) => return Ok(result),
-            Err(e) => match e.root_cause().downcast_ref::<Error>() {
-                Some(&Error::Rc(ResponseCode::KEY_NOT_FOUND)) => {}
-                _ => return Err(e),
-            },
+        let lookup = Self::not_found_is_none(Self::lookup_from_desc(db, key_desc))
+            .context("In lookup_or_generate_key: first lookup failed")?;
+        if let Some(result) = lookup {
+            Ok(result)
+        } else {
+            self.create_and_store_key(db, &key_desc, |km_dev| km_dev.generateKey(&params, None))
+                .context("In lookup_or_generate_key: generate_and_store_key failed")?;
+            Self::lookup_from_desc(db, key_desc)
+                .context("In lookup_or_generate_key: secpnd lookup failed")
         }
-        self.generate_and_store_key(db, &key_desc, &params)
-            .context("In lookup_or_generate_key: generate_and_store_key failed")?;
-        db.load_key_entry(&key_desc, KeyType::Client, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| {
-            Ok(())
-        })
-        .context("In lookup_or_generate_key: load_key_entry failed")
     }
 
     /// Call the passed closure; if it returns `KEY_REQUIRES_UPGRADE`, call upgradeKey, and
@@ -133,7 +161,7 @@
         &self,
         db: &mut KeystoreDB,
         km_dev: &Strong<dyn IKeyMintDevice>,
-        key_id_guard: KeyIdGuard,
+        key_id_guard: &KeyIdGuard,
         key_blob: &KeyBlob,
         f: F,
     ) -> Result<T>
@@ -149,7 +177,7 @@
                 new_blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
 
                 db.set_blob(
-                    &key_id_guard,
+                    key_id_guard,
                     SubComponentType::KEY_BLOB,
                     Some(&upgraded_blob),
                     Some(&new_blob_metadata),
@@ -170,13 +198,15 @@
 
     /// Use the created key in an operation that can be done with
     /// a call to begin followed by a call to finish.
+    #[allow(clippy::too_many_arguments)]
     pub fn use_key_in_one_step(
         &self,
         db: &mut KeystoreDB,
-        key_id_guard: KeyIdGuard,
+        key_id_guard: &KeyIdGuard,
         key_entry: &KeyEntry,
         purpose: KeyPurpose,
         operation_parameters: &[KeyParameter],
+        auth_token: Option<&HardwareAuthToken>,
         input: &[u8],
     ) -> Result<Vec<u8>> {
         let km_dev: Strong<dyn IKeyMintDevice> = self
@@ -193,7 +223,7 @@
 
         let begin_result: BeginResult = self
             .upgrade_keyblob_if_required_with(db, &km_dev, key_id_guard, &key_blob, |blob| {
-                map_km_error(km_dev.begin(purpose, blob, operation_parameters, None))
+                map_km_error(km_dev.begin(purpose, blob, operation_parameters, auth_token))
             })
             .context("In use_key_in_one_step: Failed to begin operation.")?;
         let operation: Strong<dyn IKeyMintOperation> = begin_result
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index b78560f..50a5f31 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -19,31 +19,45 @@
     database::EncryptedBy,
     database::KeyEntry,
     database::KeyType,
-    database::{KeyMetaData, KeyMetaEntry, KeystoreDB},
+    database::{KeyIdGuard, KeyMetaData, KeyMetaEntry, KeystoreDB},
     ec_crypto::ECDHPrivateKey,
     enforcements::Enforcements,
     error::Error,
     error::ResponseCode,
-    key_parameter::KeyParameter,
+    key_parameter::{KeyParameter, KeyParameterValue},
     legacy_blob::LegacyBlobLoader,
     legacy_migrator::LegacyMigrator,
+    raw_device::KeyMintDevice,
     try_insert::TryInsert,
 };
-use android_system_keystore2::aidl::android::system::keystore2::Domain::Domain;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, BlockMode::BlockMode, HardwareAuthToken::HardwareAuthToken,
+    HardwareAuthenticatorType::HardwareAuthenticatorType, KeyFormat::KeyFormat,
+    KeyParameter::KeyParameter as KmKeyParameter, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+    SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
 use anyhow::{Context, Result};
 use keystore2_crypto::{
     aes_gcm_decrypt, aes_gcm_encrypt, generate_aes256_key, generate_salt, Password, ZVec,
     AES_256_KEY_LENGTH,
 };
 use keystore2_system_property::PropertyWatcher;
-use std::ops::Deref;
 use std::{
     collections::HashMap,
     sync::Arc,
     sync::{Mutex, Weak},
 };
+use std::{convert::TryFrom, ops::Deref};
 
 const MAX_MAX_BOOT_LEVEL: usize = 1_000_000_000;
+/// Allow up to 15 seconds between the user unlocking using a biometric, and the auth
+/// token being used to unlock in [`SuperKeyManager::try_unlock_user_with_biometric`].
+/// This seems short enough for security purposes, while long enough that even the
+/// very slowest device will present the auth token in time.
+const BIOMETRIC_AUTH_TIMEOUT_S: i32 = 15; // seconds
 
 type UserId = u32;
 
@@ -154,6 +168,66 @@
     }
 }
 
+/// A SuperKey that has been encrypted with an AES-GCM key. For
+/// encryption the key is in memory, and for decryption it is in KM.
+struct LockedKey {
+    algorithm: SuperEncryptionAlgorithm,
+    id: SuperKeyIdentifier,
+    nonce: Vec<u8>,
+    ciphertext: Vec<u8>, // with tag appended
+}
+
+impl LockedKey {
+    fn new(key: &[u8], to_encrypt: &Arc<SuperKey>) -> Result<Self> {
+        let (mut ciphertext, nonce, mut tag) = aes_gcm_encrypt(&to_encrypt.key, key)?;
+        ciphertext.append(&mut tag);
+        Ok(LockedKey { algorithm: to_encrypt.algorithm, id: to_encrypt.id, nonce, ciphertext })
+    }
+
+    fn decrypt(
+        &self,
+        db: &mut KeystoreDB,
+        km_dev: &KeyMintDevice,
+        key_id_guard: &KeyIdGuard,
+        key_entry: &KeyEntry,
+        auth_token: &HardwareAuthToken,
+        reencrypt_with: Option<Arc<SuperKey>>,
+    ) -> Result<Arc<SuperKey>> {
+        let key_params = vec![
+            KeyParameterValue::Algorithm(Algorithm::AES),
+            KeyParameterValue::KeySize(256),
+            KeyParameterValue::BlockMode(BlockMode::GCM),
+            KeyParameterValue::PaddingMode(PaddingMode::NONE),
+            KeyParameterValue::Nonce(self.nonce.clone()),
+            KeyParameterValue::MacLength(128),
+        ];
+        let key_params: Vec<KmKeyParameter> = key_params.into_iter().map(|x| x.into()).collect();
+        let key = ZVec::try_from(km_dev.use_key_in_one_step(
+            db,
+            key_id_guard,
+            key_entry,
+            KeyPurpose::DECRYPT,
+            &key_params,
+            Some(auth_token),
+            &self.ciphertext,
+        )?)?;
+        Ok(Arc::new(SuperKey { algorithm: self.algorithm, key, id: self.id, reencrypt_with }))
+    }
+}
+
+/// Keys for unlocking UNLOCKED_DEVICE_REQUIRED keys, as LockedKeys, complete with
+/// a database descriptor for the encrypting key and the sids for the auth tokens
+/// that can be used to decrypt it.
+struct BiometricUnlock {
+    /// List of auth token SIDs that can be used to unlock these keys.
+    sids: Vec<i64>,
+    /// Database descriptor of key to use to unlock.
+    key_desc: KeyDescriptor,
+    /// Locked versions of the matching UserSuperKeys fields
+    screen_lock_bound: LockedKey,
+    screen_lock_bound_private: LockedKey,
+}
+
 #[derive(Default)]
 struct UserSuperKeys {
     /// The per boot key is used for LSKF binding of authentication bound keys. There is one
@@ -168,6 +242,8 @@
     /// When the device is locked, screen-lock-bound keys can still be encrypted, using
     /// ECDH public-key encryption. This field holds the decryption private key.
     screen_lock_bound_private: Option<Arc<SuperKey>>,
+    /// Versions of the above two keys, locked behind a biometric.
+    biometric_unlock: Option<BiometricUnlock>,
 }
 
 #[derive(Default)]
@@ -639,7 +715,7 @@
 
     /// Check if super encryption is required and if so, super-encrypt the key to be stored in
     /// the database.
-    #[allow(clippy::clippy::too_many_arguments)]
+    #[allow(clippy::too_many_arguments)]
     pub fn handle_super_encryption_on_key_init(
         &self,
         db: &mut KeystoreDB,
@@ -831,12 +907,127 @@
     }
 
     /// Wipe the screen-lock bound keys for this user from memory.
-    pub fn lock_screen_lock_bound_key(&self, user_id: UserId) {
+    pub fn lock_screen_lock_bound_key(
+        &self,
+        db: &mut KeystoreDB,
+        user_id: UserId,
+        unlocking_sids: &[i64],
+    ) {
+        log::info!("Locking screen bound for user {} sids {:?}", user_id, unlocking_sids);
         let mut data = self.data.lock().unwrap();
         let mut entry = data.user_keys.entry(user_id).or_default();
+        if !unlocking_sids.is_empty() {
+            if let (Some(aes), Some(ecdh)) = (
+                entry.screen_lock_bound.as_ref().cloned(),
+                entry.screen_lock_bound_private.as_ref().cloned(),
+            ) {
+                let res = (|| -> Result<()> {
+                    let key_desc = KeyMintDevice::internal_descriptor(format!(
+                        "biometric_unlock_key_{}",
+                        user_id
+                    ));
+                    let encrypting_key = generate_aes256_key()?;
+                    let km_dev: KeyMintDevice =
+                        KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
+                            .context("In lock_screen_lock_bound_key: KeyMintDevice::get failed")?;
+                    let mut key_params = vec![
+                        KeyParameterValue::Algorithm(Algorithm::AES),
+                        KeyParameterValue::KeySize(256),
+                        KeyParameterValue::BlockMode(BlockMode::GCM),
+                        KeyParameterValue::PaddingMode(PaddingMode::NONE),
+                        KeyParameterValue::CallerNonce,
+                        KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT),
+                        KeyParameterValue::MinMacLength(128),
+                        KeyParameterValue::AuthTimeout(BIOMETRIC_AUTH_TIMEOUT_S),
+                        KeyParameterValue::HardwareAuthenticatorType(
+                            HardwareAuthenticatorType::FINGERPRINT,
+                        ),
+                    ];
+                    for sid in unlocking_sids {
+                        key_params.push(KeyParameterValue::UserSecureID(*sid));
+                    }
+                    let key_params: Vec<KmKeyParameter> =
+                        key_params.into_iter().map(|x| x.into()).collect();
+                    km_dev.create_and_store_key(db, &key_desc, |dev| {
+                        dev.importKey(key_params.as_slice(), KeyFormat::RAW, &encrypting_key, None)
+                    })?;
+                    entry.biometric_unlock = Some(BiometricUnlock {
+                        sids: unlocking_sids.into(),
+                        key_desc,
+                        screen_lock_bound: LockedKey::new(&encrypting_key, &aes)?,
+                        screen_lock_bound_private: LockedKey::new(&encrypting_key, &ecdh)?,
+                    });
+                    Ok(())
+                })();
+                // There is no reason to propagate an error here upwards. We must discard
+                // entry.screen_lock_bound* in any case.
+                if let Err(e) = res {
+                    log::error!("Error setting up biometric unlock: {:#?}", e);
+                }
+            }
+        }
         entry.screen_lock_bound = None;
         entry.screen_lock_bound_private = None;
     }
+
+    /// User has unlocked, not using a password. See if any of our stored auth tokens can be used
+    /// to unlock the keys protecting UNLOCKED_DEVICE_REQUIRED keys.
+    pub fn try_unlock_user_with_biometric(
+        &self,
+        db: &mut KeystoreDB,
+        user_id: UserId,
+    ) -> Result<()> {
+        let mut data = self.data.lock().unwrap();
+        let mut entry = data.user_keys.entry(user_id).or_default();
+        if let Some(biometric) = entry.biometric_unlock.as_ref() {
+            let (key_id_guard, key_entry) =
+                KeyMintDevice::lookup_from_desc(db, &biometric.key_desc)?;
+            let km_dev: KeyMintDevice = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
+                .context("In try_unlock_user_with_biometric: KeyMintDevice::get failed")?;
+            for sid in &biometric.sids {
+                if let Some((auth_token_entry, _)) = db.find_auth_token_entry(|entry| {
+                    entry.auth_token().userId == *sid || entry.auth_token().authenticatorId == *sid
+                })? {
+                    let res: Result<(Arc<SuperKey>, Arc<SuperKey>)> = (|| {
+                        let slb = biometric.screen_lock_bound.decrypt(
+                            db,
+                            &km_dev,
+                            &key_id_guard,
+                            &key_entry,
+                            auth_token_entry.auth_token(),
+                            None,
+                        )?;
+                        let slbp = biometric.screen_lock_bound_private.decrypt(
+                            db,
+                            &km_dev,
+                            &key_id_guard,
+                            &key_entry,
+                            auth_token_entry.auth_token(),
+                            Some(slb.clone()),
+                        )?;
+                        Ok((slb, slbp))
+                    })();
+                    match res {
+                        Ok((slb, slbp)) => {
+                            entry.screen_lock_bound = Some(slb.clone());
+                            entry.screen_lock_bound_private = Some(slbp.clone());
+                            data.add_key_to_key_index(&slb)?;
+                            data.add_key_to_key_index(&slbp)?;
+                            log::info!(concat!(
+                                "In try_unlock_user_with_biometric: ",
+                                "Successfully unlocked with biometric"
+                            ));
+                            return Ok(());
+                        }
+                        Err(e) => {
+                            log::warn!("In try_unlock_user_with_biometric: attempt failed: {:?}", e)
+                        }
+                    }
+                }
+            }
+        }
+        Ok(())
+    }
 }
 
 /// This enum represents different states of the user's life cycle in the device.