Merge changes Id58dfa68,I42c6dea6

* changes:
  Keystore 2.0 legacy wrapper: Fix copy past error.
  Keystore 2.0 legacy wrapper: Fix hardware info.
diff --git a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
index 50e674d..8bec0f7 100644
--- a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
+++ b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
@@ -29,6 +29,7 @@
     /**
      * Allows LockSettingsService to inform keystore about adding a new user.
      * Callers require 'AddUser' permission.
+     *
      * ## Error conditions:
      * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'AddUser' permission.
      * `ResponseCode::SYSTEM_ERROR` - if failed to delete the keys of an existing user with the same
@@ -41,6 +42,7 @@
     /**
      * Allows LockSettingsService to inform keystore about removing a user.
      * Callers require 'RemoveUser' permission.
+     *
      * ## Error conditions:
      * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'RemoveUser' permission.
      * `ResponseCode::SYSTEM_ERROR` - if failed to delete the keys of the user being deleted.
@@ -52,8 +54,9 @@
     /**
      * Allows LockSettingsService to inform keystore about password change of a user.
      * Callers require 'ChangePassword' permission.
+     *
      * ## Error conditions:
-     * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'ChangePassword'
+     * `ResponseCode::PERMISSION_DENIED` - if the callers does not have the 'ChangePassword'
      *                                     permission.
      * `ResponseCode::SYSTEM_ERROR` - if failed to delete the super encrypted keys of the user.
      * `ResponseCode::Locked' -  if the keystore is locked for the given user.
@@ -71,11 +74,12 @@
      * @param nspace - The UID of the app that is to be cleared if domain is Domain.APP or
      *                 the SEPolicy namespace if domain is Domain.SELINUX.
      */
-     void clearNamespace(Domain domain, long nspace);
+    void clearNamespace(Domain domain, long nspace);
 
     /**
      * Allows querying user state, given user id.
      * Callers require 'GetState' permission.
+     *
      * ## Error conditions:
      * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'GetState'
      *                                     permission.
@@ -84,4 +88,14 @@
      * @param userId - Android user id
      */
     UserState getState(in int userId);
+
+    /**
+     * Informs Keystore 2.0 that the an off body event was detected.
+     *
+     * ## Error conditions:
+     * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the `ReportOffBody`
+     *                                     permission.
+     * `ResponseCode::SYSTEM_ERROR` - if an unexpected error occurred.
+     */
+    void onDeviceOffBody();
 }
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 0e8b3d7..5f35444 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -1702,11 +1702,11 @@
                     ],
                 )
                 .context("Failed to assign attestation key")?;
-            if result != 1 {
-                return Err(KsError::sys()).context(format!(
-                    "Expected to update a single entry but instead updated {}.",
-                    result
-                ));
+            if result == 0 {
+                return Err(KsError::Rc(ResponseCode::OUT_OF_KEYS)).context("Out of keys.");
+            } else if result > 1 {
+                return Err(KsError::sys())
+                    .context(format!("Expected to update 1 entry, instead updated {}", result));
             }
             Ok(()).no_gc()
         })
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index 1ce3e14..09ffecb 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -17,9 +17,9 @@
 use keystore2::authorization::AuthorizationManager;
 use keystore2::entropy;
 use keystore2::globals::ENFORCEMENTS;
+use keystore2::maintenance::Maintenance;
 use keystore2::remote_provisioning::RemoteProvisioningService;
 use keystore2::service::KeystoreService;
-use keystore2::user_manager::Maintenance;
 use keystore2::{apc::ApcManager, shared_secret_negotiation};
 use log::{error, info};
 use std::{panic, path::Path, sync::mpsc::channel};
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index f024cea..b824aa8 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -649,6 +649,45 @@
     }
 }
 
+ScopedAStatus
+KeyMintDevice::convertStorageKeyToEphemeral(const std::vector<uint8_t>& prefixedStorageKeyBlob,
+                                            std::vector<uint8_t>* ephemeralKeyBlob) {
+    KMV1::ErrorCode km_error;
+
+    /*
+     * Wrapped storage keys cannot be emulated (and they don't need to, because if a platform
+     * supports wrapped storage keys, then the legacy backend will support it too. So error out
+     * if the wrapped storage key given is a soft keymint key.
+     */
+    if (prefixedKeyBlobIsSoftKeyMint(prefixedStorageKeyBlob)) {
+        return convertErrorCode(KMV1::ErrorCode::UNIMPLEMENTED);
+    }
+
+    const std::vector<uint8_t>& storageKeyBlob =
+        prefixedKeyBlobRemovePrefix(prefixedStorageKeyBlob);
+
+    auto hidlCb = [&](V4_0_ErrorCode ret, const hidl_vec<uint8_t>& exportedKeyBlob) {
+        km_error = convert(ret);
+        if (km_error != KMV1::ErrorCode::OK) return;
+        /*
+         * This must return the blob without the prefix since it will be used directly
+         * as a storage encryption key. But this is alright, since this wrapped ephemeral
+         * key shouldn't/won't ever be used with keymint.
+         */
+        *ephemeralKeyBlob = exportedKeyBlob;
+    };
+
+    auto ret = mDevice->exportKey(V4_0_KeyFormat::RAW, storageKeyBlob, {}, {}, hidlCb);
+    if (!ret.isOk()) {
+        LOG(ERROR) << __func__ << " export_key failed: " << ret.description();
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    }
+    if (km_error != KMV1::ErrorCode::OK)
+        LOG(ERROR) << __func__ << " export_key failed, code " << int32_t(km_error);
+
+    return convertErrorCode(km_error);
+}
+
 ScopedAStatus KeyMintDevice::performOperation(const std::vector<uint8_t>& /* request */,
                                               std::vector<uint8_t>* /* response */) {
     return convertErrorCode(KMV1::ErrorCode::UNIMPLEMENTED);
diff --git a/keystore2/src/km_compat/km_compat.h b/keystore2/src/km_compat/km_compat.h
index b48a226..69c24b4 100644
--- a/keystore2/src/km_compat/km_compat.h
+++ b/keystore2/src/km_compat/km_compat.h
@@ -115,6 +115,9 @@
                                const std::optional<TimeStampToken>& timestampToken) override;
     ScopedAStatus earlyBootEnded() override;
 
+    ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob,
+                                               std::vector<uint8_t>* ephemeralKeyBlob) override;
+
     ScopedAStatus performOperation(const std::vector<uint8_t>& request,
                                    std::vector<uint8_t>* response) override;
 
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 2e8ced6..cb47e3e 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -27,13 +27,13 @@
 pub mod key_parameter;
 pub mod legacy_blob;
 pub mod legacy_migrator;
+pub mod maintenance;
 pub mod operation;
 pub mod permission;
 pub mod remote_provisioning;
 pub mod security_level;
 pub mod service;
 pub mod shared_secret_negotiation;
-pub mod user_manager;
 pub mod utils;
 
 mod attestation_key_utils;
diff --git a/keystore2/src/user_manager.rs b/keystore2/src/maintenance.rs
similarity index 89%
rename from keystore2/src/user_manager.rs
rename to keystore2/src/maintenance.rs
index 0cc2e92..1c206fc 100644
--- a/keystore2/src/user_manager.rs
+++ b/keystore2/src/maintenance.rs
@@ -14,12 +14,12 @@
 
 //! This module implements IKeystoreMaintenance AIDL interface.
 
-use crate::error::map_or_log_err;
 use crate::error::Error as KeystoreError;
 use crate::globals::{DB, LEGACY_MIGRATOR, SUPER_KEY};
 use crate::permission::KeystorePerm;
 use crate::super_key::UserState;
 use crate::utils::check_keystore_permission;
+use crate::{database::MonotonicRawTime, error::map_or_log_err};
 use android_security_maintenance::aidl::android::security::maintenance::{
     IKeystoreMaintenance::{BnKeystoreMaintenance, IKeystoreMaintenance},
     UserState::UserState as AidlUserState,
@@ -116,6 +116,15 @@
             UserState::LskfLocked => Ok(AidlUserState::LSKF_LOCKED),
         }
     }
+
+    fn on_device_off_body() -> Result<()> {
+        // Security critical permission check. This statement must return on fail.
+        check_keystore_permission(KeystorePerm::report_off_body())
+            .context("In on_device_off_body.")?;
+
+        DB.with(|db| db.borrow_mut().update_last_off_body(MonotonicRawTime::now()))
+            .context("In on_device_off_body: Trying to update last off body time.")
+    }
 }
 
 impl Interface for Maintenance {}
@@ -137,7 +146,11 @@
         map_or_log_err(Self::clear_namespace(domain, nspace), Ok)
     }
 
-    fn getState(&self, user_id: i32) -> binder::public_api::Result<AidlUserState> {
+    fn getState(&self, user_id: i32) -> BinderResult<AidlUserState> {
         map_or_log_err(Self::get_state(user_id), Ok)
     }
+
+    fn onDeviceOffBody(&self) -> BinderResult<()> {
+        map_or_log_err(Self::on_device_off_body(), Ok)
+    }
 }
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index 7f63834..f0a4c87 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -193,6 +193,7 @@
     /// ```
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     KeyPerm from KeyPermission with default (NONE, none) {
+        CONVERT_STORAGE_KEY_TO_EPHEMERAL,   selinux name: convert_storage_key_to_ephemeral;
         DELETE,         selinux name: delete;
         GEN_UNIQUE_ID,  selinux name: gen_unique_id;
         GET_INFO,       selinux name: get_info;
@@ -310,6 +311,8 @@
         ClearUID = 0x200,    selinux name: clear_uid;
         /// Checked when Credstore calls IKeystoreAuthorization to obtain auth tokens.
         GetAuthToken = 0x400,  selinux name: get_auth_token;
+        /// Checked when IKeystoreMaintenance::onDeviceOffBody is called.
+        ReportOffBody = 0x1000, selinux name: report_off_body;
     }
 );
 
@@ -584,6 +587,7 @@
         KeyPerm::rebind(),
         KeyPerm::update(),
         KeyPerm::use_(),
+        KeyPerm::convert_storage_key_to_ephemeral(),
     ];
 
     const SYSTEM_SERVER_PERMISSIONS_NO_GRANT: KeyPermSet = key_perm_set![
@@ -607,6 +611,7 @@
         KeyPerm::rebind(),
         KeyPerm::update(),
         KeyPerm::use_(),
+        KeyPerm::convert_storage_key_to_ephemeral(),
     ];
 
     const UNPRIV_PERMS: KeyPermSet = key_perm_set![
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 5a776fb..e50155b 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -727,6 +727,55 @@
             Ok(v) => Ok((v, None)),
         }
     }
+
+    fn convert_storage_key_to_ephemeral(&self, storage_key: &KeyDescriptor) -> Result<Vec<u8>> {
+        if storage_key.domain != Domain::BLOB {
+            return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context(concat!(
+                "In IKeystoreSecurityLevel convert_storage_key_to_ephemeral: ",
+                "Key must be of Domain::BLOB"
+            ));
+        }
+        let key_blob = storage_key
+            .blob
+            .as_ref()
+            .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+            .context(
+                "In IKeystoreSecurityLevel convert_storage_key_to_ephemeral: No key blob specified",
+            )?;
+
+        // convert_storage_key_to_ephemeral requires the associated permission
+        check_key_permission(KeyPerm::convert_storage_key_to_ephemeral(), storage_key, &None)
+            .context("In convert_storage_key_to_ephemeral: Check permission")?;
+
+        let km_dev: Strong<dyn IKeyMintDevice> = self.keymint.get_interface().context(concat!(
+            "In IKeystoreSecurityLevel convert_storage_key_to_ephemeral: ",
+            "Getting keymint device interface"
+        ))?;
+        map_km_error(km_dev.convertStorageKeyToEphemeral(key_blob))
+            .context("In keymint device convertStorageKeyToEphemeral")
+    }
+
+    fn delete_key(&self, key: &KeyDescriptor) -> Result<()> {
+        if key.domain != Domain::BLOB {
+            return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+                .context("In IKeystoreSecurityLevel delete_key: Key must be of Domain::BLOB");
+        }
+
+        let key_blob = key
+            .blob
+            .as_ref()
+            .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+            .context("In IKeystoreSecurityLevel delete_key: No key blob specified")?;
+
+        check_key_permission(KeyPerm::delete(), key, &None)
+            .context("In IKeystoreSecurityLevel delete_key: Checking delete permissions")?;
+
+        let km_dev: Strong<dyn IKeyMintDevice> = self
+            .keymint
+            .get_interface()
+            .context("In IKeystoreSecurityLevel delete_key: Getting keymint device interface")?;
+        map_km_error(km_dev.deleteKey(&key_blob)).context("In keymint device deleteKey")
+    }
 }
 
 impl binder::Interface for KeystoreSecurityLevel {}
@@ -773,4 +822,13 @@
             Ok,
         )
     }
+    fn convertStorageKeyToEphemeral(
+        &self,
+        storage_key: &KeyDescriptor,
+    ) -> binder::public_api::Result<Vec<u8>> {
+        map_or_log_err(self.convert_storage_key_to_ephemeral(storage_key), Ok)
+    }
+    fn deleteKey(&self, key: &KeyDescriptor) -> binder::public_api::Result<()> {
+        map_or_log_err(self.delete_key(key), Ok)
+    }
 }