[automerger skipped] Keystore 2.0: Rename legacy_migrator to importer. am: 4a6ba6e8cd -s ours

am skip reason: Merged-In I0612d0731083548a196911712f6c0adbbc9a5e9a with SHA-1 0ffb8a80bd is already in history

Original change: https://googleplex-android-review.googlesource.com/c/platform/system/security/+/16909354

Change-Id: I9060a7ffa1a53f8c727136dd23bfb34e3788a1fe
diff --git a/OWNERS b/OWNERS
index bb51005..563a78c 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,6 +1,10 @@
-swillden@google.com
+alanstokes@google.com
 cbrubaker@google.com
-jdanis@google.com
 hasinitg@google.com
+jbires@google.com
+jdanis@google.com
+jeffv@google.com
 kroot@google.com
+sethmo@google.com
+swillden@google.com
 zeuthen@google.com
diff --git a/diced/Android.bp b/diced/Android.bp
new file mode 100644
index 0000000..e13d863
--- /dev/null
+++ b/diced/Android.bp
@@ -0,0 +1,228 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+rust_library {
+    name: "libdiced_utils",
+    crate_name: "diced_utils",
+    srcs: ["src/utils.rs"],
+    vendor_available: true,
+
+    rustlibs: [
+        "android.hardware.security.dice-V1-rust",
+        "libanyhow",
+        "libdiced_open_dice_cbor",
+        "libkeystore2_crypto_rust",
+    ],
+}
+
+rust_test {
+    name: "diced_utils_test",
+    crate_name: "diced_utils_test",
+    srcs: ["src/utils.rs"],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    rustlibs: [
+        "android.hardware.security.dice-V1-rust",
+        "libanyhow",
+        "libdiced_open_dice_cbor",
+        "libkeystore2_crypto_rust",
+    ],
+}
+
+rust_library {
+    name: "libdiced_sample_inputs",
+    crate_name: "diced_sample_inputs",
+    srcs: ["src/sample_inputs.rs"],
+    vendor_available: true,
+
+    rustlibs: [
+        "android.hardware.security.dice-V1-rust",
+        "libanyhow",
+        "libdiced_open_dice_cbor",
+        "libdiced_utils",
+        "libkeystore2_crypto_rust",
+    ],
+}
+
+rust_test {
+    name: "diced_sample_inputs_test",
+    crate_name: "diced_sample_inputs_test",
+    srcs: ["src/sample_inputs.rs"],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    rustlibs: [
+        "android.hardware.security.dice-V1-rust",
+        "libanyhow",
+        "libdiced_open_dice_cbor",
+        "libdiced_utils",
+        "libkeystore2_crypto_rust",
+    ],
+}
+
+rust_library {
+    name: "libdiced",
+    crate_name: "diced",
+    srcs: ["src/lib.rs"],
+
+    rustlibs: [
+        "android.hardware.security.dice-V1-rust",
+        "android.security.dice-rust",
+        "libdiced_open_dice_cbor",
+        "libanyhow",
+        "libbinder_rs",
+        "libdiced_utils",
+        "libkeystore2_crypto_rust",
+        "libkeystore2_selinux",
+        "liblibc",
+        "liblog_rust",
+        "libthiserror",
+    ],
+}
+
+rust_library {
+    name: "libdiced_vendor",
+    crate_name: "diced",
+    srcs: ["src/lib_vendor.rs"],
+
+    vendor_available: true,
+    rustlibs: [
+        "android.hardware.security.dice-V1-rust",
+        "libdiced_open_dice_cbor",
+        "libanyhow",
+        "libbinder_rs",
+        "libdiced_utils",
+        "libkeystore2_crypto_rust",
+        "liblibc",
+        "liblog_rust",
+        "libnix",
+        "libserde",
+        "libserde_cbor",
+        "libthiserror",
+    ],
+}
+
+rust_binary {
+    name: "diced",
+    srcs: ["src/diced_main.rs"],
+    prefer_rlib: true,
+    rustlibs: [
+        "android.hardware.security.dice-V1-rust",
+        "libandroid_logger",
+        "libbinder_rs",
+        "libdiced",
+        "libdiced_open_dice_cbor",
+        "libdiced_sample_inputs",
+        "libdiced_utils",
+        "liblog_rust",
+    ],
+    init_rc: ["diced.rc"],
+}
+
+rust_binary {
+    name: "diced.microdroid",
+    srcs: ["src/diced_main.rs"],
+    prefer_rlib: true,
+    rustlibs: [
+        "android.hardware.security.dice-V1-rust",
+        "libandroid_logger",
+        "libbinder_rs",
+        "libdiced",
+        "libdiced_open_dice_cbor",
+        "libdiced_sample_inputs",
+        "libdiced_utils",
+        "liblog_rust",
+    ],
+    init_rc: ["diced.microdroid.rc"],
+    bootstrap: true,
+}
+
+rust_test {
+    name: "diced_test",
+    crate_name: "diced_test",
+    srcs: ["src/lib.rs"],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    rustlibs: [
+        "android.hardware.security.dice-V1-rust",
+        "android.security.dice-rust",
+        "libanyhow",
+        "libbinder_rs",
+        "libdiced_open_dice_cbor",
+        "libdiced_utils",
+        "libkeystore2_crypto_rust",
+        "libkeystore2_selinux",
+        "libkeystore2_vintf_rust",
+        "liblibc",
+        "liblog_rust",
+        "libnix",
+        "libserde",
+        "libserde_cbor",
+        "libthiserror",
+    ],
+}
+
+rust_test {
+    name: "diced_vendor_test",
+    crate_name: "diced_vendor_test",
+    srcs: ["src/lib_vendor.rs"],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    rustlibs: [
+        "android.hardware.security.dice-V1-rust",
+        "libanyhow",
+        "libdiced_open_dice_cbor",
+        "libdiced_sample_inputs",
+        "libdiced_utils",
+        "libbinder_rs",
+        "libkeystore2_crypto_rust",
+        "liblibc",
+        "liblog_rust",
+        "libnix",
+        "libserde",
+        "libserde_cbor",
+        "libthiserror",
+    ],
+}
+
+rust_test {
+    name: "diced_client_test",
+    srcs: [
+        "src/diced_client_test.rs",
+    ],
+    require_root: true,
+    auto_gen_config: true,
+    test_suites: [
+        "general-tests",
+    ],
+
+    rustlibs: [
+        "android.hardware.security.dice-V1-rust",
+        "android.security.dice-rust",
+        "libanyhow",
+        "libbinder_rs",
+        "libdiced_open_dice_cbor",
+        "libdiced_sample_inputs",
+        "libdiced_utils",
+        "libnix",
+    ],
+}
diff --git a/diced/aidl/Android.bp b/diced/aidl/Android.bp
new file mode 100644
index 0000000..75c1856
--- /dev/null
+++ b/diced/aidl/Android.bp
@@ -0,0 +1,50 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+aidl_interface {
+    name: "android.security.dice",
+    srcs: [ "android/security/dice/*.aidl" ],
+    unstable: true,
+    imports: ["android.hardware.security.dice-V1"],
+    backend: {
+         java: {
+            enabled: false,
+            platform_apis: false,
+        },
+        rust: {
+            enabled: true,
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.compos",
+            ],
+        },
+        ndk: {
+            enabled: true,
+            apps_enabled: false,
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.compos",
+            ],
+        }
+    },
+}
diff --git a/diced/aidl/android/security/dice/IDiceMaintenance.aidl b/diced/aidl/android/security/dice/IDiceMaintenance.aidl
new file mode 100644
index 0000000..c81fdea
--- /dev/null
+++ b/diced/aidl/android/security/dice/IDiceMaintenance.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.security.dice;
+
+import android.hardware.security.dice.InputValues;
+
+/**
+ * The maintenance allows callers to prompt the DICE node to demote itself.
+ *
+ * @hide
+ */
+@SensitiveData
+interface IDiceMaintenance {
+    /**
+     * The implementation must demote itself by deriving new effective artifacts
+     * based on the list of input data passed to the function.
+     * As opposed to the IDiceNode::demote, this function effects all clients of
+     * the implementation.
+     *
+     * ## Error as service specific exception:
+     *     ResponseCode::PERMISSION_DENIED if the caller does not have the demote_self permission.
+     *     May produce any ResponseCode if anything went wrong.
+     */
+    void demoteSelf(in InputValues[] input_values);
+}
diff --git a/diced/aidl/android/security/dice/IDiceNode.aidl b/diced/aidl/android/security/dice/IDiceNode.aidl
new file mode 100644
index 0000000..2b3ef76
--- /dev/null
+++ b/diced/aidl/android/security/dice/IDiceNode.aidl
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.security.dice;
+
+import android.hardware.security.dice.Bcc;
+import android.hardware.security.dice.BccHandover;
+import android.hardware.security.dice.InputValues;
+import android.hardware.security.dice.Signature;
+
+/**
+ * An implementation of IDiceNode provides access to DICE secrets to its clients. It
+ * uses binder's caller UID and security context to identify its callers and assures
+ * That clients can only access their specific DICE secrets.
+ * It may operate in two different modes, resident mode and proxy mode.
+ *
+ * ## Resident mode.
+ * In resident mode, the node is in possession of the secrets corresponding to its level in
+ * the dice tree. It can act as root of the sub tree that it serves. The secrets are memory
+ * resident in the node. It identifies its callers and prepends the caller's identity to the
+ * request's vector of input values. It then derives the required secrets by iterating through
+ * the request's vector of input values in ascending order.
+ *
+ * ## Proxy mode.
+ * In proxy mode, the node has a connection to a parent node. It serves its callers by verifying
+ * their identity, by prefixing the client's vector of input values with client's identity, and
+ * forwarding the request to the next level up.
+ *
+ * The modes are implementation details that are completely transparent to the clients.
+ *
+ * Privacy: Unprivileged apps may not use this service ever because it may provide access to a
+ * device specific id that is stable across reinstalls, reboots, and applications.
+ *
+ * @hide
+ */
+@SensitiveData
+interface IDiceNode {
+    /**
+     * Uses the a key derived from the caller's attestation secret to sign the payload using
+     * RFC 8032 PureEd25519 and returns the signature. The payload is limited to 1024 bytes.
+     *
+     * ## Error as service specific exception:
+     *     ResponseCode::PERMISSION_DENIED if the caller does not have the use_sign permission.
+     */
+    Signature sign(in InputValues[] id, in byte[] payload);
+
+    /**
+     * Returns the attestation certificate chain of the caller if `inputValues` is empty or the
+     * chain to the given child of the caller identified by the `inputValues` vector.
+     *
+     * ## Error as service specific exception:
+     *     ResponseCode::PERMISSION_DENIED if the caller does not have the get_attestation_chain
+     *          permission.
+     */
+    Bcc getAttestationChain(in InputValues[] inputValues);
+
+    /**
+     * This function allows a client to become a resident node. Called with empty InputValues
+     * vectors, an implementation returns the client's DICE secrets. If inputValues is
+     * not empty, the appropriate derivations are performed starting from the client's level.
+     * The function must never return secrets pertaining to the implementation or a parent
+     * thereof in the DICE hierarchy.
+     *
+     * ## Error as service specific exception:
+     *     ResponseCode::PERMISSION_DENIED if the implementation does not allow resident nodes
+     *     at the client's level.
+     */
+    BccHandover derive(in InputValues[] inputValues);
+
+    /**
+     * The client demotes itself to the given identity. When serving the calling client,
+     * the implementation must append the given identities. Essentially, the client assumes
+     * the identity of one of its children. This operation is not reversible, i.e., there
+     * is no promotion. Further demotion is possible.
+     *
+     * If the operation fails for any reason. No further services must be provided. Ideally,
+     * a device shutdown/reboot is triggered.
+     *
+     * ## Error as service specific exception:
+     *     ResponseCode::PERMISSION_DENIED if the caller does not have the demote permission.
+     */
+    void demote(in InputValues[] inputValues);
+}
diff --git a/diced/aidl/android/security/dice/ResponseCode.aidl b/diced/aidl/android/security/dice/ResponseCode.aidl
new file mode 100644
index 0000000..7c66058
--- /dev/null
+++ b/diced/aidl/android/security/dice/ResponseCode.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.security.dice;
+
+@Backing(type="int")
+/**
+ * Service specific error codes.
+ * @hide
+ */
+enum ResponseCode {
+    /**
+     * The caller has insufficient privilege to access the DICE API.
+     */
+    PERMISSION_DENIED = 1,
+    /**
+     * An unexpected error occurred, likely with IO or IPC.
+     */
+    SYSTEM_ERROR = 2,
+    /**
+     * Returned if the called function is not implemented.
+     */
+    NOT_IMPLEMENTED = 3,
+}
diff --git a/diced/diced.microdroid.rc b/diced/diced.microdroid.rc
new file mode 100644
index 0000000..2226f47
--- /dev/null
+++ b/diced/diced.microdroid.rc
@@ -0,0 +1,13 @@
+# Start the Diced service.
+#
+# See system/core/init/README.md for information on the init.rc language.
+
+service diced /system/bin/diced.microdroid
+    class main
+    user diced
+    group diced
+    # The diced service must not be allowed to restart.
+    # If it crashes for any reason security critical state is lost.
+    # The only remedy is to restart the device.
+    oneshot
+    writepid /dev/cpuset/foreground/tasks
diff --git a/diced/diced.rc b/diced/diced.rc
new file mode 100644
index 0000000..8c43fa5
--- /dev/null
+++ b/diced/diced.rc
@@ -0,0 +1,13 @@
+# Start the Diced service.
+#
+# See system/core/init/README.md for information on the init.rc language.
+
+service diced /system/bin/diced
+    class main
+    user diced
+    group diced
+    # The diced service must not be allowed to restart.
+    # If it crashes for any reason security critical state is lost.
+    # The only remedy is to restart the device.
+    oneshot
+    writepid /dev/cpuset/foreground/tasks
diff --git a/diced/open_dice_cbor/Android.bp b/diced/open_dice_cbor/Android.bp
new file mode 100644
index 0000000..3e67045
--- /dev/null
+++ b/diced/open_dice_cbor/Android.bp
@@ -0,0 +1,55 @@
+// 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.
+
+package {
+    default_applicable_licenses: ["system_security_license"],
+}
+
+rust_library {
+    name: "libdiced_open_dice_cbor",
+    crate_name: "diced_open_dice_cbor",
+    srcs: ["lib.rs"],
+
+    rustlibs: [
+        // For ZVec
+        "libkeystore2_crypto_rust",
+        "libopen_dice_bcc_bindgen",
+        "libopen_dice_cbor_bindgen",
+        "libthiserror",
+    ],
+    static_libs: [
+        "libopen_dice_bcc",
+        "libopen_dice_cbor",
+    ],
+    vendor_available: true,
+}
+
+rust_test {
+    name: "diced_open_dice_cbor_test",
+    crate_name: "diced_open_dice_cbor_test",
+    srcs: ["lib.rs"],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    rustlibs: [
+        "libdiced_sample_inputs",
+        "libkeystore2_crypto_rust",
+        "libopen_dice_bcc_bindgen",
+        "libopen_dice_cbor_bindgen",
+        "libthiserror",
+    ],
+    static_libs: [
+        "libopen_dice_bcc",
+        "libopen_dice_cbor",
+    ],
+}
diff --git a/diced/open_dice_cbor/lib.rs b/diced/open_dice_cbor/lib.rs
new file mode 100644
index 0000000..7122ca5
--- /dev/null
+++ b/diced/open_dice_cbor/lib.rs
@@ -0,0 +1,1037 @@
+// 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 safe wrappers around the public API of libopen-dice.
+//! ## Example:
+//! ```
+//! use diced_open_dice_cbor as dice;
+//!
+//! let context = dice::dice::OpenDiceCborContext::new()
+//! let parent_cdi_attest = [1u8, dice::CDI_SIZE];
+//! let parent_cdi_seal = [2u8, dice::CDI_SIZE];
+//! let input_values = dice::InputValuesOwned {
+//!     code_hash: [3u8, dice::HASH_SIZE],
+//!     config: dice::ConfigOwned::Descriptor("My descriptor".as_bytes().to_vec()),
+//!     authority_hash: [0u8, dice::HASH_SIZE],
+//!     mode: dice::Mode::Normal,
+//!     hidden: [0u8, dice::HIDDEN_SIZE],
+//! };
+//! let (cdi_attest, cdi_seal, cert_chain) = context
+//!     .main_flow(&parent_cdi_attest, &parent_cdi_seal, &input_values)?;
+//! ```
+
+use keystore2_crypto::{zvec, ZVec};
+use open_dice_bcc_bindgen::BccMainFlow;
+use open_dice_cbor_bindgen::{
+    DiceConfigType, DiceDeriveCdiCertificateId, DiceDeriveCdiPrivateKeySeed,
+    DiceGenerateCertificate, DiceHash, DiceInputValues, DiceKdf, DiceKeypairFromSeed, DiceMainFlow,
+    DiceMode, DiceResult, DiceSign, DiceVerify, DICE_CDI_SIZE, DICE_HASH_SIZE, DICE_HIDDEN_SIZE,
+    DICE_ID_SIZE, DICE_INLINE_CONFIG_SIZE, DICE_PRIVATE_KEY_SEED_SIZE, DICE_PRIVATE_KEY_SIZE,
+    DICE_PUBLIC_KEY_SIZE, DICE_SIGNATURE_SIZE,
+};
+use open_dice_cbor_bindgen::{
+    DiceConfigType_kDiceConfigTypeDescriptor as DICE_CONFIG_TYPE_DESCRIPTOR,
+    DiceConfigType_kDiceConfigTypeInline as DICE_CONFIG_TYPE_INLINE,
+    DiceMode_kDiceModeDebug as DICE_MODE_DEBUG,
+    DiceMode_kDiceModeMaintenance as DICE_MODE_RECOVERY,
+    DiceMode_kDiceModeNormal as DICE_MODE_NORMAL,
+    DiceMode_kDiceModeNotInitialized as DICE_MODE_NOT_CONFIGURED,
+    DiceResult_kDiceResultBufferTooSmall as DICE_RESULT_BUFFER_TOO_SMALL,
+    DiceResult_kDiceResultInvalidInput as DICE_RESULT_INVALID_INPUT,
+    DiceResult_kDiceResultOk as DICE_RESULT_OK,
+    DiceResult_kDiceResultPlatformError as DICE_RESULT_PLATFORM_ERROR,
+};
+use std::ffi::{c_void, NulError};
+
+/// The size of a DICE hash.
+pub const HASH_SIZE: usize = DICE_HASH_SIZE as usize;
+/// The size of the DICE hidden value.
+pub const HIDDEN_SIZE: usize = DICE_HIDDEN_SIZE as usize;
+/// The size of a DICE inline config.
+pub const INLINE_CONFIG_SIZE: usize = DICE_INLINE_CONFIG_SIZE as usize;
+/// The size of a private key seed.
+pub const PRIVATE_KEY_SEED_SIZE: usize = DICE_PRIVATE_KEY_SEED_SIZE as usize;
+/// The size of a CDI.
+pub const CDI_SIZE: usize = DICE_CDI_SIZE as usize;
+/// The size of an ID.
+pub const ID_SIZE: usize = DICE_ID_SIZE as usize;
+/// The size of a private key.
+pub const PRIVATE_KEY_SIZE: usize = DICE_PRIVATE_KEY_SIZE as usize;
+/// The size of a public key.
+pub const PUBLIC_KEY_SIZE: usize = DICE_PUBLIC_KEY_SIZE as usize;
+/// The size of a signature.
+pub const SIGNATURE_SIZE: usize = DICE_SIGNATURE_SIZE as usize;
+
+/// Open dice wrapper error type.
+#[derive(Debug, thiserror::Error, PartialEq)]
+pub enum Error {
+    /// The libopen-dice backend reported InvalidInput.
+    #[error("Open dice backend: Invalid input")]
+    InvalidInput,
+    /// The libopen-dice backend reported BufferTooSmall.
+    #[error("Open dice backend: Buffer too small")]
+    BufferTooSmall,
+    /// The libopen-dice backend reported PlatformError.
+    #[error("Open dice backend: Platform error")]
+    PlatformError,
+    /// The libopen-dice backend reported an error that is outside of the defined range of errors.
+    /// The returned error code is embedded in this value.
+    #[error("Open dice backend returned an unexpected error code: {0:?}")]
+    Unexpected(u32),
+
+    /// The allocation of a ZVec failed. Most likely due to a failure during the call to mlock.
+    #[error("ZVec allocation failed")]
+    ZVec(#[from] zvec::Error),
+
+    /// Functions that have to convert str to CString may fail if the string has an interior
+    /// nul byte.
+    #[error("Input string has an interior nul byte.")]
+    CStrNulError(#[from] NulError),
+}
+
+/// Open dice result type.
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl From<DiceResult> for Error {
+    fn from(result: DiceResult) -> Self {
+        match result {
+            DICE_RESULT_INVALID_INPUT => Error::InvalidInput,
+            DICE_RESULT_BUFFER_TOO_SMALL => Error::BufferTooSmall,
+            DICE_RESULT_PLATFORM_ERROR => Error::PlatformError,
+            r => Error::Unexpected(r),
+        }
+    }
+}
+
+fn check_result(result: DiceResult) -> Result<()> {
+    if result == DICE_RESULT_OK {
+        Ok(())
+    } else {
+        Err(result.into())
+    }
+}
+
+/// Configuration descriptor for dice input values.
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum Config<'a> {
+    /// A reference to an inline descriptor.
+    Inline(&'a [u8; INLINE_CONFIG_SIZE]),
+    /// A reference to a free form descriptor that will be hashed by the implementation.
+    Descriptor(&'a [u8]),
+}
+
+enum ConfigOwned {
+    Inline([u8; INLINE_CONFIG_SIZE]),
+    Descriptor(Vec<u8>),
+}
+
+impl Config<'_> {
+    fn get_type(&self) -> DiceConfigType {
+        match self {
+            Self::Inline(_) => DICE_CONFIG_TYPE_INLINE,
+            Self::Descriptor(_) => DICE_CONFIG_TYPE_DESCRIPTOR,
+        }
+    }
+
+    fn get_inline(&self) -> [u8; INLINE_CONFIG_SIZE] {
+        match self {
+            Self::Inline(inline) => **inline,
+            _ => [0u8; INLINE_CONFIG_SIZE],
+        }
+    }
+
+    fn get_descriptor_as_ptr(&self) -> *const u8 {
+        match self {
+            Self::Descriptor(descriptor) => descriptor.as_ptr(),
+            _ => std::ptr::null(),
+        }
+    }
+
+    fn get_descriptor_size(&self) -> usize {
+        match self {
+            Self::Descriptor(descriptor) => descriptor.len(),
+            _ => 0,
+        }
+    }
+}
+
+impl From<Config<'_>> for ConfigOwned {
+    fn from(config: Config) -> Self {
+        match config {
+            Config::Inline(inline) => ConfigOwned::Inline(*inline),
+            Config::Descriptor(descriptor) => ConfigOwned::Descriptor(descriptor.to_owned()),
+        }
+    }
+}
+
+/// DICE modes as defined here:
+/// https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/specification.md#mode-value-details
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum Mode {
+    /// See documentation linked above.
+    NotConfigured = 0,
+    /// See documentation linked above.
+    Normal = 1,
+    /// See documentation linked above.
+    Debug = 2,
+    /// See documentation linked above.
+    Recovery = 3,
+}
+
+impl Mode {
+    fn get_internal(&self) -> DiceMode {
+        match self {
+            Self::NotConfigured => DICE_MODE_NOT_CONFIGURED,
+            Self::Normal => DICE_MODE_NORMAL,
+            Self::Debug => DICE_MODE_DEBUG,
+            Self::Recovery => DICE_MODE_RECOVERY,
+        }
+    }
+}
+
+/// This trait allows API users to supply DICE input values without copying.
+pub trait InputValues {
+    /// Returns the code hash.
+    fn code_hash(&self) -> &[u8; HASH_SIZE];
+    /// Returns the config.
+    fn config(&self) -> Config;
+    /// Returns the authority hash.
+    fn authority_hash(&self) -> &[u8; HASH_SIZE];
+    /// Returns the authority descriptor.
+    fn authority_descriptor(&self) -> Option<&[u8]>;
+    /// Returns the mode.
+    fn mode(&self) -> Mode;
+    /// Returns the hidden value.
+    fn hidden(&self) -> &[u8; HIDDEN_SIZE];
+}
+
+/// An owning convenience type implementing `InputValues`.
+pub struct InputValuesOwned {
+    code_hash: [u8; HASH_SIZE],
+    config: ConfigOwned,
+    authority_hash: [u8; HASH_SIZE],
+    authority_descriptor: Option<Vec<u8>>,
+    mode: Mode,
+    hidden: [u8; HIDDEN_SIZE],
+}
+
+impl InputValuesOwned {
+    /// Construct a new instance of InputValuesOwned.
+    pub fn new(
+        code_hash: [u8; HASH_SIZE],
+        config: Config,
+        authority_hash: [u8; HASH_SIZE],
+        authority_descriptor: Option<Vec<u8>>,
+        mode: Mode,
+        hidden: [u8; HIDDEN_SIZE],
+    ) -> Self {
+        Self {
+            code_hash,
+            config: config.into(),
+            authority_hash,
+            authority_descriptor,
+            mode,
+            hidden,
+        }
+    }
+}
+
+impl InputValues for InputValuesOwned {
+    fn code_hash(&self) -> &[u8; HASH_SIZE] {
+        &self.code_hash
+    }
+    fn config(&self) -> Config {
+        match &self.config {
+            ConfigOwned::Inline(inline) => Config::Inline(inline),
+            ConfigOwned::Descriptor(descriptor) => Config::Descriptor(descriptor.as_slice()),
+        }
+    }
+    fn authority_hash(&self) -> &[u8; HASH_SIZE] {
+        &self.authority_hash
+    }
+    fn authority_descriptor(&self) -> Option<&[u8]> {
+        self.authority_descriptor.as_deref()
+    }
+    fn mode(&self) -> Mode {
+        self.mode
+    }
+    fn hidden(&self) -> &[u8; HIDDEN_SIZE] {
+        &self.hidden
+    }
+}
+
+fn call_with_input_values<T: InputValues + ?Sized, F, R>(input_values: &T, f: F) -> Result<R>
+where
+    F: FnOnce(*const DiceInputValues) -> Result<R>,
+{
+    let input_values = DiceInputValues {
+        code_hash: *input_values.code_hash(),
+        code_descriptor: std::ptr::null(),
+        code_descriptor_size: 0,
+        config_type: input_values.config().get_type(),
+        config_value: input_values.config().get_inline(),
+        config_descriptor: input_values.config().get_descriptor_as_ptr(),
+        config_descriptor_size: input_values.config().get_descriptor_size(),
+        authority_hash: *input_values.authority_hash(),
+        authority_descriptor: input_values
+            .authority_descriptor()
+            .map_or_else(std::ptr::null, <[u8]>::as_ptr),
+        authority_descriptor_size: input_values.authority_descriptor().map_or(0, <[u8]>::len),
+        mode: input_values.mode().get_internal(),
+        hidden: *input_values.hidden(),
+    };
+
+    f(&input_values as *const DiceInputValues)
+}
+
+/// Multiple of the open dice function required preallocated output buffer
+/// which may be too small, this function implements the retry logic to handle
+/// too small buffer allocations.
+/// The callback `F` must expect a mutable reference to a buffer and a size hint
+/// field. The callback is called repeatedly as long as it returns
+/// `Err(Error::BufferTooSmall)`. If the size hint remains 0, the buffer size is
+/// doubled with each iteration. If the size hint is set by the callback, the buffer
+/// will be set to accommodate at least this many bytes.
+/// If the callback returns `Ok(())`, the buffer is truncated to the size hint
+/// exactly.
+/// The function panics if the callback returns `Ok(())` and the size hint is
+/// larger than the buffer size.
+fn retry_while_adjusting_output_buffer<F>(mut f: F) -> Result<Vec<u8>>
+where
+    F: FnMut(&mut Vec<u8>, &mut usize) -> Result<()>,
+{
+    let mut buffer = vec![0; INITIAL_OUT_BUFFER_SIZE];
+    let mut actual_size: usize = 0;
+    loop {
+        match f(&mut buffer, &mut actual_size) {
+            // If Error::BufferTooSmall was returned, the allocated certificate
+            // buffer was to small for the output. So the buffer is resized to the actual
+            // size, and a second attempt is made with the new buffer.
+            Err(Error::BufferTooSmall) => {
+                let new_size = if actual_size == 0 {
+                    // Due to an off spec implementation of open dice cbor, actual size
+                    // does not return the required size if the buffer was too small. So
+                    // we have to try and approach it gradually.
+                    buffer.len() * 2
+                } else {
+                    actual_size
+                };
+                buffer.resize(new_size, 0);
+                continue;
+            }
+            Err(e) => return Err(e),
+            Ok(()) => {
+                if actual_size > buffer.len() {
+                    panic!(
+                        "actual_size larger than buffer size: open-dice function
+                         may have written past the end of the buffer."
+                    );
+                }
+                // Truncate the certificate buffer to the actual size because it may be
+                // smaller than the original allocation.
+                buffer.truncate(actual_size);
+                return Ok(buffer);
+            }
+        }
+    }
+}
+
+/// Some libopen-dice variants use a context. Developers that want to customize these
+/// bindings may want to implement their own Context factory that creates a context
+/// useable by their preferred backend.
+pub trait Context {
+    /// # Safety
+    /// The return value of get_context is passed to any open dice function.
+    /// Implementations must explain why the context pointer returned is safe
+    /// to be used by the open dice library.
+    unsafe fn get_context(&mut self) -> *mut c_void;
+}
+
+impl<T: Context + Send> ContextImpl for T {}
+
+/// This represents a context for the open dice library. The wrapped open dice instance, which
+/// is based on boringssl and cbor, does not use a context, so that this type is empty.
+#[derive(Default)]
+pub struct OpenDiceCborContext();
+
+impl OpenDiceCborContext {
+    /// Construct a new instance of OpenDiceCborContext.
+    pub fn new() -> Self {
+        Default::default()
+    }
+}
+
+impl Context for OpenDiceCborContext {
+    unsafe fn get_context(&mut self) -> *mut c_void {
+        // # Safety
+        // The open dice cbor implementation does not use a context. It is safe
+        // to return NULL.
+        std::ptr::null_mut()
+    }
+}
+
+/// Type alias for ZVec indicating that it holds a CDI_ATTEST secret.
+pub type CdiAttest = ZVec;
+
+/// Type alias for ZVec indicating that it holds a CDI_SEAL secret.
+pub type CdiSeal = ZVec;
+
+/// Type alias for Vec<u8> indicating that it hold a DICE certificate.
+pub type Cert = Vec<u8>;
+
+/// Type alias for Vec<u8> indicating that it holds a BCC certificate chain.
+pub type Bcc = Vec<u8>;
+
+const INITIAL_OUT_BUFFER_SIZE: usize = 1024;
+
+/// ContextImpl is a mixin trait that implements the safe wrappers around the open dice
+/// library calls. Implementations must implement Context::get_context(). As of
+/// this writing, the only implementation is OpenDiceCborContext, which returns NULL.
+pub trait ContextImpl: Context + Send {
+    /// Safe wrapper around open-dice DiceDeriveCdiPrivateKeySeed, see open dice
+    /// documentation for details.
+    fn derive_cdi_private_key_seed(&mut self, cdi_attest: &[u8; CDI_SIZE]) -> Result<ZVec> {
+        let mut seed = ZVec::new(PRIVATE_KEY_SEED_SIZE)?;
+        // SAFETY:
+        // * The first context argument may be NULL and is unused by the wrapped
+        //   implementation.
+        // * The second argument is expected to be a const array of size CDI_SIZE.
+        // * The third argument is expected to be a non const array of size
+        //   PRIVATE_KEY_SEED_SIZE which is fulfilled if the call to ZVec::new above
+        //   succeeds.
+        // * No pointers are expected to be valid beyond the scope of the function
+        //   call.
+        check_result(unsafe {
+            DiceDeriveCdiPrivateKeySeed(self.get_context(), cdi_attest.as_ptr(), seed.as_mut_ptr())
+        })?;
+        Ok(seed)
+    }
+
+    /// Safe wrapper around open-dice DiceDeriveCdiCertificateId, see open dice
+    /// documentation for details.
+    fn derive_cdi_certificate_id(&mut self, cdi_public_key: &[u8]) -> Result<ZVec> {
+        let mut id = ZVec::new(ID_SIZE)?;
+        // SAFETY:
+        // * The first context argument may be NULL and is unused by the wrapped
+        //   implementation.
+        // * The second argument is expected to be a const array with a size given by the
+        //   third argument.
+        // * The fourth argument is expected to be a non const array of size
+        //   ID_SIZE which is fulfilled if the call to ZVec::new above succeeds.
+        // * No pointers are expected to be valid beyond the scope of the function
+        //   call.
+        check_result(unsafe {
+            DiceDeriveCdiCertificateId(
+                self.get_context(),
+                cdi_public_key.as_ptr(),
+                cdi_public_key.len(),
+                id.as_mut_ptr(),
+            )
+        })?;
+        Ok(id)
+    }
+
+    /// Safe wrapper around open-dice DiceMainFlow, see open dice
+    /// documentation for details.
+    /// Returns a tuple of:
+    ///  * The next attestation CDI,
+    ///  * the next seal CDI, and
+    ///  * the next attestation certificate.
+    /// `(next_attest_cdi, next_seal_cdi, next_attestation_cert)`
+    fn main_flow<T: InputValues + ?Sized>(
+        &mut self,
+        current_cdi_attest: &[u8; CDI_SIZE],
+        current_cdi_seal: &[u8; CDI_SIZE],
+        input_values: &T,
+    ) -> Result<(CdiAttest, CdiSeal, Cert)> {
+        let mut next_attest = CdiAttest::new(CDI_SIZE)?;
+        let mut next_seal = CdiSeal::new(CDI_SIZE)?;
+
+        // SAFETY (DiceMainFlow):
+        // * The first context argument may be NULL and is unused by the wrapped
+        //   implementation.
+        // * The second argument and the third argument are const arrays of size CDI_SIZE.
+        //   This is fulfilled as per the definition of the arguments `current_cdi_attest`
+        //   and `current_cdi_seal.
+        // * The fourth argument is a pointer to `DiceInputValues`. It, and its indirect
+        //   references must be valid for the duration of the function call which
+        //   is guaranteed by `call_with_input_values` which puts `DiceInputValues`
+        //   on the stack and initializes it from the `input_values` argument which
+        //   implements the `InputValues` trait.
+        // * The fifth and sixth argument are the length of and the pointer to the
+        //   allocated certificate buffer respectively. They are used to return
+        //   the generated certificate.
+        // * The seventh argument is a pointer to a mutable usize object. It is
+        //   used to return the actual size of the output certificate.
+        // * The eighth argument and the ninth argument are pointers to mutable buffers of size
+        //   CDI_SIZE. This is fulfilled if the allocation above succeeded.
+        // * No pointers are expected to be valid beyond the scope of the function
+        //   call.
+        call_with_input_values(input_values, |input_values| {
+            let cert = retry_while_adjusting_output_buffer(|cert, actual_size| {
+                check_result(unsafe {
+                    DiceMainFlow(
+                        self.get_context(),
+                        current_cdi_attest.as_ptr(),
+                        current_cdi_seal.as_ptr(),
+                        input_values,
+                        cert.len(),
+                        cert.as_mut_ptr(),
+                        actual_size as *mut _,
+                        next_attest.as_mut_ptr(),
+                        next_seal.as_mut_ptr(),
+                    )
+                })
+            })?;
+            Ok((next_attest, next_seal, cert))
+        })
+    }
+
+    /// Safe wrapper around open-dice DiceHash, see open dice
+    /// documentation for details.
+    fn hash(&mut self, input: &[u8]) -> Result<Vec<u8>> {
+        let mut output: Vec<u8> = vec![0; HASH_SIZE];
+
+        // SAFETY:
+        // * The first context argument may be NULL and is unused by the wrapped
+        //   implementation.
+        // * The second argument and the third argument are the pointer to and length of the given
+        //   input buffer respectively.
+        // * The fourth argument must be a pointer to a mutable buffer of size HASH_SIZE
+        //   which is fulfilled by the allocation above.
+        check_result(unsafe {
+            DiceHash(self.get_context(), input.as_ptr(), input.len(), output.as_mut_ptr())
+        })?;
+        Ok(output)
+    }
+
+    /// Safe wrapper around open-dice DiceKdf, see open dice
+    /// documentation for details.
+    fn kdf(&mut self, length: usize, input_key: &[u8], salt: &[u8], info: &[u8]) -> Result<ZVec> {
+        let mut output = ZVec::new(length)?;
+
+        // SAFETY:
+        // * The first context argument may be NULL and is unused by the wrapped
+        //   implementation.
+        // * The second argument is primitive.
+        // * The third argument and the fourth argument are the pointer to and length of the given
+        //   input key.
+        // * The fifth argument and the sixth argument are the pointer to and length of the given
+        //   salt.
+        // * The seventh argument and the eighth argument are the pointer to and length of the
+        //   given info field.
+        // * The ninth argument is a pointer to the output buffer which must have the
+        //   length given by the `length` argument (see second argument). This is
+        //   fulfilled if the allocation of `output` succeeds.
+        // * All pointers must be valid for the duration of the function call, but not
+        //   longer.
+        check_result(unsafe {
+            DiceKdf(
+                self.get_context(),
+                length,
+                input_key.as_ptr(),
+                input_key.len(),
+                salt.as_ptr(),
+                salt.len(),
+                info.as_ptr(),
+                info.len(),
+                output.as_mut_ptr(),
+            )
+        })?;
+        Ok(output)
+    }
+
+    /// Safe wrapper around open-dice DiceKeyPairFromSeed, see open dice
+    /// documentation for details.
+    fn keypair_from_seed(&mut self, seed: &[u8; PRIVATE_KEY_SEED_SIZE]) -> Result<(Vec<u8>, ZVec)> {
+        let mut private_key = ZVec::new(PRIVATE_KEY_SIZE)?;
+        let mut public_key = vec![0u8; PUBLIC_KEY_SIZE];
+
+        // SAFETY:
+        // * The first context argument may be NULL and is unused by the wrapped
+        //   implementation.
+        // * The second argument is a pointer to a const buffer of size `PRIVATE_KEY_SEED_SIZE`
+        //   fulfilled by the definition of the argument.
+        // * The third argument and the fourth argument are mutable buffers of size
+        //   `PRIVATE_KEY_SIZE` and `PUBLIC_KEY_SIZE` respectively. This is fulfilled by the
+        //   allocations above.
+        // * All pointers must be valid for the duration of the function call but not beyond.
+        check_result(unsafe {
+            DiceKeypairFromSeed(
+                self.get_context(),
+                seed.as_ptr(),
+                public_key.as_mut_ptr(),
+                private_key.as_mut_ptr(),
+            )
+        })?;
+        Ok((public_key, private_key))
+    }
+
+    /// Safe wrapper around open-dice DiceSign, see open dice
+    /// documentation for details.
+    fn sign(&mut self, message: &[u8], private_key: &[u8; PRIVATE_KEY_SIZE]) -> Result<Vec<u8>> {
+        let mut signature = vec![0u8; SIGNATURE_SIZE];
+
+        // SAFETY:
+        // * The first context argument may be NULL and is unused by the wrapped
+        //   implementation.
+        // * The second argument and the third argument are the pointer to and length of the given
+        //   message buffer.
+        // * The fourth argument is a const buffer of size `PRIVATE_KEY_SIZE`. This is fulfilled
+        //   by the definition of `private key`.
+        // * The fifth argument is mutable buffer of size `SIGNATURE_SIZE`. This is fulfilled
+        //   by the allocation above.
+        // * All pointers must be valid for the duration of the function call but not beyond.
+        check_result(unsafe {
+            DiceSign(
+                self.get_context(),
+                message.as_ptr(),
+                message.len(),
+                private_key.as_ptr(),
+                signature.as_mut_ptr(),
+            )
+        })?;
+        Ok(signature)
+    }
+
+    /// Safe wrapper around open-dice DiceVerify, see open dice
+    /// documentation for details.
+    fn verify(
+        &mut self,
+        message: &[u8],
+        signature: &[u8; SIGNATURE_SIZE],
+        public_key: &[u8; PUBLIC_KEY_SIZE],
+    ) -> Result<()> {
+        // SAFETY:
+        // * The first context argument may be NULL and is unused by the wrapped
+        //   implementation.
+        // * The second argument and the third argument are the pointer to and length of the given
+        //   message buffer.
+        // * The fourth argument is a const buffer of size `SIGNATURE_SIZE`. This is fulfilled
+        //   by the definition of `signature`.
+        // * The fifth argument is a const buffer of size `PUBLIC_KEY_SIZE`. This is fulfilled
+        //   by the definition of `public_key`.
+        // * All pointers must be valid for the duration of the function call but not beyond.
+        check_result(unsafe {
+            DiceVerify(
+                self.get_context(),
+                message.as_ptr(),
+                message.len(),
+                signature.as_ptr(),
+                public_key.as_ptr(),
+            )
+        })
+    }
+
+    /// Safe wrapper around open-dice DiceGenerateCertificate, see open dice
+    /// documentation for details.
+    fn generate_certificate<T: InputValues>(
+        &mut self,
+        subject_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+        authority_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+        input_values: &T,
+    ) -> Result<Vec<u8>> {
+        // SAFETY (DiceMainFlow):
+        // * The first context argument may be NULL and is unused by the wrapped
+        //   implementation.
+        // * The second argument and the third argument are const arrays of size
+        //   `PRIVATE_KEY_SEED_SIZE`. This is fulfilled as per the definition of the arguments.
+        // * The fourth argument is a pointer to `DiceInputValues` it, and its indirect
+        //   references must be valid for the duration of the function call which
+        //   is guaranteed by `call_with_input_values` which puts `DiceInputValues`
+        //   on the stack and initializes it from the `input_values` argument which
+        //   implements the `InputValues` trait.
+        // * The fifth argument and the sixth argument are the length of and the pointer to the
+        //   allocated certificate buffer respectively. They are used to return
+        //   the generated certificate.
+        // * The seventh argument is a pointer to a mutable usize object. It is
+        //   used to return the actual size of the output certificate.
+        // * All pointers must be valid for the duration of the function call but not beyond.
+        call_with_input_values(input_values, |input_values| {
+            let cert = retry_while_adjusting_output_buffer(|cert, actual_size| {
+                check_result(unsafe {
+                    DiceGenerateCertificate(
+                        self.get_context(),
+                        subject_private_key_seed.as_ptr(),
+                        authority_private_key_seed.as_ptr(),
+                        input_values,
+                        cert.len(),
+                        cert.as_mut_ptr(),
+                        actual_size as *mut _,
+                    )
+                })
+            })?;
+            Ok(cert)
+        })
+    }
+
+    /// Safe wrapper around open-dice BccDiceMainFlow, see open dice
+    /// documentation for details.
+    /// Returns a tuple of:
+    ///  * The next attestation CDI,
+    ///  * the next seal CDI, and
+    ///  * the next bcc adding the new certificate to the given bcc.
+    /// `(next_attest_cdi, next_seal_cdi, next_bcc)`
+    fn bcc_main_flow<T: InputValues + ?Sized>(
+        &mut self,
+        current_cdi_attest: &[u8; CDI_SIZE],
+        current_cdi_seal: &[u8; CDI_SIZE],
+        bcc: &[u8],
+        input_values: &T,
+    ) -> Result<(CdiAttest, CdiSeal, Bcc)> {
+        let mut next_attest = CdiAttest::new(CDI_SIZE)?;
+        let mut next_seal = CdiSeal::new(CDI_SIZE)?;
+
+        // SAFETY (BccMainFlow):
+        // * The first context argument may be NULL and is unused by the wrapped
+        //   implementation.
+        // * The second argument and the third argument are const arrays of size CDI_SIZE.
+        //   This is fulfilled as per the definition of the arguments `current_cdi_attest`
+        //   and `current_cdi_seal`.
+        // * The fourth argument and the fifth argument are the pointer to and size of the buffer
+        //   holding the current bcc.
+        // * The sixth argument is a pointer to `DiceInputValues` it, and its indirect
+        //   references must be valid for the duration of the function call which
+        //   is guaranteed by `call_with_input_values` which puts `DiceInputValues`
+        //   on the stack and initializes it from the `input_values` argument which
+        //   implements the `InputValues` trait.
+        // * The seventh argument and the eighth argument are the length of and the pointer to the
+        //   allocated certificate buffer respectively. They are used to return the generated
+        //   certificate.
+        // * The ninth argument is a pointer to a mutable usize object. It is
+        //   used to return the actual size of the output certificate.
+        // * The tenth argument and the eleventh argument are pointers to mutable buffers of
+        //   size CDI_SIZE. This is fulfilled if the allocation above succeeded.
+        // * No pointers are expected to be valid beyond the scope of the function
+        //   call.
+        call_with_input_values(input_values, |input_values| {
+            let next_bcc = retry_while_adjusting_output_buffer(|next_bcc, actual_size| {
+                check_result(unsafe {
+                    BccMainFlow(
+                        self.get_context(),
+                        current_cdi_attest.as_ptr(),
+                        current_cdi_seal.as_ptr(),
+                        bcc.as_ptr(),
+                        bcc.len(),
+                        input_values,
+                        next_bcc.len(),
+                        next_bcc.as_mut_ptr(),
+                        actual_size as *mut _,
+                        next_attest.as_mut_ptr(),
+                        next_seal.as_mut_ptr(),
+                    )
+                })
+            })?;
+            Ok((next_attest, next_seal, next_bcc))
+        })
+    }
+}
+
+/// This submodule provides additional support for the Boot Certificate Chain (BCC)
+/// specification.
+/// See https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
+pub mod bcc {
+    use super::{check_result, retry_while_adjusting_output_buffer, Result};
+    use open_dice_bcc_bindgen::{
+        BccConfigValues, BccFormatConfigDescriptor, BCC_INPUT_COMPONENT_NAME,
+        BCC_INPUT_COMPONENT_VERSION, BCC_INPUT_RESETTABLE,
+    };
+    use std::ffi::CString;
+
+    /// Safe wrapper around BccFormatConfigDescriptor, see open dice documentation for details.
+    pub fn format_config_descriptor(
+        component_name: Option<&str>,
+        component_version: Option<u64>,
+        resettable: bool,
+    ) -> Result<Vec<u8>> {
+        let component_name = match component_name {
+            Some(n) => Some(CString::new(n)?),
+            None => None,
+        };
+        let input = BccConfigValues {
+            inputs: if component_name.is_some() { BCC_INPUT_COMPONENT_NAME } else { 0 }
+                | if component_version.is_some() { BCC_INPUT_COMPONENT_VERSION } else { 0 }
+                | if resettable { BCC_INPUT_RESETTABLE } else { 0 },
+            // SAFETY: The as_ref() in the line below is vital to keep the component_name object
+            //         alive. Removing as_ref will move the component_name and the pointer will
+            //         become invalid after this statement.
+            component_name: component_name.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
+            component_version: component_version.unwrap_or(0),
+        };
+
+        // SAFETY:
+        // * The first argument is a pointer to the BccConfigValues input assembled above.
+        //   It and its indirections must be valid for the duration of the function call.
+        // * The second argument and the third argument are the length of and the pointer to the
+        //   allocated output buffer respectively. The buffer must be at least as long
+        //   as indicated by the size argument.
+        // * The forth argument is a pointer to the actual size returned by the function.
+        // * All pointers must be valid for the duration of the function call but not beyond.
+        retry_while_adjusting_output_buffer(|config_descriptor, actual_size| {
+            check_result(unsafe {
+                BccFormatConfigDescriptor(
+                    &input as *const BccConfigValues,
+                    config_descriptor.len(),
+                    config_descriptor.as_mut_ptr(),
+                    actual_size as *mut _,
+                )
+            })
+        })
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use diced_sample_inputs::make_sample_bcc_and_cdis;
+    use std::convert::TryInto;
+
+    static SEED_TEST_VECTOR: &[u8] = &[
+        0xfa, 0x3c, 0x2f, 0x58, 0x37, 0xf5, 0x8e, 0x96, 0x16, 0x09, 0xf5, 0x22, 0xa1, 0xf1, 0xba,
+        0xaa, 0x19, 0x95, 0x01, 0x79, 0x2e, 0x60, 0x56, 0xaf, 0xf6, 0x41, 0xe7, 0xff, 0x48, 0xf5,
+        0x3a, 0x08, 0x84, 0x8a, 0x98, 0x85, 0x6d, 0xf5, 0x69, 0x21, 0x03, 0xcd, 0x09, 0xc3, 0x28,
+        0xd6, 0x06, 0xa7, 0x57, 0xbd, 0x48, 0x4b, 0x0f, 0x79, 0x0f, 0xf8, 0x2f, 0xf0, 0x0a, 0x41,
+        0x94, 0xd8, 0x8c, 0xa8,
+    ];
+
+    static CDI_ATTEST_TEST_VECTOR: &[u8] = &[
+        0xfa, 0x3c, 0x2f, 0x58, 0x37, 0xf5, 0x8e, 0x96, 0x16, 0x09, 0xf5, 0x22, 0xa1, 0xf1, 0xba,
+        0xaa, 0x19, 0x95, 0x01, 0x79, 0x2e, 0x60, 0x56, 0xaf, 0xf6, 0x41, 0xe7, 0xff, 0x48, 0xf5,
+        0x3a, 0x08,
+    ];
+    static CDI_PRIVATE_KEY_SEED_TEST_VECTOR: &[u8] = &[
+        0x5f, 0xcc, 0x8e, 0x1a, 0xd1, 0xc2, 0xb3, 0xe9, 0xfb, 0xe1, 0x68, 0xf0, 0xf6, 0x98, 0xfe,
+        0x0d, 0xee, 0xd4, 0xb5, 0x18, 0xcb, 0x59, 0x70, 0x2d, 0xee, 0x06, 0xe5, 0x70, 0xf1, 0x72,
+        0x02, 0x6e,
+    ];
+
+    static PUB_KEY_TEST_VECTOR: &[u8] = &[
+        0x47, 0x42, 0x4b, 0xbd, 0xd7, 0x23, 0xb4, 0xcd, 0xca, 0xe2, 0x8e, 0xdc, 0x6b, 0xfc, 0x23,
+        0xc9, 0x21, 0x5c, 0x48, 0x21, 0x47, 0xee, 0x5b, 0xfa, 0xaf, 0x88, 0x9a, 0x52, 0xf1, 0x61,
+        0x06, 0x37,
+    ];
+    static PRIV_KEY_TEST_VECTOR: &[u8] = &[
+        0x5f, 0xcc, 0x8e, 0x1a, 0xd1, 0xc2, 0xb3, 0xe9, 0xfb, 0xe1, 0x68, 0xf0, 0xf6, 0x98, 0xfe,
+        0x0d, 0xee, 0xd4, 0xb5, 0x18, 0xcb, 0x59, 0x70, 0x2d, 0xee, 0x06, 0xe5, 0x70, 0xf1, 0x72,
+        0x02, 0x6e, 0x47, 0x42, 0x4b, 0xbd, 0xd7, 0x23, 0xb4, 0xcd, 0xca, 0xe2, 0x8e, 0xdc, 0x6b,
+        0xfc, 0x23, 0xc9, 0x21, 0x5c, 0x48, 0x21, 0x47, 0xee, 0x5b, 0xfa, 0xaf, 0x88, 0x9a, 0x52,
+        0xf1, 0x61, 0x06, 0x37,
+    ];
+
+    static SIGNATURE_TEST_VECTOR: &[u8] = &[
+        0x44, 0xae, 0xcc, 0xe2, 0xb9, 0x96, 0x18, 0x39, 0x0e, 0x61, 0x0f, 0x53, 0x07, 0xbf, 0xf2,
+        0x32, 0x3d, 0x44, 0xd4, 0xf2, 0x07, 0x23, 0x30, 0x85, 0x32, 0x18, 0xd2, 0x69, 0xb8, 0x29,
+        0x3c, 0x26, 0xe6, 0x0d, 0x9c, 0xa5, 0xc2, 0x73, 0xcd, 0x8c, 0xb8, 0x3c, 0x3e, 0x5b, 0xfd,
+        0x62, 0x8d, 0xf6, 0xc4, 0x27, 0xa6, 0xe9, 0x11, 0x06, 0x5a, 0xb2, 0x2b, 0x64, 0xf7, 0xfc,
+        0xbb, 0xab, 0x4a, 0x0e,
+    ];
+
+    #[test]
+    fn hash_derive_sign_verify() {
+        let mut ctx = OpenDiceCborContext::new();
+        let seed = ctx.hash("MySeedString".as_bytes()).unwrap();
+        assert_eq!(seed, SEED_TEST_VECTOR);
+        let cdi_attest = &seed[..CDI_SIZE];
+        assert_eq!(cdi_attest, CDI_ATTEST_TEST_VECTOR);
+        let cdi_private_key_seed =
+            ctx.derive_cdi_private_key_seed(cdi_attest.try_into().unwrap()).unwrap();
+        assert_eq!(&cdi_private_key_seed[..], CDI_PRIVATE_KEY_SEED_TEST_VECTOR);
+        let (pub_key, priv_key) =
+            ctx.keypair_from_seed(cdi_private_key_seed[..].try_into().unwrap()).unwrap();
+        assert_eq!(&pub_key, PUB_KEY_TEST_VECTOR);
+        assert_eq!(&priv_key[..], PRIV_KEY_TEST_VECTOR);
+        let mut signature =
+            ctx.sign("MyMessage".as_bytes(), priv_key[..].try_into().unwrap()).unwrap();
+        assert_eq!(&signature, SIGNATURE_TEST_VECTOR);
+        assert!(ctx
+            .verify(
+                "MyMessage".as_bytes(),
+                signature[..].try_into().unwrap(),
+                pub_key[..].try_into().unwrap()
+            )
+            .is_ok());
+        assert!(ctx
+            .verify(
+                "MyMessage_fail".as_bytes(),
+                signature[..].try_into().unwrap(),
+                pub_key[..].try_into().unwrap()
+            )
+            .is_err());
+        signature[0] += 1;
+        assert!(ctx
+            .verify(
+                "MyMessage".as_bytes(),
+                signature[..].try_into().unwrap(),
+                pub_key[..].try_into().unwrap()
+            )
+            .is_err());
+    }
+
+    static SAMPLE_CDI_ATTEST_TEST_VECTOR: &[u8] = &[
+        0x3e, 0x57, 0x65, 0x5d, 0x48, 0x02, 0xbd, 0x5c, 0x66, 0xcc, 0x1f, 0x0f, 0xbe, 0x5e, 0x32,
+        0xb6, 0x9e, 0x3d, 0x04, 0xaf, 0x00, 0x15, 0xbc, 0xdd, 0x1f, 0xbc, 0x59, 0xe4, 0xc3, 0x87,
+        0x95, 0x5e,
+    ];
+
+    static SAMPLE_CDI_SEAL_TEST_VECTOR: &[u8] = &[
+        0x36, 0x1b, 0xd2, 0xb3, 0xc4, 0xda, 0x77, 0xb2, 0x9c, 0xba, 0x39, 0x53, 0x82, 0x93, 0xd9,
+        0xb8, 0x9f, 0x73, 0x2d, 0x27, 0x06, 0x15, 0xa8, 0xcb, 0x6d, 0x1d, 0xf2, 0xb1, 0x54, 0xbb,
+        0x62, 0xf1,
+    ];
+
+    static SAMPLE_BCC_TEST_VECTOR: &[u8] = &[
+        0x84, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0x3e, 0x85,
+        0xe5, 0x72, 0x75, 0x55, 0xe5, 0x1e, 0xe7, 0xf3, 0x35, 0x94, 0x8e, 0xbb, 0xbd, 0x74, 0x1e,
+        0x1d, 0xca, 0x49, 0x9c, 0x97, 0x39, 0x77, 0x06, 0xd3, 0xc8, 0x6e, 0x8b, 0xd7, 0x33, 0xf9,
+        0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x8a, 0xa9, 0x01, 0x78, 0x28, 0x34, 0x32,
+        0x64, 0x38, 0x38, 0x36, 0x34, 0x66, 0x39, 0x37, 0x62, 0x36, 0x35, 0x34, 0x37, 0x61, 0x35,
+        0x30, 0x63, 0x31, 0x65, 0x30, 0x61, 0x37, 0x34, 0x39, 0x66, 0x38, 0x65, 0x66, 0x38, 0x62,
+        0x38, 0x31, 0x65, 0x63, 0x36, 0x32, 0x61, 0x66, 0x02, 0x78, 0x28, 0x31, 0x66, 0x36, 0x39,
+        0x36, 0x66, 0x30, 0x37, 0x32, 0x35, 0x32, 0x66, 0x32, 0x39, 0x65, 0x39, 0x33, 0x66, 0x65,
+        0x34, 0x64, 0x65, 0x31, 0x39, 0x65, 0x65, 0x33, 0x32, 0x63, 0x64, 0x38, 0x31, 0x64, 0x63,
+        0x34, 0x30, 0x34, 0x65, 0x37, 0x36, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0x16, 0x48,
+        0xf2, 0x55, 0x53, 0x23, 0xdd, 0x15, 0x2e, 0x83, 0x38, 0xc3, 0x64, 0x38, 0x63, 0x26, 0x0f,
+        0xcf, 0x5b, 0xd1, 0x3a, 0xd3, 0x40, 0x3e, 0x23, 0xf8, 0x34, 0x4c, 0x6d, 0xa2, 0xbe, 0x25,
+        0x1c, 0xb0, 0x29, 0xe8, 0xc3, 0xfb, 0xb8, 0x80, 0xdc, 0xb1, 0xd2, 0xb3, 0x91, 0x4d, 0xd3,
+        0xfb, 0x01, 0x0f, 0xe4, 0xe9, 0x46, 0xa2, 0xc0, 0x26, 0x57, 0x5a, 0xba, 0x30, 0xf7, 0x15,
+        0x98, 0x14, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x56, 0xa3, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x63,
+        0x41, 0x42, 0x4c, 0x3a, 0x00, 0x01, 0x11, 0x72, 0x01, 0x3a, 0x00, 0x01, 0x11, 0x73, 0xf6,
+        0x3a, 0x00, 0x47, 0x44, 0x52, 0x58, 0x40, 0x47, 0xae, 0x42, 0x27, 0x4c, 0xcb, 0x65, 0x4d,
+        0xee, 0x74, 0x2d, 0x05, 0x78, 0x2a, 0x08, 0x2a, 0xa5, 0xf0, 0xcf, 0xea, 0x3e, 0x60, 0xee,
+        0x97, 0x11, 0x4b, 0x5b, 0xe6, 0x05, 0x0c, 0xe8, 0x90, 0xf5, 0x22, 0xc4, 0xc6, 0x67, 0x7a,
+        0x22, 0x27, 0x17, 0xb3, 0x79, 0xcc, 0x37, 0x64, 0x5e, 0x19, 0x4f, 0x96, 0x37, 0x67, 0x3c,
+        0xd0, 0xc5, 0xed, 0x0f, 0xdd, 0xe7, 0x2e, 0x4f, 0x70, 0x97, 0x30, 0x3a, 0x00, 0x47, 0x44,
+        0x54, 0x58, 0x40, 0xf9, 0x00, 0x9d, 0xc2, 0x59, 0x09, 0xe0, 0xb6, 0x98, 0xbd, 0xe3, 0x97,
+        0x4a, 0xcb, 0x3c, 0xe7, 0x6b, 0x24, 0xc3, 0xe4, 0x98, 0xdd, 0xa9, 0x6a, 0x41, 0x59, 0x15,
+        0xb1, 0x23, 0xe6, 0xc8, 0xdf, 0xfb, 0x52, 0xb4, 0x52, 0xc1, 0xb9, 0x61, 0xdd, 0xbc, 0x5b,
+        0x37, 0x0e, 0x12, 0x12, 0xb2, 0xfd, 0xc1, 0x09, 0xb0, 0xcf, 0x33, 0x81, 0x4c, 0xc6, 0x29,
+        0x1b, 0x99, 0xea, 0xae, 0xfd, 0xaa, 0x0d, 0x3a, 0x00, 0x47, 0x44, 0x56, 0x41, 0x01, 0x3a,
+        0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20,
+        0x06, 0x21, 0x58, 0x20, 0xb1, 0x02, 0xcc, 0x2c, 0xb2, 0x6a, 0x3b, 0xe9, 0xc1, 0xd3, 0x95,
+        0x10, 0xa0, 0xe1, 0xff, 0x51, 0xde, 0x57, 0xd5, 0x65, 0x28, 0xfd, 0x7f, 0xeb, 0xd4, 0xca,
+        0x15, 0xf3, 0xca, 0xdf, 0x37, 0x88, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40,
+        0x58, 0xd8, 0x03, 0x24, 0x53, 0x60, 0x57, 0xa9, 0x09, 0xfa, 0xab, 0xdc, 0x57, 0x1e, 0xf0,
+        0xe5, 0x1e, 0x51, 0x6f, 0x9e, 0xa3, 0x42, 0xe6, 0x6a, 0x8c, 0xaa, 0xad, 0x08, 0x48, 0xde,
+        0x7f, 0x4f, 0x6e, 0x2f, 0x7f, 0x39, 0x6c, 0xa1, 0xf8, 0x42, 0x71, 0xfe, 0x17, 0x3d, 0xca,
+        0x31, 0x83, 0x92, 0xed, 0xbb, 0x40, 0xb8, 0x10, 0xe0, 0xf2, 0x5a, 0x99, 0x53, 0x38, 0x46,
+        0x33, 0x97, 0x78, 0x05, 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x8a, 0xa9, 0x01,
+        0x78, 0x28, 0x31, 0x66, 0x36, 0x39, 0x36, 0x66, 0x30, 0x37, 0x32, 0x35, 0x32, 0x66, 0x32,
+        0x39, 0x65, 0x39, 0x33, 0x66, 0x65, 0x34, 0x64, 0x65, 0x31, 0x39, 0x65, 0x65, 0x33, 0x32,
+        0x63, 0x64, 0x38, 0x31, 0x64, 0x63, 0x34, 0x30, 0x34, 0x65, 0x37, 0x36, 0x02, 0x78, 0x28,
+        0x32, 0x35, 0x39, 0x34, 0x38, 0x39, 0x65, 0x36, 0x39, 0x37, 0x34, 0x38, 0x37, 0x30, 0x35,
+        0x64, 0x65, 0x33, 0x65, 0x32, 0x66, 0x34, 0x34, 0x32, 0x36, 0x37, 0x65, 0x61, 0x34, 0x39,
+        0x33, 0x38, 0x66, 0x66, 0x36, 0x61, 0x35, 0x37, 0x32, 0x35, 0x3a, 0x00, 0x47, 0x44, 0x50,
+        0x58, 0x40, 0xa4, 0x0c, 0xcb, 0xc1, 0xbf, 0xfa, 0xcc, 0xfd, 0xeb, 0xf4, 0xfc, 0x43, 0x83,
+        0x7f, 0x46, 0x8d, 0xd8, 0xd8, 0x14, 0xc1, 0x96, 0x14, 0x1f, 0x6e, 0xb3, 0xa0, 0xd9, 0x56,
+        0xb3, 0xbf, 0x2f, 0xfa, 0x88, 0x70, 0x11, 0x07, 0x39, 0xa4, 0xd2, 0xa9, 0x6b, 0x18, 0x28,
+        0xe8, 0x29, 0x20, 0x49, 0x0f, 0xbb, 0x8d, 0x08, 0x8c, 0xc6, 0x54, 0xe9, 0x71, 0xd2, 0x7e,
+        0xa4, 0xfe, 0x58, 0x7f, 0xd3, 0xc7, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x56, 0xa3, 0x3a, 0x00,
+        0x01, 0x11, 0x71, 0x63, 0x41, 0x56, 0x42, 0x3a, 0x00, 0x01, 0x11, 0x72, 0x01, 0x3a, 0x00,
+        0x01, 0x11, 0x73, 0xf6, 0x3a, 0x00, 0x47, 0x44, 0x52, 0x58, 0x40, 0x93, 0x17, 0xe1, 0x11,
+        0x27, 0x59, 0xd0, 0xef, 0x75, 0x0b, 0x2b, 0x1c, 0x0f, 0x5f, 0x52, 0xc3, 0x29, 0x23, 0xb5,
+        0x2a, 0xe6, 0x12, 0x72, 0x6f, 0x39, 0x86, 0x65, 0x2d, 0xf2, 0xe4, 0xe7, 0xd0, 0xaf, 0x0e,
+        0xa7, 0x99, 0x16, 0x89, 0x97, 0x21, 0xf7, 0xdc, 0x89, 0xdc, 0xde, 0xbb, 0x94, 0x88, 0x1f,
+        0xda, 0xe2, 0xf3, 0xe0, 0x54, 0xf9, 0x0e, 0x29, 0xb1, 0xbd, 0xe1, 0x0c, 0x0b, 0xd7, 0xf6,
+        0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x40, 0xb2, 0x69, 0x05, 0x48, 0x56, 0xb5, 0xfa, 0x55,
+        0x6f, 0xac, 0x56, 0xd9, 0x02, 0x35, 0x2b, 0xaa, 0x4c, 0xba, 0x28, 0xdd, 0x82, 0x3a, 0x86,
+        0xf5, 0xd4, 0xc2, 0xf1, 0xf9, 0x35, 0x7d, 0xe4, 0x43, 0x13, 0xbf, 0xfe, 0xd3, 0x36, 0xd8,
+        0x1c, 0x12, 0x78, 0x5c, 0x9c, 0x3e, 0xf6, 0x66, 0xef, 0xab, 0x3d, 0x0f, 0x89, 0xa4, 0x6f,
+        0xc9, 0x72, 0xee, 0x73, 0x43, 0x02, 0x8a, 0xef, 0xbc, 0x05, 0x98, 0x3a, 0x00, 0x47, 0x44,
+        0x56, 0x41, 0x01, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27,
+        0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0x96, 0x6d, 0x96, 0x42, 0xda, 0x64, 0x51,
+        0xad, 0xfa, 0x00, 0xbc, 0xbc, 0x95, 0x8a, 0xb0, 0xb9, 0x76, 0x01, 0xe6, 0xbd, 0xc0, 0x26,
+        0x79, 0x26, 0xfc, 0x0f, 0x1d, 0x87, 0x65, 0xf1, 0xf3, 0x99, 0x3a, 0x00, 0x47, 0x44, 0x58,
+        0x41, 0x20, 0x58, 0x40, 0x10, 0x7f, 0x77, 0xad, 0x70, 0xbd, 0x52, 0x81, 0x28, 0x8d, 0x24,
+        0x81, 0xb4, 0x3f, 0x21, 0x68, 0x9f, 0xc3, 0x80, 0x68, 0x86, 0x55, 0xfb, 0x2e, 0x6d, 0x96,
+        0xe1, 0xe1, 0xb7, 0x28, 0x8d, 0x63, 0x85, 0xba, 0x2a, 0x01, 0x33, 0x87, 0x60, 0x63, 0xbb,
+        0x16, 0x3f, 0x2f, 0x3d, 0xf4, 0x2d, 0x48, 0x5b, 0x87, 0xed, 0xda, 0x34, 0xeb, 0x9c, 0x4d,
+        0x14, 0xac, 0x65, 0xf4, 0xfa, 0xef, 0x45, 0x0b, 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59,
+        0x01, 0x8f, 0xa9, 0x01, 0x78, 0x28, 0x32, 0x35, 0x39, 0x34, 0x38, 0x39, 0x65, 0x36, 0x39,
+        0x37, 0x34, 0x38, 0x37, 0x30, 0x35, 0x64, 0x65, 0x33, 0x65, 0x32, 0x66, 0x34, 0x34, 0x32,
+        0x36, 0x37, 0x65, 0x61, 0x34, 0x39, 0x33, 0x38, 0x66, 0x66, 0x36, 0x61, 0x35, 0x37, 0x32,
+        0x35, 0x02, 0x78, 0x28, 0x35, 0x64, 0x34, 0x65, 0x64, 0x37, 0x66, 0x34, 0x31, 0x37, 0x61,
+        0x39, 0x35, 0x34, 0x61, 0x31, 0x38, 0x31, 0x34, 0x30, 0x37, 0x62, 0x35, 0x38, 0x38, 0x35,
+        0x61, 0x66, 0x64, 0x37, 0x32, 0x61, 0x35, 0x62, 0x66, 0x34, 0x30, 0x64, 0x61, 0x36, 0x3a,
+        0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x47, 0x44, 0x53,
+        0x58, 0x1a, 0xa3, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x67, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+        0x64, 0x3a, 0x00, 0x01, 0x11, 0x72, 0x0c, 0x3a, 0x00, 0x01, 0x11, 0x73, 0xf6, 0x3a, 0x00,
+        0x47, 0x44, 0x52, 0x58, 0x40, 0x26, 0x1a, 0xbd, 0x26, 0xd8, 0x37, 0x8f, 0x4a, 0xf2, 0x9e,
+        0x49, 0x4d, 0x93, 0x23, 0xc4, 0x6e, 0x02, 0xda, 0xe0, 0x00, 0x02, 0xe7, 0xed, 0x29, 0xdf,
+        0x2b, 0xb3, 0x69, 0xf3, 0x55, 0x0e, 0x4c, 0x22, 0xdc, 0xcf, 0xf5, 0x92, 0xc9, 0xfa, 0x78,
+        0x98, 0xf1, 0x0e, 0x55, 0x5f, 0xf4, 0x45, 0xed, 0xc0, 0x0a, 0x72, 0x2a, 0x7a, 0x3a, 0xd2,
+        0xb1, 0xf7, 0x76, 0xfe, 0x2a, 0x6b, 0x7b, 0x2a, 0x53, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58,
+        0x40, 0x04, 0x25, 0x5d, 0x60, 0x5f, 0x5c, 0x45, 0x0d, 0xf2, 0x9a, 0x6e, 0x99, 0x30, 0x03,
+        0xb8, 0xd6, 0xe1, 0x99, 0x71, 0x1b, 0xf8, 0x44, 0xfa, 0xb5, 0x31, 0x79, 0x1c, 0x37, 0x68,
+        0x4e, 0x1d, 0xc0, 0x24, 0x74, 0x68, 0xf8, 0x80, 0x20, 0x3e, 0x44, 0xb1, 0x43, 0xd2, 0x9c,
+        0xfc, 0x12, 0x9e, 0x77, 0x0a, 0xde, 0x29, 0x24, 0xff, 0x2e, 0xfa, 0xc7, 0x10, 0xd5, 0x73,
+        0xd4, 0xc6, 0xdf, 0x62, 0x9f, 0x3a, 0x00, 0x47, 0x44, 0x56, 0x41, 0x01, 0x3a, 0x00, 0x47,
+        0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21,
+        0x58, 0x20, 0xdb, 0xe7, 0x5b, 0x3f, 0xa3, 0x42, 0xb0, 0x9c, 0xf8, 0x40, 0x8c, 0xb0, 0x9c,
+        0xf0, 0x0a, 0xaf, 0xdf, 0x6f, 0xe5, 0x09, 0x21, 0x11, 0x92, 0xe1, 0xf8, 0xc5, 0x09, 0x02,
+        0x3d, 0x1f, 0xb7, 0xc5, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40, 0xc4, 0xc1,
+        0xd7, 0x1c, 0x2d, 0x26, 0x89, 0x22, 0xcf, 0xa6, 0x99, 0x77, 0x30, 0x84, 0x86, 0x27, 0x59,
+        0x8f, 0xd8, 0x08, 0x75, 0xe0, 0xb2, 0xef, 0xf9, 0xfa, 0xa5, 0x40, 0x8c, 0xd3, 0xeb, 0xbb,
+        0xda, 0xf2, 0xc8, 0xae, 0x41, 0x22, 0x50, 0x9c, 0xe8, 0xb2, 0x9c, 0x9b, 0x3f, 0x8a, 0x78,
+        0x76, 0xab, 0xd0, 0xbe, 0xfc, 0xe4, 0x79, 0xcb, 0x1b, 0x2b, 0xaa, 0x4d, 0xdd, 0x15, 0x61,
+        0x42, 0x06,
+    ];
+
+    // This test invokes make_sample_bcc_and_cdis and compares the result bitwise to the target
+    // vectors. The function uses main_flow, bcc_main_flow, format_config_descriptor,
+    // derive_cdi_private_key_seed, and keypair_from_seed. This test is sensitive to errors
+    // and changes in any of those functions.
+    #[test]
+    fn main_flow_and_bcc_main_flow() {
+        let (cdi_attest, cdi_seal, bcc) = make_sample_bcc_and_cdis().unwrap();
+        assert_eq!(&cdi_attest[..], SAMPLE_CDI_ATTEST_TEST_VECTOR);
+        assert_eq!(&cdi_seal[..], SAMPLE_CDI_SEAL_TEST_VECTOR);
+        assert_eq!(&bcc[..], SAMPLE_BCC_TEST_VECTOR);
+    }
+
+    static DERIVED_KEY_TEST_VECTOR: &[u8] = &[
+        0x0e, 0xd6, 0x07, 0x0e, 0x1c, 0x38, 0x2c, 0x76, 0x13, 0xc6, 0x76, 0x25, 0x7e, 0x07, 0x6f,
+        0xdb, 0x1d, 0xb1, 0x0f, 0x3f, 0xed, 0xc5, 0x2b, 0x95, 0xd1, 0x32, 0xf1, 0x63, 0x2f, 0x2a,
+        0x01, 0x5e,
+    ];
+
+    #[test]
+    fn kdf() {
+        let mut ctx = OpenDiceCborContext::new();
+        let derived_key = ctx
+            .kdf(
+                PRIVATE_KEY_SEED_SIZE,
+                "myKey".as_bytes(),
+                "mySalt".as_bytes(),
+                "myInfo".as_bytes(),
+            )
+            .unwrap();
+        assert_eq!(&derived_key[..], DERIVED_KEY_TEST_VECTOR);
+    }
+
+    static CERT_ID_TEST_VECTOR: &[u8] = &[
+        0x7a, 0x36, 0x45, 0x2c, 0x02, 0xf6, 0x2b, 0xec, 0xf9, 0x80, 0x06, 0x75, 0x87, 0xa5, 0xc1,
+        0x44, 0x0c, 0xd3, 0xc0, 0x6d,
+    ];
+
+    #[test]
+    fn derive_cdi_certificate_id() {
+        let mut ctx = OpenDiceCborContext::new();
+        let cert_id = ctx.derive_cdi_certificate_id("MyPubKey".as_bytes()).unwrap();
+        assert_eq!(&cert_id[..], CERT_ID_TEST_VECTOR);
+    }
+}
diff --git a/diced/src/diced_client_test.rs b/diced/src/diced_client_test.rs
new file mode 100644
index 0000000..3915508
--- /dev/null
+++ b/diced/src/diced_client_test.rs
@@ -0,0 +1,188 @@
+// 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.
+
+use android_hardware_security_dice::aidl::android::hardware::security::dice::{
+    Config::Config as BinderConfig, InputValues::InputValues as BinderInputValues,
+    Mode::Mode as BinderMode,
+};
+use android_security_dice::aidl::android::security::dice::IDiceMaintenance::IDiceMaintenance;
+use android_security_dice::aidl::android::security::dice::IDiceNode::IDiceNode;
+use binder::Strong;
+use diced_open_dice_cbor as dice;
+use nix::libc::uid_t;
+use std::convert::TryInto;
+
+static DICE_NODE_SERVICE_NAME: &str = "android.security.dice.IDiceNode";
+static DICE_MAINTENANCE_SERVICE_NAME: &str = "android.security.dice.IDiceMaintenance";
+
+fn get_dice_node() -> Strong<dyn IDiceNode> {
+    binder::get_interface(DICE_NODE_SERVICE_NAME).unwrap()
+}
+
+fn get_dice_maintenance() -> Strong<dyn IDiceMaintenance> {
+    binder::get_interface(DICE_MAINTENANCE_SERVICE_NAME).unwrap()
+}
+
+static TEST_MESSAGE: &[u8] = &[
+    // "My test message!"
+    0x4d, 0x79, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x21,
+    0x0a,
+];
+
+// This test calls derive with an empty argument vector and with a set of three input values.
+// It then performs the same three derivation steps on the result of the former and compares
+// the result to the result of the latter.
+fn equivalence_test() {
+    let node = get_dice_node();
+    let input_values = diced_sample_inputs::get_input_values_vector();
+    let former = node.derive(&[]).expect("Trying to call derive.");
+    let latter = node.derive(&input_values).expect("Trying to call derive with input values.");
+    let artifacts =
+        diced_utils::ResidentArtifacts::new(&former.cdiAttest, &former.cdiSeal, &former.bcc.data)
+            .unwrap();
+
+    let input_values: Vec<diced_utils::InputValues> =
+        input_values.iter().map(|v| v.into()).collect();
+
+    let artifacts =
+        artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues)).unwrap();
+    let (cdi_attest, cdi_seal, bcc) = artifacts.into_tuple();
+    let from_former = diced_utils::make_bcc_handover(
+        cdi_attest[..].try_into().unwrap(),
+        cdi_seal[..].try_into().unwrap(),
+        &bcc,
+    )
+    .unwrap();
+    // TODO when we have a parser/verifier, check equivalence rather
+    // than bit by bit equality.
+    assert_eq!(latter, from_former);
+}
+
+fn sign_and_verify() {
+    let node = get_dice_node();
+    let _signature = node.sign(&[], TEST_MESSAGE).expect("Trying to call sign.");
+
+    let _bcc = node.getAttestationChain(&[]).expect("Trying to call getAttestationChain.");
+    // TODO b/204938506 check the signature with the bcc when the verifier is available.
+}
+
+// This test calls derive with an empty argument vector, then demotes the itself using
+// a set of three input values, and then calls derive with empty argument vector again.
+// It then performs the same three derivation steps on the result of the former and compares
+// the result to the result of the latter.
+fn demote_test() {
+    let node = get_dice_node();
+    let input_values = diced_sample_inputs::get_input_values_vector();
+    let former = node.derive(&[]).expect("Trying to call derive.");
+    node.demote(&input_values).expect("Trying to call demote with input values.");
+
+    let latter = node.derive(&[]).expect("Trying to call derive after demote.");
+
+    let artifacts = diced_utils::ResidentArtifacts::new(
+        former.cdiAttest[..].try_into().unwrap(),
+        former.cdiSeal[..].try_into().unwrap(),
+        &former.bcc.data,
+    )
+    .unwrap();
+
+    let input_values: Vec<diced_utils::InputValues> =
+        input_values.iter().map(|v| v.into()).collect();
+
+    let artifacts =
+        artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues)).unwrap();
+    let (cdi_attest, cdi_seal, bcc) = artifacts.into_tuple();
+    let from_former = diced_utils::make_bcc_handover(
+        cdi_attest[..].try_into().unwrap(),
+        cdi_seal[..].try_into().unwrap(),
+        &bcc,
+    )
+    .unwrap();
+    // TODO b/204938506 when we have a parser/verifier, check equivalence rather
+    // than bit by bit equality.
+    assert_eq!(latter, from_former);
+}
+
+fn client_input_values(uid: uid_t) -> BinderInputValues {
+    BinderInputValues {
+        codeHash: [0; dice::HASH_SIZE],
+        config: BinderConfig {
+            desc: dice::bcc::format_config_descriptor(Some(&format!("{}", uid)), None, true)
+                .unwrap(),
+        },
+        authorityHash: [0; dice::HASH_SIZE],
+        authorityDescriptor: None,
+        mode: BinderMode::NORMAL,
+        hidden: [0; dice::HIDDEN_SIZE],
+    }
+}
+
+// This test calls derive with an empty argument vector `former` which look like this:
+// <common root> | <caller>
+// It then demotes diced using a set of three input values prefixed with the uid based input
+// values that diced would add to any call. It then calls derive with empty argument vector
+// again which will add another step using the identity of the caller. If diced was demoted
+// correctly the chain of `latter` will
+// look as follows:
+// <common root> | <caller> | <the three sample inputs> | <caller>
+//
+// It then performs the same three derivation steps followed by a set of caller input values
+// on `former` and compares it to `latter`.
+fn demote_self_test() {
+    let maintenance = get_dice_maintenance();
+    let node = get_dice_node();
+    let input_values = diced_sample_inputs::get_input_values_vector();
+    let former = node.derive(&[]).expect("Trying to call derive.");
+
+    let client = client_input_values(nix::unistd::getuid().into());
+
+    let mut demote_vector = vec![client.clone()];
+    demote_vector.append(&mut input_values.clone());
+    maintenance.demoteSelf(&demote_vector).expect("Trying to call demote_self with input values.");
+
+    let latter = node.derive(&[]).expect("Trying to call derive after demote.");
+
+    let artifacts = diced_utils::ResidentArtifacts::new(
+        former.cdiAttest[..].try_into().unwrap(),
+        former.cdiSeal[..].try_into().unwrap(),
+        &former.bcc.data,
+    )
+    .unwrap();
+
+    let client = [client];
+    let input_values: Vec<diced_utils::InputValues> =
+        input_values.iter().chain(client.iter()).map(|v| v.into()).collect();
+
+    let artifacts =
+        artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues)).unwrap();
+    let (cdi_attest, cdi_seal, bcc) = artifacts.into_tuple();
+    let from_former = diced_utils::make_bcc_handover(
+        cdi_attest[..].try_into().unwrap(),
+        cdi_seal[..].try_into().unwrap(),
+        &bcc,
+    )
+    .unwrap();
+    // TODO b/204938506 when we have a parser/verifier, check equivalence rather
+    // than bit by bit equality.
+    assert_eq!(latter, from_former);
+}
+
+#[test]
+fn run_serialized_test() {
+    equivalence_test();
+    sign_and_verify();
+    // The demote self test must run before the demote test or the test fails.
+    // And since demotion is not reversible the test can only pass once per boot.
+    demote_self_test();
+    demote_test();
+}
diff --git a/diced/src/diced_main.rs b/diced/src/diced_main.rs
new file mode 100644
index 0000000..c2cf02c
--- /dev/null
+++ b/diced/src/diced_main.rs
@@ -0,0 +1,76 @@
+// 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.
+
+//! Main entry point for diced, the friendly neighborhood DICE service.
+
+use binder::get_interface;
+use diced::{DiceMaintenance, DiceNode, DiceNodeImpl, ProxyNodeHal, ResidentNode};
+use std::convert::TryInto;
+use std::panic;
+use std::sync::Arc;
+
+static DICE_NODE_SERVICE_NAME: &str = "android.security.dice.IDiceNode";
+static DICE_MAINTENANCE_SERVICE_NAME: &str = "android.security.dice.IDiceMaintenance";
+static DICE_HAL_SERVICE_NAME: &str = "android.hardware.security.dice.IDiceDevice/default";
+
+fn main() {
+    android_logger::init_once(
+        android_logger::Config::default().with_tag("diced").with_min_level(log::Level::Debug),
+    );
+    // Redirect panic messages to logcat.
+    panic::set_hook(Box::new(|panic_info| {
+        log::error!("{}", panic_info);
+    }));
+
+    // Saying hi.
+    log::info!("Diced, your friendly neighborhood DICE service, is starting.");
+
+    let node_impl: Arc<dyn DiceNodeImpl + Send + Sync> = match get_interface(DICE_HAL_SERVICE_NAME)
+    {
+        Ok(dice_device) => {
+            Arc::new(ProxyNodeHal::new(dice_device).expect("Failed to construct a proxy node."))
+        }
+        Err(e) => {
+            log::warn!("Failed to connect to DICE HAL: {:?}", e);
+            log::warn!("Using sample dice artifacts.");
+            let (cdi_attest, cdi_seal, bcc) = diced_sample_inputs::make_sample_bcc_and_cdis()
+                .expect("Failed to create sample dice artifacts.");
+            Arc::new(
+                ResidentNode::new(
+                    cdi_attest[..]
+                        .try_into()
+                        .expect("Failed to convert cdi_attest into array ref."),
+                    cdi_seal[..].try_into().expect("Failed to convert cdi_seal into array ref."),
+                    bcc,
+                )
+                .expect("Failed to construct a resident node."),
+            )
+        }
+    };
+
+    let node = DiceNode::new_as_binder(node_impl.clone())
+        .expect("Failed to create IDiceNode service instance.");
+
+    let maintenance = DiceMaintenance::new_as_binder(node_impl)
+        .expect("Failed to create IDiceMaintenance service instance.");
+
+    binder::add_service(DICE_NODE_SERVICE_NAME, node.as_binder())
+        .expect("Failed to register IDiceNode Service");
+
+    binder::add_service(DICE_MAINTENANCE_SERVICE_NAME, maintenance.as_binder())
+        .expect("Failed to register IDiceMaintenance Service");
+
+    log::info!("Joining thread pool now.");
+    binder::ProcessState::join_thread_pool();
+}
diff --git a/diced/src/error.rs b/diced/src/error.rs
new file mode 100644
index 0000000..3e230e4
--- /dev/null
+++ b/diced/src/error.rs
@@ -0,0 +1,123 @@
+// 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.
+
+use android_security_dice::aidl::android::security::dice::ResponseCode::ResponseCode;
+use anyhow::Result;
+use binder::{ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode};
+use keystore2_selinux as selinux;
+use std::ffi::CString;
+
+/// This is the main Diced error type. It wraps the Diced `ResponseCode` generated
+/// from AIDL in the `Rc` variant and Binder and BinderTransaction errors in the respective
+/// variants.
+#[allow(dead_code)] // Binder error forwarding will be needed when proxy nodes are implemented.
+#[derive(Debug, thiserror::Error, Eq, PartialEq, Clone)]
+pub enum Error {
+    /// Wraps a dice `ResponseCode` as defined by the android.security.dice AIDL interface
+    /// specification.
+    #[error("Error::Rc({0:?})")]
+    Rc(ResponseCode),
+    /// Wraps a Binder exception code other than a service specific exception.
+    #[error("Binder exception code {0:?}, {1:?}")]
+    Binder(ExceptionCode, i32),
+    /// Wraps a Binder status code.
+    #[error("Binder transaction error {0:?}")]
+    BinderTransaction(StatusCode),
+}
+
+/// This function should be used by dice service calls to translate error conditions
+/// into service specific exceptions.
+///
+/// All error conditions get logged by this function.
+///
+/// All `Error::Rc(x)` variants get mapped onto a service specific error code of x.
+/// `selinux::Error::PermissionDenied` is mapped on `ResponseCode::PERMISSION_DENIED`.
+///
+/// All non `Error` error conditions and the Error::Binder variant get mapped onto
+/// ResponseCode::SYSTEM_ERROR`.
+///
+/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
+/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
+/// typically returns Ok(value).
+///
+/// # Examples
+///
+/// ```
+/// fn do_something() -> anyhow::Result<Vec<u8>> {
+///     Err(anyhow!(Error::Rc(ResponseCode::NOT_IMPLEMENTED)))
+/// }
+///
+/// map_or_log_err(do_something(), Ok)
+/// ```
+pub fn map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T>
+where
+    F: FnOnce(U) -> BinderResult<T>,
+{
+    map_err_with(
+        result,
+        |e| {
+            log::error!("{:?}", e);
+            e
+        },
+        handle_ok,
+    )
+}
+
+/// This function behaves similar to map_or_log_error, but it does not log the errors, instead
+/// it calls map_err on the error before mapping it to a binder result allowing callers to
+/// log or transform the error before mapping it.
+fn map_err_with<T, U, F1, F2>(result: Result<U>, map_err: F1, handle_ok: F2) -> BinderResult<T>
+where
+    F1: FnOnce(anyhow::Error) -> anyhow::Error,
+    F2: FnOnce(U) -> BinderResult<T>,
+{
+    result.map_or_else(
+        |e| {
+            let e = map_err(e);
+            let msg = match CString::new(format!("{:?}", e)) {
+                Ok(msg) => Some(msg),
+                Err(_) => {
+                    log::warn!(
+                        "Cannot convert error message to CStr. It contained a nul byte.
+                         Omitting message from service specific error."
+                    );
+                    None
+                }
+            };
+            let rc = get_error_code(&e);
+            Err(BinderStatus::new_service_specific_error(rc, msg.as_deref()))
+        },
+        handle_ok,
+    )
+}
+
+/// Extracts the error code from an `anyhow::Error` mapping any error that does not have a
+/// root cause of `Error::Rc` onto `ResponseCode::SYSTEM_ERROR` and to `e` with `Error::Rc(e)`
+/// otherwise.
+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,
+        // 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,
+        },
+    }
+}
diff --git a/diced/src/error_vendor.rs b/diced/src/error_vendor.rs
new file mode 100644
index 0000000..e8657e0
--- /dev/null
+++ b/diced/src/error_vendor.rs
@@ -0,0 +1,119 @@
+// 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.
+
+use android_hardware_security_dice::aidl::android::hardware::security::dice::ResponseCode::ResponseCode;
+use anyhow::Result;
+use binder::{ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode};
+use std::ffi::CString;
+
+/// This is the error type for DICE HAL implementations. It wraps
+/// `android::hardware::security::dice::ResponseCode` generated
+/// from AIDL in the `Rc` variant and Binder and BinderTransaction errors in the respective
+/// variants.
+#[allow(dead_code)] // Binder error forwarding will be needed when proxy nodes are implemented.
+#[derive(Debug, thiserror::Error, Eq, PartialEq, Clone)]
+pub enum Error {
+    /// Wraps a dice `ResponseCode` as defined by the Keystore AIDL interface specification.
+    #[error("Error::Rc({0:?})")]
+    Rc(ResponseCode),
+    /// Wraps a Binder exception code other than a service specific exception.
+    #[error("Binder exception code {0:?}, {1:?}")]
+    Binder(ExceptionCode, i32),
+    /// Wraps a Binder status code.
+    #[error("Binder transaction error {0:?}")]
+    BinderTransaction(StatusCode),
+}
+
+/// This function should be used by dice service calls to translate error conditions
+/// into service specific exceptions.
+///
+/// All error conditions get logged by this function.
+///
+/// All `Error::Rc(x)` variants get mapped onto a service specific error code of x.
+/// `selinux::Error::PermissionDenied` is mapped on `ResponseCode::PERMISSION_DENIED`.
+///
+/// All non `Error` error conditions and the Error::Binder variant get mapped onto
+/// ResponseCode::SYSTEM_ERROR`.
+///
+/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
+/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
+/// typically returns Ok(value).
+///
+/// # Examples
+///
+/// ```
+/// fn do_something() -> anyhow::Result<Vec<u8>> {
+///     Err(anyhow!(Error::Rc(ResponseCode::NOT_IMPLEMENTED)))
+/// }
+///
+/// map_or_log_err(do_something(), Ok)
+/// ```
+pub fn map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T>
+where
+    F: FnOnce(U) -> BinderResult<T>,
+{
+    map_err_with(
+        result,
+        |e| {
+            log::error!("{:?}", e);
+            e
+        },
+        handle_ok,
+    )
+}
+
+/// This function behaves similar to map_or_log_error, but it does not log the errors, instead
+/// it calls map_err on the error before mapping it to a binder result allowing callers to
+/// log or transform the error before mapping it.
+fn map_err_with<T, U, F1, F2>(result: Result<U>, map_err: F1, handle_ok: F2) -> BinderResult<T>
+where
+    F1: FnOnce(anyhow::Error) -> anyhow::Error,
+    F2: FnOnce(U) -> BinderResult<T>,
+{
+    result.map_or_else(
+        |e| {
+            let e = map_err(e);
+            let msg = match CString::new(format!("{:?}", e)) {
+                Ok(msg) => Some(msg),
+                Err(_) => {
+                    log::warn!(
+                        "Cannot convert error message to CStr. It contained a nul byte.
+                         Omitting message from service specific error."
+                    );
+                    None
+                }
+            };
+            let rc = get_error_code(&e);
+            Err(BinderStatus::new_service_specific_error(rc, msg.as_deref()))
+        },
+        handle_ok,
+    )
+}
+
+/// Extracts the error code from an `anyhow::Error` mapping any error that does not have a
+/// root cause of `Error::Rc` onto `ResponseCode::SYSTEM_ERROR` and to `e` with `Error::Rc(e)`
+/// otherwise.
+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,
+        // 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 => ResponseCode::SYSTEM_ERROR.0,
+    }
+}
diff --git a/diced/src/hal_node.rs b/diced/src/hal_node.rs
new file mode 100644
index 0000000..01a7577
--- /dev/null
+++ b/diced/src/hal_node.rs
@@ -0,0 +1,725 @@
+// 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 `ResidentHal`, an implementation of a IDiceDevice HAL Interface.
+//! While the name implies that the DICE secrets are memory resident, the residency
+//! is augmented by the implementation of the traits `DiceArtifacts` and
+//! `UpdatableDiceArtifacts`. The implementation outsources all operations that
+//! involve the DICE secrets to a short lived child process. By implementing
+//! `UpdatableDiceArtifacts` accordingly, integrators can limit the exposure of
+//! the resident DICE secrets to user space memory. E.g., an implementation might only
+//! hold a path to a securefs file allowing the child to read and update the kernel state
+//! through this path directly.
+//!
+//! ## Important Safety Note.
+//! The module is not safe to use in multi threaded processes. It uses fork and runs
+//! code that is not async signal safe in the child. Implementing a HAL service without
+//! starting a thread pool is safe, but no secondary thread must be created.
+
+use crate::error_vendor::map_or_log_err;
+use android_hardware_security_dice::aidl::android::hardware::security::dice::{
+    Bcc::Bcc, BccHandover::BccHandover, IDiceDevice::BnDiceDevice, IDiceDevice::IDiceDevice,
+    InputValues::InputValues as BinderInputValues, Signature::Signature,
+};
+use anyhow::{Context, Result};
+use binder::{BinderFeatures, Result as BinderResult, Strong};
+use dice::{ContextImpl, OpenDiceCborContext};
+use diced_open_dice_cbor as dice;
+use diced_utils as utils;
+use nix::sys::wait::{waitpid, WaitStatus};
+use nix::unistd::{
+    close, fork, pipe as nix_pipe, read as nix_read, write as nix_write, ForkResult,
+};
+use serde::{de::DeserializeOwned, Deserialize, Serialize};
+use std::convert::TryInto;
+use std::io::{Read, Write};
+use std::os::unix::io::RawFd;
+use std::sync::{Arc, RwLock};
+use utils::ResidentArtifacts;
+pub use utils::{DiceArtifacts, UpdatableDiceArtifacts};
+
+/// PipeReader is a simple wrapper around raw pipe file descriptors.
+/// It takes ownership of the file descriptor and closes it on drop. It provides `read_all`, which
+/// reads from the pipe into an expending vector, until no more data can be read.
+struct PipeReader(RawFd);
+
+impl Read for PipeReader {
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        let bytes = nix_read(self.0, buf)?;
+        Ok(bytes)
+    }
+}
+
+impl Drop for PipeReader {
+    fn drop(&mut self) {
+        close(self.0).expect("Failed to close reader pipe fd.");
+    }
+}
+
+/// PipeWriter is a simple wrapper around raw pipe file descriptors.
+/// It takes ownership of the file descriptor and closes it on drop. It provides `write`, which
+/// writes the given buffer into the pipe, returning the number of bytes written.
+struct PipeWriter(RawFd);
+
+impl Write for PipeWriter {
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        let written = nix_write(self.0, buf)?;
+        Ok(written)
+    }
+
+    fn flush(&mut self) -> std::io::Result<()> {
+        // Flush is a NO-OP.
+        Ok(())
+    }
+}
+
+impl Drop for PipeWriter {
+    fn drop(&mut self) {
+        close(self.0).expect("Failed to close writer pipe fd.");
+    }
+}
+
+fn pipe() -> Result<(PipeReader, PipeWriter), nix::Error> {
+    let (read_fd, write_fd) = nix_pipe()?;
+    Ok((PipeReader(read_fd), PipeWriter(write_fd)))
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, thiserror::Error)]
+enum RunForkedError {
+    #[error("RunForkedError::String({0:?})")]
+    String(String),
+}
+
+/// Run the given closure in a new process.
+/// Safety: The function runs code that is not async-signal-safe in the child after forking.
+/// This means, that this function must not be called by a multi threaded process.
+fn run_forked<F, R>(f: F) -> Result<R>
+where
+    R: Serialize + DeserializeOwned,
+    F: FnOnce() -> Result<R>,
+{
+    let (reader, writer) = pipe().expect("Failed to create pipe.");
+
+    match unsafe { fork() } {
+        Ok(ForkResult::Parent { child, .. }) => {
+            drop(writer);
+            let status = waitpid(child, None).expect("Failed while waiting for child.");
+            if let WaitStatus::Exited(_, 0) = status {
+                // Child exited successfully.
+                // Read the result from the pipe.
+                // Deserialize the result and return it.
+                let result: Result<R, RunForkedError> =
+                    serde_cbor::from_reader(reader).expect("Failed to deserialize result.");
+
+                result.context("In run_forked:")
+            } else {
+                panic!("Child did not exit as expected {:?}", status);
+            }
+        }
+        Ok(ForkResult::Child) => {
+            // Run the closure.
+            let result = f()
+                .map_err(|err| RunForkedError::String(format! {"Nested anyhow error {:?}", err}));
+
+            // Serialize the result of the closure.
+            serde_cbor::to_writer(writer, &result).expect("Result serialization failed");
+
+            // Set exit status to `0`.
+            std::process::exit(0);
+        }
+        Err(errno) => {
+            panic!("Failed to fork: {:?}", errno);
+        }
+    }
+}
+
+/// A DiceHal backend implementation.
+/// All functions, except `demote`, derive effective dice artifacts starting from
+/// this node and iterating through `input_values` in ascending order.
+pub trait DiceHalImpl {
+    /// Signs the message using the effective dice artifacts and Ed25519Pure.
+    fn sign(&self, input_values: &[BinderInputValues], message: &[u8]) -> Result<Signature>;
+    /// Returns the effective attestation chain.
+    fn get_attestation_chain(&self, input_values: &[BinderInputValues]) -> Result<Bcc>;
+    /// Returns the effective dice artifacts.
+    fn derive(&self, input_values: &[BinderInputValues]) -> Result<BccHandover>;
+    /// This demotes the implementation itself. I.e. a resident node would replace its resident
+    /// artifacts with the effective artifacts derived using `input_values`. A proxy node would
+    /// simply call `demote` on its parent node. This is not reversible and changes
+    /// the effective dice artifacts of all clients.
+    fn demote(&self, input_values: &[BinderInputValues]) -> Result<()>;
+}
+
+/// The ResidentHal implements a IDiceDevice backend with memory resident DICE secrets.
+pub struct ResidentHal<T: UpdatableDiceArtifacts + Serialize + DeserializeOwned + Clone + Send> {
+    artifacts: RwLock<T>,
+}
+
+impl<T: UpdatableDiceArtifacts + Serialize + DeserializeOwned + Clone + Send> ResidentHal<T> {
+    /// Creates a new Resident node with the given dice secrets and certificate chain.
+    /// ## Safety
+    /// It is not safe to use implementations of ResidentHal in multi threaded environments.
+    /// If using this library to implement a HAL service make sure not to start a thread pool.
+    pub unsafe fn new(artifacts: T) -> Result<Self> {
+        Ok(ResidentHal { artifacts: RwLock::new(artifacts) })
+    }
+
+    fn with_effective_artifacts<R, F>(&self, input_values: &[BinderInputValues], f: F) -> Result<R>
+    where
+        R: Serialize + DeserializeOwned,
+        F: FnOnce(ResidentArtifacts) -> Result<R>,
+    {
+        let artifacts = self.artifacts.read().unwrap().clone();
+
+        // Safety: run_forked must not be be called by a multi threaded process.
+        // This requirement is propagated to the public interface of this module through
+        // `ResidentHal::new`
+        run_forked(move || {
+            let artifacts = artifacts.with_artifacts(|a| ResidentArtifacts::new_from(a))?;
+            let input_values: Vec<utils::InputValues> =
+                input_values.iter().map(|v| v.into()).collect();
+            let artifacts = artifacts
+                .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
+                .context("In ResidentHal::get_effective_artifacts:")?;
+            f(artifacts)
+        })
+    }
+}
+
+impl<T: UpdatableDiceArtifacts + Serialize + DeserializeOwned + Clone + Send> DiceHalImpl
+    for ResidentHal<T>
+{
+    fn sign(&self, input_values: &[BinderInputValues], message: &[u8]) -> Result<Signature> {
+        let signature: Vec<u8> = self
+            .with_effective_artifacts(input_values, |artifacts| {
+                let (cdi_attest, _, _) = artifacts.into_tuple();
+                let mut dice = OpenDiceCborContext::new();
+                let seed = dice
+                    .derive_cdi_private_key_seed(cdi_attest[..].try_into().with_context(|| {
+                        format!(
+                            "In ResidentHal::sign: Failed to convert cdi_attest (length: {}).",
+                            cdi_attest.len()
+                        )
+                    })?)
+                    .context("In ResidentHal::sign: Failed to derive seed from cdi_attest.")?;
+                let (_public_key, private_key) = dice
+                    .keypair_from_seed(seed[..].try_into().with_context(|| {
+                        format!(
+                            "In ResidentHal::sign: Failed to convert seed (length: {}).",
+                            seed.len()
+                        )
+                    })?)
+                    .context("In ResidentHal::sign: Failed to derive keypair from seed.")?;
+                dice.sign(
+                    message,
+                    private_key[..].try_into().with_context(|| {
+                        format!(
+                            "In ResidentHal::sign: Failed to convert private_key (length: {}).",
+                            private_key.len()
+                        )
+                    })?,
+                )
+                .context("In ResidentHal::sign: Failed to sign.")
+            })
+            .context("In ResidentHal::sign:")?;
+        Ok(Signature { data: signature })
+    }
+
+    fn get_attestation_chain(&self, input_values: &[BinderInputValues]) -> Result<Bcc> {
+        let bcc = self
+            .with_effective_artifacts(input_values, |artifacts| {
+                let (_, _, bcc) = artifacts.into_tuple();
+                Ok(bcc)
+            })
+            .context("In ResidentHal::get_attestation_chain: Failed to get effective_artifacts.")?;
+
+        Ok(Bcc { data: bcc })
+    }
+
+    fn derive(&self, input_values: &[BinderInputValues]) -> Result<BccHandover> {
+        let (cdi_attest, cdi_seal, bcc): (Vec<u8>, Vec<u8>, Vec<u8>) = self
+            .with_effective_artifacts(input_values, |artifacts| {
+                let (cdi_attest, cdi_seal, bcc) = artifacts.into_tuple();
+                Ok((cdi_attest[..].to_vec(), cdi_seal[..].to_vec(), bcc))
+            })?;
+
+        utils::make_bcc_handover(
+            &cdi_attest
+                .as_slice()
+                .try_into()
+                .context("In ResidentHal::derive: Trying to convert cdi_attest to sized array.")?,
+            &cdi_seal
+                .as_slice()
+                .try_into()
+                .context("In ResidentHal::derive: Trying to convert cdi_seal to sized array.")?,
+            &bcc,
+        )
+        .context("In ResidentHal::derive: Trying to construct BccHandover.")
+    }
+
+    fn demote(&self, input_values: &[BinderInputValues]) -> Result<()> {
+        let mut artifacts = self.artifacts.write().unwrap();
+
+        let artifacts_clone = (*artifacts).clone();
+
+        // Safety: run_forked may not be called from a multi threaded process.
+        // This requirement is propagated to the public interface of this module through
+        // `ResidentHal::new`
+        *artifacts = run_forked(|| {
+            let new_artifacts =
+                artifacts_clone.with_artifacts(|a| ResidentArtifacts::new_from(a))?;
+            let input_values: Vec<utils::InputValues> =
+                input_values.iter().map(|v| v.into()).collect();
+
+            let new_artifacts = new_artifacts
+                .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
+                .context("In ResidentHal::get_effective_artifacts:")?;
+            artifacts_clone.update(&new_artifacts)
+        })?;
+
+        Ok(())
+    }
+}
+
+/// Implements android.hardware.security.dice.IDiceDevice. Forwards public API calls
+/// to the given DiceHalImpl backend.
+pub struct DiceDevice {
+    hal_impl: Arc<dyn DiceHalImpl + Sync + Send>,
+}
+
+impl DiceDevice {
+    /// Constructs an instance of DiceDevice, wraps it with a BnDiceDevice object and
+    /// returns a strong pointer to the binder. The result can be used to register
+    /// the service with service manager.
+    pub fn new_as_binder(
+        hal_impl: Arc<dyn DiceHalImpl + Sync + Send>,
+    ) -> Result<Strong<dyn IDiceDevice>> {
+        let result = BnDiceDevice::new_binder(DiceDevice { hal_impl }, BinderFeatures::default());
+        Ok(result)
+    }
+}
+
+impl binder::Interface for DiceDevice {}
+
+impl IDiceDevice for DiceDevice {
+    fn sign(&self, input_values: &[BinderInputValues], message: &[u8]) -> BinderResult<Signature> {
+        map_or_log_err(self.hal_impl.sign(input_values, message), Ok)
+    }
+    fn getAttestationChain(&self, input_values: &[BinderInputValues]) -> BinderResult<Bcc> {
+        map_or_log_err(self.hal_impl.get_attestation_chain(input_values), Ok)
+    }
+    fn derive(&self, input_values: &[BinderInputValues]) -> BinderResult<BccHandover> {
+        map_or_log_err(self.hal_impl.derive(input_values), Ok)
+    }
+    fn demote(&self, input_values: &[BinderInputValues]) -> BinderResult<()> {
+        map_or_log_err(self.hal_impl.demote(input_values), Ok)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use android_hardware_security_dice::aidl::android::hardware::security::dice::{
+        BccHandover::BccHandover, Config::Config as BinderConfig,
+        InputValues::InputValues as BinderInputValues, Mode::Mode as BinderMode,
+    };
+    use anyhow::{Context, Result};
+    use diced_open_dice_cbor as dice;
+    use diced_sample_inputs;
+    use diced_utils as utils;
+
+    #[derive(Debug, Serialize, Deserialize, Clone)]
+    struct InsecureSerializableArtifacts {
+        cdi_attest: [u8; dice::CDI_SIZE],
+        cdi_seal: [u8; dice::CDI_SIZE],
+        bcc: Vec<u8>,
+    }
+
+    impl DiceArtifacts for InsecureSerializableArtifacts {
+        fn cdi_attest(&self) -> &[u8; dice::CDI_SIZE] {
+            &self.cdi_attest
+        }
+        fn cdi_seal(&self) -> &[u8; dice::CDI_SIZE] {
+            &self.cdi_seal
+        }
+        fn bcc(&self) -> Vec<u8> {
+            self.bcc.clone()
+        }
+    }
+
+    impl UpdatableDiceArtifacts for InsecureSerializableArtifacts {
+        fn with_artifacts<F, T>(&self, f: F) -> Result<T>
+        where
+            F: FnOnce(&dyn DiceArtifacts) -> Result<T>,
+        {
+            f(self)
+        }
+        fn update(self, new_artifacts: &impl DiceArtifacts) -> Result<Self> {
+            Ok(Self {
+                cdi_attest: *new_artifacts.cdi_attest(),
+                cdi_seal: *new_artifacts.cdi_seal(),
+                bcc: new_artifacts.bcc(),
+            })
+        }
+    }
+
+    fn make_input_values(
+        code: &str,
+        config_name: &str,
+        authority: &str,
+    ) -> Result<BinderInputValues> {
+        let mut dice_ctx = dice::OpenDiceCborContext::new();
+        Ok(BinderInputValues {
+            codeHash: dice_ctx
+                .hash(code.as_bytes())
+                .context("In make_input_values: code hash failed.")?
+                .as_slice()
+                .try_into()?,
+            config: BinderConfig {
+                desc: dice::bcc::format_config_descriptor(Some(config_name), None, true)
+                    .context("In make_input_values: Failed to format config descriptor.")?,
+            },
+            authorityHash: dice_ctx
+                .hash(authority.as_bytes())
+                .context("In make_input_values: authority hash failed.")?
+                .as_slice()
+                .try_into()?,
+            authorityDescriptor: None,
+            mode: BinderMode::NORMAL,
+            hidden: [0; dice::HIDDEN_SIZE],
+        })
+    }
+
+    /// Test the resident artifact batched derivation in process.
+    #[test]
+    fn derive_with_resident_artifacts() -> Result<()> {
+        let (cdi_attest, cdi_seal, bcc) = diced_sample_inputs::make_sample_bcc_and_cdis()?;
+
+        let artifacts =
+            ResidentArtifacts::new(cdi_attest[..].try_into()?, cdi_seal[..].try_into()?, &bcc)?;
+
+        let input_values = &[
+            make_input_values("component 1 code", "component 1", "component 1 authority")?,
+            make_input_values("component 2 code", "component 2", "component 2 authority")?,
+            make_input_values("component 3 code", "component 3", "component 3 authority")?,
+        ];
+
+        let input_values: Vec<utils::InputValues> = input_values.iter().map(|v| v.into()).collect();
+
+        let new_artifacts =
+            artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))?;
+
+        let result = utils::make_bcc_handover(
+            new_artifacts.cdi_attest(),
+            new_artifacts.cdi_seal(),
+            &new_artifacts.bcc(),
+        )?;
+
+        assert_eq!(result, make_derive_test_vector());
+        Ok(())
+    }
+
+    /// Test the ResidentHal hal implementation which performs the derivation in a separate
+    /// process and returns the result through a pipe. This test compares the result against
+    /// the same test vector as the in process test above.
+    #[test]
+    fn derive_with_insecure_artifacts() -> Result<()> {
+        let (cdi_attest, cdi_seal, bcc) = diced_sample_inputs::make_sample_bcc_and_cdis()?;
+
+        // Safety: ResidentHal can only be used in single threaded environments.
+        // On-device Rust tests run each test in a separate process.
+        let hal_impl = unsafe {
+            ResidentHal::new(InsecureSerializableArtifacts {
+                cdi_attest: cdi_attest[..].try_into()?,
+                cdi_seal: cdi_seal[..].try_into()?,
+                bcc,
+            })
+        }
+        .expect("Failed to create ResidentHal.");
+
+        let bcc_handover = hal_impl
+            .derive(&[
+                make_input_values("component 1 code", "component 1", "component 1 authority")?,
+                make_input_values("component 2 code", "component 2", "component 2 authority")?,
+                make_input_values("component 3 code", "component 3", "component 3 authority")?,
+            ])
+            .expect("Failed to derive artifacts.");
+
+        assert_eq!(bcc_handover, make_derive_test_vector());
+        Ok(())
+    }
+
+    /// Demoting the implementation two steps and then performing one step of child derivation
+    /// must yield the same outcome as three derivations with the same input values.
+    #[test]
+    fn demote() -> Result<()> {
+        let (cdi_attest, cdi_seal, bcc) = diced_sample_inputs::make_sample_bcc_and_cdis()?;
+
+        // Safety: ResidentHal can only be used in single threaded environments.
+        // On-device Rust tests run each test in a separate process.
+        let hal_impl = unsafe {
+            ResidentHal::new(InsecureSerializableArtifacts {
+                cdi_attest: cdi_attest[..].try_into()?,
+                cdi_seal: cdi_seal[..].try_into()?,
+                bcc,
+            })
+        }
+        .expect("Failed to create ResidentHal.");
+
+        hal_impl
+            .demote(&[
+                make_input_values("component 1 code", "component 1", "component 1 authority")?,
+                make_input_values("component 2 code", "component 2", "component 2 authority")?,
+            ])
+            .expect("Failed to demote implementation.");
+
+        let bcc_handover = hal_impl
+            .derive(&[make_input_values(
+                "component 3 code",
+                "component 3",
+                "component 3 authority",
+            )?])
+            .expect("Failed to derive artifacts.");
+
+        assert_eq!(bcc_handover, make_derive_test_vector());
+        Ok(())
+    }
+
+    fn make_derive_test_vector() -> BccHandover {
+        utils::make_bcc_handover(
+            &[
+                // cdi_attest
+                0x8f, 0xdf, 0x93, 0x67, 0xd7, 0x0e, 0xf8, 0xb8, 0xd2, 0x9c, 0x30, 0xeb, 0x4e, 0x9b,
+                0x71, 0x5f, 0x9a, 0x5b, 0x67, 0xa6, 0x29, 0xe0, 0x00, 0x9b, 0x4d, 0xe6, 0x95, 0xcf,
+                0xf9, 0xed, 0x5e, 0x9b,
+            ],
+            &[
+                // cdi_seal
+                0x15, 0x3e, 0xd6, 0x30, 0x5a, 0x8d, 0x4b, 0x6f, 0x07, 0x3f, 0x5d, 0x89, 0xc5, 0x6e,
+                0x30, 0xba, 0x05, 0x56, 0xfc, 0x66, 0xf4, 0xae, 0xce, 0x7f, 0x81, 0xb9, 0xc5, 0x21,
+                0x9b, 0x49, 0x3d, 0xe1,
+            ],
+            &[
+                // bcc
+                0x87, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0x3e,
+                0x85, 0xe5, 0x72, 0x75, 0x55, 0xe5, 0x1e, 0xe7, 0xf3, 0x35, 0x94, 0x8e, 0xbb, 0xbd,
+                0x74, 0x1e, 0x1d, 0xca, 0x49, 0x9c, 0x97, 0x39, 0x77, 0x06, 0xd3, 0xc8, 0x6e, 0x8b,
+                0xd7, 0x33, 0xf9, 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x8a, 0xa9, 0x01,
+                0x78, 0x28, 0x34, 0x32, 0x64, 0x38, 0x38, 0x36, 0x34, 0x66, 0x39, 0x37, 0x62, 0x36,
+                0x35, 0x34, 0x37, 0x61, 0x35, 0x30, 0x63, 0x31, 0x65, 0x30, 0x61, 0x37, 0x34, 0x39,
+                0x66, 0x38, 0x65, 0x66, 0x38, 0x62, 0x38, 0x31, 0x65, 0x63, 0x36, 0x32, 0x61, 0x66,
+                0x02, 0x78, 0x28, 0x31, 0x66, 0x36, 0x39, 0x36, 0x66, 0x30, 0x37, 0x32, 0x35, 0x32,
+                0x66, 0x32, 0x39, 0x65, 0x39, 0x33, 0x66, 0x65, 0x34, 0x64, 0x65, 0x31, 0x39, 0x65,
+                0x65, 0x33, 0x32, 0x63, 0x64, 0x38, 0x31, 0x64, 0x63, 0x34, 0x30, 0x34, 0x65, 0x37,
+                0x36, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0x16, 0x48, 0xf2, 0x55, 0x53, 0x23,
+                0xdd, 0x15, 0x2e, 0x83, 0x38, 0xc3, 0x64, 0x38, 0x63, 0x26, 0x0f, 0xcf, 0x5b, 0xd1,
+                0x3a, 0xd3, 0x40, 0x3e, 0x23, 0xf8, 0x34, 0x4c, 0x6d, 0xa2, 0xbe, 0x25, 0x1c, 0xb0,
+                0x29, 0xe8, 0xc3, 0xfb, 0xb8, 0x80, 0xdc, 0xb1, 0xd2, 0xb3, 0x91, 0x4d, 0xd3, 0xfb,
+                0x01, 0x0f, 0xe4, 0xe9, 0x46, 0xa2, 0xc0, 0x26, 0x57, 0x5a, 0xba, 0x30, 0xf7, 0x15,
+                0x98, 0x14, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x56, 0xa3, 0x3a, 0x00, 0x01, 0x11, 0x71,
+                0x63, 0x41, 0x42, 0x4c, 0x3a, 0x00, 0x01, 0x11, 0x72, 0x01, 0x3a, 0x00, 0x01, 0x11,
+                0x73, 0xf6, 0x3a, 0x00, 0x47, 0x44, 0x52, 0x58, 0x40, 0x47, 0xae, 0x42, 0x27, 0x4c,
+                0xcb, 0x65, 0x4d, 0xee, 0x74, 0x2d, 0x05, 0x78, 0x2a, 0x08, 0x2a, 0xa5, 0xf0, 0xcf,
+                0xea, 0x3e, 0x60, 0xee, 0x97, 0x11, 0x4b, 0x5b, 0xe6, 0x05, 0x0c, 0xe8, 0x90, 0xf5,
+                0x22, 0xc4, 0xc6, 0x67, 0x7a, 0x22, 0x27, 0x17, 0xb3, 0x79, 0xcc, 0x37, 0x64, 0x5e,
+                0x19, 0x4f, 0x96, 0x37, 0x67, 0x3c, 0xd0, 0xc5, 0xed, 0x0f, 0xdd, 0xe7, 0x2e, 0x4f,
+                0x70, 0x97, 0x30, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x40, 0xf9, 0x00, 0x9d, 0xc2,
+                0x59, 0x09, 0xe0, 0xb6, 0x98, 0xbd, 0xe3, 0x97, 0x4a, 0xcb, 0x3c, 0xe7, 0x6b, 0x24,
+                0xc3, 0xe4, 0x98, 0xdd, 0xa9, 0x6a, 0x41, 0x59, 0x15, 0xb1, 0x23, 0xe6, 0xc8, 0xdf,
+                0xfb, 0x52, 0xb4, 0x52, 0xc1, 0xb9, 0x61, 0xdd, 0xbc, 0x5b, 0x37, 0x0e, 0x12, 0x12,
+                0xb2, 0xfd, 0xc1, 0x09, 0xb0, 0xcf, 0x33, 0x81, 0x4c, 0xc6, 0x29, 0x1b, 0x99, 0xea,
+                0xae, 0xfd, 0xaa, 0x0d, 0x3a, 0x00, 0x47, 0x44, 0x56, 0x41, 0x01, 0x3a, 0x00, 0x47,
+                0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06,
+                0x21, 0x58, 0x20, 0xb1, 0x02, 0xcc, 0x2c, 0xb2, 0x6a, 0x3b, 0xe9, 0xc1, 0xd3, 0x95,
+                0x10, 0xa0, 0xe1, 0xff, 0x51, 0xde, 0x57, 0xd5, 0x65, 0x28, 0xfd, 0x7f, 0xeb, 0xd4,
+                0xca, 0x15, 0xf3, 0xca, 0xdf, 0x37, 0x88, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20,
+                0x58, 0x40, 0x58, 0xd8, 0x03, 0x24, 0x53, 0x60, 0x57, 0xa9, 0x09, 0xfa, 0xab, 0xdc,
+                0x57, 0x1e, 0xf0, 0xe5, 0x1e, 0x51, 0x6f, 0x9e, 0xa3, 0x42, 0xe6, 0x6a, 0x8c, 0xaa,
+                0xad, 0x08, 0x48, 0xde, 0x7f, 0x4f, 0x6e, 0x2f, 0x7f, 0x39, 0x6c, 0xa1, 0xf8, 0x42,
+                0x71, 0xfe, 0x17, 0x3d, 0xca, 0x31, 0x83, 0x92, 0xed, 0xbb, 0x40, 0xb8, 0x10, 0xe0,
+                0xf2, 0x5a, 0x99, 0x53, 0x38, 0x46, 0x33, 0x97, 0x78, 0x05, 0x84, 0x43, 0xa1, 0x01,
+                0x27, 0xa0, 0x59, 0x01, 0x8a, 0xa9, 0x01, 0x78, 0x28, 0x31, 0x66, 0x36, 0x39, 0x36,
+                0x66, 0x30, 0x37, 0x32, 0x35, 0x32, 0x66, 0x32, 0x39, 0x65, 0x39, 0x33, 0x66, 0x65,
+                0x34, 0x64, 0x65, 0x31, 0x39, 0x65, 0x65, 0x33, 0x32, 0x63, 0x64, 0x38, 0x31, 0x64,
+                0x63, 0x34, 0x30, 0x34, 0x65, 0x37, 0x36, 0x02, 0x78, 0x28, 0x32, 0x35, 0x39, 0x34,
+                0x38, 0x39, 0x65, 0x36, 0x39, 0x37, 0x34, 0x38, 0x37, 0x30, 0x35, 0x64, 0x65, 0x33,
+                0x65, 0x32, 0x66, 0x34, 0x34, 0x32, 0x36, 0x37, 0x65, 0x61, 0x34, 0x39, 0x33, 0x38,
+                0x66, 0x66, 0x36, 0x61, 0x35, 0x37, 0x32, 0x35, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58,
+                0x40, 0xa4, 0x0c, 0xcb, 0xc1, 0xbf, 0xfa, 0xcc, 0xfd, 0xeb, 0xf4, 0xfc, 0x43, 0x83,
+                0x7f, 0x46, 0x8d, 0xd8, 0xd8, 0x14, 0xc1, 0x96, 0x14, 0x1f, 0x6e, 0xb3, 0xa0, 0xd9,
+                0x56, 0xb3, 0xbf, 0x2f, 0xfa, 0x88, 0x70, 0x11, 0x07, 0x39, 0xa4, 0xd2, 0xa9, 0x6b,
+                0x18, 0x28, 0xe8, 0x29, 0x20, 0x49, 0x0f, 0xbb, 0x8d, 0x08, 0x8c, 0xc6, 0x54, 0xe9,
+                0x71, 0xd2, 0x7e, 0xa4, 0xfe, 0x58, 0x7f, 0xd3, 0xc7, 0x3a, 0x00, 0x47, 0x44, 0x53,
+                0x56, 0xa3, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x63, 0x41, 0x56, 0x42, 0x3a, 0x00, 0x01,
+                0x11, 0x72, 0x01, 0x3a, 0x00, 0x01, 0x11, 0x73, 0xf6, 0x3a, 0x00, 0x47, 0x44, 0x52,
+                0x58, 0x40, 0x93, 0x17, 0xe1, 0x11, 0x27, 0x59, 0xd0, 0xef, 0x75, 0x0b, 0x2b, 0x1c,
+                0x0f, 0x5f, 0x52, 0xc3, 0x29, 0x23, 0xb5, 0x2a, 0xe6, 0x12, 0x72, 0x6f, 0x39, 0x86,
+                0x65, 0x2d, 0xf2, 0xe4, 0xe7, 0xd0, 0xaf, 0x0e, 0xa7, 0x99, 0x16, 0x89, 0x97, 0x21,
+                0xf7, 0xdc, 0x89, 0xdc, 0xde, 0xbb, 0x94, 0x88, 0x1f, 0xda, 0xe2, 0xf3, 0xe0, 0x54,
+                0xf9, 0x0e, 0x29, 0xb1, 0xbd, 0xe1, 0x0c, 0x0b, 0xd7, 0xf6, 0x3a, 0x00, 0x47, 0x44,
+                0x54, 0x58, 0x40, 0xb2, 0x69, 0x05, 0x48, 0x56, 0xb5, 0xfa, 0x55, 0x6f, 0xac, 0x56,
+                0xd9, 0x02, 0x35, 0x2b, 0xaa, 0x4c, 0xba, 0x28, 0xdd, 0x82, 0x3a, 0x86, 0xf5, 0xd4,
+                0xc2, 0xf1, 0xf9, 0x35, 0x7d, 0xe4, 0x43, 0x13, 0xbf, 0xfe, 0xd3, 0x36, 0xd8, 0x1c,
+                0x12, 0x78, 0x5c, 0x9c, 0x3e, 0xf6, 0x66, 0xef, 0xab, 0x3d, 0x0f, 0x89, 0xa4, 0x6f,
+                0xc9, 0x72, 0xee, 0x73, 0x43, 0x02, 0x8a, 0xef, 0xbc, 0x05, 0x98, 0x3a, 0x00, 0x47,
+                0x44, 0x56, 0x41, 0x01, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01,
+                0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0x96, 0x6d, 0x96, 0x42,
+                0xda, 0x64, 0x51, 0xad, 0xfa, 0x00, 0xbc, 0xbc, 0x95, 0x8a, 0xb0, 0xb9, 0x76, 0x01,
+                0xe6, 0xbd, 0xc0, 0x26, 0x79, 0x26, 0xfc, 0x0f, 0x1d, 0x87, 0x65, 0xf1, 0xf3, 0x99,
+                0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40, 0x10, 0x7f, 0x77, 0xad, 0x70,
+                0xbd, 0x52, 0x81, 0x28, 0x8d, 0x24, 0x81, 0xb4, 0x3f, 0x21, 0x68, 0x9f, 0xc3, 0x80,
+                0x68, 0x86, 0x55, 0xfb, 0x2e, 0x6d, 0x96, 0xe1, 0xe1, 0xb7, 0x28, 0x8d, 0x63, 0x85,
+                0xba, 0x2a, 0x01, 0x33, 0x87, 0x60, 0x63, 0xbb, 0x16, 0x3f, 0x2f, 0x3d, 0xf4, 0x2d,
+                0x48, 0x5b, 0x87, 0xed, 0xda, 0x34, 0xeb, 0x9c, 0x4d, 0x14, 0xac, 0x65, 0xf4, 0xfa,
+                0xef, 0x45, 0x0b, 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x8f, 0xa9, 0x01,
+                0x78, 0x28, 0x32, 0x35, 0x39, 0x34, 0x38, 0x39, 0x65, 0x36, 0x39, 0x37, 0x34, 0x38,
+                0x37, 0x30, 0x35, 0x64, 0x65, 0x33, 0x65, 0x32, 0x66, 0x34, 0x34, 0x32, 0x36, 0x37,
+                0x65, 0x61, 0x34, 0x39, 0x33, 0x38, 0x66, 0x66, 0x36, 0x61, 0x35, 0x37, 0x32, 0x35,
+                0x02, 0x78, 0x28, 0x35, 0x64, 0x34, 0x65, 0x64, 0x37, 0x66, 0x34, 0x31, 0x37, 0x61,
+                0x39, 0x35, 0x34, 0x61, 0x31, 0x38, 0x31, 0x34, 0x30, 0x37, 0x62, 0x35, 0x38, 0x38,
+                0x35, 0x61, 0x66, 0x64, 0x37, 0x32, 0x61, 0x35, 0x62, 0x66, 0x34, 0x30, 0x64, 0x61,
+                0x36, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x00, 0x00, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x58, 0x1a, 0xa3, 0x3a, 0x00, 0x01, 0x11,
+                0x71, 0x67, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x3a, 0x00, 0x01, 0x11, 0x72,
+                0x0c, 0x3a, 0x00, 0x01, 0x11, 0x73, 0xf6, 0x3a, 0x00, 0x47, 0x44, 0x52, 0x58, 0x40,
+                0x26, 0x1a, 0xbd, 0x26, 0xd8, 0x37, 0x8f, 0x4a, 0xf2, 0x9e, 0x49, 0x4d, 0x93, 0x23,
+                0xc4, 0x6e, 0x02, 0xda, 0xe0, 0x00, 0x02, 0xe7, 0xed, 0x29, 0xdf, 0x2b, 0xb3, 0x69,
+                0xf3, 0x55, 0x0e, 0x4c, 0x22, 0xdc, 0xcf, 0xf5, 0x92, 0xc9, 0xfa, 0x78, 0x98, 0xf1,
+                0x0e, 0x55, 0x5f, 0xf4, 0x45, 0xed, 0xc0, 0x0a, 0x72, 0x2a, 0x7a, 0x3a, 0xd2, 0xb1,
+                0xf7, 0x76, 0xfe, 0x2a, 0x6b, 0x7b, 0x2a, 0x53, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58,
+                0x40, 0x04, 0x25, 0x5d, 0x60, 0x5f, 0x5c, 0x45, 0x0d, 0xf2, 0x9a, 0x6e, 0x99, 0x30,
+                0x03, 0xb8, 0xd6, 0xe1, 0x99, 0x71, 0x1b, 0xf8, 0x44, 0xfa, 0xb5, 0x31, 0x79, 0x1c,
+                0x37, 0x68, 0x4e, 0x1d, 0xc0, 0x24, 0x74, 0x68, 0xf8, 0x80, 0x20, 0x3e, 0x44, 0xb1,
+                0x43, 0xd2, 0x9c, 0xfc, 0x12, 0x9e, 0x77, 0x0a, 0xde, 0x29, 0x24, 0xff, 0x2e, 0xfa,
+                0xc7, 0x10, 0xd5, 0x73, 0xd4, 0xc6, 0xdf, 0x62, 0x9f, 0x3a, 0x00, 0x47, 0x44, 0x56,
+                0x41, 0x01, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27,
+                0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0xdb, 0xe7, 0x5b, 0x3f, 0xa3, 0x42,
+                0xb0, 0x9c, 0xf8, 0x40, 0x8c, 0xb0, 0x9c, 0xf0, 0x0a, 0xaf, 0xdf, 0x6f, 0xe5, 0x09,
+                0x21, 0x11, 0x92, 0xe1, 0xf8, 0xc5, 0x09, 0x02, 0x3d, 0x1f, 0xb7, 0xc5, 0x3a, 0x00,
+                0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40, 0xc4, 0xc1, 0xd7, 0x1c, 0x2d, 0x26, 0x89,
+                0x22, 0xcf, 0xa6, 0x99, 0x77, 0x30, 0x84, 0x86, 0x27, 0x59, 0x8f, 0xd8, 0x08, 0x75,
+                0xe0, 0xb2, 0xef, 0xf9, 0xfa, 0xa5, 0x40, 0x8c, 0xd3, 0xeb, 0xbb, 0xda, 0xf2, 0xc8,
+                0xae, 0x41, 0x22, 0x50, 0x9c, 0xe8, 0xb2, 0x9c, 0x9b, 0x3f, 0x8a, 0x78, 0x76, 0xab,
+                0xd0, 0xbe, 0xfc, 0xe4, 0x79, 0xcb, 0x1b, 0x2b, 0xaa, 0x4d, 0xdd, 0x15, 0x61, 0x42,
+                0x06, 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x8d, 0xa9, 0x01, 0x78, 0x28,
+                0x35, 0x64, 0x34, 0x65, 0x64, 0x37, 0x66, 0x34, 0x31, 0x37, 0x61, 0x39, 0x35, 0x34,
+                0x61, 0x31, 0x38, 0x31, 0x34, 0x30, 0x37, 0x62, 0x35, 0x38, 0x38, 0x35, 0x61, 0x66,
+                0x64, 0x37, 0x32, 0x61, 0x35, 0x62, 0x66, 0x34, 0x30, 0x64, 0x61, 0x36, 0x02, 0x78,
+                0x28, 0x36, 0x39, 0x62, 0x31, 0x37, 0x36, 0x37, 0x35, 0x38, 0x61, 0x36, 0x66, 0x34,
+                0x34, 0x62, 0x35, 0x65, 0x38, 0x39, 0x39, 0x63, 0x64, 0x65, 0x33, 0x63, 0x66, 0x34,
+                0x35, 0x31, 0x39, 0x61, 0x39, 0x33, 0x35, 0x62, 0x63, 0x39, 0x66, 0x65, 0x34, 0x3a,
+                0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0x31, 0x0d, 0x31, 0xfa, 0x78, 0x58, 0x33, 0xf2,
+                0xf8, 0x58, 0x6b, 0xe9, 0x68, 0x32, 0x44, 0xd0, 0xfc, 0x2d, 0xe1, 0xfc, 0xe1, 0xc2,
+                0x4e, 0x2b, 0xa8, 0x2c, 0xa1, 0xc1, 0x48, 0xc6, 0xaa, 0x91, 0x89, 0x4f, 0xb7, 0x9c,
+                0x40, 0x74, 0x21, 0x36, 0x31, 0x45, 0x09, 0xdf, 0x0c, 0xb4, 0xf9, 0x9a, 0x59, 0xae,
+                0x4f, 0x21, 0x10, 0xc1, 0x38, 0xa8, 0xa2, 0xbe, 0xc6, 0x36, 0xf0, 0x56, 0x58, 0xdb,
+                0x3a, 0x00, 0x47, 0x44, 0x53, 0x58, 0x18, 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x6b,
+                0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x20, 0x31, 0x3a, 0x00, 0x01,
+                0x11, 0x73, 0xf6, 0x3a, 0x00, 0x47, 0x44, 0x52, 0x58, 0x40, 0xce, 0x8a, 0x30, 0x4e,
+                0x31, 0x53, 0xea, 0xdd, 0x2f, 0xbd, 0x15, 0xbc, 0x6b, 0x0f, 0xe7, 0x43, 0x50, 0xef,
+                0x65, 0xec, 0x4e, 0x21, 0x64, 0x6e, 0x41, 0x22, 0xac, 0x87, 0xda, 0xf1, 0xf2, 0x80,
+                0xc6, 0x8a, 0xd8, 0x7b, 0xe8, 0xe2, 0x9b, 0x87, 0x21, 0x5e, 0x26, 0x23, 0x11, 0x89,
+                0x86, 0x57, 0x2d, 0x47, 0x73, 0x3f, 0x47, 0x87, 0xfa, 0x58, 0x5c, 0x78, 0x7b, 0xa3,
+                0xfc, 0x2b, 0x6c, 0xed, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x40, 0xd8, 0x40, 0xa0,
+                0x60, 0x45, 0x28, 0x5d, 0xd4, 0xc1, 0x08, 0x3c, 0xbc, 0x91, 0xf4, 0xa6, 0xa4, 0xde,
+                0xd3, 0x3d, 0xbb, 0x24, 0x46, 0xa3, 0x58, 0x49, 0x57, 0x4d, 0x2e, 0x6d, 0x7a, 0x78,
+                0x4b, 0x9d, 0x28, 0x9a, 0x4e, 0xf1, 0x23, 0x06, 0x35, 0xff, 0x8e, 0x1e, 0xb3, 0x02,
+                0x63, 0x62, 0x9a, 0x50, 0x6d, 0x18, 0x70, 0x8e, 0xe3, 0x2e, 0x29, 0xb4, 0x22, 0x71,
+                0x31, 0x39, 0x65, 0xd5, 0xb5, 0x3a, 0x00, 0x47, 0x44, 0x56, 0x41, 0x01, 0x3a, 0x00,
+                0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20,
+                0x06, 0x21, 0x58, 0x20, 0x51, 0x3c, 0x4b, 0x56, 0x0b, 0x49, 0x0b, 0xee, 0xc5, 0x71,
+                0xd4, 0xe7, 0xbc, 0x44, 0x27, 0x4f, 0x4e, 0x67, 0xfc, 0x3a, 0xb9, 0x47, 0x8c, 0x6f,
+                0x24, 0x29, 0xf8, 0xb8, 0x2f, 0xa7, 0xb3, 0x4d, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41,
+                0x20, 0x58, 0x40, 0x4e, 0x6d, 0x0e, 0x2b, 0x1d, 0x44, 0x99, 0xb6, 0x63, 0x07, 0x86,
+                0x1a, 0xce, 0x4b, 0xdc, 0xd1, 0x3a, 0xdc, 0xbf, 0xaa, 0xb3, 0x06, 0xd9, 0xb5, 0x5c,
+                0x75, 0xf0, 0x14, 0x63, 0xa9, 0x1e, 0x7c, 0x56, 0x62, 0x2c, 0xa5, 0xda, 0xc9, 0x81,
+                0xcb, 0x3d, 0x63, 0x32, 0x6b, 0x76, 0x81, 0xd2, 0x93, 0xeb, 0xac, 0xfe, 0x0c, 0x87,
+                0x66, 0x9e, 0x87, 0x82, 0xb4, 0x81, 0x6e, 0x33, 0xf1, 0x08, 0x01, 0x84, 0x43, 0xa1,
+                0x01, 0x27, 0xa0, 0x59, 0x01, 0x8d, 0xa9, 0x01, 0x78, 0x28, 0x36, 0x39, 0x62, 0x31,
+                0x37, 0x36, 0x37, 0x35, 0x38, 0x61, 0x36, 0x66, 0x34, 0x34, 0x62, 0x35, 0x65, 0x38,
+                0x39, 0x39, 0x63, 0x64, 0x65, 0x33, 0x63, 0x66, 0x34, 0x35, 0x31, 0x39, 0x61, 0x39,
+                0x33, 0x35, 0x62, 0x63, 0x39, 0x66, 0x65, 0x34, 0x02, 0x78, 0x28, 0x32, 0x39, 0x65,
+                0x34, 0x62, 0x61, 0x63, 0x33, 0x30, 0x31, 0x65, 0x66, 0x36, 0x35, 0x61, 0x38, 0x31,
+                0x31, 0x62, 0x39, 0x39, 0x62, 0x30, 0x33, 0x64, 0x65, 0x39, 0x35, 0x34, 0x65, 0x61,
+                0x37, 0x36, 0x61, 0x38, 0x39, 0x31, 0x37, 0x38, 0x35, 0x3a, 0x00, 0x47, 0x44, 0x50,
+                0x58, 0x40, 0xa4, 0x03, 0xe3, 0xde, 0x44, 0x96, 0xed, 0x31, 0x41, 0xa0, 0xba, 0x59,
+                0xee, 0x2b, 0x03, 0x65, 0xcb, 0x63, 0x14, 0x78, 0xbe, 0xad, 0x24, 0x33, 0xb8, 0x6b,
+                0x52, 0xd8, 0xab, 0xd5, 0x79, 0x84, 0x98, 0x6c, 0xc2, 0x66, 0xeb, 0x6c, 0x24, 0xa6,
+                0xfa, 0x32, 0xa8, 0x16, 0xb8, 0x64, 0x37, 0x2b, 0xd4, 0xc0, 0xc4, 0xc2, 0x63, 0x25,
+                0x10, 0xce, 0x47, 0xe3, 0x49, 0xad, 0x41, 0xf5, 0xc8, 0xf6, 0x3a, 0x00, 0x47, 0x44,
+                0x53, 0x58, 0x18, 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x6b, 0x63, 0x6f, 0x6d, 0x70,
+                0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x20, 0x32, 0x3a, 0x00, 0x01, 0x11, 0x73, 0xf6, 0x3a,
+                0x00, 0x47, 0x44, 0x52, 0x58, 0x40, 0xc7, 0x50, 0x09, 0xd0, 0xe0, 0xdd, 0x80, 0x77,
+                0xae, 0xa7, 0xc8, 0x88, 0x1e, 0x88, 0xd0, 0xc7, 0x0d, 0x7c, 0x49, 0xc5, 0xb5, 0x64,
+                0x32, 0x28, 0x2c, 0x48, 0x94, 0xc0, 0xd6, 0x7d, 0x9c, 0x86, 0xda, 0xf7, 0x98, 0xc7,
+                0xae, 0xa4, 0x0e, 0x61, 0xc8, 0xb0, 0x8b, 0x8a, 0xe4, 0xad, 0xcf, 0xcf, 0x6d, 0x60,
+                0x60, 0x31, 0xdd, 0xa7, 0x24, 0x9b, 0x27, 0x16, 0x31, 0x90, 0x80, 0x70, 0xc3, 0xba,
+                0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x40, 0xf8, 0x86, 0xc6, 0x94, 0xf9, 0x3f, 0x66,
+                0x3c, 0x43, 0x01, 0x29, 0x27, 0x8d, 0x3c, 0xb2, 0x11, 0xf2, 0x04, 0xb6, 0x67, 0x4f,
+                0x5f, 0x90, 0xcb, 0xc6, 0x73, 0xe6, 0x25, 0x14, 0x63, 0xa7, 0x95, 0x11, 0x0e, 0xa0,
+                0x1d, 0x3f, 0x6a, 0x58, 0x0a, 0x53, 0xaa, 0x68, 0x3b, 0x92, 0x64, 0x2b, 0x2e, 0x79,
+                0x80, 0x70, 0x0e, 0x41, 0xf5, 0xe9, 0x2a, 0x36, 0x0a, 0xa4, 0xe8, 0xb4, 0xe5, 0xdd,
+                0xa6, 0x3a, 0x00, 0x47, 0x44, 0x56, 0x41, 0x01, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58,
+                0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20,
+                0x9e, 0x04, 0x11, 0x24, 0x34, 0xba, 0x40, 0xed, 0x86, 0xe9, 0x48, 0x70, 0x3b, 0xe7,
+                0x76, 0xfa, 0xc5, 0xf6, 0x6d, 0xab, 0x86, 0x12, 0x00, 0xbe, 0xc7, 0x00, 0x69, 0x0e,
+                0x97, 0x97, 0xa6, 0x12, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40, 0xb7,
+                0x31, 0xd5, 0x4c, 0x7d, 0xf5, 0xd7, 0xb8, 0xb4, 0x4f, 0x93, 0x47, 0x2c, 0x3d, 0x50,
+                0xcc, 0xad, 0x28, 0x23, 0x68, 0xcf, 0xc2, 0x90, 0xd7, 0x02, 0x00, 0xd8, 0xf1, 0x00,
+                0x14, 0x03, 0x90, 0x9e, 0x0b, 0x91, 0xa7, 0x22, 0x28, 0xfe, 0x55, 0x42, 0x30, 0x93,
+                0x05, 0x66, 0xcd, 0xce, 0xb8, 0x48, 0x07, 0x56, 0x54, 0x67, 0xa5, 0xd7, 0xe3, 0x16,
+                0xd6, 0x75, 0x7c, 0x94, 0x98, 0x1b, 0x0b, 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59,
+                0x01, 0x8d, 0xa9, 0x01, 0x78, 0x28, 0x32, 0x39, 0x65, 0x34, 0x62, 0x61, 0x63, 0x33,
+                0x30, 0x31, 0x65, 0x66, 0x36, 0x35, 0x61, 0x38, 0x31, 0x31, 0x62, 0x39, 0x39, 0x62,
+                0x30, 0x33, 0x64, 0x65, 0x39, 0x35, 0x34, 0x65, 0x61, 0x37, 0x36, 0x61, 0x38, 0x39,
+                0x31, 0x37, 0x38, 0x35, 0x02, 0x78, 0x28, 0x31, 0x38, 0x37, 0x36, 0x63, 0x61, 0x63,
+                0x34, 0x32, 0x33, 0x39, 0x35, 0x37, 0x66, 0x33, 0x62, 0x66, 0x62, 0x32, 0x62, 0x32,
+                0x63, 0x39, 0x33, 0x37, 0x64, 0x31, 0x34, 0x62, 0x62, 0x38, 0x30, 0x64, 0x30, 0x36,
+                0x37, 0x33, 0x65, 0x66, 0x66, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0xf4, 0x7d,
+                0x11, 0x21, 0xc1, 0x19, 0x57, 0x23, 0x08, 0x6e, 0x5f, 0xe4, 0x55, 0xc5, 0x08, 0x16,
+                0x40, 0x5f, 0x2a, 0x6f, 0x04, 0x1e, 0x6f, 0x22, 0xde, 0x53, 0xbd, 0x37, 0xe2, 0xfb,
+                0xb4, 0x0b, 0x65, 0xf4, 0xdc, 0xc9, 0xf4, 0xce, 0x2d, 0x82, 0x2a, 0xbc, 0xaf, 0x37,
+                0x80, 0x0b, 0x7f, 0xff, 0x3a, 0x98, 0x9c, 0xa7, 0x70, 0x4f, 0xbc, 0x59, 0x4f, 0x4e,
+                0xb1, 0x6d, 0xdf, 0x60, 0x39, 0x11, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x58, 0x18, 0xa2,
+                0x3a, 0x00, 0x01, 0x11, 0x71, 0x6b, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e,
+                0x74, 0x20, 0x33, 0x3a, 0x00, 0x01, 0x11, 0x73, 0xf6, 0x3a, 0x00, 0x47, 0x44, 0x52,
+                0x58, 0x40, 0xa4, 0xd5, 0x6f, 0xc8, 0xd6, 0xc7, 0xe4, 0x22, 0xb4, 0x7a, 0x26, 0x49,
+                0xd5, 0xb4, 0xc1, 0xc6, 0x1b, 0xfa, 0x14, 0x8c, 0x49, 0x72, 0x2f, 0xfe, 0xbc, 0xc1,
+                0xc8, 0xc6, 0x65, 0x62, 0x86, 0xf7, 0xf2, 0x74, 0x45, 0x9b, 0x1a, 0xa0, 0x2b, 0xc4,
+                0x27, 0x13, 0xc5, 0xc3, 0xe5, 0x28, 0xc2, 0x16, 0xcd, 0x90, 0x6d, 0xa0, 0xf7, 0x27,
+                0x04, 0xa8, 0xa2, 0x62, 0xaa, 0x2c, 0x0c, 0x75, 0xd5, 0x9d, 0x3a, 0x00, 0x47, 0x44,
+                0x54, 0x58, 0x40, 0x1d, 0x92, 0x34, 0xfb, 0xfe, 0x74, 0xb7, 0xce, 0x3a, 0x95, 0x45,
+                0xe5, 0x3e, 0x1f, 0x5f, 0x18, 0x53, 0x5f, 0xe1, 0x85, 0xb0, 0x1d, 0xe3, 0x8d, 0x53,
+                0x77, 0xdc, 0x86, 0x32, 0x3d, 0x9b, 0xf9, 0xa5, 0x51, 0x17, 0x51, 0x9a, 0xd8, 0xa6,
+                0x7d, 0x45, 0x98, 0x47, 0xa2, 0x73, 0x54, 0x66, 0x28, 0x66, 0x92, 0x1d, 0x28, 0x8a,
+                0xe7, 0x5d, 0xb8, 0x96, 0x4b, 0x6a, 0x9d, 0xee, 0xc2, 0xe9, 0x20, 0x3a, 0x00, 0x47,
+                0x44, 0x56, 0x41, 0x01, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01,
+                0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0x4d, 0xf5, 0x61, 0x1e,
+                0xa6, 0x64, 0x74, 0x0b, 0x6c, 0x99, 0x8b, 0x6d, 0x34, 0x42, 0x21, 0xdd, 0x82, 0x26,
+                0x13, 0xb4, 0xf0, 0xbc, 0x9a, 0x0b, 0xf6, 0x56, 0xbd, 0x5d, 0xea, 0xd5, 0x07, 0x7a,
+                0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40, 0x40, 0x4d, 0x09, 0x0d, 0x80,
+                0xba, 0x12, 0x94, 0x05, 0xfb, 0x1a, 0x23, 0xa3, 0xcb, 0x28, 0x6f, 0xd7, 0x29, 0x95,
+                0xda, 0x83, 0x07, 0x3c, 0xbe, 0x7c, 0x37, 0xeb, 0x9c, 0xb2, 0x77, 0x10, 0x3f, 0x6a,
+                0x41, 0x80, 0xce, 0x56, 0xb7, 0x55, 0x22, 0x81, 0x77, 0x2d, 0x3c, 0xf8, 0x16, 0x38,
+                0x49, 0xcc, 0x9a, 0xe8, 0x3a, 0x03, 0x33, 0x4c, 0xe6, 0x87, 0x72, 0xf6, 0x5a, 0x4a,
+                0x3f, 0x4e, 0x0a,
+            ],
+        )
+        .unwrap()
+    }
+}
diff --git a/diced/src/lib.rs b/diced/src/lib.rs
new file mode 100644
index 0000000..9594977
--- /dev/null
+++ b/diced/src/lib.rs
@@ -0,0 +1,203 @@
+// 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 the android.security.dice.IDiceNode service.
+
+mod error;
+mod permission;
+mod proxy_node_hal;
+mod resident_node;
+
+pub use crate::proxy_node_hal::ProxyNodeHal;
+pub use crate::resident_node::ResidentNode;
+use android_hardware_security_dice::aidl::android::hardware::security::dice::{
+    Bcc::Bcc, BccHandover::BccHandover, Config::Config as BinderConfig,
+    InputValues::InputValues as BinderInputValues, Mode::Mode, Signature::Signature,
+};
+use android_security_dice::aidl::android::security::dice::{
+    IDiceMaintenance::BnDiceMaintenance, IDiceMaintenance::IDiceMaintenance, IDiceNode::BnDiceNode,
+    IDiceNode::IDiceNode, ResponseCode::ResponseCode,
+};
+use anyhow::{Context, Result};
+use binder::{BinderFeatures, Result as BinderResult, Strong, ThreadState};
+pub use diced_open_dice_cbor as dice;
+use error::{map_or_log_err, Error};
+use keystore2_selinux as selinux;
+use libc::uid_t;
+use permission::Permission;
+use std::sync::Arc;
+
+/// A DiceNode backend implementation.
+/// All functions except demote_self derive effective dice artifacts staring from
+/// this node and iterating through `{ [client | demotion path], input_values }`
+/// in ascending order.
+pub trait DiceNodeImpl {
+    /// Signs the message using the effective dice artifacts and Ed25519Pure.
+    fn sign(
+        &self,
+        client: BinderInputValues,
+        input_values: &[BinderInputValues],
+        message: &[u8],
+    ) -> Result<Signature>;
+    /// Returns the effective attestation chain.
+    fn get_attestation_chain(
+        &self,
+        client: BinderInputValues,
+        input_values: &[BinderInputValues],
+    ) -> Result<Bcc>;
+    /// Returns the effective dice artifacts.
+    fn derive(
+        &self,
+        client: BinderInputValues,
+        input_values: &[BinderInputValues],
+    ) -> Result<BccHandover>;
+    /// Adds [ `client` | `input_values` ] to the demotion path of the given client.
+    /// This changes the effective dice artifacts for all subsequent API calls of the
+    /// given client.
+    fn demote(&self, client: BinderInputValues, input_values: &[BinderInputValues]) -> Result<()>;
+    /// This demotes the implementation itself. I.e. a resident node would replace its resident
+    /// with the effective artifacts derived using `input_values`. A proxy node would
+    /// simply call `demote` on its parent node. This is not reversible and changes
+    /// the effective dice artifacts of all clients.
+    fn demote_self(&self, input_values: &[BinderInputValues]) -> Result<()>;
+}
+
+/// Wraps a DiceNodeImpl and implements the actual IDiceNode AIDL API.
+pub struct DiceNode {
+    node_impl: Arc<dyn DiceNodeImpl + Sync + Send>,
+}
+
+/// This function uses its namesake in the permission module and in
+/// combination with with_calling_sid from the binder crate to check
+/// if the caller has the given keystore permission.
+pub fn check_caller_permission<T: selinux::ClassPermission>(perm: T) -> Result<()> {
+    ThreadState::with_calling_sid(|calling_sid| {
+        let target_context =
+            selinux::getcon().context("In check_caller_permission: getcon failed.")?;
+
+        selinux::check_permission(
+            calling_sid.ok_or(Error::Rc(ResponseCode::SYSTEM_ERROR)).context(
+                "In check_keystore_permission: Cannot check permission without calling_sid.",
+            )?,
+            &target_context,
+            perm,
+        )
+    })
+}
+
+fn client_input_values(uid: uid_t) -> Result<BinderInputValues> {
+    Ok(BinderInputValues {
+        codeHash: [0; dice::HASH_SIZE],
+        config: BinderConfig {
+            desc: dice::bcc::format_config_descriptor(Some(&format!("{}", uid)), None, true)
+                .context("In client_input_values: failed to format config descriptor")?,
+        },
+        authorityHash: [0; dice::HASH_SIZE],
+        authorityDescriptor: None,
+        hidden: [0; dice::HIDDEN_SIZE],
+        mode: Mode::NORMAL,
+    })
+}
+
+impl DiceNode {
+    /// Constructs an instance of DiceNode, wraps it with a BnDiceNode object and
+    /// returns a strong pointer to the binder. The result can be used to register
+    /// the service with service manager.
+    pub fn new_as_binder(
+        node_impl: Arc<dyn DiceNodeImpl + Sync + Send>,
+    ) -> Result<Strong<dyn IDiceNode>> {
+        let result = BnDiceNode::new_binder(
+            DiceNode { node_impl },
+            BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+        );
+        Ok(result)
+    }
+
+    fn sign(&self, input_values: &[BinderInputValues], message: &[u8]) -> Result<Signature> {
+        check_caller_permission(Permission::UseSign).context("In DiceNode::sign:")?;
+        let client =
+            client_input_values(ThreadState::get_calling_uid()).context("In DiceNode::sign:")?;
+        self.node_impl.sign(client, input_values, message)
+    }
+    fn get_attestation_chain(&self, input_values: &[BinderInputValues]) -> Result<Bcc> {
+        check_caller_permission(Permission::GetAttestationChain)
+            .context("In DiceNode::get_attestation_chain:")?;
+        let client = client_input_values(ThreadState::get_calling_uid())
+            .context("In DiceNode::get_attestation_chain:")?;
+        self.node_impl.get_attestation_chain(client, input_values)
+    }
+    fn derive(&self, input_values: &[BinderInputValues]) -> Result<BccHandover> {
+        check_caller_permission(Permission::Derive).context("In DiceNode::derive:")?;
+        let client =
+            client_input_values(ThreadState::get_calling_uid()).context("In DiceNode::extend:")?;
+        self.node_impl.derive(client, input_values)
+    }
+    fn demote(&self, input_values: &[BinderInputValues]) -> Result<()> {
+        check_caller_permission(Permission::Demote).context("In DiceNode::demote:")?;
+        let client =
+            client_input_values(ThreadState::get_calling_uid()).context("In DiceNode::demote:")?;
+        self.node_impl.demote(client, input_values)
+    }
+}
+
+impl binder::Interface for DiceNode {}
+
+impl IDiceNode for DiceNode {
+    fn sign(&self, input_values: &[BinderInputValues], message: &[u8]) -> BinderResult<Signature> {
+        map_or_log_err(self.sign(input_values, message), Ok)
+    }
+    fn getAttestationChain(&self, input_values: &[BinderInputValues]) -> BinderResult<Bcc> {
+        map_or_log_err(self.get_attestation_chain(input_values), Ok)
+    }
+    fn derive(&self, input_values: &[BinderInputValues]) -> BinderResult<BccHandover> {
+        map_or_log_err(self.derive(input_values), Ok)
+    }
+    fn demote(&self, input_values: &[BinderInputValues]) -> BinderResult<()> {
+        map_or_log_err(self.demote(input_values), Ok)
+    }
+}
+
+/// Wraps a DiceNodeImpl and implements the IDiceMaintenance AIDL API.
+pub struct DiceMaintenance {
+    node_impl: Arc<dyn DiceNodeImpl + Sync + Send>,
+}
+
+impl DiceMaintenance {
+    /// Constructs an instance of DiceMaintenance, wraps it with a BnDiceMaintenance object and
+    /// returns a strong pointer to the binder. The result can be used to register the service
+    /// with service manager.
+    pub fn new_as_binder(
+        node_impl: Arc<dyn DiceNodeImpl + Sync + Send>,
+    ) -> Result<Strong<dyn IDiceMaintenance>> {
+        let result = BnDiceMaintenance::new_binder(
+            DiceMaintenance { node_impl },
+            BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+        );
+        Ok(result)
+    }
+
+    fn demote_self(&self, input_values: &[BinderInputValues]) -> Result<()> {
+        check_caller_permission(Permission::DemoteSelf)
+            .context("In DiceMaintenance::demote_self:")?;
+        self.node_impl.demote_self(input_values)
+    }
+}
+
+impl binder::Interface for DiceMaintenance {}
+
+impl IDiceMaintenance for DiceMaintenance {
+    fn demoteSelf(&self, input_values: &[BinderInputValues]) -> BinderResult<()> {
+        map_or_log_err(self.demote_self(input_values), Ok)
+    }
+}
diff --git a/diced/src/lib_vendor.rs b/diced/src/lib_vendor.rs
new file mode 100644
index 0000000..01c804b
--- /dev/null
+++ b/diced/src/lib_vendor.rs
@@ -0,0 +1,20 @@
+// 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 crate implements the android.hardware.security.dice.IDiceDevice interface
+//! and provides support for implementing a DICE HAL service.
+
+mod error_vendor;
+pub mod hal_node;
+pub use diced_open_dice_cbor as dice;
diff --git a/diced/src/permission.rs b/diced/src/permission.rs
new file mode 100644
index 0000000..116df1b
--- /dev/null
+++ b/diced/src/permission.rs
@@ -0,0 +1,46 @@
+// 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 crate provides convenience wrappers for the SELinux permission
+//! defined in the diced SELinux access class.
+
+use keystore2_selinux as selinux;
+use selinux::{implement_class, ClassPermission};
+
+implement_class!(
+    /// Permission provides a convenient abstraction from the SELinux class `diced`.
+    #[selinux(class_name = diced)]
+    #[derive(Clone, Copy, Debug, PartialEq)]
+    pub enum Permission {
+        /// Checked when a client attempts to call seal or unseal.
+        #[selinux(name = use_seal)]
+        UseSeal,
+        /// Checked when a client attempts to call IDiceNode::sign.
+        #[selinux(name = use_sign)]
+        UseSign,
+        /// Checked when a client attempts to call IDiceNode::getAttestationChain.
+        #[selinux(name = get_attestation_chain)]
+        GetAttestationChain,
+        /// Checked when a client attempts to call IDiceNode::derive.
+        #[selinux(name = derive)]
+        Derive,
+        /// Checked when a client wants to demote itself by calling IDiceNode::demote.
+        #[selinux(name = demote)]
+        Demote,
+        /// Checked when a client calls IDiceMaintenance::demote in an attempt to
+        /// demote this dice node.
+        #[selinux(name = demote_self)]
+        DemoteSelf,
+    }
+);
diff --git a/diced/src/proxy_node_hal.rs b/diced/src/proxy_node_hal.rs
new file mode 100644
index 0000000..8d883d2
--- /dev/null
+++ b/diced/src/proxy_node_hal.rs
@@ -0,0 +1,119 @@
+// 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.
+
+//! A proxy dice node delegates all accesses to CDI_attest and CDI_seal to a parent
+//! node, here an implementation of android.hardware.security.dice.IDiceDevice.
+
+#![allow(dead_code)]
+
+use crate::DiceNodeImpl;
+use android_hardware_security_dice::aidl::android::hardware::security::dice::{
+    Bcc::Bcc, BccHandover::BccHandover, IDiceDevice::IDiceDevice,
+    InputValues::InputValues as BinderInputValues, Signature::Signature,
+};
+use anyhow::{Context, Result};
+use binder::Strong;
+use std::collections::HashMap;
+use std::sync::RwLock;
+
+/// The ProxyNodeHal implements a IDiceNode backend delegating crypto operations
+/// to the corresponding HAL.
+pub struct ProxyNodeHal {
+    parent: Strong<dyn IDiceDevice>,
+    demotion_db: RwLock<HashMap<BinderInputValues, Vec<BinderInputValues>>>,
+}
+
+impl ProxyNodeHal {
+    /// Creates a new proxy node with a reference to the parent service.
+    pub fn new(parent: Strong<dyn IDiceDevice>) -> Result<Self> {
+        Ok(ProxyNodeHal { parent, demotion_db: Default::default() })
+    }
+
+    fn get_effective_input_values(
+        &self,
+        client: BinderInputValues,
+        input_values: &[BinderInputValues],
+    ) -> Vec<BinderInputValues> {
+        let demotion_db = self.demotion_db.read().unwrap();
+
+        let client_arr = [client];
+
+        demotion_db
+            .get(&client_arr[0])
+            .map(|v| v.iter())
+            .unwrap_or_else(|| client_arr.iter())
+            .chain(input_values.iter())
+            .cloned()
+            .collect()
+    }
+}
+
+impl DiceNodeImpl for ProxyNodeHal {
+    fn sign(
+        &self,
+        client: BinderInputValues,
+        input_values: &[BinderInputValues],
+        message: &[u8],
+    ) -> Result<Signature> {
+        self.parent
+            .sign(&self.get_effective_input_values(client, input_values), message)
+            .context("In ProxyNodeHal::sign:")
+    }
+
+    fn get_attestation_chain(
+        &self,
+        client: BinderInputValues,
+        input_values: &[BinderInputValues],
+    ) -> Result<Bcc> {
+        self.parent
+            .getAttestationChain(&self.get_effective_input_values(client, input_values))
+            .context("In ProxyNodeHal::get_attestation_chain:")
+    }
+
+    fn derive(
+        &self,
+        client: BinderInputValues,
+        input_values: &[BinderInputValues],
+    ) -> Result<BccHandover> {
+        self.parent
+            .derive(&self.get_effective_input_values(client, input_values))
+            .context("In ProxyNodeHal::derive:")
+    }
+
+    fn demote(&self, client: BinderInputValues, input_values: &[BinderInputValues]) -> Result<()> {
+        let mut demotion_db = self.demotion_db.write().unwrap();
+
+        let client_arr = [client];
+
+        // The following statement consults demotion database which yields an optional demotion
+        // path. It then constructs an iterator over the following elements, then clones and
+        // collects them into a new vector:
+        // [ demotion path | client ], input_values
+        let new_path: Vec<BinderInputValues> = demotion_db
+            .get(&client_arr[0])
+            .map(|v| v.iter())
+            .unwrap_or_else(|| client_arr.iter())
+            .chain(input_values)
+            .cloned()
+            .collect();
+
+        let [client] = client_arr;
+        demotion_db.insert(client, new_path);
+        Ok(())
+    }
+
+    fn demote_self(&self, input_values: &[BinderInputValues]) -> Result<()> {
+        self.parent.demote(input_values).context("In ProxyNodeHal::demote_self:")
+    }
+}
diff --git a/diced/src/resident_node.rs b/diced/src/resident_node.rs
new file mode 100644
index 0000000..99a6dc9
--- /dev/null
+++ b/diced/src/resident_node.rs
@@ -0,0 +1,191 @@
+// 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.
+
+//! A resident dice node keeps CDI_attest and CDI_seal memory resident and can serve
+//! its clients directly by performing all crypto operations including derivations and
+//! certificate generation itself.
+
+use crate::DiceNodeImpl;
+use android_hardware_security_dice::aidl::android::hardware::security::dice::{
+    Bcc::Bcc, BccHandover::BccHandover, InputValues::InputValues as BinderInputValues,
+    Signature::Signature,
+};
+use anyhow::{Context, Result};
+use dice::{ContextImpl, OpenDiceCborContext};
+use diced_open_dice_cbor as dice;
+use diced_utils::{self as utils, InputValues, ResidentArtifacts};
+use std::collections::HashMap;
+use std::convert::TryInto;
+use std::sync::RwLock;
+
+/// The ResidentNode implements a IDiceNode backend with memory resident DICE secrets.
+pub struct ResidentNode {
+    artifacts: RwLock<ResidentArtifacts>,
+    demotion_db: RwLock<HashMap<BinderInputValues, Vec<BinderInputValues>>>,
+}
+
+impl ResidentNode {
+    /// Creates a new Resident node with the given dice secrets and certificate chain.
+    pub fn new(
+        cdi_attest: &[u8; dice::CDI_SIZE],
+        cdi_seal: &[u8; dice::CDI_SIZE],
+        bcc: Vec<u8>,
+    ) -> Result<Self> {
+        Ok(ResidentNode {
+            artifacts: RwLock::new(
+                ResidentArtifacts::new(cdi_attest, cdi_seal, &bcc)
+                    .context("In ResidentNode::new: Trying to initialize ResidentArtifacts")?,
+            ),
+            demotion_db: Default::default(),
+        })
+    }
+
+    fn get_effective_artifacts(
+        &self,
+        client: BinderInputValues,
+        input_values: &[BinderInputValues],
+    ) -> Result<ResidentArtifacts> {
+        let artifacts = self.artifacts.read().unwrap().try_clone()?;
+        let demotion_db = self.demotion_db.read().unwrap();
+
+        let client_arr = [client];
+
+        let input_values: Vec<utils::InputValues> = demotion_db
+            .get(&client_arr[0])
+            .map(|v| v.iter())
+            .unwrap_or_else(|| client_arr.iter())
+            .chain(input_values.iter())
+            .map(|v| v.into())
+            .collect();
+
+        artifacts
+            .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
+            .context("In get_effective_artifacts:")
+    }
+}
+
+impl DiceNodeImpl for ResidentNode {
+    fn sign(
+        &self,
+        client: BinderInputValues,
+        input_values: &[BinderInputValues],
+        message: &[u8],
+    ) -> Result<Signature> {
+        let (cdi_attest, _, _) = self
+            .get_effective_artifacts(client, input_values)
+            .context("In ResidentNode::sign: Failed to get effective_artifacts.")?
+            .into_tuple();
+        let mut dice = OpenDiceCborContext::new();
+        let seed = dice
+            .derive_cdi_private_key_seed(cdi_attest[..].try_into().with_context(|| {
+                format!(
+                    "In ResidentNode::sign: Failed to convert cdi_attest (length: {}).",
+                    cdi_attest.len()
+                )
+            })?)
+            .context("In ResidentNode::sign: Failed to derive seed from cdi_attest.")?;
+        let (_public_key, private_key) = dice
+            .keypair_from_seed(seed[..].try_into().with_context(|| {
+                format!("In ResidentNode::sign: Failed to convert seed (length: {}).", seed.len())
+            })?)
+            .context("In ResidentNode::sign: Failed to derive keypair from seed.")?;
+        Ok(Signature {
+            data: dice
+                .sign(
+                    message,
+                    private_key[..].try_into().with_context(|| {
+                        format!(
+                            "In ResidentNode::sign: Failed to convert private_key (length: {}).",
+                            private_key.len()
+                        )
+                    })?,
+                )
+                .context("In ResidentNode::sign: Failed to sign.")?,
+        })
+    }
+
+    fn get_attestation_chain(
+        &self,
+        client: BinderInputValues,
+        input_values: &[BinderInputValues],
+    ) -> Result<Bcc> {
+        let (_, _, bcc) = self
+            .get_effective_artifacts(client, input_values)
+            .context("In ResidentNode::get_attestation_chain: Failed to get effective_artifacts.")?
+            .into_tuple();
+
+        Ok(Bcc { data: bcc })
+    }
+
+    fn derive(
+        &self,
+        client: BinderInputValues,
+        input_values: &[BinderInputValues],
+    ) -> Result<BccHandover> {
+        let (cdi_attest, cdi_seal, bcc) =
+            self.get_effective_artifacts(client, input_values)?.into_tuple();
+
+        utils::make_bcc_handover(
+            &cdi_attest[..]
+                .try_into()
+                .context("In ResidentNode::derive: Trying to convert cdi_attest to sized array.")?,
+            &cdi_seal[..]
+                .try_into()
+                .context("In ResidentNode::derive: Trying to convert cdi_attest to sized array.")?,
+            &bcc,
+        )
+        .context("In ResidentNode::derive: Trying to format bcc handover.")
+    }
+
+    fn demote(&self, client: BinderInputValues, input_values: &[BinderInputValues]) -> Result<()> {
+        let mut demotion_db = self.demotion_db.write().unwrap();
+
+        let client_arr = [client];
+
+        // The following statement consults demotion database which yields an optional demotion
+        // path. It then constructs an iterator over the following elements, then clones and
+        // collects them into a new vector:
+        // [ demotion path | client ], input_values
+        let new_path: Vec<BinderInputValues> = demotion_db
+            .get(&client_arr[0])
+            .map(|v| v.iter())
+            .unwrap_or_else(|| client_arr.iter())
+            .chain(input_values)
+            .cloned()
+            .collect();
+
+        let [client] = client_arr;
+        demotion_db.insert(client, new_path);
+        Ok(())
+    }
+
+    fn demote_self(&self, input_values: &[BinderInputValues]) -> Result<()> {
+        let mut artifacts = self.artifacts.write().unwrap();
+
+        let input_values = input_values
+            .iter()
+            .map(|v| {
+                v.try_into().with_context(|| format!("Failed to convert input values: {:#?}", v))
+            })
+            .collect::<Result<Vec<InputValues>>>()
+            .context("In ResidentNode::demote_self:")?;
+
+        *artifacts = artifacts
+            .try_clone()
+            .context("In ResidentNode::demote_self: Failed to clone resident artifacts")?
+            .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
+            .context("In ResidentNode::demote_self:")?;
+        Ok(())
+    }
+}
diff --git a/diced/src/sample_inputs.rs b/diced/src/sample_inputs.rs
new file mode 100644
index 0000000..93897a6
--- /dev/null
+++ b/diced/src/sample_inputs.rs
@@ -0,0 +1,255 @@
+// 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 a set of sample input values for a DICE chain, a sample UDS,
+//! as well as tuple of CDIs and BCC derived thereof.
+
+use android_hardware_security_dice::aidl::android::hardware::security::dice::{
+    Config::Config as BinderConfig, InputValues::InputValues as BinderInputValues, Mode::Mode,
+};
+use anyhow::{Context, Result};
+use dice::ContextImpl;
+use diced_open_dice_cbor as dice;
+use diced_utils::cbor;
+use diced_utils::InputValues;
+use keystore2_crypto::ZVec;
+use std::convert::{TryFrom, TryInto};
+use std::io::Write;
+
+/// Sample UDS used to perform the root dice flow by `make_sample_bcc_and_cdis`.
+pub static UDS: &[u8; dice::CDI_SIZE] = &[
+    0x65, 0x4f, 0xab, 0xa9, 0xa5, 0xad, 0x0f, 0x5e, 0x15, 0xc3, 0x12, 0xf7, 0x77, 0x45, 0xfa, 0x55,
+    0x18, 0x6a, 0xa6, 0x34, 0xb6, 0x7c, 0x82, 0x7b, 0x89, 0x4c, 0xc5, 0x52, 0xd3, 0x27, 0x35, 0x8e,
+];
+
+fn encode_pub_key_ed25519(pub_key: &[u8], stream: &mut dyn Write) -> Result<()> {
+    cbor::encode_header(5 /* CBOR MAP */, 5, stream)
+        .context("In encode_pub_key_ed25519: Trying to encode map header.")?;
+    cbor::encode_number(1, stream)
+        .context("In encode_pub_key_ed25519: Trying to encode Key type tag.")?;
+    cbor::encode_number(1, stream)
+        .context("In encode_pub_key_ed25519: Trying to encode Key type.")?;
+    cbor::encode_number(3, stream)
+        .context("In encode_pub_key_ed25519: Trying to encode algorithm tag.")?;
+    // Encoding a -8 for AlgorithmEdDSA. The encoded number is -1 - <header argument>,
+    // the an argument of 7 below.
+    cbor::encode_header(1 /* CBOR NEGATIVE INT */, 7 /* -1 -7 = -8*/, stream)
+        .context("In encode_pub_key_ed25519: Trying to encode algorithm.")?;
+    cbor::encode_number(4, stream)
+        .context("In encode_pub_key_ed25519: Trying to encode ops tag.")?;
+    // Ops 2 for verify.
+    cbor::encode_number(2, stream).context("In encode_pub_key_ed25519: Trying to encode ops.")?;
+    cbor::encode_header(1 /* CBOR NEGATIVE INT */, 0 /* -1 -0 = -1*/, stream)
+        .context("In encode_pub_key_ed25519: Trying to encode curve tag.")?;
+    // Curve 6 for Ed25519
+    cbor::encode_number(6, stream).context("In encode_pub_key_ed25519: Trying to encode curve.")?;
+    cbor::encode_header(1 /* CBOR NEGATIVE INT */, 1 /* -1 -1 = -2*/, stream)
+        .context("In encode_pub_key_ed25519: Trying to encode X coordinate tag.")?;
+    cbor::encode_bstr(pub_key, stream)
+        .context("In encode_pub_key_ed25519: Trying to encode X coordinate.")?;
+    Ok(())
+}
+
+/// Derives a tuple of (CDI_ATTEST, CDI_SEAL, BCC) derived of the vector of input values returned
+/// by `get_input_values_vector`.
+pub fn make_sample_bcc_and_cdis() -> Result<(ZVec, ZVec, Vec<u8>)> {
+    let mut dice_ctx = dice::OpenDiceCborContext::new();
+    let private_key_seed = dice_ctx
+        .derive_cdi_private_key_seed(UDS)
+        .context("In make_sample_bcc_and_cdis: Trying to derive private key seed.")?;
+
+    let (public_key, _) =
+        dice_ctx
+            .keypair_from_seed(&private_key_seed[..].try_into().context(
+                "In make_sample_bcc_and_cids: Failed to convert seed to array reference.",
+            )?)
+            .context("In make_sample_bcc_and_cids: Failed to generate key pair.")?;
+
+    let input_values_vector = get_input_values_vector();
+
+    let (cdi_attest, cdi_seal, mut cert) = dice_ctx
+        .main_flow(
+            UDS,
+            UDS,
+            &InputValues::try_from(&input_values_vector[0])
+                .context("In make_sample_bcc_and_cdis: Trying to convert input values. (0)")?,
+        )
+        .context("In make_sample_bcc_and_cdis: Trying to run first main flow.")?;
+
+    let mut bcc: Vec<u8> = vec![];
+
+    cbor::encode_header(4 /* CBOR ARRAY */, 2, &mut bcc)
+        .context("In make_sample_bcc_and_cdis: Trying to encode array header.")?;
+    encode_pub_key_ed25519(&public_key, &mut bcc)
+        .context("In make_sample_bcc_and_cdis: Trying encode pub_key.")?;
+
+    bcc.append(&mut cert);
+
+    let (cdi_attest, cdi_seal, bcc) = dice_ctx
+        .bcc_main_flow(
+            &cdi_attest[..].try_into().context(
+                "In make_sample_bcc_and_cdis: Failed to convert cdi_attest to array reference. (1)",
+            )?,
+            &cdi_seal[..].try_into().context(
+                "In make_sample_bcc_and_cdis: Failed to convert cdi_seal to array reference. (1)",
+            )?,
+            &bcc,
+            &InputValues::try_from(&input_values_vector[1])
+                .context("In make_sample_bcc_and_cdis: Trying to convert input values. (1)")?,
+        )
+        .context("In make_sample_bcc_and_cdis: Trying to run first bcc main flow.")?;
+    dice_ctx
+        .bcc_main_flow(
+            &cdi_attest[..].try_into().context(
+                "In make_sample_bcc_and_cdis: Failed to convert cdi_attest to array reference. (2)",
+            )?,
+            &cdi_seal[..].try_into().context(
+                "In make_sample_bcc_and_cdis: Failed to convert cdi_seal to array reference. (2)",
+            )?,
+            &bcc,
+            &InputValues::try_from(&input_values_vector[2])
+                .context("In make_sample_bcc_and_cdis: Trying to convert input values. (2)")?,
+        )
+        .context("In make_sample_bcc_and_cdis: Trying to run second bcc main flow.")
+}
+
+fn make_input_values(
+    code_hash: &[u8; dice::HASH_SIZE],
+    authority_hash: &[u8; dice::HASH_SIZE],
+    config_name: &str,
+    config_version: u64,
+    config_resettable: bool,
+    mode: Mode,
+    hidden: &[u8; dice::HIDDEN_SIZE],
+) -> Result<BinderInputValues> {
+    Ok(BinderInputValues {
+        codeHash: *code_hash,
+        config: BinderConfig {
+            desc: dice::bcc::format_config_descriptor(
+                Some(config_name),
+                Some(config_version),
+                config_resettable,
+            )
+            .context("In make_input_values: Failed to format config descriptor.")?,
+        },
+        authorityHash: *authority_hash,
+        authorityDescriptor: None,
+        hidden: *hidden,
+        mode,
+    })
+}
+
+/// Returns a set of sample input for a dice chain comprising the android boot loader ABL,
+/// the verified boot information AVB, and Android S.
+pub fn get_input_values_vector() -> Vec<BinderInputValues> {
+    vec![
+        make_input_values(
+            &[
+                // code hash
+                0x16, 0x48, 0xf2, 0x55, 0x53, 0x23, 0xdd, 0x15, 0x2e, 0x83, 0x38, 0xc3, 0x64, 0x38,
+                0x63, 0x26, 0x0f, 0xcf, 0x5b, 0xd1, 0x3a, 0xd3, 0x40, 0x3e, 0x23, 0xf8, 0x34, 0x4c,
+                0x6d, 0xa2, 0xbe, 0x25, 0x1c, 0xb0, 0x29, 0xe8, 0xc3, 0xfb, 0xb8, 0x80, 0xdc, 0xb1,
+                0xd2, 0xb3, 0x91, 0x4d, 0xd3, 0xfb, 0x01, 0x0f, 0xe4, 0xe9, 0x46, 0xa2, 0xc0, 0x26,
+                0x57, 0x5a, 0xba, 0x30, 0xf7, 0x15, 0x98, 0x14,
+            ],
+            &[
+                // authority hash
+                0xf9, 0x00, 0x9d, 0xc2, 0x59, 0x09, 0xe0, 0xb6, 0x98, 0xbd, 0xe3, 0x97, 0x4a, 0xcb,
+                0x3c, 0xe7, 0x6b, 0x24, 0xc3, 0xe4, 0x98, 0xdd, 0xa9, 0x6a, 0x41, 0x59, 0x15, 0xb1,
+                0x23, 0xe6, 0xc8, 0xdf, 0xfb, 0x52, 0xb4, 0x52, 0xc1, 0xb9, 0x61, 0xdd, 0xbc, 0x5b,
+                0x37, 0x0e, 0x12, 0x12, 0xb2, 0xfd, 0xc1, 0x09, 0xb0, 0xcf, 0x33, 0x81, 0x4c, 0xc6,
+                0x29, 0x1b, 0x99, 0xea, 0xae, 0xfd, 0xaa, 0x0d,
+            ],
+            "ABL", // config name
+            1,     // config version
+            true,  // resettable
+            Mode::NORMAL,
+            &[
+                // hidden
+                0xa2, 0x01, 0xd0, 0xc0, 0xaa, 0x75, 0x3c, 0x06, 0x43, 0x98, 0x6c, 0xc3, 0x5a, 0xb5,
+                0x5f, 0x1f, 0x0f, 0x92, 0x44, 0x3b, 0x0e, 0xd4, 0x29, 0x75, 0xe3, 0xdb, 0x36, 0xda,
+                0xc8, 0x07, 0x97, 0x4d, 0xff, 0xbc, 0x6a, 0xa4, 0x8a, 0xef, 0xc4, 0x7f, 0xf8, 0x61,
+                0x7d, 0x51, 0x4d, 0x2f, 0xdf, 0x7e, 0x8c, 0x3d, 0xa3, 0xfc, 0x63, 0xd4, 0xd4, 0x74,
+                0x8a, 0xc4, 0x14, 0x45, 0x83, 0x6b, 0x12, 0x7e,
+            ],
+        )
+        .unwrap(),
+        make_input_values(
+            &[
+                // code hash
+                0xa4, 0x0c, 0xcb, 0xc1, 0xbf, 0xfa, 0xcc, 0xfd, 0xeb, 0xf4, 0xfc, 0x43, 0x83, 0x7f,
+                0x46, 0x8d, 0xd8, 0xd8, 0x14, 0xc1, 0x96, 0x14, 0x1f, 0x6e, 0xb3, 0xa0, 0xd9, 0x56,
+                0xb3, 0xbf, 0x2f, 0xfa, 0x88, 0x70, 0x11, 0x07, 0x39, 0xa4, 0xd2, 0xa9, 0x6b, 0x18,
+                0x28, 0xe8, 0x29, 0x20, 0x49, 0x0f, 0xbb, 0x8d, 0x08, 0x8c, 0xc6, 0x54, 0xe9, 0x71,
+                0xd2, 0x7e, 0xa4, 0xfe, 0x58, 0x7f, 0xd3, 0xc7,
+            ],
+            &[
+                // authority hash
+                0xb2, 0x69, 0x05, 0x48, 0x56, 0xb5, 0xfa, 0x55, 0x6f, 0xac, 0x56, 0xd9, 0x02, 0x35,
+                0x2b, 0xaa, 0x4c, 0xba, 0x28, 0xdd, 0x82, 0x3a, 0x86, 0xf5, 0xd4, 0xc2, 0xf1, 0xf9,
+                0x35, 0x7d, 0xe4, 0x43, 0x13, 0xbf, 0xfe, 0xd3, 0x36, 0xd8, 0x1c, 0x12, 0x78, 0x5c,
+                0x9c, 0x3e, 0xf6, 0x66, 0xef, 0xab, 0x3d, 0x0f, 0x89, 0xa4, 0x6f, 0xc9, 0x72, 0xee,
+                0x73, 0x43, 0x02, 0x8a, 0xef, 0xbc, 0x05, 0x98,
+            ],
+            "AVB", // config name
+            1,     // config version
+            true,  // resettable
+            Mode::NORMAL,
+            &[
+                // hidden
+                0x5b, 0x3f, 0xc9, 0x6b, 0xe3, 0x95, 0x59, 0x40, 0x5e, 0x64, 0xe5, 0x64, 0x3f, 0xfd,
+                0x21, 0x09, 0x9d, 0xf3, 0xcd, 0xc7, 0xa4, 0x2a, 0xe2, 0x97, 0xdd, 0xe2, 0x4f, 0xb0,
+                0x7d, 0x7e, 0xf5, 0x8e, 0xd6, 0x4d, 0x84, 0x25, 0x54, 0x41, 0x3f, 0x8f, 0x78, 0x64,
+                0x1a, 0x51, 0x27, 0x9d, 0x55, 0x8a, 0xe9, 0x90, 0x35, 0xab, 0x39, 0x80, 0x4b, 0x94,
+                0x40, 0x84, 0xa2, 0xfd, 0x73, 0xeb, 0x35, 0x7a,
+            ],
+        )
+        .unwrap(),
+        make_input_values(
+            &[
+                // code hash
+                0; dice::HASH_SIZE
+            ],
+            &[
+                // authority hash
+                0x04, 0x25, 0x5d, 0x60, 0x5f, 0x5c, 0x45, 0x0d, 0xf2, 0x9a, 0x6e, 0x99, 0x30, 0x03,
+                0xb8, 0xd6, 0xe1, 0x99, 0x71, 0x1b, 0xf8, 0x44, 0xfa, 0xb5, 0x31, 0x79, 0x1c, 0x37,
+                0x68, 0x4e, 0x1d, 0xc0, 0x24, 0x74, 0x68, 0xf8, 0x80, 0x20, 0x3e, 0x44, 0xb1, 0x43,
+                0xd2, 0x9c, 0xfc, 0x12, 0x9e, 0x77, 0x0a, 0xde, 0x29, 0x24, 0xff, 0x2e, 0xfa, 0xc7,
+                0x10, 0xd5, 0x73, 0xd4, 0xc6, 0xdf, 0x62, 0x9f,
+            ],
+            "Android", // config name
+            12,        // config version
+            true,      // resettable
+            Mode::NORMAL,
+            &[
+                // hidden
+                0; dice::HIDDEN_SIZE
+            ],
+        )
+        .unwrap(),
+    ]
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    // This simple test checks if the invocation succeeds, essentially it tests
+    // if the initial bcc is accepted by `DiceContext::bcc_main_flow`.
+    #[test]
+    fn make_sample_bcc_and_cdis_test() {
+        make_sample_bcc_and_cdis().unwrap();
+    }
+}
diff --git a/diced/src/utils.rs b/diced/src/utils.rs
new file mode 100644
index 0000000..03e8969
--- /dev/null
+++ b/diced/src/utils.rs
@@ -0,0 +1,381 @@
+// 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 utility functions and types for diced and the dice HAL.
+
+use android_hardware_security_dice::aidl::android::hardware::security::dice::{
+    Bcc::Bcc, BccHandover::BccHandover, InputValues::InputValues as BinderInputValues,
+    Mode::Mode as BinderMode,
+};
+use anyhow::{Context, Result};
+use dice::ContextImpl;
+use diced_open_dice_cbor as dice;
+use keystore2_crypto::ZVec;
+use std::convert::TryInto;
+
+/// This new type wraps a reference to BinderInputValues and implements the open dice
+/// InputValues trait.
+#[derive(Debug)]
+pub struct InputValues<'a>(&'a BinderInputValues);
+
+impl<'a> From<&'a BinderInputValues> for InputValues<'a> {
+    fn from(input_values: &'a BinderInputValues) -> InputValues<'a> {
+        Self(input_values)
+    }
+}
+
+impl From<&InputValues<'_>> for BinderInputValues {
+    fn from(input_values: &InputValues) -> BinderInputValues {
+        input_values.0.clone()
+    }
+}
+impl From<InputValues<'_>> for BinderInputValues {
+    fn from(input_values: InputValues) -> BinderInputValues {
+        input_values.0.clone()
+    }
+}
+
+impl dice::InputValues for InputValues<'_> {
+    fn code_hash(&self) -> &[u8; dice::HASH_SIZE] {
+        &self.0.codeHash
+    }
+
+    fn config(&self) -> dice::Config {
+        dice::Config::Descriptor(self.0.config.desc.as_slice())
+    }
+
+    fn authority_hash(&self) -> &[u8; dice::HASH_SIZE] {
+        &self.0.authorityHash
+    }
+
+    fn authority_descriptor(&self) -> Option<&[u8]> {
+        self.0.authorityDescriptor.as_deref()
+    }
+
+    fn mode(&self) -> dice::Mode {
+        match self.0.mode {
+            BinderMode::NOT_INITIALIZED => dice::Mode::NotConfigured,
+            BinderMode::NORMAL => dice::Mode::Normal,
+            BinderMode::DEBUG => dice::Mode::Debug,
+            BinderMode::RECOVERY => dice::Mode::Recovery,
+            _ => dice::Mode::NotConfigured,
+        }
+    }
+
+    fn hidden(&self) -> &[u8; dice::HIDDEN_SIZE] {
+        // If `self` was created using try_from the length was checked and this cannot panic.
+        &self.0.hidden
+    }
+}
+
+/// Initializes an aidl defined BccHandover object with the arguments `cdi_attest`, `cdi_seal`,
+/// and `bcc`.
+pub fn make_bcc_handover(
+    cdi_attest: &[u8; dice::CDI_SIZE],
+    cdi_seal: &[u8; dice::CDI_SIZE],
+    bcc: &[u8],
+) -> Result<BccHandover> {
+    Ok(BccHandover { cdiAttest: *cdi_attest, cdiSeal: *cdi_seal, bcc: Bcc { data: bcc.to_vec() } })
+}
+
+/// ResidentArtifacts stores a set of dice artifacts comprising CDI_ATTEST, CDI_SEAL,
+/// and the BCC formatted attestation certificate chain. The sensitive secrets are
+/// stored in zeroing vectors, and it implements functionality to perform DICE
+/// derivation steps using libopen-dice-cbor.
+pub struct ResidentArtifacts {
+    cdi_attest: ZVec,
+    cdi_seal: ZVec,
+    bcc: Vec<u8>,
+}
+
+impl ResidentArtifacts {
+    /// Create a ResidentArtifacts object. The parameters ensure that the stored secrets
+    /// can only have the appropriate size, so that subsequent casts to array references
+    /// cannot fail.
+    pub fn new(
+        cdi_attest: &[u8; dice::CDI_SIZE],
+        cdi_seal: &[u8; dice::CDI_SIZE],
+        bcc: &[u8],
+    ) -> Result<Self> {
+        Ok(ResidentArtifacts {
+            cdi_attest: cdi_attest[..]
+                .try_into()
+                .context("In ResidentArtifacts::new: Trying to convert cdi_attest to ZVec.")?,
+            cdi_seal: cdi_seal[..]
+                .try_into()
+                .context("In ResidentArtifacts::new: Trying to convert cdi_seal to ZVec.")?,
+            bcc: bcc.to_vec(),
+        })
+    }
+
+    /// Creates a ResidentArtifacts object from another one implementing the DiceArtifacts
+    /// trait. Like `new` this function can only create artifacts of appropriate size
+    /// because DiceArtifacts returns array references of appropriate size.
+    pub fn new_from<T: DiceArtifacts + ?Sized>(artifacts: &T) -> Result<Self> {
+        Ok(ResidentArtifacts {
+            cdi_attest: artifacts.cdi_attest()[..].try_into()?,
+            cdi_seal: artifacts.cdi_seal()[..].try_into()?,
+            bcc: artifacts.bcc(),
+        })
+    }
+
+    /// Attempts to clone the artifacts. This operation is fallible due to the fallible
+    /// nature of ZVec.
+    pub fn try_clone(&self) -> Result<Self> {
+        Ok(ResidentArtifacts {
+            cdi_attest: self
+                .cdi_attest
+                .try_clone()
+                .context("In ResidentArtifacts::new: Trying to clone cdi_attest.")?,
+            cdi_seal: self
+                .cdi_seal
+                .try_clone()
+                .context("In ResidentArtifacts::new: Trying to clone cdi_seal.")?,
+            bcc: self.bcc.clone(),
+        })
+    }
+
+    /// Deconstruct the Artifacts into a tuple.
+    /// (CDI_ATTEST, CDI_SEAL, BCC)
+    pub fn into_tuple(self) -> (ZVec, ZVec, Vec<u8>) {
+        let ResidentArtifacts { cdi_attest, cdi_seal, bcc } = self;
+        (cdi_attest, cdi_seal, bcc)
+    }
+
+    fn execute_step(self, input_values: &dyn dice::InputValues) -> Result<Self> {
+        let ResidentArtifacts { cdi_attest, cdi_seal, bcc } = self;
+
+        let (cdi_attest, cdi_seal, bcc) = dice::OpenDiceCborContext::new()
+            .bcc_main_flow(
+                cdi_attest[..].try_into().with_context(|| {
+                    format!("Trying to convert cdi_attest. (length: {})", cdi_attest.len())
+                })?,
+                cdi_seal[..].try_into().with_context(|| {
+                    format!("Trying to convert cdi_seal. (length: {})", cdi_seal.len())
+                })?,
+                &bcc,
+                input_values,
+            )
+            .context("In ResidentArtifacts::execute_step:")?;
+        Ok(ResidentArtifacts { cdi_attest, cdi_seal, bcc })
+    }
+
+    /// Iterate through the iterator of dice input values performing one
+    /// BCC main flow step on each element.
+    pub fn execute_steps<'a, Iter>(self, input_values: Iter) -> Result<Self>
+    where
+        Iter: IntoIterator<Item = &'a dyn dice::InputValues>,
+    {
+        input_values
+            .into_iter()
+            .try_fold(self, |acc, input_values| acc.execute_step(input_values))
+            .context("In ResidentArtifacts::execute_step:")
+    }
+}
+
+/// An object that implements this trait provides the typical DICE artifacts.
+/// CDI_ATTEST, CDI_SEAL, and a certificate chain up to the public key that
+/// can be derived from CDI_ATTEST. Implementations should check the length of
+/// the stored CDI_* secrets on creation so that any valid instance returns the
+/// correct secrets in an infallible way.
+pub trait DiceArtifacts {
+    /// Returns CDI_ATTEST.
+    fn cdi_attest(&self) -> &[u8; dice::CDI_SIZE];
+    /// Returns CDI_SEAL.
+    fn cdi_seal(&self) -> &[u8; dice::CDI_SIZE];
+    /// Returns the attestation certificate chain in BCC format.
+    fn bcc(&self) -> Vec<u8>;
+}
+
+/// Implement this trait to provide read and write access to a secure artifact
+/// storage that can be used by the ResidentHal implementation.
+pub trait UpdatableDiceArtifacts {
+    /// With artifacts provides access to the stored artifacts for the duration
+    /// of the function call by means of calling the callback.
+    fn with_artifacts<F, T>(&self, f: F) -> Result<T>
+    where
+        F: FnOnce(&dyn DiceArtifacts) -> Result<T>;
+
+    /// Consumes the object and returns a an updated version of itself.
+    fn update(self, new_artifacts: &impl DiceArtifacts) -> Result<Self>
+    where
+        Self: Sized;
+}
+
+impl DiceArtifacts for ResidentArtifacts {
+    fn cdi_attest(&self) -> &[u8; dice::CDI_SIZE] {
+        self.cdi_attest[..].try_into().unwrap()
+    }
+    fn cdi_seal(&self) -> &[u8; dice::CDI_SIZE] {
+        self.cdi_seal[..].try_into().unwrap()
+    }
+    fn bcc(&self) -> Vec<u8> {
+        self.bcc.clone()
+    }
+}
+
+/// This submodule implements a limited set of CBOR generation functionality. Essentially,
+/// a cbor header generator and some convenience functions for number and BSTR encoding.
+pub mod cbor {
+    use anyhow::{anyhow, Context, Result};
+    use std::convert::TryInto;
+    use std::io::Write;
+
+    /// CBOR encodes a positive number.
+    pub fn encode_number(n: u64, buffer: &mut dyn Write) -> Result<()> {
+        encode_header(0, n, buffer)
+    }
+
+    /// CBOR encodes a binary string.
+    pub fn encode_bstr(bstr: &[u8], buffer: &mut dyn Write) -> Result<()> {
+        encode_header(
+            2,
+            bstr.len().try_into().context("In encode_bstr: Failed to convert usize to u64.")?,
+            buffer,
+        )
+        .context("In encode_bstr: While writing header.")?;
+        let written = buffer.write(bstr).context("In encode_bstr: While writing payload.")?;
+        if written != bstr.len() {
+            return Err(anyhow!("In encode_bstr: Buffer too small. ({}, {})", written, bstr.len()));
+        }
+        Ok(())
+    }
+
+    /// Formats a CBOR header. `t` is the type, and n is the header argument.
+    pub fn encode_header(t: u8, n: u64, buffer: &mut dyn Write) -> Result<()> {
+        match n {
+            n if n < 24 => {
+                let written = buffer
+                    .write(&u8::to_be_bytes(((t as u8) << 5) | (n as u8 & 0x1F)))
+                    .with_context(|| {
+                    format!("In encode_header: Failed to write header ({}, {})", t, n)
+                })?;
+                if written != 1 {
+                    return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
+                }
+            }
+            n if n <= 0xFF => {
+                let written =
+                    buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (24u8 & 0x1F))).with_context(
+                        || format!("In encode_header: Failed to write header ({}, {})", t, n),
+                    )?;
+                if written != 1 {
+                    return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
+                }
+                let written = buffer.write(&u8::to_be_bytes(n as u8)).with_context(|| {
+                    format!("In encode_header: Failed to write size ({}, {})", t, n)
+                })?;
+                if written != 1 {
+                    return Err(anyhow!(
+                        "In encode_header while writing size: Buffer to small. ({}, {})",
+                        t,
+                        n
+                    ));
+                }
+            }
+            n if n <= 0xFFFF => {
+                let written =
+                    buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (25u8 & 0x1F))).with_context(
+                        || format!("In encode_header: Failed to write header ({}, {})", t, n),
+                    )?;
+                if written != 1 {
+                    return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
+                }
+                let written = buffer.write(&u16::to_be_bytes(n as u16)).with_context(|| {
+                    format!("In encode_header: Failed to write size ({}, {})", t, n)
+                })?;
+                if written != 2 {
+                    return Err(anyhow!(
+                        "In encode_header while writing size: Buffer to small. ({}, {})",
+                        t,
+                        n
+                    ));
+                }
+            }
+            n if n <= 0xFFFFFFFF => {
+                let written =
+                    buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (26u8 & 0x1F))).with_context(
+                        || format!("In encode_header: Failed to write header ({}, {})", t, n),
+                    )?;
+                if written != 1 {
+                    return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
+                }
+                let written = buffer.write(&u32::to_be_bytes(n as u32)).with_context(|| {
+                    format!("In encode_header: Failed to write size ({}, {})", t, n)
+                })?;
+                if written != 4 {
+                    return Err(anyhow!(
+                        "In encode_header while writing size: Buffer to small. ({}, {})",
+                        t,
+                        n
+                    ));
+                }
+            }
+            n => {
+                let written =
+                    buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (27u8 & 0x1F))).with_context(
+                        || format!("In encode_header: Failed to write header ({}, {})", t, n),
+                    )?;
+                if written != 1 {
+                    return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
+                }
+                let written = buffer.write(&u64::to_be_bytes(n as u64)).with_context(|| {
+                    format!("In encode_header: Failed to write size ({}, {})", t, n)
+                })?;
+                if written != 8 {
+                    return Err(anyhow!(
+                        "In encode_header while writing size: Buffer to small. ({}, {})",
+                        t,
+                        n
+                    ));
+                }
+            }
+        }
+        Ok(())
+    }
+
+    #[cfg(test)]
+    mod test {
+        use super::*;
+
+        fn encode_header_helper(t: u8, n: u64) -> Vec<u8> {
+            let mut b: Vec<u8> = vec![];
+            encode_header(t, n, &mut b).unwrap();
+            b
+        }
+
+        #[test]
+        fn encode_header_test() {
+            assert_eq!(&encode_header_helper(0, 0), &[0b000_00000]);
+            assert_eq!(&encode_header_helper(0, 23), &[0b000_10111]);
+            assert_eq!(&encode_header_helper(0, 24), &[0b000_11000, 24]);
+            assert_eq!(&encode_header_helper(0, 0xff), &[0b000_11000, 0xff]);
+            assert_eq!(&encode_header_helper(0, 0x100), &[0b000_11001, 0x01, 0x00]);
+            assert_eq!(&encode_header_helper(0, 0xffff), &[0b000_11001, 0xff, 0xff]);
+            assert_eq!(&encode_header_helper(0, 0x10000), &[0b000_11010, 0x00, 0x01, 0x00, 0x00]);
+            assert_eq!(
+                &encode_header_helper(0, 0xffffffff),
+                &[0b000_11010, 0xff, 0xff, 0xff, 0xff]
+            );
+            assert_eq!(
+                &encode_header_helper(0, 0x100000000),
+                &[0b000_11011, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]
+            );
+            assert_eq!(
+                &encode_header_helper(0, 0xffffffffffffffff),
+                &[0b000_11011, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
+            );
+        }
+    }
+}
diff --git a/fsverity/Android.bp b/fsverity/Android.bp
new file mode 100644
index 0000000..2fc3c01
--- /dev/null
+++ b/fsverity/Android.bp
@@ -0,0 +1,66 @@
+// Copyright (C) 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+python_library_host {
+    name: "fsverity_digests_proto_python",
+    srcs: [
+        "fsverity_digests.proto",
+    ],
+    required: [
+        "fsverity",
+    ],
+    proto: {
+        canonical_path_from_root: false,
+    },
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+}
+
+rust_protobuf {
+    name: "libfsverity_digests_proto_rust",
+    crate_name: "fsverity_digests_proto",
+    source_stem: "fsverity_digests_proto",
+    protos: [
+        "fsverity_digests.proto",
+    ],
+    apex_available: [
+        "com.android.compos",
+    ],
+}
+
+cc_library_static {
+    name: "libfsverity_digests_proto_cc",
+    proto: {
+        type: "lite",
+        static: true,
+        canonical_path_from_root: false,
+        export_proto_headers: true,
+    },
+    srcs: ["fsverity_digests.proto"],
+}
diff --git a/fsverity/AndroidManifest.xml b/fsverity/AndroidManifest.xml
new file mode 100644
index 0000000..434955c
--- /dev/null
+++ b/fsverity/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.security.fsverity_metadata" />
diff --git a/fsverity/OWNERS b/fsverity/OWNERS
new file mode 100644
index 0000000..f9e7b25
--- /dev/null
+++ b/fsverity/OWNERS
@@ -0,0 +1,5 @@
+alanstokes@google.com
+ebiggers@google.com
+jeffv@google.com
+jiyong@google.com
+victorhsieh@google.com
diff --git a/keystore2/system_property/system_property_bindgen.hpp b/fsverity/fsverity_digests.proto
similarity index 75%
rename from keystore2/system_property/system_property_bindgen.hpp
rename to fsverity/fsverity_digests.proto
index e3c1ade..816ae61 100644
--- a/keystore2/system_property/system_property_bindgen.hpp
+++ b/fsverity/fsverity_digests.proto
@@ -13,6 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#pragma once
 
-#include "sys/system_properties.h"
+syntax = "proto3";
+
+package android.security.fsverity;
+
+message FSVerityDigests {
+  message Digest {
+    bytes digest = 1;
+    string hash_alg = 2;
+  }
+  map<string, Digest> digests = 1;
+}
diff --git a/fsverity_init/Android.bp b/fsverity_init/Android.bp
index 39d4e6b..83c5945 100644
--- a/fsverity_init/Android.bp
+++ b/fsverity_init/Android.bp
@@ -10,17 +10,34 @@
 cc_binary {
     name: "fsverity_init",
     srcs: [
-        "fsverity_init.cpp",
+        "main.cpp",
     ],
     static_libs: [
         "libc++fs",
+        "libfsverity_init",
+        "libmini_keyctl_static",
+    ],
+    shared_libs: [
+        "libbase",
+        "libkeyutils",
+        "liblog",
+    ],
+    cflags: ["-Werror", "-Wall", "-Wextra"],
+}
+
+cc_library {
+    name: "libfsverity_init",
+    srcs: ["fsverity_init.cpp"],
+    static_libs: [
+        "libc++fs",
         "libmini_keyctl_static",
     ],
     shared_libs: [
         "libbase",
         "libkeyutils",
         "liblog",
-        "liblogwrap",
     ],
     cflags: ["-Werror", "-Wall", "-Wextra"],
+    export_include_dirs: ["include"],
+    recovery_available: true,
 }
diff --git a/fsverity_init/OWNERS b/fsverity_init/OWNERS
new file mode 100644
index 0000000..f9e7b25
--- /dev/null
+++ b/fsverity_init/OWNERS
@@ -0,0 +1,5 @@
+alanstokes@google.com
+ebiggers@google.com
+jeffv@google.com
+jiyong@google.com
+victorhsieh@google.com
diff --git a/fsverity_init/fsverity_init.cpp b/fsverity_init/fsverity_init.cpp
index 7ab4097..61f84dd 100644
--- a/fsverity_init/fsverity_init.cpp
+++ b/fsverity_init/fsverity_init.cpp
@@ -37,15 +37,17 @@
     return true;
 }
 
-void LoadKeyFromStdin(key_serial_t keyring_id, const char* keyname) {
+bool LoadKeyFromStdin(key_serial_t keyring_id, const char* keyname) {
     std::string content;
     if (!android::base::ReadFdToString(STDIN_FILENO, &content)) {
         LOG(ERROR) << "Failed to read key from stdin";
-        return;
+        return false;
     }
     if (!LoadKeyToKeyring(keyring_id, keyname, content.c_str(), content.size())) {
         LOG(ERROR) << "Failed to load key from stdin";
+        return false;
     }
+    return true;
 }
 
 void LoadKeyFromFile(key_serial_t keyring_id, const char* keyname, const std::string& path) {
@@ -79,45 +81,3 @@
     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) {
-    if (argc < 2) {
-        LOG(ERROR) << "Not enough arguments";
-        return -1;
-    }
-
-    key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
-    if (keyring_id < 0) {
-        LOG(ERROR) << "Failed to find .fs-verity keyring id";
-        return -1;
-    }
-
-    const std::string_view command = argv[1];
-
-    if (command == "--load-verified-keys") {
-        LoadKeyFromVerifiedPartitions(keyring_id);
-    } else if (command == "--load-extra-key") {
-        if (argc != 3) {
-            LOG(ERROR) << "--load-extra-key requires <key_name> argument.";
-            return -1;
-        }
-        LoadKeyFromStdin(keyring_id, argv[2]);
-    } else if (command == "--lock") {
-        // Requires files backed by fs-verity to be verified with a key in .fs-verity
-        // keyring.
-        if (!android::base::WriteStringToFile("1", "/proc/sys/fs/verity/require_signatures")) {
-            PLOG(ERROR) << "Failed to enforce fs-verity signature";
-        }
-
-        if (!android::base::GetBoolProperty("ro.debuggable", false)) {
-            if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
-                PLOG(ERROR) << "Cannot restrict .fs-verity keyring";
-            }
-        }
-    } else {
-        LOG(ERROR) << "Unknown argument(s).";
-        return -1;
-    }
-
-    return 0;
-}
diff --git a/keystore2/system_property/system_property_bindgen.hpp b/fsverity_init/include/fsverity_init.h
similarity index 70%
copy from keystore2/system_property/system_property_bindgen.hpp
copy to fsverity_init/include/fsverity_init.h
index e3c1ade..c3bc93b 100644
--- a/keystore2/system_property/system_property_bindgen.hpp
+++ b/fsverity_init/include/fsverity_init.h
@@ -13,6 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#pragma once
 
-#include "sys/system_properties.h"
+#include <mini_keyctl_utils.h>
+
+bool LoadKeyFromStdin(key_serial_t keyring_id, const char* keyname);
+void LoadKeyFromFile(key_serial_t keyring_id, const char* keyname, const std::string& path);
+void LoadKeyFromVerifiedPartitions(key_serial_t keyring_id);
diff --git a/fsverity_init/main.cpp b/fsverity_init/main.cpp
new file mode 100644
index 0000000..3f75dca
--- /dev/null
+++ b/fsverity_init/main.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <fsverity_init.h>
+#include <log/log.h>
+#include <mini_keyctl_utils.h>
+
+int main(int argc, const char** argv) {
+    if (argc < 2) {
+        LOG(ERROR) << "Not enough arguments";
+        return -1;
+    }
+
+    key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
+    if (keyring_id < 0) {
+        LOG(ERROR) << "Failed to find .fs-verity keyring id";
+        return -1;
+    }
+
+    const std::string_view command = argv[1];
+
+    if (command == "--load-verified-keys") {
+        LoadKeyFromVerifiedPartitions(keyring_id);
+    } else if (command == "--load-extra-key") {
+        if (argc != 3) {
+            LOG(ERROR) << "--load-extra-key requires <key_name> argument.";
+            return -1;
+        }
+        if (!LoadKeyFromStdin(keyring_id, argv[2])) {
+            return -1;
+        }
+    } else if (command == "--lock") {
+        // Requires files backed by fs-verity to be verified with a key in .fs-verity
+        // keyring.
+        if (!android::base::WriteStringToFile("1", "/proc/sys/fs/verity/require_signatures")) {
+            PLOG(ERROR) << "Failed to enforce fs-verity signature";
+        }
+
+        if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+            if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
+                PLOG(ERROR) << "Cannot restrict .fs-verity keyring";
+            }
+        }
+    } else {
+        LOG(ERROR) << "Unknown argument(s).";
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/identity/Android.bp b/identity/Android.bp
index 8267a6b..c69ead1 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -24,7 +24,11 @@
 
 cc_binary {
     name: "credstore",
-    defaults: ["identity_defaults"],
+    defaults: [
+        "identity_defaults",
+        "keymint_use_latest_hal_aidl_ndk_shared",
+        "keymint_use_latest_hal_aidl_cpp_static",
+    ],
 
     srcs: [
         "main.cpp",
@@ -33,6 +37,7 @@
         "WritableCredential.cpp",
         "Credential.cpp",
         "CredentialData.cpp",
+        "Session.cpp",
         "Util.cpp",
     ],
     init_rc: ["credstore.rc"],
@@ -48,14 +53,15 @@
         "android.hardware.identity-support-lib",
         "libkeymaster4support",
         "libkeystore-attestation-application-id",
-        "android.hardware.security.keymint-V1-ndk_platform",
-        "android.security.authorization-ndk_platform",
+        "android.security.authorization-ndk",
+        "android.security.remoteprovisioning-cpp",
+        "libutilscallstack",
     ],
     static_libs: [
-        "android.hardware.identity-V3-cpp",
-        "android.hardware.keymaster-V3-cpp",
+        "android.hardware.identity-V4-cpp",
+        "android.hardware.keymaster-V4-cpp",
         "libcppbor_external",
-    ]
+    ],
 }
 
 filegroup {
@@ -75,6 +81,7 @@
         "binder/android/security/identity/AuthKeyParcel.aidl",
         "binder/android/security/identity/SecurityHardwareInfoParcel.aidl",
         "binder/android/security/identity/ICredentialStoreFactory.aidl",
+        "binder/android/security/identity/ISession.aidl",
     ],
     path: "binder",
 }
diff --git a/identity/Credential.cpp b/identity/Credential.cpp
index 7c75d8a..c67fe4a 100644
--- a/identity/Credential.cpp
+++ b/identity/Credential.cpp
@@ -70,10 +70,10 @@
 Credential::Credential(CipherSuite cipherSuite, const std::string& dataPath,
                        const std::string& credentialName, uid_t callingUid,
                        HardwareInformation hwInfo, sp<IIdentityCredentialStore> halStoreBinder,
-                       int halApiVersion)
+                       sp<IPresentationSession> halSessionBinder, int halApiVersion)
     : cipherSuite_(cipherSuite), dataPath_(dataPath), credentialName_(credentialName),
       callingUid_(callingUid), hwInfo_(std::move(hwInfo)), halStoreBinder_(halStoreBinder),
-      halApiVersion_(halApiVersion) {}
+      halSessionBinder_(halSessionBinder), halApiVersion_(halApiVersion) {}
 
 Credential::~Credential() {}
 
@@ -85,25 +85,40 @@
                                                 "Error loading data for credential");
     }
 
-    sp<IIdentityCredential> halBinder;
-    Status status =
-        halStoreBinder_->getCredential(cipherSuite_, data->getCredentialData(), &halBinder);
-    if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
-        int code = status.serviceSpecificErrorCode();
-        if (code == IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED) {
-            return halStatusToError(status, ICredentialStore::ERROR_CIPHER_SUITE_NOT_SUPPORTED);
+    // If we're in a session we explicitly don't get the binder to IIdentityCredential until
+    // it's used in getEntries() which is the only method call allowed for sessions.
+    //
+    // Why? This is because we want to throw the IIdentityCredential object away as soon as it's
+    // used because the HAL only guarantees a single IIdentityCredential object alive at a time
+    // and in a session there may be multiple credentials in play and we want to do multiple
+    // getEntries() calls on all of them.
+    //
+
+    if (!halSessionBinder_) {
+        sp<IIdentityCredential> halBinder;
+        Status status =
+            halStoreBinder_->getCredential(cipherSuite_, data->getCredentialData(), &halBinder);
+        if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
+            int code = status.serviceSpecificErrorCode();
+            if (code == IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED) {
+                return halStatusToError(status, ICredentialStore::ERROR_CIPHER_SUITE_NOT_SUPPORTED);
+            }
         }
+        if (!status.isOk()) {
+            LOG(ERROR) << "Error getting HAL binder";
+            return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC);
+        }
+        halBinder_ = halBinder;
     }
-    if (!status.isOk()) {
-        LOG(ERROR) << "Error getting HAL binder";
-        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC);
-    }
-    halBinder_ = halBinder;
 
     return Status::ok();
 }
 
 Status Credential::getCredentialKeyCertificateChain(std::vector<uint8_t>* _aidl_return) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
@@ -116,7 +131,11 @@
 
 // Returns operation handle
 Status Credential::selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
-                                 int64_t* _aidl_return) {
+                                 bool incrementUsageCount, int64_t* _aidl_return) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
@@ -127,7 +146,7 @@
     // We just check if a key is available, we actually don't store it since we
     // don't keep CredentialData around between binder calls.
     const AuthKeyData* authKey =
-        data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
+        data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys, incrementUsageCount);
     if (authKey == nullptr) {
         return Status::fromServiceSpecificError(
             ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
@@ -148,10 +167,19 @@
     }
 
     int64_t challenge;
-    Status status = halBinder_->createAuthChallenge(&challenge);
-    if (!status.isOk()) {
-        LOG(ERROR) << "Error getting challenge: " << status.exceptionMessage();
-        return false;
+    // If we're in a session, the challenge is selected by the session
+    if (halSessionBinder_) {
+        Status status = halSessionBinder_->getAuthChallenge(&challenge);
+        if (!status.isOk()) {
+            LOG(ERROR) << "Error getting challenge from session: " << status.exceptionMessage();
+            return false;
+        }
+    } else {
+        Status status = halBinder_->createAuthChallenge(&challenge);
+        if (!status.isOk()) {
+            LOG(ERROR) << "Error getting challenge: " << status.exceptionMessage();
+            return false;
+        }
     }
     if (challenge == 0) {
         LOG(ERROR) << "Returned challenge is 0 (bug in HAL or TA)";
@@ -218,7 +246,8 @@
                               const vector<RequestNamespaceParcel>& requestNamespaces,
                               const vector<uint8_t>& sessionTranscript,
                               const vector<uint8_t>& readerSignature, bool allowUsingExhaustedKeys,
-                              bool allowUsingExpiredKeys, GetEntriesResultParcel* _aidl_return) {
+                              bool allowUsingExpiredKeys, bool incrementUsageCount,
+                              GetEntriesResultParcel* _aidl_return) {
     GetEntriesResultParcel ret;
 
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
@@ -228,6 +257,28 @@
                                                 "Error loading data for credential");
     }
 
+    // If used in a session, get the binder on demand...
+    //
+    sp<IIdentityCredential> halBinder = halBinder_;
+    if (halSessionBinder_) {
+        if (halBinder) {
+            LOG(ERROR) << "Unexpected HAL binder for session";
+            return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                    "Unexpected HAL binder for session");
+        }
+        Status status = halSessionBinder_->getCredential(data->getCredentialData(), &halBinder);
+        if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
+            int code = status.serviceSpecificErrorCode();
+            if (code == IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED) {
+                return halStatusToError(status, ICredentialStore::ERROR_CIPHER_SUITE_NOT_SUPPORTED);
+            }
+        }
+        if (!status.isOk()) {
+            LOG(ERROR) << "Error getting HAL binder";
+            return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC);
+        }
+    }
+
     // Calculate requestCounts ahead of time and be careful not to include
     // elements that don't exist.
     //
@@ -354,33 +405,40 @@
         }
     }
 
-    // Note that the selectAuthKey() method is only called if a CryptoObject is involved at
-    // the Java layer. So we could end up with no previously selected auth key and we may
-    // need one.
+    // Reuse the same AuthKey over multiple getEntries() calls.
     //
-    const AuthKeyData* authKey =
-        data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
-    if (authKey == nullptr) {
-        // If no authKey is available, consider it an error only when a
-        // SessionTranscript was provided.
+    bool updateUseCountOnDisk = false;
+    if (!selectedAuthKey_) {
+        // Note that the selectAuthKey() method is only called if a CryptoObject is involved at
+        // the Java layer. So we could end up with no previously selected auth key and we may
+        // need one.
         //
-        // We allow no SessionTranscript to be provided because it makes
-        // the API simpler to deal with insofar it can be used without having
-        // to generate any authentication keys.
-        //
-        // In this "no SessionTranscript is provided" mode we don't return
-        // DeviceNameSpaces nor a MAC over DeviceAuthentication so we don't
-        // need a device key.
-        //
-        if (sessionTranscript.size() > 0) {
-            return Status::fromServiceSpecificError(
-                ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
-                "No suitable authentication key available and one is needed");
+        const AuthKeyData* authKey = data->selectAuthKey(
+            allowUsingExhaustedKeys, allowUsingExpiredKeys, incrementUsageCount);
+        if (authKey == nullptr) {
+            // If no authKey is available, consider it an error only when a
+            // SessionTranscript was provided.
+            //
+            // We allow no SessionTranscript to be provided because it makes
+            // the API simpler to deal with insofar it can be used without having
+            // to generate any authentication keys.
+            //
+            // In this "no SessionTranscript is provided" mode we don't return
+            // DeviceNameSpaces nor a MAC over DeviceAuthentication so we don't
+            // need a device key.
+            //
+            if (sessionTranscript.size() > 0) {
+                return Status::fromServiceSpecificError(
+                    ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
+                    "No suitable authentication key available and one is needed");
+            }
+        } else {
+            // We did find an authKey. Store its contents for future getEntries() calls.
+            updateUseCountOnDisk = true;
+            selectedAuthKeySigningKeyBlob_ = authKey->keyBlob;
+            selectedAuthKeyStaticAuthData_ = authKey->staticAuthenticationData;
         }
-    }
-    vector<uint8_t> signingKeyBlob;
-    if (authKey != nullptr) {
-        signingKeyBlob = authKey->keyBlob;
+        selectedAuthKey_ = true;
     }
 
     // Pass the HAL enough information to allow calculating the size of
@@ -405,22 +463,22 @@
     }
     // This is not catastrophic, we might be dealing with a version 1 implementation which
     // doesn't have this method.
-    Status status = halBinder_->setRequestedNamespaces(halRequestNamespaces);
+    Status status = halBinder->setRequestedNamespaces(halRequestNamespaces);
     if (!status.isOk()) {
         LOG(INFO) << "Failed setting expected requested namespaces, assuming V1 HAL "
                   << "and continuing";
     }
 
     // Pass the verification token. Failure is OK, this method isn't in the V1 HAL.
-    status = halBinder_->setVerificationToken(aidlVerificationToken);
+    status = halBinder->setVerificationToken(aidlVerificationToken);
     if (!status.isOk()) {
         LOG(INFO) << "Failed setting verification token, assuming V1 HAL "
                   << "and continuing";
     }
 
-    status =
-        halBinder_->startRetrieval(selectedProfiles, aidlAuthToken, requestMessage, signingKeyBlob,
-                                   sessionTranscript, readerSignature, requestCounts);
+    status = halBinder->startRetrieval(selectedProfiles, aidlAuthToken, requestMessage,
+                                       selectedAuthKeySigningKeyBlob_, sessionTranscript,
+                                       readerSignature, requestCounts);
     if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
         int code = status.serviceSpecificErrorCode();
         if (code == IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND) {
@@ -453,8 +511,8 @@
             }
 
             status =
-                halBinder_->startRetrieveEntryValue(rns.namespaceName, rep.name, eData.value().size,
-                                                    eData.value().accessControlProfileIds);
+                halBinder->startRetrieveEntryValue(rns.namespaceName, rep.name, eData.value().size,
+                                                   eData.value().accessControlProfileIds);
             if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
                 int code = status.serviceSpecificErrorCode();
                 if (code == IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED) {
@@ -482,7 +540,7 @@
             vector<uint8_t> value;
             for (const auto& encryptedChunk : eData.value().encryptedChunks) {
                 vector<uint8_t> chunk;
-                status = halBinder_->retrieveEntryValue(encryptedChunk, &chunk);
+                status = halBinder->retrieveEntryValue(encryptedChunk, &chunk);
                 if (!status.isOk()) {
                     return halStatusToGenericError(status);
                 }
@@ -496,16 +554,14 @@
         ret.resultNamespaces.push_back(resultNamespaceParcel);
     }
 
-    status = halBinder_->finishRetrieval(&ret.mac, &ret.deviceNameSpaces);
+    status = halBinder->finishRetrieval(&ret.mac, &ret.deviceNameSpaces);
     if (!status.isOk()) {
         return halStatusToGenericError(status);
     }
-    if (authKey != nullptr) {
-        ret.staticAuthenticationData = authKey->staticAuthenticationData;
-    }
+    ret.staticAuthenticationData = selectedAuthKeyStaticAuthData_;
 
     // Ensure useCount is updated on disk.
-    if (authKey != nullptr) {
+    if (updateUseCountOnDisk) {
         if (!data->saveToDisk()) {
             return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                     "Error saving data");
@@ -517,6 +573,11 @@
 }
 
 Status Credential::deleteCredential(vector<uint8_t>* _aidl_return) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     vector<uint8_t> proofOfDeletionSignature;
 
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
@@ -544,6 +605,12 @@
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
                                                 "Not implemented by HAL");
     }
+
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     vector<uint8_t> proofOfDeletionSignature;
 
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
@@ -570,6 +637,12 @@
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
                                                 "Not implemented by HAL");
     }
+
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     vector<uint8_t> proofOfOwnershipSignature;
     Status status = halBinder_->proveOwnership(challenge, &proofOfOwnershipSignature);
     if (!status.isOk()) {
@@ -580,19 +653,26 @@
 }
 
 Status Credential::createEphemeralKeyPair(vector<uint8_t>* _aidl_return) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     vector<uint8_t> keyPair;
     Status status = halBinder_->createEphemeralKeyPair(&keyPair);
     if (!status.isOk()) {
         return halStatusToGenericError(status);
     }
 
+    time_t nowSeconds = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
+    time_t validityNotBefore = nowSeconds;
+    time_t validityNotAfter = nowSeconds + 24 * 60 * 60;
     optional<vector<uint8_t>> pkcs12Bytes = ecKeyPairGetPkcs12(keyPair,
                                                                "ephemeralKey",  // Alias for key
                                                                "0",  // Serial, as a decimal number
                                                                "Credstore",      // Issuer
                                                                "Ephemeral Key",  // Subject
-                                                               0,  // Validity Not Before
-                                                               24 * 60 * 60);  // Validity Not After
+                                                               validityNotBefore, validityNotAfter);
     if (!pkcs12Bytes) {
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                 "Error creating PKCS#12 structure for key pair");
@@ -602,6 +682,11 @@
 }
 
 Status Credential::setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     Status status = halBinder_->setReaderEphemeralPublicKey(publicKey);
     if (!status.isOk()) {
         return halStatusToGenericError(status);
@@ -610,6 +695,11 @@
 }
 
 Status Credential::setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
@@ -625,6 +715,11 @@
 }
 
 Status Credential::getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
@@ -653,6 +748,11 @@
 
 Status Credential::storeStaticAuthenticationData(const AuthKeyParcel& authenticationKey,
                                                  const vector<uint8_t>& staticAuthData) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
@@ -681,6 +781,12 @@
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
                                                 "Not implemented by HAL");
     }
+
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
@@ -702,6 +808,11 @@
 }
 
 Status Credential::getAuthenticationDataUsageCount(vector<int32_t>* _aidl_return) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
@@ -741,6 +852,12 @@
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
                                                 "Not implemented by HAL");
     }
+
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
diff --git a/identity/Credential.h b/identity/Credential.h
index a76f3cc..0906fea 100644
--- a/identity/Credential.h
+++ b/identity/Credential.h
@@ -39,6 +39,7 @@
 using ::android::hardware::identity::HardwareInformation;
 using ::android::hardware::identity::IIdentityCredential;
 using ::android::hardware::identity::IIdentityCredentialStore;
+using ::android::hardware::identity::IPresentationSession;
 using ::android::hardware::identity::RequestDataItem;
 using ::android::hardware::identity::RequestNamespace;
 
@@ -46,7 +47,8 @@
   public:
     Credential(CipherSuite cipherSuite, const string& dataPath, const string& credentialName,
                uid_t callingUid, HardwareInformation hwInfo,
-               sp<IIdentityCredentialStore> halStoreBinder, int halApiVersion);
+               sp<IIdentityCredentialStore> halStoreBinder,
+               sp<IPresentationSession> halSessionBinder, int halApiVersion);
     ~Credential();
 
     Status ensureOrReplaceHalBinder();
@@ -67,13 +69,14 @@
     Status getCredentialKeyCertificateChain(vector<uint8_t>* _aidl_return) override;
 
     Status selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
-                         int64_t* _aidl_return) override;
+                         bool incrementUsageCount, int64_t* _aidl_return) override;
 
     Status getEntries(const vector<uint8_t>& requestMessage,
                       const vector<RequestNamespaceParcel>& requestNamespaces,
                       const vector<uint8_t>& sessionTranscript,
                       const vector<uint8_t>& readerSignature, bool allowUsingExhaustedKeys,
-                      bool allowUsingExpiredKeys, GetEntriesResultParcel* _aidl_return) override;
+                      bool allowUsingExpiredKeys, bool incrementUsageCount,
+                      GetEntriesResultParcel* _aidl_return) override;
 
     Status setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) override;
     Status getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) override;
@@ -94,12 +97,20 @@
     uid_t callingUid_;
     HardwareInformation hwInfo_;
     sp<IIdentityCredentialStore> halStoreBinder_;
+    sp<IPresentationSession> halSessionBinder_;
 
     uint64_t selectedChallenge_ = 0;
 
     sp<IIdentityCredential> halBinder_;
     int halApiVersion_;
 
+    // This is used to cache the selected AuthKey to ensure the same AuthKey is used across
+    // multiple getEntries() calls.
+    //
+    bool selectedAuthKey_ = false;
+    vector<uint8_t> selectedAuthKeySigningKeyBlob_;
+    vector<uint8_t> selectedAuthKeyStaticAuthData_;
+
     bool ensureChallenge();
 
     ssize_t
diff --git a/identity/CredentialData.cpp b/identity/CredentialData.cpp
index 74b995d..2189f90 100644
--- a/identity/CredentialData.cpp
+++ b/identity/CredentialData.cpp
@@ -538,7 +538,8 @@
 }
 
 const AuthKeyData* CredentialData::selectAuthKey(bool allowUsingExhaustedKeys,
-                                                 bool allowUsingExpiredKeys) {
+                                                 bool allowUsingExpiredKeys,
+                                                 bool incrementUsageCount) {
     AuthKeyData* candidate;
 
     // First try to find a un-expired key..
@@ -556,7 +557,9 @@
         }
     }
 
-    candidate->useCount += 1;
+    if (incrementUsageCount) {
+        candidate->useCount += 1;
+    }
     return candidate;
 }
 
diff --git a/identity/CredentialData.h b/identity/CredentialData.h
index 24b55d3..e240e47 100644
--- a/identity/CredentialData.h
+++ b/identity/CredentialData.h
@@ -111,7 +111,8 @@
 
     // Returns |nullptr| if a suitable key cannot be found. Otherwise returns
     // the authentication and increases its use-count.
-    const AuthKeyData* selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys);
+    const AuthKeyData* selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
+                                     bool incrementUsageCount);
 
     optional<vector<vector<uint8_t>>>
     getAuthKeysNeedingCertification(const sp<IIdentityCredential>& halBinder);
diff --git a/identity/CredentialStore.cpp b/identity/CredentialStore.cpp
index 071cf24..c5c429b 100644
--- a/identity/CredentialStore.cpp
+++ b/identity/CredentialStore.cpp
@@ -17,20 +17,66 @@
 #define LOG_TAG "credstore"
 
 #include <algorithm>
+#include <optional>
 
 #include <android-base/logging.h>
-
+#include <android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <android/hardware/security/keymint/RpcHardwareInfo.h>
+#include <android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.h>
+#include <android/security/remoteprovisioning/RemotelyProvisionedKey.h>
 #include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
 
 #include "Credential.h"
 #include "CredentialData.h"
 #include "CredentialStore.h"
+#include "Session.h"
 #include "Util.h"
 #include "WritableCredential.h"
 
 namespace android {
 namespace security {
 namespace identity {
+namespace {
+
+using ::android::hardware::security::keymint::IRemotelyProvisionedComponent;
+using ::android::hardware::security::keymint::RpcHardwareInfo;
+using ::android::security::remoteprovisioning::IRemotelyProvisionedKeyPool;
+using ::android::security::remoteprovisioning::RemotelyProvisionedKey;
+
+std::optional<std::string>
+getRemotelyProvisionedComponentId(const sp<IIdentityCredentialStore>& hal) {
+    auto init = [](const sp<IIdentityCredentialStore>& hal) -> std::optional<std::string> {
+        sp<IRemotelyProvisionedComponent> remotelyProvisionedComponent;
+        Status status = hal->getRemotelyProvisionedComponent(&remotelyProvisionedComponent);
+        if (!status.isOk()) {
+            LOG(ERROR) << "Error getting remotely provisioned component: " << status;
+            return std::nullopt;
+        }
+
+        RpcHardwareInfo rpcHwInfo;
+        status = remotelyProvisionedComponent->getHardwareInfo(&rpcHwInfo);
+        if (!status.isOk()) {
+            LOG(ERROR) << "Error getting remotely provisioned component hardware info: " << status;
+            return std::nullopt;
+        }
+
+        if (!rpcHwInfo.uniqueId) {
+            LOG(ERROR) << "Remotely provisioned component is missing a unique id, which is "
+                       << "required for credential key remotely provisioned attestation keys. "
+                       << "This is a bug in the vendor implementation.";
+            return std::nullopt;
+        }
+
+        // This id is required to later fetch remotely provisioned attestation keys.
+        return *rpcHwInfo.uniqueId;
+    };
+
+    static std::optional<std::string> id = init(hal);
+    return id;
+}
+
+}  // namespace
 
 CredentialStore::CredentialStore(const std::string& dataPath, sp<IIdentityCredentialStore> hal)
     : dataPath_(dataPath), hal_(hal) {}
@@ -43,6 +89,16 @@
     }
     halApiVersion_ = hal_->getInterfaceVersion();
 
+    if (hwInfo_.isRemoteKeyProvisioningSupported) {
+        keyPool_ = android::waitForService<IRemotelyProvisionedKeyPool>(
+            IRemotelyProvisionedKeyPool::descriptor);
+        if (keyPool_.get() == nullptr) {
+            LOG(ERROR) << "Error getting IRemotelyProvisionedKeyPool HAL with service name '"
+                       << IRemotelyProvisionedKeyPool::descriptor << "'";
+            return false;
+        }
+    }
+
     LOG(INFO) << "Connected to Identity Credential HAL with API version " << halApiVersion_
               << " and name '" << hwInfo_.credentialStoreName << "' authored by '"
               << hwInfo_.credentialStoreAuthorName << "' with chunk size " << hwInfo_.dataChunkSize
@@ -89,13 +145,21 @@
         return halStatusToGenericError(status);
     }
 
+    if (hwInfo_.isRemoteKeyProvisioningSupported) {
+        status = setRemotelyProvisionedAttestationKey(halWritableCredential.get());
+        if (!status.isOk()) {
+            return halStatusToGenericError(status);
+        }
+    }
+
     sp<IWritableCredential> writableCredential = new WritableCredential(
         dataPath_, credentialName, docType, false, hwInfo_, halWritableCredential);
     *_aidl_return = writableCredential;
     return Status::ok();
 }
 
-Status CredentialStore::getCredentialByName(const std::string& credentialName, int32_t cipherSuite,
+Status CredentialStore::getCredentialCommon(const std::string& credentialName, int32_t cipherSuite,
+                                            sp<IPresentationSession> halSessionBinder,
                                             sp<ICredential>* _aidl_return) {
     *_aidl_return = nullptr;
 
@@ -113,8 +177,9 @@
 
     // Note: IdentityCredentialStore.java's CipherSuite enumeration and CipherSuite from the
     // HAL is manually kept in sync. So this cast is safe.
-    sp<Credential> credential = new Credential(CipherSuite(cipherSuite), dataPath_, credentialName,
-                                               callingUid, hwInfo_, hal_, halApiVersion_);
+    sp<Credential> credential =
+        new Credential(CipherSuite(cipherSuite), dataPath_, credentialName, callingUid, hwInfo_,
+                       hal_, halSessionBinder, halApiVersion_);
 
     Status loadStatus = credential->ensureOrReplaceHalBinder();
     if (!loadStatus.isOk()) {
@@ -125,6 +190,50 @@
     return loadStatus;
 }
 
+Status CredentialStore::getCredentialByName(const std::string& credentialName, int32_t cipherSuite,
+                                            sp<ICredential>* _aidl_return) {
+    return getCredentialCommon(credentialName, cipherSuite, nullptr, _aidl_return);
+}
+
+Status CredentialStore::createPresentationSession(int32_t cipherSuite, sp<ISession>* _aidl_return) {
+    sp<IPresentationSession> halPresentationSession;
+    Status status =
+        hal_->createPresentationSession(CipherSuite(cipherSuite), &halPresentationSession);
+    if (!status.isOk()) {
+        return halStatusToGenericError(status);
+    }
+
+    *_aidl_return = new Session(cipherSuite, halPresentationSession, this);
+    return Status::ok();
+}
+
+Status CredentialStore::setRemotelyProvisionedAttestationKey(
+    IWritableIdentityCredential* halWritableCredential) {
+    std::optional<std::string> rpcId = getRemotelyProvisionedComponentId(hal_);
+    if (!rpcId) {
+        return Status::fromServiceSpecificError(ERROR_GENERIC,
+                                                "Error getting remotely provisioned component id");
+    }
+
+    uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
+    RemotelyProvisionedKey key;
+    Status status = keyPool_->getAttestationKey(callingUid, *rpcId, &key);
+    if (!status.isOk()) {
+        LOG(WARNING) << "Unable to fetch remotely provisioned attestation key, falling back "
+                     << "to the factory-provisioned attestation key.";
+        return Status::ok();
+    }
+
+    status = halWritableCredential->setRemotelyProvisionedAttestationKey(key.keyBlob,
+                                                                         key.encodedCertChain);
+    if (!status.isOk()) {
+        LOG(ERROR) << "Error setting remotely provisioned attestation key on credential";
+        return status;
+    }
+
+    return Status::ok();
+}
+
 }  // namespace identity
 }  // namespace security
 }  // namespace android
diff --git a/identity/CredentialStore.h b/identity/CredentialStore.h
index 15da4eb..df7928e 100644
--- a/identity/CredentialStore.h
+++ b/identity/CredentialStore.h
@@ -21,8 +21,8 @@
 #include <vector>
 
 #include <android/hardware/identity/IIdentityCredentialStore.h>
-
 #include <android/security/identity/BnCredentialStore.h>
+#include <android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.h>
 
 namespace android {
 namespace security {
@@ -30,12 +30,16 @@
 
 using ::android::sp;
 using ::android::binder::Status;
+using ::std::optional;
 using ::std::string;
 using ::std::unique_ptr;
 using ::std::vector;
 
 using ::android::hardware::identity::HardwareInformation;
 using ::android::hardware::identity::IIdentityCredentialStore;
+using ::android::hardware::identity::IPresentationSession;
+using ::android::hardware::identity::IWritableIdentityCredential;
+using ::android::security::remoteprovisioning::IRemotelyProvisionedKeyPool;
 
 class CredentialStore : public BnCredentialStore {
   public:
@@ -44,6 +48,12 @@
 
     bool init();
 
+    // Used by both getCredentialByName() and Session::getCredential()
+    //
+    Status getCredentialCommon(const string& credentialName, int32_t cipherSuite,
+                               sp<IPresentationSession> halSessionBinder,
+                               sp<ICredential>* _aidl_return);
+
     // ICredentialStore overrides
     Status getSecurityHardwareInfo(SecurityHardwareInfoParcel* _aidl_return) override;
 
@@ -53,12 +63,18 @@
     Status getCredentialByName(const string& credentialName, int32_t cipherSuite,
                                sp<ICredential>* _aidl_return) override;
 
+    Status createPresentationSession(int32_t cipherSuite, sp<ISession>* _aidl_return) override;
+
   private:
+    Status setRemotelyProvisionedAttestationKey(IWritableIdentityCredential* halWritableCredential);
+
     string dataPath_;
 
     sp<IIdentityCredentialStore> hal_;
     int halApiVersion_;
 
+    sp<IRemotelyProvisionedKeyPool> keyPool_;
+
     HardwareInformation hwInfo_;
 };
 
diff --git a/identity/Session.cpp b/identity/Session.cpp
new file mode 100644
index 0000000..98ba3d3
--- /dev/null
+++ b/identity/Session.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#define LOG_TAG "credstore"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <android/security/identity/ICredentialStore.h>
+#include <android/security/identity/ISession.h>
+
+#include "Session.h"
+#include "Util.h"
+
+namespace android {
+namespace security {
+namespace identity {
+
+using std::optional;
+
+using ::android::hardware::identity::IPresentationSession;
+using ::android::hardware::identity::IWritableIdentityCredential;
+
+using ::android::hardware::identity::support::ecKeyPairGetPkcs12;
+using ::android::hardware::identity::support::ecKeyPairGetPrivateKey;
+using ::android::hardware::identity::support::ecKeyPairGetPublicKey;
+using ::android::hardware::identity::support::hexdump;
+using ::android::hardware::identity::support::sha256;
+
+Status Session::getEphemeralKeyPair(vector<uint8_t>* _aidl_return) {
+    vector<uint8_t> keyPair;
+    Status status = halBinder_->getEphemeralKeyPair(&keyPair);
+    if (!status.isOk()) {
+        return halStatusToGenericError(status);
+    }
+    time_t nowSeconds = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
+    time_t validityNotBefore = nowSeconds;
+    time_t validityNotAfter = nowSeconds + 24 * 60 * 60;
+    optional<vector<uint8_t>> pkcs12Bytes = ecKeyPairGetPkcs12(keyPair,
+                                                               "ephemeralKey",  // Alias for key
+                                                               "0",  // Serial, as a decimal number
+                                                               "Credstore",      // Issuer
+                                                               "Ephemeral Key",  // Subject
+                                                               validityNotBefore, validityNotAfter);
+    if (!pkcs12Bytes) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error creating PKCS#12 structure for key pair");
+    }
+    *_aidl_return = pkcs12Bytes.value();
+    return Status::ok();
+}
+
+Status Session::setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) {
+    Status status = halBinder_->setReaderEphemeralPublicKey(publicKey);
+    if (!status.isOk()) {
+        return halStatusToGenericError(status);
+    }
+    return Status::ok();
+}
+
+Status Session::setSessionTranscript(const vector<uint8_t>& sessionTranscript) {
+    Status status = halBinder_->setSessionTranscript(sessionTranscript);
+    if (!status.isOk()) {
+        return halStatusToGenericError(status);
+    }
+    return Status::ok();
+}
+
+Status Session::getCredentialForPresentation(const string& credentialName,
+                                             sp<ICredential>* _aidl_return) {
+    return store_->getCredentialCommon(credentialName, cipherSuite_, halBinder_, _aidl_return);
+}
+
+Status Session::getAuthChallenge(int64_t* _aidl_return) {
+    *_aidl_return = 0;
+    int64_t authChallenge;
+    Status status = halBinder_->getAuthChallenge(&authChallenge);
+    if (!status.isOk()) {
+        return halStatusToGenericError(status);
+    }
+    *_aidl_return = authChallenge;
+    return Status::ok();
+}
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
diff --git a/identity/Session.h b/identity/Session.h
new file mode 100644
index 0000000..116c2fd
--- /dev/null
+++ b/identity/Session.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef SYSTEM_SECURITY_PRESENTATION_H_
+#define SYSTEM_SECURITY_PRESENTATION_H_
+
+#include <string>
+#include <vector>
+
+#include <android/security/identity/BnSession.h>
+
+#include <android/hardware/identity/IPresentationSession.h>
+
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+
+#include "CredentialStore.h"
+
+namespace android {
+namespace security {
+namespace identity {
+
+using ::android::sp;
+using ::android::binder::Status;
+using ::std::string;
+using ::std::vector;
+
+using ::android::hardware::identity::CipherSuite;
+using ::android::hardware::identity::HardwareInformation;
+using ::android::hardware::identity::IIdentityCredential;
+using ::android::hardware::identity::IIdentityCredentialStore;
+using ::android::hardware::identity::IPresentationSession;
+using ::android::hardware::identity::RequestDataItem;
+using ::android::hardware::identity::RequestNamespace;
+
+class Session : public BnSession {
+  public:
+    Session(int32_t cipherSuite, sp<IPresentationSession> halBinder, sp<CredentialStore> store)
+        : cipherSuite_(cipherSuite), halBinder_(halBinder), store_(store) {}
+
+    bool initialize();
+
+    // ISession overrides
+    Status getEphemeralKeyPair(vector<uint8_t>* _aidl_return) override;
+
+    Status setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) override;
+
+    Status setSessionTranscript(const vector<uint8_t>& sessionTranscript) override;
+
+    Status getAuthChallenge(int64_t* _aidl_return) override;
+
+    Status getCredentialForPresentation(const string& credentialName,
+                                        sp<ICredential>* _aidl_return) override;
+
+  private:
+    int32_t cipherSuite_;
+    sp<IPresentationSession> halBinder_;
+    sp<CredentialStore> store_;
+};
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
+
+#endif  // SYSTEM_SECURITY_SESSION_H_
diff --git a/identity/TEST_MAPPING b/identity/TEST_MAPPING
index 87707a8..6444c56 100644
--- a/identity/TEST_MAPPING
+++ b/identity/TEST_MAPPING
@@ -2,6 +2,9 @@
   "presubmit": [
     {
       "name": "CtsIdentityTestCases"
+    },
+    {
+      "name": "identity-credential-util-tests"
     }
   ]
 }
diff --git a/identity/binder/android/security/identity/ICredential.aidl b/identity/binder/android/security/identity/ICredential.aidl
index 2165810..e6a9fae 100644
--- a/identity/binder/android/security/identity/ICredential.aidl
+++ b/identity/binder/android/security/identity/ICredential.aidl
@@ -49,14 +49,16 @@
     byte[] getCredentialKeyCertificateChain();
 
     long selectAuthKey(in boolean allowUsingExhaustedKeys,
-                       in boolean allowUsingExpiredKeys);
+                       in boolean allowUsingExpiredKeys,
+                       in boolean incrementUsageCount);
 
     GetEntriesResultParcel getEntries(in byte[] requestMessage,
                                       in RequestNamespaceParcel[] requestNamespaces,
                                       in byte[] sessionTranscript,
                                       in byte[] readerSignature,
                                       in boolean allowUsingExhaustedKeys,
-                                      in boolean allowUsingExpiredKeys);
+                                      in boolean allowUsingExpiredKeys,
+                                      in boolean incrementUsageCount);
 
     void setAvailableAuthenticationKeys(in int keyCount, in int maxUsesPerKey);
 
diff --git a/identity/binder/android/security/identity/ICredentialStore.aidl b/identity/binder/android/security/identity/ICredentialStore.aidl
index 8357f47..39b5e5f 100644
--- a/identity/binder/android/security/identity/ICredentialStore.aidl
+++ b/identity/binder/android/security/identity/ICredentialStore.aidl
@@ -19,6 +19,7 @@
 import android.security.identity.IWritableCredential;
 import android.security.identity.ICredential;
 import android.security.identity.SecurityHardwareInfoParcel;
+import android.security.identity.ISession;
 
 /**
  * @hide
@@ -45,6 +46,9 @@
 
     IWritableCredential createCredential(in @utf8InCpp String credentialName,
                                          in @utf8InCpp String docType);
+
     ICredential getCredentialByName(in @utf8InCpp String credentialName,
                                     in int cipherSuite);
+
+    ISession createPresentationSession(in int cipherSuite);
 }
diff --git a/identity/binder/android/security/identity/ISession.aidl b/identity/binder/android/security/identity/ISession.aidl
new file mode 100644
index 0000000..2139ec1
--- /dev/null
+++ b/identity/binder/android/security/identity/ISession.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.security.identity.ICredential;
+
+/**
+ * @hide
+ */
+interface ISession {
+    byte[] getEphemeralKeyPair();
+
+    long getAuthChallenge();
+
+    void setReaderEphemeralPublicKey(in byte[] publicKey);
+
+    void setSessionTranscript(in byte[] sessionTranscript);
+
+    ICredential getCredentialForPresentation(in @utf8InCpp String credentialName);
+}
diff --git a/identity/util/Android.bp b/identity/util/Android.bp
new file mode 100644
index 0000000..71d7718
--- /dev/null
+++ b/identity/util/Android.bp
@@ -0,0 +1,42 @@
+// 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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+    name: "identity-credential-util",
+    srcs: [
+        "src/java/**/*.java",
+    ],
+    static_libs: [
+        "androidx.annotation_annotation",
+        "bouncycastle-unbundled",
+        "cbor-java",
+    ],
+}
+
+android_test {
+    name: "identity-credential-util-tests",
+    test_suites: ["general-tests"],
+    srcs: [
+        "test/java/**/*.java",
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "identity-credential-util",
+        "junit",
+    ],
+}
diff --git a/identity/util/AndroidManifest.xml b/identity/util/AndroidManifest.xml
new file mode 100644
index 0000000..eece4dc
--- /dev/null
+++ b/identity/util/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.security.identity.internal">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.security.identity.internal"
+        android:label="Unit tests for com.android.security.identity.internal"/>
+
+</manifest>
+
diff --git a/identity/util/AndroidTest.xml b/identity/util/AndroidTest.xml
new file mode 100644
index 0000000..345460f
--- /dev/null
+++ b/identity/util/AndroidTest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+<configuration description="Config for identity cred support library tests">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="identity-credential-util-tests.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.InstrumentationTest" >
+        <option name="package" value="com.android.security.identity.internal" />
+    </test>
+</configuration>
diff --git a/identity/util/src/java/com/android/security/identity/internal/Iso18013.java b/identity/util/src/java/com/android/security/identity/internal/Iso18013.java
new file mode 100644
index 0000000..6da90e5
--- /dev/null
+++ b/identity/util/src/java/com/android/security/identity/internal/Iso18013.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.security.identity.internal;
+
+import static com.android.security.identity.internal.Util.CBOR_SEMANTIC_TAG_ENCODED_CBOR;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.KeyPair;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECPoint;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import javax.crypto.KeyAgreement;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import co.nstant.in.cbor.CborBuilder;
+import co.nstant.in.cbor.CborDecoder;
+import co.nstant.in.cbor.CborEncoder;
+import co.nstant.in.cbor.CborException;
+import co.nstant.in.cbor.builder.MapBuilder;
+import co.nstant.in.cbor.model.ByteString;
+import co.nstant.in.cbor.model.DataItem;
+
+/**
+ * Various utilities for working with the ISO mobile driving license (mDL)
+ * application specification (ISO 18013-5).
+ */
+public class Iso18013 {
+    /**
+     * Each version of the spec is namespaced, and all namespace-specific constants
+     * are thus collected into a namespace-specific nested class.
+     */
+    public static class V1 {
+        public static final String NAMESPACE = "org.iso.18013.5.1";
+        public static final String DOC_TYPE = "org.iso.18013.5.1.mdl";
+
+        public static final String FAMILY_NAME = "family_name";
+        public static final String GIVEN_NAME = "given_name";
+        public static final String BIRTH_DATE = "birth_date";
+        public static final String ISSUE_DATE = "issue_date";
+        public static final String EXPIRY = "expiry_date";
+        public static final String ISSUING_COUNTRY = "issuing_country";
+        public static final String ISSUING_AUTHORITY = "issuing_authority";
+        public static final String DOCUMENT_NUMBER = "document_number";
+        public static final String PORTRAIT = "portrait";
+        public static final String DRIVING_PRIVILEGES = "driving_privileges";
+        public static final String UN_DISTINGUISHING_SIGN = "un_distinguishing_sign";
+        public static final String HEIGHT = "height";
+        public static final String BIO_FACE = "biometric_template_face";
+
+        public static String ageOver(int age) {
+            if (age < 0 || age > 99) {
+                throw new InvalidParameterException("age must be between 0 and 99, inclusive");
+            }
+            return String.format("age_over_%02d", age);
+        }
+    }
+
+    public static byte[] buildDeviceAuthenticationCbor(String docType,
+            byte[] encodedSessionTranscript,
+            byte[] deviceNameSpacesBytes) {
+        ByteArrayOutputStream daBaos = new ByteArrayOutputStream();
+        try {
+            ByteArrayInputStream bais = new ByteArrayInputStream(encodedSessionTranscript);
+            List<DataItem> dataItems = null;
+            dataItems = new CborDecoder(bais).decode();
+            DataItem sessionTranscript = dataItems.get(0);
+            ByteString deviceNameSpacesBytesItem = new ByteString(deviceNameSpacesBytes);
+            deviceNameSpacesBytesItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+            new CborEncoder(daBaos).encode(new CborBuilder()
+                    .addArray()
+                    .add("DeviceAuthentication")
+                    .add(sessionTranscript)
+                    .add(docType)
+                    .add(deviceNameSpacesBytesItem)
+                    .end()
+                    .build());
+        } catch (CborException e) {
+            throw new RuntimeException("Error encoding DeviceAuthentication", e);
+        }
+        return daBaos.toByteArray();
+    }
+
+    public static byte[] buildReaderAuthenticationBytesCbor(
+            byte[] encodedSessionTranscript,
+            byte[] requestMessageBytes) {
+
+        ByteArrayOutputStream daBaos = new ByteArrayOutputStream();
+        try {
+            ByteArrayInputStream bais = new ByteArrayInputStream(encodedSessionTranscript);
+            List<DataItem> dataItems = null;
+            dataItems = new CborDecoder(bais).decode();
+            DataItem sessionTranscript = dataItems.get(0);
+            ByteString requestMessageBytesItem = new ByteString(requestMessageBytes);
+            requestMessageBytesItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+            new CborEncoder(daBaos).encode(new CborBuilder()
+                    .addArray()
+                    .add("ReaderAuthentication")
+                    .add(sessionTranscript)
+                    .add(requestMessageBytesItem)
+                    .end()
+                    .build());
+        } catch (CborException e) {
+            throw new RuntimeException("Error encoding ReaderAuthentication", e);
+        }
+        byte[] readerAuthentication = daBaos.toByteArray();
+        return Util.prependSemanticTagForEncodedCbor(readerAuthentication);
+    }
+
+    // This returns a SessionTranscript which satisfy the requirement
+    // that the uncompressed X and Y coordinates of the public key for the
+    // mDL's ephemeral key-pair appear somewhere in the encoded
+    // DeviceEngagement.
+    public static byte[] buildSessionTranscript(KeyPair ephemeralKeyPair) {
+        // Make the coordinates appear in an already encoded bstr - this
+        // mimics how the mDL COSE_Key appear as encoded data inside the
+        // encoded DeviceEngagement
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
+            // X and Y are always positive so for interop we remove any leading zeroes
+            // inserted by the BigInteger encoder.
+            byte[] x = stripLeadingZeroes(w.getAffineX().toByteArray());
+            byte[] y = stripLeadingZeroes(w.getAffineY().toByteArray());
+            baos.write(new byte[]{41});
+            baos.write(x);
+            baos.write(y);
+            baos.write(new byte[]{42, 44});
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+        byte[] blobWithCoords = baos.toByteArray();
+
+        baos = new ByteArrayOutputStream();
+        try {
+            new CborEncoder(baos).encode(new CborBuilder()
+                    .addArray()
+                    .add(blobWithCoords)
+                    .end()
+                    .build());
+        } catch (CborException e) {
+            e.printStackTrace();
+            return null;
+        }
+        ByteString encodedDeviceEngagementItem = new ByteString(baos.toByteArray());
+        ByteString encodedEReaderKeyItem = new ByteString(Util.cborEncodeString("doesn't matter"));
+        encodedDeviceEngagementItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+        encodedEReaderKeyItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+
+        baos = new ByteArrayOutputStream();
+        try {
+            new CborEncoder(baos).encode(new CborBuilder()
+                    .addArray()
+                    .add(encodedDeviceEngagementItem)
+                    .add(encodedEReaderKeyItem)
+                    .end()
+                    .build());
+        } catch (CborException e) {
+            e.printStackTrace();
+            return null;
+        }
+        return baos.toByteArray();
+    }
+
+    /*
+     * Helper function to create a CBOR data for requesting data items. The IntentToRetain
+     * value will be set to false for all elements.
+     *
+     * <p>The returned CBOR data conforms to the following CDDL schema:</p>
+     *
+     * <pre>
+     *   ItemsRequest = {
+     *     ? "docType" : DocType,
+     *     "nameSpaces" : NameSpaces,
+     *     ? "RequestInfo" : {* tstr => any} ; Additional info the reader wants to provide
+     *   }
+     *
+     *   NameSpaces = {
+     *     + NameSpace => DataElements     ; Requested data elements for each NameSpace
+     *   }
+     *
+     *   DataElements = {
+     *     + DataElement => IntentToRetain
+     *   }
+     *
+     *   DocType = tstr
+     *
+     *   DataElement = tstr
+     *   IntentToRetain = bool
+     *   NameSpace = tstr
+     * </pre>
+     *
+     * @param entriesToRequest       The entries to request, organized as a map of namespace
+     *                               names with each value being a collection of data elements
+     *                               in the given namespace.
+     * @param docType                  The document type or {@code null} if there is no document
+     *                                 type.
+     * @return CBOR data conforming to the CDDL mentioned above.
+     */
+    public static @NonNull
+    byte[] createItemsRequest(
+            @NonNull Map<String, Collection<String>> entriesToRequest,
+            @Nullable String docType) {
+        CborBuilder builder = new CborBuilder();
+        MapBuilder<CborBuilder> mapBuilder = builder.addMap();
+        if (docType != null) {
+            mapBuilder.put("docType", docType);
+        }
+
+        MapBuilder<MapBuilder<CborBuilder>> nsMapBuilder = mapBuilder.putMap("nameSpaces");
+        for (String namespaceName : entriesToRequest.keySet()) {
+            Collection<String> entryNames = entriesToRequest.get(namespaceName);
+            MapBuilder<MapBuilder<MapBuilder<CborBuilder>>> entryNameMapBuilder =
+                    nsMapBuilder.putMap(namespaceName);
+            for (String entryName : entryNames) {
+                entryNameMapBuilder.put(entryName, false);
+            }
+        }
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        CborEncoder encoder = new CborEncoder(baos);
+        try {
+            encoder.encode(builder.build());
+        } catch (CborException e) {
+            throw new RuntimeException("Error encoding CBOR", e);
+        }
+        return baos.toByteArray();
+    }
+
+    public static SecretKey calcEMacKeyForReader(PublicKey authenticationPublicKey,
+            PrivateKey ephemeralReaderPrivateKey,
+            byte[] encodedSessionTranscript) {
+        try {
+            KeyAgreement ka = KeyAgreement.getInstance("ECDH");
+            ka.init(ephemeralReaderPrivateKey);
+            ka.doPhase(authenticationPublicKey, true);
+            byte[] sharedSecret = ka.generateSecret();
+
+            byte[] sessionTranscriptBytes =
+                    Util.cborEncode(Util.buildCborTaggedByteString(encodedSessionTranscript));
+
+            byte[] salt = MessageDigest.getInstance("SHA-256").digest(sessionTranscriptBytes);
+            byte[] info = new byte[]{'E', 'M', 'a', 'c', 'K', 'e', 'y'};
+            byte[] derivedKey = Util.computeHkdf("HmacSha256", sharedSecret, salt, info, 32);
+
+            SecretKey secretKey = new SecretKeySpec(derivedKey, "");
+            return secretKey;
+        } catch (InvalidKeyException
+                | NoSuchAlgorithmException e) {
+            throw new IllegalStateException("Error performing key agreement", e);
+        }
+    }
+
+    private static byte[] stripLeadingZeroes(byte[] value) {
+        int n = 0;
+        while (n < value.length && value[n] == 0) {
+            n++;
+        }
+        int newLen = value.length - n;
+        byte[] ret = new byte[newLen];
+        int m = 0;
+        while (n < value.length) {
+            ret[m++] = value[n++];
+        }
+        return ret;
+    }
+}
diff --git a/identity/util/src/java/com/android/security/identity/internal/Util.java b/identity/util/src/java/com/android/security/identity/internal/Util.java
new file mode 100644
index 0000000..4ec54a7
--- /dev/null
+++ b/identity/util/src/java/com/android/security/identity/internal/Util.java
@@ -0,0 +1,1316 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.security.identity.internal;
+
+import android.security.identity.ResultData;
+import android.security.identity.IdentityCredentialStore;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.FeatureInfo;
+import android.os.SystemProperties;
+import android.security.keystore.KeyProperties;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyStore;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.ECGenParameterSpec;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Formatter;
+import java.util.Map;
+
+import javax.crypto.KeyAgreement;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECPoint;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1OctetString;
+
+import co.nstant.in.cbor.CborBuilder;
+import co.nstant.in.cbor.CborDecoder;
+import co.nstant.in.cbor.CborEncoder;
+import co.nstant.in.cbor.CborException;
+import co.nstant.in.cbor.builder.ArrayBuilder;
+import co.nstant.in.cbor.builder.MapBuilder;
+import co.nstant.in.cbor.model.AbstractFloat;
+import co.nstant.in.cbor.model.Array;
+import co.nstant.in.cbor.model.ByteString;
+import co.nstant.in.cbor.model.DataItem;
+import co.nstant.in.cbor.model.DoublePrecisionFloat;
+import co.nstant.in.cbor.model.MajorType;
+import co.nstant.in.cbor.model.NegativeInteger;
+import co.nstant.in.cbor.model.SimpleValue;
+import co.nstant.in.cbor.model.SimpleValueType;
+import co.nstant.in.cbor.model.SpecialType;
+import co.nstant.in.cbor.model.UnicodeString;
+import co.nstant.in.cbor.model.UnsignedInteger;
+
+public class Util {
+    private static final String TAG = "Util";
+
+    public static byte[] canonicalizeCbor(byte[] encodedCbor) throws CborException {
+        ByteArrayInputStream bais = new ByteArrayInputStream(encodedCbor);
+        List<DataItem> dataItems = new CborDecoder(bais).decode();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        for(DataItem dataItem : dataItems) {
+            CborEncoder encoder = new CborEncoder(baos);
+            encoder.encode(dataItem);
+        }
+        return baos.toByteArray();
+    }
+
+
+    public static String cborPrettyPrint(byte[] encodedBytes) throws CborException {
+        StringBuilder sb = new StringBuilder();
+
+        ByteArrayInputStream bais = new ByteArrayInputStream(encodedBytes);
+        List<DataItem> dataItems = new CborDecoder(bais).decode();
+        int count = 0;
+        for (DataItem dataItem : dataItems) {
+            if (count > 0) {
+                sb.append(",\n");
+            }
+            cborPrettyPrintDataItem(sb, 0, dataItem);
+            count++;
+        }
+
+        return sb.toString();
+    }
+
+    // Returns true iff all elements in |items| are not compound (e.g. an array or a map).
+    static boolean cborAreAllDataItemsNonCompound(List<DataItem> items) {
+        for (DataItem item : items) {
+            switch (item.getMajorType()) {
+                case ARRAY:
+                case MAP:
+                    return false;
+                default:
+                    // continue inspecting other data items
+            }
+        }
+        return true;
+    }
+
+    public static void cborPrettyPrintDataItem(StringBuilder sb, int indent, DataItem dataItem) {
+        StringBuilder indentBuilder = new StringBuilder();
+        for (int n = 0; n < indent; n++) {
+            indentBuilder.append(' ');
+        }
+        String indentString = indentBuilder.toString();
+
+        if (dataItem.hasTag()) {
+            sb.append(String.format("tag %d ", dataItem.getTag().getValue()));
+        }
+
+        switch (dataItem.getMajorType()) {
+            case INVALID:
+                // TODO: throw
+                sb.append("<invalid>");
+                break;
+            case UNSIGNED_INTEGER: {
+                // Major type 0: an unsigned integer.
+                BigInteger value = ((UnsignedInteger) dataItem).getValue();
+                sb.append(value);
+            }
+            break;
+            case NEGATIVE_INTEGER: {
+                // Major type 1: a negative integer.
+                BigInteger value = ((NegativeInteger) dataItem).getValue();
+                sb.append(value);
+            }
+            break;
+            case BYTE_STRING: {
+                // Major type 2: a byte string.
+                byte[] value = ((ByteString) dataItem).getBytes();
+                sb.append("[");
+                int count = 0;
+                for (byte b : value) {
+                    if (count > 0) {
+                        sb.append(", ");
+                    }
+                    sb.append(String.format("0x%02x", b));
+                    count++;
+                }
+                sb.append("]");
+            }
+            break;
+            case UNICODE_STRING: {
+                // Major type 3: string of Unicode characters that is encoded as UTF-8 [RFC3629].
+                String value = ((UnicodeString) dataItem).getString();
+                // TODO: escape ' in |value|
+                sb.append("'" + value + "'");
+            }
+            break;
+            case ARRAY: {
+                // Major type 4: an array of data items.
+                List<DataItem> items = ((co.nstant.in.cbor.model.Array) dataItem).getDataItems();
+                if (items.size() == 0) {
+                    sb.append("[]");
+                } else if (cborAreAllDataItemsNonCompound(items)) {
+                    // The case where everything fits on one line.
+                    sb.append("[");
+                    int count = 0;
+                    for (DataItem item : items) {
+                        cborPrettyPrintDataItem(sb, indent, item);
+                        if (++count < items.size()) {
+                            sb.append(", ");
+                        }
+                    }
+                    sb.append("]");
+                } else {
+                    sb.append("[\n" + indentString);
+                    int count = 0;
+                    for (DataItem item : items) {
+                        sb.append("  ");
+                        cborPrettyPrintDataItem(sb, indent + 2, item);
+                        if (++count < items.size()) {
+                            sb.append(",");
+                        }
+                        sb.append("\n" + indentString);
+                    }
+                    sb.append("]");
+                }
+            }
+            break;
+            case MAP: {
+                // Major type 5: a map of pairs of data items.
+                Collection<DataItem> keys = ((co.nstant.in.cbor.model.Map) dataItem).getKeys();
+                if (keys.size() == 0) {
+                    sb.append("{}");
+                } else {
+                    sb.append("{\n" + indentString);
+                    int count = 0;
+                    for (DataItem key : keys) {
+                        sb.append("  ");
+                        DataItem value = ((co.nstant.in.cbor.model.Map) dataItem).get(key);
+                        cborPrettyPrintDataItem(sb, indent + 2, key);
+                        sb.append(" : ");
+                        cborPrettyPrintDataItem(sb, indent + 2, value);
+                        if (++count < keys.size()) {
+                            sb.append(",");
+                        }
+                        sb.append("\n" + indentString);
+                    }
+                    sb.append("}");
+                }
+            }
+            break;
+            case TAG:
+                // Major type 6: optional semantic tagging of other major types
+                //
+                // We never encounter this one since it's automatically handled via the
+                // DataItem that is tagged.
+                throw new RuntimeException("Semantic tag data item not expected");
+
+            case SPECIAL:
+                // Major type 7: floating point numbers and simple data types that need no
+                // content, as well as the "break" stop code.
+                if (dataItem instanceof SimpleValue) {
+                    switch (((SimpleValue) dataItem).getSimpleValueType()) {
+                        case FALSE:
+                            sb.append("false");
+                            break;
+                        case TRUE:
+                            sb.append("true");
+                            break;
+                        case NULL:
+                            sb.append("null");
+                            break;
+                        case UNDEFINED:
+                            sb.append("undefined");
+                            break;
+                        case RESERVED:
+                            sb.append("reserved");
+                            break;
+                        case UNALLOCATED:
+                            sb.append("unallocated");
+                            break;
+                    }
+                } else if (dataItem instanceof DoublePrecisionFloat) {
+                    DecimalFormat df = new DecimalFormat("0",
+                            DecimalFormatSymbols.getInstance(Locale.ENGLISH));
+                    df.setMaximumFractionDigits(340);
+                    sb.append(df.format(((DoublePrecisionFloat) dataItem).getValue()));
+                } else if (dataItem instanceof AbstractFloat) {
+                    DecimalFormat df = new DecimalFormat("0",
+                            DecimalFormatSymbols.getInstance(Locale.ENGLISH));
+                    df.setMaximumFractionDigits(340);
+                    sb.append(df.format(((AbstractFloat) dataItem).getValue()));
+                } else {
+                    sb.append("break");
+                }
+                break;
+        }
+    }
+
+    public static byte[] encodeCbor(List<DataItem> dataItems) {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        CborEncoder encoder = new CborEncoder(baos);
+        try {
+            encoder.encode(dataItems);
+        } catch (CborException e) {
+            throw new RuntimeException("Error encoding data", e);
+        }
+        return baos.toByteArray();
+    }
+
+    public static byte[] coseBuildToBeSigned(byte[] encodedProtectedHeaders,
+            byte[] payload,
+            byte[] detachedContent) {
+        CborBuilder sigStructure = new CborBuilder();
+        ArrayBuilder<CborBuilder> array = sigStructure.addArray();
+
+        array.add("Signature1");
+        array.add(encodedProtectedHeaders);
+
+        // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+        // so external_aad is the empty bstr
+        byte emptyExternalAad[] = new byte[0];
+        array.add(emptyExternalAad);
+
+        // Next field is the payload, independently of how it's transported (RFC
+        // 8152 section 4.4). Since our API specifies only one of |data| and
+        // |detachedContent| can be non-empty, it's simply just the non-empty one.
+        if (payload != null && payload.length > 0) {
+            array.add(payload);
+        } else {
+            array.add(detachedContent);
+        }
+        array.end();
+        return encodeCbor(sigStructure.build());
+    }
+
+    private static final int COSE_LABEL_ALG = 1;
+    private static final int COSE_LABEL_X5CHAIN = 33;  // temporary identifier
+
+    // From "COSE Algorithms" registry
+    private static final int COSE_ALG_ECDSA_256 = -7;
+    private static final int COSE_ALG_HMAC_256_256 = 5;
+
+    private static byte[] signatureDerToCose(byte[] signature) {
+        if (signature.length > 128) {
+            throw new RuntimeException("Unexpected length " + signature.length
+                    + ", expected less than 128");
+        }
+        if (signature[0] != 0x30) {
+            throw new RuntimeException("Unexpected first byte " + signature[0]
+                    + ", expected 0x30");
+        }
+        if ((signature[1] & 0x80) != 0x00) {
+            throw new RuntimeException("Unexpected second byte " + signature[1]
+                    + ", bit 7 shouldn't be set");
+        }
+        int rOffset = 2;
+        int rSize = signature[rOffset + 1];
+        byte[] rBytes = stripLeadingZeroes(
+            Arrays.copyOfRange(signature,rOffset + 2, rOffset + rSize + 2));
+
+        int sOffset = rOffset + 2 + rSize;
+        int sSize = signature[sOffset + 1];
+        byte[] sBytes = stripLeadingZeroes(
+            Arrays.copyOfRange(signature, sOffset + 2, sOffset + sSize + 2));
+
+        if (rBytes.length > 32) {
+            throw new RuntimeException("rBytes.length is " + rBytes.length + " which is > 32");
+        }
+        if (sBytes.length > 32) {
+            throw new RuntimeException("sBytes.length is " + sBytes.length + " which is > 32");
+        }
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            for (int n = 0; n < 32 - rBytes.length; n++) {
+                baos.write(0x00);
+            }
+            baos.write(rBytes);
+            for (int n = 0; n < 32 - sBytes.length; n++) {
+                baos.write(0x00);
+            }
+            baos.write(sBytes);
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+        return baos.toByteArray();
+    }
+
+    // Adds leading 0x00 if the first encoded byte MSB is set.
+    private static byte[] encodePositiveBigInteger(BigInteger i) {
+        byte[] bytes = i.toByteArray();
+        if ((bytes[0] & 0x80) != 0) {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            try {
+                baos.write(0x00);
+                baos.write(bytes);
+            } catch (IOException e) {
+                e.printStackTrace();
+                throw new RuntimeException("Failed writing data", e);
+            }
+            bytes = baos.toByteArray();
+        }
+        return bytes;
+    }
+
+    private static byte[] signatureCoseToDer(byte[] signature) {
+        if (signature.length != 64) {
+            throw new RuntimeException("signature.length is " + signature.length + ", expected 64");
+        }
+        // r and s are always positive and may use all 256 bits so use the constructor which
+        // parses them as unsigned.
+        BigInteger r = new BigInteger(1, Arrays.copyOfRange(signature, 0, 32));
+        BigInteger s = new BigInteger(1, Arrays.copyOfRange(signature, 32, 64));
+        byte[] rBytes = encodePositiveBigInteger(r);
+        byte[] sBytes = encodePositiveBigInteger(s);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            baos.write(0x30);
+            baos.write(2 + rBytes.length + 2 + sBytes.length);
+            baos.write(0x02);
+            baos.write(rBytes.length);
+            baos.write(rBytes);
+            baos.write(0x02);
+            baos.write(sBytes.length);
+            baos.write(sBytes);
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+        return baos.toByteArray();
+    }
+
+    public static byte[] coseSign1Sign(PrivateKey key,
+            @Nullable byte[] data,
+            byte[] detachedContent,
+            @Nullable Collection<X509Certificate> certificateChain)
+            throws NoSuchAlgorithmException, InvalidKeyException, CertificateEncodingException {
+
+        int dataLen = (data != null ? data.length : 0);
+        int detachedContentLen = (detachedContent != null ? detachedContent.length : 0);
+        if (dataLen > 0 && detachedContentLen > 0) {
+            throw new RuntimeException("data and detachedContent cannot both be non-empty");
+        }
+
+        CborBuilder protectedHeaders = new CborBuilder();
+        MapBuilder<CborBuilder> protectedHeadersMap = protectedHeaders.addMap();
+        protectedHeadersMap.put(COSE_LABEL_ALG, COSE_ALG_ECDSA_256);
+        byte[] protectedHeadersBytes = encodeCbor(protectedHeaders.build());
+
+        byte[] toBeSigned = coseBuildToBeSigned(protectedHeadersBytes, data, detachedContent);
+
+        byte[] coseSignature = null;
+        try {
+            Signature s = Signature.getInstance("SHA256withECDSA");
+            s.initSign(key);
+            s.update(toBeSigned);
+            byte[] derSignature = s.sign();
+            coseSignature = signatureDerToCose(derSignature);
+        } catch (SignatureException e) {
+            throw new RuntimeException("Error signing data");
+        }
+
+        CborBuilder builder = new CborBuilder();
+        ArrayBuilder<CborBuilder> array = builder.addArray();
+        array.add(protectedHeadersBytes);
+        MapBuilder<ArrayBuilder<CborBuilder>> unprotectedHeaders = array.addMap();
+        if (certificateChain != null && certificateChain.size() > 0) {
+            if (certificateChain.size() == 1) {
+                X509Certificate cert = certificateChain.iterator().next();
+                unprotectedHeaders.put(COSE_LABEL_X5CHAIN, cert.getEncoded());
+            } else {
+                ArrayBuilder<MapBuilder<ArrayBuilder<CborBuilder>>> x5chainsArray =
+                        unprotectedHeaders.putArray(COSE_LABEL_X5CHAIN);
+                for (X509Certificate cert : certificateChain) {
+                    x5chainsArray.add(cert.getEncoded());
+                }
+            }
+        }
+        if (data == null || data.length == 0) {
+            array.add(new SimpleValue(SimpleValueType.NULL));
+        } else {
+            array.add(data);
+        }
+        array.add(coseSignature);
+
+        return encodeCbor(builder.build());
+    }
+
+    public static boolean coseSign1CheckSignature(byte[] signatureCose1,
+            byte[] detachedContent,
+            PublicKey publicKey) throws NoSuchAlgorithmException, InvalidKeyException {
+        ByteArrayInputStream bais = new ByteArrayInputStream(signatureCose1);
+        List<DataItem> dataItems = null;
+        try {
+            dataItems = new CborDecoder(bais).decode();
+        } catch (CborException e) {
+            throw new RuntimeException("Given signature is not valid CBOR", e);
+        }
+        if (dataItems.size() != 1) {
+            throw new RuntimeException("Expected just one data item");
+        }
+        DataItem dataItem = dataItems.get(0);
+        if (dataItem.getMajorType() != MajorType.ARRAY) {
+            throw new RuntimeException("Data item is not an array");
+        }
+        List<DataItem> items = ((co.nstant.in.cbor.model.Array) dataItem).getDataItems();
+        if (items.size() < 4) {
+            throw new RuntimeException("Expected at least four items in COSE_Sign1 array");
+        }
+        if (items.get(0).getMajorType() != MajorType.BYTE_STRING) {
+            throw new RuntimeException("Item 0 (protected headers) is not a byte-string");
+        }
+        byte[] encodedProtectedHeaders =
+                ((co.nstant.in.cbor.model.ByteString) items.get(0)).getBytes();
+        byte[] payload = new byte[0];
+        if (items.get(2).getMajorType() == MajorType.SPECIAL) {
+            if (((co.nstant.in.cbor.model.Special) items.get(2)).getSpecialType()
+                != SpecialType.SIMPLE_VALUE) {
+                throw new RuntimeException("Item 2 (payload) is a special but not a simple value");
+            }
+            SimpleValue simple = (co.nstant.in.cbor.model.SimpleValue) items.get(2);
+            if (simple.getSimpleValueType() != SimpleValueType.NULL) {
+                throw new RuntimeException("Item 2 (payload) is a simple but not the value null");
+            }
+        } else if (items.get(2).getMajorType() == MajorType.BYTE_STRING) {
+            payload = ((co.nstant.in.cbor.model.ByteString) items.get(2)).getBytes();
+        } else {
+            throw new RuntimeException("Item 2 (payload) is not nil or byte-string");
+        }
+        if (items.get(3).getMajorType() != MajorType.BYTE_STRING) {
+            throw new RuntimeException("Item 3 (signature) is not a byte-string");
+        }
+        byte[] coseSignature = ((co.nstant.in.cbor.model.ByteString) items.get(3)).getBytes();
+
+        byte[] derSignature = signatureCoseToDer(coseSignature);
+
+        int dataLen = payload.length;
+        int detachedContentLen = (detachedContent != null ? detachedContent.length : 0);
+        if (dataLen > 0 && detachedContentLen > 0) {
+            throw new RuntimeException("data and detachedContent cannot both be non-empty");
+        }
+
+        byte[] toBeSigned = Util.coseBuildToBeSigned(encodedProtectedHeaders,
+                payload, detachedContent);
+
+        try {
+            Signature verifier = Signature.getInstance("SHA256withECDSA");
+            verifier.initVerify(publicKey);
+            verifier.update(toBeSigned);
+            return verifier.verify(derSignature);
+        } catch (SignatureException e) {
+            throw new RuntimeException("Error verifying signature");
+        }
+    }
+
+    // Returns the empty byte-array if no data is included in the structure.
+    //
+    // Throws RuntimeException if the given bytes aren't valid COSE_Sign1.
+    //
+    public static byte[] coseSign1GetData(byte[] signatureCose1) {
+        ByteArrayInputStream bais = new ByteArrayInputStream(signatureCose1);
+        List<DataItem> dataItems = null;
+        try {
+            dataItems = new CborDecoder(bais).decode();
+        } catch (CborException e) {
+            throw new RuntimeException("Given signature is not valid CBOR", e);
+        }
+        if (dataItems.size() != 1) {
+            throw new RuntimeException("Expected just one data item");
+        }
+        DataItem dataItem = dataItems.get(0);
+        if (dataItem.getMajorType() != MajorType.ARRAY) {
+            throw new RuntimeException("Data item is not an array");
+        }
+        List<DataItem> items = ((co.nstant.in.cbor.model.Array) dataItem).getDataItems();
+        if (items.size() < 4) {
+            throw new RuntimeException("Expected at least four items in COSE_Sign1 array");
+        }
+        byte[] payload = new byte[0];
+        if (items.get(2).getMajorType() == MajorType.SPECIAL) {
+            if (((co.nstant.in.cbor.model.Special) items.get(2)).getSpecialType()
+                != SpecialType.SIMPLE_VALUE) {
+                throw new RuntimeException("Item 2 (payload) is a special but not a simple value");
+            }
+            SimpleValue simple = (co.nstant.in.cbor.model.SimpleValue) items.get(2);
+            if (simple.getSimpleValueType() != SimpleValueType.NULL) {
+                throw new RuntimeException("Item 2 (payload) is a simple but not the value null");
+            }
+        } else if (items.get(2).getMajorType() == MajorType.BYTE_STRING) {
+            payload = ((co.nstant.in.cbor.model.ByteString) items.get(2)).getBytes();
+        } else {
+            throw new RuntimeException("Item 2 (payload) is not nil or byte-string");
+        }
+        return payload;
+    }
+
+    // Returns the empty collection if no x5chain is included in the structure.
+    //
+    // Throws RuntimeException if the given bytes aren't valid COSE_Sign1.
+    //
+    public static Collection<X509Certificate> coseSign1GetX5Chain(byte[] signatureCose1)
+            throws CertificateException {
+        ArrayList<X509Certificate> ret = new ArrayList<>();
+        ByteArrayInputStream bais = new ByteArrayInputStream(signatureCose1);
+        List<DataItem> dataItems = null;
+        try {
+            dataItems = new CborDecoder(bais).decode();
+        } catch (CborException e) {
+            throw new RuntimeException("Given signature is not valid CBOR", e);
+        }
+        if (dataItems.size() != 1) {
+            throw new RuntimeException("Expected just one data item");
+        }
+        DataItem dataItem = dataItems.get(0);
+        if (dataItem.getMajorType() != MajorType.ARRAY) {
+            throw new RuntimeException("Data item is not an array");
+        }
+        List<DataItem> items = ((co.nstant.in.cbor.model.Array) dataItem).getDataItems();
+        if (items.size() < 4) {
+            throw new RuntimeException("Expected at least four items in COSE_Sign1 array");
+        }
+        if (items.get(1).getMajorType() != MajorType.MAP) {
+            throw new RuntimeException("Item 1 (unprocted headers) is not a map");
+        }
+        co.nstant.in.cbor.model.Map map = (co.nstant.in.cbor.model.Map) items.get(1);
+        DataItem x5chainItem = map.get(new UnsignedInteger(COSE_LABEL_X5CHAIN));
+        if (x5chainItem != null) {
+            CertificateFactory factory = CertificateFactory.getInstance("X.509");
+            if (x5chainItem instanceof ByteString) {
+                ByteArrayInputStream certBais =
+                        new ByteArrayInputStream(((ByteString) x5chainItem).getBytes());
+                ret.add((X509Certificate) factory.generateCertificate(certBais));
+            } else if (x5chainItem instanceof Array) {
+                for (DataItem certItem : ((Array) x5chainItem).getDataItems()) {
+                    if (!(certItem instanceof ByteString)) {
+                        throw new RuntimeException(
+                            "Unexpected type for array item in x5chain value");
+                    }
+                    ByteArrayInputStream certBais =
+                            new ByteArrayInputStream(((ByteString) certItem).getBytes());
+                    ret.add((X509Certificate) factory.generateCertificate(certBais));
+                }
+            } else {
+                throw new RuntimeException("Unexpected type for x5chain value");
+            }
+        }
+        return ret;
+    }
+
+    public static byte[] coseBuildToBeMACed(byte[] encodedProtectedHeaders,
+            byte[] payload,
+            byte[] detachedContent) {
+        CborBuilder macStructure = new CborBuilder();
+        ArrayBuilder<CborBuilder> array = macStructure.addArray();
+
+        array.add("MAC0");
+        array.add(encodedProtectedHeaders);
+
+        // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+        // so external_aad is the empty bstr
+        byte emptyExternalAad[] = new byte[0];
+        array.add(emptyExternalAad);
+
+        // Next field is the payload, independently of how it's transported (RFC
+        // 8152 section 4.4). Since our API specifies only one of |data| and
+        // |detachedContent| can be non-empty, it's simply just the non-empty one.
+        if (payload != null && payload.length > 0) {
+            array.add(payload);
+        } else {
+            array.add(detachedContent);
+        }
+
+        return encodeCbor(macStructure.build());
+    }
+
+    public static byte[] coseMac0(SecretKey key,
+            @Nullable byte[] data,
+            byte[] detachedContent)
+            throws NoSuchAlgorithmException, InvalidKeyException, CertificateEncodingException {
+
+        int dataLen = (data != null ? data.length : 0);
+        int detachedContentLen = (detachedContent != null ? detachedContent.length : 0);
+        if (dataLen > 0 && detachedContentLen > 0) {
+            throw new RuntimeException("data and detachedContent cannot both be non-empty");
+        }
+
+        CborBuilder protectedHeaders = new CborBuilder();
+        MapBuilder<CborBuilder> protectedHeadersMap = protectedHeaders.addMap();
+        protectedHeadersMap.put(COSE_LABEL_ALG, COSE_ALG_HMAC_256_256);
+        byte[] protectedHeadersBytes = encodeCbor(protectedHeaders.build());
+
+        byte[] toBeMACed = coseBuildToBeMACed(protectedHeadersBytes, data, detachedContent);
+
+        byte[] mac = null;
+        Mac m = Mac.getInstance("HmacSHA256");
+        m.init(key);
+        m.update(toBeMACed);
+        mac = m.doFinal();
+
+        CborBuilder builder = new CborBuilder();
+        ArrayBuilder<CborBuilder> array = builder.addArray();
+        array.add(protectedHeadersBytes);
+        MapBuilder<ArrayBuilder<CborBuilder>> unprotectedHeaders = array.addMap();
+        if (data == null || data.length == 0) {
+            array.add(new SimpleValue(SimpleValueType.NULL));
+        } else {
+            array.add(data);
+        }
+        array.add(mac);
+
+        return encodeCbor(builder.build());
+    }
+
+    public static String replaceLine(String text, int lineNumber, String replacementLine) {
+        String[] lines = text.split("\n");
+        int numLines = lines.length;
+        if (lineNumber < 0) {
+            lineNumber = numLines - (-lineNumber);
+        }
+        StringBuilder sb = new StringBuilder();
+        for (int n = 0; n < numLines; n++) {
+            if (n == lineNumber) {
+                sb.append(replacementLine);
+            } else {
+                sb.append(lines[n]);
+            }
+            // Only add terminating newline if passed-in string ends in a newline.
+            if (n == numLines - 1) {
+                if (text.endsWith(("\n"))) {
+                    sb.append('\n');
+                }
+            } else {
+                sb.append('\n');
+            }
+        }
+        return sb.toString();
+    }
+
+    public static byte[] cborEncode(DataItem dataItem) {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            new CborEncoder(baos).encode(dataItem);
+        } catch (CborException e) {
+            // This should never happen and we don't want cborEncode() to throw since that
+            // would complicate all callers. Log it instead.
+            e.printStackTrace();
+            Log.e(TAG, "Error encoding DataItem");
+        }
+        return baos.toByteArray();
+    }
+
+    public static byte[] cborEncodeBoolean(boolean value) {
+        return cborEncode(new CborBuilder().add(value).build().get(0));
+    }
+
+    public static byte[] cborEncodeString(@NonNull String value) {
+        return cborEncode(new CborBuilder().add(value).build().get(0));
+    }
+
+    public static byte[] cborEncodeBytestring(@NonNull byte[] value) {
+        return cborEncode(new CborBuilder().add(value).build().get(0));
+    }
+
+    public static byte[] cborEncodeInt(long value) {
+        return cborEncode(new CborBuilder().add(value).build().get(0));
+    }
+
+    static final int CBOR_SEMANTIC_TAG_ENCODED_CBOR = 24;
+
+    public static DataItem cborToDataItem(byte[] data) {
+        ByteArrayInputStream bais = new ByteArrayInputStream(data);
+        try {
+            List<DataItem> dataItems = new CborDecoder(bais).decode();
+            if (dataItems.size() != 1) {
+                throw new RuntimeException("Expected 1 item, found " + dataItems.size());
+            }
+            return dataItems.get(0);
+        } catch (CborException e) {
+            throw new RuntimeException("Error decoding data", e);
+        }
+    }
+
+    public static boolean cborDecodeBoolean(@NonNull byte[] data) {
+        return cborToDataItem(data) == SimpleValue.TRUE;
+    }
+
+    public static String cborDecodeString(@NonNull byte[] data) {
+        return ((co.nstant.in.cbor.model.UnicodeString) cborToDataItem(data)).getString();
+    }
+
+    public static long cborDecodeInt(@NonNull byte[] data) {
+        return ((co.nstant.in.cbor.model.Number) cborToDataItem(data)).getValue().longValue();
+    }
+
+    public static byte[] cborDecodeBytestring(@NonNull byte[] data) {
+        return ((co.nstant.in.cbor.model.ByteString) cborToDataItem(data)).getBytes();
+    }
+
+    public static String getStringEntry(ResultData data, String namespaceName, String name) {
+        return Util.cborDecodeString(data.getEntry(namespaceName, name));
+    }
+
+    public static boolean getBooleanEntry(ResultData data, String namespaceName, String name) {
+        return Util.cborDecodeBoolean(data.getEntry(namespaceName, name));
+    }
+
+    public static long getIntegerEntry(ResultData data, String namespaceName, String name) {
+        return Util.cborDecodeInt(data.getEntry(namespaceName, name));
+    }
+
+    public static byte[] getBytestringEntry(ResultData data, String namespaceName, String name) {
+        return Util.cborDecodeBytestring(data.getEntry(namespaceName, name));
+    }
+
+    /*
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: ecdsa-with-SHA256
+        Issuer: CN=fake
+        Validity
+            Not Before: Jan  1 00:00:00 1970 GMT
+            Not After : Jan  1 00:00:00 2048 GMT
+        Subject: CN=fake
+        Subject Public Key Info:
+            Public Key Algorithm: id-ecPublicKey
+                Public-Key: (256 bit)
+                00000000  04 9b 60 70 8a 99 b6 bf  e3 b8 17 02 9e 93 eb 48  |..`p...........H|
+                00000010  23 b9 39 89 d1 00 bf a0  0f d0 2f bd 6b 11 bc d1  |#.9......./.k...|
+                00000020  19 53 54 28 31 00 f5 49  db 31 fb 9f 7d 99 bf 23  |.ST(1..I.1..}..#|
+                00000030  fb 92 04 6b 23 63 55 98  ad 24 d2 68 c4 83 bf 99  |...k#cU..$.h....|
+                00000040  62                                                |b|
+    Signature Algorithm: ecdsa-with-SHA256
+         30:45:02:20:67:ad:d1:34:ed:a5:68:3f:5b:33:ee:b3:18:a2:
+         eb:03:61:74:0f:21:64:4a:a3:2e:82:b3:92:5c:21:0f:88:3f:
+         02:21:00:b7:38:5c:9b:f2:9c:b1:27:86:37:44:df:eb:4a:b2:
+         6c:11:9a:c1:ff:b2:80:95:ce:fc:5f:26:b4:20:6e:9b:0d
+     */
+
+
+    public static @NonNull X509Certificate signPublicKeyWithPrivateKey(String keyToSignAlias,
+            String keyToSignWithAlias) {
+
+        KeyStore ks = null;
+        try {
+            ks = KeyStore.getInstance("AndroidKeyStore");
+            ks.load(null);
+
+            /* First note that KeyStore.getCertificate() returns a self-signed X.509 certificate
+             * for the key in question. As per RFC 5280, section 4.1 an X.509 certificate has the
+             * following structure:
+             *
+             *   Certificate  ::=  SEQUENCE  {
+             *        tbsCertificate       TBSCertificate,
+             *        signatureAlgorithm   AlgorithmIdentifier,
+             *        signatureValue       BIT STRING  }
+             *
+             * Conveniently, the X509Certificate class has a getTBSCertificate() method which
+             * returns the tbsCertificate blob. So all we need to do is just sign that and build
+             * signatureAlgorithm and signatureValue and combine it with tbsCertificate. We don't
+             * need a full-blown ASN.1/DER encoder to do this.
+             */
+            X509Certificate selfSignedCert = (X509Certificate) ks.getCertificate(keyToSignAlias);
+            byte[] tbsCertificate = selfSignedCert.getTBSCertificate();
+
+            KeyStore.Entry keyToSignWithEntry = ks.getEntry(keyToSignWithAlias, null);
+            Signature s = Signature.getInstance("SHA256withECDSA");
+            s.initSign(((KeyStore.PrivateKeyEntry) keyToSignWithEntry).getPrivateKey());
+            s.update(tbsCertificate);
+            byte[] signatureValue = s.sign();
+
+            /* The DER encoding for a SEQUENCE of length 128-65536 - the length is updated below.
+             *
+             * We assume - and test for below - that the final length is always going to be in
+             * this range. This is a sound assumption given we're using 256-bit EC keys.
+             */
+            byte[] sequence = new byte[]{
+                    0x30, (byte) 0x82, 0x00, 0x00
+            };
+
+            /* The DER encoding for the ECDSA with SHA-256 signature algorithm:
+             *
+             *   SEQUENCE (1 elem)
+             *      OBJECT IDENTIFIER 1.2.840.10045.4.3.2 ecdsaWithSHA256 (ANSI X9.62 ECDSA
+             *      algorithm with SHA256)
+             */
+            byte[] signatureAlgorithm = new byte[]{
+                    0x30, 0x0a, 0x06, 0x08, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, 0x3d, 0x04, 0x03,
+                    0x02
+            };
+
+            /* The DER encoding for a BIT STRING with one element - the length is updated below.
+             *
+             * We assume the length of signatureValue is always going to be less than 128. This
+             * assumption works since we know ecdsaWithSHA256 signatures are always 69, 70, or
+             * 71 bytes long when DER encoded.
+             */
+            byte[] bitStringForSignature = new byte[]{0x03, 0x00, 0x00};
+
+            // Calculate sequence length and set it in |sequence|.
+            int sequenceLength = tbsCertificate.length
+                    + signatureAlgorithm.length
+                    + bitStringForSignature.length
+                    + signatureValue.length;
+            if (sequenceLength < 128 || sequenceLength > 65535) {
+                throw new Exception("Unexpected sequenceLength " + sequenceLength);
+            }
+            sequence[2] = (byte) (sequenceLength >> 8);
+            sequence[3] = (byte) (sequenceLength & 0xff);
+
+            // Calculate signatureValue length and set it in |bitStringForSignature|.
+            int signatureValueLength = signatureValue.length + 1;
+            if (signatureValueLength >= 128) {
+                throw new Exception("Unexpected signatureValueLength " + signatureValueLength);
+            }
+            bitStringForSignature[1] = (byte) signatureValueLength;
+
+            // Finally concatenate everything together.
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            baos.write(sequence);
+            baos.write(tbsCertificate);
+            baos.write(signatureAlgorithm);
+            baos.write(bitStringForSignature);
+            baos.write(signatureValue);
+            byte[] resultingCertBytes = baos.toByteArray();
+
+            CertificateFactory cf = CertificateFactory.getInstance("X.509");
+            ByteArrayInputStream bais = new ByteArrayInputStream(resultingCertBytes);
+            X509Certificate result = (X509Certificate) cf.generateCertificate(bais);
+            return result;
+        } catch (Exception e) {
+            throw new RuntimeException("Error signing public key with private key", e);
+        }
+    }
+
+    public static byte[] buildDeviceAuthenticationCbor(String docType,
+            byte[] encodedSessionTranscript,
+            byte[] deviceNameSpacesBytes) {
+        ByteArrayOutputStream daBaos = new ByteArrayOutputStream();
+        try {
+            ByteArrayInputStream bais = new ByteArrayInputStream(encodedSessionTranscript);
+            List<DataItem> dataItems = null;
+            dataItems = new CborDecoder(bais).decode();
+            DataItem sessionTranscript = dataItems.get(0);
+            ByteString deviceNameSpacesBytesItem = new ByteString(deviceNameSpacesBytes);
+            deviceNameSpacesBytesItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+            new CborEncoder(daBaos).encode(new CborBuilder()
+                    .addArray()
+                    .add("DeviceAuthentication")
+                    .add(sessionTranscript)
+                    .add(docType)
+                    .add(deviceNameSpacesBytesItem)
+                    .end()
+                    .build());
+        } catch (CborException e) {
+            throw new RuntimeException("Error encoding DeviceAuthentication", e);
+        }
+        return daBaos.toByteArray();
+    }
+
+    public static byte[] buildReaderAuthenticationBytesCbor(
+            byte[] encodedSessionTranscript,
+            byte[] requestMessageBytes) {
+
+        ByteArrayOutputStream daBaos = new ByteArrayOutputStream();
+        try {
+            ByteArrayInputStream bais = new ByteArrayInputStream(encodedSessionTranscript);
+            List<DataItem> dataItems = null;
+            dataItems = new CborDecoder(bais).decode();
+            DataItem sessionTranscript = dataItems.get(0);
+            ByteString requestMessageBytesItem = new ByteString(requestMessageBytes);
+            requestMessageBytesItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+            new CborEncoder(daBaos).encode(new CborBuilder()
+                    .addArray()
+                    .add("ReaderAuthentication")
+                    .add(sessionTranscript)
+                    .add(requestMessageBytesItem)
+                    .end()
+                    .build());
+        } catch (CborException e) {
+            throw new RuntimeException("Error encoding ReaderAuthentication", e);
+        }
+        byte[] readerAuthentication = daBaos.toByteArray();
+        return Util.prependSemanticTagForEncodedCbor(readerAuthentication);
+    }
+
+    // Returns #6.24(bstr) of the given already encoded CBOR
+    //
+    public static @NonNull DataItem buildCborTaggedByteString(@NonNull byte[] encodedCbor) {
+        DataItem item = new ByteString(encodedCbor);
+        item.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+        return item;
+    }
+
+    public static byte[] prependSemanticTagForEncodedCbor(byte[] encodedCbor) {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            new CborEncoder(baos).encode(buildCborTaggedByteString(encodedCbor));
+        } catch (CborException e) {
+            throw new RuntimeException("Error encoding with semantic tag for CBOR encoding", e);
+        }
+        return baos.toByteArray();
+    }
+
+    public static byte[] concatArrays(byte[] a, byte[] b) {
+        byte[] ret = new byte[a.length + b.length];
+        System.arraycopy(a, 0, ret, 0, a.length);
+        System.arraycopy(b, 0, ret, a.length, b.length);
+        return ret;
+    }
+
+    public static SecretKey calcEMacKeyForReader(PublicKey authenticationPublicKey,
+            PrivateKey ephemeralReaderPrivateKey,
+            byte[] encodedSessionTranscript) {
+        try {
+            KeyAgreement ka = KeyAgreement.getInstance("ECDH");
+            ka.init(ephemeralReaderPrivateKey);
+            ka.doPhase(authenticationPublicKey, true);
+            byte[] sharedSecret = ka.generateSecret();
+
+            byte[] sessionTranscriptBytes =
+                    Util.prependSemanticTagForEncodedCbor(encodedSessionTranscript);
+
+            byte[] salt = MessageDigest.getInstance("SHA-256").digest(sessionTranscriptBytes);
+            byte[] info = new byte[] {'E', 'M', 'a', 'c', 'K', 'e', 'y'};
+            byte[] derivedKey = Util.computeHkdf("HmacSha256", sharedSecret, salt, info, 32);
+            SecretKey secretKey = new SecretKeySpec(derivedKey, "");
+            return secretKey;
+        } catch (InvalidKeyException
+                | NoSuchAlgorithmException e) {
+            throw new RuntimeException("Error performing key agreement", e);
+        }
+    }
+
+    /**
+     * Computes an HKDF.
+     *
+     * This is based on https://github.com/google/tink/blob/master/java/src/main/java/com/google
+     * /crypto/tink/subtle/Hkdf.java
+     * which is also Copyright (c) Google and also licensed under the Apache 2 license.
+     *
+     * @param macAlgorithm the MAC algorithm used for computing the Hkdf. I.e., "HMACSHA1" or
+     *                     "HMACSHA256".
+     * @param ikm          the input keying material.
+     * @param salt         optional salt. A possibly non-secret random value. If no salt is
+     *                     provided (i.e. if
+     *                     salt has length 0) then an array of 0s of the same size as the hash
+     *                     digest is used as salt.
+     * @param info         optional context and application specific information.
+     * @param size         The length of the generated pseudorandom string in bytes. The maximal
+     *                     size is
+     *                     255.DigestSize, where DigestSize is the size of the underlying HMAC.
+     * @return size pseudorandom bytes.
+     */
+    public static byte[] computeHkdf(
+            String macAlgorithm, final byte[] ikm, final byte[] salt, final byte[] info, int size) {
+        Mac mac = null;
+        try {
+            mac = Mac.getInstance(macAlgorithm);
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("No such algorithm: " + macAlgorithm, e);
+        }
+        if (size > 255 * mac.getMacLength()) {
+            throw new RuntimeException("size too large");
+        }
+        try {
+            if (salt == null || salt.length == 0) {
+                // According to RFC 5869, Section 2.2 the salt is optional. If no salt is provided
+                // then HKDF uses a salt that is an array of zeros of the same length as the hash
+                // digest.
+                mac.init(new SecretKeySpec(new byte[mac.getMacLength()], macAlgorithm));
+            } else {
+                mac.init(new SecretKeySpec(salt, macAlgorithm));
+            }
+            byte[] prk = mac.doFinal(ikm);
+            byte[] result = new byte[size];
+            int ctr = 1;
+            int pos = 0;
+            mac.init(new SecretKeySpec(prk, macAlgorithm));
+            byte[] digest = new byte[0];
+            while (true) {
+                mac.update(digest);
+                mac.update(info);
+                mac.update((byte) ctr);
+                digest = mac.doFinal();
+                if (pos + digest.length < size) {
+                    System.arraycopy(digest, 0, result, pos, digest.length);
+                    pos += digest.length;
+                    ctr++;
+                } else {
+                    System.arraycopy(digest, 0, result, pos, size - pos);
+                    break;
+                }
+            }
+            return result;
+        } catch (InvalidKeyException e) {
+            throw new RuntimeException("Error MACing", e);
+        }
+    }
+
+    static byte[] stripLeadingZeroes(byte[] value) {
+        int n = 0;
+        while (n < value.length && value[n] == 0) {
+            n++;
+        }
+        int newLen = value.length - n;
+        byte[] ret = new byte[newLen];
+        int m = 0;
+        while (n < value.length) {
+            ret[m++] = value[n++];
+        }
+        return ret;
+    }
+
+    public static void hexdump(String name, byte[] data) {
+        int n, m, o;
+        StringBuilder sb = new StringBuilder();
+        Formatter fmt = new Formatter(sb);
+        for (n = 0; n < data.length; n += 16) {
+            fmt.format("%04x  ", n);
+            for (m = 0; m < 16 && n + m < data.length; m++) {
+                fmt.format("%02x ", data[n + m]);
+            }
+            for (o = m; o < 16; o++) {
+                sb.append("   ");
+            }
+            sb.append(" ");
+            for (m = 0; m < 16 && n + m < data.length; m++) {
+                int c = data[n + m] & 0xff;
+                fmt.format("%c", Character.isISOControl(c) ? '.' : c);
+            }
+            sb.append("\n");
+        }
+        sb.append("\n");
+        Log.e(TAG, name + ": dumping " + data.length + " bytes\n" + fmt.toString());
+    }
+
+
+    // This returns a SessionTranscript which satisfy the requirement
+    // that the uncompressed X and Y coordinates of the public key for the
+    // mDL's ephemeral key-pair appear somewhere in the encoded
+    // DeviceEngagement.
+    public static byte[] buildSessionTranscript(KeyPair ephemeralKeyPair) {
+        // Make the coordinates appear in an already encoded bstr - this
+        // mimics how the mDL COSE_Key appear as encoded data inside the
+        // encoded DeviceEngagement
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
+            // X and Y are always positive so for interop we remove any leading zeroes
+            // inserted by the BigInteger encoder.
+            byte[] x = stripLeadingZeroes(w.getAffineX().toByteArray());
+            byte[] y = stripLeadingZeroes(w.getAffineY().toByteArray());
+            baos.write(new byte[]{42});
+            baos.write(x);
+            baos.write(y);
+            baos.write(new byte[]{43, 44});
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+        byte[] blobWithCoords = baos.toByteArray();
+
+        baos = new ByteArrayOutputStream();
+        try {
+            new CborEncoder(baos).encode(new CborBuilder()
+                    .addArray()
+                    .add(blobWithCoords)
+                    .end()
+                    .build());
+        } catch (CborException e) {
+            e.printStackTrace();
+            return null;
+        }
+        ByteString encodedDeviceEngagementItem = new ByteString(baos.toByteArray());
+        ByteString encodedEReaderKeyItem = new ByteString(cborEncodeString("doesn't matter"));
+        encodedDeviceEngagementItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+        encodedEReaderKeyItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+
+        baos = new ByteArrayOutputStream();
+        try {
+            new CborEncoder(baos).encode(new CborBuilder()
+                    .addArray()
+                    .add(encodedDeviceEngagementItem)
+                    .add(encodedEReaderKeyItem)
+                    .end()
+                    .build());
+        } catch (CborException e) {
+            e.printStackTrace();
+            return null;
+        }
+        return baos.toByteArray();
+    }
+
+    /*
+     * Helper function to create a CBOR data for requesting data items. The IntentToRetain
+     * value will be set to false for all elements.
+     *
+     * <p>The returned CBOR data conforms to the following CDDL schema:</p>
+     *
+     * <pre>
+     *   ItemsRequest = {
+     *     ? "docType" : DocType,
+     *     "nameSpaces" : NameSpaces,
+     *     ? "RequestInfo" : {* tstr => any} ; Additional info the reader wants to provide
+     *   }
+     *
+     *   NameSpaces = {
+     *     + NameSpace => DataElements     ; Requested data elements for each NameSpace
+     *   }
+     *
+     *   DataElements = {
+     *     + DataElement => IntentToRetain
+     *   }
+     *
+     *   DocType = tstr
+     *
+     *   DataElement = tstr
+     *   IntentToRetain = bool
+     *   NameSpace = tstr
+     * </pre>
+     *
+     * @param entriesToRequest       The entries to request, organized as a map of namespace
+     *                               names with each value being a collection of data elements
+     *                               in the given namespace.
+     * @param docType                  The document type or {@code null} if there is no document
+     *                                 type.
+     * @return CBOR data conforming to the CDDL mentioned above.
+     */
+    public static @NonNull byte[] createItemsRequest(
+            @NonNull Map<String, Collection<String>> entriesToRequest,
+            @Nullable String docType) {
+        CborBuilder builder = new CborBuilder();
+        MapBuilder<CborBuilder> mapBuilder = builder.addMap();
+        if (docType != null) {
+            mapBuilder.put("docType", docType);
+        }
+
+        MapBuilder<MapBuilder<CborBuilder>> nsMapBuilder = mapBuilder.putMap("nameSpaces");
+        for (String namespaceName : entriesToRequest.keySet()) {
+            Collection<String> entryNames = entriesToRequest.get(namespaceName);
+            MapBuilder<MapBuilder<MapBuilder<CborBuilder>>> entryNameMapBuilder =
+                    nsMapBuilder.putMap(namespaceName);
+            for (String entryName : entryNames) {
+                entryNameMapBuilder.put(entryName, false);
+            }
+        }
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        CborEncoder encoder = new CborEncoder(baos);
+        try {
+            encoder.encode(builder.build());
+        } catch (CborException e) {
+            throw new RuntimeException("Error encoding CBOR", e);
+        }
+        return baos.toByteArray();
+    }
+
+    public static KeyPair createEphemeralKeyPair() {
+        try {
+            KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC);
+            ECGenParameterSpec ecSpec = new ECGenParameterSpec("prime256v1");
+            kpg.initialize(ecSpec);
+            KeyPair keyPair = kpg.generateKeyPair();
+            return keyPair;
+        } catch (NoSuchAlgorithmException
+                | InvalidAlgorithmParameterException e) {
+            throw new RuntimeException("Error generating ephemeral key-pair", e);
+        }
+    }
+
+    public static byte[] getPopSha256FromAuthKeyCert(X509Certificate cert) {
+        byte[] octetString = cert.getExtensionValue("1.3.6.1.4.1.11129.2.1.26");
+        if (octetString == null) {
+            return null;
+        }
+        Util.hexdump("octetString", octetString);
+
+        try {
+            ASN1InputStream asn1InputStream = new ASN1InputStream(octetString);
+            byte[] cborBytes = ((ASN1OctetString) asn1InputStream.readObject()).getOctets();
+            Util.hexdump("cborBytes", cborBytes);
+
+            ByteArrayInputStream bais = new ByteArrayInputStream(cborBytes);
+            List<DataItem> dataItems = new CborDecoder(bais).decode();
+            if (dataItems.size() != 1) {
+                throw new RuntimeException("Expected 1 item, found " + dataItems.size());
+            }
+            if (!(dataItems.get(0) instanceof co.nstant.in.cbor.model.Array)) {
+                throw new RuntimeException("Item is not a map");
+            }
+            co.nstant.in.cbor.model.Array array = (co.nstant.in.cbor.model.Array) dataItems.get(0);
+            List<DataItem> items = array.getDataItems();
+            if (items.size() < 2) {
+                throw new RuntimeException(
+                        "Expected at least 2 array items, found " + items.size());
+            }
+            if (!(items.get(0) instanceof UnicodeString)) {
+                throw new RuntimeException("First array item is not a string");
+            }
+            String id = ((UnicodeString) items.get(0)).getString();
+            if (!id.equals("ProofOfBinding")) {
+                throw new RuntimeException("Expected ProofOfBinding, got " + id);
+            }
+            if (!(items.get(1) instanceof ByteString)) {
+                throw new RuntimeException("Second array item is not a bytestring");
+            }
+            byte[] popSha256 = ((ByteString) items.get(1)).getBytes();
+            if (popSha256.length != 32) {
+                throw new RuntimeException(
+                        "Expected bstr to be 32 bytes, it is " + popSha256.length);
+            }
+            return popSha256;
+        } catch (IOException e) {
+            throw new RuntimeException("Error decoding extension data", e);
+        } catch (CborException e) {
+            throw new RuntimeException("Error decoding data", e);
+        }
+    }
+
+}
diff --git a/identity/util/test/java/com/android/security/identity/internal/HkdfTest.java b/identity/util/test/java/com/android/security/identity/internal/HkdfTest.java
new file mode 100644
index 0000000..6a75090
--- /dev/null
+++ b/identity/util/test/java/com/android/security/identity/internal/HkdfTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.security.identity.internal;
+
+import androidx.test.runner.AndroidJUnit4;
+import com.android.security.identity.internal.Util;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.security.GeneralSecurityException;
+import java.util.Random;
+
+/*
+ * This is based on https://github.com/google/tink/blob/master/java/src/test/java/com/google
+ * /crypto/tink/subtle/HkdfTest.java
+ * which is also Copyright (c) Google and licensed under the Apache 2 license.
+ */
+@RunWith(AndroidJUnit4.class)
+public class HkdfTest {
+
+    static Random sRandom = new Random();
+
+    /** Encodes a byte array to hex. */
+    static String hexEncode(final byte[] bytes) {
+        String chars = "0123456789abcdef";
+        StringBuilder result = new StringBuilder(2 * bytes.length);
+        for (byte b : bytes) {
+            // convert to unsigned
+            int val = b & 0xff;
+            result.append(chars.charAt(val / 16));
+            result.append(chars.charAt(val % 16));
+        }
+        return result.toString();
+    }
+
+    /** Decodes a hex string to a byte array. */
+    static byte[] hexDecode(String hex) {
+        if (hex.length() % 2 != 0) {
+            throw new IllegalArgumentException("Expected a string of even length");
+        }
+        int size = hex.length() / 2;
+        byte[] result = new byte[size];
+        for (int i = 0; i < size; i++) {
+            int hi = Character.digit(hex.charAt(2 * i), 16);
+            int lo = Character.digit(hex.charAt(2 * i + 1), 16);
+            if ((hi == -1) || (lo == -1)) {
+                throw new IllegalArgumentException("input is not hexadecimal");
+            }
+            result[i] = (byte) (16 * hi + lo);
+        }
+        return result;
+    }
+
+    static byte[] randBytes(int numBytes) {
+        byte[] bytes = new byte[numBytes];
+        sRandom.nextBytes(bytes);
+        return bytes;
+    }
+
+    @Test
+    public void testNullSaltOrInfo() throws Exception {
+        byte[] ikm = randBytes(20);
+        byte[] info = randBytes(20);
+        int size = 40;
+
+        byte[] hkdfWithNullSalt = Util.computeHkdf("HmacSha256", ikm, null, info, size);
+        byte[] hkdfWithEmptySalt = Util.computeHkdf("HmacSha256", ikm, new byte[0], info, size);
+        assertArrayEquals(hkdfWithNullSalt, hkdfWithEmptySalt);
+
+        byte[] salt = randBytes(20);
+        byte[] hkdfWithNullInfo = Util.computeHkdf("HmacSha256", ikm, salt, null, size);
+        byte[] hkdfWithEmptyInfo = Util.computeHkdf("HmacSha256", ikm, salt, new byte[0], size);
+        assertArrayEquals(hkdfWithNullInfo, hkdfWithEmptyInfo);
+    }
+
+    @Test
+    public void testInvalidCodeSize() throws Exception {
+        try {
+            Util.computeHkdf("HmacSha256", new byte[0], new byte[0], new byte[0], 32 * 256);
+            fail("Invalid size, should have thrown exception");
+        } catch (RuntimeException expected) {
+
+            // Expected
+        }
+    }
+
+    /**
+     * Tests the implementation against the test vectors from RFC 5869.
+     */
+    @Test
+    public void testVectors() throws Exception {
+        // Test case 1
+        assertEquals(
+                "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf"
+                + "1a5a4c5db02d56ecc4c5bf34007208d5b887185865",
+                computeHkdfHex("HmacSha256",
+                        "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+                        "000102030405060708090a0b0c",
+                        "f0f1f2f3f4f5f6f7f8f9",
+                        42));
+
+        // Test case 2
+        assertEquals(
+                "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c"
+                        + "59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71"
+                        + "cc30c58179ec3e87c14c01d5c1f3434f1d87",
+                computeHkdfHex("HmacSha256",
+                        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
+                                + "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
+                                + "404142434445464748494a4b4c4d4e4f",
+                        "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f"
+                                + "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"
+                                + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
+                        "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
+                                + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef"
+                                + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+                        82));
+
+        // Test case 3: salt is empty
+        assertEquals(
+                "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d"
+                        + "9d201395faa4b61a96c8",
+                computeHkdfHex("HmacSha256",
+                        "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "", "",
+                        42));
+
+        // Test Case 4
+        assertEquals(
+                "085a01ea1b10f36933068b56efa5ad81a4f14b822f"
+                + "5b091568a9cdd4f155fda2c22e422478d305f3f896",
+                computeHkdfHex(
+                        "HmacSha1",
+                        "0b0b0b0b0b0b0b0b0b0b0b",
+                        "000102030405060708090a0b0c",
+                        "f0f1f2f3f4f5f6f7f8f9",
+                        42));
+
+        // Test Case 5
+        assertEquals(
+                "0bd770a74d1160f7c9f12cd5912a06ebff6adcae899d92191fe4305673ba2ffe"
+                        + "8fa3f1a4e5ad79f3f334b3b202b2173c486ea37ce3d397ed034c7f9dfeb15c5e"
+                        + "927336d0441f4c4300e2cff0d0900b52d3b4",
+                computeHkdfHex(
+                        "HmacSha1",
+                        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
+                                + "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
+                                + "404142434445464748494a4b4c4d4e4f",
+                        "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f"
+                                + "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"
+                                + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
+                        "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
+                                + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef"
+                                + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+                        82));
+
+        // Test Case 6: salt is empty
+        assertEquals(
+                "0ac1af7002b3d761d1e55298da9d0506b9ae52057220a306e07b6b87e8df21d0"
+                        + "ea00033de03984d34918",
+                computeHkdfHex("HmacSha1", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "", "",
+                        42));
+
+        // Test Case 7
+        assertEquals(
+                "2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5"
+                        + "673a081d70cce7acfc48",
+                computeHkdfHex("HmacSha1", "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", "", "",
+                        42));
+    }
+
+    /**
+     * Test version of Hkdf where all inputs and outputs are hexadecimal.
+     */
+    private String computeHkdfHex(String macAlgorithm, String ikmHex, String saltHex,
+            String infoHex,
+            int size) throws GeneralSecurityException {
+        return hexEncode(
+                Util.computeHkdf(macAlgorithm, hexDecode(ikmHex), hexDecode(saltHex),
+                        hexDecode(infoHex), size));
+    }
+
+}
diff --git a/identity/util/test/java/com/android/security/identity/internal/UtilUnitTests.java b/identity/util/test/java/com/android/security/identity/internal/UtilUnitTests.java
new file mode 100644
index 0000000..9c27c14
--- /dev/null
+++ b/identity/util/test/java/com/android/security/identity/internal/UtilUnitTests.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.security.identity.internal;
+
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import com.android.security.identity.internal.Util;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import java.security.cert.X509Certificate;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import co.nstant.in.cbor.CborBuilder;
+import co.nstant.in.cbor.CborDecoder;
+import co.nstant.in.cbor.CborEncoder;
+import co.nstant.in.cbor.CborException;
+import co.nstant.in.cbor.builder.ArrayBuilder;
+import co.nstant.in.cbor.model.ByteString;
+import co.nstant.in.cbor.model.DataItem;
+import co.nstant.in.cbor.model.DoublePrecisionFloat;
+import co.nstant.in.cbor.model.HalfPrecisionFloat;
+import co.nstant.in.cbor.model.NegativeInteger;
+import co.nstant.in.cbor.model.SimpleValue;
+import co.nstant.in.cbor.model.SimpleValueType;
+import co.nstant.in.cbor.model.SinglePrecisionFloat;
+import co.nstant.in.cbor.model.UnicodeString;
+import co.nstant.in.cbor.model.UnsignedInteger;
+
+@RunWith(AndroidJUnit4.class)
+public class UtilUnitTests {
+    @Test
+    public void prettyPrintMultipleCompleteTypes() throws CborException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new CborBuilder()
+                .add("text")                // add string
+                .add(1234)                  // add integer
+                .add(new byte[]{0x10})   // add byte array
+                .addArray()                 // add array
+                .add(1)
+                .add("text")
+                .end()
+                .build());
+        assertEquals("'text',\n"
+                + "1234,\n"
+                + "[0x10],\n"
+                + "[1, 'text']", Util.cborPrettyPrint(baos.toByteArray()));
+    }
+
+    @Test
+    public void prettyPrintString() throws CborException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new UnicodeString("foobar"));
+        assertEquals("'foobar'", Util.cborPrettyPrint(baos.toByteArray()));
+    }
+
+    @Test
+    public void prettyPrintBytestring() throws CborException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new ByteString(new byte[]{1, 2, 33, (byte) 254}));
+        assertEquals("[0x01, 0x02, 0x21, 0xfe]", Util.cborPrettyPrint(baos.toByteArray()));
+    }
+
+    @Test
+    public void prettyPrintUnsignedInteger() throws CborException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new UnsignedInteger(42));
+        assertEquals("42", Util.cborPrettyPrint(baos.toByteArray()));
+    }
+
+    @Test
+    public void prettyPrintNegativeInteger() throws CborException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new NegativeInteger(-42));
+        assertEquals("-42", Util.cborPrettyPrint(baos.toByteArray()));
+    }
+
+    @Test
+    public void prettyPrintDouble() throws CborException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new DoublePrecisionFloat(1.1));
+        assertEquals("1.1", Util.cborPrettyPrint(baos.toByteArray()));
+
+        baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new DoublePrecisionFloat(-42.0000000001));
+        assertEquals("-42.0000000001", Util.cborPrettyPrint(baos.toByteArray()));
+
+        baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new DoublePrecisionFloat(-5));
+        assertEquals("-5", Util.cborPrettyPrint(baos.toByteArray()));
+    }
+
+    @Test
+    public void prettyPrintFloat() throws CborException {
+        ByteArrayOutputStream baos;
+
+        // TODO: These two tests yield different results on different devices, disable for now
+        /*
+        baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new SinglePrecisionFloat(1.1f));
+        assertEquals("1.100000023841858", Util.cborPrettyPrint(baos.toByteArray()));
+
+        baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new SinglePrecisionFloat(-42.0001f));
+        assertEquals("-42.000099182128906", Util.cborPrettyPrint(baos.toByteArray()));
+        */
+
+        baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new SinglePrecisionFloat(-5f));
+        assertEquals("-5", Util.cborPrettyPrint(baos.toByteArray()));
+    }
+
+    @Test
+    public void prettyPrintHalfFloat() throws CborException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new HalfPrecisionFloat(1.1f));
+        assertEquals("1.099609375", Util.cborPrettyPrint(baos.toByteArray()));
+
+        baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new HalfPrecisionFloat(-42.0001f));
+        assertEquals("-42", Util.cborPrettyPrint(baos.toByteArray()));
+
+        baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new HalfPrecisionFloat(-5f));
+        assertEquals("-5", Util.cborPrettyPrint(baos.toByteArray()));
+    }
+
+    @Test
+    public void prettyPrintFalse() throws CborException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new SimpleValue(SimpleValueType.FALSE));
+        assertEquals("false", Util.cborPrettyPrint(baos.toByteArray()));
+    }
+
+    @Test
+    public void prettyPrintTrue() throws CborException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new SimpleValue(SimpleValueType.TRUE));
+        assertEquals("true", Util.cborPrettyPrint(baos.toByteArray()));
+    }
+
+    @Test
+    public void prettyPrintNull() throws CborException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new SimpleValue(SimpleValueType.NULL));
+        assertEquals("null", Util.cborPrettyPrint(baos.toByteArray()));
+    }
+
+    @Test
+    public void prettyPrintUndefined() throws CborException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new SimpleValue(SimpleValueType.UNDEFINED));
+        assertEquals("undefined", Util.cborPrettyPrint(baos.toByteArray()));
+    }
+
+    @Test
+    public void prettyPrintTag() throws CborException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new CborBuilder()
+                        .addTag(0)
+                        .add("ABC")
+                        .build());
+        byte[] data = baos.toByteArray();
+        assertEquals("tag 0 'ABC'", Util.cborPrettyPrint(data));
+    }
+
+    @Test
+    public void prettyPrintArrayNoCompounds() throws CborException {
+        // If an array has no compound elements, no newlines are used.
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new CborBuilder()
+                .addArray()                 // add array
+                .add(1)
+                .add("text")
+                .add(new ByteString(new byte[]{1, 2, 3}))
+                .end()
+                .build());
+        assertEquals("[1, 'text', [0x01, 0x02, 0x03]]", Util.cborPrettyPrint(baos.toByteArray()));
+    }
+
+    @Test
+    public void prettyPrintArray() throws CborException {
+        // This array contains a compound value so will use newlines
+        CborBuilder array = new CborBuilder();
+        ArrayBuilder<CborBuilder> arrayBuilder = array.addArray();
+        arrayBuilder.add(2);
+        arrayBuilder.add(3);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new CborBuilder()
+                .addArray()                 // add array
+                .add(1)
+                .add("text")
+                .add(new ByteString(new byte[]{1, 2, 3}))
+                .add(array.build().get(0))
+                .end()
+                .build());
+        assertEquals("[\n"
+                + "  1,\n"
+                + "  'text',\n"
+                + "  [0x01, 0x02, 0x03],\n"
+                + "  [2, 3]\n"
+                + "]", Util.cborPrettyPrint(baos.toByteArray()));
+    }
+
+    @Test
+    public void prettyPrintMap() throws CborException {
+        // If an array has no compound elements, no newlines are used.
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new CborEncoder(baos).encode(new CborBuilder()
+                .addMap()
+                .put("Foo", 42)
+                .put("Bar", "baz")
+                .put(43, 44)
+                .put(new UnicodeString("bstr"), new ByteString(new byte[]{1, 2, 3}))
+                .put(new ByteString(new byte[]{1, 2, 3}), new UnicodeString("other way"))
+                .end()
+                .build());
+        assertEquals("{\n"
+                + "  43 : 44,\n"
+                + "  [0x01, 0x02, 0x03] : 'other way',\n"
+                + "  'Bar' : 'baz',\n"
+                + "  'Foo' : 42,\n"
+                + "  'bstr' : [0x01, 0x02, 0x03]\n"
+                + "}", Util.cborPrettyPrint(baos.toByteArray()));
+    }
+
+    @Test
+    public void testCanonicalizeCbor() throws Exception {
+        // {"one":1, 2:"two"}
+        byte[] first =
+                new byte[]{(byte) 0xA2, 0x63, 0x6F, 0x6E, 0x65, 0x01, 0x02, 0x63, 0x74, 0x77, 0x6F};
+
+        // {2: "two", "one": 1}
+        byte[] second =
+                new byte[]{(byte) 0xA2, 0x02, 0x63, 0x74, 0x77, 0x6F, 0x63, 0x6F, 0x6E, 0x65, 0x01};
+
+        assertArrayEquals(Util.canonicalizeCbor(first), Util.canonicalizeCbor(second));
+    }
+
+    @Test
+    public void cborEncodeDecodeSingle() throws Exception {
+        List<DataItem> items = new CborBuilder()
+                .addMap().put(1,"one").put("one", 1).end()
+                .addArray().add(42).add(true).addMap().end().end()
+                .add("STRING")
+                .build();
+        for (DataItem item: items) {
+            assertEquals(item, Util.cborToDataItem(Util.cborEncode(item)));
+        }
+    }
+
+    @Test
+    public void cborEncodeDecodeBoolean() {
+        assertEquals(true, Util.cborDecodeBoolean(Util.cborEncodeBoolean(true)));
+        assertEquals(false, Util.cborDecodeBoolean(Util.cborEncodeBoolean(false)));
+    }
+
+    @Test
+    public void cborEncodeDecodeString() {
+        assertEquals("foo bar", Util.cborDecodeString(Util.cborEncodeString("foo bar")));
+    }
+
+    @Test
+    public void cborEncodeDecodeBytestring() {
+        byte[] bits = new byte[256];
+        for (int i = 0; i < bits.length; ++i) {
+            bits[i] = (byte)i;
+        }
+        assertArrayEquals(bits, Util.cborDecodeBytestring(Util.cborEncodeBytestring(bits)));
+    }
+
+    @Test
+    public void cborEncodeDecodeInt() {
+        assertEquals(0, Util.cborDecodeInt(Util.cborEncodeInt(0)));
+        assertEquals(Integer.MAX_VALUE, Util.cborDecodeInt(Util.cborEncodeInt(Integer.MAX_VALUE)));
+        assertEquals(Integer.MIN_VALUE, Util.cborDecodeInt(Util.cborEncodeInt(Integer.MIN_VALUE)));
+    }
+
+    @Test
+    public void prependSemanticTagForEncodedCbor() throws Exception {
+        byte[] inputBytes = new byte[] {1, 2, 3, 4};
+        byte[] encodedInput = Util.cborEncodeBytestring(inputBytes);
+        byte[] encodedWithTag = Util.prependSemanticTagForEncodedCbor(encodedInput);
+
+        ByteString decodedWithTag = (ByteString)Util.cborToDataItem(encodedWithTag);
+        assertEquals(decodedWithTag.getTag().getValue(), 24);  // RFC 8949 defines 24
+
+        byte[] decodedBytes = Util.cborDecodeBytestring(decodedWithTag.getBytes());
+        assertArrayEquals(inputBytes, decodedBytes);
+    }
+
+    private KeyPair coseGenerateKeyPair() throws Exception {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance(
+            KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
+        KeyGenParameterSpec.Builder builder =
+                new KeyGenParameterSpec.Builder(
+                    "coseTestKeyPair",
+                    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                        .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512);
+        kpg.initialize(builder.build());
+        return kpg.generateKeyPair();
+    }
+
+    @Test
+    public void coseSignAndVerify() throws Exception {
+        KeyPair keyPair = coseGenerateKeyPair();
+        byte[] data = new byte[] {0x10, 0x11, 0x12, 0x13};
+        byte[] detachedContent = new byte[] {};
+        byte[] sig = Util.coseSign1Sign(keyPair.getPrivate(), data, detachedContent, null);
+        assertTrue(Util.coseSign1CheckSignature(sig, detachedContent, keyPair.getPublic()));
+        assertArrayEquals(data, Util.coseSign1GetData(sig));
+        assertEquals(new ArrayList() {}, Util.coseSign1GetX5Chain(sig));
+    }
+
+    @Test
+    public void coseSignAndVerifyDetachedContent() throws Exception {
+        KeyPair keyPair = coseGenerateKeyPair();
+        byte[] data = new byte[] {};
+        byte[] detachedContent = new byte[] {0x20, 0x21, 0x22, 0x23, 0x24};
+        byte[] sig = Util.coseSign1Sign(keyPair.getPrivate(), data, detachedContent, null);
+        assertTrue(Util.coseSign1CheckSignature(sig, detachedContent, keyPair.getPublic()));
+        assertArrayEquals(data, Util.coseSign1GetData(sig));
+        assertEquals(new ArrayList() {}, Util.coseSign1GetX5Chain(sig));
+    }
+
+    @Test
+    public void coseSignAndVerifySingleCertificate() throws Exception {
+        KeyPair keyPair = coseGenerateKeyPair();
+        byte[] data = new byte[] {};
+        byte[] detachedContent = new byte[] {0x20, 0x21, 0x22, 0x23, 0x24};
+        ArrayList<X509Certificate> certs = new ArrayList() {};
+        certs.add(Util.signPublicKeyWithPrivateKey("coseTestKeyPair", "coseTestKeyPair"));
+        byte[] sig = Util.coseSign1Sign(keyPair.getPrivate(), data, detachedContent, certs);
+        assertTrue(Util.coseSign1CheckSignature(sig, detachedContent, keyPair.getPublic()));
+        assertArrayEquals(data, Util.coseSign1GetData(sig));
+        assertEquals(certs, Util.coseSign1GetX5Chain(sig));
+    }
+
+    @Test
+    public void coseSignAndVerifyMultipleCertificates() throws Exception {
+        KeyPair keyPair = coseGenerateKeyPair();
+        byte[] data = new byte[] {};
+        byte[] detachedContent = new byte[] {0x20, 0x21, 0x22, 0x23, 0x24};
+        ArrayList<X509Certificate> certs = new ArrayList() {};
+        certs.add(Util.signPublicKeyWithPrivateKey("coseTestKeyPair", "coseTestKeyPair"));
+        certs.add(Util.signPublicKeyWithPrivateKey("coseTestKeyPair", "coseTestKeyPair"));
+        certs.add(Util.signPublicKeyWithPrivateKey("coseTestKeyPair", "coseTestKeyPair"));
+        byte[] sig = Util.coseSign1Sign(keyPair.getPrivate(), data, detachedContent, certs);
+        assertTrue(Util.coseSign1CheckSignature(sig, detachedContent, keyPair.getPublic()));
+        assertArrayEquals(data, Util.coseSign1GetData(sig));
+        assertEquals(certs, Util.coseSign1GetX5Chain(sig));
+    }
+
+    @Test
+    public void coseMac0() throws Exception {
+        SecretKey secretKey = new SecretKeySpec(new byte[32], "");
+        byte[] data = new byte[] {0x10, 0x11, 0x12, 0x13};
+        byte[] detachedContent = new byte[] {};
+        byte[] mac = Util.coseMac0(secretKey, data, detachedContent);
+        assertEquals("[\n"
+                + "  [0xa1, 0x01, 0x05],\n"
+                + "  {},\n"
+                + "  [0x10, 0x11, 0x12, 0x13],\n"
+                + "  [0x6c, 0xec, 0xb5, 0x6a, 0xc9, 0x5c, 0xae, 0x3b, 0x41, 0x13, 0xde, 0xa4, "
+                + "0xd8, 0x86, 0x5c, 0x28, 0x2c, 0xd5, 0xa5, 0x13, 0xff, 0x3b, 0xd1, 0xde, 0x70, "
+                + "0x5e, 0xbb, 0xe2, 0x2d, 0x42, 0xbe, 0x53]\n"
+                + "]", Util.cborPrettyPrint(mac));
+    }
+
+    @Test
+    public void coseMac0DetachedContent() throws Exception {
+        SecretKey secretKey = new SecretKeySpec(new byte[32], "");
+        byte[] data = new byte[] {};
+        byte[] detachedContent = new byte[] {0x10, 0x11, 0x12, 0x13};
+        byte[] mac = Util.coseMac0(secretKey, data, detachedContent);
+        // Same HMAC as in coseMac0 test, only difference is that payload is null.
+        assertEquals("[\n"
+                + "  [0xa1, 0x01, 0x05],\n"
+                + "  {},\n"
+                + "  null,\n"
+                + "  [0x6c, 0xec, 0xb5, 0x6a, 0xc9, 0x5c, 0xae, 0x3b, 0x41, 0x13, 0xde, 0xa4, "
+                + "0xd8, 0x86, 0x5c, 0x28, 0x2c, 0xd5, 0xa5, 0x13, 0xff, 0x3b, 0xd1, 0xde, 0x70, "
+                + "0x5e, 0xbb, 0xe2, 0x2d, 0x42, 0xbe, 0x53]\n"
+                + "]", Util.cborPrettyPrint(mac));
+    }
+
+    @Test
+    public void replaceLineTest() {
+        assertEquals("foo",
+                Util.replaceLine("Hello World", 0, "foo"));
+        assertEquals("foo\n",
+                Util.replaceLine("Hello World\n", 0, "foo"));
+        assertEquals("Hello World",
+                Util.replaceLine("Hello World", 1, "foo"));
+        assertEquals("Hello World\n",
+                Util.replaceLine("Hello World\n", 1, "foo"));
+        assertEquals("foo\ntwo\nthree",
+                Util.replaceLine("one\ntwo\nthree", 0, "foo"));
+        assertEquals("one\nfoo\nthree",
+                Util.replaceLine("one\ntwo\nthree", 1, "foo"));
+        assertEquals("one\ntwo\nfoo",
+                Util.replaceLine("one\ntwo\nthree", 2, "foo"));
+        assertEquals("one\ntwo\nfoo",
+                Util.replaceLine("one\ntwo\nthree", -1, "foo"));
+        assertEquals("one\ntwo\nthree\nfoo",
+                Util.replaceLine("one\ntwo\nthree\nfour", -1, "foo"));
+        assertEquals("one\ntwo\nfoo\nfour",
+                Util.replaceLine("one\ntwo\nthree\nfour", -2, "foo"));
+    }
+
+}
diff --git a/keystore-engine/Android.bp b/keystore-engine/Android.bp
index 0cecfd8..cb75cde 100644
--- a/keystore-engine/Android.bp
+++ b/keystore-engine/Android.bp
@@ -36,7 +36,7 @@
     ],
 
     shared_libs: [
-        "android.system.keystore2-V1-ndk_platform",
+        "android.system.keystore2-V1-ndk",
         "libbinder_ndk",
         "libcrypto",
         "libcutils",
@@ -66,7 +66,7 @@
     ],
 
     shared_libs: [
-        "android.system.keystore2-V1-ndk_platform",
+        "android.system.keystore2-V1-ndk",
         "libbase",
         "libbinder_ndk",
         "libcrypto",
diff --git a/keystore-engine/keystore2_engine.cpp b/keystore-engine/keystore2_engine.cpp
index 49c5e9a..69caf51 100644
--- a/keystore-engine/keystore2_engine.cpp
+++ b/keystore-engine/keystore2_engine.cpp
@@ -384,7 +384,7 @@
 
     // If the key_id starts with the grant id prefix, we parse the following string as numeric
     // grant id. We can then use the grant domain without alias to load the designated key.
-    if (alias.find(keystore2_grant_id_prefix) == 0) {
+    if (android::base::StartsWith(alias, keystore2_grant_id_prefix)) {
         std::stringstream s(alias.substr(keystore2_grant_id_prefix.size()));
         s >> std::hex >> reinterpret_cast<uint64_t&>(descriptor.nspace);
         descriptor.domain = ks2::Domain::GRANT;
diff --git a/keystore/Android.bp b/keystore/Android.bp
index 0f2000c..892c5b4 100644
--- a/keystore/Android.bp
+++ b/keystore/Android.bp
@@ -36,7 +36,10 @@
 
 cc_binary {
     name: "keystore_cli_v2",
-    defaults: ["keystore_defaults"],
+    defaults: [
+        "keystore_defaults",
+        "keystore2_use_latest_aidl_ndk_shared",
+    ],
 
     cflags: [
         "-DKEYMASTER_NAME_TAGS",
@@ -47,8 +50,7 @@
         "keystore_client.proto",
     ],
     shared_libs: [
-        "android.security.apc-ndk_platform",
-        "android.system.keystore2-V1-ndk_platform",
+        "android.security.apc-ndk",
         "libbinder",
         "libbinder_ndk",
         "libchrome",
@@ -63,7 +65,7 @@
 
 // Library used by both keystore and credstore for generating the ASN.1 stored
 // in Tag::ATTESTATION_APPLICATION_ID
-cc_library_shared {
+cc_library {
     name: "libkeystore-attestation-application-id",
     defaults: ["keystore_defaults"],
 
@@ -87,7 +89,7 @@
 }
 
 // Library for keystore clients using the WiFi HIDL interface
-cc_library_shared {
+cc_library {
     name: "libkeystore-wifi-hidl",
     defaults: ["keystore_defaults"],
 
diff --git a/keystore/keystore_cli_v2.cpp b/keystore/keystore_cli_v2.cpp
index 43f72a9..1e9126d 100644
--- a/keystore/keystore_cli_v2.cpp
+++ b/keystore/keystore_cli_v2.cpp
@@ -19,6 +19,7 @@
 #include <iostream>
 #include <memory>
 #include <string>
+#include <variant>
 #include <vector>
 
 #include <base/command_line.h>
@@ -616,9 +617,9 @@
     return std::move(parameters);
 }
 
-keymint::AuthorizationSet GetECDSAParameters(uint32_t key_size, bool sha256_only) {
+keymint::AuthorizationSet GetECDSAParameters(keymint::EcCurve curve, bool sha256_only) {
     keymint::AuthorizationSetBuilder parameters;
-    parameters.EcdsaSigningKey(key_size)
+    parameters.EcdsaSigningKey(curve)
         .Digest(keymint::Digest::SHA_2_256)
         .Authorization(keymint::TAG_NO_AUTH_REQUIRED);
     if (!sha256_only) {
@@ -662,11 +663,12 @@
         {"RSA-2048 Encrypt", true, GetRSAEncryptParameters(2048)},
         {"RSA-3072 Encrypt", false, GetRSAEncryptParameters(3072)},
         {"RSA-4096 Encrypt", false, GetRSAEncryptParameters(4096)},
-        {"ECDSA-P256 Sign", true, GetECDSAParameters(256, true)},
-        {"ECDSA-P256 Sign (more digests)", false, GetECDSAParameters(256, false)},
-        {"ECDSA-P224 Sign", false, GetECDSAParameters(224, false)},
-        {"ECDSA-P384 Sign", false, GetECDSAParameters(384, false)},
-        {"ECDSA-P521 Sign", false, GetECDSAParameters(521, false)},
+        {"ECDSA-P256 Sign", true, GetECDSAParameters(keymint::EcCurve::P_256, true)},
+        {"ECDSA-P256 Sign (more digests)", false,
+         GetECDSAParameters(keymint::EcCurve::P_256, false)},
+        {"ECDSA-P224 Sign", false, GetECDSAParameters(keymint::EcCurve::P_224, false)},
+        {"ECDSA-P384 Sign", false, GetECDSAParameters(keymint::EcCurve::P_384, false)},
+        {"ECDSA-P521 Sign", false, GetECDSAParameters(keymint::EcCurve::P_521, false)},
         {"AES-128", true, GetAESParameters(128, false)},
         {"AES-256", true, GetAESParameters(256, false)},
         {"AES-128-GCM", false, GetAESParameters(128, true)},
diff --git a/keystore/tests/Android.bp b/keystore/tests/Android.bp
index 39601eb..f51cc2f 100644
--- a/keystore/tests/Android.bp
+++ b/keystore/tests/Android.bp
@@ -62,7 +62,7 @@
         "libgtest_main",
         "libutils",
         "liblog",
-        "android.security.apc-ndk_platform",
+        "android.security.apc-ndk",
     ],
     shared_libs: [
         "libbinder_ndk",
diff --git a/keystore/tests/fuzzer/Android.bp b/keystore/tests/fuzzer/Android.bp
new file mode 100644
index 0000000..589cef7
--- /dev/null
+++ b/keystore/tests/fuzzer/Android.bp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+cc_fuzz {
+    name: "keystoreGetWifiHidl_fuzzer",
+    vendor: true,
+    srcs: [
+        "keystoreGetWifiHidl_fuzzer.cpp",
+    ],
+    static_libs: [
+        "libkeystore-wifi-hidl",
+        "libutils",
+    ],
+    shared_libs: [
+        "android.system.wifi.keystore@1.0",
+        "libhidlbase",
+        "liblog",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
+
+cc_defaults {
+    name: "keystoreAttestation_defaults",
+    static_libs: [
+        "libkeystore-attestation-application-id",
+        "liblog",
+        "libutils",
+        "libbase",
+        "libhidlbase",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libcrypto",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
+
+cc_fuzz {
+    name: "keystoreSignature_fuzzer",
+    srcs: [
+        "keystoreSignature_fuzzer.cpp",
+    ],
+    defaults: [
+        "keystoreAttestation_defaults",
+    ],
+}
+
+cc_fuzz {
+    name: "keystorePackageInfo_fuzzer",
+    srcs: [
+        "keystorePackageInfo_fuzzer.cpp",
+    ],
+    defaults: [
+        "keystoreAttestation_defaults",
+    ],
+}
+
+cc_fuzz {
+    name: "keystoreApplicationId_fuzzer",
+    srcs: [
+        "keystoreApplicationId_fuzzer.cpp",
+    ],
+    defaults: [
+        "keystoreAttestation_defaults",
+    ],
+}
+
+cc_fuzz {
+    name: "keystoreAttestationId_fuzzer",
+    srcs: [
+        "keystoreAttestationId_fuzzer.cpp",
+    ],
+    defaults: [
+        "keystoreAttestation_defaults",
+    ],
+}
diff --git a/keystore/tests/fuzzer/README.md b/keystore/tests/fuzzer/README.md
new file mode 100644
index 0000000..25d53ab
--- /dev/null
+++ b/keystore/tests/fuzzer/README.md
@@ -0,0 +1,103 @@
+# Fuzzer for libkeystore
+## Table of contents
++ [libkeystore-get-wifi-hidl](#libkeystore-get-wifi-hidl)
++ [libkeystore_attestation_application_id](#libkeystore_attestation_application_id)
+
+# <a name="libkeystore-get-wifi-hidl"></a> Fuzzer for libkeystore-get-wifi-hidl
+## Plugin Design Considerations
+The fuzzer plugin for libkeystore-get-wifi-hidl is designed based on the understanding of the library and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+libkeystore-get-wifi-hidl supports the following parameters:
+1. Key (parameter name: `key`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `key` | `String` | Value obtained from FuzzedDataProvider|
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the libkeystore-get-wifi-hidl module.
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build keystoreGetWifiHidl_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) keystoreGetWifiHidl_fuzzer
+```
+#### Steps to run
+
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreGetWifiHidl_fuzzer/keystoreGetWifiHidl_fuzzer
+```
+
+# <a name="libkeystore_attestation_application_id"></a> Fuzzer for libkeystore_attestation_application_id
+## Plugin Design Considerations
+The fuzzer plugin for libkeystore-attestation-application-id are designed based on the understanding of the library and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+libkeystore-attestation-application-id supports the following parameters:
+1. Package Name (parameter name: `packageName`)
+2. Version Code (parameter name: `versionCode`)
+3. Uid (parameter name: `uid`)
+
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `packageName` | `String` | Value obtained from FuzzedDataProvider|
+| `versionCode` | `INT64_MIN` to `INT64_MAX` | Value obtained from FuzzedDataProvider|
+| `uid` | `0` to `1000` | Value obtained from FuzzedDataProvider|
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugins feed the entire input data to the libkeystore_attestation_application_id module.
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build keystoreSignature_fuzzer, keystorePackageInfo_fuzzer, keystoreApplicationId_fuzzer and keystoreAttestationId_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) keystoreSignature_fuzzer
+  $ mm -j$(nproc) keystorePackageInfo_fuzzer
+  $ mm -j$(nproc) keystoreApplicationId_fuzzer
+  $ mm -j$(nproc) keystoreAttestationId_fuzzer
+```
+#### Steps to run
+
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreSignature_fuzzer/keystoreSignature_fuzzer
+  $ adb shell /data/fuzz/${TARGET_ARCH}/keystorePackageInfo_fuzzer/keystorePackageInfo_fuzzer
+  $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreApplicationId_fuzzer/keystoreApplicationId_fuzzer
+  $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreAttestationId_fuzzer/keystoreAttestationId_fuzzer
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp b/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp
new file mode 100644
index 0000000..0eddb9a
--- /dev/null
+++ b/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "keystoreCommon.h"
+#include <keystore/KeyAttestationApplicationId.h>
+
+using ::security::keymaster::KeyAttestationApplicationId;
+
+constexpr size_t kPackageVectorSizeMin = 1;
+constexpr size_t kPackageVectorSizeMax = 10;
+
+class KeystoreApplicationId {
+  public:
+    void process(const uint8_t* data, size_t size);
+    ~KeystoreApplicationId() {}
+
+  private:
+    void invokeApplicationId();
+    std::unique_ptr<FuzzedDataProvider> mFdp;
+};
+
+void KeystoreApplicationId::invokeApplicationId() {
+    std::optional<KeyAttestationApplicationId> applicationId;
+    bool shouldUsePackageInfoVector = mFdp->ConsumeBool();
+    if (shouldUsePackageInfoVector) {
+        KeyAttestationApplicationId::PackageInfoVector packageInfoVector;
+        int32_t packageVectorSize =
+            mFdp->ConsumeIntegralInRange<int32_t>(kPackageVectorSizeMin, kPackageVectorSizeMax);
+        for (int32_t packageSize = 0; packageSize < packageVectorSize; ++packageSize) {
+            auto packageInfoData = initPackageInfoData(mFdp.get());
+            packageInfoVector.push_back(make_optional<KeyAttestationPackageInfo>(
+                String16((packageInfoData.packageName).c_str()), packageInfoData.versionCode,
+                packageInfoData.sharedSignaturesVector));
+        }
+        applicationId = KeyAttestationApplicationId(std::move(packageInfoVector));
+    } else {
+        auto packageInfoData = initPackageInfoData(mFdp.get());
+        applicationId = KeyAttestationApplicationId(make_optional<KeyAttestationPackageInfo>(
+            String16((packageInfoData.packageName).c_str()), packageInfoData.versionCode,
+            packageInfoData.sharedSignaturesVector));
+    }
+    invokeReadWriteParcel(&applicationId.value());
+}
+
+void KeystoreApplicationId::process(const uint8_t* data, size_t size) {
+    mFdp = std::make_unique<FuzzedDataProvider>(data, size);
+    invokeApplicationId();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    KeystoreApplicationId keystoreApplicationId;
+    keystoreApplicationId.process(data, size);
+    return 0;
+}
diff --git a/keystore/tests/fuzzer/keystoreAttestationId_fuzzer.cpp b/keystore/tests/fuzzer/keystoreAttestationId_fuzzer.cpp
new file mode 100644
index 0000000..581da46
--- /dev/null
+++ b/keystore/tests/fuzzer/keystoreAttestationId_fuzzer.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <keystore/keystore_attestation_id.h>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+    uint32_t uid = fdp.ConsumeIntegral<uint32_t>();
+    auto result = android::security::gather_attestation_application_id(uid);
+    result.isOk();
+    result.status();
+    result.value();
+    return 0;
+}
diff --git a/keystore/tests/fuzzer/keystoreCommon.h b/keystore/tests/fuzzer/keystoreCommon.h
new file mode 100644
index 0000000..7af3ba8
--- /dev/null
+++ b/keystore/tests/fuzzer/keystoreCommon.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 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.
+ */
+#ifndef KEYSTORECOMMON_H
+#define KEYSTORECOMMON_H
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <keystore/KeyAttestationPackageInfo.h>
+#include <keystore/Signature.h>
+#include <vector>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+using namespace android;
+using namespace std;
+using ::content::pm::Signature;
+using ::security::keymaster::KeyAttestationPackageInfo;
+
+constexpr size_t kSignatureSizeMin = 1;
+constexpr size_t kSignatureSizeMax = 1000;
+constexpr size_t kRandomStringLength = 256;
+constexpr size_t kSignatureVectorSizeMin = 1;
+constexpr size_t kSignatureVectorSizeMax = 1000;
+
+struct PackageInfoData {
+    string packageName;
+    int64_t versionCode;
+    KeyAttestationPackageInfo::SharedSignaturesVector sharedSignaturesVector;
+};
+
+inline void invokeReadWriteParcel(Parcelable* obj) {
+    Parcel parcel;
+    obj->writeToParcel(&parcel);
+    parcel.setDataPosition(0);
+    obj->readFromParcel(&parcel);
+}
+
+inline vector<uint8_t> initSignatureData(FuzzedDataProvider* fdp) {
+    size_t signatureSize = fdp->ConsumeIntegralInRange(kSignatureSizeMin, kSignatureSizeMax);
+    vector<uint8_t> signatureData = fdp->ConsumeBytes<uint8_t>(signatureSize);
+    return signatureData;
+}
+
+inline PackageInfoData initPackageInfoData(FuzzedDataProvider* fdp) {
+    PackageInfoData packageInfoData;
+    packageInfoData.packageName = fdp->ConsumeRandomLengthString(kRandomStringLength);
+    packageInfoData.versionCode = fdp->ConsumeIntegral<int64_t>();
+    size_t signatureVectorSize =
+        fdp->ConsumeIntegralInRange(kSignatureVectorSizeMin, kSignatureVectorSizeMax);
+    KeyAttestationPackageInfo::SignaturesVector signatureVector;
+    for (size_t size = 0; size < signatureVectorSize; ++size) {
+        bool shouldUseParameterizedConstructor = fdp->ConsumeBool();
+        if (shouldUseParameterizedConstructor) {
+            vector<uint8_t> signatureData = initSignatureData(fdp);
+            signatureVector.push_back(make_optional<Signature>(signatureData));
+        } else {
+            signatureVector.push_back(std::nullopt);
+        }
+    }
+    packageInfoData.sharedSignaturesVector =
+        make_shared<KeyAttestationPackageInfo::SignaturesVector>(move(signatureVector));
+    return packageInfoData;
+}
+#endif  // KEYSTORECOMMON_H
diff --git a/keystore/tests/fuzzer/keystoreGetWifiHidl_fuzzer.cpp b/keystore/tests/fuzzer/keystoreGetWifiHidl_fuzzer.cpp
new file mode 100644
index 0000000..1e033c8
--- /dev/null
+++ b/keystore/tests/fuzzer/keystoreGetWifiHidl_fuzzer.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include <inttypes.h>
+#include <keystore/keystore_get.h>
+
+using namespace std;
+
+constexpr int32_t kMaxKeySize = 256;
+const string kValidStrKeyPrefix[] = {"USRSKEY_",
+                                     "PLATFORM_VPN_",
+                                     "USRPKEY_",
+                                     "CACERT_",
+                                     "VPN_"
+                                     "USRCERT_",
+                                     "WIFI_"};
+constexpr char kStrGrantKeyPrefix[] = "ks2_keystore-engine_grant_id:";
+constexpr char kStrKeySuffix[] = "LOCKDOWN_VPN";
+constexpr size_t kGrantIdSize = 20;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+    size_t keyLength = fdp.ConsumeIntegralInRange<size_t>(0, kMaxKeySize);
+    bool usePrefix = fdp.ConsumeBool();
+    string strKeyPrefix;
+    size_t strKeyPrefixLength = 0;
+    size_t strKeySuffixLength = min(fdp.remaining_bytes(), keyLength);
+    if (usePrefix) {
+        strKeyPrefix = fdp.PickValueInArray(kValidStrKeyPrefix);
+        strKeyPrefixLength = sizeof(strKeyPrefix);
+        strKeySuffixLength =
+            (strKeySuffixLength > strKeyPrefixLength) ? strKeySuffixLength - strKeyPrefixLength : 0;
+    }
+    string strKeySuffix =
+        fdp.ConsumeBool() ? string(kStrKeySuffix) : fdp.ConsumeBytesAsString(strKeySuffixLength);
+    string strKey;
+    strKey = usePrefix ? strKeyPrefix + strKeySuffix : strKeySuffix;
+    if (fdp.ConsumeBool()) {
+        uint64_t grant = fdp.ConsumeIntegral<uint64_t>();
+        char grantId[kGrantIdSize] = "";
+        snprintf(grantId, kGrantIdSize, "%" PRIx64, grant);
+        strKey = strKey + string(kStrGrantKeyPrefix) + grantId;
+    }
+    const char* key = strKey.c_str();
+    uint8_t* value = nullptr;
+    keystore_get(key, strlen(key), &value);
+    free(value);
+    return 0;
+}
diff --git a/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp b/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp
new file mode 100644
index 0000000..63899ff
--- /dev/null
+++ b/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "keystoreCommon.h"
+
+class KeystorePackageInfoFuzzer {
+  public:
+    void process(const uint8_t* data, size_t size);
+    ~KeystorePackageInfoFuzzer() {}
+
+  private:
+    void invokePackageInfo();
+    std::unique_ptr<FuzzedDataProvider> mFdp;
+};
+
+void KeystorePackageInfoFuzzer::invokePackageInfo() {
+    auto packageInfoData = initPackageInfoData(mFdp.get());
+    KeyAttestationPackageInfo packageInfo(String16((packageInfoData.packageName).c_str()),
+                                          packageInfoData.versionCode,
+                                          packageInfoData.sharedSignaturesVector);
+    invokeReadWriteParcel(&packageInfo);
+}
+
+void KeystorePackageInfoFuzzer::process(const uint8_t* data, size_t size) {
+    mFdp = std::make_unique<FuzzedDataProvider>(data, size);
+    invokePackageInfo();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    KeystorePackageInfoFuzzer keystorePackageInfoFuzzer;
+    keystorePackageInfoFuzzer.process(data, size);
+    return 0;
+}
diff --git a/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp b/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp
new file mode 100644
index 0000000..b8f8a73
--- /dev/null
+++ b/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 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.
+ */
+#include "keystoreCommon.h"
+#include <keystore/Signature.h>
+
+class KeystoreSignatureFuzzer {
+  public:
+    void process(const uint8_t* data, size_t size);
+    ~KeystoreSignatureFuzzer() {}
+
+  private:
+    void invokeSignature();
+    std::unique_ptr<FuzzedDataProvider> mFdp;
+};
+
+void KeystoreSignatureFuzzer::invokeSignature() {
+    std::optional<Signature> signature;
+    bool shouldUseParameterizedConstructor = mFdp->ConsumeBool();
+    if (shouldUseParameterizedConstructor) {
+        std::vector<uint8_t> signatureData = initSignatureData(mFdp.get());
+        signature = Signature(signatureData);
+    } else {
+        signature = Signature();
+    }
+    invokeReadWriteParcel(&signature.value());
+}
+
+void KeystoreSignatureFuzzer::process(const uint8_t* data, size_t size) {
+    mFdp = std::make_unique<FuzzedDataProvider>(data, size);
+    invokeSignature();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    KeystoreSignatureFuzzer keystoreSignatureFuzzer;
+    keystoreSignatureFuzzer.process(data, size);
+    return 0;
+}
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 0069f95..2027af4 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -25,9 +25,11 @@
     name: "libkeystore2_defaults",
     crate_name: "keystore2",
     srcs: ["src/lib.rs"],
+    defaults: [
+        "keymint_use_latest_hal_aidl_rust",
+    ],
 
     rustlibs: [
-        "android.hardware.security.keymint-V1-rust",
         "android.hardware.security.secureclock-V1-rust",
         "android.hardware.security.sharedsecret-V1-rust",
         "android.os.permissions_aidl-rust",
@@ -37,24 +39,23 @@
         "android.security.maintenance-rust",
         "android.security.metrics-rust",
         "android.security.remoteprovisioning-rust",
-        "android.system.keystore2-V1-rust",
+        "android.system.keystore2-V2-rust",
         "libanyhow",
         "libbinder_rs",
-        "libcutils_bindgen",
         "libkeystore2_aaid-rust",
         "libkeystore2_apc_compat-rust",
         "libkeystore2_crypto_rust",
         "libkeystore2_km_compat",
         "libkeystore2_selinux",
-        "libkeystore2_system_property-rust",
         "libkeystore2_vintf_rust",
         "liblazy_static",
         "liblibc",
-        "liblibsqlite3_sys",
         "liblog_event_list",
         "liblog_rust",
         "librand",
-        "librusqlite",
+        "librustutils",
+        "libserde",
+        "libserde_cbor",
         "libthiserror",
     ],
     shared_libs: [
@@ -68,6 +69,10 @@
 rust_library {
     name: "libkeystore2",
     defaults: ["libkeystore2_defaults"],
+    rustlibs: [
+        "liblibsqlite3_sys",
+        "librusqlite",
+    ],
 }
 
 rust_library {
@@ -75,8 +80,29 @@
     crate_name: "keystore2_test_utils",
     srcs: ["test_utils/lib.rs"],
     rustlibs: [
+        "libkeystore2_selinux",
         "liblog_rust",
+        "libnix",
         "librand",
+        "libserde",
+        "libserde_cbor",
+    ],
+}
+
+rust_test {
+    name: "keystore2_test_utils_test",
+    srcs: ["test_utils/lib.rs"],
+    test_suites: ["general-tests"],
+    require_root: true,
+    auto_gen_config: true,
+    compile_multilib: "first",
+    rustlibs: [
+        "libkeystore2_selinux",
+        "liblog_rust",
+        "libnix",
+        "librand",
+        "libserde",
+        "libserde_cbor",
     ],
 }
 
@@ -90,7 +116,9 @@
     rustlibs: [
         "libandroid_logger",
         "libkeystore2_test_utils",
+        "liblibsqlite3_sys",
         "libnix",
+        "librusqlite",
     ],
     // The test should always include watchdog.
     features: [
@@ -98,15 +126,13 @@
     ],
 }
 
-rust_binary {
-    name: "keystore2",
+rust_defaults {
+    name: "keystore2_defaults",
     srcs: ["src/keystore2_main.rs"],
     rustlibs: [
         "libandroid_logger",
         "libbinder_rs",
-        "libkeystore2",
         "liblog_rust",
-        "liblegacykeystore-rust",
     ],
     init_rc: ["keystore2.rc"],
 
@@ -118,31 +144,17 @@
     // selection available in the build system.
     prefer_rlib: true,
 
-    // TODO(b/187412695)
-    // This is a hack to work around the build system not installing
-    // dynamic dependencies of rlibs to the device. This section should
-    // be removed once that works correctly.
-    shared_libs: [
-        "android.hardware.confirmationui@1.0",
-        "android.hardware.security.sharedsecret-V1-ndk_platform",
-        "android.security.compat-ndk_platform",
-        "libc",
-        "libdl_android",
-        "libdl",
-        "libandroidicu",
-        "libkeymint",
-        "libkeystore2_aaid",
-        "libkeystore2_apc_compat",
-        "libkeystore2_crypto",
-        "libkeystore2_vintf_cpp",
-        "libkm_compat_service",
-        "libkm_compat",
-        "libm",
-        "libstatspull",
-        "libstatssocket",
-    ],
-
     vintf_fragments: ["android.system.keystore2-service.xml"],
 
     required: ["keystore_cli_v2"],
 }
+
+rust_binary {
+    name: "keystore2",
+    defaults: ["keystore2_defaults"],
+    rustlibs: [
+        "libkeystore2",
+        "liblegacykeystore-rust",
+        "librusqlite",
+    ],
+}
diff --git a/keystore2/TEST_MAPPING b/keystore2/TEST_MAPPING
index 16b6f85..049adc7 100644
--- a/keystore2/TEST_MAPPING
+++ b/keystore2/TEST_MAPPING
@@ -10,7 +10,23 @@
       "name": "keystore2_test"
     },
     {
+      "name": "keystore2_test_utils_test"
+    },
+    {
       "name": "CtsIdentityTestCases"
+    },
+    {
+      "name": "CtsKeystoreTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.RequiresDevice"
+        }
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "CtsKeystorePerformanceTestCases"
     }
   ]
 }
diff --git a/keystore2/aaid/Android.bp b/keystore2/aaid/Android.bp
index c04ce51..3417960 100644
--- a/keystore2/aaid/Android.bp
+++ b/keystore2/aaid/Android.bp
@@ -57,3 +57,13 @@
         "libkeystore2_aaid",
     ],
 }
+
+rust_test {
+    name: "libkeystore2_aaid_bindgen_test",
+    srcs: [":libkeystore2_aaid_bindgen"],
+    crate_name: "keystore2_aaid_bindgen_test",
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    clippy_lints: "none",
+    lints: "none",
+}
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
index 4a7b7b4..ae08567 100644
--- a/keystore2/aidl/Android.bp
+++ b/keystore2/aidl/Android.bp
@@ -24,12 +24,11 @@
 aidl_interface {
     name: "android.security.attestationmanager",
     srcs: [ "android/security/attestationmanager/*.aidl", ],
-    imports: [ "android.hardware.security.keymint-V1" ],
+    imports: [ "android.hardware.security.keymint-V2" ],
     unstable: true,
     backend: {
         java: {
             platform_apis: true,
-            srcs_available: true,
         },
         rust: {
             enabled: true,
@@ -45,14 +44,13 @@
     name: "android.security.authorization",
     srcs: [ "android/security/authorization/*.aidl" ],
     imports: [
-        "android.hardware.security.keymint-V1",
+        "android.hardware.security.keymint-V2",
         "android.hardware.security.secureclock-V1",
     ],
     unstable: true,
     backend: {
         java: {
             platform_apis: true,
-            srcs_available: true,
         },
         rust: {
             enabled: true,
@@ -71,7 +69,6 @@
     backend: {
         java: {
             enabled: true,
-            srcs_available: true,
         },
         rust: {
             enabled: true,
@@ -86,7 +83,7 @@
     name: "android.security.compat",
     srcs: [ "android/security/compat/*.aidl" ],
     imports: [
-        "android.hardware.security.keymint-V1",
+        "android.hardware.security.keymint-V2",
         "android.hardware.security.secureclock-V1",
         "android.hardware.security.sharedsecret-V1",
     ],
@@ -94,7 +91,6 @@
     backend: {
         java: {
             platform_apis: true,
-            srcs_available: true,
         },
         rust: {
             enabled: true,
@@ -110,13 +106,12 @@
     name: "android.security.remoteprovisioning",
     srcs: [ "android/security/remoteprovisioning/*.aidl" ],
     imports: [
-        "android.hardware.security.keymint-V1",
+        "android.hardware.security.keymint-V2",
     ],
     unstable: true,
     backend: {
         java: {
             platform_apis: true,
-            srcs_available: true,
         },
         ndk: {
             enabled: true,
@@ -132,13 +127,12 @@
     name: "android.security.maintenance",
     srcs: [ "android/security/maintenance/*.aidl" ],
     imports: [
-        "android.system.keystore2-V1",
+        "android.system.keystore2-V2",
     ],
     unstable: true,
     backend: {
         java: {
             platform_apis: true,
-            srcs_available: true,
         },
         rust: {
             enabled: true,
@@ -157,7 +151,6 @@
     backend: {
         java: {
             platform_apis: true,
-            srcs_available: true,
         },
         rust: {
             enabled: true,
@@ -173,13 +166,12 @@
     name: "android.security.metrics",
     srcs: [ "android/security/metrics/*.aidl" ],
     imports: [
-        "android.system.keystore2-V1",
+        "android.system.keystore2-V2",
     ],
     unstable: true,
     backend: {
         java: {
             platform_apis: true,
-            srcs_available: true,
         },
         rust: {
             enabled: true,
@@ -191,3 +183,19 @@
     },
 }
 
+// cc_defaults that includes the latest Keystore2 AIDL library.
+// Modules that depend on KeyMint directly can include this cc_defaults to avoid
+// managing dependency versions explicitly.
+cc_defaults {
+    name: "keystore2_use_latest_aidl_ndk_static",
+    static_libs: [
+        "android.system.keystore2-V2-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "keystore2_use_latest_aidl_ndk_shared",
+    shared_libs: [
+        "android.system.keystore2-V2-ndk",
+    ],
+}
diff --git a/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
index 50bfa19..8e347f0 100644
--- a/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
+++ b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
@@ -29,8 +29,17 @@
  */
 interface IKeystoreCompatService {
     /**
-     * Return an implementation of IKeyMintDevice, that it implemented by Keystore 2.0 itself
-     * by means of Keymaster 4.1 or lower.
+     * Return an implementation of IKeyMintDevice, that it implemented by Keystore 2.0 itself.
+     * The underlying implementation depends on the requested securityLevel:
+     * - TRUSTED_ENVIRONMENT or STRONGBOX: implementation is by means of a hardware-backed
+     *   Keymaster 4.x instance. In this case, the returned device supports version 1 of
+     *   the IKeyMintDevice interface, with some small omissions:
+     *     - KeyPurpose::ATTEST_KEY is not supported (b/216437537)
+     *     - Specification of the MGF1 digest for RSA-OAEP is not supported (b/216436980)
+     *     - Specification of CERTIFICATE_{SUBJECT,SERIAL} is not supported for keys attested
+     *       by hardware (b/216468666).
+     * - SOFTWARE: implementation is entirely software based.  In this case, the returned device
+     *   supports the current version of the IKeyMintDevice interface.
      */
     IKeyMintDevice getKeyMintDevice (SecurityLevel securityLevel);
 
diff --git a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
index 6a37c78..3df5936 100644
--- a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
+++ b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
@@ -23,10 +23,15 @@
  * user's password.
  * @hide
  */
- @SensitiveData
+@SensitiveData
 interface IKeystoreMaintenance {
 
     /**
+     * Special value indicating the callers uid.
+     */
+    const int UID_SELF = -1;
+
+    /**
      * Allows LockSettingsService to inform keystore about adding a new user.
      * Callers require 'AddUser' permission.
      *
@@ -115,6 +120,10 @@
      * The source may be specified by Domain::APP, Domain::SELINUX, or Domain::KEY_ID. The target
      * may be specified by Domain::APP or Domain::SELINUX.
      *
+     * If Domain::APP is selected in either source or destination, nspace must be set to UID_SELF,
+     * implying the caller's UID. If the caller has the MIGRATE_ANY_KEY permission, Domain::APP may
+     * be used with other nspace values which then indicates the UID of a different application.
+     *
      * ## Error conditions:
      * `ResponseCode::PERMISSION_DENIED` - If the caller lacks any of the required permissions.
      * `ResponseCode::KEY_NOT_FOUND` - If the source did not exist.
@@ -131,4 +140,22 @@
      * Tag::ROLLBACK_RESISTANCE may or may not be rendered unusable.
      */
     void deleteAllKeys();
+
+    /**
+     * List all entries accessible by the caller in the given `domain` and `nspace`.
+     *
+     * Callers either has to have the `GET_INFO` permission for the requested namespace or `LIST`
+     * permission to list all the entries.
+     *
+     * ## Error conditions
+     * `ResponseCode::INVALID_ARGUMENT` if `domain` is other than `Domain::APP` or `Domain::SELINUX`
+     * `ResponseCode::PERMISSION_DENIED` if the caller does not have the permission
+     *
+     * @param domain `Domain::APP` or `Domain::SELINUX`.
+     *
+     * @param nspace The SELinux keystore2_key namespace.
+     *
+     * @return List of KeyDescriptors.
+     */
+    KeyDescriptor[] listEntries(in Domain domain, in long nspace);
 }
diff --git a/keystore2/aidl/android/security/metrics/EcCurve.aidl b/keystore2/aidl/android/security/metrics/EcCurve.aidl
index b190d83..7b1a5a2 100644
--- a/keystore2/aidl/android/security/metrics/EcCurve.aidl
+++ b/keystore2/aidl/android/security/metrics/EcCurve.aidl
@@ -29,4 +29,5 @@
     P_256 = 2,
     P_384 = 3,
     P_521 = 4,
+    CURVE_25519 = 5,
 }
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.aidl b/keystore2/aidl/android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.aidl
new file mode 100644
index 0000000..7d45e52
--- /dev/null
+++ b/keystore2/aidl/android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.aidl
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.security.remoteprovisioning;
+
+import android.security.remoteprovisioning.RemotelyProvisionedKey;
+
+/**
+ * This is the interface providing access to remotely-provisioned attestation keys
+ * for an `IRemotelyProvisionedComponent`.
+ *
+ * @hide
+ */
+interface IRemotelyProvisionedKeyPool {
+
+    /**
+     * Fetches an attestation key for the given uid and `IRemotelyProvisionedComponent`, as
+     * identified by the given id.
+
+     * Callers require the keystore2::get_attestation_key permission.
+     *
+     * ## Error conditions
+     * `android.system.keystore2.ResponseCode::PERMISSION_DENIED` if the caller does not have the
+     *      `keystore2::get_attestation_key` permission
+     *
+     * @param clientUid The client application for which an attestation key is needed.
+     *
+     * @param irpcId The unique identifier for the `IRemotelyProvisionedComponent` for which a key
+     *      is requested. This id may be retrieved from a given component via the
+     *      `IRemotelyProvisionedComponent::getHardwareInfo` function.
+     *
+     * @return A `RemotelyProvisionedKey` parcelable containing a key and certification chain for
+     *      the given `IRemotelyProvisionedComponent`.
+     */
+    RemotelyProvisionedKey getAttestationKey(in int clientUid, in @utf8InCpp String irpcId);
+}
diff --git a/keystore2/aidl/android/security/remoteprovisioning/RemotelyProvisionedKey.aidl b/keystore2/aidl/android/security/remoteprovisioning/RemotelyProvisionedKey.aidl
new file mode 100644
index 0000000..ae21855
--- /dev/null
+++ b/keystore2/aidl/android/security/remoteprovisioning/RemotelyProvisionedKey.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package android.security.remoteprovisioning;
+
+/**
+ * A `RemotelyProvisionedKey` holds an attestation key and the corresponding remotely provisioned
+ * certificate chain.
+ *
+ * @hide
+ */
+@RustDerive(Eq=true, PartialEq=true)
+parcelable RemotelyProvisionedKey {
+    /**
+     * The remotely-provisioned key that may be used to sign attestations. The format of this key
+     * is opaque, and need only be understood by the IRemotelyProvisionedComponent that generated
+     * it.
+     *
+     * Any private key material contained within this blob must be encrypted.
+     */
+    byte[] keyBlob;
+
+    /**
+     * Sequence of DER-encoded X.509 certificates that make up the attestation key's certificate
+     * chain. This is the binary encoding for a chain that is supported by Java's
+     * CertificateFactory.generateCertificates API.
+     */
+    byte[] encodedCertChain;
+}
diff --git a/keystore2/android.system.keystore2-service.xml b/keystore2/android.system.keystore2-service.xml
index 6b8d0cb..20c2fba 100644
--- a/keystore2/android.system.keystore2-service.xml
+++ b/keystore2/android.system.keystore2-service.xml
@@ -1,6 +1,7 @@
 <manifest version="1.0" type="framework">
     <hal format="aidl">
         <name>android.system.keystore2</name>
+        <version>2</version>
         <interface>
             <name>IKeystoreService</name>
             <instance>default</instance>
diff --git a/keystore2/apc_compat/Android.bp b/keystore2/apc_compat/Android.bp
index bf21675..df7521e 100644
--- a/keystore2/apc_compat/Android.bp
+++ b/keystore2/apc_compat/Android.bp
@@ -63,3 +63,13 @@
         "libkeystore2_apc_compat",
     ],
 }
+
+rust_test {
+    name: "libkeystore2_apc_compat_bindgen_test",
+    srcs: [":libkeystore2_apc_compat_bindgen"],
+    crate_name: "keystore2_apc_compat_bindgen_test",
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    clippy_lints: "none",
+    lints: "none",
+}
diff --git a/keystore2/keystore2.rc b/keystore2/keystore2.rc
index 82bf3b8..6f88dd3 100644
--- a/keystore2/keystore2.rc
+++ b/keystore2/keystore2.rc
@@ -10,4 +10,4 @@
     class early_hal
     user keystore
     group keystore readproc log
-    writepid /dev/cpuset/foreground/tasks
+    task_profiles ProcessCapacityHigh
diff --git a/keystore2/legacykeystore/Android.bp b/keystore2/legacykeystore/Android.bp
index fb6f60f..505b165 100644
--- a/keystore2/legacykeystore/Android.bp
+++ b/keystore2/legacykeystore/Android.bp
@@ -21,8 +21,8 @@
     default_applicable_licenses: ["system_security_license"],
 }
 
-rust_library {
-    name: "liblegacykeystore-rust",
+rust_defaults {
+    name: "liblegacykeystore-rust_defaults",
     crate_name: "legacykeystore",
     srcs: [
         "lib.rs",
@@ -31,14 +31,22 @@
         "android.security.legacykeystore-rust",
         "libanyhow",
         "libbinder_rs",
-        "libcutils_bindgen",
-        "libkeystore2",
         "liblog_rust",
         "librusqlite",
+        "librustutils",
         "libthiserror",
     ],
 }
 
+rust_library {
+    name: "liblegacykeystore-rust",
+    defaults: ["liblegacykeystore-rust_defaults"],
+    rustlibs: [
+        "libkeystore2",
+        "librusqlite",
+    ],
+}
+
 rust_test {
     name: "legacykeystore_test",
     crate_name: "legacykeystore",
@@ -49,11 +57,11 @@
         "android.security.legacykeystore-rust",
         "libanyhow",
         "libbinder_rs",
-        "libcutils_bindgen",
         "libkeystore2",
         "libkeystore2_test_utils",
         "liblog_rust",
         "librusqlite",
+        "librustutils",
         "libthiserror",
     ],
 }
diff --git a/keystore2/legacykeystore/lib.rs b/keystore2/legacykeystore/lib.rs
index efa0870..e2d952d 100644
--- a/keystore2/legacykeystore/lib.rs
+++ b/keystore2/legacykeystore/lib.rs
@@ -25,8 +25,9 @@
 };
 use anyhow::{Context, Result};
 use keystore2::{
-    async_task::AsyncTask, legacy_blob::LegacyBlobLoader, maintenance::DeleteListener,
-    maintenance::Domain, utils::watchdog as wd,
+    async_task::AsyncTask, error::anyhow_error_to_cstring, globals::SUPER_KEY,
+    legacy_blob::LegacyBlobLoader, maintenance::DeleteListener, maintenance::Domain,
+    utils::uid_to_android_user, utils::watchdog as wd,
 };
 use rusqlite::{
     params, Connection, OptionalExtension, Transaction, TransactionBehavior, NO_PARAMS,
@@ -161,7 +162,7 @@
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             tx.execute(
                 "DELETE FROM profiles WHERE cast ( ( owner/? ) as int) = ?;",
-                params![cutils_bindgen::AID_USER_OFFSET, user_id],
+                params![rustutils::users::AID_USER_OFFSET, user_id],
             )
             .context("In remove_uid: Failed to delete.")
         })?;
@@ -226,7 +227,10 @@
             if log_error {
                 log::error!("{:?}", e);
             }
-            Err(BinderStatus::new_service_specific_error(rc, None))
+            Err(BinderStatus::new_service_specific_error(
+                rc,
+                anyhow_error_to_cstring(&e).as_deref(),
+            ))
         },
         handle_ok,
     )
@@ -312,8 +316,8 @@
         if let Some(entry) = db.get(uid, alias).context("In get: Trying to load entry from DB.")? {
             return Ok(entry);
         }
-        if self.get_legacy(uid, alias).context("In get: Trying to migrate legacy blob.")? {
-            // If we were able to migrate a legacy blob try again.
+        if self.get_legacy(uid, alias).context("In get: Trying to import legacy blob.")? {
+            // If we were able to import a legacy blob try again.
             if let Some(entry) =
                 db.get(uid, alias).context("In get: Trying to load entry from DB.")?
             {
@@ -325,19 +329,20 @@
 
     fn put(&self, alias: &str, uid: i32, entry: &[u8]) -> Result<()> {
         let uid = Self::get_effective_uid(uid).context("In put.")?;
-        // In order to make sure that we don't have stale legacy entries, make sure they are
-        // migrated before replacing them.
-        let _ = self.get_legacy(uid, alias);
         let mut db = self.open_db().context("In put.")?;
-        db.put(uid, alias, entry).context("In put: Trying to insert entry into DB.")
+        db.put(uid, alias, entry).context("In put: Trying to insert entry into DB.")?;
+        // When replacing an entry, make sure that there is no stale legacy file entry.
+        let _ = self.remove_legacy(uid, alias);
+        Ok(())
     }
 
     fn remove(&self, alias: &str, uid: i32) -> Result<()> {
         let uid = Self::get_effective_uid(uid).context("In remove.")?;
         let mut db = self.open_db().context("In remove.")?;
-        // In order to make sure that we don't have stale legacy entries, make sure they are
-        // migrated before removing them.
-        let _ = self.get_legacy(uid, alias);
+
+        if self.remove_legacy(uid, alias).context("In remove: trying to remove legacy entry")? {
+            return Ok(());
+        }
         let removed =
             db.remove(uid, alias).context("In remove: Trying to remove entry from DB.")?;
         if removed {
@@ -427,17 +432,30 @@
                 return Ok(true);
             }
             let mut db = DB::new(&state.db_path).context("In open_db: Failed to open db.")?;
-            let migrated =
-                Self::migrate_one_legacy_entry(uid, &alias, &state.legacy_loader, &mut db)
-                    .context("Trying to migrate legacy keystore entries.")?;
-            if migrated {
+            let imported =
+                Self::import_one_legacy_entry(uid, &alias, &state.legacy_loader, &mut db)
+                    .context("Trying to import legacy keystore entries.")?;
+            if imported {
                 state.recently_imported.insert((uid, alias));
             }
-            Ok(migrated)
+            Ok(imported)
         })
         .context("In get_legacy.")
     }
 
+    fn remove_legacy(&self, uid: u32, alias: &str) -> Result<bool> {
+        let alias = alias.to_string();
+        self.do_serialized(move |state| {
+            if state.recently_imported.contains(&(uid, alias.clone())) {
+                return Ok(false);
+            }
+            state
+                .legacy_loader
+                .remove_legacy_keystore_entry(uid, &alias)
+                .context("Trying to remove legacy entry.")
+        })
+    }
+
     fn bulk_delete_uid(&self, uid: u32) -> Result<()> {
         self.do_serialized(move |state| {
             let entries = state
@@ -470,21 +488,31 @@
         })
     }
 
-    fn migrate_one_legacy_entry(
+    fn import_one_legacy_entry(
         uid: u32,
         alias: &str,
         legacy_loader: &LegacyBlobLoader,
         db: &mut DB,
     ) -> Result<bool> {
         let blob = legacy_loader
-            .read_legacy_keystore_entry(uid, alias)
-            .context("In migrate_one_legacy_entry: Trying to read legacy keystore entry.")?;
+            .read_legacy_keystore_entry(uid, alias, |ciphertext, iv, tag, _salt, _key_size| {
+                if let Some(key) = SUPER_KEY
+                    .read()
+                    .unwrap()
+                    .get_per_boot_key_by_user_id(uid_to_android_user(uid as u32))
+                {
+                    key.decrypt(ciphertext, iv, tag)
+                } else {
+                    Err(Error::sys()).context("No key found for user. Device may be locked.")
+                }
+            })
+            .context("In import_one_legacy_entry: Trying to read legacy keystore entry.")?;
         if let Some(entry) = blob {
             db.put(uid, alias, &entry)
-                .context("In migrate_one_legacy_entry: Trying to insert entry into DB.")?;
+                .context("In import_one_legacy_entry: Trying to insert entry into DB.")?;
             legacy_loader
                 .remove_legacy_keystore_entry(uid, alias)
-                .context("In migrate_one_legacy_entry: Trying to delete legacy keystore entry.")?;
+                .context("In import_one_legacy_entry: Trying to delete legacy keystore entry.")?;
             Ok(true)
         } else {
             Ok(false)
@@ -526,7 +554,7 @@
     use std::time::Duration;
     use std::time::Instant;
 
-    static TEST_ALIAS: &str = &"test_alias";
+    static TEST_ALIAS: &str = "test_alias";
     static TEST_BLOB1: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
     static TEST_BLOB2: &[u8] = &[2, 2, 3, 4, 5, 6, 7, 8, 9, 0];
     static TEST_BLOB3: &[u8] = &[3, 2, 3, 4, 5, 6, 7, 8, 9, 0];
@@ -600,9 +628,9 @@
             .expect("Failed to open database.");
 
         // Insert three entries for owner 2.
-        db.put(2 + 2 * cutils_bindgen::AID_USER_OFFSET, "test1", TEST_BLOB1)
+        db.put(2 + 2 * rustutils::users::AID_USER_OFFSET, "test1", TEST_BLOB1)
             .expect("Failed to insert test1.");
-        db.put(4 + 2 * cutils_bindgen::AID_USER_OFFSET, "test2", TEST_BLOB2)
+        db.put(4 + 2 * rustutils::users::AID_USER_OFFSET, "test2", TEST_BLOB2)
             .expect("Failed to insert test2.");
         db.put(3, "test3", TEST_BLOB3).expect("Failed to insert test3.");
 
@@ -610,12 +638,12 @@
 
         assert_eq!(
             Vec::<String>::new(),
-            db.list(2 + 2 * cutils_bindgen::AID_USER_OFFSET).expect("Failed to list entries.")
+            db.list(2 + 2 * rustutils::users::AID_USER_OFFSET).expect("Failed to list entries.")
         );
 
         assert_eq!(
             Vec::<String>::new(),
-            db.list(4 + 2 * cutils_bindgen::AID_USER_OFFSET).expect("Failed to list entries.")
+            db.list(4 + 2 * rustutils::users::AID_USER_OFFSET).expect("Failed to list entries.")
         );
 
         assert_eq!(vec!["test3".to_string(),], db.list(3).expect("Failed to list entries."));
@@ -694,9 +722,9 @@
                 }
                 let mut db = DB::new(&db_path3).expect("Failed to open database.");
 
-                db.put(3, &TEST_ALIAS, TEST_BLOB3).expect("Failed to add entry (3).");
+                db.put(3, TEST_ALIAS, TEST_BLOB3).expect("Failed to add entry (3).");
 
-                db.remove(3, &TEST_ALIAS).expect("Remove failed (3).");
+                db.remove(3, TEST_ALIAS).expect("Remove failed (3).");
             }
         });
 
@@ -710,7 +738,7 @@
                 let mut db = DB::new(&db_path).expect("Failed to open database.");
 
                 // This may return Some or None but it must not fail.
-                db.get(3, &TEST_ALIAS).expect("Failed to get entry (4).");
+                db.get(3, TEST_ALIAS).expect("Failed to get entry (4).");
             }
         });
 
diff --git a/keystore2/selinux/Android.bp b/keystore2/selinux/Android.bp
index 748e406..254f95e 100644
--- a/keystore2/selinux/Android.bp
+++ b/keystore2/selinux/Android.bp
@@ -63,3 +63,24 @@
         "libthiserror",
     ],
 }
+
+rust_test {
+    name: "keystore2_selinux_concurrency_test",
+    srcs: [
+        "src/concurrency_test.rs",
+    ],
+    crate_name: "keystore2_selinux_concurrency_test",
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+
+    rustlibs: [
+        "libandroid_logger",
+        "libanyhow",
+        "libkeystore2_selinux",
+        "liblazy_static",
+        "liblog_rust",
+        "libnix",
+        "libnum_cpus",
+        "libthiserror",
+    ],
+}
diff --git a/keystore2/selinux/src/concurrency_test.rs b/keystore2/selinux/src/concurrency_test.rs
new file mode 100644
index 0000000..a5d2df2
--- /dev/null
+++ b/keystore2/selinux/src/concurrency_test.rs
@@ -0,0 +1,190 @@
+// 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.
+
+use keystore2_selinux::{check_access, Context};
+use nix::sched::sched_setaffinity;
+use nix::sched::CpuSet;
+use nix::unistd::getpid;
+use std::thread;
+use std::{
+    sync::{atomic::AtomicU8, atomic::Ordering, Arc},
+    time::{Duration, Instant},
+};
+
+#[derive(Clone, Copy)]
+struct CatCount(u8, u8, u8, u8);
+
+impl CatCount {
+    fn next(&mut self) -> CatCount {
+        let result = *self;
+        if self.3 == 255 {
+            if self.2 == 254 {
+                if self.1 == 253 {
+                    if self.0 == 252 {
+                        self.0 = 255;
+                    }
+                    self.0 += 1;
+                    self.1 = self.0;
+                }
+                self.1 += 1;
+                self.2 = self.1;
+            }
+            self.2 += 1;
+            self.3 = self.2;
+        }
+        self.3 += 1;
+        result
+    }
+
+    fn make_string(&self) -> String {
+        format!("c{},c{},c{},c{}", self.0, self.1, self.2, self.3)
+    }
+}
+
+impl Default for CatCount {
+    fn default() -> Self {
+        Self(0, 1, 2, 3)
+    }
+}
+
+/// This test calls selinux_check_access concurrently causing access vector cache misses
+/// in libselinux avc. The test then checks if any of the threads fails to report back
+/// after a burst of access checks. The purpose of the test is to draw out a specific
+/// access vector cache corruption that sends a calling thread into an infinite loop.
+/// This was observed when keystore2 used libselinux concurrently in a non thread safe
+/// way. See b/184006658.
+#[test]
+fn test_concurrent_check_access() {
+    android_logger::init_once(
+        android_logger::Config::default()
+            .with_tag("keystore2_selinux_concurrency_test")
+            .with_min_level(log::Level::Debug),
+    );
+
+    let cpus = num_cpus::get();
+    let turnpike = Arc::new(AtomicU8::new(0));
+    let complete_count = Arc::new(AtomicU8::new(0));
+    let mut threads: Vec<thread::JoinHandle<()>> = Vec::new();
+
+    for i in 0..cpus {
+        log::info!("Spawning thread {}", i);
+        let turnpike_clone = turnpike.clone();
+        let complete_count_clone = complete_count.clone();
+        threads.push(thread::spawn(move || {
+            let mut cpu_set = CpuSet::new();
+            cpu_set.set(i).unwrap();
+            sched_setaffinity(getpid(), &cpu_set).unwrap();
+            let mut cat_count: CatCount = Default::default();
+
+            log::info!("Thread 0 reached turnpike");
+            loop {
+                turnpike_clone.fetch_add(1, Ordering::Relaxed);
+                loop {
+                    match turnpike_clone.load(Ordering::Relaxed) {
+                        0 => break,
+                        255 => return,
+                        _ => {}
+                    }
+                }
+
+                for _ in 0..250 {
+                    let (tctx, sctx, perm, class) = (
+                        Context::new("u:object_r:keystore:s0").unwrap(),
+                        Context::new(&format!(
+                            "u:r:untrusted_app:s0:{}",
+                            cat_count.next().make_string()
+                        ))
+                        .unwrap(),
+                        "use",
+                        "keystore2_key",
+                    );
+
+                    check_access(&sctx, &tctx, class, perm).unwrap();
+                }
+
+                complete_count_clone.fetch_add(1, Ordering::Relaxed);
+                while complete_count_clone.load(Ordering::Relaxed) as usize != cpus {
+                    thread::sleep(Duration::from_millis(5));
+                }
+            }
+        }));
+    }
+
+    let mut i = 0;
+    let run_time = Instant::now();
+
+    loop {
+        const TEST_ITERATIONS: u32 = 500;
+        const MAX_SLEEPS: u64 = 500;
+        const SLEEP_MILLISECONDS: u64 = 5;
+        let mut sleep_count: u64 = 0;
+        while turnpike.load(Ordering::Relaxed) as usize != cpus {
+            thread::sleep(Duration::from_millis(SLEEP_MILLISECONDS));
+            sleep_count += 1;
+            assert!(
+                sleep_count < MAX_SLEEPS,
+                "Waited too long to go ready on iteration {}, only {} are ready",
+                i,
+                turnpike.load(Ordering::Relaxed)
+            );
+        }
+
+        if i % 100 == 0 {
+            let elapsed = run_time.elapsed().as_secs();
+            println!("{:02}:{:02}: Iteration {}", elapsed / 60, elapsed % 60, i);
+        }
+
+        // Give the threads some time to reach and spin on the turn pike.
+        assert_eq!(turnpike.load(Ordering::Relaxed) as usize, cpus, "i = {}", i);
+        if i >= TEST_ITERATIONS {
+            turnpike.store(255, Ordering::Relaxed);
+            break;
+        }
+
+        // Now go.
+        complete_count.store(0, Ordering::Relaxed);
+        turnpike.store(0, Ordering::Relaxed);
+        i += 1;
+
+        // Wait for them to all complete.
+        sleep_count = 0;
+        while complete_count.load(Ordering::Relaxed) as usize != cpus {
+            thread::sleep(Duration::from_millis(SLEEP_MILLISECONDS));
+            sleep_count += 1;
+            if sleep_count >= MAX_SLEEPS {
+                // Enable the following block to park the thread to allow attaching a debugger.
+                if false {
+                    println!(
+                        "Waited {} seconds and we seem stuck. Going to sleep forever.",
+                        (MAX_SLEEPS * SLEEP_MILLISECONDS) as f32 / 1000.0
+                    );
+                    loop {
+                        thread::park();
+                    }
+                } else {
+                    assert!(
+                        sleep_count < MAX_SLEEPS,
+                        "Waited too long to complete on iteration {}, only {} are complete",
+                        i,
+                        complete_count.load(Ordering::Relaxed)
+                    );
+                }
+            }
+        }
+    }
+
+    for t in threads {
+        t.join().unwrap();
+    }
+}
diff --git a/keystore2/selinux/src/lib.rs b/keystore2/selinux/src/lib.rs
index 5197cf6..c0593b7 100644
--- a/keystore2/selinux/src/lib.rs
+++ b/keystore2/selinux/src/lib.rs
@@ -130,7 +130,7 @@
     fn deref(&self) -> &Self::Target {
         match self {
             Self::Raw(p) => unsafe { CStr::from_ptr(*p) },
-            Self::CString(cstr) => &cstr,
+            Self::CString(cstr) => cstr,
         }
     }
 }
@@ -321,6 +321,323 @@
     }
 }
 
+/// Safe wrapper around setcon.
+pub fn setcon(target: &CStr) -> std::io::Result<()> {
+    // SAFETY: `setcon` takes a const char* and only performs read accesses on it
+    // using strdup and strcmp. `setcon` does not retain a pointer to `target`
+    // and `target` outlives the call to `setcon`.
+    if unsafe { selinux::setcon(target.as_ptr()) } != 0 {
+        Err(std::io::Error::last_os_error())
+    } else {
+        Ok(())
+    }
+}
+
+/// Represents an SEPolicy permission belonging to a specific class.
+pub trait ClassPermission {
+    /// The permission string of the given instance as specified in the class vector.
+    fn name(&self) -> &'static str;
+    /// The class of the permission.
+    fn class_name(&self) -> &'static str;
+}
+
+/// This macro implements an enum with values mapped to SELinux permission names.
+/// The example below implements `enum MyPermission with public visibility:
+///  * From<i32> and Into<i32> are implemented. Where the implementation of From maps
+///    any variant not specified to the default `None` with value `0`.
+///  * `MyPermission` implements ClassPermission.
+///  * An implicit default values `MyPermission::None` is created with a numeric representation
+///    of `0` and a string representation of `"none"`.
+///  * Specifying a value is optional. If the value is omitted it is set to the value of the
+///    previous variant left shifted by 1.
+///
+/// ## Example
+/// ```
+/// implement_class!(
+///     /// MyPermission documentation.
+///     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+///     #[selinux(class_name = my_class)]
+///     pub enum MyPermission {
+///         #[selinux(name = foo)]
+///         Foo = 1,
+///         #[selinux(name = bar)]
+///         Bar = 2,
+///         #[selinux(name = snafu)]
+///         Snafu, // Implicit value: MyPermission::Bar << 1 -> 4
+///     }
+///     assert_eq!(MyPermission::Foo.name(), &"foo");
+///     assert_eq!(MyPermission::Foo.class_name(), &"my_class");
+///     assert_eq!(MyPermission::Snafu as i32, 4);
+/// );
+/// ```
+#[macro_export]
+macro_rules! implement_class {
+    // First rule: Public interface.
+    (
+        $(#[$($enum_meta:tt)+])*
+        $enum_vis:vis enum $enum_name:ident $body:tt
+    ) => {
+        implement_class! {
+            @extract_class
+            []
+            [$(#[$($enum_meta)+])*]
+            $enum_vis enum $enum_name $body
+        }
+    };
+
+    // The next two rules extract the #[selinux(class_name = <name>)] meta field from
+    // the types meta list.
+    // This first rule finds the field and terminates the recursion through the meta fields.
+    (
+        @extract_class
+        [$(#[$mout:meta])*]
+        [
+            #[selinux(class_name = $class_name:ident)]
+            $(#[$($mtail:tt)+])*
+        ]
+        $enum_vis:vis enum $enum_name:ident {
+            $(
+                $(#[$($emeta:tt)+])*
+                $vname:ident$( = $vval:expr)?
+            ),* $(,)?
+        }
+    ) => {
+        implement_class!{
+            @extract_perm_name
+            $class_name
+            $(#[$mout])*
+            $(#[$($mtail)+])*
+            $enum_vis enum $enum_name {
+                1;
+                []
+                [$(
+                    [] [$(#[$($emeta)+])*]
+                    $vname$( = $vval)?,
+                )*]
+            }
+        }
+    };
+
+    // The second rule iterates through the type global meta fields.
+    (
+        @extract_class
+        [$(#[$mout:meta])*]
+        [
+            #[$front:meta]
+            $(#[$($mtail:tt)+])*
+        ]
+        $enum_vis:vis enum $enum_name:ident $body:tt
+    ) => {
+        implement_class!{
+            @extract_class
+            [
+                $(#[$mout])*
+                #[$front]
+            ]
+            [$(#[$($mtail)+])*]
+            $enum_vis enum $enum_name $body
+        }
+    };
+
+    // The next four rules implement two nested recursions. The outer iterates through
+    // the enum variants and the inner iterates through the meta fields of each variant.
+    // The first two rules find the #[selinux(name = <name>)] stanza, terminate the inner
+    // recursion and descend a level in the outer recursion.
+    // The first rule matches variants with explicit initializer $vval. And updates the next
+    // value to ($vval << 1).
+    (
+        @extract_perm_name
+        $class_name:ident
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            $next_val:expr;
+            [$($out:tt)*]
+            [
+                [$(#[$mout:meta])*]
+                [
+                    #[selinux(name = $selinux_name:ident)]
+                    $(#[$($mtail:tt)+])*
+                ]
+                $vname:ident = $vval:expr,
+                $($tail:tt)*
+            ]
+        }
+    ) => {
+        implement_class!{
+            @extract_perm_name
+            $class_name
+            $(#[$enum_meta])*
+            $enum_vis enum $enum_name {
+                ($vval << 1);
+                [
+                    $($out)*
+                    $(#[$mout])*
+                    $(#[$($mtail)+])*
+                    $selinux_name $vname = $vval,
+                ]
+                [$($tail)*]
+            }
+        }
+    };
+
+    // The second rule differs form the previous in that there is no explicit initializer.
+    // Instead $next_val is used as initializer and the next value is set to (&next_val << 1).
+    (
+        @extract_perm_name
+        $class_name:ident
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            $next_val:expr;
+            [$($out:tt)*]
+            [
+                [$(#[$mout:meta])*]
+                [
+                    #[selinux(name = $selinux_name:ident)]
+                    $(#[$($mtail:tt)+])*
+                ]
+                $vname:ident,
+                $($tail:tt)*
+            ]
+        }
+    ) => {
+        implement_class!{
+            @extract_perm_name
+            $class_name
+            $(#[$enum_meta])*
+            $enum_vis enum $enum_name {
+                ($next_val << 1);
+                [
+                    $($out)*
+                    $(#[$mout])*
+                    $(#[$($mtail)+])*
+                    $selinux_name $vname = $next_val,
+                ]
+                [$($tail)*]
+            }
+        }
+    };
+
+    // The third rule descends a step in the inner recursion.
+    (
+        @extract_perm_name
+        $class_name:ident
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            $next_val:expr;
+            [$($out:tt)*]
+            [
+                [$(#[$mout:meta])*]
+                [
+                    #[$front:meta]
+                    $(#[$($mtail:tt)+])*
+                ]
+                $vname:ident$( = $vval:expr)?,
+                $($tail:tt)*
+            ]
+        }
+    ) => {
+        implement_class!{
+            @extract_perm_name
+            $class_name
+            $(#[$enum_meta])*
+            $enum_vis enum $enum_name {
+                $next_val;
+                [$($out)*]
+                [
+                    [
+                        $(#[$mout])*
+                        #[$front]
+                    ]
+                    [$(#[$($mtail)+])*]
+                    $vname$( = $vval)?,
+                    $($tail)*
+                ]
+            }
+        }
+    };
+
+    // The fourth rule terminates the outer recursion and transitions to the
+    // implementation phase @spill.
+    (
+        @extract_perm_name
+        $class_name:ident
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            $next_val:expr;
+            [$($out:tt)*]
+            []
+        }
+    ) => {
+        implement_class!{
+            @spill
+            $class_name
+            $(#[$enum_meta])*
+            $enum_vis enum $enum_name {
+                $($out)*
+            }
+        }
+    };
+
+    (
+        @spill
+        $class_name:ident
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            $(
+                $(#[$emeta:meta])*
+                $selinux_name:ident $vname:ident = $vval:expr,
+            )*
+        }
+    ) => {
+        $(#[$enum_meta])*
+        $enum_vis enum $enum_name {
+            /// The default variant of the enum.
+            None = 0,
+            $(
+                $(#[$emeta])*
+                $vname = $vval,
+            )*
+        }
+
+        impl From<i32> for $enum_name {
+            #[allow(non_upper_case_globals)]
+            fn from (p: i32) -> Self {
+                // Creating constants forces the compiler to evaluate the value expressions
+                // so that they can be used in the match statement below.
+                $(const $vname: i32 = $vval;)*
+                match p {
+                    0 => Self::None,
+                    $($vname => Self::$vname,)*
+                    _ => Self::None,
+                }
+            }
+        }
+
+        impl From<$enum_name> for i32 {
+            fn from(p: $enum_name) -> i32 {
+                p as i32
+            }
+        }
+
+        impl ClassPermission for $enum_name {
+            fn name(&self) -> &'static str {
+                match self {
+                    Self::None => &"none",
+                    $(Self::$vname => stringify!($selinux_name),)*
+                }
+            }
+            fn class_name(&self) -> &'static str {
+                stringify!($class_name)
+            }
+        }
+    };
+}
+
+/// Calls `check_access` on the given class permission.
+pub fn check_permission<T: ClassPermission>(source: &CStr, target: &CStr, perm: T) -> Result<()> {
+    check_access(source, target, perm.class_name(), perm.name())
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/keystore2/src/apc.rs b/keystore2/src/apc.rs
index 0096686..7d56dc9 100644
--- a/keystore2/src/apc.rs
+++ b/keystore2/src/apc.rs
@@ -21,6 +21,7 @@
     sync::{mpsc::Sender, Arc, Mutex},
 };
 
+use crate::error::anyhow_error_to_cstring;
 use crate::utils::{compat_2_response_code, ui_opts_2_compat, watchdog as wd};
 use android_security_apc::aidl::android::security::apc::{
     IConfirmationCallback::IConfirmationCallback,
@@ -110,7 +111,10 @@
                     _ => ResponseCode::SYSTEM_ERROR.0,
                 },
             };
-            Err(BinderStatus::new_service_specific_error(rc, None))
+            Err(BinderStatus::new_service_specific_error(
+                rc,
+                anyhow_error_to_cstring(&e).as_deref(),
+            ))
         },
         handle_ok,
     )
diff --git a/keystore2/src/async_task.rs b/keystore2/src/async_task.rs
index e130024..0515c8f 100644
--- a/keystore2/src/async_task.rs
+++ b/keystore2/src/async_task.rs
@@ -417,7 +417,9 @@
             Err(RecvTimeoutError::Timeout)
         );
         done_receiver.recv().unwrap();
-        idle_done_receiver.recv_timeout(Duration::from_millis(1)).unwrap();
+        // Now that the last low-priority job has completed, the idle task should
+        // fire pretty much immediately.
+        idle_done_receiver.recv_timeout(Duration::from_millis(50)).unwrap();
 
         // Idle callback not executed again even if we wait for a while.
         assert_eq!(
@@ -438,7 +440,7 @@
             Err(RecvTimeoutError::Timeout)
         );
         done_receiver.recv().unwrap();
-        idle_done_receiver.recv_timeout(Duration::from_millis(1)).unwrap();
+        idle_done_receiver.recv_timeout(Duration::from_millis(50)).unwrap();
     }
 
     #[test]
diff --git a/keystore2/src/attestation_key_utils.rs b/keystore2/src/attestation_key_utils.rs
index ca00539..8354ba5 100644
--- a/keystore2/src/attestation_key_utils.rs
+++ b/keystore2/src/attestation_key_utils.rs
@@ -35,6 +35,7 @@
 /// handled quite differently, thus the different representations.
 pub enum AttestationKeyInfo {
     RemoteProvisioned {
+        key_id_guard: KeyIdGuard,
         attestation_key: AttestationKey,
         attestation_certs: Certificate,
     },
@@ -60,18 +61,22 @@
     let challenge_present = params.iter().any(|kp| kp.tag == Tag::ATTESTATION_CHALLENGE);
     match attest_key_descriptor {
         None if challenge_present => rem_prov_state
-            .get_remotely_provisioned_attestation_key_and_certs(&key, caller_uid, params, db)
+            .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 }
+                result.map(|(key_id_guard, attestation_key, attestation_certs)| {
+                    AttestationKeyInfo::RemoteProvisioned {
+                        key_id_guard,
+                        attestation_key,
+                        attestation_certs,
+                    }
                 })
             }),
         None => Ok(None),
-        Some(attest_key) => get_user_generated_attestation_key(&attest_key, caller_uid, db)
+        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),
     }
@@ -83,7 +88,7 @@
     db: &mut KeystoreDB,
 ) -> Result<AttestationKeyInfo> {
     let (key_id_guard, blob, cert, blob_metadata) =
-        load_attest_key_blob_and_cert(&key, caller_uid, db)
+        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(
@@ -105,11 +110,11 @@
         _ => {
             let (key_id_guard, mut key_entry) = db
                 .load_key_entry(
-                    &key,
+                    key,
                     KeyType::Client,
                     KeyEntryLoadBits::BOTH,
                     caller_uid,
-                    |k, av| check_key_permission(KeyPerm::use_(), k, &av),
+                    |k, av| check_key_permission(KeyPerm::Use, k, &av),
                 )
                 .context("In load_attest_key_blob_and_cert: Failed to load key.")?;
 
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index e205883..8265dd0 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -15,6 +15,7 @@
 //! This module implements IKeystoreAuthorization AIDL interface.
 
 use crate::error::Error as KeystoreError;
+use crate::error::anyhow_error_to_cstring;
 use crate::globals::{ENFORCEMENTS, SUPER_KEY, DB, LEGACY_IMPORTER};
 use crate::permission::KeystorePerm;
 use crate::super_key::UserState;
@@ -88,7 +89,10 @@
                     // as well.
                     _ => ResponseCode::SYSTEM_ERROR.0,
                 };
-                return Err(BinderStatus::new_service_specific_error(rc, None));
+                return Err(BinderStatus::new_service_specific_error(
+                    rc,
+                    anyhow_error_to_cstring(&e).as_deref(),
+                ));
             }
             let rc = match root_cause.downcast_ref::<Error>() {
                 Some(Error::Rc(rcode)) => rcode.0,
@@ -98,7 +102,10 @@
                     _ => ResponseCode::SYSTEM_ERROR.0,
                 },
             };
-            Err(BinderStatus::new_service_specific_error(rc, None))
+            Err(BinderStatus::new_service_specific_error(
+                rc,
+                anyhow_error_to_cstring(&e).as_deref(),
+            ))
         },
         handle_ok,
     )
@@ -119,7 +126,7 @@
 
     fn add_auth_token(&self, auth_token: &HardwareAuthToken) -> Result<()> {
         // Check keystore permission.
-        check_keystore_permission(KeystorePerm::add_auth()).context("In add_auth_token.")?;
+        check_keystore_permission(KeystorePerm::AddAuth).context("In add_auth_token.")?;
 
         ENFORCEMENTS.add_auth_token(auth_token.clone());
         Ok(())
@@ -143,12 +150,14 @@
             (LockScreenEvent::UNLOCK, Some(password)) => {
                 // This corresponds to the unlock() method in legacy keystore API.
                 // check permission
-                check_keystore_permission(KeystorePerm::unlock())
+                check_keystore_permission(KeystorePerm::Unlock)
                     .context("In on_lock_screen_event: Unlock with password.")?;
                 ENFORCEMENTS.set_device_locked(user_id, false);
 
+                let mut skm = SUPER_KEY.write().unwrap();
+
                 DB.with(|db| {
-                    SUPER_KEY.unlock_screen_lock_bound_key(
+                    skm.unlock_screen_lock_bound_key(
                         &mut db.borrow_mut(),
                         user_id as u32,
                         &password,
@@ -159,10 +168,9 @@
                 // Unlock super key.
                 if let UserState::Uninitialized = DB
                     .with(|db| {
-                        UserState::get_with_password_unlock(
+                        skm.unlock_and_get_user_state(
                             &mut db.borrow_mut(),
                             &LEGACY_IMPORTER,
-                            &SUPER_KEY,
                             user_id as u32,
                             &password,
                         )
@@ -177,21 +185,23 @@
                 Ok(())
             }
             (LockScreenEvent::UNLOCK, None) => {
-                check_keystore_permission(KeystorePerm::unlock())
+                check_keystore_permission(KeystorePerm::Unlock)
                     .context("In on_lock_screen_event: Unlock.")?;
                 ENFORCEMENTS.set_device_locked(user_id, false);
+                let mut skm = SUPER_KEY.write().unwrap();
                 DB.with(|db| {
-                    SUPER_KEY.try_unlock_user_with_biometric(&mut db.borrow_mut(), user_id as u32)
+                    skm.try_unlock_user_with_biometric(&mut db.borrow_mut(), user_id as u32)
                 })
                 .context("In on_lock_screen_event: try_unlock_user_with_biometric failed")?;
                 Ok(())
             }
             (LockScreenEvent::LOCK, None) => {
-                check_keystore_permission(KeystorePerm::lock())
+                check_keystore_permission(KeystorePerm::Lock)
                     .context("In on_lock_screen_event: Lock")?;
                 ENFORCEMENTS.set_device_locked(user_id, true);
+                let mut skm = SUPER_KEY.write().unwrap();
                 DB.with(|db| {
-                    SUPER_KEY.lock_screen_lock_bound_key(
+                    skm.lock_screen_lock_bound_key(
                         &mut db.borrow_mut(),
                         user_id as u32,
                         unlocking_sids.unwrap_or(&[]),
@@ -215,7 +225,7 @@
     ) -> Result<AuthorizationTokens> {
         // Check permission. Function should return if this failed. Therefore having '?' at the end
         // is very important.
-        check_keystore_permission(KeystorePerm::get_auth_token())
+        check_keystore_permission(KeystorePerm::GetAuthToken)
             .context("In get_auth_tokens_for_credstore.")?;
 
         // If the challenge is zero, return error
@@ -265,7 +275,7 @@
         challenge: i64,
         secure_user_id: i64,
         auth_token_max_age_millis: i64,
-    ) -> binder::public_api::Result<AuthorizationTokens> {
+    ) -> binder::Result<AuthorizationTokens> {
         let _wp = wd::watch_millis("IKeystoreAuthorization::getAuthTokensForCredStore", 500);
         map_or_log_err(
             self.get_auth_tokens_for_credstore(
diff --git a/keystore2/src/boot_level_keys.rs b/keystore2/src/boot_level_keys.rs
index 1110caf..08c52af 100644
--- a/keystore2/src/boot_level_keys.rs
+++ b/keystore2/src/boot_level_keys.rs
@@ -243,40 +243,40 @@
     fn test_output_is_consistent() -> Result<()> {
         let initial_key = b"initial key";
         let mut blkc = BootLevelKeyCache::new(ZVec::try_from(initial_key as &[u8])?);
-        assert_eq!(true, blkc.level_accessible(0));
-        assert_eq!(true, blkc.level_accessible(9));
-        assert_eq!(true, blkc.level_accessible(10));
-        assert_eq!(true, blkc.level_accessible(100));
+        assert!(blkc.level_accessible(0));
+        assert!(blkc.level_accessible(9));
+        assert!(blkc.level_accessible(10));
+        assert!(blkc.level_accessible(100));
         let v0 = blkc.aes_key(0).unwrap().unwrap();
         let v10 = blkc.aes_key(10).unwrap().unwrap();
         assert_eq!(Some(&v0), blkc.aes_key(0)?.as_ref());
         assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref());
         blkc.advance_boot_level(5)?;
-        assert_eq!(false, blkc.level_accessible(0));
-        assert_eq!(true, blkc.level_accessible(9));
-        assert_eq!(true, blkc.level_accessible(10));
-        assert_eq!(true, blkc.level_accessible(100));
+        assert!(!blkc.level_accessible(0));
+        assert!(blkc.level_accessible(9));
+        assert!(blkc.level_accessible(10));
+        assert!(blkc.level_accessible(100));
         assert_eq!(None, blkc.aes_key(0)?);
         assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref());
         blkc.advance_boot_level(10)?;
-        assert_eq!(false, blkc.level_accessible(0));
-        assert_eq!(false, blkc.level_accessible(9));
-        assert_eq!(true, blkc.level_accessible(10));
-        assert_eq!(true, blkc.level_accessible(100));
+        assert!(!blkc.level_accessible(0));
+        assert!(!blkc.level_accessible(9));
+        assert!(blkc.level_accessible(10));
+        assert!(blkc.level_accessible(100));
         assert_eq!(None, blkc.aes_key(0)?);
         assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref());
         blkc.advance_boot_level(0)?;
-        assert_eq!(false, blkc.level_accessible(0));
-        assert_eq!(false, blkc.level_accessible(9));
-        assert_eq!(true, blkc.level_accessible(10));
-        assert_eq!(true, blkc.level_accessible(100));
+        assert!(!blkc.level_accessible(0));
+        assert!(!blkc.level_accessible(9));
+        assert!(blkc.level_accessible(10));
+        assert!(blkc.level_accessible(100));
         assert_eq!(None, blkc.aes_key(0)?);
         assert_eq!(Some(v10), blkc.aes_key(10)?);
         blkc.finish();
-        assert_eq!(false, blkc.level_accessible(0));
-        assert_eq!(false, blkc.level_accessible(9));
-        assert_eq!(false, blkc.level_accessible(10));
-        assert_eq!(false, blkc.level_accessible(100));
+        assert!(!blkc.level_accessible(0));
+        assert!(!blkc.level_accessible(9));
+        assert!(!blkc.level_accessible(10));
+        assert!(!blkc.level_accessible(100));
         assert_eq!(None, blkc.aes_key(0)?);
         assert_eq!(None, blkc.aes_key(10)?);
         Ok(())
diff --git a/keystore2/src/crypto/Android.bp b/keystore2/src/crypto/Android.bp
index 3ba47cd..c3f6f3c 100644
--- a/keystore2/src/crypto/Android.bp
+++ b/keystore2/src/crypto/Android.bp
@@ -35,6 +35,7 @@
         "libkeystore2_crypto",
         "libcrypto",
     ],
+    vendor_available: true,
 }
 
 cc_library {
@@ -48,6 +49,7 @@
         "libcrypto",
         "liblog",
     ],
+    vendor_available: true,
 }
 
 rust_bindgen {
@@ -56,9 +58,11 @@
     crate_name: "keystore2_crypto_bindgen",
     source_stem: "bindings",
     host_supported: true,
+    vendor_available: true,
     shared_libs: ["libcrypto"],
     bindgen_flags: [
         "--size_t-is-usize",
+        "--allowlist-function", "hmacSha256",
         "--allowlist-function", "randomBytes",
         "--allowlist-function", "AES_gcm_encrypt",
         "--allowlist-function", "AES_gcm_decrypt",
@@ -125,3 +129,13 @@
         "libcrypto",
     ],
 }
+
+rust_test {
+    name: "libkeystore2_crypto_bindgen_test",
+    srcs: [":libkeystore2_crypto_bindgen"],
+    crate_name: "keystore2_crypto_bindgen_test",
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    clippy_lints: "none",
+    lints: "none",
+}
diff --git a/keystore2/src/crypto/certificate_utils.cpp b/keystore2/src/crypto/certificate_utils.cpp
index 24b3793..64bf1d0 100644
--- a/keystore2/src/crypto/certificate_utils.cpp
+++ b/keystore2/src/crypto/certificate_utils.cpp
@@ -19,6 +19,7 @@
 #include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/mem.h>
+#include <openssl/ossl_typ.h>
 #include <openssl/x509v3.h>
 
 #include <functional>
@@ -517,10 +518,7 @@
     return ASN1_STRING_Ptr(algo_str);
 }
 
-CertUtilsError makeAndSetAlgo(X509_ALGOR* algo_field, Algo algo, Padding padding, Digest digest) {
-    if (algo_field == nullptr) {
-        return CertUtilsError::UnexpectedNullPointer;
-    }
+std::variant<CertUtilsError, X509_ALGOR_Ptr> makeAlgo(Algo algo, Padding padding, Digest digest) {
     ASN1_STRING_Ptr param;
     int param_type = V_ASN1_UNDEF;
     int nid = 0;
@@ -589,23 +587,29 @@
         return CertUtilsError::InvalidArgument;
     }
 
-    if (!X509_ALGOR_set0(algo_field, OBJ_nid2obj(nid), param_type, param.get())) {
+    X509_ALGOR_Ptr result(X509_ALGOR_new());
+    if (!result) {
+        return CertUtilsError::MemoryAllocation;
+    }
+    if (!X509_ALGOR_set0(result.get(), OBJ_nid2obj(nid), param_type, param.get())) {
         return CertUtilsError::Encoding;
     }
     // The X509 struct took ownership.
     param.release();
-    return CertUtilsError::Ok;
+    return result;
 }
 
 // This function allows for signing a
 CertUtilsError signCertWith(X509* certificate,
                             std::function<std::vector<uint8_t>(const uint8_t*, size_t)> sign,
                             Algo algo, Padding padding, Digest digest) {
-    if (auto error = makeAndSetAlgo(certificate->sig_alg, algo, padding, digest)) {
-        return error;
+    auto algo_objV = makeAlgo(algo, padding, digest);
+    if (auto error = std::get_if<CertUtilsError>(&algo_objV)) {
+        return *error;
     }
-    if (auto error = makeAndSetAlgo(certificate->cert_info->signature, algo, padding, digest)) {
-        return error;
+    auto& algo_obj = std::get<X509_ALGOR_Ptr>(algo_objV);
+    if (!X509_set1_signature_algo(certificate, algo_obj.get())) {
+        return CertUtilsError::BoringSsl;
     }
 
     uint8_t* cert_buf = nullptr;
@@ -620,13 +624,10 @@
         return CertUtilsError::SignatureFailed;
     }
 
-    if (!ASN1_STRING_set(certificate->signature, signature.data(), signature.size())) {
+    if (!X509_set1_signature_value(certificate, signature.data(), signature.size())) {
         return CertUtilsError::BoringSsl;
     }
 
-    certificate->signature->flags &= ~(0x07);
-    certificate->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
-
     return CertUtilsError::Ok;
 }
 
diff --git a/keystore2/src/crypto/crypto.cpp b/keystore2/src/crypto/crypto.cpp
index 5d360a1..34a9a40 100644
--- a/keystore2/src/crypto/crypto.cpp
+++ b/keystore2/src/crypto/crypto.cpp
@@ -25,6 +25,7 @@
 #include <openssl/ecdh.h>
 #include <openssl/evp.h>
 #include <openssl/hkdf.h>
+#include <openssl/hmac.h>
 #include <openssl/rand.h>
 #include <openssl/x509.h>
 
@@ -66,6 +67,14 @@
     return cipher;
 }
 
+bool hmacSha256(const uint8_t* key, size_t key_size, const uint8_t* msg, size_t msg_size,
+                uint8_t* out, size_t out_size) {
+    const EVP_MD* digest = EVP_sha256();
+    unsigned int actual_out_size = out_size;
+    uint8_t* p = HMAC(digest, key, key_size, msg, msg_size, out, &actual_out_size);
+    return (p != nullptr);
+}
+
 bool randomBytes(uint8_t* out, size_t len) {
     return RAND_bytes(out, len);
 }
diff --git a/keystore2/src/crypto/crypto.hpp b/keystore2/src/crypto/crypto.hpp
index f841eb3..d66532f 100644
--- a/keystore2/src/crypto/crypto.hpp
+++ b/keystore2/src/crypto/crypto.hpp
@@ -22,6 +22,8 @@
 #include <stddef.h>
 
 extern "C" {
+  bool hmacSha256(const uint8_t* key, size_t key_size, const uint8_t* msg, size_t msg_size,
+                  uint8_t* out, size_t out_size);
   bool randomBytes(uint8_t* out, size_t len);
   bool AES_gcm_encrypt(const uint8_t* in, uint8_t* out, size_t len,
                        const uint8_t* key, size_t key_size, const uint8_t* iv, uint8_t* tag);
diff --git a/keystore2/src/crypto/error.rs b/keystore2/src/crypto/error.rs
index a369012..48a2d4c 100644
--- a/keystore2/src/crypto/error.rs
+++ b/keystore2/src/crypto/error.rs
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 //! This module implements Error for the keystore2_crypto library.
+use crate::zvec;
 
 /// Crypto specific error codes.
 #[derive(Debug, thiserror::Error, Eq, PartialEq)]
@@ -93,4 +94,12 @@
     /// This is returned if the C implementation of extractSubjectFromCertificate failed.
     #[error("Failed to extract certificate subject.")]
     ExtractSubjectFailed,
+
+    /// This is returned if the C implementation of hmacSha256 failed.
+    #[error("Failed to calculate HMAC-SHA256.")]
+    HmacSha256Failed,
+
+    /// Zvec error.
+    #[error(transparent)]
+    ZVec(#[from] zvec::Error),
 }
diff --git a/keystore2/src/crypto/include/certificate_utils.h b/keystore2/src/crypto/include/certificate_utils.h
index b483d88..cad82b6 100644
--- a/keystore2/src/crypto/include/certificate_utils.h
+++ b/keystore2/src/crypto/include/certificate_utils.h
@@ -39,6 +39,7 @@
 DEFINE_OPENSSL_OBJECT_POINTER(ASN1_TIME);
 DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY);
 DEFINE_OPENSSL_OBJECT_POINTER(X509);
+DEFINE_OPENSSL_OBJECT_POINTER(X509_ALGOR);
 DEFINE_OPENSSL_OBJECT_POINTER(X509_EXTENSION);
 DEFINE_OPENSSL_OBJECT_POINTER(X509_NAME);
 DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY_CTX);
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index 5f8a2ef..14bdf04 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -16,11 +16,11 @@
 //! Keystore 2.0.
 
 mod error;
-mod zvec;
+pub mod zvec;
 pub use error::Error;
 use keystore2_crypto_bindgen::{
-    extractSubjectFromCertificate, generateKeyFromPassword, randomBytes, AES_gcm_decrypt,
-    AES_gcm_encrypt, ECDHComputeKey, ECKEYGenerateKey, ECKEYMarshalPrivateKey,
+    extractSubjectFromCertificate, generateKeyFromPassword, hmacSha256, randomBytes,
+    AES_gcm_decrypt, 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,
 };
@@ -39,6 +39,8 @@
 pub const AES_128_KEY_LENGTH: usize = 16;
 /// Length of the expected salt for key from password generation.
 pub const SALT_LENGTH: usize = 16;
+/// Length of an HMAC-SHA256 tag in bytes.
+pub const HMAC_SHA256_LEN: usize = 32;
 
 /// Older versions of keystore produced IVs with four extra
 /// ignored zero bytes at the end; recognise and trim those.
@@ -72,6 +74,21 @@
     }
 }
 
+/// Perform HMAC-SHA256.
+pub fn hmac_sha256(key: &[u8], msg: &[u8]) -> Result<Vec<u8>, Error> {
+    let mut tag = vec![0; HMAC_SHA256_LEN];
+    // Safety: The first two pairs of arguments must point to const buffers with
+    // size given by the second arg of the pair.  The final pair of arguments
+    // must point to an output buffer with size given by the second arg of the
+    // pair.
+    match unsafe {
+        hmacSha256(key.as_ptr(), key.len(), msg.as_ptr(), msg.len(), tag.as_mut_ptr(), tag.len())
+    } {
+        true => Ok(tag),
+        false => Err(Error::HmacSha256Failed),
+    }
+}
+
 /// Uses AES GCM to decipher a message given an initialization vector, aead tag, and key.
 /// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based
 /// on the key length.
@@ -565,4 +582,18 @@
         assert_eq!(left_key, right_key);
         Ok(())
     }
+
+    #[test]
+    fn test_hmac_sha256() {
+        let key = b"This is the key";
+        let msg1 = b"This is a message";
+        let msg2 = b"This is another message";
+        let tag1a = hmac_sha256(key, msg1).unwrap();
+        assert_eq!(tag1a.len(), HMAC_SHA256_LEN);
+        let tag1b = hmac_sha256(key, msg1).unwrap();
+        assert_eq!(tag1a, tag1b);
+        let tag2 = hmac_sha256(key, msg2).unwrap();
+        assert_eq!(tag2.len(), HMAC_SHA256_LEN);
+        assert_ne!(tag1a, tag2);
+    }
 }
diff --git a/keystore2/src/crypto/tests/certificate_utils_test.cpp b/keystore2/src/crypto/tests/certificate_utils_test.cpp
index ebd6792..bd94928 100644
--- a/keystore2/src/crypto/tests/certificate_utils_test.cpp
+++ b/keystore2/src/crypto/tests/certificate_utils_test.cpp
@@ -334,4 +334,4 @@
     // And one millisecond earlier must be GeneralizedTime.
     // This also checks that the rounding direction does not flip when the input is negative.
     ASSERT_EQ(std::string(toTimeString(-631152000001)->data()), "19491231235959Z");
-}
\ No newline at end of file
+}
diff --git a/keystore2/src/crypto/zvec.rs b/keystore2/src/crypto/zvec.rs
index 78b474e..5a173c3 100644
--- a/keystore2/src/crypto/zvec.rs
+++ b/keystore2/src/crypto/zvec.rs
@@ -12,7 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::error::Error;
+//! Implements ZVec, a vector that is mlocked during its lifetime and zeroed
+//! when dropped.
+
 use nix::sys::mman::{mlock, munlock};
 use std::convert::TryFrom;
 use std::fmt;
@@ -29,6 +31,14 @@
     len: usize,
 }
 
+/// ZVec specific error codes.
+#[derive(Debug, thiserror::Error, Eq, PartialEq)]
+pub enum Error {
+    /// Underlying libc error.
+    #[error(transparent)]
+    NixError(#[from] nix::Error),
+}
+
 impl ZVec {
     /// Create a ZVec with the given size.
     pub fn new(size: usize) -> Result<Self, Error> {
@@ -48,6 +58,14 @@
             self.len = len;
         }
     }
+
+    /// Attempts to make a clone of the Zvec. This may fail due trying to mlock
+    /// the new memory region.
+    pub fn try_clone(&self) -> Result<Self, Error> {
+        let mut result = Self::new(self.len())?;
+        result[..].copy_from_slice(&self[..]);
+        Ok(result)
+    }
 }
 
 impl Drop for ZVec {
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index de23328..7713618 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -82,7 +82,7 @@
 #[cfg(not(test))]
 use rand::prelude::random;
 use rusqlite::{
-    params,
+    params, params_from_iter,
     types::FromSql,
     types::FromSqlResult,
     types::ToSqlOutput,
@@ -142,7 +142,7 @@
             let db_tag: i64 = row.get(0).context("Failed to read tag.")?;
             metadata.insert(
                 db_tag,
-                KeyMetaEntry::new_from_sql(db_tag, &SqlField::new(1, &row))
+                KeyMetaEntry::new_from_sql(db_tag, &SqlField::new(1, row))
                     .context("Failed to read KeyMetaEntry.")?,
             );
             Ok(())
@@ -217,7 +217,7 @@
             let db_tag: i64 = row.get(0).context("Failed to read tag.")?;
             metadata.insert(
                 db_tag,
-                BlobMetaEntry::new_from_sql(db_tag, &SqlField::new(1, &row))
+                BlobMetaEntry::new_from_sql(db_tag, &SqlField::new(1, row))
                     .context("Failed to read BlobMetaEntry.")?,
             );
             Ok(())
@@ -323,6 +323,8 @@
     0x41, 0xe3, 0xb9, 0xce, 0x27, 0x58, 0x4e, 0x91, 0xbc, 0xfd, 0xa5, 0x5d, 0x91, 0x85, 0xab, 0x11,
 ]);
 
+static EXPIRATION_BUFFER_MS: i64 = 20000;
+
 /// Indicates how the sensitive part of this key blob is encrypted.
 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
 pub enum EncryptedBy {
@@ -388,12 +390,12 @@
     }
 
     /// Returns unix epoch time in milliseconds.
-    pub fn to_millis_epoch(&self) -> i64 {
+    pub fn to_millis_epoch(self) -> i64 {
         self.0
     }
 
     /// Returns unix epoch time in seconds.
-    pub fn to_secs_epoch(&self) -> i64 {
+    pub fn to_secs_epoch(self) -> i64 {
         self.0 / 1000
     }
 }
@@ -578,6 +580,36 @@
     cert_chain: Option<Vec<u8>>,
 }
 
+/// This type represents a Blob with its metadata and an optional superseded blob.
+#[derive(Debug)]
+pub struct BlobInfo<'a> {
+    blob: &'a [u8],
+    metadata: &'a BlobMetaData,
+    /// Superseded blobs are an artifact of legacy import. In some rare occasions
+    /// the key blob needs to be upgraded during import. In that case two
+    /// blob are imported, the superseded one will have to be imported first,
+    /// so that the garbage collector can reap it.
+    superseded_blob: Option<(&'a [u8], &'a BlobMetaData)>,
+}
+
+impl<'a> BlobInfo<'a> {
+    /// Create a new instance of blob info with blob and corresponding metadata
+    /// and no superseded blob info.
+    pub fn new(blob: &'a [u8], metadata: &'a BlobMetaData) -> Self {
+        Self { blob, metadata, superseded_blob: None }
+    }
+
+    /// Create a new instance of blob info with blob and corresponding metadata
+    /// as well as superseded blob info.
+    pub fn new_with_superseded(
+        blob: &'a [u8],
+        metadata: &'a BlobMetaData,
+        superseded_blob: Option<(&'a [u8], &'a BlobMetaData)>,
+    ) -> Self {
+        Self { blob, metadata, superseded_blob }
+    }
+}
+
 impl CertificateInfo {
     /// Constructs a new CertificateInfo object from `cert` and `cert_chain`
     pub fn new(cert: Option<Vec<u8>>, cert_chain: Option<Vec<u8>>) -> Self {
@@ -832,7 +864,7 @@
     const UPGRADERS: &'static [fn(&Transaction) -> Result<u32>] = &[Self::from_0_to_1];
 
     /// Name of the file that holds the cross-boot persistent database.
-    pub const PERSISTENT_DB_FILENAME: &'static str = &"persistent.sqlite";
+    pub const PERSISTENT_DB_FILENAME: &'static str = "persistent.sqlite";
 
     /// This will create a new database connection connecting the two
     /// files persistent.sqlite and perboot.sqlite in the given directory.
@@ -842,7 +874,7 @@
     pub fn new(db_root: &Path, gc: Option<Arc<Gc>>) -> Result<Self> {
         let _wp = wd::watch_millis("KeystoreDB::new", 500);
 
-        let persistent_path = Self::make_persistent_path(&db_root)?;
+        let persistent_path = Self::make_persistent_path(db_root)?;
         let conn = Self::make_connection(&persistent_path)?;
 
         let mut db = Self { conn, gc, perboot: perboot::PERBOOT_DB.clone() };
@@ -1029,7 +1061,7 @@
         params: &[&str],
     ) -> Result<StorageStats> {
         let (total, unused) = self.with_transaction(TransactionBehavior::Deferred, |tx| {
-            tx.query_row(query, params, |row| Ok((row.get(0)?, row.get(1)?)))
+            tx.query_row(query, params_from_iter(params), |row| Ok((row.get(0)?, row.get(1)?)))
                 .with_context(|| {
                     format!("get_storage_stat: Error size of storage type {}", storage_type.0)
                 })
@@ -1244,7 +1276,7 @@
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let key_descriptor =
                 KeyDescriptor { domain, nspace, alias: Some(alias.to_string()), blob: None };
-            let result = Self::load_key_entry_id(&tx, &key_descriptor, key_type);
+            let result = Self::load_key_entry_id(tx, &key_descriptor, key_type);
             match result {
                 Ok(_) => Ok(true),
                 Err(error) => match error.root_cause().downcast_ref::<KsError>() {
@@ -1290,7 +1322,7 @@
             key_metadata.store_in_db(key_id, tx).context("KeyMetaData::store_in_db failed")?;
 
             Self::set_blob_internal(
-                &tx,
+                tx,
                 key_id,
                 SubComponentType::KEY_BLOB,
                 Some(blob),
@@ -1320,10 +1352,10 @@
                 alias: Some(key_type.alias.into()),
                 blob: None,
             };
-            let id = Self::load_key_entry_id(&tx, &key_descriptor, KeyType::Super);
+            let id = Self::load_key_entry_id(tx, &key_descriptor, KeyType::Super);
             match id {
                 Ok(id) => {
-                    let key_entry = Self::load_key_components(&tx, KeyEntryLoadBits::KM, id)
+                    let key_entry = Self::load_key_components(tx, KeyEntryLoadBits::KM, id)
                         .context("In load_super_key. Failed to load key entry.")?;
                     Ok(Some((KEY_ID_LOCK.get(id), key_entry)))
                 }
@@ -1383,7 +1415,7 @@
             let (id, entry) = match id {
                 Some(id) => (
                     id,
-                    Self::load_key_components(&tx, KeyEntryLoadBits::KM, id)
+                    Self::load_key_components(tx, KeyEntryLoadBits::KM, id)
                         .context("In get_or_create_key_with.")?,
                 ),
 
@@ -1409,7 +1441,7 @@
                     let (blob, metadata) =
                         create_new_key().context("In get_or_create_key_with.")?;
                     Self::set_blob_internal(
-                        &tx,
+                        tx,
                         id,
                         SubComponentType::KEY_BLOB,
                         Some(&blob),
@@ -1560,7 +1592,7 @@
                 .context("In create_key_entry")?,
             );
             Self::set_blob_internal(
-                &tx,
+                tx,
                 key_id.0,
                 SubComponentType::KEY_BLOB,
                 Some(private_key),
@@ -1569,7 +1601,7 @@
             let mut metadata = KeyMetaData::new();
             metadata.add(KeyMetaEntry::AttestationMacedPublicKey(maced_public_key.to_vec()));
             metadata.add(KeyMetaEntry::AttestationRawPubKey(raw_public_key.to_vec()));
-            metadata.store_in_db(key_id.0, &tx)?;
+            metadata.store_in_db(key_id.0, tx)?;
             Ok(()).no_gc()
         })
         .context("In create_attestation_key_entry")
@@ -1592,7 +1624,7 @@
         let _wp = wd::watch_millis("KeystoreDB::set_blob", 500);
 
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
-            Self::set_blob_internal(&tx, key_id.0, sc_type, blob, blob_metadata).need_gc()
+            Self::set_blob_internal(tx, key_id.0, sc_type, blob, blob_metadata).need_gc()
         })
         .context("In set_blob.")
     }
@@ -1606,7 +1638,7 @@
 
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             Self::set_blob_internal(
-                &tx,
+                tx,
                 Self::UNASSIGNED_KEY_ID,
                 SubComponentType::KEY_BLOB,
                 Some(blob),
@@ -1699,7 +1731,7 @@
     #[cfg(test)]
     fn insert_key_metadata(&mut self, key_id: &KeyIdGuard, metadata: &KeyMetaData) -> Result<()> {
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
-            metadata.store_in_db(key_id.0, &tx).no_gc()
+            metadata.store_in_db(key_id.0, tx).no_gc()
         })
         .context("In insert_key_metadata.")
     }
@@ -1761,16 +1793,16 @@
             metadata.add(KeyMetaEntry::AttestationExpirationDate(DateTime::from_millis_epoch(
                 expiration_date,
             )));
-            metadata.store_in_db(key_id, &tx).context("Failed to insert key metadata.")?;
+            metadata.store_in_db(key_id, tx).context("Failed to insert key metadata.")?;
             Self::set_blob_internal(
-                &tx,
+                tx,
                 key_id,
                 SubComponentType::CERT_CHAIN,
                 Some(cert_chain),
                 None,
             )
             .context("Failed to insert cert chain")?;
-            Self::set_blob_internal(&tx, key_id, SubComponentType::CERT, Some(batch_cert), None)
+            Self::set_blob_internal(tx, key_id, SubComponentType::CERT, Some(batch_cert), None)
                 .context("Failed to insert cert")?;
             Ok(()).no_gc()
         })
@@ -1909,12 +1941,15 @@
                 )?
                 .collect::<rusqlite::Result<Vec<(i64, DateTime)>>>()
                 .context("Failed to get date metadata")?;
+            // Calculate curr_time with a discount factor to avoid a key that's milliseconds away
+            // from expiration dodging this delete call.
             let curr_time = DateTime::from_millis_epoch(
-                SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis() as i64,
+                SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis() as i64
+                    + EXPIRATION_BUFFER_MS,
             );
             let mut num_deleted = 0;
             for id in key_ids_to_check.iter().filter(|kt| kt.1 < curr_time).map(|kt| kt.0) {
-                if Self::mark_unreferenced(&tx, id)? {
+                if Self::mark_unreferenced(tx, id)? {
                     num_deleted += 1;
                 }
             }
@@ -1941,7 +1976,7 @@
                 .context("Failed to execute statement")?;
             let num_deleted = keys_to_delete
                 .iter()
-                .map(|id| Self::mark_unreferenced(&tx, *id))
+                .map(|id| Self::mark_unreferenced(tx, *id))
                 .collect::<Result<Vec<bool>>>()
                 .context("Failed to execute mark_unreferenced on a keyid")?
                 .into_iter()
@@ -2019,6 +2054,41 @@
         .context("In get_attestation_pool_status: ")
     }
 
+    fn query_kid_for_attestation_key_and_cert_chain(
+        &self,
+        tx: &Transaction,
+        domain: Domain,
+        namespace: i64,
+        km_uuid: &Uuid,
+    ) -> Result<Option<i64>> {
+        let mut stmt = tx.prepare(
+            "SELECT id
+             FROM persistent.keyentry
+             WHERE key_type = ?
+                   AND domain = ?
+                   AND namespace = ?
+                   AND state = ?
+                   AND km_uuid = ?;",
+        )?;
+        let rows = stmt
+            .query_map(
+                params![
+                    KeyType::Attestation,
+                    domain.0 as u32,
+                    namespace,
+                    KeyLifeCycle::Live,
+                    km_uuid
+                ],
+                |row| row.get(0),
+            )?
+            .collect::<rusqlite::Result<Vec<i64>>>()
+            .context("query failed.")?;
+        if rows.is_empty() {
+            return Ok(None);
+        }
+        Ok(Some(rows[0]))
+    }
+
     /// Fetches the private key and corresponding certificate chain assigned to a
     /// domain/namespace pair. Will either return nothing if the domain/namespace is
     /// not assigned, or one CertificateChain.
@@ -2027,7 +2097,7 @@
         domain: Domain,
         namespace: i64,
         km_uuid: &Uuid,
-    ) -> Result<Option<CertificateChain>> {
+    ) -> Result<Option<(KeyIdGuard, CertificateChain)>> {
         let _wp = wd::watch_millis("KeystoreDB::retrieve_attestation_key_and_cert_chain", 500);
 
         match domain {
@@ -2037,69 +2107,71 @@
                     .context(format!("Domain {:?} must be either App or SELinux.", domain));
             }
         }
-        self.with_transaction(TransactionBehavior::Deferred, |tx| {
-            let mut stmt = tx.prepare(
-                "SELECT subcomponent_type, blob
-             FROM persistent.blobentry
-             WHERE keyentryid IN
-                (SELECT id
-                 FROM persistent.keyentry
-                 WHERE key_type = ?
-                       AND domain = ?
-                       AND namespace = ?
-                       AND state = ?
-                       AND km_uuid = ?);",
-            )?;
-            let rows = stmt
-                .query_map(
-                    params![
-                        KeyType::Attestation,
-                        domain.0 as u32,
-                        namespace,
-                        KeyLifeCycle::Live,
-                        km_uuid
-                    ],
-                    |row| Ok((row.get(0)?, row.get(1)?)),
-                )?
-                .collect::<rusqlite::Result<Vec<(SubComponentType, Vec<u8>)>>>()
-                .context("query failed.")?;
-            if rows.is_empty() {
-                return Ok(None).no_gc();
-            } else if rows.len() != 3 {
-                return Err(KsError::sys()).context(format!(
-                    concat!(
-                        "Expected to get a single attestation",
-                        "key, cert, and cert chain for a total of 3 entries, but instead got {}."
-                    ),
-                    rows.len()
-                ));
-            }
-            let mut km_blob: Vec<u8> = Vec::new();
-            let mut cert_chain_blob: Vec<u8> = Vec::new();
-            let mut batch_cert_blob: Vec<u8> = Vec::new();
-            for row in rows {
-                let sub_type: SubComponentType = row.0;
-                match sub_type {
-                    SubComponentType::KEY_BLOB => {
-                        km_blob = row.1;
-                    }
-                    SubComponentType::CERT_CHAIN => {
-                        cert_chain_blob = row.1;
-                    }
-                    SubComponentType::CERT => {
-                        batch_cert_blob = row.1;
-                    }
-                    _ => Err(KsError::sys()).context("Unknown or incorrect subcomponent type.")?,
+
+        self.delete_expired_attestation_keys().context(
+            "In retrieve_attestation_key_and_cert_chain: failed to prune expired attestation keys",
+        )?;
+        let tx = self.conn.unchecked_transaction().context(
+            "In retrieve_attestation_key_and_cert_chain: Failed to initialize transaction.",
+        )?;
+        let key_id: i64 = match self
+            .query_kid_for_attestation_key_and_cert_chain(&tx, domain, namespace, km_uuid)?
+        {
+            None => return Ok(None),
+            Some(kid) => kid,
+        };
+        tx.commit()
+            .context("In retrieve_attestation_key_and_cert_chain: Failed to commit keyid query")?;
+        let key_id_guard = KEY_ID_LOCK.get(key_id);
+        let tx = self.conn.unchecked_transaction().context(
+            "In retrieve_attestation_key_and_cert_chain: Failed to initialize transaction.",
+        )?;
+        let mut stmt = tx.prepare(
+            "SELECT subcomponent_type, blob
+            FROM persistent.blobentry
+            WHERE keyentryid = ?;",
+        )?;
+        let rows = stmt
+            .query_map(params![key_id_guard.id()], |row| Ok((row.get(0)?, row.get(1)?)))?
+            .collect::<rusqlite::Result<Vec<(SubComponentType, Vec<u8>)>>>()
+            .context("query failed.")?;
+        if rows.is_empty() {
+            return Ok(None);
+        } else if rows.len() != 3 {
+            return Err(KsError::sys()).context(format!(
+                concat!(
+                    "Expected to get a single attestation",
+                    "key, cert, and cert chain for a total of 3 entries, but instead got {}."
+                ),
+                rows.len()
+            ));
+        }
+        let mut km_blob: Vec<u8> = Vec::new();
+        let mut cert_chain_blob: Vec<u8> = Vec::new();
+        let mut batch_cert_blob: Vec<u8> = Vec::new();
+        for row in rows {
+            let sub_type: SubComponentType = row.0;
+            match sub_type {
+                SubComponentType::KEY_BLOB => {
+                    km_blob = row.1;
                 }
+                SubComponentType::CERT_CHAIN => {
+                    cert_chain_blob = row.1;
+                }
+                SubComponentType::CERT => {
+                    batch_cert_blob = row.1;
+                }
+                _ => Err(KsError::sys()).context("Unknown or incorrect subcomponent type.")?,
             }
-            Ok(Some(CertificateChain {
+        }
+        Ok(Some((
+            key_id_guard,
+            CertificateChain {
                 private_key: ZVec::try_from(km_blob)?,
                 batch_cert: batch_cert_blob,
                 cert_chain: cert_chain_blob,
-            }))
-            .no_gc()
-        })
-        .context("In retrieve_attestation_key_and_cert_chain:")
+            },
+        )))
     }
 
     /// Updates the alias column of the given key id `newid` with the given alias,
@@ -2227,13 +2299,13 @@
     /// fields, and rebinds the given alias to the new key.
     /// The boolean returned is a hint for the garbage collector. If true, a key was replaced,
     /// is now unreferenced and needs to be collected.
-    #[allow(clippy::clippy::too_many_arguments)]
+    #[allow(clippy::too_many_arguments)]
     pub fn store_new_key(
         &mut self,
         key: &KeyDescriptor,
         key_type: KeyType,
         params: &[KeyParameter],
-        blob_info: &(&[u8], &BlobMetaData),
+        blob_info: &BlobInfo,
         cert_info: &CertificateInfo,
         metadata: &KeyMetaData,
         km_uuid: &Uuid,
@@ -2253,17 +2325,37 @@
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let key_id = Self::create_key_entry_internal(tx, &domain, namespace, key_type, km_uuid)
                 .context("Trying to create new key entry.")?;
-            let (blob, blob_metadata) = *blob_info;
+            let BlobInfo { blob, metadata: blob_metadata, superseded_blob } = *blob_info;
+
+            // In some occasions the key blob is already upgraded during the import.
+            // In order to make sure it gets properly deleted it is inserted into the
+            // database here and then immediately replaced by the superseding blob.
+            // The garbage collector will then subject the blob to deleteKey of the
+            // KM back end to permanently invalidate the key.
+            let need_gc = if let Some((blob, blob_metadata)) = superseded_blob {
+                Self::set_blob_internal(
+                    tx,
+                    key_id.id(),
+                    SubComponentType::KEY_BLOB,
+                    Some(blob),
+                    Some(blob_metadata),
+                )
+                .context("Trying to insert superseded key blob.")?;
+                true
+            } else {
+                false
+            };
+
             Self::set_blob_internal(
                 tx,
                 key_id.id(),
                 SubComponentType::KEY_BLOB,
                 Some(blob),
-                Some(&blob_metadata),
+                Some(blob_metadata),
             )
             .context("Trying to insert the key blob.")?;
             if let Some(cert) = &cert_info.cert {
-                Self::set_blob_internal(tx, key_id.id(), SubComponentType::CERT, Some(&cert), None)
+                Self::set_blob_internal(tx, key_id.id(), SubComponentType::CERT, Some(cert), None)
                     .context("Trying to insert the certificate.")?;
             }
             if let Some(cert_chain) = &cert_info.cert_chain {
@@ -2271,7 +2363,7 @@
                     tx,
                     key_id.id(),
                     SubComponentType::CERT_CHAIN,
-                    Some(&cert_chain),
+                    Some(cert_chain),
                     None,
                 )
                 .context("Trying to insert the certificate chain.")?;
@@ -2279,8 +2371,9 @@
             Self::insert_keyparameter_internal(tx, &key_id, params)
                 .context("Trying to insert key parameters.")?;
             metadata.store_in_db(key_id.id(), tx).context("Trying to insert key metadata.")?;
-            let need_gc = Self::rebind_alias(tx, &key_id, &alias, &domain, namespace, key_type)
-                .context("Trying to rebind alias.")?;
+            let need_gc = Self::rebind_alias(tx, &key_id, alias, &domain, namespace, key_type)
+                .context("Trying to rebind alias.")?
+                || need_gc;
             Ok(key_id).do_gc(need_gc)
         })
         .context("In store_new_key.")
@@ -2329,7 +2422,7 @@
 
             metadata.store_in_db(key_id.id(), tx).context("Trying to insert key metadata.")?;
 
-            let need_gc = Self::rebind_alias(tx, &key_id, &alias, &domain, namespace, key_type)
+            let need_gc = Self::rebind_alias(tx, &key_id, alias, &domain, namespace, key_type)
                 .context("Trying to rebind alias.")?;
             Ok(key_id).do_gc(need_gc)
         })
@@ -2398,7 +2491,7 @@
                 if access_key.domain == Domain::APP {
                     access_key.nspace = caller_uid as i64;
                 }
-                let key_id = Self::load_key_entry_id(&tx, &access_key, key_type)
+                let key_id = Self::load_key_entry_id(tx, &access_key, key_type)
                     .with_context(|| format!("With key.domain = {:?}.", access_key.domain))?;
 
                 Ok((key_id, access_key, None))
@@ -2563,7 +2656,7 @@
             let tag = Tag(row.get(0).context("Failed to read tag.")?);
             let sec_level = SecurityLevel(row.get(2).context("Failed to read sec_level.")?);
             parameters.push(
-                KeyParameter::new_from_sql(tag, &SqlField::new(1, &row), sec_level)
+                KeyParameter::new_from_sql(tag, &SqlField::new(1, row), sec_level)
                     .context("Failed to read KeyParameter.")?,
             );
             Ok(())
@@ -2939,7 +3032,7 @@
                         }
                     }
                 }
-                notify_gc = Self::mark_unreferenced(&tx, key_id)
+                notify_gc = Self::mark_unreferenced(tx, key_id)
                     .context("In unbind_keys_for_user.")?
                     || notify_gc;
             }
@@ -2953,16 +3046,15 @@
         load_bits: KeyEntryLoadBits,
         key_id: i64,
     ) -> Result<KeyEntry> {
-        let metadata = KeyMetaData::load_from_db(key_id, &tx).context("In load_key_components.")?;
+        let metadata = KeyMetaData::load_from_db(key_id, tx).context("In load_key_components.")?;
 
         let (has_km_blob, key_blob_info, cert_blob, cert_chain_blob) =
-            Self::load_blob_components(key_id, load_bits, &tx)
-                .context("In load_key_components.")?;
+            Self::load_blob_components(key_id, load_bits, tx).context("In load_key_components.")?;
 
-        let parameters = Self::load_key_parameters(key_id, &tx)
+        let parameters = Self::load_key_parameters(key_id, tx)
             .context("In load_key_components: Trying to load key parameters.")?;
 
-        let km_uuid = Self::get_key_km_uuid(&tx, key_id)
+        let km_uuid = Self::get_key_km_uuid(tx, key_id)
             .context("In load_key_components: Trying to get KM uuid.")?;
 
         Ok(KeyEntry {
@@ -3046,7 +3138,7 @@
             // But even if we load the access tuple by grant here, the permission
             // check denies the attempt to create a grant by grant descriptor.
             let (key_id, access_key_descriptor, _) =
-                Self::load_access_tuple(&tx, key, KeyType::Client, caller_uid)
+                Self::load_access_tuple(tx, key, KeyType::Client, caller_uid)
                     .context("In grant")?;
 
             // Perform access control. It is vital that we return here if the permission
@@ -3106,7 +3198,7 @@
             // Load the key_id and complete the access control tuple.
             // We ignore the access vector here because grants cannot be granted.
             let (key_id, access_key_descriptor, _) =
-                Self::load_access_tuple(&tx, key, KeyType::Client, caller_uid)
+                Self::load_access_tuple(tx, key, KeyType::Client, caller_uid)
                     .context("In ungrant.")?;
 
             // Perform access control. We must return here if the permission
@@ -3208,7 +3300,7 @@
 }
 
 #[cfg(test)]
-mod tests {
+pub mod tests {
 
     use super::*;
     use crate::key_parameter::{
@@ -3232,13 +3324,14 @@
     use std::collections::BTreeMap;
     use std::fmt::Write;
     use std::sync::atomic::{AtomicU8, Ordering};
-    use std::sync::Arc;
+    use std::sync::{Arc, RwLock};
     use std::thread;
     use std::time::{Duration, SystemTime};
+    use crate::utils::AesGcm;
     #[cfg(disabled)]
     use std::time::Instant;
 
-    fn new_test_db() -> Result<KeystoreDB> {
+    pub fn new_test_db() -> Result<KeystoreDB> {
         let conn = KeystoreDB::make_connection("file::memory:")?;
 
         let mut db = KeystoreDB { conn, gc: None, perboot: Arc::new(perboot::PerbootDB::new()) };
@@ -3252,7 +3345,7 @@
     where
         F: Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static,
     {
-        let super_key: Arc<SuperKeyManager> = Default::default();
+        let super_key: Arc<RwLock<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));
@@ -3457,15 +3550,18 @@
     #[test]
     fn test_store_signed_attestation_certificate_chain() -> Result<()> {
         let mut db = new_test_db()?;
-        let expiration_date: i64 = 20;
+        let expiration_date: i64 =
+            SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis() as i64
+                + EXPIRATION_BUFFER_MS
+                + 10000;
         let namespace: i64 = 30;
         let base_byte: u8 = 1;
         let loaded_values =
             load_attestation_key_pool(&mut db, expiration_date, namespace, base_byte)?;
         let chain =
             db.retrieve_attestation_key_and_cert_chain(Domain::APP, namespace, &KEYSTORE_UUID)?;
-        assert_eq!(true, chain.is_some());
-        let cert_chain = chain.unwrap();
+        assert!(chain.is_some());
+        let (_, cert_chain) = chain.unwrap();
         assert_eq!(cert_chain.private_key.to_vec(), loaded_values.priv_key);
         assert_eq!(cert_chain.batch_cert, loaded_values.batch_cert);
         assert_eq!(cert_chain.cert_chain, loaded_values.cert_chain);
@@ -3534,7 +3630,9 @@
             TempDir::new("test_remove_expired_certs_").expect("Failed to create temp dir.");
         let mut db = new_test_db_with_gc(temp_dir.path(), |_, _| Ok(()))?;
         let expiration_date: i64 =
-            SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis() as i64 + 10000;
+            SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis() as i64
+                + EXPIRATION_BUFFER_MS
+                + 10000;
         let namespace: i64 = 30;
         let namespace_del1: i64 = 45;
         let namespace_del2: i64 = 60;
@@ -3545,7 +3643,7 @@
             0x01, /* base_byte */
         )?;
         load_attestation_key_pool(&mut db, 45, namespace_del1, 0x02)?;
-        load_attestation_key_pool(&mut db, 60, namespace_del2, 0x03)?;
+        load_attestation_key_pool(&mut db, expiration_date - 10001, namespace_del2, 0x03)?;
 
         let blob_entry_row_count: u32 = db
             .conn
@@ -3560,7 +3658,7 @@
         let mut cert_chain =
             db.retrieve_attestation_key_and_cert_chain(Domain::APP, namespace, &KEYSTORE_UUID)?;
         assert!(cert_chain.is_some());
-        let value = cert_chain.unwrap();
+        let (_, value) = cert_chain.unwrap();
         assert_eq!(entry_values.batch_cert, value.batch_cert);
         assert_eq!(entry_values.cert_chain, value.cert_chain);
         assert_eq!(entry_values.priv_key, value.private_key.to_vec());
@@ -3570,13 +3668,13 @@
             namespace_del1,
             &KEYSTORE_UUID,
         )?;
-        assert!(!cert_chain.is_some());
+        assert!(cert_chain.is_none());
         cert_chain = db.retrieve_attestation_key_and_cert_chain(
             Domain::APP,
             namespace_del2,
             &KEYSTORE_UUID,
         )?;
-        assert!(!cert_chain.is_some());
+        assert!(cert_chain.is_none());
 
         // Give the garbage collector half a second to catch up.
         std::thread::sleep(Duration::from_millis(500));
@@ -3592,6 +3690,73 @@
         Ok(())
     }
 
+    fn compare_rem_prov_values(
+        expected: &RemoteProvValues,
+        actual: Option<(KeyIdGuard, CertificateChain)>,
+    ) {
+        assert!(actual.is_some());
+        let (_, value) = actual.unwrap();
+        assert_eq!(expected.batch_cert, value.batch_cert);
+        assert_eq!(expected.cert_chain, value.cert_chain);
+        assert_eq!(expected.priv_key, value.private_key.to_vec());
+    }
+
+    #[test]
+    fn test_dont_remove_valid_certs() -> Result<()> {
+        let temp_dir =
+            TempDir::new("test_remove_expired_certs_").expect("Failed to create temp dir.");
+        let mut db = new_test_db_with_gc(temp_dir.path(), |_, _| Ok(()))?;
+        let expiration_date: i64 =
+            SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis() as i64
+                + EXPIRATION_BUFFER_MS
+                + 10000;
+        let namespace1: i64 = 30;
+        let namespace2: i64 = 45;
+        let namespace3: i64 = 60;
+        let entry_values1 = load_attestation_key_pool(
+            &mut db,
+            expiration_date,
+            namespace1,
+            0x01, /* base_byte */
+        )?;
+        let entry_values2 =
+            load_attestation_key_pool(&mut db, expiration_date + 40000, namespace2, 0x02)?;
+        let entry_values3 =
+            load_attestation_key_pool(&mut db, expiration_date - 9000, namespace3, 0x03)?;
+
+        let blob_entry_row_count: u32 = db
+            .conn
+            .query_row("SELECT COUNT(id) FROM persistent.blobentry;", NO_PARAMS, |row| row.get(0))
+            .expect("Failed to get blob entry row count.");
+        // We expect 9 rows here because there are three blobs per attestation key, i.e.,
+        // one key, one certificate chain, and one certificate.
+        assert_eq!(blob_entry_row_count, 9);
+
+        let mut cert_chain =
+            db.retrieve_attestation_key_and_cert_chain(Domain::APP, namespace1, &KEYSTORE_UUID)?;
+        compare_rem_prov_values(&entry_values1, cert_chain);
+
+        cert_chain =
+            db.retrieve_attestation_key_and_cert_chain(Domain::APP, namespace2, &KEYSTORE_UUID)?;
+        compare_rem_prov_values(&entry_values2, cert_chain);
+
+        cert_chain =
+            db.retrieve_attestation_key_and_cert_chain(Domain::APP, namespace3, &KEYSTORE_UUID)?;
+        compare_rem_prov_values(&entry_values3, cert_chain);
+
+        // Give the garbage collector half a second to catch up.
+        std::thread::sleep(Duration::from_millis(500));
+
+        let blob_entry_row_count: u32 = db
+            .conn
+            .query_row("SELECT COUNT(id) FROM persistent.blobentry;", NO_PARAMS, |row| row.get(0))
+            .expect("Failed to get blob entry row count.");
+        // There shound be 9 blob entries left, because all three keys are valid with
+        // three blobs each.
+        assert_eq!(blob_entry_row_count, 9);
+
+        Ok(())
+    }
     #[test]
     fn test_delete_all_attestation_keys() -> Result<()> {
         let mut db = new_test_db()?;
@@ -3703,8 +3868,8 @@
             alias: Some("key".to_string()),
             blob: None,
         };
-        const PVEC1: KeyPermSet = key_perm_set![KeyPerm::use_(), KeyPerm::get_info()];
-        const PVEC2: KeyPermSet = key_perm_set![KeyPerm::use_()];
+        const PVEC1: KeyPermSet = key_perm_set![KeyPerm::Use, KeyPerm::GetInfo];
+        const PVEC2: KeyPermSet = key_perm_set![KeyPerm::Use];
 
         // Reset totally predictable random number generator in case we
         // are not the first test running on this thread.
@@ -4180,7 +4345,7 @@
                 },
                 1,
                 2,
-                key_perm_set![KeyPerm::use_()],
+                key_perm_set![KeyPerm::Use],
                 |_k, _av| Ok(()),
             )
             .unwrap();
@@ -4190,7 +4355,7 @@
         let (_key_guard, key_entry) = db
             .load_key_entry(&granted_key, KeyType::Client, KeyEntryLoadBits::BOTH, 2, |k, av| {
                 assert_eq!(Domain::GRANT, k.domain);
-                assert!(av.unwrap().includes(KeyPerm::use_()));
+                assert!(av.unwrap().includes(KeyPerm::Use));
                 Ok(())
             })
             .unwrap();
@@ -4237,7 +4402,7 @@
             },
             OWNER_UID,
             GRANTEE_UID,
-            key_perm_set![KeyPerm::use_()],
+            key_perm_set![KeyPerm::Use],
             |_k, _av| Ok(()),
         )
         .unwrap();
@@ -4256,7 +4421,7 @@
                 |k, av| {
                     assert_eq!(Domain::APP, k.domain);
                     assert_eq!(OWNER_UID as i64, k.nspace);
-                    assert!(av.unwrap().includes(KeyPerm::use_()));
+                    assert!(av.unwrap().includes(KeyPerm::Use));
                     Ok(())
                 },
             )
@@ -4307,8 +4472,8 @@
         let mut db = new_test_db()?;
         const SOURCE_UID: u32 = 1u32;
         const DESTINATION_UID: u32 = 2u32;
-        static SOURCE_ALIAS: &str = &"SOURCE_ALIAS";
-        static DESTINATION_ALIAS: &str = &"DESTINATION_ALIAS";
+        static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
+        static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
         let key_id_guard =
             make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
                 .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
@@ -4376,8 +4541,8 @@
         const SOURCE_UID: u32 = 1u32;
         const DESTINATION_UID: u32 = 2u32;
         const DESTINATION_NAMESPACE: i64 = 1000i64;
-        static SOURCE_ALIAS: &str = &"SOURCE_ALIAS";
-        static DESTINATION_ALIAS: &str = &"DESTINATION_ALIAS";
+        static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
+        static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
         let key_id_guard =
             make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
                 .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
@@ -4444,8 +4609,8 @@
         let mut db = new_test_db()?;
         const SOURCE_UID: u32 = 1u32;
         const DESTINATION_UID: u32 = 2u32;
-        static SOURCE_ALIAS: &str = &"SOURCE_ALIAS";
-        static DESTINATION_ALIAS: &str = &"DESTINATION_ALIAS";
+        static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
+        static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
         let key_id_guard =
             make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
                 .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
@@ -4477,9 +4642,9 @@
 
     #[test]
     fn test_upgrade_0_to_1() {
-        const ALIAS1: &str = &"test_upgrade_0_to_1_1";
-        const ALIAS2: &str = &"test_upgrade_0_to_1_2";
-        const ALIAS3: &str = &"test_upgrade_0_to_1_3";
+        const ALIAS1: &str = "test_upgrade_0_to_1_1";
+        const ALIAS2: &str = "test_upgrade_0_to_1_2";
+        const ALIAS3: &str = "test_upgrade_0_to_1_3";
         const UID: u32 = 33;
         let temp_dir = Arc::new(TempDir::new("test_upgrade_0_to_1").unwrap());
         let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
@@ -4964,10 +5129,7 @@
                 Ok(KeyEntryRow {
                     id: row.get(0)?,
                     key_type: row.get(1)?,
-                    domain: match row.get(2)? {
-                        Some(i) => Some(Domain(i)),
-                        None => None,
-                    },
+                    domain: row.get::<_, Option<_>>(2)?.map(Domain),
                     namespace: row.get(3)?,
                     alias: row.get(4)?,
                     state: row.get(5)?,
@@ -5551,7 +5713,7 @@
         )?;
 
         // Check if super key exists.
-        assert!(db.key_exists(Domain::APP, 1, &USER_SUPER_KEY.alias, KeyType::Super)?);
+        assert!(db.key_exists(Domain::APP, 1, USER_SUPER_KEY.alias, KeyType::Super)?);
 
         let (_, key_entry) = db.load_super_key(&USER_SUPER_KEY, 1)?.unwrap();
         let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(
@@ -5561,8 +5723,7 @@
             None,
         )?;
 
-        let decrypted_secret_bytes =
-            loaded_super_key.aes_gcm_decrypt(&encrypted_secret, &iv, &tag)?;
+        let decrypted_secret_bytes = loaded_super_key.decrypt(&encrypted_secret, &iv, &tag)?;
         assert_eq!(secret_bytes, &*decrypted_secret_bytes);
 
         Ok(())
@@ -5658,7 +5819,7 @@
                     && updated_stats[&k].unused_size == baseline[&k].unused_size,
                 "updated_stats:\n{}\nbaseline:\n{}",
                 stringify(&updated_stats),
-                stringify(&baseline)
+                stringify(baseline)
             );
         }
     }
@@ -5752,7 +5913,7 @@
             },
             OWNER as u32,
             123,
-            key_perm_set![KeyPerm::use_()],
+            key_perm_set![KeyPerm::Use],
             |_, _| Ok(()),
         )?;
 
diff --git a/keystore2/src/database/utils.rs b/keystore2/src/database/utils.rs
index 90f5616..b4590da 100644
--- a/keystore2/src/database/utils.rs
+++ b/keystore2/src/database/utils.rs
@@ -44,7 +44,7 @@
     loop {
         match rows.next().context("In with_rows_extract_all: Failed to unpack row")? {
             Some(row) => {
-                row_extractor(&row).context("In with_rows_extract_all.")?;
+                row_extractor(row).context("In with_rows_extract_all.")?;
             }
             None => break Ok(()),
         }
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index b7b40f7..cb6a266 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -28,14 +28,13 @@
     KeyParameter::KeyParameter as KmKeyParameter, KeyPurpose::KeyPurpose, Tag::Tag,
 };
 use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
-    ISecureClock::ISecureClock, TimeStampToken::TimeStampToken,
+    TimeStampToken::TimeStampToken,
 };
 use android_security_authorization::aidl::android::security::authorization::ResponseCode::ResponseCode as AuthzResponseCode;
 use android_system_keystore2::aidl::android::system::keystore2::{
     Domain::Domain, IKeystoreSecurityLevel::KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING,
     OperationChallenge::OperationChallenge,
 };
-use android_system_keystore2::binder::Strong;
 use anyhow::{Context, Result};
 use std::{
     collections::{HashMap, HashSet},
@@ -219,13 +218,10 @@
 }
 
 fn get_timestamp_token(challenge: i64) -> Result<TimeStampToken, Error> {
-    let dev: Strong<dyn ISecureClock> = get_timestamp_service()
-        .expect(concat!(
-            "Secure Clock service must be present ",
-            "if TimeStampTokens are required."
-        ))
-        .get_interface()
-        .expect("Fatal: Timestamp service does not implement ISecureClock.");
+    let dev = get_timestamp_service().expect(concat!(
+        "Secure Clock service must be present ",
+        "if TimeStampTokens are required."
+    ));
     map_binder_status(dev.generateTimeStamp(challenge))
 }
 
@@ -454,7 +450,7 @@
                         KeyParameterValue::Algorithm(Algorithm::RSA)
                         | KeyParameterValue::Algorithm(Algorithm::EC) => {
                             return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE)).context(
-                                "In authorize_create: public operations on asymmetric keys are not
+                                "In authorize_create: public operations on asymmetric keys are not \
                                  supported.",
                             );
                         }
@@ -570,8 +566,7 @@
         // if both NO_AUTH_REQUIRED and USER_SECURE_ID tags are present, return error
         if !user_secure_ids.is_empty() && no_auth_required {
             return Err(Error::Km(Ec::INVALID_KEY_BLOB)).context(
-                "In authorize_create: key has both NO_AUTH_REQUIRED
-                and USER_SECURE_ID tags.",
+                "In authorize_create: key has both NO_AUTH_REQUIRED and USER_SECURE_ID tags.",
             );
         }
 
@@ -580,8 +575,8 @@
             || (user_auth_type.is_none() && !user_secure_ids.is_empty())
         {
             return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
-                "In authorize_create: Auth required, but either auth type or secure ids
-                are not present.",
+                "In authorize_create: Auth required, but either auth type or secure ids \
+                 are not present.",
             );
         }
 
@@ -591,8 +586,7 @@
             && op_params.iter().any(|kp| kp.tag == Tag::NONCE)
         {
             return Err(Error::Km(Ec::CALLER_NONCE_PROHIBITED)).context(
-                "In authorize_create, NONCE is present,
-                    although CALLER_NONCE is not present",
+                "In authorize_create, NONCE is present, although CALLER_NONCE is not present",
             );
         }
 
@@ -606,7 +600,7 @@
         }
 
         if let Some(level) = max_boot_level {
-            if !SUPER_KEY.level_accessible(level) {
+            if !SUPER_KEY.read().unwrap().level_accessible(level) {
                 return Err(Error::Km(Ec::BOOT_LEVEL_EXCEEDED))
                     .context("In authorize_create: boot level is too late.");
             }
@@ -841,8 +835,12 @@
                         .context("In get_auth_tokens: No auth token found.");
                 }
             } else {
-                return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND))
-                    .context("In get_auth_tokens: Passed-in auth token max age is zero.");
+                return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND)).context(
+                    concat!(
+                        "In get_auth_tokens: No auth token found for ",
+                        "the given challenge and passed-in auth token max age is zero."
+                    ),
+                );
             }
         };
         // Wait and obtain the timestamp token from secure clock service.
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index f969cb6..f34c5da 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -37,6 +37,7 @@
 };
 use keystore2_selinux as selinux;
 use std::cmp::PartialEq;
+use std::ffi::CString;
 
 /// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
 /// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
@@ -66,10 +67,15 @@
         Error::Rc(ResponseCode::SYSTEM_ERROR)
     }
 
-    /// Short hand for `Error::Rc(ResponseCode::PERMISSION_DENIED`
+    /// Short hand for `Error::Rc(ResponseCode::PERMISSION_DENIED)`
     pub fn perm() -> Self {
         Error::Rc(ResponseCode::PERMISSION_DENIED)
     }
+
+    /// Short hand for `Error::Rc(ResponseCode::OUT_OF_KEYS)`
+    pub fn out_of_keys() -> Self {
+        Error::Rc(ResponseCode::OUT_OF_KEYS)
+    }
 }
 
 /// Helper function to map the binder status we get from calls into KeyMint
@@ -184,6 +190,20 @@
     )
 }
 
+/// This function turns an anyhow error into an optional CString.
+/// This is especially useful to add a message string to a service specific error.
+/// If the formatted string was not convertible because it contained a nul byte,
+/// None is returned and a warning is logged.
+pub fn anyhow_error_to_cstring(e: &anyhow::Error) -> Option<CString> {
+    match CString::new(format!("{:?}", e)) {
+        Ok(msg) => Some(msg),
+        Err(_) => {
+            log::warn!("Cannot convert error message to CStr. It contained a nul byte.");
+            None
+        }
+    }
+}
+
 /// This function behaves similar to map_or_log_error, but it does not log the errors, instead
 /// it calls map_err on the error before mapping it to a binder result allowing callers to
 /// log or transform the error before mapping it.
@@ -200,7 +220,10 @@
         |e| {
             let e = map_err(e);
             let rc = get_error_code(&e);
-            Err(BinderStatus::new_service_specific_error(rc, None))
+            Err(BinderStatus::new_service_specific_error(
+                rc,
+                anyhow_error_to_cstring(&e).as_deref(),
+            ))
         },
         handle_ok,
     )
diff --git a/keystore2/src/fuzzers/Android.bp b/keystore2/src/fuzzers/Android.bp
new file mode 100644
index 0000000..384ab77
--- /dev/null
+++ b/keystore2/src/fuzzers/Android.bp
@@ -0,0 +1,29 @@
+// 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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_fuzz {
+    name: "legacy_blob_fuzzer",
+    srcs: ["legacy_blob_fuzzer.rs"],
+    rustlibs: [
+        "libkeystore2",
+    ],
+    fuzz_config: {
+        fuzz_on_haiku_device: true,
+        fuzz_on_haiku_host: false,
+    },
+}
diff --git a/keystore2/src/fuzzers/legacy_blob_fuzzer.rs b/keystore2/src/fuzzers/legacy_blob_fuzzer.rs
new file mode 100644
index 0000000..7e3e848
--- /dev/null
+++ b/keystore2/src/fuzzers/legacy_blob_fuzzer.rs
@@ -0,0 +1,26 @@
+// 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.
+
+#![allow(missing_docs)]
+#![no_main]
+#[macro_use]
+extern crate libfuzzer_sys;
+use keystore2::legacy_blob::LegacyBlobLoader;
+
+fuzz_target!(|data: &[u8]| {
+    if !data.is_empty() {
+        let string = data.iter().filter_map(|c| std::char::from_u32(*c as u32)).collect::<String>();
+        let _res = LegacyBlobLoader::decode_alias(&string);
+    }
+});
diff --git a/keystore2/src/gc.rs b/keystore2/src/gc.rs
index 2010c79..341aa0a 100644
--- a/keystore2/src/gc.rs
+++ b/keystore2/src/gc.rs
@@ -27,7 +27,7 @@
 use async_task::AsyncTask;
 use std::sync::{
     atomic::{AtomicU8, Ordering},
-    Arc,
+    Arc, RwLock,
 };
 
 pub struct Gc {
@@ -47,7 +47,7 @@
         F: FnOnce() -> (
                 Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
                 KeystoreDB,
-                Arc<SuperKeyManager>,
+                Arc<RwLock<SuperKeyManager>>,
             ) + Send
             + 'static,
     {
@@ -87,7 +87,7 @@
     invalidate_key: Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
     db: KeystoreDB,
     async_task: std::sync::Weak<AsyncTask>,
-    super_key: Arc<SuperKeyManager>,
+    super_key: Arc<RwLock<SuperKeyManager>>,
     notified: Arc<AtomicU8>,
 }
 
@@ -121,9 +121,11 @@
             if let Some(uuid) = blob_metadata.km_uuid() {
                 let blob = self
                     .super_key
+                    .read()
+                    .unwrap()
                     .unwrap_key_if_required(&blob_metadata, &blob)
                     .context("In process_one_key: Trying to unwrap to-be-deleted blob.")?;
-                (self.invalidate_key)(&uuid, &*blob)
+                (self.invalidate_key)(uuid, &*blob)
                     .context("In process_one_key: Trying to invalidate key.")?;
             }
         }
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index 1111d75..14b3601 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -21,18 +21,21 @@
 use crate::legacy_importer::LegacyImporter;
 use crate::super_key::SuperKeyManager;
 use crate::utils::watchdog as wd;
-use crate::utils::Asp;
 use crate::{async_task::AsyncTask, database::MonotonicRawTime};
 use crate::{
     database::KeystoreDB,
     database::Uuid,
     error::{map_binder_status, map_binder_status_code, Error, ErrorCode},
 };
+use crate::km_compat::{KeyMintV1, BacklevelKeyMintWrapper};
 use crate::{enforcements::Enforcements, error::map_km_error};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     IKeyMintDevice::IKeyMintDevice, IRemotelyProvisionedComponent::IRemotelyProvisionedComponent,
     KeyMintHardwareInfo::KeyMintHardwareInfo, SecurityLevel::SecurityLevel,
 };
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
+    ISecureClock::ISecureClock,
+};
 use android_hardware_security_keymint::binder::{StatusCode, Strong};
 use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
 use anyhow::{Context, Result};
@@ -85,34 +88,33 @@
             RefCell::new(create_thread_local_db());
 }
 
-#[derive(Default)]
-struct DevicesMap {
-    devices_by_uuid: HashMap<Uuid, (Asp, KeyMintHardwareInfo)>,
+struct DevicesMap<T: FromIBinder + ?Sized> {
+    devices_by_uuid: HashMap<Uuid, (Strong<T>, KeyMintHardwareInfo)>,
     uuid_by_sec_level: HashMap<SecurityLevel, Uuid>,
 }
 
-impl DevicesMap {
+impl<T: FromIBinder + ?Sized> DevicesMap<T> {
     fn dev_by_sec_level(
         &self,
         sec_level: &SecurityLevel,
-    ) -> Option<(Asp, KeyMintHardwareInfo, Uuid)> {
+    ) -> Option<(Strong<T>, KeyMintHardwareInfo, Uuid)> {
         self.uuid_by_sec_level.get(sec_level).and_then(|uuid| self.dev_by_uuid(uuid))
     }
 
-    fn dev_by_uuid(&self, uuid: &Uuid) -> Option<(Asp, KeyMintHardwareInfo, Uuid)> {
+    fn dev_by_uuid(&self, uuid: &Uuid) -> Option<(Strong<T>, KeyMintHardwareInfo, Uuid)> {
         self.devices_by_uuid
             .get(uuid)
             .map(|(dev, hw_info)| ((*dev).clone(), (*hw_info).clone(), *uuid))
     }
 
-    fn devices<T: FromIBinder + ?Sized>(&self) -> Vec<Strong<T>> {
-        self.devices_by_uuid.values().filter_map(|(asp, _)| asp.get_interface::<T>().ok()).collect()
+    fn devices(&self) -> Vec<Strong<T>> {
+        self.devices_by_uuid.values().map(|(dev, _)| dev.clone()).collect()
     }
 
     /// The requested security level and the security level of the actual implementation may
     /// differ. So we map the requested security level to the uuid of the implementation
     /// so that there cannot be any confusion as to which KeyMint instance is requested.
-    fn insert(&mut self, sec_level: SecurityLevel, dev: Asp, hw_info: KeyMintHardwareInfo) {
+    fn insert(&mut self, sec_level: SecurityLevel, dev: Strong<T>, hw_info: KeyMintHardwareInfo) {
         // For now we use the reported security level of the KM instance as UUID.
         // TODO update this section once UUID was added to the KM hardware info.
         let uuid: Uuid = sec_level.into();
@@ -121,17 +123,31 @@
     }
 }
 
-#[derive(Default)]
-struct RemotelyProvisionedDevicesMap {
-    devices_by_sec_level: HashMap<SecurityLevel, Asp>,
+impl<T: FromIBinder + ?Sized> Default for DevicesMap<T> {
+    fn default() -> Self {
+        Self {
+            devices_by_uuid: HashMap::<Uuid, (Strong<T>, KeyMintHardwareInfo)>::new(),
+            uuid_by_sec_level: Default::default(),
+        }
+    }
 }
 
-impl RemotelyProvisionedDevicesMap {
-    fn dev_by_sec_level(&self, sec_level: &SecurityLevel) -> Option<Asp> {
+struct RemotelyProvisionedDevicesMap<T: FromIBinder + ?Sized> {
+    devices_by_sec_level: HashMap<SecurityLevel, Strong<T>>,
+}
+
+impl<T: FromIBinder + ?Sized> Default for RemotelyProvisionedDevicesMap<T> {
+    fn default() -> Self {
+        Self { devices_by_sec_level: HashMap::<SecurityLevel, Strong<T>>::new() }
+    }
+}
+
+impl<T: FromIBinder + ?Sized> RemotelyProvisionedDevicesMap<T> {
+    fn dev_by_sec_level(&self, sec_level: &SecurityLevel) -> Option<Strong<T>> {
         self.devices_by_sec_level.get(sec_level).map(|dev| (*dev).clone())
     }
 
-    fn insert(&mut self, sec_level: SecurityLevel, dev: Asp) {
+    fn insert(&mut self, sec_level: SecurityLevel, dev: Strong<T>) {
         self.devices_by_sec_level.insert(sec_level, dev);
     }
 }
@@ -141,13 +157,15 @@
     pub static ref DB_PATH: RwLock<PathBuf> = RwLock::new(
         Path::new("/data/misc/keystore").to_path_buf());
     /// Runtime database of unwrapped super keys.
-    pub static ref SUPER_KEY: Arc<SuperKeyManager> = Default::default();
+    pub static ref SUPER_KEY: Arc<RwLock<SuperKeyManager>> = Default::default();
     /// Map of KeyMint devices.
-    static ref KEY_MINT_DEVICES: Mutex<DevicesMap> = Default::default();
+    static ref KEY_MINT_DEVICES: Mutex<DevicesMap<dyn IKeyMintDevice>> = Default::default();
     /// Timestamp service.
-    static ref TIME_STAMP_DEVICE: Mutex<Option<Asp>> = Default::default();
+    static ref TIME_STAMP_DEVICE: Mutex<Option<Strong<dyn ISecureClock>>> = Default::default();
     /// RemotelyProvisionedComponent HAL devices.
-    static ref REMOTELY_PROVISIONED_COMPONENT_DEVICES: Mutex<RemotelyProvisionedDevicesMap> = Default::default();
+    static ref REMOTELY_PROVISIONED_COMPONENT_DEVICES:
+            Mutex<RemotelyProvisionedDevicesMap<dyn IRemotelyProvisionedComponent>> =
+                    Default::default();
     /// A single on-demand worker thread that handles deferred tasks with two different
     /// priorities.
     pub static ref ASYNC_TASK: Arc<AsyncTask> = Default::default();
@@ -166,8 +184,7 @@
     static ref GC: Arc<Gc> = Arc::new(Gc::new_init_with(ASYNC_TASK.clone(), || {
         (
             Box::new(|uuid, blob| {
-                let km_dev: Strong<dyn IKeyMintDevice> =
-                    get_keymint_dev_by_uuid(uuid).map(|(dev, _)| dev)?.get_interface()?;
+                let km_dev = get_keymint_dev_by_uuid(uuid).map(|(dev, _)| dev)?;
                 let _wp = wd::watch_millis("In invalidate key closure: calling deleteKey", 500);
                 map_km_error(km_dev.deleteKey(&*blob))
                     .context("In invalidate key closure: Trying to invalidate key blob.")
@@ -181,40 +198,70 @@
 
 static KEYMINT_SERVICE_NAME: &str = "android.hardware.security.keymint.IKeyMintDevice";
 
-/// Make a new connection to a KeyMint device of the given security level.
-/// If no native KeyMint device can be found this function also brings
-/// up the compatibility service and attempts to connect to the legacy wrapper.
-fn connect_keymint(security_level: &SecurityLevel) -> Result<(Asp, KeyMintHardwareInfo)> {
+/// Determine the service name for a KeyMint device of the given security level
+/// which implements at least the specified version of the `IKeyMintDevice`
+/// interface.
+fn keymint_service_name_by_version(
+    security_level: &SecurityLevel,
+    version: i32,
+) -> Result<Option<(i32, String)>> {
     let keymint_instances =
-        get_aidl_instances("android.hardware.security.keymint", 1, "IKeyMintDevice");
+        get_aidl_instances("android.hardware.security.keymint", version as usize, "IKeyMintDevice");
 
     let service_name = match *security_level {
         SecurityLevel::TRUSTED_ENVIRONMENT => {
-            if keymint_instances.as_vec()?.iter().any(|instance| *instance == "default") {
+            if keymint_instances.iter().any(|instance| *instance == "default") {
                 Some(format!("{}/default", KEYMINT_SERVICE_NAME))
             } else {
                 None
             }
         }
         SecurityLevel::STRONGBOX => {
-            if keymint_instances.as_vec()?.iter().any(|instance| *instance == "strongbox") {
+            if keymint_instances.iter().any(|instance| *instance == "strongbox") {
                 Some(format!("{}/strongbox", KEYMINT_SERVICE_NAME))
             } else {
                 None
             }
         }
         _ => {
-            return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
-                .context("In connect_keymint.")
+            return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)).context(format!(
+                "In keymint_service_name_by_version: Trying to find keymint V{} for security level: {:?}",
+                version, security_level
+            ));
         }
     };
 
-    let (keymint, hal_version) = if let Some(service_name) = service_name {
-        (
+    Ok(service_name.map(|service_name| (version, service_name)))
+}
+
+/// Make a new connection to a KeyMint device of the given security level.
+/// If no native KeyMint device can be found this function also brings
+/// up the compatibility service and attempts to connect to the legacy wrapper.
+fn connect_keymint(
+    security_level: &SecurityLevel,
+) -> Result<(Strong<dyn IKeyMintDevice>, KeyMintHardwareInfo)> {
+    // Count down from the current interface version back to one in order to
+    // also find out the interface version -- an implementation of V2 will show
+    // up in the list of V1-capable devices, but not vice-versa.
+    let service_name = keymint_service_name_by_version(security_level, 2)
+        .and_then(|sl| {
+            if sl.is_none() {
+                keymint_service_name_by_version(security_level, 1)
+            } else {
+                Ok(sl)
+            }
+        })
+        .context("In connect_keymint.")?;
+
+    let (keymint, hal_version) = if let Some((version, service_name)) = service_name {
+        let km: Strong<dyn IKeyMintDevice> =
             map_binder_status_code(binder::get_interface(&service_name))
-                .context("In connect_keymint: Trying to connect to genuine KeyMint service.")?,
-            Some(100i32), // The HAL version code for KeyMint V1 is 100.
-        )
+                .context("In connect_keymint: Trying to connect to genuine KeyMint service.")?;
+        // Map the HAL version code for KeyMint to be <AIDL version> * 100, so
+        // - V1 is 100
+        // - V2 is 200
+        // etc.
+        (km, Some(version * 100))
     } else {
         // This is a no-op if it was called before.
         keystore2_km_compat::add_keymint_device_service();
@@ -235,6 +282,48 @@
         )
     };
 
+    // If the KeyMint device is back-level, use a wrapper that intercepts and
+    // emulates things that are not supported by the hardware.
+    let keymint = match hal_version {
+        Some(200) => {
+            // Current KeyMint version: use as-is.
+            log::info!(
+                "KeyMint device is current version ({:?}) for security level: {:?}",
+                hal_version,
+                security_level
+            );
+            keymint
+        }
+        Some(100) => {
+            // KeyMint v1: perform software emulation.
+            log::info!(
+                "Add emulation wrapper around {:?} device for security level: {:?}",
+                hal_version,
+                security_level
+            );
+            BacklevelKeyMintWrapper::wrap(KeyMintV1::new(*security_level), keymint)
+                .context("In connect_keymint: Trying to create V1 compatibility wrapper.")?
+        }
+        None => {
+            // Compatibility wrapper around a KeyMaster device: this roughly
+            // behaves like KeyMint V1 (e.g. it includes AGREE_KEY support,
+            // albeit in software.)
+            log::info!(
+                "Add emulation wrapper around Keymaster device for security level: {:?}",
+                security_level
+            );
+            BacklevelKeyMintWrapper::wrap(KeyMintV1::new(*security_level), keymint).context(
+                "In connect_keymint: Trying to create km_compat V1 compatibility wrapper .",
+            )?
+        }
+        _ => {
+            return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)).context(format!(
+                "In connect_keymint: unexpected hal_version {:?} for security level: {:?}",
+                hal_version, security_level
+            ))
+        }
+    };
+
     let wp = wd::watch_millis("In connect_keymint: calling getHardwareInfo()", 500);
     let mut hw_info = map_km_error(keymint.getHardwareInfo())
         .context("In connect_keymint: Failed to get hardware info.")?;
@@ -242,16 +331,19 @@
 
     // The legacy wrapper sets hw_info.versionNumber to the underlying HAL version like so:
     // 10 * <major> + <minor>, e.g., KM 3.0 = 30. So 30, 40, and 41 are the only viable values.
-    // For KeyMint the versionNumber is implementation defined and thus completely meaningless
-    // to Keystore 2.0. So at this point the versionNumber field is set to the HAL version, so
-    // that higher levels have a meaningful guide as to which feature set to expect from the
-    // implementation. As of this writing the only meaningful version number is 100 for KeyMint V1,
-    // and future AIDL versions should follow the pattern <AIDL version> * 100.
+    //
+    // For KeyMint the returned versionNumber is implementation defined and thus completely
+    // meaningless to Keystore 2.0.  So set the versionNumber field that is returned to
+    // the rest of the code to be the <AIDL version> * 100, so KeyMint V1 is 100, KeyMint V2 is 200
+    // and so on.
+    //
+    // This ensures that versionNumber value across KeyMaster and KeyMint is monotonically
+    // increasing (and so comparisons like `versionNumber >= KEY_MINT_1` are valid).
     if let Some(hal_version) = hal_version {
         hw_info.versionNumber = hal_version;
     }
 
-    Ok((Asp::new(keymint.as_binder()), hw_info))
+    Ok((keymint, hw_info))
 }
 
 /// Get a keymint device for the given security level either from our cache or
@@ -259,9 +351,9 @@
 /// TODO the latter can be removed when the uuid is part of the hardware info.
 pub fn get_keymint_device(
     security_level: &SecurityLevel,
-) -> Result<(Asp, KeyMintHardwareInfo, Uuid)> {
+) -> Result<(Strong<dyn IKeyMintDevice>, KeyMintHardwareInfo, Uuid)> {
     let mut devices_map = KEY_MINT_DEVICES.lock().unwrap();
-    if let Some((dev, hw_info, uuid)) = devices_map.dev_by_sec_level(&security_level) {
+    if let Some((dev, hw_info, uuid)) = devices_map.dev_by_sec_level(security_level) {
         Ok((dev, hw_info, uuid))
     } else {
         let (dev, hw_info) = connect_keymint(security_level).context("In get_keymint_device.")?;
@@ -275,7 +367,9 @@
 /// attempt to establish a new connection. It is assumed that the cache is already populated
 /// when this is called. This is a fair assumption, because service.rs iterates through all
 /// security levels when it gets instantiated.
-pub fn get_keymint_dev_by_uuid(uuid: &Uuid) -> Result<(Asp, KeyMintHardwareInfo)> {
+pub fn get_keymint_dev_by_uuid(
+    uuid: &Uuid,
+) -> Result<(Strong<dyn IKeyMintDevice>, KeyMintHardwareInfo)> {
     let devices_map = KEY_MINT_DEVICES.lock().unwrap();
     if let Some((dev, hw_info, _)) = devices_map.dev_by_uuid(uuid) {
         Ok((dev, hw_info))
@@ -294,12 +388,12 @@
 /// Make a new connection to a secure clock service.
 /// If no native SecureClock device can be found brings up the compatibility service and attempts
 /// to connect to the legacy wrapper.
-fn connect_secureclock() -> Result<Asp> {
+fn connect_secureclock() -> Result<Strong<dyn ISecureClock>> {
     let secureclock_instances =
         get_aidl_instances("android.hardware.security.secureclock", 1, "ISecureClock");
 
     let secure_clock_available =
-        secureclock_instances.as_vec()?.iter().any(|instance| *instance == "default");
+        secureclock_instances.iter().any(|instance| *instance == "default");
 
     let default_time_stamp_service_name = format!("{}/default", TIME_STAMP_SERVICE_NAME);
 
@@ -325,12 +419,12 @@
             .context("In connect_secureclock: Trying to get Legacy wrapper.")
     }?;
 
-    Ok(Asp::new(secureclock.as_binder()))
+    Ok(secureclock)
 }
 
 /// Get the timestamp service that verifies auth token timeliness towards security levels with
 /// different clocks.
-pub fn get_timestamp_service() -> Result<Asp> {
+pub fn get_timestamp_service() -> Result<Strong<dyn ISecureClock>> {
     let mut ts_device = TIME_STAMP_DEVICE.lock().unwrap();
     if let Some(dev) = &*ts_device {
         Ok(dev.clone())
@@ -344,20 +438,22 @@
 static REMOTE_PROVISIONING_HAL_SERVICE_NAME: &str =
     "android.hardware.security.keymint.IRemotelyProvisionedComponent";
 
-fn connect_remotely_provisioned_component(security_level: &SecurityLevel) -> Result<Asp> {
+fn connect_remotely_provisioned_component(
+    security_level: &SecurityLevel,
+) -> Result<Strong<dyn IRemotelyProvisionedComponent>> {
     let remotely_prov_instances =
         get_aidl_instances("android.hardware.security.keymint", 1, "IRemotelyProvisionedComponent");
 
     let service_name = match *security_level {
         SecurityLevel::TRUSTED_ENVIRONMENT => {
-            if remotely_prov_instances.as_vec()?.iter().any(|instance| *instance == "default") {
+            if remotely_prov_instances.iter().any(|instance| *instance == "default") {
                 Some(format!("{}/default", REMOTE_PROVISIONING_HAL_SERVICE_NAME))
             } else {
                 None
             }
         }
         SecurityLevel::STRONGBOX => {
-            if remotely_prov_instances.as_vec()?.iter().any(|instance| *instance == "strongbox") {
+            if remotely_prov_instances.iter().any(|instance| *instance == "strongbox") {
                 Some(format!("{}/strongbox", REMOTE_PROVISIONING_HAL_SERVICE_NAME))
             } else {
                 None
@@ -375,14 +471,16 @@
                 " RemotelyProvisionedComponent service."
             ))
             .map_err(|e| e)?;
-    Ok(Asp::new(rem_prov_hal.as_binder()))
+    Ok(rem_prov_hal)
 }
 
 /// Get a remote provisiong component device for the given security level either from the cache or
 /// by making a new connection. Returns the device.
-pub fn get_remotely_provisioned_component(security_level: &SecurityLevel) -> Result<Asp> {
+pub fn get_remotely_provisioned_component(
+    security_level: &SecurityLevel,
+) -> Result<Strong<dyn IRemotelyProvisionedComponent>> {
     let mut devices_map = REMOTELY_PROVISIONED_COMPONENT_DEVICES.lock().unwrap();
-    if let Some(dev) = devices_map.dev_by_sec_level(&security_level) {
+    if let Some(dev) = devices_map.dev_by_sec_level(security_level) {
         Ok(dev)
     } else {
         let dev = connect_remotely_provisioned_component(security_level)
diff --git a/keystore2/src/id_rotation.rs b/keystore2/src/id_rotation.rs
index dbf0fc9..e3992d8 100644
--- a/keystore2/src/id_rotation.rs
+++ b/keystore2/src/id_rotation.rs
@@ -27,7 +27,7 @@
 use std::time::Duration;
 
 const ID_ROTATION_PERIOD: Duration = Duration::from_secs(30 * 24 * 60 * 60); // Thirty days.
-static TIMESTAMP_FILE_NAME: &str = &"timestamp";
+static TIMESTAMP_FILE_NAME: &str = "timestamp";
 
 /// The IdRotationState stores the path to the timestamp file for deferred usage. The data
 /// partition is usually not available when Keystore 2.0 starts up. So this object is created
@@ -83,7 +83,7 @@
     fn test_had_factory_reset_since_id_rotation() -> Result<()> {
         let temp_dir = TempDir::new("test_had_factory_reset_since_id_rotation_")
             .expect("Failed to create temp dir.");
-        let id_rotation_state = IdRotationState::new(&temp_dir.path());
+        let id_rotation_state = IdRotationState::new(temp_dir.path());
 
         let mut temp_file_path = temp_dir.path().to_owned();
         temp_file_path.push(TIMESTAMP_FILE_NAME);
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index cf2ba04..55f5d15 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -19,19 +19,24 @@
 use keystore2::maintenance::Maintenance;
 use keystore2::metrics::Metrics;
 use keystore2::metrics_store;
-use keystore2::remote_provisioning::RemoteProvisioningService;
+use keystore2::remote_provisioning::{
+    RemoteProvisioningService, RemotelyProvisionedKeyPoolService,
+};
 use keystore2::service::KeystoreService;
 use keystore2::{apc::ApcManager, shared_secret_negotiation};
 use keystore2::{authorization::AuthorizationManager, id_rotation::IdRotationState};
 use legacykeystore::LegacyKeystore;
 use log::{error, info};
-use std::{panic, path::Path, sync::mpsc::channel};
+use rusqlite::trace as sqlite_trace;
+use std::{os::raw::c_int, panic, path::Path, sync::mpsc::channel};
 
 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 METRICS_SERVICE_NAME: &str = "android.security.metrics";
 static REMOTE_PROVISIONING_SERVICE_NAME: &str = "android.security.remoteprovisioning";
+static REMOTELY_PROVISIONED_KEY_POOL_SERVICE_NAME: &str =
+    "android.security.remoteprovisioning.IRemotelyProvisionedKeyPool";
 static USER_MANAGER_SERVICE_NAME: &str = "android.security.maintenance";
 static LEGACY_KEYSTORE_SERVICE_NAME: &str = "android.security.legacykeystore";
 
@@ -39,7 +44,10 @@
 fn main() {
     // Initialize android logging.
     android_logger::init_once(
-        android_logger::Config::default().with_tag("keystore2").with_min_level(log::Level::Debug),
+        android_logger::Config::default()
+            .with_tag("keystore2")
+            .with_min_level(log::Level::Debug)
+            .with_log_id(android_logger::LogId::System),
     );
     // Redirect panic messages to logcat.
     panic::set_hook(Box::new(|panic_info| {
@@ -52,6 +60,14 @@
     let mut args = std::env::args();
     args.next().expect("That's odd. How is there not even a first argument?");
 
+    // This must happen early before any other sqlite operations.
+    log::info!("Setting up sqlite logging for keystore2");
+    fn sqlite_log_handler(err: c_int, message: &str) {
+        log::error!("[SQLITE3] {}: {}", err, message);
+    }
+    unsafe { sqlite_trace::config_log(Some(sqlite_log_handler)) }
+        .expect("Error setting sqlite log callback.");
+
     // Write/update keystore.crash_count system property.
     metrics_store::update_keystore_crash_sysprop();
 
@@ -63,7 +79,7 @@
         let db_path = Path::new(&dir);
         *keystore2::globals::DB_PATH.write().expect("Could not lock DB_PATH.") =
             db_path.to_path_buf();
-        IdRotationState::new(&db_path)
+        IdRotationState::new(db_path)
     } else {
         panic!("Must specify a database directory.");
     };
@@ -136,6 +152,25 @@
         });
     }
 
+    // Even if the IRemotelyProvisionedComponent HAL is implemented, it doesn't mean that the keys
+    // may be fetched via the key pool. The HAL must be a new version that exports a unique id. If
+    // none of the HALs support this, then the key pool service is not published.
+    match RemotelyProvisionedKeyPoolService::new_native_binder() {
+        Ok(key_pool_service) => {
+            binder::add_service(
+                REMOTELY_PROVISIONED_KEY_POOL_SERVICE_NAME,
+                key_pool_service.as_binder(),
+            )
+            .unwrap_or_else(|e| {
+                panic!(
+                    "Failed to register service {} because of {:?}.",
+                    REMOTELY_PROVISIONED_KEY_POOL_SERVICE_NAME, e
+                );
+            });
+        }
+        Err(e) => log::info!("Not publishing IRemotelyProvisionedKeyPool service: {:?}", e),
+    }
+
     binder::add_service(LEGACY_KEYSTORE_SERVICE_NAME, legacykeystore.as_binder()).unwrap_or_else(
         |e| {
             panic!(
diff --git a/keystore2/src/km_compat.rs b/keystore2/src/km_compat.rs
new file mode 100644
index 0000000..788beef
--- /dev/null
+++ b/keystore2/src/km_compat.rs
@@ -0,0 +1,588 @@
+// Copyright 2020, 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.
+
+//! Provide a wrapper around a KeyMint device that allows up-level features to
+//! be emulated on back-level devices.
+
+use crate::error::{map_binder_status, map_binder_status_code, map_or_log_err, Error, ErrorCode};
+use android_hardware_security_keymint::binder::{BinderFeatures, StatusCode, Strong};
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::TimeStampToken::TimeStampToken;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    AttestationKey::AttestationKey, BeginResult::BeginResult, EcCurve::EcCurve,
+    HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::BnKeyMintDevice,
+    IKeyMintDevice::IKeyMintDevice, KeyCharacteristics::KeyCharacteristics,
+    KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat,
+    KeyMintHardwareInfo::KeyMintHardwareInfo, KeyParameter::KeyParameter,
+    KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+    Tag::Tag,
+};
+use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
+use anyhow::Context;
+use keystore2_crypto::{hmac_sha256, HMAC_SHA256_LEN};
+
+/// Key data associated with key generation/import.
+#[derive(Debug, PartialEq, Eq)]
+pub enum KeyImportData<'a> {
+    None,
+    Pkcs8(&'a [u8]),
+    Raw(&'a [u8]),
+}
+
+impl<'a> KeyImportData<'a> {
+    /// Translate import parameters into a `KeyImportData` instance.
+    fn new(key_format: KeyFormat, key_data: &'a [u8]) -> binder::Result<Self> {
+        match key_format {
+            KeyFormat::PKCS8 => Ok(KeyImportData::Pkcs8(key_data)),
+            KeyFormat::RAW => Ok(KeyImportData::Raw(key_data)),
+            _ => Err(binder::Status::new_service_specific_error(
+                ErrorCode::UNSUPPORTED_KEY_FORMAT.0,
+                None,
+            )),
+        }
+    }
+}
+
+/// A key blob that may be software-emulated or may be directly produced by an
+/// underlying device.  In either variant the inner data is the keyblob itself,
+/// as seen by the relevant device.
+#[derive(Debug, PartialEq, Eq)]
+pub enum KeyBlob<'a> {
+    Raw(&'a [u8]),
+    Wrapped(&'a [u8]),
+}
+
+/// Trait for detecting that software emulation of a current-version KeyMint
+/// feature is required for a back-level KeyMint implementation.
+pub trait EmulationDetector: Send + Sync {
+    /// Indicate whether software emulation is required for key
+    /// generation/import using the provided parameters.
+    fn emulation_required(&self, params: &[KeyParameter], import_data: &KeyImportData) -> bool;
+}
+
+const KEYBLOB_PREFIX: &[u8] = b"SoftKeyMintForV1Blob";
+const KEYBLOB_HMAC_KEY: &[u8] = b"SoftKeyMintForV1HMACKey";
+
+/// Wrap the provided keyblob:
+/// - prefix it with an identifier specific to this wrapper
+/// - suffix it with an HMAC tag, using the [`KEYBLOB_HMAC_KEY`] and `keyblob`.
+fn wrap_keyblob(keyblob: &[u8]) -> anyhow::Result<Vec<u8>> {
+    let mut result = Vec::with_capacity(KEYBLOB_PREFIX.len() + keyblob.len() + HMAC_SHA256_LEN);
+    result.extend_from_slice(KEYBLOB_PREFIX);
+    result.extend_from_slice(keyblob);
+    let tag = hmac_sha256(KEYBLOB_HMAC_KEY, keyblob)
+        .context("In wrap_keyblob, failed to calculate HMAC-SHA256")?;
+    result.extend_from_slice(&tag);
+    Ok(result)
+}
+
+/// Return an unwrapped version of the provided `keyblob`, which may or may
+/// not be associated with the software emulation.
+fn unwrap_keyblob(keyblob: &[u8]) -> KeyBlob {
+    if !keyblob.starts_with(KEYBLOB_PREFIX) {
+        return KeyBlob::Raw(keyblob);
+    }
+    let without_prefix = &keyblob[KEYBLOB_PREFIX.len()..];
+    if without_prefix.len() < HMAC_SHA256_LEN {
+        return KeyBlob::Raw(keyblob);
+    }
+    let (inner_keyblob, want_tag) = without_prefix.split_at(without_prefix.len() - HMAC_SHA256_LEN);
+    let got_tag = match hmac_sha256(KEYBLOB_HMAC_KEY, inner_keyblob) {
+        Ok(tag) => tag,
+        Err(e) => {
+            log::error!("Error calculating HMAC-SHA256 for keyblob unwrap: {:?}", e);
+            return KeyBlob::Raw(keyblob);
+        }
+    };
+    // Comparison does not need to be constant-time here.
+    if want_tag == got_tag {
+        KeyBlob::Wrapped(inner_keyblob)
+    } else {
+        KeyBlob::Raw(keyblob)
+    }
+}
+
+/// Wrapper around a real device that implements a back-level version of
+/// `IKeyMintDevice`
+pub struct BacklevelKeyMintWrapper<T: EmulationDetector> {
+    /// The `real` device implements some earlier version of `IKeyMintDevice`
+    real: Strong<dyn IKeyMintDevice>,
+    /// The `soft`ware device implements the current version of `IKeyMintDevice`
+    soft: Strong<dyn IKeyMintDevice>,
+    /// Detector for operations that are not supported by the earlier version of
+    /// `IKeyMintDevice`. Or possibly a large flightless bird, who can tell.
+    emu: T,
+}
+
+impl<T> BacklevelKeyMintWrapper<T>
+where
+    T: EmulationDetector + 'static,
+{
+    /// Create a wrapper around the provided back-level KeyMint device, so that
+    /// software emulation can be performed for any current-version features not
+    /// provided by the real device.
+    pub fn wrap(
+        emu: T,
+        real: Strong<dyn IKeyMintDevice>,
+    ) -> anyhow::Result<Strong<dyn IKeyMintDevice>> {
+        // This is a no-op if it was called before.
+        keystore2_km_compat::add_keymint_device_service();
+
+        let keystore_compat_service: Strong<dyn IKeystoreCompatService> = map_binder_status_code(
+            binder::get_interface("android.security.compat"),
+        )
+        .context("In BacklevelKeyMintWrapper::wrap: Trying to connect to compat service.")?;
+        let soft =
+            map_binder_status(keystore_compat_service.getKeyMintDevice(SecurityLevel::SOFTWARE))
+                .map_err(|e| match e {
+                    Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
+                        Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
+                    }
+                    e => e,
+                })
+                .context("In BacklevelKeyMintWrapper::wrap: Trying to get software device.")?;
+
+        Ok(BnKeyMintDevice::new_binder(
+            Self { real, soft, emu },
+            BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+        ))
+    }
+}
+
+impl<T> binder::Interface for BacklevelKeyMintWrapper<T> where T: EmulationDetector {}
+
+impl<T> IKeyMintDevice for BacklevelKeyMintWrapper<T>
+where
+    T: EmulationDetector + 'static,
+{
+    // For methods that don't involve keyblobs, forward to either the real
+    // device, or to both real & emulated devices.
+    fn getHardwareInfo(&self) -> binder::Result<KeyMintHardwareInfo> {
+        self.real.getHardwareInfo()
+    }
+    fn addRngEntropy(&self, data: &[u8]) -> binder::Result<()> {
+        self.real.addRngEntropy(data)
+    }
+    fn deleteAllKeys(&self) -> binder::Result<()> {
+        self.real.deleteAllKeys()
+    }
+    fn destroyAttestationIds(&self) -> binder::Result<()> {
+        self.real.destroyAttestationIds()
+    }
+    fn deviceLocked(
+        &self,
+        password_only: bool,
+        timestamp_token: Option<&TimeStampToken>,
+    ) -> binder::Result<()> {
+        // Propagate to both real and software devices, but only pay attention
+        // to the result from the real device.
+        let _ = self.soft.deviceLocked(password_only, timestamp_token);
+        self.real.deviceLocked(password_only, timestamp_token)
+    }
+    fn earlyBootEnded(&self) -> binder::Result<()> {
+        // Propagate to both real and software devices, but only pay attention
+        // to the result from the real device.
+        let _ = self.soft.earlyBootEnded();
+        self.real.earlyBootEnded()
+    }
+
+    // For methods that emit keyblobs, check whether the underlying real device
+    // supports the relevant parameters, and forward to the appropriate device.
+    // If the emulated device is used, ensure that the created keyblob gets
+    // prefixed so we can recognize it in future.
+    fn generateKey(
+        &self,
+        key_params: &[KeyParameter],
+        attestation_key: Option<&AttestationKey>,
+    ) -> binder::Result<KeyCreationResult> {
+        if self.emu.emulation_required(key_params, &KeyImportData::None) {
+            let mut result = self.soft.generateKey(key_params, attestation_key)?;
+            result.keyBlob = map_or_log_err(wrap_keyblob(&result.keyBlob), Ok)?;
+            Ok(result)
+        } else {
+            self.real.generateKey(key_params, attestation_key)
+        }
+    }
+    fn importKey(
+        &self,
+        key_params: &[KeyParameter],
+        key_format: KeyFormat,
+        key_data: &[u8],
+        attestation_key: Option<&AttestationKey>,
+    ) -> binder::Result<KeyCreationResult> {
+        if self.emu.emulation_required(key_params, &KeyImportData::new(key_format, key_data)?) {
+            let mut result =
+                self.soft.importKey(key_params, key_format, key_data, attestation_key)?;
+            result.keyBlob = map_or_log_err(wrap_keyblob(&result.keyBlob), Ok)?;
+            Ok(result)
+        } else {
+            self.real.importKey(key_params, key_format, key_data, attestation_key)
+        }
+    }
+    fn importWrappedKey(
+        &self,
+        wrapped_key_data: &[u8],
+        wrapping_key_blob: &[u8],
+        masking_key: &[u8],
+        unwrapping_params: &[KeyParameter],
+        password_sid: i64,
+        biometric_sid: i64,
+    ) -> binder::Result<KeyCreationResult> {
+        // A wrapped key cannot be software-emulated, as the wrapping key is
+        // likely hardware-bound.
+        self.real.importWrappedKey(
+            wrapped_key_data,
+            wrapping_key_blob,
+            masking_key,
+            unwrapping_params,
+            password_sid,
+            biometric_sid,
+        )
+    }
+
+    // For methods that use keyblobs, determine which device to forward the
+    // operation to based on whether the keyblob is appropriately prefixed.
+    fn upgradeKey(
+        &self,
+        keyblob_to_upgrade: &[u8],
+        upgrade_params: &[KeyParameter],
+    ) -> binder::Result<Vec<u8>> {
+        match unwrap_keyblob(keyblob_to_upgrade) {
+            KeyBlob::Raw(keyblob) => self.real.upgradeKey(keyblob, upgrade_params),
+            KeyBlob::Wrapped(keyblob) => {
+                // Re-wrap the upgraded keyblob.
+                let upgraded_keyblob = self.soft.upgradeKey(keyblob, upgrade_params)?;
+                map_or_log_err(wrap_keyblob(&upgraded_keyblob), Ok)
+            }
+        }
+    }
+    fn deleteKey(&self, keyblob: &[u8]) -> binder::Result<()> {
+        match unwrap_keyblob(keyblob) {
+            KeyBlob::Raw(keyblob) => self.real.deleteKey(keyblob),
+            KeyBlob::Wrapped(keyblob) => {
+                // Forward to the software implementation for completeness, but
+                // this should always be a no-op.
+                self.soft.deleteKey(keyblob)
+            }
+        }
+    }
+    fn begin(
+        &self,
+        purpose: KeyPurpose,
+        keyblob: &[u8],
+        params: &[KeyParameter],
+        auth_token: Option<&HardwareAuthToken>,
+    ) -> binder::Result<BeginResult> {
+        match unwrap_keyblob(keyblob) {
+            KeyBlob::Raw(keyblob) => self.real.begin(purpose, keyblob, params, auth_token),
+            KeyBlob::Wrapped(keyblob) => self.soft.begin(purpose, keyblob, params, auth_token),
+        }
+    }
+    fn getKeyCharacteristics(
+        &self,
+        keyblob: &[u8],
+        app_id: &[u8],
+        app_data: &[u8],
+    ) -> binder::Result<Vec<KeyCharacteristics>> {
+        match unwrap_keyblob(keyblob) {
+            KeyBlob::Raw(keyblob) => self.real.getKeyCharacteristics(keyblob, app_id, app_data),
+            KeyBlob::Wrapped(keyblob) => self.soft.getKeyCharacteristics(keyblob, app_id, app_data),
+        }
+    }
+    fn getRootOfTrustChallenge(&self) -> binder::Result<[u8; 16]> {
+        self.real.getRootOfTrustChallenge()
+    }
+    fn getRootOfTrust(&self, challenge: &[u8; 16]) -> binder::Result<Vec<u8>> {
+        self.real.getRootOfTrust(challenge)
+    }
+    fn sendRootOfTrust(&self, root_of_trust: &[u8]) -> binder::Result<()> {
+        self.real.sendRootOfTrust(root_of_trust)
+    }
+    fn convertStorageKeyToEphemeral(&self, storage_keyblob: &[u8]) -> binder::Result<Vec<u8>> {
+        // Storage keys should never be associated with a software emulated device.
+        self.real.convertStorageKeyToEphemeral(storage_keyblob)
+    }
+}
+
+/// Detector for current features that are not implemented by KeyMint V1.
+#[derive(Debug)]
+pub struct KeyMintV1 {
+    sec_level: SecurityLevel,
+}
+
+impl KeyMintV1 {
+    pub fn new(sec_level: SecurityLevel) -> Self {
+        Self { sec_level }
+    }
+}
+
+impl EmulationDetector for KeyMintV1 {
+    fn emulation_required(&self, params: &[KeyParameter], _import_data: &KeyImportData) -> bool {
+        // No current difference from KeyMint v1 for STRONGBOX (it doesn't
+        // support curve 25519).
+        if self.sec_level == SecurityLevel::STRONGBOX {
+            return false;
+        }
+
+        // KeyMint V1 does not support the use of curve 25519, so hunt for that
+        // in the parameters.
+        if params.iter().any(|p| {
+            p.tag == Tag::EC_CURVE && p.value == KeyParameterValue::EcCurve(EcCurve::CURVE_25519)
+        }) {
+            return true;
+        }
+        // In theory, if the `import_data` is `KeyImportData::Pkcs8` we could
+        // check the imported keymaterial for the Ed25519 / X25519 OIDs in the
+        // PKCS8 keydata, and use that to decide to route to software. However,
+        // the KeyMint spec doesn't require that so don't attempt to parse the
+        // key material here.
+        false
+    }
+}
+
+/// Detector for current features that are not implemented by KeyMaster, via the
+/// km_compat wrapper.
+#[derive(Debug)]
+pub struct Keymaster {
+    v1: KeyMintV1,
+}
+
+/// TODO(b/216434270): This could be used this to replace the emulation routing
+/// in the km_compat C++ code, and allow support for imported ECDH keys along
+/// the way. Would need to figure out what would happen to existing emulated
+/// keys though.
+#[allow(dead_code)]
+impl Keymaster {
+    pub fn new(sec_level: SecurityLevel) -> Self {
+        Self { v1: KeyMintV1::new(sec_level) }
+    }
+}
+
+impl EmulationDetector for Keymaster {
+    fn emulation_required(&self, params: &[KeyParameter], import_data: &KeyImportData) -> bool {
+        // The km_compat wrapper on top of Keymaster emulates the KeyMint V1
+        // interface, so any feature from > v1 needs to be emulated.
+        if self.v1.emulation_required(params, import_data) {
+            return true;
+        }
+
+        // Keymaster does not support ECDH (KeyPurpose::AGREE_KEY), so hunt for
+        // that in the parameters.
+        if params.iter().any(|p| {
+            p.tag == Tag::PURPOSE && p.value == KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY)
+        }) {
+            return true;
+        }
+        false
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_key_import_data() {
+        let data = vec![1, 2, 3];
+        assert_eq!(KeyImportData::new(KeyFormat::PKCS8, &data), Ok(KeyImportData::Pkcs8(&data)));
+        assert_eq!(KeyImportData::new(KeyFormat::RAW, &data), Ok(KeyImportData::Raw(&data)));
+        assert!(KeyImportData::new(KeyFormat::X509, &data).is_err());
+    }
+
+    #[test]
+    fn test_wrap_keyblob() {
+        let keyblob = vec![1, 2, 3];
+        let wrapped = wrap_keyblob(&keyblob).unwrap();
+        assert_eq!(&wrapped[..KEYBLOB_PREFIX.len()], KEYBLOB_PREFIX);
+        assert_eq!(&wrapped[KEYBLOB_PREFIX.len()..KEYBLOB_PREFIX.len() + keyblob.len()], &keyblob);
+        assert_eq!(unwrap_keyblob(&keyblob), KeyBlob::Raw(&keyblob));
+        assert_eq!(unwrap_keyblob(&wrapped), KeyBlob::Wrapped(&keyblob));
+
+        let mut corrupt_prefix = wrapped.clone();
+        corrupt_prefix[0] ^= 0x01;
+        assert_eq!(unwrap_keyblob(&corrupt_prefix), KeyBlob::Raw(&corrupt_prefix));
+
+        let mut corrupt_suffix = wrapped.clone();
+        corrupt_suffix[wrapped.len() - 1] ^= 0x01;
+        assert_eq!(unwrap_keyblob(&corrupt_suffix), KeyBlob::Raw(&corrupt_suffix));
+
+        let too_short = &wrapped[..wrapped.len() - 4];
+        assert_eq!(unwrap_keyblob(too_short), KeyBlob::Raw(too_short));
+    }
+
+    #[test]
+    fn test_keymintv1_emulation_required() {
+        let tests = vec![
+            (SecurityLevel::TRUSTED_ENVIRONMENT, vec![], false),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
+                    },
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::VERIFY),
+                    },
+                ],
+                false,
+            ),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![KeyParameter {
+                    tag: Tag::PURPOSE,
+                    value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                }],
+                false,
+            ),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                    },
+                    KeyParameter {
+                        tag: Tag::EC_CURVE,
+                        value: KeyParameterValue::EcCurve(EcCurve::P_256),
+                    },
+                ],
+                false,
+            ),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                    },
+                    KeyParameter {
+                        tag: Tag::EC_CURVE,
+                        value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
+                    },
+                ],
+                true,
+            ),
+            (
+                SecurityLevel::STRONGBOX,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                    },
+                    KeyParameter {
+                        tag: Tag::EC_CURVE,
+                        value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
+                    },
+                ],
+                false,
+            ),
+        ];
+        for (sec_level, params, want) in tests {
+            let v1 = KeyMintV1::new(sec_level);
+            let got = v1.emulation_required(&params, &KeyImportData::None);
+            assert_eq!(got, want, "emulation_required({:?})={}, want {}", params, got, want);
+        }
+    }
+
+    #[test]
+    fn test_keymaster_emulation_required() {
+        let tests = vec![
+            (SecurityLevel::TRUSTED_ENVIRONMENT, vec![], false),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
+                    },
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::VERIFY),
+                    },
+                ],
+                false,
+            ),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![KeyParameter {
+                    tag: Tag::PURPOSE,
+                    value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                }],
+                true,
+            ),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                    },
+                    KeyParameter {
+                        tag: Tag::EC_CURVE,
+                        value: KeyParameterValue::EcCurve(EcCurve::P_256),
+                    },
+                ],
+                true,
+            ),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                    },
+                    KeyParameter {
+                        tag: Tag::EC_CURVE,
+                        value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
+                    },
+                ],
+                true,
+            ),
+            (
+                SecurityLevel::STRONGBOX,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                    },
+                    KeyParameter {
+                        tag: Tag::EC_CURVE,
+                        value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
+                    },
+                ],
+                true,
+            ),
+            (
+                SecurityLevel::STRONGBOX,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
+                    },
+                    KeyParameter {
+                        tag: Tag::EC_CURVE,
+                        value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
+                    },
+                ],
+                false,
+            ),
+        ];
+        for (sec_level, params, want) in tests {
+            let v0 = Keymaster::new(sec_level);
+            let got = v0.emulation_required(&params, &KeyImportData::None);
+            assert_eq!(got, want, "emulation_required({:?})={}, want {}", params, got, want);
+        }
+    }
+}
diff --git a/keystore2/src/km_compat/Android.bp b/keystore2/src/km_compat/Android.bp
index 541788e..806f3dc 100644
--- a/keystore2/src/km_compat/Android.bp
+++ b/keystore2/src/km_compat/Android.bp
@@ -25,9 +25,10 @@
     name: "libkeystore2_km_compat",
     crate_name: "keystore2_km_compat",
     srcs: ["lib.rs"],
-
+    defaults: [
+        "keymint_use_latest_hal_aidl_rust",
+    ],
     rustlibs: [
-        "android.hardware.security.keymint-V1-rust",
         "android.security.compat-rust",
     ],
     shared_libs: [
@@ -41,8 +42,10 @@
     srcs: ["lib.rs"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
+    defaults: [
+        "keymint_use_latest_hal_aidl_rust",
+    ],
     rustlibs: [
-        "android.hardware.security.keymint-V1-rust",
         "android.security.compat-rust",
     ],
     shared_libs: [
@@ -53,15 +56,17 @@
 cc_library {
     name: "libkm_compat",
     srcs: ["km_compat.cpp"],
+    defaults: [
+        "keymint_use_latest_hal_aidl_ndk_shared",
+        "keystore2_use_latest_aidl_ndk_shared",
+    ],
     shared_libs: [
         "android.hardware.keymaster@3.0",
         "android.hardware.keymaster@4.0",
         "android.hardware.keymaster@4.1",
-        "android.hardware.security.keymint-V1-ndk_platform",
-        "android.hardware.security.secureclock-V1-ndk_platform",
-        "android.hardware.security.sharedsecret-V1-ndk_platform",
-        "android.security.compat-ndk_platform",
-        "android.system.keystore2-V1-ndk_platform",
+        "android.hardware.security.secureclock-V1-ndk",
+        "android.hardware.security.sharedsecret-V1-ndk",
+        "android.security.compat-ndk",
         "libbase",
         "libbinder_ndk",
         "libcrypto",
@@ -77,11 +82,13 @@
 cc_library {
     name: "libkm_compat_service",
     srcs: ["km_compat_service.cpp"],
+    defaults: [
+        "keymint_use_latest_hal_aidl_ndk_shared",
+    ],
     shared_libs: [
-        "android.hardware.security.keymint-V1-ndk_platform",
-        "android.hardware.security.secureclock-V1-ndk_platform",
-        "android.hardware.security.sharedsecret-V1-ndk_platform",
-        "android.security.compat-ndk_platform",
+        "android.hardware.security.secureclock-V1-ndk",
+        "android.hardware.security.sharedsecret-V1-ndk",
+        "android.security.compat-ndk",
         "libbinder_ndk",
         "libcrypto",
         "libkm_compat",
@@ -103,15 +110,17 @@
         "parameter_conversion_test.cpp",
         "slot_test.cpp",
     ],
+    defaults: [
+        "keymint_use_latest_hal_aidl_ndk_shared",
+        "keystore2_use_latest_aidl_ndk_shared",
+    ],
     shared_libs: [
         "android.hardware.keymaster@3.0",
         "android.hardware.keymaster@4.0",
         "android.hardware.keymaster@4.1",
-        "android.hardware.security.keymint-V1-ndk_platform",
-        "android.hardware.security.secureclock-V1-ndk_platform",
-        "android.hardware.security.sharedsecret-V1-ndk_platform",
-        "android.security.compat-ndk_platform",
-        "android.system.keystore2-V1-ndk_platform",
+        "android.hardware.security.secureclock-V1-ndk",
+        "android.hardware.security.sharedsecret-V1-ndk",
+        "android.security.compat-ndk",
         "libbase",
         "libbinder_ndk",
         "libcrypto",
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index bb60047..3db1a9f 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -384,29 +384,39 @@
     return ssps;
 }
 
-void OperationSlots::setNumFreeSlots(uint8_t numFreeSlots) {
+void OperationSlotManager::setNumFreeSlots(uint8_t numFreeSlots) {
     std::lock_guard<std::mutex> lock(mNumFreeSlotsMutex);
     mNumFreeSlots = numFreeSlots;
 }
 
-bool OperationSlots::claimSlot() {
-    std::lock_guard<std::mutex> lock(mNumFreeSlotsMutex);
-    if (mNumFreeSlots > 0) {
-        mNumFreeSlots--;
-        return true;
+std::optional<OperationSlot>
+OperationSlotManager::claimSlot(std::shared_ptr<OperationSlotManager> operationSlots) {
+    std::lock_guard<std::mutex> lock(operationSlots->mNumFreeSlotsMutex);
+    if (operationSlots->mNumFreeSlots > 0) {
+        operationSlots->mNumFreeSlots--;
+        return OperationSlot(std::move(operationSlots), std::nullopt);
     }
-    return false;
+    return std::nullopt;
 }
 
-void OperationSlots::freeSlot() {
+OperationSlot
+OperationSlotManager::claimReservedSlot(std::shared_ptr<OperationSlotManager> operationSlots) {
+    std::unique_lock<std::mutex> reservedGuard(operationSlots->mReservedSlotMutex);
+    return OperationSlot(std::move(operationSlots), std::move(reservedGuard));
+}
+
+OperationSlot::OperationSlot(std::shared_ptr<OperationSlotManager> slots,
+                             std::optional<std::unique_lock<std::mutex>> reservedGuard)
+    : mOperationSlots(std::move(slots)), mReservedGuard(std::move(reservedGuard)) {}
+
+void OperationSlotManager::freeSlot() {
     std::lock_guard<std::mutex> lock(mNumFreeSlotsMutex);
     mNumFreeSlots++;
 }
 
-void OperationSlot::freeSlot() {
-    if (mIsActive) {
+OperationSlot::~OperationSlot() {
+    if (!mReservedGuard && mOperationSlots) {
         mOperationSlots->freeSlot();
-        mIsActive = false;
     }
 }
 
@@ -496,16 +506,15 @@
     auto legacyKeyGENParams = convertKeyParametersToLegacy(extractGenerationParams(inKeyParams));
     auto legacyKeyFormat = convertKeyFormatToLegacy(in_inKeyFormat);
     KMV1::ErrorCode errorCode;
-    auto result = mDevice->importKey(legacyKeyGENParams, legacyKeyFormat, in_inKeyData,
-                                     [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
-                                         const V4_0_KeyCharacteristics& keyCharacteristics) {
-                                         errorCode = convert(error);
-                                         out_creationResult->keyBlob =
-                                             keyBlobPrefix(keyBlob, false);
-                                         out_creationResult->keyCharacteristics =
-                                             processLegacyCharacteristics(
-                                                 securityLevel_, inKeyParams, keyCharacteristics);
-                                     });
+    auto result = mDevice->importKey(
+        legacyKeyGENParams, legacyKeyFormat, in_inKeyData,
+        [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
+            const V4_0_KeyCharacteristics& keyCharacteristics) {
+            errorCode = convert(error);
+            out_creationResult->keyBlob = keyBlobPrefix(keyBlob, false);
+            out_creationResult->keyCharacteristics =
+                processLegacyCharacteristics(securityLevel_, inKeyParams, keyCharacteristics);
+        });
     if (!result.isOk()) {
         LOG(ERROR) << __func__ << " transaction failed. " << result.description();
         return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
@@ -613,9 +622,15 @@
                                    const std::vector<KeyParameter>& in_inParams,
                                    const std::optional<HardwareAuthToken>& in_inAuthToken,
                                    BeginResult* _aidl_return) {
-    if (!mOperationSlots.claimSlot()) {
-        return convertErrorCode(V4_0_ErrorCode::TOO_MANY_OPERATIONS);
-    }
+    return beginInternal(in_inPurpose, prefixedKeyBlob, in_inParams, in_inAuthToken,
+                         false /* useReservedSlot */, _aidl_return);
+}
+
+ScopedAStatus KeyMintDevice::beginInternal(KeyPurpose in_inPurpose,
+                                           const std::vector<uint8_t>& prefixedKeyBlob,
+                                           const std::vector<KeyParameter>& in_inParams,
+                                           const std::optional<HardwareAuthToken>& in_inAuthToken,
+                                           bool useReservedSlot, BeginResult* _aidl_return) {
 
     const std::vector<uint8_t>& in_inKeyBlob = prefixedKeyBlobRemovePrefix(prefixedKeyBlob);
     if (prefixedKeyBlobIsSoftKeyMint(prefixedKeyBlob)) {
@@ -623,28 +638,41 @@
                                          _aidl_return);
     }
 
+    OperationSlot slot;
+    // No need to claim a slot for software device.
+    if (useReservedSlot) {
+        // There is only one reserved slot. This function blocks until
+        // the reserved slot becomes available.
+        slot = OperationSlotManager::claimReservedSlot(mOperationSlots);
+    } else {
+        if (auto opt_slot = OperationSlotManager::claimSlot(mOperationSlots)) {
+            slot = std::move(*opt_slot);
+        } else {
+            return convertErrorCode(V4_0_ErrorCode::TOO_MANY_OPERATIONS);
+        }
+    }
+
     auto legacyPurpose =
         static_cast<::android::hardware::keymaster::V4_0::KeyPurpose>(in_inPurpose);
     auto legacyParams = convertKeyParametersToLegacy(in_inParams);
     auto legacyAuthToken = convertAuthTokenToLegacy(in_inAuthToken);
     KMV1::ErrorCode errorCode;
-    auto result = mDevice->begin(
-        legacyPurpose, in_inKeyBlob, legacyParams, legacyAuthToken,
-        [&](V4_0_ErrorCode error, const hidl_vec<V4_0_KeyParameter>& outParams,
-            uint64_t operationHandle) {
-            errorCode = convert(error);
-            _aidl_return->challenge = operationHandle;
-            _aidl_return->params = convertKeyParametersFromLegacy(outParams);
-            _aidl_return->operation = ndk::SharedRefBase::make<KeyMintOperation>(
-                mDevice, operationHandle, &mOperationSlots, error == V4_0_ErrorCode::OK);
-        });
+    auto result =
+        mDevice->begin(legacyPurpose, in_inKeyBlob, legacyParams, legacyAuthToken,
+                       [&](V4_0_ErrorCode error, const hidl_vec<V4_0_KeyParameter>& outParams,
+                           uint64_t operationHandle) {
+                           errorCode = convert(error);
+                           if (error == V4_0_ErrorCode::OK) {
+                               _aidl_return->challenge = operationHandle;
+                               _aidl_return->params = convertKeyParametersFromLegacy(outParams);
+                               _aidl_return->operation = ndk::SharedRefBase::make<KeyMintOperation>(
+                                   mDevice, operationHandle, std::move(slot));
+                           }
+                       });
     if (!result.isOk()) {
         LOG(ERROR) << __func__ << " transaction failed. " << result.description();
         errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
     }
-    if (errorCode != KMV1::ErrorCode::OK) {
-        mOperationSlots.freeSlot();
-    }
     return convertErrorCode(errorCode);
 }
 
@@ -704,8 +732,9 @@
         LOG(ERROR) << __func__ << " export_key failed: " << ret.description();
         return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
     }
-    if (km_error != KMV1::ErrorCode::OK)
+    if (km_error != KMV1::ErrorCode::OK) {
         LOG(ERROR) << __func__ << " export_key failed, code " << int32_t(km_error);
+    }
 
     return convertErrorCode(km_error);
 }
@@ -741,6 +770,19 @@
     }
 }
 
+ScopedAStatus KeyMintDevice::getRootOfTrustChallenge(std::array<uint8_t, 16>* /* challenge */) {
+    return convertErrorCode(KMV1::ErrorCode::UNIMPLEMENTED);
+}
+
+ScopedAStatus KeyMintDevice::getRootOfTrust(const std::array<uint8_t, 16>& /* challenge */,
+                                            std::vector<uint8_t>* /* rootOfTrust */) {
+    return convertErrorCode(KMV1::ErrorCode::UNIMPLEMENTED);
+}
+
+ScopedAStatus KeyMintDevice::sendRootOfTrust(const std::vector<uint8_t>& /* rootOfTrust */) {
+    return convertErrorCode(KMV1::ErrorCode::UNIMPLEMENTED);
+}
+
 ScopedAStatus KeyMintOperation::updateAad(const std::vector<uint8_t>& input,
                                           const std::optional<HardwareAuthToken>& optAuthToken,
                                           const std::optional<TimeStampToken>& optTimeStampToken) {
@@ -757,7 +799,11 @@
         LOG(ERROR) << __func__ << " transaction failed. " << result.description();
         errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
     }
-    if (errorCode != KMV1::ErrorCode::OK) mOperationSlot.freeSlot();
+
+    // Operation slot is no longer occupied.
+    if (errorCode != KMV1::ErrorCode::OK) {
+        mOperationSlot = std::nullopt;
+    }
 
     return convertErrorCode(errorCode);
 }
@@ -815,7 +861,10 @@
         inputPos += consumed;
     }
 
-    if (errorCode != KMV1::ErrorCode::OK) mOperationSlot.freeSlot();
+    // Operation slot is no longer occupied.
+    if (errorCode != KMV1::ErrorCode::OK) {
+        mOperationSlot = std::nullopt;
+    }
 
     return convertErrorCode(errorCode);
 }
@@ -846,17 +895,19 @@
             *out_output = output;
         });
 
-    mOperationSlot.freeSlot();
     if (!result.isOk()) {
         LOG(ERROR) << __func__ << " transaction failed. " << result.description();
         errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
     }
+
+    mOperationSlot = std::nullopt;
+
     return convertErrorCode(errorCode);
 }
 
 ScopedAStatus KeyMintOperation::abort() {
     auto result = mDevice->abort(mOperationHandle);
-    mOperationSlot.freeSlot();
+    mOperationSlot = std::nullopt;
     if (!result.isOk()) {
         LOG(ERROR) << __func__ << " transaction failed. " << result.description();
         return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
@@ -865,7 +916,7 @@
 }
 
 KeyMintOperation::~KeyMintOperation() {
-    if (mOperationSlot.hasSlot()) {
+    if (mOperationSlot) {
         auto error = abort();
         if (!error.isOk()) {
             LOG(WARNING) << "Error calling abort in ~KeyMintOperation: " << error.getMessage();
@@ -1118,8 +1169,8 @@
                 kps.push_back(KMV1::makeKeyParameter(KMV1::TAG_PADDING, origPadding));
             }
             BeginResult beginResult;
-            auto error =
-                begin(KeyPurpose::SIGN, prefixedKeyBlob, kps, HardwareAuthToken(), &beginResult);
+            auto error = beginInternal(KeyPurpose::SIGN, prefixedKeyBlob, kps, HardwareAuthToken(),
+                                       true /* useReservedSlot */, &beginResult);
             if (!error.isOk()) {
                 errorCode = toErrorCode(error);
                 return std::vector<uint8_t>();
@@ -1355,20 +1406,21 @@
 }
 
 void KeyMintDevice::setNumFreeSlots(uint8_t numFreeSlots) {
-    mOperationSlots.setNumFreeSlots(numFreeSlots);
+    mOperationSlots->setNumFreeSlots(numFreeSlots);
 }
 
 // Constructors and helpers.
 
 KeyMintDevice::KeyMintDevice(sp<Keymaster> device, KeyMintSecurityLevel securityLevel)
-    : mDevice(device), securityLevel_(securityLevel) {
+    : mDevice(device), mOperationSlots(std::make_shared<OperationSlotManager>()),
+      securityLevel_(securityLevel) {
     if (securityLevel == KeyMintSecurityLevel::STRONGBOX) {
         setNumFreeSlots(3);
     } else {
         setNumFreeSlots(15);
     }
 
-    softKeyMintDevice_.reset(CreateKeyMintDevice(KeyMintSecurityLevel::SOFTWARE));
+    softKeyMintDevice_ = CreateKeyMintDevice(KeyMintSecurityLevel::SOFTWARE);
 }
 
 sp<Keymaster> getDevice(KeyMintSecurityLevel securityLevel) {
@@ -1391,14 +1443,33 @@
     }
 }
 
+std::shared_ptr<IKeyMintDevice> getSoftwareKeymintDevice() {
+    static std::mutex mutex;
+    static std::shared_ptr<IKeyMintDevice> swDevice;
+    std::lock_guard<std::mutex> lock(mutex);
+    if (!swDevice) {
+        swDevice = CreateKeyMintDevice(KeyMintSecurityLevel::SOFTWARE);
+    }
+    return swDevice;
+}
+
 std::shared_ptr<KeyMintDevice>
-KeyMintDevice::createKeyMintDevice(KeyMintSecurityLevel securityLevel) {
+KeyMintDevice::getWrappedKeymasterDevice(KeyMintSecurityLevel securityLevel) {
     if (auto dev = getDevice(securityLevel)) {
         return ndk::SharedRefBase::make<KeyMintDevice>(std::move(dev), securityLevel);
     }
     return {};
 }
 
+std::shared_ptr<IKeyMintDevice>
+KeyMintDevice::createKeyMintDevice(KeyMintSecurityLevel securityLevel) {
+    if (securityLevel == KeyMintSecurityLevel::SOFTWARE) {
+        return getSoftwareKeymintDevice();
+    } else {
+        return getWrappedKeymasterDevice(securityLevel);
+    }
+}
+
 std::shared_ptr<SharedSecret> SharedSecret::createSharedSecret(KeyMintSecurityLevel securityLevel) {
     auto device = getDevice(securityLevel);
     if (!device) {
diff --git a/keystore2/src/km_compat/km_compat.h b/keystore2/src/km_compat/km_compat.h
index 70c7b86..6654c4a 100644
--- a/keystore2/src/km_compat/km_compat.h
+++ b/keystore2/src/km_compat/km_compat.h
@@ -50,41 +50,55 @@
 using ::android::hardware::keymaster::V4_1::support::Keymaster;
 using ::ndk::ScopedAStatus;
 
-class OperationSlots {
-  private:
-    uint8_t mNumFreeSlots;
-    std::mutex mNumFreeSlotsMutex;
-
-  public:
-    void setNumFreeSlots(uint8_t numFreeSlots);
-    bool claimSlot();
-    void freeSlot();
-};
-
+class OperationSlot;
+class OperationSlotManager;
 // An abstraction for a single operation slot.
 // This contains logic to ensure that we do not free the slot multiple times,
 // e.g., if we call abort twice on the same operation.
 class OperationSlot {
+    friend OperationSlotManager;
+
   private:
-    OperationSlots* mOperationSlots;
-    bool mIsActive;
+    std::shared_ptr<OperationSlotManager> mOperationSlots;
+    std::optional<std::unique_lock<std::mutex>> mReservedGuard;
+
+  protected:
+    OperationSlot(std::shared_ptr<OperationSlotManager>,
+                  std::optional<std::unique_lock<std::mutex>> reservedGuard);
+    OperationSlot(const OperationSlot&) = delete;
+    OperationSlot& operator=(const OperationSlot&) = delete;
 
   public:
-    OperationSlot(OperationSlots* slots, bool isActive)
-        : mOperationSlots(slots), mIsActive(isActive) {}
+    OperationSlot() : mOperationSlots(nullptr), mReservedGuard(std::nullopt) {}
+    OperationSlot(OperationSlot&&) = default;
+    OperationSlot& operator=(OperationSlot&&) = default;
+    ~OperationSlot();
+};
 
+class OperationSlotManager {
+  private:
+    uint8_t mNumFreeSlots;
+    std::mutex mNumFreeSlotsMutex;
+    std::mutex mReservedSlotMutex;
+
+  public:
+    void setNumFreeSlots(uint8_t numFreeSlots);
+    static std::optional<OperationSlot>
+    claimSlot(std::shared_ptr<OperationSlotManager> operationSlots);
+    static OperationSlot claimReservedSlot(std::shared_ptr<OperationSlotManager> operationSlots);
     void freeSlot();
-    bool hasSlot() { return mIsActive; }
 };
 
 class KeyMintDevice : public aidl::android::hardware::security::keymint::BnKeyMintDevice {
   private:
     ::android::sp<Keymaster> mDevice;
-    OperationSlots mOperationSlots;
+    std::shared_ptr<OperationSlotManager> mOperationSlots;
 
   public:
     explicit KeyMintDevice(::android::sp<Keymaster>, KeyMintSecurityLevel);
-    static std::shared_ptr<KeyMintDevice> createKeyMintDevice(KeyMintSecurityLevel securityLevel);
+    static std::shared_ptr<IKeyMintDevice> createKeyMintDevice(KeyMintSecurityLevel securityLevel);
+    static std::shared_ptr<KeyMintDevice>
+    getWrappedKeymasterDevice(KeyMintSecurityLevel securityLevel);
 
     ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* _aidl_return) override;
     ScopedAStatus addRngEntropy(const std::vector<uint8_t>& in_data) override;
@@ -107,10 +121,15 @@
     ScopedAStatus deleteKey(const std::vector<uint8_t>& in_inKeyBlob) override;
     ScopedAStatus deleteAllKeys() override;
     ScopedAStatus destroyAttestationIds() override;
+
     ScopedAStatus begin(KeyPurpose in_inPurpose, const std::vector<uint8_t>& in_inKeyBlob,
                         const std::vector<KeyParameter>& in_inParams,
                         const std::optional<HardwareAuthToken>& in_inAuthToken,
                         BeginResult* _aidl_return) override;
+    ScopedAStatus beginInternal(KeyPurpose in_inPurpose, const std::vector<uint8_t>& in_inKeyBlob,
+                                const std::vector<KeyParameter>& in_inParams,
+                                const std::optional<HardwareAuthToken>& in_inAuthToken,
+                                bool useReservedSlot, BeginResult* _aidl_return);
     ScopedAStatus deviceLocked(bool passwordOnly,
                                const std::optional<TimeStampToken>& timestampToken) override;
     ScopedAStatus earlyBootEnded() override;
@@ -123,6 +142,11 @@
                           const std::vector<uint8_t>& appId, const std::vector<uint8_t>& appData,
                           std::vector<KeyCharacteristics>* keyCharacteristics) override;
 
+    ScopedAStatus getRootOfTrustChallenge(std::array<uint8_t, 16>* challenge);
+    ScopedAStatus getRootOfTrust(const std::array<uint8_t, 16>& challenge,
+                                 std::vector<uint8_t>* rootOfTrust);
+    ScopedAStatus sendRootOfTrust(const std::vector<uint8_t>& rootOfTrust);
+
     // These are public to allow testing code to use them directly.
     // This class should not be used publicly anyway.
     std::variant<std::vector<Certificate>, KMV1_ErrorCode>
@@ -141,9 +165,8 @@
 
 class KeyMintOperation : public aidl::android::hardware::security::keymint::BnKeyMintOperation {
   public:
-    KeyMintOperation(::android::sp<Keymaster> device, uint64_t operationHandle,
-                     OperationSlots* slots, bool isActive)
-        : mDevice(device), mOperationHandle(operationHandle), mOperationSlot(slots, isActive) {}
+    KeyMintOperation(::android::sp<Keymaster> device, uint64_t operationHandle, OperationSlot slot)
+        : mDevice(device), mOperationHandle(operationHandle), mOperationSlot(std::move(slot)) {}
     ~KeyMintOperation();
 
     ScopedAStatus updateAad(const std::vector<uint8_t>& input,
@@ -181,7 +204,7 @@
     std::vector<uint8_t> mUpdateBuffer;
     ::android::sp<Keymaster> mDevice;
     uint64_t mOperationHandle;
-    OperationSlot mOperationSlot;
+    std::optional<OperationSlot> mOperationSlot;
 };
 
 class SharedSecret : public aidl::android::hardware::security::sharedsecret::BnSharedSecret {
diff --git a/keystore2/src/km_compat/km_compat_type_conversion.h b/keystore2/src/km_compat/km_compat_type_conversion.h
index de09477..33248a4 100644
--- a/keystore2/src/km_compat/km_compat_type_conversion.h
+++ b/keystore2/src/km_compat/km_compat_type_conversion.h
@@ -16,6 +16,9 @@
 
 #pragma once
 
+#include <optional>
+
+#include <aidl/android/hardware/security/keymint/EcCurve.h>
 #include <aidl/android/hardware/security/keymint/ErrorCode.h>
 #include <keymasterV4_1/keymaster_tags.h>
 #include <keymint_support/keymint_tags.h>
@@ -278,7 +281,7 @@
     }
 }
 
-static V4_0::EcCurve convert(KMV1::EcCurve e) {
+static std::optional<V4_0::EcCurve> convert(KMV1::EcCurve e) {
     switch (e) {
     case KMV1::EcCurve::P_224:
         return V4_0::EcCurve::P_224;
@@ -288,7 +291,11 @@
         return V4_0::EcCurve::P_384;
     case KMV1::EcCurve::P_521:
         return V4_0::EcCurve::P_521;
+    case KMV1::EcCurve::CURVE_25519:
+        // KeyMaster did not support curve 25519
+        return std::nullopt;
     }
+    return std::nullopt;
 }
 
 static KMV1::EcCurve convert(V4_0::EcCurve e) {
@@ -490,7 +497,9 @@
         break;
     case KMV1::Tag::EC_CURVE:
         if (auto v = KMV1::authorizationValue(KMV1::TAG_EC_CURVE, kp)) {
-            return V4_0::makeKeyParameter(V4_0::TAG_EC_CURVE, convert(v->get()));
+            if (auto curve = convert(v->get())) {
+                return V4_0::makeKeyParameter(V4_0::TAG_EC_CURVE, curve.value());
+            }
         }
         break;
     case KMV1::Tag::RSA_PUBLIC_EXPONENT:
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
index 56c35bf..13f7760 100644
--- a/keystore2/src/km_compat/lib.rs
+++ b/keystore2/src/km_compat/lib.rs
@@ -260,7 +260,7 @@
         if let Some(mut extras) = extra_params {
             kps.append(&mut extras);
         }
-        let result = legacy.begin(purpose, &blob, &kps, None);
+        let result = legacy.begin(purpose, blob, &kps, None);
         assert!(result.is_ok(), "{:?}", result);
         result.unwrap()
     }
@@ -286,7 +286,7 @@
         let operation = begin_result.operation.unwrap();
 
         let update_aad_result = operation.updateAad(
-            &b"foobar".to_vec(),
+            b"foobar".as_ref(),
             None, /* authToken */
             None, /* timestampToken */
         );
@@ -310,7 +310,7 @@
         let operation = begin_result.operation.unwrap();
 
         let update_aad_result = operation.updateAad(
-            &b"foobar".to_vec(),
+            b"foobar".as_ref(),
             None, /* authToken */
             None, /* timestampToken */
         );
@@ -450,10 +450,6 @@
         )));
         assert!(sec_level_enforced.iter().any(|kp| matches!(
             kp,
-            KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KeyParameterValue::Integer(_) }
-        )));
-        assert!(sec_level_enforced.iter().any(|kp| matches!(
-            kp,
             KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KeyParameterValue::Integer(_) }
         )));
     }
diff --git a/keystore2/src/km_compat/slot_test.cpp b/keystore2/src/km_compat/slot_test.cpp
index 43f3bc6..d734970 100644
--- a/keystore2/src/km_compat/slot_test.cpp
+++ b/keystore2/src/km_compat/slot_test.cpp
@@ -26,6 +26,7 @@
 using ::aidl::android::hardware::security::keymint::BlockMode;
 using ::aidl::android::hardware::security::keymint::Certificate;
 using ::aidl::android::hardware::security::keymint::Digest;
+using ::aidl::android::hardware::security::keymint::EcCurve;
 using ::aidl::android::hardware::security::keymint::ErrorCode;
 using ::aidl::android::hardware::security::keymint::IKeyMintOperation;
 using ::aidl::android::hardware::security::keymint::KeyCharacteristics;
@@ -53,6 +54,25 @@
     return creationResult.keyBlob;
 }
 
+static bool generateECSingingKey(std::shared_ptr<KeyMintDevice> device) {
+    uint64_t now_ms = (uint64_t)time(nullptr) * 1000;
+
+    auto keyParams = std::vector<KeyParameter>({
+        KMV1::makeKeyParameter(KMV1::TAG_ALGORITHM, Algorithm::EC),
+        KMV1::makeKeyParameter(KMV1::TAG_EC_CURVE, EcCurve::P_256),
+        KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED, true),
+        KMV1::makeKeyParameter(KMV1::TAG_DIGEST, Digest::SHA_2_256),
+        KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::SIGN),
+        KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::VERIFY),
+        KMV1::makeKeyParameter(KMV1::TAG_CERTIFICATE_NOT_BEFORE, now_ms - 60 * 60 * 1000),
+        KMV1::makeKeyParameter(KMV1::TAG_CERTIFICATE_NOT_AFTER, now_ms + 60 * 60 * 1000),
+    });
+    KeyCreationResult creationResult;
+    auto status = device->generateKey(keyParams, std::nullopt /* attest_key */, &creationResult);
+    EXPECT_TRUE(status.isOk()) << status.getDescription();
+    return status.isOk();
+}
+
 static std::variant<BeginResult, ScopedAStatus> begin(std::shared_ptr<KeyMintDevice> device,
                                                       bool valid) {
     auto blob = generateAESKey(device);
@@ -69,17 +89,57 @@
     return beginResult;
 }
 
+static std::shared_ptr<KMV1::IKeyMintOperation>
+generateAndBeginECDHKeyOperation(std::shared_ptr<KeyMintDevice> device) {
+    uint64_t now_ms = (uint64_t)time(nullptr) * 1000;
+
+    auto keyParams = std::vector<KeyParameter>({
+        KMV1::makeKeyParameter(KMV1::TAG_ALGORITHM, Algorithm::EC),
+        KMV1::makeKeyParameter(KMV1::TAG_EC_CURVE, EcCurve::P_256),
+        KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED, true),
+        KMV1::makeKeyParameter(KMV1::TAG_DIGEST, Digest::NONE),
+        KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::AGREE_KEY),
+        KMV1::makeKeyParameter(KMV1::TAG_CERTIFICATE_NOT_BEFORE, now_ms - 60 * 60 * 1000),
+        KMV1::makeKeyParameter(KMV1::TAG_CERTIFICATE_NOT_AFTER, now_ms + 60 * 60 * 1000),
+    });
+    KeyCreationResult creationResult;
+    auto status = device->generateKey(keyParams, std::nullopt /* attest_key */, &creationResult);
+    EXPECT_TRUE(status.isOk()) << status.getDescription();
+    if (!status.isOk()) {
+        return {};
+    }
+    std::vector<KeyParameter> kps;
+    BeginResult beginResult;
+    auto bstatus = device->begin(KeyPurpose::AGREE_KEY, creationResult.keyBlob, kps,
+                                 HardwareAuthToken(), &beginResult);
+    EXPECT_TRUE(status.isOk()) << status.getDescription();
+    if (status.isOk()) {
+        return beginResult.operation;
+    }
+    return {};
+}
+
 static const int NUM_SLOTS = 2;
 
 TEST(SlotTest, TestSlots) {
     static std::shared_ptr<KeyMintDevice> device =
-        KeyMintDevice::createKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
+        KeyMintDevice::getWrappedKeymasterDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
+    ASSERT_NE(device.get(), nullptr);
+
     device->setNumFreeSlots(NUM_SLOTS);
 
     // A begin() that returns a failure should not use a slot.
     auto result = begin(device, false);
     ASSERT_TRUE(std::holds_alternative<ScopedAStatus>(result));
 
+    // Software emulated operations must not leak virtual slots.
+    ASSERT_TRUE(!!generateAndBeginECDHKeyOperation(device));
+
+    // Software emulated operations must not impact virtual slots accounting.
+    // As opposed to the previous call, the software operation is kept alive.
+    auto software_op = generateAndBeginECDHKeyOperation(device);
+    ASSERT_TRUE(!!software_op);
+
     // Fill up all the slots.
     std::vector<std::shared_ptr<IKeyMintOperation>> operations;
     for (int i = 0; i < NUM_SLOTS; i++) {
@@ -94,6 +154,14 @@
     ASSERT_EQ(std::get<ScopedAStatus>(result).getServiceSpecificError(),
               static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
 
+    // At this point all slots are in use. We should still be able to generate keys which
+    // require an operation slot during generation.
+    ASSERT_TRUE(generateECSingingKey(device));
+
+    // Software emulated operations should work despite having all virtual operation slots
+    // depleted.
+    ASSERT_TRUE(generateAndBeginECDHKeyOperation(device));
+
     // TODO: I'm not sure how to generate a failing update call to test that.
 
     // Calling finish should free up a slot.
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index 6b16d2e..cbc680d 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -17,8 +17,8 @@
 use crate::{
     error::{Error as KsError, ResponseCode},
     key_parameter::{KeyParameter, KeyParameterValue},
-    super_key::SuperKeyManager,
     utils::uid_to_android_user,
+    utils::AesGcm,
 };
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     SecurityLevel::SecurityLevel, Tag::Tag, TagType::TagType,
@@ -26,6 +26,7 @@
 use anyhow::{Context, Result};
 use keystore2_crypto::{aes_gcm_decrypt, Password, ZVec};
 use std::collections::{HashMap, HashSet};
+use std::sync::Arc;
 use std::{convert::TryInto, fs::File, path::Path, path::PathBuf};
 use std::{
     fs,
@@ -87,6 +88,14 @@
     /// an invalid alias filename encoding.
     #[error("Invalid alias filename encoding.")]
     BadEncoding,
+    /// A component of the requested entry other than the KM key blob itself
+    /// was encrypted and no super key was provided.
+    #[error("Locked entry component.")]
+    LockedComponent,
+    /// The uids presented to move_keystore_entry belonged to different
+    /// Android users.
+    #[error("Cannot move keys across Android users.")]
+    AndroidUserMismatch,
 }
 
 /// The blob payload, optionally with all information required to decrypt it.
@@ -96,6 +105,16 @@
     Generic(Vec<u8>),
     /// A legacy key characteristics file. This has only a single list of Authorizations.
     Characteristics(Vec<u8>),
+    /// A legacy key characteristics file. This has only a single list of Authorizations.
+    /// Additionally, this characteristics file was encrypted with the user's super key.
+    EncryptedCharacteristics {
+        /// Initialization vector.
+        iv: Vec<u8>,
+        /// Aead tag for integrity verification.
+        tag: Vec<u8>,
+        /// Ciphertext.
+        data: Vec<u8>,
+    },
     /// A key characteristics cache has both a hardware enforced and a software enforced list
     /// of authorizations.
     CharacteristicsCache(Vec<u8>),
@@ -124,6 +143,17 @@
         /// Ciphertext.
         data: Vec<u8>,
     },
+    /// An encrypted blob. Includes the initialization vector, the aead tag, and the
+    /// ciphertext data. The key can be selected from context, i.e., the owner of the key
+    /// blob. This is a special case for generic encrypted blobs as opposed to key blobs.
+    EncryptedGeneric {
+        /// Initialization vector.
+        iv: Vec<u8>,
+        /// Aead tag for integrity verification.
+        tag: Vec<u8>,
+        /// Ciphertext.
+        data: Vec<u8>,
+    },
     /// Holds the plaintext key blob either after unwrapping an encrypted blob or when the
     /// blob was stored in "plaintext" on disk. The "plaintext" of a key blob is not actual
     /// plaintext because all KeyMint blobs are encrypted with a device bound key. The key
@@ -132,6 +162,19 @@
     Decrypted(ZVec),
 }
 
+/// Keystore used two different key characteristics file formats in the past.
+/// The key characteristics cache which superseded the characteristics file.
+/// The latter stored only one list of key parameters, while the former stored
+/// a hardware enforced and a software enforced list. This Enum indicates which
+/// type was read from the file system.
+#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub enum LegacyKeyCharacteristics {
+    /// A characteristics cache was read.
+    Cache(Vec<KeyParameter>),
+    /// A characteristics file was read.
+    File(Vec<KeyParameter>),
+}
+
 /// Represents a loaded legacy key blob file.
 #[derive(Debug, Eq, PartialEq)]
 pub struct Blob {
@@ -169,6 +212,16 @@
 }
 
 impl Blob {
+    /// Creates a new blob from flags and value.
+    pub fn new(flags: u8, value: BlobValue) -> Self {
+        Self { flags, value }
+    }
+
+    /// Return the raw flags of this Blob.
+    pub fn get_flags(&self) -> u8 {
+        self.flags
+    }
+
     /// This blob was generated with a fallback software KM device.
     pub fn is_fallback(&self) -> bool {
         self.flags & flags::FALLBACK != 0
@@ -212,10 +265,14 @@
     // version (1 Byte)
     // blob_type (1 Byte)
     // flags (1 Byte)
-    // info (1 Byte)
+    // info (1 Byte) Size of an info field appended to the blob.
     // initialization_vector (16 Bytes)
     // integrity (MD5 digest or gcm tag) (16 Bytes)
     // length (4 Bytes)
+    //
+    // The info field is used to store the salt for password encrypted blobs.
+    // The beginning of the info field can be computed from the file length
+    // and the info byte from the header: <file length> - <info> bytes.
     const COMMON_HEADER_SIZE: usize = 4 + Self::IV_SIZE + Self::GCM_TAG_LENGTH + 4;
 
     const VERSION_OFFSET: usize = 0;
@@ -341,12 +398,28 @@
         let tag = &buffer[Self::AEAD_TAG_OFFSET..Self::AEAD_TAG_OFFSET + Self::GCM_TAG_LENGTH];
 
         match (blob_type, is_encrypted, salt) {
-            (blob_types::GENERIC, _, _) => {
+            (blob_types::GENERIC, false, _) => {
                 Ok(Blob { flags, value: BlobValue::Generic(value.to_vec()) })
             }
-            (blob_types::KEY_CHARACTERISTICS, _, _) => {
+            (blob_types::GENERIC, true, _) => Ok(Blob {
+                flags,
+                value: BlobValue::EncryptedGeneric {
+                    iv: iv.to_vec(),
+                    tag: tag.to_vec(),
+                    data: value.to_vec(),
+                },
+            }),
+            (blob_types::KEY_CHARACTERISTICS, false, _) => {
                 Ok(Blob { flags, value: BlobValue::Characteristics(value.to_vec()) })
             }
+            (blob_types::KEY_CHARACTERISTICS, true, _) => Ok(Blob {
+                flags,
+                value: BlobValue::EncryptedCharacteristics {
+                    iv: iv.to_vec(),
+                    tag: tag.to_vec(),
+                    data: value.to_vec(),
+                },
+            }),
             (blob_types::KEY_CHARACTERISTICS_CACHE, _, _) => {
                 Ok(Blob { flags, value: BlobValue::CharacteristicsCache(value.to_vec()) })
             }
@@ -416,17 +489,26 @@
             BlobValue::Encrypted { iv, tag, data } => Ok(Blob {
                 flags: blob.flags,
                 value: BlobValue::Decrypted(
-                    decrypt(&data, &iv, &tag, None, None)
+                    decrypt(data, iv, tag, None, None)
                         .context("In new_from_stream_decrypt_with.")?,
                 ),
             }),
             BlobValue::PwEncrypted { iv, tag, data, salt, key_size } => Ok(Blob {
                 flags: blob.flags,
                 value: BlobValue::Decrypted(
-                    decrypt(&data, &iv, &tag, Some(salt), Some(*key_size))
+                    decrypt(data, iv, tag, Some(salt), Some(*key_size))
                         .context("In new_from_stream_decrypt_with.")?,
                 ),
             }),
+            BlobValue::EncryptedGeneric { iv, tag, data } => Ok(Blob {
+                flags: blob.flags,
+                value: BlobValue::Generic(
+                    decrypt(data, iv, tag, None, None)
+                        .context("In new_from_stream_decrypt_with.")?[..]
+                        .to_vec(),
+                ),
+            }),
+
             _ => Ok(blob),
         }
     }
@@ -546,24 +628,91 @@
         Ok(params)
     }
 
+    /// This function takes a Blob and an optional AesGcm. Plain text blob variants are
+    /// passed through as is. If a super key is given an attempt is made to decrypt the
+    /// blob thereby mapping BlobValue variants as follows:
+    /// BlobValue::Encrypted => BlobValue::Decrypted
+    /// BlobValue::EncryptedGeneric => BlobValue::Generic
+    /// BlobValue::EncryptedCharacteristics => BlobValue::Characteristics
+    /// If now super key is given or BlobValue::PwEncrypted is encountered,
+    /// Err(Error::LockedComponent) is returned.
+    fn decrypt_if_required(super_key: &Option<Arc<dyn AesGcm>>, blob: Blob) -> Result<Blob> {
+        match blob {
+            Blob { value: BlobValue::Generic(_), .. }
+            | Blob { value: BlobValue::Characteristics(_), .. }
+            | Blob { value: BlobValue::CharacteristicsCache(_), .. }
+            | Blob { value: BlobValue::Decrypted(_), .. } => Ok(blob),
+            Blob { value: BlobValue::EncryptedCharacteristics { iv, tag, data }, flags }
+                if super_key.is_some() =>
+            {
+                Ok(Blob {
+                    value: BlobValue::Characteristics(
+                        super_key.as_ref().unwrap().decrypt(&data, &iv, &tag).context(
+                            "In decrypt_if_required: Failed to decrypt EncryptedCharacteristics",
+                        )?[..]
+                            .to_vec(),
+                    ),
+                    flags,
+                })
+            }
+            Blob { value: BlobValue::Encrypted { iv, tag, data }, flags }
+                if super_key.is_some() =>
+            {
+                Ok(Blob {
+                    value: BlobValue::Decrypted(
+                        super_key
+                            .as_ref()
+                            .unwrap()
+                            .decrypt(&data, &iv, &tag)
+                            .context("In decrypt_if_required: Failed to decrypt Encrypted")?,
+                    ),
+                    flags,
+                })
+            }
+            Blob { value: BlobValue::EncryptedGeneric { iv, tag, data }, flags }
+                if super_key.is_some() =>
+            {
+                Ok(Blob {
+                    value: BlobValue::Generic(
+                        super_key
+                            .as_ref()
+                            .unwrap()
+                            .decrypt(&data, &iv, &tag)
+                            .context("In decrypt_if_required: Failed to decrypt Encrypted")?[..]
+                            .to_vec(),
+                    ),
+                    flags,
+                })
+            }
+            // This arm catches all encrypted cases where super key is not present or cannot
+            // decrypt the blob, the latter being BlobValue::PwEncrypted.
+            _ => Err(Error::LockedComponent)
+                .context("In decrypt_if_required: Encountered encrypted blob without super key."),
+        }
+    }
+
     fn read_characteristics_file(
         &self,
         uid: u32,
         prefix: &str,
         alias: &str,
         hw_sec_level: SecurityLevel,
-    ) -> Result<Vec<KeyParameter>> {
+        super_key: &Option<Arc<dyn AesGcm>>,
+    ) -> Result<LegacyKeyCharacteristics> {
         let blob = Self::read_generic_blob(&self.make_chr_filename(uid, alias, prefix))
             .context("In read_characteristics_file")?;
 
         let blob = match blob {
-            None => return Ok(Vec::new()),
+            None => return Ok(LegacyKeyCharacteristics::Cache(Vec::new())),
             Some(blob) => blob,
         };
 
-        let mut stream = match blob.value() {
-            BlobValue::Characteristics(data) => &data[..],
-            BlobValue::CharacteristicsCache(data) => &data[..],
+        let blob = Self::decrypt_if_required(super_key, blob)
+            .context("In read_characteristics_file: Trying to decrypt blob.")?;
+
+        let (mut stream, is_cache) = match blob.value() {
+            BlobValue::Characteristics(data) => (&data[..], false),
+            BlobValue::CharacteristicsCache(data) => (&data[..], true),
             _ => {
                 return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(concat!(
                     "In read_characteristics_file: ",
@@ -589,7 +738,12 @@
             .into_iter()
             .map(|value| KeyParameter::new(value, SecurityLevel::KEYSTORE));
 
-        Ok(hw_list.into_iter().flatten().chain(sw_list).collect())
+        let params: Vec<KeyParameter> = hw_list.into_iter().flatten().chain(sw_list).collect();
+        if is_cache {
+            Ok(LegacyKeyCharacteristics::Cache(params))
+        } else {
+            Ok(LegacyKeyCharacteristics::File(params))
+        }
     }
 
     // This is a list of known prefixes that the Keystore 1.0 SPI used to use.
@@ -639,14 +793,40 @@
         Ok(Some(Self::new_from_stream(&mut file).context("In read_generic_blob.")?))
     }
 
+    fn read_generic_blob_decrypt_with<F>(path: &Path, decrypt: F) -> Result<Option<Blob>>
+    where
+        F: FnOnce(&[u8], &[u8], &[u8], Option<&[u8]>, Option<usize>) -> Result<ZVec>,
+    {
+        let mut file = match Self::with_retry_interrupted(|| File::open(path)) {
+            Ok(file) => file,
+            Err(e) => match e.kind() {
+                ErrorKind::NotFound => return Ok(None),
+                _ => return Err(e).context("In read_generic_blob_decrypt_with."),
+            },
+        };
+
+        Ok(Some(
+            Self::new_from_stream_decrypt_with(&mut file, decrypt)
+                .context("In read_generic_blob_decrypt_with.")?,
+        ))
+    }
+
     /// Read a legacy keystore entry blob.
-    pub fn read_legacy_keystore_entry(&self, uid: u32, alias: &str) -> Result<Option<Vec<u8>>> {
+    pub fn read_legacy_keystore_entry<F>(
+        &self,
+        uid: u32,
+        alias: &str,
+        decrypt: F,
+    ) -> Result<Option<Vec<u8>>>
+    where
+        F: FnOnce(&[u8], &[u8], &[u8], Option<&[u8]>, Option<usize>) -> Result<ZVec>,
+    {
         let path = match self.make_legacy_keystore_entry_filename(uid, alias) {
             Some(path) => path,
             None => return Ok(None),
         };
 
-        let blob = Self::read_generic_blob(&path)
+        let blob = Self::read_generic_blob_decrypt_with(&path, decrypt)
             .context("In read_legacy_keystore_entry: Failed to read blob.")?;
 
         Ok(blob.and_then(|blob| match blob.value {
@@ -659,22 +839,23 @@
     }
 
     /// Remove a legacy keystore entry by the name alias with owner uid.
-    pub fn remove_legacy_keystore_entry(&self, uid: u32, alias: &str) -> Result<()> {
+    pub fn remove_legacy_keystore_entry(&self, uid: u32, alias: &str) -> Result<bool> {
         let path = match self.make_legacy_keystore_entry_filename(uid, alias) {
             Some(path) => path,
-            None => return Ok(()),
+            None => return Ok(false),
         };
 
         if let Err(e) = Self::with_retry_interrupted(|| fs::remove_file(path.as_path())) {
             match e.kind() {
-                ErrorKind::NotFound => return Ok(()),
+                ErrorKind::NotFound => return Ok(false),
                 _ => return Err(e).context("In remove_legacy_keystore_entry."),
             }
         }
 
         let user_id = uid_to_android_user(uid);
         self.remove_user_dir_if_empty(user_id)
-            .context("In remove_legacy_keystore_entry: Trying to remove empty user dir.")
+            .context("In remove_legacy_keystore_entry: Trying to remove empty user dir.")?;
+        Ok(true)
     }
 
     /// List all entries belonging to the given uid.
@@ -836,7 +1017,7 @@
         // in are all in the printable range that don't get mangled.
         for prefix in Self::KNOWN_KEYSTORE_PREFIXES {
             if let Some(alias) = encoded_alias.strip_prefix(prefix) {
-                return Self::decode_alias(&alias).ok();
+                return Self::decode_alias(alias).ok();
             }
         }
         None
@@ -988,6 +1169,88 @@
         Ok(something_was_deleted)
     }
 
+    /// This function moves a keystore file if it exists. It constructs the source and destination
+    /// file name using the make_filename function with the arguments uid, alias, and prefix.
+    /// The function overwrites existing destination files silently. If the source does not exist,
+    /// this function has no side effect and returns successfully.
+    fn move_keystore_file_if_exists<F>(
+        src_uid: u32,
+        dest_uid: u32,
+        src_alias: &str,
+        dest_alias: &str,
+        prefix: &str,
+        make_filename: F,
+    ) -> Result<()>
+    where
+        F: Fn(u32, &str, &str) -> PathBuf,
+    {
+        let src_path = make_filename(src_uid, src_alias, prefix);
+        let dest_path = make_filename(dest_uid, dest_alias, prefix);
+        match Self::with_retry_interrupted(|| fs::rename(&src_path, &dest_path)) {
+            Err(e) if e.kind() == ErrorKind::NotFound => Ok(()),
+            r => r.context("In move_keystore_file_if_exists: Trying to rename."),
+        }
+    }
+
+    /// Moves a keystore entry from one uid to another. The uids must have the same android user
+    /// component. Moves across android users are not permitted.
+    pub fn move_keystore_entry(
+        &self,
+        src_uid: u32,
+        dest_uid: u32,
+        src_alias: &str,
+        dest_alias: &str,
+    ) -> Result<()> {
+        if src_uid == dest_uid {
+            // Nothing to do in the trivial case.
+            return Ok(());
+        }
+
+        if uid_to_android_user(src_uid) != uid_to_android_user(dest_uid) {
+            return Err(Error::AndroidUserMismatch).context("In move_keystore_entry.");
+        }
+
+        let prefixes = ["USRPKEY", "USRSKEY", "USRCERT", "CACERT"];
+        for prefix in prefixes {
+            Self::move_keystore_file_if_exists(
+                src_uid,
+                dest_uid,
+                src_alias,
+                dest_alias,
+                prefix,
+                |uid, alias, prefix| self.make_blob_filename(uid, alias, prefix),
+            )
+            .with_context(|| {
+                format!(
+                    "In move_keystore_entry: Trying to move blob file with prefix: \"{}\"",
+                    prefix
+                )
+            })?;
+        }
+
+        let prefixes = ["USRPKEY", "USRSKEY"];
+
+        for prefix in prefixes {
+            Self::move_keystore_file_if_exists(
+                src_uid,
+                dest_uid,
+                src_alias,
+                dest_alias,
+                prefix,
+                |uid, alias, prefix| self.make_chr_filename(uid, alias, prefix),
+            )
+            .with_context(|| {
+                format!(
+                    "In move_keystore_entry: Trying to move characteristics file with \
+                     prefix: \"{}\"",
+                    prefix
+                )
+            })?;
+        }
+
+        Ok(())
+    }
+
     fn remove_user_dir_if_empty(&self, user_id: u32) -> Result<()> {
         if self
             .is_empty_user(user_id)
@@ -1004,79 +1267,66 @@
         &self,
         uid: u32,
         alias: &str,
-        key_manager: Option<&SuperKeyManager>,
-    ) -> Result<(Option<(Blob, Vec<KeyParameter>)>, Option<Vec<u8>>, Option<Vec<u8>>)> {
+        super_key: &Option<Arc<dyn AesGcm>>,
+    ) -> Result<(Option<(Blob, LegacyKeyCharacteristics)>, Option<Vec<u8>>, Option<Vec<u8>>)> {
         let km_blob = self.read_km_blob_file(uid, alias).context("In load_by_uid_alias.")?;
 
         let km_blob = match km_blob {
             Some((km_blob, prefix)) => {
-                let km_blob = match km_blob {
-                    Blob { flags: _, value: BlobValue::Decrypted(_) } => km_blob,
-                    // Unwrap the key blob if required and if we have key_manager.
-                    Blob { flags, value: BlobValue::Encrypted { ref iv, ref tag, ref data } } => {
-                        if let Some(key_manager) = key_manager {
-                            let decrypted = match key_manager
-                                .get_per_boot_key_by_user_id(uid_to_android_user(uid))
-                            {
-                                Some(key) => key.aes_gcm_decrypt(data, iv, tag).context(
-                                    "In load_by_uid_alias: while trying to decrypt legacy blob.",
-                                )?,
-                                None => {
-                                    return Err(KsError::Rc(ResponseCode::LOCKED)).context(format!(
-                                        concat!(
-                                            "In load_by_uid_alias: ",
-                                            "User {} has not unlocked the keystore yet.",
-                                        ),
-                                        uid_to_android_user(uid)
-                                    ))
-                                }
-                            };
-                            Blob { flags, value: BlobValue::Decrypted(decrypted) }
-                        } else {
-                            km_blob
-                        }
-                    }
-                    _ => {
-                        return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(
+                let km_blob =
+                    match km_blob {
+                        Blob { flags: _, value: BlobValue::Decrypted(_) }
+                        | Blob { flags: _, value: BlobValue::Encrypted { .. } } => km_blob,
+                        _ => return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(
                             "In load_by_uid_alias: Found wrong blob type in legacy key blob file.",
-                        )
-                    }
-                };
+                        ),
+                    };
 
                 let hw_sec_level = match km_blob.is_strongbox() {
                     true => SecurityLevel::STRONGBOX,
                     false => SecurityLevel::TRUSTED_ENVIRONMENT,
                 };
                 let key_parameters = self
-                    .read_characteristics_file(uid, &prefix, alias, hw_sec_level)
+                    .read_characteristics_file(uid, &prefix, alias, hw_sec_level, super_key)
                     .context("In load_by_uid_alias.")?;
                 Some((km_blob, key_parameters))
             }
             None => None,
         };
 
-        let user_cert =
-            match Self::read_generic_blob(&self.make_blob_filename(uid, alias, "USRCERT"))
-                .context("In load_by_uid_alias: While loading user cert.")?
-            {
-                Some(Blob { value: BlobValue::Generic(data), .. }) => Some(data),
-                None => None,
-                _ => {
-                    return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(
-                        "In load_by_uid_alias: Found unexpected blob type in USRCERT file",
-                    )
-                }
-            };
+        let user_cert_blob =
+            Self::read_generic_blob(&self.make_blob_filename(uid, alias, "USRCERT"))
+                .context("In load_by_uid_alias: While loading user cert.")?;
 
-        let ca_cert = match Self::read_generic_blob(&self.make_blob_filename(uid, alias, "CACERT"))
-            .context("In load_by_uid_alias: While loading ca cert.")?
-        {
-            Some(Blob { value: BlobValue::Generic(data), .. }) => Some(data),
-            None => None,
-            _ => {
+        let user_cert = if let Some(blob) = user_cert_blob {
+            let blob = Self::decrypt_if_required(super_key, blob)
+                .context("In load_by_uid_alias: While decrypting user cert.")?;
+
+            if let Blob { value: BlobValue::Generic(data), .. } = blob {
+                Some(data)
+            } else {
                 return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
-                    .context("In load_by_uid_alias: Found unexpected blob type in CACERT file")
+                    .context("In load_by_uid_alias: Found unexpected blob type in USRCERT file");
             }
+        } else {
+            None
+        };
+
+        let ca_cert_blob = Self::read_generic_blob(&self.make_blob_filename(uid, alias, "CACERT"))
+            .context("In load_by_uid_alias: While loading ca cert.")?;
+
+        let ca_cert = if let Some(blob) = ca_cert_blob {
+            let blob = Self::decrypt_if_required(super_key, blob)
+                .context("In load_by_uid_alias: While decrypting ca cert.")?;
+
+            if let Blob { value: BlobValue::Generic(data), .. } = blob {
+                Some(data)
+            } else {
+                return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
+                    .context("In load_by_uid_alias: Found unexpected blob type in CACERT file");
+            }
+        } else {
+            None
         };
 
         Ok((km_blob, user_cert, ca_cert))
@@ -1139,15 +1389,271 @@
 
 #[cfg(test)]
 mod test {
+    #![allow(dead_code)]
     use super::*;
-    use anyhow::anyhow;
-    use keystore2_crypto::aes_gcm_decrypt;
+    use keystore2_crypto::{aes_gcm_decrypt, aes_gcm_encrypt};
     use rand::Rng;
     use std::string::FromUtf8Error;
     mod legacy_blob_test_vectors;
-    use crate::error;
+    use crate::legacy_blob::blob_types::{
+        GENERIC, KEY_CHARACTERISTICS, KEY_CHARACTERISTICS_CACHE, KM_BLOB, SUPER_KEY,
+        SUPER_KEY_AES256,
+    };
     use crate::legacy_blob::test::legacy_blob_test_vectors::*;
+    use anyhow::{anyhow, Result};
     use keystore2_test_utils::TempDir;
+    use std::convert::TryInto;
+    use std::fs::OpenOptions;
+    use std::io::Write;
+    use std::ops::Deref;
+
+    /// This function takes a blob and synchronizes the encrypted/super encrypted flags
+    /// with the blob type for the pairs Generic/EncryptedGeneric,
+    /// Characteristics/EncryptedCharacteristics and Encrypted/Decrypted.
+    /// E.g. if a non encrypted enum variant is encountered with flags::SUPER_ENCRYPTED
+    /// or flags::ENCRYPTED is set, the payload is encrypted and the corresponding
+    /// encrypted variant is returned, and vice versa. All other variants remain untouched
+    /// even if flags and BlobValue variant are inconsistent.
+    fn prepare_blob(blob: Blob, key: &[u8]) -> Result<Blob> {
+        match blob {
+            Blob { value: BlobValue::Generic(data), flags } if blob.is_encrypted() => {
+                let (ciphertext, iv, tag) = aes_gcm_encrypt(&data, key).unwrap();
+                Ok(Blob { value: BlobValue::EncryptedGeneric { data: ciphertext, iv, tag }, flags })
+            }
+            Blob { value: BlobValue::Characteristics(data), flags } if blob.is_encrypted() => {
+                let (ciphertext, iv, tag) = aes_gcm_encrypt(&data, key).unwrap();
+                Ok(Blob {
+                    value: BlobValue::EncryptedCharacteristics { data: ciphertext, iv, tag },
+                    flags,
+                })
+            }
+            Blob { value: BlobValue::Decrypted(data), flags } if blob.is_encrypted() => {
+                let (ciphertext, iv, tag) = aes_gcm_encrypt(&data, key).unwrap();
+                Ok(Blob { value: BlobValue::Encrypted { data: ciphertext, iv, tag }, flags })
+            }
+            Blob { value: BlobValue::EncryptedGeneric { data, iv, tag }, flags }
+                if !blob.is_encrypted() =>
+            {
+                let plaintext = aes_gcm_decrypt(&data, &iv, &tag, key).unwrap();
+                Ok(Blob { value: BlobValue::Generic(plaintext[..].to_vec()), flags })
+            }
+            Blob { value: BlobValue::EncryptedCharacteristics { data, iv, tag }, flags }
+                if !blob.is_encrypted() =>
+            {
+                let plaintext = aes_gcm_decrypt(&data, &iv, &tag, key).unwrap();
+                Ok(Blob { value: BlobValue::Characteristics(plaintext[..].to_vec()), flags })
+            }
+            Blob { value: BlobValue::Encrypted { data, iv, tag }, flags }
+                if !blob.is_encrypted() =>
+            {
+                let plaintext = aes_gcm_decrypt(&data, &iv, &tag, key).unwrap();
+                Ok(Blob { value: BlobValue::Decrypted(plaintext), flags })
+            }
+            _ => Ok(blob),
+        }
+    }
+
+    struct LegacyBlobHeader {
+        version: u8,
+        blob_type: u8,
+        flags: u8,
+        info: u8,
+        iv: [u8; 12],
+        tag: [u8; 16],
+        blob_size: u32,
+    }
+
+    /// This function takes a Blob and writes it to out as a legacy blob file
+    /// version 3. Note that the flags field and the values field may be
+    /// inconsistent and could be sanitized by this function. It is intentionally
+    /// not done to enable tests to construct malformed blobs.
+    fn write_legacy_blob(out: &mut dyn Write, blob: Blob) -> Result<usize> {
+        let (header, data, salt) = match blob {
+            Blob { value: BlobValue::Generic(data), flags } => (
+                LegacyBlobHeader {
+                    version: 3,
+                    blob_type: GENERIC,
+                    flags,
+                    info: 0,
+                    iv: [0u8; 12],
+                    tag: [0u8; 16],
+                    blob_size: data.len() as u32,
+                },
+                data,
+                None,
+            ),
+            Blob { value: BlobValue::Characteristics(data), flags } => (
+                LegacyBlobHeader {
+                    version: 3,
+                    blob_type: KEY_CHARACTERISTICS,
+                    flags,
+                    info: 0,
+                    iv: [0u8; 12],
+                    tag: [0u8; 16],
+                    blob_size: data.len() as u32,
+                },
+                data,
+                None,
+            ),
+            Blob { value: BlobValue::CharacteristicsCache(data), flags } => (
+                LegacyBlobHeader {
+                    version: 3,
+                    blob_type: KEY_CHARACTERISTICS_CACHE,
+                    flags,
+                    info: 0,
+                    iv: [0u8; 12],
+                    tag: [0u8; 16],
+                    blob_size: data.len() as u32,
+                },
+                data,
+                None,
+            ),
+            Blob { value: BlobValue::PwEncrypted { iv, tag, data, salt, key_size }, flags } => (
+                LegacyBlobHeader {
+                    version: 3,
+                    blob_type: if key_size == keystore2_crypto::AES_128_KEY_LENGTH {
+                        SUPER_KEY
+                    } else {
+                        SUPER_KEY_AES256
+                    },
+                    flags,
+                    info: 0,
+                    iv: iv.try_into().unwrap(),
+                    tag: tag[..].try_into().unwrap(),
+                    blob_size: data.len() as u32,
+                },
+                data,
+                Some(salt),
+            ),
+            Blob { value: BlobValue::Encrypted { iv, tag, data }, flags } => (
+                LegacyBlobHeader {
+                    version: 3,
+                    blob_type: KM_BLOB,
+                    flags,
+                    info: 0,
+                    iv: iv.try_into().unwrap(),
+                    tag: tag[..].try_into().unwrap(),
+                    blob_size: data.len() as u32,
+                },
+                data,
+                None,
+            ),
+            Blob { value: BlobValue::EncryptedGeneric { iv, tag, data }, flags } => (
+                LegacyBlobHeader {
+                    version: 3,
+                    blob_type: GENERIC,
+                    flags,
+                    info: 0,
+                    iv: iv.try_into().unwrap(),
+                    tag: tag[..].try_into().unwrap(),
+                    blob_size: data.len() as u32,
+                },
+                data,
+                None,
+            ),
+            Blob { value: BlobValue::EncryptedCharacteristics { iv, tag, data }, flags } => (
+                LegacyBlobHeader {
+                    version: 3,
+                    blob_type: KEY_CHARACTERISTICS,
+                    flags,
+                    info: 0,
+                    iv: iv.try_into().unwrap(),
+                    tag: tag[..].try_into().unwrap(),
+                    blob_size: data.len() as u32,
+                },
+                data,
+                None,
+            ),
+            Blob { value: BlobValue::Decrypted(data), flags } => (
+                LegacyBlobHeader {
+                    version: 3,
+                    blob_type: KM_BLOB,
+                    flags,
+                    info: 0,
+                    iv: [0u8; 12],
+                    tag: [0u8; 16],
+                    blob_size: data.len() as u32,
+                },
+                data[..].to_vec(),
+                None,
+            ),
+        };
+        write_legacy_blob_helper(out, &header, &data, salt.as_deref())
+    }
+
+    fn write_legacy_blob_helper(
+        out: &mut dyn Write,
+        header: &LegacyBlobHeader,
+        data: &[u8],
+        info: Option<&[u8]>,
+    ) -> Result<usize> {
+        if 1 != out.write(&[header.version])? {
+            return Err(anyhow!("Unexpected size while writing version."));
+        }
+        if 1 != out.write(&[header.blob_type])? {
+            return Err(anyhow!("Unexpected size while writing blob_type."));
+        }
+        if 1 != out.write(&[header.flags])? {
+            return Err(anyhow!("Unexpected size while writing flags."));
+        }
+        if 1 != out.write(&[header.info])? {
+            return Err(anyhow!("Unexpected size while writing info."));
+        }
+        if 12 != out.write(&header.iv)? {
+            return Err(anyhow!("Unexpected size while writing iv."));
+        }
+        if 4 != out.write(&[0u8; 4])? {
+            return Err(anyhow!("Unexpected size while writing last 4 bytes of iv."));
+        }
+        if 16 != out.write(&header.tag)? {
+            return Err(anyhow!("Unexpected size while writing tag."));
+        }
+        if 4 != out.write(&header.blob_size.to_be_bytes())? {
+            return Err(anyhow!("Unexpected size while writing blob size."));
+        }
+        if data.len() != out.write(data)? {
+            return Err(anyhow!("Unexpected size while writing blob."));
+        }
+        if let Some(info) = info {
+            if info.len() != out.write(info)? {
+                return Err(anyhow!("Unexpected size while writing inof."));
+            }
+        }
+        Ok(40 + data.len() + info.map(|v| v.len()).unwrap_or(0))
+    }
+
+    fn make_encrypted_characteristics_file<P: AsRef<Path>>(path: P, key: &[u8]) -> Result<()> {
+        let mut file = OpenOptions::new().write(true).create_new(true).open(path).unwrap();
+        let blob = Blob {
+            value: BlobValue::Characteristics(KEY_PARAMETERS.to_vec()),
+            flags: flags::ENCRYPTED,
+        };
+        let blob = prepare_blob(blob, key).unwrap();
+        write_legacy_blob(&mut file, blob).unwrap();
+        Ok(())
+    }
+
+    fn make_encrypted_usr_cert_file<P: AsRef<Path>>(path: P, key: &[u8]) -> Result<()> {
+        let mut file = OpenOptions::new().write(true).create_new(true).open(path).unwrap();
+        let blob = Blob {
+            value: BlobValue::Generic(LOADED_CERT_AUTHBOUND.to_vec()),
+            flags: flags::ENCRYPTED,
+        };
+        let blob = prepare_blob(blob, key).unwrap();
+        write_legacy_blob(&mut file, blob).unwrap();
+        Ok(())
+    }
+
+    fn make_encrypted_ca_cert_file<P: AsRef<Path>>(path: P, key: &[u8]) -> Result<()> {
+        let mut file = OpenOptions::new().write(true).create_new(true).open(path).unwrap();
+        let blob = Blob {
+            value: BlobValue::Generic(LOADED_CACERT_AUTHBOUND.to_vec()),
+            flags: flags::ENCRYPTED,
+        };
+        let blob = prepare_blob(blob, key).unwrap();
+        write_legacy_blob(&mut file, blob).unwrap();
+        Ok(())
+    }
 
     #[test]
     fn decode_encode_alias_test() {
@@ -1203,7 +1709,8 @@
     fn read_golden_key_blob_test() -> anyhow::Result<()> {
         let blob = LegacyBlobLoader::new_from_stream_decrypt_with(&mut &*BLOB, |_, _, _, _, _| {
             Err(anyhow!("should not be called"))
-        })?;
+        })
+        .unwrap();
         assert!(!blob.is_encrypted());
         assert!(!blob.is_fallback());
         assert!(!blob.is_strongbox());
@@ -1213,7 +1720,8 @@
         let blob = LegacyBlobLoader::new_from_stream_decrypt_with(
             &mut &*REAL_LEGACY_BLOB,
             |_, _, _, _, _| Err(anyhow!("should not be called")),
-        )?;
+        )
+        .unwrap();
         assert!(!blob.is_encrypted());
         assert!(!blob.is_fallback());
         assert!(!blob.is_strongbox());
@@ -1301,62 +1809,75 @@
 
     #[test]
     fn test_legacy_blobs() -> anyhow::Result<()> {
-        let temp_dir = TempDir::new("legacy_blob_test")?;
-        std::fs::create_dir(&*temp_dir.build().push("user_0"))?;
+        let temp_dir = TempDir::new("legacy_blob_test").unwrap();
+        std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
 
-        std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY)?;
+        std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
 
         std::fs::write(
             &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
             USRPKEY_AUTHBOUND,
-        )?;
+        )
+        .unwrap();
         std::fs::write(
             &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
             USRPKEY_AUTHBOUND_CHR,
-        )?;
+        )
+        .unwrap();
         std::fs::write(
             &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
             USRCERT_AUTHBOUND,
-        )?;
+        )
+        .unwrap();
         std::fs::write(
             &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
             CACERT_AUTHBOUND,
-        )?;
+        )
+        .unwrap();
 
         std::fs::write(
             &*temp_dir.build().push("user_0").push("10223_USRPKEY_non_authbound"),
             USRPKEY_NON_AUTHBOUND,
-        )?;
+        )
+        .unwrap();
         std::fs::write(
             &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_non_authbound"),
             USRPKEY_NON_AUTHBOUND_CHR,
-        )?;
+        )
+        .unwrap();
         std::fs::write(
             &*temp_dir.build().push("user_0").push("10223_USRCERT_non_authbound"),
             USRCERT_NON_AUTHBOUND,
-        )?;
+        )
+        .unwrap();
         std::fs::write(
             &*temp_dir.build().push("user_0").push("10223_CACERT_non_authbound"),
             CACERT_NON_AUTHBOUND,
-        )?;
+        )
+        .unwrap();
 
-        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());
 
-        assert_eq!(
-            legacy_blob_loader
-                .load_by_uid_alias(10223, "authbound", Some(&key_manager))
-                .unwrap_err()
-                .root_cause()
-                .downcast_ref::<error::Error>(),
-            Some(&error::Error::Rc(ResponseCode::LOCKED))
-        );
-
-        key_manager.unlock_user_key(&mut db, 0, &(PASSWORD.into()), &legacy_blob_loader)?;
+        if let (Some((Blob { flags, value }, _params)), Some(cert), Some(chain)) =
+            legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None)?
+        {
+            assert_eq!(flags, 4);
+            assert_eq!(
+                value,
+                BlobValue::Encrypted {
+                    data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
+                    iv: USRPKEY_AUTHBOUND_IV.to_vec(),
+                    tag: USRPKEY_AUTHBOUND_TAG.to_vec()
+                }
+            );
+            assert_eq!(&cert[..], LOADED_CERT_AUTHBOUND);
+            assert_eq!(&chain[..], LOADED_CACERT_AUTHBOUND);
+        } else {
+            panic!("");
+        }
 
         if let (Some((Blob { flags, value: _ }, _params)), Some(cert), Some(chain)) =
-            legacy_blob_loader.load_by_uid_alias(10223, "authbound", Some(&key_manager))?
+            legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None)?
         {
             assert_eq!(flags, 4);
             //assert_eq!(value, BlobValue::Encrypted(..));
@@ -1366,7 +1887,7 @@
             panic!("");
         }
         if let (Some((Blob { flags, value }, _params)), Some(cert), Some(chain)) =
-            legacy_blob_loader.load_by_uid_alias(10223, "non_authbound", Some(&key_manager))?
+            legacy_blob_loader.load_by_uid_alias(10223, "non_authbound", &None)?
         {
             assert_eq!(flags, 0);
             assert_eq!(value, BlobValue::Decrypted(LOADED_USRPKEY_NON_AUTHBOUND.try_into()?));
@@ -1383,11 +1904,11 @@
 
         assert_eq!(
             (None, None, None),
-            legacy_blob_loader.load_by_uid_alias(10223, "authbound", Some(&key_manager))?
+            legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None)?
         );
         assert_eq!(
             (None, None, None),
-            legacy_blob_loader.load_by_uid_alias(10223, "non_authbound", Some(&key_manager))?
+            legacy_blob_loader.load_by_uid_alias(10223, "non_authbound", &None)?
         );
 
         // The database should not be empty due to the super key.
@@ -1406,9 +1927,314 @@
         Ok(())
     }
 
+    struct TestKey(ZVec);
+
+    impl crate::utils::AesGcmKey for TestKey {
+        fn key(&self) -> &[u8] {
+            &self.0
+        }
+    }
+
+    impl Deref for TestKey {
+        type Target = [u8];
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+
+    #[test]
+    fn test_with_encrypted_characteristics() -> anyhow::Result<()> {
+        let temp_dir = TempDir::new("test_with_encrypted_characteristics").unwrap();
+        std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
+
+        let pw: Password = PASSWORD.into();
+        let pw_key = TestKey(pw.derive_key(Some(SUPERKEY_SALT), 32).unwrap());
+        let super_key =
+            Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
+
+        std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
+
+        std::fs::write(
+            &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
+            USRPKEY_AUTHBOUND,
+        )
+        .unwrap();
+        make_encrypted_characteristics_file(
+            &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
+            &super_key,
+        )
+        .unwrap();
+        std::fs::write(
+            &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
+            USRCERT_AUTHBOUND,
+        )
+        .unwrap();
+        std::fs::write(
+            &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
+            CACERT_AUTHBOUND,
+        )
+        .unwrap();
+
+        let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+        assert_eq!(
+            legacy_blob_loader
+                .load_by_uid_alias(10223, "authbound", &None)
+                .unwrap_err()
+                .root_cause()
+                .downcast_ref::<Error>(),
+            Some(&Error::LockedComponent)
+        );
+
+        assert_eq!(
+            legacy_blob_loader.load_by_uid_alias(10223, "authbound", &Some(super_key)).unwrap(),
+            (
+                Some((
+                    Blob {
+                        flags: 4,
+                        value: BlobValue::Encrypted {
+                            data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
+                            iv: USRPKEY_AUTHBOUND_IV.to_vec(),
+                            tag: USRPKEY_AUTHBOUND_TAG.to_vec()
+                        }
+                    },
+                    structured_test_params()
+                )),
+                Some(LOADED_CERT_AUTHBOUND.to_vec()),
+                Some(LOADED_CACERT_AUTHBOUND.to_vec())
+            )
+        );
+
+        legacy_blob_loader.remove_keystore_entry(10223, "authbound").expect("This should succeed.");
+
+        assert_eq!(
+            (None, None, None),
+            legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None).unwrap()
+        );
+
+        // The database should not be empty due to the super key.
+        assert!(!legacy_blob_loader.is_empty().unwrap());
+        assert!(!legacy_blob_loader.is_empty_user(0).unwrap());
+
+        // The database should be considered empty for user 1.
+        assert!(legacy_blob_loader.is_empty_user(1).unwrap());
+
+        legacy_blob_loader.remove_super_key(0);
+
+        // Now it should be empty.
+        assert!(legacy_blob_loader.is_empty_user(0).unwrap());
+        assert!(legacy_blob_loader.is_empty().unwrap());
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_with_encrypted_certificates() -> anyhow::Result<()> {
+        let temp_dir = TempDir::new("test_with_encrypted_certificates").unwrap();
+        std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
+
+        let pw: Password = PASSWORD.into();
+        let pw_key = TestKey(pw.derive_key(Some(SUPERKEY_SALT), 32).unwrap());
+        let super_key =
+            Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
+
+        std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
+
+        std::fs::write(
+            &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
+            USRPKEY_AUTHBOUND,
+        )
+        .unwrap();
+        std::fs::write(
+            &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
+            USRPKEY_AUTHBOUND_CHR,
+        )
+        .unwrap();
+        make_encrypted_usr_cert_file(
+            &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
+            &super_key,
+        )
+        .unwrap();
+        make_encrypted_ca_cert_file(
+            &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
+            &super_key,
+        )
+        .unwrap();
+
+        let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+        assert_eq!(
+            legacy_blob_loader
+                .load_by_uid_alias(10223, "authbound", &None)
+                .unwrap_err()
+                .root_cause()
+                .downcast_ref::<Error>(),
+            Some(&Error::LockedComponent)
+        );
+
+        assert_eq!(
+            legacy_blob_loader.load_by_uid_alias(10223, "authbound", &Some(super_key)).unwrap(),
+            (
+                Some((
+                    Blob {
+                        flags: 4,
+                        value: BlobValue::Encrypted {
+                            data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
+                            iv: USRPKEY_AUTHBOUND_IV.to_vec(),
+                            tag: USRPKEY_AUTHBOUND_TAG.to_vec()
+                        }
+                    },
+                    structured_test_params_cache()
+                )),
+                Some(LOADED_CERT_AUTHBOUND.to_vec()),
+                Some(LOADED_CACERT_AUTHBOUND.to_vec())
+            )
+        );
+
+        legacy_blob_loader.remove_keystore_entry(10223, "authbound").expect("This should succeed.");
+
+        assert_eq!(
+            (None, None, None),
+            legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None).unwrap()
+        );
+
+        // The database should not be empty due to the super key.
+        assert!(!legacy_blob_loader.is_empty().unwrap());
+        assert!(!legacy_blob_loader.is_empty_user(0).unwrap());
+
+        // The database should be considered empty for user 1.
+        assert!(legacy_blob_loader.is_empty_user(1).unwrap());
+
+        legacy_blob_loader.remove_super_key(0);
+
+        // Now it should be empty.
+        assert!(legacy_blob_loader.is_empty_user(0).unwrap());
+        assert!(legacy_blob_loader.is_empty().unwrap());
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_in_place_key_migration() -> anyhow::Result<()> {
+        let temp_dir = TempDir::new("test_in_place_key_migration").unwrap();
+        std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
+
+        let pw: Password = PASSWORD.into();
+        let pw_key = TestKey(pw.derive_key(Some(SUPERKEY_SALT), 32).unwrap());
+        let super_key =
+            Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
+
+        std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
+
+        std::fs::write(
+            &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
+            USRPKEY_AUTHBOUND,
+        )
+        .unwrap();
+        std::fs::write(
+            &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
+            USRPKEY_AUTHBOUND_CHR,
+        )
+        .unwrap();
+        make_encrypted_usr_cert_file(
+            &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
+            &super_key,
+        )
+        .unwrap();
+        make_encrypted_ca_cert_file(
+            &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
+            &super_key,
+        )
+        .unwrap();
+
+        let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+        assert_eq!(
+            legacy_blob_loader
+                .load_by_uid_alias(10223, "authbound", &None)
+                .unwrap_err()
+                .root_cause()
+                .downcast_ref::<Error>(),
+            Some(&Error::LockedComponent)
+        );
+
+        let super_key: Option<Arc<dyn AesGcm>> = Some(super_key);
+
+        assert_eq!(
+            legacy_blob_loader.load_by_uid_alias(10223, "authbound", &super_key).unwrap(),
+            (
+                Some((
+                    Blob {
+                        flags: 4,
+                        value: BlobValue::Encrypted {
+                            data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
+                            iv: USRPKEY_AUTHBOUND_IV.to_vec(),
+                            tag: USRPKEY_AUTHBOUND_TAG.to_vec()
+                        }
+                    },
+                    structured_test_params_cache()
+                )),
+                Some(LOADED_CERT_AUTHBOUND.to_vec()),
+                Some(LOADED_CACERT_AUTHBOUND.to_vec())
+            )
+        );
+
+        legacy_blob_loader.move_keystore_entry(10223, 10224, "authbound", "boundauth").unwrap();
+
+        assert_eq!(
+            legacy_blob_loader
+                .load_by_uid_alias(10224, "boundauth", &None)
+                .unwrap_err()
+                .root_cause()
+                .downcast_ref::<Error>(),
+            Some(&Error::LockedComponent)
+        );
+
+        assert_eq!(
+            legacy_blob_loader.load_by_uid_alias(10224, "boundauth", &super_key).unwrap(),
+            (
+                Some((
+                    Blob {
+                        flags: 4,
+                        value: BlobValue::Encrypted {
+                            data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
+                            iv: USRPKEY_AUTHBOUND_IV.to_vec(),
+                            tag: USRPKEY_AUTHBOUND_TAG.to_vec()
+                        }
+                    },
+                    structured_test_params_cache()
+                )),
+                Some(LOADED_CERT_AUTHBOUND.to_vec()),
+                Some(LOADED_CACERT_AUTHBOUND.to_vec())
+            )
+        );
+
+        legacy_blob_loader.remove_keystore_entry(10224, "boundauth").expect("This should succeed.");
+
+        assert_eq!(
+            (None, None, None),
+            legacy_blob_loader.load_by_uid_alias(10224, "boundauth", &None).unwrap()
+        );
+
+        // The database should not be empty due to the super key.
+        assert!(!legacy_blob_loader.is_empty().unwrap());
+        assert!(!legacy_blob_loader.is_empty_user(0).unwrap());
+
+        // The database should be considered empty for user 1.
+        assert!(legacy_blob_loader.is_empty_user(1).unwrap());
+
+        legacy_blob_loader.remove_super_key(0);
+
+        // Now it should be empty.
+        assert!(legacy_blob_loader.is_empty_user(0).unwrap());
+        assert!(legacy_blob_loader.is_empty().unwrap());
+
+        Ok(())
+    }
+
     #[test]
     fn list_non_existing_user() -> Result<()> {
-        let temp_dir = TempDir::new("list_non_existing_user")?;
+        let temp_dir = TempDir::new("list_non_existing_user").unwrap();
         let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
 
         assert!(legacy_blob_loader.list_user(20)?.is_empty());
@@ -1418,11 +2244,66 @@
 
     #[test]
     fn list_legacy_keystore_entries_on_non_existing_user() -> Result<()> {
-        let temp_dir = TempDir::new("list_legacy_keystore_entries_on_non_existing_user")?;
+        let temp_dir = TempDir::new("list_legacy_keystore_entries_on_non_existing_user").unwrap();
         let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
 
         assert!(legacy_blob_loader.list_legacy_keystore_entries_for_user(20)?.is_empty());
 
         Ok(())
     }
+
+    #[test]
+    fn test_move_keystore_entry() {
+        let temp_dir = TempDir::new("test_move_keystore_entry").unwrap();
+        std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
+
+        const SOME_CONTENT: &[u8] = b"some content";
+        const ANOTHER_CONTENT: &[u8] = b"another content";
+        const SOME_FILENAME: &str = "some_file";
+        const ANOTHER_FILENAME: &str = "another_file";
+
+        std::fs::write(&*temp_dir.build().push("user_0").push(SOME_FILENAME), SOME_CONTENT)
+            .unwrap();
+
+        std::fs::write(&*temp_dir.build().push("user_0").push(ANOTHER_FILENAME), ANOTHER_CONTENT)
+            .unwrap();
+
+        // Non existent source id silently ignored.
+        assert!(LegacyBlobLoader::move_keystore_file_if_exists(
+            1,
+            2,
+            "non_existent",
+            ANOTHER_FILENAME,
+            "ignored",
+            |_, alias, _| temp_dir.build().push("user_0").push(alias).to_path_buf()
+        )
+        .is_ok());
+
+        // Content of another_file has not changed.
+        let another_content =
+            std::fs::read(&*temp_dir.build().push("user_0").push(ANOTHER_FILENAME)).unwrap();
+        assert_eq!(&another_content, ANOTHER_CONTENT);
+
+        // Check that some_file still exists.
+        assert!(temp_dir.build().push("user_0").push(SOME_FILENAME).exists());
+        // Existing target files are silently overwritten.
+
+        assert!(LegacyBlobLoader::move_keystore_file_if_exists(
+            1,
+            2,
+            SOME_FILENAME,
+            ANOTHER_FILENAME,
+            "ignored",
+            |_, alias, _| temp_dir.build().push("user_0").push(alias).to_path_buf()
+        )
+        .is_ok());
+
+        // Content of another_file is now "some content".
+        let another_content =
+            std::fs::read(&*temp_dir.build().push("user_0").push(ANOTHER_FILENAME)).unwrap();
+        assert_eq!(&another_content, SOME_CONTENT);
+
+        // Check that some_file no longer exists.
+        assert!(!temp_dir.build().push("user_0").push(SOME_FILENAME).exists());
+    }
 }
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 14bd40c..2049ac2 100644
--- a/keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs
+++ b/keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs
@@ -12,6 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use crate::key_parameter::{KeyParameter, KeyParameterValue};
+use crate::legacy_blob::LegacyKeyCharacteristics;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve,
+    HardwareAuthenticatorType::HardwareAuthenticatorType, KeyOrigin::KeyOrigin,
+    KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+};
+
 pub static BLOB: &[u8] = &[
     3, // version
     1, // type
@@ -22,6 +30,106 @@
     0, 0, 0, 4, // length in big endian
     0xde, 0xed, 0xbe, 0xef, // payload
 ];
+
+pub fn structured_test_params() -> LegacyKeyCharacteristics {
+    LegacyKeyCharacteristics::File(vec![
+        KeyParameter::new(KeyParameterValue::KeyPurpose(KeyPurpose::SIGN), SecurityLevel::KEYSTORE),
+        KeyParameter::new(
+            KeyParameterValue::KeyPurpose(KeyPurpose::VERIFY),
+            SecurityLevel::KEYSTORE,
+        ),
+        KeyParameter::new(KeyParameterValue::Digest(Digest::SHA_2_256), SecurityLevel::KEYSTORE),
+        KeyParameter::new(
+            KeyParameterValue::UserSecureID(2100322049669824240),
+            SecurityLevel::KEYSTORE,
+        ),
+        KeyParameter::new(KeyParameterValue::Algorithm(Algorithm::EC), SecurityLevel::KEYSTORE),
+        KeyParameter::new(KeyParameterValue::KeySize(256), SecurityLevel::KEYSTORE),
+        KeyParameter::new(KeyParameterValue::EcCurve(EcCurve::P_256), SecurityLevel::KEYSTORE),
+        KeyParameter::new(
+            KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType::FINGERPRINT),
+            SecurityLevel::KEYSTORE,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::KeyOrigin(KeyOrigin::GENERATED),
+            SecurityLevel::KEYSTORE,
+        ),
+        KeyParameter::new(KeyParameterValue::OSVersion(110000), SecurityLevel::KEYSTORE),
+        KeyParameter::new(KeyParameterValue::OSPatchLevel(202101), SecurityLevel::KEYSTORE),
+        KeyParameter::new(KeyParameterValue::BootPatchLevel(20210105), SecurityLevel::KEYSTORE),
+        KeyParameter::new(KeyParameterValue::VendorPatchLevel(20210105), SecurityLevel::KEYSTORE),
+    ])
+}
+
+pub fn structured_test_params_cache() -> LegacyKeyCharacteristics {
+    LegacyKeyCharacteristics::Cache(vec![
+        KeyParameter::new(
+            KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::KeyPurpose(KeyPurpose::VERIFY),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::Digest(Digest::SHA_2_256),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::UserSecureID(2100322049669824240),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::Algorithm(Algorithm::EC),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(KeyParameterValue::KeySize(256), SecurityLevel::TRUSTED_ENVIRONMENT),
+        KeyParameter::new(
+            KeyParameterValue::EcCurve(EcCurve::P_256),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType::FINGERPRINT),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::KeyOrigin(KeyOrigin::GENERATED),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(KeyParameterValue::OSVersion(110000), SecurityLevel::TRUSTED_ENVIRONMENT),
+        KeyParameter::new(
+            KeyParameterValue::OSPatchLevel(202101),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::BootPatchLevel(20210105),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::VendorPatchLevel(20210105),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::CreationDateTime(1607149002000),
+            SecurityLevel::KEYSTORE,
+        ),
+        KeyParameter::new(KeyParameterValue::UserID(0), SecurityLevel::KEYSTORE),
+    ])
+}
+
+// One encoded list of key parameters.
+pub static KEY_PARAMETERS: &[u8] = &[
+    0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x20,
+    0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x20,
+    0x04, 0x00, 0x00, 0x00, 0xf6, 0x01, 0x00, 0xa0, 0xf0, 0x7e, 0x7d, 0xb4, 0xc6, 0xd7, 0x25, 0x1d,
+    0x02, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00,
+    0x0a, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x2d, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
+    0xf8, 0x01, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0xbe, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
+    0xc1, 0x02, 0x00, 0x30, 0xb0, 0xad, 0x01, 0x00, 0xc2, 0x02, 0x00, 0x30, 0x75, 0x15, 0x03, 0x00,
+    0xcf, 0x02, 0x00, 0x30, 0xb9, 0x61, 0x34, 0x01, 0xce, 0x02, 0x00, 0x30, 0xb9, 0x61, 0x34, 0x01,
+    0x30, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
+];
+
 pub static REAL_LEGACY_BLOB: &[u8] = &[
     0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -164,6 +272,24 @@
     0x76, 0x04, 0x2a, 0x48, 0xd1, 0xa7, 0x59, 0xd1, 0x04, 0x5b, 0xb4, 0x8a, 0x09, 0x22, 0x13, 0x0c,
     0x94, 0xb6, 0x67, 0x7b, 0x39, 0x85, 0x28, 0x11,
 ];
+
+pub static SUPERKEY_IV: &[u8] = &[
+    0x9a, 0x81, 0x56, 0x7d, 0xf5, 0x86, 0x7c, 0x62, 0xd7, 0xf9, 0x26, 0x06, 0x00, 0x00, 0x00, 0x00,
+];
+
+pub static SUPERKEY_TAG: &[u8] = &[
+    0xde, 0x2a, 0xcb, 0xac, 0x98, 0x57, 0x2b, 0xe5, 0x57, 0x18, 0x78, 0x57, 0x6e, 0x10, 0x09, 0x84,
+];
+
+pub static SUPERKEY_SALT: &[u8] = &[
+    0x04, 0x5b, 0xb4, 0x8a, 0x09, 0x22, 0x13, 0x0c, 0x94, 0xb6, 0x67, 0x7b, 0x39, 0x85, 0x28, 0x11,
+];
+
+pub static SUPERKEY_PAYLOAD: &[u8] = &[
+    0xac, 0x6d, 0x13, 0xe6, 0xad, 0x2c, 0x89, 0x53, 0x1a, 0x99, 0xa5, 0x6c, 0x88, 0xe9, 0xeb, 0x5c,
+    0xef, 0x68, 0x5e, 0x5b, 0x53, 0xa8, 0xe7, 0xa2, 0x76, 0x04, 0x2a, 0x48, 0xd1, 0xa7, 0x59, 0xd1,
+];
+
 pub static USRPKEY_AUTHBOUND: &[u8] = &[
     0x03, 0x04, 0x04, 0x00, 0x1c, 0x34, 0x87, 0x6f, 0xc8, 0x35, 0x0d, 0x34, 0x88, 0x59, 0xbc, 0xf5,
     0x00, 0x00, 0x00, 0x00, 0x62, 0xe3, 0x38, 0x2d, 0xd0, 0x58, 0x40, 0xc1, 0xb0, 0xf2, 0x4a, 0xdd,
@@ -203,6 +329,52 @@
     0xaf, 0x17, 0x2f, 0x21, 0x07, 0xea, 0x61, 0xff, 0x73, 0x08, 0x50, 0xb2, 0x19, 0xe8, 0x23, 0x1b,
     0x83, 0x42, 0xdd, 0x4e, 0x6d,
 ];
+
+pub static USRPKEY_AUTHBOUND_IV: &[u8] = &[
+    0x1c, 0x34, 0x87, 0x6f, 0xc8, 0x35, 0x0d, 0x34, 0x88, 0x59, 0xbc, 0xf5, 0x00, 0x00, 0x00, 0x00,
+];
+
+pub static USRPKEY_AUTHBOUND_TAG: &[u8] = &[
+    0x62, 0xe3, 0x38, 0x2d, 0xd0, 0x58, 0x40, 0xc1, 0xb0, 0xf2, 0x4a, 0xdd, 0xf7, 0x81, 0x67, 0x0b,
+];
+
+pub static USRPKEY_AUTHBOUND_ENC_PAYLOAD: &[u8] = &[
+    0x05, 0xb2, 0x5a, 0x1d, 0x1b, 0x25, 0x19, 0x48, 0xbf, 0x76, 0x0b, 0x37, 0x8c, 0x60, 0x52, 0xea,
+    0x30, 0x2a, 0x2c, 0x89, 0x99, 0x95, 0x57, 0x5c, 0xec, 0x62, 0x3c, 0x08, 0x1a, 0xc6, 0x65, 0xf9,
+    0xad, 0x24, 0x99, 0xf0, 0x5c, 0x44, 0xa0, 0xea, 0x9a, 0x60, 0xa2, 0xef, 0xf5, 0x27, 0x50, 0xba,
+    0x9c, 0xef, 0xa6, 0x08, 0x88, 0x4b, 0x0f, 0xfe, 0x5d, 0x41, 0xac, 0xba, 0xef, 0x9d, 0xa4, 0xb7,
+    0x72, 0xd3, 0xc8, 0x11, 0x92, 0x06, 0xf6, 0x26, 0xdf, 0x90, 0xe2, 0x66, 0x89, 0xf3, 0x85, 0x16,
+    0x4a, 0xdf, 0x7f, 0xac, 0x94, 0x4a, 0x1c, 0xce, 0x18, 0xee, 0xf4, 0x1f, 0x8e, 0xd6, 0xaf, 0xfd,
+    0x1d, 0xe5, 0x80, 0x4a, 0x6b, 0xbf, 0x91, 0xe2, 0x36, 0x1d, 0xb3, 0x53, 0x12, 0xfd, 0xc9, 0x0b,
+    0xa6, 0x69, 0x00, 0x45, 0xcb, 0x4c, 0x40, 0x6b, 0x70, 0xcb, 0xd2, 0xa0, 0x44, 0x0b, 0x4b, 0xec,
+    0xd6, 0x4f, 0x6f, 0x64, 0x37, 0xa7, 0xc7, 0x25, 0x54, 0xf4, 0xac, 0x6b, 0x34, 0x53, 0xea, 0x4e,
+    0x56, 0x49, 0xba, 0xf4, 0x1e, 0xc6, 0x52, 0x8f, 0xf4, 0x85, 0xe7, 0xb5, 0xaf, 0x49, 0x68, 0xb3,
+    0xb8, 0x7d, 0x63, 0xfc, 0x6e, 0x83, 0xa0, 0xf3, 0x91, 0x04, 0x80, 0xfd, 0xc5, 0x54, 0x7e, 0x92,
+    0x1a, 0x87, 0x2c, 0x6e, 0xa6, 0x29, 0xb9, 0x1e, 0x3f, 0xef, 0x30, 0x12, 0x7b, 0x2f, 0xa2, 0x16,
+    0x61, 0x8a, 0xcf, 0x14, 0x2d, 0x62, 0x98, 0x15, 0xae, 0x3b, 0xe6, 0x08, 0x1e, 0xb1, 0xf1, 0x21,
+    0xb0, 0x50, 0xc0, 0x4b, 0x81, 0x71, 0x29, 0xe7, 0x86, 0xbf, 0x29, 0xe1, 0xeb, 0xfe, 0xbc, 0x11,
+    0x3c, 0xc6, 0x15, 0x47, 0x9b, 0x41, 0x84, 0x61, 0x33, 0xbf, 0xca, 0xfe, 0x24, 0x92, 0x9e, 0x70,
+    0x26, 0x36, 0x46, 0xca, 0xfe, 0xd3, 0x5a, 0x1d, 0x9e, 0x30, 0x19, 0xbd, 0x26, 0x49, 0xb4, 0x90,
+    0x0c, 0x8d, 0xa2, 0x28, 0xa6, 0x24, 0x62, 0x6b, 0xe2, 0xfa, 0xe0, 0x53, 0xaa, 0x01, 0xeb, 0xaa,
+    0x41, 0x2b, 0xcb, 0xb1, 0x08, 0x66, 0x9d, 0x21, 0x2d, 0x2a, 0x47, 0x44, 0xee, 0xd5, 0x06, 0xe3,
+    0x4a, 0xb9, 0x3f, 0xcd, 0x78, 0x67, 0x89, 0x5b, 0xf7, 0x51, 0xc0, 0xc4, 0xa9, 0x68, 0xee, 0x44,
+    0x9c, 0x47, 0xa4, 0xbd, 0x6f, 0x7b, 0xdd, 0x64, 0xa8, 0xc7, 0x1e, 0x77, 0x1d, 0x68, 0x87, 0xaa,
+    0xae, 0x3c, 0xfc, 0x58, 0xb6, 0x3c, 0xcf, 0x58, 0xd0, 0x10, 0xaa, 0xef, 0xf0, 0x98, 0x67, 0x14,
+    0x29, 0x4d, 0x40, 0x8b, 0xe5, 0xb1, 0xdf, 0x7f, 0x40, 0xb1, 0xd8, 0xea, 0x6c, 0xa8, 0xf7, 0x64,
+    0xed, 0x02, 0x8d, 0xe7, 0x93, 0xfe, 0x79, 0x9a, 0x88, 0x62, 0x4f, 0xd0, 0x8a, 0x80, 0x36, 0x42,
+    0x0a, 0xf1, 0xa2, 0x0e, 0x30, 0x39, 0xbd, 0x26, 0x1d, 0xd4, 0xf1, 0xc8, 0x6e, 0xdd, 0xc5, 0x41,
+    0x29, 0xd8, 0xc1, 0x9e, 0x24, 0xf0, 0x25, 0x07, 0x05, 0x06, 0xc5, 0x08, 0xe3, 0x02, 0x2b, 0xe1,
+    0x40, 0xc5, 0x67, 0xd2, 0x82, 0x96, 0x20, 0x80, 0xcf, 0x87, 0x3a, 0xc6, 0xb0, 0xbe, 0xcc, 0xbb,
+    0x5a, 0x01, 0xab, 0xdd, 0x00, 0xc7, 0x0e, 0x7b, 0x02, 0x35, 0x27, 0xf4, 0x70, 0xfe, 0xd1, 0x19,
+    0x6a, 0x64, 0x23, 0x9d, 0xba, 0xe9, 0x1d, 0x76, 0x90, 0xfe, 0x7f, 0xd6, 0xb5, 0xa0, 0xe7, 0xb9,
+    0xf3, 0x56, 0x82, 0x8e, 0x57, 0x35, 0xf2, 0x69, 0xce, 0x52, 0xac, 0xc2, 0xf6, 0x5e, 0xb6, 0x54,
+    0x95, 0x83, 0x3b, 0x9f, 0x48, 0xbb, 0x04, 0x06, 0xac, 0x55, 0xa9, 0xb9, 0xa3, 0xe7, 0x89, 0x6e,
+    0x5c, 0x3a, 0x08, 0x67, 0x00, 0x8f, 0x1e, 0x26, 0x1b, 0x4d, 0x8a, 0xa6, 0x17, 0xa0, 0xa6, 0x18,
+    0xe6, 0x31, 0x43, 0x15, 0xb8, 0x7f, 0x9e, 0xf5, 0x78, 0x58, 0x98, 0xb1, 0x8c, 0xf5, 0x22, 0x42,
+    0x33, 0xc0, 0x42, 0x72, 0x4f, 0xce, 0x9f, 0x31, 0xaf, 0x17, 0x2f, 0x21, 0x07, 0xea, 0x61, 0xff,
+    0x73, 0x08, 0x50, 0xb2, 0x19, 0xe8, 0x23, 0x1b, 0x83, 0x42, 0xdd, 0x4e, 0x6d,
+];
+
 pub static USRPKEY_AUTHBOUND_CHR: &[u8] = &[
     0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/keystore2/src/legacy_importer.rs b/keystore2/src/legacy_importer.rs
index 107b9b0..5a64020 100644
--- a/keystore2/src/legacy_importer.rs
+++ b/keystore2/src/legacy_importer.rs
@@ -14,18 +14,19 @@
 
 //! This module acts as a bridge between the legacy key database and the keystore2 database.
 
-use crate::key_parameter::KeyParameterValue;
-use crate::legacy_blob::BlobValue;
-use crate::utils::{uid_to_android_user, watchdog as wd};
-use crate::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader};
-use crate::{database::KeyType, error::Error};
-use crate::{
-    database::{
-        BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, EncryptedBy, KeyMetaData,
-        KeyMetaEntry, KeystoreDB, Uuid, KEYSTORE_UUID,
-    },
-    super_key::USER_SUPER_KEY,
+use crate::database::{
+    BlobInfo, BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, EncryptedBy, KeyMetaData,
+    KeyMetaEntry, KeyType, KeystoreDB, Uuid, KEYSTORE_UUID,
 };
+use crate::error::{map_km_error, Error};
+use crate::key_parameter::{KeyParameter, KeyParameterValue};
+use crate::legacy_blob::{self, Blob, BlobValue, LegacyKeyCharacteristics};
+use crate::super_key::USER_SUPER_KEY;
+use crate::utils::{
+    key_characteristics_to_internal, uid_to_android_user, upgrade_keyblob_if_required_with,
+    watchdog as wd, AesGcm,
+};
+use crate::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader};
 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,
@@ -279,6 +280,116 @@
         result
     }
 
+    /// This function behaves exactly like with_try_import unless the src_key has an encrypted
+    /// component (other than the key blob itself [1]) and super_key is None.
+    /// In that case the files belonging to the src_key will be renamed to be moved to the
+    /// namespace indicated by dst_key. The destination domain must be in Domain::APP.
+    ///
+    /// [1] Components that cannot be encrypted with the super key in keystore2 include the
+    /// characteristics file, which was encrypted before Android Q, and certificate entries
+    /// added by KeyChain before Android Q.
+    pub fn with_try_import_or_migrate_namespaces<F, T>(
+        &self,
+        src: (u32, &KeyDescriptor),
+        dest: (u32, &KeyDescriptor),
+        super_key: Option<Arc<dyn AesGcm + Send + Sync>>,
+        has_migrate_any_permission: bool,
+        key_accessor: F,
+    ) -> Result<Option<T>>
+    where
+        F: Fn() -> Result<T>,
+    {
+        let _wp = wd::watch_millis("LegacyImporter::with_try_import_or_migrate_namespaces", 500);
+
+        let (src_uid, src_key) = src;
+        let (dest_uid, dest_key) = dest;
+
+        // Access the key and return on success.
+        match key_accessor() {
+            Ok(result) => return Ok(Some(result)),
+            Err(e) => {
+                if e.root_cause().downcast_ref::<Error>()
+                    != Some(&Error::Rc(ResponseCode::KEY_NOT_FOUND))
+                {
+                    return Err(e);
+                }
+            }
+        }
+
+        // Filter inputs. We can only load legacy app domain keys as well
+        // as the SELINUX WIFI_NAMESPACE, which will be populated from AID_WIFI.
+        let src_uid = match src_key {
+            KeyDescriptor { domain: Domain::APP, alias: Some(_), .. } => src_uid,
+            KeyDescriptor { domain: Domain::SELINUX, nspace, alias: Some(_), .. } => {
+                match *nspace {
+                    Self::WIFI_NAMESPACE => Self::AID_WIFI,
+                    _ => {
+                        return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+                            .context(format!("No legacy keys for namespace {}", nspace))
+                    }
+                }
+            }
+            _ => {
+                return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+                    .context("No legacy keys for key descriptor.")
+            }
+        };
+
+        let dest_uid = match dest_key {
+            KeyDescriptor { domain: Domain::APP, alias: Some(_), .. } => Some(dest_uid),
+            KeyDescriptor { domain: Domain::SELINUX, alias: Some(_), .. } => {
+                // Domain::SELINUX cannot be migrated in place, but we cannot fail at this point
+                // because the import may succeed at which point the actual migration will
+                // be performed by the caller.
+                None
+            }
+            _ => {
+                return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+                    .context("No legacy keys for key descriptor.")
+            }
+        };
+
+        let src_key_clone = src_key.clone();
+        let dest_key_clone = dest_key.clone();
+        let result = self.do_serialized(move |importer_state| {
+            let super_key = super_key.map(|sk| -> Arc<dyn AesGcm> { sk });
+            match (
+                importer_state.check_and_import(src_uid, src_key_clone.clone(), super_key),
+                dest_uid,
+            ) {
+                // The import into the database was successful. Return Ok(true)
+                (Ok(()), _) => Ok(true),
+                // The import failed because a certificate and/or characteristics
+                // file was encrypted and no super_key was available. Migration within the
+                // legacy database is attempted and Ok(false) is returned on success.
+                (Err(e), Some(dest_uid))
+                    if has_migrate_any_permission
+                        && e.root_cause().downcast_ref::<Error>()
+                            == Some(&Error::Rc(ResponseCode::LOCKED)) =>
+                {
+                    importer_state
+                        .migrate_namespaces(src_uid, dest_uid, src_key_clone, dest_key_clone)
+                        .map(|_| false)
+                }
+                (Err(e), _) => Err(e),
+            }
+        });
+
+        match result {
+            None => {
+                Err(Error::Rc(ResponseCode::KEY_NOT_FOUND)).context("Legacy database is empty.")
+            }
+
+            Some(Ok(true)) => {
+                // After successful import try again.
+                key_accessor().map(|v| Some(v))
+            }
+            // The entry was successfully migrated within the legacy database.
+            Some(Ok(false)) => Ok(None),
+            Some(Err(e)) => Err(e),
+        }
+    }
+
     /// Runs the key_accessor function and returns its result. If it returns an error and the
     /// root cause was KEY_NOT_FOUND, tries to import a key with the given parameters from
     /// the legacy database to the new database and runs the key_accessor function again if
@@ -287,6 +398,7 @@
         &self,
         key: &KeyDescriptor,
         caller_uid: u32,
+        super_key: Option<Arc<dyn AesGcm + Send + Sync>>,
         key_accessor: F,
     ) -> Result<T>
     where
@@ -323,8 +435,10 @@
         };
 
         let key_clone = key.clone();
-        let result = self
-            .do_serialized(move |importer_state| importer_state.check_and_import(uid, key_clone));
+        let result = self.do_serialized(move |importer_state| {
+            let super_key = super_key.map(|sk| -> Arc<dyn AesGcm> { sk });
+            importer_state.check_and_import(uid, key_clone, super_key)
+        });
 
         if let Some(result) = result {
             result?;
@@ -430,9 +544,199 @@
             .context("In list_uid: Trying to list legacy entries.")
     }
 
+    fn migrate_namespaces(
+        &mut self,
+        src_uid: u32,
+        dest_uid: u32,
+        src_key: KeyDescriptor,
+        dest_key: KeyDescriptor,
+    ) -> Result<()> {
+        let src_alias = src_key.alias.ok_or_else(|| {
+            anyhow::anyhow!(Error::sys()).context(
+                "In legacy_migrator::migrate_namespace: src_key.alias must be Some because \
+                 our caller must not have called us otherwise.",
+            )
+        })?;
+
+        if dest_key.domain != Domain::APP {
+            return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(
+                "In legacy_migrator::migrate_namespace: \
+                 Legacy in-place migration to SELinux namespace is not supported.",
+            );
+        }
+
+        let dest_alias = dest_key.alias.ok_or_else(|| {
+            anyhow::anyhow!(Error::sys()).context(concat!(
+                "In legacy_migrator::migrate_namespace: dest_key.alias must be Some because ",
+                "our caller must not have called us otherwise."
+            ))
+        })?;
+
+        self.legacy_loader
+            .move_keystore_entry(src_uid, dest_uid, &src_alias, &dest_alias)
+            .context("In legacy_migrator::migrate_namespace: Moving key entry files.")
+    }
+
+    /// Checks if the key can potentially be unlocked. And deletes the key entry otherwise.
+    /// If the super_key has already been imported, the super key database id is returned.
+    fn get_super_key_id_check_unlockable_or_delete(
+        &mut self,
+        uid: u32,
+        alias: &str,
+    ) -> Result<i64> {
+        let user_id = uid_to_android_user(uid);
+
+        match self
+            .db
+            .load_super_key(&USER_SUPER_KEY, user_id)
+            .context("In get_super_key_id_check_unlockable_or_delete: Failed to load super key")?
+        {
+            Some((_, entry)) => Ok(entry.id()),
+            None => {
+                // This might be the first time we access the super key,
+                // and it may not have been imported. We cannot import
+                // the legacy super_key key now, because we need to reencrypt
+                // it which we cannot do if we are not unlocked, which we are
+                // not because otherwise the key would have been imported.
+                // We can check though if the key exists. If it does,
+                // we can return Locked. Otherwise, we can delete the
+                // key and return NotFound, because the key will never
+                // be unlocked again.
+                if self.legacy_loader.has_super_key(user_id) {
+                    Err(Error::Rc(ResponseCode::LOCKED)).context(
+                        "In get_super_key_id_check_unlockable_or_delete: \
+                         Cannot import super key of this key while user is locked.",
+                    )
+                } else {
+                    self.legacy_loader.remove_keystore_entry(uid, alias).context(
+                        "In get_super_key_id_check_unlockable_or_delete: \
+                         Trying to remove obsolete key.",
+                    )?;
+                    Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+                        .context("In get_super_key_id_check_unlockable_or_delete: Obsolete key.")
+                }
+            }
+        }
+    }
+
+    fn characteristics_file_to_cache(
+        &mut self,
+        km_blob_params: Option<(Blob, LegacyKeyCharacteristics)>,
+        super_key: &Option<Arc<dyn AesGcm>>,
+        uid: u32,
+        alias: &str,
+    ) -> Result<(Option<(Blob, Vec<KeyParameter>)>, Option<(LegacyBlob<'static>, BlobMetaData)>)>
+    {
+        let (km_blob, params) = match km_blob_params {
+            Some((km_blob, LegacyKeyCharacteristics::File(params))) => (km_blob, params),
+            Some((km_blob, LegacyKeyCharacteristics::Cache(params))) => {
+                return Ok((Some((km_blob, params)), None))
+            }
+            None => return Ok((None, None)),
+        };
+
+        let km_uuid = self
+            .get_km_uuid(km_blob.is_strongbox())
+            .context("In characteristics_file_to_cache: Trying to get KM UUID")?;
+
+        let blob = match (&km_blob.value(), super_key.as_ref()) {
+            (BlobValue::Encrypted { iv, tag, data }, Some(super_key)) => {
+                let blob = super_key
+                    .decrypt(data, iv, tag)
+                    .context("In characteristics_file_to_cache: Decryption failed.")?;
+                LegacyBlob::ZVec(blob)
+            }
+            (BlobValue::Encrypted { .. }, None) => {
+                return Err(Error::Rc(ResponseCode::LOCKED)).context(
+                    "In characteristics_file_to_cache: Oh uh, so close. \
+                     This ancient key cannot be imported unless the user is unlocked.",
+                );
+            }
+            (BlobValue::Decrypted(data), _) => LegacyBlob::Ref(data),
+            _ => {
+                return Err(Error::sys())
+                    .context("In characteristics_file_to_cache: Unexpected blob type.")
+            }
+        };
+
+        let (km_params, upgraded_blob) = get_key_characteristics_without_app_data(&km_uuid, &*blob)
+            .context(
+                "In characteristics_file_to_cache: Failed to get key characteristics from device.",
+            )?;
+
+        let flags = km_blob.get_flags();
+
+        let (current_blob, superseded_blob) = if let Some(upgraded_blob) = upgraded_blob {
+            match (km_blob.take_value(), super_key.as_ref()) {
+                (BlobValue::Encrypted { iv, tag, data }, Some(super_key)) => {
+                    let super_key_id =
+                        self.get_super_key_id_check_unlockable_or_delete(uid, alias).context(
+                            "In characteristics_file_to_cache: \
+                             How is there a super key but no super key id?",
+                        )?;
+
+                    let mut superseded_metadata = BlobMetaData::new();
+                    superseded_metadata.add(BlobMetaEntry::Iv(iv.to_vec()));
+                    superseded_metadata.add(BlobMetaEntry::AeadTag(tag.to_vec()));
+                    superseded_metadata
+                        .add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key_id)));
+                    superseded_metadata.add(BlobMetaEntry::KmUuid(km_uuid));
+                    let superseded_blob = (LegacyBlob::Vec(data), superseded_metadata);
+
+                    let (data, iv, tag) = super_key.encrypt(&upgraded_blob).context(
+                        "In characteristics_file_to_cache: \
+                         Failed to encrypt upgraded key blob.",
+                    )?;
+                    (
+                        Blob::new(flags, BlobValue::Encrypted { data, iv, tag }),
+                        Some(superseded_blob),
+                    )
+                }
+                (BlobValue::Encrypted { .. }, None) => {
+                    return Err(Error::sys()).context(
+                        "In characteristics_file_to_cache: This should not be reachable. \
+                         The blob could not have been decrypted above.",
+                    );
+                }
+                (BlobValue::Decrypted(data), _) => {
+                    let mut superseded_metadata = BlobMetaData::new();
+                    superseded_metadata.add(BlobMetaEntry::KmUuid(km_uuid));
+                    let superseded_blob = (LegacyBlob::ZVec(data), superseded_metadata);
+                    (
+                        Blob::new(
+                            flags,
+                            BlobValue::Decrypted(upgraded_blob.try_into().context(
+                                "In characteristics_file_to_cache: \
+                             Failed to convert upgraded blob to ZVec.",
+                            )?),
+                        ),
+                        Some(superseded_blob),
+                    )
+                }
+                _ => {
+                    return Err(Error::sys()).context(
+                        "In characteristics_file_to_cache: This should not be reachable. \
+                         Any other variant should have resulted in a different error.",
+                    )
+                }
+            }
+        } else {
+            (km_blob, None)
+        };
+
+        let params =
+            augment_legacy_characteristics_file_with_key_characteristics(km_params, params);
+        Ok((Some((current_blob, params)), superseded_blob))
+    }
+
     /// This is a key import request that must run in the importer thread. This must
     /// be passed to do_serialized.
-    fn check_and_import(&mut self, uid: u32, mut key: KeyDescriptor) -> Result<()> {
+    fn check_and_import(
+        &mut self,
+        uid: u32,
+        mut key: KeyDescriptor,
+        super_key: Option<Arc<dyn AesGcm>>,
+    ) -> Result<()> {
         let alias = key.alias.clone().ok_or_else(|| {
             anyhow::anyhow!(Error::sys()).context(
                 "In check_and_import: Must be Some because \
@@ -451,49 +755,42 @@
         // If the key is not found in the cache, try to load from the legacy database.
         let (km_blob_params, user_cert, ca_cert) = self
             .legacy_loader
-            .load_by_uid_alias(uid, &alias, None)
+            .load_by_uid_alias(uid, &alias, &super_key)
+            .map_err(|e| {
+                if e.root_cause().downcast_ref::<legacy_blob::Error>()
+                    == Some(&legacy_blob::Error::LockedComponent)
+                {
+                    // There is no chance to succeed at this point. We just check if there is
+                    // a super key so that this entry might be unlockable in the future.
+                    // If not the entry will be deleted and KEY_NOT_FOUND is returned.
+                    // If a super key id was returned we still have to return LOCKED but the key
+                    // may be imported when the user unlocks the device.
+                    self.get_super_key_id_check_unlockable_or_delete(uid, &alias)
+                        .and_then::<i64, _>(|_| {
+                            Err(Error::Rc(ResponseCode::LOCKED))
+                                .context("Super key present but locked.")
+                        })
+                        .unwrap_err()
+                } else {
+                    e
+                }
+            })
             .context("In check_and_import: Trying to load legacy blob.")?;
+
+        let (km_blob_params, superseded_blob) = self
+            .characteristics_file_to_cache(km_blob_params, &super_key, uid, &alias)
+            .context("In check_and_import: Trying to update legacy charateristics.")?;
+
         let result = match km_blob_params {
             Some((km_blob, params)) => {
                 let is_strongbox = km_blob.is_strongbox();
+
                 let (blob, mut blob_metadata) = match km_blob.take_value() {
                     BlobValue::Encrypted { iv, tag, data } => {
                         // Get super key id for user id.
-                        let user_id = uid_to_android_user(uid as u32);
-
-                        let super_key_id = match self
-                            .db
-                            .load_super_key(&USER_SUPER_KEY, user_id)
-                            .context("In check_and_import: Failed to load super key")?
-                        {
-                            Some((_, entry)) => entry.id(),
-                            None => {
-                                // This might be the first time we access the super key,
-                                // and it may not have been imported. We cannot import
-                                // the legacy super_key key now, because we need to reencrypt
-                                // it which we cannot do if we are not unlocked, which we are
-                                // not because otherwise the key would have been imported.
-                                // We can check though if the key exists. If it does,
-                                // we can return Locked. Otherwise, we can delete the
-                                // key and return NotFound, because the key will never
-                                // be unlocked again.
-                                if self.legacy_loader.has_super_key(user_id) {
-                                    return Err(Error::Rc(ResponseCode::LOCKED)).context(concat!(
-                                        "In check_and_import: Cannot import super key of this ",
-                                        "key while user is locked."
-                                    ));
-                                } else {
-                                    self.legacy_loader.remove_keystore_entry(uid, &alias).context(
-                                        concat!(
-                                            "In check_and_import: ",
-                                            "Trying to remove obsolete key."
-                                        ),
-                                    )?;
-                                    return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
-                                        .context("In check_and_import: Obsolete key.");
-                                }
-                            }
-                        };
+                        let super_key_id = self
+                            .get_super_key_id_check_unlockable_or_delete(uid, &alias)
+                            .context("In check_and_import: Failed to get super key id.")?;
 
                         let mut blob_metadata = BlobMetaData::new();
                         blob_metadata.add(BlobMetaEntry::Iv(iv.to_vec()));
@@ -519,13 +816,18 @@
                     .context("In check_and_import: Trying to make creation time.")?;
                 metadata.add(KeyMetaEntry::CreationDate(creation_date));
 
+                let blob_info = BlobInfo::new_with_superseded(
+                    &blob,
+                    &blob_metadata,
+                    superseded_blob.as_ref().map(|(b, m)| (&**b, m)),
+                );
                 // Store legacy key in the database.
                 self.db
                     .store_new_key(
                         &key,
                         KeyType::Client,
                         &params,
-                        &(&blob, &blob_metadata),
+                        &blob_info,
                         &CertificateInfo::new(user_cert, ca_cert),
                         &metadata,
                         &km_uuid,
@@ -567,7 +869,7 @@
 
         if let Some(super_key) = self
             .legacy_loader
-            .load_super_key(user_id, &pw)
+            .load_super_key(user_id, pw)
             .context("In check_and_import_super_key: Trying to load legacy super key.")?
         {
             let (blob, blob_metadata) =
@@ -630,18 +932,21 @@
 
         for (uid, alias) in aliases
             .into_iter()
-            .map(|(uid, aliases)| aliases.into_iter().map(move |alias| (uid, alias)))
-            .flatten()
+            .flat_map(|(uid, aliases)| aliases.into_iter().map(move |alias| (uid, alias)))
         {
             let (km_blob_params, _, _) = self
                 .legacy_loader
-                .load_by_uid_alias(uid, &alias, None)
+                .load_by_uid_alias(uid, &alias, &None)
                 .context("In bulk_delete: Trying to load legacy blob.")?;
 
             // Determine if the key needs special handling to be deleted.
             let (need_gc, is_super_encrypted) = km_blob_params
                 .as_ref()
                 .map(|(blob, params)| {
+                    let params = match params {
+                        LegacyKeyCharacteristics::Cache(params)
+                        | LegacyKeyCharacteristics::File(params) => params,
+                    };
                     (
                         params.iter().any(|kp| {
                             KeyParameterValue::RollbackResistance == *kp.key_parameter_value()
@@ -714,18 +1019,68 @@
     }
 }
 
-enum LegacyBlob {
+enum LegacyBlob<'a> {
     Vec(Vec<u8>),
     ZVec(ZVec),
+    Ref(&'a [u8]),
 }
 
-impl Deref for LegacyBlob {
+impl Deref for LegacyBlob<'_> {
     type Target = [u8];
 
     fn deref(&self) -> &Self::Target {
         match self {
-            Self::Vec(v) => &v,
-            Self::ZVec(v) => &v,
+            Self::Vec(v) => v,
+            Self::ZVec(v) => v,
+            Self::Ref(v) => v,
         }
     }
 }
+
+/// This function takes two KeyParameter lists. The first is assumed to have been retrieved from the
+/// KM back end using km_dev.getKeyCharacteristics. The second is assumed to have been retrieved
+/// from a legacy key characteristics file (not cache) as used in Android P and older. The function
+/// augments the former with entries from the latter only if no equivalent entry is present ignoring.
+/// the security level of enforcement. All entries in the latter are assumed to have security level
+/// KEYSTORE.
+fn augment_legacy_characteristics_file_with_key_characteristics<T>(
+    mut from_km: Vec<KeyParameter>,
+    legacy: T,
+) -> Vec<KeyParameter>
+where
+    T: IntoIterator<Item = KeyParameter>,
+{
+    for legacy_kp in legacy.into_iter() {
+        if !from_km
+            .iter()
+            .any(|km_kp| km_kp.key_parameter_value() == legacy_kp.key_parameter_value())
+        {
+            from_km.push(legacy_kp);
+        }
+    }
+    from_km
+}
+
+/// Attempts to retrieve the key characteristics for the given blob from the KM back end with the
+/// given UUID. It may upgrade the key blob in the process. In that case the upgraded blob is
+/// returned as the second tuple member.
+fn get_key_characteristics_without_app_data(
+    uuid: &Uuid,
+    blob: &[u8],
+) -> Result<(Vec<KeyParameter>, Option<Vec<u8>>)> {
+    let (km_dev, _) = crate::globals::get_keymint_dev_by_uuid(uuid)
+        .with_context(|| format!("In foo: Trying to get km device for id {:?}", uuid))?;
+
+    let (characteristics, upgraded_blob) = upgrade_keyblob_if_required_with(
+        &*km_dev,
+        blob,
+        &[],
+        |blob| {
+            let _wd = wd::watch_millis("In foo: Calling GetKeyCharacteristics.", 500);
+            map_km_error(km_dev.getKeyCharacteristics(blob, &[], &[]))
+        },
+        |_| Ok(()),
+    )
+    .context("In foo.")?;
+    Ok((key_characteristics_to_internal(characteristics), upgraded_blob))
+}
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 66763e9..4a23843 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -40,12 +40,12 @@
 pub mod security_level;
 pub mod service;
 pub mod shared_secret_negotiation;
-pub mod try_insert;
 pub mod utils;
 
 mod attestation_key_utils;
 mod audit_log;
 mod gc;
+mod km_compat;
 mod super_key;
 
 #[cfg(feature = "watchdog")]
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index 468a37d..0d637d8 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -21,12 +21,16 @@
 use crate::globals::get_keymint_device;
 use crate::globals::{DB, LEGACY_IMPORTER, SUPER_KEY};
 use crate::permission::{KeyPerm, KeystorePerm};
-use crate::super_key::UserState;
-use crate::utils::{check_key_permission, check_keystore_permission, watchdog as wd};
-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 crate::super_key::{SuperKeyManager, UserState};
+use crate::utils::{
+    check_key_permission, check_keystore_permission, list_key_entries, uid_to_android_user,
+    watchdog as wd,
+};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    IKeyMintDevice::IKeyMintDevice, SecurityLevel::SecurityLevel,
+};
 use android_security_maintenance::aidl::android::security::maintenance::{
-    IKeystoreMaintenance::{BnKeystoreMaintenance, IKeystoreMaintenance},
+    IKeystoreMaintenance::{BnKeystoreMaintenance, IKeystoreMaintenance, UID_SELF},
     UserState::UserState as AidlUserState,
 };
 use android_security_maintenance::binder::{
@@ -36,6 +40,7 @@
 use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
 use anyhow::{Context, Result};
 use keystore2_crypto::Password;
+use keystore2_selinux as selinux;
 
 /// Reexport Domain for the benefit of DeleteListener
 pub use android_system_keystore2::aidl::android::system::keystore2::Domain::Domain;
@@ -66,24 +71,25 @@
     }
 
     fn on_user_password_changed(user_id: i32, password: Option<Password>) -> Result<()> {
-        //Check permission. Function should return if this failed. Therefore having '?' at the end
-        //is very important.
-        check_keystore_permission(KeystorePerm::change_password())
+        // Check permission. Function should return if this failed. Therefore having '?' at the end
+        // is very important.
+        check_keystore_permission(KeystorePerm::ChangePassword)
             .context("In on_user_password_changed.")?;
 
+        let mut skm = SUPER_KEY.write().unwrap();
+
         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)
+                skm.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(
+                skm.reset_or_init_user_and_get_user_state(
                     &mut db.borrow_mut(),
                     &LEGACY_IMPORTER,
-                    &SUPER_KEY,
                     user_id as u32,
                     password.as_ref(),
                 )
@@ -105,11 +111,11 @@
     fn add_or_remove_user(&self, user_id: i32) -> Result<()> {
         // Check permission. Function should return if this failed. Therefore having '?' at the end
         // is very important.
-        check_keystore_permission(KeystorePerm::change_user()).context("In add_or_remove_user.")?;
+        check_keystore_permission(KeystorePerm::ChangeUser).context("In add_or_remove_user.")?;
+
         DB.with(|db| {
-            UserState::reset_user(
+            SUPER_KEY.write().unwrap().reset_user(
                 &mut db.borrow_mut(),
-                &SUPER_KEY,
                 &LEGACY_IMPORTER,
                 user_id as u32,
                 false,
@@ -123,7 +129,7 @@
 
     fn clear_namespace(&self, domain: Domain, nspace: i64) -> Result<()> {
         // Permission check. Must return on error. Do not touch the '?'.
-        check_keystore_permission(KeystorePerm::clear_uid()).context("In clear_namespace.")?;
+        check_keystore_permission(KeystorePerm::ClearUID).context("In clear_namespace.")?;
 
         LEGACY_IMPORTER
             .bulk_delete_uid(domain, nspace)
@@ -138,10 +144,14 @@
     fn get_state(user_id: i32) -> Result<AidlUserState> {
         // Check permission. Function should return if this failed. Therefore having '?' at the end
         // is very important.
-        check_keystore_permission(KeystorePerm::get_state()).context("In get_state.")?;
+        check_keystore_permission(KeystorePerm::GetState).context("In get_state.")?;
         let state = DB
             .with(|db| {
-                UserState::get(&mut db.borrow_mut(), &LEGACY_IMPORTER, &SUPER_KEY, user_id as u32)
+                SUPER_KEY.read().unwrap().get_user_state(
+                    &mut db.borrow_mut(),
+                    &LEGACY_IMPORTER,
+                    user_id as u32,
+                )
             })
             .context("In get_state. Trying to get UserState.")?;
 
@@ -154,13 +164,10 @@
 
     fn call_with_watchdog<F>(sec_level: SecurityLevel, name: &'static str, op: &F) -> Result<()>
     where
-        F: Fn(Strong<dyn IKeyMintDevice>) -> binder::public_api::Result<()>,
+        F: Fn(Strong<dyn IKeyMintDevice>) -> binder::Result<()>,
     {
-        let (dev, _, _) = get_keymint_device(&sec_level)
+        let (km_dev, _, _) = get_keymint_device(&sec_level)
             .context("In call_with_watchdog: getting keymint device")?;
-        let km_dev: Strong<dyn IKeyMintDevice> = dev
-            .get_interface()
-            .context("In call_with_watchdog: getting keymint device interface")?;
 
         let _wp = wd::watch_millis_with("In call_with_watchdog", 500, move || {
             format!("Seclevel: {:?} Op: {}", sec_level, name)
@@ -171,7 +178,7 @@
 
     fn call_on_all_security_levels<F>(name: &'static str, op: F) -> Result<()>
     where
-        F: Fn(Strong<dyn IKeyMintDevice>) -> binder::public_api::Result<()>,
+        F: Fn(Strong<dyn IKeyMintDevice>) -> binder::Result<()>,
     {
         let sec_levels = [
             (SecurityLevel::TRUSTED_ENVIRONMENT, "TRUSTED_ENVIRONMENT"),
@@ -197,11 +204,13 @@
     }
 
     fn early_boot_ended() -> Result<()> {
-        check_keystore_permission(KeystorePerm::early_boot_ended())
+        check_keystore_permission(KeystorePerm::EarlyBootEnded)
             .context("In early_boot_ended. Checking permission")?;
         log::info!("In early_boot_ended.");
 
-        if let Err(e) = DB.with(|db| SUPER_KEY.set_up_boot_level_cache(&mut db.borrow_mut())) {
+        if let Err(e) =
+            DB.with(|db| SuperKeyManager::set_up_boot_level_cache(&SUPER_KEY, &mut db.borrow_mut()))
+        {
             log::error!("SUPER_KEY.set_up_boot_level_cache failed:\n{:?}\n:(", e);
         }
         Maintenance::call_on_all_security_levels("earlyBootEnded", |dev| dev.earlyBootEnded())
@@ -209,58 +218,128 @@
 
     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.")?;
+        check_keystore_permission(KeystorePerm::ReportOffBody).context("In on_device_off_body.")?;
 
         DB.with(|db| db.borrow_mut().update_last_off_body(MonotonicRawTime::now()));
         Ok(())
     }
 
     fn migrate_key_namespace(source: &KeyDescriptor, destination: &KeyDescriptor) -> Result<()> {
-        let caller_uid = ThreadState::get_calling_uid();
+        let migrate_any_key_permission =
+            check_keystore_permission(KeystorePerm::MigrateAnyKey).is_ok();
+
+        let src_uid = match source.domain {
+            Domain::SELINUX | Domain::KEY_ID => ThreadState::get_calling_uid(),
+            Domain::APP if source.nspace == UID_SELF.into() => ThreadState::get_calling_uid(),
+            Domain::APP if source.nspace != UID_SELF.into() && migrate_any_key_permission => {
+                source.nspace as u32
+            }
+            _ => {
+                return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(
+                    "In migrate_key_namespace: \
+                     Source domain must be one of APP, SELINUX, or KEY_ID.",
+                )
+            }
+        };
+
+        let dest_uid = match destination.domain {
+            Domain::SELINUX => ThreadState::get_calling_uid(),
+            Domain::APP if destination.nspace == UID_SELF.into() => ThreadState::get_calling_uid(),
+            Domain::APP if destination.nspace != UID_SELF.into() && migrate_any_key_permission => {
+                destination.nspace as u32
+            }
+            _ => {
+                return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(
+                    "In migrate_key_namespace: \
+                     Destination domain must be one of APP or SELINUX.",
+                )
+            }
+        };
+
+        let user_id = uid_to_android_user(dest_uid);
+
+        if user_id != uid_to_android_user(src_uid)
+            && (source.domain == Domain::APP || destination.domain == Domain::APP)
+        {
+            return Err(Error::sys()).context(
+                "In migrate_key_namespace: Keys cannot be migrated across android users.",
+            );
+        }
+
+        let super_key = SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(user_id);
 
         DB.with(|db| {
-            let key_id_guard = match source.domain {
-                Domain::APP | Domain::SELINUX | Domain::KEY_ID => {
-                    let (key_id_guard, _) = LEGACY_IMPORTER
-                        .with_try_import(&source, caller_uid, || {
-                            db.borrow_mut().load_key_entry(
-                                &source,
-                                KeyType::Client,
-                                KeyEntryLoadBits::NONE,
-                                caller_uid,
-                                |k, av| {
-                                    check_key_permission(KeyPerm::use_(), k, &av)?;
-                                    check_key_permission(KeyPerm::delete(), k, &av)?;
-                                    check_key_permission(KeyPerm::grant(), k, &av)
-                                },
-                            )
-                        })
-                        .context("In migrate_key_namespace: Failed to load key blob.")?;
-                    key_id_guard
-                }
-                _ => {
-                    return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(concat!(
-                        "In migrate_key_namespace: ",
-                        "Source domain must be one of APP, SELINUX, or KEY_ID."
-                    ))
-                }
-            };
-
-            db.borrow_mut().migrate_key_namespace(key_id_guard, destination, caller_uid, |k| {
-                check_key_permission(KeyPerm::rebind(), k, &None)
-            })
+            if let Some((key_id_guard, _)) = LEGACY_IMPORTER
+                .with_try_import_or_migrate_namespaces(
+                    (src_uid, source),
+                    (dest_uid, destination),
+                    super_key,
+                    migrate_any_key_permission,
+                    || {
+                        db.borrow_mut().load_key_entry(
+                            source,
+                            KeyType::Client,
+                            KeyEntryLoadBits::NONE,
+                            src_uid,
+                            |k, av| {
+                                if migrate_any_key_permission {
+                                    Ok(())
+                                } else {
+                                    check_key_permission(KeyPerm::Use, k, &av)?;
+                                    check_key_permission(KeyPerm::Delete, k, &av)?;
+                                    check_key_permission(KeyPerm::Grant, k, &av)
+                                }
+                            },
+                        )
+                    },
+                )
+                .context("In migrate_key_namespace: Failed to load key blob.")?
+            {
+                db.borrow_mut().migrate_key_namespace(key_id_guard, destination, dest_uid, |k| {
+                    if migrate_any_key_permission {
+                        Ok(())
+                    } else {
+                        check_key_permission(KeyPerm::Rebind, k, &None)
+                    }
+                })
+            } else {
+                Ok(())
+            }
         })
     }
 
     fn delete_all_keys() -> Result<()> {
         // Security critical permission check. This statement must return on fail.
-        check_keystore_permission(KeystorePerm::delete_all_keys())
+        check_keystore_permission(KeystorePerm::DeleteAllKeys)
             .context("In delete_all_keys. Checking permission")?;
         log::info!("In delete_all_keys.");
 
         Maintenance::call_on_all_security_levels("deleteAllKeys", |dev| dev.deleteAllKeys())
     }
+
+    fn list_entries(domain: Domain, nspace: i64) -> Result<Vec<KeyDescriptor>> {
+        let k = match domain {
+            Domain::APP | Domain::SELINUX => KeyDescriptor{domain, nspace, ..Default::default()},
+            _ => return Err(Error::perm()).context(
+                "In list_entries: List entries is only supported for Domain::APP and Domain::SELINUX."
+            ),
+        };
+
+        // The caller has to have either GetInfo for the namespace or List permission
+        check_key_permission(KeyPerm::GetInfo, &k, &None)
+            .or_else(|e| {
+                if Some(&selinux::Error::PermissionDenied)
+                    == e.root_cause().downcast_ref::<selinux::Error>()
+                {
+                    check_keystore_permission(KeystorePerm::List)
+                } else {
+                    Err(e)
+                }
+            })
+            .context("In list_entries: While checking key and keystore permission.")?;
+
+        DB.with(|db| list_key_entries(&mut db.borrow_mut(), domain, nspace))
+    }
 }
 
 impl Interface for Maintenance {}
@@ -310,6 +389,11 @@
         map_or_log_err(Self::migrate_key_namespace(source, destination), Ok)
     }
 
+    fn listEntries(&self, domain: Domain, namespace: i64) -> BinderResult<Vec<KeyDescriptor>> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::listEntries", 500);
+        map_or_log_err(Self::list_entries(domain, namespace), Ok)
+    }
+
     fn deleteAllKeys(&self) -> BinderResult<()> {
         let _wp = wd::watch_millis("IKeystoreMaintenance::deleteAllKeys", 500);
         map_or_log_err(Self::delete_all_keys(), Ok)
diff --git a/keystore2/src/metrics.rs b/keystore2/src/metrics.rs
index 42295b7..3d8d6d3 100644
--- a/keystore2/src/metrics.rs
+++ b/keystore2/src/metrics.rs
@@ -41,7 +41,7 @@
     fn pull_metrics(&self, atom_id: AtomID) -> Result<Vec<KeystoreAtom>> {
         // Check permission. Function should return if this failed. Therefore having '?' at the end
         // is very important.
-        check_keystore_permission(KeystorePerm::pull_metrics()).context("In pull_metrics.")?;
+        check_keystore_permission(KeystorePerm::PullMetrics).context("In pull_metrics.")?;
         METRICS_STORE.get_atoms(atom_id)
     }
 }
diff --git a/keystore2/src/metrics_store.rs b/keystore2/src/metrics_store.rs
index a064f65..b6f1343 100644
--- a/keystore2/src/metrics_store.rs
+++ b/keystore2/src/metrics_store.rs
@@ -46,8 +46,8 @@
 };
 use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
 use anyhow::{Context, Result};
-use keystore2_system_property::{write, PropertyWatcher, PropertyWatcherError};
 use lazy_static::lazy_static;
+use rustutils::system_properties::PropertyWatcherError;
 use std::collections::HashMap;
 use std::sync::Mutex;
 use std::time::{Duration, SystemTime, UNIX_EPOCH};
@@ -288,6 +288,7 @@
                     EcCurve::P_256 => MetricsEcCurve::P_256,
                     EcCurve::P_384 => MetricsEcCurve::P_384,
                     EcCurve::P_521 => MetricsEcCurve::P_521,
+                    EcCurve::CURVE_25519 => MetricsEcCurve::CURVE_25519,
                     _ => MetricsEcCurve::EC_CURVE_UNSPECIFIED,
                 }
             }
@@ -631,7 +632,9 @@
         }
     };
 
-    if let Err(e) = write(KEYSTORE_CRASH_COUNT_PROPERTY, &new_count.to_string()) {
+    if let Err(e) =
+        rustutils::system_properties::write(KEYSTORE_CRASH_COUNT_PROPERTY, &new_count.to_string())
+    {
         log::error!(
             concat!(
                 "In update_keystore_crash_sysprop:: ",
@@ -644,12 +647,11 @@
 
 /// Read the system property: keystore.crash_count.
 pub fn read_keystore_crash_count() -> Result<i32> {
-    let mut prop_reader = PropertyWatcher::new("keystore.crash_count").context(concat!(
-        "In read_keystore_crash_count: Failed to create reader a PropertyWatcher."
-    ))?;
-    prop_reader
-        .read(|_n, v| v.parse::<i32>().map_err(std::convert::Into::into))
-        .context("In read_keystore_crash_count: Failed to read the existing system property.")
+    rustutils::system_properties::read("keystore.crash_count")
+        .context("In read_keystore_crash_count: Failed read property.")?
+        .context("In read_keystore_crash_count: Property not set.")?
+        .parse::<i32>()
+        .map_err(std::convert::Into::into)
 }
 
 /// Enum defining the bit position for each padding mode. Since padding mode can be repeatable, it
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
index 7171864..5da3b32 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -128,12 +128,12 @@
 use crate::enforcements::AuthInfo;
 use crate::error::{map_err_with, map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
 use crate::metrics_store::log_key_operation_event_stats;
-use crate::utils::{watchdog as wd, Asp};
+use crate::utils::watchdog as wd;
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     IKeyMintOperation::IKeyMintOperation, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
     SecurityLevel::SecurityLevel,
 };
-use android_hardware_security_keymint::binder::BinderFeatures;
+use android_hardware_security_keymint::binder::{BinderFeatures, Strong};
 use android_system_keystore2::aidl::android::system::keystore2::{
     IKeystoreOperation::BnKeystoreOperation, IKeystoreOperation::IKeystoreOperation,
 };
@@ -170,7 +170,7 @@
 pub struct Operation {
     // The index of this operation in the OperationDb.
     index: usize,
-    km_op: Asp,
+    km_op: Strong<dyn IKeyMintOperation>,
     last_usage: Mutex<Instant>,
     outcome: Mutex<Outcome>,
     owner: u32, // Uid of the operation's owner.
@@ -222,7 +222,7 @@
     ) -> Self {
         Self {
             index,
-            km_op: Asp::new(km_op.as_binder()),
+            km_op,
             last_usage: Mutex::new(Instant::now()),
             outcome: Mutex::new(Outcome::Unknown),
             owner,
@@ -282,19 +282,10 @@
         }
         *locked_outcome = Outcome::Pruned;
 
-        let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
-            match self.km_op.get_interface() {
-                Ok(km_op) => km_op,
-                Err(e) => {
-                    log::error!("In prune: Failed to get KeyMintOperation interface.\n    {:?}", e);
-                    return Err(Error::sys());
-                }
-            };
-
         let _wp = wd::watch_millis("In Operation::prune: calling abort()", 500);
 
         // We abort the operation. If there was an error we log it but ignore it.
-        if let Err(e) = map_km_error(km_op.abort()) {
+        if let Err(e) = map_km_error(self.km_op.abort()) {
             log::error!("In prune: KeyMint::abort failed with {:?}.", e);
         }
 
@@ -362,9 +353,6 @@
         Self::check_input_length(aad_input).context("In update_aad")?;
         self.touch();
 
-        let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
-            self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
-
         let (hat, tst) = self
             .auth_info
             .lock()
@@ -374,7 +362,7 @@
 
         self.update_outcome(&mut *outcome, {
             let _wp = wd::watch_millis("Operation::update_aad: calling updateAad", 500);
-            map_km_error(km_op.updateAad(aad_input, hat.as_ref(), tst.as_ref()))
+            map_km_error(self.km_op.updateAad(aad_input, hat.as_ref(), tst.as_ref()))
         })
         .context("In update_aad: KeyMint::update failed.")?;
 
@@ -388,9 +376,6 @@
         Self::check_input_length(input).context("In update")?;
         self.touch();
 
-        let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
-            self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
-
         let (hat, tst) = self
             .auth_info
             .lock()
@@ -401,7 +386,7 @@
         let output = self
             .update_outcome(&mut *outcome, {
                 let _wp = wd::watch_millis("Operation::update: calling update", 500);
-                map_km_error(km_op.update(input, hat.as_ref(), tst.as_ref()))
+                map_km_error(self.km_op.update(input, hat.as_ref(), tst.as_ref()))
             })
             .context("In update: KeyMint::update failed.")?;
 
@@ -421,9 +406,6 @@
         }
         self.touch();
 
-        let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
-            self.km_op.get_interface().context("In finish: Failed to get KeyMintOperation.")?;
-
         let (hat, tst, confirmation_token) = self
             .auth_info
             .lock()
@@ -434,7 +416,7 @@
         let output = self
             .update_outcome(&mut *outcome, {
                 let _wp = wd::watch_millis("Operation::finish: calling finish", 500);
-                map_km_error(km_op.finish(
+                map_km_error(self.km_op.finish(
                     input,
                     signature,
                     hat.as_ref(),
@@ -462,12 +444,10 @@
     fn abort(&self, outcome: Outcome) -> Result<()> {
         let mut locked_outcome = self.check_active().context("In abort")?;
         *locked_outcome = outcome;
-        let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
-            self.km_op.get_interface().context("In abort: Failed to get KeyMintOperation.")?;
 
         {
             let _wp = wd::watch_millis("Operation::abort: calling abort", 500);
-            map_km_error(km_op.abort()).context("In abort: KeyMint::abort failed.")
+            map_km_error(self.km_op.abort()).context("In abort: KeyMint::abort failed.")
         }
     }
 }
@@ -513,7 +493,7 @@
     /// owner uid and returns a new Operation wrapped in a `std::sync::Arc`.
     pub fn create_operation(
         &self,
-        km_op: binder::public_api::Strong<dyn IKeyMintOperation>,
+        km_op: binder::Strong<dyn IKeyMintOperation>,
         owner: u32,
         auth_info: AuthInfo,
         forced: bool,
@@ -791,9 +771,7 @@
     /// BnKeystoreOperation proxy object. It also enables
     /// `BinderFeatures::set_requesting_sid` on the new interface, because
     /// we need it for checking Keystore permissions.
-    pub fn new_native_binder(
-        operation: Arc<Operation>,
-    ) -> binder::public_api::Strong<dyn IKeystoreOperation> {
+    pub fn new_native_binder(operation: Arc<Operation>) -> binder::Strong<dyn IKeystoreOperation> {
         BnKeystoreOperation::new_binder(
             Self { operation: Mutex::new(Some(operation)) },
             BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
@@ -841,7 +819,7 @@
 impl binder::Interface for KeystoreOperation {}
 
 impl IKeystoreOperation for KeystoreOperation {
-    fn updateAad(&self, aad_input: &[u8]) -> binder::public_api::Result<()> {
+    fn updateAad(&self, aad_input: &[u8]) -> binder::Result<()> {
         let _wp = wd::watch_millis("IKeystoreOperation::updateAad", 500);
         map_or_log_err(
             self.with_locked_operation(
@@ -852,7 +830,7 @@
         )
     }
 
-    fn update(&self, input: &[u8]) -> binder::public_api::Result<Option<Vec<u8>>> {
+    fn update(&self, input: &[u8]) -> binder::Result<Option<Vec<u8>>> {
         let _wp = wd::watch_millis("IKeystoreOperation::update", 500);
         map_or_log_err(
             self.with_locked_operation(
@@ -866,7 +844,7 @@
         &self,
         input: Option<&[u8]>,
         signature: Option<&[u8]>,
-    ) -> binder::public_api::Result<Option<Vec<u8>>> {
+    ) -> binder::Result<Option<Vec<u8>>> {
         let _wp = wd::watch_millis("IKeystoreOperation::finish", 500);
         map_or_log_err(
             self.with_locked_operation(
@@ -877,7 +855,7 @@
         )
     }
 
-    fn abort(&self) -> binder::public_api::Result<()> {
+    fn abort(&self) -> binder::Result<()> {
         let _wp = wd::watch_millis("IKeystoreOperation::abort", 500);
         map_err_with(
             self.with_locked_operation(
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index 4add899..1e6f10a 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -18,23 +18,18 @@
 //! It also provides KeystorePerm and KeyPerm as convenience wrappers for the SELinux permission
 //! defined by keystore2 and keystore2_key respectively.
 
+use crate::error::Error as KsError;
 use android_system_keystore2::aidl::android::system::keystore2::{
     Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
 };
-
+use anyhow::Context as AnyhowContext;
+use keystore2_selinux as selinux;
+use lazy_static::lazy_static;
+use selinux::{implement_class, Backend, ClassPermission};
 use std::cmp::PartialEq;
 use std::convert::From;
 use std::ffi::CStr;
 
-use crate::error::Error as KsError;
-use keystore2_selinux as selinux;
-
-use anyhow::Context as AnyhowContext;
-
-use selinux::Backend;
-
-use lazy_static::lazy_static;
-
 // Replace getcon with a mock in the test situation
 #[cfg(not(test))]
 use selinux::getcon;
@@ -52,273 +47,111 @@
     KEYSTORE2_KEY_LABEL_BACKEND.lookup(&namespace.to_string())
 }
 
-/// ## Background
-///
-/// AIDL enums are represented as constants of the form:
-/// ```
-/// mod EnumName {
-///     pub type EnumName = i32;
-///     pub const Variant1: EnumName = <value1>;
-///     pub const Variant2: EnumName = <value2>;
-///     ...
-/// }
-///```
-/// This macro wraps the enum in a new type, e.g., `MyPerm` and maps each variant to an SELinux
-/// permission while providing the following interface:
-///  * From<EnumName> and Into<EnumName> are implemented. Where the implementation of From maps
-///    any variant not specified to the default.
-///  * Every variant has a constructor with a name corresponding to its lower case SELinux string
-///    representation.
-///  * `MyPerm.to_selinux(&self)` returns the SELinux string representation of the
-///    represented permission.
-///
-/// ## Special behavior
-/// If the keyword `use` appears as an selinux name `use_` is used as identifier for the
-/// constructor function (e.g. `MePerm::use_()`) but the string returned by `to_selinux` will
-/// still be `"use"`.
-///
-/// ## Example
-/// ```
-///
-/// implement_permission!(
-///     /// MyPerm documentation.
-///     #[derive(Clone, Copy, Debug, PartialEq)]
-///     MyPerm from EnumName with default (None, none) {}
-///         Variant1,    selinux name: variant1;
-///         Variant2,    selinux name: variant1;
-///     }
-/// );
-/// ```
-macro_rules! implement_permission_aidl {
-    // This rule provides the public interface of the macro. And starts the preprocessing
-    // recursion (see below).
-    ($(#[$m:meta])* $name:ident from $aidl_name:ident with default ($($def:tt)*)
-        { $($element:tt)* })
-    => {
-        implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*), [],
-            $($element)*);
-    };
-
-    // The following three rules recurse through the elements of the form
-    // `<enum variant>, selinux name: <selinux_name>;`
-    // preprocessing the input.
-
-    // The first rule terminates the recursion and passes the processed arguments to the final
-    // rule that spills out the implementation.
-    (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*], ) => {
-        implement_permission_aidl!(@end $($m)*, $name, $aidl_name, ($($def)*) { $($out)* } );
-    };
-
-    // The second rule is triggered if the selinux name of an element is literally `use`.
-    // It produces the tuple `<enum variant>, use_, use;`
-    // and appends it to the out list.
-    (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*],
-        $e_name:ident, selinux name: use; $($element:tt)*)
-    => {
-        implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*),
-                              [$($out)* $e_name, use_, use;], $($element)*);
-    };
-
-    // The third rule is the default rule which replaces every input tuple with
-    // `<enum variant>, <selinux_name>, <selinux_name>;`
-    // and appends the result to the out list.
-    (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*],
-        $e_name:ident, selinux name: $e_str:ident; $($element:tt)*)
-    => {
-        implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*),
-                              [$($out)* $e_name, $e_str, $e_str;], $($element)*);
-    };
-
-    (@end $($m:meta)*, $name:ident, $aidl_name:ident,
-        ($def_name:ident, $def_selinux_name:ident) {
-            $($element_name:ident, $element_identifier:ident,
-                $selinux_name:ident;)*
-        })
-    =>
-    {
-        $(#[$m])*
-        pub struct $name(pub $aidl_name);
-
-        impl From<$aidl_name> for $name {
-            fn from (p: $aidl_name) -> Self {
-                match p {
-                    $aidl_name::$def_name => Self($aidl_name::$def_name),
-                    $($aidl_name::$element_name => Self($aidl_name::$element_name),)*
-                    _ => Self($aidl_name::$def_name),
-                }
-            }
-        }
-
-        impl From<$name> for $aidl_name {
-            fn from(p: $name) -> $aidl_name {
-                p.0
-            }
-        }
-
-        impl $name {
-            /// Returns a string representation of the permission as required by
-            /// `selinux::check_access`.
-            pub fn to_selinux(&self) -> &'static str {
-                match self {
-                    Self($aidl_name::$def_name) => stringify!($def_selinux_name),
-                    $(Self($aidl_name::$element_name) => stringify!($selinux_name),)*
-                    _ => stringify!($def_selinux_name),
-                }
-            }
-
-            /// Creates an instance representing a permission with the same name.
-            pub const fn $def_selinux_name() -> Self { Self($aidl_name::$def_name) }
-            $(
-                /// Creates an instance representing a permission with the same name.
-                pub const fn $element_identifier() -> Self { Self($aidl_name::$element_name) }
-            )*
-        }
-    };
-}
-
-implement_permission_aidl!(
+implement_class!(
     /// KeyPerm provides a convenient abstraction from the SELinux class `keystore2_key`.
     /// At the same time it maps `KeyPermissions` from the Keystore 2.0 AIDL Grant interface to
-    /// the SELinux permissions. With the implement_permission macro, we conveniently
-    /// provide mappings between the wire type bit field values, the rust enum and the SELinux
-    /// string representation.
-    ///
-    /// ## Example
-    ///
-    /// In this access check `KeyPerm::get_info().to_selinux()` would return the SELinux representation
-    /// "info".
-    /// ```
-    /// selinux::check_access(source_context, target_context, "keystore2_key",
-    ///                       KeyPerm::get_info().to_selinux());
-    /// ```
-    #[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;
-        GRANT,          selinux name: grant;
-        MANAGE_BLOB,    selinux name: manage_blob;
-        REBIND,         selinux name: rebind;
-        REQ_FORCED_OP,  selinux name: req_forced_op;
-        UPDATE,         selinux name: update;
-        USE,            selinux name: use;
-        USE_DEV_ID,     selinux name: use_dev_id;
+    /// the SELinux permissions.
+    #[repr(i32)]
+    #[selinux(class_name = keystore2_key)]
+    #[derive(Clone, Copy, Debug, PartialEq)]
+    pub enum KeyPerm {
+        /// Checked when convert_storage_key_to_ephemeral is called.
+        #[selinux(name = convert_storage_key_to_ephemeral)]
+        ConvertStorageKeyToEphemeral = KeyPermission::CONVERT_STORAGE_KEY_TO_EPHEMERAL.0,
+        /// Checked when the caller tries do delete a key.
+        #[selinux(name = delete)]
+        Delete = KeyPermission::DELETE.0,
+        /// Checked when the caller tries to use a unique id.
+        #[selinux(name = gen_unique_id)]
+        GenUniqueId = KeyPermission::GEN_UNIQUE_ID.0,
+        /// Checked when the caller tries to load a key.
+        #[selinux(name = get_info)]
+        GetInfo = KeyPermission::GET_INFO.0,
+        /// Checked when the caller attempts to grant a key to another uid.
+        /// Also used for gating key migration attempts.
+        #[selinux(name = grant)]
+        Grant = KeyPermission::GRANT.0,
+        /// Checked when the caller attempts to use Domain::BLOB.
+        #[selinux(name = manage_blob)]
+        ManageBlob = KeyPermission::MANAGE_BLOB.0,
+        /// Checked when the caller tries to create a key which implies rebinding
+        /// an alias to the new key.
+        #[selinux(name = rebind)]
+        Rebind = KeyPermission::REBIND.0,
+        /// Checked when the caller attempts to create a forced operation.
+        #[selinux(name = req_forced_op)]
+        ReqForcedOp = KeyPermission::REQ_FORCED_OP.0,
+        /// Checked when the caller attempts to update public key artifacts.
+        #[selinux(name = update)]
+        Update = KeyPermission::UPDATE.0,
+        /// Checked when the caller attempts to use a private or public key.
+        #[selinux(name = use)]
+        Use = KeyPermission::USE.0,
+        /// Checked when the caller attempts to use device ids for attestation.
+        #[selinux(name = use_dev_id)]
+        UseDevId = KeyPermission::USE_DEV_ID.0,
     }
 );
 
-/// This macro implements an enum with values mapped to SELinux permission names.
-/// The below example wraps the enum MyPermission in the tuple struct `MyPerm` and implements
-///  * From<i32> and Into<i32> are implemented. Where the implementation of From maps
-///    any variant not specified to the default.
-///  * Every variant has a constructor with a name corresponding to its lower case SELinux string
-///    representation.
-///  * `MyPerm.to_selinux(&self)` returns the SELinux string representation of the
-///    represented permission.
-///
-/// ## Example
-/// ```
-/// implement_permission!(
-///     /// MyPerm documentation.
-///     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
-///     MyPerm with default (None = 0, none) {
-///         Foo = 1,           selinux name: foo;
-///         Bar = 2,           selinux name: bar;
-///     }
-/// );
-/// ```
-macro_rules! implement_permission {
-    // This rule provides the public interface of the macro. And starts the preprocessing
-    // recursion (see below).
-    ($(#[$m:meta])* $name:ident with default
-        ($def_name:ident = $def_val:expr, $def_selinux_name:ident)
-        {
-            $($(#[$element_meta:meta])*
-            $element_name:ident = $element_val:expr, selinux name: $selinux_name:ident;)*
-        })
-    => {
-        $(#[$m])*
-        pub enum $name {
-            /// The default variant of an enum.
-            $def_name = $def_val,
-            $(
-                $(#[$element_meta])*
-                $element_name = $element_val,
-            )*
-        }
-
-        impl From<i32> for $name {
-            fn from (p: i32) -> Self {
-                match p {
-                    $def_val => Self::$def_name,
-                    $($element_val => Self::$element_name,)*
-                    _ => Self::$def_name,
-                }
-            }
-        }
-
-        impl From<$name> for i32 {
-            fn from(p: $name) -> i32 {
-                p as i32
-            }
-        }
-
-        impl $name {
-            /// Returns a string representation of the permission as required by
-            /// `selinux::check_access`.
-            pub fn to_selinux(&self) -> &'static str {
-                match self {
-                    Self::$def_name => stringify!($def_selinux_name),
-                    $(Self::$element_name => stringify!($selinux_name),)*
-                }
-            }
-
-            /// Creates an instance representing a permission with the same name.
-            pub const fn $def_selinux_name() -> Self { Self::$def_name }
-            $(
-                /// Creates an instance representing a permission with the same name.
-                pub const fn $selinux_name() -> Self { Self::$element_name }
-            )*
-        }
-    };
-}
-
-implement_permission!(
+implement_class!(
     /// KeystorePerm provides a convenient abstraction from the SELinux class `keystore2`.
     /// Using the implement_permission macro we get the same features as `KeyPerm`.
+    #[selinux(class_name = keystore2)]
     #[derive(Clone, Copy, Debug, PartialEq)]
-    KeystorePerm with default (None = 0, none) {
+    pub enum KeystorePerm {
         /// Checked when a new auth token is installed.
-        AddAuth = 1,    selinux name: add_auth;
+        #[selinux(name = add_auth)]
+        AddAuth,
         /// Checked when an app is uninstalled or wiped.
-        ClearNs = 2,    selinux name: clear_ns;
+        #[selinux(name = clear_ns)]
+        ClearNs,
         /// Checked when the user state is queried from Keystore 2.0.
-        GetState = 4,   selinux name: get_state;
+        #[selinux(name = get_state)]
+        GetState,
         /// Checked when Keystore 2.0 is asked to list a namespace that the caller
         /// does not have the get_info permission for.
-        List = 8,       selinux name: list;
+        #[selinux(name = list)]
+        List,
         /// Checked when Keystore 2.0 gets locked.
-        Lock = 0x10,       selinux name: lock;
+        #[selinux(name = lock)]
+        Lock,
         /// Checked when Keystore 2.0 shall be reset.
-        Reset = 0x20,    selinux name: reset;
+        #[selinux(name = reset)]
+        Reset,
         /// Checked when Keystore 2.0 shall be unlocked.
-        Unlock = 0x40,    selinux name: unlock;
+        #[selinux(name = unlock)]
+        Unlock,
         /// Checked when user is added or removed.
-        ChangeUser = 0x80,    selinux name: change_user;
+        #[selinux(name = change_user)]
+        ChangeUser,
         /// Checked when password of the user is changed.
-        ChangePassword = 0x100,    selinux name: change_password;
+        #[selinux(name = change_password)]
+        ChangePassword,
         /// Checked when a UID is cleared.
-        ClearUID = 0x200,    selinux name: clear_uid;
+        #[selinux(name = clear_uid)]
+        ClearUID,
         /// Checked when Credstore calls IKeystoreAuthorization to obtain auth tokens.
-        GetAuthToken = 0x400,  selinux name: get_auth_token;
+        #[selinux(name = get_auth_token)]
+        GetAuthToken,
         /// Checked when earlyBootEnded() is called.
-        EarlyBootEnded = 0x800,   selinux name: early_boot_ended;
+        #[selinux(name = early_boot_ended)]
+        EarlyBootEnded,
         /// Checked when IKeystoreMaintenance::onDeviceOffBody is called.
-        ReportOffBody = 0x1000, selinux name: report_off_body;
-        /// Checked when IkeystoreMetrics::pullMetris is called.
-        PullMetrics = 0x2000, selinux name: pull_metrics;
+        #[selinux(name = report_off_body)]
+        ReportOffBody,
+        /// Checked when IkeystoreMetrics::pullMetrics is called.
+        #[selinux(name = pull_metrics)]
+        PullMetrics,
         /// Checked when IKeystoreMaintenance::deleteAllKeys is called.
-        DeleteAllKeys = 0x4000, selinux name: delete_all_keys;
+        #[selinux(name = delete_all_keys)]
+        DeleteAllKeys,
+        /// Checked when migrating any key from any namespace to any other namespace. It was
+        /// introduced for migrating keys when an app leaves a sharedUserId.
+        #[selinux(name = migrate_any_key)]
+        MigrateAnyKey,
+        /// Checked on calls to IRemotelyProvisionedKeyPool::getAttestationKey
+        #[selinux(name = get_attestation_key)]
+        GetAttestationKey,
     }
 );
 
@@ -332,17 +165,17 @@
 ///
 /// ## Example
 /// ```
-/// let perms1 = key_perm_set![KeyPerm::use_(), KeyPerm::manage_blob(), KeyPerm::grant()];
-/// let perms2 = key_perm_set![KeyPerm::use_(), KeyPerm::manage_blob()];
+/// let perms1 = key_perm_set![KeyPerm::Use, KeyPerm::ManageBlob, KeyPerm::Grant];
+/// let perms2 = key_perm_set![KeyPerm::Use, KeyPerm::ManageBlob];
 ///
 /// assert!(perms1.includes(perms2))
 /// assert!(!perms2.includes(perms1))
 ///
 /// let i = perms1.into_iter();
 /// // iteration in ascending order of the permission's numeric representation.
-/// assert_eq(Some(KeyPerm::manage_blob()), i.next());
-/// assert_eq(Some(KeyPerm::grant()), i.next());
-/// assert_eq(Some(KeyPerm::use_()), i.next());
+/// assert_eq(Some(KeyPerm::ManageBlob), i.next());
+/// assert_eq(Some(KeyPerm::Grant), i.next());
+/// assert_eq(Some(KeyPerm::Use), i.next());
 /// assert_eq(None, i.next());
 /// ```
 #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
@@ -373,7 +206,7 @@
                 let p = self.vec.0 & (1 << self.pos);
                 self.pos += 1;
                 if p != 0 {
-                    return Some(KeyPerm::from(KeyPermission(p)));
+                    return Some(KeyPerm::from(p));
                 }
             }
         }
@@ -382,7 +215,7 @@
 
 impl From<KeyPerm> for KeyPermSet {
     fn from(p: KeyPerm) -> Self {
-        Self((p.0).0 as i32)
+        Self(p as i32)
     }
 }
 
@@ -417,7 +250,7 @@
 macro_rules! key_perm_set {
     () => { KeyPermSet(0) };
     ($head:expr $(, $tail:expr)* $(,)?) => {
-        KeyPermSet(($head.0).0 $(| ($tail.0).0)*)
+        KeyPermSet($head as i32 $(| $tail as i32)*)
     };
 }
 
@@ -430,14 +263,14 @@
     }
 }
 
-/// Uses `selinux::check_access` to check if the given caller context `caller_cxt` may access
+/// Uses `selinux::check_permission` to check if the given caller context `caller_cxt` may access
 /// the given permision `perm` of the `keystore2` security class.
 pub fn check_keystore_permission(caller_ctx: &CStr, perm: KeystorePerm) -> anyhow::Result<()> {
     let target_context = getcon().context("check_keystore_permission: getcon failed.")?;
-    selinux::check_access(caller_ctx, &target_context, "keystore2", perm.to_selinux())
+    selinux::check_permission(caller_ctx, &target_context, perm)
 }
 
-/// Uses `selinux::check_access` to check if the given caller context `caller_cxt` has
+/// Uses `selinux::check_permission` to check if the given caller context `caller_cxt` has
 /// all the permissions indicated in `access_vec` for the target domain indicated by the key
 /// descriptor `key` in the security class `keystore2_key`.
 ///
@@ -462,27 +295,24 @@
         _ => return Err(KsError::sys()).context(format!("Cannot grant {:?}.", key.domain)),
     };
 
-    selinux::check_access(caller_ctx, &target_context, "keystore2_key", "grant")
+    selinux::check_permission(caller_ctx, &target_context, KeyPerm::Grant)
         .context("Grant permission is required when granting.")?;
 
-    if access_vec.includes(KeyPerm::grant()) {
+    if access_vec.includes(KeyPerm::Grant) {
         return Err(selinux::Error::perm()).context("Grant permission cannot be granted.");
     }
 
     for p in access_vec.into_iter() {
-        selinux::check_access(caller_ctx, &target_context, "keystore2_key", p.to_selinux())
-            .context(format!(
-                concat!(
-                    "check_grant_permission: check_access failed. ",
-                    "The caller may have tried to grant a permission that they don't possess. {:?}"
-                ),
-                p
-            ))?
+        selinux::check_permission(caller_ctx, &target_context, p).context(format!(
+            "check_grant_permission: check_permission failed. \
+            The caller may have tried to grant a permission that they don't possess. {:?}",
+            p
+        ))?
     }
     Ok(())
 }
 
-/// Uses `selinux::check_access` to check if the given caller context `caller_cxt`
+/// Uses `selinux::check_permission` to check if the given caller context `caller_cxt`
 /// has the permissions indicated by `perm` for the target domain indicated by the key
 /// descriptor `key` in the security class `keystore2_key`.
 ///
@@ -492,7 +322,7 @@
 ///                      backend, and the result is used as target context.
 ///  * `Domain::BLOB` Same as SELinux but the "manage_blob" permission is always checked additionally
 ///                   to the one supplied in `perm`.
-///  * `Domain::GRANT` Does not use selinux::check_access. Instead the `access_vector`
+///  * `Domain::GRANT` Does not use selinux::check_permission. Instead the `access_vector`
 ///                    parameter is queried for permission, which must be supplied in this case.
 ///
 /// ## Return values.
@@ -536,7 +366,7 @@
             match access_vector {
                 Some(_) => {
                     return Err(selinux::Error::perm())
-                        .context(format!("\"{}\" not granted", perm.to_selinux()));
+                        .context(format!("\"{}\" not granted", perm.name()));
                 }
                 None => {
                     // If DOMAIN_GRANT was selected an access vector must be supplied.
@@ -557,12 +387,7 @@
                 .context("Domain::BLOB: Failed to lookup namespace.")?;
             // If DOMAIN_KEY_BLOB was specified, we check for the "manage_blob"
             // permission in addition to the requested permission.
-            selinux::check_access(
-                caller_ctx,
-                &tctx,
-                "keystore2_key",
-                KeyPerm::manage_blob().to_selinux(),
-            )?;
+            selinux::check_permission(caller_ctx, &tctx, KeyPerm::ManageBlob)?;
 
             tctx
         }
@@ -572,7 +397,7 @@
         }
     };
 
-    selinux::check_access(caller_ctx, &target_context, "keystore2_key", perm.to_selinux())
+    selinux::check_permission(caller_ctx, &target_context, perm)
 }
 
 #[cfg(test)]
@@ -583,49 +408,49 @@
     use keystore2_selinux::*;
 
     const ALL_PERMS: KeyPermSet = key_perm_set![
-        KeyPerm::manage_blob(),
-        KeyPerm::delete(),
-        KeyPerm::use_dev_id(),
-        KeyPerm::req_forced_op(),
-        KeyPerm::gen_unique_id(),
-        KeyPerm::grant(),
-        KeyPerm::get_info(),
-        KeyPerm::rebind(),
-        KeyPerm::update(),
-        KeyPerm::use_(),
-        KeyPerm::convert_storage_key_to_ephemeral(),
+        KeyPerm::ManageBlob,
+        KeyPerm::Delete,
+        KeyPerm::UseDevId,
+        KeyPerm::ReqForcedOp,
+        KeyPerm::GenUniqueId,
+        KeyPerm::Grant,
+        KeyPerm::GetInfo,
+        KeyPerm::Rebind,
+        KeyPerm::Update,
+        KeyPerm::Use,
+        KeyPerm::ConvertStorageKeyToEphemeral,
     ];
 
     const SYSTEM_SERVER_PERMISSIONS_NO_GRANT: KeyPermSet = key_perm_set![
-        KeyPerm::delete(),
-        KeyPerm::use_dev_id(),
-        // No KeyPerm::grant()
-        KeyPerm::get_info(),
-        KeyPerm::rebind(),
-        KeyPerm::update(),
-        KeyPerm::use_(),
+        KeyPerm::Delete,
+        KeyPerm::UseDevId,
+        // No KeyPerm::Grant
+        KeyPerm::GetInfo,
+        KeyPerm::Rebind,
+        KeyPerm::Update,
+        KeyPerm::Use,
     ];
 
     const NOT_GRANT_PERMS: KeyPermSet = key_perm_set![
-        KeyPerm::manage_blob(),
-        KeyPerm::delete(),
-        KeyPerm::use_dev_id(),
-        KeyPerm::req_forced_op(),
-        KeyPerm::gen_unique_id(),
-        // No KeyPerm::grant()
-        KeyPerm::get_info(),
-        KeyPerm::rebind(),
-        KeyPerm::update(),
-        KeyPerm::use_(),
-        KeyPerm::convert_storage_key_to_ephemeral(),
+        KeyPerm::ManageBlob,
+        KeyPerm::Delete,
+        KeyPerm::UseDevId,
+        KeyPerm::ReqForcedOp,
+        KeyPerm::GenUniqueId,
+        // No KeyPerm::Grant
+        KeyPerm::GetInfo,
+        KeyPerm::Rebind,
+        KeyPerm::Update,
+        KeyPerm::Use,
+        KeyPerm::ConvertStorageKeyToEphemeral,
     ];
 
     const UNPRIV_PERMS: KeyPermSet = key_perm_set![
-        KeyPerm::delete(),
-        KeyPerm::get_info(),
-        KeyPerm::rebind(),
-        KeyPerm::update(),
-        KeyPerm::use_(),
+        KeyPerm::Delete,
+        KeyPerm::GetInfo,
+        KeyPerm::Rebind,
+        KeyPerm::Update,
+        KeyPerm::Use,
     ];
 
     /// The su_key namespace as defined in su.te and keystore_key_contexts of the
@@ -672,28 +497,26 @@
     #[test]
     fn check_keystore_permission_test() -> Result<()> {
         let system_server_ctx = Context::new("u:r:system_server:s0")?;
-        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::add_auth()).is_ok());
-        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::clear_ns()).is_ok());
-        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::get_state()).is_ok());
-        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::lock()).is_ok());
-        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::reset()).is_ok());
-        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::unlock()).is_ok());
-        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::change_user()).is_ok());
-        assert!(
-            check_keystore_permission(&system_server_ctx, KeystorePerm::change_password()).is_ok()
-        );
-        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::clear_uid()).is_ok());
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::AddAuth).is_ok());
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ClearNs).is_ok());
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::GetState).is_ok());
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::Lock).is_ok());
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::Reset).is_ok());
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::Unlock).is_ok());
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ChangeUser).is_ok());
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ChangePassword).is_ok());
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ClearUID).is_ok());
         let shell_ctx = Context::new("u:r:shell:s0")?;
-        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::add_auth()));
-        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::clear_ns()));
-        assert!(check_keystore_permission(&shell_ctx, KeystorePerm::get_state()).is_ok());
-        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::list()));
-        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::lock()));
-        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::reset()));
-        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::unlock()));
-        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::change_user()));
-        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::change_password()));
-        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::clear_uid()));
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::AddAuth));
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ClearNs));
+        assert!(check_keystore_permission(&shell_ctx, KeystorePerm::GetState).is_ok());
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::List));
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::Lock));
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::Reset));
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::Unlock));
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ChangeUser));
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ChangePassword));
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ClearUID));
         Ok(())
     }
 
@@ -708,7 +531,7 @@
         // attempts to grant the grant permission must always fail even when privileged.
         assert_perm_failed!(check_grant_permission(
             &system_server_ctx,
-            KeyPerm::grant().into(),
+            KeyPerm::Grant.into(),
             &key
         ));
         // unprivileged grant attempts always fail. shell does not have the grant permission.
@@ -728,7 +551,7 @@
         if is_su {
             assert!(check_grant_permission(&sctx, NOT_GRANT_PERMS, &key).is_ok());
             // attempts to grant the grant permission must always fail even when privileged.
-            assert_perm_failed!(check_grant_permission(&sctx, KeyPerm::grant().into(), &key));
+            assert_perm_failed!(check_grant_permission(&sctx, KeyPerm::Grant.into(), &key));
         } else {
             // unprivileged grant attempts always fail. shell does not have the grant permission.
             assert_perm_failed!(check_grant_permission(&sctx, UNPRIV_PERMS, &key));
@@ -743,7 +566,7 @@
         assert_perm_failed!(check_key_permission(
             0,
             &selinux::Context::new("ignored").unwrap(),
-            KeyPerm::grant(),
+            KeyPerm::Grant,
             &key,
             &Some(UNPRIV_PERMS)
         ));
@@ -751,7 +574,7 @@
         check_key_permission(
             0,
             &selinux::Context::new("ignored").unwrap(),
-            KeyPerm::use_(),
+            KeyPerm::Use,
             &key,
             &Some(ALL_PERMS),
         )
@@ -765,61 +588,31 @@
 
         let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
 
-        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::use_(), &key, &None).is_ok());
-        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::delete(), &key, &None).is_ok());
-        assert!(
-            check_key_permission(0, &system_server_ctx, KeyPerm::get_info(), &key, &None).is_ok()
-        );
-        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::rebind(), &key, &None).is_ok());
-        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::update(), &key, &None).is_ok());
-        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::grant(), &key, &None).is_ok());
-        assert!(
-            check_key_permission(0, &system_server_ctx, KeyPerm::use_dev_id(), &key, &None).is_ok()
-        );
-        assert!(
-            check_key_permission(0, &gmscore_app, KeyPerm::gen_unique_id(), &key, &None).is_ok()
-        );
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Use, &key, &None).is_ok());
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Delete, &key, &None).is_ok());
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::GetInfo, &key, &None).is_ok());
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Rebind, &key, &None).is_ok());
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Update, &key, &None).is_ok());
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Grant, &key, &None).is_ok());
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::UseDevId, &key, &None).is_ok());
+        assert!(check_key_permission(0, &gmscore_app, KeyPerm::GenUniqueId, &key, &None).is_ok());
 
-        assert!(check_key_permission(0, &shell_ctx, KeyPerm::use_(), &key, &None).is_ok());
-        assert!(check_key_permission(0, &shell_ctx, KeyPerm::delete(), &key, &None).is_ok());
-        assert!(check_key_permission(0, &shell_ctx, KeyPerm::get_info(), &key, &None).is_ok());
-        assert!(check_key_permission(0, &shell_ctx, KeyPerm::rebind(), &key, &None).is_ok());
-        assert!(check_key_permission(0, &shell_ctx, KeyPerm::update(), &key, &None).is_ok());
-        assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::grant(), &key, &None));
-        assert_perm_failed!(check_key_permission(
-            0,
-            &shell_ctx,
-            KeyPerm::req_forced_op(),
-            &key,
-            &None
-        ));
-        assert_perm_failed!(check_key_permission(
-            0,
-            &shell_ctx,
-            KeyPerm::manage_blob(),
-            &key,
-            &None
-        ));
-        assert_perm_failed!(check_key_permission(
-            0,
-            &shell_ctx,
-            KeyPerm::use_dev_id(),
-            &key,
-            &None
-        ));
-        assert_perm_failed!(check_key_permission(
-            0,
-            &shell_ctx,
-            KeyPerm::gen_unique_id(),
-            &key,
-            &None
-        ));
+        assert!(check_key_permission(0, &shell_ctx, KeyPerm::Use, &key, &None).is_ok());
+        assert!(check_key_permission(0, &shell_ctx, KeyPerm::Delete, &key, &None).is_ok());
+        assert!(check_key_permission(0, &shell_ctx, KeyPerm::GetInfo, &key, &None).is_ok());
+        assert!(check_key_permission(0, &shell_ctx, KeyPerm::Rebind, &key, &None).is_ok());
+        assert!(check_key_permission(0, &shell_ctx, KeyPerm::Update, &key, &None).is_ok());
+        assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::Grant, &key, &None));
+        assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::ReqForcedOp, &key, &None));
+        assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::ManageBlob, &key, &None));
+        assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::UseDevId, &key, &None));
+        assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::GenUniqueId, &key, &None));
 
         // Also make sure that the permission fails if the caller is not the owner.
         assert_perm_failed!(check_key_permission(
             1, // the owner is 0
             &system_server_ctx,
-            KeyPerm::use_(),
+            KeyPerm::Use,
             &key,
             &None
         ));
@@ -827,18 +620,18 @@
         assert!(check_key_permission(
             1,
             &system_server_ctx,
-            KeyPerm::use_(),
+            KeyPerm::Use,
             &key,
-            &Some(key_perm_set![KeyPerm::use_()])
+            &Some(key_perm_set![KeyPerm::Use])
         )
         .is_ok());
         // But fail if the grant did not cover the requested permission.
         assert_perm_failed!(check_key_permission(
             1,
             &system_server_ctx,
-            KeyPerm::use_(),
+            KeyPerm::Use,
             &key,
-            &Some(key_perm_set![KeyPerm::get_info()])
+            &Some(key_perm_set![KeyPerm::GetInfo])
         ));
 
         Ok(())
@@ -854,46 +647,24 @@
             blob: None,
         };
 
+        assert!(check_key_permission(0, &sctx, KeyPerm::Use, &key, &None).is_ok());
+        assert!(check_key_permission(0, &sctx, KeyPerm::Delete, &key, &None).is_ok());
+        assert!(check_key_permission(0, &sctx, KeyPerm::GetInfo, &key, &None).is_ok());
+        assert!(check_key_permission(0, &sctx, KeyPerm::Rebind, &key, &None).is_ok());
+        assert!(check_key_permission(0, &sctx, KeyPerm::Update, &key, &None).is_ok());
+
         if is_su {
-            assert!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None).is_ok());
-            assert!(check_key_permission(0, &sctx, KeyPerm::delete(), &key, &None).is_ok());
-            assert!(check_key_permission(0, &sctx, KeyPerm::get_info(), &key, &None).is_ok());
-            assert!(check_key_permission(0, &sctx, KeyPerm::rebind(), &key, &None).is_ok());
-            assert!(check_key_permission(0, &sctx, KeyPerm::update(), &key, &None).is_ok());
-            assert!(check_key_permission(0, &sctx, KeyPerm::grant(), &key, &None).is_ok());
-            assert!(check_key_permission(0, &sctx, KeyPerm::manage_blob(), &key, &None).is_ok());
-            assert!(check_key_permission(0, &sctx, KeyPerm::use_dev_id(), &key, &None).is_ok());
-            assert!(check_key_permission(0, &sctx, KeyPerm::gen_unique_id(), &key, &None).is_ok());
-            assert!(check_key_permission(0, &sctx, KeyPerm::req_forced_op(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::Grant, &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::ManageBlob, &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::UseDevId, &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::GenUniqueId, &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::ReqForcedOp, &key, &None).is_ok());
         } else {
-            assert!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None).is_ok());
-            assert!(check_key_permission(0, &sctx, KeyPerm::delete(), &key, &None).is_ok());
-            assert!(check_key_permission(0, &sctx, KeyPerm::get_info(), &key, &None).is_ok());
-            assert!(check_key_permission(0, &sctx, KeyPerm::rebind(), &key, &None).is_ok());
-            assert!(check_key_permission(0, &sctx, KeyPerm::update(), &key, &None).is_ok());
-            assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::grant(), &key, &None));
-            assert_perm_failed!(check_key_permission(
-                0,
-                &sctx,
-                KeyPerm::req_forced_op(),
-                &key,
-                &None
-            ));
-            assert_perm_failed!(check_key_permission(
-                0,
-                &sctx,
-                KeyPerm::manage_blob(),
-                &key,
-                &None
-            ));
-            assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::use_dev_id(), &key, &None));
-            assert_perm_failed!(check_key_permission(
-                0,
-                &sctx,
-                KeyPerm::gen_unique_id(),
-                &key,
-                &None
-            ));
+            assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::Grant, &key, &None));
+            assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::ReqForcedOp, &key, &None));
+            assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::ManageBlob, &key, &None));
+            assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::UseDevId, &key, &None));
+            assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::GenUniqueId, &key, &None));
         }
         Ok(())
     }
@@ -909,9 +680,9 @@
         };
 
         if is_su {
-            check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None)
+            check_key_permission(0, &sctx, KeyPerm::Use, &key, &None)
         } else {
-            assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None));
+            assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::Use, &key, &None));
             Ok(())
         }
     }
@@ -925,7 +696,7 @@
             check_key_permission(
                 0,
                 &selinux::Context::new("ignored").unwrap(),
-                KeyPerm::use_(),
+                KeyPerm::Use,
                 &key,
                 &None
             )
@@ -940,45 +711,45 @@
     #[test]
     fn key_perm_set_all_test() {
         let v = key_perm_set![
-            KeyPerm::manage_blob(),
-            KeyPerm::delete(),
-            KeyPerm::use_dev_id(),
-            KeyPerm::req_forced_op(),
-            KeyPerm::gen_unique_id(),
-            KeyPerm::grant(),
-            KeyPerm::get_info(),
-            KeyPerm::rebind(),
-            KeyPerm::update(),
-            KeyPerm::use_() // Test if the macro accepts missing comma at the end of the list.
+            KeyPerm::ManageBlob,
+            KeyPerm::Delete,
+            KeyPerm::UseDevId,
+            KeyPerm::ReqForcedOp,
+            KeyPerm::GenUniqueId,
+            KeyPerm::Grant,
+            KeyPerm::GetInfo,
+            KeyPerm::Rebind,
+            KeyPerm::Update,
+            KeyPerm::Use // Test if the macro accepts missing comma at the end of the list.
         ];
         let mut i = v.into_iter();
-        assert_eq!(i.next().unwrap().to_selinux(), "delete");
-        assert_eq!(i.next().unwrap().to_selinux(), "gen_unique_id");
-        assert_eq!(i.next().unwrap().to_selinux(), "get_info");
-        assert_eq!(i.next().unwrap().to_selinux(), "grant");
-        assert_eq!(i.next().unwrap().to_selinux(), "manage_blob");
-        assert_eq!(i.next().unwrap().to_selinux(), "rebind");
-        assert_eq!(i.next().unwrap().to_selinux(), "req_forced_op");
-        assert_eq!(i.next().unwrap().to_selinux(), "update");
-        assert_eq!(i.next().unwrap().to_selinux(), "use");
-        assert_eq!(i.next().unwrap().to_selinux(), "use_dev_id");
+        assert_eq!(i.next().unwrap().name(), "delete");
+        assert_eq!(i.next().unwrap().name(), "gen_unique_id");
+        assert_eq!(i.next().unwrap().name(), "get_info");
+        assert_eq!(i.next().unwrap().name(), "grant");
+        assert_eq!(i.next().unwrap().name(), "manage_blob");
+        assert_eq!(i.next().unwrap().name(), "rebind");
+        assert_eq!(i.next().unwrap().name(), "req_forced_op");
+        assert_eq!(i.next().unwrap().name(), "update");
+        assert_eq!(i.next().unwrap().name(), "use");
+        assert_eq!(i.next().unwrap().name(), "use_dev_id");
         assert_eq!(None, i.next());
     }
     #[test]
     fn key_perm_set_sparse_test() {
         let v = key_perm_set![
-            KeyPerm::manage_blob(),
-            KeyPerm::req_forced_op(),
-            KeyPerm::gen_unique_id(),
-            KeyPerm::update(),
-            KeyPerm::use_(), // Test if macro accepts the comma at the end of the list.
+            KeyPerm::ManageBlob,
+            KeyPerm::ReqForcedOp,
+            KeyPerm::GenUniqueId,
+            KeyPerm::Update,
+            KeyPerm::Use, // Test if macro accepts the comma at the end of the list.
         ];
         let mut i = v.into_iter();
-        assert_eq!(i.next().unwrap().to_selinux(), "gen_unique_id");
-        assert_eq!(i.next().unwrap().to_selinux(), "manage_blob");
-        assert_eq!(i.next().unwrap().to_selinux(), "req_forced_op");
-        assert_eq!(i.next().unwrap().to_selinux(), "update");
-        assert_eq!(i.next().unwrap().to_selinux(), "use");
+        assert_eq!(i.next().unwrap().name(), "gen_unique_id");
+        assert_eq!(i.next().unwrap().name(), "manage_blob");
+        assert_eq!(i.next().unwrap().name(), "req_forced_op");
+        assert_eq!(i.next().unwrap().name(), "update");
+        assert_eq!(i.next().unwrap().name(), "use");
         assert_eq!(None, i.next());
     }
     #[test]
@@ -990,23 +761,23 @@
     #[test]
     fn key_perm_set_include_subset_test() {
         let v1 = key_perm_set![
-            KeyPerm::manage_blob(),
-            KeyPerm::delete(),
-            KeyPerm::use_dev_id(),
-            KeyPerm::req_forced_op(),
-            KeyPerm::gen_unique_id(),
-            KeyPerm::grant(),
-            KeyPerm::get_info(),
-            KeyPerm::rebind(),
-            KeyPerm::update(),
-            KeyPerm::use_(),
+            KeyPerm::ManageBlob,
+            KeyPerm::Delete,
+            KeyPerm::UseDevId,
+            KeyPerm::ReqForcedOp,
+            KeyPerm::GenUniqueId,
+            KeyPerm::Grant,
+            KeyPerm::GetInfo,
+            KeyPerm::Rebind,
+            KeyPerm::Update,
+            KeyPerm::Use,
         ];
         let v2 = key_perm_set![
-            KeyPerm::manage_blob(),
-            KeyPerm::delete(),
-            KeyPerm::rebind(),
-            KeyPerm::update(),
-            KeyPerm::use_(),
+            KeyPerm::ManageBlob,
+            KeyPerm::Delete,
+            KeyPerm::Rebind,
+            KeyPerm::Update,
+            KeyPerm::Use,
         ];
         assert!(v1.includes(v2));
         assert!(!v2.includes(v1));
@@ -1014,18 +785,18 @@
     #[test]
     fn key_perm_set_include_equal_test() {
         let v1 = key_perm_set![
-            KeyPerm::manage_blob(),
-            KeyPerm::delete(),
-            KeyPerm::rebind(),
-            KeyPerm::update(),
-            KeyPerm::use_(),
+            KeyPerm::ManageBlob,
+            KeyPerm::Delete,
+            KeyPerm::Rebind,
+            KeyPerm::Update,
+            KeyPerm::Use,
         ];
         let v2 = key_perm_set![
-            KeyPerm::manage_blob(),
-            KeyPerm::delete(),
-            KeyPerm::rebind(),
-            KeyPerm::update(),
-            KeyPerm::use_(),
+            KeyPerm::ManageBlob,
+            KeyPerm::Delete,
+            KeyPerm::Rebind,
+            KeyPerm::Update,
+            KeyPerm::Use,
         ];
         assert!(v1.includes(v2));
         assert!(v2.includes(v1));
@@ -1033,33 +804,29 @@
     #[test]
     fn key_perm_set_include_overlap_test() {
         let v1 = key_perm_set![
-            KeyPerm::manage_blob(),
-            KeyPerm::delete(),
-            KeyPerm::grant(), // only in v1
-            KeyPerm::rebind(),
-            KeyPerm::update(),
-            KeyPerm::use_(),
+            KeyPerm::ManageBlob,
+            KeyPerm::Delete,
+            KeyPerm::Grant, // only in v1
+            KeyPerm::Rebind,
+            KeyPerm::Update,
+            KeyPerm::Use,
         ];
         let v2 = key_perm_set![
-            KeyPerm::manage_blob(),
-            KeyPerm::delete(),
-            KeyPerm::req_forced_op(), // only in v2
-            KeyPerm::rebind(),
-            KeyPerm::update(),
-            KeyPerm::use_(),
+            KeyPerm::ManageBlob,
+            KeyPerm::Delete,
+            KeyPerm::ReqForcedOp, // only in v2
+            KeyPerm::Rebind,
+            KeyPerm::Update,
+            KeyPerm::Use,
         ];
         assert!(!v1.includes(v2));
         assert!(!v2.includes(v1));
     }
     #[test]
     fn key_perm_set_include_no_overlap_test() {
-        let v1 = key_perm_set![KeyPerm::manage_blob(), KeyPerm::delete(), KeyPerm::grant(),];
-        let v2 = key_perm_set![
-            KeyPerm::req_forced_op(),
-            KeyPerm::rebind(),
-            KeyPerm::update(),
-            KeyPerm::use_(),
-        ];
+        let v1 = key_perm_set![KeyPerm::ManageBlob, KeyPerm::Delete, KeyPerm::Grant,];
+        let v2 =
+            key_perm_set![KeyPerm::ReqForcedOp, KeyPerm::Rebind, KeyPerm::Update, KeyPerm::Use,];
         assert!(!v1.includes(v2));
         assert!(!v2.includes(v1));
     }
diff --git a/keystore2/src/raw_device.rs b/keystore2/src/raw_device.rs
index cd54915..4ce9dce 100644
--- a/keystore2/src/raw_device.rs
+++ b/keystore2/src/raw_device.rs
@@ -16,8 +16,9 @@
 
 use crate::{
     database::{
-        BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, KeyEntry, KeyEntryLoadBits,
-        KeyIdGuard, KeyMetaData, KeyMetaEntry, KeyType, KeystoreDB, SubComponentType, Uuid,
+        BlobInfo, BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, KeyEntry,
+        KeyEntryLoadBits, KeyIdGuard, KeyMetaData, KeyMetaEntry, KeyType, KeystoreDB,
+        SubComponentType, Uuid,
     },
     error::{map_km_error, Error, ErrorCode},
     globals::get_keymint_device,
@@ -59,14 +60,16 @@
     pub const KEY_MASTER_V4_1: i32 = 41;
     /// Version number of KeyMintDevice@V1
     pub const KEY_MINT_V1: i32 = 100;
+    /// Version number of KeyMintDevice@V2
+    pub const KEY_MINT_V2: i32 = 200;
 
     /// Get a [`KeyMintDevice`] for the given [`SecurityLevel`]
     pub fn get(security_level: SecurityLevel) -> Result<KeyMintDevice> {
-        let (asp, hw_info, km_uuid) = get_keymint_device(&security_level)
+        let (km_dev, hw_info, km_uuid) = get_keymint_device(&security_level)
             .context("In KeyMintDevice::get: get_keymint_device failed")?;
 
         Ok(KeyMintDevice {
-            km_dev: asp.get_interface()?,
+            km_dev,
             km_uuid,
             version: hw_info.versionNumber,
             security_level: hw_info.securityLevel,
@@ -120,10 +123,10 @@
         blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
 
         db.store_new_key(
-            &key_desc,
+            key_desc,
             key_type,
             &key_parameters,
-            &(&creation_result.keyBlob, &blob_metadata),
+            &BlobInfo::new(&creation_result.keyBlob, &blob_metadata),
             &CertificateInfo::new(None, None),
             &key_metadata,
             &self.km_uuid,
@@ -148,7 +151,7 @@
         key_desc: &KeyDescriptor,
         key_type: KeyType,
     ) -> Result<(KeyIdGuard, KeyEntry)> {
-        db.load_key_entry(&key_desc, key_type, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| Ok(()))
+        db.load_key_entry(key_desc, key_type, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| Ok(()))
             .context("In lookup_from_desc: load_key_entry failed.")
     }
 
@@ -228,8 +231,8 @@
             };
         }
 
-        self.create_and_store_key(db, &key_desc, key_type, |km_dev| {
-            km_dev.generateKey(&params, None)
+        self.create_and_store_key(db, key_desc, key_type, |km_dev| {
+            km_dev.generateKey(params, None)
         })
         .context("In lookup_or_generate_key: generate_and_store_key failed")?;
         Self::lookup_from_desc(db, key_desc, key_type)
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index 212bf39..be23ae5 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -30,21 +30,27 @@
 };
 use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
     AttestationPoolStatus::AttestationPoolStatus, IRemoteProvisioning::BnRemoteProvisioning,
-    IRemoteProvisioning::IRemoteProvisioning, ImplInfo::ImplInfo,
+    IRemoteProvisioning::IRemoteProvisioning,
+    IRemotelyProvisionedKeyPool::BnRemotelyProvisionedKeyPool,
+    IRemotelyProvisionedKeyPool::IRemotelyProvisionedKeyPool, ImplInfo::ImplInfo,
+    RemotelyProvisionedKey::RemotelyProvisionedKey,
 };
 use android_security_remoteprovisioning::binder::{BinderFeatures, Strong};
 use android_system_keystore2::aidl::android::system::keystore2::{
-    Domain::Domain, KeyDescriptor::KeyDescriptor,
+    Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
 };
 use anyhow::{Context, Result};
 use keystore2_crypto::parse_subject_from_certificate;
+use serde_cbor::Value;
+use std::collections::BTreeMap;
 use std::sync::atomic::{AtomicBool, Ordering};
 
-use crate::database::{CertificateChain, KeystoreDB, Uuid};
+use crate::database::{CertificateChain, KeyIdGuard, KeystoreDB, Uuid};
 use crate::error::{self, map_or_log_err, map_rem_prov_error, Error};
 use crate::globals::{get_keymint_device, get_remotely_provisioned_component, DB};
 use crate::metrics_store::log_rkp_error_stats;
-use crate::utils::{watchdog as wd, Asp};
+use crate::permission::KeystorePerm;
+use crate::utils::{check_keystore_permission, watchdog as wd};
 use android_security_metrics::aidl::android::security::metrics::RkpError::RkpError as MetricsRkpError;
 
 /// Contains helper functions to check if remote provisioning is enabled on the system and, if so,
@@ -56,12 +62,22 @@
     is_hal_present: AtomicBool,
 }
 
+static COSE_KEY_XCOORD: Value = Value::Integer(-2);
+static COSE_KEY_YCOORD: Value = Value::Integer(-3);
+static COSE_MAC0_LEN: usize = 4;
+static COSE_MAC0_PAYLOAD: usize = 2;
+
 impl RemProvState {
     /// Creates a RemProvState struct.
     pub fn new(security_level: SecurityLevel, km_uuid: Uuid) -> Self {
         Self { security_level, km_uuid, is_hal_present: AtomicBool::new(true) }
     }
 
+    /// Returns the uuid for the KM instance attached to this RemProvState struct.
+    pub fn get_uuid(&self) -> Uuid {
+        self.km_uuid
+    }
+
     /// Checks if remote provisioning is enabled and partially caches the result. On a hybrid system
     /// remote provisioning can flip from being disabled to enabled depending on responses from the
     /// server, so unfortunately caching the presence or absence of the HAL is not enough to fully
@@ -83,70 +99,6 @@
         Ok(pool_status.total != 0)
     }
 
-    /// Fetches a remote provisioning attestation key and certificate chain inside of the
-    /// returned `CertificateChain` struct if one exists for the given caller_uid. If one has not
-    /// been assigned, this function will assign it. If there are no signed attestation keys
-    /// available to be assigned, it will return the ResponseCode `OUT_OF_KEYS`
-    fn get_rem_prov_attest_key(
-        &self,
-        key: &KeyDescriptor,
-        caller_uid: u32,
-        db: &mut KeystoreDB,
-    ) -> Result<Option<CertificateChain>> {
-        match key.domain {
-            Domain::APP => {
-                // Attempt to get an Attestation Key once. If it fails, then the app doesn't
-                // have a valid chain assigned to it. The helper function will return None after
-                // attempting to assign a key. An error will be thrown if the pool is simply out
-                // of usable keys. Then another attempt to fetch the just-assigned key will be
-                // made. If this fails too, something is very wrong.
-                self.get_rem_prov_attest_key_helper(key, caller_uid, db)
-                    .context("In get_rem_prov_attest_key: Failed to get a key")?
-                    .map_or_else(
-                        || self.get_rem_prov_attest_key_helper(key, caller_uid, db),
-                        |v| Ok(Some(v)),
-                    )
-                    .context(concat!(
-                        "In get_rem_prov_attest_key: Failed to get a key after",
-                        "attempting to assign one."
-                    ))?
-                    .map_or_else(
-                        || {
-                            Err(Error::sys()).context(concat!(
-                                "In get_rem_prov_attest_key: Attempted to assign a ",
-                                "key and failed silently. Something is very wrong."
-                            ))
-                        },
-                        |cert_chain| Ok(Some(cert_chain)),
-                    )
-            }
-            _ => Ok(None),
-        }
-    }
-
-    /// Returns None if an AttestationKey fails to be assigned. Errors if no keys are available.
-    fn get_rem_prov_attest_key_helper(
-        &self,
-        key: &KeyDescriptor,
-        caller_uid: u32,
-        db: &mut KeystoreDB,
-    ) -> Result<Option<CertificateChain>> {
-        let cert_chain = db
-            .retrieve_attestation_key_and_cert_chain(key.domain, caller_uid as i64, &self.km_uuid)
-            .context("In get_rem_prov_attest_key_helper: Failed to retrieve a key + cert chain")?;
-        match cert_chain {
-            Some(cert_chain) => Ok(Some(cert_chain)),
-            // Either this app needs to be assigned a key, or the pool is empty. An error will
-            // be thrown if there is no key available to assign. This will indicate that the app
-            // should be nudged to provision more keys so keystore can retry.
-            None => {
-                db.assign_attestation_key(key.domain, caller_uid as i64, &self.km_uuid)
-                    .context("In get_rem_prov_attest_key_helper: Failed to assign a key")?;
-                Ok(None)
-            }
-        }
-    }
-
     fn is_asymmetric_key(&self, params: &[KeyParameter]) -> bool {
         params.iter().any(|kp| {
             matches!(
@@ -174,7 +126,7 @@
         caller_uid: u32,
         params: &[KeyParameter],
         db: &mut KeystoreDB,
-    ) -> Result<Option<(AttestationKey, Certificate)>> {
+    ) -> Result<Option<(KeyIdGuard, 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
@@ -182,7 +134,7 @@
             // and therefore will not be attested.
             Ok(None)
         } else {
-            match self.get_rem_prov_attest_key(&key, caller_uid, db) {
+            match get_rem_prov_attest_key(key.domain, caller_uid, db, &self.km_uuid) {
                 Err(e) => {
                     log::error!(
                         concat!(
@@ -195,7 +147,8 @@
                     Ok(None)
                 }
                 Ok(v) => match v {
-                    Some(cert_chain) => Ok(Some((
+                    Some((guard, cert_chain)) => Ok(Some((
+                        guard,
                         AttestationKey {
                             keyBlob: cert_chain.private_key.to_vec(),
                             attestKeyParams: vec![],
@@ -218,7 +171,7 @@
 /// Implementation of the IRemoteProvisioning service.
 #[derive(Default)]
 pub struct RemoteProvisioningService {
-    device_by_sec_level: HashMap<SecurityLevel, Asp>,
+    device_by_sec_level: HashMap<SecurityLevel, Strong<dyn IRemotelyProvisionedComponent>>,
     curve_by_sec_level: HashMap<SecurityLevel, i32>,
 }
 
@@ -226,9 +179,9 @@
     fn get_dev_by_sec_level(
         &self,
         sec_level: &SecurityLevel,
-    ) -> Result<Strong<dyn IRemotelyProvisionedComponent>> {
+    ) -> Result<&dyn IRemotelyProvisionedComponent> {
         if let Some(dev) = self.device_by_sec_level.get(sec_level) {
-            dev.get_interface().context("In get_dev_by_sec_level.")
+            Ok(dev.as_ref())
         } else {
             Err(error::Error::sys()).context(concat!(
                 "In get_dev_by_sec_level: Remote instance for requested security level",
@@ -242,21 +195,17 @@
         let mut result: Self = Default::default();
         let dev = get_remotely_provisioned_component(&SecurityLevel::TRUSTED_ENVIRONMENT)
             .context("In new_native_binder: Failed to get TEE Remote Provisioner instance.")?;
-        let rkp_tee_dev: Strong<dyn IRemotelyProvisionedComponent> = dev.get_interface()?;
         result.curve_by_sec_level.insert(
             SecurityLevel::TRUSTED_ENVIRONMENT,
-            rkp_tee_dev
-                .getHardwareInfo()
+            dev.getHardwareInfo()
                 .context("In new_native_binder: Failed to get hardware info for the TEE.")?
                 .supportedEekCurve,
         );
         result.device_by_sec_level.insert(SecurityLevel::TRUSTED_ENVIRONMENT, dev);
         if let Ok(dev) = get_remotely_provisioned_component(&SecurityLevel::STRONGBOX) {
-            let rkp_sb_dev: Strong<dyn IRemotelyProvisionedComponent> = dev.get_interface()?;
             result.curve_by_sec_level.insert(
                 SecurityLevel::STRONGBOX,
-                rkp_sb_dev
-                    .getHardwareInfo()
+                dev.getHardwareInfo()
                     .context("In new_native_binder: Failed to get hardware info for StrongBox.")?
                     .supportedEekCurve,
             );
@@ -265,6 +214,27 @@
         Ok(BnRemoteProvisioning::new_binder(result, BinderFeatures::default()))
     }
 
+    fn extract_payload_from_cose_mac(data: &[u8]) -> Result<Value> {
+        let cose_mac0: Vec<Value> = serde_cbor::from_slice(data).context(
+            "In extract_payload_from_cose_mac: COSE_Mac0 returned from IRPC cannot be parsed",
+        )?;
+        if cose_mac0.len() != COSE_MAC0_LEN {
+            return Err(error::Error::sys()).context(format!(
+                "In extract_payload_from_cose_mac: COSE_Mac0 has improper length. \
+                    Expected: {}, Actual: {}",
+                COSE_MAC0_LEN,
+                cose_mac0.len(),
+            ));
+        }
+        match &cose_mac0[COSE_MAC0_PAYLOAD] {
+            Value::Bytes(key) => Ok(serde_cbor::from_slice(key)
+                .context("In extract_payload_from_cose_mac: COSE_Mac0 payload is malformed.")?),
+            _ => Err(error::Error::sys()).context(
+                "In extract_payload_from_cose_mac: COSE_Mac0 payload is the wrong type.",
+            )?,
+        }
+    }
+
     /// Generates a CBOR blob which will be assembled by the calling code into a larger
     /// CBOR blob intended for delivery to a provisioning serever. This blob will contain
     /// `num_csr` certificate signing requests for attestation keys generated in the TEE,
@@ -294,7 +264,7 @@
                 .map(|key| MacedPublicKey { macedKey: key.to_vec() })
                 .collect())
         })?;
-        let mut mac = map_rem_prov_error(dev.generateCertificateRequest(
+        let mac = map_rem_prov_error(dev.generateCertificateRequest(
             test_mode,
             &keys_to_sign,
             eek,
@@ -303,30 +273,16 @@
             protected_data,
         ))
         .context("In generate_csr: Failed to generate csr")?;
-        // TODO(b/180392379): Replace this manual CBOR generation with the cbor-serde crate as well.
-        //                    This generates an array consisting of the mac and the public key Maps.
-        //                    Just generate the actual MacedPublicKeys structure when the crate is
-        //                    available.
-        let mut cose_mac_0: Vec<u8> = vec![
-            (0b100_00000 | (keys_to_sign.len() + 1)) as u8,
-            0b010_11000, // mac
-            (mac.len() as u8),
-        ];
-        cose_mac_0.append(&mut mac);
-        // If this is a test mode key, there is an extra 6 bytes added as an additional entry in
-        // the COSE_Key struct to denote that.
-        let test_mode_entry_shift = if test_mode { 0 } else { 6 };
-        let byte_dist_mac0_payload = 8;
-        let cose_key_size = 83 - test_mode_entry_shift;
+        let mut mac_and_keys: Vec<Value> = vec![Value::from(mac)];
         for maced_public_key in keys_to_sign {
-            if maced_public_key.macedKey.len() > cose_key_size + byte_dist_mac0_payload {
-                cose_mac_0.extend_from_slice(
-                    &maced_public_key.macedKey
-                        [byte_dist_mac0_payload..cose_key_size + byte_dist_mac0_payload],
-                );
-            }
+            mac_and_keys.push(
+                Self::extract_payload_from_cose_mac(&maced_public_key.macedKey)
+                    .context("In generate_csr: Failed to get the payload from the COSE_Mac0")?,
+            )
         }
-        Ok(cose_mac_0)
+        let cbor_array: Value = Value::Array(mac_and_keys);
+        serde_cbor::to_vec(&cbor_array)
+            .context("In generate_csr: Failed to serialize the mac and keys array")
     }
 
     /// Provisions a certificate chain for a key whose CSR was included in generate_csr. The
@@ -336,52 +292,106 @@
     /// here.
     pub fn provision_cert_chain(
         &self,
+        db: &mut KeystoreDB,
         public_key: &[u8],
         batch_cert: &[u8],
         certs: &[u8],
         expiration_date: i64,
         sec_level: SecurityLevel,
     ) -> Result<()> {
-        DB.with::<_, Result<()>>(|db| {
-            let mut db = db.borrow_mut();
-            let (_, _, uuid) = get_keymint_device(&sec_level)?;
-            db.store_signed_attestation_certificate_chain(
-                public_key,
-                batch_cert,
-                certs, /* DER encoded certificate chain */
-                expiration_date,
-                &uuid,
-            )
-        })
+        let (_, _, uuid) = get_keymint_device(&sec_level)?;
+        db.store_signed_attestation_certificate_chain(
+            public_key,
+            batch_cert,
+            certs, /* DER encoded certificate chain */
+            expiration_date,
+            &uuid,
+        )
+    }
+
+    fn parse_cose_mac0_for_coords(data: &[u8]) -> Result<Vec<u8>> {
+        let cose_mac0: Vec<Value> = serde_cbor::from_slice(data).context(
+            "In parse_cose_mac0_for_coords: COSE_Mac0 returned from IRPC cannot be parsed",
+        )?;
+        if cose_mac0.len() != COSE_MAC0_LEN {
+            return Err(error::Error::sys()).context(format!(
+                "In parse_cose_mac0_for_coords: COSE_Mac0 has improper length. \
+                    Expected: {}, Actual: {}",
+                COSE_MAC0_LEN,
+                cose_mac0.len(),
+            ));
+        }
+        let cose_key: BTreeMap<Value, Value> = match &cose_mac0[COSE_MAC0_PAYLOAD] {
+            Value::Bytes(key) => serde_cbor::from_slice(key)
+                .context("In parse_cose_mac0_for_coords: COSE_Key is malformed.")?,
+            _ => Err(error::Error::sys())
+                .context("In parse_cose_mac0_for_coords: COSE_Mac0 payload is the wrong type.")?,
+        };
+        if !cose_key.contains_key(&COSE_KEY_XCOORD) || !cose_key.contains_key(&COSE_KEY_YCOORD) {
+            return Err(error::Error::sys()).context(
+                "In parse_cose_mac0_for_coords: \
+                COSE_Key returned from IRPC is lacking required fields",
+            );
+        }
+        let mut raw_key: Vec<u8> = vec![0; 64];
+        match &cose_key[&COSE_KEY_XCOORD] {
+            Value::Bytes(x_coord) if x_coord.len() == 32 => {
+                raw_key[0..32].clone_from_slice(x_coord)
+            }
+            Value::Bytes(x_coord) => {
+                return Err(error::Error::sys()).context(format!(
+                "In parse_cose_mac0_for_coords: COSE_Key X-coordinate is not the right length. \
+                Expected: 32; Actual: {}",
+                    x_coord.len()
+                ))
+            }
+            _ => {
+                return Err(error::Error::sys())
+                    .context("In parse_cose_mac0_for_coords: COSE_Key X-coordinate is not a bstr")
+            }
+        }
+        match &cose_key[&COSE_KEY_YCOORD] {
+            Value::Bytes(y_coord) if y_coord.len() == 32 => {
+                raw_key[32..64].clone_from_slice(y_coord)
+            }
+            Value::Bytes(y_coord) => {
+                return Err(error::Error::sys()).context(format!(
+                "In parse_cose_mac0_for_coords: COSE_Key Y-coordinate is not the right length. \
+                Expected: 32; Actual: {}",
+                    y_coord.len()
+                ))
+            }
+            _ => {
+                return Err(error::Error::sys())
+                    .context("In parse_cose_mac0_for_coords: COSE_Key Y-coordinate is not a bstr")
+            }
+        }
+        Ok(raw_key)
     }
 
     /// Submits a request to the Remote Provisioner HAL to generate a signing key pair.
     /// `is_test_mode` indicates whether or not the returned public key should be marked as being
     /// for testing in order to differentiate them from private keys. If the call is successful,
     /// the key pair is then added to the database.
-    pub fn generate_key_pair(&self, is_test_mode: bool, sec_level: SecurityLevel) -> Result<()> {
+    pub fn generate_key_pair(
+        &self,
+        db: &mut KeystoreDB,
+        is_test_mode: bool,
+        sec_level: SecurityLevel,
+    ) -> Result<()> {
         let (_, _, uuid) = get_keymint_device(&sec_level)?;
-        let dev = self.get_dev_by_sec_level(&sec_level)?;
+        let dev = self.get_dev_by_sec_level(&sec_level).context(format!(
+            "In generate_key_pair: Failed to get device for security level {:?}",
+            sec_level
+        ))?;
         let mut maced_key = MacedPublicKey { macedKey: Vec::new() };
         let priv_key =
             map_rem_prov_error(dev.generateEcdsaP256KeyPair(is_test_mode, &mut maced_key))
                 .context("In generate_key_pair: Failed to generated ECDSA keypair.")?;
-        // TODO(b/180392379): This is a brittle hack that relies on the consistent formatting of
-        //                    the returned CBOR blob in order to extract the public key.
-        let data = &maced_key.macedKey;
-        if data.len() < 85 {
-            return Err(error::Error::sys()).context(concat!(
-                "In generate_key_pair: CBOR blob returned from",
-                "RemotelyProvisionedComponent is definitely malformatted or empty."
-            ));
-        }
-        let mut raw_key: Vec<u8> = vec![0; 64];
-        raw_key[0..32].clone_from_slice(&data[18..18 + 32]);
-        raw_key[32..64].clone_from_slice(&data[53..53 + 32]);
-        DB.with::<_, Result<()>>(|db| {
-            let mut db = db.borrow_mut();
-            db.create_attestation_key_entry(&maced_key.macedKey, &raw_key, &priv_key, &uuid)
-        })
+        let raw_key = Self::parse_cose_mac0_for_coords(&maced_key.macedKey)
+            .context("In generate_key_pair: Failed to parse raw key")?;
+        db.create_attestation_key_entry(&maced_key.macedKey, &raw_key, &priv_key, &uuid)
+            .context("In generate_key_pair: Failed to insert attestation key entry")
     }
 
     /// Checks the security level of each available IRemotelyProvisionedComponent hal and returns
@@ -420,6 +430,70 @@
     })
 }
 
+/// Fetches a remote provisioning attestation key and certificate chain inside of the
+/// returned `CertificateChain` struct if one exists for the given caller_uid. If one has not
+/// been assigned, this function will assign it. If there are no signed attestation keys
+/// available to be assigned, it will return the ResponseCode `OUT_OF_KEYS`
+fn get_rem_prov_attest_key(
+    domain: Domain,
+    caller_uid: u32,
+    db: &mut KeystoreDB,
+    km_uuid: &Uuid,
+) -> Result<Option<(KeyIdGuard, CertificateChain)>> {
+    match domain {
+        Domain::APP => {
+            // Attempt to get an Attestation Key once. If it fails, then the app doesn't
+            // have a valid chain assigned to it. The helper function will return None after
+            // attempting to assign a key. An error will be thrown if the pool is simply out
+            // of usable keys. Then another attempt to fetch the just-assigned key will be
+            // made. If this fails too, something is very wrong.
+            get_rem_prov_attest_key_helper(domain, caller_uid, db, km_uuid)
+                .context("In get_rem_prov_attest_key: Failed to get a key")?
+                .map_or_else(
+                    || get_rem_prov_attest_key_helper(domain, caller_uid, db, km_uuid),
+                    |v| Ok(Some(v)),
+                )
+                .context(concat!(
+                    "In get_rem_prov_attest_key: Failed to get a key after",
+                    "attempting to assign one."
+                ))?
+                .map_or_else(
+                    || {
+                        Err(Error::sys()).context(concat!(
+                            "In get_rem_prov_attest_key: Attempted to assign a ",
+                            "key and failed silently. Something is very wrong."
+                        ))
+                    },
+                    |(guard, cert_chain)| Ok(Some((guard, cert_chain))),
+                )
+        }
+        _ => Ok(None),
+    }
+}
+
+/// Returns None if an AttestationKey fails to be assigned. Errors if no keys are available.
+fn get_rem_prov_attest_key_helper(
+    domain: Domain,
+    caller_uid: u32,
+    db: &mut KeystoreDB,
+    km_uuid: &Uuid,
+) -> Result<Option<(KeyIdGuard, CertificateChain)>> {
+    let guard_and_chain = db
+        .retrieve_attestation_key_and_cert_chain(domain, caller_uid as i64, km_uuid)
+        .context("In get_rem_prov_attest_key_helper: Failed to retrieve a key + cert chain")?;
+    match guard_and_chain {
+        Some((guard, cert_chain)) => Ok(Some((guard, cert_chain))),
+        // Either this app needs to be assigned a key, or the pool is empty. An error will
+        // be thrown if there is no key available to assign. This will indicate that the app
+        // should be nudged to provision more keys so keystore can retry.
+        None => {
+            db.assign_attestation_key(domain, caller_uid as i64, km_uuid)
+                .context("In get_rem_prov_attest_key_helper: Failed to assign a key")?;
+            Ok(None)
+        }
+    }
+}
+
 impl binder::Interface for RemoteProvisioningService {}
 
 // Implementation of IRemoteProvisioning. See AIDL spec at
@@ -429,7 +503,7 @@
         &self,
         expired_by: i64,
         sec_level: SecurityLevel,
-    ) -> binder::public_api::Result<AttestationPoolStatus> {
+    ) -> binder::Result<AttestationPoolStatus> {
         let _wp = wd::watch_millis("IRemoteProvisioning::getPoolStatus", 500);
         map_or_log_err(get_pool_status(expired_by, sec_level), Ok)
     }
@@ -443,7 +517,7 @@
         sec_level: SecurityLevel,
         protected_data: &mut ProtectedData,
         device_info: &mut DeviceInfo,
-    ) -> binder::public_api::Result<Vec<u8>> {
+    ) -> binder::Result<Vec<u8>> {
         let _wp = wd::watch_millis("IRemoteProvisioning::generateCsr", 500);
         map_or_log_err(
             self.generate_csr(
@@ -466,30 +540,516 @@
         certs: &[u8],
         expiration_date: i64,
         sec_level: SecurityLevel,
-    ) -> binder::public_api::Result<()> {
+    ) -> binder::Result<()> {
         let _wp = wd::watch_millis("IRemoteProvisioning::provisionCertChain", 500);
-        map_or_log_err(
-            self.provision_cert_chain(public_key, batch_cert, certs, expiration_date, sec_level),
-            Ok,
-        )
+        DB.with::<_, binder::Result<()>>(|db| {
+            map_or_log_err(
+                self.provision_cert_chain(
+                    &mut db.borrow_mut(),
+                    public_key,
+                    batch_cert,
+                    certs,
+                    expiration_date,
+                    sec_level,
+                ),
+                Ok,
+            )
+        })
     }
 
-    fn generateKeyPair(
-        &self,
-        is_test_mode: bool,
-        sec_level: SecurityLevel,
-    ) -> binder::public_api::Result<()> {
+    fn generateKeyPair(&self, is_test_mode: bool, sec_level: SecurityLevel) -> binder::Result<()> {
         let _wp = wd::watch_millis("IRemoteProvisioning::generateKeyPair", 500);
-        map_or_log_err(self.generate_key_pair(is_test_mode, sec_level), Ok)
+        DB.with::<_, binder::Result<()>>(|db| {
+            map_or_log_err(
+                self.generate_key_pair(&mut db.borrow_mut(), is_test_mode, sec_level),
+                Ok,
+            )
+        })
     }
 
-    fn getImplementationInfo(&self) -> binder::public_api::Result<Vec<ImplInfo>> {
+    fn getImplementationInfo(&self) -> binder::Result<Vec<ImplInfo>> {
         let _wp = wd::watch_millis("IRemoteProvisioning::getSecurityLevels", 500);
         map_or_log_err(self.get_implementation_info(), Ok)
     }
 
-    fn deleteAllKeys(&self) -> binder::public_api::Result<i64> {
+    fn deleteAllKeys(&self) -> binder::Result<i64> {
         let _wp = wd::watch_millis("IRemoteProvisioning::deleteAllKeys", 500);
         map_or_log_err(self.delete_all_keys(), Ok)
     }
 }
+
+/// Implementation of the IRemotelyProvisionedKeyPool service.
+#[derive(Default)]
+pub struct RemotelyProvisionedKeyPoolService {
+    unique_id_to_sec_level: HashMap<String, SecurityLevel>,
+}
+
+impl RemotelyProvisionedKeyPoolService {
+    /// Fetches a remotely provisioned certificate chain and key for the given client uid that
+    /// was provisioned using the IRemotelyProvisionedComponent with the given id. The same key
+    /// will be returned for a given caller_uid on every request. If there are no attestation keys
+    /// available, `OUT_OF_KEYS` is returned.
+    fn get_attestation_key(
+        &self,
+        db: &mut KeystoreDB,
+        caller_uid: i32,
+        irpc_id: &str,
+    ) -> Result<RemotelyProvisionedKey> {
+        log::info!("get_attestation_key(self, {}, {}", caller_uid, irpc_id);
+
+        let sec_level = self
+            .unique_id_to_sec_level
+            .get(irpc_id)
+            .ok_or(Error::Rc(ResponseCode::INVALID_ARGUMENT))
+            .context(format!("In get_attestation_key: unknown irpc id '{}'", irpc_id))?;
+        let (_, _, km_uuid) = get_keymint_device(sec_level)?;
+
+        let guard_and_cert_chain =
+            get_rem_prov_attest_key(Domain::APP, caller_uid as u32, db, &km_uuid)
+                .context("In get_attestation_key")?;
+        match guard_and_cert_chain {
+            Some((_, chain)) => Ok(RemotelyProvisionedKey {
+                keyBlob: chain.private_key.to_vec(),
+                encodedCertChain: chain.cert_chain,
+            }),
+            // It should be impossible to get `None`, but handle it just in case as a
+            // precaution against future behavioral changes in `get_rem_prov_attest_key`.
+            None => Err(error::Error::Rc(ResponseCode::OUT_OF_KEYS))
+                .context("In get_attestation_key: No available attestation keys"),
+        }
+    }
+
+    /// Creates a new instance of the remotely provisioned key pool service, used for fetching
+    /// remotely provisioned attestation keys.
+    pub fn new_native_binder() -> Result<Strong<dyn IRemotelyProvisionedKeyPool>> {
+        let mut result: Self = Default::default();
+
+        let dev = get_remotely_provisioned_component(&SecurityLevel::TRUSTED_ENVIRONMENT)
+            .context("In new_native_binder: Failed to get TEE Remote Provisioner instance.")?;
+        if let Some(id) = dev.getHardwareInfo()?.uniqueId {
+            result.unique_id_to_sec_level.insert(id, SecurityLevel::TRUSTED_ENVIRONMENT);
+        }
+
+        if let Ok(dev) = get_remotely_provisioned_component(&SecurityLevel::STRONGBOX) {
+            if let Some(id) = dev.getHardwareInfo()?.uniqueId {
+                if result.unique_id_to_sec_level.contains_key(&id) {
+                    anyhow::bail!("In new_native_binder: duplicate irpc id found: '{}'", id)
+                }
+                result.unique_id_to_sec_level.insert(id, SecurityLevel::STRONGBOX);
+            }
+        }
+
+        // If none of the remotely provisioned components have unique ids, then we shouldn't
+        // bother publishing the service, as it's impossible to match keys with their backends.
+        if result.unique_id_to_sec_level.is_empty() {
+            anyhow::bail!(
+                "In new_native_binder: No remotely provisioned components have unique ids"
+            )
+        }
+
+        Ok(BnRemotelyProvisionedKeyPool::new_binder(
+            result,
+            BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+        ))
+    }
+}
+
+impl binder::Interface for RemotelyProvisionedKeyPoolService {}
+
+// Implementation of IRemotelyProvisionedKeyPool. See AIDL spec at
+// :aidl/android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.aidl
+impl IRemotelyProvisionedKeyPool for RemotelyProvisionedKeyPoolService {
+    fn getAttestationKey(
+        &self,
+        caller_uid: i32,
+        irpc_id: &str,
+    ) -> binder::Result<RemotelyProvisionedKey> {
+        let _wp = wd::watch_millis("IRemotelyProvisionedKeyPool::getAttestationKey", 500);
+        map_or_log_err(check_keystore_permission(KeystorePerm::GetAttestationKey), Ok)?;
+        DB.with::<_, binder::Result<RemotelyProvisionedKey>>(|db| {
+            map_or_log_err(self.get_attestation_key(&mut db.borrow_mut(), caller_uid, irpc_id), Ok)
+        })
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use serde_cbor::Value;
+    use std::collections::BTreeMap;
+    use std::sync::{Arc, Mutex};
+    use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+        RpcHardwareInfo::RpcHardwareInfo,
+    };
+
+    #[derive(Default)]
+    struct MockRemotelyProvisionedComponentValues {
+        hw_info: RpcHardwareInfo,
+        private_key: Vec<u8>,
+        maced_public_key: Vec<u8>,
+    }
+
+    // binder::Interface requires the Send trait, so we have to use a Mutex even though the test
+    // is single threaded.
+    #[derive(Default)]
+    struct MockRemotelyProvisionedComponent(Arc<Mutex<MockRemotelyProvisionedComponentValues>>);
+
+    impl binder::Interface for MockRemotelyProvisionedComponent {}
+
+    impl IRemotelyProvisionedComponent for MockRemotelyProvisionedComponent {
+        fn getHardwareInfo(&self) -> binder::Result<RpcHardwareInfo> {
+            Ok(self.0.lock().unwrap().hw_info.clone())
+        }
+
+        fn generateEcdsaP256KeyPair(
+            &self,
+            test_mode: bool,
+            maced_public_key: &mut MacedPublicKey,
+        ) -> binder::Result<Vec<u8>> {
+            assert!(test_mode);
+            maced_public_key.macedKey = self.0.lock().unwrap().maced_public_key.clone();
+            Ok(self.0.lock().unwrap().private_key.clone())
+        }
+
+        fn generateCertificateRequest(
+            &self,
+            _test_mode: bool,
+            _keys_to_sign: &[MacedPublicKey],
+            _eek: &[u8],
+            _challenge: &[u8],
+            _device_info: &mut DeviceInfo,
+            _protected_data: &mut ProtectedData,
+        ) -> binder::Result<Vec<u8>> {
+            Err(binder::StatusCode::INVALID_OPERATION.into())
+        }
+    }
+
+    // Hard coded cert that can be parsed -- the content doesn't matter for testing, only that it's valid.
+    fn get_fake_cert() -> Vec<u8> {
+        vec![
+            0x30, 0x82, 0x01, 0xbb, 0x30, 0x82, 0x01, 0x61, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02,
+            0x14, 0x3a, 0xd5, 0x67, 0xce, 0xfe, 0x93, 0xe1, 0xea, 0xb7, 0xe4, 0xbf, 0x64, 0x19,
+            0xa4, 0x11, 0xe1, 0x87, 0x40, 0x20, 0x37, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48,
+            0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x33, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+            0x04, 0x06, 0x13, 0x02, 0x55, 0x54, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+            0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
+            0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67,
+            0x6c, 0x65, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x32, 0x31, 0x30, 0x32, 0x32,
+            0x30, 0x38, 0x35, 0x32, 0x5a, 0x17, 0x0d, 0x34, 0x39, 0x30, 0x34, 0x32, 0x36, 0x32,
+            0x32, 0x30, 0x38, 0x35, 0x32, 0x5a, 0x30, 0x33, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+            0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x54, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+            0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
+            0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f,
+            0x67, 0x6c, 0x65, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d,
+            0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42,
+            0x00, 0x04, 0x1e, 0xac, 0x0c, 0xe0, 0x0d, 0xc5, 0x25, 0x84, 0x1b, 0xd2, 0x77, 0x2d,
+            0xe7, 0xba, 0xf1, 0xde, 0xa7, 0xf6, 0x39, 0x7f, 0x38, 0x91, 0xbf, 0xa4, 0x58, 0xf5,
+            0x62, 0x6b, 0xce, 0x06, 0xcf, 0xb9, 0x73, 0x91, 0x0d, 0x8a, 0x60, 0xa0, 0xc6, 0xa2,
+            0x22, 0xe6, 0x51, 0x2e, 0x58, 0xd6, 0x43, 0x02, 0x80, 0x43, 0x44, 0x29, 0x38, 0x9a,
+            0x99, 0xf3, 0xa4, 0xdd, 0xd0, 0xb4, 0x6f, 0x8b, 0x44, 0x2d, 0xa3, 0x53, 0x30, 0x51,
+            0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xdb, 0x13, 0x68,
+            0xe0, 0x0e, 0x47, 0x10, 0xf8, 0xcb, 0x88, 0x83, 0xfe, 0x42, 0x3c, 0xd9, 0x3f, 0x1a,
+            0x33, 0xe9, 0xaa, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+            0x80, 0x14, 0xdb, 0x13, 0x68, 0xe0, 0x0e, 0x47, 0x10, 0xf8, 0xcb, 0x88, 0x83, 0xfe,
+            0x42, 0x3c, 0xd9, 0x3f, 0x1a, 0x33, 0xe9, 0xaa, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d,
+            0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0a, 0x06,
+            0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45,
+            0x02, 0x20, 0x10, 0xdf, 0x40, 0xc3, 0x20, 0x54, 0x36, 0xb5, 0xc9, 0x3c, 0x70, 0xe3,
+            0x55, 0x37, 0xd2, 0x04, 0x51, 0xeb, 0x0f, 0x18, 0x83, 0xd0, 0x58, 0xa1, 0x08, 0x77,
+            0x8d, 0x4d, 0xa4, 0x20, 0xee, 0x33, 0x02, 0x21, 0x00, 0x8d, 0xe3, 0xa6, 0x6c, 0x0d,
+            0x86, 0x25, 0xdc, 0x59, 0x0d, 0x21, 0x43, 0x22, 0x3a, 0xb9, 0xa1, 0x73, 0x28, 0xc9,
+            0x16, 0x9e, 0x91, 0x15, 0xc4, 0xc3, 0xd7, 0xeb, 0xe5, 0xce, 0xdc, 0x1c, 0x1b,
+        ]
+    }
+
+    // Generate a fake COSE_Mac0 with a key that's just `byte` repeated
+    fn generate_maced_pubkey(byte: u8) -> Vec<u8> {
+        vec![
+            0x84, 0x43, 0xA1, 0x01, 0x05, 0xA0, 0x58, 0x4D, 0xA5, 0x01, 0x02, 0x03, 0x26, 0x20,
+            0x01, 0x21, 0x58, 0x20, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte,
+            byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte,
+            byte, byte, byte, byte, byte, byte, byte, byte, 0x22, 0x58, 0x20, byte, byte, byte,
+            byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte,
+            byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte,
+            byte, 0x58, 0x20, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte,
+            byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte,
+            byte, byte, byte, byte, byte, byte, byte,
+        ]
+    }
+
+    #[test]
+    fn test_parse_cose_mac0_for_coords_raw_bytes() -> Result<()> {
+        let cose_mac0: Vec<u8> = vec![
+            0x84, 0x01, 0x02, 0x58, 0x4D, 0xA5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58,
+            0x20, 0x1A, 0xFB, 0xB2, 0xD9, 0x9D, 0xF6, 0x2D, 0xF0, 0xC3, 0xA8, 0xFC, 0x7E, 0xC9,
+            0x21, 0x26, 0xED, 0xB5, 0x4A, 0x98, 0x9B, 0xF3, 0x0D, 0x91, 0x3F, 0xC6, 0x42, 0x5C,
+            0x43, 0x22, 0xC8, 0xEE, 0x03, 0x22, 0x58, 0x20, 0x40, 0xB3, 0x9B, 0xFC, 0x47, 0x95,
+            0x90, 0xA7, 0x5C, 0x5A, 0x16, 0x31, 0x34, 0xAF, 0x0C, 0x5B, 0xF2, 0xB2, 0xD8, 0x2A,
+            0xA3, 0xB3, 0x1A, 0xB4, 0x4C, 0xA6, 0x3B, 0xE7, 0x22, 0xEC, 0x41, 0xDC, 0x03,
+        ];
+        let raw_key = RemoteProvisioningService::parse_cose_mac0_for_coords(&cose_mac0)?;
+        assert_eq!(
+            raw_key,
+            vec![
+                0x1A, 0xFB, 0xB2, 0xD9, 0x9D, 0xF6, 0x2D, 0xF0, 0xC3, 0xA8, 0xFC, 0x7E, 0xC9, 0x21,
+                0x26, 0xED, 0xB5, 0x4A, 0x98, 0x9B, 0xF3, 0x0D, 0x91, 0x3F, 0xC6, 0x42, 0x5C, 0x43,
+                0x22, 0xC8, 0xEE, 0x03, 0x40, 0xB3, 0x9B, 0xFC, 0x47, 0x95, 0x90, 0xA7, 0x5C, 0x5A,
+                0x16, 0x31, 0x34, 0xAF, 0x0C, 0x5B, 0xF2, 0xB2, 0xD8, 0x2A, 0xA3, 0xB3, 0x1A, 0xB4,
+                0x4C, 0xA6, 0x3B, 0xE7, 0x22, 0xEC, 0x41, 0xDC,
+            ]
+        );
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_cose_mac0_for_coords_constructed_mac() -> Result<()> {
+        let x_coord: Vec<u8> = vec![0; 32];
+        let y_coord: Vec<u8> = vec![1; 32];
+        let mut expected_key: Vec<u8> = Vec::new();
+        expected_key.extend(&x_coord);
+        expected_key.extend(&y_coord);
+        let key_map: BTreeMap<Value, Value> = BTreeMap::from([
+            (Value::Integer(1), Value::Integer(2)),
+            (Value::Integer(3), Value::Integer(-7)),
+            (Value::Integer(-1), Value::Integer(1)),
+            (Value::Integer(-2), Value::Bytes(x_coord)),
+            (Value::Integer(-3), Value::Bytes(y_coord)),
+        ]);
+        let cose_mac0: Vec<Value> = vec![
+            Value::Integer(0),
+            Value::Integer(1),
+            Value::from(serde_cbor::to_vec(&key_map)?),
+            Value::Integer(2),
+        ];
+        let raw_key = RemoteProvisioningService::parse_cose_mac0_for_coords(&serde_cbor::to_vec(
+            &Value::from(cose_mac0),
+        )?)?;
+        assert_eq!(expected_key, raw_key);
+        Ok(())
+    }
+
+    #[test]
+    fn test_extract_payload_from_cose_mac() -> Result<()> {
+        let key_map = Value::Map(BTreeMap::from([(Value::Integer(1), Value::Integer(2))]));
+        let payload = Value::Bytes(serde_cbor::to_vec(&key_map)?);
+        let cose_mac0 =
+            Value::Array(vec![Value::Integer(0), Value::Integer(1), payload, Value::Integer(3)]);
+        let extracted_map = RemoteProvisioningService::extract_payload_from_cose_mac(
+            &serde_cbor::to_vec(&cose_mac0)?,
+        )?;
+        assert_eq!(key_map, extracted_map);
+        Ok(())
+    }
+
+    #[test]
+    fn test_extract_payload_from_cose_mac_fails_malformed_payload() -> Result<()> {
+        let payload = Value::Bytes(vec![5; 10]);
+        let cose_mac0 =
+            Value::Array(vec![Value::Integer(0), Value::Integer(1), payload, Value::Integer(3)]);
+        let extracted_payload = RemoteProvisioningService::extract_payload_from_cose_mac(
+            &serde_cbor::to_vec(&cose_mac0)?,
+        );
+        assert!(extracted_payload.is_err());
+        Ok(())
+    }
+
+    #[test]
+    fn test_extract_payload_from_cose_mac_fails_type() -> Result<()> {
+        let payload = Value::Integer(1);
+        let cose_mac0 =
+            Value::Array(vec![Value::Integer(0), Value::Integer(1), payload, Value::Integer(3)]);
+        let extracted_payload = RemoteProvisioningService::extract_payload_from_cose_mac(
+            &serde_cbor::to_vec(&cose_mac0)?,
+        );
+        assert!(extracted_payload.is_err());
+        Ok(())
+    }
+
+    #[test]
+    fn test_extract_payload_from_cose_mac_fails_length() -> Result<()> {
+        let cose_mac0 = Value::Array(vec![Value::Integer(0), Value::Integer(1)]);
+        let extracted_payload = RemoteProvisioningService::extract_payload_from_cose_mac(
+            &serde_cbor::to_vec(&cose_mac0)?,
+        );
+        assert!(extracted_payload.is_err());
+        Ok(())
+    }
+
+    #[test]
+    #[ignore] // b/215746308
+    fn test_get_attestation_key_no_keys_provisioned() {
+        let mut db = crate::database::tests::new_test_db().unwrap();
+        let mock_rpc = Box::<MockRemotelyProvisionedComponent>::default();
+        mock_rpc.0.lock().unwrap().hw_info.uniqueId = Some(String::from("mallory"));
+
+        let mut service: RemotelyProvisionedKeyPoolService = Default::default();
+        service
+            .unique_id_to_sec_level
+            .insert(String::from("mallory"), SecurityLevel::TRUSTED_ENVIRONMENT);
+
+        assert_eq!(
+            service
+                .get_attestation_key(&mut db, 0, "mallory")
+                .unwrap_err()
+                .downcast::<error::Error>()
+                .unwrap(),
+            error::Error::Rc(ResponseCode::OUT_OF_KEYS)
+        );
+    }
+
+    #[test]
+    #[ignore] // b/215746308
+    fn test_get_attestation_key() {
+        let mut db = crate::database::tests::new_test_db().unwrap();
+        let sec_level = SecurityLevel::TRUSTED_ENVIRONMENT;
+        let irpc_id = "paul";
+        let caller_uid = 0;
+
+        let mock_rpc = Box::<MockRemotelyProvisionedComponent>::default();
+        let mock_values = mock_rpc.0.clone();
+        let mut remote_provisioning: RemoteProvisioningService = Default::default();
+        remote_provisioning.device_by_sec_level.insert(sec_level, Strong::new(mock_rpc));
+        let mut key_pool: RemotelyProvisionedKeyPoolService = Default::default();
+        key_pool.unique_id_to_sec_level.insert(String::from(irpc_id), sec_level);
+
+        mock_values.lock().unwrap().hw_info.uniqueId = Some(String::from(irpc_id));
+        mock_values.lock().unwrap().private_key = vec![8, 6, 7, 5, 3, 0, 9];
+        mock_values.lock().unwrap().maced_public_key = generate_maced_pubkey(0x11);
+        remote_provisioning.generate_key_pair(&mut db, true, sec_level).unwrap();
+
+        let public_key = RemoteProvisioningService::parse_cose_mac0_for_coords(
+            mock_values.lock().unwrap().maced_public_key.as_slice(),
+        )
+        .unwrap();
+        let batch_cert = get_fake_cert();
+        let certs = &[5, 6, 7, 8];
+        assert!(remote_provisioning
+            .provision_cert_chain(
+                &mut db,
+                public_key.as_slice(),
+                batch_cert.as_slice(),
+                certs,
+                0,
+                sec_level
+            )
+            .is_ok());
+
+        // ensure we got the key we expected
+        let first_key = key_pool
+            .get_attestation_key(&mut db, caller_uid, irpc_id)
+            .context("get first key")
+            .unwrap();
+        assert_eq!(first_key.keyBlob, mock_values.lock().unwrap().private_key);
+        assert_eq!(first_key.encodedCertChain, certs);
+
+        // ensure that multiple calls get the same key
+        assert_eq!(
+            first_key,
+            key_pool
+                .get_attestation_key(&mut db, caller_uid, irpc_id)
+                .context("get second key")
+                .unwrap()
+        );
+
+        // no more keys for new clients
+        assert_eq!(
+            key_pool
+                .get_attestation_key(&mut db, caller_uid + 1, irpc_id)
+                .unwrap_err()
+                .downcast::<error::Error>()
+                .unwrap(),
+            error::Error::Rc(ResponseCode::OUT_OF_KEYS)
+        );
+    }
+
+    #[test]
+    #[ignore] // b/215746308
+    fn test_get_attestation_key_gets_different_key_for_different_client() {
+        let mut db = crate::database::tests::new_test_db().unwrap();
+        let sec_level = SecurityLevel::TRUSTED_ENVIRONMENT;
+        let irpc_id = "ringo";
+        let first_caller = 0;
+        let second_caller = first_caller + 1;
+
+        let mock_rpc = Box::<MockRemotelyProvisionedComponent>::default();
+        let mock_values = mock_rpc.0.clone();
+        let mut remote_provisioning: RemoteProvisioningService = Default::default();
+        remote_provisioning.device_by_sec_level.insert(sec_level, Strong::new(mock_rpc));
+        let mut key_pool: RemotelyProvisionedKeyPoolService = Default::default();
+        key_pool.unique_id_to_sec_level.insert(String::from(irpc_id), sec_level);
+
+        // generate two distinct keys and provision them with certs
+        mock_values.lock().unwrap().hw_info.uniqueId = Some(String::from(irpc_id));
+        mock_values.lock().unwrap().private_key = vec![3, 1, 4, 1, 5];
+        mock_values.lock().unwrap().maced_public_key = generate_maced_pubkey(0x11);
+        assert!(remote_provisioning.generate_key_pair(&mut db, true, sec_level).is_ok());
+        let public_key = RemoteProvisioningService::parse_cose_mac0_for_coords(
+            mock_values.lock().unwrap().maced_public_key.as_slice(),
+        )
+        .unwrap();
+        assert!(remote_provisioning
+            .provision_cert_chain(
+                &mut db,
+                public_key.as_slice(),
+                get_fake_cert().as_slice(),
+                &[1],
+                0,
+                sec_level
+            )
+            .is_ok());
+
+        mock_values.lock().unwrap().hw_info.uniqueId = Some(String::from(irpc_id));
+        mock_values.lock().unwrap().private_key = vec![9, 0, 2, 1, 0];
+        mock_values.lock().unwrap().maced_public_key = generate_maced_pubkey(0x22);
+        assert!(remote_provisioning.generate_key_pair(&mut db, true, sec_level).is_ok());
+        let public_key = RemoteProvisioningService::parse_cose_mac0_for_coords(
+            mock_values.lock().unwrap().maced_public_key.as_slice(),
+        )
+        .unwrap();
+        assert!(remote_provisioning
+            .provision_cert_chain(
+                &mut db,
+                public_key.as_slice(),
+                get_fake_cert().as_slice(),
+                &[2],
+                0,
+                sec_level
+            )
+            .is_ok());
+
+        // make sure each caller gets a distinct key
+        assert_ne!(
+            key_pool
+                .get_attestation_key(&mut db, first_caller, irpc_id)
+                .context("get first key")
+                .unwrap(),
+            key_pool
+                .get_attestation_key(&mut db, second_caller, irpc_id)
+                .context("get second key")
+                .unwrap()
+        );
+
+        // repeated calls should return the same key for a given caller
+        assert_eq!(
+            key_pool
+                .get_attestation_key(&mut db, first_caller, irpc_id)
+                .context("first caller a")
+                .unwrap(),
+            key_pool
+                .get_attestation_key(&mut db, first_caller, irpc_id)
+                .context("first caller b")
+                .unwrap(),
+        );
+
+        assert_eq!(
+            key_pool
+                .get_attestation_key(&mut db, second_caller, irpc_id)
+                .context("second caller a")
+                .unwrap(),
+            key_pool
+                .get_attestation_key(&mut db, second_caller, irpc_id)
+                .context("second caller b")
+                .unwrap()
+        );
+    }
+}
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 9ba38ec..1f6be32 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -18,7 +18,7 @@
 use crate::audit_log::{
     log_key_deleted, log_key_generated, log_key_imported, log_key_integrity_violation,
 };
-use crate::database::{CertificateInfo, KeyIdGuard};
+use crate::database::{BlobInfo, CertificateInfo, KeyIdGuard};
 use crate::error::{self, map_km_error, map_or_log_err, Error, ErrorCode};
 use crate::globals::{DB, ENFORCEMENTS, LEGACY_IMPORTER, SUPER_KEY};
 use crate::key_parameter::KeyParameter as KsKeyParam;
@@ -28,7 +28,7 @@
 use crate::super_key::{KeyBlob, SuperKeyManager};
 use crate::utils::{
     check_device_attestation_permissions, check_key_permission, is_device_id_attestation_tag,
-    key_characteristics_to_internal, uid_to_android_user, watchdog as wd, Asp,
+    key_characteristics_to_internal, uid_to_android_user, watchdog as wd,
 };
 use crate::{
     database::{
@@ -63,7 +63,7 @@
 /// Implementation of the IKeystoreSecurityLevel Interface.
 pub struct KeystoreSecurityLevel {
     security_level: SecurityLevel,
-    keymint: Asp,
+    keymint: Strong<dyn IKeyMintDevice>,
     hw_info: KeyMintHardwareInfo,
     km_uuid: Uuid,
     operation_db: OperationDb,
@@ -132,8 +132,7 @@
                 _ => Some(
                     certificate_chain
                         .iter()
-                        .map(|c| c.encodedCertificate.iter())
-                        .flatten()
+                        .flat_map(|c| c.encodedCertificate.iter())
                         .copied()
                         .collect(),
                 ),
@@ -160,6 +159,8 @@
                     let mut db = db.borrow_mut();
 
                     let (key_blob, mut blob_metadata) = SUPER_KEY
+                        .read()
+                        .unwrap()
                         .handle_super_encryption_on_key_init(
                             &mut db,
                             &LEGACY_IMPORTER,
@@ -180,7 +181,7 @@
                             &key,
                             KeyType::Client,
                             &key_parameters,
-                            &(&key_blob, &blob_metadata),
+                            &BlobInfo::new(&key_blob, &blob_metadata),
                             &cert_info,
                             &key_metadata,
                             &self.km_uuid,
@@ -218,10 +219,10 @@
         let scoping_blob: Vec<u8>;
         let (km_blob, key_properties, key_id_guard, blob_metadata) = match key.domain {
             Domain::BLOB => {
-                check_key_permission(KeyPerm::use_(), key, &None)
+                check_key_permission(KeyPerm::Use, key, &None)
                     .context("In create_operation: checking use permission for Domain::BLOB.")?;
                 if forced {
-                    check_key_permission(KeyPerm::req_forced_op(), key, &None).context(
+                    check_key_permission(KeyPerm::ReqForcedOp, key, &None).context(
                         "In create_operation: checking forced permission for Domain::BLOB.",
                     )?;
                 }
@@ -241,18 +242,22 @@
                 )
             }
             _ => {
+                let super_key = SUPER_KEY
+                    .read()
+                    .unwrap()
+                    .get_per_boot_key_by_user_id(uid_to_android_user(caller_uid));
                 let (key_id_guard, mut key_entry) = DB
                     .with::<_, Result<(KeyIdGuard, KeyEntry)>>(|db| {
-                        LEGACY_IMPORTER.with_try_import(key, caller_uid, || {
+                        LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || {
                             db.borrow_mut().load_key_entry(
-                                &key,
+                                key,
                                 KeyType::Client,
                                 KeyEntryLoadBits::KM,
                                 caller_uid,
                                 |k, av| {
-                                    check_key_permission(KeyPerm::use_(), k, &av)?;
+                                    check_key_permission(KeyPerm::Use, k, &av)?;
                                     if forced {
-                                        check_key_permission(KeyPerm::req_forced_op(), k, &av)?;
+                                        check_key_permission(KeyPerm::ReqForcedOp, k, &av)?;
                                     }
                                     Ok(())
                                 },
@@ -303,28 +308,30 @@
             .context("In create_operation.")?;
 
         let km_blob = SUPER_KEY
+            .read()
+            .unwrap()
             .unwrap_key_if_required(&blob_metadata, km_blob)
             .context("In create_operation. Failed to handle super encryption.")?;
 
-        let km_dev: Strong<dyn IKeyMintDevice> = self
-            .keymint
-            .get_interface()
-            .context("In create_operation: Failed to get KeyMint device")?;
-
         let (begin_result, upgraded_blob) = self
             .upgrade_keyblob_if_required_with(
-                &*km_dev,
+                &*self.keymint,
                 key_id_guard,
                 &km_blob,
-                &blob_metadata,
-                &operation_parameters,
+                blob_metadata.km_uuid().copied(),
+                operation_parameters,
                 |blob| loop {
                     match map_km_error({
                         let _wp = self.watch_millis(
                             "In KeystoreSecurityLevel::create_operation: calling begin",
                             500,
                         );
-                        km_dev.begin(purpose, blob, &operation_parameters, immediate_hat.as_ref())
+                        self.keymint.begin(
+                            purpose,
+                            blob,
+                            operation_parameters,
+                            immediate_hat.as_ref(),
+                        )
                     }) {
                         Err(Error::Km(ErrorCode::TOO_MANY_OPERATIONS)) => {
                             self.operation_db.prune(caller_uid, forced)?;
@@ -368,7 +375,7 @@
             }
         };
 
-        let op_binder: binder::public_api::Strong<dyn IKeystoreOperation> =
+        let op_binder: binder::Strong<dyn IKeystoreOperation> =
             KeystoreOperation::new_native_binder(operation)
                 .as_binder()
                 .into_interface()
@@ -405,23 +412,26 @@
             );
         }
 
-        result.push(KeyParameter {
-            tag: Tag::CREATION_DATETIME,
-            value: KeyParameterValue::DateTime(
-                SystemTime::now()
-                    .duration_since(SystemTime::UNIX_EPOCH)
-                    .context(
-                        "In KeystoreSecurityLevel::add_required_parameters: \
+        // Add CREATION_DATETIME only if the backend version Keymint V1 (100) or newer.
+        if self.hw_info.versionNumber >= 100 {
+            result.push(KeyParameter {
+                tag: Tag::CREATION_DATETIME,
+                value: KeyParameterValue::DateTime(
+                    SystemTime::now()
+                        .duration_since(SystemTime::UNIX_EPOCH)
+                        .context(
+                            "In KeystoreSecurityLevel::add_required_parameters: \
                         Failed to get epoch time.",
-                    )?
-                    .as_millis()
-                    .try_into()
-                    .context(
-                        "In KeystoreSecurityLevel::add_required_parameters: \
+                        )?
+                        .as_millis()
+                        .try_into()
+                        .context(
+                            "In KeystoreSecurityLevel::add_required_parameters: \
                         Failed to convert epoch time.",
-                    )?,
-            ),
-        });
+                        )?,
+                ),
+            });
+        }
 
         // If there is an attestation challenge we need to get an application id.
         if params.iter().any(|kp| kp.tag == Tag::ATTESTATION_CHALLENGE) {
@@ -442,7 +452,7 @@
         }
 
         if params.iter().any(|kp| kp.tag == Tag::INCLUDE_UNIQUE_ID) {
-            check_key_permission(KeyPerm::gen_unique_id(), key, &None).context(concat!(
+            check_key_permission(KeyPerm::GenUniqueId, key, &None).context(concat!(
                 "In add_required_parameters: ",
                 "Caller does not have the permission to generate a unique ID"
             ))?;
@@ -514,7 +524,7 @@
 
         // generate_key requires the rebind permission.
         // Must return on error for security reasons.
-        check_key_permission(KeyPerm::rebind(), &key, &None).context("In generate_key.")?;
+        check_key_permission(KeyPerm::Rebind, &key, &None).context("In generate_key.")?;
 
         let attestation_key_info = match (key.domain, attest_key_descriptor) {
             (Domain::BLOB, _) => None,
@@ -535,8 +545,6 @@
             .add_required_parameters(caller_uid, params, &key)
             .context("In generate_key: Trying to get aaid.")?;
 
-        let km_dev: Strong<dyn IKeyMintDevice> = self.keymint.get_interface()?;
-
         let creation_result = match attestation_key_info {
             Some(AttestationKeyInfo::UserGenerated {
                 key_id_guard,
@@ -545,10 +553,10 @@
                 issuer_subject,
             }) => self
                 .upgrade_keyblob_if_required_with(
-                    &*km_dev,
+                    &*self.keymint,
                     Some(key_id_guard),
                     &KeyBlob::Ref(&blob),
-                    &blob_metadata,
+                    blob_metadata.km_uuid().copied(),
                     &params,
                     |blob| {
                         let attest_key = Some(AttestationKey {
@@ -564,29 +572,46 @@
                                 ),
                                 5000, // Generate can take a little longer.
                             );
-                            km_dev.generateKey(&params, attest_key.as_ref())
+                            self.keymint.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({
-                    let _wp = self.watch_millis(
-                        concat!(
-                            "In KeystoreSecurityLevel::generate_key (RemoteProvisioned): ",
-                            "calling generate_key.",
-                        ),
-                        5000, // Generate can take a little longer.
-                    );
-                    km_dev.generateKey(&params, Some(&attestation_key))
-                })
+            Some(AttestationKeyInfo::RemoteProvisioned {
+                key_id_guard,
+                attestation_key,
+                attestation_certs,
+            }) => self
+                .upgrade_keyblob_if_required_with(
+                    &*self.keymint,
+                    Some(key_id_guard),
+                    &KeyBlob::Ref(&attestation_key.keyBlob),
+                    Some(self.rem_prov_state.get_uuid()),
+                    &[],
+                    |blob| {
+                        map_km_error({
+                            let _wp = self.watch_millis(
+                                concat!(
+                                    "In KeystoreSecurityLevel::generate_key (RemoteProvisioned): ",
+                                    "calling generate_key.",
+                                ),
+                                5000, // Generate can take a little longer.
+                            );
+                            let dynamic_attest_key = Some(AttestationKey {
+                                keyBlob: blob.to_vec(),
+                                attestKeyParams: vec![],
+                                issuerSubjectName: attestation_key.issuerSubjectName.clone(),
+                            });
+                            self.keymint.generateKey(&params, dynamic_attest_key.as_ref())
+                        })
+                    },
+                )
                 .context("While generating Key with remote provisioned attestation key.")
-                .map(|mut creation_result| {
-                    creation_result.certificateChain.push(attestation_certs);
-                    creation_result
-                })
-            }
+                .map(|(mut result, _)| {
+                    result.certificateChain.push(attestation_certs);
+                    result
+                }),
             None => map_km_error({
                 let _wp = self.watch_millis(
                     concat!(
@@ -595,7 +620,7 @@
                     ),
                     5000, // Generate can take a little longer.
                 );
-                km_dev.generateKey(&params, None)
+                self.keymint.generateKey(&params, None)
             })
             .context("While generating Key without explicit attestation key."),
         }
@@ -630,7 +655,7 @@
         };
 
         // import_key requires the rebind permission.
-        check_key_permission(KeyPerm::rebind(), &key, &None).context("In import_key.")?;
+        check_key_permission(KeyPerm::Rebind, &key, &None).context("In import_key.")?;
 
         let params = self
             .add_required_parameters(caller_uid, params, &key)
@@ -652,8 +677,7 @@
             })
             .context("In import_key.")?;
 
-        let km_dev: Strong<dyn IKeyMintDevice> =
-            self.keymint.get_interface().context("In import_key: Trying to get the KM device")?;
+        let km_dev = &self.keymint;
         let creation_result = map_km_error({
             let _wp =
                 self.watch_millis("In KeystoreSecurityLevel::import_key: calling importKey.", 500);
@@ -715,17 +739,19 @@
         };
 
         // Import_wrapped_key requires the rebind permission for the new key.
-        check_key_permission(KeyPerm::rebind(), &key, &None).context("In import_wrapped_key.")?;
+        check_key_permission(KeyPerm::Rebind, &key, &None).context("In import_wrapped_key.")?;
+
+        let super_key = SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(user_id);
 
         let (wrapping_key_id_guard, mut wrapping_key_entry) = DB
             .with(|db| {
-                LEGACY_IMPORTER.with_try_import(&key, caller_uid, || {
+                LEGACY_IMPORTER.with_try_import(&key, caller_uid, super_key, || {
                     db.borrow_mut().load_key_entry(
-                        &wrapping_key,
+                        wrapping_key,
                         KeyType::Client,
                         KeyEntryLoadBits::KM,
                         caller_uid,
-                        |k, av| check_key_permission(KeyPerm::use_(), k, &av),
+                        |k, av| check_key_permission(KeyPerm::Use, k, &av),
                     )
                 })
             })
@@ -736,8 +762,11 @@
             .ok_or_else(error::Error::sys)
             .context("No km_blob after successfully loading key. This should never happen.")?;
 
-        let wrapping_key_blob =
-            SUPER_KEY.unwrap_key_if_required(&wrapping_blob_metadata, &wrapping_key_blob).context(
+        let wrapping_key_blob = SUPER_KEY
+            .read()
+            .unwrap()
+            .unwrap_key_if_required(&wrapping_blob_metadata, &wrapping_key_blob)
+            .context(
                 "In import_wrapped_key. Failed to handle super encryption for wrapping key.",
             )?;
 
@@ -763,24 +792,23 @@
 
         let masking_key = masking_key.unwrap_or(ZERO_BLOB_32);
 
-        let km_dev: Strong<dyn IKeyMintDevice> = self.keymint.get_interface()?;
         let (creation_result, _) = self
             .upgrade_keyblob_if_required_with(
-                &*km_dev,
+                &*self.keymint,
                 Some(wrapping_key_id_guard),
                 &wrapping_key_blob,
-                &wrapping_blob_metadata,
+                wrapping_blob_metadata.km_uuid().copied(),
                 &[],
                 |wrapping_blob| {
                     let _wp = self.watch_millis(
                         "In KeystoreSecurityLevel::import_wrapped_key: calling importWrappedKey.",
                         500,
                     );
-                    let creation_result = map_km_error(km_dev.importWrappedKey(
+                    let creation_result = map_km_error(self.keymint.importWrappedKey(
                         wrapped_data,
                         wrapping_blob,
                         masking_key,
-                        &params,
+                        params,
                         pw_sid,
                         fp_sid,
                     ))?;
@@ -795,17 +823,17 @@
 
     fn store_upgraded_keyblob(
         key_id_guard: KeyIdGuard,
-        km_uuid: Option<&Uuid>,
+        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)
+            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_default();
         if let Some(uuid) = km_uuid {
-            new_blob_metadata.add(BlobMetaEntry::KmUuid(*uuid));
+            new_blob_metadata.add(BlobMetaEntry::KmUuid(uuid));
         }
 
         DB.with(|db| {
@@ -823,69 +851,46 @@
     fn upgrade_keyblob_if_required_with<T, F>(
         &self,
         km_dev: &dyn IKeyMintDevice,
-        key_id_guard: Option<KeyIdGuard>,
+        mut key_id_guard: Option<KeyIdGuard>,
         key_blob: &KeyBlob,
-        blob_metadata: &BlobMetaData,
+        km_uuid: Option<Uuid>,
         params: &[KeyParameter],
         f: F,
     ) -> Result<(T, Option<Vec<u8>>)>
     where
         F: Fn(&[u8]) -> Result<T, Error>,
     {
-        match f(key_blob) {
-            Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
-                let upgraded_blob = {
-                    let _wp = self.watch_millis(
-                        concat!(
-                            "In KeystoreSecurityLevel::upgrade_keyblob_if_required_with: ",
-                            "calling upgradeKey."
-                        ),
-                        500,
-                    );
-                    map_km_error(km_dev.upgradeKey(key_blob, params))
-                }
-                .context("In upgrade_keyblob_if_required_with: Upgrade failed.")?;
-
-                if let Some(kid) = key_id_guard {
-                    Self::store_upgraded_keyblob(
-                        kid,
-                        blob_metadata.km_uuid(),
-                        key_blob,
-                        &upgraded_blob,
-                    )
-                    .context(
+        let (v, upgraded_blob) = crate::utils::upgrade_keyblob_if_required_with(
+            km_dev,
+            key_blob,
+            params,
+            f,
+            |upgraded_blob| {
+                if key_id_guard.is_some() {
+                    // Unwrap cannot panic, because the is_some was true.
+                    let kid = key_id_guard.take().unwrap();
+                    Self::store_upgraded_keyblob(kid, km_uuid, key_blob, upgraded_blob).context(
                         "In upgrade_keyblob_if_required_with: store_upgraded_keyblob failed",
-                    )?;
+                    )
+                } else {
+                    Ok(())
                 }
+            },
+        )
+        .context("In KeystoreSecurityLevel::upgrade_keyblob_if_required_with.")?;
 
-                match f(&upgraded_blob) {
-                    Ok(v) => Ok((v, Some(upgraded_blob))),
-                    Err(e) => Err(e).context(concat!(
-                        "In upgrade_keyblob_if_required_with: ",
-                        "Failed to perform operation on second try."
-                    )),
-                }
-            }
-            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.")
+        // If no upgrade was needed, use the opportunity to reencrypt the blob if required
+        // and if the a key_id_guard is held. Note: key_id_guard can only be Some if no
+        // upgrade was performed above and if one was given in the first place.
+        if key_blob.force_reencrypt() {
+            if let Some(kid) = key_id_guard {
+                Self::store_upgraded_keyblob(kid, km_uuid, key_blob, key_blob).context(concat!(
+                    "In upgrade_keyblob_if_required_with: ",
+                    "store_upgraded_keyblob failed in forced reencrypt"
+                ))?;
             }
         }
+        Ok((v, upgraded_blob))
     }
 
     fn convert_storage_key_to_ephemeral(
@@ -907,13 +912,10 @@
             )?;
 
         // convert_storage_key_to_ephemeral requires the associated permission
-        check_key_permission(KeyPerm::convert_storage_key_to_ephemeral(), storage_key, &None)
+        check_key_permission(KeyPerm::ConvertStorageKeyToEphemeral, 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"
-        ))?;
+        let km_dev = &self.keymint;
         match {
             let _wp = self.watch_millis(
                 concat!(
@@ -969,17 +971,14 @@
             .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
             .context("In IKeystoreSecurityLevel delete_key: No key blob specified")?;
 
-        check_key_permission(KeyPerm::delete(), key, &None)
+        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")?;
+        let km_dev = &self.keymint;
         {
             let _wp =
                 self.watch_millis("In KeystoreSecuritylevel::delete_key: calling deleteKey", 500);
-            map_km_error(km_dev.deleteKey(&key_blob)).context("In keymint device deleteKey")
+            map_km_error(km_dev.deleteKey(key_blob)).context("In keymint device deleteKey")
         }
     }
 }
@@ -992,7 +991,7 @@
         key: &KeyDescriptor,
         operation_parameters: &[KeyParameter],
         forced: bool,
-    ) -> binder::public_api::Result<CreateOperationResponse> {
+    ) -> binder::Result<CreateOperationResponse> {
         let _wp = self.watch_millis("IKeystoreSecurityLevel::createOperation", 500);
         map_or_log_err(self.create_operation(key, operation_parameters, forced), Ok)
     }
@@ -1003,7 +1002,7 @@
         params: &[KeyParameter],
         flags: i32,
         entropy: &[u8],
-    ) -> binder::public_api::Result<KeyMetadata> {
+    ) -> binder::Result<KeyMetadata> {
         // Duration is set to 5 seconds, because generateKey - especially for RSA keys, takes more
         // time than other operations
         let _wp = self.watch_millis("IKeystoreSecurityLevel::generateKey", 5000);
@@ -1019,7 +1018,7 @@
         params: &[KeyParameter],
         flags: i32,
         key_data: &[u8],
-    ) -> binder::public_api::Result<KeyMetadata> {
+    ) -> binder::Result<KeyMetadata> {
         let _wp = self.watch_millis("IKeystoreSecurityLevel::importKey", 500);
         let result = self.import_key(key, attestation_key, params, flags, key_data);
         log_key_creation_event_stats(self.security_level, params, &result);
@@ -1033,7 +1032,7 @@
         masking_key: Option<&[u8]>,
         params: &[KeyParameter],
         authenticators: &[AuthenticatorSpec],
-    ) -> binder::public_api::Result<KeyMetadata> {
+    ) -> binder::Result<KeyMetadata> {
         let _wp = self.watch_millis("IKeystoreSecurityLevel::importWrappedKey", 500);
         let result =
             self.import_wrapped_key(key, wrapping_key, masking_key, params, authenticators);
@@ -1044,11 +1043,11 @@
     fn convertStorageKeyToEphemeral(
         &self,
         storage_key: &KeyDescriptor,
-    ) -> binder::public_api::Result<EphemeralStorageKeyResponse> {
+    ) -> binder::Result<EphemeralStorageKeyResponse> {
         let _wp = self.watch_millis("IKeystoreSecurityLevel::convertStorageKeyToEphemeral", 500);
         map_or_log_err(self.convert_storage_key_to_ephemeral(storage_key), Ok)
     }
-    fn deleteKey(&self, key: &KeyDescriptor) -> binder::public_api::Result<()> {
+    fn deleteKey(&self, key: &KeyDescriptor) -> binder::Result<()> {
         let _wp = self.watch_millis("IKeystoreSecurityLevel::deleteKey", 500);
         let result = self.delete_key(key);
         log_key_deleted(key, ThreadState::get_calling_uid(), result.is_ok());
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index b7c90f7..79e7692 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -22,11 +22,11 @@
 use crate::security_level::KeystoreSecurityLevel;
 use crate::utils::{
     check_grant_permission, check_key_permission, check_keystore_permission,
-    key_parameters_to_authorizations, watchdog as wd, Asp,
+    key_parameters_to_authorizations, list_key_entries, uid_to_android_user, watchdog as wd,
 };
 use crate::{
     database::Uuid,
-    globals::{create_thread_local_db, DB, LEGACY_BLOB_LOADER, LEGACY_IMPORTER},
+    globals::{create_thread_local_db, DB, LEGACY_BLOB_LOADER, LEGACY_IMPORTER, SUPER_KEY},
 };
 use crate::{database::KEYSTORE_UUID, permission};
 use crate::{
@@ -51,7 +51,7 @@
 /// Implementation of the IKeystoreService.
 #[derive(Default)]
 pub struct KeystoreService {
-    i_sec_level_by_uuid: HashMap<Uuid, Asp>,
+    i_sec_level_by_uuid: HashMap<Uuid, Strong<dyn IKeystoreSecurityLevel>>,
     uuid_by_sec_level: HashMap<SecurityLevel, Uuid>,
 }
 
@@ -68,15 +68,13 @@
         .context(concat!(
             "In KeystoreService::new_native_binder: ",
             "Trying to construct mandatory security level TEE."
-        ))
-        .map(|(dev, uuid)| (Asp::new(dev.as_binder()), uuid))?;
+        ))?;
         result.i_sec_level_by_uuid.insert(uuid, dev);
         result.uuid_by_sec_level.insert(SecurityLevel::TRUSTED_ENVIRONMENT, uuid);
 
         // Strongbox is optional, so we ignore errors and turn the result into an Option.
         if let Ok((dev, uuid)) =
             KeystoreSecurityLevel::new_native_binder(SecurityLevel::STRONGBOX, id_rotation_state)
-                .map(|(dev, uuid)| (Asp::new(dev.as_binder()), uuid))
         {
             result.i_sec_level_by_uuid.insert(uuid, dev);
             result.uuid_by_sec_level.insert(SecurityLevel::STRONGBOX, uuid);
@@ -88,7 +86,7 @@
                 (create_thread_local_db(), uuid_by_sec_level, LEGACY_BLOB_LOADER.clone())
             })
             .context(
-                "In KeystoreService::new_native_binder: Trying to initialize the legacy importer.",
+                "In KeystoreService::new_native_binder: Trying to initialize the legacy migrator.",
             )?;
 
         Ok(BnKeystoreService::new_binder(
@@ -107,7 +105,7 @@
 
     fn get_i_sec_level_by_uuid(&self, uuid: &Uuid) -> Result<Strong<dyn IKeystoreSecurityLevel>> {
         if let Some(dev) = self.i_sec_level_by_uuid.get(uuid) {
-            dev.get_interface().context("In get_i_sec_level_by_uuid.")
+            Ok(dev.clone())
         } else {
             Err(error::Error::sys())
                 .context("In get_i_sec_level_by_uuid: KeyMint instance for key not found.")
@@ -123,7 +121,7 @@
             .get(&sec_level)
             .and_then(|uuid| self.i_sec_level_by_uuid.get(uuid))
         {
-            dev.get_interface().context("In get_security_level.")
+            Ok(dev.clone())
         } else {
             Err(error::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
                 .context("In get_security_level: No such security level.")
@@ -132,15 +130,19 @@
 
     fn get_key_entry(&self, key: &KeyDescriptor) -> Result<KeyEntryResponse> {
         let caller_uid = ThreadState::get_calling_uid();
+
+        let super_key =
+            SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(caller_uid));
+
         let (key_id_guard, mut key_entry) = DB
             .with(|db| {
-                LEGACY_IMPORTER.with_try_import(&key, caller_uid, || {
+                LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || {
                     db.borrow_mut().load_key_entry(
-                        &key,
+                        key,
                         KeyType::Client,
                         KeyEntryLoadBits::PUBLIC,
                         caller_uid,
-                        |k, av| check_key_permission(KeyPerm::get_info(), k, &av),
+                        |k, av| check_key_permission(KeyPerm::GetInfo, k, &av),
                     )
                 })
             })
@@ -184,15 +186,18 @@
         certificate_chain: Option<&[u8]>,
     ) -> Result<()> {
         let caller_uid = ThreadState::get_calling_uid();
+        let super_key =
+            SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(caller_uid));
+
         DB.with::<_, Result<()>>(|db| {
-            let entry = match LEGACY_IMPORTER.with_try_import(&key, caller_uid, || {
+            let entry = match LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || {
                 db.borrow_mut().load_key_entry(
-                    &key,
+                    key,
                     KeyType::Client,
                     KeyEntryLoadBits::NONE,
                     caller_uid,
                     |k, av| {
-                        check_key_permission(KeyPerm::update(), k, &av)
+                        check_key_permission(KeyPerm::Update, k, &av)
                             .context("In update_subcomponent.")
                     },
                 )
@@ -238,7 +243,7 @@
             };
 
             // Security critical: This must return on failure. Do not remove the `?`;
-            check_key_permission(KeyPerm::rebind(), &key, &None)
+            check_key_permission(KeyPerm::Rebind, &key, &None)
                 .context("Caller does not have permission to insert this certificate.")?;
 
             db.store_new_certificate(
@@ -271,12 +276,12 @@
         // If the first check fails we check if the caller has the list permission allowing to list
         // any namespace. In that case we also adjust the queried namespace if a specific uid was
         // selected.
-        match check_key_permission(KeyPerm::get_info(), &k, &None) {
+        match check_key_permission(KeyPerm::GetInfo, &k, &None) {
             Err(e) => {
                 if let Some(selinux::Error::PermissionDenied) =
                     e.root_cause().downcast_ref::<selinux::Error>()
                 {
-                    check_keystore_permission(KeystorePerm::list())
+                    check_keystore_permission(KeystorePerm::List)
                         .context("In list_entries: While checking keystore permission.")?;
                     if namespace != -1 {
                         k.nspace = namespace;
@@ -288,30 +293,18 @@
             Ok(()) => {}
         };
 
-        let mut result = LEGACY_IMPORTER
-            .list_uid(k.domain, k.nspace)
-            .context("In list_entries: Trying to list legacy keys.")?;
-
-        result.append(
-            &mut DB
-                .with(|db| {
-                    let mut db = db.borrow_mut();
-                    db.list(k.domain, k.nspace, KeyType::Client)
-                })
-                .context("In list_entries: Trying to list keystore database.")?,
-        );
-
-        result.sort_unstable();
-        result.dedup();
-        Ok(result)
+        DB.with(|db| list_key_entries(&mut db.borrow_mut(), k.domain, k.nspace))
     }
 
     fn delete_key(&self, key: &KeyDescriptor) -> Result<()> {
         let caller_uid = ThreadState::get_calling_uid();
+        let super_key =
+            SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(caller_uid));
+
         DB.with(|db| {
-            LEGACY_IMPORTER.with_try_import(&key, caller_uid, || {
-                db.borrow_mut().unbind_key(&key, KeyType::Client, caller_uid, |k, av| {
-                    check_key_permission(KeyPerm::delete(), k, &av).context("During delete_key.")
+            LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || {
+                db.borrow_mut().unbind_key(key, KeyType::Client, caller_uid, |k, av| {
+                    check_key_permission(KeyPerm::Delete, k, &av).context("During delete_key.")
                 })
             })
         })
@@ -326,10 +319,13 @@
         access_vector: permission::KeyPermSet,
     ) -> Result<KeyDescriptor> {
         let caller_uid = ThreadState::get_calling_uid();
+        let super_key =
+            SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(caller_uid));
+
         DB.with(|db| {
-            LEGACY_IMPORTER.with_try_import(&key, caller_uid, || {
+            LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || {
                 db.borrow_mut().grant(
-                    &key,
+                    key,
                     caller_uid,
                     grantee_uid as u32,
                     access_vector,
@@ -342,8 +338,8 @@
 
     fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> Result<()> {
         DB.with(|db| {
-            db.borrow_mut().ungrant(&key, ThreadState::get_calling_uid(), grantee_uid as u32, |k| {
-                check_key_permission(KeyPerm::grant(), k, &None)
+            db.borrow_mut().ungrant(key, ThreadState::get_calling_uid(), grantee_uid as u32, |k| {
+                check_key_permission(KeyPerm::Grant, k, &None)
             })
         })
         .context("In KeystoreService::ungrant.")
@@ -358,13 +354,13 @@
     fn getSecurityLevel(
         &self,
         security_level: SecurityLevel,
-    ) -> binder::public_api::Result<Strong<dyn IKeystoreSecurityLevel>> {
+    ) -> binder::Result<Strong<dyn IKeystoreSecurityLevel>> {
         let _wp = wd::watch_millis_with("IKeystoreService::getSecurityLevel", 500, move || {
             format!("security_level: {}", security_level.0)
         });
         map_or_log_err(self.get_security_level(security_level), Ok)
     }
-    fn getKeyEntry(&self, key: &KeyDescriptor) -> binder::public_api::Result<KeyEntryResponse> {
+    fn getKeyEntry(&self, key: &KeyDescriptor) -> binder::Result<KeyEntryResponse> {
         let _wp = wd::watch_millis("IKeystoreService::get_key_entry", 500);
         map_or_log_err(self.get_key_entry(key), Ok)
     }
@@ -373,19 +369,15 @@
         key: &KeyDescriptor,
         public_cert: Option<&[u8]>,
         certificate_chain: Option<&[u8]>,
-    ) -> binder::public_api::Result<()> {
+    ) -> binder::Result<()> {
         let _wp = wd::watch_millis("IKeystoreService::updateSubcomponent", 500);
         map_or_log_err(self.update_subcomponent(key, public_cert, certificate_chain), Ok)
     }
-    fn listEntries(
-        &self,
-        domain: Domain,
-        namespace: i64,
-    ) -> binder::public_api::Result<Vec<KeyDescriptor>> {
+    fn listEntries(&self, domain: Domain, namespace: i64) -> binder::Result<Vec<KeyDescriptor>> {
         let _wp = wd::watch_millis("IKeystoreService::listEntries", 500);
         map_or_log_err(self.list_entries(domain, namespace), Ok)
     }
-    fn deleteKey(&self, key: &KeyDescriptor) -> binder::public_api::Result<()> {
+    fn deleteKey(&self, key: &KeyDescriptor) -> binder::Result<()> {
         let _wp = wd::watch_millis("IKeystoreService::deleteKey", 500);
         let result = self.delete_key(key);
         log_key_deleted(key, ThreadState::get_calling_uid(), result.is_ok());
@@ -396,11 +388,11 @@
         key: &KeyDescriptor,
         grantee_uid: i32,
         access_vector: i32,
-    ) -> binder::public_api::Result<KeyDescriptor> {
+    ) -> binder::Result<KeyDescriptor> {
         let _wp = wd::watch_millis("IKeystoreService::grant", 500);
         map_or_log_err(self.grant(key, grantee_uid, access_vector.into()), Ok)
     }
-    fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> binder::public_api::Result<()> {
+    fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> binder::Result<()> {
         let _wp = wd::watch_millis("IKeystoreService::ungrant", 500);
         map_or_log_err(self.ungrant(key, grantee_uid), Ok)
     }
diff --git a/keystore2/src/shared_secret_negotiation.rs b/keystore2/src/shared_secret_negotiation.rs
index 64bc2c3..1862f73 100644
--- a/keystore2/src/shared_secret_negotiation.rs
+++ b/keystore2/src/shared_secret_negotiation.rs
@@ -21,7 +21,7 @@
     ISharedSecret::ISharedSecret, SharedSecretParameters::SharedSecretParameters,
 };
 use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
-use anyhow::{Context, Result};
+use anyhow::Result;
 use keystore2_vintf::{get_aidl_instances, get_hidl_instances};
 use std::fmt::{self, Display, Formatter};
 use std::time::Duration;
@@ -118,47 +118,32 @@
         .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)).and_then(
-                                |sp| {
-                                    if let SharedSecretParticipant::Hidl {
-                                        is_strongbox: true,
-                                        ..
-                                    } = &sp
-                                    {
-                                        if !legacy_strongbox_found {
-                                            legacy_strongbox_found = true;
-                                            return Some(sp);
-                                        }
-                                    } else if !legacy_default_found {
-                                        legacy_default_found = true;
-                                        return Some(sp);
-                                    }
-                                    None
-                                },
-                            )
-                        })
-                        .collect::<Vec<SharedSecretParticipant>>()
+                .into_iter()
+                .filter_map(|name| {
+                    filter_map_legacy_km_instances(name, (*ma, *mi)).and_then(|sp| {
+                        if let SharedSecretParticipant::Hidl { is_strongbox: true, .. } = &sp {
+                            if !legacy_strongbox_found {
+                                legacy_strongbox_found = true;
+                                return Some(sp);
+                            }
+                        } else if !legacy_default_found {
+                            legacy_default_found = true;
+                            return Some(sp);
+                        }
+                        None
+                    })
                 })
+                .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()))
+        .into_iter()
+        .flatten()
+        .chain({
+            get_aidl_instances(SHARED_SECRET_PACKAGE_NAME, 1, SHARED_SECRET_INTERFACE_NAME)
+                .into_iter()
+                .map(SharedSecretParticipant::Aidl)
+                .collect::<Vec<_>>()
+                .into_iter()
         })
-        .context("In list_participants.")?
         .collect())
 }
 
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index cfcab80..74e3e56 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -28,9 +28,7 @@
     legacy_blob::LegacyBlobLoader,
     legacy_importer::LegacyImporter,
     raw_device::KeyMintDevice,
-    try_insert::TryInsert,
-    utils::watchdog as wd,
-    utils::AID_KEYSTORE,
+    utils::{watchdog as wd, AesGcm, AID_KEYSTORE},
 };
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Algorithm::Algorithm, BlockMode::BlockMode, HardwareAuthToken::HardwareAuthToken,
@@ -46,11 +44,11 @@
     aes_gcm_decrypt, aes_gcm_encrypt, generate_aes256_key, generate_salt, Password, ZVec,
     AES_256_KEY_LENGTH,
 };
-use keystore2_system_property::PropertyWatcher;
+use rustutils::system_properties::PropertyWatcher;
 use std::{
     collections::HashMap,
     sync::Arc,
-    sync::{Mutex, Weak},
+    sync::{Mutex, RwLock, Weak},
 };
 use std::{convert::TryFrom, ops::Deref};
 
@@ -76,7 +74,7 @@
 /// different purpose, distinguished by alias. Each is associated with a static
 /// constant of this type.
 pub struct SuperKeyType<'a> {
-    /// Alias used to look the key up in the `persistent.keyentry` table.
+    /// Alias used to look up the key in the `persistent.keyentry` table.
     pub alias: &'a str,
     /// Encryption algorithm
     pub algorithm: SuperEncryptionAlgorithm,
@@ -126,10 +124,8 @@
     fn from_metadata(metadata: &BlobMetaData) -> Option<Self> {
         if let Some(EncryptedBy::KeyId(key_id)) = metadata.encrypted_by() {
             Some(SuperKeyIdentifier::DatabaseId(*key_id))
-        } else if let Some(boot_level) = metadata.max_boot_level() {
-            Some(SuperKeyIdentifier::BootLevel(*boot_level))
         } else {
-            None
+            metadata.max_boot_level().map(|boot_level| SuperKeyIdentifier::BootLevel(*boot_level))
         }
     }
 
@@ -157,15 +153,22 @@
     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> {
+impl AesGcm for SuperKey {
+    fn 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")
+                .context("In SuperKey::decrypt: Decryption failed.")
         } else {
-            Err(Error::sys()).context("In aes_gcm_decrypt: Key is not an AES key")
+            Err(Error::sys()).context("In SuperKey::decrypt: Key is not an AES key.")
+        }
+    }
+
+    fn encrypt(&self, plaintext: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>)> {
+        if self.algorithm == SuperEncryptionAlgorithm::Aes256Gcm {
+            aes_gcm_encrypt(plaintext, &self.key)
+                .context("In SuperKey::encrypt: Encryption failed.")
+        } else {
+            Err(Error::sys()).context("In SuperKey::encrypt: Key is not an AES key.")
         }
     }
 }
@@ -258,7 +261,7 @@
 struct SkmState {
     user_keys: HashMap<UserId, UserSuperKeys>,
     key_index: HashMap<i64, Weak<SuperKey>>,
-    boot_level_key_cache: Option<BootLevelKeyCache>,
+    boot_level_key_cache: Option<Mutex<BootLevelKeyCache>>,
 }
 
 impl SkmState {
@@ -277,24 +280,24 @@
 
 #[derive(Default)]
 pub struct SuperKeyManager {
-    data: Mutex<SkmState>,
+    data: SkmState,
 }
 
 impl SuperKeyManager {
-    pub fn set_up_boot_level_cache(self: &Arc<Self>, db: &mut KeystoreDB) -> Result<()> {
-        let mut data = self.data.lock().unwrap();
-        if data.boot_level_key_cache.is_some() {
+    pub fn set_up_boot_level_cache(skm: &Arc<RwLock<Self>>, db: &mut KeystoreDB) -> Result<()> {
+        let mut skm_guard = skm.write().unwrap();
+        if skm_guard.data.boot_level_key_cache.is_some() {
             log::info!("In set_up_boot_level_cache: called for a second time");
             return Ok(());
         }
         let level_zero_key = get_level_zero_key(db)
             .context("In set_up_boot_level_cache: get_level_zero_key failed")?;
-        data.boot_level_key_cache = Some(BootLevelKeyCache::new(level_zero_key));
+        skm_guard.data.boot_level_key_cache =
+            Some(Mutex::new(BootLevelKeyCache::new(level_zero_key)));
         log::info!("Starting boot level watcher.");
-        let clone = self.clone();
+        let clone = skm.clone();
         std::thread::spawn(move || {
-            clone
-                .watch_boot_level()
+            Self::watch_boot_level(clone)
                 .unwrap_or_else(|e| log::error!("watch_boot_level failed:\n{:?}", e));
         });
         Ok(())
@@ -302,32 +305,40 @@
 
     /// Watch the `keystore.boot_level` system property, and keep boot level up to date.
     /// Blocks waiting for system property changes, so must be run in its own thread.
-    fn watch_boot_level(&self) -> Result<()> {
+    fn watch_boot_level(skm: Arc<RwLock<Self>>) -> Result<()> {
         let mut w = PropertyWatcher::new("keystore.boot_level")
             .context("In watch_boot_level: PropertyWatcher::new failed")?;
         loop {
             let level = w
                 .read(|_n, v| v.parse::<usize>().map_err(std::convert::Into::into))
                 .context("In watch_boot_level: read of property failed")?;
-            // watch_boot_level should only be called once data.boot_level_key_cache is Some,
-            // so it's safe to unwrap in the branches below.
-            if level < MAX_MAX_BOOT_LEVEL {
-                log::info!("Read keystore.boot_level value {}", level);
-                let mut data = self.data.lock().unwrap();
-                data.boot_level_key_cache
+
+            // This scope limits the skm_guard life, so we don't hold the skm_guard while
+            // waiting.
+            {
+                let mut skm_guard = skm.write().unwrap();
+                let boot_level_key_cache = skm_guard
+                    .data
+                    .boot_level_key_cache
                     .as_mut()
-                    .unwrap()
-                    .advance_boot_level(level)
-                    .context("In watch_boot_level: advance_boot_level failed")?;
-            } else {
-                log::info!(
-                    "keystore.boot_level {} hits maximum {}, finishing.",
-                    level,
-                    MAX_MAX_BOOT_LEVEL
-                );
-                let mut data = self.data.lock().unwrap();
-                data.boot_level_key_cache.as_mut().unwrap().finish();
-                break;
+                    .ok_or_else(Error::sys)
+                    .context("In watch_boot_level: Boot level cache not initialized")?
+                    .get_mut()
+                    .unwrap();
+                if level < MAX_MAX_BOOT_LEVEL {
+                    log::info!("Read keystore.boot_level value {}", level);
+                    boot_level_key_cache
+                        .advance_boot_level(level)
+                        .context("In watch_boot_level: advance_boot_level failed")?;
+                } else {
+                    log::info!(
+                        "keystore.boot_level {} hits maximum {}, finishing.",
+                        level,
+                        MAX_MAX_BOOT_LEVEL
+                    );
+                    boot_level_key_cache.finish();
+                    break;
+                }
             }
             w.wait().context("In watch_boot_level: property wait failed")?;
         }
@@ -336,34 +347,37 @@
 
     pub fn level_accessible(&self, boot_level: i32) -> bool {
         self.data
-            .lock()
-            .unwrap()
             .boot_level_key_cache
             .as_ref()
-            .map_or(false, |c| c.level_accessible(boot_level as usize))
+            .map_or(false, |c| c.lock().unwrap().level_accessible(boot_level as usize))
     }
 
-    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_for_user(&mut self, user: UserId) {
+        self.data.user_keys.remove(&user);
     }
 
-    fn install_per_boot_key_for_user(&self, user: UserId, super_key: Arc<SuperKey>) -> Result<()> {
-        let mut data = self.data.lock().unwrap();
-        data.add_key_to_key_index(&super_key)
+    fn install_per_boot_key_for_user(
+        &mut self,
+        user: UserId,
+        super_key: Arc<SuperKey>,
+    ) -> Result<()> {
+        self.data
+            .add_key_to_key_index(&super_key)
             .context("In install_per_boot_key_for_user: add_key_to_key_index failed")?;
-        data.user_keys.entry(user).or_default().per_boot = Some(super_key);
+        self.data.user_keys.entry(user).or_default().per_boot = Some(super_key);
         Ok(())
     }
 
     fn lookup_key(&self, key_id: &SuperKeyIdentifier) -> Result<Option<Arc<SuperKey>>> {
-        let mut data = self.data.lock().unwrap();
         Ok(match key_id {
-            SuperKeyIdentifier::DatabaseId(id) => data.key_index.get(id).and_then(|k| k.upgrade()),
-            SuperKeyIdentifier::BootLevel(level) => data
+            SuperKeyIdentifier::DatabaseId(id) => {
+                self.data.key_index.get(id).and_then(|k| k.upgrade())
+            }
+            SuperKeyIdentifier::BootLevel(level) => self
+                .data
                 .boot_level_key_cache
-                .as_mut()
-                .map(|b| b.aes_key(*level as usize))
+                .as_ref()
+                .map(|b| b.lock().unwrap().aes_key(*level as usize))
                 .transpose()
                 .context("In lookup_key: aes_key failed")?
                 .flatten()
@@ -378,9 +392,16 @@
         })
     }
 
-    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).and_then(|e| e.per_boot.as_ref().cloned())
+    pub fn get_per_boot_key_by_user_id(
+        &self,
+        user_id: UserId,
+    ) -> Option<Arc<dyn AesGcm + Send + Sync>> {
+        self.get_per_boot_key_by_user_id_internal(user_id)
+            .map(|sk| -> Arc<dyn AesGcm + Send + Sync> { sk })
+    }
+
+    fn get_per_boot_key_by_user_id_internal(&self, user_id: UserId) -> Option<Arc<SuperKey>> {
+        self.data.user_keys.get(&user_id).and_then(|e| e.per_boot.as_ref().cloned())
     }
 
     /// This function unlocks the super keys for a given user.
@@ -388,7 +409,7 @@
     /// super key cache. If there is no such key a new key is created, encrypted with
     /// a key derived from the given password and stored in the database.
     pub fn unlock_user_key(
-        &self,
+        &mut self,
         db: &mut KeystoreDB,
         user: UserId,
         pw: &Password,
@@ -398,7 +419,7 @@
             .get_or_create_key_with(
                 Domain::APP,
                 user as u64 as i64,
-                &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.
@@ -457,7 +478,7 @@
         match key.algorithm {
             SuperEncryptionAlgorithm::Aes256Gcm => match (metadata.iv(), metadata.aead_tag()) {
                 (Some(iv), Some(tag)) => key
-                    .aes_gcm_decrypt(blob, iv, tag)
+                    .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!(
@@ -495,13 +516,16 @@
     }
 
     /// Checks if user has setup LSKF, even when super key cache is empty for the user.
-    pub fn super_key_exists_in_db_for_user(
+    /// The reference to self is unused but it is required to prevent calling this function
+    /// concurrently with skm state database changes.
+    fn super_key_exists_in_db_for_user(
+        &self,
         db: &mut KeystoreDB,
         legacy_importer: &LegacyImporter,
         user_id: UserId,
     ) -> Result<bool> {
         let key_in_db = db
-            .key_exists(Domain::APP, user_id as u64 as i64, &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 {
@@ -517,7 +541,7 @@
     /// legacy database). If not, return Uninitialized state.
     /// Otherwise, decrypt the super key from the password and return LskfUnlocked state.
     pub fn check_and_unlock_super_key(
-        &self,
+        &mut self,
         db: &mut KeystoreDB,
         legacy_importer: &LegacyImporter,
         user_id: UserId,
@@ -546,24 +570,23 @@
     /// and return LskfUnlocked state.
     /// If the password is not provided, return Uninitialized state.
     pub fn check_and_initialize_super_key(
-        &self,
+        &mut self,
         db: &mut KeystoreDB,
         legacy_importer: &LegacyImporter,
         user_id: UserId,
         pw: Option<&Password>,
     ) -> Result<UserState> {
-        let super_key_exists_in_db =
-            Self::super_key_exists_in_db_for_user(db, legacy_importer, user_id).context(
-                "In check_and_initialize_super_key. Failed to check if super key exists.",
-            )?;
+        let super_key_exists_in_db = self
+            .super_key_exists_in_db_for_user(db, legacy_importer, user_id)
+            .context("In check_and_initialize_super_key. Failed to check if super key exists.")?;
         if super_key_exists_in_db {
             Ok(UserState::LskfLocked)
         } else if let Some(pw) = pw {
-            //generate a new super key.
+            // Generate a new super key.
             let super_key = generate_aes256_key()
                 .context("In check_and_initialize_super_key: Failed to generate AES 256 key.")?;
-            //derive an AES256 key from the password and re-encrypt the super key
-            //before we insert it in the database.
+            // Derive an AES256 key from the password and re-encrypt the super key
+            // before we insert it in the database.
             let (encrypted_super_key, blob_metadata) = Self::encrypt_with_password(&super_key, pw)
                 .context("In check_and_initialize_super_key.")?;
 
@@ -591,9 +614,9 @@
         }
     }
 
-    //helper function to populate super key cache from the super key blob loaded from the database
+    // Helper function to populate super key cache from the super key blob loaded from the database.
     fn populate_cache_from_super_key_blob(
-        &self,
+        &mut self,
         user_id: UserId,
         algorithm: SuperEncryptionAlgorithm,
         entry: KeyEntry,
@@ -607,7 +630,7 @@
         Ok(super_key)
     }
 
-    /// Extracts super key from the entry loaded from the database
+    /// Extracts super key from the entry loaded from the database.
     pub fn extract_super_key_from_key_entry(
         algorithm: SuperEncryptionAlgorithm,
         entry: KeyEntry,
@@ -622,7 +645,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
+                    // 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.",
                     )?;
@@ -686,7 +709,8 @@
         user_id: UserId,
         key_blob: &[u8],
     ) -> Result<(Vec<u8>, BlobMetaData)> {
-        match UserState::get(db, legacy_importer, self, user_id)
+        match self
+            .get_user_state(db, legacy_importer, user_id)
             .context("In super_encrypt. Failed to get user state.")?
         {
             UserState::LskfUnlocked(super_key) => {
@@ -701,9 +725,9 @@
         }
     }
 
-    //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.
+    // 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_aes_super_key(
         key_blob: &[u8],
         super_key: &SuperKey,
@@ -737,16 +761,16 @@
         match Enforcements::super_encryption_required(domain, key_parameters, flags) {
             SuperEncryptionType::None => Ok((key_blob.to_vec(), BlobMetaData::new())),
             SuperEncryptionType::LskfBound => self
-                .super_encrypt_on_key_init(db, legacy_importer, user_id, &key_blob)
+                .super_encrypt_on_key_init(db, legacy_importer, 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!(
+                let entry =
+                    self.data.user_keys.get(&user_id).and_then(|e| e.screen_lock_bound.as_ref());
+                if let Some(super_key) = entry {
+                    Self::encrypt_with_aes_super_key(key_blob, super_key).context(concat!(
                         "In handle_super_encryption_on_key_init. ",
                         "Failed to encrypt with ScreenLockBound key."
                     ))
@@ -815,6 +839,7 @@
     /// 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(
+        &mut self,
         db: &mut KeystoreDB,
         user_id: UserId,
         key_type: &SuperKeyType,
@@ -849,8 +874,8 @@
                     )
                 }
             };
-            //derive an AES256 key from the password and re-encrypt the super key
-            //before we insert it in the database.
+            // 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.")?;
@@ -878,52 +903,64 @@
 
     /// Decrypt the screen-lock bound keys for this user using the password and store in memory.
     pub fn unlock_screen_lock_bound_key(
-        &self,
+        &mut 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_P521_KEY,
-                    password,
-                    Some(aes.clone()),
-                )
-            })?
-            .clone();
-        data.add_key_to_key_index(&aes)?;
-        data.add_key_to_key_index(&ecdh)?;
+        let (screen_lock_bound, screen_lock_bound_private) = self
+            .data
+            .user_keys
+            .get(&user_id)
+            .map(|e| (e.screen_lock_bound.clone(), e.screen_lock_bound_private.clone()))
+            .unwrap_or((None, None));
+
+        if screen_lock_bound.is_some() && screen_lock_bound_private.is_some() {
+            // Already unlocked.
+            return Ok(());
+        }
+
+        let aes = if let Some(screen_lock_bound) = screen_lock_bound {
+            // This is weird. If this point is reached only one of the screen locked keys was
+            // initialized. This should never happen.
+            screen_lock_bound
+        } else {
+            self.get_or_create_super_key(db, user_id, &USER_SCREEN_LOCK_BOUND_KEY, password, None)
+                .context("In unlock_screen_lock_bound_key: Trying to get or create symmetric key.")?
+        };
+
+        let ecdh = if let Some(screen_lock_bound_private) = screen_lock_bound_private {
+            // This is weird. If this point is reached only one of the screen locked keys was
+            // initialized. This should never happen.
+            screen_lock_bound_private
+        } else {
+            self.get_or_create_super_key(
+                db,
+                user_id,
+                &USER_SCREEN_LOCK_BOUND_P521_KEY,
+                password,
+                Some(aes.clone()),
+            )
+            .context("In unlock_screen_lock_bound_key: Trying to get or create asymmetric key.")?
+        };
+
+        self.data.add_key_to_key_index(&aes)?;
+        self.data.add_key_to_key_index(&ecdh)?;
+        let entry = self.data.user_keys.entry(user_id).or_default();
+        entry.screen_lock_bound = Some(aes);
+        entry.screen_lock_bound_private = Some(ecdh);
         Ok(())
     }
 
     /// Wipe the screen-lock bound keys for this user from memory.
     pub fn lock_screen_lock_bound_key(
-        &self,
+        &mut self,
         db: &mut KeystoreDB,
         user_id: UserId,
         unlocking_sids: &[i64],
     ) {
         log::info!("Locking screen bound for user {} sids {:?}", user_id, unlocking_sids);
-        let mut data = self.data.lock().unwrap();
-        let mut entry = data.user_keys.entry(user_id).or_default();
+        let mut entry = self.data.user_keys.entry(user_id).or_default();
         if !unlocking_sids.is_empty() {
             if let (Some(aes), Some(ecdh)) = (
                 entry.screen_lock_bound.as_ref().cloned(),
@@ -995,12 +1032,11 @@
     /// User has unlocked, not using a password. See if any of our stored auth tokens can be used
     /// to unlock the keys protecting UNLOCKED_DEVICE_REQUIRED keys.
     pub fn try_unlock_user_with_biometric(
-        &self,
+        &mut self,
         db: &mut KeystoreDB,
         user_id: UserId,
     ) -> Result<()> {
-        let mut data = self.data.lock().unwrap();
-        let mut entry = data.user_keys.entry(user_id).or_default();
+        let mut entry = self.data.user_keys.entry(user_id).or_default();
         if let Some(biometric) = entry.biometric_unlock.as_ref() {
             let (key_id_guard, key_entry) = db
                 .load_key_entry(
@@ -1040,8 +1076,8 @@
                         Ok((slb, slbp)) => {
                             entry.screen_lock_bound = Some(slb.clone());
                             entry.screen_lock_bound_private = Some(slbp.clone());
-                            data.add_key_to_key_index(&slb)?;
-                            data.add_key_to_key_index(&slbp)?;
+                            self.data.add_key_to_key_index(&slb)?;
+                            self.data.add_key_to_key_index(&slbp)?;
                             log::info!(concat!(
                                 "In try_unlock_user_with_biometric: ",
                                 "Successfully unlocked with biometric"
@@ -1057,6 +1093,122 @@
         }
         Ok(())
     }
+
+    /// Returns the keystore locked state of the given user. It requires the thread local
+    /// keystore database and a reference to the legacy migrator because it may need to
+    /// import the super key from the legacy blob database to the keystore database.
+    pub fn get_user_state(
+        &self,
+        db: &mut KeystoreDB,
+        legacy_importer: &LegacyImporter,
+        user_id: UserId,
+    ) -> Result<UserState> {
+        match self.get_per_boot_key_by_user_id_internal(user_id) {
+            Some(super_key) => Ok(UserState::LskfUnlocked(super_key)),
+            None => {
+                // Check if a super key exists in the database or legacy database.
+                // If so, return locked user state.
+                if self
+                    .super_key_exists_in_db_for_user(db, legacy_importer, user_id)
+                    .context("In get_user_state.")?
+                {
+                    Ok(UserState::LskfLocked)
+                } else {
+                    Ok(UserState::Uninitialized)
+                }
+            }
+        }
+    }
+
+    /// If the given user is unlocked:
+    /// * and `password` is None, the user is reset, all authentication bound keys are deleted and
+    ///   `Ok(UserState::Uninitialized)` is returned.
+    /// * and `password` is Some, `Ok(UserState::LskfUnlocked)` is returned.
+    /// If the given user is locked:
+    /// * and the user was initialized before, `Ok(UserState::Locked)` is returned.
+    /// * and the user was not initialized before:
+    ///   * and `password` is None, `Ok(Uninitialized)` is returned.
+    ///   * and `password` is Some, super keys are generated and `Ok(UserState::LskfUnlocked)` is
+    ///     returned.
+    pub fn reset_or_init_user_and_get_user_state(
+        &mut self,
+        db: &mut KeystoreDB,
+        legacy_importer: &LegacyImporter,
+        user_id: UserId,
+        password: Option<&Password>,
+    ) -> Result<UserState> {
+        match self.get_per_boot_key_by_user_id_internal(user_id) {
+            Some(_) if password.is_none() => {
+                // Transitioning to swiping, delete only the super key in database and cache,
+                // and super-encrypted keys in database (and in KM).
+                self.reset_user(db, legacy_importer, user_id, true).context(
+                    "In reset_or_init_user_and_get_user_state: Trying to delete keys from the db.",
+                )?;
+                // Lskf is now removed in Keystore.
+                Ok(UserState::Uninitialized)
+            }
+            Some(super_key) => {
+                // Keystore won't be notified when changing to a new password when LSKF is
+                // already setup. Therefore, ideally this path wouldn't be reached.
+                Ok(UserState::LskfUnlocked(super_key))
+            }
+            None => {
+                // Check if a super key exists in the database or legacy database.
+                // If so, return LskfLocked state.
+                // Otherwise, i) if the password is provided, initialize the super key and return
+                // LskfUnlocked state ii) if password is not provided, return Uninitialized state.
+                self.check_and_initialize_super_key(db, legacy_importer, user_id, password)
+            }
+        }
+    }
+
+    /// Unlocks the given user with the given password. If the key was already unlocked or unlocking
+    /// was successful, `Ok(UserState::LskfUnlocked)` is returned.
+    /// If the user was never initialized `Ok(UserState::Uninitialized)` is returned.
+    pub fn unlock_and_get_user_state(
+        &mut self,
+        db: &mut KeystoreDB,
+        legacy_importer: &LegacyImporter,
+        user_id: UserId,
+        password: &Password,
+    ) -> Result<UserState> {
+        match self.get_per_boot_key_by_user_id_internal(user_id) {
+            Some(super_key) => {
+                log::info!("In unlock_and_get_user_state. Trying to unlock when already unlocked.");
+                Ok(UserState::LskfUnlocked(super_key))
+            }
+            None => {
+                // Check if a super key exists in the database or legacy database.
+                // If not, return Uninitialized state.
+                // Otherwise, try to unlock the super key and if successful,
+                // return LskfUnlocked.
+                self.check_and_unlock_super_key(db, legacy_importer, user_id, password)
+                    .context("In unlock_and_get_user_state. Failed to unlock super key.")
+            }
+        }
+    }
+
+    /// Delete all the keys created on behalf of the user.
+    /// If 'keep_non_super_encrypted_keys' is set to true, delete only the super key and super
+    /// encrypted keys.
+    pub fn reset_user(
+        &mut self,
+        db: &mut KeystoreDB,
+        legacy_importer: &LegacyImporter,
+        user_id: UserId,
+        keep_non_super_encrypted_keys: bool,
+    ) -> Result<()> {
+        // Mark keys created on behalf of the user as unreferenced.
+        legacy_importer
+            .bulk_delete_user(user_id, keep_non_super_encrypted_keys)
+            .context("In reset_user: Trying to delete legacy 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.
+        self.forget_all_keys_for_user(user_id);
+        Ok(())
+    }
 }
 
 /// This enum represents different states of the user's life cycle in the device.
@@ -1074,110 +1226,6 @@
     Uninitialized,
 }
 
-impl UserState {
-    pub fn get(
-        db: &mut KeystoreDB,
-        legacy_importer: &LegacyImporter,
-        skm: &SuperKeyManager,
-        user_id: UserId,
-    ) -> Result<UserState> {
-        match skm.get_per_boot_key_by_user_id(user_id) {
-            Some(super_key) => Ok(UserState::LskfUnlocked(super_key)),
-            None => {
-                //Check if a super key exists in the database or legacy database.
-                //If so, return locked user state.
-                if SuperKeyManager::super_key_exists_in_db_for_user(db, legacy_importer, user_id)
-                    .context("In get.")?
-                {
-                    Ok(UserState::LskfLocked)
-                } else {
-                    Ok(UserState::Uninitialized)
-                }
-            }
-        }
-    }
-
-    /// Queries user state when serving password change requests.
-    pub fn get_with_password_changed(
-        db: &mut KeystoreDB,
-        legacy_importer: &LegacyImporter,
-        skm: &SuperKeyManager,
-        user_id: UserId,
-        password: Option<&Password>,
-    ) -> Result<UserState> {
-        match skm.get_per_boot_key_by_user_id(user_id) {
-            Some(super_key) => {
-                if password.is_none() {
-                    //transitioning to swiping, delete only the super key in database and cache, and
-                    //super-encrypted keys in database (and in KM)
-                    Self::reset_user(db, skm, legacy_importer, user_id, true).context(
-                        "In get_with_password_changed: Trying to delete keys from the db.",
-                    )?;
-                    //Lskf is now removed in Keystore
-                    Ok(UserState::Uninitialized)
-                } else {
-                    //Keystore won't be notified when changing to a new password when LSKF is
-                    //already setup. Therefore, ideally this path wouldn't be reached.
-                    Ok(UserState::LskfUnlocked(super_key))
-                }
-            }
-            None => {
-                //Check if a super key exists in the database or legacy database.
-                //If so, return LskfLocked state.
-                //Otherwise, i) if the password is provided, initialize the super key and return
-                //LskfUnlocked state ii) if password is not provided, return Uninitialized state.
-                skm.check_and_initialize_super_key(db, legacy_importer, user_id, password)
-            }
-        }
-    }
-
-    /// Queries user state when serving password unlock requests.
-    pub fn get_with_password_unlock(
-        db: &mut KeystoreDB,
-        legacy_importer: &LegacyImporter,
-        skm: &SuperKeyManager,
-        user_id: UserId,
-        password: &Password,
-    ) -> Result<UserState> {
-        match skm.get_per_boot_key_by_user_id(user_id) {
-            Some(super_key) => {
-                log::info!("In get_with_password_unlock. Trying to unlock when already unlocked.");
-                Ok(UserState::LskfUnlocked(super_key))
-            }
-            None => {
-                //Check if a super key exists in the database or legacy database.
-                //If not, return Uninitialized state.
-                //Otherwise, try to unlock the super key and if successful,
-                //return LskfUnlocked state
-                skm.check_and_unlock_super_key(db, legacy_importer, user_id, password)
-                    .context("In get_with_password_unlock. Failed to unlock super key.")
-            }
-        }
-    }
-
-    /// Delete all the keys created on behalf of the user.
-    /// If 'keep_non_super_encrypted_keys' is set to true, delete only the super key and super
-    /// encrypted keys.
-    pub fn reset_user(
-        db: &mut KeystoreDB,
-        skm: &SuperKeyManager,
-        legacy_importer: &LegacyImporter,
-        user_id: UserId,
-        keep_non_super_encrypted_keys: bool,
-    ) -> Result<()> {
-        // mark keys created on behalf of the user as unreferenced.
-        legacy_importer
-            .bulk_delete_user(user_id, keep_non_super_encrypted_keys)
-            .context("In reset_user: Trying to delete legacy 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);
-        Ok(())
-    }
-}
-
 /// This enum represents three states a KeyMint Blob can be in, w.r.t super encryption.
 /// `Sensitive` holds the non encrypted key and a reference to its super key.
 /// `NonSensitive` holds a non encrypted key that is never supposed to be encrypted.
@@ -1215,8 +1263,8 @@
 
     fn deref(&self) -> &Self::Target {
         match self {
-            Self::Sensitive { key, .. } => &key,
-            Self::NonSensitive(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
deleted file mode 100644
index 6dd3962..0000000
--- a/keystore2/src/try_insert.rs
+++ /dev/null
@@ -1,100 +0,0 @@
-// 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 a110c64..a312c4b 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -15,11 +15,17 @@
 //! This module implements utility functions used by the Keystore 2.0 service
 //! implementation.
 
-use crate::error::{map_binder_status, Error, ErrorCode};
+use crate::error::{map_binder_status, map_km_error, Error, ErrorCode};
+use crate::key_parameter::KeyParameter;
 use crate::permission;
 use crate::permission::{KeyPerm, KeyPermSet, KeystorePerm};
+use crate::{
+    database::{KeyType, KeystoreDB},
+    globals::LEGACY_IMPORTER,
+};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    KeyCharacteristics::KeyCharacteristics, Tag::Tag,
+    IKeyMintDevice::IKeyMintDevice, KeyCharacteristics::KeyCharacteristics,
+    KeyParameter::KeyParameter as KmKeyParameter, Tag::Tag,
 };
 use android_os_permissions_aidl::aidl::android::os::IPermissionController;
 use android_security_apc::aidl::android::security::apc::{
@@ -27,16 +33,17 @@
     ResponseCode::ResponseCode as ApcResponseCode,
 };
 use android_system_keystore2::aidl::android::system::keystore2::{
-    Authorization::Authorization, KeyDescriptor::KeyDescriptor,
+    Authorization::Authorization, Domain::Domain, KeyDescriptor::KeyDescriptor,
 };
-use anyhow::{anyhow, Context};
-use binder::{FromIBinder, SpIBinder, ThreadState};
+use anyhow::{Context, Result};
+use binder::{Strong, ThreadState};
 use keystore2_apc_compat::{
     ApcCompatUiOptions, APC_COMPAT_ERROR_ABORTED, APC_COMPAT_ERROR_CANCELLED,
     APC_COMPAT_ERROR_IGNORED, APC_COMPAT_ERROR_OK, APC_COMPAT_ERROR_OPERATION_PENDING,
     APC_COMPAT_ERROR_SYSTEM_ERROR,
 };
-use std::sync::Mutex;
+use keystore2_crypto::{aes_gcm_decrypt, aes_gcm_encrypt, ZVec};
+use std::iter::IntoIterator;
 
 /// This function uses its namesake in the permission module and in
 /// combination with with_calling_sid from the binder crate to check
@@ -44,7 +51,7 @@
 pub fn check_keystore_permission(perm: KeystorePerm) -> anyhow::Result<()> {
     ThreadState::with_calling_sid(|calling_sid| {
         permission::check_keystore_permission(
-            &calling_sid.ok_or_else(Error::sys).context(
+            calling_sid.ok_or_else(Error::sys).context(
                 "In check_keystore_permission: Cannot check permission without calling_sid.",
             )?,
             perm,
@@ -58,7 +65,7 @@
 pub fn check_grant_permission(access_vec: KeyPermSet, key: &KeyDescriptor) -> anyhow::Result<()> {
     ThreadState::with_calling_sid(|calling_sid| {
         permission::check_grant_permission(
-            &calling_sid.ok_or_else(Error::sys).context(
+            calling_sid.ok_or_else(Error::sys).context(
                 "In check_grant_permission: Cannot check permission without calling_sid.",
             )?,
             access_vec,
@@ -78,7 +85,7 @@
     ThreadState::with_calling_sid(|calling_sid| {
         permission::check_key_permission(
             ThreadState::get_calling_uid(),
-            &calling_sid
+            calling_sid
                 .ok_or_else(Error::sys)
                 .context("In check_key_permission: Cannot check permission without calling_sid.")?,
             perm,
@@ -103,7 +110,7 @@
 /// identifiers. It throws an error if the permissions cannot be verified, or if the caller doesn't
 /// have the right permissions, and returns silently otherwise.
 pub fn check_device_attestation_permissions() -> anyhow::Result<()> {
-    let permission_controller: binder::Strong<dyn IPermissionController::IPermissionController> =
+    let permission_controller: Strong<dyn IPermissionController::IPermissionController> =
         binder::get_interface("permission")?;
 
     let binder_result = {
@@ -128,55 +135,64 @@
     }
 }
 
-/// Thread safe wrapper around SpIBinder. It is safe to have SpIBinder smart pointers to the
-/// same object in multiple threads, but cloning a SpIBinder is not thread safe.
-/// Keystore frequently hands out binder tokens to the security level interface. If this
-/// is to happen from a multi threaded thread pool, the SpIBinder needs to be protected by a
-/// Mutex.
-#[derive(Debug)]
-pub struct Asp(Mutex<SpIBinder>);
-
-impl Asp {
-    /// Creates a new instance owning a SpIBinder wrapped in a Mutex.
-    pub fn new(i: SpIBinder) -> Self {
-        Self(Mutex::new(i))
-    }
-
-    /// Clones the owned SpIBinder and attempts to convert it into the requested interface.
-    pub fn get_interface<T: FromIBinder + ?Sized>(&self) -> anyhow::Result<binder::Strong<T>> {
-        // We can use unwrap here because we never panic when locked, so the mutex
-        // can never be poisoned.
-        let lock = self.0.lock().unwrap();
-        (*lock)
-            .clone()
-            .into_interface()
-            .map_err(|e| anyhow!(format!("get_interface failed with error code {:?}", e)))
-    }
-}
-
-impl Clone for Asp {
-    fn clone(&self) -> Self {
-        let lock = self.0.lock().unwrap();
-        Self(Mutex::new((*lock).clone()))
-    }
-}
-
 /// Converts a set of key characteristics as returned from KeyMint into the internal
 /// representation of the keystore service.
 pub fn key_characteristics_to_internal(
     key_characteristics: Vec<KeyCharacteristics>,
-) -> Vec<crate::key_parameter::KeyParameter> {
+) -> Vec<KeyParameter> {
     key_characteristics
         .into_iter()
         .flat_map(|aidl_key_char| {
             let sec_level = aidl_key_char.securityLevel;
-            aidl_key_char.authorizations.into_iter().map(move |aidl_kp| {
-                crate::key_parameter::KeyParameter::new(aidl_kp.into(), sec_level)
-            })
+            aidl_key_char
+                .authorizations
+                .into_iter()
+                .map(move |aidl_kp| KeyParameter::new(aidl_kp.into(), sec_level))
         })
         .collect()
 }
 
+/// This function can be used to upgrade key blobs on demand. The return value of
+/// `km_op` is inspected and if ErrorCode::KEY_REQUIRES_UPGRADE is encountered,
+/// an attempt is made to upgrade the key blob. On success `new_blob_handler` is called
+/// with the upgraded blob as argument. Then `km_op` is called a second time with the
+/// upgraded blob as argument. On success a tuple of the `km_op`s result and the
+/// optional upgraded blob is returned.
+pub fn upgrade_keyblob_if_required_with<T, KmOp, NewBlobHandler>(
+    km_dev: &dyn IKeyMintDevice,
+    key_blob: &[u8],
+    upgrade_params: &[KmKeyParameter],
+    km_op: KmOp,
+    new_blob_handler: NewBlobHandler,
+) -> Result<(T, Option<Vec<u8>>)>
+where
+    KmOp: Fn(&[u8]) -> Result<T, Error>,
+    NewBlobHandler: FnOnce(&[u8]) -> Result<()>,
+{
+    match km_op(key_blob) {
+        Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
+            let upgraded_blob = {
+                let _wp = watchdog::watch_millis(
+                    "In utils::upgrade_keyblob_if_required_with: calling upgradeKey.",
+                    500,
+                );
+                map_km_error(km_dev.upgradeKey(key_blob, upgrade_params))
+            }
+            .context("In utils::upgrade_keyblob_if_required_with: Upgrade failed.")?;
+
+            new_blob_handler(&upgraded_blob)
+                .context("In utils::upgrade_keyblob_if_required_with: calling new_blob_handler.")?;
+
+            km_op(&upgraded_blob)
+                .map(|v| (v, Some(upgraded_blob)))
+                .context("In utils::upgrade_keyblob_if_required_with: Calling km_op after upgrade.")
+        }
+        r => r
+            .map(|v| (v, None))
+            .context("In utils::upgrade_keyblob_if_required_with: Calling km_op."),
+    }
+}
+
 /// Converts a set of key characteristics from the internal representation into a set of
 /// Authorizations as they are used to convey key characteristics to the clients of keystore.
 pub fn key_parameters_to_authorizations(
@@ -222,16 +238,37 @@
 }
 
 /// AID offset for uid space partitioning.
-pub const AID_USER_OFFSET: u32 = cutils_bindgen::AID_USER_OFFSET;
+pub const AID_USER_OFFSET: u32 = rustutils::users::AID_USER_OFFSET;
 
 /// AID of the keystore process itself, used for keys that
 /// keystore generates for its own use.
-pub const AID_KEYSTORE: u32 = cutils_bindgen::AID_KEYSTORE;
+pub const AID_KEYSTORE: u32 = rustutils::users::AID_KEYSTORE;
 
 /// Extracts the android user from the given uid.
 pub fn uid_to_android_user(uid: u32) -> u32 {
-    // Safety: No memory access
-    unsafe { cutils_bindgen::multiuser_get_user_id(uid) }
+    rustutils::users::multiuser_get_user_id(uid)
+}
+
+/// List all key aliases for a given domain + namespace.
+pub fn list_key_entries(
+    db: &mut KeystoreDB,
+    domain: Domain,
+    namespace: i64,
+) -> Result<Vec<KeyDescriptor>> {
+    let mut result = Vec::new();
+    result.append(
+        &mut LEGACY_IMPORTER
+            .list_uid(domain, namespace)
+            .context("In list_key_entries: Trying to list legacy keys.")?,
+    );
+    result.append(
+        &mut db
+            .list(domain, namespace, KeyType::Client)
+            .context("In list_key_entries: Trying to list keystore database.")?,
+    );
+    result.sort_unstable();
+    result.dedup();
+    Ok(result)
 }
 
 /// This module provides helpers for simplified use of the watchdog module.
@@ -264,6 +301,36 @@
     }
 }
 
+/// Trait implemented by objects that can be used to decrypt cipher text using AES-GCM.
+pub trait AesGcm {
+    /// Deciphers `data` using the initialization vector `iv` and AEAD tag `tag`
+    /// and AES-GCM. The implementation provides the key material and selects
+    /// the implementation variant, e.g., AES128 or AES265.
+    fn decrypt(&self, data: &[u8], iv: &[u8], tag: &[u8]) -> Result<ZVec>;
+
+    /// Encrypts `data` and returns the ciphertext, the initialization vector `iv`
+    /// and AEAD tag `tag`. The implementation provides the key material and selects
+    /// the implementation variant, e.g., AES128 or AES265.
+    fn encrypt(&self, plaintext: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>)>;
+}
+
+/// Marks an object as AES-GCM key.
+pub trait AesGcmKey {
+    /// Provides access to the raw key material.
+    fn key(&self) -> &[u8];
+}
+
+impl<T: AesGcmKey> AesGcm for T {
+    fn decrypt(&self, data: &[u8], iv: &[u8], tag: &[u8]) -> Result<ZVec> {
+        aes_gcm_decrypt(data, iv, tag, self.key())
+            .context("In AesGcm<T>::decrypt: Decryption failed")
+    }
+
+    fn encrypt(&self, plaintext: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>)> {
+        aes_gcm_encrypt(plaintext, self.key()).context("In AesGcm<T>::encrypt: Encryption failed.")
+    }
+}
+
 /// This module provides empty/noop implementations of the watch dog utility functions.
 #[cfg(not(feature = "watchdog"))]
 pub mod watchdog {
diff --git a/keystore2/src/vintf/Android.bp b/keystore2/src/vintf/Android.bp
index 3ab0ec5..34719aa 100644
--- a/keystore2/src/vintf/Android.bp
+++ b/keystore2/src/vintf/Android.bp
@@ -26,39 +26,32 @@
     crate_name: "keystore2_vintf",
     srcs: ["lib.rs"],
     rustlibs: [
-        "libkeystore2_vintf_bindgen",
+        "libcxx",
     ],
     shared_libs: [
+        "libvintf",
+    ],
+    static_libs: [
         "libkeystore2_vintf_cpp",
-        "libvintf",
     ],
 }
 
-cc_library {
+cc_library_static {
     name: "libkeystore2_vintf_cpp",
-    srcs: [
-        "vintf.cpp",
-    ],
+    srcs: ["vintf.cpp"],
+    generated_headers: ["cxx-bridge-header"],
+    generated_sources: ["vintf_bridge_code"],
     shared_libs: [
         "libvintf",
     ],
 }
 
-rust_bindgen {
-    name: "libkeystore2_vintf_bindgen",
-    wrapper_src: "vintf.hpp",
-    crate_name: "keystore2_vintf_bindgen",
-    source_stem: "bindings",
-    host_supported: true,
-    shared_libs: ["libvintf"],
-    bindgen_flags: [
-        "--size_t-is-usize",
-        "--allowlist-function", "getHalNames",
-        "--allowlist-function", "getHalNamesAndVersions",
-        "--allowlist-function", "getHidlInstances",
-        "--allowlist-function", "getAidlInstances",
-        "--allowlist-function", "freeNames",
-    ],
+genrule {
+    name: "vintf_bridge_code",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) >> $(out)",
+    srcs: ["lib.rs"],
+    out: ["vintf_cxx_generated.cc"],
 }
 
 rust_test {
@@ -68,7 +61,7 @@
     test_suites: ["general-tests"],
     auto_gen_config: true,
     rustlibs: [
-        "libkeystore2_vintf_bindgen",
+        "libcxx",
     ],
     static_libs: [
         "libkeystore2_vintf_cpp",
diff --git a/keystore2/src/vintf/lib.rs b/keystore2/src/vintf/lib.rs
index 8730a3e..89e18eb 100644
--- a/keystore2/src/vintf/lib.rs
+++ b/keystore2/src/vintf/lib.rs
@@ -14,96 +14,35 @@
 
 //! Bindings for getting the list of HALs.
 
-use keystore2_vintf_bindgen::{
-    freeNames, getAidlInstances, getHalNames, getHalNamesAndVersions, getHidlInstances,
-};
-use std::ffi::{CStr, CString};
-use std::os::raw::c_char;
-use std::str::Utf8Error;
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        include!("vintf.hpp");
 
-/// A struct that contains a list of HALs (optionally with version numbers).
-/// To use it, call as_vec to get a Vec view of the data it contains.
-pub struct HalNames {
-    data: *mut *mut c_char,
-    len: usize,
-}
+        /// Gets all HAL names.
+        /// Note that this is not a zero-cost shim: it will make copies of the strings.
+        fn get_hal_names() -> Vec<String>;
 
-impl Drop for HalNames {
-    fn drop(&mut self) {
-        // Safety: The memory is allocated by our C shim so it must free it as well.
-        unsafe { freeNames(self.data, self.len) }
+        /// Gets all HAL names and versions.
+        /// Note that this is not a zero-cost shim: it will make copies of the strings.
+        fn get_hal_names_and_versions() -> Vec<String>;
+
+        /// 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.
+        fn get_hidl_instances(
+            package: &str,
+            major_version: usize,
+            minor_version: usize,
+            interface_name: &str,
+        ) -> Vec<String>;
+
+        /// 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.
+        fn get_aidl_instances(package: &str, version: usize, interface_name: &str) -> Vec<String>;
     }
 }
 
-impl<'a> HalNames {
-    /// Get a Vec view of the list of HALs.
-    pub fn as_vec(&'a self) -> Result<Vec<&'a str>, Utf8Error> {
-        // Safety: self.data contains self.len C strings.
-        // The lifetimes ensure that the HalNames (and hence the strings) live
-        // at least as long as the returned vector.
-        unsafe { (0..self.len).map(|i| CStr::from_ptr(*self.data.add(i)).to_str()) }.collect()
-    }
-}
-
-/// Gets all HAL names.
-/// Note that this is not a zero-cost shim: it will make copies of the strings.
-pub fn get_hal_names() -> HalNames {
-    let mut len: usize = 0;
-    // 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 { getHalNames(&mut len) };
-    HalNames { data: raw_strs, len }
-}
-
-/// Gets all HAL names and versions.
-/// Note that this is not a zero-cost shim: it will make copies of the strings.
-pub fn get_hal_names_and_versions() -> HalNames {
-    let mut len: usize = 0;
-    // 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 { getHalNamesAndVersions(&mut len) };
-    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_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.");
-    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 { getAidlInstances(&mut len, packages.as_ptr(), version, interface_name.as_ptr()) };
-    HalNames { data: raw_strs, len }
-}
+pub use ffi::*;
 
 #[cfg(test)]
 mod tests {
@@ -111,17 +50,13 @@
     use super::*;
 
     #[test]
-    fn test() -> Result<(), Utf8Error> {
-        let result = get_hal_names();
-        let names = result.as_vec()?;
+    fn test() {
+        let names = get_hal_names();
         assert_ne!(names.len(), 0);
 
-        let result = get_hal_names_and_versions();
-        let names_and_versions = result.as_vec()?;
+        let names_and_versions = get_hal_names_and_versions();
         assert_ne!(names_and_versions.len(), 0);
 
         assert!(names_and_versions.len() >= names.len());
-
-        Ok(())
     }
 }
diff --git a/keystore2/src/vintf/vintf.cpp b/keystore2/src/vintf/vintf.cpp
index e407efa..00625bf 100644
--- a/keystore2/src/vintf/vintf.cpp
+++ b/keystore2/src/vintf/vintf.cpp
@@ -14,55 +14,43 @@
  * limitations under the License.
  */
 
-#include "vintf.hpp"
-
+#include <algorithm>
 #include <vintf/HalManifest.h>
 #include <vintf/VintfObject.h>
 
-// Converts a set<string> into a C-style array of C strings.
-static char** convert(const std::set<std::string>& names) {
-    char** ret = new char*[names.size()];
-    char** ptr = ret;
-    for (const auto& name : names) {
-        *(ptr++) = strdup(name.c_str());
-    }
-    return ret;
+#include "rust/cxx.h"
+
+rust::Vec<rust::String> convert(const std::set<std::string>& names) {
+    rust::Vec<rust::String> result;
+    std::copy(names.begin(), names.end(), std::back_inserter(result));
+    return result;
 }
 
-char** getHalNames(size_t* len) {
-    auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+rust::Vec<rust::String> get_hal_names() {
+    const auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
     const auto names = manifest->getHalNames();
-    *len = names.size();
     return convert(names);
 }
 
-char** getHalNamesAndVersions(size_t* len) {
-    auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+rust::Vec<rust::String> get_hal_names_and_versions() {
+    const auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
     const auto names = manifest->getHalNamesAndVersions();
-    *len = names.size();
     return convert(names);
 }
 
-char** getHidlInstances(size_t* len, const char* package, size_t major_version,
-                        size_t minor_version, const char* interfaceName) {
+rust::Vec<rust::String> get_hidl_instances(rust::Str package, size_t major_version,
+                                           size_t minor_version, rust::Str 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();
+    const auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+    const auto names = manifest->getHidlInstances(static_cast<std::string>(package), version,
+                                                  static_cast<std::string>(interfaceName));
     return convert(names);
 }
 
-char** getAidlInstances(size_t* len, const char* package, size_t version,
-                        const char* interfaceName) {
-    auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
-    const auto names = manifest->getAidlInstances(package, version, interfaceName);
-    *len = names.size();
+rust::Vec<rust::String> get_aidl_instances(rust::Str package, size_t version,
+                                           rust::Str interfaceName) {
+    const auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+    const auto names = manifest->getAidlInstances(static_cast<std::string>(package), version,
+                                                  static_cast<std::string>(interfaceName));
     return convert(names);
 }
-
-void freeNames(char** names, size_t len) {
-    for (int i = 0; i < len; i++) {
-        free(names[i]);
-    }
-    delete[] names;
-}
diff --git a/keystore2/src/vintf/vintf.hpp b/keystore2/src/vintf/vintf.hpp
index 091e8e8..dbc88f0 100644
--- a/keystore2/src/vintf/vintf.hpp
+++ b/keystore2/src/vintf/vintf.hpp
@@ -14,20 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef __VINTF_H__
-#define __VINTF_H__
+#pragma once
 
-#include <stddef.h>
+#include "rust/cxx.h"
 
-extern "C" {
-
-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);
-}
-
-#endif  //  __VINTF_H__
+rust::Vec<rust::String> get_hal_names();
+rust::Vec<rust::String> get_hal_names_and_versions();
+rust::Vec<rust::String> get_hidl_instances(rust::Str package, size_t major_version,
+                                           size_t minor_version, rust::Str interfaceName);
+rust::Vec<rust::String> get_aidl_instances(rust::Str package, size_t version,
+                                           rust::Str interfaceName);
diff --git a/keystore2/src/watchdog.rs b/keystore2/src/watchdog.rs
index 9cca171..a26b632 100644
--- a/keystore2/src/watchdog.rs
+++ b/keystore2/src/watchdog.rs
@@ -111,11 +111,44 @@
         }
         self.last_report = Instant::now();
         self.has_overdue = has_overdue;
-        log::warn!("Keystore Watchdog report:");
-        log::warn!("Overdue records:");
+        log::warn!("### Keystore Watchdog report - BEGIN ###");
+
         let now = Instant::now();
-        for (i, r) in self.records.iter() {
-            if r.deadline.saturating_duration_since(now) == Duration::new(0, 0) {
+        let mut overdue_records: Vec<(&Index, &Record)> = self
+            .records
+            .iter()
+            .filter(|(_, r)| r.deadline.saturating_duration_since(now) == Duration::new(0, 0))
+            .collect();
+
+        log::warn!("When extracting from a bug report, please include this header");
+        log::warn!("and all {} records below.", overdue_records.len());
+
+        // Watch points can be nested, i.e., a single thread may have multiple armed
+        // watch points. And the most recent on each thread (thread recent) is closest to the point
+        // where something is blocked. Furthermore, keystore2 has various critical section
+        // and common backend resources KeyMint that can only be entered serialized. So if one
+        // thread hangs, the others will soon follow suite. Thus the oldest "thread recent" watch
+        // point is most likely pointing toward the culprit.
+        // Thus, sort by start time first.
+        overdue_records.sort_unstable_by(|(_, r1), (_, r2)| r1.started.cmp(&r2.started));
+        // Then we groups all of the watch points per thread preserving the order within
+        // groups.
+        let groups = overdue_records.iter().fold(
+            HashMap::<thread::ThreadId, Vec<(&Index, &Record)>>::new(),
+            |mut acc, (i, r)| {
+                acc.entry(i.tid).or_default().push((i, r));
+                acc
+            },
+        );
+        // Put the groups back into a vector.
+        let mut groups: Vec<Vec<(&Index, &Record)>> = groups.into_iter().map(|(_, v)| v).collect();
+        // Sort the groups by start time of the most recent (.last()) of each group.
+        // It is panic safe to use unwrap() here because we never add empty vectors to
+        // the map.
+        groups.sort_by(|v1, v2| v1.last().unwrap().1.started.cmp(&v2.last().unwrap().1.started));
+
+        for g in groups.iter() {
+            for (i, r) in g.iter() {
                 match &r.callback {
                     Some(cb) => {
                         log::warn!(
@@ -139,6 +172,7 @@
                 }
             }
         }
+        log::warn!("### Keystore Watchdog report - END ###");
         true
     }
 
diff --git a/keystore2/system_property/Android.bp b/keystore2/system_property/Android.bp
deleted file mode 100644
index 773804d..0000000
--- a/keystore2/system_property/Android.bp
+++ /dev/null
@@ -1,53 +0,0 @@
-// 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.
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "system_security_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["system_security_license"],
-}
-
-rust_bindgen {
-    name: "libkeystore2_system_property_bindgen",
-    wrapper_src: "system_property_bindgen.hpp",
-    crate_name: "keystore2_system_property_bindgen",
-    source_stem: "bindings",
-
-    bindgen_flags: [
-        "--size_t-is-usize",
-        "--allowlist-function=__system_property_find",
-        "--allowlist-function=__system_property_read_callback",
-        "--allowlist-function=__system_property_set",
-        "--allowlist-function=__system_property_wait",
-    ],
-}
-
-rust_library {
-    name: "libkeystore2_system_property-rust",
-    crate_name: "keystore2_system_property",
-    srcs: [
-        "lib.rs",
-    ],
-    rustlibs: [
-        "libanyhow",
-        "libkeystore2_system_property_bindgen",
-        "libthiserror",
-    ],
-    shared_libs: [
-        "libbase",
-    ],
-}
diff --git a/keystore2/system_property/lib.rs b/keystore2/system_property/lib.rs
deleted file mode 100644
index b993c87..0000000
--- a/keystore2/system_property/lib.rs
+++ /dev/null
@@ -1,217 +0,0 @@
-// 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 crate provides the PropertyWatcher type, which watches for changes
-//! in Android system properties.
-
-use anyhow::{anyhow, Context, Result as AnyhowResult};
-use keystore2_system_property_bindgen::prop_info as PropInfo;
-use std::os::raw::c_char;
-use std::ptr::null;
-use std::{
-    ffi::{c_void, CStr, CString},
-    str::Utf8Error,
-};
-use thiserror::Error;
-
-/// Errors this crate can generate
-#[derive(Error, Debug)]
-pub enum PropertyWatcherError {
-    /// We can't watch for a property whose name contains a NUL character.
-    #[error("Cannot convert name to C string")]
-    BadNameError(#[from] std::ffi::NulError),
-    /// We can only watch for properties that exist when the watcher is created.
-    #[error("System property is absent")]
-    SystemPropertyAbsent,
-    /// __system_property_wait timed out despite being given no timeout.
-    #[error("Wait failed")]
-    WaitFailed,
-    /// read callback was not called
-    #[error("__system_property_read_callback did not call callback")]
-    ReadCallbackNotCalled,
-    /// read callback gave us a NULL pointer
-    #[error("__system_property_read_callback gave us a NULL pointer instead of a string")]
-    MissingCString,
-    /// read callback gave us a bad C string
-    #[error("__system_property_read_callback gave us a non-UTF8 C string")]
-    BadCString(#[from] Utf8Error),
-    /// read callback returned an error
-    #[error("Callback failed")]
-    CallbackError(#[from] anyhow::Error),
-    /// Failure in setting the system property
-    #[error("__system_property_set failed.")]
-    SetPropertyFailed,
-}
-
-/// Result type specific for this crate.
-pub type Result<T> = std::result::Result<T, PropertyWatcherError>;
-
-/// PropertyWatcher takes the name of an Android system property such
-/// as `keystore.boot_level`; it can report the current value of this
-/// property, or wait for it to change.
-pub struct PropertyWatcher {
-    prop_name: CString,
-    prop_info: *const PropInfo,
-    serial: keystore2_system_property_bindgen::__uint32_t,
-}
-
-impl PropertyWatcher {
-    /// Create a PropertyWatcher for the named system property.
-    pub fn new(name: &str) -> Result<Self> {
-        Ok(Self { prop_name: CString::new(name)?, prop_info: null(), serial: 0 })
-    }
-
-    // Lazy-initializing accessor for self.prop_info.
-    fn get_prop_info(&mut self) -> Option<*const PropInfo> {
-        if self.prop_info.is_null() {
-            // Unsafe required for FFI call. Input and output are both const.
-            // The returned pointer is valid for the lifetime of the program.
-            self.prop_info = unsafe {
-                keystore2_system_property_bindgen::__system_property_find(self.prop_name.as_ptr())
-            };
-        }
-        if self.prop_info.is_null() {
-            None
-        } else {
-            Some(self.prop_info)
-        }
-    }
-
-    fn read_raw(prop_info: *const PropInfo, mut f: impl FnOnce(Option<&CStr>, Option<&CStr>)) {
-        // Unsafe function converts values passed to us by
-        // __system_property_read_callback to Rust form
-        // and pass them to inner callback.
-        unsafe extern "C" fn callback(
-            res_p: *mut c_void,
-            name: *const c_char,
-            value: *const c_char,
-            _: keystore2_system_property_bindgen::__uint32_t,
-        ) {
-            let name = if name.is_null() { None } else { Some(CStr::from_ptr(name)) };
-            let value = if value.is_null() { None } else { Some(CStr::from_ptr(value)) };
-            let f = &mut *res_p.cast::<&mut dyn FnMut(Option<&CStr>, Option<&CStr>)>();
-            f(name, value);
-        }
-
-        let mut f: &mut dyn FnOnce(Option<&CStr>, Option<&CStr>) = &mut f;
-
-        // Unsafe block for FFI call. We convert the FnOnce
-        // to a void pointer, and unwrap it in our callback.
-        unsafe {
-            keystore2_system_property_bindgen::__system_property_read_callback(
-                prop_info,
-                Some(callback),
-                &mut f as *mut _ as *mut c_void,
-            )
-        }
-    }
-
-    /// Call the passed function, passing it the name and current value
-    /// of this system property. See documentation for
-    /// `__system_property_read_callback` for details.
-    /// Returns an error if the property is empty or doesn't exist.
-    pub fn read<T, F>(&mut self, f: F) -> Result<T>
-    where
-        F: FnOnce(&str, &str) -> anyhow::Result<T>,
-    {
-        let prop_info = self.get_prop_info().ok_or(PropertyWatcherError::SystemPropertyAbsent)?;
-        let mut result = Err(PropertyWatcherError::ReadCallbackNotCalled);
-        Self::read_raw(prop_info, |name, value| {
-            // use a wrapping closure as an erzatz try block.
-            result = (|| {
-                let name = name.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
-                let value = value.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
-                f(name, value).map_err(PropertyWatcherError::CallbackError)
-            })()
-        });
-        result
-    }
-
-    // Waits for the property that self is watching to be created. Returns immediately if the
-    // property already exists.
-    fn wait_for_property_creation(&mut self) -> Result<()> {
-        let mut global_serial = 0;
-        loop {
-            match self.get_prop_info() {
-                Some(_) => return Ok(()),
-                None => {
-                    // Unsafe call for FFI. The function modifies only global_serial, and has
-                    // no side-effects.
-                    if !unsafe {
-                        // Wait for a global serial number change, then try again. On success,
-                        // the function will update global_serial with the last version seen.
-                        keystore2_system_property_bindgen::__system_property_wait(
-                            null(),
-                            global_serial,
-                            &mut global_serial,
-                            null(),
-                        )
-                    } {
-                        return Err(PropertyWatcherError::WaitFailed);
-                    }
-                }
-            }
-        }
-    }
-
-    /// Wait for the system property to change. This
-    /// records the serial number of the last change, so
-    /// race conditions are avoided.
-    pub fn wait(&mut self) -> Result<()> {
-        // If the property is null, then wait for it to be created. Subsequent waits will
-        // skip this step and wait for our specific property to change.
-        if self.prop_info.is_null() {
-            return self.wait_for_property_creation();
-        }
-
-        let mut new_serial = self.serial;
-        // Unsafe block to call __system_property_wait.
-        // All arguments are private to PropertyWatcher so we
-        // can be confident they are valid.
-        if !unsafe {
-            keystore2_system_property_bindgen::__system_property_wait(
-                self.prop_info,
-                self.serial,
-                &mut new_serial,
-                null(),
-            )
-        } {
-            return Err(PropertyWatcherError::WaitFailed);
-        }
-        self.serial = new_serial;
-        Ok(())
-    }
-}
-
-/// Writes a system property.
-pub fn write(name: &str, value: &str) -> AnyhowResult<()> {
-    if
-    // Unsafe required for FFI call. Input and output are both const and valid strings.
-    unsafe {
-        // If successful, __system_property_set returns 0, otherwise, returns -1.
-        keystore2_system_property_bindgen::__system_property_set(
-            CString::new(name)
-                .context("In keystore2::system_property::write: Construction CString from name.")?
-                .as_ptr(),
-            CString::new(value)
-                .context("In keystore2::system_property::write: Constructing CString from value.")?
-                .as_ptr(),
-        )
-    } == 0
-    {
-        Ok(())
-    } else {
-        Err(anyhow!(PropertyWatcherError::SetPropertyFailed))
-    }
-}
diff --git a/keystore2/test_utils/lib.rs b/keystore2/test_utils/lib.rs
index 627af20..a355544 100644
--- a/keystore2/test_utils/lib.rs
+++ b/keystore2/test_utils/lib.rs
@@ -19,6 +19,8 @@
 use std::path::{Path, PathBuf};
 use std::{env::temp_dir, ops::Deref};
 
+pub mod run_as;
+
 /// Represents the lifecycle of a temporary directory for testing.
 #[derive(Debug)]
 pub struct TempDir {
diff --git a/keystore2/test_utils/run_as.rs b/keystore2/test_utils/run_as.rs
new file mode 100644
index 0000000..d42303d
--- /dev/null
+++ b/keystore2/test_utils/run_as.rs
@@ -0,0 +1,191 @@
+// 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 is intended for testing access control enforcement of services such as keystore2,
+//! by assuming various identities with varying levels of privilege. Consequently, appropriate
+//! privileges are required, or the attempt will fail causing a panic.
+//! The `run_as` module provides the function `run_as`, which takes a UID, GID, an SELinux
+//! context, and a closure. The return type of the closure, which is also the return type of
+//! `run_as`, must implement `serde::Serialize` and `serde::Deserialize`.
+//! `run_as` forks, transitions to the given identity, and executes the closure in the newly
+//! forked process. If the closure returns, i.e., does not panic, the forked process exits with
+//! a status of `0`, and the return value is serialized and sent through a pipe to the parent where
+//! it gets deserialized and returned. The STDIO is not changed and the parent's panic handler
+//! remains unchanged. So if the closure panics, the panic message is printed on the parent's STDERR
+//! and the exit status is set to a non `0` value. The latter causes the parent to panic as well,
+//! and if run in a test context, the test to fail.
+
+use keystore2_selinux as selinux;
+use nix::sys::wait::{waitpid, WaitStatus};
+use nix::unistd::{
+    close, fork, pipe as nix_pipe, read as nix_read, setgid, setuid, write as nix_write,
+    ForkResult, Gid, Uid,
+};
+use serde::{de::DeserializeOwned, Serialize};
+use std::os::unix::io::RawFd;
+
+fn transition(se_context: selinux::Context, uid: Uid, gid: Gid) {
+    setgid(gid).expect("Failed to set GID. This test might need more privileges.");
+    setuid(uid).expect("Failed to set UID. This test might need more privileges.");
+
+    selinux::setcon(&se_context)
+        .expect("Failed to set SELinux context. This test might need more privileges.");
+}
+
+/// PipeReader is a simple wrapper around raw pipe file descriptors.
+/// It takes ownership of the file descriptor and closes it on drop. It provides `read_all`, which
+/// reads from the pipe into an expending vector, until no more data can be read.
+struct PipeReader(RawFd);
+
+impl PipeReader {
+    pub fn read_all(&self) -> Result<Vec<u8>, nix::Error> {
+        let mut buffer = [0u8; 128];
+        let mut result = Vec::<u8>::new();
+        loop {
+            let bytes = nix_read(self.0, &mut buffer)?;
+            if bytes == 0 {
+                return Ok(result);
+            }
+            result.extend_from_slice(&buffer[0..bytes]);
+        }
+    }
+}
+
+impl Drop for PipeReader {
+    fn drop(&mut self) {
+        close(self.0).expect("Failed to close reader pipe fd.");
+    }
+}
+
+/// PipeWriter is a simple wrapper around raw pipe file descriptors.
+/// It takes ownership of the file descriptor and closes it on drop. It provides `write`, which
+/// writes the given buffer into the pipe, returning the number of bytes written.
+struct PipeWriter(RawFd);
+
+impl PipeWriter {
+    pub fn write(&self, data: &[u8]) -> Result<usize, nix::Error> {
+        nix_write(self.0, data)
+    }
+}
+
+impl Drop for PipeWriter {
+    fn drop(&mut self) {
+        close(self.0).expect("Failed to close writer pipe fd.");
+    }
+}
+
+fn pipe() -> Result<(PipeReader, PipeWriter), nix::Error> {
+    let (read_fd, write_fd) = nix_pipe()?;
+    Ok((PipeReader(read_fd), PipeWriter(write_fd)))
+}
+
+/// Run the given closure in a new process running with the new identity given as
+/// `uid`, `gid`, and `se_context`.
+pub fn run_as<F, R>(se_context: &str, uid: Uid, gid: Gid, f: F) -> R
+where
+    R: Serialize + DeserializeOwned,
+    F: 'static + Send + FnOnce() -> R,
+{
+    let se_context =
+        selinux::Context::new(se_context).expect("Unable to construct selinux::Context.");
+    let (reader, writer) = pipe().expect("Failed to create pipe.");
+
+    match unsafe { fork() } {
+        Ok(ForkResult::Parent { child, .. }) => {
+            drop(writer);
+            let status = waitpid(child, None).expect("Failed while waiting for child.");
+            if let WaitStatus::Exited(_, 0) = status {
+                // Child exited successfully.
+                // Read the result from the pipe.
+                let serialized_result =
+                    reader.read_all().expect("Failed to read result from child.");
+
+                // Deserialize the result and return it.
+                serde_cbor::from_slice(&serialized_result).expect("Failed to deserialize result.")
+            } else {
+                panic!("Child did not exit as expected {:?}", status);
+            }
+        }
+        Ok(ForkResult::Child) => {
+            // This will panic on error or insufficient privileges.
+            transition(se_context, uid, gid);
+
+            // Run the closure.
+            let result = f();
+
+            // Serialize the result of the closure.
+            let vec = serde_cbor::to_vec(&result).expect("Result serialization failed");
+
+            // Send the result to the parent using the pipe.
+            writer.write(&vec).expect("Failed to send serialized result to parent.");
+
+            // Set exit status to `0`.
+            std::process::exit(0);
+        }
+        Err(errno) => {
+            panic!("Failed to fork: {:?}", errno);
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use keystore2_selinux as selinux;
+    use nix::unistd::{getgid, getuid};
+    use serde::{Deserialize, Serialize};
+
+    /// This test checks that the closure does not produce an exit status of `0` when run inside a
+    /// test and the closure panics. This would mask test failures as success.
+    #[test]
+    #[should_panic]
+    fn test_run_as_panics_on_closure_panic() {
+        run_as(selinux::getcon().unwrap().to_str().unwrap(), getuid(), getgid(), || {
+            panic!("Closure must panic.")
+        });
+    }
+
+    static TARGET_UID: Uid = Uid::from_raw(10020);
+    static TARGET_GID: Gid = Gid::from_raw(10020);
+    static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+    /// Tests that the closure is running as the target identity.
+    #[test]
+    fn test_transition_to_untrusted_app() {
+        run_as(TARGET_CTX, TARGET_UID, TARGET_GID, || {
+            assert_eq!(TARGET_UID, getuid());
+            assert_eq!(TARGET_GID, getgid());
+            assert_eq!(TARGET_CTX, selinux::getcon().unwrap().to_str().unwrap());
+        });
+    }
+
+    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
+    struct SomeResult {
+        a: u32,
+        b: u64,
+        c: String,
+    }
+
+    #[test]
+    fn test_serialized_result() {
+        let test_result = SomeResult {
+            a: 5,
+            b: 0xffffffffffffffff,
+            c: "supercalifragilisticexpialidocious".to_owned(),
+        };
+        let test_result_clone = test_result.clone();
+        let result = run_as(TARGET_CTX, TARGET_UID, TARGET_GID, || test_result_clone);
+        assert_eq!(test_result, result);
+    }
+}
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
index 432e585..bdc94b7 100644
--- a/ondevice-signing/Android.bp
+++ b/ondevice-signing/Android.bp
@@ -11,8 +11,6 @@
 // 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.
-// List of clang-tidy checks that are reported as errors.
-// Please keep this list ordered lexicographically.
 
 package {
     // See: http://go/android-license-faq
@@ -23,6 +21,8 @@
     default_applicable_licenses: ["system_security_license"],
 }
 
+// List of clang-tidy checks that are reported as errors.
+// Please keep this list ordered lexicographically.
 tidy_errors = [
   "cert-err34-c",
   "google-default-arguments",
@@ -74,6 +74,33 @@
   ],
 }
 
+cc_library {
+  name: "libsigningutils",
+  defaults: [
+    "odsign_flags_defaults",
+  ],
+  cpp_std: "experimental",
+  srcs: [
+    "CertUtils.cpp",
+    "VerityUtils.cpp",
+  ],
+
+  static_libs: [
+    "libc++fs",
+  ],
+
+  shared_libs: [
+    "libbase",
+    "libcrypto",
+    "libcrypto_utils",
+    "libfsverity",
+    "libprotobuf-cpp-lite",
+    "libutils",
+  ],
+  export_include_dirs: ["include"],
+  recovery_available: true,
+}
+
 cc_binary {
   name: "odsign",
   defaults: [
@@ -82,20 +109,18 @@
   cpp_std: "experimental",
   init_rc: ["odsign.rc"],
   srcs: [
-    "odsign_main.cpp",
-    "CertUtils.cpp",
     "KeystoreKey.cpp",
     "KeystoreHmacKey.cpp",
-    "VerityUtils.cpp",
+    "odsign_main.cpp",
   ],
 
   header_libs: ["odrefresh_headers"],
 
   static_libs: [
     "libc++fs",
+    "libsigningutils",
     "lib_odsign_proto",
   ],
-
   shared_libs: [
     "android.system.keystore2-V1-cpp",
     "android.hardware.security.keymint-V1-cpp",
@@ -105,7 +130,7 @@
     "libcrypto_utils",
     "libfsverity",
     "liblogwrap",
-    "libprotobuf-cpp-full",
+    "libprotobuf-cpp-lite",
     "libutils",
   ],
 }
diff --git a/ondevice-signing/CertUtils.cpp b/ondevice-signing/CertUtils.cpp
index b0b75a6..8fe0816 100644
--- a/ondevice-signing/CertUtils.cpp
+++ b/ondevice-signing/CertUtils.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "CertUtils.h"
+
 #include <android-base/logging.h>
 #include <android-base/result.h>
 
@@ -21,54 +23,126 @@
 #include <openssl/crypto.h>
 #include <openssl/pkcs7.h>
 #include <openssl/rsa.h>
+#include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
-#include <fcntl.h>
+#include <optional>
 #include <vector>
 
 #include "KeyConstants.h"
 
-const char kBasicConstraints[] = "CA:TRUE";
-const char kKeyUsage[] = "critical,keyCertSign,cRLSign,digitalSignature";
-const char kSubjectKeyIdentifier[] = "hash";
+// Common properties for all of our certificates.
 constexpr int kCertLifetimeSeconds = 10 * 365 * 24 * 60 * 60;
+const char* const kIssuerCountry = "US";
+const char* const kIssuerOrg = "Android";
 
-using android::base::Result;
-// using android::base::ErrnoError;
+using android::base::ErrnoError;
 using android::base::Error;
+using android::base::Result;
 
-static bool add_ext(X509* cert, int nid, const char* value) {
-    size_t len = strlen(value) + 1;
-    std::vector<char> mutableValue(value, value + len);
-    X509V3_CTX context;
+static Result<bssl::UniquePtr<X509>> loadX509(const std::string& path) {
+    X509* rawCert;
+    auto f = fopen(path.c_str(), "re");
+    if (f == nullptr) {
+        return Error() << "Failed to open " << path;
+    }
+    if (!d2i_X509_fp(f, &rawCert)) {
+        fclose(f);
+        return Error() << "Unable to decode x509 cert at " << path;
+    }
+    bssl::UniquePtr<X509> cert(rawCert);
 
-    X509V3_set_ctx_nodb(&context);
+    fclose(f);
+    return cert;
+}
 
-    X509V3_set_ctx(&context, cert, cert, nullptr, nullptr, 0);
-    X509_EXTENSION* ex = X509V3_EXT_nconf_nid(nullptr, &context, nid, mutableValue.data());
+static X509V3_CTX makeContext(X509* issuer, X509* subject) {
+    X509V3_CTX context = {};
+    X509V3_set_ctx(&context, issuer, subject, nullptr, nullptr, 0);
+    return context;
+}
+
+static bool add_ext(X509V3_CTX* context, X509* cert, int nid, const char* value) {
+    bssl::UniquePtr<X509_EXTENSION> ex(X509V3_EXT_nconf_nid(nullptr, context, nid, value));
     if (!ex) {
         return false;
     }
 
-    X509_add_ext(cert, ex, -1);
-    X509_EXTENSION_free(ex);
+    X509_add_ext(cert, ex.get(), -1);
     return true;
 }
 
-Result<bssl::UniquePtr<RSA>> getRsa(const std::vector<uint8_t>& publicKey) {
-    bssl::UniquePtr<RSA> rsaPubkey(RSA_new());
-    rsaPubkey->n = BN_new();
-    rsaPubkey->e = BN_new();
+static void addNameEntry(X509_NAME* name, const char* field, const char* value) {
+    X509_NAME_add_entry_by_txt(name, field, MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>(value), -1, -1, 0);
+}
 
-    BN_bin2bn(publicKey.data(), publicKey.size(), rsaPubkey->n);
-    BN_set_word(rsaPubkey->e, kRsaKeyExponent);
+static Result<bssl::UniquePtr<RSA>> getRsaFromModulus(const std::vector<uint8_t>& publicKey) {
+    bssl::UniquePtr<BIGNUM> n(BN_new());
+    bssl::UniquePtr<BIGNUM> e(BN_new());
+    bssl::UniquePtr<RSA> rsaPubkey(RSA_new());
+    if (!n || !e || !rsaPubkey || !BN_bin2bn(publicKey.data(), publicKey.size(), n.get()) ||
+        !BN_set_word(e.get(), kRsaKeyExponent) ||
+        !RSA_set0_key(rsaPubkey.get(), n.get(), e.get(), /*d=*/nullptr)) {
+        return Error() << "Failed to create RSA key";
+    }
+    // RSA_set0_key takes ownership of |n| and |e| on success.
+    (void)n.release();
+    (void)e.release();
 
     return rsaPubkey;
 }
 
+static Result<bssl::UniquePtr<RSA>>
+getRsaFromRsaPublicKey(const std::vector<uint8_t>& rsaPublicKey) {
+    auto derBytes = rsaPublicKey.data();
+    bssl::UniquePtr<RSA> rsaKey(d2i_RSAPublicKey(nullptr, &derBytes, rsaPublicKey.size()));
+    if (rsaKey.get() == nullptr) {
+        return Error() << "Failed to parse RsaPublicKey";
+    }
+    if (derBytes != rsaPublicKey.data() + rsaPublicKey.size()) {
+        return Error() << "Key has unexpected trailing data";
+    }
+
+    return rsaKey;
+}
+
+static Result<bssl::UniquePtr<EVP_PKEY>> modulusToRsaPkey(const std::vector<uint8_t>& publicKey) {
+    // "publicKey" corresponds to the raw public key bytes - need to create
+    // a new RSA key with the correct exponent.
+    auto rsaPubkey = getRsaFromModulus(publicKey);
+    if (!rsaPubkey.ok()) {
+        return rsaPubkey.error();
+    }
+
+    bssl::UniquePtr<EVP_PKEY> public_key(EVP_PKEY_new());
+    if (!EVP_PKEY_assign_RSA(public_key.get(), rsaPubkey->release())) {
+        return Error() << "Failed to assign key";
+    }
+    return public_key;
+}
+
+static Result<bssl::UniquePtr<EVP_PKEY>>
+rsaPublicKeyToRsaPkey(const std::vector<uint8_t>& rsaPublicKey) {
+    // rsaPublicKey contains both modulus and exponent, DER-encoded.
+    auto rsaKey = getRsaFromRsaPublicKey(rsaPublicKey);
+    if (!rsaKey.ok()) {
+        return rsaKey.error();
+    }
+
+    bssl::UniquePtr<EVP_PKEY> public_key(EVP_PKEY_new());
+    if (!EVP_PKEY_assign_RSA(public_key.get(), rsaKey->release())) {
+        return Error() << "Failed to assign key";
+    }
+    return public_key;
+}
+
 Result<void> verifySignature(const std::string& message, const std::string& signature,
                              const std::vector<uint8_t>& publicKey) {
-    auto rsaKey = getRsa(publicKey);
+    auto rsaKey = getRsaFromModulus(publicKey);
+    if (!rsaKey.ok()) {
+        return rsaKey.error();
+    }
     uint8_t hashBuf[SHA256_DIGEST_LENGTH];
     SHA256(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(message.c_str())),
            message.length(), hashBuf);
@@ -77,85 +151,157 @@
                               (const uint8_t*)signature.c_str(), signature.length(), rsaKey->get());
 
     if (!success) {
-        return Error() << "Failed to verify signature.";
+        return Error() << "Failed to verify signature";
     }
     return {};
 }
 
+Result<void> verifyRsaPublicKeySignature(const std::string& message, const std::string& signature,
+                                         const std::vector<uint8_t>& rsaPublicKey) {
+    auto rsaKey = getRsaFromRsaPublicKey(rsaPublicKey);
+    if (!rsaKey.ok()) {
+        return rsaKey.error();
+    }
+
+    uint8_t hashBuf[SHA256_DIGEST_LENGTH];
+    SHA256(reinterpret_cast<const uint8_t*>(message.data()), message.size(), hashBuf);
+
+    bool success = RSA_verify(NID_sha256, hashBuf, sizeof(hashBuf),
+                              reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
+                              rsaKey->get());
+    if (!success) {
+        return Error() << "Failed to verify signature";
+    }
+    return {};
+}
+
+static Result<void> createCertificate(
+    const CertSubject& subject, EVP_PKEY* publicKey,
+    const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
+    const std::optional<std::string>& issuerCertPath, const std::string& path) {
+
+    // If an issuer cert is specified, we are signing someone else's key.
+    // Otherwise we are signing our key - a self-signed certificate.
+    bool selfSigned = !issuerCertPath;
+
+    bssl::UniquePtr<X509> x509(X509_new());
+    if (!x509) {
+        return Error() << "Unable to allocate x509 container";
+    }
+    X509_set_version(x509.get(), 2);
+    X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
+    X509_gmtime_adj(X509_get_notAfter(x509.get()), kCertLifetimeSeconds);
+    ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), subject.serialNumber);
+
+    bssl::UniquePtr<X509_ALGOR> algor(X509_ALGOR_new());
+    if (!algor ||
+        !X509_ALGOR_set0(algor.get(), OBJ_nid2obj(NID_sha256WithRSAEncryption), V_ASN1_NULL,
+                         NULL) ||
+        !X509_set1_signature_algo(x509.get(), algor.get())) {
+        return Error() << "Unable to set x509 signature algorithm";
+    }
+
+    if (!X509_set_pubkey(x509.get(), publicKey)) {
+        return Error() << "Unable to set x509 public key";
+    }
+
+    X509_NAME* subjectName = X509_get_subject_name(x509.get());
+    if (!subjectName) {
+        return Error() << "Unable to get x509 subject name";
+    }
+    addNameEntry(subjectName, "C", kIssuerCountry);
+    addNameEntry(subjectName, "O", kIssuerOrg);
+    addNameEntry(subjectName, "CN", subject.commonName);
+
+    if (selfSigned) {
+        if (!X509_set_issuer_name(x509.get(), subjectName)) {
+            return Error() << "Unable to set x509 issuer name";
+        }
+    } else {
+        X509_NAME* issuerName = X509_get_issuer_name(x509.get());
+        if (!issuerName) {
+            return Error() << "Unable to get x509 issuer name";
+        }
+        addNameEntry(issuerName, "C", kIssuerCountry);
+        addNameEntry(issuerName, "O", kIssuerOrg);
+        addNameEntry(issuerName, "CN", kRootSubject.commonName);
+    }
+
+    // Beware: context contains a pointer to issuerCert, so we need to keep it alive.
+    bssl::UniquePtr<X509> issuerCert;
+    X509V3_CTX context;
+
+    if (selfSigned) {
+        context = makeContext(x509.get(), x509.get());
+    } else {
+        auto certStatus = loadX509(*issuerCertPath);
+        if (!certStatus.ok()) {
+            return Error() << "Unable to load issuer cert: " << certStatus.error();
+        }
+        issuerCert = std::move(certStatus.value());
+        context = makeContext(issuerCert.get(), x509.get());
+    }
+
+    // If it's a self-signed cert we use it for signing certs, otherwise only for signing data.
+    const char* basicConstraints = selfSigned ? "CA:TRUE" : "CA:FALSE";
+    const char* keyUsage =
+        selfSigned ? "critical,keyCertSign,cRLSign,digitalSignature" : "critical,digitalSignature";
+
+    add_ext(&context, x509.get(), NID_basic_constraints, basicConstraints);
+    add_ext(&context, x509.get(), NID_key_usage, keyUsage);
+    add_ext(&context, x509.get(), NID_subject_key_identifier, "hash");
+    add_ext(&context, x509.get(), NID_authority_key_identifier, "keyid:always");
+
+    // Get the data to be signed
+    unsigned char* to_be_signed_buf(nullptr);
+    size_t to_be_signed_length = i2d_re_X509_tbs(x509.get(), &to_be_signed_buf);
+
+    auto signed_data = signFunction(
+        std::string(reinterpret_cast<const char*>(to_be_signed_buf), to_be_signed_length));
+    if (!signed_data.ok()) {
+        return signed_data.error();
+    }
+
+    if (!X509_set1_signature_value(x509.get(),
+                                   reinterpret_cast<const uint8_t*>(signed_data->data()),
+                                   signed_data->size())) {
+        return Error() << "Unable to set x509 signature";
+    }
+
+    auto f = fopen(path.c_str(), "wbe");
+    if (f == nullptr) {
+        return ErrnoError() << "Failed to open " << path;
+    }
+    i2d_X509_fp(f, x509.get());
+    if (fclose(f) != 0) {
+        return ErrnoError() << "Failed to close " << path;
+    }
+
+    return {};
+}
+
 Result<void> createSelfSignedCertificate(
     const std::vector<uint8_t>& publicKey,
     const std::function<Result<std::string>(const std::string&)>& signFunction,
     const std::string& path) {
-    bssl::UniquePtr<X509> x509(X509_new());
-    if (!x509) {
-        return Error() << "Unable to allocate x509 container";
-    }
-    X509_set_version(x509.get(), 2);
-
-    ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 1);
-    X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
-    X509_gmtime_adj(X509_get_notAfter(x509.get()), kCertLifetimeSeconds);
-
-    // "publicKey" corresponds to the raw public key bytes - need to create
-    // a new RSA key with the correct exponent.
-    auto rsaPubkey = getRsa(publicKey);
-
-    EVP_PKEY* public_key = EVP_PKEY_new();
-    EVP_PKEY_assign_RSA(public_key, rsaPubkey->release());
-
-    if (!X509_set_pubkey(x509.get(), public_key)) {
-        return Error() << "Unable to set x509 public key";
+    auto rsa_pkey = modulusToRsaPkey(publicKey);
+    if (!rsa_pkey.ok()) {
+        return rsa_pkey.error();
     }
 
-    X509_NAME* name = X509_get_subject_name(x509.get());
-    if (!name) {
-        return Error() << "Unable to get x509 subject name";
-    }
-    X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
-                               reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
-    X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
-                               reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
-    X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
-                               reinterpret_cast<const unsigned char*>("ODS"), -1, -1, 0);
-    if (!X509_set_issuer_name(x509.get(), name)) {
-        return Error() << "Unable to set x509 issuer name";
+    return createCertificate(kRootSubject, rsa_pkey.value().get(), signFunction, {}, path);
+}
+
+android::base::Result<void> createLeafCertificate(
+    const CertSubject& subject, const std::vector<uint8_t>& rsaPublicKey,
+    const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
+    const std::string& issuerCertPath, const std::string& path) {
+    auto rsa_pkey = rsaPublicKeyToRsaPkey(rsaPublicKey);
+    if (!rsa_pkey.ok()) {
+        return rsa_pkey.error();
     }
 
-    add_ext(x509.get(), NID_basic_constraints, kBasicConstraints);
-    add_ext(x509.get(), NID_key_usage, kKeyUsage);
-    add_ext(x509.get(), NID_subject_key_identifier, kSubjectKeyIdentifier);
-    add_ext(x509.get(), NID_authority_key_identifier, "keyid:always");
-
-    X509_ALGOR_set0(x509->cert_info->signature, OBJ_nid2obj(NID_sha256WithRSAEncryption),
-                    V_ASN1_NULL, NULL);
-    X509_ALGOR_set0(x509->sig_alg, OBJ_nid2obj(NID_sha256WithRSAEncryption), V_ASN1_NULL, NULL);
-
-    // Get the data to be signed
-    char* to_be_signed_buf(nullptr);
-    size_t to_be_signed_length = i2d_re_X509_tbs(x509.get(), (unsigned char**)&to_be_signed_buf);
-
-    auto signed_data = signFunction(std::string(to_be_signed_buf, to_be_signed_length));
-    if (!signed_data.ok()) {
-        return signed_data.error();
-    }
-
-    // This is the only part that doesn't use boringssl default functions - we manually copy in the
-    // signature that was provided to us.
-    x509->signature->data = (unsigned char*)OPENSSL_malloc(signed_data->size());
-    memcpy(x509->signature->data, signed_data->c_str(), signed_data->size());
-    x509->signature->length = signed_data->size();
-
-    x509->signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
-    x509->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
-    auto f = fopen(path.c_str(), "wbe");
-    if (f == nullptr) {
-        return Error() << "Failed to open " << path;
-    }
-    i2d_X509_fp(f, x509.get());
-    fclose(f);
-
-    EVP_PKEY_free(public_key);
-    return {};
+    return createCertificate(subject, rsa_pkey.value().get(), signFunction, issuerCertPath, path);
 }
 
 Result<std::vector<uint8_t>> extractPublicKey(EVP_PKEY* pkey) {
@@ -163,15 +309,14 @@
         return Error() << "Failed to extract public key from x509 cert";
     }
 
-    if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
+    if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA) {
         return Error() << "The public key is not an RSA key";
     }
 
-    RSA* rsa = EVP_PKEY_get1_RSA(pkey);
-    auto num_bytes = BN_num_bytes(rsa->n);
+    RSA* rsa = EVP_PKEY_get0_RSA(pkey);
+    auto num_bytes = BN_num_bytes(RSA_get0_n(rsa));
     std::vector<uint8_t> pubKey(num_bytes);
-    int res = BN_bn2bin(rsa->n, pubKey.data());
-    RSA_free(rsa);
+    int res = BN_bn2bin(RSA_get0_n(rsa), pubKey.data());
 
     if (!res) {
         return Error() << "Failed to convert public key to bytes";
@@ -183,14 +328,14 @@
 Result<std::vector<uint8_t>>
 extractPublicKeyFromSubjectPublicKeyInfo(const std::vector<uint8_t>& keyData) {
     auto keyDataBytes = keyData.data();
-    EVP_PKEY* public_key = d2i_PUBKEY(nullptr, &keyDataBytes, keyData.size());
+    bssl::UniquePtr<EVP_PKEY> public_key(d2i_PUBKEY(nullptr, &keyDataBytes, keyData.size()));
 
-    return extractPublicKey(public_key);
+    return extractPublicKey(public_key.get());
 }
 
-Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::vector<uint8_t>& keyData) {
-    auto keyDataBytes = keyData.data();
-    bssl::UniquePtr<X509> decoded_cert(d2i_X509(nullptr, &keyDataBytes, keyData.size()));
+Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::vector<uint8_t>& derCert) {
+    auto derCertBytes = derCert.data();
+    bssl::UniquePtr<X509> decoded_cert(d2i_X509(nullptr, &derCertBytes, derCert.size()));
     if (decoded_cert.get() == nullptr) {
         return Error() << "Failed to decode X509 certificate.";
     }
@@ -200,21 +345,75 @@
 }
 
 Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path) {
-    X509* cert;
-    auto f = fopen(path.c_str(), "re");
-    if (f == nullptr) {
-        return Error() << "Failed to open " << path;
+    auto cert = loadX509(path);
+    if (!cert.ok()) {
+        return cert.error();
     }
-    if (!d2i_X509_fp(f, &cert)) {
-        fclose(f);
-        return Error() << "Unable to decode x509 cert at " << path;
-    }
-
-    fclose(f);
-    return extractPublicKey(X509_get_pubkey(cert));
+    return extractPublicKey(X509_get_pubkey(cert.value().get()));
 }
 
-Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signed_digest) {
+static Result<std::vector<uint8_t>> extractRsaPublicKey(EVP_PKEY* pkey) {
+    RSA* rsa = EVP_PKEY_get0_RSA(pkey);
+    if (rsa == nullptr) {
+        return Error() << "The public key is not an RSA key";
+    }
+
+    uint8_t* out = nullptr;
+    int size = i2d_RSAPublicKey(rsa, &out);
+    if (size < 0 || !out) {
+        return Error() << "Failed to convert to RSAPublicKey";
+    }
+
+    bssl::UniquePtr<uint8_t> buffer(out);
+    std::vector<uint8_t> result(out, out + size);
+    return result;
+}
+
+Result<CertInfo> verifyAndExtractCertInfoFromX509(const std::string& path,
+                                                  const std::vector<uint8_t>& publicKey) {
+    auto public_key = modulusToRsaPkey(publicKey);
+    if (!public_key.ok()) {
+        return public_key.error();
+    }
+
+    auto cert = loadX509(path);
+    if (!cert.ok()) {
+        return cert.error();
+    }
+    X509* x509 = cert.value().get();
+
+    // Make sure we signed it.
+    if (X509_verify(x509, public_key.value().get()) != 1) {
+        return Error() << "Failed to verify certificate.";
+    }
+
+    bssl::UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(x509));
+    auto subject_key = extractRsaPublicKey(pkey.get());
+    if (!subject_key.ok()) {
+        return subject_key.error();
+    }
+
+    // The pointers here are all owned by x509, and each function handles an
+    // error return from the previous call correctly.
+    X509_NAME* name = X509_get_subject_name(x509);
+    int index = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
+    X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, index);
+    ASN1_STRING* asn1cn = X509_NAME_ENTRY_get_data(entry);
+    unsigned char* utf8cn;
+    int length = ASN1_STRING_to_UTF8(&utf8cn, asn1cn);
+    if (length < 0) {
+        return Error() << "Failed to read subject CN";
+    }
+
+    bssl::UniquePtr<unsigned char> utf8owner(utf8cn);
+    std::string cn(reinterpret_cast<char*>(utf8cn), static_cast<size_t>(length));
+
+    CertInfo cert_info{std::move(cn), std::move(subject_key.value())};
+    return cert_info;
+}
+
+Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signed_digest,
+                                         const CertSubject& signer) {
     CBB out, outer_seq, wrapped_seq, seq, digest_algos_set, digest_algo, null;
     CBB content_info, issuer_and_serial, signer_infos, signer_info, sign_algo, signature;
     uint8_t *pkcs7_data, *name_der;
@@ -222,19 +421,20 @@
     BIGNUM* serial = BN_new();
     int sig_nid = NID_rsaEncryption;
 
-    X509_NAME* name = X509_NAME_new();
-    if (!name) {
-        return Error() << "Unable to get x509 subject name";
+    X509_NAME* issuer_name = X509_NAME_new();
+    if (!issuer_name) {
+        return Error() << "Unable to create x509 subject name";
     }
-    X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
-                               reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
-    X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
-                               reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
-    X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
-                               reinterpret_cast<const unsigned char*>("ODS"), -1, -1, 0);
+    X509_NAME_add_entry_by_txt(issuer_name, "C", MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>(kIssuerCountry), -1, -1, 0);
+    X509_NAME_add_entry_by_txt(issuer_name, "O", MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>(kIssuerOrg), -1, -1, 0);
+    X509_NAME_add_entry_by_txt(issuer_name, "CN", MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>(kRootSubject.commonName), -1,
+                               -1, 0);
 
-    BN_set_word(serial, 1);
-    name_der_len = i2d_X509_NAME(name, &name_der);
+    BN_set_word(serial, signer.serialNumber);
+    name_der_len = i2d_X509_NAME(issuer_name, &name_der);
     CBB_init(&out, 1024);
 
     if (!CBB_add_asn1(&out, &outer_seq, CBS_ASN1_SEQUENCE) ||
diff --git a/ondevice-signing/CertUtils.h b/ondevice-signing/CertUtils.h
deleted file mode 100644
index 66dff04..0000000
--- a/ondevice-signing/CertUtils.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#pragma once
-
-#include <android-base/result.h>
-
-android::base::Result<void> createSelfSignedCertificate(
-    const std::vector<uint8_t>& publicKey,
-    const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
-    const std::string& path);
-android::base::Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signedData);
-
-android::base::Result<std::vector<uint8_t>>
-extractPublicKeyFromX509(const std::vector<uint8_t>& x509);
-android::base::Result<std::vector<uint8_t>>
-extractPublicKeyFromSubjectPublicKeyInfo(const std::vector<uint8_t>& subjectKeyInfo);
-android::base::Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path);
-
-android::base::Result<void> verifySignature(const std::string& message,
-                                            const std::string& signature,
-                                            const std::vector<uint8_t>& publicKey);
diff --git a/ondevice-signing/KeystoreHmacKey.cpp b/ondevice-signing/KeystoreHmacKey.cpp
index a2208ce..916cbbc 100644
--- a/ondevice-signing/KeystoreHmacKey.cpp
+++ b/ondevice-signing/KeystoreHmacKey.cpp
@@ -49,17 +49,14 @@
 
 using android::base::unique_fd;
 
-// Keystore boot level that the odsign key uses
-static const int kOdsignBootLevel = 30;
-
-static KeyDescriptor getHmacKeyDescriptor() {
+static KeyDescriptor getHmacKeyDescriptor(const android::String16& keyAlias, int64_t keyNspace) {
     // AIDL parcelable objects don't have constructor
     static KeyDescriptor descriptor;
     static std::once_flag flag;
     std::call_once(flag, [&]() {
         descriptor.domain = Domain::SELINUX;
-        descriptor.alias = String16("ondevice-signing-hmac");
-        descriptor.nspace = 101;  // odsign_key
+        descriptor.alias = keyAlias + android::String16("-hmac");
+        descriptor.nspace = keyNspace;
     });
 
     return descriptor;
@@ -106,13 +103,13 @@
 
     KeyParameter boot_level;
     boot_level.tag = Tag::MAX_BOOT_LEVEL;
-    boot_level.value = KeyParameterValue::make<KeyParameterValue::integer>(kOdsignBootLevel);
+    boot_level.value = KeyParameterValue::make<KeyParameterValue::integer>(mKeyBootLevel);
     params.push_back(boot_level);
 
     KeyMetadata metadata;
     auto status = mSecurityLevel->generateKey(mDescriptor, {}, params, 0, {}, &metadata);
     if (!status.isOk()) {
-        return Error() << "Failed to create new HMAC key";
+        return Error() << "Failed to create new HMAC key: " << status;
     }
 
     return {};
@@ -133,7 +130,7 @@
         // Make sure this is an early boot key
         for (const auto& auth : keyEntryResponse.metadata.authorizations) {
             if (auth.keyParameter.tag == Tag::MAX_BOOT_LEVEL) {
-                if (auth.keyParameter.value.get<KeyParameterValue::integer>() == kOdsignBootLevel) {
+                if (auth.keyParameter.value.get<KeyParameterValue::integer>() == mKeyBootLevel) {
                     keyValid = true;
                     break;
                 }
@@ -152,9 +149,9 @@
     }
 }
 
-KeystoreHmacKey::KeystoreHmacKey() {
-    mDescriptor = getHmacKeyDescriptor();
-}
+KeystoreHmacKey::KeystoreHmacKey(const android::String16& keyAlias, int64_t keyNspace,
+                                 int keyBootLevel)
+    : mDescriptor(getHmacKeyDescriptor(keyAlias, keyNspace)), mKeyBootLevel(keyBootLevel) {}
 
 static std::vector<KeyParameter> getVerifyOpParameters() {
     std::vector<KeyParameter> opParameters;
@@ -209,8 +206,7 @@
 
     auto status = mSecurityLevel->createOperation(mDescriptor, params, false, &opResponse);
     if (!status.isOk()) {
-        return Error() << "Failed to create keystore signing operation: "
-                       << status.serviceSpecificErrorCode();
+        return Error() << "Failed to create keystore signing operation: " << status;
     }
     auto operation = opResponse.iOperation;
 
@@ -240,8 +236,7 @@
 
     auto status = mSecurityLevel->createOperation(mDescriptor, params, false, &opResponse);
     if (!status.isOk()) {
-        return Error() << "Failed to create keystore verification operation: "
-                       << status.serviceSpecificErrorCode();
+        return Error() << "Failed to create keystore verification operation: " << status;
     }
     auto operation = opResponse.iOperation;
 
@@ -260,3 +255,12 @@
 
     return {};
 }
+
+Result<void> KeystoreHmacKey::deleteKey() const {
+    auto status = mService->deleteKey(mDescriptor);
+    if (!status.isOk()) {
+        return Error() << "Failed to delete HMAC key: " << status;
+    }
+
+    return {};
+}
diff --git a/ondevice-signing/KeystoreHmacKey.h b/ondevice-signing/KeystoreHmacKey.h
index fbad0fd..1a815a3 100644
--- a/ondevice-signing/KeystoreHmacKey.h
+++ b/ondevice-signing/KeystoreHmacKey.h
@@ -31,16 +31,19 @@
     using KeyDescriptor = ::android::system::keystore2::KeyDescriptor;
 
   public:
-    KeystoreHmacKey();
+    KeystoreHmacKey(const android::String16& keyAlias, int64_t keyNspace, int keyBootLevel);
     android::base::Result<void> initialize(android::sp<IKeystoreService> service,
                                            android::sp<IKeystoreSecurityLevel> securityLevel);
     android::base::Result<std::string> sign(const std::string& message) const;
     android::base::Result<void> verify(const std::string& message,
                                        const std::string& signature) const;
+    android::base::Result<void> deleteKey() const;
 
   private:
     android::base::Result<void> createKey();
     KeyDescriptor mDescriptor;
     android::sp<IKeystoreService> mService;
     android::sp<IKeystoreSecurityLevel> mSecurityLevel;
+
+    int mKeyBootLevel;
 };
diff --git a/ondevice-signing/KeystoreKey.cpp b/ondevice-signing/KeystoreKey.cpp
index 0951d92..6ce65d6 100644
--- a/ondevice-signing/KeystoreKey.cpp
+++ b/ondevice-signing/KeystoreKey.cpp
@@ -50,27 +50,24 @@
 using android::base::Error;
 using android::base::Result;
 
-// Keystore boot level that the odsign key uses
-static const int kOdsignBootLevel = 30;
-
-const std::string kPublicKeySignature = "/data/misc/odsign/publickey.signature";
-
-static KeyDescriptor getKeyDescriptor() {
+static KeyDescriptor getKeyDescriptor(const android::String16& keyAlias, int64_t keyNspace) {
     // AIDL parcelable objects don't have constructor
     static KeyDescriptor descriptor;
     static std::once_flag flag;
     std::call_once(flag, [&]() {
         descriptor.domain = Domain::SELINUX;
-        descriptor.alias = String16("ondevice-signing");
-        descriptor.nspace = 101;  // odsign_key
+        descriptor.alias = keyAlias;
+        descriptor.nspace = keyNspace;
     });
 
     return descriptor;
 }
 
-KeystoreKey::KeystoreKey() {
-    mDescriptor = getKeyDescriptor();
-}
+KeystoreKey::KeystoreKey(std::string signedPubKeyPath, const android::String16& keyAlias,
+                         int64_t keyNspace, int keyBootLevel)
+    : mDescriptor(getKeyDescriptor(keyAlias, keyNspace)),
+      mHmacKey(keyAlias, keyNspace, keyBootLevel), mSignedPubKeyPath(std::move(signedPubKeyPath)),
+      mKeyBootLevel(keyBootLevel) {}
 
 Result<std::vector<uint8_t>> KeystoreKey::createKey() {
     std::vector<KeyParameter> params;
@@ -113,13 +110,13 @@
 
     KeyParameter boot_level;
     boot_level.tag = Tag::MAX_BOOT_LEVEL;
-    boot_level.value = KeyParameterValue::make<KeyParameterValue::integer>(kOdsignBootLevel);
+    boot_level.value = KeyParameterValue::make<KeyParameterValue::integer>(mKeyBootLevel);
     params.push_back(boot_level);
 
     KeyMetadata metadata;
     auto status = mSecurityLevel->generateKey(mDescriptor, {}, params, 0, {}, &metadata);
     if (!status.isOk()) {
-        return Error() << "Failed to create new key";
+        return Error() << "Failed to create new key: " << status;
     }
 
     // Extract the public key from the certificate, HMAC it and store the signature
@@ -137,7 +134,7 @@
         return Error() << "Failed to sign public key.";
     }
 
-    if (!android::base::WriteStringToFile(*signature, kPublicKeySignature)) {
+    if (!android::base::WriteStringToFile(*signature, mSignedPubKeyPath)) {
         return Error() << "Can't write public key signature.";
     }
 
@@ -172,11 +169,13 @@
 
     auto key = getOrCreateKey();
     if (!key.ok()) {
+        // Delete the HMAC, just in case signing failed, and we could recover by recreating it.
+        mHmacKey.deleteKey();
         LOG(ERROR) << key.error().message();
         return false;
     }
     mPublicKey = *key;
-    LOG(ERROR) << "Initialized Keystore key.";
+    LOG(INFO) << "Initialized Keystore key.";
     return true;
 }
 
@@ -204,7 +203,7 @@
     bool foundBootLevel = false;
     for (const auto& auth : keyEntryResponse.metadata.authorizations) {
         if (auth.keyParameter.tag == Tag::MAX_BOOT_LEVEL) {
-            if (auth.keyParameter.value.get<KeyParameterValue::integer>() == kOdsignBootLevel) {
+            if (auth.keyParameter.value.get<KeyParameterValue::integer>() == mKeyBootLevel) {
                 foundBootLevel = true;
                 break;
             }
@@ -230,7 +229,7 @@
     std::string publicKeyString = {publicKey->begin(), publicKey->end()};
 
     std::string signature;
-    if (!android::base::ReadFileToString(kPublicKeySignature, &signature)) {
+    if (!android::base::ReadFileToString(mSignedPubKeyPath, &signature)) {
         return Error() << "Can't find signature for public key.";
     }
 
@@ -254,13 +253,15 @@
     return *existingKey;
 }
 
-Result<SigningKey*> KeystoreKey::getInstance() {
-    static KeystoreKey keystoreKey;
+Result<SigningKey*> KeystoreKey::getInstance(const std::string& signedPubKeyPath,
+                                             const android::String16& keyAlias, int64_t keyNspace,
+                                             int keyBootLevel) {
+    auto keystoreKey = new KeystoreKey(signedPubKeyPath, keyAlias, keyNspace, keyBootLevel);
 
-    if (!keystoreKey.initialize()) {
+    if (!keystoreKey->initialize()) {
         return Error() << "Failed to initialize keystore key.";
     } else {
-        return &keystoreKey;
+        return keystoreKey;
     }
 }
 
@@ -297,19 +298,13 @@
 
     auto status = mSecurityLevel->createOperation(mDescriptor, opParameters, false, &opResponse);
     if (!status.isOk()) {
-        return Error() << "Failed to create keystore signing operation: "
-                       << status.serviceSpecificErrorCode();
+        return Error() << "Failed to create keystore signing operation: " << status;
     }
     auto operation = opResponse.iOperation;
 
-    std::optional<std::vector<uint8_t>> out;
-    status = operation->update({message.begin(), message.end()}, &out);
-    if (!status.isOk()) {
-        return Error() << "Failed to call keystore update operation.";
-    }
-
+    std::optional<std::vector<uint8_t>> input{std::in_place, message.begin(), message.end()};
     std::optional<std::vector<uint8_t>> signature;
-    status = operation->finish({}, {}, &signature);
+    status = operation->finish(input, {}, &signature);
     if (!status.isOk()) {
         return Error() << "Failed to call keystore finish operation.";
     }
diff --git a/ondevice-signing/KeystoreKey.h b/ondevice-signing/KeystoreKey.h
index 1257cbb..3c9a0ab 100644
--- a/ondevice-signing/KeystoreKey.h
+++ b/ondevice-signing/KeystoreKey.h
@@ -20,7 +20,6 @@
 
 #include <android-base/macros.h>
 #include <android-base/result.h>
-#include <android-base/unique_fd.h>
 
 #include <utils/StrongPointer.h>
 
@@ -37,13 +36,16 @@
 
   public:
     virtual ~KeystoreKey(){};
-    static android::base::Result<SigningKey*> getInstance();
+    static android::base::Result<SigningKey*> getInstance(const std::string& signedPubKeyPath,
+                                                          const android::String16& keyAlias,
+                                                          int64_t KeyNspace, int keyBootLevel);
 
     virtual android::base::Result<std::string> sign(const std::string& message) const;
     virtual android::base::Result<std::vector<uint8_t>> getPublicKey() const;
 
   private:
-    KeystoreKey();
+    KeystoreKey(std::string signedPubKeyPath, const android::String16& keyAlias, int64_t keyNspace,
+                int keyBootLevel);
     bool initialize();
     android::base::Result<std::vector<uint8_t>> verifyExistingKey();
     android::base::Result<std::vector<uint8_t>> createKey();
@@ -54,4 +56,7 @@
     android::sp<IKeystoreService> mService;
     android::sp<IKeystoreSecurityLevel> mSecurityLevel;
     std::vector<uint8_t> mPublicKey;
+
+    std::string mSignedPubKeyPath;
+    int mKeyBootLevel;
 };
diff --git a/ondevice-signing/TEST_MAPPING b/ondevice-signing/TEST_MAPPING
index 03b9b95..4b2c8c6 100644
--- a/ondevice-signing/TEST_MAPPING
+++ b/ondevice-signing/TEST_MAPPING
@@ -1,7 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "odsign_e2e_tests"
+      "name": "libsigningutils_test"
     }
   ]
 }
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
index cd38308..cd9a1ea 100644
--- a/ondevice-signing/VerityUtils.cpp
+++ b/ondevice-signing/VerityUtils.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <charconv>
 #include <filesystem>
 #include <map>
 #include <span>
@@ -25,8 +26,10 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
+#include <asm/byteorder.h>
 #include <libfsverity.h>
 #include <linux/fsverity.h>
 
@@ -41,23 +44,13 @@
 using android::base::unique_fd;
 
 static const char* kFsVerityInitPath = "/system/bin/fsverity_init";
+static const char* kFsVerityProcPath = "/proc/sys/fs/verity";
 
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define cpu_to_le16(v) ((__force __le16)(uint16_t)(v))
-#define le16_to_cpu(v) ((__force uint16_t)(__le16)(v))
-#else
-#define cpu_to_le16(v) ((__force __le16)__builtin_bswap16(v))
-#define le16_to_cpu(v) (__builtin_bswap16((__force uint16_t)(v)))
-#endif
+bool SupportsFsVerity() {
+    return access(kFsVerityProcPath, F_OK) == 0;
+}
 
-struct fsverity_signed_digest {
-    char magic[8]; /* must be "FSVerity" */
-    __le16 digest_algorithm;
-    __le16 digest_size;
-    __u8 digest[];
-};
-
-static std::string toHex(std::span<uint8_t> data) {
+static std::string toHex(std::span<const uint8_t> data) {
     std::stringstream ss;
     for (auto it = data.begin(); it != data.end(); ++it) {
         ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
@@ -65,22 +58,34 @@
     return ss.str();
 }
 
+static std::vector<uint8_t> fromHex(std::string_view hex) {
+    if (hex.size() % 2 != 0) {
+        return {};
+    }
+    std::vector<uint8_t> result;
+    result.reserve(hex.size() / 2);
+    for (size_t i = 0; i < hex.size(); i += 2) {
+        uint8_t byte;
+        auto conversion_result = std::from_chars(&hex[i], &hex[i + 2], byte, 16);
+        if (conversion_result.ptr != &hex[i + 2] || conversion_result.ec != std::errc()) {
+            return {};
+        }
+        result.push_back(byte);
+    }
+    return result;
+}
+
 static int read_callback(void* file, void* buf, size_t count) {
     int* fd = (int*)file;
     if (TEMP_FAILURE_RETRY(read(*fd, buf, count)) < 0) return errno ? -errno : -EIO;
     return 0;
 }
 
-Result<std::vector<uint8_t>> createDigest(const std::string& path) {
+static Result<std::vector<uint8_t>> createDigest(int fd) {
     struct stat filestat;
-    unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
-    if (fd < 0) {
-        return ErrnoError() << "Failed to open " << path;
-    }
-
-    int ret = stat(path.c_str(), &filestat);
+    int ret = fstat(fd, &filestat);
     if (ret < 0) {
-        return ErrnoError() << "Failed to stat " << path;
+        return ErrnoError() << "Failed to fstat";
     }
     struct libfsverity_merkle_tree_params params = {
         .version = 1,
@@ -92,7 +97,7 @@
     struct libfsverity_digest* digest;
     ret = libfsverity_compute_digest(&fd, &read_callback, &params, &digest);
     if (ret < 0) {
-        return ErrnoError() << "Failed to compute fs-verity digest for " << path;
+        return ErrnoError() << "Failed to compute fs-verity digest";
     }
     int expected_digest_size = libfsverity_get_digest_size(FS_VERITY_HASH_ALG_SHA256);
     if (digest->digest_size != expected_digest_size) {
@@ -104,6 +109,14 @@
     return digestVector;
 }
 
+Result<std::vector<uint8_t>> createDigest(const std::string& path) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (!fd.ok()) {
+        return ErrnoError() << "Unable to open";
+    }
+    return createDigest(fd.get());
+}
+
 namespace {
 template <typename T> struct DeleteAsPODArray {
     void operator()(T* x) {
@@ -113,6 +126,19 @@
         }
     }
 };
+
+static Result<void> measureFsVerity(int fd, const fsverity_digest* digest) {
+    if (ioctl(fd, FS_IOC_MEASURE_VERITY, digest) != 0) {
+        if (errno == ENODATA) {
+            return Error() << "File is not in fs-verity";
+        } else {
+            return ErrnoError() << "Failed to FS_IOC_MEASURE_VERITY";
+        }
+    }
+
+    return {};
+}
+
 }  // namespace
 
 template <typename T> using trailing_unique_ptr = std::unique_ptr<T, DeleteAsPODArray<T>>;
@@ -126,11 +152,11 @@
 
 static Result<std::vector<uint8_t>> signDigest(const SigningKey& key,
                                                const std::vector<uint8_t>& digest) {
-    auto d = makeUniqueWithTrailingData<fsverity_signed_digest>(digest.size());
+    auto d = makeUniqueWithTrailingData<fsverity_formatted_digest>(digest.size());
 
     memcpy(d->magic, "FSVerity", 8);
-    d->digest_algorithm = cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
-    d->digest_size = cpu_to_le16(digest.size());
+    d->digest_algorithm = __cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
+    d->digest_size = __cpu_to_le16(digest.size());
     memcpy(d->digest, digest.data(), digest.size());
 
     auto signed_digest = key.sign(std::string((char*)d.get(), sizeof(*d) + digest.size()));
@@ -141,10 +167,27 @@
     return std::vector<uint8_t>(signed_digest->begin(), signed_digest->end());
 }
 
-Result<std::string> enableFsVerity(const std::string& path, const SigningKey& key) {
-    auto digest = createDigest(path);
+static Result<void> enableFsVerity(int fd, std::span<uint8_t> pkcs7) {
+    struct fsverity_enable_arg arg = {.version = 1};
+
+    arg.sig_ptr = reinterpret_cast<uint64_t>(pkcs7.data());
+    arg.sig_size = pkcs7.size();
+    arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
+    arg.block_size = 4096;
+
+    int ret = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
+
+    if (ret != 0) {
+        return ErrnoError() << "Failed to call FS_IOC_ENABLE_VERITY";
+    }
+
+    return {};
+}
+
+Result<std::string> enableFsVerity(int fd, const SigningKey& key) {
+    auto digest = createDigest(fd);
     if (!digest.ok()) {
-        return digest.error();
+        return Error() << digest.error();
     }
 
     auto signed_digest = signDigest(key, digest.value());
@@ -152,65 +195,62 @@
         return signed_digest.error();
     }
 
-    auto pkcs7_data = createPkcs7(signed_digest.value());
+    auto pkcs7_data = createPkcs7(signed_digest.value(), kRootSubject);
+    if (!pkcs7_data.ok()) {
+        return pkcs7_data.error();
+    }
 
-    struct fsverity_enable_arg arg = {.version = 1};
-
-    arg.sig_ptr = (uint64_t)pkcs7_data->data();
-    arg.sig_size = pkcs7_data->size();
-    arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
-    arg.block_size = 4096;
-
-    unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
-    int ret = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
-
-    if (ret != 0) {
-        return ErrnoError() << "Failed to call FS_IOC_ENABLE_VERITY on " << path;
+    auto enabled = enableFsVerity(fd, pkcs7_data.value());
+    if (!enabled.ok()) {
+        return Error() << enabled.error();
     }
 
     // Return the root hash as a hex string
     return toHex(digest.value());
 }
 
-Result<std::string> isFileInVerity(const std::string& path) {
-    unsigned int flags;
+static Result<std::string> isFileInVerity(int fd) {
+    auto d = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE);
+    d->digest_size = FS_VERITY_MAX_DIGEST_SIZE;
 
+    const auto& status = measureFsVerity(fd, d.get());
+    if (!status.ok()) {
+        return status.error();
+    }
+
+    return toHex({&d->digest[0], &d->digest[d->digest_size]});
+}
+
+static Result<std::string> isFileInVerity(const std::string& path) {
     unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
-    if (fd < 0) {
+    if (!fd.ok()) {
         return ErrnoError() << "Failed to open " << path;
     }
 
-    int ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
-    if (ret < 0) {
-        return ErrnoError() << "Failed to FS_IOC_GETFLAGS for " << path;
-    }
-    if (!(flags & FS_VERITY_FL)) {
-        return Error() << "File is not in fs-verity: " << path;
+    auto digest = isFileInVerity(fd.get());
+    if (!digest.ok()) {
+        return Error() << digest.error() << ": " << path;
     }
 
-    auto d = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE);
-    d->digest_size = FS_VERITY_MAX_DIGEST_SIZE;
-    ret = ioctl(fd, FS_IOC_MEASURE_VERITY, d.get());
-    if (ret < 0) {
-        return ErrnoError() << "Failed to FS_IOC_MEASURE_VERITY for " << path;
-    }
-    return toHex({&d->digest[0], &d->digest[d->digest_size]});
+    return digest;
 }
 
 Result<std::map<std::string, std::string>> addFilesToVerityRecursive(const std::string& path,
                                                                      const SigningKey& key) {
     std::map<std::string, std::string> digests;
+
     std::error_code ec;
-
     auto it = std::filesystem::recursive_directory_iterator(path, ec);
-    auto end = std::filesystem::recursive_directory_iterator();
-
-    while (!ec && it != end) {
+    for (auto end = std::filesystem::recursive_directory_iterator(); it != end; it.increment(ec)) {
         if (it->is_regular_file()) {
-            auto digest = isFileInVerity(it->path());
+            unique_fd fd(TEMP_FAILURE_RETRY(open(it->path().c_str(), O_RDONLY | O_CLOEXEC)));
+            if (!fd.ok()) {
+                return ErrnoError() << "Failed to open " << path;
+            }
+            auto digest = isFileInVerity(fd);
             if (!digest.ok()) {
                 LOG(INFO) << "Adding " << it->path() << " to fs-verity...";
-                auto result = enableFsVerity(it->path(), key);
+                auto result = enableFsVerity(fd, key);
                 if (!result.ok()) {
                     return result.error();
                 }
@@ -220,15 +260,39 @@
                 digests[it->path()] = *digest;
             }
         }
-        ++it;
     }
     if (ec) {
-        return Error() << "Failed to iterate " << path << ": " << ec;
+        return Error() << "Failed to iterate " << path << ": " << ec.message();
     }
 
     return digests;
 }
 
+Result<void> enableFsVerity(const std::string& path, const std::string& signature_path) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (!fd.ok()) {
+        return Error() << "Can't open " << path;
+    }
+
+    std::string signature;
+    android::base::ReadFileToString(signature_path, &signature);
+    std::vector<uint8_t> span = std::vector<uint8_t>(signature.begin(), signature.end());
+
+    const auto& enable = enableFsVerity(fd.get(), span);
+    if (!enable.ok()) {
+        return enable.error();
+    }
+
+    auto digest = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE);
+    digest->digest_size = FS_VERITY_MAX_DIGEST_SIZE;
+    const auto& measure = measureFsVerity(fd.get(), digest.get());
+    if (!measure.ok()) {
+        return measure.error();
+    }
+
+    return {};
+}
+
 Result<std::map<std::string, std::string>> verifyAllFilesInVerity(const std::string& path) {
     std::map<std::string, std::string> digests;
     std::error_code ec;
@@ -260,10 +324,90 @@
     return digests;
 }
 
-Result<void> addCertToFsVerityKeyring(const std::string& path) {
-    const char* const argv[] = {kFsVerityInitPath, "--load-extra-key", "fsv_ods"};
+Result<void> verifyAllFilesUsingCompOs(const std::string& directory_path,
+                                       const std::map<std::string, std::string>& digests,
+                                       const SigningKey& signing_key) {
+    std::error_code ec;
+    size_t verified_count = 0;
+    auto it = std::filesystem::recursive_directory_iterator(directory_path, ec);
+    for (auto end = std::filesystem::recursive_directory_iterator(); it != end; it.increment(ec)) {
+        auto& path = it->path();
+        if (it->is_regular_file()) {
+            auto entry = digests.find(path);
+            if (entry == digests.end()) {
+                return Error() << "Unexpected file found: " << path;
+            }
+            auto& compos_digest = entry->second;
+
+            unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+            if (!fd.ok()) {
+                return ErrnoError() << "Can't open " << path;
+            }
+
+            auto verity_digest = isFileInVerity(fd);
+            if (verity_digest.ok()) {
+                // The file is already in fs-verity. We need to make sure it was signed
+                // by CompOS, so we just check that it has the digest we expect.
+                if (verity_digest.value() == compos_digest) {
+                    ++verified_count;
+                } else {
+                    return Error() << "fs-verity digest does not match CompOS digest: " << path;
+                }
+            } else {
+                // Not in fs-verity yet. We know the digest CompOS provided; If
+                // it's not the correct digest for the file then enabling
+                // fs-verity will fail, so we don't need to check it explicitly
+                // ourselves. Otherwise we should be good.
+                LOG(INFO) << "Adding " << path << " to fs-verity...";
+
+                auto digest_bytes = fromHex(compos_digest);
+                if (digest_bytes.empty()) {
+                    return Error() << "Invalid digest " << compos_digest;
+                }
+                auto signed_digest = signDigest(signing_key, digest_bytes);
+                if (!signed_digest.ok()) {
+                    return signed_digest.error();
+                }
+
+                auto pkcs7_data = createPkcs7(signed_digest.value(), kRootSubject);
+                if (!pkcs7_data.ok()) {
+                    return pkcs7_data.error();
+                }
+
+                auto enabled = enableFsVerity(fd, pkcs7_data.value());
+                if (!enabled.ok()) {
+                    return Error() << enabled.error();
+                }
+                ++verified_count;
+            }
+        } else if (it->is_directory()) {
+            // These are fine to ignore
+        } else if (it->is_symlink()) {
+            return Error() << "Rejecting artifacts, symlink at " << path;
+        } else {
+            return Error() << "Rejecting artifacts, unexpected file type for " << path;
+        }
+    }
+    if (ec) {
+        return Error() << "Failed to iterate " << directory_path << ": " << ec.message();
+    }
+
+    // Make sure all the files we expected have been seen
+    if (verified_count != digests.size()) {
+        return Error() << "Verified " << verified_count << " files, but expected "
+                       << digests.size();
+    }
+
+    return {};
+}
+
+Result<void> addCertToFsVerityKeyring(const std::string& path, const char* keyName) {
+    const char* const argv[] = {kFsVerityInitPath, "--load-extra-key", keyName};
 
     int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+    if (fd == -1) {
+        return ErrnoError() << "Failed to open " << path;
+    }
     pid_t pid = fork();
     if (pid == 0) {
         dup2(fd, STDIN_FILENO);
@@ -272,7 +416,7 @@
         char* argv_child[argc + 1];
         memcpy(argv_child, argv, argc * sizeof(char*));
         argv_child[argc] = nullptr;
-        execvp(argv_child[0], const_cast<char**>(argv_child));
+        execvp(argv_child[0], argv_child);
         PLOG(ERROR) << "exec in ForkExecvp";
         _exit(EXIT_FAILURE);
     } else {
@@ -288,10 +432,8 @@
     if (!WIFEXITED(status)) {
         return Error() << kFsVerityInitPath << ": abnormal process exit";
     }
-    if (WEXITSTATUS(status)) {
-        if (status != 0) {
-            return Error() << kFsVerityInitPath << " exited with " << status;
-        }
+    if (WEXITSTATUS(status) != 0) {
+        return Error() << kFsVerityInitPath << " exited with " << WEXITSTATUS(status);
     }
 
     return {};
diff --git a/ondevice-signing/include/CertUtils.h b/ondevice-signing/include/CertUtils.h
new file mode 100644
index 0000000..fe703fa
--- /dev/null
+++ b/ondevice-signing/include/CertUtils.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include <android-base/result.h>
+
+// Information extracted from a certificate.
+struct CertInfo {
+    std::string subjectCn;
+    std::vector<uint8_t> subjectRsaPublicKey;
+};
+
+// Subjects of certificates we issue.
+struct CertSubject {
+    const char* commonName;
+    unsigned serialNumber;
+};
+
+// These are all the certificates we ever sign (the first one being our
+// self-signed cert).  We shouldn't really re-use serial numbers for different
+// certificates for the same subject but we do; only one should be in use at a
+// time though.
+inline const CertSubject kRootSubject{"ODS", 1};
+inline const CertSubject kCompOsSubject{"CompOs", 2};
+
+android::base::Result<void> createSelfSignedCertificate(
+    const std::vector<uint8_t>& publicKey,
+    const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
+    const std::string& path);
+
+android::base::Result<void> createLeafCertificate(
+    const CertSubject& subject, const std::vector<uint8_t>& publicKey,
+    const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
+    const std::string& issuerCertPath, const std::string& outPath);
+
+android::base::Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signedData,
+                                                        const CertSubject& signer);
+
+android::base::Result<std::vector<uint8_t>>
+extractPublicKeyFromX509(const std::vector<uint8_t>& x509);
+android::base::Result<std::vector<uint8_t>>
+extractPublicKeyFromSubjectPublicKeyInfo(const std::vector<uint8_t>& subjectKeyInfo);
+android::base::Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path);
+
+android::base::Result<CertInfo>
+verifyAndExtractCertInfoFromX509(const std::string& path, const std::vector<uint8_t>& publicKey);
+
+android::base::Result<void> verifySignature(const std::string& message,
+                                            const std::string& signature,
+                                            const std::vector<uint8_t>& publicKey);
+
+android::base::Result<void> verifyRsaPublicKeySignature(const std::string& message,
+                                                        const std::string& signature,
+                                                        const std::vector<uint8_t>& rsaPublicKey);
diff --git a/ondevice-signing/SigningKey.h b/ondevice-signing/include/SigningKey.h
similarity index 100%
rename from ondevice-signing/SigningKey.h
rename to ondevice-signing/include/SigningKey.h
diff --git a/ondevice-signing/VerityUtils.h b/ondevice-signing/include/VerityUtils.h
similarity index 65%
rename from ondevice-signing/VerityUtils.h
rename to ondevice-signing/include/VerityUtils.h
index a834e61..e6e49c7 100644
--- a/ondevice-signing/VerityUtils.h
+++ b/ondevice-signing/include/VerityUtils.h
@@ -18,10 +18,16 @@
 
 #include <android-base/result.h>
 
+#include <map>
+#include <string>
+#include <vector>
+
 #include "SigningKey.h"
 
-android::base::Result<void> addCertToFsVerityKeyring(const std::string& path);
+android::base::Result<void> addCertToFsVerityKeyring(const std::string& path, const char* keyName);
 android::base::Result<std::vector<uint8_t>> createDigest(const std::string& path);
+android::base::Result<std::string> enableFsVerity(int fd, const SigningKey& key);
+bool SupportsFsVerity();
 android::base::Result<std::map<std::string, std::string>>
 verifyAllFilesInVerity(const std::string& path);
 
@@ -29,3 +35,12 @@
 // for those files it will return the existing digest.
 android::base::Result<std::map<std::string, std::string>>
 addFilesToVerityRecursive(const std::string& path, const SigningKey& key);
+
+// Enable verity on the provided file, using the given PKCS7 signature.
+android::base::Result<void> enableFsVerity(const std::string& path,
+                                           const std::string& signature_path);
+
+android::base::Result<void>
+verifyAllFilesUsingCompOs(const std::string& directory_path,
+                          const std::map<std::string, std::string>& digests,
+                          const SigningKey& signing_key);
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
index 425776b..fc55846 100644
--- a/ondevice-signing/odsign_main.cpp
+++ b/ondevice-signing/odsign_main.cpp
@@ -44,29 +44,57 @@
 
 using OdsignInfo = ::odsign::proto::OdsignInfo;
 
-const std::string kSigningKeyBlob = "/data/misc/odsign/key.blob";
+// Keystore boot level that the odsign key uses
+const int kKeyBootLevel = 30;
+const std::string kPublicKeySignature = "/data/misc/odsign/publickey.signature";
+const android::String16 kKeyAlias{"ondevice-signing"};
+constexpr int kKeyNspace = 101;  // odsign_key
+
 const std::string kSigningKeyCert = "/data/misc/odsign/key.cert";
 const std::string kOdsignInfo = "/data/misc/odsign/odsign.info";
 const std::string kOdsignInfoSignature = "/data/misc/odsign/odsign.info.signature";
 
 const std::string kArtArtifactsDir = "/data/misc/apexdata/com.android.art/dalvik-cache";
 
-static const char* kOdrefreshPath = "/apex/com.android.art/bin/odrefresh";
+constexpr const char* kOdrefreshPath = "/apex/com.android.art/bin/odrefresh";
+constexpr const char* kCompOsVerifyPath = "/apex/com.android.compos/bin/compos_verify";
 
-static const char* kFsVerityProcPath = "/proc/sys/fs/verity";
+constexpr bool kForceCompilation = false;
+constexpr bool kUseCompOs = true;
 
-static const bool kForceCompilation = false;
+const std::string kCompOsPendingArtifactsDir = "/data/misc/apexdata/com.android.art/compos-pending";
+const std::string kCompOsInfo = kArtArtifactsDir + "/compos.info";
+const std::string kCompOsInfoSignature = kCompOsInfo + ".signature";
 
-static const char* kOdsignVerificationDoneProp = "odsign.verification.done";
-static const char* kOdsignKeyDoneProp = "odsign.key.done";
+constexpr const char* kCompOsPendingInfoPath =
+    "/data/misc/apexdata/com.android.art/compos-pending/compos.info";
+constexpr const char* kCompOsPendingInfoSignaturePath =
+    "/data/misc/apexdata/com.android.art/compos-pending/compos.info.signature";
 
-static const char* kOdsignVerificationStatusProp = "odsign.verification.success";
-static const char* kOdsignVerificationStatusValid = "1";
-static const char* kOdsignVerificationStatusError = "0";
+constexpr const char* kOdsignVerificationDoneProp = "odsign.verification.done";
+constexpr const char* kOdsignKeyDoneProp = "odsign.key.done";
 
-static const char* kStopServiceProp = "ctl.stop";
+constexpr const char* kOdsignVerificationStatusProp = "odsign.verification.success";
+constexpr const char* kOdsignVerificationStatusValid = "1";
+constexpr const char* kOdsignVerificationStatusError = "0";
 
-static int removeDirectory(const std::string& directory) {
+constexpr const char* kStopServiceProp = "ctl.stop";
+
+enum class CompOsInstance { kCurrent, kPending };
+
+namespace {
+
+bool rename(const std::string& from, const std::string& to) {
+    std::error_code ec;
+    std::filesystem::rename(from, to, ec);
+    if (ec) {
+        LOG(ERROR) << "Can't rename " << from << " to " << to << ": " << ec.message();
+        return false;
+    }
+    return true;
+}
+
+int removeDirectory(const std::string& directory) {
     std::error_code ec;
     auto num_removed = std::filesystem::remove_all(directory, ec);
     if (ec) {
@@ -80,13 +108,46 @@
     }
 }
 
-Result<void> verifyExistingCert(const SigningKey& key) {
+bool directoryHasContent(const std::string& directory) {
+    std::error_code ec;
+    return std::filesystem::is_directory(directory, ec) &&
+           !std::filesystem::is_empty(directory, ec);
+}
+
+art::odrefresh::ExitCode compileArtifacts(bool force) {
+    const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
+    const int exit_code =
+        logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
+    return static_cast<art::odrefresh::ExitCode>(exit_code);
+}
+
+art::odrefresh::ExitCode checkArtifacts() {
+    const char* const argv[] = {kOdrefreshPath, "--check"};
+    const int exit_code =
+        logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
+    return static_cast<art::odrefresh::ExitCode>(exit_code);
+}
+
+std::string toHex(const std::vector<uint8_t>& digest) {
+    std::stringstream ss;
+    for (auto it = digest.begin(); it != digest.end(); ++it) {
+        ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
+    }
+    return ss.str();
+}
+
+bool compOsPresent() {
+    // We must have the CompOS APEX
+    return access(kCompOsVerifyPath, X_OK) == 0;
+}
+
+Result<void> verifyExistingRootCert(const SigningKey& key) {
     if (access(kSigningKeyCert.c_str(), F_OK) < 0) {
         return ErrnoError() << "Key certificate not found: " << kSigningKeyCert;
     }
     auto trustedPublicKey = key.getPublicKey();
     if (!trustedPublicKey.ok()) {
-        return Error() << "Failed to retrieve signing public key.";
+        return Error() << "Failed to retrieve signing public key: " << trustedPublicKey.error();
     }
 
     auto publicKeyFromExistingCert = extractPublicKeyFromX509(kSigningKeyCert);
@@ -98,11 +159,12 @@
                        << " does not match signing public key.";
     }
 
-    // At this point, we know the cert matches
+    // At this point, we know the cert is for our key; it's unimportant whether it's
+    // actually self-signed.
     return {};
 }
 
-Result<void> createX509Cert(const SigningKey& key, const std::string& outPath) {
+Result<void> createX509RootCert(const SigningKey& key, const std::string& outPath) {
     auto publicKey = key.getPublicKey();
 
     if (!publicKey.ok()) {
@@ -110,30 +172,7 @@
     }
 
     auto keySignFunction = [&](const std::string& to_be_signed) { return key.sign(to_be_signed); };
-    createSelfSignedCertificate(*publicKey, keySignFunction, outPath);
-    return {};
-}
-
-art::odrefresh::ExitCode checkArtifacts() {
-    const char* const argv[] = {kOdrefreshPath, "--check"};
-    const int exit_code =
-        logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
-    return static_cast<art::odrefresh::ExitCode>(exit_code);
-}
-
-art::odrefresh::ExitCode compileArtifacts(bool force) {
-    const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
-    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) {
-    std::stringstream ss;
-    for (auto it = digest.begin(); it != digest.end(); ++it) {
-        ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
-    }
-    return ss.str();
+    return createSelfSignedCertificate(*publicKey, keySignFunction, outPath);
 }
 
 Result<std::map<std::string, std::string>> computeDigests(const std::string& path) {
@@ -147,7 +186,8 @@
         if (it->is_regular_file()) {
             auto digest = createDigest(it->path());
             if (!digest.ok()) {
-                return Error() << "Failed to compute digest for " << it->path();
+                return Error() << "Failed to compute digest for " << it->path() << ": "
+                               << digest.error();
             }
             digests[it->path()] = toHex(*digest);
         }
@@ -281,22 +321,8 @@
     return {};
 }
 
-static int removeArtifacts() {
-    std::error_code ec;
-    auto num_removed = std::filesystem::remove_all(kArtArtifactsDir, ec);
-    if (ec) {
-        LOG(ERROR) << "Can't remove " << kArtArtifactsDir << ": " << ec.message();
-        return 0;
-    } else {
-        if (num_removed > 0) {
-            LOG(INFO) << "Removed " << num_removed << " entries from " << kArtArtifactsDir;
-        }
-        return num_removed;
-    }
-}
-
-static Result<void> verifyArtifacts(const std::map<std::string, std::string>& trusted_digests,
-                                    bool supportsFsVerity) {
+Result<void> verifyArtifactsIntegrity(const std::map<std::string, std::string>& trusted_digests,
+                                      bool supportsFsVerity) {
     Result<void> integrityStatus;
 
     if (supportsFsVerity) {
@@ -305,16 +331,137 @@
         integrityStatus = verifyIntegrityNoFsVerity(trusted_digests);
     }
     if (!integrityStatus.ok()) {
-        return Error() << integrityStatus.error().message();
+        return integrityStatus.error();
     }
 
     return {};
 }
 
-int main(int /* argc */, char** /* argv */) {
+Result<OdsignInfo> getComposInfo() {
+    const char* const argv[] = {kCompOsVerifyPath, "--instance", "current"};
+    int result =
+        logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
+    if (result != 0) {
+        return Error() << kCompOsVerifyPath << " returned " << result;
+    }
+
+    std::string compos_info_str;
+    if (!android::base::ReadFileToString(kCompOsInfo, &compos_info_str)) {
+        return ErrnoError() << "Failed to read " << kCompOsInfo;
+    }
+
+    // Delete the files - we don't need them any more, and they'd confuse
+    // artifact verification
+    if (unlink(kCompOsInfo.c_str()) != 0 || unlink(kCompOsInfoSignature.c_str()) != 0) {
+        return ErrnoError() << "Unable to delete CompOS info/signature file";
+    }
+
+    OdsignInfo compos_info;
+    if (!compos_info.ParseFromString(compos_info_str)) {
+        return Error() << "Failed to parse " << kCompOsInfo;
+    }
+
+    LOG(INFO) << "Loaded " << kCompOsInfo;
+    return compos_info;
+}
+
+art::odrefresh::ExitCode checkCompOsPendingArtifacts(const SigningKey& signing_key,
+                                                     bool* digests_verified) {
+    if (!directoryHasContent(kCompOsPendingArtifactsDir)) {
+        // No pending CompOS artifacts, all that matters is the current ones.
+        return checkArtifacts();
+    }
+
+    // CompOS has generated some artifacts that may, or may not, match the
+    // current state.  But if there are already valid artifacts present the
+    // CompOS ones are redundant.
+    art::odrefresh::ExitCode odrefresh_status = checkArtifacts();
+    if (odrefresh_status != art::odrefresh::ExitCode::kCompilationRequired) {
+        if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
+            LOG(INFO) << "Current artifacts are OK, deleting pending artifacts";
+            removeDirectory(kCompOsPendingArtifactsDir);
+        }
+        return odrefresh_status;
+    }
+
+    // No useful current artifacts, lets see if the CompOS ones are ok
+    if (access(kCompOsPendingInfoPath, R_OK) != 0 ||
+        access(kCompOsPendingInfoSignaturePath, R_OK) != 0) {
+        LOG(INFO) << "Missing CompOS info/signature, deleting pending artifacts";
+        removeDirectory(kCompOsPendingArtifactsDir);
+        return art::odrefresh::ExitCode::kCompilationRequired;
+    }
+
+    LOG(INFO) << "Current artifacts are out of date, switching to pending artifacts";
+    removeDirectory(kArtArtifactsDir);
+    if (!rename(kCompOsPendingArtifactsDir, kArtArtifactsDir)) {
+        removeDirectory(kCompOsPendingArtifactsDir);
+        return art::odrefresh::ExitCode::kCompilationRequired;
+    }
+
+    // Make sure the artifacts we have are genuinely produced by the current
+    // instance of CompOS.
+    auto compos_info = getComposInfo();
+    if (!compos_info.ok()) {
+        LOG(WARNING) << compos_info.error();
+    } else {
+        std::map<std::string, std::string> compos_digests(compos_info->file_hashes().begin(),
+                                                          compos_info->file_hashes().end());
+
+        auto status = verifyAllFilesUsingCompOs(kArtArtifactsDir, compos_digests, signing_key);
+        if (!status.ok()) {
+            LOG(WARNING) << "Faild to verify CompOS artifacts: " << status.error();
+        } else {
+            LOG(INFO) << "CompOS artifacts successfully verified.";
+            odrefresh_status = checkArtifacts();
+            switch (odrefresh_status) {
+            case art::odrefresh::ExitCode::kCompilationRequired:
+                // We have verified all the files, and we need to make sure
+                // we don't check them against odsign.info which will be out
+                // of date.
+                *digests_verified = true;
+                return odrefresh_status;
+            case art::odrefresh::ExitCode::kOkay: {
+                // We have digests of all the files, so we can just sign them & save them now.
+                // We need to make sure we don't check them against odsign.info which will
+                // be out of date.
+                auto persisted = persistDigests(compos_digests, signing_key);
+                if (!persisted.ok()) {
+                    LOG(ERROR) << persisted.error();
+                    // Don't try to compile again - if we can't write the digests, things
+                    // are pretty bad.
+                    return art::odrefresh::ExitCode::kCleanupFailed;
+                }
+                LOG(INFO) << "Persisted CompOS digests.";
+                *digests_verified = true;
+                return odrefresh_status;
+            }
+            default:
+                return odrefresh_status;
+            }
+        }
+    }
+
+    // We can't use the existing artifacts, so we will need to generate new
+    // ones.
+    if (removeDirectory(kArtArtifactsDir) == 0) {
+        // We have unsigned artifacts that we can't delete, so it's not safe to continue.
+        LOG(ERROR) << "Unable to delete invalid CompOS artifacts";
+        return art::odrefresh::ExitCode::kCleanupFailed;
+    }
+
+    return art::odrefresh::ExitCode::kCompilationRequired;
+}
+}  // namespace
+
+int main(int /* argc */, char** argv) {
+    android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
+
     auto errorScopeGuard = []() {
-        // In case we hit any error, remove the artifacts and tell Zygote not to use anything
-        removeArtifacts();
+        // In case we hit any error, remove the artifacts and tell Zygote not to use
+        // anything
+        removeDirectory(kArtArtifactsDir);
+        removeDirectory(kCompOsPendingArtifactsDir);
         // Tell init we don't need to use our key anymore
         SetProperty(kOdsignKeyDoneProp, "1");
         // Tell init we're done with verification, and that it was an error
@@ -329,43 +476,47 @@
         LOG(INFO) << "Device doesn't support updatable APEX, exiting.";
         return 0;
     }
-
-    auto keystoreResult = KeystoreKey::getInstance();
+    auto keystoreResult =
+        KeystoreKey::getInstance(kPublicKeySignature, kKeyAlias, kKeyNspace, kKeyBootLevel);
     if (!keystoreResult.ok()) {
-        LOG(ERROR) << "Could not create keystore key: " << keystoreResult.error().message();
+        LOG(ERROR) << "Could not create keystore key: " << keystoreResult.error();
         return -1;
     }
     SigningKey* key = keystoreResult.value();
 
-    bool supportsFsVerity = access(kFsVerityProcPath, F_OK) == 0;
+    bool supportsFsVerity = SupportsFsVerity();
     if (!supportsFsVerity) {
         LOG(INFO) << "Device doesn't support fsverity. Falling back to full verification.";
     }
 
+    bool useCompOs = kUseCompOs && supportsFsVerity && compOsPresent();
+
     if (supportsFsVerity) {
-        auto existing_cert = verifyExistingCert(*key);
+        auto existing_cert = verifyExistingRootCert(*key);
         if (!existing_cert.ok()) {
-            LOG(WARNING) << existing_cert.error().message();
+            LOG(WARNING) << existing_cert.error();
 
             // Try to create a new cert
-            auto new_cert = createX509Cert(*key, kSigningKeyCert);
+            auto new_cert = createX509RootCert(*key, kSigningKeyCert);
             if (!new_cert.ok()) {
-                LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error().message();
+                LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error();
                 // TODO apparently the key become invalid - delete the blob / cert
                 return -1;
             }
         } else {
             LOG(INFO) << "Found and verified existing public key certificate: " << kSigningKeyCert;
         }
-        auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
+        auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert, "fsv_ods");
         if (!cert_add_result.ok()) {
             LOG(ERROR) << "Failed to add certificate to fs-verity keyring: "
-                       << cert_add_result.error().message();
+                       << cert_add_result.error();
             return -1;
         }
     }
 
-    art::odrefresh::ExitCode odrefresh_status = checkArtifacts();
+    bool digests_verified = false;
+    art::odrefresh::ExitCode odrefresh_status =
+        useCompOs ? checkCompOsPendingArtifacts(*key, &digests_verified) : checkArtifacts();
 
     // The artifacts dir doesn't necessarily need to exist; if the existing
     // artifacts on the system partition are valid, those can be used.
@@ -373,8 +524,9 @@
     // If we receive any error other than ENOENT, be suspicious
     bool artifactsPresent = (err == 0) || (err < 0 && errno != ENOENT);
 
-    if (artifactsPresent && (odrefresh_status == art::odrefresh::ExitCode::kOkay ||
-                             odrefresh_status == art::odrefresh::ExitCode::kCompilationRequired)) {
+    if (artifactsPresent && !digests_verified &&
+        (odrefresh_status == art::odrefresh::ExitCode::kOkay ||
+         odrefresh_status == art::odrefresh::ExitCode::kCompilationRequired)) {
         // If we haven't verified the digests yet, we need to validate them. We
         // need to do this both in case the existing artifacts are okay, but
         // also if odrefresh said that a recompile is required. In the latter
@@ -395,7 +547,7 @@
             SetProperty(kOdsignKeyDoneProp, "1");
         }
 
-        auto verificationResult = verifyArtifacts(trusted_digests, supportsFsVerity);
+        auto verificationResult = verifyArtifactsIntegrity(trusted_digests, supportsFsVerity);
         if (!verificationResult.ok()) {
             int num_removed = removeDirectory(kArtArtifactsDir);
             if (num_removed == 0) {
@@ -430,12 +582,12 @@
             digests = computeDigests(kArtArtifactsDir);
         }
         if (!digests.ok()) {
-            LOG(ERROR) << digests.error().message();
+            LOG(ERROR) << digests.error();
             return -1;
         }
         auto persistStatus = persistDigests(*digests, *key);
         if (!persistStatus.ok()) {
-            LOG(ERROR) << persistStatus.error().message();
+            LOG(ERROR) << persistStatus.error();
             return -1;
         }
     } else if (odrefresh_status == art::odrefresh::ExitCode::kCleanupFailed) {
diff --git a/ondevice-signing/proto/Android.bp b/ondevice-signing/proto/Android.bp
index fd48f31..356e661 100644
--- a/ondevice-signing/proto/Android.bp
+++ b/ondevice-signing/proto/Android.bp
@@ -23,7 +23,22 @@
     host_supported: true,
     proto: {
         export_proto_headers: true,
-        type: "full",
+        type: "lite",
     },
     srcs: ["odsign_info.proto"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.compos",
+    ],
+}
+
+rust_protobuf {
+    name: "libodsign_proto_rust",
+    crate_name: "odsign_proto",
+    protos: ["odsign_info.proto"],
+    source_stem: "odsign_proto",
+    host_supported: true,
+    apex_available: [
+        "com.android.compos",
+    ],
 }
diff --git a/ondevice-signing/tests/Android.bp b/ondevice-signing/tests/Android.bp
new file mode 100644
index 0000000..4027220
--- /dev/null
+++ b/ondevice-signing/tests/Android.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+cc_test {
+  name: "libsigningutils_test",
+  srcs: ["SigningUtilsTest.cpp"],
+  test_suites: ["device-tests"],
+  compile_multilib: "both",
+  defaults: [
+    "odsign_flags_defaults",
+  ],
+  static_libs: [
+    "libsigningutils",
+  ],
+  shared_libs: [
+    "libbase",
+    "libcrypto",
+  ],
+  data: [
+    "test_file",
+    "test_file.sig",
+    "SigningUtils.cert.der",
+  ],
+}
diff --git a/ondevice-signing/tests/SigningUtils.cert.der b/ondevice-signing/tests/SigningUtils.cert.der
new file mode 100644
index 0000000..0703d59
--- /dev/null
+++ b/ondevice-signing/tests/SigningUtils.cert.der
Binary files differ
diff --git a/ondevice-signing/tests/SigningUtils.pem b/ondevice-signing/tests/SigningUtils.pem
new file mode 100644
index 0000000..01dfa5e
--- /dev/null
+++ b/ondevice-signing/tests/SigningUtils.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAwyOUvcTpPuPxw2I9E28uhXT9pbJAgNE0dc1emOWRfapZ5iow
+6PC7am47DfHyyZ/dwtw0vvi3QWCLIpsyFQbspO5XfmJH7mHBTvG8SkV40rFjptf8
+iwMl5i1ZUc5LChxuRh6DIS1fT2tFiXQ8iX3FUNNqjj/tOOtp45JEsgFK7WDW8YU3
+UseUn2BpkccUHMbe78EkEb4F6/AJWISk2INMOMPzRPUwdE3eUPVrufkp6jVJs69I
+c6skRvAAqpiOx4rYpVzs3V5PuzhmCr7jt8hTQ0DA6q501UT06og6gIKPdqLjugKK
+jt88XNXhhofckSbIH7edwa20LhvE4vtM/x7E0u+sCJozr0uyNgRXWs6imsVrC7Hb
+cclecBUFqETVzP6RG49cCXVkuOkSNBm3HLt4291+O821gn3yygTE9bpf9uuDHSb1
+l13MOMbVeT3ipaRGMwD1bp7kBW3cEZ2zrQ4WSwwtADeWI6Z0k5SpcHfLxp+6dhPN
+JtqBmgUTHuycmvoAPZK6XMnV0wCo2gUYS2q3Vc9yqsJRG0q2CKhOtKnuEmSqcjcC
+52OnP06M5pLfvms+qoOLVUHMhidMtYs3yRPZ1ZPzScQhjg64QDsRiG+PC0lL/NFz
+XPFcgzSsoIJ8UNZNjw7l5hh9NehJam2pqMizIcECl3gwrqGtq7Qo/AIXc4sCAwEA
+AQKCAgBqkCqw+zBYvMgQ57vsugGQtdOyQcaB0j0wu6cWHf+2vWl8jLvK6XOfanTr
+Z54rRxcmS3SueUox9JPmoRPXccGXS+URyn/3iQC0qMQnVwrlHCQMP9TU4TI4Ibmu
+N9a4vc/mkNERNCLhTvZZWtWYS8uOGPYOmpBkTgK0WPMUtioBuamHmTUeCol6A3+D
+MVElaeDi0vlsivXW421nHoCbEBB2y2M03CTKzp9CXNOoao3eLZ2C94y8RdB4wKXM
+g6UtCQDIRRfAx7kIx4LKCXZ3rXjyuBDh18VLle2diilQdnv70HZF5Q9feD8Rf2c6
+PUVRKvmMgIww8Tf9GgMJ5SwmAdp/VblgcKsdjr8tnT7V9ljkYCL4K3H4FT5LKXjI
+FiGyqBie8jvLYCLx6DlVTIi+Q/kvxaFT9twazVlz9jfgufQ4ICBKlNp4A6kpwrzb
+9QnMrHI+gTOrCCEeklg90M+xk5UERueLPnXYbvAG8cv1FeVpi9ldSQds5VHN1ZJ4
+hWWeWfGgIDiNeZuKL141NLS/sX9rCGEsQyVLTKkSDIgh5ncepVcXhB40GZFfggml
+A3HfNRN5lHLwH5+JKWlVx7PfUGPOTgx62i7HF+qcV3bQJfqnAMLPA7hh1bo8xIPp
+hiAbaZkwCGlNCwadzOq6U99Dx7eorwfAgGDKWnAr/rMaK9n+OQKCAQEA8UItzBLq
+yyJ+Xj7n/M3x/+v2E6dcDYH64oeMpVEEdH6cSSNcdUm0vgX4p25W0uY5kgj1RsXV
+gOk+9W6cM84p+2+DIGI8fydnuIv8q6TFiouDCY+T3FVdj4MasNZuAeHR7c6Coc5N
+Eckv3F1sfoGlH4z2AzppY0T3VX5TkKkh719X7A/cpIirZBRLfddDA3Hr6pm/vgSo
+mX1X5BhjrujasRoZhU2RsXnAflXp18aP67zvlRlzGrpMGeueRjp2NXF3pItiMAjN
+EOSoY3elEi6Um+WomFGLsY5SAuId+TJy8SqsGKWNHN+UPx4tGYQmEPA1e76Nu5Ex
+xDxpHIWyZ4Hz/wKCAQEAzw/9bPFgihzSnrBbJiFZVGPhFxw39aeuB5/3ZMPjknfK
+fyWonbhrJF14/86JSd16Xime6J4a3OsXKzTo36sLBDsYfYXof7bwfKk/GUryMFOG
+0aZqiZbiRQ8uCfzd+MtnNxO0WxiyZvj8i7hMjK50yBhRs/5YAHSoLxFOfVVSLNAi
+nhIDqtzeLA2W05PGusgz/0w8FHI6J64jZY4EQLgr43K6DXoFLQjtsl8JZfMO9fEc
+0j5vRytXtYlTSlQEtKeQ8cvpcqbY0gmrEasZ4v1jEzemXzvFkx+ck+Ayl/vHx60A
+AZCom66BudFArAnuVdrMAWUH+78Xf2l5en7kz40QdQKCAQBWxkran+M7dQimtVGT
+qC9msWQs5YFCioHGgKKhw2Yq0G8+Dy3uMbiEsHkjH5iy+oOydu5hqj6Ew2AVvtcH
++xs2iIFNYIgJ5A52XkNfKUCz+EIFalLwaPPh7nHnMPkYTDTJqAFsWVt3DjnctO2V
+AuR1WKoTtyq4vdGIOour+GlwQ4bILVxbAZ1Dvdj5RjegQZVtKCfDHMHXkzHNpMgV
+3ULreEu9mozQnM4ToqsdJRoW3DoAEstHzcIZgJnJALYLuughktCaHlBDxzqZrCr/
+QynIeO4O+yWXk20EBHhrbS3SeFq18rWysOgNW7k0+EcIyJ00CPHJiQuxXVkhHSVx
+/VfZAoIBAGA1isg642No/wgS41c1OZ93hRfK2cl/ruIGFtowFqZwmJs5cT5PeSD9
+eYJKggnbKcdkyVxGUi8B4NMHk4iRnd3KY5e3R49H/je+H/5tj1ibBtKU432oqNvz
+sK2dW7oFMKEru6p0MDieSiHVcWQQj1yFyDi83kDf82FjRjgAE92Um/EcZ63VUDnh
+2onWaQlSiq59ypCpfpH/XJ0MPrefm2zkWsR2RL9nHaK6e9Bt/i6SaJTbw7Kq1ecY
+tqWbolAaZ8OhvoeyNJ5rNZxRBwcsOwOr4NbxG90/W+5txrRNnccOgCk6AM3NaKNh
+Mg590sr7jby8J9h2MsHVzUb4fPJfFh0CggEBANS+aqEzWflHMOR4knhM7QHHaTfS
+4wwR3zL3/tSaV/cxNGehtjyEg85/aKklt/ude3/sm/Z0m6jQEMWgAt5iwuBovPA+
+1/rGkWTHL+dQr0i81C3b/Ymx7izL7BobaYV0o00EoKotC0NP5qR0fBKSkTfFqAYG
+SxnHtw/vduxu2H6TyIrdtvNNqc1PbHdzDI/FwzcWZFNyHBzSWwZxB5w+21uRUayv
+Iz3zcytrZZbAuOjCnhxNL/6XgcttqWSVFB4Ul1xiXrXDx2Xq+FfM40UF7oKGd+Kt
+B0wMqoZJj+0CdFfRZxHA6/n8v1Al+8lYo8smp+R9fR6qZKcugEFgdVkIl7E=
+-----END RSA PRIVATE KEY-----
diff --git a/ondevice-signing/tests/SigningUtilsTest.cpp b/ondevice-signing/tests/SigningUtilsTest.cpp
new file mode 100644
index 0000000..10f7629
--- /dev/null
+++ b/ondevice-signing/tests/SigningUtilsTest.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include "CertUtils.h"
+#include "VerityUtils.h"
+
+// These files were created using the following commands:
+// openssl genrsa -out SigningUtils.pem 4096
+// openssl req -new -x509 -key SigningUtils.pem -out SigningUtils.cert.pem
+// openssl x509 -in SigningUtils.cert.pem -out SigningUtils.cert.der -outform DER
+// head -c 4096 </dev/urandom >test_file
+// openssl dgst -sign SigningUtils.pem -keyform PEM -sha256 -out test_file.sig -binary test_file
+const std::string kTestCert = "SigningUtils.cert.der";
+const std::string kTestFile = "test_file";
+const std::string kTestFileSignature = "test_file.sig";
+
+TEST(SigningUtilsTest, CheckVerifySignature) {
+    std::string signature;
+    std::string sigFile = android::base::GetExecutableDirectory() + "/" + kTestFileSignature;
+    ASSERT_TRUE(android::base::ReadFileToString(sigFile, &signature));
+
+    std::string data;
+    std::string testFile = android::base::GetExecutableDirectory() + "/" + kTestFile;
+    ASSERT_TRUE(android::base::ReadFileToString(testFile, &data));
+
+    std::string testCert = android::base::GetExecutableDirectory() + "/" + kTestCert;
+    auto trustedKey = extractPublicKeyFromX509(testCert.c_str());
+    ASSERT_TRUE(trustedKey.ok());
+
+    auto result = verifySignature(data, signature, *trustedKey);
+    ASSERT_TRUE(result.ok());
+}
diff --git a/ondevice-signing/tests/test_file b/ondevice-signing/tests/test_file
new file mode 100644
index 0000000..8a121be
--- /dev/null
+++ b/ondevice-signing/tests/test_file
Binary files differ
diff --git a/ondevice-signing/tests/test_file.sig b/ondevice-signing/tests/test_file.sig
new file mode 100644
index 0000000..ffd95dc
--- /dev/null
+++ b/ondevice-signing/tests/test_file.sig
Binary files differ
diff --git a/provisioner/Android.bp b/provisioner/Android.bp
index afbc405..665a9e7 100644
--- a/provisioner/Android.bp
+++ b/provisioner/Android.bp
@@ -47,8 +47,10 @@
     name: "rkp_factory_extraction_tool",
     vendor: true,
     srcs: ["rkp_factory_extraction_tool.cpp"],
+    defaults: [
+        "keymint_use_latest_hal_aidl_ndk_shared",
+    ],
     shared_libs: [
-        "android.hardware.security.keymint-V1-ndk_platform",
         "libbinder",
         "libbinder_ndk",
         "libcrypto",
diff --git a/provisioner/rkp_factory_extraction_tool.cpp b/provisioner/rkp_factory_extraction_tool.cpp
index c439b99..c29bacb 100644
--- a/provisioner/rkp_factory_extraction_tool.cpp
+++ b/provisioner/rkp_factory_extraction_tool.cpp
@@ -22,6 +22,7 @@
 #include <cppbor.h>
 #include <gflags/gflags.h>
 #include <keymaster/cppcose/cppcose.h>
+#include <openssl/base64.h>
 #include <remote_prov/remote_prov_utils.h>
 #include <sys/random.h>
 
@@ -29,6 +30,7 @@
 using aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent;
 using aidl::android::hardware::security::keymint::MacedPublicKey;
 using aidl::android::hardware::security::keymint::ProtectedData;
+using aidl::android::hardware::security::keymint::RpcHardwareInfo;
 using aidl::android::hardware::security::keymint::remote_prov::generateEekChain;
 using aidl::android::hardware::security::keymint::remote_prov::getProdEekChain;
 using aidl::android::hardware::security::keymint::remote_prov::jsonEncodeCsrWithBuild;
@@ -49,6 +51,26 @@
 
 constexpr size_t kChallengeSize = 16;
 
+std::string toBase64(const std::vector<uint8_t>& buffer) {
+    size_t base64Length;
+    int rc = EVP_EncodedLength(&base64Length, buffer.size());
+    if (!rc) {
+        std::cerr << "Error getting base64 length. Size overflow?" << std::endl;
+        exit(-1);
+    }
+
+    std::string base64(base64Length, ' ');
+    rc = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(base64.data()), buffer.data(), buffer.size());
+    ++rc;  // Account for NUL, which BoringSSL does not for some reason.
+    if (rc != base64Length) {
+        std::cerr << "Error writing base64. Expected " << base64Length
+                  << " bytes to be written, but " << rc << " bytes were actually written."
+                  << std::endl;
+        exit(-1);
+    }
+    return base64;
+}
+
 std::vector<uint8_t> generateChallenge() {
     std::vector<uint8_t> challenge(kChallengeSize);
 
@@ -56,9 +78,13 @@
     uint8_t* writePtr = challenge.data();
     while (bytesRemaining > 0) {
         int bytesRead = getrandom(writePtr, bytesRemaining, /*flags=*/0);
-        if (bytesRead < 0 && errno != EINTR) {
-            std::cerr << errno << ": " << strerror(errno) << std::endl;
-            exit(-1);
+        if (bytesRead < 0) {
+            if (errno == EINTR) {
+                continue;
+            } else {
+                std::cerr << errno << ": " << strerror(errno) << std::endl;
+                exit(-1);
+            }
         }
         bytesRemaining -= bytesRead;
         writePtr += bytesRead;
@@ -88,19 +114,22 @@
     return certificateRequest;
 }
 
-std::vector<uint8_t> getEekChain() {
+std::vector<uint8_t> getEekChain(uint32_t curve) {
     if (FLAGS_test_mode) {
         const std::vector<uint8_t> kFakeEekId = {'f', 'a', 'k', 'e', 0};
-        auto eekOrErr = generateEekChain(3 /* chainlength */, kFakeEekId);
+        auto eekOrErr = generateEekChain(curve, 3 /* chainlength */, kFakeEekId);
         if (!eekOrErr) {
             std::cerr << "Failed to generate test EEK somehow: " << eekOrErr.message() << std::endl;
             exit(-1);
         }
-        auto [eek, ignored_pubkey, ignored_privkey] = eekOrErr.moveValue();
+        auto [eek, pubkey, privkey] = eekOrErr.moveValue();
+        std::cout << "EEK raw keypair:" << std::endl;
+        std::cout << "  pub:  " << toBase64(pubkey) << std::endl;
+        std::cout << "  priv: " << toBase64(privkey) << std::endl;
         return eek;
     }
 
-    return getProdEekChain();
+    return getProdEekChain(curve);
 }
 
 void writeOutput(const Array& csr) {
@@ -134,16 +163,23 @@
     auto rkp_service = IRemotelyProvisionedComponent::fromBinder(rkp_binder);
     if (!rkp_service) {
         std::cerr << "Unable to get binder object for '" << fullName << "', skipping.";
-        return;
+        exit(-1);
     }
 
     std::vector<uint8_t> keysToSignMac;
     std::vector<MacedPublicKey> emptyKeys;
     DeviceInfo verifiedDeviceInfo;
     ProtectedData protectedData;
-    ::ndk::ScopedAStatus status = rkp_service->generateCertificateRequest(
-        FLAGS_test_mode, emptyKeys, getEekChain(), challenge, &verifiedDeviceInfo, &protectedData,
-        &keysToSignMac);
+    RpcHardwareInfo hwInfo;
+    ::ndk::ScopedAStatus status = rkp_service->getHardwareInfo(&hwInfo);
+    if (!status.isOk()) {
+        std::cerr << "Failed to get hardware info for '" << fullName
+                  << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+        exit(-1);
+    }
+    status = rkp_service->generateCertificateRequest(
+        FLAGS_test_mode, emptyKeys, getEekChain(hwInfo.supportedEekCurve), challenge,
+        &verifiedDeviceInfo, &protectedData, &keysToSignMac);
     if (!status.isOk()) {
         std::cerr << "Bundle extraction failed for '" << fullName
                   << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;