Merge "Keystore 2.0: Remove Keystore 1.0 and remaining references"
diff --git a/OWNERS b/OWNERS
index 7d1fd14..bb51005 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,5 +1,6 @@
 swillden@google.com
 cbrubaker@google.com
 jdanis@google.com
+hasinitg@google.com
 kroot@google.com
-zeuthen@google.com
\ No newline at end of file
+zeuthen@google.com
diff --git a/fsverity_init/fsverity_init.cpp b/fsverity_init/fsverity_init.cpp
index b81fb22..7ab4097 100644
--- a/fsverity_init/fsverity_init.cpp
+++ b/fsverity_init/fsverity_init.cpp
@@ -49,6 +49,7 @@
 }
 
 void LoadKeyFromFile(key_serial_t keyring_id, const char* keyname, const std::string& path) {
+    LOG(INFO) << "LoadKeyFromFile path=" << path << " keyname=" << keyname;
     std::string content;
     if (!android::base::ReadFileToString(path, &content)) {
         LOG(ERROR) << "Failed to read key from " << path;
@@ -59,22 +60,24 @@
     }
 }
 
-void LoadKeyFromDirectory(key_serial_t keyring_id, const char* keyname, const char* dir) {
+void LoadKeyFromDirectory(key_serial_t keyring_id, const char* keyname_prefix, const char* dir) {
     if (!std::filesystem::exists(dir)) {
         return;
     }
+    int counter = 0;
     for (const auto& entry : std::filesystem::directory_iterator(dir)) {
         if (!android::base::EndsWithIgnoreCase(entry.path().c_str(), ".der")) continue;
-
-        LoadKeyFromFile(keyring_id, keyname, entry.path());
+        std::string keyname = keyname_prefix + std::to_string(counter);
+        counter++;
+        LoadKeyFromFile(keyring_id, keyname.c_str(), entry.path());
     }
 }
 
 void LoadKeyFromVerifiedPartitions(key_serial_t keyring_id) {
     // NB: Directories need to be synced with FileIntegrityService.java in
     // frameworks/base.
-    LoadKeyFromDirectory(keyring_id, "fsv_system", "/system/etc/security/fsverity");
-    LoadKeyFromDirectory(keyring_id, "fsv_product", "/product/etc/security/fsverity");
+    LoadKeyFromDirectory(keyring_id, "fsv_system_", "/system/etc/security/fsverity");
+    LoadKeyFromDirectory(keyring_id, "fsv_product_", "/product/etc/security/fsverity");
 }
 
 int main(int argc, const char** argv) {
diff --git a/identity/Android.bp b/identity/Android.bp
index d66f4ec..8267a6b 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -42,6 +42,7 @@
         "libbinder_ndk",
         "android.hardware.keymaster@4.0",
         "libcredstore_aidl",
+        "libcrypto",
         "libutils",
         "libhidlbase",
         "android.hardware.identity-support-lib",
@@ -53,7 +54,7 @@
     static_libs: [
         "android.hardware.identity-V3-cpp",
         "android.hardware.keymaster-V3-cpp",
-        "libcppbor",
+        "libcppbor_external",
     ]
 }
 
diff --git a/identity/Credential.cpp b/identity/Credential.cpp
index 2e6b9c1..7c75d8a 100644
--- a/identity/Credential.cpp
+++ b/identity/Credential.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "Credential"
+#define LOG_TAG "credstore"
 
 #include <android-base/logging.h>
 #include <android/binder_manager.h>
diff --git a/identity/CredentialData.cpp b/identity/CredentialData.cpp
index 96c436a..74b995d 100644
--- a/identity/CredentialData.cpp
+++ b/identity/CredentialData.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "CredentialData"
+#define LOG_TAG "credstore"
 
 #include <chrono>
 
@@ -273,7 +273,7 @@
     }
 
     for (size_t n = 0; n < map->size(); n++) {
-        auto [keyItem, valueItem] = (*map)[n];
+        auto& [keyItem, valueItem] = (*map)[n];
         const cppbor::Tstr* tstr = keyItem->asTstr();
         if (tstr == nullptr) {
             LOG(ERROR) << "Key item in top-level map is not a tstr";
@@ -325,7 +325,7 @@
                 return false;
             }
             for (size_t m = 0; m < map->size(); m++) {
-                auto [ecKeyItem, ecValueItem] = (*map)[m];
+                auto& [ecKeyItem, ecValueItem] = (*map)[m];
                 const cppbor::Tstr* ecTstr = ecKeyItem->asTstr();
                 if (ecTstr == nullptr) {
                     LOG(ERROR) << "Key item in encryptedChunks map is not a tstr";
diff --git a/identity/CredentialStore.cpp b/identity/CredentialStore.cpp
index 509e022..071cf24 100644
--- a/identity/CredentialStore.cpp
+++ b/identity/CredentialStore.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "CredentialStore"
+#define LOG_TAG "credstore"
 
 #include <algorithm>
 
diff --git a/identity/CredentialStoreFactory.cpp b/identity/CredentialStoreFactory.cpp
index 5c3bf36..0e901ba 100644
--- a/identity/CredentialStoreFactory.cpp
+++ b/identity/CredentialStoreFactory.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "CredentialStoreFactory"
+#define LOG_TAG "credstore"
 
 #include <android-base/logging.h>
 
diff --git a/identity/TEST_MAPPING b/identity/TEST_MAPPING
new file mode 100644
index 0000000..87707a8
--- /dev/null
+++ b/identity/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsIdentityTestCases"
+    }
+  ]
+}
diff --git a/identity/Util.cpp b/identity/Util.cpp
index cd29017..3a46bca 100644
--- a/identity/Util.cpp
+++ b/identity/Util.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "Util"
+#define LOG_TAG "credstore"
 
 #include <fcntl.h>
 #include <stdlib.h>
diff --git a/identity/WritableCredential.cpp b/identity/WritableCredential.cpp
index a300e51..9827d75 100644
--- a/identity/WritableCredential.cpp
+++ b/identity/WritableCredential.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "WritableCredential"
+#define LOG_TAG "credstore"
 
 #include <android-base/logging.h>
 #include <android/hardware/identity/support/IdentityCredentialSupport.h>
diff --git a/identity/main.cpp b/identity/main.cpp
index 08f2219..2559789 100644
--- a/identity/main.cpp
+++ b/identity/main.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "android.security.identity"
+#define LOG_TAG "credstore"
 
 #include <filesystem>
 
@@ -51,7 +51,11 @@
 
     auto ret = sm->addService(String16("android.security.identity"), factory);
     CHECK(ret == ::android::OK) << "Couldn't register binder service";
-    LOG(ERROR) << "Registered binder service";
+    LOG(INFO) << "Registered binder service";
+
+    // Credstore is a single-threaded process. So devote the main thread
+    // to handling binder messages.
+    IPCThreadState::self()->joinThreadPool();
 
     return 0;
 }
diff --git a/keystore-engine/keystore2_engine.cpp b/keystore-engine/keystore2_engine.cpp
index 8d25f48..69d2ca6 100644
--- a/keystore-engine/keystore2_engine.cpp
+++ b/keystore-engine/keystore2_engine.cpp
@@ -33,7 +33,7 @@
 
 #define AT __func__ << ":" << __LINE__ << " "
 
-constexpr const char keystore2_service_name[] = "android.system.keystore2";
+constexpr const char keystore2_service_name[] = "android.system.keystore2.IKeystoreService/default";
 const std::string keystore2_grant_id_prefix("ks2_keystore-engine_grant_id:");
 
 /**
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 9875d64..aff824b 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -29,6 +29,7 @@
     rustlibs: [
         "android.hardware.security.keymint-V1-rust",
         "android.hardware.security.secureclock-V1-rust",
+        "android.hardware.security.sharedsecret-V1-rust",
         "android.os.permissions_aidl-rust",
         "android.security.apc-rust",
         "android.security.authorization-rust",
@@ -38,6 +39,7 @@
         "android.system.keystore2-V1-rust",
         "libanyhow",
         "libbinder_rs",
+        "libcutils_bindgen",
         "libkeystore2_aaid-rust",
         "libkeystore2_apc_compat-rust",
         "libkeystore2_crypto_rust",
@@ -51,8 +53,12 @@
         "liblog_rust",
         "librand",
         "librusqlite",
+        "libstatslog_rust",
         "libthiserror",
     ],
+    shared_libs: [
+        "libcutils",
+    ],
 }
 
 rust_library {
@@ -94,4 +100,6 @@
         "libvpnprofilestore-rust",
     ],
     init_rc: ["keystore2.rc"],
+
+    vintf_fragments: ["android.system.keystore2-service.xml"],
 }
diff --git a/keystore2/TEST_MAPPING b/keystore2/TEST_MAPPING
index 99a1e60..16b6f85 100644
--- a/keystore2/TEST_MAPPING
+++ b/keystore2/TEST_MAPPING
@@ -8,6 +8,9 @@
     },
     {
       "name": "keystore2_test"
+    },
+    {
+      "name": "CtsIdentityTestCases"
     }
   ]
 }
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
index baf160d..9828bd0 100644
--- a/keystore2/aidl/Android.bp
+++ b/keystore2/aidl/Android.bp
@@ -28,7 +28,8 @@
     unstable: true,
     backend: {
         java: {
-            sdk_version: "module_current",
+            platform_apis: true,
+            srcs_available: true,
         },
         rust: {
             enabled: true,
@@ -46,13 +47,15 @@
     unstable: true,
     backend: {
         java: {
-            sdk_version: "module_current",
+            platform_apis: true,
+            srcs_available: true,
         },
         rust: {
             enabled: true,
         },
         ndk: {
             enabled: true,
+            apps_enabled: false,
         }
     },
 }
@@ -64,6 +67,7 @@
     backend: {
         java: {
             enabled: true,
+            srcs_available: true,
         },
         rust: {
             enabled: true,
@@ -85,13 +89,15 @@
     unstable: true,
     backend: {
         java: {
-            sdk_version: "module_current",
+            platform_apis: true,
+            srcs_available: true,
         },
         rust: {
             enabled: true,
         },
         ndk: {
             enabled: true,
+            apps_enabled: false,
         }
     },
 }
@@ -105,9 +111,8 @@
     unstable: true,
     backend: {
         java: {
-            enabled: true,
-            sdk_version: "module_current",
             platform_apis: true,
+            srcs_available: true,
         },
         ndk: {
             enabled: true,
@@ -127,13 +132,15 @@
     unstable: true,
     backend: {
         java: {
-            sdk_version: "module_current",
+            platform_apis: true,
+            srcs_available: true,
         },
         rust: {
             enabled: true,
         },
         ndk: {
             enabled: true,
+            apps_enabled: false,
         }
     },
 }
@@ -144,7 +151,8 @@
     unstable: true,
     backend: {
         java: {
-            sdk_version: "module_current",
+            platform_apis: true,
+            srcs_available: true,
         },
         rust: {
             enabled: true,
diff --git a/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl b/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
index f47d7f5..277b9dd 100644
--- a/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
+++ b/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
@@ -21,6 +21,7 @@
 /**
  * This callback interface must be implemented by the client to receive the result of the user
  * confirmation.
+ * @hide
  */
 interface IConfirmationCallback {
     /**
diff --git a/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl b/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
index 26ccf0f..3162224 100644
--- a/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
+++ b/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
@@ -18,6 +18,7 @@
 
 import android.security.apc.IConfirmationCallback;
 
+/** @hide */
 interface IProtectedConfirmation {
 
     /**
diff --git a/keystore2/aidl/android/security/apc/ResponseCode.aidl b/keystore2/aidl/android/security/apc/ResponseCode.aidl
index 7ae3e1c..9a3619f 100644
--- a/keystore2/aidl/android/security/apc/ResponseCode.aidl
+++ b/keystore2/aidl/android/security/apc/ResponseCode.aidl
@@ -19,6 +19,7 @@
 /**
  * Used as service specific exception code by IProtectedConfirmation and as result
  * code by IConfirmationCallback
+ * @hide
  */
 @Backing(type="int")
 enum ResponseCode {
diff --git a/keystore2/aidl/android/security/attestationmanager/ByteArray.aidl b/keystore2/aidl/android/security/attestationmanager/ByteArray.aidl
index a1592ec..dc37b1b 100644
--- a/keystore2/aidl/android/security/attestationmanager/ByteArray.aidl
+++ b/keystore2/aidl/android/security/attestationmanager/ByteArray.aidl
@@ -18,7 +18,6 @@
 
 /**
  * Simple data holder for a byte array, allowing for multidimensional arrays in AIDL.
- *
  * @hide
  */
 parcelable ByteArray {
diff --git a/keystore2/aidl/android/security/attestationmanager/IAttestationManager.aidl b/keystore2/aidl/android/security/attestationmanager/IAttestationManager.aidl
index 85eee57..e77a21e 100644
--- a/keystore2/aidl/android/security/attestationmanager/IAttestationManager.aidl
+++ b/keystore2/aidl/android/security/attestationmanager/IAttestationManager.aidl
@@ -21,7 +21,6 @@
 
 /**
  * Internal interface for performing device attestation.
- *
  * @hide
  */
 interface IAttestationManager {
diff --git a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
index 6dc172e..01616b1 100644
--- a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
+++ b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
@@ -23,7 +23,9 @@
 /**
  * IKeystoreAuthorization interface exposes the methods for other system components to
  * provide keystore with the information required to enforce authorizations on key usage.
+ * @hide
  */
+ @SensitiveData
 interface IKeystoreAuthorization {
 
     /**
diff --git a/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl b/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl
index 877a916..c7553a2 100644
--- a/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl
+++ b/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl
@@ -14,6 +14,7 @@
 
 package android.security.authorization;
 
+/** @hide */
 @Backing(type="int")
 enum LockScreenEvent {
     UNLOCK = 0,
diff --git a/keystore2/aidl/android/security/authorization/ResponseCode.aidl b/keystore2/aidl/android/security/authorization/ResponseCode.aidl
index 94f1120..169dc7b 100644
--- a/keystore2/aidl/android/security/authorization/ResponseCode.aidl
+++ b/keystore2/aidl/android/security/authorization/ResponseCode.aidl
@@ -16,6 +16,7 @@
 
 /**
  * Used as exception codes by IKeystoreAuthorization.
+ * @hide
  */
 @Backing(type="int")
 enum ResponseCode {
diff --git a/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
index 4b6a93b..50bfa19 100644
--- a/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
+++ b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
@@ -25,6 +25,7 @@
  * The compatibility service allows Keystore 2.0 to connect to legacy wrapper implementations that
  * it hosts itself without registering them as a service. Keystore 2.0 would not be allowed to
  * register a HAL service, so instead it registers this service which it can then connect to.
+ * @hide
  */
 interface IKeystoreCompatService {
     /**
diff --git a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
index 3115e92..21ddd9b 100644
--- a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
+++ b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
@@ -17,52 +17,51 @@
 import android.system.keystore2.Domain;
 import android.security.maintenance.UserState;
 
-// TODO: mark the interface with @SensitiveData when the annotation is ready (b/176110256).
-
 /**
  * IKeystoreMaintenance interface exposes the methods for adding/removing users and changing the
  * user's password.
  * @hide
  */
+ @SensitiveData
 interface IKeystoreMaintenance {
 
     /**
      * 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
      * user id.
      *
      * @param userId - Android user id
-     * @hide
      */
     void onUserAdded(in int userId);
 
     /**
      * 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.
      *
      * @param userId - Android user id
-     * @hide
      */
     void onUserRemoved(in int userId);
 
     /**
      * 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.
      *
      * @param userId - Android user id
      * @param password - a secret derived from the synthetic password of the user
-     * @hide
      */
     void onUserPasswordChanged(in int userId, in @nullable byte[] password);
 
@@ -73,20 +72,39 @@
      * @param domain - One of Domain.APP or Domain.SELINUX.
      * @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.
-     * @hide
      */
-     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.
      * `ResponseCode::SYSTEM_ERROR` - if an error occurred when querying the user state.
      *
      * @param userId - Android user id
-     * @hide
      */
     UserState getState(in int userId);
+
+    /**
+     * This function notifies the Keymint device of the specified securityLevel that
+     * early boot has ended, so that they no longer allow early boot keys to be used.
+     * ## Error conditions:
+     * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the 'EarlyBootEnded'
+     *                                     permission.
+     * A KeyMint ErrorCode may be returned indicating a backend diagnosed error.
+     */
+     void earlyBootEnded();
+
+    /**
+     * 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/aidl/android/security/maintenance/UserState.aidl b/keystore2/aidl/android/security/maintenance/UserState.aidl
index b6fe278..376f4fb 100644
--- a/keystore2/aidl/android/security/maintenance/UserState.aidl
+++ b/keystore2/aidl/android/security/maintenance/UserState.aidl
@@ -14,6 +14,7 @@
 
 package android.security.maintenance;
 
+/** @hide */
 @Backing(type="int")
 enum UserState {
     UNINITIALIZED = 0,
diff --git a/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl b/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
index 5c2d0b1..4a092af 100644
--- a/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
+++ b/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
@@ -16,6 +16,7 @@
 
 package android.security.remoteprovisioning;
 
+import android.hardware.security.keymint.DeviceInfo;
 import android.hardware.security.keymint.ProtectedData;
 import android.hardware.security.keymint.SecurityLevel;
 import android.security.remoteprovisioning.AttestationPoolStatus;
@@ -90,7 +91,7 @@
      *                         request.
      */
     byte[] generateCsr(in boolean testMode, in int numCsr, in byte[] eek, in byte[] challenge,
-        in SecurityLevel secLevel, out ProtectedData protectedData);
+        in SecurityLevel secLevel, out ProtectedData protectedData, out DeviceInfo deviceInfo);
 
     /**
      * This method provides a way for the returned attestation certificate chains to be provisioned
diff --git a/keystore2/aidl/android/security/vpnprofilestore/IVpnProfileStore.aidl b/keystore2/aidl/android/security/vpnprofilestore/IVpnProfileStore.aidl
index 054a4d7..8375b7b 100644
--- a/keystore2/aidl/android/security/vpnprofilestore/IVpnProfileStore.aidl
+++ b/keystore2/aidl/android/security/vpnprofilestore/IVpnProfileStore.aidl
@@ -18,7 +18,6 @@
 
 /**
  * Internal interface for accessing and storing VPN profiles.
- *
  * @hide
  */
 interface IVpnProfileStore {
diff --git a/keystore2/android.system.keystore2-service.xml b/keystore2/android.system.keystore2-service.xml
new file mode 100644
index 0000000..6b8d0cb
--- /dev/null
+++ b/keystore2/android.system.keystore2-service.xml
@@ -0,0 +1,9 @@
+<manifest version="1.0" type="framework">
+    <hal format="aidl">
+        <name>android.system.keystore2</name>
+        <interface>
+            <name>IKeystoreService</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/keystore2/src/attestation_key_utils.rs b/keystore2/src/attestation_key_utils.rs
new file mode 100644
index 0000000..425eec6
--- /dev/null
+++ b/keystore2/src/attestation_key_utils.rs
@@ -0,0 +1,126 @@
+// Copyright 2021, 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.
+
+//! Implements get_attestation_key_info which loads remote provisioned or user
+//! generated attestation keys.
+
+use crate::database::{BlobMetaData, KeyEntryLoadBits, KeyType};
+use crate::database::{KeyIdGuard, KeystoreDB};
+use crate::error::{Error, ErrorCode};
+use crate::permission::KeyPerm;
+use crate::remote_provisioning::RemProvState;
+use crate::utils::check_key_permission;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    AttestationKey::AttestationKey, Certificate::Certificate, KeyParameter::KeyParameter,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
+use anyhow::{Context, Result};
+use keystore2_crypto::parse_subject_from_certificate;
+
+/// KeyMint takes two different kinds of attestation keys. Remote provisioned keys
+/// and those that have been generated by the user. Unfortunately, they need to be
+/// handled quite differently, thus the different representations.
+pub enum AttestationKeyInfo {
+    RemoteProvisioned {
+        attestation_key: AttestationKey,
+        attestation_certs: Certificate,
+    },
+    UserGenerated {
+        key_id_guard: KeyIdGuard,
+        blob: Vec<u8>,
+        blob_metadata: BlobMetaData,
+        issuer_subject: Vec<u8>,
+    },
+}
+
+/// This function loads and, optionally, assigns the caller's remote provisioned
+/// attestation key or, if `attest_key_descriptor` is given, it loads the user
+/// generated attestation key from the database.
+pub fn get_attest_key_info(
+    key: &KeyDescriptor,
+    caller_uid: u32,
+    attest_key_descriptor: Option<&KeyDescriptor>,
+    params: &[KeyParameter],
+    rem_prov_state: &RemProvState,
+    db: &mut KeystoreDB,
+) -> Result<Option<AttestationKeyInfo>> {
+    match attest_key_descriptor {
+        None => rem_prov_state
+            .get_remotely_provisioned_attestation_key_and_certs(&key, caller_uid, params, db)
+            .context(concat!(
+                "In get_attest_key_and_cert_chain: ",
+                "Trying to get remotely provisioned attestation key."
+            ))
+            .map(|result| {
+                result.map(|(attestation_key, attestation_certs)| {
+                    AttestationKeyInfo::RemoteProvisioned { attestation_key, attestation_certs }
+                })
+            }),
+        Some(attest_key) => get_user_generated_attestation_key(&attest_key, caller_uid, db)
+            .context("In get_attest_key_and_cert_chain: Trying to load attest key")
+            .map(Some),
+    }
+}
+
+fn get_user_generated_attestation_key(
+    key: &KeyDescriptor,
+    caller_uid: u32,
+    db: &mut KeystoreDB,
+) -> Result<AttestationKeyInfo> {
+    let (key_id_guard, blob, cert, blob_metadata) =
+        load_attest_key_blob_and_cert(&key, caller_uid, db)
+            .context("In get_user_generated_attestation_key: Failed to load blob and cert")?;
+
+    let issuer_subject: Vec<u8> = parse_subject_from_certificate(&cert).context(
+        "In get_user_generated_attestation_key: Failed to parse subject from certificate.",
+    )?;
+
+    Ok(AttestationKeyInfo::UserGenerated { key_id_guard, blob, issuer_subject, blob_metadata })
+}
+
+fn load_attest_key_blob_and_cert(
+    key: &KeyDescriptor,
+    caller_uid: u32,
+    db: &mut KeystoreDB,
+) -> Result<(KeyIdGuard, Vec<u8>, Vec<u8>, BlobMetaData)> {
+    match key.domain {
+        Domain::BLOB => Err(Error::Km(ErrorCode::INVALID_ARGUMENT)).context(
+            "In load_attest_key_blob_and_cert: Domain::BLOB attestation keys not supported",
+        ),
+        _ => {
+            let (key_id_guard, mut key_entry) = db
+                .load_key_entry(
+                    &key,
+                    KeyType::Client,
+                    KeyEntryLoadBits::BOTH,
+                    caller_uid,
+                    |k, av| check_key_permission(KeyPerm::use_(), k, &av),
+                )
+                .context("In load_attest_key_blob_and_cert: Failed to load key.")?;
+
+            let (blob, blob_metadata) =
+                key_entry.take_key_blob_info().ok_or_else(Error::sys).context(concat!(
+                    "In load_attest_key_blob_and_cert: Successfully loaded key entry,",
+                    " but KM blob was missing."
+                ))?;
+            let cert = key_entry.take_cert().ok_or_else(Error::sys).context(concat!(
+                "In load_attest_key_blob_and_cert: Successfully loaded key entry,",
+                " but cert was missing."
+            ))?;
+            Ok((key_id_guard, blob, cert, blob_metadata))
+        }
+    }
+}
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index 553746a..06b5598 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -138,6 +138,16 @@
                 check_keystore_permission(KeystorePerm::unlock())
                     .context("In on_lock_screen_event: Unlock with password.")?;
                 ENFORCEMENTS.set_device_locked(user_id, false);
+
+                DB.with(|db| {
+                    SUPER_KEY.unlock_screen_lock_bound_key(
+                        &mut db.borrow_mut(),
+                        user_id as u32,
+                        &password,
+                    )
+                })
+                .context("In on_lock_screen_event: unlock_screen_lock_bound_key failed")?;
+
                 // Unlock super key.
                 if let UserState::Uninitialized = DB
                     .with(|db| {
@@ -168,6 +178,8 @@
                 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);
+
                 Ok(())
             }
             _ => {
diff --git a/keystore2/src/crypto/Android.bp b/keystore2/src/crypto/Android.bp
index e386735..21c9b74 100644
--- a/keystore2/src/crypto/Android.bp
+++ b/keystore2/src/crypto/Android.bp
@@ -68,7 +68,8 @@
         "--whitelist-function", "HKDFExpand",
         "--whitelist-function", "ECDHComputeKey",
         "--whitelist-function", "ECKEYGenerateKey",
-        "--whitelist-function", "ECKEYDeriveFromSecret",
+        "--whitelist-function", "ECKEYMarshalPrivateKey",
+        "--whitelist-function", "ECKEYParsePrivateKey",
         "--whitelist-function", "EC_KEY_get0_public_key",
         "--whitelist-function", "ECPOINTPoint2Oct",
         "--whitelist-function", "ECPOINTOct2Point",
diff --git a/keystore2/src/crypto/crypto.cpp b/keystore2/src/crypto/crypto.cpp
index 2e613fd..e4a1ac3 100644
--- a/keystore2/src/crypto/crypto.cpp
+++ b/keystore2/src/crypto/crypto.cpp
@@ -236,10 +236,28 @@
     return key;
 }
 
-EC_KEY* ECKEYDeriveFromSecret(const uint8_t* secret, size_t secret_len) {
+size_t ECKEYMarshalPrivateKey(const EC_KEY* priv_key, uint8_t* buf, size_t len) {
+    CBB cbb;
+    size_t out_len;
+    if (!CBB_init_fixed(&cbb, buf, len) ||
+        !EC_KEY_marshal_private_key(&cbb, priv_key, EC_PKEY_NO_PARAMETERS | EC_PKEY_NO_PUBKEY) ||
+        !CBB_finish(&cbb, nullptr, &out_len)) {
+        return 0;
+    } else {
+        return out_len;
+    }
+}
+
+EC_KEY* ECKEYParsePrivateKey(const uint8_t* buf, size_t len) {
+    CBS cbs;
+    CBS_init(&cbs, buf, len);
     EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
-    auto result = EC_KEY_derive_from_secret(group, secret, secret_len);
+    auto result = EC_KEY_parse_private_key(&cbs, group);
     EC_GROUP_free(group);
+    if (result != nullptr && CBS_len(&cbs) != 0) {
+        EC_KEY_free(result);
+        return nullptr;
+    }
     return result;
 }
 
diff --git a/keystore2/src/crypto/crypto.hpp b/keystore2/src/crypto/crypto.hpp
index 6686c8c..f841eb3 100644
--- a/keystore2/src/crypto/crypto.hpp
+++ b/keystore2/src/crypto/crypto.hpp
@@ -55,7 +55,9 @@
 
   EC_KEY* ECKEYGenerateKey();
 
-  EC_KEY* ECKEYDeriveFromSecret(const uint8_t *secret, size_t secret_len);
+  size_t ECKEYMarshalPrivateKey(const EC_KEY *priv_key, uint8_t *buf, size_t len);
+
+  EC_KEY* ECKEYParsePrivateKey(const uint8_t *buf, size_t len);
 
   size_t ECPOINTPoint2Oct(const EC_POINT *point, uint8_t *buf, size_t len);
 
diff --git a/keystore2/src/crypto/error.rs b/keystore2/src/crypto/error.rs
index 1eec321..a369012 100644
--- a/keystore2/src/crypto/error.rs
+++ b/keystore2/src/crypto/error.rs
@@ -74,9 +74,13 @@
     #[error("Failed to generate key.")]
     ECKEYGenerateKeyFailed,
 
-    /// This is returned if the C implementation of ECKEYDeriveFromSecret returned null.
-    #[error("Failed to derive key.")]
-    ECKEYDeriveFailed,
+    /// This is returned if the C implementation of ECKEYMarshalPrivateKey returned 0.
+    #[error("Failed to marshal private key.")]
+    ECKEYMarshalPrivateKeyFailed,
+
+    /// This is returned if the C implementation of ECKEYParsePrivateKey returned null.
+    #[error("Failed to parse private key.")]
+    ECKEYParsePrivateKeyFailed,
 
     /// This is returned if the C implementation of ECPOINTPoint2Oct returned 0.
     #[error("Failed to convert point to oct.")]
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index 98e6eef..3523a9d 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -20,9 +20,9 @@
 pub use error::Error;
 use keystore2_crypto_bindgen::{
     extractSubjectFromCertificate, generateKeyFromPassword, randomBytes, AES_gcm_decrypt,
-    AES_gcm_encrypt, ECDHComputeKey, ECKEYDeriveFromSecret, ECKEYGenerateKey, ECPOINTOct2Point,
-    ECPOINTPoint2Oct, EC_KEY_free, EC_KEY_get0_public_key, EC_POINT_free, HKDFExpand, HKDFExtract,
-    EC_KEY, EC_MAX_BYTES, EC_POINT, EVP_MAX_MD_SIZE,
+    AES_gcm_encrypt, ECDHComputeKey, ECKEYGenerateKey, ECKEYMarshalPrivateKey,
+    ECKEYParsePrivateKey, ECPOINTOct2Point, ECPOINTPoint2Oct, EC_KEY_free, EC_KEY_get0_public_key,
+    EC_POINT_free, HKDFExpand, HKDFExtract, EC_KEY, EC_MAX_BYTES, EC_POINT, EVP_MAX_MD_SIZE,
 };
 use std::convert::TryFrom;
 use std::convert::TryInto;
@@ -338,14 +338,32 @@
     Ok(ECKey(key))
 }
 
-/// Calls the boringssl EC_KEY_derive_from_secret function.
-pub fn ec_key_derive_from_secret(secret: &[u8]) -> Result<ECKey, Error> {
-    // Safety: secret is a valid buffer.
-    let result = unsafe { ECKEYDeriveFromSecret(secret.as_ptr(), secret.len()) };
-    if result.is_null() {
-        return Err(Error::ECKEYDeriveFailed);
+/// Calls the boringssl EC_KEY_marshal_private_key function.
+pub fn ec_key_marshal_private_key(key: &ECKey) -> Result<ZVec, Error> {
+    let len = 39; // Empirically observed length of private key
+    let mut buf = ZVec::new(len)?;
+    // Safety: the key is valid.
+    // This will not write past the specified length of the buffer; if the
+    // len above is too short, it returns 0.
+    let written_len =
+        unsafe { ECKEYMarshalPrivateKey(key.0, buf.as_mut_ptr(), buf.len()) } as usize;
+    if written_len == len {
+        Ok(buf)
+    } else {
+        Err(Error::ECKEYMarshalPrivateKeyFailed)
     }
-    Ok(ECKey(result))
+}
+
+/// Calls the boringssl EC_KEY_parse_private_key function.
+pub fn ec_key_parse_private_key(buf: &[u8]) -> Result<ECKey, Error> {
+    // Safety: this will not read past the specified length of the buffer.
+    // It fails if less than the whole buffer is consumed.
+    let key = unsafe { ECKEYParsePrivateKey(buf.as_ptr(), buf.len()) };
+    if key.is_null() {
+        Err(Error::ECKEYParsePrivateKeyFailed)
+    } else {
+        Ok(ECKey(key))
+    }
 }
 
 /// Calls the boringssl EC_KEY_get0_public_key function.
@@ -519,26 +537,26 @@
     }
 
     #[test]
-    fn test_ec() {
-        let key = ec_key_generate_key();
-        assert!(key.is_ok());
-        assert!(!key.unwrap().0.is_null());
+    fn test_ec() -> Result<(), Error> {
+        let priv0 = ec_key_generate_key()?;
+        assert!(!priv0.0.is_null());
+        let pub0 = ec_key_get0_public_key(&priv0);
 
-        let key = ec_key_derive_from_secret(&[42; 16]);
-        assert!(key.is_ok());
-        let key = key.unwrap();
-        assert!(!key.0.is_null());
+        let priv1 = ec_key_generate_key()?;
+        let pub1 = ec_key_get0_public_key(&priv1);
 
-        let point = ec_key_get0_public_key(&key);
+        let priv0s = ec_key_marshal_private_key(&priv0)?;
+        let pub0s = ec_point_point_to_oct(pub0.get_point())?;
+        let pub1s = ec_point_point_to_oct(pub1.get_point())?;
 
-        let result = ecdh_compute_key(point.get_point(), &key);
-        assert!(result.is_ok());
+        let priv0 = ec_key_parse_private_key(&priv0s)?;
+        let pub0 = ec_point_oct_to_point(&pub0s)?;
+        let pub1 = ec_point_oct_to_point(&pub1s)?;
 
-        let oct = ec_point_point_to_oct(point.get_point());
-        assert!(oct.is_ok());
-        let oct = oct.unwrap();
+        let left_key = ecdh_compute_key(pub0.get_point(), &priv1)?;
+        let right_key = ecdh_compute_key(pub1.get_point(), &priv0)?;
 
-        let point2 = ec_point_oct_to_point(oct.as_slice());
-        assert!(point2.is_ok());
+        assert_eq!(left_key, right_key);
+        Ok(())
     }
 }
diff --git a/keystore2/src/crypto/zvec.rs b/keystore2/src/crypto/zvec.rs
index e75e1dc..78b474e 100644
--- a/keystore2/src/crypto/zvec.rs
+++ b/keystore2/src/crypto/zvec.rs
@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#![allow(dead_code)]
-
 use crate::error::Error;
 use nix::sys::mman::{mlock, munlock};
 use std::convert::TryFrom;
@@ -106,12 +104,16 @@
 impl TryFrom<Vec<u8>> for ZVec {
     type Error = Error;
 
-    fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> {
+    fn try_from(mut v: Vec<u8>) -> Result<Self, Self::Error> {
+        let len = v.len();
+        // into_boxed_slice calls shrink_to_fit, which may move the pointer.
+        // But sometimes the contents of the Vec are already sensitive and
+        // mustn't be copied. So ensure the shrink_to_fit call is a NOP.
+        v.resize(v.capacity(), 0);
         let b = v.into_boxed_slice();
         if !b.is_empty() {
             unsafe { mlock(b.as_ptr() as *const std::ffi::c_void, b.len()) }?;
         }
-        let len = b.len();
         Ok(Self { elems: b, len })
     }
 }
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 0e8b3d7..6a07716 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -41,7 +41,6 @@
 //! from the database module these functions take permission check
 //! callbacks.
 
-use crate::error::{Error as KsError, ErrorCode, ResponseCode};
 use crate::impl_metadata; // This is in db_utils.rs
 use crate::key_parameter::{KeyParameter, Tag};
 use crate::permission::KeyPermSet;
@@ -49,6 +48,11 @@
 use crate::{
     db_utils::{self, SqlField},
     gc::Gc,
+    super_key::USER_SUPER_KEY,
+};
+use crate::{
+    error::{Error as KsError, ErrorCode, ResponseCode},
+    super_key::SuperKeyType,
 };
 use anyhow::{anyhow, Context, Result};
 use std::{convert::TryFrom, convert::TryInto, ops::Deref, time::SystemTimeError};
@@ -108,6 +112,8 @@
         /// Vector representing the raw public key so results from the server can be matched
         /// to the right entry
         AttestationRawPubKey(Vec<u8>) with accessor attestation_raw_pub_key,
+        /// SEC1 public key for ECDH encryption
+        Sec1PublicKey(Vec<u8>) with accessor sec1_public_key,
         //  --- ADD NEW META DATA FIELDS HERE ---
         // For backwards compatibility add new entries only to
         // end of this list and above this comment.
@@ -178,6 +184,8 @@
         AeadTag(Vec<u8>) with accessor aead_tag,
         /// The uuid of the owning KeyMint instance.
         KmUuid(Uuid) with accessor km_uuid,
+        /// If the key is ECDH encrypted, this is the ephemeral public key
+        PublicKey(Vec<u8>) with accessor public_key,
         //  --- ADD NEW META DATA FIELDS HERE ---
         // For backwards compatibility add new entries only to
         // end of this list and above this comment.
@@ -816,9 +824,6 @@
     const UNASSIGNED_KEY_ID: i64 = -1i64;
     const PERBOOT_DB_FILE_NAME: &'static str = &"file:perboot.sqlite?mode=memory&cache=shared";
 
-    /// The alias of the user super key.
-    pub const USER_SUPER_KEY_ALIAS: &'static str = &"USER_SUPER_KEY";
-
     /// This creates a PerBootDbKeepAlive object to keep the per boot database alive.
     pub fn keep_perboot_db_alive() -> Result<PerBootDbKeepAlive> {
         let conn = Connection::open_in_memory()
@@ -1149,7 +1154,10 @@
     pub fn store_super_key(
         &mut self,
         user_id: u32,
-        blob_info: &(&[u8], &BlobMetaData),
+        key_type: &SuperKeyType,
+        blob: &[u8],
+        blob_metadata: &BlobMetaData,
+        key_metadata: &KeyMetaData,
     ) -> Result<KeyEntry> {
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let key_id = Self::insert_with_retry(|id| {
@@ -1162,7 +1170,7 @@
                         KeyType::Super,
                         Domain::APP.0,
                         user_id as i64,
-                        Self::USER_SUPER_KEY_ALIAS,
+                        key_type.alias,
                         KeyLifeCycle::Live,
                         &KEYSTORE_UUID,
                     ],
@@ -1170,7 +1178,8 @@
             })
             .context("Failed to insert into keyentry table.")?;
 
-            let (blob, blob_metadata) = *blob_info;
+            key_metadata.store_in_db(key_id, tx).context("KeyMetaData::store_in_db failed")?;
+
             Self::set_blob_internal(
                 &tx,
                 key_id,
@@ -1188,12 +1197,16 @@
     }
 
     /// Loads super key of a given user, if exists
-    pub fn load_super_key(&mut self, user_id: u32) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
+    pub fn load_super_key(
+        &mut self,
+        key_type: &SuperKeyType,
+        user_id: u32,
+    ) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let key_descriptor = KeyDescriptor {
                 domain: Domain::APP,
                 nspace: user_id as i64,
-                alias: Some(String::from("USER_SUPER_KEY")),
+                alias: Some(key_type.alias.into()),
                 blob: None,
             };
             let id = Self::load_key_entry_id(&tx, &key_descriptor, KeyType::Super);
@@ -1289,7 +1302,7 @@
                         Some(&blob),
                         Some(&metadata),
                     )
-                    .context("In get_of_create_key_with.")?;
+                    .context("In get_or_create_key_with.")?;
                     (
                         id,
                         KeyEntry {
@@ -1702,11 +1715,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()
         })
@@ -2649,7 +2662,7 @@
                     // OR super key:
                     KeyType::Super,
                     user_id,
-                    Self::USER_SUPER_KEY_ALIAS,
+                    USER_SUPER_KEY.alias,
                     KeyLifeCycle::Live
                 ])
                 .context("In unbind_keys_for_user. Failed to query the keys created by apps.")?;
@@ -3012,7 +3025,7 @@
     where
         F: Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static,
     {
-        let super_key = Arc::new(SuperKeyManager::new());
+        let super_key: Arc<SuperKeyManager> = Default::default();
 
         let gc_db = KeystoreDB::new(path, None).expect("Failed to open test gc db_connection.");
         let gc = Gc::new_init_with(Default::default(), move || (Box::new(cb), gc_db, super_key));
@@ -4431,7 +4444,6 @@
     }
 
     #[derive(Debug, PartialEq)]
-    #[allow(dead_code)]
     struct KeyEntryRow {
         id: i64,
         key_type: KeyType,
@@ -4887,29 +4899,34 @@
         let mut db = new_test_db()?;
         let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
         let super_key = keystore2_crypto::generate_aes256_key()?;
-        let secret = String::from("keystore2 is great.");
-        let secret_bytes = secret.into_bytes();
+        let secret_bytes = b"keystore2 is great.";
         let (encrypted_secret, iv, tag) =
-            keystore2_crypto::aes_gcm_encrypt(&secret_bytes, &super_key)?;
+            keystore2_crypto::aes_gcm_encrypt(secret_bytes, &super_key)?;
 
         let (encrypted_super_key, metadata) =
             SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
-        db.store_super_key(1, &(&encrypted_super_key, &metadata))?;
+        db.store_super_key(
+            1,
+            &USER_SUPER_KEY,
+            &encrypted_super_key,
+            &metadata,
+            &KeyMetaData::new(),
+        )?;
 
         //check if super key exists
-        assert!(db.key_exists(Domain::APP, 1, "USER_SUPER_KEY", KeyType::Super)?);
+        assert!(db.key_exists(Domain::APP, 1, &USER_SUPER_KEY.alias, KeyType::Super)?);
 
-        let (_, key_entry) = db.load_super_key(1)?.unwrap();
-        let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(key_entry, &pw)?;
-
-        let decrypted_secret_bytes = keystore2_crypto::aes_gcm_decrypt(
-            &encrypted_secret,
-            &iv,
-            &tag,
-            &loaded_super_key.get_key(),
+        let (_, key_entry) = db.load_super_key(&USER_SUPER_KEY, 1)?.unwrap();
+        let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(
+            USER_SUPER_KEY.algorithm,
+            key_entry,
+            &pw,
+            None,
         )?;
-        let decrypted_secret = String::from_utf8((&decrypted_secret_bytes).to_vec())?;
-        assert_eq!(String::from("keystore2 is great."), decrypted_secret);
+
+        let decrypted_secret_bytes =
+            loaded_super_key.aes_gcm_decrypt(&encrypted_secret, &iv, &tag)?;
+        assert_eq!(secret_bytes, &*decrypted_secret_bytes);
         Ok(())
     }
 }
diff --git a/keystore2/src/ec_crypto.rs b/keystore2/src/ec_crypto.rs
new file mode 100644
index 0000000..0425d4a
--- /dev/null
+++ b/keystore2/src/ec_crypto.rs
@@ -0,0 +1,137 @@
+// Copyright 2021, 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.
+
+//! Implement ECDH-based encryption.
+
+use anyhow::{Context, Result};
+use keystore2_crypto::{
+    aes_gcm_decrypt, aes_gcm_encrypt, ec_key_generate_key, ec_key_get0_public_key,
+    ec_key_marshal_private_key, ec_key_parse_private_key, ec_point_oct_to_point,
+    ec_point_point_to_oct, ecdh_compute_key, generate_salt, hkdf_expand, hkdf_extract, ECKey, ZVec,
+    AES_256_KEY_LENGTH,
+};
+
+/// Private key for ECDH encryption.
+pub struct ECDHPrivateKey(ECKey);
+
+impl ECDHPrivateKey {
+    /// Randomly generate a fresh keypair.
+    pub fn generate() -> Result<ECDHPrivateKey> {
+        ec_key_generate_key()
+            .map(ECDHPrivateKey)
+            .context("In ECDHPrivateKey::generate: generation failed")
+    }
+
+    /// Deserialize bytes into an ECDH keypair
+    pub fn from_private_key(buf: &[u8]) -> Result<ECDHPrivateKey> {
+        ec_key_parse_private_key(buf)
+            .map(ECDHPrivateKey)
+            .context("In ECDHPrivateKey::from_private_key: parsing failed")
+    }
+
+    /// Serialize the ECDH key into bytes
+    pub fn private_key(&self) -> Result<ZVec> {
+        ec_key_marshal_private_key(&self.0)
+            .context("In ECDHPrivateKey::private_key: marshalling failed")
+    }
+
+    /// Generate the serialization of the corresponding public key
+    pub fn public_key(&self) -> Result<Vec<u8>> {
+        let point = ec_key_get0_public_key(&self.0);
+        ec_point_point_to_oct(point.get_point())
+            .context("In ECDHPrivateKey::public_key: marshalling failed")
+    }
+
+    /// Use ECDH to agree an AES key with another party whose public key we have.
+    /// Sender and recipient public keys are passed separately because they are
+    /// switched in encryption vs decryption.
+    fn agree_key(
+        &self,
+        salt: &[u8],
+        other_public_key: &[u8],
+        sender_public_key: &[u8],
+        recipient_public_key: &[u8],
+    ) -> Result<ZVec> {
+        let hkdf = hkdf_extract(sender_public_key, salt)
+            .context("In ECDHPrivateKey::agree_key: hkdf_extract on sender_public_key failed")?;
+        let hkdf = hkdf_extract(recipient_public_key, &hkdf)
+            .context("In ECDHPrivateKey::agree_key: hkdf_extract on recipient_public_key failed")?;
+        let other_public_key = ec_point_oct_to_point(other_public_key)
+            .context("In ECDHPrivateKey::agree_key: ec_point_oct_to_point failed")?;
+        let secret = ecdh_compute_key(other_public_key.get_point(), &self.0)
+            .context("In ECDHPrivateKey::agree_key: ecdh_compute_key failed")?;
+        let prk = hkdf_extract(&secret, &hkdf)
+            .context("In ECDHPrivateKey::agree_key: hkdf_extract on secret failed")?;
+
+        let aes_key = hkdf_expand(AES_256_KEY_LENGTH, &prk, b"AES-256-GCM key")
+            .context("In ECDHPrivateKey::agree_key: hkdf_expand failed")?;
+        Ok(aes_key)
+    }
+
+    /// Encrypt a message to the party with the given public key
+    pub fn encrypt_message(
+        recipient_public_key: &[u8],
+        message: &[u8],
+    ) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>)> {
+        let sender_key =
+            Self::generate().context("In ECDHPrivateKey::encrypt_message: generate failed")?;
+        let sender_public_key = sender_key
+            .public_key()
+            .context("In ECDHPrivateKey::encrypt_message: public_key failed")?;
+        let salt =
+            generate_salt().context("In ECDHPrivateKey::encrypt_message: generate_salt failed")?;
+        let aes_key = sender_key
+            .agree_key(&salt, recipient_public_key, &sender_public_key, recipient_public_key)
+            .context("In ECDHPrivateKey::encrypt_message: agree_key failed")?;
+        let (ciphertext, iv, tag) = aes_gcm_encrypt(message, &aes_key)
+            .context("In ECDHPrivateKey::encrypt_message: aes_gcm_encrypt failed")?;
+        Ok((sender_public_key, salt, iv, ciphertext, tag))
+    }
+
+    /// Decrypt a message sent to us
+    pub fn decrypt_message(
+        &self,
+        sender_public_key: &[u8],
+        salt: &[u8],
+        iv: &[u8],
+        ciphertext: &[u8],
+        tag: &[u8],
+    ) -> Result<ZVec> {
+        let recipient_public_key = self.public_key()?;
+        let aes_key = self
+            .agree_key(salt, sender_public_key, sender_public_key, &recipient_public_key)
+            .context("In ECDHPrivateKey::decrypt_message: agree_key failed")?;
+        aes_gcm_decrypt(ciphertext, iv, tag, &aes_key)
+            .context("In ECDHPrivateKey::decrypt_message: aes_gcm_decrypt failed")
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_crypto_roundtrip() -> Result<()> {
+        let message = b"Hello world";
+        let recipient = ECDHPrivateKey::generate()?;
+        let (sender_public_key, salt, iv, ciphertext, tag) =
+            ECDHPrivateKey::encrypt_message(&recipient.public_key()?, message)?;
+        let recipient = ECDHPrivateKey::from_private_key(&recipient.private_key()?)?;
+        let decrypted =
+            recipient.decrypt_message(&sender_public_key, &salt, &iv, &ciphertext, &tag)?;
+        let dc: &[u8] = &decrypted;
+        assert_eq!(message, dc);
+        Ok(())
+    }
+}
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 06648f1..3f003be 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -14,11 +14,11 @@
 
 //! This is the Keystore 2.0 Enforcements module.
 // TODO: more description to follow.
-use crate::authorization::Error as AuthzError;
 use crate::database::{AuthTokenEntry, MonotonicRawTime};
 use crate::error::{map_binder_status, Error, ErrorCode};
 use crate::globals::{get_timestamp_service, ASYNC_TASK, DB, ENFORCEMENTS};
 use crate::key_parameter::{KeyParameter, KeyParameterValue};
+use crate::{authorization::Error as AuthzError, super_key::SuperEncryptionType};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Algorithm::Algorithm, ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
     HardwareAuthenticatorType::HardwareAuthenticatorType,
@@ -29,7 +29,7 @@
 };
 use android_security_authorization::aidl::android::security::authorization::ResponseCode::ResponseCode as AuthzResponseCode;
 use android_system_keystore2::aidl::android::system::keystore2::{
-    IKeystoreSecurityLevel::KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING,
+    Domain::Domain, IKeystoreSecurityLevel::KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING,
     OperationChallenge::OperationChallenge,
 };
 use android_system_keystore2::binder::Strong;
@@ -757,16 +757,32 @@
     }
 
     /// Given the set of key parameters and flags, check if super encryption is required.
-    pub fn super_encryption_required(key_parameters: &[KeyParameter], flags: Option<i32>) -> bool {
-        let auth_bound = key_parameters.iter().any(|kp| kp.get_tag() == Tag::USER_SECURE_ID);
-
-        let skip_lskf_binding = if let Some(flags) = flags {
-            (flags & KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING) != 0
-        } else {
-            false
-        };
-
-        auth_bound && !skip_lskf_binding
+    pub fn super_encryption_required(
+        domain: &Domain,
+        key_parameters: &[KeyParameter],
+        flags: Option<i32>,
+    ) -> SuperEncryptionType {
+        if *domain != Domain::APP {
+            return SuperEncryptionType::None;
+        }
+        if let Some(flags) = flags {
+            if (flags & KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING) != 0 {
+                return SuperEncryptionType::None;
+            }
+        }
+        if key_parameters
+            .iter()
+            .any(|kp| matches!(kp.key_parameter_value(), KeyParameterValue::UnlockedDeviceRequired))
+        {
+            return SuperEncryptionType::ScreenLockBound;
+        }
+        if key_parameters
+            .iter()
+            .any(|kp| matches!(kp.key_parameter_value(), KeyParameterValue::UserSecureID(_)))
+        {
+            return SuperEncryptionType::LskfBound;
+        }
+        SuperEncryptionType::None
     }
 
     /// Finds a matching auth token along with a timestamp token.
@@ -804,7 +820,7 @@
                     let token_valid = now_in_millis
                         .checked_sub(auth_token_entry.time_received().milli_seconds())
                         .map_or(false, |token_age_in_millis| {
-                            token_age_in_millis > auth_token_max_age_millis
+                            auth_token_max_age_millis > token_age_in_millis
                         });
                     token_valid && auth_token_entry.satisfies(&sids, auth_type)
                 })
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index 388487c..465dcfa 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -196,28 +196,33 @@
     result.map_or_else(
         |e| {
             let e = map_err(e);
-            let root_cause = e.root_cause();
-            let rc = match root_cause.downcast_ref::<Error>() {
-                Some(Error::Rc(rcode)) => rcode.0,
-                Some(Error::Km(ec)) => ec.0,
-                Some(Error::Rp(_)) => ResponseCode::SYSTEM_ERROR.0,
-                // If an Error::Binder reaches this stage we report a system error.
-                // The exception code and possible service specific error will be
-                // printed in the error log above.
-                Some(Error::Binder(_, _)) | Some(Error::BinderTransaction(_)) => {
-                    ResponseCode::SYSTEM_ERROR.0
-                }
-                None => match root_cause.downcast_ref::<selinux::Error>() {
-                    Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
-                    _ => ResponseCode::SYSTEM_ERROR.0,
-                },
-            };
+            let rc = get_error_code(&e);
             Err(BinderStatus::new_service_specific_error(rc, None))
         },
         handle_ok,
     )
 }
 
+/// Returns the error code given a reference to the error
+pub fn get_error_code(e: &anyhow::Error) -> i32 {
+    let root_cause = e.root_cause();
+    match root_cause.downcast_ref::<Error>() {
+        Some(Error::Rc(rcode)) => rcode.0,
+        Some(Error::Km(ec)) => ec.0,
+        Some(Error::Rp(_)) => ResponseCode::SYSTEM_ERROR.0,
+        // If an Error::Binder reaches this stage we report a system error.
+        // The exception code and possible service specific error will be
+        // printed in the error log above.
+        Some(Error::Binder(_, _)) | Some(Error::BinderTransaction(_)) => {
+            ResponseCode::SYSTEM_ERROR.0
+        }
+        None => match root_cause.downcast_ref::<selinux::Error>() {
+            Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
+            _ => ResponseCode::SYSTEM_ERROR.0,
+        },
+    }
+}
+
 #[cfg(test)]
 pub mod tests {
 
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index 54f7dc7..58142a4 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -278,8 +278,10 @@
     let secure_clock_available =
         secureclock_instances.as_vec()?.iter().any(|instance| *instance == "default");
 
+    let default_time_stamp_service_name = format!("{}/default", TIME_STAMP_SERVICE_NAME);
+
     let secureclock = if secure_clock_available {
-        map_binder_status_code(binder::get_interface(TIME_STAMP_SERVICE_NAME))
+        map_binder_status_code(binder::get_interface(&default_time_stamp_service_name))
             .context("In connect_secureclock: Trying to connect to genuine secure clock service.")
     } else {
         // This is a no-op if it was called before.
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
index c10da95..e536e45 100644
--- a/keystore2/src/key_parameter.rs
+++ b/keystore2/src/key_parameter.rs
@@ -776,7 +776,7 @@
 
 implement_key_parameter_value! {
 /// KeyParameterValue holds a value corresponding to one of the Tags defined in
-/// the AIDL spec at hardware/interfaces/keymint
+/// the AIDL spec at hardware/interfaces/security/keymint
 #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
 pub enum KeyParameterValue {
     /// Associated with Tag:INVALID
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index 5d99449..e745697 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -14,18 +14,18 @@
 
 //! This crate implements the Keystore 2.0 service entry point.
 
-use keystore2::apc::ApcManager;
 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};
 use vpnprofilestore::VpnProfileStore;
 
-static KS2_SERVICE_NAME: &str = "android.system.keystore2";
+static KS2_SERVICE_NAME: &str = "android.system.keystore2.IKeystoreService/default";
 static APC_SERVICE_NAME: &str = "android.security.apc";
 static AUTHORIZATION_SERVICE_NAME: &str = "android.security.authorization";
 static REMOTE_PROVISIONING_SERVICE_NAME: &str = "android.security.remoteprovisioning";
@@ -76,6 +76,7 @@
     });
 
     entropy::register_feeder();
+    shared_secret_negotiation::perform_shared_secret_negotiation();
 
     info!("Starting thread pool now.");
     binder::ProcessState::start_thread_pool();
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index 26b099a..b824aa8 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -391,20 +391,13 @@
 // KeyMintDevice implementation
 
 ScopedAStatus KeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* _aidl_return) {
-    // TODO: What do I do about the version number?  Is it the version of the device I get?
-    auto result = mDevice->getHardwareInfo([&](auto securityLevel, const auto& keymasterName,
-                                               const auto& keymasterAuthorName) {
-        securityLevel_ =
-            static_cast<::aidl::android::hardware::security::keymint::SecurityLevel>(securityLevel);
-
-        _aidl_return->securityLevel = securityLevel_;
-        _aidl_return->keyMintName = keymasterName;
-        _aidl_return->keyMintAuthorName = keymasterAuthorName;
-    });
-    if (!result.isOk()) {
-        LOG(ERROR) << __func__ << " transaction failed. " << result.description();
-        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
-    }
+    auto result = mDevice->halVersion();
+    _aidl_return->versionNumber = result.majorVersion * 10 + result.minorVersion;
+    securityLevel_ = convert(result.securityLevel);
+    _aidl_return->securityLevel = securityLevel_;
+    _aidl_return->keyMintName = result.keymasterName;
+    _aidl_return->keyMintAuthorName = result.authorName;
+    _aidl_return->timestampTokenRequired = securityLevel_ == KMV1::SecurityLevel::STRONGBOX;
     return ScopedAStatus::ok();
 }
 
@@ -656,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);
@@ -1339,7 +1371,7 @@
 }
 
 ScopedAStatus KeystoreCompatService::getSecureClock(std::shared_ptr<ISecureClock>* _aidl_return) {
-    if (!mSharedSecret) {
+    if (!mSecureClock) {
         // The legacy verification service was always provided by the TEE variant.
         auto clock = SecureClock::createSecureClock(KeyMintSecurityLevel::TRUSTED_ENVIRONMENT);
         if (!clock) {
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/km_compat/km_compat_type_conversion.h b/keystore2/src/km_compat/km_compat_type_conversion.h
index c2b4669..e3240e9 100644
--- a/keystore2/src/km_compat/km_compat_type_conversion.h
+++ b/keystore2/src/km_compat/km_compat_type_conversion.h
@@ -665,13 +665,19 @@
         }
         break;
     case KMV1::Tag::ATTESTATION_ID_SERIAL:
-        // TODO This tag is missing from 4.0 keymaster_tags.h
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_SERIAL, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_ID_SERIAL, v->get());
+        }
         break;
     case KMV1::Tag::ATTESTATION_ID_IMEI:
-        // TODO This tag is missing from 4.0 keymaster_tags.h
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_IMEI, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_ID_IMEI, v->get());
+        }
         break;
     case KMV1::Tag::ATTESTATION_ID_MEID:
-        // TODO This tag is missing from 4.0 keymaster_tags.h
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_MEID, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_ID_MEID, v->get());
+        }
         break;
     case KMV1::Tag::ATTESTATION_ID_MANUFACTURER:
         if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_MANUFACTURER, kp)) {
@@ -971,13 +977,19 @@
         }
         break;
     case V4_0::Tag::ATTESTATION_ID_SERIAL:
-        // TODO This tag is missing from 4.0 keymaster_tags.h
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_ID_SERIAL, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_ID_SERIAL, v->get());
+        }
         break;
     case V4_0::Tag::ATTESTATION_ID_IMEI:
-        // TODO This tag is missing from 4.0 keymaster_tags.h
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_ID_IMEI, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_ID_IMEI, v->get());
+        }
         break;
     case V4_0::Tag::ATTESTATION_ID_MEID:
-        // TODO This tag is missing from 4.0 keymaster_tags.h
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_ID_MEID, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_ID_MEID, v->get());
+        }
         break;
     case V4_0::Tag::ATTESTATION_ID_MANUFACTURER:
         if (auto v =
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
index 6e27b5c..5ece8a7 100644
--- a/keystore2/src/km_compat/lib.rs
+++ b/keystore2/src/km_compat/lib.rs
@@ -12,13 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// TODO: Once this is stable, remove this and document everything public.
-#![allow(missing_docs)]
+//! Export into Rust a function to create a KeyMintDevice and add it as a service.
 
+#[allow(missing_docs)] // TODO remove this
 extern "C" {
     fn addKeyMintDeviceService() -> i32;
 }
 
+#[allow(missing_docs)] // TODO remove this
 pub fn add_keymint_device_service() -> i32 {
     unsafe { addKeyMintDeviceService() }
 }
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index 5f40ece..c108b32 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#![allow(dead_code)]
-
 //! This module implements methods to load legacy keystore key blob files.
 
 use crate::{
@@ -216,7 +214,7 @@
     // flags (1 Byte)
     // info (1 Byte)
     // initialization_vector (16 Bytes)
-    // integrity (MD5 digest or gcb tag) (16 Bytes)
+    // integrity (MD5 digest or gcm tag) (16 Bytes)
     // length (4 Bytes)
     const COMMON_HEADER_SIZE: usize = 4 + Self::IV_SIZE + Self::GCM_TAG_LENGTH + 4;
 
@@ -227,7 +225,7 @@
     const LENGTH_OFFSET: usize = 4 + Self::IV_SIZE + Self::GCM_TAG_LENGTH;
     const IV_OFFSET: usize = 4;
     const AEAD_TAG_OFFSET: usize = Self::IV_OFFSET + Self::IV_SIZE;
-    const DIGEST_OFFSET: usize = Self::IV_OFFSET + Self::IV_SIZE;
+    const _DIGEST_OFFSET: usize = Self::IV_OFFSET + Self::IV_SIZE;
 
     /// Construct a new LegacyBlobLoader with a root path of `path` relative to which it will
     /// expect legacy key blob files.
@@ -965,8 +963,7 @@
                             let decrypted = match key_manager
                                 .get_per_boot_key_by_user_id(uid_to_android_user(uid))
                             {
-                                Some(key) => aes_gcm_decrypt(data, iv, tag, &(key.get_key()))
-                                    .context(
+                                Some(key) => key.aes_gcm_decrypt(data, iv, tag).context(
                                     "In load_by_uid_alias: while trying to decrypt legacy blob.",
                                 )?,
                                 None => {
@@ -1043,16 +1040,22 @@
 
         let blob = match blob {
             Some(blob) => match blob {
-                Blob {
-                    value: BlobValue::PwEncrypted { iv, tag, data, salt, key_size }, ..
-                } => {
-                    let key = pw
-                        .derive_key(Some(&salt), key_size)
-                        .context("In load_super_key: Failed to derive key from password.")?;
-                    let blob = aes_gcm_decrypt(&data, &iv, &tag, &key).context(
-                        "In load_super_key: while trying to decrypt legacy super key blob.",
-                    )?;
-                    Some(blob)
+                Blob { flags, value: BlobValue::PwEncrypted { iv, tag, data, salt, key_size } } => {
+                    if (flags & flags::ENCRYPTED) != 0 {
+                        let key = pw
+                            .derive_key(Some(&salt), key_size)
+                            .context("In load_super_key: Failed to derive key from password.")?;
+                        let blob = aes_gcm_decrypt(&data, &iv, &tag, &key).context(
+                            "In load_super_key: while trying to decrypt legacy super key blob.",
+                        )?;
+                        Some(blob)
+                    } else {
+                        // In 2019 we had some unencrypted super keys due to b/141955555.
+                        Some(
+                            data.try_into()
+                                .context("In load_super_key: Trying to convert key into ZVec")?,
+                        )
+                    }
                 }
                 _ => {
                     return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(
@@ -1282,7 +1285,7 @@
             CACERT_NON_AUTHBOUND,
         )?;
 
-        let key_manager = crate::super_key::SuperKeyManager::new();
+        let key_manager: SuperKeyManager = Default::default();
         let mut db = crate::database::KeystoreDB::new(temp_dir.path(), None)?;
         let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
 
diff --git a/keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs b/keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs
index aa99162..14bd40c 100644
--- a/keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs
+++ b/keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs
@@ -741,7 +741,7 @@
     0xab, 0xae, 0x24, 0xe2, 0x44, 0x35, 0x16, 0x8d, 0x55, 0x3c, 0xe4,
 ];
 
-pub static DECRYPTED_USRPKEY_AUTHBOUND: &[u8] = &[
+pub static _DECRYPTED_USRPKEY_AUTHBOUND: &[u8] = &[
     0x44, 0x4b, 0x4d, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
     0xc6, 0x15, 0x3a, 0x08, 0x1e, 0x43, 0xba, 0x7a, 0x0f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
     0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/keystore2/src/legacy_migrator.rs b/keystore2/src/legacy_migrator.rs
index 7567070..e5bcae4 100644
--- a/keystore2/src/legacy_migrator.rs
+++ b/keystore2/src/legacy_migrator.rs
@@ -14,15 +14,18 @@
 
 //! This module acts as a bridge between the legacy key database and the keystore2 database.
 
-use crate::database::{
-    BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, EncryptedBy, KeyMetaData, KeyMetaEntry,
-    KeystoreDB, Uuid, KEYSTORE_UUID,
-};
 use crate::error::Error;
 use crate::key_parameter::KeyParameterValue;
 use crate::legacy_blob::BlobValue;
 use crate::utils::uid_to_android_user;
 use crate::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader};
+use crate::{
+    database::{
+        BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, EncryptedBy, KeyMetaData,
+        KeyMetaEntry, KeystoreDB, Uuid, KEYSTORE_UUID,
+    },
+    super_key::USER_SUPER_KEY,
+};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
 use android_system_keystore2::aidl::android::system::keystore2::{
     Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
@@ -450,7 +453,7 @@
 
                         let super_key_id = match self
                             .db
-                            .load_super_key(user_id)
+                            .load_super_key(&USER_SUPER_KEY, user_id)
                             .context("In check_and_migrate: Failed to load super key")?
                         {
                             Some((_, entry)) => entry.id(),
@@ -560,10 +563,18 @@
                 crate::super_key::SuperKeyManager::encrypt_with_password(&super_key, pw)
                     .context("In check_and_migrate_super_key: Trying to encrypt super key.")?;
 
-            self.db.store_super_key(user_id, &(&blob, &blob_metadata)).context(concat!(
-                "In check_and_migrate_super_key: ",
-                "Trying to insert legacy super_key into the database."
-            ))?;
+            self.db
+                .store_super_key(
+                    user_id,
+                    &USER_SUPER_KEY,
+                    &blob,
+                    &blob_metadata,
+                    &KeyMetaData::new(),
+                )
+                .context(concat!(
+                    "In check_and_migrate_super_key: ",
+                    "Trying to insert legacy super_key into the database."
+                ))?;
             self.legacy_loader.remove_super_key(user_id);
             self.recently_migrated_super_key.insert(user_id);
             Ok(())
@@ -602,7 +613,7 @@
 
         let super_key_id = self
             .db
-            .load_super_key(user_id)
+            .load_super_key(&USER_SUPER_KEY, user_id)
             .context("In bulk_delete: Failed to load super key")?
             .map(|(_, entry)| entry.id());
 
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 0e51eff..62dc16a 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -19,6 +19,7 @@
 pub mod async_task;
 pub mod authorization;
 pub mod database;
+pub mod ec_crypto;
 pub mod enforcements;
 pub mod entropy;
 pub mod error;
@@ -27,14 +28,18 @@
 pub mod key_parameter;
 pub mod legacy_blob;
 pub mod legacy_migrator;
+pub mod maintenance;
+pub mod metrics;
 pub mod operation;
 pub mod permission;
 pub mod remote_provisioning;
 pub mod security_level;
 pub mod service;
-pub mod user_manager;
+pub mod shared_secret_negotiation;
+pub mod try_insert;
 pub mod utils;
 
+mod attestation_key_utils;
 mod db_utils;
 mod gc;
 mod super_key;
diff --git a/keystore2/src/user_manager.rs b/keystore2/src/maintenance.rs
similarity index 68%
rename from keystore2/src/user_manager.rs
rename to keystore2/src/maintenance.rs
index 0cc2e92..e059a0b 100644
--- a/keystore2/src/user_manager.rs
+++ b/keystore2/src/maintenance.rs
@@ -14,12 +14,16 @@
 
 //! This module implements IKeystoreMaintenance AIDL interface.
 
-use crate::error::map_or_log_err;
+use crate::error::map_km_error;
 use crate::error::Error as KeystoreError;
+use crate::globals::get_keymint_device;
 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_hardware_security_keymint::aidl::android::hardware::security::keymint::IKeyMintDevice::IKeyMintDevice;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
 use android_security_maintenance::aidl::android::security::maintenance::{
     IKeystoreMaintenance::{BnKeystoreMaintenance, IKeystoreMaintenance},
     UserState::UserState as AidlUserState,
@@ -49,6 +53,13 @@
         check_keystore_permission(KeystorePerm::change_password())
             .context("In on_user_password_changed.")?;
 
+        if let Some(pw) = password.as_ref() {
+            DB.with(|db| {
+                SUPER_KEY.unlock_screen_lock_bound_key(&mut db.borrow_mut(), user_id as u32, pw)
+            })
+            .context("In on_user_password_changed: unlock_screen_lock_bound_key failed")?;
+        }
+
         match DB
             .with(|db| {
                 UserState::get_with_password_changed(
@@ -116,6 +127,45 @@
             UserState::LskfLocked => Ok(AidlUserState::LSKF_LOCKED),
         }
     }
+
+    fn early_boot_ended_help(sec_level: &SecurityLevel) -> Result<()> {
+        let (dev, _, _) =
+            get_keymint_device(sec_level).context("In early_boot_ended: getting keymint device")?;
+        let km_dev: Strong<dyn IKeyMintDevice> =
+            dev.get_interface().context("In early_boot_ended: getting keymint device interface")?;
+        map_km_error(km_dev.earlyBootEnded())
+            .context("In keymint device: calling earlyBootEnded")?;
+        Ok(())
+    }
+
+    fn early_boot_ended() -> Result<()> {
+        check_keystore_permission(KeystorePerm::early_boot_ended())
+            .context("In early_boot_ended. Checking permission")?;
+
+        let sec_levels = [
+            (SecurityLevel::TRUSTED_ENVIRONMENT, "TRUSTED_ENVIRONMENT"),
+            (SecurityLevel::STRONGBOX, "STRONGBOX"),
+        ];
+        sec_levels.iter().fold(Ok(()), |result, (sec_level, sec_level_string)| {
+            let curr_result = Maintenance::early_boot_ended_help(sec_level);
+            if curr_result.is_err() {
+                log::error!(
+                    "Call to earlyBootEnded failed for security level {}.",
+                    &sec_level_string
+                );
+            }
+            result.and(curr_result)
+        })
+    }
+
+    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 +187,15 @@
         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 earlyBootEnded(&self) -> BinderResult<()> {
+        map_or_log_err(Self::early_boot_ended(), Ok)
+    }
+
+    fn onDeviceOffBody(&self) -> BinderResult<()> {
+        map_or_log_err(Self::on_device_off_body(), Ok)
+    }
 }
diff --git a/keystore2/src/metrics.rs b/keystore2/src/metrics.rs
new file mode 100644
index 0000000..c5dd582
--- /dev/null
+++ b/keystore2/src/metrics.rs
@@ -0,0 +1,452 @@
+// Copyright 2021, 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.
+
+//! This module provides convenience functions for keystore2 logging.
+use crate::error::get_error_code;
+use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
+use crate::operation::Outcome;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+    HardwareAuthenticatorType::HardwareAuthenticatorType, KeyOrigin::KeyOrigin,
+    KeyParameter::KeyParameter, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+    SecurityLevel::SecurityLevel,
+};
+use statslog_rust::keystore2_key_creation_event_reported::{
+    Algorithm as StatsdAlgorithm, EcCurve as StatsdEcCurve, KeyOrigin as StatsdKeyOrigin,
+    Keystore2KeyCreationEventReported, SecurityLevel as StatsdKeyCreationSecurityLevel,
+    UserAuthType as StatsdUserAuthType,
+};
+use statslog_rust::keystore2_key_operation_event_reported::{
+    Keystore2KeyOperationEventReported, Outcome as StatsdOutcome, Purpose as StatsdKeyPurpose,
+    SecurityLevel as StatsdKeyOperationSecurityLevel,
+};
+
+fn create_default_key_creation_atom() -> Keystore2KeyCreationEventReported {
+    // If a value is not present, fields represented by bitmaps and i32 fields
+    // will take 0, except error_code which defaults to 1 indicating NO_ERROR and key_size,
+    // and auth_time_out which default to -1.
+    // The boolean fields are set to false by default.
+    // Some keymint enums do have 0 as an enum variant value. In such cases, the corresponding
+    // enum variant value in atoms.proto is incremented by 1, in order to have 0 as the reserved
+    // value for unspecified fields.
+    Keystore2KeyCreationEventReported {
+        algorithm: StatsdAlgorithm::AlgorithmUnspecified,
+        key_size: -1,
+        key_origin: StatsdKeyOrigin::OriginUnspecified,
+        user_auth_type: StatsdUserAuthType::AuthTypeUnspecified,
+        user_auth_key_timeout_seconds: -1,
+        padding_mode_bitmap: 0,
+        digest_bitmap: 0,
+        block_mode_bitmap: 0,
+        purpose_bitmap: 0,
+        ec_curve: StatsdEcCurve::EcCurveUnspecified,
+        // as per keystore2/ResponseCode.aidl, 1 is reserved for NO_ERROR
+        error_code: 1,
+        attestation_requested: false,
+        security_level: StatsdKeyCreationSecurityLevel::SecurityLevelUnspecified,
+    }
+}
+
+fn create_default_key_operation_atom() -> Keystore2KeyOperationEventReported {
+    Keystore2KeyOperationEventReported {
+        purpose: StatsdKeyPurpose::KeyPurposeUnspecified,
+        padding_mode_bitmap: 0,
+        digest_bitmap: 0,
+        block_mode_bitmap: 0,
+        outcome: StatsdOutcome::OutcomeUnspecified,
+        error_code: 1,
+        key_upgraded: false,
+        security_level: StatsdKeyOperationSecurityLevel::SecurityLevelUnspecified,
+    }
+}
+
+/// Log key creation events via statsd API.
+pub fn log_key_creation_event_stats<U>(
+    sec_level: SecurityLevel,
+    key_params: &[KeyParameter],
+    result: &anyhow::Result<U>,
+) {
+    let key_creation_event_stats =
+        construct_key_creation_event_stats(sec_level, key_params, result);
+
+    let logging_result = key_creation_event_stats.stats_write();
+
+    if let Err(e) = logging_result {
+        log::error!(
+            "In log_key_creation_event_stats. Error in logging key creation event. {:?}",
+            e
+        );
+    }
+}
+
+/// Log key operation events via statsd API.
+pub fn log_key_operation_event_stats(
+    sec_level: SecurityLevel,
+    key_purpose: KeyPurpose,
+    op_params: &[KeyParameter],
+    op_outcome: &Outcome,
+    key_upgraded: bool,
+) {
+    let key_operation_event_stats = construct_key_operation_event_stats(
+        sec_level,
+        key_purpose,
+        op_params,
+        op_outcome,
+        key_upgraded,
+    );
+
+    let logging_result = key_operation_event_stats.stats_write();
+
+    if let Err(e) = logging_result {
+        log::error!(
+            "In log_key_operation_event_stats. Error in logging key operation event. {:?}",
+            e
+        );
+    }
+}
+
+fn construct_key_creation_event_stats<U>(
+    sec_level: SecurityLevel,
+    key_params: &[KeyParameter],
+    result: &anyhow::Result<U>,
+) -> Keystore2KeyCreationEventReported {
+    let mut key_creation_event_atom = create_default_key_creation_atom();
+
+    if let Err(ref e) = result {
+        key_creation_event_atom.error_code = get_error_code(e);
+    }
+
+    key_creation_event_atom.security_level = match sec_level {
+        SecurityLevel::SOFTWARE => StatsdKeyCreationSecurityLevel::SecurityLevelSoftware,
+        SecurityLevel::TRUSTED_ENVIRONMENT => {
+            StatsdKeyCreationSecurityLevel::SecurityLevelTrustedEnvironment
+        }
+        SecurityLevel::STRONGBOX => StatsdKeyCreationSecurityLevel::SecurityLevelStrongbox,
+        //KEYSTORE is not a valid variant here
+        _ => StatsdKeyCreationSecurityLevel::SecurityLevelUnspecified,
+    };
+
+    for key_param in key_params.iter().map(KsKeyParamValue::from) {
+        match key_param {
+            KsKeyParamValue::Algorithm(a) => {
+                key_creation_event_atom.algorithm = match a {
+                    Algorithm::RSA => StatsdAlgorithm::Rsa,
+                    Algorithm::EC => StatsdAlgorithm::Ec,
+                    Algorithm::AES => StatsdAlgorithm::Aes,
+                    Algorithm::TRIPLE_DES => StatsdAlgorithm::TripleDes,
+                    Algorithm::HMAC => StatsdAlgorithm::Hmac,
+                    _ => StatsdAlgorithm::AlgorithmUnspecified,
+                }
+            }
+            KsKeyParamValue::KeySize(s) => {
+                key_creation_event_atom.key_size = s;
+            }
+            KsKeyParamValue::KeyOrigin(o) => {
+                key_creation_event_atom.key_origin = match o {
+                    KeyOrigin::GENERATED => StatsdKeyOrigin::Generated,
+                    KeyOrigin::DERIVED => StatsdKeyOrigin::Derived,
+                    KeyOrigin::IMPORTED => StatsdKeyOrigin::Imported,
+                    KeyOrigin::RESERVED => StatsdKeyOrigin::Reserved,
+                    KeyOrigin::SECURELY_IMPORTED => StatsdKeyOrigin::SecurelyImported,
+                    _ => StatsdKeyOrigin::OriginUnspecified,
+                }
+            }
+            KsKeyParamValue::HardwareAuthenticatorType(a) => {
+                key_creation_event_atom.user_auth_type = match a {
+                    HardwareAuthenticatorType::NONE => StatsdUserAuthType::None,
+                    HardwareAuthenticatorType::PASSWORD => StatsdUserAuthType::Password,
+                    HardwareAuthenticatorType::FINGERPRINT => StatsdUserAuthType::Fingerprint,
+                    HardwareAuthenticatorType::ANY => StatsdUserAuthType::Any,
+                    _ => StatsdUserAuthType::AuthTypeUnspecified,
+                }
+            }
+            KsKeyParamValue::AuthTimeout(t) => {
+                key_creation_event_atom.user_auth_key_timeout_seconds = t;
+            }
+            KsKeyParamValue::PaddingMode(p) => {
+                key_creation_event_atom.padding_mode_bitmap =
+                    compute_padding_mode_bitmap(&key_creation_event_atom.padding_mode_bitmap, p);
+            }
+            KsKeyParamValue::Digest(d) => {
+                key_creation_event_atom.digest_bitmap =
+                    compute_digest_bitmap(&key_creation_event_atom.digest_bitmap, d);
+            }
+            KsKeyParamValue::BlockMode(b) => {
+                key_creation_event_atom.block_mode_bitmap =
+                    compute_block_mode_bitmap(&key_creation_event_atom.block_mode_bitmap, b);
+            }
+            KsKeyParamValue::KeyPurpose(k) => {
+                key_creation_event_atom.purpose_bitmap =
+                    compute_purpose_bitmap(&key_creation_event_atom.purpose_bitmap, k);
+            }
+            KsKeyParamValue::EcCurve(e) => {
+                key_creation_event_atom.ec_curve = match e {
+                    EcCurve::P_224 => StatsdEcCurve::P224,
+                    EcCurve::P_256 => StatsdEcCurve::P256,
+                    EcCurve::P_384 => StatsdEcCurve::P384,
+                    EcCurve::P_521 => StatsdEcCurve::P521,
+                    _ => StatsdEcCurve::EcCurveUnspecified,
+                }
+            }
+            KsKeyParamValue::AttestationChallenge(_) => {
+                key_creation_event_atom.attestation_requested = true;
+            }
+            _ => {}
+        }
+    }
+    key_creation_event_atom
+}
+
+fn construct_key_operation_event_stats(
+    sec_level: SecurityLevel,
+    key_purpose: KeyPurpose,
+    op_params: &[KeyParameter],
+    op_outcome: &Outcome,
+    key_upgraded: bool,
+) -> Keystore2KeyOperationEventReported {
+    let mut key_operation_event_atom = create_default_key_operation_atom();
+
+    key_operation_event_atom.security_level = match sec_level {
+        SecurityLevel::SOFTWARE => StatsdKeyOperationSecurityLevel::SecurityLevelSoftware,
+        SecurityLevel::TRUSTED_ENVIRONMENT => {
+            StatsdKeyOperationSecurityLevel::SecurityLevelTrustedEnvironment
+        }
+        SecurityLevel::STRONGBOX => StatsdKeyOperationSecurityLevel::SecurityLevelStrongbox,
+        //KEYSTORE is not a valid variant here
+        _ => StatsdKeyOperationSecurityLevel::SecurityLevelUnspecified,
+    };
+
+    key_operation_event_atom.key_upgraded = key_upgraded;
+
+    key_operation_event_atom.purpose = match key_purpose {
+        KeyPurpose::ENCRYPT => StatsdKeyPurpose::Encrypt,
+        KeyPurpose::DECRYPT => StatsdKeyPurpose::Decrypt,
+        KeyPurpose::SIGN => StatsdKeyPurpose::Sign,
+        KeyPurpose::VERIFY => StatsdKeyPurpose::Verify,
+        KeyPurpose::WRAP_KEY => StatsdKeyPurpose::WrapKey,
+        KeyPurpose::AGREE_KEY => StatsdKeyPurpose::AgreeKey,
+        KeyPurpose::ATTEST_KEY => StatsdKeyPurpose::AttestKey,
+        _ => StatsdKeyPurpose::KeyPurposeUnspecified,
+    };
+
+    key_operation_event_atom.outcome = match op_outcome {
+        Outcome::Unknown | Outcome::Dropped => StatsdOutcome::Dropped,
+        Outcome::Success => StatsdOutcome::Success,
+        Outcome::Abort => StatsdOutcome::Abort,
+        Outcome::Pruned => StatsdOutcome::Pruned,
+        Outcome::ErrorCode(e) => {
+            key_operation_event_atom.error_code = e.0;
+            StatsdOutcome::Error
+        }
+    };
+
+    for key_param in op_params.iter().map(KsKeyParamValue::from) {
+        match key_param {
+            KsKeyParamValue::PaddingMode(p) => {
+                key_operation_event_atom.padding_mode_bitmap =
+                    compute_padding_mode_bitmap(&key_operation_event_atom.padding_mode_bitmap, p);
+            }
+            KsKeyParamValue::Digest(d) => {
+                key_operation_event_atom.digest_bitmap =
+                    compute_digest_bitmap(&key_operation_event_atom.digest_bitmap, d);
+            }
+            KsKeyParamValue::BlockMode(b) => {
+                key_operation_event_atom.block_mode_bitmap =
+                    compute_block_mode_bitmap(&key_operation_event_atom.block_mode_bitmap, b);
+            }
+            _ => {}
+        }
+    }
+
+    key_operation_event_atom
+}
+
+fn compute_purpose_bitmap(purpose_bitmap: &i32, purpose: KeyPurpose) -> i32 {
+    let mut bitmap = *purpose_bitmap;
+    match purpose {
+        KeyPurpose::ENCRYPT => {
+            bitmap |= 1 << KeyPurposeBitPosition::ENCRYPT_BIT_POS as i32;
+        }
+        KeyPurpose::DECRYPT => {
+            bitmap |= 1 << KeyPurposeBitPosition::DECRYPT_BIT_POS as i32;
+        }
+        KeyPurpose::SIGN => {
+            bitmap |= 1 << KeyPurposeBitPosition::SIGN_BIT_POS as i32;
+        }
+        KeyPurpose::VERIFY => {
+            bitmap |= 1 << KeyPurposeBitPosition::VERIFY_BIT_POS as i32;
+        }
+        KeyPurpose::WRAP_KEY => {
+            bitmap |= 1 << KeyPurposeBitPosition::WRAP_KEY_BIT_POS as i32;
+        }
+        KeyPurpose::AGREE_KEY => {
+            bitmap |= 1 << KeyPurposeBitPosition::AGREE_KEY_BIT_POS as i32;
+        }
+        KeyPurpose::ATTEST_KEY => {
+            bitmap |= 1 << KeyPurposeBitPosition::ATTEST_KEY_BIT_POS as i32;
+        }
+        _ => {}
+    }
+    bitmap
+}
+
+fn compute_padding_mode_bitmap(padding_mode_bitmap: &i32, padding_mode: PaddingMode) -> i32 {
+    let mut bitmap = *padding_mode_bitmap;
+    match padding_mode {
+        PaddingMode::NONE => {
+            bitmap |= 1 << PaddingModeBitPosition::NONE_BIT_POSITION as i32;
+        }
+        PaddingMode::RSA_OAEP => {
+            bitmap |= 1 << PaddingModeBitPosition::RSA_OAEP_BIT_POS as i32;
+        }
+        PaddingMode::RSA_PSS => {
+            bitmap |= 1 << PaddingModeBitPosition::RSA_PSS_BIT_POS as i32;
+        }
+        PaddingMode::RSA_PKCS1_1_5_ENCRYPT => {
+            bitmap |= 1 << PaddingModeBitPosition::RSA_PKCS1_1_5_ENCRYPT_BIT_POS as i32;
+        }
+        PaddingMode::RSA_PKCS1_1_5_SIGN => {
+            bitmap |= 1 << PaddingModeBitPosition::RSA_PKCS1_1_5_SIGN_BIT_POS as i32;
+        }
+        PaddingMode::PKCS7 => {
+            bitmap |= 1 << PaddingModeBitPosition::PKCS7_BIT_POS as i32;
+        }
+        _ => {}
+    }
+    bitmap
+}
+
+fn compute_digest_bitmap(digest_bitmap: &i32, digest: Digest) -> i32 {
+    let mut bitmap = *digest_bitmap;
+    match digest {
+        Digest::NONE => {
+            bitmap |= 1 << DigestBitPosition::NONE_BIT_POSITION as i32;
+        }
+        Digest::MD5 => {
+            bitmap |= 1 << DigestBitPosition::MD5_BIT_POS as i32;
+        }
+        Digest::SHA1 => {
+            bitmap |= 1 << DigestBitPosition::SHA_1_BIT_POS as i32;
+        }
+        Digest::SHA_2_224 => {
+            bitmap |= 1 << DigestBitPosition::SHA_2_224_BIT_POS as i32;
+        }
+        Digest::SHA_2_256 => {
+            bitmap |= 1 << DigestBitPosition::SHA_2_256_BIT_POS as i32;
+        }
+        Digest::SHA_2_384 => {
+            bitmap |= 1 << DigestBitPosition::SHA_2_384_BIT_POS as i32;
+        }
+        Digest::SHA_2_512 => {
+            bitmap |= 1 << DigestBitPosition::SHA_2_512_BIT_POS as i32;
+        }
+        _ => {}
+    }
+    bitmap
+}
+
+fn compute_block_mode_bitmap(block_mode_bitmap: &i32, block_mode: BlockMode) -> i32 {
+    let mut bitmap = *block_mode_bitmap;
+    match block_mode {
+        BlockMode::ECB => {
+            bitmap |= 1 << BlockModeBitPosition::ECB_BIT_POS as i32;
+        }
+        BlockMode::CBC => {
+            bitmap |= 1 << BlockModeBitPosition::CBC_BIT_POS as i32;
+        }
+        BlockMode::CTR => {
+            bitmap |= 1 << BlockModeBitPosition::CTR_BIT_POS as i32;
+        }
+        BlockMode::GCM => {
+            bitmap |= 1 << BlockModeBitPosition::GCM_BIT_POS as i32;
+        }
+        _ => {}
+    }
+    bitmap
+}
+/// Enum defining the bit position for each padding mode. Since padding mode can be repeatable, it
+/// is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+pub enum PaddingModeBitPosition {
+    ///Bit position in the PaddingMode bitmap for NONE.
+    NONE_BIT_POSITION = 0,
+    ///Bit position in the PaddingMode bitmap for RSA_OAEP.
+    RSA_OAEP_BIT_POS = 1,
+    ///Bit position in the PaddingMode bitmap for RSA_PSS.
+    RSA_PSS_BIT_POS = 2,
+    ///Bit position in the PaddingMode bitmap for RSA_PKCS1_1_5_ENCRYPT.
+    RSA_PKCS1_1_5_ENCRYPT_BIT_POS = 3,
+    ///Bit position in the PaddingMode bitmap for RSA_PKCS1_1_5_SIGN.
+    RSA_PKCS1_1_5_SIGN_BIT_POS = 4,
+    ///Bit position in the PaddingMode bitmap for RSA_PKCS7.
+    PKCS7_BIT_POS = 5,
+}
+
+/// Enum defining the bit position for each digest type. Since digest can be repeatable in
+/// key parameters, it is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+pub enum DigestBitPosition {
+    ///Bit position in the Digest bitmap for NONE.
+    NONE_BIT_POSITION = 0,
+    ///Bit position in the Digest bitmap for MD5.
+    MD5_BIT_POS = 1,
+    ///Bit position in the Digest bitmap for SHA1.
+    SHA_1_BIT_POS = 2,
+    ///Bit position in the Digest bitmap for SHA_2_224.
+    SHA_2_224_BIT_POS = 3,
+    ///Bit position in the Digest bitmap for SHA_2_256.
+    SHA_2_256_BIT_POS = 4,
+    ///Bit position in the Digest bitmap for SHA_2_384.
+    SHA_2_384_BIT_POS = 5,
+    ///Bit position in the Digest bitmap for SHA_2_512.
+    SHA_2_512_BIT_POS = 6,
+}
+
+/// Enum defining the bit position for each block mode type. Since block mode can be repeatable in
+/// key parameters, it is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+enum BlockModeBitPosition {
+    ///Bit position in the BlockMode bitmap for ECB.
+    ECB_BIT_POS = 1,
+    ///Bit position in the BlockMode bitmap for CBC.
+    CBC_BIT_POS = 2,
+    ///Bit position in the BlockMode bitmap for CTR.
+    CTR_BIT_POS = 3,
+    ///Bit position in the BlockMode bitmap for GCM.
+    GCM_BIT_POS = 4,
+}
+
+/// Enum defining the bit position for each key purpose. Since key purpose can be repeatable in
+/// key parameters, it is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+enum KeyPurposeBitPosition {
+    ///Bit position in the KeyPurpose bitmap for Encrypt.
+    ENCRYPT_BIT_POS = 1,
+    ///Bit position in the KeyPurpose bitmap for Decrypt.
+    DECRYPT_BIT_POS = 2,
+    ///Bit position in the KeyPurpose bitmap for Sign.
+    SIGN_BIT_POS = 3,
+    ///Bit position in the KeyPurpose bitmap for Verify.
+    VERIFY_BIT_POS = 4,
+    ///Bit position in the KeyPurpose bitmap for Wrap Key.
+    WRAP_KEY_BIT_POS = 5,
+    ///Bit position in the KeyPurpose bitmap for Agree Key.
+    AGREE_KEY_BIT_POS = 6,
+    ///Bit position in the KeyPurpose bitmap for Attest Key.
+    ATTEST_KEY_BIT_POS = 7,
+}
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
index 0f5bcaf..c2539a7 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -127,9 +127,11 @@
 
 use crate::enforcements::AuthInfo;
 use crate::error::{map_err_with, map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
+use crate::metrics::log_key_operation_event_stats;
 use crate::utils::Asp;
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    IKeyMintOperation::IKeyMintOperation,
+    IKeyMintOperation::IKeyMintOperation, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
+    SecurityLevel::SecurityLevel,
 };
 use android_system_keystore2::aidl::android::system::keystore2::{
     IKeystoreOperation::BnKeystoreOperation, IKeystoreOperation::IKeystoreOperation,
@@ -147,12 +149,18 @@
 /// to one of the other variants exactly once. The distinction in outcome is mainly
 /// for the statistic.
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
-enum Outcome {
+pub enum Outcome {
+    /// Operations have `Outcome::Unknown` as long as they are active.
     Unknown,
+    /// Operation is successful.
     Success,
+    /// Operation is aborted.
     Abort,
+    /// Operation is dropped.
     Dropped,
+    /// Operation is pruned.
     Pruned,
+    /// Operation is failed with the error code.
     ErrorCode(ErrorCode),
 }
 
@@ -168,6 +176,28 @@
     owner: u32, // Uid of the operation's owner.
     auth_info: Mutex<AuthInfo>,
     forced: bool,
+    logging_info: LoggingInfo,
+}
+
+/// Keeps track of the information required for logging operations.
+#[derive(Debug)]
+pub struct LoggingInfo {
+    sec_level: SecurityLevel,
+    purpose: KeyPurpose,
+    op_params: Vec<KeyParameter>,
+    key_upgraded: bool,
+}
+
+impl LoggingInfo {
+    /// Constructor
+    pub fn new(
+        sec_level: SecurityLevel,
+        purpose: KeyPurpose,
+        op_params: Vec<KeyParameter>,
+        key_upgraded: bool,
+    ) -> LoggingInfo {
+        Self { sec_level, purpose, op_params, key_upgraded }
+    }
 }
 
 struct PruningInfo {
@@ -188,6 +218,7 @@
         owner: u32,
         auth_info: AuthInfo,
         forced: bool,
+        logging_info: LoggingInfo,
     ) -> Self {
         Self {
             index,
@@ -197,6 +228,7 @@
             owner,
             auth_info: Mutex::new(auth_info),
             forced,
+            logging_info,
         }
     }
 
@@ -437,7 +469,16 @@
 
 impl Drop for Operation {
     fn drop(&mut self) {
-        if let Ok(Outcome::Unknown) = self.outcome.get_mut() {
+        let guard = self.outcome.lock().expect("In drop.");
+        log_key_operation_event_stats(
+            self.logging_info.sec_level,
+            self.logging_info.purpose,
+            &(self.logging_info.op_params),
+            &guard,
+            self.logging_info.key_upgraded,
+        );
+        if let Outcome::Unknown = *guard {
+            drop(guard);
             // If the operation was still active we call abort, setting
             // the outcome to `Outcome::Dropped`
             if let Err(e) = self.abort(Outcome::Dropped) {
@@ -471,6 +512,7 @@
         owner: u32,
         auth_info: AuthInfo,
         forced: bool,
+        logging_info: LoggingInfo,
     ) -> Arc<Operation> {
         // We use unwrap because we don't allow code that can panic while locked.
         let mut operations = self.operations.lock().expect("In create_operation.");
@@ -483,13 +525,26 @@
             s.upgrade().is_none()
         }) {
             Some(free_slot) => {
-                let new_op = Arc::new(Operation::new(index - 1, km_op, owner, auth_info, forced));
+                let new_op = Arc::new(Operation::new(
+                    index - 1,
+                    km_op,
+                    owner,
+                    auth_info,
+                    forced,
+                    logging_info,
+                ));
                 *free_slot = Arc::downgrade(&new_op);
                 new_op
             }
             None => {
-                let new_op =
-                    Arc::new(Operation::new(operations.len(), km_op, owner, auth_info, forced));
+                let new_op = Arc::new(Operation::new(
+                    operations.len(),
+                    km_op,
+                    owner,
+                    auth_info,
+                    forced,
+                    logging_info,
+                ));
                 operations.push(Arc::downgrade(&new_op));
                 new_op
             }
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index 7f63834..45c4dc1 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,10 @@
         ClearUID = 0x200,    selinux name: clear_uid;
         /// Checked when Credstore calls IKeystoreAuthorization to obtain auth tokens.
         GetAuthToken = 0x400,  selinux name: get_auth_token;
+        /// Checked when earlyBootEnded() is called.
+        EarlyBootEnded = 0x800,   selinux name: early_boot_ended;
+        /// Checked when IKeystoreMaintenance::onDeviceOffBody is called.
+        ReportOffBody = 0x1000, selinux name: report_off_body;
     }
 );
 
@@ -584,6 +589,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 +613,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/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index cc97573..1c757c9 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -23,9 +23,10 @@
 
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Algorithm::Algorithm, AttestationKey::AttestationKey, Certificate::Certificate,
-    IRemotelyProvisionedComponent::IRemotelyProvisionedComponent, KeyParameter::KeyParameter,
-    KeyParameterValue::KeyParameterValue, MacedPublicKey::MacedPublicKey,
-    ProtectedData::ProtectedData, SecurityLevel::SecurityLevel, Tag::Tag,
+    DeviceInfo::DeviceInfo, IRemotelyProvisionedComponent::IRemotelyProvisionedComponent,
+    KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue,
+    MacedPublicKey::MacedPublicKey, ProtectedData::ProtectedData, SecurityLevel::SecurityLevel,
+    Tag::Tag,
 };
 use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
     AttestationPoolStatus::AttestationPoolStatus, IRemoteProvisioning::BnRemoteProvisioning,
@@ -165,26 +166,26 @@
     ///
     /// It returns the ResponseCode `OUT_OF_KEYS` if there is not one key currently assigned to the
     /// `caller_uid` and there are none available to assign.
-    pub fn get_remote_provisioning_key_and_certs(
+    pub fn get_remotely_provisioned_attestation_key_and_certs(
         &self,
         key: &KeyDescriptor,
         caller_uid: u32,
         params: &[KeyParameter],
         db: &mut KeystoreDB,
-    ) -> Result<(Option<AttestationKey>, Option<Certificate>)> {
+    ) -> Result<Option<(AttestationKey, Certificate)>> {
         if !self.is_asymmetric_key(params) || !self.check_rem_prov_enabled(db)? {
             // There is no remote provisioning component for this security level on the
             // device. Return None so the underlying KM instance knows to use its
             // factory provisioned key instead. Alternatively, it's not an asymmetric key
             // and therefore will not be attested.
-            Ok((None, None))
+            Ok(None)
         } else {
             match self.get_rem_prov_attest_key(&key, caller_uid, db).context(concat!(
                 "In get_remote_provisioning_key_and_certs: Failed to get ",
                 "attestation key"
             ))? {
-                Some(cert_chain) => Ok((
-                    Some(AttestationKey {
+                Some(cert_chain) => Ok(Some((
+                    AttestationKey {
                         keyBlob: cert_chain.private_key.to_vec(),
                         attestKeyParams: vec![],
                         issuerSubjectName: parse_subject_from_certificate(&cert_chain.batch_cert)
@@ -192,10 +193,10 @@
                             "In get_remote_provisioning_key_and_certs: Failed to ",
                             "parse subject."
                         ))?,
-                    }),
-                    Some(Certificate { encodedCertificate: cert_chain.cert_chain }),
-                )),
-                None => Ok((None, None)),
+                    },
+                    Certificate { encodedCertificate: cert_chain.cert_chain },
+                ))),
+                None => Ok(None),
             }
         }
     }
@@ -261,6 +262,7 @@
     /// challenge will ensure freshness. A `test_mode` flag will instruct the remote provisioning
     /// HAL if it is okay to accept EEKs that aren't signed by something that chains back to the
     /// baked in root of trust in the underlying IRemotelyProvisionedComponent instance.
+    #[allow(clippy::too_many_arguments)]
     pub fn generate_csr(
         &self,
         test_mode: bool,
@@ -269,6 +271,7 @@
         challenge: &[u8],
         sec_level: SecurityLevel,
         protected_data: &mut ProtectedData,
+        device_info: &mut DeviceInfo,
     ) -> Result<Vec<u8>> {
         let dev = self.get_dev_by_sec_level(&sec_level)?;
         let (_, _, uuid) = get_keymint_device(&sec_level)?;
@@ -280,13 +283,12 @@
                 .map(|key| MacedPublicKey { macedKey: key.to_vec() })
                 .collect())
         })?;
-        let mut mac = Vec::<u8>::with_capacity(32);
-        map_rem_prov_error(dev.generateCertificateRequest(
+        let mut mac = map_rem_prov_error(dev.generateCertificateRequest(
             test_mode,
             &keys_to_sign,
             eek,
             challenge,
-            &mut mac,
+            device_info,
             protected_data,
         ))
         .context("In generate_csr: Failed to generate csr")?;
@@ -399,9 +401,18 @@
         challenge: &[u8],
         sec_level: SecurityLevel,
         protected_data: &mut ProtectedData,
+        device_info: &mut DeviceInfo,
     ) -> binder::public_api::Result<Vec<u8>> {
         map_or_log_err(
-            self.generate_csr(test_mode, num_csr, eek, challenge, sec_level, protected_data),
+            self.generate_csr(
+                test_mode,
+                num_csr,
+                eek,
+                challenge,
+                sec_level,
+                protected_data,
+                device_info,
+            ),
             Ok,
         )
     }
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index d7a0a12..50d697e 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -12,13 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#![allow(unused_variables)]
-
 //! This crate implements the IKeystoreSecurityLevel interface.
 
 use crate::globals::get_keymint_device;
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    Algorithm::Algorithm, AttestationKey::AttestationKey, Certificate::Certificate,
+    Algorithm::Algorithm, AttestationKey::AttestationKey,
     HardwareAuthenticatorType::HardwareAuthenticatorType, IKeyMintDevice::IKeyMintDevice,
     KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat,
     KeyMintHardwareInfo::KeyMintHardwareInfo, KeyParameter::KeyParameter,
@@ -32,10 +30,12 @@
     KeyMetadata::KeyMetadata, KeyParameters::KeyParameters,
 };
 
-use crate::database::{CertificateInfo, KeyIdGuard, KeystoreDB};
+use crate::attestation_key_utils::{get_attest_key_info, AttestationKeyInfo};
+use crate::database::{CertificateInfo, KeyIdGuard};
 use crate::globals::{DB, ENFORCEMENTS, LEGACY_MIGRATOR, SUPER_KEY};
 use crate::key_parameter::KeyParameter as KsKeyParam;
 use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
+use crate::metrics::log_key_creation_event_stats;
 use crate::remote_provisioning::RemProvState;
 use crate::super_key::{KeyBlob, SuperKeyManager};
 use crate::utils::{
@@ -48,6 +48,7 @@
         KeyMetaEntry, KeyType, SubComponentType, Uuid,
     },
     operation::KeystoreOperation,
+    operation::LoggingInfo,
     operation::OperationDb,
     permission::KeyPerm,
 };
@@ -57,13 +58,11 @@
 };
 use anyhow::{anyhow, Context, Result};
 use binder::{IBinderInternal, Strong, ThreadState};
-use keystore2_crypto::parse_subject_from_certificate;
 
 /// Implementation of the IKeystoreSecurityLevel Interface.
 pub struct KeystoreSecurityLevel {
     security_level: SecurityLevel,
     keymint: Asp,
-    #[allow(dead_code)]
     hw_info: KeyMintHardwareInfo,
     km_uuid: Uuid,
     operation_db: OperationDb,
@@ -300,7 +299,8 @@
             .upgrade_keyblob_if_required_with(
                 &*km_dev,
                 key_id_guard,
-                &(&km_blob, &blob_metadata),
+                &km_blob,
+                &blob_metadata,
                 &operation_parameters,
                 |blob| loop {
                     match map_km_error(km_dev.begin(
@@ -321,9 +321,13 @@
 
         let operation_challenge = auth_info.finalize_create_authorization(begin_result.challenge);
 
+        let op_params: Vec<KeyParameter> = operation_parameters.to_vec();
+
         let operation = match begin_result.operation {
             Some(km_op) => {
-                self.operation_db.create_operation(km_op, caller_uid, auth_info, forced)
+                self.operation_db.create_operation(km_op, caller_uid, auth_info, forced,
+                    LoggingInfo::new(self.security_level, purpose, op_params,
+                         upgraded_blob.is_some()))
             },
             None => return Err(Error::sys()).context("In create_operation: Begin operation returned successfully, but did not return a valid operation."),
         };
@@ -368,7 +372,7 @@
         if params.iter().any(|kp| kp.tag == Tag::INCLUDE_UNIQUE_ID) {
             check_key_permission(KeyPerm::gen_unique_id(), key, &None).context(concat!(
                 "In add_certificate_parameters: ",
-                "Caller does not have the permission for device unique attestation."
+                "Caller does not have the permission to generate a unique ID"
             ))?;
         }
 
@@ -410,7 +414,7 @@
         attest_key_descriptor: Option<&KeyDescriptor>,
         params: &[KeyParameter],
         flags: i32,
-        entropy: &[u8],
+        _entropy: &[u8],
     ) -> Result<KeyMetadata> {
         if key.domain != Domain::BLOB && key.alias.is_none() {
             return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
@@ -429,16 +433,19 @@
         };
 
         // generate_key requires the rebind permission.
+        // Must return on error for security reasons.
         check_key_permission(KeyPerm::rebind(), &key, &None).context("In generate_key.")?;
-        let (attest_key, cert_chain) = match (key.domain, attest_key_descriptor) {
-            (Domain::BLOB, None) => (None, None),
+
+        let attestation_key_info = match (key.domain, attest_key_descriptor) {
+            (Domain::BLOB, _) => None,
             _ => DB
-                .with::<_, Result<(Option<AttestationKey>, Option<Certificate>)>>(|db| {
-                    self.get_attest_key_and_cert_chain(
+                .with(|db| {
+                    get_attest_key_info(
                         &key,
                         caller_uid,
                         attest_key_descriptor,
                         params,
+                        &self.rem_prov_state,
                         &mut db.borrow_mut(),
                     )
                 })
@@ -448,96 +455,52 @@
             .context("In generate_key: Trying to get aaid.")?;
 
         let km_dev: Strong<dyn IKeyMintDevice> = self.keymint.get_interface()?;
-        map_km_error(km_dev.addRngEntropy(entropy))
-            .context("In generate_key: Trying to add entropy.")?;
-        let mut creation_result = map_km_error(km_dev.generateKey(&params, attest_key.as_ref()))
-            .context("In generate_key: While generating Key")?;
-        // The certificate chain ultimately gets flattened into a big DER encoded byte array,
-        // so providing that blob upfront in a single certificate entry should be fine.
-        if let Some(cert) = cert_chain {
-            creation_result.certificateChain.push(cert);
+
+        let creation_result = match attestation_key_info {
+            Some(AttestationKeyInfo::UserGenerated {
+                key_id_guard,
+                blob,
+                blob_metadata,
+                issuer_subject,
+            }) => self
+                .upgrade_keyblob_if_required_with(
+                    &*km_dev,
+                    Some(key_id_guard),
+                    &KeyBlob::Ref(&blob),
+                    &blob_metadata,
+                    &params,
+                    |blob| {
+                        let attest_key = Some(AttestationKey {
+                            keyBlob: blob.to_vec(),
+                            attestKeyParams: vec![],
+                            issuerSubjectName: issuer_subject.clone(),
+                        });
+                        map_km_error(km_dev.generateKey(&params, attest_key.as_ref()))
+                    },
+                )
+                .context("In generate_key: Using user generated attestation key.")
+                .map(|(result, _)| result),
+            Some(AttestationKeyInfo::RemoteProvisioned { attestation_key, attestation_certs }) => {
+                map_km_error(km_dev.generateKey(&params, Some(&attestation_key)))
+                    .context("While generating Key with remote provisioned attestation key.")
+                    .map(|mut creation_result| {
+                        creation_result.certificateChain.push(attestation_certs);
+                        creation_result
+                    })
+            }
+            None => map_km_error(km_dev.generateKey(&params, None))
+                .context("While generating Key without explicit attestation key."),
         }
+        .context("In generate_key.")?;
+
         let user_id = uid_to_android_user(caller_uid);
         self.store_new_key(key, creation_result, user_id, Some(flags)).context("In generate_key.")
     }
 
-    fn get_attest_key_and_cert_chain(
-        &self,
-        key: &KeyDescriptor,
-        caller_uid: u32,
-        attest_key_descriptor: Option<&KeyDescriptor>,
-        params: &[KeyParameter],
-        db: &mut KeystoreDB,
-    ) -> Result<(Option<AttestationKey>, Option<Certificate>)> {
-        match attest_key_descriptor {
-            None => self
-                .rem_prov_state
-                .get_remote_provisioning_key_and_certs(&key, caller_uid, params, db),
-            Some(attest_key) => Ok((
-                Some(
-                    self.get_attest_key(&attest_key, caller_uid)
-                        .context("In generate_key: Trying to load attest key")?,
-                ),
-                None,
-            )),
-        }
-    }
-
-    fn get_attest_key(&self, key: &KeyDescriptor, caller_uid: u32) -> Result<AttestationKey> {
-        let (km_blob, cert) = self
-            .load_attest_key_blob_and_cert(&key, caller_uid)
-            .context("In get_attest_key: Failed to load blob and cert")?;
-
-        let issuer_subject: Vec<u8> = parse_subject_from_certificate(&cert)
-            .context("In get_attest_key: Failed to parse subject from certificate.")?;
-
-        Ok(AttestationKey {
-            keyBlob: km_blob.to_vec(),
-            attestKeyParams: [].to_vec(),
-            issuerSubjectName: issuer_subject,
-        })
-    }
-
-    fn load_attest_key_blob_and_cert(
-        &self,
-        key: &KeyDescriptor,
-        caller_uid: u32,
-    ) -> Result<(Vec<u8>, Vec<u8>)> {
-        match key.domain {
-            Domain::BLOB => Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context(
-                "In load_attest_key_blob_and_cert: Domain::BLOB attestation keys not supported",
-            ),
-            _ => {
-                let (key_id_guard, mut key_entry) = DB
-                    .with::<_, Result<(KeyIdGuard, KeyEntry)>>(|db| {
-                        db.borrow_mut().load_key_entry(
-                            &key,
-                            KeyType::Client,
-                            KeyEntryLoadBits::BOTH,
-                            caller_uid,
-                            |k, av| check_key_permission(KeyPerm::use_(), k, &av),
-                        )
-                    })
-                    .context("In load_attest_key_blob_and_cert: Failed to load key.")?;
-
-                let (blob, _) =
-                    key_entry.take_key_blob_info().ok_or_else(Error::sys).context(concat!(
-                        "In load_attest_key_blob_and_cert: Successfully loaded key entry,",
-                        " but KM blob was missing."
-                    ))?;
-                let cert = key_entry.take_cert().ok_or_else(Error::sys).context(concat!(
-                    "In load_attest_key_blob_and_cert: Successfully loaded key entry,",
-                    " but cert was missing."
-                ))?;
-                Ok((blob, cert))
-            }
-        }
-    }
-
     fn import_key(
         &self,
         key: &KeyDescriptor,
-        attestation_key: Option<&KeyDescriptor>,
+        _attestation_key: Option<&KeyDescriptor>,
         params: &[KeyParameter],
         flags: i32,
         key_data: &[u8],
@@ -693,7 +656,8 @@
             .upgrade_keyblob_if_required_with(
                 &*km_dev,
                 Some(wrapping_key_id_guard),
-                &(&wrapping_key_blob, &wrapping_blob_metadata),
+                &wrapping_key_blob,
+                &wrapping_blob_metadata,
                 &[],
                 |wrapping_blob| {
                     let creation_result = map_km_error(km_dev.importWrappedKey(
@@ -713,48 +677,62 @@
             .context("In import_wrapped_key: Trying to store the new key.")
     }
 
+    fn store_upgraded_keyblob(
+        key_id_guard: KeyIdGuard,
+        km_uuid: Option<&Uuid>,
+        key_blob: &KeyBlob,
+        upgraded_blob: &[u8],
+    ) -> Result<()> {
+        let (upgraded_blob_to_be_stored, new_blob_metadata) =
+            SuperKeyManager::reencrypt_if_required(key_blob, &upgraded_blob)
+                .context("In store_upgraded_keyblob: Failed to handle super encryption.")?;
+
+        let mut new_blob_metadata = new_blob_metadata.unwrap_or_else(BlobMetaData::new);
+        if let Some(uuid) = km_uuid {
+            new_blob_metadata.add(BlobMetaEntry::KmUuid(*uuid));
+        }
+
+        DB.with(|db| {
+            let mut db = db.borrow_mut();
+            db.set_blob(
+                &key_id_guard,
+                SubComponentType::KEY_BLOB,
+                Some(&upgraded_blob_to_be_stored),
+                Some(&new_blob_metadata),
+            )
+        })
+        .context("In store_upgraded_keyblob: Failed to insert upgraded blob into the database.")
+    }
+
     fn upgrade_keyblob_if_required_with<T, F>(
         &self,
         km_dev: &dyn IKeyMintDevice,
         key_id_guard: Option<KeyIdGuard>,
-        blob_info: &(&KeyBlob, &BlobMetaData),
+        key_blob: &KeyBlob,
+        blob_metadata: &BlobMetaData,
         params: &[KeyParameter],
         f: F,
     ) -> Result<(T, Option<Vec<u8>>)>
     where
         F: Fn(&[u8]) -> Result<T, Error>,
     {
-        match f(blob_info.0) {
+        match f(key_blob) {
             Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
-                let upgraded_blob = map_km_error(km_dev.upgradeKey(blob_info.0, params))
+                let upgraded_blob = map_km_error(km_dev.upgradeKey(key_blob, params))
                     .context("In upgrade_keyblob_if_required_with: Upgrade failed.")?;
 
-                let (upgraded_blob_to_be_stored, blob_metadata) =
-                    SuperKeyManager::reencrypt_on_upgrade_if_required(blob_info.0, &upgraded_blob)
-                        .context(
-                        "In upgrade_keyblob_if_required_with: Failed to handle super encryption.",
+                if let Some(kid) = key_id_guard {
+                    Self::store_upgraded_keyblob(
+                        kid,
+                        blob_metadata.km_uuid(),
+                        key_blob,
+                        &upgraded_blob,
+                    )
+                    .context(
+                        "In upgrade_keyblob_if_required_with: store_upgraded_keyblob failed",
                     )?;
-
-                let mut blob_metadata = blob_metadata.unwrap_or_else(BlobMetaData::new);
-                if let Some(uuid) = blob_info.1.km_uuid() {
-                    blob_metadata.add(BlobMetaEntry::KmUuid(*uuid));
                 }
 
-                key_id_guard.map_or(Ok(()), |key_id_guard| {
-                    DB.with(|db| {
-                        let mut db = db.borrow_mut();
-                        db.set_blob(
-                            &key_id_guard,
-                            SubComponentType::KEY_BLOB,
-                            Some(&upgraded_blob_to_be_stored),
-                            Some(&blob_metadata),
-                        )
-                    })
-                    .context(concat!(
-                        "In upgrade_keyblob_if_required_with: ",
-                        "Failed to insert upgraded blob into the database.",
-                    ))
-                })?;
                 match f(&upgraded_blob) {
                     Ok(v) => Ok((v, Some(upgraded_blob))),
                     Err(e) => Err(e).context(concat!(
@@ -763,12 +741,76 @@
                     )),
                 }
             }
-            Err(e) => {
-                Err(e).context("In upgrade_keyblob_if_required_with: Failed perform operation.")
+            result => {
+                if let Some(kid) = key_id_guard {
+                    if key_blob.force_reencrypt() {
+                        Self::store_upgraded_keyblob(
+                            kid,
+                            blob_metadata.km_uuid(),
+                            key_blob,
+                            key_blob,
+                        )
+                        .context(concat!(
+                            "In upgrade_keyblob_if_required_with: ",
+                            "store_upgraded_keyblob failed in forced reencrypt"
+                        ))?;
+                    }
+                }
+                result
+                    .map(|v| (v, None))
+                    .context("In upgrade_keyblob_if_required_with: Called closure failed.")
             }
-            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 {}
@@ -790,7 +832,9 @@
         flags: i32,
         entropy: &[u8],
     ) -> binder::public_api::Result<KeyMetadata> {
-        map_or_log_err(self.generate_key(key, attestation_key, params, flags, entropy), Ok)
+        let result = self.generate_key(key, attestation_key, params, flags, entropy);
+        log_key_creation_event_stats(self.security_level, params, &result);
+        map_or_log_err(result, Ok)
     }
     fn importKey(
         &self,
@@ -800,7 +844,9 @@
         flags: i32,
         key_data: &[u8],
     ) -> binder::public_api::Result<KeyMetadata> {
-        map_or_log_err(self.import_key(key, attestation_key, params, flags, key_data), Ok)
+        let result = self.import_key(key, attestation_key, params, flags, key_data);
+        log_key_creation_event_stats(self.security_level, params, &result);
+        map_or_log_err(result, Ok)
     }
     fn importWrappedKey(
         &self,
@@ -810,9 +856,18 @@
         params: &[KeyParameter],
         authenticators: &[AuthenticatorSpec],
     ) -> binder::public_api::Result<KeyMetadata> {
-        map_or_log_err(
-            self.import_wrapped_key(key, wrapping_key, masking_key, params, authenticators),
-            Ok,
-        )
+        let result =
+            self.import_wrapped_key(key, wrapping_key, masking_key, params, authenticators);
+        log_key_creation_event_stats(self.security_level, params, &result);
+        map_or_log_err(result, 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)
     }
 }
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index 15b729d..73bd526 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -12,9 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// TODO remove when fully implemented.
-#![allow(unused_variables)]
-
 //! This crate implement the core Keystore 2.0 service API as defined by the Keystore 2.0
 //! AIDL spec.
 
@@ -199,7 +196,7 @@
             .context("Failed to load key entry.")?;
 
             let mut db = db.borrow_mut();
-            if let Some((key_id_guard, key_entry)) = entry {
+            if let Some((key_id_guard, _key_entry)) = entry {
                 db.set_blob(&key_id_guard, SubComponentType::CERT, public_cert, None)
                     .context("Failed to update cert subcomponent.")?;
 
diff --git a/keystore2/src/shared_secret_negotiation.rs b/keystore2/src/shared_secret_negotiation.rs
new file mode 100644
index 0000000..fb55f33
--- /dev/null
+++ b/keystore2/src/shared_secret_negotiation.rs
@@ -0,0 +1,265 @@
+// Copyright 2021, 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.
+
+//! This module implements the shared secret negotiation.
+
+use crate::error::{map_binder_status, map_binder_status_code, Error};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use android_hardware_security_keymint::binder::Strong;
+use android_hardware_security_sharedsecret::aidl::android::hardware::security::sharedsecret::{
+    ISharedSecret::ISharedSecret, SharedSecretParameters::SharedSecretParameters,
+};
+use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
+use anyhow::{Context, Result};
+use keystore2_vintf::{get_aidl_instances, get_hidl_instances};
+use std::fmt::{self, Display, Formatter};
+
+/// This function initiates the shared secret negotiation. It starts a thread and then returns
+/// immediately. The thread consults the vintf manifest to enumerate expected negotiation
+/// participants. It then attempts to connect to all of these participants. If any connection
+/// fails the thread will retry once per second to connect to the failed instance(s) until all of
+/// the instances are connected. It then performs the negotiation.
+///
+/// During the first phase of the negotiation it will again try every second until
+/// all instances have responded successfully to account for instances that register early but
+/// are not fully functioning at this time due to hardware delays or boot order dependency issues.
+/// An error during the second phase or a checksum mismatch leads to a panic.
+pub fn perform_shared_secret_negotiation() {
+    std::thread::spawn(|| {
+        let participants = list_participants()
+            .expect("In perform_shared_secret_negotiation: Trying to list participants.");
+        let connected = connect_participants(participants);
+        negotiate_shared_secret(connected);
+        log::info!("Shared secret negotiation concluded successfully.");
+    });
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+enum SharedSecretParticipant {
+    /// Represents an instance of android.hardware.security.sharedsecret.ISharedSecret.
+    Aidl(String),
+    /// In the legacy case there can be at most one TEE and one Strongbox hal.
+    Hidl { is_strongbox: bool, version: (usize, usize) },
+}
+
+impl Display for SharedSecretParticipant {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        match self {
+            Self::Aidl(instance) => write!(
+                f,
+                "{}.{}/{}",
+                SHARED_SECRET_PACKAGE_NAME, SHARED_SECRET_INTERFACE_NAME, instance
+            ),
+            Self::Hidl { is_strongbox, version: (ma, mi) } => write!(
+                f,
+                "{}@V{}.{}::{}/{}",
+                KEYMASTER_PACKAGE_NAME,
+                ma,
+                mi,
+                KEYMASTER_INTERFACE_NAME,
+                if *is_strongbox { "strongbox" } else { "default" }
+            ),
+        }
+    }
+}
+
+#[derive(thiserror::Error, Debug)]
+enum SharedSecretError {
+    #[error("Shared parameter retrieval failed on instance {p} with error {e:?}.")]
+    ParameterRetrieval { e: Error, p: SharedSecretParticipant },
+    #[error("Shared secret computation failed on instance {p} with error {e:?}.")]
+    Computation { e: Error, p: SharedSecretParticipant },
+    #[error("Checksum comparison failed on instance {0}.")]
+    Checksum(SharedSecretParticipant),
+}
+
+fn filter_map_legacy_km_instances(
+    name: String,
+    version: (usize, usize),
+) -> Option<SharedSecretParticipant> {
+    match name.as_str() {
+        "default" => Some(SharedSecretParticipant::Hidl { is_strongbox: false, version }),
+        "strongbox" => Some(SharedSecretParticipant::Hidl { is_strongbox: true, version }),
+        _ => {
+            log::warn!("Found unexpected keymaster instance: \"{}\"", name);
+            log::warn!("Device is misconfigured. Allowed instances are:");
+            log::warn!("   * default");
+            log::warn!("   * strongbox");
+            None
+        }
+    }
+}
+
+static KEYMASTER_PACKAGE_NAME: &str = "android.hardware.keymaster";
+static KEYMASTER_INTERFACE_NAME: &str = "IKeymasterDevice";
+static SHARED_SECRET_PACKAGE_NAME: &str = "android.hardware.security.sharedsecret";
+static SHARED_SECRET_INTERFACE_NAME: &str = "ISharedSecret";
+static COMPAT_PACKAGE_NAME: &str = "android.security.compat";
+
+/// Lists participants.
+fn list_participants() -> Result<Vec<SharedSecretParticipant>> {
+    Ok([(4, 0), (4, 1)]
+        .iter()
+        .map(|(ma, mi)| {
+            get_hidl_instances(KEYMASTER_PACKAGE_NAME, *ma, *mi, KEYMASTER_INTERFACE_NAME)
+                .as_vec()
+                .with_context(|| format!("Trying to convert KM{}.{} names to vector.", *ma, *mi))
+                .map(|instances| {
+                    instances
+                        .into_iter()
+                        .filter_map(|name| {
+                            filter_map_legacy_km_instances(name.to_string(), (*ma, *mi))
+                        })
+                        .collect::<Vec<SharedSecretParticipant>>()
+                })
+        })
+        .collect::<Result<Vec<_>>>()
+        .map(|v| v.into_iter().flatten())
+        .and_then(|i| {
+            let participants_aidl: Vec<SharedSecretParticipant> =
+                get_aidl_instances(SHARED_SECRET_PACKAGE_NAME, 1, SHARED_SECRET_INTERFACE_NAME)
+                    .as_vec()
+                    .context("In list_participants: Trying to convert KM1.0 names to vector.")?
+                    .into_iter()
+                    .map(|name| SharedSecretParticipant::Aidl(name.to_string()))
+                    .collect();
+            Ok(i.chain(participants_aidl.into_iter()))
+        })
+        .context("In list_participants.")?
+        .collect())
+}
+
+fn connect_participants(
+    mut participants: Vec<SharedSecretParticipant>,
+) -> Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> {
+    let mut connected_participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> =
+        vec![];
+    loop {
+        let (connected, not_connected) = participants.into_iter().fold(
+            (connected_participants, vec![]),
+            |(mut connected, mut failed), e| {
+                match e {
+                    SharedSecretParticipant::Aidl(instance_name) => {
+                        let service_name = format!(
+                            "{}.{}/{}",
+                            SHARED_SECRET_PACKAGE_NAME, SHARED_SECRET_INTERFACE_NAME, instance_name
+                        );
+                        match map_binder_status_code(binder::get_interface(&service_name)) {
+                            Err(e) => {
+                                log::warn!(
+                                    "Unable to connect \"{}\" with error:\n{:?}\nRetrying later.",
+                                    service_name,
+                                    e
+                                );
+                                failed.push(SharedSecretParticipant::Aidl(instance_name));
+                            }
+                            Ok(service) => connected
+                                .push((service, SharedSecretParticipant::Aidl(instance_name))),
+                        }
+                    }
+                    SharedSecretParticipant::Hidl { is_strongbox, version } => {
+                        // This is a no-op if it was called before.
+                        keystore2_km_compat::add_keymint_device_service();
+
+                        // If we cannot connect to the compatibility service there is no way to
+                        // recover.
+                        // PANIC! - Unless you brought your towel.
+                        let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
+                            map_binder_status_code(binder::get_interface(COMPAT_PACKAGE_NAME))
+                                .expect(
+                                    "In connect_participants: Trying to connect to compat service.",
+                                );
+
+                        match map_binder_status(keystore_compat_service.getSharedSecret(
+                            if is_strongbox {
+                                SecurityLevel::STRONGBOX
+                            } else {
+                                SecurityLevel::TRUSTED_ENVIRONMENT
+                            },
+                        )) {
+                            Err(e) => {
+                                log::warn!(
+                                    concat!(
+                                        "Unable to connect keymaster device \"{}\" ",
+                                        "with error:\n{:?}\nRetrying later."
+                                    ),
+                                    if is_strongbox { "strongbox" } else { "TEE" },
+                                    e
+                                );
+                                failed
+                                    .push(SharedSecretParticipant::Hidl { is_strongbox, version });
+                            }
+                            Ok(service) => connected.push((
+                                service,
+                                SharedSecretParticipant::Hidl { is_strongbox, version },
+                            )),
+                        }
+                    }
+                }
+                (connected, failed)
+            },
+        );
+        participants = not_connected;
+        connected_participants = connected;
+        if participants.is_empty() {
+            break;
+        }
+        std::thread::sleep(std::time::Duration::from_millis(1000));
+    }
+    connected_participants
+}
+
+fn negotiate_shared_secret(
+    participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)>,
+) {
+    // Phase 1: Get the sharing parameters from all participants.
+    let mut params = loop {
+        let result: Result<Vec<SharedSecretParameters>, SharedSecretError> = participants
+            .iter()
+            .map(|(s, p)| {
+                map_binder_status(s.getSharedSecretParameters())
+                    .map_err(|e| SharedSecretError::ParameterRetrieval { e, p: (*p).clone() })
+            })
+            .collect();
+
+        match result {
+            Err(e) => {
+                log::warn!("{:?}", e);
+                log::warn!("Retrying in one second.");
+                std::thread::sleep(std::time::Duration::from_millis(1000));
+            }
+            Ok(params) => break params,
+        }
+    };
+
+    params.sort_unstable();
+
+    // Phase 2: Send the sorted sharing parameters to all participants.
+    participants
+        .into_iter()
+        .try_fold(None, |acc, (s, p)| {
+            match (acc, map_binder_status(s.computeSharedSecret(&params))) {
+                (None, Ok(new_sum)) => Ok(Some(new_sum)),
+                (Some(old_sum), Ok(new_sum)) => {
+                    if old_sum == new_sum {
+                        Ok(Some(old_sum))
+                    } else {
+                        Err(SharedSecretError::Checksum(p))
+                    }
+                }
+                (_, Err(e)) => Err(SharedSecretError::Computation { e, p }),
+            }
+        })
+        .expect("Fatal: Shared secret computation failed.");
+}
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index aa434d6..3fa4cf0 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -12,13 +12,21 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#![allow(dead_code)]
-
 use crate::{
-    database::BlobMetaData, database::BlobMetaEntry, database::EncryptedBy, database::KeyEntry,
-    database::KeyType, database::KeystoreDB, enforcements::Enforcements, error::Error,
-    error::ResponseCode, key_parameter::KeyParameter, legacy_blob::LegacyBlobLoader,
+    database::BlobMetaData,
+    database::BlobMetaEntry,
+    database::EncryptedBy,
+    database::KeyEntry,
+    database::KeyType,
+    database::{KeyMetaData, KeyMetaEntry, KeystoreDB},
+    ec_crypto::ECDHPrivateKey,
+    enforcements::Enforcements,
+    error::Error,
+    error::ResponseCode,
+    key_parameter::KeyParameter,
+    legacy_blob::LegacyBlobLoader,
     legacy_migrator::LegacyMigrator,
+    try_insert::TryInsert,
 };
 use android_system_keystore2::aidl::android::system::keystore2::Domain::Domain;
 use anyhow::{Context, Result};
@@ -35,6 +43,79 @@
 
 type UserId = u32;
 
+/// Encryption algorithm used by a particular type of superencryption key
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum SuperEncryptionAlgorithm {
+    /// Symmetric encryption with AES-256-GCM
+    Aes256Gcm,
+    /// Public-key encryption with ECDH P-256
+    EcdhP256,
+}
+
+/// A particular user may have several superencryption keys in the database, each for a
+/// different purpose, distinguished by alias. Each is associated with a static
+/// constant of this type.
+pub struct SuperKeyType {
+    /// Alias used to look the key up in the `persistent.keyentry` table.
+    pub alias: &'static str,
+    /// Encryption algorithm
+    pub algorithm: SuperEncryptionAlgorithm,
+}
+
+/// Key used for LskfLocked keys; the corresponding superencryption key is loaded in memory
+/// when the user first unlocks, and remains in memory until the device reboots.
+pub const USER_SUPER_KEY: SuperKeyType =
+    SuperKeyType { alias: "USER_SUPER_KEY", algorithm: SuperEncryptionAlgorithm::Aes256Gcm };
+/// Key used for ScreenLockBound keys; the corresponding superencryption key is loaded in memory
+/// each time the user enters their LSKF, and cleared from memory each time the device is locked.
+/// Symmetric.
+pub const USER_SCREEN_LOCK_BOUND_KEY: SuperKeyType = SuperKeyType {
+    alias: "USER_SCREEN_LOCK_BOUND_KEY",
+    algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+};
+/// Key used for ScreenLockBound keys; the corresponding superencryption key is loaded in memory
+/// each time the user enters their LSKF, and cleared from memory each time the device is locked.
+/// Asymmetric, so keys can be encrypted when the device is locked.
+pub const USER_SCREEN_LOCK_BOUND_ECDH_KEY: SuperKeyType = SuperKeyType {
+    alias: "USER_SCREEN_LOCK_BOUND_ECDH_KEY",
+    algorithm: SuperEncryptionAlgorithm::EcdhP256,
+};
+
+/// Superencryption to apply to a new key.
+#[derive(Debug, Clone, Copy)]
+pub enum SuperEncryptionType {
+    /// Do not superencrypt this key.
+    None,
+    /// Superencrypt with a key that remains in memory from first unlock to reboot.
+    LskfBound,
+    /// Superencrypt with a key cleared from memory when the device is locked.
+    ScreenLockBound,
+}
+
+pub struct SuperKey {
+    algorithm: SuperEncryptionAlgorithm,
+    key: ZVec,
+    // id of the super key in the database.
+    id: i64,
+    /// ECDH is more expensive than AES. So on ECDH private keys we set the
+    /// reencrypt_with field to point at the corresponding AES key, and the
+    /// keys will be re-encrypted with AES on first use.
+    reencrypt_with: Option<Arc<SuperKey>>,
+}
+
+impl SuperKey {
+    /// For most purposes `unwrap_key` handles decryption,
+    /// but legacy handling and some tests need to assume AES and decrypt directly.
+    pub fn aes_gcm_decrypt(&self, data: &[u8], iv: &[u8], tag: &[u8]) -> Result<ZVec> {
+        if self.algorithm == SuperEncryptionAlgorithm::Aes256Gcm {
+            aes_gcm_decrypt(data, iv, tag, &self.key)
+                .context("In aes_gcm_decrypt: decryption failed")
+        } else {
+            Err(Error::sys()).context("In aes_gcm_decrypt: Key is not an AES key")
+        }
+    }
+}
+
 #[derive(Default)]
 struct UserSuperKeys {
     /// The per boot key is used for LSKF binding of authentication bound keys. There is one
@@ -42,34 +123,25 @@
     /// 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.
-    per_boot: Option<SuperKey>,
+    per_boot: Option<Arc<SuperKey>>,
     /// The screen lock key works like the per boot key with the distinction that it is cleared
     /// from memory when the screen lock is engaged.
-    /// TODO the life cycle is not fully implemented at this time.
-    screen_lock: Option<Arc<ZVec>>,
-}
-
-#[derive(Default, Clone)]
-pub struct SuperKey {
-    key: Arc<ZVec>,
-    // id of the super key in the database.
-    id: i64,
-}
-
-impl SuperKey {
-    pub fn get_key(&self) -> &Arc<ZVec> {
-        &self.key
-    }
-
-    pub fn get_id(&self) -> i64 {
-        self.id
-    }
+    screen_lock_bound: Option<Arc<SuperKey>>,
+    /// 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>>,
 }
 
 #[derive(Default)]
 struct SkmState {
     user_keys: HashMap<UserId, UserSuperKeys>,
-    key_index: HashMap<i64, Weak<ZVec>>,
+    key_index: HashMap<i64, Weak<SuperKey>>,
+}
+
+impl SkmState {
+    fn add_key_to_key_index(&mut self, super_key: &Arc<SuperKey>) {
+        self.key_index.insert(super_key.id, Arc::downgrade(super_key));
+    }
 }
 
 #[derive(Default)]
@@ -78,48 +150,24 @@
 }
 
 impl SuperKeyManager {
-    pub fn new() -> Self {
-        Self { data: Mutex::new(Default::default()) }
-    }
-
-    pub fn forget_screen_lock_key_for_user(&self, user: UserId) {
-        let mut data = self.data.lock().unwrap();
-        if let Some(usk) = data.user_keys.get_mut(&user) {
-            usk.screen_lock = None;
-        }
-    }
-
-    pub fn forget_screen_lock_keys(&self) {
-        let mut data = self.data.lock().unwrap();
-        for (_, usk) in data.user_keys.iter_mut() {
-            usk.screen_lock = None;
-        }
-    }
-
     pub fn forget_all_keys_for_user(&self, user: UserId) {
         let mut data = self.data.lock().unwrap();
         data.user_keys.remove(&user);
     }
 
-    pub fn forget_all_keys(&self) {
+    fn install_per_boot_key_for_user(&self, user: UserId, super_key: Arc<SuperKey>) {
         let mut data = self.data.lock().unwrap();
-        data.user_keys.clear();
-        data.key_index.clear();
-    }
-
-    fn install_per_boot_key_for_user(&self, user: UserId, super_key: SuperKey) {
-        let mut data = self.data.lock().unwrap();
-        data.key_index.insert(super_key.id, Arc::downgrade(&(super_key.key)));
+        data.add_key_to_key_index(&super_key);
         data.user_keys.entry(user).or_default().per_boot = Some(super_key);
     }
 
-    fn get_key(&self, key_id: &i64) -> Option<Arc<ZVec>> {
+    fn lookup_key(&self, key_id: &i64) -> Option<Arc<SuperKey>> {
         self.data.lock().unwrap().key_index.get(key_id).and_then(|k| k.upgrade())
     }
 
-    pub fn get_per_boot_key_by_user_id(&self, user_id: u32) -> Option<SuperKey> {
+    pub fn get_per_boot_key_by_user_id(&self, user_id: UserId) -> Option<Arc<SuperKey>> {
         let data = self.data.lock().unwrap();
-        data.user_keys.get(&user_id).map(|e| e.per_boot.clone()).flatten()
+        data.user_keys.get(&user_id).and_then(|e| e.per_boot.as_ref().cloned())
     }
 
     /// This function unlocks the super keys for a given user.
@@ -137,7 +185,7 @@
             .get_or_create_key_with(
                 Domain::APP,
                 user as u64 as i64,
-                KeystoreDB::USER_SUPER_KEY_ALIAS,
+                &USER_SUPER_KEY.alias,
                 crate::database::KEYSTORE_UUID,
                 || {
                     // For backward compatibility we need to check if there is a super key present.
@@ -162,42 +210,73 @@
             )
             .context("In unlock_user_key: Failed to get key id.")?;
 
-        self.populate_cache_from_super_key_blob(user, entry, pw).context("In unlock_user_key.")?;
+        self.populate_cache_from_super_key_blob(user, USER_SUPER_KEY.algorithm, entry, pw)
+            .context("In unlock_user_key.")?;
         Ok(())
     }
 
     /// Unwraps an encrypted key blob given metadata identifying the encryption key.
     /// The function queries `metadata.encrypted_by()` to determine the encryption key.
-    /// It then check if the required key is memory resident, and if so decrypts the
+    /// It then checks if the required key is memory resident, and if so decrypts the
     /// blob.
     pub fn unwrap_key<'a>(&self, blob: &'a [u8], metadata: &BlobMetaData) -> Result<KeyBlob<'a>> {
-        match metadata.encrypted_by() {
-            Some(EncryptedBy::KeyId(key_id)) => match self.get_key(key_id) {
-                Some(key) => Ok(KeyBlob::Sensitive(
-                    Self::unwrap_key_with_key(blob, metadata, &key).context("In unwrap_key.")?,
-                    SuperKey { key: key.clone(), id: *key_id },
-                )),
-                None => Err(Error::Rc(ResponseCode::LOCKED))
-                    .context("In unwrap_key: Key is not usable until the user entered their LSKF."),
-            },
-            _ => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED))
-                .context("In unwrap_key: Cannot determined wrapping key."),
-        }
+        let key_id = if let Some(EncryptedBy::KeyId(key_id)) = metadata.encrypted_by() {
+            key_id
+        } else {
+            return Err(Error::Rc(ResponseCode::VALUE_CORRUPTED))
+                .context("In unwrap_key: Cannot determine wrapping key.");
+        };
+        let super_key = self
+            .lookup_key(&key_id)
+            .ok_or(Error::Rc(ResponseCode::LOCKED))
+            .context("In unwrap_key: Required super decryption key is not in memory.")?;
+        Ok(KeyBlob::Sensitive {
+            key: Self::unwrap_key_with_key(blob, metadata, &super_key)
+                .context("In unwrap_key: unwrap_key_with_key failed")?,
+            reencrypt_with: super_key.reencrypt_with.as_ref().unwrap_or(&super_key).clone(),
+            force_reencrypt: super_key.reencrypt_with.is_some(),
+        })
     }
 
     /// Unwraps an encrypted key blob given an encryption key.
-    fn unwrap_key_with_key(blob: &[u8], metadata: &BlobMetaData, key: &[u8]) -> Result<ZVec> {
-        match (metadata.iv(), metadata.aead_tag()) {
-            (Some(iv), Some(tag)) => aes_gcm_decrypt(blob, iv, tag, key)
-                .context("In unwrap_key_with_key: Failed to decrypt the key blob."),
-            (iv, tag) => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
-                concat!(
-                    "In unwrap_key_with_key: Key has incomplete metadata.",
-                    "Present: iv: {}, aead_tag: {}."
-                ),
-                iv.is_some(),
-                tag.is_some(),
-            )),
+    fn unwrap_key_with_key(blob: &[u8], metadata: &BlobMetaData, key: &SuperKey) -> Result<ZVec> {
+        match key.algorithm {
+            SuperEncryptionAlgorithm::Aes256Gcm => match (metadata.iv(), metadata.aead_tag()) {
+                (Some(iv), Some(tag)) => key
+                    .aes_gcm_decrypt(blob, iv, tag)
+                    .context("In unwrap_key_with_key: Failed to decrypt the key blob."),
+                (iv, tag) => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
+                    concat!(
+                        "In unwrap_key_with_key: Key has incomplete metadata.",
+                        "Present: iv: {}, aead_tag: {}."
+                    ),
+                    iv.is_some(),
+                    tag.is_some(),
+                )),
+            },
+            SuperEncryptionAlgorithm::EcdhP256 => {
+                match (metadata.public_key(), metadata.salt(), metadata.iv(), metadata.aead_tag()) {
+                    (Some(public_key), Some(salt), Some(iv), Some(aead_tag)) => {
+                        ECDHPrivateKey::from_private_key(&key.key)
+                            .and_then(|k| k.decrypt_message(public_key, salt, iv, blob, aead_tag))
+                            .context(
+                                "In unwrap_key_with_key: Failed to decrypt the key blob with ECDH.",
+                            )
+                    }
+                    (public_key, salt, iv, aead_tag) => {
+                        Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
+                            concat!(
+                                "In unwrap_key_with_key: Key has incomplete metadata.",
+                                "Present: public_key: {}, salt: {}, iv: {}, aead_tag: {}."
+                            ),
+                            public_key.is_some(),
+                            salt.is_some(),
+                            iv.is_some(),
+                            aead_tag.is_some(),
+                        ))
+                    }
+                }
+            }
         }
     }
 
@@ -205,15 +284,10 @@
     pub fn super_key_exists_in_db_for_user(
         db: &mut KeystoreDB,
         legacy_migrator: &LegacyMigrator,
-        user_id: u32,
+        user_id: UserId,
     ) -> Result<bool> {
         let key_in_db = db
-            .key_exists(
-                Domain::APP,
-                user_id as u64 as i64,
-                KeystoreDB::USER_SUPER_KEY_ALIAS,
-                KeyType::Super,
-            )
+            .key_exists(Domain::APP, user_id as u64 as i64, &USER_SUPER_KEY.alias, KeyType::Super)
             .context("In super_key_exists_in_db_for_user.")?;
 
         if key_in_db {
@@ -232,17 +306,18 @@
         &self,
         db: &mut KeystoreDB,
         legacy_migrator: &LegacyMigrator,
-        user_id: u32,
+        user_id: UserId,
         pw: &Password,
     ) -> Result<UserState> {
+        let alias = &USER_SUPER_KEY;
         let result = legacy_migrator
-            .with_try_migrate_super_key(user_id, pw, || db.load_super_key(user_id))
+            .with_try_migrate_super_key(user_id, pw, || db.load_super_key(alias, user_id))
             .context("In check_and_unlock_super_key. Failed to load super key")?;
 
         match result {
             Some((_, entry)) => {
                 let super_key = self
-                    .populate_cache_from_super_key_blob(user_id, entry, pw)
+                    .populate_cache_from_super_key_blob(user_id, alias.algorithm, entry, pw)
                     .context("In check_and_unlock_super_key.")?;
                 Ok(UserState::LskfUnlocked(super_key))
             }
@@ -260,7 +335,7 @@
         &self,
         db: &mut KeystoreDB,
         legacy_migrator: &LegacyMigrator,
-        user_id: u32,
+        user_id: UserId,
         pw: Option<&Password>,
     ) -> Result<UserState> {
         let super_key_exists_in_db =
@@ -279,11 +354,22 @@
                 .context("In check_and_initialize_super_key.")?;
 
             let key_entry = db
-                .store_super_key(user_id, &(&encrypted_super_key, &blob_metadata))
+                .store_super_key(
+                    user_id,
+                    &USER_SUPER_KEY,
+                    &encrypted_super_key,
+                    &blob_metadata,
+                    &KeyMetaData::new(),
+                )
                 .context("In check_and_initialize_super_key. Failed to store super key.")?;
 
             let super_key = self
-                .populate_cache_from_super_key_blob(user_id, key_entry, pw)
+                .populate_cache_from_super_key_blob(
+                    user_id,
+                    USER_SUPER_KEY.algorithm,
+                    key_entry,
+                    pw,
+                )
                 .context("In check_and_initialize_super_key.")?;
             Ok(UserState::LskfUnlocked(super_key))
         } else {
@@ -294,19 +380,26 @@
     //helper function to populate super key cache from the super key blob loaded from the database
     fn populate_cache_from_super_key_blob(
         &self,
-        user_id: u32,
+        user_id: UserId,
+        algorithm: SuperEncryptionAlgorithm,
         entry: KeyEntry,
         pw: &Password,
-    ) -> Result<SuperKey> {
-        let super_key = Self::extract_super_key_from_key_entry(entry, pw).context(
-            "In populate_cache_from_super_key_blob. Failed to extract super key from key entry",
-        )?;
+    ) -> Result<Arc<SuperKey>> {
+        let super_key = Self::extract_super_key_from_key_entry(algorithm, entry, pw, None)
+            .context(
+                "In populate_cache_from_super_key_blob. Failed to extract super key from key entry",
+            )?;
         self.install_per_boot_key_for_user(user_id, super_key.clone());
         Ok(super_key)
     }
 
     /// Extracts super key from the entry loaded from the database
-    pub fn extract_super_key_from_key_entry(entry: KeyEntry, pw: &Password) -> Result<SuperKey> {
+    pub fn extract_super_key_from_key_entry(
+        algorithm: SuperEncryptionAlgorithm,
+        entry: KeyEntry,
+        pw: &Password,
+        reencrypt_with: Option<Arc<SuperKey>>,
+    ) -> Result<Arc<SuperKey>> {
         if let Some((blob, metadata)) = entry.key_blob_info() {
             let key = match (
                 metadata.encrypted_by(),
@@ -315,6 +408,7 @@
                 metadata.aead_tag(),
             ) {
                 (Some(&EncryptedBy::Password), Some(salt), Some(iv), Some(tag)) => {
+                    // Note that password encryption is AES no matter the value of algorithm
                     let key = pw.derive_key(Some(salt), AES_256_KEY_LENGTH).context(
                         "In extract_super_key_from_key_entry: Failed to generate key from password.",
                     )?;
@@ -327,16 +421,16 @@
                     return Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
                         concat!(
                         "In extract_super_key_from_key_entry: Super key has incomplete metadata.",
-                        "Present: encrypted_by: {}, salt: {}, iv: {}, aead_tag: {}."
+                        "encrypted_by: {:?}; Present: salt: {}, iv: {}, aead_tag: {}."
                     ),
-                        enc_by.is_some(),
+                        enc_by,
                         salt.is_some(),
                         iv.is_some(),
                         tag.is_some()
                     ));
                 }
             };
-            Ok(SuperKey { key: Arc::new(key), id: entry.id() })
+            Ok(Arc::new(SuperKey { algorithm, key, id: entry.id(), reencrypt_with }))
         } else {
             Err(Error::Rc(ResponseCode::VALUE_CORRUPTED))
                 .context("In extract_super_key_from_key_entry: No key blob info.")
@@ -370,14 +464,14 @@
         &self,
         db: &mut KeystoreDB,
         legacy_migrator: &LegacyMigrator,
-        user_id: u32,
+        user_id: UserId,
         key_blob: &[u8],
     ) -> Result<(Vec<u8>, BlobMetaData)> {
         match UserState::get(db, legacy_migrator, self, user_id)
             .context("In super_encrypt. Failed to get user state.")?
         {
             UserState::LskfUnlocked(super_key) => {
-                Self::encrypt_with_super_key(key_blob, &super_key)
+                Self::encrypt_with_aes_super_key(key_blob, &super_key)
                     .context("In super_encrypt_on_key_init. Failed to encrypt the key.")
             }
             UserState::LskfLocked => {
@@ -391,13 +485,17 @@
     //Helper function to encrypt a key with the given super key. Callers should select which super
     //key to be used. This is called when a key is super encrypted at its creation as well as at its
     //upgrade.
-    fn encrypt_with_super_key(
+    fn encrypt_with_aes_super_key(
         key_blob: &[u8],
         super_key: &SuperKey,
     ) -> Result<(Vec<u8>, BlobMetaData)> {
+        if super_key.algorithm != SuperEncryptionAlgorithm::Aes256Gcm {
+            return Err(Error::sys())
+                .context("In encrypt_with_aes_super_key: unexpected algorithm");
+        }
         let mut metadata = BlobMetaData::new();
         let (encrypted_key, iv, tag) = aes_gcm_encrypt(key_blob, &(super_key.key))
-            .context("In encrypt_with_super_key: Failed to encrypt new super key.")?;
+            .context("In encrypt_with_aes_super_key: Failed to encrypt new super key.")?;
         metadata.add(BlobMetaEntry::Iv(iv));
         metadata.add(BlobMetaEntry::AeadTag(tag));
         metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key.id)));
@@ -414,17 +512,52 @@
         domain: &Domain,
         key_parameters: &[KeyParameter],
         flags: Option<i32>,
-        user_id: u32,
+        user_id: UserId,
         key_blob: &[u8],
     ) -> Result<(Vec<u8>, BlobMetaData)> {
-        match (*domain, Enforcements::super_encryption_required(key_parameters, flags)) {
-            (Domain::APP, true) => {
-                self.super_encrypt_on_key_init(db, legacy_migrator, user_id, &key_blob).context(
-                    "In handle_super_encryption_on_key_init.
-                         Failed to super encrypt the key.",
-                )
+        match Enforcements::super_encryption_required(domain, key_parameters, flags) {
+            SuperEncryptionType::None => Ok((key_blob.to_vec(), BlobMetaData::new())),
+            SuperEncryptionType::LskfBound => self
+                .super_encrypt_on_key_init(db, legacy_migrator, user_id, &key_blob)
+                .context(concat!(
+                    "In handle_super_encryption_on_key_init. ",
+                    "Failed to super encrypt with LskfBound key."
+                )),
+            SuperEncryptionType::ScreenLockBound => {
+                let mut data = self.data.lock().unwrap();
+                let entry = data.user_keys.entry(user_id).or_default();
+                if let Some(super_key) = entry.screen_lock_bound.as_ref() {
+                    Self::encrypt_with_aes_super_key(key_blob, &super_key).context(concat!(
+                        "In handle_super_encryption_on_key_init. ",
+                        "Failed to encrypt with ScreenLockBound key."
+                    ))
+                } else {
+                    // Symmetric key is not available, use public key encryption
+                    let loaded =
+                        db.load_super_key(&USER_SCREEN_LOCK_BOUND_ECDH_KEY, user_id).context(
+                            "In handle_super_encryption_on_key_init: load_super_key failed.",
+                        )?;
+                    let (key_id_guard, key_entry) = loaded.ok_or_else(Error::sys).context(
+                        "In handle_super_encryption_on_key_init: User ECDH key missing.",
+                    )?;
+                    let public_key =
+                        key_entry.metadata().sec1_public_key().ok_or_else(Error::sys).context(
+                            "In handle_super_encryption_on_key_init: sec1_public_key missing.",
+                        )?;
+                    let mut metadata = BlobMetaData::new();
+                    let (ephem_key, salt, iv, encrypted_key, aead_tag) =
+                        ECDHPrivateKey::encrypt_message(public_key, key_blob).context(concat!(
+                            "In handle_super_encryption_on_key_init: ",
+                            "ECDHPrivateKey::encrypt_message failed."
+                        ))?;
+                    metadata.add(BlobMetaEntry::PublicKey(ephem_key));
+                    metadata.add(BlobMetaEntry::Salt(salt));
+                    metadata.add(BlobMetaEntry::Iv(iv));
+                    metadata.add(BlobMetaEntry::AeadTag(aead_tag));
+                    metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(key_id_guard.id())));
+                    Ok((encrypted_key, metadata))
+                }
             }
-            _ => Ok((key_blob.to_vec(), BlobMetaData::new())),
         }
     }
 
@@ -448,17 +581,15 @@
     /// Check if a given key needs re-super-encryption, from its KeyBlob type.
     /// If so, re-super-encrypt the key and return a new set of metadata,
     /// containing the new super encryption information.
-    pub fn reencrypt_on_upgrade_if_required<'a>(
+    pub fn reencrypt_if_required<'a>(
         key_blob_before_upgrade: &KeyBlob,
         key_after_upgrade: &'a [u8],
     ) -> Result<(KeyBlob<'a>, Option<BlobMetaData>)> {
         match key_blob_before_upgrade {
-            KeyBlob::Sensitive(_, super_key) => {
-                let (key, metadata) = Self::encrypt_with_super_key(key_after_upgrade, super_key)
-                    .context(concat!(
-                        "In reencrypt_on_upgrade_if_required. ",
-                        "Failed to re-super-encrypt key on key upgrade."
-                    ))?;
+            KeyBlob::Sensitive { reencrypt_with: super_key, .. } => {
+                let (key, metadata) =
+                    Self::encrypt_with_aes_super_key(key_after_upgrade, super_key)
+                        .context("In reencrypt_if_required: Failed to re-super-encrypt key.")?;
                 Ok((KeyBlob::NonSensitive(key), Some(metadata)))
             }
             _ => Ok((KeyBlob::Ref(key_after_upgrade), None)),
@@ -472,6 +603,117 @@
         }
         false
     }
+
+    /// Fetch a superencryption key from the database, or create it if it doesn't already exist.
+    /// When this is called, the caller must hold the lock on the SuperKeyManager.
+    /// So it's OK that the check and creation are different DB transactions.
+    fn get_or_create_super_key(
+        db: &mut KeystoreDB,
+        user_id: UserId,
+        key_type: &SuperKeyType,
+        password: &Password,
+        reencrypt_with: Option<Arc<SuperKey>>,
+    ) -> Result<Arc<SuperKey>> {
+        let loaded_key = db.load_super_key(key_type, user_id)?;
+        if let Some((_, key_entry)) = loaded_key {
+            Ok(Self::extract_super_key_from_key_entry(
+                key_type.algorithm,
+                key_entry,
+                password,
+                reencrypt_with,
+            )?)
+        } else {
+            let (super_key, public_key) = match key_type.algorithm {
+                SuperEncryptionAlgorithm::Aes256Gcm => (
+                    generate_aes256_key()
+                        .context("In get_or_create_super_key: Failed to generate AES 256 key.")?,
+                    None,
+                ),
+                SuperEncryptionAlgorithm::EcdhP256 => {
+                    let key = ECDHPrivateKey::generate()
+                        .context("In get_or_create_super_key: Failed to generate ECDH key")?;
+                    (
+                        key.private_key()
+                            .context("In get_or_create_super_key: private_key failed")?,
+                        Some(
+                            key.public_key()
+                                .context("In get_or_create_super_key: public_key failed")?,
+                        ),
+                    )
+                }
+            };
+            //derive an AES256 key from the password and re-encrypt the super key
+            //before we insert it in the database.
+            let (encrypted_super_key, blob_metadata) =
+                Self::encrypt_with_password(&super_key, password)
+                    .context("In get_or_create_super_key.")?;
+            let mut key_metadata = KeyMetaData::new();
+            if let Some(pk) = public_key {
+                key_metadata.add(KeyMetaEntry::Sec1PublicKey(pk));
+            }
+            let key_entry = db
+                .store_super_key(
+                    user_id,
+                    key_type,
+                    &encrypted_super_key,
+                    &blob_metadata,
+                    &key_metadata,
+                )
+                .context("In get_or_create_super_key. Failed to store super key.")?;
+            Ok(Arc::new(SuperKey {
+                algorithm: key_type.algorithm,
+                key: super_key,
+                id: key_entry.id(),
+                reencrypt_with,
+            }))
+        }
+    }
+
+    /// Decrypt the screen-lock bound keys for this user using the password and store in memory.
+    pub fn unlock_screen_lock_bound_key(
+        &self,
+        db: &mut KeystoreDB,
+        user_id: UserId,
+        password: &Password,
+    ) -> Result<()> {
+        let mut data = self.data.lock().unwrap();
+        let entry = data.user_keys.entry(user_id).or_default();
+        let aes = entry
+            .screen_lock_bound
+            .get_or_try_to_insert_with(|| {
+                Self::get_or_create_super_key(
+                    db,
+                    user_id,
+                    &USER_SCREEN_LOCK_BOUND_KEY,
+                    password,
+                    None,
+                )
+            })?
+            .clone();
+        let ecdh = entry
+            .screen_lock_bound_private
+            .get_or_try_to_insert_with(|| {
+                Self::get_or_create_super_key(
+                    db,
+                    user_id,
+                    &USER_SCREEN_LOCK_BOUND_ECDH_KEY,
+                    password,
+                    Some(aes.clone()),
+                )
+            })?
+            .clone();
+        data.add_key_to_key_index(&aes);
+        data.add_key_to_key_index(&ecdh);
+        Ok(())
+    }
+
+    /// Wipe the screen-lock bound keys for this user from memory.
+    pub fn lock_screen_lock_bound_key(&self, user_id: UserId) {
+        let mut data = self.data.lock().unwrap();
+        let mut entry = data.user_keys.entry(user_id).or_default();
+        entry.screen_lock_bound = None;
+        entry.screen_lock_bound_private = None;
+    }
 }
 
 /// This enum represents different states of the user's life cycle in the device.
@@ -479,7 +721,7 @@
 pub enum UserState {
     // The user has registered LSKF and has unlocked the device by entering PIN/Password,
     // and hence the per-boot super key is available in the cache.
-    LskfUnlocked(SuperKey),
+    LskfUnlocked(Arc<SuperKey>),
     // The user has registered LSKF, but has not unlocked the device using password, after reboot.
     // Hence the per-boot super-key(s) is not available in the cache.
     // However, the encrypted super key is available in the database.
@@ -494,7 +736,7 @@
         db: &mut KeystoreDB,
         legacy_migrator: &LegacyMigrator,
         skm: &SuperKeyManager,
-        user_id: u32,
+        user_id: UserId,
     ) -> Result<UserState> {
         match skm.get_per_boot_key_by_user_id(user_id) {
             Some(super_key) => Ok(UserState::LskfUnlocked(super_key)),
@@ -517,7 +759,7 @@
         db: &mut KeystoreDB,
         legacy_migrator: &LegacyMigrator,
         skm: &SuperKeyManager,
-        user_id: u32,
+        user_id: UserId,
         password: Option<&Password>,
     ) -> Result<UserState> {
         match skm.get_per_boot_key_by_user_id(user_id) {
@@ -551,7 +793,7 @@
         db: &mut KeystoreDB,
         legacy_migrator: &LegacyMigrator,
         skm: &SuperKeyManager,
-        user_id: u32,
+        user_id: UserId,
         password: &Password,
     ) -> Result<UserState> {
         match skm.get_per_boot_key_by_user_id(user_id) {
@@ -577,18 +819,18 @@
         db: &mut KeystoreDB,
         skm: &SuperKeyManager,
         legacy_migrator: &LegacyMigrator,
-        user_id: u32,
+        user_id: UserId,
         keep_non_super_encrypted_keys: bool,
     ) -> Result<()> {
         // mark keys created on behalf of the user as unreferenced.
         legacy_migrator
             .bulk_delete_user(user_id, keep_non_super_encrypted_keys)
             .context("In reset_user: Trying to delete legacy keys.")?;
-        db.unbind_keys_for_user(user_id as u32, keep_non_super_encrypted_keys)
+        db.unbind_keys_for_user(user_id, keep_non_super_encrypted_keys)
             .context("In reset user. Error in unbinding keys.")?;
 
         //delete super key in cache, if exists
-        skm.forget_all_keys_for_user(user_id as u32);
+        skm.forget_all_keys_for_user(user_id);
         Ok(())
     }
 }
@@ -599,18 +841,38 @@
 /// `Ref` holds a reference to a key blob when it does not need to be modified if its
 /// life time allows it.
 pub enum KeyBlob<'a> {
-    Sensitive(ZVec, SuperKey),
+    Sensitive {
+        key: ZVec,
+        /// If KeyMint reports that the key must be upgraded, we must
+        /// re-encrypt the key before writing to the database; we use
+        /// this key.
+        reencrypt_with: Arc<SuperKey>,
+        /// If this key was decrypted with an ECDH key, we want to
+        /// re-encrypt it on first use whether it was upgraded or not;
+        /// this field indicates that that's necessary.
+        force_reencrypt: bool,
+    },
     NonSensitive(Vec<u8>),
     Ref(&'a [u8]),
 }
 
+impl<'a> KeyBlob<'a> {
+    pub fn force_reencrypt(&self) -> bool {
+        if let KeyBlob::Sensitive { force_reencrypt, .. } = self {
+            *force_reencrypt
+        } else {
+            false
+        }
+    }
+}
+
 /// Deref returns a reference to the key material in any variant.
 impl<'a> Deref for KeyBlob<'a> {
     type Target = [u8];
 
     fn deref(&self) -> &Self::Target {
         match self {
-            Self::Sensitive(key, _) => &key,
+            Self::Sensitive { key, .. } => &key,
             Self::NonSensitive(key) => &key,
             Self::Ref(key) => key,
         }
diff --git a/keystore2/src/try_insert.rs b/keystore2/src/try_insert.rs
new file mode 100644
index 0000000..6dd3962
--- /dev/null
+++ b/keystore2/src/try_insert.rs
@@ -0,0 +1,100 @@
+// Copyright 2021, 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.
+
+//! The TryInsert trait adds to Option<T> the method
+//! get_or_try_to_insert_with, which is analogous to
+//! get_or_insert_with, but allows the called function to fail and propagates the failure.
+
+/// The TryInsert trait adds to Option<T> the method
+/// get_or_try_to_insert_with, which is analogous to
+/// get_or_insert_with, but allows the called function to fail and propagates the failure.
+pub trait TryInsert {
+    /// Type of the Ok branch of the Result
+    type Item;
+    /// Inserts a value computed from `f` into the option if it is [`None`],
+    /// then returns a mutable reference to the contained value. If `f`
+    /// returns Err, the Option is unchanged.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// let mut x = None;
+    /// assert_eq!(x.get_or_try_to_insert_with(Err("oops".to_string())), Err("oops".to_string()))
+    /// {
+    ///     let y: &mut u32 = x.get_or_try_to_insert_with(|| Ok(5))?;
+    ///     assert_eq!(y, &5);
+    ///
+    ///     *y = 7;
+    /// }
+    ///
+    /// assert_eq!(x, Some(7));
+    /// ```
+    fn get_or_try_to_insert_with<E, F: FnOnce() -> Result<Self::Item, E>>(
+        &mut self,
+        f: F,
+    ) -> Result<&mut Self::Item, E>;
+}
+
+impl<T> TryInsert for Option<T> {
+    type Item = T;
+    fn get_or_try_to_insert_with<E, F: FnOnce() -> Result<Self::Item, E>>(
+        &mut self,
+        f: F,
+    ) -> Result<&mut Self::Item, E> {
+        if self.is_none() {
+            *self = Some(f()?);
+        }
+
+        match self {
+            Some(v) => Ok(v),
+            // SAFETY: a `None` variant for `self` would have been replaced by a `Some`
+            // variant in the code above.
+            None => unsafe { std::hint::unreachable_unchecked() },
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    fn fails() -> Result<i32, String> {
+        Err("fail".to_string())
+    }
+
+    fn succeeds() -> Result<i32, String> {
+        Ok(99)
+    }
+
+    #[test]
+    fn test() {
+        let mut x = None;
+        assert_eq!(x.get_or_try_to_insert_with(fails), Err("fail".to_string()));
+        assert_eq!(x, None);
+        assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 99);
+        assert_eq!(x, Some(99));
+        x = Some(42);
+        assert_eq!(*x.get_or_try_to_insert_with(fails).unwrap(), 42);
+        assert_eq!(x, Some(42));
+        assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 42);
+        assert_eq!(x, Some(42));
+        *x.get_or_try_to_insert_with(fails).unwrap() = 2;
+        assert_eq!(x, Some(2));
+        *x.get_or_try_to_insert_with(succeeds).unwrap() = 3;
+        assert_eq!(x, Some(3));
+        x = None;
+        *x.get_or_try_to_insert_with(succeeds).unwrap() = 5;
+        assert_eq!(x, Some(5));
+    }
+}
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index 2748025..48e9bfb 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -91,7 +91,13 @@
 
 /// This function checks whether a given tag corresponds to the access of device identifiers.
 pub fn is_device_id_attestation_tag(tag: Tag) -> bool {
-    matches!(tag, Tag::ATTESTATION_ID_IMEI | Tag::ATTESTATION_ID_MEID | Tag::ATTESTATION_ID_SERIAL)
+    matches!(
+        tag,
+        Tag::ATTESTATION_ID_IMEI
+            | Tag::ATTESTATION_ID_MEID
+            | Tag::ATTESTATION_ID_SERIAL
+            | Tag::DEVICE_UNIQUE_ATTESTATION
+    )
 }
 
 /// This function checks whether the calling app has the Android permissions needed to attest device
@@ -215,12 +221,12 @@
 }
 
 /// AID offset for uid space partitioning.
-/// TODO: Replace with bindgen generated from libcutils. b/175619259
-pub const AID_USER_OFFSET: u32 = 100000;
+pub const AID_USER_OFFSET: u32 = cutils_bindgen::AID_USER_OFFSET;
 
 /// Extracts the android user from the given uid.
 pub fn uid_to_android_user(uid: u32) -> u32 {
-    uid / AID_USER_OFFSET
+    // Safety: No memory access
+    unsafe { cutils_bindgen::multiuser_get_user_id(uid) }
 }
 
 #[cfg(test)]
diff --git a/keystore2/src/vintf/Android.bp b/keystore2/src/vintf/Android.bp
index 77ec57d..feec8ae 100644
--- a/keystore2/src/vintf/Android.bp
+++ b/keystore2/src/vintf/Android.bp
@@ -55,6 +55,7 @@
         "--size_t-is-usize",
         "--whitelist-function", "getHalNames",
         "--whitelist-function", "getHalNamesAndVersions",
+        "--whitelist-function", "getHidlInstances",
         "--whitelist-function", "getAidlInstances",
         "--whitelist-function", "freeNames",
     ],
diff --git a/keystore2/src/vintf/lib.rs b/keystore2/src/vintf/lib.rs
index c3d6d8a..8730a3e 100644
--- a/keystore2/src/vintf/lib.rs
+++ b/keystore2/src/vintf/lib.rs
@@ -14,7 +14,9 @@
 
 //! Bindings for getting the list of HALs.
 
-use keystore2_vintf_bindgen::{freeNames, getAidlInstances, getHalNames, getHalNamesAndVersions};
+use keystore2_vintf_bindgen::{
+    freeNames, getAidlInstances, getHalNames, getHalNamesAndVersions, getHidlInstances,
+};
 use std::ffi::{CStr, CString};
 use std::os::raw::c_char;
 use std::str::Utf8Error;
@@ -65,6 +67,32 @@
 
 /// Gets the instances of the given package, version, and interface tuple.
 /// Note that this is not a zero-cost shim: it will make copies of the strings.
+pub fn get_hidl_instances(
+    package: &str,
+    major_version: usize,
+    minor_version: usize,
+    interface_name: &str,
+) -> HalNames {
+    let mut len: usize = 0;
+    let packages = CString::new(package).expect("Failed to make CString from package.");
+    let interface_name =
+        CString::new(interface_name).expect("Failed to make CString from interface_name.");
+    // Safety: We'll wrap this in HalNames to free the memory it allocates.
+    // It stores the size of the array it returns in len.
+    let raw_strs = unsafe {
+        getHidlInstances(
+            &mut len,
+            packages.as_ptr(),
+            major_version,
+            minor_version,
+            interface_name.as_ptr(),
+        )
+    };
+    HalNames { data: raw_strs, len }
+}
+
+/// Gets the instances of the given package, version, and interface tuple.
+/// Note that this is not a zero-cost shim: it will make copies of the strings.
 pub fn get_aidl_instances(package: &str, version: usize, interface_name: &str) -> HalNames {
     let mut len: usize = 0;
     let packages = CString::new(package).expect("Failed to make CString from package.");
diff --git a/keystore2/src/vintf/vintf.cpp b/keystore2/src/vintf/vintf.cpp
index dbdc046..e407efa 100644
--- a/keystore2/src/vintf/vintf.cpp
+++ b/keystore2/src/vintf/vintf.cpp
@@ -43,6 +43,15 @@
     return convert(names);
 }
 
+char** getHidlInstances(size_t* len, const char* package, size_t major_version,
+                        size_t minor_version, const char* interfaceName) {
+    android::vintf::Version version(major_version, minor_version);
+    auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+    const auto names = manifest->getHidlInstances(package, version, interfaceName);
+    *len = names.size();
+    return convert(names);
+}
+
 char** getAidlInstances(size_t* len, const char* package, size_t version,
                         const char* interfaceName) {
     auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
diff --git a/keystore2/src/vintf/vintf.hpp b/keystore2/src/vintf/vintf.hpp
index 75e80f6..091e8e8 100644
--- a/keystore2/src/vintf/vintf.hpp
+++ b/keystore2/src/vintf/vintf.hpp
@@ -23,6 +23,8 @@
 
 char** getHalNames(size_t* len);
 char** getHalNamesAndVersions(size_t* len);
+char** getHidlInstances(size_t* len, const char* package, size_t major_version,
+                        size_t minor_version, const char* interfaceName);
 char** getAidlInstances(size_t* len, const char* package, size_t version,
                         const char* interfaceName);
 void freeNames(char** names, size_t len);
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
index 1c3706d..2e5e02e 100644
--- a/ondevice-signing/Android.bp
+++ b/ondevice-signing/Android.bp
@@ -90,6 +90,8 @@
     "VerityUtils.cpp",
   ],
 
+  header_libs: ["odrefresh_headers"],
+
   static_libs: [
     "libmini_keyctl_static", // TODO need static?
     "libc++fs",
diff --git a/ondevice-signing/KeystoreKey.cpp b/ondevice-signing/KeystoreKey.cpp
index de7033f..840b683 100644
--- a/ondevice-signing/KeystoreKey.cpp
+++ b/ondevice-signing/KeystoreKey.cpp
@@ -129,7 +129,7 @@
     if (sm == nullptr) {
         return false;
     }
-    auto service = sm->getService(String16("android.system.keystore2"));
+    auto service = sm->getService(String16("android.system.keystore2.IKeystoreService/default"));
     if (service == nullptr) {
         return false;
     }
diff --git a/ondevice-signing/TEST_MAPPING b/ondevice-signing/TEST_MAPPING
new file mode 100644
index 0000000..03b9b95
--- /dev/null
+++ b/ondevice-signing/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "odsign_e2e_tests"
+    }
+  ]
+}
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
index 33d04ca..6cab8b6 100644
--- a/ondevice-signing/odsign_main.cpp
+++ b/ondevice-signing/odsign_main.cpp
@@ -29,6 +29,7 @@
 #include <android-base/properties.h>
 #include <android-base/scopeguard.h>
 #include <logwrap/logwrap.h>
+#include <odrefresh/odrefresh.h>
 
 #include "CertUtils.h"
 #include "KeymasterSigningKey.h"
@@ -101,18 +102,11 @@
     return {};
 }
 
-bool compileArtifacts(bool force) {
+art::odrefresh::ExitCode compileArtifacts(bool force) {
     const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
-
-    return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
-           0;
-}
-
-bool validateArtifacts() {
-    const char* const argv[] = {kOdrefreshPath, "--check"};
-
-    return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
-           0;
+    const int exit_code =
+        logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
+    return static_cast<art::odrefresh::ExitCode>(exit_code);
 }
 
 static std::string toHex(const std::vector<uint8_t>& digest) {
@@ -303,6 +297,11 @@
     };
     auto scope_guard = android::base::make_scope_guard(errorScopeGuard);
 
+    if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
+        LOG(INFO) << "Device doesn't support updatable APEX, exiting.";
+        return 0;
+    }
+
     SigningKey* key;
     if (kUseKeystore) {
         auto keystoreResult = KeystoreKey::getInstance();
@@ -349,30 +348,27 @@
         }
     }
 
-    // Ask ART whether it considers the artifacts valid
-    LOG(INFO) << "Asking odrefresh to verify artifacts (if present)...";
-    bool artifactsValid = validateArtifacts();
-    LOG(INFO) << "odrefresh said they are " << (artifactsValid ? "VALID" : "INVALID");
-
-    // A post-condition of validating artifacts is that if the ones on /system
-    // are used, kArtArtifactsDir is removed. Conversely, if kArtArtifactsDir
-    // exists, those are artifacts that will be used, and we should verify them.
-    int err = access(kArtArtifactsDir.c_str(), F_OK);
-    // If we receive any error other than ENOENT, be suspicious
-    bool artifactsPresent = (err == 0) || (err < 0 && errno != ENOENT);
-    if (artifactsPresent) {
-        auto verificationResult = verifyArtifacts(*key, supportsFsVerity);
-        if (!verificationResult.ok()) {
-            LOG(ERROR) << verificationResult.error().message();
-            return -1;
+    art::odrefresh::ExitCode odrefresh_status = compileArtifacts(kForceCompilation);
+    if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
+        LOG(INFO) << "odrefresh said artifacts are VALID";
+        // A post-condition of validating artifacts is that if the ones on /system
+        // are used, kArtArtifactsDir is removed. Conversely, if kArtArtifactsDir
+        // exists, those are artifacts that will be used, and we should verify them.
+        int err = access(kArtArtifactsDir.c_str(), F_OK);
+        // If we receive any error other than ENOENT, be suspicious
+        bool artifactsPresent = (err == 0) || (err < 0 && errno != ENOENT);
+        if (artifactsPresent) {
+            auto verificationResult = verifyArtifacts(*key, supportsFsVerity);
+            if (!verificationResult.ok()) {
+                LOG(ERROR) << verificationResult.error().message();
+                return -1;
+            }
         }
-    }
-
-    if (!artifactsValid || kForceCompilation) {
-        LOG(INFO) << "Starting compilation... ";
-        bool ret = compileArtifacts(kForceCompilation);
-        LOG(INFO) << "Compilation done, returned " << ret;
-
+    } else if (odrefresh_status == art::odrefresh::ExitCode::kCompilationSuccess ||
+               odrefresh_status == art::odrefresh::ExitCode::kCompilationFailed) {
+        const bool compiled_all = odrefresh_status == art::odrefresh::ExitCode::kCompilationSuccess;
+        LOG(INFO) << "odrefresh compiled " << (compiled_all ? "all" : "partial")
+                  << " artifacts, returned " << odrefresh_status;
         Result<std::map<std::string, std::string>> digests;
         if (supportsFsVerity) {
             digests = addFilesToVerityRecursive(kArtArtifactsDir, *key);
@@ -385,12 +381,17 @@
             LOG(ERROR) << digests.error().message();
             return -1;
         }
-
         auto persistStatus = persistDigests(*digests, *key);
         if (!persistStatus.ok()) {
             LOG(ERROR) << persistStatus.error().message();
             return -1;
         }
+    } else if (odrefresh_status == art::odrefresh::ExitCode::kCleanupFailed) {
+        LOG(ERROR) << "odrefresh failed cleaning up existing artifacts";
+        return -1;
+    } else {
+        LOG(ERROR) << "odrefresh exited unexpectedly, returned " << odrefresh_status;
+        return -1;
     }
 
     LOG(INFO) << "On-device signing done.";