Snap for 10453563 from 1c6058cc0da2c2f03d7114ec9e7417b30bc97811 to mainline-art-release

Change-Id: I36d021d1d0a0e4389bc586c98828e6ff731a675c
diff --git a/OWNERS b/OWNERS
index 563a78c..03e5769 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,8 +1,9 @@
 alanstokes@google.com
 cbrubaker@google.com
+drysdale@google.com
+eranm@google.com
 hasinitg@google.com
 jbires@google.com
-jdanis@google.com
 jeffv@google.com
 kroot@google.com
 sethmo@google.com
diff --git a/diced/Android.bp b/diced/Android.bp
deleted file mode 100644
index e13d863..0000000
--- a/diced/Android.bp
+++ /dev/null
@@ -1,228 +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_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/OWNERS b/diced/OWNERS
new file mode 100644
index 0000000..387cd93
--- /dev/null
+++ b/diced/OWNERS
@@ -0,0 +1,3 @@
+alanstokes@google.com
+aliceywang@google.com
+ascull@google.com
diff --git a/diced/TEST_MAPPING b/diced/TEST_MAPPING
new file mode 100644
index 0000000..caf847f
--- /dev/null
+++ b/diced/TEST_MAPPING
@@ -0,0 +1,19 @@
+{
+  "presubmit": [
+    {
+      "name": "libdiced_open_dice.integration_test"
+    },
+    {
+      "name": "libdiced_open_dice_nostd.integration_test"
+    },
+    {
+      "name": "libopen_dice_cbor_bindgen_test"
+    },
+    {
+      "name": "libopen_dice_bcc_bindgen_test"
+    },
+    {
+      "name": "libdiced_sample_inputs.integration_test"
+    }
+  ]
+}
diff --git a/diced/aidl/Android.bp b/diced/aidl/Android.bp
deleted file mode 100644
index 75c1856..0000000
--- a/diced/aidl/Android.bp
+++ /dev/null
@@ -1,50 +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"],
-}
-
-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
deleted file mode 100644
index c81fdea..0000000
--- a/diced/aidl/android/security/dice/IDiceMaintenance.aidl
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 2b3ef76..0000000
--- a/diced/aidl/android/security/dice/IDiceNode.aidl
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 7c66058..0000000
--- a/diced/aidl/android/security/dice/ResponseCode.aidl
+++ /dev/null
@@ -1,37 +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 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
deleted file mode 100644
index 2226f47..0000000
--- a/diced/diced.microdroid.rc
+++ /dev/null
@@ -1,13 +0,0 @@
-# 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
deleted file mode 100644
index 8c43fa5..0000000
--- a/diced/diced.rc
+++ /dev/null
@@ -1,13 +0,0 @@
-# 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/Android.bp b/diced/open_dice/Android.bp
new file mode 100644
index 0000000..2505b42
--- /dev/null
+++ b/diced/open_dice/Android.bp
@@ -0,0 +1,256 @@
+package {
+    default_visibility: [":__subpackages__"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+    name: "libdiced_open_dice_defaults",
+    crate_name: "diced_open_dice",
+    srcs: ["src/lib.rs"],
+    static_libs: [
+        "libopen_dice_cbor",
+    ],
+    vendor_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.virt",
+    ],
+}
+
+rust_library_rlib {
+    name: "libdiced_open_dice_nostd",
+    defaults: ["libdiced_open_dice_defaults"],
+    rustlibs: [
+        "libopen_dice_bcc_bindgen_nostd",
+        "libopen_dice_cbor_bindgen_nostd",
+        "libzeroize_nostd",
+    ],
+    whole_static_libs: [
+        "libopen_dice_cbor",
+        "libcrypto_baremetal",
+    ],
+    visibility: [
+        "//packages/modules/Virtualization:__subpackages__",
+    ],
+}
+
+rust_library {
+    name: "libdiced_open_dice",
+    defaults: ["libdiced_open_dice_defaults"],
+    rustlibs: [
+        "libopen_dice_bcc_bindgen",
+        "libopen_dice_cbor_bindgen",
+        "libzeroize",
+    ],
+    features: [
+        "std",
+    ],
+    shared_libs: [
+        "libcrypto",
+    ],
+    whole_static_libs: [
+        "libopen_dice_bcc",
+    ],
+    visibility: [
+        "//system/security/diced:__subpackages__",
+        "//packages/modules/Virtualization:__subpackages__",
+        "//hardware/interfaces/security/dice/aidl:__subpackages__",
+    ],
+}
+
+rust_defaults {
+    name: "libdiced_open_dice_test_defaults",
+    crate_name: "diced_open_dice_test",
+    srcs: ["tests/*.rs"],
+    test_suites: ["general-tests"],
+}
+
+rust_test {
+    name: "libdiced_open_dice.integration_test",
+    defaults: ["libdiced_open_dice_test_defaults"],
+    rustlibs: [
+        "libdiced_open_dice",
+    ],
+}
+
+rust_test {
+    name: "libdiced_open_dice_nostd.integration_test",
+    defaults: ["libdiced_open_dice_test_defaults"],
+    rustlibs: [
+        "libdiced_open_dice_nostd",
+    ],
+}
+
+rust_defaults {
+    name: "libopen_dice_bindgen_nostd.rust_defaults",
+    bindgen_flags: [
+        "--use-core",
+        "--ctypes-prefix=core::ffi",
+        "--raw-line=#![no_std]",
+    ],
+    no_stdlibs: true,
+    prefer_rlib: true,
+    stdlibs: [
+        "libcore.rust_sysroot",
+        "libcompiler_builtins.rust_sysroot",
+    ],
+    target: {
+        musl: {
+            enabled: false,
+        },
+        glibc: {
+            enabled: false,
+        },
+        darwin: {
+            enabled: false,
+        },
+    },
+}
+
+rust_defaults {
+    name: "libopen_dice.rust_defaults",
+    host_supported: true,
+    vendor_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.compos",
+        "com.android.virt",
+    ],
+}
+
+rust_defaults {
+    name: "libopen_dice_cbor_bindgen.rust_defaults",
+    defaults: ["libopen_dice.rust_defaults"],
+    wrapper_src: "bindgen/dice.h",
+    crate_name: "open_dice_cbor_bindgen",
+    source_stem: "bindings",
+    bindgen_flags: [
+        "--size_t-is-usize",
+        "--rustified-enum DiceConfigType",
+        "--rustified-enum DiceMode",
+        "--rustified-enum DiceResult",
+
+        // By generating only essential functions, we can make bindings concise and
+        // optimize compilation time.
+        "--allowlist-function=DiceDeriveCdiPrivateKeySeed",
+        "--allowlist-function=DiceDeriveCdiCertificateId",
+        "--allowlist-function=DiceMainFlow",
+        "--allowlist-function=DiceHash",
+        "--allowlist-function=DiceKdf",
+        "--allowlist-function=DiceKeypairFromSeed",
+        "--allowlist-function=DiceSign",
+        "--allowlist-function=DiceVerify",
+        "--allowlist-function=DiceGenerateCertificate",
+
+        // We also need some constants in addition to the functions.
+        "--allowlist-var=DICE_CDI_SIZE",
+        "--allowlist-var=DICE_HASH_SIZE",
+        "--allowlist-var=DICE_HIDDEN_SIZE",
+        "--allowlist-var=DICE_INLINE_CONFIG_SIZE",
+        "--allowlist-var=DICE_PRIVATE_KEY_SEED_SIZE",
+        "--allowlist-var=DICE_ID_SIZE",
+        "--allowlist-var=DICE_PUBLIC_KEY_SIZE",
+        "--allowlist-var=DICE_PRIVATE_KEY_SIZE",
+        "--allowlist-var=DICE_SIGNATURE_SIZE",
+    ],
+}
+
+rust_bindgen {
+    name: "libopen_dice_cbor_bindgen",
+    defaults: ["libopen_dice_cbor_bindgen.rust_defaults"],
+    whole_static_libs: ["libopen_dice_cbor"],
+}
+
+rust_bindgen {
+    name: "libopen_dice_cbor_bindgen_nostd",
+    defaults: [
+        "libopen_dice_cbor_bindgen.rust_defaults",
+        "libopen_dice_bindgen_nostd.rust_defaults",
+    ],
+    whole_static_libs: ["libopen_dice_cbor_baremetal"],
+}
+
+rust_defaults {
+    name: "libopen_dice_bcc_bindgen.rust_defaults",
+    defaults: ["libopen_dice.rust_defaults"],
+    wrapper_src: "bindgen/android/bcc.h",
+    crate_name: "open_dice_bcc_bindgen",
+    source_stem: "bindings",
+    bindgen_flags: [
+        "--size_t-is-usize",
+
+        // By generating only essential functions, we can make bindings concise and
+        // optimize compilation time.
+        "--allowlist-function=BccFormatConfigDescriptor",
+        "--allowlist-function=BccMainFlow",
+        "--allowlist-function=BccHandoverMainFlow",
+        "--allowlist-function=BccHandoverParse",
+
+        // We also need some constants in addition to the functions.
+        "--allowlist-var=BCC_INPUT_COMPONENT_NAME",
+        "--allowlist-var=BCC_INPUT_COMPONENT_VERSION",
+        "--allowlist-var=BCC_INPUT_RESETTABLE",
+
+        // Prevent DiceInputValues from being generated a second time and
+        // import it instead from open_dice_cbor_bindgen.
+        "--blocklist-type=DiceInputValues_",
+        "--blocklist-type=DiceInputValues",
+        "--raw-line",
+        "pub use open_dice_cbor_bindgen::DiceInputValues;",
+
+        // Prevent DiceResult from being generated a second time and
+        // import it instead from open_dice_cbor_bindgen.
+        "--blocklist-type=DiceResult",
+        "--raw-line",
+        "pub use open_dice_cbor_bindgen::DiceResult;",
+    ],
+
+}
+
+rust_bindgen {
+    name: "libopen_dice_bcc_bindgen",
+    defaults: ["libopen_dice_bcc_bindgen.rust_defaults"],
+    rustlibs: [
+        "libopen_dice_cbor_bindgen",
+    ],
+    whole_static_libs: ["libopen_dice_bcc"],
+}
+
+rust_bindgen {
+    name: "libopen_dice_bcc_bindgen_nostd",
+    defaults: [
+        "libopen_dice_bcc_bindgen.rust_defaults",
+        "libopen_dice_bindgen_nostd.rust_defaults",
+    ],
+    rustlibs: [
+        "libopen_dice_cbor_bindgen_nostd",
+    ],
+    whole_static_libs: ["libopen_dice_bcc_baremetal"],
+}
+
+rust_test {
+    name: "libopen_dice_cbor_bindgen_test",
+    srcs: [
+        ":libopen_dice_cbor_bindgen",
+    ],
+    crate_name: "open_dice_cbor_bindgen_test",
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    clippy_lints: "none",
+    lints: "none",
+}
+
+rust_test {
+    name: "libopen_dice_bcc_bindgen_test",
+    srcs: [
+        ":libopen_dice_bcc_bindgen",
+    ],
+    crate_name: "open_dice_bcc_bindgen_test",
+    rustlibs: [
+        "libopen_dice_cbor_bindgen",
+    ],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    clippy_lints: "none",
+    lints: "none",
+}
diff --git a/diced/open_dice/bindgen/android/bcc.h b/diced/open_dice/bindgen/android/bcc.h
new file mode 100644
index 0000000..4dfc862
--- /dev/null
+++ b/diced/open_dice/bindgen/android/bcc.h
@@ -0,0 +1,17 @@
+// Copyright 2021 Google LLC
+//
+// 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
+//
+//     https://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 <dice/android/bcc.h>
diff --git a/diced/open_dice/bindgen/dice.h b/diced/open_dice/bindgen/dice.h
new file mode 100644
index 0000000..47fe911
--- /dev/null
+++ b/diced/open_dice/bindgen/dice.h
@@ -0,0 +1,18 @@
+// Copyright 2021 Google LLC
+//
+// 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
+//
+//     https://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 <dice/dice.h>
+#include <dice/ops.h>
diff --git a/diced/open_dice/src/bcc.rs b/diced/open_dice/src/bcc.rs
new file mode 100644
index 0000000..1575113
--- /dev/null
+++ b/diced/open_dice/src/bcc.rs
@@ -0,0 +1,192 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This module mirrors the content in open-dice/include/dice/android/bcc.h
+
+use crate::dice::{Cdi, CdiValues, DiceArtifacts, InputValues, CDI_SIZE};
+use crate::error::{check_result, DiceError, Result};
+use open_dice_bcc_bindgen::{
+    BccConfigValues, BccFormatConfigDescriptor, BccHandoverMainFlow, BccHandoverParse, BccMainFlow,
+    BCC_INPUT_COMPONENT_NAME, BCC_INPUT_COMPONENT_VERSION, BCC_INPUT_RESETTABLE,
+};
+use std::{ffi::CStr, ptr};
+
+/// Formats a configuration descriptor following the BCC's specification.
+/// See https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl
+pub fn bcc_format_config_descriptor(
+    name: Option<&CStr>,
+    version: Option<u64>,
+    resettable: bool,
+    buffer: &mut [u8],
+) -> Result<usize> {
+    let mut inputs = 0;
+    if name.is_some() {
+        inputs |= BCC_INPUT_COMPONENT_NAME;
+    }
+    if version.is_some() {
+        inputs |= BCC_INPUT_COMPONENT_VERSION;
+    }
+    if resettable {
+        inputs |= BCC_INPUT_RESETTABLE;
+    }
+
+    let values = BccConfigValues {
+        inputs,
+        component_name: name.map_or(ptr::null(), |p| p.as_ptr()),
+        component_version: version.unwrap_or(0),
+    };
+
+    let mut buffer_size = 0;
+    // SAFETY: The function writes to the buffer, within the given bounds, and only reads the
+    // input values. It writes its result to buffer_size.
+    check_result(unsafe {
+        BccFormatConfigDescriptor(&values, buffer.len(), buffer.as_mut_ptr(), &mut buffer_size)
+    })?;
+    Ok(buffer_size)
+}
+
+/// Executes the main BCC flow.
+///
+/// Given a full set of input values along with the current BCC and CDI values,
+/// computes the next CDI values and matching updated BCC.
+pub fn bcc_main_flow(
+    current_cdi_attest: &Cdi,
+    current_cdi_seal: &Cdi,
+    current_bcc: &[u8],
+    input_values: &InputValues,
+    next_cdi_values: &mut CdiValues,
+    next_bcc: &mut [u8],
+) -> Result<usize> {
+    let mut next_bcc_size = 0;
+    // SAFETY: `BccMainFlow` only reads the current `bcc` and CDI values and writes
+    // to `next_bcc` and next CDI values within its bounds. It also reads
+    // `input_values` as a constant input and doesn't store any pointer.
+    // The first argument can be null and is not used in the current implementation.
+    check_result(unsafe {
+        BccMainFlow(
+            ptr::null_mut(), // context
+            current_cdi_attest.as_ptr(),
+            current_cdi_seal.as_ptr(),
+            current_bcc.as_ptr(),
+            current_bcc.len(),
+            input_values.as_ptr(),
+            next_bcc.len(),
+            next_bcc.as_mut_ptr(),
+            &mut next_bcc_size,
+            next_cdi_values.cdi_attest.as_mut_ptr(),
+            next_cdi_values.cdi_seal.as_mut_ptr(),
+        )
+    })?;
+    Ok(next_bcc_size)
+}
+
+/// Executes the main BCC handover flow.
+///
+/// A BCC handover combines the BCC and CDIs in a single CBOR object.
+/// This function takes the current boot stage's BCC handover bundle and produces a
+/// bundle for the next stage.
+pub fn bcc_handover_main_flow(
+    current_bcc_handover: &[u8],
+    input_values: &InputValues,
+    next_bcc_handover: &mut [u8],
+) -> Result<usize> {
+    let mut next_bcc_handover_size = 0;
+    // SAFETY - The function only reads `current_bcc_handover` and writes to `next_bcc_handover`
+    // within its bounds,
+    // It also reads `input_values` as a constant input and doesn't store any pointer.
+    // The first argument can be null and is not used in the current implementation.
+    check_result(unsafe {
+        BccHandoverMainFlow(
+            ptr::null_mut(), // context
+            current_bcc_handover.as_ptr(),
+            current_bcc_handover.len(),
+            input_values.as_ptr(),
+            next_bcc_handover.len(),
+            next_bcc_handover.as_mut_ptr(),
+            &mut next_bcc_handover_size,
+        )
+    })?;
+
+    Ok(next_bcc_handover_size)
+}
+
+/// A BCC handover combines the BCC and CDIs in a single CBOR object.
+/// This struct is used as return of the function `bcc_handover_parse`, its lifetime is tied
+/// to the lifetime of the raw BCC handover slice.
+#[derive(Debug)]
+pub struct BccHandover<'a> {
+    /// Attestation CDI.
+    cdi_attest: &'a [u8; CDI_SIZE],
+    /// Sealing CDI.
+    cdi_seal: &'a [u8; CDI_SIZE],
+    /// Boot Certificate Chain.
+    bcc: Option<&'a [u8]>,
+}
+
+impl<'a> DiceArtifacts for BccHandover<'a> {
+    fn cdi_attest(&self) -> &[u8; CDI_SIZE] {
+        self.cdi_attest
+    }
+
+    fn cdi_seal(&self) -> &[u8; CDI_SIZE] {
+        self.cdi_seal
+    }
+
+    fn bcc(&self) -> Option<&[u8]> {
+        self.bcc
+    }
+}
+
+/// A BCC handover combines the BCC and CDIs in a single CBOR object.
+/// This function parses the `bcc_handover` to extracts the BCC and CDIs.
+/// The lifetime of the returned `BccHandover` is tied to the given `bcc_handover` slice.
+pub fn bcc_handover_parse(bcc_handover: &[u8]) -> Result<BccHandover> {
+    let mut cdi_attest: *const u8 = ptr::null();
+    let mut cdi_seal: *const u8 = ptr::null();
+    let mut bcc: *const u8 = ptr::null();
+    let mut bcc_size = 0;
+    // SAFETY: The `bcc_handover` is only read and never stored and the returned pointers should all
+    // point within the address range of the `bcc_handover` or be NULL.
+    check_result(unsafe {
+        BccHandoverParse(
+            bcc_handover.as_ptr(),
+            bcc_handover.len(),
+            &mut cdi_attest,
+            &mut cdi_seal,
+            &mut bcc,
+            &mut bcc_size,
+        )
+    })?;
+    let cdi_attest = sub_slice(bcc_handover, cdi_attest, CDI_SIZE)?;
+    let cdi_seal = sub_slice(bcc_handover, cdi_seal, CDI_SIZE)?;
+    let bcc = sub_slice(bcc_handover, bcc, bcc_size).ok();
+    Ok(BccHandover {
+        cdi_attest: cdi_attest.try_into().map_err(|_| DiceError::PlatformError)?,
+        cdi_seal: cdi_seal.try_into().map_err(|_| DiceError::PlatformError)?,
+        bcc,
+    })
+}
+
+/// Gets a slice the `addr` points to and of length `len`.
+/// The slice should be contained in the buffer.
+fn sub_slice(buffer: &[u8], addr: *const u8, len: usize) -> Result<&[u8]> {
+    if addr.is_null() || !buffer.as_ptr_range().contains(&addr) {
+        return Err(DiceError::PlatformError);
+    }
+    // SAFETY: This is safe because addr is not null and is within the range of the buffer.
+    let start: usize = unsafe {
+        addr.offset_from(buffer.as_ptr()).try_into().map_err(|_| DiceError::PlatformError)?
+    };
+    start.checked_add(len).and_then(|end| buffer.get(start..end)).ok_or(DiceError::PlatformError)
+}
diff --git a/diced/open_dice/src/dice.rs b/diced/open_dice/src/dice.rs
new file mode 100644
index 0000000..9266b6f
--- /dev/null
+++ b/diced/open_dice/src/dice.rs
@@ -0,0 +1,270 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Structs and functions about the types used in DICE.
+//! This module mirrors the content in open-dice/include/dice/dice.h
+
+use crate::error::{check_result, Result};
+pub use open_dice_cbor_bindgen::DiceMode;
+use open_dice_cbor_bindgen::{
+    DiceConfigType, DiceDeriveCdiCertificateId, DiceDeriveCdiPrivateKeySeed, DiceInputValues,
+    DiceMainFlow, 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 std::ptr;
+use zeroize::{Zeroize, ZeroizeOnDrop};
+
+/// 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.
+const INLINE_CONFIG_SIZE: usize = DICE_INLINE_CONFIG_SIZE as usize;
+/// The size of a CDI.
+pub const CDI_SIZE: usize = DICE_CDI_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 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;
+/// The size of an ID.
+pub const ID_SIZE: usize = DICE_ID_SIZE as usize;
+
+/// Array type of hashes used by DICE.
+pub type Hash = [u8; HASH_SIZE];
+/// Array type of additional input.
+pub type Hidden = [u8; HIDDEN_SIZE];
+/// Array type of inline configuration values.
+pub type InlineConfig = [u8; INLINE_CONFIG_SIZE];
+/// Array type of CDIs.
+pub type Cdi = [u8; CDI_SIZE];
+/// Array type of the public key.
+pub type PublicKey = [u8; PUBLIC_KEY_SIZE];
+/// Array type of the signature.
+pub type Signature = [u8; SIGNATURE_SIZE];
+/// Array type of DICE ID.
+pub type DiceId = [u8; ID_SIZE];
+
+/// A trait for types that represent Dice artifacts, which include:
+///
+/// - Attestation CDI
+/// - Sealing CDI
+/// - Boot Certificate Chain
+///
+/// Types that implement this trait provide an access these artifacts.
+pub trait DiceArtifacts {
+    /// Returns a reference to the attestation CDI.
+    fn cdi_attest(&self) -> &[u8; CDI_SIZE];
+
+    /// Returns a reference to the sealing CDI.
+    fn cdi_seal(&self) -> &[u8; CDI_SIZE];
+
+    /// Returns a reference to the Boot Certificate Chain, if present.
+    fn bcc(&self) -> Option<&[u8]>;
+}
+
+/// TODO(b/268587826): Clean up the memory cache after zeroing out the memory
+/// for sensitive data like CDI values and private key.
+/// CDI Values.
+#[derive(Debug, Zeroize, ZeroizeOnDrop, Default)]
+pub struct CdiValues {
+    /// Attestation CDI.
+    pub cdi_attest: [u8; CDI_SIZE],
+    /// Sealing CDI.
+    pub cdi_seal: [u8; CDI_SIZE],
+}
+
+/// Private key seed. The data is zeroed out when the struct is dropped.
+#[derive(Zeroize, ZeroizeOnDrop, Default)]
+pub struct PrivateKeySeed([u8; PRIVATE_KEY_SEED_SIZE]);
+
+impl PrivateKeySeed {
+    /// Returns an array reference of the private key seed.
+    pub fn as_array(&self) -> &[u8; PRIVATE_KEY_SEED_SIZE] {
+        &self.0
+    }
+
+    /// Returns a mutable pointer to the slice buffer of the private key seed.
+    pub fn as_mut_ptr(&mut self) -> *mut u8 {
+        self.0.as_mut_ptr()
+    }
+}
+
+/// Private key. The data is zeroed out when the struct is dropped.
+#[derive(Zeroize, ZeroizeOnDrop)]
+pub struct PrivateKey([u8; PRIVATE_KEY_SIZE]);
+
+impl Default for PrivateKey {
+    /// Creates a new `PrivateKey` instance with all bytes set to 0.
+    ///
+    /// Since the size of the private key array is too large to be initialized
+    /// with a default value, this implementation sets all the bytes in the array
+    /// to 0 using the `[0u8; PRIVATE_KEY_SIZE]` syntax.
+    fn default() -> Self {
+        Self([0u8; PRIVATE_KEY_SIZE])
+    }
+}
+
+impl PrivateKey {
+    /// Returns an array reference of the private key.
+    pub fn as_array(&self) -> &[u8; PRIVATE_KEY_SIZE] {
+        &self.0
+    }
+
+    /// Returns a mutable pointer to the slice buffer of the private key.
+    pub fn as_mut_ptr(&mut self) -> *mut u8 {
+        self.0.as_mut_ptr()
+    }
+}
+
+/// Configuration descriptor for DICE input values.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Config<'a> {
+    /// Reference to an inline descriptor.
+    Inline(&'a InlineConfig),
+    /// Reference to a free form descriptor that will be hashed by the implementation.
+    Descriptor(&'a [u8]),
+}
+
+impl Config<'_> {
+    fn dice_config_type(&self) -> DiceConfigType {
+        match self {
+            Self::Inline(_) => DiceConfigType::kDiceConfigTypeInline,
+            Self::Descriptor(_) => DiceConfigType::kDiceConfigTypeDescriptor,
+        }
+    }
+
+    fn inline_config(&self) -> InlineConfig {
+        match self {
+            Self::Inline(inline) => **inline,
+            Self::Descriptor(_) => [0u8; INLINE_CONFIG_SIZE],
+        }
+    }
+
+    fn descriptor_ptr(&self) -> *const u8 {
+        match self {
+            Self::Descriptor(descriptor) => descriptor.as_ptr(),
+            _ => ptr::null(),
+        }
+    }
+
+    fn descriptor_size(&self) -> usize {
+        match self {
+            Self::Descriptor(descriptor) => descriptor.len(),
+            _ => 0,
+        }
+    }
+}
+
+/// Wrap of `DiceInputValues`.
+#[derive(Clone, Debug)]
+pub struct InputValues(DiceInputValues);
+
+impl InputValues {
+    /// Creates a new `InputValues`.
+    pub fn new(
+        code_hash: Hash,
+        config: Config,
+        authority_hash: Hash,
+        mode: DiceMode,
+        hidden: Hidden,
+    ) -> Self {
+        Self(DiceInputValues {
+            code_hash,
+            code_descriptor: ptr::null(),
+            code_descriptor_size: 0,
+            config_type: config.dice_config_type(),
+            config_value: config.inline_config(),
+            config_descriptor: config.descriptor_ptr(),
+            config_descriptor_size: config.descriptor_size(),
+            authority_hash,
+            authority_descriptor: ptr::null(),
+            authority_descriptor_size: 0,
+            mode,
+            hidden,
+        })
+    }
+
+    /// Returns a raw pointer to the wrapped `DiceInputValues`.
+    pub fn as_ptr(&self) -> *const DiceInputValues {
+        &self.0 as *const DiceInputValues
+    }
+}
+
+/// Derives a CDI private key seed from a `cdi_attest` value.
+pub fn derive_cdi_private_key_seed(cdi_attest: &Cdi) -> Result<PrivateKeySeed> {
+    let mut seed = PrivateKeySeed::default();
+    // SAFETY: The function writes to the buffer within the given bounds, and only reads the
+    // input values. The first argument context is not used in this function.
+    check_result(unsafe {
+        DiceDeriveCdiPrivateKeySeed(
+            ptr::null_mut(), // context
+            cdi_attest.as_ptr(),
+            seed.as_mut_ptr(),
+        )
+    })?;
+    Ok(seed)
+}
+
+/// Derives an ID from the given `cdi_public_key` value.
+pub fn derive_cdi_certificate_id(cdi_public_key: &[u8]) -> Result<DiceId> {
+    let mut id = [0u8; ID_SIZE];
+    // SAFETY: The function writes to the buffer within the given bounds, and only reads the
+    // input values. The first argument context is not used in this function.
+    check_result(unsafe {
+        DiceDeriveCdiCertificateId(
+            ptr::null_mut(), // context
+            cdi_public_key.as_ptr(),
+            cdi_public_key.len(),
+            id.as_mut_ptr(),
+        )
+    })?;
+    Ok(id)
+}
+
+/// Executes the main DICE flow.
+///
+/// Given a full set of input values and the current CDI values, computes the
+/// next CDI values and a matching certificate.
+/// Returns the actual size of the next CDI certificate.
+pub fn dice_main_flow(
+    current_cdi_attest: &Cdi,
+    current_cdi_seal: &Cdi,
+    input_values: &InputValues,
+    next_cdi_certificate: &mut [u8],
+    next_cdi_values: &mut CdiValues,
+) -> Result<usize> {
+    let mut next_cdi_certificate_actual_size = 0;
+    // SAFETY: The function only reads the current CDI values and inputs and writes
+    // to `next_cdi_certificate` and next CDI values within its bounds.
+    // The first argument can be null and is not used in the current implementation.
+    check_result(unsafe {
+        DiceMainFlow(
+            ptr::null_mut(), // context
+            current_cdi_attest.as_ptr(),
+            current_cdi_seal.as_ptr(),
+            input_values.as_ptr(),
+            next_cdi_certificate.len(),
+            next_cdi_certificate.as_mut_ptr(),
+            &mut next_cdi_certificate_actual_size,
+            next_cdi_values.cdi_attest.as_mut_ptr(),
+            next_cdi_values.cdi_seal.as_mut_ptr(),
+        )
+    })?;
+    Ok(next_cdi_certificate_actual_size)
+}
diff --git a/diced/open_dice/src/error.rs b/diced/open_dice/src/error.rs
new file mode 100644
index 0000000..4c67335
--- /dev/null
+++ b/diced/open_dice/src/error.rs
@@ -0,0 +1,59 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Errors and relating functions thrown in this library.
+
+use open_dice_cbor_bindgen::DiceResult;
+use std::{fmt, result};
+
+#[cfg(feature = "std")]
+use std::error::Error;
+
+/// Error type used by DICE.
+#[derive(Debug)]
+pub enum DiceError {
+    /// Provided input was invalid.
+    InvalidInput,
+    /// Provided buffer was too small.
+    BufferTooSmall,
+    /// Platform error.
+    PlatformError,
+}
+
+/// This makes `DiceError` accepted by anyhow.
+#[cfg(feature = "std")]
+impl Error for DiceError {}
+
+impl fmt::Display for DiceError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Self::InvalidInput => write!(f, "invalid input"),
+            Self::BufferTooSmall => write!(f, "buffer too small"),
+            Self::PlatformError => write!(f, "platform error"),
+        }
+    }
+}
+
+/// DICE result type.
+pub type Result<T> = result::Result<T, DiceError>;
+
+/// Checks the given `DiceResult`. Returns an error if it's not OK.
+pub fn check_result(result: DiceResult) -> Result<()> {
+    match result {
+        DiceResult::kDiceResultOk => Ok(()),
+        DiceResult::kDiceResultInvalidInput => Err(DiceError::InvalidInput),
+        DiceResult::kDiceResultBufferTooSmall => Err(DiceError::BufferTooSmall),
+        DiceResult::kDiceResultPlatformError => Err(DiceError::PlatformError),
+    }
+}
diff --git a/diced/open_dice/src/lib.rs b/diced/open_dice/src/lib.rs
new file mode 100644
index 0000000..e7ec56b
--- /dev/null
+++ b/diced/open_dice/src/lib.rs
@@ -0,0 +1,45 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Implements safe wrappers around the public API of libopen-dice for
+//! both std and nostd usages.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+#[cfg(not(feature = "std"))]
+extern crate core as std;
+
+mod bcc;
+mod dice;
+mod error;
+mod ops;
+#[cfg(feature = "std")]
+mod retry;
+
+pub use bcc::{
+    bcc_format_config_descriptor, bcc_handover_main_flow, bcc_handover_parse, bcc_main_flow,
+    BccHandover,
+};
+pub use dice::{
+    derive_cdi_certificate_id, derive_cdi_private_key_seed, dice_main_flow, Cdi, CdiValues, Config,
+    DiceArtifacts, DiceMode, Hash, Hidden, InlineConfig, InputValues, PrivateKey, PrivateKeySeed,
+    PublicKey, Signature, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE, ID_SIZE, PRIVATE_KEY_SEED_SIZE,
+};
+pub use error::{check_result, DiceError, Result};
+pub use ops::{generate_certificate, hash, kdf, keypair_from_seed, sign, verify};
+#[cfg(feature = "std")]
+pub use retry::{
+    retry_bcc_format_config_descriptor, retry_bcc_main_flow, retry_dice_main_flow,
+    retry_generate_certificate, OwnedDiceArtifacts,
+};
diff --git a/diced/open_dice/src/ops.rs b/diced/open_dice/src/ops.rs
new file mode 100644
index 0000000..8222b26
--- /dev/null
+++ b/diced/open_dice/src/ops.rs
@@ -0,0 +1,142 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This module mirrors the content in open-dice/include/dice/ops.h
+//! It contains the set of functions that implement various operations that the
+//! main DICE functions depend on.
+
+use crate::dice::{
+    Hash, InputValues, PrivateKey, PublicKey, Signature, HASH_SIZE, PRIVATE_KEY_SEED_SIZE,
+    PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE, SIGNATURE_SIZE,
+};
+use crate::error::{check_result, Result};
+use open_dice_cbor_bindgen::{
+    DiceGenerateCertificate, DiceHash, DiceKdf, DiceKeypairFromSeed, DiceSign, DiceVerify,
+};
+use std::ptr;
+
+/// Hashes the provided input using DICE's hash function `DiceHash`.
+pub fn hash(input: &[u8]) -> Result<Hash> {
+    let mut output: Hash = [0; HASH_SIZE];
+    // SAFETY: DiceHash takes a sized input buffer and writes to a constant-sized output buffer.
+    // The first argument context is not used in this function.
+    check_result(unsafe {
+        DiceHash(
+            ptr::null_mut(), // context
+            input.as_ptr(),
+            input.len(),
+            output.as_mut_ptr(),
+        )
+    })?;
+    Ok(output)
+}
+
+/// An implementation of HKDF-SHA512. Derives a key of `derived_key.len()` bytes from `ikm`, `salt`,
+/// and `info`. The derived key is written to the `derived_key`.
+pub fn kdf(ikm: &[u8], salt: &[u8], info: &[u8], derived_key: &mut [u8]) -> Result<()> {
+    // SAFETY: The function writes to the `derived_key`, within the given bounds, and only reads the
+    // input values. The first argument context is not used in this function.
+    check_result(unsafe {
+        DiceKdf(
+            ptr::null_mut(), // context
+            derived_key.len(),
+            ikm.as_ptr(),
+            ikm.len(),
+            salt.as_ptr(),
+            salt.len(),
+            info.as_ptr(),
+            info.len(),
+            derived_key.as_mut_ptr(),
+        )
+    })
+}
+
+/// Deterministically generates a public and private key pair from `seed`.
+/// Since this is deterministic, `seed` is as sensitive as a private key and can
+/// be used directly as the private key.
+pub fn keypair_from_seed(seed: &[u8; PRIVATE_KEY_SEED_SIZE]) -> Result<(PublicKey, PrivateKey)> {
+    let mut public_key = [0u8; PUBLIC_KEY_SIZE];
+    let mut private_key = PrivateKey::default();
+    // SAFETY: The function writes to the `public_key` and `private_key` within the given bounds,
+    // and only reads the `seed`. The first argument context is not used in this function.
+    check_result(unsafe {
+        DiceKeypairFromSeed(
+            ptr::null_mut(), // context
+            seed.as_ptr(),
+            public_key.as_mut_ptr(),
+            private_key.as_mut_ptr(),
+        )
+    })?;
+    Ok((public_key, private_key))
+}
+
+/// Signs the `message` with the give `private_key` using `DiceSign`.
+pub fn sign(message: &[u8], private_key: &[u8; PRIVATE_KEY_SIZE]) -> Result<Signature> {
+    let mut signature = [0u8; SIGNATURE_SIZE];
+    // SAFETY: The function writes to the `signature` within the given bounds, and only reads the
+    // message and the private key. The first argument context is not used in this function.
+    check_result(unsafe {
+        DiceSign(
+            ptr::null_mut(), // context
+            message.as_ptr(),
+            message.len(),
+            private_key.as_ptr(),
+            signature.as_mut_ptr(),
+        )
+    })?;
+    Ok(signature)
+}
+
+/// Verifies the `signature` of the `message` with the given `public_key` using `DiceVerify`.
+pub fn verify(message: &[u8], signature: &Signature, public_key: &PublicKey) -> Result<()> {
+    // SAFETY: only reads the messages, signature and public key as constant values.
+    // The first argument context is not used in this function.
+    check_result(unsafe {
+        DiceVerify(
+            ptr::null_mut(), // context
+            message.as_ptr(),
+            message.len(),
+            signature.as_ptr(),
+            public_key.as_ptr(),
+        )
+    })
+}
+
+/// Generates an X.509 certificate from the given `subject_private_key_seed` and
+/// `input_values`, and signed by `authority_private_key_seed`.
+/// The subject private key seed is supplied here so the implementation can choose
+/// between asymmetric mechanisms, for example ECDSA vs Ed25519.
+/// Returns the actual size of the generated certificate.
+pub fn generate_certificate(
+    subject_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+    authority_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+    input_values: &InputValues,
+    certificate: &mut [u8],
+) -> Result<usize> {
+    let mut certificate_actual_size = 0;
+    // SAFETY: The function writes to the `certificate` within the given bounds, and only reads the
+    // input values and the key seeds. The first argument context is not used in this function.
+    check_result(unsafe {
+        DiceGenerateCertificate(
+            ptr::null_mut(), // context
+            subject_private_key_seed.as_ptr(),
+            authority_private_key_seed.as_ptr(),
+            input_values.as_ptr(),
+            certificate.len(),
+            certificate.as_mut_ptr(),
+            &mut certificate_actual_size,
+        )
+    })?;
+    Ok(certificate_actual_size)
+}
diff --git a/diced/open_dice/src/retry.rs b/diced/open_dice/src/retry.rs
new file mode 100644
index 0000000..76a214c
--- /dev/null
+++ b/diced/open_dice/src/retry.rs
@@ -0,0 +1,160 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This module implements a retry version for multiple DICE functions that
+//! require preallocated output buffer. As the retry functions require
+//! memory allocation on heap, currently we only expose these functions in
+//! std environment.
+
+use crate::bcc::{bcc_format_config_descriptor, bcc_main_flow};
+use crate::dice::{
+    dice_main_flow, Cdi, CdiValues, DiceArtifacts, InputValues, CDI_SIZE, PRIVATE_KEY_SEED_SIZE,
+};
+use crate::error::{DiceError, Result};
+use crate::ops::generate_certificate;
+use std::ffi::CStr;
+
+/// Artifacts stores a set of dice artifacts comprising CDI_ATTEST, CDI_SEAL,
+/// and the BCC formatted attestation certificate chain.
+/// As we align with the DICE standards today, this is the certificate chain
+/// is also called DICE certificate chain.
+#[derive(Debug)]
+pub struct OwnedDiceArtifacts {
+    /// CDI Values.
+    cdi_values: CdiValues,
+    /// Boot Certificate Chain.
+    bcc: Vec<u8>,
+}
+
+impl DiceArtifacts for OwnedDiceArtifacts {
+    fn cdi_attest(&self) -> &[u8; CDI_SIZE] {
+        &self.cdi_values.cdi_attest
+    }
+
+    fn cdi_seal(&self) -> &[u8; CDI_SIZE] {
+        &self.cdi_values.cdi_seal
+    }
+
+    fn bcc(&self) -> Option<&[u8]> {
+        Some(&self.bcc)
+    }
+}
+
+/// Retries the given function with bigger output buffer size.
+fn retry_with_bigger_buffer<F>(mut f: F) -> Result<Vec<u8>>
+where
+    F: FnMut(&mut Vec<u8>) -> Result<usize>,
+{
+    const INITIAL_BUFFER_SIZE: usize = 256;
+    const MAX_BUFFER_SIZE: usize = 64 * 1024 * 1024;
+
+    let mut buffer = vec![0u8; INITIAL_BUFFER_SIZE];
+    while buffer.len() <= MAX_BUFFER_SIZE {
+        match f(&mut buffer) {
+            Err(DiceError::BufferTooSmall) => {
+                let new_size = buffer.len() * 2;
+                buffer.resize(new_size, 0);
+            }
+            Err(e) => return Err(e),
+            Ok(actual_size) => {
+                if actual_size > buffer.len() {
+                    panic!(
+                        "actual_size larger than buffer size: open-dice function
+                         may have written past the end of the buffer."
+                    );
+                }
+                buffer.truncate(actual_size);
+                return Ok(buffer);
+            }
+        }
+    }
+    Err(DiceError::PlatformError)
+}
+
+/// Formats a configuration descriptor following the BCC's specification.
+pub fn retry_bcc_format_config_descriptor(
+    name: Option<&CStr>,
+    version: Option<u64>,
+    resettable: bool,
+) -> Result<Vec<u8>> {
+    retry_with_bigger_buffer(|buffer| {
+        bcc_format_config_descriptor(name, version, resettable, buffer)
+    })
+}
+
+/// Executes the main BCC flow.
+///
+/// Given a full set of input values along with the current BCC and CDI values,
+/// computes the next CDI values and matching updated BCC.
+pub fn retry_bcc_main_flow(
+    current_cdi_attest: &Cdi,
+    current_cdi_seal: &Cdi,
+    bcc: &[u8],
+    input_values: &InputValues,
+) -> Result<OwnedDiceArtifacts> {
+    let mut next_cdi_values = CdiValues::default();
+    let next_bcc = retry_with_bigger_buffer(|next_bcc| {
+        bcc_main_flow(
+            current_cdi_attest,
+            current_cdi_seal,
+            bcc,
+            input_values,
+            &mut next_cdi_values,
+            next_bcc,
+        )
+    })?;
+    Ok(OwnedDiceArtifacts { cdi_values: next_cdi_values, bcc: next_bcc })
+}
+
+/// Executes the main DICE flow.
+///
+/// Given a full set of input values and the current CDI values, computes the
+/// next CDI values and a matching certificate.
+pub fn retry_dice_main_flow(
+    current_cdi_attest: &Cdi,
+    current_cdi_seal: &Cdi,
+    input_values: &InputValues,
+) -> Result<(CdiValues, Vec<u8>)> {
+    let mut next_cdi_values = CdiValues::default();
+    let next_cdi_certificate = retry_with_bigger_buffer(|next_cdi_certificate| {
+        dice_main_flow(
+            current_cdi_attest,
+            current_cdi_seal,
+            input_values,
+            next_cdi_certificate,
+            &mut next_cdi_values,
+        )
+    })?;
+    Ok((next_cdi_values, next_cdi_certificate))
+}
+
+/// Generates an X.509 certificate from the given `subject_private_key_seed` and
+/// `input_values`, and signed by `authority_private_key_seed`.
+/// The subject private key seed is supplied here so the implementation can choose
+/// between asymmetric mechanisms, for example ECDSA vs Ed25519.
+/// Returns the generated certificate.
+pub fn retry_generate_certificate(
+    subject_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+    authority_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+    input_values: &InputValues,
+) -> Result<Vec<u8>> {
+    retry_with_bigger_buffer(|certificate| {
+        generate_certificate(
+            subject_private_key_seed,
+            authority_private_key_seed,
+            input_values,
+            certificate,
+        )
+    })
+}
diff --git a/diced/open_dice/tests/api_test.rs b/diced/open_dice/tests/api_test.rs
new file mode 100644
index 0000000..a47265b
--- /dev/null
+++ b/diced/open_dice/tests/api_test.rs
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use diced_open_dice::{
+    derive_cdi_certificate_id, derive_cdi_private_key_seed, hash, kdf, keypair_from_seed, sign,
+    verify, CDI_SIZE, HASH_SIZE, ID_SIZE, PRIVATE_KEY_SEED_SIZE,
+};
+
+#[test]
+fn hash_succeeds() {
+    const EXPECTED_HASH: [u8; HASH_SIZE] = [
+        0x30, 0x9e, 0xcc, 0x48, 0x9c, 0x12, 0xd6, 0xeb, 0x4c, 0xc4, 0x0f, 0x50, 0xc9, 0x02, 0xf2,
+        0xb4, 0xd0, 0xed, 0x77, 0xee, 0x51, 0x1a, 0x7c, 0x7a, 0x9b, 0xcd, 0x3c, 0xa8, 0x6d, 0x4c,
+        0xd8, 0x6f, 0x98, 0x9d, 0xd3, 0x5b, 0xc5, 0xff, 0x49, 0x96, 0x70, 0xda, 0x34, 0x25, 0x5b,
+        0x45, 0xb0, 0xcf, 0xd8, 0x30, 0xe8, 0x1f, 0x60, 0x5d, 0xcf, 0x7d, 0xc5, 0x54, 0x2e, 0x93,
+        0xae, 0x9c, 0xd7, 0x6f,
+    ];
+    assert_eq!(EXPECTED_HASH, hash(b"hello world").expect("hash failed"));
+}
+
+#[test]
+fn kdf_succeeds() {
+    let mut derived_key = [0u8; PRIVATE_KEY_SEED_SIZE];
+    kdf(b"myInitialKeyMaterial", b"mySalt", b"myInfo", &mut derived_key).unwrap();
+    const EXPECTED_DERIVED_KEY: [u8; PRIVATE_KEY_SEED_SIZE] = [
+        0x91, 0x9b, 0x8d, 0x29, 0xc4, 0x1b, 0x93, 0xd7, 0xeb, 0x09, 0xfa, 0xd7, 0xc9, 0x87, 0xb0,
+        0xd1, 0xcc, 0x26, 0xef, 0x07, 0x83, 0x42, 0xcf, 0xa3, 0x45, 0x0a, 0x57, 0xe9, 0x19, 0x86,
+        0xef, 0x48,
+    ];
+    assert_eq!(EXPECTED_DERIVED_KEY, derived_key);
+}
+
+#[test]
+fn derive_cdi_certificate_id_succeeds() {
+    const EXPECTED_ID: [u8; ID_SIZE] = [
+        0x7a, 0x36, 0x45, 0x2c, 0x02, 0xf6, 0x2b, 0xec, 0xf9, 0x80, 0x06, 0x75, 0x87, 0xa5, 0xc1,
+        0x44, 0x0c, 0xd3, 0xc0, 0x6d,
+    ];
+    assert_eq!(EXPECTED_ID, derive_cdi_certificate_id(b"MyPubKey").unwrap());
+}
+
+const EXPECTED_SEED: &[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,
+];
+
+const EXPECTED_CDI_ATTEST: &[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,
+];
+
+const EXPECTED_CDI_PRIVATE_KEY_SEED: &[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,
+];
+
+const EXPECTED_PUB_KEY: &[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,
+];
+const EXPECTED_PRIV_KEY: &[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,
+];
+
+const EXPECTED_SIGNATURE: &[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 seed = hash(b"MySeedString").unwrap();
+    assert_eq!(seed, EXPECTED_SEED);
+    let cdi_attest = &seed[..CDI_SIZE];
+    assert_eq!(cdi_attest, EXPECTED_CDI_ATTEST);
+    let cdi_private_key_seed = derive_cdi_private_key_seed(cdi_attest.try_into().unwrap()).unwrap();
+    assert_eq!(cdi_private_key_seed.as_array(), EXPECTED_CDI_PRIVATE_KEY_SEED);
+    let (pub_key, priv_key) = keypair_from_seed(cdi_private_key_seed.as_array()).unwrap();
+    assert_eq!(&pub_key, EXPECTED_PUB_KEY);
+    assert_eq!(priv_key.as_array(), EXPECTED_PRIV_KEY);
+    let mut signature = sign(b"MyMessage", priv_key.as_array()).unwrap();
+    assert_eq!(&signature, EXPECTED_SIGNATURE);
+    assert!(verify(b"MyMessage", &signature, &pub_key).is_ok());
+    assert!(verify(b"MyMessage_fail", &signature, &pub_key).is_err());
+    signature[0] += 1;
+    assert!(verify(b"MyMessage", &signature, &pub_key).is_err());
+}
diff --git a/diced/open_dice_cbor/Android.bp b/diced/open_dice_cbor/Android.bp
deleted file mode 100644
index 3e67045..0000000
--- a/diced/open_dice_cbor/Android.bp
+++ /dev/null
@@ -1,55 +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 {
-    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
deleted file mode 100644
index 7122ca5..0000000
--- a/diced/open_dice_cbor/lib.rs
+++ /dev/null
@@ -1,1037 +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.
-
-//! 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/sample_inputs/Android.bp b/diced/sample_inputs/Android.bp
new file mode 100644
index 0000000..cf6ef5f
--- /dev/null
+++ b/diced/sample_inputs/Android.bp
@@ -0,0 +1,45 @@
+// 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_sample_inputs",
+    crate_name: "diced_sample_inputs",
+    srcs: ["src/lib.rs"],
+    rustlibs: [
+        "libanyhow",
+        "libciborium",
+        "libcoset",
+        "libdiced_open_dice",
+    ],
+}
+
+rust_test {
+    name: "libdiced_sample_inputs.integration_test",
+    crate_name: "diced_sample_inputs_test",
+    srcs: ["tests/*.rs"],
+    test_suites: ["general-tests"],
+    rustlibs: [
+        "libdiced_open_dice",
+        "libdiced_sample_inputs",
+    ],
+}
diff --git a/diced/sample_inputs/src/lib.rs b/diced/sample_inputs/src/lib.rs
new file mode 100644
index 0000000..ebbfd29
--- /dev/null
+++ b/diced/sample_inputs/src/lib.rs
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Provides a set of sample inputs for a DICE chain and CDI values derived
+//! from it.
+
+mod sample_inputs;
+
+pub use sample_inputs::make_sample_bcc_and_cdis;
diff --git a/diced/sample_inputs/src/sample_inputs.rs b/diced/sample_inputs/src/sample_inputs.rs
new file mode 100644
index 0000000..c665eb3
--- /dev/null
+++ b/diced/sample_inputs/src/sample_inputs.rs
@@ -0,0 +1,167 @@
+// 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 anyhow::{anyhow, Context, Result};
+use ciborium::{de, ser, value::Value};
+use coset::{iana, Algorithm, AsCborValue, CoseKey, KeyOperation, KeyType, Label};
+use diced_open_dice::{
+    derive_cdi_private_key_seed, keypair_from_seed, retry_bcc_format_config_descriptor,
+    retry_bcc_main_flow, retry_dice_main_flow, Config, DiceArtifacts, DiceMode, InputValues,
+    OwnedDiceArtifacts, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE,
+};
+use std::ffi::CStr;
+
+/// Sample UDS used to perform the root dice flow by `make_sample_bcc_and_cdis`.
+const UDS: &[u8; 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,
+];
+
+const CODE_HASH_ABL: [u8; HASH_SIZE] = [
+    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,
+];
+const AUTHORITY_HASH_ABL: [u8; HASH_SIZE] = [
+    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,
+];
+const HIDDEN_ABL: [u8; HIDDEN_SIZE] = [
+    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,
+];
+const CODE_HASH_AVB: [u8; HASH_SIZE] = [
+    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,
+];
+const AUTHORITY_HASH_AVB: [u8; HASH_SIZE] = [
+    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,
+];
+const HIDDEN_AVB: [u8; HIDDEN_SIZE] = [
+    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,
+];
+const AUTHORITY_HASH_ANDROID: [u8; HASH_SIZE] = [
+    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,
+];
+
+fn ed25519_public_key_to_cbor_value(public_key: &[u8]) -> Result<Value> {
+    let key = CoseKey {
+        kty: KeyType::Assigned(iana::KeyType::OKP),
+        alg: Some(Algorithm::Assigned(iana::Algorithm::EdDSA)),
+        key_ops: vec![KeyOperation::Assigned(iana::KeyOperation::Verify)].into_iter().collect(),
+        params: vec![
+            (
+                Label::Int(iana::Ec2KeyParameter::Crv as i64),
+                Value::from(iana::EllipticCurve::Ed25519 as u64),
+            ),
+            (Label::Int(iana::Ec2KeyParameter::X as i64), Value::Bytes(public_key.to_vec())),
+        ],
+        ..Default::default()
+    };
+    key.to_cbor_value()
+        .map_err(|e| anyhow!(format!("Failed to serialize the key to CBOR data. Error: {e}")))
+}
+
+/// Makes a DICE chain (BCC) from the sample input.
+///
+/// The DICE chain is of the following format:
+/// public key derived from UDS -> ABL certificate -> AVB certificate -> Android certificate
+pub fn make_sample_bcc_and_cdis() -> Result<OwnedDiceArtifacts> {
+    let private_key_seed = derive_cdi_private_key_seed(UDS)
+        .context("In make_sample_bcc_and_cdis: Trying to derive private key seed.")?;
+
+    // Gets the root public key in DICE chain (BCC).
+    let (public_key, _) = keypair_from_seed(private_key_seed.as_array())
+        .context("In make_sample_bcc_and_cids: Failed to generate key pair.")?;
+    let ed25519_public_key_value = ed25519_public_key_to_cbor_value(&public_key)?;
+
+    // Gets the ABL certificate to as the root certificate of DICE chain.
+    let config_descriptor = retry_bcc_format_config_descriptor(
+        Some(CStr::from_bytes_with_nul(b"ABL\0").unwrap()),
+        Some(1), // version
+        true,
+    )?;
+    let input_values = InputValues::new(
+        CODE_HASH_ABL,
+        Config::Descriptor(config_descriptor.as_slice()),
+        AUTHORITY_HASH_ABL,
+        DiceMode::kDiceModeNormal,
+        HIDDEN_ABL,
+    );
+    let (cdi_values, cert) = retry_dice_main_flow(UDS, UDS, &input_values)
+        .context("In make_sample_bcc_and_cdis: Trying to run first main flow.")?;
+    let bcc_value = Value::Array(vec![
+        ed25519_public_key_value,
+        de::from_reader(&cert[..]).context("Deserialize root DICE certificate failed")?,
+    ]);
+    let mut bcc: Vec<u8> = vec![];
+    ser::into_writer(&bcc_value, &mut bcc)?;
+
+    // Appends AVB certificate to DICE chain.
+    let config_descriptor = retry_bcc_format_config_descriptor(
+        Some(CStr::from_bytes_with_nul(b"AVB\0").unwrap()),
+        Some(1), // version
+        true,
+    )?;
+    let input_values = InputValues::new(
+        CODE_HASH_AVB,
+        Config::Descriptor(config_descriptor.as_slice()),
+        AUTHORITY_HASH_AVB,
+        DiceMode::kDiceModeNormal,
+        HIDDEN_AVB,
+    );
+    let dice_artifacts =
+        retry_bcc_main_flow(&cdi_values.cdi_attest, &cdi_values.cdi_seal, &bcc, &input_values)
+            .context("In make_sample_bcc_and_cdis: Trying to run first bcc main flow.")?;
+
+    // Appends Android certificate to DICE chain.
+    let config_descriptor = retry_bcc_format_config_descriptor(
+        Some(CStr::from_bytes_with_nul(b"Android\0").unwrap()),
+        Some(12), // version
+        true,
+    )?;
+    let input_values = InputValues::new(
+        [0u8; HASH_SIZE], // code_hash
+        Config::Descriptor(config_descriptor.as_slice()),
+        AUTHORITY_HASH_ANDROID,
+        DiceMode::kDiceModeNormal,
+        [0u8; HIDDEN_SIZE], // hidden
+    );
+    retry_bcc_main_flow(
+        dice_artifacts.cdi_attest(),
+        dice_artifacts.cdi_seal(),
+        dice_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?,
+        &input_values,
+    )
+    .context("In make_sample_bcc_and_cdis: Trying to run second bcc main flow.")
+}
diff --git a/diced/sample_inputs/tests/api_test.rs b/diced/sample_inputs/tests/api_test.rs
new file mode 100644
index 0000000..f0d6c0d
--- /dev/null
+++ b/diced/sample_inputs/tests/api_test.rs
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use diced_open_dice::DiceArtifacts;
+use diced_sample_inputs::make_sample_bcc_and_cdis;
+
+const EXPECTED_SAMPLE_CDI_ATTEST: &[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,
+];
+
+const EXPECTED_SAMPLE_CDI_SEAL: &[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,
+];
+
+const EXPECTED_SAMPLE_BCC: &[u8] = &[
+    0x84, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 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,
+];
+
+#[test]
+fn sample_bcc_and_cdis_are_as_expected() {
+    let dice_artifacts = make_sample_bcc_and_cdis().unwrap();
+    assert_eq!(dice_artifacts.cdi_attest(), EXPECTED_SAMPLE_CDI_ATTEST);
+    assert_eq!(dice_artifacts.cdi_seal(), EXPECTED_SAMPLE_CDI_SEAL);
+    assert_eq!(dice_artifacts.bcc(), Some(EXPECTED_SAMPLE_BCC));
+}
diff --git a/diced/src/diced_client_test.rs b/diced/src/diced_client_test.rs
deleted file mode 100644
index 3915508..0000000
--- a/diced/src/diced_client_test.rs
+++ /dev/null
@@ -1,188 +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.
-
-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
deleted file mode 100644
index c2cf02c..0000000
--- a/diced/src/diced_main.rs
+++ /dev/null
@@ -1,76 +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.
-
-//! 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
deleted file mode 100644
index 3e230e4..0000000
--- a/diced/src/error.rs
+++ /dev/null
@@ -1,123 +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.
-
-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
deleted file mode 100644
index e8657e0..0000000
--- a/diced/src/error_vendor.rs
+++ /dev/null
@@ -1,119 +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.
-
-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
deleted file mode 100644
index 01a7577..0000000
--- a/diced/src/hal_node.rs
+++ /dev/null
@@ -1,725 +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 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
deleted file mode 100644
index 50e0e96..0000000
--- a/diced/src/lib.rs
+++ /dev/null
@@ -1,203 +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.
-
-//! 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, false)
-                .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/permission.rs b/diced/src/permission.rs
deleted file mode 100644
index 116df1b..0000000
--- a/diced/src/permission.rs
+++ /dev/null
@@ -1,46 +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 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
deleted file mode 100644
index 8d883d2..0000000
--- a/diced/src/proxy_node_hal.rs
+++ /dev/null
@@ -1,119 +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.
-
-//! 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
deleted file mode 100644
index 99a6dc9..0000000
--- a/diced/src/resident_node.rs
+++ /dev/null
@@ -1,191 +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.
-
-//! 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
deleted file mode 100644
index 93897a6..0000000
--- a/diced/src/sample_inputs.rs
+++ /dev/null
@@ -1,255 +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 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
deleted file mode 100644
index 03e8969..0000000
--- a/diced/src/utils.rs
+++ /dev/null
@@ -1,381 +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.
-
-//! 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
index 2fc3c01..ce3b499 100644
--- a/fsverity/Android.bp
+++ b/fsverity/Android.bp
@@ -32,14 +32,12 @@
     proto: {
         canonical_path_from_root: false,
     },
-    version: {
-        py2: {
-            enabled: true,
-        },
-        py3: {
-            enabled: true,
-        },
-    },
+}
+
+python_binary_host {
+    name: "fsverity_manifest_generator",
+    srcs: ["fsverity_manifest_generator.py"],
+    libs: ["fsverity_digests_proto_python"],
 }
 
 rust_protobuf {
diff --git a/fsverity/TEST_MAPPING b/fsverity/TEST_MAPPING
new file mode 100644
index 0000000..b327cb8
--- /dev/null
+++ b/fsverity/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "ComposHostTestCases"
+    }
+  ]
+}
diff --git a/fsverity/fsverity_manifest_generator.py b/fsverity/fsverity_manifest_generator.py
new file mode 100644
index 0000000..181758a
--- /dev/null
+++ b/fsverity/fsverity_manifest_generator.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+#
+# Copyright 2022 Google Inc. All rights reserved.
+#
+# 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.
+
+"""
+`fsverity_manifest_generator` generates the a manifest file containing digests
+of target files.
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+from fsverity_digests_pb2 import FSVerityDigests
+
+HASH_ALGORITHM = 'sha256'
+
+def _digest(fsverity_path, input_file):
+  cmd = [fsverity_path, 'digest', input_file]
+  cmd.extend(['--compact'])
+  cmd.extend(['--hash-alg', HASH_ALGORITHM])
+  out = subprocess.check_output(cmd, universal_newlines=True).strip()
+  return bytes(bytearray.fromhex(out))
+
+if __name__ == '__main__':
+  p = argparse.ArgumentParser()
+  p.add_argument(
+      '--output',
+      help='Path to the output manifest',
+      required=True)
+  p.add_argument(
+      '--fsverity-path',
+      help='path to the fsverity program',
+      required=True)
+  p.add_argument(
+      '--base-dir',
+      help='directory to use as a relative root for the inputs',
+      required=True)
+  p.add_argument(
+      'inputs',
+      nargs='*',
+      help='input file for the build manifest')
+  args = p.parse_args(sys.argv[1:])
+
+  digests = FSVerityDigests()
+  for f in sorted(args.inputs):
+    # f is a full path for now; make it relative so it starts with {mount_point}/
+    digest = digests.digests[os.path.relpath(f, args.base_dir)]
+    digest.digest = _digest(args.fsverity_path, f)
+    digest.hash_alg = HASH_ALGORITHM
+
+  manifest = digests.SerializeToString()
+
+  with open(args.output, "wb") as f:
+    f.write(manifest)
diff --git a/fsverity/libfsverity_rs/Android.bp b/fsverity/libfsverity_rs/Android.bp
new file mode 100644
index 0000000..91b1248
--- /dev/null
+++ b/fsverity/libfsverity_rs/Android.bp
@@ -0,0 +1,17 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_library {
+    name: "libfsverity_rs",
+    crate_name: "fsverity",
+    srcs: ["lib.rs"],
+    edition: "2021",
+    rustlibs: [
+        "libnix",
+    ],
+    apex_available: [
+        "com.android.compos",
+        "com.android.virt",
+    ],
+}
diff --git a/fsverity/libfsverity_rs/lib.rs b/fsverity/libfsverity_rs/lib.rs
new file mode 100644
index 0000000..473b2d5
--- /dev/null
+++ b/fsverity/libfsverity_rs/lib.rs
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! A wrapper library to use fs-verity
+
+mod sys;
+
+use crate::sys::*;
+use std::io;
+use std::os::fd::AsRawFd;
+use std::os::unix::io::BorrowedFd;
+
+fn read_metadata(fd: i32, metadata_type: u64, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
+    let mut arg = fsverity_read_metadata_arg {
+        metadata_type,
+        offset,
+        length: buf.len() as u64,
+        buf_ptr: buf.as_mut_ptr() as u64,
+        __reserved: 0,
+    };
+    // SAFETY: the ioctl doesn't change the sematics in the current process
+    Ok(unsafe { read_verity_metadata(fd, &mut arg) }? as usize)
+}
+
+/// Read the raw Merkle tree from the fd, if it exists. The API semantics is similar to a regular
+/// pread(2), and may not return full requested buffer.
+pub fn read_merkle_tree(fd: i32, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
+    read_metadata(fd, FS_VERITY_METADATA_TYPE_MERKLE_TREE, offset, buf)
+}
+
+/// Read the fs-verity signature from the fd (if exists). The returned signature should be complete.
+pub fn read_signature(fd: i32, buf: &mut [u8]) -> io::Result<usize> {
+    read_metadata(fd, FS_VERITY_METADATA_TYPE_SIGNATURE, 0 /* offset */, buf)
+}
+
+/// Enable fs-verity to the `fd`, with sha256 hash algorithm and 4KB block size.
+pub fn enable(fd: BorrowedFd) -> io::Result<()> {
+    let arg = fsverity_enable_arg {
+        version: 1,
+        hash_algorithm: FS_VERITY_HASH_ALG_SHA256,
+        block_size: 4096,
+        salt_size: 0,
+        salt_ptr: 0,
+        sig_size: 0,
+        __reserved1: 0,
+        sig_ptr: 0,
+        __reserved2: [0; 11],
+    };
+    // SAFETY: the ioctl doesn't change the sematics in the current process
+    if unsafe { enable_verity(fd.as_raw_fd(), &arg) } == Ok(0) {
+        Ok(())
+    } else {
+        Err(io::Error::last_os_error())
+    }
+}
diff --git a/fsverity/libfsverity_rs/sys.rs b/fsverity/libfsverity_rs/sys.rs
new file mode 100644
index 0000000..8ce0836
--- /dev/null
+++ b/fsverity/libfsverity_rs/sys.rs
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Stable API definition copied from uapi/linux/fsverity.h
+
+use nix::{ioctl_readwrite, ioctl_write_ptr};
+
+const FS_IOCTL_MAGIC: u8 = b'f';
+const FS_IOC_ENABLE_VERITY: u8 = 133;
+const FS_IOCTL_READ_VERITY_METADATA: u8 = 135;
+
+pub const FS_VERITY_HASH_ALG_SHA256: u32 = 1;
+pub const FS_VERITY_METADATA_TYPE_MERKLE_TREE: u64 = 1;
+pub const FS_VERITY_METADATA_TYPE_SIGNATURE: u64 = 3;
+
+#[repr(C)]
+pub struct fsverity_read_metadata_arg {
+    pub metadata_type: u64,
+    pub offset: u64,
+    pub length: u64,
+    pub buf_ptr: u64,
+    pub __reserved: u64,
+}
+
+ioctl_readwrite!(
+    read_verity_metadata,
+    FS_IOCTL_MAGIC,
+    FS_IOCTL_READ_VERITY_METADATA,
+    fsverity_read_metadata_arg
+);
+
+#[repr(C)]
+pub struct fsverity_enable_arg {
+    pub version: u32,
+    pub hash_algorithm: u32,
+    pub block_size: u32,
+    pub salt_size: u32,
+    pub salt_ptr: u64,
+    pub sig_size: u32,
+    pub __reserved1: u32,
+    pub sig_ptr: u64,
+    pub __reserved2: [u64; 11],
+}
+
+ioctl_write_ptr!(enable_verity, FS_IOCTL_MAGIC, FS_IOC_ENABLE_VERITY, fsverity_enable_arg);
diff --git a/fsverity_init/main.cpp b/fsverity_init/main.cpp
index 3f75dca..b502b91 100644
--- a/fsverity_init/main.cpp
+++ b/fsverity_init/main.cpp
@@ -48,12 +48,6 @@
             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";
diff --git a/identity/Android.bp b/identity/Android.bp
index b3b704f..f4fcc0a 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -19,47 +19,50 @@
     sanitize: {
         misc_undefined : ["integer"],
     },
-    clang : true,
+
 }
 
 cc_binary {
     name: "credstore",
     defaults: [
         "identity_defaults",
+        "identity_use_latest_hal_aidl_cpp_static",
         "keymint_use_latest_hal_aidl_ndk_shared",
         "keymint_use_latest_hal_aidl_cpp_static",
     ],
 
     srcs: [
-        "main.cpp",
-        "CredentialStore.cpp",
-        "CredentialStoreFactory.cpp",
-        "WritableCredential.cpp",
         "Credential.cpp",
         "CredentialData.cpp",
+        "CredentialStore.cpp",
+        "CredentialStoreFactory.cpp",
         "Session.cpp",
         "Util.cpp",
+        "WritableCredential.cpp",
+        "main.cpp",
     ],
     init_rc: ["credstore.rc"],
     shared_libs: [
+        "android.hardware.identity-support-lib",
+        "android.hardware.keymaster@4.0",
+        "android.security.authorization-ndk",
         "libbase",
         "libbinder",
         "libbinder_ndk",
-        "android.hardware.keymaster@4.0",
         "libcredstore_aidl",
         "libcrypto",
-        "libutils",
         "libhidlbase",
-        "android.hardware.identity-support-lib",
         "libkeymaster4support",
         "libkeystore-attestation-application-id",
-        "android.security.authorization-ndk",
-        "android.security.remoteprovisioning-cpp",
+        "librkp_support",
+        "libutils",
         "libutilscallstack",
+        "libvintf",
     ],
     static_libs: [
-        "android.hardware.identity-V4-cpp",
+        "android.hardware.security.rkp-V3-cpp",
         "android.hardware.keymaster-V3-cpp",
+        "android.security.rkp_aidl-cpp",
         "libcppbor_external",
     ],
 }
diff --git a/identity/Credential.cpp b/identity/Credential.cpp
index c67fe4a..0b1d171 100644
--- a/identity/Credential.cpp
+++ b/identity/Credential.cpp
@@ -554,9 +554,18 @@
         ret.resultNamespaces.push_back(resultNamespaceParcel);
     }
 
-    status = halBinder->finishRetrieval(&ret.mac, &ret.deviceNameSpaces);
-    if (!status.isOk()) {
-        return halStatusToGenericError(status);
+    // API version 5 (feature version 202301) supports both MAC and ECDSA signature.
+    if (halApiVersion_ >= 5) {
+        status = halBinder->finishRetrievalWithSignature(&ret.mac, &ret.deviceNameSpaces,
+                                                         &ret.signature);
+        if (!status.isOk()) {
+            return halStatusToGenericError(status);
+        }
+    } else {
+        status = halBinder->finishRetrieval(&ret.mac, &ret.deviceNameSpaces);
+        if (!status.isOk()) {
+            return halStatusToGenericError(status);
+        }
     }
     ret.staticAuthenticationData = selectedAuthKeyStaticAuthData_;
 
@@ -694,7 +703,8 @@
     return Status::ok();
 }
 
-Status Credential::setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) {
+Status Credential::setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey,
+                                                  int64_t minValidTimeMillis) {
     if (halSessionBinder_) {
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                 "Cannot be used with session");
@@ -706,7 +716,7 @@
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                 "Error loading data for credential");
     }
-    data->setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
+    data->setAvailableAuthenticationKeys(keyCount, maxUsesPerKey, minValidTimeMillis);
     if (!data->saveToDisk()) {
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                 "Error saving data");
@@ -777,11 +787,6 @@
 Credential::storeStaticAuthenticationDataWithExpiration(const AuthKeyParcel& authenticationKey,
                                                         int64_t expirationDateMillisSinceEpoch,
                                                         const vector<uint8_t>& staticAuthData) {
-    if (halApiVersion_ < 3) {
-        return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
-                                                "Not implemented by HAL");
-    }
-
     if (halSessionBinder_) {
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                 "Cannot be used with session");
@@ -828,6 +833,29 @@
     return Status::ok();
 }
 
+Status Credential::getAuthenticationDataExpirations(vector<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";
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error loading data for credential");
+    }
+    const vector<AuthKeyData>& authKeyDatas = data->getAuthKeyDatas();
+    vector<int64_t> ret;
+    ret.reserve(authKeyDatas.size());
+    for (const AuthKeyData& authKeyData : authKeyDatas) {
+        // Note: value is INT64_MAX if expiration date is not set.
+        ret.push_back(authKeyData.expirationDateMillisSinceEpoch);
+    }
+    *_aidl_return = ret;
+    return Status::ok();
+}
+
 optional<string> extractDocType(const vector<uint8_t>& credentialData) {
     auto [item, _ /* newPos */, message] = cppbor::parse(credentialData);
     if (item == nullptr) {
@@ -887,8 +915,8 @@
         dataPath_, credentialName_, docType.value(), true, hwInfo_, halWritableCredential);
 
     writableCredential->setAttestationCertificate(data->getAttestationCertificate());
-    auto [keyCount, maxUsesPerKey] = data->getAvailableAuthenticationKeys();
-    writableCredential->setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
+    auto [keyCount, maxUsesPerKey, minValidTimeMillis] = data->getAvailableAuthenticationKeys();
+    writableCredential->setAvailableAuthenticationKeys(keyCount, maxUsesPerKey, minValidTimeMillis);
 
     // Because its data has changed, we need to replace the binder for the
     // IIdentityCredential when the credential has been updated... otherwise the
diff --git a/identity/Credential.h b/identity/Credential.h
index 0906fea..4ecf92e 100644
--- a/identity/Credential.h
+++ b/identity/Credential.h
@@ -78,7 +78,8 @@
                       bool allowUsingExpiredKeys, bool incrementUsageCount,
                       GetEntriesResultParcel* _aidl_return) override;
 
-    Status setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) override;
+    Status setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey,
+                                          int64_t minValidTimeMillis) override;
     Status getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) override;
     Status storeStaticAuthenticationData(const AuthKeyParcel& authenticationKey,
                                          const vector<uint8_t>& staticAuthData) override;
@@ -87,6 +88,7 @@
                                                 int64_t expirationDateMillisSinceEpoch,
                                                 const vector<uint8_t>& staticAuthData) override;
     Status getAuthenticationDataUsageCount(vector<int32_t>* _aidl_return) override;
+    Status getAuthenticationDataExpirations(vector<int64_t>* _aidl_return) override;
 
     Status update(sp<IWritableCredential>* _aidl_return) override;
 
diff --git a/identity/CredentialData.cpp b/identity/CredentialData.cpp
index 2189f90..1bf1527 100644
--- a/identity/CredentialData.cpp
+++ b/identity/CredentialData.cpp
@@ -117,6 +117,7 @@
     map.add("entryData", std::move(encryptedBlobsMap));
     map.add("authKeyCount", keyCount_);
     map.add("maxUsesPerAuthKey", maxUsesPerKey_);
+    map.add("minValidTimeMillis", minValidTimeMillis_);
 
     cppbor::Array authKeyDatasArray;
     for (const AuthKeyData& data : authKeyDatas_) {
@@ -253,6 +254,7 @@
     authKeyDatas_.clear();
     keyCount_ = 0;
     maxUsesPerKey_ = 1;
+    minValidTimeMillis_ = 0;
 
     optional<vector<uint8_t>> data = fileGetContents(fileName_);
     if (!data) {
@@ -398,6 +400,14 @@
                 return false;
             }
             maxUsesPerKey_ = number->value();
+
+        } else if (key == "minValidTimeMillis") {
+            const cppbor::Int* number = valueItem->asInt();
+            if (number == nullptr) {
+                LOG(ERROR) << "Value for minValidTimeMillis is not a number";
+                return false;
+            }
+            minValidTimeMillis_ = number->value();
         }
     }
 
@@ -479,9 +489,11 @@
 
 // ---
 
-void CredentialData::setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey) {
+void CredentialData::setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey,
+                                                    int64_t minValidTimeMillis) {
     keyCount_ = keyCount;
     maxUsesPerKey_ = maxUsesPerKey;
+    minValidTimeMillis_ = minValidTimeMillis;
 
     // If growing the number of auth keys (prevKeyCount < keyCount_ case) we'll add
     // new AuthKeyData structs to |authKeyDatas_| and each struct will have empty |certificate|
@@ -499,8 +511,9 @@
     return authKeyDatas_;
 }
 
-pair<int /* keyCount */, int /*maxUsersPerKey */> CredentialData::getAvailableAuthenticationKeys() {
-    return std::make_pair(keyCount_, maxUsesPerKey_);
+tuple<int /* keyCount */, int /*maxUsersPerKey */, int64_t /* minValidTimeMillis */>
+CredentialData::getAvailableAuthenticationKeys() const {
+    return std::make_tuple(keyCount_, maxUsesPerKey_, minValidTimeMillis_);
 }
 
 AuthKeyData* CredentialData::findAuthKey_(bool allowUsingExhaustedKeys,
@@ -568,14 +581,19 @@
 
     vector<vector<uint8_t>> keysNeedingCert;
 
-    int64_t nowMilliSeconds =
-        std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) * 1000;
+    time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
+    int64_t nowMilliseconds;
+    if (__builtin_mul_overflow(int64_t(now), int64_t(1000), &nowMilliseconds)) {
+        LOG(ERROR) << "Overflow converting " << now << " to milliseconds";
+        return {};
+    }
 
     for (AuthKeyData& data : authKeyDatas_) {
         bool keyExceedUseCount = (data.useCount >= maxUsesPerKey_);
-        bool keyBeyondExpirationDate = (nowMilliSeconds > data.expirationDateMillisSinceEpoch);
+        int64_t expirationDateAdjusted = data.expirationDateMillisSinceEpoch - minValidTimeMillis_;
+        bool keyBeyondAdjustedExpirationDate = (nowMilliseconds > expirationDateAdjusted);
         bool newKeyNeeded =
-            (data.certificate.size() == 0) || keyExceedUseCount || keyBeyondExpirationDate;
+            (data.certificate.size() == 0) || keyExceedUseCount || keyBeyondAdjustedExpirationDate;
         bool certificationPending = (data.pendingCertificate.size() > 0);
         if (newKeyNeeded && !certificationPending) {
             vector<uint8_t> signingKeyBlob;
diff --git a/identity/CredentialData.h b/identity/CredentialData.h
index e240e47..3f7cd3a 100644
--- a/identity/CredentialData.h
+++ b/identity/CredentialData.h
@@ -37,7 +37,6 @@
 using ::android::hardware::identity::SecureAccessControlProfile;
 using ::std::map;
 using ::std::optional;
-using ::std::pair;
 using ::std::string;
 using ::std::tuple;
 using ::std::vector;
@@ -89,7 +88,8 @@
 
     bool deleteCredential();
 
-    void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey);
+    void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey,
+                                        int64_t minValidTimeMillis);
 
     // Getters
 
@@ -107,7 +107,8 @@
 
     const vector<AuthKeyData>& getAuthKeyDatas() const;
 
-    pair<int /* keyCount */, int /*maxUsersPerKey */> getAvailableAuthenticationKeys();
+    tuple<int /* keyCount */, int /*maxUsersPerKey */, int64_t /* minValidTimeMillis */>
+    getAvailableAuthenticationKeys() const;
 
     // Returns |nullptr| if a suitable key cannot be found. Otherwise returns
     // the authentication and increases its use-count.
@@ -143,6 +144,7 @@
 
     int keyCount_ = 0;
     int maxUsesPerKey_ = 1;
+    int64_t minValidTimeMillis_ = 0;
     vector<AuthKeyData> authKeyDatas_;  // Always |keyCount_| long.
 };
 
diff --git a/identity/CredentialStore.cpp b/identity/CredentialStore.cpp
index c5c429b..cb2e8c7 100644
--- a/identity/CredentialStore.cpp
+++ b/identity/CredentialStore.cpp
@@ -22,10 +22,10 @@
 #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 <rkp/support/rkpd_client.h>
+#include <vintf/VintfObject.h>
 
 #include "Credential.h"
 #include "CredentialData.h"
@@ -39,42 +39,8 @@
 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;
-}
+using ::android::security::rkp::RemotelyProvisionedKey;
+using ::android::security::rkp::support::getRpcKey;
 
 }  // namespace
 
@@ -90,11 +56,9 @@
     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 << "'";
+        status = hal_->getRemotelyProvisionedComponent(&rpc_);
+        if (!status.isOk()) {
+            LOG(ERROR) << "Error getting remotely provisioned component: " << status;
             return false;
         }
     }
@@ -102,7 +66,9 @@
     LOG(INFO) << "Connected to Identity Credential HAL with API version " << halApiVersion_
               << " and name '" << hwInfo_.credentialStoreName << "' authored by '"
               << hwInfo_.credentialStoreAuthorName << "' with chunk size " << hwInfo_.dataChunkSize
-              << " and directoAccess set to " << (hwInfo_.isDirectAccess ? "true" : "false");
+              << " directoAccess set to " << (hwInfo_.isDirectAccess ? "true" : "false")
+              << " and remote key provisioning support "
+              << (hwInfo_.isRemoteKeyProvisioningSupported ? "enabled" : "disabled");
     return true;
 }
 
@@ -148,7 +114,9 @@
     if (hwInfo_.isRemoteKeyProvisioningSupported) {
         status = setRemotelyProvisionedAttestationKey(halWritableCredential.get());
         if (!status.isOk()) {
-            return halStatusToGenericError(status);
+            LOG(WARNING) << status.toString8()
+                         << "\nUnable to fetch remotely provisioned attestation key, falling back "
+                         << "to the factory-provisioned attestation key.";
         }
     }
 
@@ -209,28 +177,32 @@
 
 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");
-    }
+    std::vector<uint8_t> keyBlob;
+    std::vector<uint8_t> encodedCertChain;
+    Status status;
+
+    LOG(INFO) << "Fetching attestation key from RKPD";
 
     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();
+    std::optional<RemotelyProvisionedKey> key = getRpcKey(rpc_, callingUid);
+    if (!key) {
+        return Status::fromServiceSpecificError(
+            ERROR_GENERIC, "Failed to get remotely provisioned attestation key");
     }
 
-    status = halWritableCredential->setRemotelyProvisionedAttestationKey(key.keyBlob,
-                                                                         key.encodedCertChain);
+    if (key->keyBlob.empty()) {
+        return Status::fromServiceSpecificError(
+            ERROR_GENERIC, "Remotely provisioned attestation key blob is empty");
+    }
+
+    keyBlob = std::move(key->keyBlob);
+    encodedCertChain = std::move(key->encodedCertChain);
+
+    status = halWritableCredential->setRemotelyProvisionedAttestationKey(keyBlob, encodedCertChain);
     if (!status.isOk()) {
         LOG(ERROR) << "Error setting remotely provisioned attestation key on credential";
         return status;
     }
-
     return Status::ok();
 }
 
diff --git a/identity/CredentialStore.h b/identity/CredentialStore.h
index df7928e..8bc02e8 100644
--- a/identity/CredentialStore.h
+++ b/identity/CredentialStore.h
@@ -22,7 +22,6 @@
 
 #include <android/hardware/identity/IIdentityCredentialStore.h>
 #include <android/security/identity/BnCredentialStore.h>
-#include <android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.h>
 
 namespace android {
 namespace security {
@@ -39,7 +38,7 @@
 using ::android::hardware::identity::IIdentityCredentialStore;
 using ::android::hardware::identity::IPresentationSession;
 using ::android::hardware::identity::IWritableIdentityCredential;
-using ::android::security::remoteprovisioning::IRemotelyProvisionedKeyPool;
+using ::android::hardware::security::keymint::IRemotelyProvisionedComponent;
 
 class CredentialStore : public BnCredentialStore {
   public:
@@ -73,9 +72,9 @@
     sp<IIdentityCredentialStore> hal_;
     int halApiVersion_;
 
-    sp<IRemotelyProvisionedKeyPool> keyPool_;
-
     HardwareInformation hwInfo_;
+
+    sp<IRemotelyProvisionedComponent> rpc_;
 };
 
 }  // namespace identity
diff --git a/identity/WritableCredential.cpp b/identity/WritableCredential.cpp
index 9827d75..d863494 100644
--- a/identity/WritableCredential.cpp
+++ b/identity/WritableCredential.cpp
@@ -101,9 +101,11 @@
     attestationCertificate_ = attestationCertificate;
 }
 
-void WritableCredential::setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey) {
+void WritableCredential::setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey,
+                                                        int64_t minValidTimeMillis) {
     keyCount_ = keyCount;
     maxUsesPerKey_ = maxUsesPerKey;
+    minValidTimeMillis_ = minValidTimeMillis;
 }
 
 ssize_t WritableCredential::calcExpectedProofOfProvisioningSize(
@@ -260,7 +262,7 @@
     }
     data.setCredentialData(credentialData);
 
-    data.setAvailableAuthenticationKeys(keyCount_, maxUsesPerKey_);
+    data.setAvailableAuthenticationKeys(keyCount_, maxUsesPerKey_, minValidTimeMillis_);
 
     if (!data.saveToDisk()) {
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
diff --git a/identity/WritableCredential.h b/identity/WritableCredential.h
index 838b956..c92d58a 100644
--- a/identity/WritableCredential.h
+++ b/identity/WritableCredential.h
@@ -45,7 +45,8 @@
 
     // Used when updating a credential
     void setAttestationCertificate(const vector<uint8_t>& attestationCertificate);
-    void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey);
+    void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey,
+                                        int64_t minValidTimeMillis);
 
     // Used by Credential::update()
     void setCredentialToReloadWhenUpdated(sp<Credential> credential);
@@ -69,6 +70,7 @@
     vector<uint8_t> attestationCertificate_;
     int keyCount_ = 0;
     int maxUsesPerKey_ = 1;
+    int64_t minValidTimeMillis_ = 0;
 
     sp<Credential> credentialToReloadWhenUpdated_;
 
diff --git a/identity/binder/android/security/identity/GetEntriesResultParcel.aidl b/identity/binder/android/security/identity/GetEntriesResultParcel.aidl
index 03b363c..51281b9 100644
--- a/identity/binder/android/security/identity/GetEntriesResultParcel.aidl
+++ b/identity/binder/android/security/identity/GetEntriesResultParcel.aidl
@@ -26,4 +26,5 @@
     byte[] deviceNameSpaces;
     byte[] mac;
     byte[] staticAuthenticationData;
+    byte[] signature;                      // Added in Android 14 / U
 }
diff --git a/identity/binder/android/security/identity/ICredential.aidl b/identity/binder/android/security/identity/ICredential.aidl
index e6a9fae..875b934 100644
--- a/identity/binder/android/security/identity/ICredential.aidl
+++ b/identity/binder/android/security/identity/ICredential.aidl
@@ -60,7 +60,9 @@
                                       in boolean allowUsingExpiredKeys,
                                       in boolean incrementUsageCount);
 
-    void setAvailableAuthenticationKeys(in int keyCount, in int maxUsesPerKey);
+    void setAvailableAuthenticationKeys(in int keyCount,
+                                        in int maxUsesPerKey,
+                                        in long minValidTimeMillis);
 
     AuthKeyParcel[] getAuthKeysNeedingCertification();
 
@@ -73,6 +75,8 @@
 
     int[] getAuthenticationDataUsageCount();
 
+    long[] getAuthenticationDataExpirations();
+
     IWritableCredential update();
 }
 
diff --git a/identity/main.cpp b/identity/main.cpp
index 2559789..b3a41ec 100644
--- a/identity/main.cpp
+++ b/identity/main.cpp
@@ -23,6 +23,7 @@
 #include <android-base/logging.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
 
 #include "CredentialStoreFactory.h"
 
@@ -32,6 +33,7 @@
 
 using ::android::IPCThreadState;
 using ::android::IServiceManager;
+using ::android::ProcessState;
 using ::android::sp;
 using ::android::String16;
 using ::android::base::InitLogging;
@@ -53,8 +55,10 @@
     CHECK(ret == ::android::OK) << "Couldn't register binder service";
     LOG(INFO) << "Registered binder service";
 
-    // Credstore is a single-threaded process. So devote the main thread
-    // to handling binder messages.
+    // Credstore needs one thread to handle binder messages and one to handle
+    // asynchronous responses from RKPD.
+    ProcessState::self()->setThreadPoolMaxThreadCount(2);
+    ProcessState::self()->startThreadPool();
     IPCThreadState::self()->joinThreadPool();
 
     return 0;
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
index 6da90e5..b47009b 100644
--- a/identity/util/src/java/com/android/security/identity/internal/Iso18013.java
+++ b/identity/util/src/java/com/android/security/identity/internal/Iso18013.java
@@ -145,14 +145,11 @@
         // 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);
+
+            ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
+            baos.write(Util.convertP256PublicKeyToDERFormat(w));
+
             baos.write(new byte[]{42, 44});
         } catch (IOException e) {
             e.printStackTrace();
@@ -279,18 +276,4 @@
             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
index 4ec54a7..ee12cd0 100644
--- a/identity/util/src/java/com/android/security/identity/internal/Util.java
+++ b/identity/util/src/java/com/android/security/identity/internal/Util.java
@@ -1130,6 +1130,48 @@
         Log.e(TAG, name + ": dumping " + data.length + " bytes\n" + fmt.toString());
     }
 
+    // Convert EC P256 public key to DER format binary format
+    public static byte[] convertP256PublicKeyToDERFormat(ECPoint w) {
+        byte[] ret = new byte[64];
+
+        // Each coordinate may be encoded in 33*, 32, or fewer bytes.
+        //
+        //  * : it can be 33 bytes because toByteArray() guarantees "The array will contain the
+        //      minimum number of bytes required to represent this BigInteger, including at
+        //      least one sign bit, which is (ceil((this.bitLength() + 1)/8))" which means that
+        //      the MSB is always 0x00. This is taken care of by calling calling
+        //      stripLeadingZeroes().
+        //
+        // We need the encoding to be exactly 32 bytes since according to RFC 5480 section 2.2
+        // and SEC 1: Elliptic Curve Cryptography section 2.3.3 the encoding is 0x04 | X | Y
+        // where X and Y are encoded in exactly 32 byte, big endian integer values each.
+        //
+        byte[] xBytes = stripLeadingZeroes(w.getAffineX().toByteArray());
+        if (xBytes.length > 32) {
+            throw new RuntimeException("xBytes is " + xBytes.length + " which is unexpected");
+        }
+        int numLeadingZeroBytes = 32 - xBytes.length;
+        for (int n = 0; n < numLeadingZeroBytes; n++) {
+            ret[n] = 0x00;
+        }
+        for (int n = 0; n < xBytes.length; n++) {
+            ret[numLeadingZeroBytes + n] = xBytes[n];
+        }
+
+        byte[] yBytes = stripLeadingZeroes(w.getAffineY().toByteArray());
+        if (yBytes.length > 32) {
+            throw new RuntimeException("yBytes is " + yBytes.length + " which is unexpected");
+        }
+        numLeadingZeroBytes = 32 - yBytes.length;
+        for (int n = 0; n < numLeadingZeroBytes; n++) {
+            ret[32 + n] = 0x00;
+        }
+        for (int n = 0; n < yBytes.length; n++) {
+            ret[32 + numLeadingZeroBytes + n] = yBytes[n];
+        }
+
+        return ret;
+    }
 
     // This returns a SessionTranscript which satisfy the requirement
     // that the uncompressed X and Y coordinates of the public key for the
@@ -1141,14 +1183,11 @@
         // 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);
+
+            ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
+            baos.write(convertP256PublicKeyToDERFormat(w));
+
             baos.write(new byte[]{43, 44});
         } catch (IOException e) {
             e.printStackTrace();
diff --git a/keystore-engine/keystore2_engine.cpp b/keystore-engine/keystore2_engine.cpp
index 69caf51..dc90be3 100644
--- a/keystore-engine/keystore2_engine.cpp
+++ b/keystore-engine/keystore2_engine.cpp
@@ -146,11 +146,13 @@
         return nullptr;
     }
 
-    rsa->n = BN_dup(public_rsa->n);
-    rsa->e = BN_dup(public_rsa->e);
-    if (rsa->n == nullptr || rsa->e == nullptr) {
+    bssl::UniquePtr<BIGNUM> n(BN_dup(RSA_get0_n(public_rsa)));
+    bssl::UniquePtr<BIGNUM> e(BN_dup(RSA_get0_e(public_rsa)));
+    if (n == nullptr || e == nullptr || !RSA_set0_key(rsa.get(), n.get(), e.get(), nullptr)) {
         return nullptr;
     }
+    OWNERSHIP_TRANSFERRED(n);
+    OWNERSHIP_TRANSFERRED(e);
 
     bssl::UniquePtr<EVP_PKEY> result(EVP_PKEY_new());
     if (result.get() == nullptr || !EVP_PKEY_assign_RSA(result.get(), rsa.get())) {
@@ -420,19 +422,19 @@
         Keystore2KeyBackend{response.metadata.key, response.iSecurityLevel});
 
     bssl::UniquePtr<EVP_PKEY> result;
-    switch (EVP_PKEY_type(pkey->type)) {
+    switch (EVP_PKEY_id(pkey.get())) {
     case EVP_PKEY_RSA: {
-        bssl::UniquePtr<RSA> public_rsa(EVP_PKEY_get1_RSA(pkey.get()));
-        result = wrap_rsa(key_backend, public_rsa.get());
+        RSA* public_rsa = EVP_PKEY_get0_RSA(pkey.get());
+        result = wrap_rsa(key_backend, public_rsa);
         break;
     }
     case EVP_PKEY_EC: {
-        bssl::UniquePtr<EC_KEY> public_ecdsa(EVP_PKEY_get1_EC_KEY(pkey.get()));
-        result = wrap_ecdsa(key_backend, public_ecdsa.get());
+        EC_KEY* public_ecdsa = EVP_PKEY_get0_EC_KEY(pkey.get());
+        result = wrap_ecdsa(key_backend, public_ecdsa);
         break;
     }
     default:
-        LOG(ERROR) << AT << "Unsupported key type " << EVP_PKEY_type(pkey->type);
+        LOG(ERROR) << AT << "Unsupported key type " << EVP_PKEY_id(pkey.get());
         return nullptr;
     }
 
diff --git a/keystore/Android.bp b/keystore/Android.bp
index 892c5b4..221ead9 100644
--- a/keystore/Android.bp
+++ b/keystore/Android.bp
@@ -31,7 +31,6 @@
         ],
     },
 
-    clang: true,
 }
 
 cc_binary {
diff --git a/keystore/keystore_cli_v2.cpp b/keystore/keystore_cli_v2.cpp
index d01c67d..ab3e22c 100644
--- a/keystore/keystore_cli_v2.cpp
+++ b/keystore/keystore_cli_v2.cpp
@@ -59,6 +59,16 @@
 
 constexpr const char keystore2_service_name[] = "android.system.keystore2.IKeystoreService/default";
 
+std::string string_replace_all(std::string str, const std::string& from,
+                                      const std::string& to) {
+    size_t start = 0;
+    while ((start = str.find(from, start)) != std::string::npos) {
+        str.replace(start, from.length(), to);
+        start += to.length();
+    }
+    return str;
+}
+
 int unwrapError(const ndk::ScopedAStatus& status) {
     if (status.isOk()) return 0;
     if (status.getExceptionCode() == EX_SERVICE_SPECIFIC) {
@@ -1028,7 +1038,8 @@
     auto listener = ndk::SharedRefBase::make<ConfirmationListener>();
 
     auto future = listener->get_future();
-    auto rc = apcService->presentPrompt(listener, promptText, extraData, locale, uiOptionsAsFlags);
+    auto rc = apcService->presentPrompt(listener, string_replace_all(promptText, "\\n", "\n"),
+                                        extraData, locale, uiOptionsAsFlags);
 
     if (!rc.isOk()) {
         std::cerr << "Presenting confirmation prompt failed: " << rc.getDescription() << std::endl;
diff --git a/keystore/tests/fuzzer/keystoreCommon.h b/keystore/tests/fuzzer/keystoreCommon.h
index 7af3ba8..e1265bf 100644
--- a/keystore/tests/fuzzer/keystoreCommon.h
+++ b/keystore/tests/fuzzer/keystoreCommon.h
@@ -71,7 +71,7 @@
         }
     }
     packageInfoData.sharedSignaturesVector =
-        make_shared<KeyAttestationPackageInfo::SignaturesVector>(move(signatureVector));
+        make_shared<KeyAttestationPackageInfo::SignaturesVector>(std::move(signatureVector));
     return packageInfoData;
 }
 #endif  // KEYSTORECOMMON_H
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index e6cb4fb..4084ace 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -27,6 +27,7 @@
     srcs: ["src/lib.rs"],
     defaults: [
         "keymint_use_latest_hal_aidl_rust",
+        "keystore2_use_latest_aidl_rust",
     ],
 
     rustlibs: [
@@ -38,8 +39,7 @@
         "android.security.compat-rust",
         "android.security.maintenance-rust",
         "android.security.metrics-rust",
-        "android.security.remoteprovisioning-rust",
-        "android.system.keystore2-V2-rust",
+        "android.security.rkp_aidl-rust",
         "libanyhow",
         "libbinder_rs",
         "libkeystore2_aaid-rust",
@@ -57,6 +57,7 @@
         "libserde",
         "libserde_cbor",
         "libthiserror",
+        "libtokio",
     ],
     shared_libs: [
         "libcutils",
@@ -79,9 +80,11 @@
     name: "libkeystore2_test_utils",
     crate_name: "keystore2_test_utils",
     srcs: ["test_utils/lib.rs"],
-    defaults: ["keymint_use_latest_hal_aidl_rust"],
+    defaults: [
+        "keymint_use_latest_hal_aidl_rust",
+        "keystore2_use_latest_aidl_rust",
+    ],
     rustlibs: [
-        "android.system.keystore2-V2-rust",
         "libbinder_rs",
         "libkeystore2_selinux",
         "liblog_rust",
@@ -89,6 +92,8 @@
         "librand",
         "libserde",
         "libserde_cbor",
+        "libthiserror",
+        "libanyhow",
     ],
 }
 
@@ -108,13 +113,15 @@
 rust_test {
     name: "keystore2_test_utils_test",
     srcs: ["test_utils/lib.rs"],
-    defaults: ["keymint_use_latest_hal_aidl_rust"],
+    defaults: [
+        "keymint_use_latest_hal_aidl_rust",
+        "keystore2_use_latest_aidl_rust",
+    ],
     test_suites: ["general-tests"],
     require_root: true,
     auto_gen_config: true,
     compile_multilib: "first",
     rustlibs: [
-        "android.system.keystore2-V2-rust",
         "libbinder_rs",
         "libkeystore2_selinux",
         "liblog_rust",
@@ -122,6 +129,8 @@
         "librand",
         "libserde",
         "libserde_cbor",
+        "libthiserror",
+        "libanyhow",
     ],
 }
 
@@ -145,6 +154,7 @@
         "watchdog",
         "keystore2_blob_test_utils",
     ],
+    require_root: true,
 }
 
 rust_defaults {
diff --git a/keystore2/aaid/lib.rs b/keystore2/aaid/lib.rs
index 3187198..8f6a09e 100644
--- a/keystore2/aaid/lib.rs
+++ b/keystore2/aaid/lib.rs
@@ -29,7 +29,7 @@
     // in the second pointer argument.
     let status = unsafe { aaid_keystore_attestation_id(uid, buffer.as_mut_ptr(), &mut size) };
     match status {
-        0 => Ok(buffer[0..size as usize].to_vec()),
+        0 => Ok(buffer[0..size].to_vec()),
         status => Err(status),
     }
 }
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
index ae08567..8f5c13b 100644
--- a/keystore2/aidl/Android.bp
+++ b/keystore2/aidl/Android.bp
@@ -24,7 +24,7 @@
 aidl_interface {
     name: "android.security.attestationmanager",
     srcs: [ "android/security/attestationmanager/*.aidl", ],
-    imports: [ "android.hardware.security.keymint-V2" ],
+    imports: [ "android.hardware.security.keymint-V3" ],
     unstable: true,
     backend: {
         java: {
@@ -44,7 +44,7 @@
     name: "android.security.authorization",
     srcs: [ "android/security/authorization/*.aidl" ],
     imports: [
-        "android.hardware.security.keymint-V2",
+        "android.hardware.security.keymint-V3",
         "android.hardware.security.secureclock-V1",
     ],
     unstable: true,
@@ -83,7 +83,7 @@
     name: "android.security.compat",
     srcs: [ "android/security/compat/*.aidl" ],
     imports: [
-        "android.hardware.security.keymint-V2",
+        "android.hardware.security.keymint-V3",
         "android.hardware.security.secureclock-V1",
         "android.hardware.security.sharedsecret-V1",
     ],
@@ -103,31 +103,10 @@
 }
 
 aidl_interface {
-    name: "android.security.remoteprovisioning",
-    srcs: [ "android/security/remoteprovisioning/*.aidl" ],
-    imports: [
-        "android.hardware.security.keymint-V2",
-    ],
-    unstable: true,
-    backend: {
-        java: {
-            platform_apis: true,
-        },
-        ndk: {
-            enabled: true,
-            apps_enabled: false,
-        },
-        rust: {
-            enabled: true,
-        },
-    },
-}
-
-aidl_interface {
     name: "android.security.maintenance",
     srcs: [ "android/security/maintenance/*.aidl" ],
     imports: [
-        "android.system.keystore2-V2",
+        "android.system.keystore2-V3",
     ],
     unstable: true,
     backend: {
@@ -166,7 +145,7 @@
     name: "android.security.metrics",
     srcs: [ "android/security/metrics/*.aidl" ],
     imports: [
-        "android.system.keystore2-V2",
+        "android.system.keystore2-V3",
     ],
     unstable: true,
     backend: {
@@ -183,19 +162,68 @@
     },
 }
 
+// java_defaults that includes the latest Keystore2 AIDL library.
+// Modules that depend on KeyMint directly can include this java_defaults to avoid
+// managing dependency versions explicitly.
+java_defaults {
+    name: "keystore2_use_latest_aidl_java_static",
+    static_libs: [
+        "android.system.keystore2-V3-java-source"
+    ],
+}
+
+java_defaults {
+    name: "keystore2_use_latest_aidl_java_shared",
+    libs: [
+        "android.system.keystore2-V3-java-source"
+    ],
+}
+
+java_defaults {
+    name: "keystore2_use_latest_aidl_java",
+    libs: [
+        "android.system.keystore2-V3-java"
+    ],
+}
+
 // 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",
+        "android.system.keystore2-V3-ndk",
     ],
 }
 
 cc_defaults {
     name: "keystore2_use_latest_aidl_ndk_shared",
     shared_libs: [
-        "android.system.keystore2-V2-ndk",
+        "android.system.keystore2-V3-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "keystore2_use_latest_aidl_cpp_shared",
+    shared_libs: [
+        "android.system.keystore2-V3-cpp",
+    ],
+}
+
+cc_defaults {
+    name: "keystore2_use_latest_aidl_cpp_static",
+    static_libs: [
+        "android.system.keystore2-V3-cpp",
+    ],
+}
+
+
+// A rust_defaults that includes the latest Keystore2 AIDL library.
+// Modules that depend on Keystore2 directly can include this rust_defaults to avoid
+// managing dependency versions explicitly.
+rust_defaults {
+    name: "keystore2_use_latest_aidl_rust",
+    rustlibs: [
+        "android.system.keystore2-V3-rust",
     ],
 }
diff --git a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
index 3f33431..e3b7d11 100644
--- a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
+++ b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
@@ -41,8 +41,26 @@
 
     /**
      * Unlocks the keystore for the given user id.
+     *
      * Callers require 'Unlock' permission.
-     * If a password was set, a password must be given on unlock or the operation fails.
+     *
+     * Super-Encryption Key:
+     * When the device is unlocked (and password is non-null), Keystore stores in memory
+     * a super-encryption key derived from the password that protects UNLOCKED_DEVICE_REQUIRED
+     * keys; this key is wiped from memory when the device is locked.
+     *
+     * If unlockingSids is non-empty on lock, then before the super-encryption key is wiped from
+     * memory, a copy of it is stored in memory encrypted with a fresh AES key. This key is then
+     * imported into KM, tagged such that it can be used given a valid, recent auth token for any
+     * of the unlockingSids.
+     *
+     * Options for unlock:
+     *  - If the password is non-null, the super-encryption key is re-derived as above.
+     *  - If the password is null, then if a suitable auth token to access the encrypted
+     *    Super-encryption key stored in KM has been sent to keystore (via addAuthToken), the
+     *    encrypted super-encryption key is recovered so that UNLOCKED_DEVICE_REQUIRED keys can
+     *    be used once again.
+     *  - If neither of these are met, then the operation fails.
      *
      * ## Error conditions:
      * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'Unlock' permission.
@@ -50,33 +68,10 @@
      * `ResponseCode::VALUE_CORRUPTED` - if the super key can not be decrypted.
      * `ResponseCode::KEY_NOT_FOUND` - if the super key is not found.
      *
-     * @lockScreenEvent - Indicates what happened.
-     *                    * LockScreenEvent.UNLOCK if the screen was unlocked.
-     *                    * LockScreenEvent.LOCK if the screen was locked.
-     *
-     * @param userId - Android user id
-     *
-     * @param password - synthetic password derived by the user denoted by the user id
-     *
-     * @param unlockingSids - list of biometric SIDs for this user. This will be null when
-     *                        lockScreenEvent is UNLOCK, but may be non-null when
-     *                        lockScreenEvent is LOCK.
-     *
-     *                        When the device is unlocked, Keystore stores in memory
-     *                        a super-encryption key that protects UNLOCKED_DEVICE_REQUIRED
-     *                        keys; this key is wiped from memory when the device is locked.
-     *
-     *                        If unlockingSids is non-empty on lock, then before the
-     *                        super-encryption key is wiped from memory, a copy of it
-     *                        is stored in memory encrypted with a fresh AES key.
-     *                        This key is then imported into KM, tagged such that it can be
-     *                        used given a valid, recent auth token for any of the
-     *                        unlockingSids.
-     *
-     *                        Then, when the device is unlocked again, if a suitable auth token
-     *                        has been sent to keystore, it is used to recover the
-     *                        super-encryption key, so that UNLOCKED_DEVICE_REQUIRED keys can
-     *                        be used once again.
+     * @param lockScreenEvent whether the lock screen locked or unlocked
+     * @param userId android user id
+     * @param password synthetic password derived from the user's LSKF, must be null on lock
+     * @param unlockingSids list of biometric SIDs for this user, ignored on unlock
      */
     void onLockScreenEvent(in LockScreenEvent lockScreenEvent, in int userId,
                            in @nullable byte[] password, in @nullable long[] unlockingSids);
diff --git a/keystore2/aidl/android/security/metrics/AtomID.aidl b/keystore2/aidl/android/security/metrics/AtomID.aidl
index 166e753..3043ed3 100644
--- a/keystore2/aidl/android/security/metrics/AtomID.aidl
+++ b/keystore2/aidl/android/security/metrics/AtomID.aidl
@@ -23,7 +23,7 @@
 @Backing(type="int")
 enum AtomID {
     STORAGE_STATS = 10103,
-    RKP_POOL_STATS = 10104,
+    // reserved 10104
     KEY_CREATION_WITH_GENERAL_INFO = 10118,
     KEY_CREATION_WITH_AUTH_INFO = 10119,
     KEY_CREATION_WITH_PURPOSE_AND_MODES_INFO = 10120,
@@ -32,4 +32,4 @@
     KEY_OPERATION_WITH_GENERAL_INFO = 10123,
     RKP_ERROR_STATS = 10124,
     CRASH_STATS = 10125,
-}
\ No newline at end of file
+}
diff --git a/keystore2/aidl/android/security/metrics/KeystoreAtom.aidl b/keystore2/aidl/android/security/metrics/KeystoreAtom.aidl
index 266267a..843e80b 100644
--- a/keystore2/aidl/android/security/metrics/KeystoreAtom.aidl
+++ b/keystore2/aidl/android/security/metrics/KeystoreAtom.aidl
@@ -22,7 +22,7 @@
  * Encapsulates a particular atom object of type KeystoreAtomPayload its count. Note that
  * the field: count is only relevant for the atom types that are stored in the
  * in-memory metrics store. E.g. count field is not relevant for the atom types such as StorageStats
- * and RkpPoolStats that are not stored in the metrics store.
+ * that are not stored in the metrics store.
  * @hide
  */
 @RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
diff --git a/keystore2/aidl/android/security/metrics/KeystoreAtomPayload.aidl b/keystore2/aidl/android/security/metrics/KeystoreAtomPayload.aidl
index a3e4dd6..2f89a2d 100644
--- a/keystore2/aidl/android/security/metrics/KeystoreAtomPayload.aidl
+++ b/keystore2/aidl/android/security/metrics/KeystoreAtomPayload.aidl
@@ -24,14 +24,12 @@
 import android.security.metrics.StorageStats;
 import android.security.metrics.Keystore2AtomWithOverflow;
 import android.security.metrics.RkpErrorStats;
-import android.security.metrics.RkpPoolStats;
 import android.security.metrics.CrashStats;
 
 /** @hide */
 @RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
 union KeystoreAtomPayload {
     StorageStats storageStats;
-    RkpPoolStats rkpPoolStats;
     KeyCreationWithGeneralInfo keyCreationWithGeneralInfo;
     KeyCreationWithAuthInfo keyCreationWithAuthInfo;
     KeyCreationWithPurposeAndModesInfo keyCreationWithPurposeAndModesInfo;
@@ -40,4 +38,4 @@
     KeyOperationWithGeneralInfo keyOperationWithGeneralInfo;
     RkpErrorStats rkpErrorStats;
     CrashStats crashStats;
-}
\ No newline at end of file
+}
diff --git a/keystore2/aidl/android/security/metrics/PoolStatus.aidl b/keystore2/aidl/android/security/metrics/PoolStatus.aidl
deleted file mode 100644
index 3530163..0000000
--- a/keystore2/aidl/android/security/metrics/PoolStatus.aidl
+++ /dev/null
@@ -1,30 +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 android.security.metrics;
-
-/**
- * Status of the remotely provisioned keys, as defined in RkpPoolStats of
- * frameworks/proto_logging/stats/atoms.proto.
- * @hide
- */
-@Backing(type="int")
-enum PoolStatus {
-    EXPIRING = 1,
-    UNASSIGNED = 2,
-    ATTESTED = 3,
-    TOTAL = 4,
-}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/RkpPoolStats.aidl b/keystore2/aidl/android/security/metrics/RkpPoolStats.aidl
deleted file mode 100644
index 016b6ff..0000000
--- a/keystore2/aidl/android/security/metrics/RkpPoolStats.aidl
+++ /dev/null
@@ -1,32 +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 android.security.metrics;
-
-import android.security.metrics.SecurityLevel;
-
-/**
- * Count of keys in the attestation key pool related to Remote Key Provisioning (RKP).
- * @hide
- */
-@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
-parcelable RkpPoolStats {
-    SecurityLevel security_level;
-    int expiring;
-    int unassigned;
-    int attested;
-    int total;
-}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/remoteprovisioning/AttestationPoolStatus.aidl b/keystore2/aidl/android/security/remoteprovisioning/AttestationPoolStatus.aidl
deleted file mode 100644
index 3528b42..0000000
--- a/keystore2/aidl/android/security/remoteprovisioning/AttestationPoolStatus.aidl
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-
-package android.security.remoteprovisioning;
-
-/**
- * This parcelable provides information about the state of the attestation key pool.
- * @hide
- */
-parcelable AttestationPoolStatus {
-    /**
-     * The number of signed attestation certificate chains which will expire when the date provided
-     * to keystore to check against is reached.
-     */
-    int expiring;
-    /**
-     * The number of signed attestation certificate chains which have not yet been assigned to an
-     * app. This should be less than or equal to signed keys. The remainder of `signed` -
-     * `unassigned` gives the number of signed keys that have been assigned to an app.
-     */
-    int unassigned;
-    /**
-     * The number of signed attestation keys. This should be less than or equal to `total`. The
-     * remainder of `total` - `attested` gives the number of keypairs available to be sent off to
-     * the server for signing.
-     */
-    int attested;
-    /**
-     * The total number of attestation keys.
-     */
-    int total;
-}
diff --git a/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl b/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
deleted file mode 100644
index ecdc790..0000000
--- a/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
+++ /dev/null
@@ -1,148 +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.
- */
-
-package android.security.remoteprovisioning;
-
-import android.hardware.security.keymint.DeviceInfo;
-import android.hardware.security.keymint.ProtectedData;
-import android.hardware.security.keymint.SecurityLevel;
-import android.security.remoteprovisioning.AttestationPoolStatus;
-import android.security.remoteprovisioning.ImplInfo;
-
-/**
- * `IRemoteProvisioning` is the interface provided to use the remote provisioning functionality
- * provided through KeyStore. The intent is for a higher level system component to use these
- * functions in order to drive the process through which the device can receive functioning
- * attestation certificates.
- *
- * ## Error conditions
- * Error conditions are reported as service specific errors.
- * Positive codes correspond to `android.security.remoteprovisioning.ResponseCode`
- * and indicate error conditions diagnosed by the Keystore 2.0 service.
- * TODO: Remote Provisioning HAL error code info
- *
- * `ResponseCode::PERMISSION_DENIED` if the caller does not have the permissions
- * to use the RemoteProvisioning API. This permission is defined under access_vectors in SEPolicy
- * in the keystore2 class: remotely_provision
- *
- * `ResponseCode::SYSTEM_ERROR` for any unexpected errors like IO or IPC failures.
- *
- * @hide
- */
-interface IRemoteProvisioning {
-
-    /**
-     * Returns the status of the attestation key pool in the database.
-     *
-     * @param expiredBy The date as seconds since epoch by which to judge expiration status of
-     *                        certificates.
-     *
-     * @param secLevel The security level to specify which KM instance to get the pool for.
-     *
-     * @return The `AttestationPoolStatus` parcelable contains fields communicating information
-     *                        relevant to making decisions about when to generate and provision
-     *                        more attestation keys.
-     */
-    AttestationPoolStatus getPoolStatus(in long expiredBy, in SecurityLevel secLevel);
-
-    /**
-     * This is the primary entry point for beginning a remote provisioning flow. The caller
-     * specifies how many CSRs should be generated and provides an X25519 ECDH public key along
-     * with a challenge to encrypt privacy sensitive portions of the returned CBOR blob and
-     * guarantee freshness of the request to the certifying third party.
-     *
-     * ## Error conditions
-     * `ResponseCode::NO_UNSIGNED_KEYS` if there are no unsigned keypairs in the database that can
-     *                         be used for the CSRs.
-     *
-     * A RemoteProvisioning HAL response code may indicate backend errors such as failed EEK
-     *                         verification.
-     *
-     * @param testMode Whether or not the TA implementing the Remote Provisioning HAL should accept
-     *                         any EEK (Endpoint Encryption Key), or only one signed by a chain
-     *                         that verifies back to the Root of Trust baked into the TA. True
-     *                         means that any key is accepted.
-     *
-     * @param numCsr How many certificate signing requests should be generated.
-     *
-     * @param eek A chain of certificates terminating in an X25519 public key, the Endpoint
-     *                         Encryption Key.
-     *
-     * @param challenge A challenge to be included and MACed in the returned CBOR blob.
-     *
-     * @param secLevel The security level to specify which KM instance from which to generate a
-     *                         CSR.
-     *
-     * @param protectedData The encrypted CBOR blob generated by the remote provisioner
-     *
-     * @return A CBOR blob composed of various elements required by the server to verify the
-     *                         request.
-     */
-    byte[] generateCsr(in boolean testMode, in int numCsr, in byte[] eek, in byte[] challenge,
-        in SecurityLevel secLevel, out ProtectedData protectedData, out DeviceInfo deviceInfo);
-
-    /**
-     * This method provides a way for the returned attestation certificate chains to be provisioned
-     * to the attestation key database. When an app requests an attesation key, it will be assigned
-     * one of these certificate chains along with the corresponding private key.
-     *
-     * @param publicKey The raw public key encoded in the leaf certificate.
-     *
-     * @param batchCert The batch certificate corresponding to the attestation key. Separated for
-     *                          the purpose of making Subject lookup for KM attestation easier.
-     *
-     * @param certs An X.509, DER encoded certificate chain for the attestation key.
-     *
-     * @param expirationDate The expiration date on the certificate chain, provided by the caller
-     *                          for convenience.
-     *
-     * @param secLevel The security level representing the KM instance containing the key that this
-     *                          chain corresponds to.
-     */
-    void provisionCertChain(in byte[] publicKey, in byte[] batchCert, in byte[] certs,
-        in long expirationDate, in SecurityLevel secLevel);
-
-    /**
-     * This method allows the caller to instruct KeyStore to generate and store a key pair to be
-     * used for attestation in the `generateCsr` method. The caller should handle spacing out these
-     * requests so as not to jam up the KeyStore work queue.
-     *
-     * @param is_test_mode Instructs the underlying HAL interface to mark the generated key with a
-     *                        tag to indicate that it's for testing.
-     *
-     * @param secLevel The security level to specify which KM instance should generate a key pair.
-     */
-    void generateKeyPair(in boolean is_test_mode, in SecurityLevel secLevel);
-
-    /**
-     * This method returns implementation information for whichever instances of
-     * IRemotelyProvisionedComponent are running on the device. The RemoteProvisioner app needs to
-     * know which KM instances it should be generating and managing attestation keys for, and which
-     * EC curves are supported in those instances.
-     *
-     * @return The array of ImplInfo parcelables.
-     */
-     ImplInfo[] getImplementationInfo();
-
-    /**
-     * This method deletes all remotely provisioned attestation keys in the database, regardless
-     * of what state in their life cycle they are in. This is primarily useful to facilitate
-     * testing.
-     *
-     * @return Number of keys deleted
-     */
-    long deleteAllKeys();
-}
diff --git a/keystore2/aidl/android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.aidl b/keystore2/aidl/android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.aidl
deleted file mode 100644
index 7d45e52..0000000
--- a/keystore2/aidl/android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.aidl
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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/ImplInfo.aidl b/keystore2/aidl/android/security/remoteprovisioning/ImplInfo.aidl
deleted file mode 100644
index 9baeb24..0000000
--- a/keystore2/aidl/android/security/remoteprovisioning/ImplInfo.aidl
+++ /dev/null
@@ -1,37 +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 android.security.remoteprovisioning;
-
-import android.hardware.security.keymint.SecurityLevel;
-
-/**
- * This parcelable provides information about the underlying IRemotelyProvisionedComponent
- * implementation.
- * @hide
- */
-parcelable ImplInfo {
-    /**
-     * The security level of the underlying implementation: TEE or StrongBox.
-     */
-    SecurityLevel secLevel;
-    /**
-     * An integer denoting which EC curve is supported in the underlying implementation. The current
-     * options are either P256 or 25519, with values defined in
-     * hardware/interfaces/security/keymint/aidl/.../RpcHardwareInfo.aidl
-     */
-    int supportedCurve;
-}
diff --git a/keystore2/aidl/android/security/remoteprovisioning/RemotelyProvisionedKey.aidl b/keystore2/aidl/android/security/remoteprovisioning/RemotelyProvisionedKey.aidl
deleted file mode 100644
index ae21855..0000000
--- a/keystore2/aidl/android/security/remoteprovisioning/RemotelyProvisionedKey.aidl
+++ /dev/null
@@ -1,42 +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 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/aidl/android/security/remoteprovisioning/ResponseCode.aidl b/keystore2/aidl/android/security/remoteprovisioning/ResponseCode.aidl
deleted file mode 100644
index c9877db..0000000
--- a/keystore2/aidl/android/security/remoteprovisioning/ResponseCode.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.
- */
-
-package android.security.remoteprovisioning;
-
-@Backing(type="int")
-/** @hide */
-enum ResponseCode {
-    /**
-     * Returned if there are no keys available in the database to be used in a CSR
-     */
-    NO_UNSIGNED_KEYS = 1,
-    /**
-     * The caller has imrproper SELinux permissions to access the Remote Provisioning API.
-     */
-    PERMISSION_DENIED = 2,
-    /**
-     * An unexpected error occurred, likely with IO or IPC.
-     */
-    SYSTEM_ERROR = 3,
-}
diff --git a/keystore2/android.system.keystore2-service.xml b/keystore2/android.system.keystore2-service.xml
index 20c2fba..45f995c 100644
--- a/keystore2/android.system.keystore2-service.xml
+++ b/keystore2/android.system.keystore2-service.xml
@@ -1,7 +1,7 @@
 <manifest version="1.0" type="framework">
     <hal format="aidl">
         <name>android.system.keystore2</name>
-        <version>2</version>
+        <version>3</version>
         <interface>
             <name>IKeystoreService</name>
             <instance>default</instance>
diff --git a/keystore2/apc_compat/Android.bp b/keystore2/apc_compat/Android.bp
index df7521e..61697a8 100644
--- a/keystore2/apc_compat/Android.bp
+++ b/keystore2/apc_compat/Android.bp
@@ -27,7 +27,9 @@
         "apc_compat.cpp",
     ],
     shared_libs: [
+        "libbinder_ndk",
         "android.hardware.confirmationui@1.0",
+        "android.hardware.confirmationui-V1-ndk",
         "libbase",
         "libhidlbase",
         "libutils",
diff --git a/keystore2/apc_compat/apc_compat.cpp b/keystore2/apc_compat/apc_compat.cpp
index 08a8e45..9f60db2 100644
--- a/keystore2/apc_compat/apc_compat.cpp
+++ b/keystore2/apc_compat/apc_compat.cpp
@@ -19,6 +19,12 @@
 #include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
 #include <hwbinder/IBinder.h>
 
+#include <aidl/android/hardware/confirmationui/BnConfirmationResultCallback.h>
+#include <aidl/android/hardware/confirmationui/IConfirmationResultCallback.h>
+#include <aidl/android/hardware/confirmationui/IConfirmationUI.h>
+#include <aidl/android/hardware/confirmationui/UIOption.h>
+#include <android/binder_manager.h>
+
 #include <memory>
 #include <string>
 #include <thread>
@@ -33,41 +39,52 @@
 using android::hardware::hidl_vec;
 using android::hardware::Return;
 using android::hardware::Status;
-using android::hardware::confirmationui::V1_0::IConfirmationResultCallback;
-using android::hardware::confirmationui::V1_0::IConfirmationUI;
+using HidlConfirmationResultCb =
+    android::hardware::confirmationui::V1_0::IConfirmationResultCallback;
+using HidlConfirmationUI = android::hardware::confirmationui::V1_0::IConfirmationUI;
 using android::hardware::confirmationui::V1_0::ResponseCode;
-using android::hardware::confirmationui::V1_0::UIOption;
+using HidlUIOptions = android::hardware::confirmationui::V1_0::UIOption;
 
-static uint32_t responseCode2Compat(ResponseCode rc) {
-    switch (rc) {
-    case ResponseCode::OK:
-        return APC_COMPAT_ERROR_OK;
-    case ResponseCode::Canceled:
-        return APC_COMPAT_ERROR_CANCELLED;
-    case ResponseCode::Aborted:
-        return APC_COMPAT_ERROR_ABORTED;
-    case ResponseCode::OperationPending:
-        return APC_COMPAT_ERROR_OPERATION_PENDING;
-    case ResponseCode::Ignored:
-        return APC_COMPAT_ERROR_IGNORED;
-    case ResponseCode::SystemError:
-    case ResponseCode::Unimplemented:
-    case ResponseCode::Unexpected:
-    case ResponseCode::UIError:
-    case ResponseCode::UIErrorMissingGlyph:
-    case ResponseCode::UIErrorMessageTooLong:
-    case ResponseCode::UIErrorMalformedUTF8Encoding:
-    default:
-        return APC_COMPAT_ERROR_SYSTEM_ERROR;
-    }
-}
+using AidlConfirmationUI = ::aidl::android::hardware::confirmationui::IConfirmationUI;
+using AidlBnConfirmationResultCb =
+    ::aidl::android::hardware::confirmationui::BnConfirmationResultCallback;
+using AidlUIOptions = ::aidl::android::hardware::confirmationui::UIOption;
 
-class ConfuiCompatSession : public IConfirmationResultCallback, public hidl_death_recipient {
+class CompatSessionCB {
   public:
-    static sp<ConfuiCompatSession>* tryGetService() {
-        sp<IConfirmationUI> service = IConfirmationUI::tryGetService();
+    void
+    finalize(uint32_t responseCode, ApcCompatCallback callback,
+             std::optional<std::reference_wrapper<const std::vector<uint8_t>>> dataConfirmed,
+             std::optional<std::reference_wrapper<const std::vector<uint8_t>>> confirmationToken) {
+        if (callback.result != nullptr) {
+            size_t dataConfirmedSize = 0;
+            const uint8_t* dataConfirmedPtr = nullptr;
+            size_t confirmationTokenSize = 0;
+            const uint8_t* confirmationTokenPtr = nullptr;
+            if (responseCode == APC_COMPAT_ERROR_OK) {
+                if (dataConfirmed) {
+                    dataConfirmedPtr = dataConfirmed->get().data();
+                    dataConfirmedSize = dataConfirmed->get().size();
+                }
+                if (confirmationToken) {
+                    confirmationTokenPtr = confirmationToken->get().data();
+                    confirmationTokenSize = confirmationToken->get().size();
+                }
+            }
+            callback.result(callback.data, responseCode, dataConfirmedPtr, dataConfirmedSize,
+                            confirmationTokenPtr, confirmationTokenSize);
+        }
+    }
+};
+
+class ConfuiHidlCompatSession : public HidlConfirmationResultCb,
+                                public hidl_death_recipient,
+                                public CompatSessionCB {
+  public:
+    static sp<ConfuiHidlCompatSession> tryGetService() {
+        sp<HidlConfirmationUI> service = HidlConfirmationUI::tryGetService();
         if (service) {
-            return new sp(new ConfuiCompatSession(std::move(service)));
+            return sp<ConfuiHidlCompatSession>(new ConfuiHidlCompatSession(std::move(service)));
         } else {
             return nullptr;
         }
@@ -78,13 +95,12 @@
                                     const char* locale, ApcCompatUiOptions ui_options) {
         std::string hidl_prompt(prompt_text);
         std::vector<uint8_t> hidl_extra(extra_data, extra_data + extra_data_size);
-        std::string hidl_locale(locale);
-        std::vector<UIOption> hidl_ui_options;
+        std::vector<HidlUIOptions> hidl_ui_options;
         if (ui_options.inverted) {
-            hidl_ui_options.push_back(UIOption::AccessibilityInverted);
+            hidl_ui_options.push_back(HidlUIOptions::AccessibilityInverted);
         }
         if (ui_options.magnified) {
-            hidl_ui_options.push_back(UIOption::AccessibilityMagnified);
+            hidl_ui_options.push_back(HidlUIOptions::AccessibilityMagnified);
         }
         auto lock = std::lock_guard(callback_lock_);
         if (callback_.result != nullptr) {
@@ -98,7 +114,7 @@
             return APC_COMPAT_ERROR_SYSTEM_ERROR;
         }
 
-        auto rc = service_->promptUserConfirmation(sp(this), hidl_prompt, hidl_extra, hidl_locale,
+        auto rc = service_->promptUserConfirmation(sp(this), hidl_prompt, hidl_extra, locale,
                                                    hidl_ui_options);
         if (!rc.isOk()) {
             LOG(ERROR) << "Communication error: promptUserConfirmation: " << rc.description();
@@ -111,10 +127,8 @@
 
     void abort() { service_->abort(); }
 
-    void
-    finalize(ResponseCode responseCode,
-             std::optional<std::reference_wrapper<const hidl_vec<uint8_t>>> dataConfirmed,
-             std::optional<std::reference_wrapper<const hidl_vec<uint8_t>>> confirmationToken) {
+    void finalize(ResponseCode responseCode, const hidl_vec<uint8_t>& dataConfirmed,
+                  const hidl_vec<uint8_t>& confirmationToken) {
         ApcCompatCallback callback;
         {
             auto lock = std::lock_guard(callback_lock_);
@@ -128,26 +142,14 @@
         if (callback.result != nullptr) {
             service_->unlinkToDeath(sp(this));
 
-            size_t dataConfirmedSize = 0;
-            const uint8_t* dataConfirmedPtr = nullptr;
-            size_t confirmationTokenSize = 0;
-            const uint8_t* confirmationTokenPtr = nullptr;
-            if (responseCode == ResponseCode::OK) {
-                if (dataConfirmed) {
-                    dataConfirmedPtr = dataConfirmed->get().data();
-                    dataConfirmedSize = dataConfirmed->get().size();
-                }
-                if (dataConfirmed) {
-                    confirmationTokenPtr = confirmationToken->get().data();
-                    confirmationTokenSize = confirmationToken->get().size();
-                }
-            }
-            callback.result(callback.data, responseCode2Compat(responseCode), dataConfirmedPtr,
-                            dataConfirmedSize, confirmationTokenPtr, confirmationTokenSize);
+            std::vector<uint8_t> data = dataConfirmed;
+            std::vector<uint8_t> token = confirmationToken;
+
+            CompatSessionCB::finalize(responseCode2Compat(responseCode), callback, data, token);
         }
     }
 
-    // IConfirmationResultCallback overrides:
+    // HidlConfirmationResultCb overrides:
     android::hardware::Return<void> result(ResponseCode responseCode,
                                            const hidl_vec<uint8_t>& dataConfirmed,
                                            const hidl_vec<uint8_t>& confirmationToken) override {
@@ -160,10 +162,34 @@
         finalize(ResponseCode::SystemError, {}, {});
     }
 
+    static uint32_t responseCode2Compat(ResponseCode rc) {
+        switch (rc) {
+        case ResponseCode::OK:
+            return APC_COMPAT_ERROR_OK;
+        case ResponseCode::Canceled:
+            return APC_COMPAT_ERROR_CANCELLED;
+        case ResponseCode::Aborted:
+            return APC_COMPAT_ERROR_ABORTED;
+        case ResponseCode::OperationPending:
+            return APC_COMPAT_ERROR_OPERATION_PENDING;
+        case ResponseCode::Ignored:
+            return APC_COMPAT_ERROR_IGNORED;
+        case ResponseCode::SystemError:
+        case ResponseCode::Unimplemented:
+        case ResponseCode::Unexpected:
+        case ResponseCode::UIError:
+        case ResponseCode::UIErrorMissingGlyph:
+        case ResponseCode::UIErrorMessageTooLong:
+        case ResponseCode::UIErrorMalformedUTF8Encoding:
+        default:
+            return APC_COMPAT_ERROR_SYSTEM_ERROR;
+        }
+    }
+
   private:
-    ConfuiCompatSession(sp<IConfirmationUI> service)
+    ConfuiHidlCompatSession(sp<HidlConfirmationUI> service)
         : service_(service), callback_{nullptr, nullptr} {}
-    sp<IConfirmationUI> service_;
+    sp<HidlConfirmationUI> service_;
 
     // The callback_lock_ protects the callback_ field against concurrent modification.
     // IMPORTANT: It must never be held while calling the call back.
@@ -171,34 +197,248 @@
     ApcCompatCallback callback_;
 };
 
+class ConfuiAidlCompatSession : public AidlBnConfirmationResultCb, public CompatSessionCB {
+  public:
+    static std::shared_ptr<ConfuiAidlCompatSession> tryGetService() {
+        constexpr const char confirmationUIServiceName[] =
+            "android.hardware.confirmationui.IConfirmationUI/default";
+        if (!AServiceManager_isDeclared(confirmationUIServiceName)) {
+            LOG(INFO) << confirmationUIServiceName << " is not declared in VINTF";
+            return nullptr;
+        }
+        std::shared_ptr<AidlConfirmationUI> aidlService = AidlConfirmationUI::fromBinder(
+            ndk::SpAIBinder(AServiceManager_waitForService(confirmationUIServiceName)));
+        if (aidlService) {
+            return ::ndk::SharedRefBase::make<ConfuiAidlCompatSession>(aidlService);
+        }
+
+        return nullptr;
+    }
+
+    uint32_t promptUserConfirmation(ApcCompatCallback callback, const char* prompt_text,
+                                    const uint8_t* extra_data, size_t extra_data_size,
+                                    const char* locale, ApcCompatUiOptions ui_options) {
+        std::vector<uint8_t> aidl_prompt(prompt_text, prompt_text + strlen(prompt_text));
+        std::vector<uint8_t> aidl_extra(extra_data, extra_data + extra_data_size);
+        std::vector<AidlUIOptions> aidl_ui_options;
+        if (ui_options.inverted) {
+            aidl_ui_options.push_back(AidlUIOptions::ACCESSIBILITY_INVERTED);
+        }
+        if (ui_options.magnified) {
+            aidl_ui_options.push_back(AidlUIOptions::ACCESSIBILITY_MAGNIFIED);
+        }
+        auto lock = std::lock_guard(callback_lock_);
+        if (callback_.result != nullptr) {
+            return APC_COMPAT_ERROR_OPERATION_PENDING;
+        }
+
+        if (!aidlService_) {
+            return APC_COMPAT_ERROR_SYSTEM_ERROR;
+        }
+        auto linkRet =
+            AIBinder_linkToDeath(aidlService_->asBinder().get(), death_recipient_.get(), this);
+        if (linkRet != STATUS_OK) {
+            LOG(ERROR) << "Communication error: promptUserConfirmation: "
+                          "Trying to register death recipient: ";
+            return APC_COMPAT_ERROR_SYSTEM_ERROR;
+        }
+
+        auto rc = aidlService_->promptUserConfirmation(ref<ConfuiAidlCompatSession>(), aidl_prompt,
+                                                       aidl_extra, locale, aidl_ui_options);
+        int ret = getReturnCode(rc);
+        if (ret == AidlConfirmationUI::OK) {
+            callback_ = callback;
+        } else {
+            LOG(ERROR) << "Communication error: promptUserConfirmation: " << rc.getDescription();
+        }
+        return responseCode2Compat(ret);
+    }
+
+    void abort() {
+        if (aidlService_) {
+            aidlService_->abort();
+        }
+    }
+
+    void
+    finalize(int32_t responseCode,
+             std::optional<std::reference_wrapper<const std::vector<uint8_t>>> dataConfirmed,
+             std::optional<std::reference_wrapper<const std::vector<uint8_t>>> confirmationToken) {
+        ApcCompatCallback callback;
+        {
+            auto lock = std::lock_guard(callback_lock_);
+            // Calling the callback consumes the callback data structure. We have to make
+            // sure that it can only be called once.
+            callback = callback_;
+            callback_ = {nullptr, nullptr};
+            // Unlock the callback_lock_ here. It must never be held while calling the callback.
+        }
+
+        if (callback.result != nullptr) {
+            if (aidlService_) {
+                AIBinder_unlinkToDeath(aidlService_->asBinder().get(), death_recipient_.get(),
+                                       this);
+            }
+            CompatSessionCB::finalize(responseCode2Compat(responseCode), callback, dataConfirmed,
+                                      confirmationToken);
+        }
+    }
+
+    // AidlBnConfirmationResultCb overrides:
+    ::ndk::ScopedAStatus result(int32_t responseCode, const std::vector<uint8_t>& dataConfirmed,
+                                const std::vector<uint8_t>& confirmationToken) override {
+        finalize(responseCode, dataConfirmed, confirmationToken);
+        return ::ndk::ScopedAStatus::ok();
+    };
+
+    void serviceDied() {
+        aidlService_.reset();
+        aidlService_ = nullptr;
+        finalize(AidlConfirmationUI::SYSTEM_ERROR, {}, {});
+    }
+
+    static void binderDiedCallbackAidl(void* ptr) {
+        LOG(ERROR) << __func__ << " : ConfuiAidlCompatSession Service died.";
+        auto aidlSession = static_cast<ConfuiAidlCompatSession*>(ptr);
+        if (aidlSession == nullptr) {
+            LOG(ERROR) << __func__ << ": Null ConfuiAidlCompatSession HAL died.";
+            return;
+        }
+        aidlSession->serviceDied();
+    }
+
+    int getReturnCode(const ::ndk::ScopedAStatus& result) {
+        if (result.isOk()) return AidlConfirmationUI::OK;
+
+        if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+            return static_cast<int>(result.getServiceSpecificError());
+        }
+        return result.getStatus();
+    }
+
+    uint32_t responseCode2Compat(int32_t rc) {
+        switch (rc) {
+        case AidlConfirmationUI::OK:
+            return APC_COMPAT_ERROR_OK;
+        case AidlConfirmationUI::CANCELED:
+            return APC_COMPAT_ERROR_CANCELLED;
+        case AidlConfirmationUI::ABORTED:
+            return APC_COMPAT_ERROR_ABORTED;
+        case AidlConfirmationUI::OPERATION_PENDING:
+            return APC_COMPAT_ERROR_OPERATION_PENDING;
+        case AidlConfirmationUI::IGNORED:
+            return APC_COMPAT_ERROR_IGNORED;
+        case AidlConfirmationUI::SYSTEM_ERROR:
+        case AidlConfirmationUI::UNIMPLEMENTED:
+        case AidlConfirmationUI::UNEXPECTED:
+        case AidlConfirmationUI::UI_ERROR:
+        case AidlConfirmationUI::UI_ERROR_MISSING_GLYPH:
+        case AidlConfirmationUI::UI_ERROR_MESSAGE_TOO_LONG:
+        case AidlConfirmationUI::UI_ERROR_MALFORMED_UTF8ENCODING:
+        default:
+            return APC_COMPAT_ERROR_SYSTEM_ERROR;
+        }
+    }
+
+    ConfuiAidlCompatSession(std::shared_ptr<AidlConfirmationUI> service)
+        : aidlService_(service), callback_{nullptr, nullptr} {
+        death_recipient_ = ::ndk::ScopedAIBinder_DeathRecipient(
+            AIBinder_DeathRecipient_new(binderDiedCallbackAidl));
+    }
+
+    virtual ~ConfuiAidlCompatSession() = default;
+    ConfuiAidlCompatSession(const ConfuiAidlCompatSession&) = delete;
+    ConfuiAidlCompatSession& operator=(const ConfuiAidlCompatSession&) = delete;
+
+  private:
+    std::shared_ptr<AidlConfirmationUI> aidlService_;
+
+    // The callback_lock_ protects the callback_ field against concurrent modification.
+    // IMPORTANT: It must never be held while calling the call back.
+    std::mutex callback_lock_;
+    ApcCompatCallback callback_;
+
+    ::ndk::ScopedAIBinder_DeathRecipient death_recipient_;
+};
+
+class ApcCompatSession {
+  public:
+    static ApcCompatServiceHandle getApcCompatSession() {
+        auto aidlCompatSession = ConfuiAidlCompatSession::tryGetService();
+        if (aidlCompatSession) {
+            return new ApcCompatSession(std::move(aidlCompatSession), nullptr);
+        }
+
+        sp<ConfuiHidlCompatSession> hidlCompatSession = ConfuiHidlCompatSession::tryGetService();
+        if (hidlCompatSession) {
+            return new ApcCompatSession(nullptr, std::move(hidlCompatSession));
+        }
+
+        LOG(ERROR) << "ConfirmationUI: Not found Service";
+        return nullptr;
+    }
+
+    uint32_t promptUserConfirmation(ApcCompatCallback callback, const char* prompt_text,
+                                    const uint8_t* extra_data, size_t extra_data_size,
+                                    char const* locale, ApcCompatUiOptions ui_options) {
+        if (aidlCompatSession_) {
+            return aidlCompatSession_->promptUserConfirmation(callback, prompt_text, extra_data,
+                                                              extra_data_size, locale, ui_options);
+        } else {
+            return hidlCompatSession_->promptUserConfirmation(callback, prompt_text, extra_data,
+                                                              extra_data_size, locale, ui_options);
+        }
+    }
+
+    void abortUserConfirmation() {
+        if (aidlCompatSession_) {
+            return aidlCompatSession_->abort();
+        } else {
+            return hidlCompatSession_->abort();
+        }
+    }
+
+    void closeUserConfirmationService() {
+        // Closing the handle implicitly aborts an ongoing sessions.
+        // Note that a resulting callback is still safely conducted, because we only delete a
+        // StrongPointer below. libhwbinder still owns another StrongPointer to this session.
+        abortUserConfirmation();
+    }
+
+    ApcCompatSession(std::shared_ptr<ConfuiAidlCompatSession> aidlCompatSession,
+                     sp<ConfuiHidlCompatSession> hidlCompatSession)
+        : aidlCompatSession_(aidlCompatSession), hidlCompatSession_(hidlCompatSession) {}
+
+  private:
+    std::shared_ptr<ConfuiAidlCompatSession> aidlCompatSession_;
+    sp<ConfuiHidlCompatSession> hidlCompatSession_;
+};
 }  // namespace keystore2
 
 using namespace keystore2;
 
 ApcCompatServiceHandle tryGetUserConfirmationService() {
-    return reinterpret_cast<ApcCompatServiceHandle>(ConfuiCompatSession::tryGetService());
+    return reinterpret_cast<ApcCompatServiceHandle>(ApcCompatSession::getApcCompatSession());
 }
 
 uint32_t promptUserConfirmation(ApcCompatServiceHandle handle, ApcCompatCallback callback,
                                 const char* prompt_text, const uint8_t* extra_data,
                                 size_t extra_data_size, char const* locale,
                                 ApcCompatUiOptions ui_options) {
-    auto session = reinterpret_cast<sp<ConfuiCompatSession>*>(handle);
-    return (*session)->promptUserConfirmation(callback, prompt_text, extra_data, extra_data_size,
-                                              locale, ui_options);
+    auto session = reinterpret_cast<ApcCompatSession*>(handle);
+    return session->promptUserConfirmation(callback, prompt_text, extra_data, extra_data_size,
+                                           locale, ui_options);
 }
 
 void abortUserConfirmation(ApcCompatServiceHandle handle) {
-    auto session = reinterpret_cast<sp<ConfuiCompatSession>*>(handle);
-    (*session)->abort();
+    auto session = reinterpret_cast<ApcCompatSession*>(handle);
+    session->abortUserConfirmation();
 }
 
 void closeUserConfirmationService(ApcCompatServiceHandle handle) {
-    // Closing the handle implicitly aborts an ongoing sessions.
-    // Note that a resulting callback is still safely conducted, because we only delete a
-    // StrongPointer below. libhwbinder still owns another StrongPointer to this session.
-    abortUserConfirmation(handle);
-    delete reinterpret_cast<sp<ConfuiCompatSession>*>(handle);
+    auto session = reinterpret_cast<ApcCompatSession*>(handle);
+    session->closeUserConfirmationService();
+    delete reinterpret_cast<ApcCompatSession*>(handle);
 }
 
 const ApcCompatServiceHandle INVALID_SERVICE_HANDLE = nullptr;
diff --git a/keystore2/apc_compat/apc_compat.rs b/keystore2/apc_compat/apc_compat.rs
index 57f8710..480f14d 100644
--- a/keystore2/apc_compat/apc_compat.rs
+++ b/keystore2/apc_compat/apc_compat.rs
@@ -19,7 +19,7 @@
 //! client.
 
 use keystore2_apc_compat_bindgen::{
-    abortUserConfirmation, closeUserConfirmationService, promptUserConfirmation, size_t,
+    abortUserConfirmation, closeUserConfirmationService, promptUserConfirmation,
     tryGetUserConfirmationService, ApcCompatCallback, ApcCompatServiceHandle,
 };
 pub use keystore2_apc_compat_bindgen::{
@@ -76,9 +76,9 @@
     handle: *mut ::std::os::raw::c_void,
     rc: u32,
     tbs_message: *const u8,
-    tbs_message_size: size_t,
+    tbs_message_size: usize,
     confirmation_token: *const u8,
-    confirmation_token_size: size_t,
+    confirmation_token_size: usize,
 ) {
     // # Safety:
     // The C/C++ implementation must pass to us the handle that was created
@@ -94,7 +94,7 @@
             // If the pointer and size is not nullptr and not 0 respectively, the C/C++
             // implementation must pass a valid pointer to an allocation of at least size bytes,
             // and the pointer must be valid until this function returns.
-            unsafe { slice::from_raw_parts(tbs_message, s as usize) },
+            unsafe { slice::from_raw_parts(tbs_message, s) },
         ),
     };
     let confirmation_token = match (confirmation_token.is_null(), confirmation_token_size) {
@@ -104,7 +104,7 @@
             // If the pointer and size is not nullptr and not 0 respectively, the C/C++
             // implementation must pass a valid pointer to an allocation of at least size bytes,
             // and the pointer must be valid until this function returns.
-            unsafe { slice::from_raw_parts(confirmation_token, s as usize) },
+            unsafe { slice::from_raw_parts(confirmation_token, s) },
         ),
     };
     hal_cb(rc, tbs_message, confirmation_token)
@@ -178,7 +178,7 @@
                 cb,
                 prompt_text.as_ptr(),
                 extra_data.as_ptr(),
-                extra_data.len() as size_t,
+                extra_data.len(),
                 locale.as_ptr(),
                 ui_opts,
             )
diff --git a/keystore2/legacykeystore/lib.rs b/keystore2/legacykeystore/lib.rs
index e2d952d..464f0a2 100644
--- a/keystore2/legacykeystore/lib.rs
+++ b/keystore2/legacykeystore/lib.rs
@@ -108,6 +108,12 @@
                 .prepare("SELECT alias FROM profiles WHERE owner = ? ORDER BY alias ASC;")
                 .context("In list: Failed to prepare statement.")?;
 
+            // This allow is necessary to avoid the following error:
+            //
+            // error[E0597]: `stmt` does not live long enough
+            //
+            // See: https://github.com/rust-lang/rust-clippy/issues/8114
+            #[allow(clippy::let_and_return)]
             let aliases = stmt
                 .query_map(params![caller_uid], |row| row.get(0))?
                 .collect::<rusqlite::Result<Vec<String>>>()
@@ -172,7 +178,7 @@
 
 /// This is the main LegacyKeystore error type, it wraps binder exceptions and the
 /// LegacyKeystore errors.
-#[derive(Debug, thiserror::Error, PartialEq)]
+#[derive(Debug, thiserror::Error, PartialEq, Eq)]
 pub enum Error {
     /// Wraps a LegacyKeystore error code.
     #[error("Error::Error({0:?})")]
@@ -387,7 +393,7 @@
         let uid = Self::get_effective_uid(uid).context("In list.")?;
         let mut result = self.list_legacy(uid).context("In list.")?;
         result.append(&mut db.list(uid).context("In list: Trying to get list of entries.")?);
-        result = result.into_iter().filter(|s| s.starts_with(prefix)).collect();
+        result.retain(|s| s.starts_with(prefix));
         result.sort_unstable();
         result.dedup();
         Ok(result)
@@ -496,10 +502,8 @@
     ) -> Result<bool> {
         let blob = legacy_loader
             .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))
+                if let Some(key) =
+                    SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(uid))
                 {
                     key.decrypt(ciphertext, iv, tag)
                 } else {
diff --git a/keystore2/rustfmt.toml b/keystore2/rustfmt.toml
new file mode 100644
index 0000000..4335d66
--- /dev/null
+++ b/keystore2/rustfmt.toml
@@ -0,0 +1,5 @@
+# Android Format Style
+
+edition = "2021"
+use_small_heuristics = "Max"
+newline_style = "Unix"
\ No newline at end of file
diff --git a/keystore2/selinux/src/lib.rs b/keystore2/selinux/src/lib.rs
index c0593b7..e5c3091 100644
--- a/keystore2/selinux/src/lib.rs
+++ b/keystore2/selinux/src/lib.rs
@@ -65,7 +65,7 @@
 }
 
 /// Selinux Error code.
-#[derive(thiserror::Error, Debug, PartialEq)]
+#[derive(thiserror::Error, Debug, PartialEq, Eq)]
 pub enum Error {
     /// Indicates that an access check yielded no access.
     #[error("Permission Denied")]
diff --git a/keystore2/src/apc.rs b/keystore2/src/apc.rs
index 7d56dc9..5d2083d 100644
--- a/keystore2/src/apc.rs
+++ b/keystore2/src/apc.rs
@@ -22,6 +22,7 @@
 };
 
 use crate::error::anyhow_error_to_cstring;
+use crate::ks_err;
 use crate::utils::{compat_2_response_code, ui_opts_2_compat, watchdog as wd};
 use android_security_apc::aidl::android::security::apc::{
     IConfirmationCallback::IConfirmationCallback,
@@ -39,7 +40,7 @@
 
 /// This is the main APC error type, it wraps binder exceptions and the
 /// APC ResponseCode.
-#[derive(Debug, thiserror::Error, PartialEq)]
+#[derive(Debug, thiserror::Error, PartialEq, Eq)]
 pub enum Error {
     /// Wraps an Android Protected Confirmation (APC) response code as defined by the
     /// android.security.apc AIDL interface specification.
@@ -259,13 +260,10 @@
 
         if let Ok(listener) = callback.into_interface::<dyn IConfirmationCallback>() {
             if let Err(e) = listener.onCompleted(rc, data_confirmed) {
-                log::error!(
-                    "In ApcManagerCallback::result: Reporting completion to client failed {:?}",
-                    e
-                )
+                log::error!("Reporting completion to client failed {:?}", e)
             }
         } else {
-            log::error!("In ApcManagerCallback::result: SpIBinder is not a IConfirmationCallback.");
+            log::error!("SpIBinder is not a IConfirmationCallback.");
         }
     }
 
@@ -279,8 +277,7 @@
     ) -> Result<()> {
         let mut state = self.state.lock().unwrap();
         if state.session.is_some() {
-            return Err(Error::pending())
-                .context("In ApcManager::present_prompt: Session pending.");
+            return Err(Error::pending()).context(ks_err!("APC Session pending."));
         }
 
         // Perform rate limiting.
@@ -289,8 +286,8 @@
             None => {}
             Some(rate_info) => {
                 if let Some(back_off) = rate_info.get_remaining_back_off() {
-                    return Err(Error::sys()).context(format!(
-                        "In ApcManager::present_prompt: Cooling down. Remaining back-off: {}s",
+                    return Err(Error::sys()).context(ks_err!(
+                        "APC Cooling down. Remaining back-off: {}s",
                         back_off.as_secs()
                     ));
                 }
@@ -300,8 +297,7 @@
         let hal = ApcHal::try_get_service();
         let hal = match hal {
             None => {
-                return Err(Error::unimplemented())
-                    .context("In ApcManager::present_prompt: APC not supported.")
+                return Err(Error::unimplemented()).context(ks_err!("APC not supported."));
             }
             Some(h) => Arc::new(h),
         };
@@ -319,7 +315,7 @@
             },
         )
         .map_err(|rc| Error::Rc(compat_2_response_code(rc)))
-        .context("In present_prompt: Failed to present prompt.")?;
+        .context(ks_err!("APC Failed to present prompt."))?;
         state.session = Some(ApcSessionState {
             hal,
             cb: listener.as_binder(),
@@ -335,13 +331,12 @@
         let hal = match &mut state.session {
             None => {
                 return Err(Error::ignored())
-                    .context("In cancel_prompt: Attempt to cancel non existing session. Ignoring.")
+                    .context(ks_err!("Attempt to cancel non existing session. Ignoring."));
             }
             Some(session) => {
                 if session.cb != listener.as_binder() {
-                    return Err(Error::ignored()).context(concat!(
-                        "In cancel_prompt: Attempt to cancel session not belonging to caller. ",
-                        "Ignoring."
+                    return Err(Error::ignored()).context(ks_err!(
+                        "Attempt to cancel session not belonging to caller. Ignoring."
                     ));
                 }
                 session.client_aborted = true;
diff --git a/keystore2/src/async_task.rs b/keystore2/src/async_task.rs
index 0515c8f..6548445 100644
--- a/keystore2/src/async_task.rs
+++ b/keystore2/src/async_task.rs
@@ -67,7 +67,7 @@
     pub fn get_mut<T: Any + Send + Default>(&mut self) -> &mut T {
         self.0
             .entry(TypeId::of::<T>())
-            .or_insert_with(|| Box::new(T::default()) as Box<dyn Any + Send>)
+            .or_insert_with(|| Box::<T>::default() as Box<dyn Any + Send>)
             .downcast_mut::<T>()
             .unwrap()
     }
diff --git a/keystore2/src/attestation_key_utils.rs b/keystore2/src/attestation_key_utils.rs
index 3408942..184b3cb 100644
--- a/keystore2/src/attestation_key_utils.rs
+++ b/keystore2/src/attestation_key_utils.rs
@@ -18,6 +18,7 @@
 use crate::database::{BlobMetaData, KeyEntryLoadBits, KeyType};
 use crate::database::{KeyIdGuard, KeystoreDB};
 use crate::error::{Error, ErrorCode};
+use crate::ks_err;
 use crate::permission::KeyPerm;
 use crate::remote_provisioning::RemProvState;
 use crate::utils::check_key_permission;
@@ -25,7 +26,7 @@
     AttestationKey::AttestationKey, Certificate::Certificate, KeyParameter::KeyParameter, Tag::Tag,
 };
 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;
@@ -34,8 +35,7 @@
 /// and those that have been generated by the user. Unfortunately, they need to be
 /// handled quite differently, thus the different representations.
 pub enum AttestationKeyInfo {
-    RemoteProvisioned {
-        key_id_guard: KeyIdGuard,
+    RkpdProvisioned {
         attestation_key: AttestationKey,
         attestation_certs: Certificate,
     },
@@ -64,23 +64,16 @@
     match attest_key_descriptor {
         // Do not select an RKP key if DEVICE_UNIQUE_ATTESTATION is present.
         None if challenge_present && !is_device_unique_attestation => rem_prov_state
-            .get_remotely_provisioned_attestation_key_and_certs(key, caller_uid, params, db)
-            .context(concat!(
-                "In get_attest_key_and_cert_chain: ",
-                "Trying to get remotely provisioned attestation key."
-            ))
+            .get_rkpd_attestation_key_and_certs(key, caller_uid, params)
+            .context(ks_err!("Trying to get attestation key from RKPD."))
             .map(|result| {
-                result.map(|(key_id_guard, attestation_key, attestation_certs)| {
-                    AttestationKeyInfo::RemoteProvisioned {
-                        key_id_guard,
-                        attestation_key,
-                        attestation_certs,
-                    }
+                result.map(|(attestation_key, attestation_certs)| {
+                    AttestationKeyInfo::RkpdProvisioned { attestation_key, attestation_certs }
                 })
             }),
         None => Ok(None),
         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")
+            .context(ks_err!("Trying to load attest key"))
             .map(Some),
     }
 }
@@ -92,11 +85,10 @@
 ) -> Result<AttestationKeyInfo> {
     let (key_id_guard, blob, cert, blob_metadata) =
         load_attest_key_blob_and_cert(key, caller_uid, db)
-            .context("In get_user_generated_attestation_key: Failed to load blob and cert")?;
+            .context(ks_err!("Failed to load blob and cert"))?;
 
-    let issuer_subject: Vec<u8> = parse_subject_from_certificate(&cert).context(
-        "In get_user_generated_attestation_key: Failed to parse subject from certificate.",
-    )?;
+    let issuer_subject: Vec<u8> = parse_subject_from_certificate(&cert)
+        .context(ks_err!("Failed to parse subject from certificate"))?;
 
     Ok(AttestationKeyInfo::UserGenerated { key_id_guard, blob, issuer_subject, blob_metadata })
 }
@@ -107,9 +99,8 @@
     db: &mut KeystoreDB,
 ) -> Result<(KeyIdGuard, Vec<u8>, Vec<u8>, BlobMetaData)> {
     match key.domain {
-        Domain::BLOB => Err(Error::Km(ErrorCode::INVALID_ARGUMENT)).context(
-            "In load_attest_key_blob_and_cert: Domain::BLOB attestation keys not supported",
-        ),
+        Domain::BLOB => Err(Error::Km(ErrorCode::INVALID_ARGUMENT))
+            .context(ks_err!("Domain::BLOB attestation keys not supported")),
         _ => {
             let (key_id_guard, mut key_entry) = db
                 .load_key_entry(
@@ -119,17 +110,16 @@
                     caller_uid,
                     |k, av| check_key_permission(KeyPerm::Use, k, &av),
                 )
-                .context("In load_attest_key_blob_and_cert: Failed to load key.")?;
+                .context(ks_err!("Failed to load key."))?;
 
-            let (blob, blob_metadata) =
-                key_entry.take_key_blob_info().ok_or_else(Error::sys).context(concat!(
-                    "In load_attest_key_blob_and_cert: Successfully loaded key entry,",
-                    " but KM blob was missing."
-                ))?;
-            let cert = key_entry.take_cert().ok_or_else(Error::sys).context(concat!(
-                "In load_attest_key_blob_and_cert: Successfully loaded key entry,",
-                " but cert was missing."
-            ))?;
+            let (blob, blob_metadata) = key_entry
+                .take_key_blob_info()
+                .ok_or(Error::Rc(ResponseCode::INVALID_ARGUMENT))
+                .context(ks_err!("Successfully loaded key entry, but KM blob was missing"))?;
+            let cert = key_entry
+                .take_cert()
+                .ok_or(Error::Rc(ResponseCode::INVALID_ARGUMENT))
+                .context(ks_err!("Successfully loaded key entry, but cert was missing"))?;
             Ok((key_id_guard, blob, cert, blob_metadata))
         }
     }
diff --git a/keystore2/src/audit_log.rs b/keystore2/src/audit_log.rs
index 3d7d26e..07509d3 100644
--- a/keystore2/src/audit_log.rs
+++ b/keystore2/src/audit_log.rs
@@ -67,7 +67,7 @@
 fn log_key_event(tag: u32, key: &KeyDescriptor, calling_app: uid_t, success: bool) {
     with_log_context(tag, |ctx| {
         let owner = key_owner(key.domain, key.nspace, calling_app as i32);
-        ctx.append_i32(if success { 1 } else { 0 })
+        ctx.append_i32(i32::from(success))
             .append_str(key.alias.as_ref().map_or("none", String::as_str))
             .append_i32(owner)
     })
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index 8265dd0..1953920 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -14,6 +14,7 @@
 
 //! This module implements IKeystoreAuthorization AIDL interface.
 
+use crate::ks_err;
 use crate::error::Error as KeystoreError;
 use crate::error::anyhow_error_to_cstring;
 use crate::globals::{ENFORCEMENTS, SUPER_KEY, DB, LEGACY_IMPORTER};
@@ -23,22 +24,22 @@
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     HardwareAuthToken::HardwareAuthToken,
 };
-use android_security_authorization::binder::{BinderFeatures,ExceptionCode, Interface, Result as BinderResult,
-     Strong, Status as BinderStatus};
+use android_security_authorization::binder::{BinderFeatures, ExceptionCode, Interface, Result as BinderResult,
+    Strong, Status as BinderStatus};
 use android_security_authorization::aidl::android::security::authorization::{
     IKeystoreAuthorization::BnKeystoreAuthorization, IKeystoreAuthorization::IKeystoreAuthorization,
     LockScreenEvent::LockScreenEvent, AuthorizationTokens::AuthorizationTokens,
     ResponseCode::ResponseCode,
 };
 use android_system_keystore2::aidl::android::system::keystore2::{
-    ResponseCode::ResponseCode as KsResponseCode };
+    ResponseCode::ResponseCode as KsResponseCode};
 use anyhow::{Context, Result};
 use keystore2_crypto::Password;
 use keystore2_selinux as selinux;
 
 /// This is the Authorization error type, it wraps binder exceptions and the
 /// Authorization ResponseCode
-#[derive(Debug, thiserror::Error, PartialEq)]
+#[derive(Debug, thiserror::Error, PartialEq, Eq)]
 pub enum Error {
     /// Wraps an IKeystoreAuthorization response code as defined by
     /// android.security.authorization AIDL interface specification.
@@ -126,7 +127,7 @@
 
     fn add_auth_token(&self, auth_token: &HardwareAuthToken) -> Result<()> {
         // Check keystore permission.
-        check_keystore_permission(KeystorePerm::AddAuth).context("In add_auth_token.")?;
+        check_keystore_permission(KeystorePerm::AddAuth).context(ks_err!())?;
 
         ENFORCEMENTS.add_auth_token(auth_token.clone());
         Ok(())
@@ -151,7 +152,7 @@
                 // This corresponds to the unlock() method in legacy keystore API.
                 // check permission
                 check_keystore_permission(KeystorePerm::Unlock)
-                    .context("In on_lock_screen_event: Unlock with password.")?;
+                    .context(ks_err!("Unlock with password."))?;
                 ENFORCEMENTS.set_device_locked(user_id, false);
 
                 let mut skm = SUPER_KEY.write().unwrap();
@@ -163,7 +164,7 @@
                         &password,
                     )
                 })
-                .context("In on_lock_screen_event: unlock_screen_lock_bound_key failed")?;
+                .context(ks_err!("unlock_screen_lock_bound_key failed"))?;
 
                 // Unlock super key.
                 if let UserState::Uninitialized = DB
@@ -175,7 +176,7 @@
                             &password,
                         )
                     })
-                    .context("In on_lock_screen_event: Unlock with password.")?
+                    .context(ks_err!("Unlock with password."))?
                 {
                     log::info!(
                         "In on_lock_screen_event. Trying to unlock when LSKF is uninitialized."
@@ -185,19 +186,17 @@
                 Ok(())
             }
             (LockScreenEvent::UNLOCK, None) => {
-                check_keystore_permission(KeystorePerm::Unlock)
-                    .context("In on_lock_screen_event: Unlock.")?;
+                check_keystore_permission(KeystorePerm::Unlock).context(ks_err!("Unlock."))?;
                 ENFORCEMENTS.set_device_locked(user_id, false);
                 let mut skm = SUPER_KEY.write().unwrap();
                 DB.with(|db| {
                     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")?;
+                .context(ks_err!("try_unlock_user_with_biometric failed"))?;
                 Ok(())
             }
             (LockScreenEvent::LOCK, None) => {
-                check_keystore_permission(KeystorePerm::Lock)
-                    .context("In on_lock_screen_event: Lock")?;
+                check_keystore_permission(KeystorePerm::Lock).context(ks_err!("Lock"))?;
                 ENFORCEMENTS.set_device_locked(user_id, true);
                 let mut skm = SUPER_KEY.write().unwrap();
                 DB.with(|db| {
@@ -211,8 +210,7 @@
             }
             _ => {
                 // Any other combination is not supported.
-                Err(Error::Rc(ResponseCode::INVALID_ARGUMENT))
-                    .context("In on_lock_screen_event: Unknown event.")
+                Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(ks_err!("Unknown event."))
             }
         }
     }
@@ -225,13 +223,12 @@
     ) -> Result<AuthorizationTokens> {
         // Check permission. Function should return if this failed. Therefore having '?' at the end
         // is very important.
-        check_keystore_permission(KeystorePerm::GetAuthToken)
-            .context("In get_auth_tokens_for_credstore.")?;
+        check_keystore_permission(KeystorePerm::GetAuthToken).context(ks_err!("GetAuthToken"))?;
 
         // If the challenge is zero, return error
         if challenge == 0 {
             return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT))
-                .context("In get_auth_tokens_for_credstore. Challenge can not be zero.");
+                .context(ks_err!("Challenge can not be zero."));
         }
         // Obtain the auth token and the timestamp token from the enforcement module.
         let (auth_token, ts_token) =
diff --git a/keystore2/src/boot_level_keys.rs b/keystore2/src/boot_level_keys.rs
index 08c52af..e2e67ff 100644
--- a/keystore2/src/boot_level_keys.rs
+++ b/keystore2/src/boot_level_keys.rs
@@ -14,6 +14,7 @@
 
 //! Offer keys based on the "boot level" for superencryption.
 
+use crate::ks_err;
 use crate::{
     database::{KeyType, KeystoreDB},
     key_parameter::KeyParameterValue,
@@ -21,26 +22,86 @@
 };
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Algorithm::Algorithm, Digest::Digest, KeyParameter::KeyParameter as KmKeyParameter,
-    KeyParameterValue::KeyParameterValue as KmKeyParameterValue, KeyPurpose::KeyPurpose,
-    SecurityLevel::SecurityLevel, Tag::Tag,
+    KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
 };
 use anyhow::{Context, Result};
 use keystore2_crypto::{hkdf_expand, ZVec, AES_256_KEY_LENGTH};
 use std::{collections::VecDeque, convert::TryFrom};
 
-fn get_preferred_km_instance_for_level_zero_key() -> Result<KeyMintDevice> {
-    let tee = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
-        .context("In get_preferred_km_instance_for_level_zero_key: Get TEE instance failed.")?;
-    if tee.version() >= KeyMintDevice::KEY_MASTER_V4_1 {
-        Ok(tee)
+/// Strategies used to prevent later boot stages from using the KM key that protects the level 0
+/// key
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+enum DenyLaterStrategy {
+    /// set MaxUsesPerBoot to 1. This is much less secure, since the attacker can replace the key
+    /// itself, and therefore create artifacts which appear to come from early boot.
+    MaxUsesPerBoot,
+    /// set the EarlyBootOnly property. This property is only supported in KM from 4.1 on, but
+    /// it ensures that the level 0 key was genuinely created in early boot
+    EarlyBootOnly,
+}
+
+/// Generally the L0 KM and strategy are chosen by probing KM versions in TEE and Strongbox.
+/// However, once a device is launched the KM and strategy must never change, even if the
+/// KM version in TEE or Strongbox is updated. Setting this property at build time using
+/// `PRODUCT_VENDOR_PROPERTIES` means that the strategy can be fixed no matter what versions
+/// of KM are present.
+const PROPERTY_NAME: &str = "ro.keystore.boot_level_key.strategy";
+
+fn lookup_level_zero_km_and_strategy() -> Result<Option<(SecurityLevel, DenyLaterStrategy)>> {
+    let property_val = rustutils::system_properties::read(PROPERTY_NAME)
+        .with_context(|| ks_err!("property read failed: {}", PROPERTY_NAME))?;
+    // TODO: use feature(let_else) when that's stabilized.
+    let property_val = if let Some(p) = property_val {
+        p
     } else {
-        match KeyMintDevice::get_or_none(SecurityLevel::STRONGBOX).context(
-            "In get_preferred_km_instance_for_level_zero_key: Get Strongbox instance failed.",
-        )? {
+        log::info!("{} not set, inferring from installed KM instances", PROPERTY_NAME);
+        return Ok(None);
+    };
+    let (level, strategy) = if let Some(c) = property_val.split_once(':') {
+        c
+    } else {
+        log::error!("Missing colon in {}: {:?}", PROPERTY_NAME, property_val);
+        return Ok(None);
+    };
+    let level = match level {
+        "TRUSTED_ENVIRONMENT" => SecurityLevel::TRUSTED_ENVIRONMENT,
+        "STRONGBOX" => SecurityLevel::STRONGBOX,
+        _ => {
+            log::error!("Unknown security level in {}: {:?}", PROPERTY_NAME, level);
+            return Ok(None);
+        }
+    };
+    let strategy = match strategy {
+        "EARLY_BOOT_ONLY" => DenyLaterStrategy::EarlyBootOnly,
+        "MAX_USES_PER_BOOT" => DenyLaterStrategy::MaxUsesPerBoot,
+        _ => {
+            log::error!("Unknown DenyLaterStrategy in {}: {:?}", PROPERTY_NAME, strategy);
+            return Ok(None);
+        }
+    };
+    log::info!("Set from {}: {}", PROPERTY_NAME, property_val);
+    Ok(Some((level, strategy)))
+}
+
+fn get_level_zero_key_km_and_strategy() -> Result<(KeyMintDevice, DenyLaterStrategy)> {
+    if let Some((level, strategy)) = lookup_level_zero_km_and_strategy()? {
+        return Ok((
+            KeyMintDevice::get(level).context(ks_err!("Get KM instance failed."))?,
+            strategy,
+        ));
+    }
+    let tee = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
+        .context(ks_err!("Get TEE instance failed."))?;
+    if tee.version() >= KeyMintDevice::KEY_MASTER_V4_1 {
+        Ok((tee, DenyLaterStrategy::EarlyBootOnly))
+    } else {
+        match KeyMintDevice::get_or_none(SecurityLevel::STRONGBOX)
+            .context(ks_err!("Get Strongbox instance failed."))?
+        {
             Some(strongbox) if strongbox.version() >= KeyMintDevice::KEY_MASTER_V4_1 => {
-                Ok(strongbox)
+                Ok((strongbox, DenyLaterStrategy::EarlyBootOnly))
             }
-            _ => Ok(tee),
+            _ => Ok((tee, DenyLaterStrategy::MaxUsesPerBoot)),
         }
     }
 }
@@ -49,55 +110,52 @@
 /// In practice the caller is SuperKeyManager and the lock is the
 /// Mutex on its internal state.
 pub fn get_level_zero_key(db: &mut KeystoreDB) -> Result<ZVec> {
-    let km_dev = get_preferred_km_instance_for_level_zero_key()
-        .context("In get_level_zero_key: get preferred KM instance failed")?;
-
-    let key_desc = KeyMintDevice::internal_descriptor("boot_level_key".to_string());
-    let mut params = vec![
+    let (km_dev, deny_later_strategy) = get_level_zero_key_km_and_strategy()
+        .context(ks_err!("get preferred KM instance failed"))?;
+    log::info!(
+        "In get_level_zero_key: security_level={:?}, deny_later_strategy={:?}",
+        km_dev.security_level(),
+        deny_later_strategy
+    );
+    let required_security_level = km_dev.security_level();
+    let required_param: KmKeyParameter = match deny_later_strategy {
+        DenyLaterStrategy::EarlyBootOnly => KeyParameterValue::EarlyBootOnly,
+        DenyLaterStrategy::MaxUsesPerBoot => KeyParameterValue::MaxUsesPerBoot(1),
+    }
+    .into();
+    let params = vec![
         KeyParameterValue::Algorithm(Algorithm::HMAC).into(),
         KeyParameterValue::Digest(Digest::SHA_2_256).into(),
         KeyParameterValue::KeySize(256).into(),
         KeyParameterValue::MinMacLength(256).into(),
         KeyParameterValue::KeyPurpose(KeyPurpose::SIGN).into(),
         KeyParameterValue::NoAuthRequired.into(),
+        required_param.clone(),
     ];
 
-    let has_early_boot_only = km_dev.version() >= KeyMintDevice::KEY_MASTER_V4_1;
-
-    if has_early_boot_only {
-        params.push(KeyParameterValue::EarlyBootOnly.into());
-    } else {
-        params.push(KeyParameterValue::MaxUsesPerBoot(1).into())
-    }
-
+    let key_desc = KeyMintDevice::internal_descriptor("boot_level_key".to_string());
     let (key_id_guard, key_entry) = km_dev
         .lookup_or_generate_key(db, &key_desc, KeyType::Client, &params, |key_characteristics| {
             key_characteristics.iter().any(|kc| {
-                if kc.securityLevel == km_dev.security_level() {
-                    kc.authorizations.iter().any(|a| {
-                        matches!(
-                            (has_early_boot_only, a),
-                            (
-                                true,
-                                KmKeyParameter {
-                                    tag: Tag::EARLY_BOOT_ONLY,
-                                    value: KmKeyParameterValue::BoolValue(true)
-                                }
-                            ) | (
-                                false,
-                                KmKeyParameter {
-                                    tag: Tag::MAX_USES_PER_BOOT,
-                                    value: KmKeyParameterValue::Integer(1)
-                                }
-                            )
-                        )
-                    })
-                } else {
-                    false
+                if kc.securityLevel != required_security_level {
+                    log::error!(
+                        "In get_level_zero_key: security level expected={:?} got={:?}",
+                        required_security_level,
+                        kc.securityLevel
+                    );
+                    return false;
                 }
+                if !kc.authorizations.iter().any(|a| a == &required_param) {
+                    log::error!(
+                        "In get_level_zero_key: required param absent {:?}",
+                        required_param
+                    );
+                    return false;
+                }
+                true
             })
         })
-        .context("In get_level_zero_key: lookup_or_generate_key failed")?;
+        .context(ks_err!("lookup_or_generate_key failed"))?;
 
     let params = [
         KeyParameterValue::MacLength(256).into(),
@@ -113,11 +171,11 @@
             None,
             b"Create boot level key",
         )
-        .context("In get_level_zero_key: use_key_in_one_step failed")?;
+        .context(ks_err!("use_key_in_one_step failed"))?;
     // TODO: this is rather unsatisfactory, we need a better way to handle
     // sensitive binder returns.
-    let level_zero_key = ZVec::try_from(level_zero_key)
-        .context("In get_level_zero_key: conversion to ZVec failed")?;
+    let level_zero_key =
+        ZVec::try_from(level_zero_key).context(ks_err!("conversion to ZVec failed"))?;
     Ok(level_zero_key)
 }
 
@@ -169,7 +227,7 @@
             // so this must unwrap.
             let highest_key = self.cache.back().unwrap();
             let next_key = hkdf_expand(Self::HKDF_KEY_SIZE, highest_key, Self::HKDF_ADVANCE)
-                .context("In BootLevelKeyCache::get_hkdf_key: Advancing key one step")?;
+                .context(ks_err!("Advancing key one step"))?;
             self.cache.push_back(next_key);
         }
 
@@ -182,10 +240,7 @@
     pub fn advance_boot_level(&mut self, new_boot_level: usize) -> Result<()> {
         if !self.level_accessible(new_boot_level) {
             log::error!(
-                concat!(
-                    "In BootLevelKeyCache::advance_boot_level: ",
-                    "Failed to advance boot level to {}, current is {}, cache size {}"
-                ),
+                "Failed to advance boot level to {}, current is {}, cache size {}",
                 new_boot_level,
                 self.current,
                 self.cache.len()
@@ -195,8 +250,7 @@
 
         // We `get` the new boot level for the side effect of advancing the cache to a point
         // where the new boot level is present.
-        self.get_hkdf_key(new_boot_level)
-            .context("In BootLevelKeyCache::advance_boot_level: Advancing cache")?;
+        self.get_hkdf_key(new_boot_level).context(ks_err!("Advancing cache"))?;
 
         // Then we split the queue at the index of the new boot level and discard the front,
         // keeping only the keys with the current boot level or higher.
@@ -222,16 +276,16 @@
         info: &[u8],
     ) -> Result<Option<ZVec>> {
         self.get_hkdf_key(boot_level)
-            .context("In BootLevelKeyCache::expand_key: Looking up HKDF key")?
+            .context(ks_err!("Looking up HKDF key"))?
             .map(|k| hkdf_expand(out_len, k, info))
             .transpose()
-            .context("In BootLevelKeyCache::expand_key: Calling hkdf_expand")
+            .context(ks_err!("Calling hkdf_expand"))
     }
 
     /// Return the AES-256-GCM key for the current boot level.
     pub fn aes_key(&mut self, boot_level: usize) -> Result<Option<ZVec>> {
         self.expand_key(boot_level, AES_256_KEY_LENGTH, BootLevelKeyCache::HKDF_AES)
-            .context("In BootLevelKeyCache::aes_key: expand_key failed")
+            .context(ks_err!("expand_key failed"))
     }
 }
 
diff --git a/keystore2/src/crypto/Android.bp b/keystore2/src/crypto/Android.bp
index c3f6f3c..1ac6467 100644
--- a/keystore2/src/crypto/Android.bp
+++ b/keystore2/src/crypto/Android.bp
@@ -36,6 +36,10 @@
         "libcrypto",
     ],
     vendor_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.virt",
+    ],
 }
 
 cc_library {
@@ -50,6 +54,10 @@
         "liblog",
     ],
     vendor_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.virt",
+    ],
 }
 
 rust_bindgen {
@@ -86,6 +94,10 @@
         "--allowlist-var", "EVP_MAX_MD_SIZE",
     ],
     cflags: ["-DBORINGSSL_NO_CXX"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.virt",
+    ],
 }
 
 rust_test {
@@ -138,4 +150,8 @@
     auto_gen_config: true,
     clippy_lints: "none",
     lints: "none",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.virt",
+    ],
 }
diff --git a/keystore2/src/crypto/crypto.cpp b/keystore2/src/crypto/crypto.cpp
index 34a9a40..7feeaff 100644
--- a/keystore2/src/crypto/crypto.cpp
+++ b/keystore2/src/crypto/crypto.cpp
@@ -18,6 +18,7 @@
 
 #include "crypto.hpp"
 
+#include <assert.h>
 #include <log/log.h>
 #include <openssl/aes.h>
 #include <openssl/ec.h>
@@ -192,16 +193,6 @@
 
 void generateKeyFromPassword(uint8_t* key, size_t key_len, const char* pw, size_t pw_len,
                              const uint8_t* salt) {
-    size_t saltSize;
-    if (salt != nullptr) {
-        saltSize = SALT_SIZE;
-    } else {
-        // Pre-gingerbread used this hardwired salt, readMasterKey will rewrite these when found
-        salt = reinterpret_cast<const uint8_t*>("keystore");
-        // sizeof = 9, not strlen = 8
-        saltSize = sizeof("keystore");
-    }
-
     const EVP_MD* digest = EVP_sha256();
 
     // SHA1 was used prior to increasing the key size
@@ -209,7 +200,7 @@
         digest = EVP_sha1();
     }
 
-    PKCS5_PBKDF2_HMAC(pw, pw_len, salt, saltSize, 8192, digest, key_len, key);
+    PKCS5_PBKDF2_HMAC(pw, pw_len, salt, SALT_SIZE, 8192, digest, key_len, key);
 }
 
 // New code.
diff --git a/keystore2/src/crypto/crypto.hpp b/keystore2/src/crypto/crypto.hpp
index d66532f..4a161e6 100644
--- a/keystore2/src/crypto/crypto.hpp
+++ b/keystore2/src/crypto/crypto.hpp
@@ -36,6 +36,7 @@
 
   bool CreateKeyId(const uint8_t* key_blob, size_t len, km_id_t* out_id);
 
+  // The salt parameter must be non-nullptr and point to 16 bytes of data.
   void generateKeyFromPassword(uint8_t* key, size_t key_len, const char* pw,
                                size_t pw_len, const uint8_t* salt);
 
diff --git a/keystore2/src/crypto/include/certificate_utils.h b/keystore2/src/crypto/include/certificate_utils.h
index cad82b6..13d3ef0 100644
--- a/keystore2/src/crypto/include/certificate_utils.h
+++ b/keystore2/src/crypto/include/certificate_utils.h
@@ -20,6 +20,7 @@
 #include <openssl/x509.h>
 #include <stdint.h>
 
+#include <functional>
 #include <memory>
 #include <optional>
 #include <variant>
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index 14bdf04..08b7589 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -190,31 +190,23 @@
     fn get_key(&'a self) -> &'a [u8] {
         match self {
             Self::Ref(b) => b,
-            Self::Owned(z) => &*z,
+            Self::Owned(z) => z,
         }
     }
 
     /// Generate a key from the given password and salt.
     /// The salt must be exactly 16 bytes long.
     /// Two key sizes are accepted: 16 and 32 bytes.
-    pub fn derive_key(&self, salt: Option<&[u8]>, key_length: usize) -> Result<ZVec, Error> {
-        let pw = self.get_key();
-
-        let salt: *const u8 = match salt {
-            Some(s) => {
-                if s.len() != SALT_LENGTH {
-                    return Err(Error::InvalidSaltLength);
-                }
-                s.as_ptr()
-            }
-            None => std::ptr::null(),
-        };
-
+    pub fn derive_key(&self, salt: &[u8], key_length: usize) -> Result<ZVec, Error> {
+        if salt.len() != SALT_LENGTH {
+            return Err(Error::InvalidSaltLength);
+        }
         match key_length {
             AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
             _ => return Err(Error::InvalidKeyLength),
         }
 
+        let pw = self.get_key();
         let mut result = ZVec::new(key_length)?;
 
         unsafe {
@@ -223,7 +215,7 @@
                 result.len(),
                 pw.as_ptr() as *const std::os::raw::c_char,
                 pw.len(),
-                salt,
+                salt.as_ptr(),
             )
         };
 
@@ -368,8 +360,7 @@
     // Safety: the key is valid.
     // This will not write past the specified length of the buffer; if the
     // len above is too short, it returns 0.
-    let written_len =
-        unsafe { ECKEYMarshalPrivateKey(key.0, buf.as_mut_ptr(), buf.len()) } as usize;
+    let written_len = unsafe { ECKEYMarshalPrivateKey(key.0, buf.as_mut_ptr(), buf.len()) };
     if written_len == len {
         Ok(buf)
     } else {
@@ -541,9 +532,9 @@
     fn test_generate_key_from_password() {
         let mut key = vec![0; 16];
         let pw = vec![0; 16];
-        let mut salt = vec![0; 16];
+        let salt = vec![0; 16];
         unsafe {
-            generateKeyFromPassword(key.as_mut_ptr(), 16, pw.as_ptr(), 16, salt.as_mut_ptr());
+            generateKeyFromPassword(key.as_mut_ptr(), 16, pw.as_ptr(), 16, salt.as_ptr());
         }
         assert_ne!(key, vec![0; 16]);
     }
diff --git a/keystore2/src/crypto/tests/certificate_utils_test.cpp b/keystore2/src/crypto/tests/certificate_utils_test.cpp
index bd94928..a851798 100644
--- a/keystore2/src/crypto/tests/certificate_utils_test.cpp
+++ b/keystore2/src/crypto/tests/certificate_utils_test.cpp
@@ -313,7 +313,15 @@
     const uint8_t* p = encCert.data();
     X509_Ptr decoded_cert(d2i_X509(nullptr, &p, (long)encCert.size()));
     EVP_PKEY_Ptr decoded_pkey(X509_get_pubkey(decoded_cert.get()));
-    ASSERT_TRUE(X509_verify(decoded_cert.get(), decoded_pkey.get()));
+    if ((padding == Padding::PSS) && (digest == Digest::SHA1 || digest == Digest::SHA224)) {
+        // BoringSSL after https://boringssl-review.googlesource.com/c/boringssl/+/53865
+        // does not support these PSS combinations, so skip certificate verification for them
+        // and just check _something_ was returned.
+        EXPECT_NE(decoded_cert.get(), nullptr);
+        EXPECT_NE(decoded_pkey.get(), nullptr);
+    } else {
+        ASSERT_TRUE(X509_verify(decoded_cert.get(), decoded_pkey.get()));
+    }
 }
 
 TEST(TimeStringTests, toTimeStringTest) {
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index a3979bd..6108221 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -49,6 +49,7 @@
 use crate::globals::get_keymint_dev_by_uuid;
 use crate::impl_metadata; // This is in db_utils.rs
 use crate::key_parameter::{KeyParameter, Tag};
+use crate::ks_err;
 use crate::metrics_store::log_rkp_error_stats;
 use crate::permission::KeyPermSet;
 use crate::utils::{get_current_time_in_milliseconds, watchdog as wd, AID_USER_OFFSET};
@@ -62,20 +63,16 @@
 use utils::SqlField;
 
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    HardwareAuthToken::HardwareAuthToken,
-    HardwareAuthenticatorType::HardwareAuthenticatorType, SecurityLevel::SecurityLevel,
+    HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType,
+    SecurityLevel::SecurityLevel,
+};
+use android_security_metrics::aidl::android::security::metrics::{
+    RkpError::RkpError as MetricsRkpError, Storage::Storage as MetricsStorage,
+    StorageStats::StorageStats,
 };
 use android_system_keystore2::aidl::android::system::keystore2::{
     Domain::Domain, KeyDescriptor::KeyDescriptor,
 };
-use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
-    AttestationPoolStatus::AttestationPoolStatus,
-};
-use android_security_metrics::aidl::android::security::metrics::{
-    StorageStats::StorageStats,
-    Storage::Storage as MetricsStorage,
-    RkpError::RkpError as MetricsRkpError,
-};
 
 use keystore2_crypto::ZVec;
 use lazy_static::lazy_static;
@@ -133,12 +130,13 @@
                 "SELECT tag, data from persistent.keymetadata
                     WHERE keyentryid = ?;",
             )
-            .context("In KeyMetaData::load_from_db: prepare statement failed.")?;
+            .context(ks_err!("KeyMetaData::load_from_db: prepare statement failed."))?;
 
         let mut metadata: HashMap<i64, KeyMetaEntry> = Default::default();
 
-        let mut rows =
-            stmt.query(params![key_id]).context("In KeyMetaData::load_from_db: query failed.")?;
+        let mut rows = stmt
+            .query(params![key_id])
+            .context(ks_err!("KeyMetaData::load_from_db: query failed."))?;
         db_utils::with_rows_extract_all(&mut rows, |row| {
             let db_tag: i64 = row.get(0).context("Failed to read tag.")?;
             metadata.insert(
@@ -148,7 +146,7 @@
             );
             Ok(())
         })
-        .context("In KeyMetaData::load_from_db.")?;
+        .context(ks_err!("KeyMetaData::load_from_db."))?;
 
         Ok(Self { data: metadata })
     }
@@ -159,12 +157,12 @@
                 "INSERT or REPLACE INTO persistent.keymetadata (keyentryid, tag, data)
                     VALUES (?, ?, ?);",
             )
-            .context("In KeyMetaData::store_in_db: Failed to prepare statement.")?;
+            .context(ks_err!("KeyMetaData::store_in_db: Failed to prepare statement."))?;
 
         let iter = self.data.iter();
         for (tag, entry) in iter {
             stmt.insert(params![key_id, tag, entry,]).with_context(|| {
-                format!("In KeyMetaData::store_in_db: Failed to insert {:?}", entry)
+                ks_err!("KeyMetaData::store_in_db: Failed to insert {:?}", entry)
             })?;
         }
         Ok(())
@@ -208,12 +206,11 @@
                 "SELECT tag, data from persistent.blobmetadata
                     WHERE blobentryid = ?;",
             )
-            .context("In BlobMetaData::load_from_db: prepare statement failed.")?;
+            .context(ks_err!("BlobMetaData::load_from_db: prepare statement failed."))?;
 
         let mut metadata: HashMap<i64, BlobMetaEntry> = Default::default();
 
-        let mut rows =
-            stmt.query(params![blob_id]).context("In BlobMetaData::load_from_db: query failed.")?;
+        let mut rows = stmt.query(params![blob_id]).context(ks_err!("query failed."))?;
         db_utils::with_rows_extract_all(&mut rows, |row| {
             let db_tag: i64 = row.get(0).context("Failed to read tag.")?;
             metadata.insert(
@@ -223,7 +220,7 @@
             );
             Ok(())
         })
-        .context("In BlobMetaData::load_from_db.")?;
+        .context(ks_err!("BlobMetaData::load_from_db"))?;
 
         Ok(Self { data: metadata })
     }
@@ -234,12 +231,12 @@
                 "INSERT or REPLACE INTO persistent.blobmetadata (blobentryid, tag, data)
                     VALUES (?, ?, ?);",
             )
-            .context("In BlobMetaData::store_in_db: Failed to prepare statement.")?;
+            .context(ks_err!("BlobMetaData::store_in_db: Failed to prepare statement.",))?;
 
         let iter = self.data.iter();
         for (tag, entry) in iter {
             stmt.insert(params![blob_id, tag, entry,]).with_context(|| {
-                format!("In BlobMetaData::store_in_db: Failed to insert {:?}", entry)
+                ks_err!("BlobMetaData::store_in_db: Failed to insert {:?}", entry)
             })?;
         }
         Ok(())
@@ -324,7 +321,7 @@
     0x41, 0xe3, 0xb9, 0xce, 0x27, 0x58, 0x4e, 0x91, 0xbc, 0xfd, 0xa5, 0x5d, 0x91, 0x85, 0xab, 0x11,
 ]);
 
-static EXPIRATION_BUFFER_MS: i64 = 20000;
+static EXPIRATION_BUFFER_MS: i64 = 12 * 60 * 60 * 1000;
 
 /// Indicates how the sensitive part of this key blob is encrypted.
 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
@@ -829,7 +826,7 @@
     pub fn satisfies(&self, user_secure_ids: &[i64], auth_type: HardwareAuthenticatorType) -> bool {
         user_secure_ids.iter().any(|&sid| {
             (sid == self.auth_token.userId || sid == self.auth_token.authenticatorId)
-                && (((auth_type.0 as i32) & (self.auth_token.authenticatorType.0 as i32)) != 0)
+                && ((auth_type.0 & self.auth_token.authenticatorType.0) != 0)
         })
     }
 
@@ -881,7 +878,7 @@
         let mut db = Self { conn, gc, perboot: perboot::PERBOOT_DB.clone() };
         db.with_transaction(TransactionBehavior::Immediate, |tx| {
             versioning::upgrade_database(tx, Self::CURRENT_DB_VERSION, Self::UPGRADERS)
-                .context("In KeystoreDB::new: trying to upgrade database.")?;
+                .context(ks_err!("KeystoreDB::new: trying to upgrade database."))?;
             Self::init_tables(tx).context("Trying to initialize tables.").no_gc()
         })?;
         Ok(db)
@@ -903,7 +900,7 @@
                  );",
             params![KeyLifeCycle::Unreferenced, Tag::MAX_BOOT_LEVEL.0, BlobMetaData::MaxBootLevel],
         )
-        .context("In from_0_to_1: Failed to delete logical boot level keys.")?;
+        .context(ks_err!("Failed to delete logical boot level keys."))?;
         Ok(1)
     }
 
@@ -1064,7 +1061,7 @@
         let (total, unused) = self.with_transaction(TransactionBehavior::Deferred, |tx| {
             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)
+                    ks_err!("get_storage_stat: Error size of storage type {}", storage_type.0)
                 })
                 .no_gc()
         })?;
@@ -1239,7 +1236,7 @@
 
             Ok(vec![]).no_gc()
         })
-        .context("In handle_next_superseded_blobs.")
+        .context(ks_err!())
     }
 
     /// This maintenance function should be called only once before the database is used for the
@@ -1261,7 +1258,7 @@
             .context("Failed to execute query.")
             .need_gc()
         })
-        .context("In cleanup_leftovers.")
+        .context(ks_err!())
     }
 
     /// Checks if a key exists with given key type and key descriptor properties.
@@ -1282,12 +1279,12 @@
                 Ok(_) => Ok(true),
                 Err(error) => match error.root_cause().downcast_ref::<KsError>() {
                     Some(KsError::Rc(ResponseCode::KEY_NOT_FOUND)) => Ok(false),
-                    _ => Err(error).context("In key_exists: Failed to find if the key exists."),
+                    _ => Err(error).context(ks_err!("Failed to find if the key exists.")),
                 },
             }
             .no_gc()
         })
-        .context("In key_exists.")
+        .context(ks_err!())
     }
 
     /// Stores a super key in the database.
@@ -1335,7 +1332,7 @@
                 .context("Trying to load key components.")
                 .no_gc()
         })
-        .context("In store_super_key.")
+        .context(ks_err!())
     }
 
     /// Loads super key of a given user, if exists
@@ -1357,17 +1354,17 @@
             match id {
                 Ok(id) => {
                     let key_entry = Self::load_key_components(tx, KeyEntryLoadBits::KM, id)
-                        .context("In load_super_key. Failed to load key entry.")?;
+                        .context(ks_err!("Failed to load key entry."))?;
                     Ok(Some((KEY_ID_LOCK.get(id), key_entry)))
                 }
                 Err(error) => match error.root_cause().downcast_ref::<KsError>() {
                     Some(KsError::Rc(ResponseCode::KEY_NOT_FOUND)) => Ok(None),
-                    _ => Err(error).context("In load_super_key."),
+                    _ => Err(error).context(ks_err!()),
                 },
             }
             .no_gc()
         })
-        .context("In load_super_key.")
+        .context(ks_err!())
     }
 
     /// Atomically loads a key entry and associated metadata or creates it using the
@@ -1399,10 +1396,10 @@
                     AND alias = ?
                     AND state = ?;",
                     )
-                    .context("In get_or_create_key_with: Failed to select from keyentry table.")?;
+                    .context(ks_err!("Failed to select from keyentry table."))?;
                 let mut rows = stmt
                     .query(params![KeyType::Super, domain.0, namespace, alias, KeyLifeCycle::Live])
-                    .context("In get_or_create_key_with: Failed to query from keyentry table.")?;
+                    .context(ks_err!("Failed to query from keyentry table."))?;
 
                 db_utils::with_rows_extract_one(&mut rows, |row| {
                     Ok(match row {
@@ -1410,14 +1407,13 @@
                         None => None,
                     })
                 })
-                .context("In get_or_create_key_with.")?
+                .context(ks_err!())?
             };
 
             let (id, entry) = match id {
                 Some(id) => (
                     id,
-                    Self::load_key_components(tx, KeyEntryLoadBits::KM, id)
-                        .context("In get_or_create_key_with.")?,
+                    Self::load_key_components(tx, KeyEntryLoadBits::KM, id).context(ks_err!())?,
                 ),
 
                 None => {
@@ -1437,10 +1433,9 @@
                             ],
                         )
                     })
-                    .context("In get_or_create_key_with.")?;
+                    .context(ks_err!())?;
 
-                    let (blob, metadata) =
-                        create_new_key().context("In get_or_create_key_with.")?;
+                    let (blob, metadata) = create_new_key().context(ks_err!())?;
                     Self::set_blob_internal(
                         tx,
                         id,
@@ -1448,7 +1443,7 @@
                         Some(&blob),
                         Some(&metadata),
                     )
-                    .context("In get_or_create_key_with.")?;
+                    .context(ks_err!())?;
                     (
                         id,
                         KeyEntry {
@@ -1462,7 +1457,7 @@
             };
             Ok((KEY_ID_LOCK.get(id), entry)).no_gc()
         })
-        .context("In get_or_create_key_with.")
+        .context(ks_err!())
     }
 
     /// Creates a transaction with the given behavior and executes f with the new transaction.
@@ -1476,10 +1471,10 @@
             match self
                 .conn
                 .transaction_with_behavior(behavior)
-                .context("In with_transaction.")
+                .context(ks_err!())
                 .and_then(|tx| f(&tx).map(|result| (result, tx)))
                 .and_then(|(result, tx)| {
-                    tx.commit().context("In with_transaction: Failed to commit transaction.")?;
+                    tx.commit().context(ks_err!("Failed to commit transaction."))?;
                     Ok(result)
                 }) {
                 Ok(result) => break Ok(result),
@@ -1488,7 +1483,7 @@
                         std::thread::sleep(std::time::Duration::from_micros(500));
                         continue;
                     } else {
-                        return Err(e).context("In with_transaction.");
+                        return Err(e).context(ks_err!());
                     }
                 }
             }
@@ -1529,7 +1524,7 @@
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             Self::create_key_entry_internal(tx, domain, namespace, key_type, km_uuid).no_gc()
         })
-        .context("In create_key_entry.")
+        .context(ks_err!())
     }
 
     fn create_key_entry_internal(
@@ -1543,7 +1538,7 @@
             Domain::APP | Domain::SELINUX => {}
             _ => {
                 return Err(KsError::sys())
-                    .context(format!("Domain {:?} must be either App or SELinux.", domain));
+                    .context(ks_err!("Domain {:?} must be either App or SELinux.", domain));
             }
         }
         Ok(KEY_ID_LOCK.get(
@@ -1562,7 +1557,7 @@
                     ],
                 )
             })
-            .context("In create_key_entry_internal")?,
+            .context(ks_err!())?,
         ))
     }
 
@@ -1590,7 +1585,7 @@
                         params![id, KeyType::Attestation, KeyLifeCycle::Live, km_uuid],
                     )
                 })
-                .context("In create_key_entry")?,
+                .context(ks_err!())?,
             );
             Self::set_blob_internal(
                 tx,
@@ -1605,7 +1600,7 @@
             metadata.store_in_db(key_id.0, tx)?;
             Ok(()).no_gc()
         })
-        .context("In create_attestation_key_entry")
+        .context(ks_err!())
     }
 
     /// Set a new blob and associates it with the given key id. Each blob
@@ -1627,7 +1622,7 @@
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             Self::set_blob_internal(tx, key_id.0, sc_type, blob, blob_metadata).need_gc()
         })
-        .context("In set_blob.")
+        .context(ks_err!())
     }
 
     /// Why would we insert a deleted blob? This weird function is for the purpose of legacy
@@ -1647,7 +1642,7 @@
             )
             .need_gc()
         })
-        .context("In set_deleted_blob.")
+        .context(ks_err!())
     }
 
     fn set_blob_internal(
@@ -1664,16 +1659,16 @@
                      (subcomponent_type, keyentryid, blob) VALUES (?, ?, ?);",
                     params![sc_type, key_id, blob],
                 )
-                .context("In set_blob_internal: Failed to insert blob.")?;
+                .context(ks_err!("Failed to insert blob."))?;
                 if let Some(blob_metadata) = blob_metadata {
                     let blob_id = tx
                         .query_row("SELECT MAX(id) FROM persistent.blobentry;", NO_PARAMS, |row| {
                             row.get(0)
                         })
-                        .context("In set_blob_internal: Failed to get new blob id.")?;
+                        .context(ks_err!("Failed to get new blob id."))?;
                     blob_metadata
                         .store_in_db(blob_id, tx)
-                        .context("In set_blob_internal: Trying to store blob metadata.")?;
+                        .context(ks_err!("Trying to store blob metadata."))?;
                 }
             }
             (None, SubComponentType::CERT) | (None, SubComponentType::CERT_CHAIN) => {
@@ -1682,11 +1677,11 @@
                     WHERE subcomponent_type = ? AND keyentryid = ?;",
                     params![sc_type, key_id],
                 )
-                .context("In set_blob_internal: Failed to delete blob.")?;
+                .context(ks_err!("Failed to delete blob."))?;
             }
             (None, _) => {
                 return Err(KsError::sys())
-                    .context("In set_blob_internal: Other blobs cannot be deleted in this way.");
+                    .context(ks_err!("Other blobs cannot be deleted in this way."));
             }
         }
         Ok(())
@@ -1699,7 +1694,7 @@
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             Self::insert_keyparameter_internal(tx, key_id, params).no_gc()
         })
-        .context("In insert_keyparameter.")
+        .context(ks_err!())
     }
 
     fn insert_keyparameter_internal(
@@ -1712,7 +1707,7 @@
                 "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level)
                 VALUES (?, ?, ?, ?);",
             )
-            .context("In insert_keyparameter_internal: Failed to prepare statement.")?;
+            .context(ks_err!("Failed to prepare statement."))?;
 
         for p in params.iter() {
             stmt.insert(params![
@@ -1721,9 +1716,7 @@
                 p.key_parameter_value(),
                 p.security_level().0
             ])
-            .with_context(|| {
-                format!("In insert_keyparameter_internal: Failed to insert {:?}", p)
-            })?;
+            .with_context(|| ks_err!("Failed to insert {:?}", p))?;
         }
         Ok(())
     }
@@ -1734,7 +1727,7 @@
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             metadata.store_in_db(key_id.0, tx).no_gc()
         })
-        .context("In insert_key_metadata.")
+        .context(ks_err!())
     }
 
     /// Stores a signed certificate chain signed by a remote provisioning server, keyed
@@ -1807,7 +1800,7 @@
                 .context("Failed to insert cert")?;
             Ok(()).no_gc()
         })
-        .context("In store_signed_attestation_certificate_chain: ")
+        .context(ks_err!())
     }
 
     /// Assigns the next unassigned attestation key to a domain/namespace combo that does not
@@ -1823,13 +1816,8 @@
         match domain {
             Domain::APP | Domain::SELINUX => {}
             _ => {
-                return Err(KsError::sys()).context(format!(
-                    concat!(
-                        "In assign_attestation_key: Domain {:?} ",
-                        "must be either App or SELinux.",
-                    ),
-                    domain
-                ));
+                return Err(KsError::sys())
+                    .context(ks_err!("Domain {:?} must be either App or SELinux.", domain));
             }
         }
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
@@ -1867,14 +1855,15 @@
                 let (_, hw_info) = get_keymint_dev_by_uuid(km_uuid)
                     .context("Error in retrieving keymint device by UUID.")?;
                 log_rkp_error_stats(MetricsRkpError::OUT_OF_KEYS, &hw_info.securityLevel);
-                return Err(KsError::Rc(ResponseCode::OUT_OF_KEYS)).context("Out of keys.");
+                return Err(KsError::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
+                    .context("Out of keys.");
             } else if result > 1 {
                 return Err(KsError::sys())
                     .context(format!("Expected to update 1 entry, instead updated {}", result));
             }
             Ok(()).no_gc()
         })
-        .context("In assign_attestation_key: ")
+        .context(ks_err!())
     }
 
     /// Retrieves num_keys number of attestation keys that have not yet been signed by a remote
@@ -1918,7 +1907,7 @@
                 .context("Failed to execute statement")?;
             Ok(rows).no_gc()
         })
-        .context("In fetch_unsigned_attestation_keys")
+        .context(ks_err!())
     }
 
     /// Removes any keys that have expired as of the current time. Returns the number of keys
@@ -1958,7 +1947,7 @@
             }
             Ok(num_deleted).do_gc(num_deleted != 0)
         })
-        .context("In delete_expired_attestation_keys: ")
+        .context(ks_err!())
     }
 
     /// Deletes all remotely provisioned attestation keys in the system, regardless of the state
@@ -1987,74 +1976,7 @@
                 .count() as i64;
             Ok(num_deleted).do_gc(num_deleted != 0)
         })
-        .context("In delete_all_attestation_keys: ")
-    }
-
-    /// Counts the number of keys that will expire by the provided epoch date and the number of
-    /// keys not currently assigned to a domain.
-    pub fn get_attestation_pool_status(
-        &mut self,
-        date: i64,
-        km_uuid: &Uuid,
-    ) -> Result<AttestationPoolStatus> {
-        let _wp = wd::watch_millis("KeystoreDB::get_attestation_pool_status", 500);
-
-        self.with_transaction(TransactionBehavior::Immediate, |tx| {
-            let mut stmt = tx.prepare(
-                "SELECT data
-                 FROM persistent.keymetadata
-                 WHERE tag = ? AND keyentryid IN
-                     (SELECT id
-                      FROM persistent.keyentry
-                      WHERE alias IS NOT NULL
-                            AND key_type = ?
-                            AND km_uuid = ?
-                            AND state = ?);",
-            )?;
-            let times = stmt
-                .query_map(
-                    params![
-                        KeyMetaData::AttestationExpirationDate,
-                        KeyType::Attestation,
-                        km_uuid,
-                        KeyLifeCycle::Live
-                    ],
-                    |row| row.get(0),
-                )?
-                .collect::<rusqlite::Result<Vec<DateTime>>>()
-                .context("Failed to execute metadata statement")?;
-            let expiring =
-                times.iter().filter(|time| time < &&DateTime::from_millis_epoch(date)).count()
-                    as i32;
-            stmt = tx.prepare(
-                "SELECT alias, domain
-                 FROM persistent.keyentry
-                 WHERE key_type = ? AND km_uuid = ? AND state = ?;",
-            )?;
-            let rows = stmt
-                .query_map(params![KeyType::Attestation, km_uuid, KeyLifeCycle::Live], |row| {
-                    Ok((row.get(0)?, row.get(1)?))
-                })?
-                .collect::<rusqlite::Result<Vec<(Option<String>, Option<u32>)>>>()
-                .context("Failed to execute keyentry statement")?;
-            let mut unassigned = 0i32;
-            let mut attested = 0i32;
-            let total = rows.len() as i32;
-            for (alias, domain) in rows {
-                match (alias, domain) {
-                    (Some(_alias), None) => {
-                        attested += 1;
-                        unassigned += 1;
-                    }
-                    (Some(_alias), Some(_domain)) => {
-                        attested += 1;
-                    }
-                    _ => {}
-                }
-            }
-            Ok(AttestationPoolStatus { expiring, unassigned, attested, total }).no_gc()
-        })
-        .context("In get_attestation_pool_status: ")
+        .context(ks_err!())
     }
 
     fn query_kid_for_attestation_key_and_cert_chain(
@@ -2111,24 +2033,24 @@
             }
         }
 
-        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.",
-        )?;
+        self.delete_expired_attestation_keys()
+            .context(ks_err!("Failed to prune expired attestation keys",))?;
+        let tx = self
+            .conn
+            .unchecked_transaction()
+            .context(ks_err!("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")?;
+        tx.commit().context(ks_err!("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 tx = self
+            .conn
+            .unchecked_transaction()
+            .context(ks_err!("Failed to initialize transaction."))?;
         let mut stmt = tx.prepare(
             "SELECT subcomponent_type, blob
             FROM persistent.blobentry
@@ -2193,10 +2115,8 @@
         match *domain {
             Domain::APP | Domain::SELINUX => {}
             _ => {
-                return Err(KsError::sys()).context(format!(
-                    "In rebind_alias: Domain {:?} must be either App or SELinux.",
-                    domain
-                ));
+                return Err(KsError::sys())
+                    .context(ks_err!("Domain {:?} must be either App or SELinux.", domain));
             }
         }
         let updated = tx
@@ -2206,7 +2126,7 @@
                  WHERE alias = ? AND domain = ? AND namespace = ? AND key_type = ?;",
                 params![KeyLifeCycle::Unreferenced, alias, domain.0 as u32, namespace, key_type],
             )
-            .context("In rebind_alias: Failed to rebind existing entry.")?;
+            .context(ks_err!("Failed to rebind existing entry."))?;
         let result = tx
             .execute(
                 "UPDATE persistent.keyentry
@@ -2222,10 +2142,10 @@
                     key_type,
                 ],
             )
-            .context("In rebind_alias: Failed to set alias.")?;
+            .context(ks_err!("Failed to set alias."))?;
         if result != 1 {
-            return Err(KsError::sys()).context(format!(
-                "In rebind_alias: Expected to update a single entry but instead updated {}.",
+            return Err(KsError::sys()).context(ks_err!(
+                "Expected to update a single entry but instead updated {}.",
                 result
             ));
         }
@@ -2253,14 +2173,13 @@
         };
 
         // Security critical: Must return immediately on failure. Do not remove the '?';
-        check_permission(&destination)
-            .context("In migrate_key_namespace: Trying to check permission.")?;
+        check_permission(&destination).context(ks_err!("Trying to check permission."))?;
 
         let alias = destination
             .alias
             .as_ref()
             .ok_or(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
-            .context("In migrate_key_namespace: Alias must be specified.")?;
+            .context(ks_err!("Alias must be specified."))?;
 
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             // Query the destination location. If there is a key, the migration request fails.
@@ -2294,7 +2213,7 @@
             }
             Ok(()).no_gc()
         })
-        .context("In migrate_key_namespace:")
+        .context(ks_err!())
     }
 
     /// Store a new key in a single transaction.
@@ -2322,7 +2241,7 @@
             }
             _ => {
                 return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
-                    .context("In store_new_key: Need alias and domain must be APP or SELINUX.")
+                    .context(ks_err!("Need alias and domain must be APP or SELINUX."));
             }
         };
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
@@ -2379,7 +2298,7 @@
                 || need_gc;
             Ok(key_id).do_gc(need_gc)
         })
-        .context("In store_new_key.")
+        .context(ks_err!())
     }
 
     /// Store a new certificate
@@ -2400,9 +2319,8 @@
                 (alias, key.domain, nspace)
             }
             _ => {
-                return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT)).context(
-                    "In store_new_certificate: Need alias and domain must be APP or SELINUX.",
-                )
+                return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
+                    .context(ks_err!("Need alias and domain must be APP or SELINUX."));
             }
         };
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
@@ -2429,7 +2347,7 @@
                 .context("Trying to rebind alias.")?;
             Ok(key_id).do_gc(need_gc)
         })
-        .context("In store_new_certificate.")
+        .context(ks_err!())
     }
 
     // Helper function loading the key_id given the key descriptor
@@ -2460,7 +2378,7 @@
                 .get(0)
                 .context("Failed to unpack id.")
         })
-        .context("In load_key_entry_id.")
+        .context(ks_err!())
     }
 
     /// This helper function completes the access tuple of a key, which is required
@@ -2581,7 +2499,7 @@
 
                 Ok((key_id, access_key, access_vector))
             }
-            _ => Err(anyhow!(KsError::sys())),
+            _ => Err(anyhow!(KsError::Rc(ResponseCode::INVALID_ARGUMENT))),
         }
     }
 
@@ -2595,10 +2513,9 @@
                 "SELECT MAX(id), subcomponent_type, blob FROM persistent.blobentry
                     WHERE keyentryid = ? GROUP BY subcomponent_type;",
             )
-            .context("In load_blob_components: prepare statement failed.")?;
+            .context(ks_err!("prepare statement failed."))?;
 
-        let mut rows =
-            stmt.query(params![key_id]).context("In load_blob_components: query failed.")?;
+        let mut rows = stmt.query(params![key_id]).context(ks_err!("query failed."))?;
 
         let mut key_blob: Option<(i64, Vec<u8>)> = None;
         let mut cert_blob: Option<Vec<u8>> = None;
@@ -2630,13 +2547,13 @@
             }
             Ok(())
         })
-        .context("In load_blob_components.")?;
+        .context(ks_err!())?;
 
         let blob_info = key_blob.map_or::<Result<_>, _>(Ok(None), |(blob_id, blob)| {
             Ok(Some((
                 blob,
                 BlobMetaData::load_from_db(blob_id, tx)
-                    .context("In load_blob_components: Trying to load blob_metadata.")?,
+                    .context(ks_err!("Trying to load blob_metadata."))?,
             )))
         })?;
 
@@ -2664,7 +2581,7 @@
             );
             Ok(())
         })
-        .context("In load_key_parameters.")?;
+        .context(ks_err!())?;
 
         Ok(parameters)
     }
@@ -2706,7 +2623,7 @@
                 _ => Ok(()).no_gc(),
             }
         })
-        .context("In check_and_update_key_usage_count.")
+        .context(ks_err!())
     }
 
     /// Load a key entry by the given key descriptor.
@@ -2738,7 +2655,7 @@
                         std::thread::sleep(std::time::Duration::from_micros(500));
                         continue;
                     } else {
-                        return Err(e).context("In load_key_entry.");
+                        return Err(e).context(ks_err!());
                     }
                 }
             }
@@ -2764,16 +2681,15 @@
         let tx = self
             .conn
             .unchecked_transaction()
-            .context("In load_key_entry: Failed to initialize transaction.")?;
+            .context(ks_err!("Failed to initialize transaction."))?;
 
         // Load the key_id and complete the access control tuple.
         let (key_id, access_key_descriptor, access_vector) =
-            Self::load_access_tuple(&tx, key, key_type, caller_uid)
-                .context("In load_key_entry.")?;
+            Self::load_access_tuple(&tx, key, key_type, caller_uid).context(ks_err!())?;
 
         // Perform access control. It is vital that we return here if the permission is denied.
         // So do not touch that '?' at the end.
-        check_permission(&access_key_descriptor, access_vector).context("In load_key_entry.")?;
+        check_permission(&access_key_descriptor, access_vector).context(ks_err!())?;
 
         // KEY ID LOCK 2/2
         // If we did not get a key id lock by now, it was because we got a key descriptor
@@ -2790,7 +2706,7 @@
             None => match KEY_ID_LOCK.try_get(key_id) {
                 None => {
                     // Roll back the transaction.
-                    tx.rollback().context("In load_key_entry: Failed to roll back transaction.")?;
+                    tx.rollback().context(ks_err!("Failed to roll back transaction."))?;
 
                     // Block until we have a key id lock.
                     let key_id_guard = KEY_ID_LOCK.get(key_id);
@@ -2799,7 +2715,7 @@
                     let tx = self
                         .conn
                         .unchecked_transaction()
-                        .context("In load_key_entry: Failed to initialize transaction.")?;
+                        .context(ks_err!("Failed to initialize transaction."))?;
 
                     Self::load_access_tuple(
                         &tx,
@@ -2813,7 +2729,7 @@
                         key_type,
                         caller_uid,
                     )
-                    .context("In load_key_entry. (deferred key lock)")?;
+                    .context(ks_err!("(deferred key lock)"))?;
                     (key_id_guard, tx)
                 }
                 Some(l) => (l, tx),
@@ -2821,10 +2737,10 @@
             Some(key_id_guard) => (key_id_guard, tx),
         };
 
-        let key_entry = Self::load_key_components(&tx, load_bits, key_id_guard.id())
-            .context("In load_key_entry.")?;
+        let key_entry =
+            Self::load_key_components(&tx, load_bits, key_id_guard.id()).context(ks_err!())?;
 
-        tx.commit().context("In load_key_entry: Failed to commit transaction.")?;
+        tx.commit().context(ks_err!("Failed to commit transaction."))?;
 
         Ok((key_id_guard, key_entry))
     }
@@ -2867,7 +2783,7 @@
                 .map(|need_gc| (need_gc, ()))
                 .context("Trying to mark the key unreferenced.")
         })
-        .context("In unbind_key.")
+        .context(ks_err!())
     }
 
     fn get_key_km_uuid(tx: &Transaction, key_id: i64) -> Result<Uuid> {
@@ -2876,7 +2792,7 @@
             params![key_id],
             |row| row.get(0),
         )
-        .context("In get_key_km_uuid.")
+        .context(ks_err!())
     }
 
     /// Delete all artifacts belonging to the namespace given by the domain-namespace tuple.
@@ -2885,8 +2801,7 @@
         let _wp = wd::watch_millis("KeystoreDB::unbind_keys_for_namespace", 500);
 
         if !(domain == Domain::APP || domain == Domain::SELINUX) {
-            return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
-                .context("In unbind_keys_for_namespace.");
+            return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT)).context(ks_err!());
         }
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             tx.execute(
@@ -2924,7 +2839,7 @@
             .context("Trying to delete keyentry.")?;
             Ok(()).need_gc()
         })
-        .context("In unbind_keys_for_namespace")
+        .context(ks_err!())
     }
 
     fn cleanup_unreferenced(tx: &Transaction) -> Result<()> {
@@ -2965,7 +2880,7 @@
             .context("Trying to delete keyentry.")?;
             Result::<()>::Ok(())
         }
-        .context("In cleanup_unreferenced")
+        .context(ks_err!())
     }
 
     /// Delete the keys created on behalf of the user, denoted by the user id.
@@ -3012,7 +2927,7 @@
                     user_id,
                     KeyLifeCycle::Live
                 ])
-                .context("In unbind_keys_for_user. Failed to query the keys created by apps.")?;
+                .context(ks_err!("Failed to query the keys created by apps."))?;
 
             let mut key_ids: Vec<i64> = Vec::new();
             db_utils::with_rows_extract_all(&mut rows, |row| {
@@ -3020,7 +2935,7 @@
                     .push(row.get(0).context("Failed to read key id of a key created by an app.")?);
                 Ok(())
             })
-            .context("In unbind_keys_for_user.")?;
+            .context(ks_err!())?;
 
             let mut notify_gc = false;
             for key_id in key_ids {
@@ -3028,7 +2943,7 @@
                     // Load metadata and filter out non-super-encrypted keys.
                     if let (_, Some((_, blob_metadata)), _, _) =
                         Self::load_blob_components(key_id, KeyEntryLoadBits::KM, tx)
-                            .context("In unbind_keys_for_user: Trying to load blob info.")?
+                            .context(ks_err!("Trying to load blob info."))?
                     {
                         if blob_metadata.encrypted_by().is_none() {
                             continue;
@@ -3041,7 +2956,7 @@
             }
             Ok(()).do_gc(notify_gc)
         })
-        .context("In unbind_keys_for_user.")
+        .context(ks_err!())
     }
 
     fn load_key_components(
@@ -3072,32 +2987,50 @@
         })
     }
 
-    /// Returns a list of KeyDescriptors in the selected domain/namespace.
+    /// Returns a list of KeyDescriptors in the selected domain/namespace whose
+    /// aliases are greater than the specified 'start_past_alias'. If no value
+    /// is provided, returns all KeyDescriptors.
     /// The key descriptors will have the domain, nspace, and alias field set.
+    /// The returned list will be sorted by alias.
     /// Domain must be APP or SELINUX, the caller must make sure of that.
-    pub fn list(
+    pub fn list_past_alias(
         &mut self,
         domain: Domain,
         namespace: i64,
         key_type: KeyType,
+        start_past_alias: Option<&str>,
     ) -> Result<Vec<KeyDescriptor>> {
-        let _wp = wd::watch_millis("KeystoreDB::list", 500);
+        let _wp = wd::watch_millis("KeystoreDB::list_past_alias", 500);
 
-        self.with_transaction(TransactionBehavior::Deferred, |tx| {
-            let mut stmt = tx
-                .prepare(
-                    "SELECT alias FROM persistent.keyentry
+        let query = format!(
+            "SELECT DISTINCT alias FROM persistent.keyentry
                      WHERE domain = ?
                      AND namespace = ?
                      AND alias IS NOT NULL
                      AND state = ?
-                     AND key_type = ?;",
-                )
-                .context("In list: Failed to prepare.")?;
+                     AND key_type = ?
+                     {}
+                     ORDER BY alias ASC;",
+            if start_past_alias.is_some() { " AND alias > ?" } else { "" }
+        );
 
-            let mut rows = stmt
-                .query(params![domain.0 as u32, namespace, KeyLifeCycle::Live, key_type])
-                .context("In list: Failed to query.")?;
+        self.with_transaction(TransactionBehavior::Deferred, |tx| {
+            let mut stmt = tx.prepare(&query).context(ks_err!("Failed to prepare."))?;
+
+            let mut rows = match start_past_alias {
+                Some(past_alias) => stmt
+                    .query(params![
+                        domain.0 as u32,
+                        namespace,
+                        KeyLifeCycle::Live,
+                        key_type,
+                        past_alias
+                    ])
+                    .context(ks_err!("Failed to query."))?,
+                None => stmt
+                    .query(params![domain.0 as u32, namespace, KeyLifeCycle::Live, key_type,])
+                    .context(ks_err!("Failed to query."))?,
+            };
 
             let mut descriptors: Vec<KeyDescriptor> = Vec::new();
             db_utils::with_rows_extract_all(&mut rows, |row| {
@@ -3109,11 +3042,38 @@
                 });
                 Ok(())
             })
-            .context("In list: Failed to extract rows.")?;
+            .context(ks_err!("Failed to extract rows."))?;
             Ok(descriptors).no_gc()
         })
     }
 
+    /// Returns a number of KeyDescriptors in the selected domain/namespace.
+    /// Domain must be APP or SELINUX, the caller must make sure of that.
+    pub fn count_keys(
+        &mut self,
+        domain: Domain,
+        namespace: i64,
+        key_type: KeyType,
+    ) -> Result<usize> {
+        let _wp = wd::watch_millis("KeystoreDB::countKeys", 500);
+
+        let num_keys = self.with_transaction(TransactionBehavior::Deferred, |tx| {
+            tx.query_row(
+                "SELECT COUNT(alias) FROM persistent.keyentry
+                     WHERE domain = ?
+                     AND namespace = ?
+                     AND alias IS NOT NULL
+                     AND state = ?
+                     AND key_type = ?;",
+                params![domain.0 as u32, namespace, KeyLifeCycle::Live, key_type],
+                |row| row.get(0),
+            )
+            .context(ks_err!("Failed to count number of keys."))
+            .no_gc()
+        })?;
+        Ok(num_keys)
+    }
+
     /// Adds a grant to the grant table.
     /// Like `load_key_entry` this function loads the access tuple before
     /// it uses the callback for a permission check. Upon success,
@@ -3141,8 +3101,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)
-                    .context("In grant")?;
+                Self::load_access_tuple(tx, key, KeyType::Client, caller_uid).context(ks_err!())?;
 
             // Perform access control. It is vital that we return here if the permission
             // was denied. So do not touch that '?' at the end of the line.
@@ -3150,7 +3109,7 @@
             // for the given key and in addition to all of the permissions
             // expressed in `access_vector`.
             check_permission(&access_key_descriptor, &access_vector)
-                .context("In grant: check_permission failed.")?;
+                .context(ks_err!("check_permission failed"))?;
 
             let grant_id = if let Some(grant_id) = tx
                 .query_row(
@@ -3160,7 +3119,7 @@
                     |row| row.get(0),
                 )
                 .optional()
-                .context("In grant: Failed get optional existing grant id.")?
+                .context(ks_err!("Failed get optional existing grant id."))?
             {
                 tx.execute(
                     "UPDATE persistent.grant
@@ -3168,7 +3127,7 @@
                     WHERE id = ?;",
                     params![i32::from(access_vector), grant_id],
                 )
-                .context("In grant: Failed to update existing grant.")?;
+                .context(ks_err!("Failed to update existing grant."))?;
                 grant_id
             } else {
                 Self::insert_with_retry(|id| {
@@ -3178,7 +3137,7 @@
                         params![id, grantee_uid, key_id, i32::from(access_vector)],
                     )
                 })
-                .context("In grant")?
+                .context(ks_err!())?
             };
 
             Ok(KeyDescriptor { domain: Domain::GRANT, nspace: grant_id, alias: None, blob: None })
@@ -3201,13 +3160,12 @@
             // 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)
-                    .context("In ungrant.")?;
+                Self::load_access_tuple(tx, key, KeyType::Client, caller_uid).context(ks_err!())?;
 
             // Perform access control. We must return here if the permission
             // was denied. So do not touch the '?' at the end of this line.
             check_permission(&access_key_descriptor)
-                .context("In grant: check_permission failed.")?;
+                .context(ks_err!("check_permission failed."))?;
 
             tx.execute(
                 "DELETE FROM persistent.grant
@@ -3239,7 +3197,7 @@
                     _,
                 )) => (),
                 Err(e) => {
-                    return Err(e).context("In insert_with_retry: failed to insert into database.")
+                    return Err(e).context(ks_err!("failed to insert into database."));
                 }
                 _ => return Ok(newid),
             }
@@ -3298,7 +3256,7 @@
             .context("Trying to load key descriptor")
             .no_gc()
         })
-        .context("In load_key_descriptor.")
+        .context(ks_err!())
     }
 }
 
@@ -3366,7 +3324,7 @@
         db.with_transaction(TransactionBehavior::Immediate, |tx| {
             KeystoreDB::rebind_alias(tx, newid, alias, &domain, &namespace, KeyType::Client).no_gc()
         })
-        .context("In rebind_alias.")
+        .context(ks_err!())
     }
 
     #[test]
@@ -3518,15 +3476,15 @@
         // Test that we must pass in a valid Domain.
         check_result_is_error_containing_string(
             db.create_key_entry(&Domain::GRANT, &102, KeyType::Client, &KEYSTORE_UUID),
-            "Domain Domain(1) must be either App or SELinux.",
+            &format!("Domain {:?} must be either App or SELinux.", Domain::GRANT),
         );
         check_result_is_error_containing_string(
             db.create_key_entry(&Domain::BLOB, &103, KeyType::Client, &KEYSTORE_UUID),
-            "Domain Domain(3) must be either App or SELinux.",
+            &format!("Domain {:?} must be either App or SELinux.", Domain::BLOB),
         );
         check_result_is_error_containing_string(
             db.create_key_entry(&Domain::KEY_ID, &104, KeyType::Client, &KEYSTORE_UUID),
-            "Domain Domain(4) must be either App or SELinux.",
+            &format!("Domain {:?} must be either App or SELinux.", Domain::KEY_ID),
         );
 
         Ok(())
@@ -3572,62 +3530,6 @@
     }
 
     #[test]
-    fn test_get_attestation_pool_status() -> Result<()> {
-        let mut db = new_test_db()?;
-        let namespace: i64 = 30;
-        load_attestation_key_pool(
-            &mut db, 10, /* expiration */
-            namespace, 0x01, /* base_byte */
-        )?;
-        load_attestation_key_pool(&mut db, 20 /* expiration */, namespace + 1, 0x02)?;
-        load_attestation_key_pool(&mut db, 40 /* expiration */, namespace + 2, 0x03)?;
-        let mut status = db.get_attestation_pool_status(9 /* expiration */, &KEYSTORE_UUID)?;
-        assert_eq!(status.expiring, 0);
-        assert_eq!(status.attested, 3);
-        assert_eq!(status.unassigned, 0);
-        assert_eq!(status.total, 3);
-        assert_eq!(
-            db.get_attestation_pool_status(15 /* expiration */, &KEYSTORE_UUID)?.expiring,
-            1
-        );
-        assert_eq!(
-            db.get_attestation_pool_status(25 /* expiration */, &KEYSTORE_UUID)?.expiring,
-            2
-        );
-        assert_eq!(
-            db.get_attestation_pool_status(60 /* expiration */, &KEYSTORE_UUID)?.expiring,
-            3
-        );
-        let public_key: Vec<u8> = vec![0x01, 0x02, 0x03];
-        let private_key: Vec<u8> = vec![0x04, 0x05, 0x06];
-        let raw_public_key: Vec<u8> = vec![0x07, 0x08, 0x09];
-        let cert_chain: Vec<u8> = vec![0x0a, 0x0b, 0x0c];
-        let batch_cert: Vec<u8> = vec![0x0d, 0x0e, 0x0f];
-        db.create_attestation_key_entry(
-            &public_key,
-            &raw_public_key,
-            &private_key,
-            &KEYSTORE_UUID,
-        )?;
-        status = db.get_attestation_pool_status(0 /* expiration */, &KEYSTORE_UUID)?;
-        assert_eq!(status.attested, 3);
-        assert_eq!(status.unassigned, 0);
-        assert_eq!(status.total, 4);
-        db.store_signed_attestation_certificate_chain(
-            &raw_public_key,
-            &batch_cert,
-            &cert_chain,
-            20,
-            &KEYSTORE_UUID,
-        )?;
-        status = db.get_attestation_pool_status(0 /* expiration */, &KEYSTORE_UUID)?;
-        assert_eq!(status.attested, 4);
-        assert_eq!(status.unassigned, 1);
-        assert_eq!(status.total, 4);
-        Ok(())
-    }
-
-    #[test]
     fn test_remove_expired_certs() -> Result<()> {
         let temp_dir =
             TempDir::new("test_remove_expired_certs_").expect("Failed to create temp dir.");
@@ -3825,15 +3727,15 @@
         // Test that we must pass in a valid Domain.
         check_result_is_error_containing_string(
             rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::GRANT, 42),
-            "Domain Domain(1) must be either App or SELinux.",
+            &format!("Domain {:?} must be either App or SELinux.", Domain::GRANT),
         );
         check_result_is_error_containing_string(
             rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::BLOB, 42),
-            "Domain Domain(3) must be either App or SELinux.",
+            &format!("Domain {:?} must be either App or SELinux.", Domain::BLOB),
         );
         check_result_is_error_containing_string(
             rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::KEY_ID, 42),
-            "Domain Domain(4) must be either App or SELinux.",
+            &format!("Domain {:?} must be either App or SELinux.", Domain::KEY_ID),
         );
 
         // Test that we correctly handle setting an alias for something that does not exist.
@@ -4579,7 +4481,7 @@
                 DESTINATION_UID,
                 |k, av| {
                     assert_eq!(Domain::SELINUX, k.domain);
-                    assert_eq!(DESTINATION_NAMESPACE as i64, k.nspace);
+                    assert_eq!(DESTINATION_NAMESPACE, k.nspace);
                     assert!(av.is_none());
                     Ok(())
                 },
@@ -5039,8 +4941,8 @@
         let list_o_keys: Vec<(i64, i64)> = LIST_O_ENTRIES
             .iter()
             .map(|(domain, ns, alias)| {
-                let entry = make_test_key_entry(&mut db, *domain, *ns, *alias, None)
-                    .unwrap_or_else(|e| {
+                let entry =
+                    make_test_key_entry(&mut db, *domain, *ns, alias, None).unwrap_or_else(|e| {
                         panic!("Failed to insert {:?} {} {}. Error {:?}", domain, ns, alias, e)
                     });
                 (entry.id(), *ns)
@@ -5063,7 +4965,7 @@
                 })
                 .collect();
             list_o_descriptors.sort();
-            let mut list_result = db.list(*domain, *namespace, KeyType::Client)?;
+            let mut list_result = db.list_past_alias(*domain, *namespace, KeyType::Client, None)?;
             list_result.sort();
             assert_eq!(list_o_descriptors, list_result);
 
@@ -5093,7 +4995,10 @@
             loaded_entries.sort_unstable();
             assert_eq!(list_o_ids, loaded_entries);
         }
-        assert_eq!(Vec::<KeyDescriptor>::new(), db.list(Domain::SELINUX, 101, KeyType::Client)?);
+        assert_eq!(
+            Vec::<KeyDescriptor>::new(),
+            db.list_past_alias(Domain::SELINUX, 101, KeyType::Client, None)?
+        );
 
         Ok(())
     }
@@ -5352,6 +5257,10 @@
                 SecurityLevel::TRUSTED_ENVIRONMENT,
             ),
             KeyParameter::new(
+                KeyParameterValue::AttestationIdSecondIMEI(vec![4u8, 3u8, 1u8, 2u8]),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
                 KeyParameterValue::AttestationIdMEID(vec![4u8, 3u8, 1u8, 2u8]),
                 SecurityLevel::TRUSTED_ENVIRONMENT,
             ),
@@ -5613,11 +5522,11 @@
         make_test_key_entry(&mut db, Domain::APP, 110000, TEST_ALIAS, None)?;
         db.unbind_keys_for_user(2, false)?;
 
-        assert_eq!(1, db.list(Domain::APP, 110000, KeyType::Client)?.len());
-        assert_eq!(0, db.list(Domain::APP, 210000, KeyType::Client)?.len());
+        assert_eq!(1, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
+        assert_eq!(0, db.list_past_alias(Domain::APP, 210000, KeyType::Client, None)?.len());
 
         db.unbind_keys_for_user(1, true)?;
-        assert_eq!(0, db.list(Domain::APP, 110000, KeyType::Client)?.len());
+        assert_eq!(0, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
 
         Ok(())
     }
diff --git a/keystore2/src/ec_crypto.rs b/keystore2/src/ec_crypto.rs
index 0425d4a..4fb3747 100644
--- a/keystore2/src/ec_crypto.rs
+++ b/keystore2/src/ec_crypto.rs
@@ -14,6 +14,7 @@
 
 //! Implement ECDH-based encryption.
 
+use crate::ks_err;
 use anyhow::{Context, Result};
 use keystore2_crypto::{
     aes_gcm_decrypt, aes_gcm_encrypt, ec_key_generate_key, ec_key_get0_public_key,
@@ -28,29 +29,23 @@
 impl ECDHPrivateKey {
     /// Randomly generate a fresh keypair.
     pub fn generate() -> Result<ECDHPrivateKey> {
-        ec_key_generate_key()
-            .map(ECDHPrivateKey)
-            .context("In ECDHPrivateKey::generate: generation failed")
+        ec_key_generate_key().map(ECDHPrivateKey).context(ks_err!("generation failed"))
     }
 
     /// Deserialize bytes into an ECDH keypair
     pub fn from_private_key(buf: &[u8]) -> Result<ECDHPrivateKey> {
-        ec_key_parse_private_key(buf)
-            .map(ECDHPrivateKey)
-            .context("In ECDHPrivateKey::from_private_key: parsing failed")
+        ec_key_parse_private_key(buf).map(ECDHPrivateKey).context(ks_err!("parsing failed"))
     }
 
     /// Serialize the ECDH key into bytes
     pub fn private_key(&self) -> Result<ZVec> {
-        ec_key_marshal_private_key(&self.0)
-            .context("In ECDHPrivateKey::private_key: marshalling failed")
+        ec_key_marshal_private_key(&self.0).context(ks_err!("marshalling failed"))
     }
 
     /// Generate the serialization of the corresponding public key
     pub fn public_key(&self) -> Result<Vec<u8>> {
         let point = ec_key_get0_public_key(&self.0);
-        ec_point_point_to_oct(point.get_point())
-            .context("In ECDHPrivateKey::public_key: marshalling failed")
+        ec_point_point_to_oct(point.get_point()).context(ks_err!("marshalling failed"))
     }
 
     /// Use ECDH to agree an AES key with another party whose public key we have.
@@ -64,18 +59,17 @@
         recipient_public_key: &[u8],
     ) -> Result<ZVec> {
         let hkdf = hkdf_extract(sender_public_key, salt)
-            .context("In ECDHPrivateKey::agree_key: hkdf_extract on sender_public_key failed")?;
+            .context(ks_err!("hkdf_extract on sender_public_key failed"))?;
         let hkdf = hkdf_extract(recipient_public_key, &hkdf)
-            .context("In ECDHPrivateKey::agree_key: hkdf_extract on recipient_public_key failed")?;
+            .context(ks_err!("hkdf_extract on recipient_public_key failed"))?;
         let other_public_key = ec_point_oct_to_point(other_public_key)
-            .context("In ECDHPrivateKey::agree_key: ec_point_oct_to_point failed")?;
+            .context(ks_err!("ec_point_oct_to_point failed"))?;
         let secret = ecdh_compute_key(other_public_key.get_point(), &self.0)
-            .context("In ECDHPrivateKey::agree_key: ecdh_compute_key failed")?;
-        let prk = hkdf_extract(&secret, &hkdf)
-            .context("In ECDHPrivateKey::agree_key: hkdf_extract on secret failed")?;
+            .context(ks_err!("ecdh_compute_key failed"))?;
+        let prk = hkdf_extract(&secret, &hkdf).context(ks_err!("hkdf_extract on secret failed"))?;
 
         let aes_key = hkdf_expand(AES_256_KEY_LENGTH, &prk, b"AES-256-GCM key")
-            .context("In ECDHPrivateKey::agree_key: hkdf_expand failed")?;
+            .context(ks_err!("hkdf_expand failed"))?;
         Ok(aes_key)
     }
 
@@ -84,18 +78,14 @@
         recipient_public_key: &[u8],
         message: &[u8],
     ) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>)> {
-        let sender_key =
-            Self::generate().context("In ECDHPrivateKey::encrypt_message: generate failed")?;
-        let sender_public_key = sender_key
-            .public_key()
-            .context("In ECDHPrivateKey::encrypt_message: public_key failed")?;
-        let salt =
-            generate_salt().context("In ECDHPrivateKey::encrypt_message: generate_salt failed")?;
+        let sender_key = Self::generate().context(ks_err!("generate failed"))?;
+        let sender_public_key = sender_key.public_key().context(ks_err!("public_key failed"))?;
+        let salt = generate_salt().context(ks_err!("generate_salt failed"))?;
         let aes_key = sender_key
             .agree_key(&salt, recipient_public_key, &sender_public_key, recipient_public_key)
-            .context("In ECDHPrivateKey::encrypt_message: agree_key failed")?;
-        let (ciphertext, iv, tag) = aes_gcm_encrypt(message, &aes_key)
-            .context("In ECDHPrivateKey::encrypt_message: aes_gcm_encrypt failed")?;
+            .context(ks_err!("agree_key failed"))?;
+        let (ciphertext, iv, tag) =
+            aes_gcm_encrypt(message, &aes_key).context(ks_err!("aes_gcm_encrypt failed"))?;
         Ok((sender_public_key, salt, iv, ciphertext, tag))
     }
 
@@ -111,9 +101,8 @@
         let recipient_public_key = self.public_key()?;
         let aes_key = self
             .agree_key(salt, sender_public_key, sender_public_key, &recipient_public_key)
-            .context("In ECDHPrivateKey::decrypt_message: agree_key failed")?;
-        aes_gcm_decrypt(ciphertext, iv, tag, &aes_key)
-            .context("In ECDHPrivateKey::decrypt_message: aes_gcm_decrypt failed")
+            .context(ks_err!("agree_key failed"))?;
+        aes_gcm_decrypt(ciphertext, iv, tag, &aes_key).context(ks_err!("aes_gcm_decrypt failed"))
     }
 }
 
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index cb6a266..8d5e985 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -14,6 +14,7 @@
 
 //! This is the Keystore 2.0 Enforcements module.
 // TODO: more description to follow.
+use crate::ks_err;
 use crate::error::{map_binder_status, Error, ErrorCode};
 use crate::globals::{get_timestamp_service, ASYNC_TASK, DB, ENFORCEMENTS};
 use crate::key_parameter::{KeyParameter, KeyParameterValue};
@@ -95,14 +96,14 @@
             .unwrap()
             .take()
             .ok_or(Error::Km(ErrorCode::KEY_USER_NOT_AUTHENTICATED))
-            .context("In get_auth_tokens: No operation auth token received.")?;
+            .context(ks_err!("No operation auth token received."))?;
 
         let tst = match &self.state {
             AuthRequestState::TimeStampedOpAuth(recv) | AuthRequestState::TimeStamp(recv) => {
                 let result = recv.recv().context("In get_auth_tokens: Sender disconnected.")?;
-                Some(result.context(concat!(
-                    "In get_auth_tokens: Worker responded with error ",
-                    "from generating timestamp token."
+                Some(result.context(ks_err!(
+                    "Worker responded with error \
+                    from generating timestamp token.",
                 ))?)
             }
             AuthRequestState::OpAuth => None,
@@ -228,10 +229,7 @@
 fn timestamp_token_request(challenge: i64, sender: Sender<Result<TimeStampToken, Error>>) {
     if let Err(e) = sender.send(get_timestamp_token(challenge)) {
         log::info!(
-            concat!(
-                "In timestamp_token_request: Receiver hung up ",
-                "before timestamp token could be delivered. {:?}"
-            ),
+            concat!("Receiver hung up ", "before timestamp token could be delivered. {:?}"),
             e
         );
     }
@@ -322,7 +320,7 @@
                     .check_and_update_key_usage_count(key_id)
                     .context("Trying to update key usage count.")
             })
-            .context("In after_finish.")?;
+            .context(ks_err!())?;
         }
         Ok(())
     }
@@ -349,14 +347,14 @@
             DeferredAuthState::OpAuthRequired
             | DeferredAuthState::TimeStampedOpAuthRequired
             | DeferredAuthState::TimeStampRequired(_) => {
-                Err(Error::Km(ErrorCode::KEY_USER_NOT_AUTHENTICATED)).context(concat!(
-                    "In AuthInfo::get_auth_tokens: No operation auth token requested??? ",
-                    "This should not happen."
+                Err(Error::Km(ErrorCode::KEY_USER_NOT_AUTHENTICATED)).context(ks_err!(
+                    "No operation auth token requested??? \
+                    This should not happen."
                 ))
             }
             // This should not be reachable, because it should have been handled above.
             DeferredAuthState::Waiting(_) => {
-                Err(Error::sys()).context("In AuthInfo::get_auth_tokens: Cannot be reached.")
+                Err(Error::sys()).context(ks_err!("AuthInfo::get_auth_tokens: Cannot be reached.",))
             }
         }
     }
@@ -418,7 +416,7 @@
                         key_usage_limited: None,
                         confirmation_token_receiver: None,
                     },
-                ))
+                ));
             }
         };
 
@@ -428,7 +426,7 @@
             // Rule out WRAP_KEY purpose
             KeyPurpose::WRAP_KEY => {
                 return Err(Error::Km(Ec::INCOMPATIBLE_PURPOSE))
-                    .context("In authorize_create: WRAP_KEY purpose is not allowed here.");
+                    .context(ks_err!("WRAP_KEY purpose is not allowed here.",));
             }
             // Allow AGREE_KEY for EC keys only.
             KeyPurpose::AGREE_KEY => {
@@ -436,9 +434,8 @@
                     if kp.get_tag() == Tag::ALGORITHM
                         && *kp.key_parameter_value() != KeyParameterValue::Algorithm(Algorithm::EC)
                     {
-                        return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE)).context(
-                            "In authorize_create: key agreement is only supported for EC keys.",
-                        );
+                        return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE))
+                            .context(ks_err!("key agreement is only supported for EC keys.",));
                     }
                 }
             }
@@ -449,10 +446,10 @@
                     match *kp.key_parameter_value() {
                         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 \
-                                 supported.",
-                            );
+                            return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE)).context(ks_err!(
+                                "public operations on asymmetric keys are \
+                                 not supported."
+                            ));
                         }
                         _ => {}
                     }
@@ -460,7 +457,7 @@
             }
             _ => {
                 return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE))
-                    .context("In authorize_create: specified purpose is not supported.");
+                    .context(ks_err!("authorize_create: specified purpose is not supported."));
             }
         }
         // The following variables are to record information from key parameters to be used in
@@ -505,23 +502,21 @@
                 KeyParameterValue::ActiveDateTime(a) => {
                     if !Enforcements::is_given_time_passed(*a, true) {
                         return Err(Error::Km(Ec::KEY_NOT_YET_VALID))
-                            .context("In authorize_create: key is not yet active.");
+                            .context(ks_err!("key is not yet active."));
                     }
                 }
                 KeyParameterValue::OriginationExpireDateTime(o) => {
                     if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN)
                         && Enforcements::is_given_time_passed(*o, false)
                     {
-                        return Err(Error::Km(Ec::KEY_EXPIRED))
-                            .context("In authorize_create: key is expired.");
+                        return Err(Error::Km(Ec::KEY_EXPIRED)).context(ks_err!("key is expired."));
                     }
                 }
                 KeyParameterValue::UsageExpireDateTime(u) => {
                     if (purpose == KeyPurpose::DECRYPT || purpose == KeyPurpose::VERIFY)
                         && Enforcements::is_given_time_passed(*u, false)
                     {
-                        return Err(Error::Km(Ec::KEY_EXPIRED))
-                            .context("In authorize_create: key is expired.");
+                        return Err(Error::Km(Ec::KEY_EXPIRED)).context(ks_err!("key is expired."));
                     }
                 }
                 KeyParameterValue::UserSecureID(s) => {
@@ -560,24 +555,23 @@
         // authorize the purpose
         if !key_purpose_authorized {
             return Err(Error::Km(Ec::INCOMPATIBLE_PURPOSE))
-                .context("In authorize_create: the purpose is not authorized.");
+                .context(ks_err!("the purpose is not authorized."));
         }
 
         // 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.",
-            );
+            return Err(Error::Km(Ec::INVALID_KEY_BLOB))
+                .context(ks_err!("key has both NO_AUTH_REQUIRED and USER_SECURE_ID tags."));
         }
 
         // if either of auth_type or secure_id is present and the other is not present, return error
         if (user_auth_type.is_some() && user_secure_ids.is_empty())
             || (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.",
-            );
+            return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(ks_err!(
+                "Auth required, but either auth type or secure ids \
+                 are not present."
+            ));
         }
 
         // validate caller nonce for origination purposes
@@ -585,24 +579,22 @@
             && !caller_nonce_allowed
             && 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",
-            );
+            return Err(Error::Km(Ec::CALLER_NONCE_PROHIBITED))
+                .context(ks_err!("NONCE is present, although CALLER_NONCE is not present"));
         }
 
         if unlocked_device_required {
             // check the device locked status. If locked, operations on the key are not
             // allowed.
             if self.is_device_locked(user_id) {
-                return Err(Error::Km(Ec::DEVICE_LOCKED))
-                    .context("In authorize_create: device is locked.");
+                return Err(Error::Km(Ec::DEVICE_LOCKED)).context(ks_err!("device is locked."));
             }
         }
 
         if let Some(level) = max_boot_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.");
+                    .context(ks_err!("boot level is too late."));
             }
         }
 
@@ -636,7 +628,7 @@
             Some(
                 hat_and_last_off_body
                     .ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
-                    .context("In authorize_create: No suitable auth token found.")?,
+                    .context(ks_err!("No suitable auth token found."))?,
             )
         } else {
             None
@@ -649,16 +641,16 @@
                 let token_age = now
                     .checked_sub(&hat.time_received())
                     .ok_or_else(Error::sys)
-                    .context(concat!(
-                        "In authorize_create: Overflow while computing Auth token validity. ",
-                        "Validity cannot be established."
+                    .context(ks_err!(
+                        "Overflow while computing Auth token validity. \
+                    Validity cannot be established."
                     ))?;
 
                 let on_body_extended = allow_while_on_body && last_off_body < hat.time_received();
 
                 if token_age.seconds() > key_time_out && !on_body_extended {
                     return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
-                        .context("In authorize_create: matching auth token is expired.");
+                        .context(ks_err!("matching auth token is expired."));
                 }
                 Some(hat)
             }
@@ -832,20 +824,20 @@
                     auth_token_entry.take_auth_token()
                 } else {
                     return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND))
-                        .context("In get_auth_tokens: No auth token found.");
+                        .context(ks_err!("No auth token found."));
                 }
             } else {
                 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."
+                    ks_err!(
+                        "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.
-        let tst = get_timestamp_token(challenge)
-            .context("In get_auth_tokens. Error in getting timestamp token.")?;
+        let tst =
+            get_timestamp_token(challenge).context(ks_err!("Error in getting timestamp token."))?;
         Ok((auth_token, tst))
     }
 }
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index f34c5da..3ca3942 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -41,7 +41,7 @@
 
 /// 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.
-#[derive(Debug, thiserror::Error, PartialEq)]
+#[derive(Debug, thiserror::Error, PartialEq, Eq)]
 pub enum Error {
     /// Wraps a Keystore `ResponseCode` as defined by the Keystore AIDL interface specification.
     #[error("Error::Rc({0:?})")]
@@ -71,11 +71,6 @@
     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
diff --git a/keystore2/src/fuzzers/Android.bp b/keystore2/src/fuzzers/Android.bp
index 384ab77..4ac83e3 100644
--- a/keystore2/src/fuzzers/Android.bp
+++ b/keystore2/src/fuzzers/Android.bp
@@ -17,13 +17,51 @@
 }
 
 rust_fuzz {
-    name: "legacy_blob_fuzzer",
-    srcs: ["legacy_blob_fuzzer.rs"],
+    name: "keystore2_unsafe_fuzzer",
+    srcs: ["keystore2_unsafe_fuzzer.rs"],
     rustlibs: [
+        "libbinder_rs",
         "libkeystore2",
+        "libkeystore2_crypto_rust",
+        "libkeystore2_vintf_rust",
+        "libkeystore2_aaid-rust",
+        "libkeystore2_apc_compat-rust",
+        "libkeystore2_selinux",
+        "libarbitrary",
     ],
     fuzz_config: {
         fuzz_on_haiku_device: true,
         fuzz_on_haiku_host: false,
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
+
+
+rust_fuzz {
+    name: "authorization_service_fuzzer",
+    srcs: ["aidl-fuzzers/authorization_service_fuzzer.rs"],
+    rustlibs: [
+        "libkeystore2",
+        "libkeystore2_crypto_rust",
+        "libkeystore2_vintf_rust",
+        "libkeystore2_aaid-rust",
+        "libkeystore2_apc_compat-rust",
+        "libkeystore2_selinux",
+        "libbinder_rs",
+        "libbinder_random_parcel_rs",
+    ],
+    fuzz_config: {
+        fuzz_on_haiku_device: true,
+        fuzz_on_haiku_host: false,
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+            "smoreland@google.com",
+            "waghpawan@google.com"
+        ],
+        // Adds bugs to hotlist "AIDL fuzzers bugs" on buganizer
+        hotlists: ["4637097"],
     },
 }
diff --git a/keystore2/src/fuzzers/README.md b/keystore2/src/fuzzers/README.md
new file mode 100644
index 0000000..a4ed095
--- /dev/null
+++ b/keystore2/src/fuzzers/README.md
@@ -0,0 +1,18 @@
+# Fuzzers for libkeystore2
+## Table of contents
++ [keystore2_unsafe_fuzzer](#Keystore2Unsafe)
+
+# <a name="Keystore2Unsafe"></a> Fuzzer for Keystore2Unsafe
+All the parameters of Keystore2Unsafe are populated randomly from libfuzzer. You can find the possible values in the fuzzer's source code.
+
+#### Steps to run
+1. Build the fuzzer
+```
+$ m -j$(nproc) keystore2_unsafe_fuzzer
+```
+
+2. Run on device
+```
+$ adb sync data
+$ adb shell /data/fuzz/${TARGET_ARCH}/keystore2_unsafe_fuzzer/keystore2_unsafe_fuzzer
+```
diff --git a/keystore2/src/fuzzers/aidl-fuzzers/authorization_service_fuzzer.rs b/keystore2/src/fuzzers/aidl-fuzzers/authorization_service_fuzzer.rs
new file mode 100644
index 0000000..c1b2098
--- /dev/null
+++ b/keystore2/src/fuzzers/aidl-fuzzers/authorization_service_fuzzer.rs
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#![allow(missing_docs)]
+#![no_main]
+#[macro_use]
+extern crate libfuzzer_sys;
+
+use binder_random_parcel_rs::fuzz_service;
+use keystore2::authorization::AuthorizationManager;
+
+fuzz_target!(|data: &[u8]| {
+    let authorization_service = AuthorizationManager::new_native_binder().unwrap_or_else(|e| {
+        panic!("Failed to create android.security.authorization service because of {:?}", e);
+    });
+    fuzz_service(&mut authorization_service.as_binder(), data);
+});
diff --git a/keystore2/src/fuzzers/keystore2_unsafe_fuzzer.rs b/keystore2/src/fuzzers/keystore2_unsafe_fuzzer.rs
new file mode 100644
index 0000000..0dca3a2
--- /dev/null
+++ b/keystore2/src/fuzzers/keystore2_unsafe_fuzzer.rs
@@ -0,0 +1,247 @@
+// Copyright 2022, 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.
+
+//! Fuzzes unsafe APIs of libkeystore2 module
+
+#![no_main]
+
+use binder::get_declared_instances;
+use keystore2::{legacy_blob::LegacyBlobLoader, utils::ui_opts_2_compat};
+use keystore2_aaid::get_aaid;
+use keystore2_apc_compat::ApcHal;
+use keystore2_crypto::{
+    aes_gcm_decrypt, aes_gcm_encrypt, ec_key_generate_key, ec_key_get0_public_key,
+    ec_key_marshal_private_key, ec_key_parse_private_key, ec_point_oct_to_point,
+    ec_point_point_to_oct, ecdh_compute_key, generate_random_data, hkdf_expand, hkdf_extract,
+    hmac_sha256, parse_subject_from_certificate, Password, ZVec,
+};
+use keystore2_selinux::{check_access, getpidcon, setcon, Backend, Context, KeystoreKeyBackend};
+use keystore2_vintf::get_hidl_instances;
+use libfuzzer_sys::{arbitrary::Arbitrary, fuzz_target};
+use std::{ffi::CString, sync::Arc};
+
+// Avoid allocating too much memory and crashing the fuzzer.
+const MAX_SIZE_MODIFIER: usize = 1024;
+
+/// CString does not contain any internal 0 bytes
+fn get_valid_cstring_data(data: &[u8]) -> &[u8] {
+    match data.iter().position(|&b| b == 0) {
+        Some(idx) => &data[0..idx],
+        None => data,
+    }
+}
+
+#[derive(Arbitrary, Debug)]
+enum FuzzCommand<'a> {
+    DecodeAlias {
+        string: String,
+    },
+    TryFrom {
+        vector_data: Vec<u8>,
+    },
+    GenerateRandomData {
+        size: usize,
+    },
+    HmacSha256 {
+        key_hmac: &'a [u8],
+        msg: &'a [u8],
+    },
+    AesGcmDecrypt {
+        data: &'a [u8],
+        iv: &'a [u8],
+        tag: &'a [u8],
+        key_aes_decrypt: &'a [u8],
+    },
+    AesGcmEecrypt {
+        plaintext: &'a [u8],
+        key_aes_encrypt: &'a [u8],
+    },
+    Password {
+        pw: &'a [u8],
+        salt: &'a [u8],
+        key_length: usize,
+    },
+    HkdfExtract {
+        hkdf_secret: &'a [u8],
+        hkdf_salt: &'a [u8],
+    },
+    HkdfExpand {
+        out_len: usize,
+        hkdf_prk: &'a [u8],
+        hkdf_info: &'a [u8],
+    },
+    PublicPrivateKey {
+        ec_priv_buf: &'a [u8],
+        ec_oct_buf: &'a [u8],
+    },
+    ParseSubjectFromCertificate {
+        parse_buf: &'a [u8],
+    },
+    GetHidlInstances {
+        hidl_package: &'a str,
+        major_version: usize,
+        minor_version: usize,
+        hidl_interface_name: &'a str,
+    },
+    GetAidlInstances {
+        aidl_package: &'a str,
+        aidl_interface_name: &'a str,
+    },
+    GetAaid {
+        aaid_uid: u32,
+    },
+    Hal {
+        opt: i32,
+        prompt_text: &'a str,
+        locale: &'a str,
+        extra_data: &'a [u8],
+    },
+    Context {
+        context: &'a str,
+    },
+    Backend {
+        namespace: &'a str,
+    },
+    GetPidCon {
+        pid: i32,
+    },
+    CheckAccess {
+        source: &'a [u8],
+        target: &'a [u8],
+        tclass: &'a str,
+        perm: &'a str,
+    },
+    SetCon {
+        set_target: &'a [u8],
+    },
+}
+
+fuzz_target!(|commands: Vec<FuzzCommand>| {
+    for command in commands {
+        match command {
+            FuzzCommand::DecodeAlias { string } => {
+                let _res = LegacyBlobLoader::decode_alias(&string);
+            }
+            FuzzCommand::TryFrom { vector_data } => {
+                let _res = ZVec::try_from(vector_data);
+            }
+            FuzzCommand::GenerateRandomData { size } => {
+                let _res = generate_random_data(size % MAX_SIZE_MODIFIER);
+            }
+            FuzzCommand::HmacSha256 { key_hmac, msg } => {
+                let _res = hmac_sha256(key_hmac, msg);
+            }
+            FuzzCommand::AesGcmDecrypt { data, iv, tag, key_aes_decrypt } => {
+                let _res = aes_gcm_decrypt(data, iv, tag, key_aes_decrypt);
+            }
+            FuzzCommand::AesGcmEecrypt { plaintext, key_aes_encrypt } => {
+                let _res = aes_gcm_encrypt(plaintext, key_aes_encrypt);
+            }
+            FuzzCommand::Password { pw, salt, key_length } => {
+                let _res = Password::from(pw).derive_key(salt, key_length % MAX_SIZE_MODIFIER);
+            }
+            FuzzCommand::HkdfExtract { hkdf_secret, hkdf_salt } => {
+                let _res = hkdf_extract(hkdf_secret, hkdf_salt);
+            }
+            FuzzCommand::HkdfExpand { out_len, hkdf_prk, hkdf_info } => {
+                let _res = hkdf_expand(out_len % MAX_SIZE_MODIFIER, hkdf_prk, hkdf_info);
+            }
+            FuzzCommand::PublicPrivateKey { ec_priv_buf, ec_oct_buf } => {
+                let check_private_key = {
+                    let mut check_private_key = ec_key_parse_private_key(ec_priv_buf);
+                    if check_private_key.is_err() {
+                        check_private_key = ec_key_generate_key();
+                    };
+                    check_private_key
+                };
+                let check_ecpoint = ec_point_oct_to_point(ec_oct_buf);
+                if check_private_key.is_ok() {
+                    let private_key = check_private_key.unwrap();
+                    ec_key_get0_public_key(&private_key);
+                    let _res = ec_key_marshal_private_key(&private_key);
+
+                    if check_ecpoint.is_ok() {
+                        let public_key = check_ecpoint.unwrap();
+                        let _res = ec_point_point_to_oct(public_key.get_point());
+                        let _res = ecdh_compute_key(public_key.get_point(), &private_key);
+                    }
+                }
+            }
+            FuzzCommand::ParseSubjectFromCertificate { parse_buf } => {
+                let _res = parse_subject_from_certificate(parse_buf);
+            }
+            FuzzCommand::GetHidlInstances {
+                hidl_package,
+                major_version,
+                minor_version,
+                hidl_interface_name,
+            } => {
+                get_hidl_instances(hidl_package, major_version, minor_version, hidl_interface_name);
+            }
+            FuzzCommand::GetAidlInstances { aidl_package, aidl_interface_name } => {
+                get_declared_instances(
+                    format!("{}.{}", aidl_package, aidl_interface_name).as_str(),
+                )
+                .unwrap();
+            }
+            FuzzCommand::GetAaid { aaid_uid } => {
+                let _res = get_aaid(aaid_uid);
+            }
+            FuzzCommand::Hal { opt, prompt_text, locale, extra_data } => {
+                let hal = ApcHal::try_get_service();
+                if hal.is_some() {
+                    let hal = Arc::new(hal.unwrap());
+                    let apc_compat_options = ui_opts_2_compat(opt);
+                    let prompt_text =
+                        std::str::from_utf8(get_valid_cstring_data(prompt_text.as_bytes()))
+                            .unwrap();
+                    let locale =
+                        std::str::from_utf8(get_valid_cstring_data(locale.as_bytes())).unwrap();
+                    let _res = hal.prompt_user_confirmation(
+                        prompt_text,
+                        extra_data,
+                        locale,
+                        apc_compat_options,
+                        move |_, _, _| {},
+                    );
+                }
+            }
+            FuzzCommand::Context { context } => {
+                let _res = Context::new(context);
+            }
+            FuzzCommand::Backend { namespace } => {
+                let backend = KeystoreKeyBackend::new();
+                if let Ok(backend) = backend {
+                    let _res = backend.lookup(namespace);
+                }
+            }
+            FuzzCommand::GetPidCon { pid } => {
+                let _res = getpidcon(pid);
+            }
+            FuzzCommand::CheckAccess { source, target, tclass, perm } => {
+                let source = get_valid_cstring_data(source);
+                let target = get_valid_cstring_data(target);
+                let _res = check_access(
+                    &CString::new(source).unwrap(),
+                    &CString::new(target).unwrap(),
+                    tclass,
+                    perm,
+                );
+            }
+            FuzzCommand::SetCon { set_target } => {
+                let _res = setcon(&CString::new(get_valid_cstring_data(set_target)).unwrap());
+            }
+        }
+    }
+});
diff --git a/keystore2/src/fuzzers/legacy_blob_fuzzer.rs b/keystore2/src/fuzzers/legacy_blob_fuzzer.rs
deleted file mode 100644
index 7e3e848..0000000
--- a/keystore2/src/fuzzers/legacy_blob_fuzzer.rs
+++ /dev/null
@@ -1,26 +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.
-
-#![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 341aa0a..a033356 100644
--- a/keystore2/src/gc.rs
+++ b/keystore2/src/gc.rs
@@ -18,6 +18,7 @@
 //! optionally dispose of sensitive key material appropriately, and then delete
 //! the key entry from the database.
 
+use crate::ks_err;
 use crate::{
     async_task,
     database::{BlobMetaData, KeystoreDB, Uuid},
@@ -103,7 +104,7 @@
             let blobs = self
                 .db
                 .handle_next_superseded_blobs(&self.deleted_blob_ids, 20)
-                .context("In process_one_key: Trying to handle superseded blob.")?;
+                .context(ks_err!("Trying to handle superseded blob."))?;
             self.deleted_blob_ids = vec![];
             self.superseded_blobs = blobs;
         }
@@ -124,9 +125,8 @@
                     .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)
-                    .context("In process_one_key: Trying to invalidate key.")?;
+                    .context(ks_err!("Trying to unwrap to-be-deleted blob.",))?;
+                (self.invalidate_key)(uuid, &blob).context(ks_err!("Trying to invalidate key."))?;
             }
         }
         Ok(())
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index 14b3601..10d6f46 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -16,6 +16,7 @@
 //! database connections and connections to services that Keystore needs
 //! to talk to.
 
+use crate::ks_err;
 use crate::gc::Gc;
 use crate::legacy_blob::LegacyBlobLoader;
 use crate::legacy_importer::LegacyImporter;
@@ -30,7 +31,7 @@
 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,
+    IKeyMintDevice::BpKeyMintDevice, IKeyMintDevice::IKeyMintDevice,
     KeyMintHardwareInfo::KeyMintHardwareInfo, SecurityLevel::SecurityLevel,
 };
 use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
@@ -40,7 +41,7 @@
 use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
 use anyhow::{Context, Result};
 use binder::FromIBinder;
-use keystore2_vintf::get_aidl_instances;
+use binder::get_declared_instances;
 use lazy_static::lazy_static;
 use std::sync::{Arc, Mutex, RwLock};
 use std::{cell::RefCell, sync::Once};
@@ -132,26 +133,6 @@
     }
 }
 
-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: Strong<T>) {
-        self.devices_by_sec_level.insert(sec_level, dev);
-    }
-}
-
 lazy_static! {
     /// The path where keystore stores all its keys.
     pub static ref DB_PATH: RwLock<PathBuf> = RwLock::new(
@@ -162,10 +143,6 @@
     static ref KEY_MINT_DEVICES: Mutex<DevicesMap<dyn IKeyMintDevice>> = Default::default();
     /// Timestamp service.
     static ref TIME_STAMP_DEVICE: Mutex<Option<Strong<dyn ISecureClock>>> = Default::default();
-    /// RemotelyProvisionedComponent HAL devices.
-    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();
@@ -186,8 +163,8 @@
             Box::new(|uuid, blob| {
                 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.")
+                map_km_error(km_dev.deleteKey(blob))
+                    .context(ks_err!("Trying to invalidate key blob."))
             }),
             KeystoreDB::new(&DB_PATH.read().expect("Could not get the database directory."), None)
                 .expect("Failed to open database."),
@@ -196,42 +173,37 @@
     }));
 }
 
-static KEYMINT_SERVICE_NAME: &str = "android.hardware.security.keymint.IKeyMintDevice";
-
 /// 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", version as usize, "IKeyMintDevice");
+fn keymint_service_name(security_level: &SecurityLevel) -> Result<Option<String>> {
+    let keymint_descriptor: &str = <BpKeyMintDevice as IKeyMintDevice>::get_descriptor();
+    let keymint_instances = get_declared_instances(keymint_descriptor).unwrap();
 
     let service_name = match *security_level {
         SecurityLevel::TRUSTED_ENVIRONMENT => {
             if keymint_instances.iter().any(|instance| *instance == "default") {
-                Some(format!("{}/default", KEYMINT_SERVICE_NAME))
+                Some(format!("{}/default", keymint_descriptor))
             } else {
                 None
             }
         }
         SecurityLevel::STRONGBOX => {
             if keymint_instances.iter().any(|instance| *instance == "strongbox") {
-                Some(format!("{}/strongbox", KEYMINT_SERVICE_NAME))
+                Some(format!("{}/strongbox", keymint_descriptor))
             } else {
                 None
             }
         }
         _ => {
-            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
+            return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)).context(ks_err!(
+                "Trying to find keymint for security level: {:?}",
+                security_level
             ));
         }
     };
 
-    Ok(service_name.map(|service_name| (version, service_name)))
+    Ok(service_name)
 }
 
 /// Make a new connection to a KeyMint device of the given security level.
@@ -240,35 +212,29 @@
 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.")?;
+    // Connects to binder to get the current keymint interface and
+    // based on the security level returns a service name to connect
+    // to.
+    let service_name = keymint_service_name(security_level).context(ks_err!("Get service name"))?;
 
-    let (keymint, hal_version) = if let Some((version, service_name)) = service_name {
+    let (keymint, hal_version) = if let Some(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.")?;
+                .context(ks_err!("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
+        // - V3 is 300
         // etc.
-        (km, Some(version * 100))
+        let km_version = km.getInterfaceVersion()?;
+        (km, Some(km_version * 100))
     } else {
         // 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 connect_keymint: Trying to connect to compat service.")?;
+                .context(ks_err!("Trying to connect to compat service."))?;
         (
             map_binder_status(keystore_compat_service.getKeyMintDevice(*security_level))
                 .map_err(|e| match e {
@@ -277,7 +243,7 @@
                     }
                     e => e,
                 })
-                .context("In connect_keymint: Trying to get Legacy wrapper.")?,
+                .context(ks_err!("Trying to get Legacy wrapper."))?,
             None,
         )
     };
@@ -285,8 +251,17 @@
     // 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(300) => {
+            // Current KeyMint version: use as-is as v3 Keymint is current version
+            log::info!(
+                "KeyMint device is current version ({:?}) for security level: {:?}",
+                hal_version,
+                security_level
+            );
+            keymint
+        }
         Some(200) => {
-            // Current KeyMint version: use as-is.
+            // Previous KeyMint version: use as-is as we don't have any software emulation of v3-specific KeyMint features.
             log::info!(
                 "KeyMint device is current version ({:?}) for security level: {:?}",
                 hal_version,
@@ -302,7 +277,7 @@
                 security_level
             );
             BacklevelKeyMintWrapper::wrap(KeyMintV1::new(*security_level), keymint)
-                .context("In connect_keymint: Trying to create V1 compatibility wrapper.")?
+                .context(ks_err!("Trying to create V1 compatibility wrapper."))?
         }
         None => {
             // Compatibility wrapper around a KeyMaster device: this roughly
@@ -312,21 +287,21 @@
                 "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 .",
-            )?
+            BacklevelKeyMintWrapper::wrap(KeyMintV1::new(*security_level), keymint)
+                .context(ks_err!("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
-            ))
+            return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)).context(ks_err!(
+                "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.")?;
+    let mut hw_info =
+        map_km_error(keymint.getHardwareInfo()).context(ks_err!("Failed to get hardware info."))?;
     drop(wp);
 
     // The legacy wrapper sets hw_info.versionNumber to the underlying HAL version like so:
@@ -356,7 +331,8 @@
     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.")?;
+        let (dev, hw_info) =
+            connect_keymint(security_level).context(ks_err!("Cannot connect to Keymint"))?;
         devices_map.insert(*security_level, dev, hw_info);
         // Unwrap must succeed because we just inserted it.
         Ok(devices_map.dev_by_sec_level(security_level).unwrap())
@@ -374,7 +350,7 @@
     if let Some((dev, hw_info, _)) = devices_map.dev_by_uuid(uuid) {
         Ok((dev, hw_info))
     } else {
-        Err(Error::sys()).context("In get_keymint_dev_by_uuid: No KeyMint instance found.")
+        Err(Error::sys()).context(ks_err!("No KeyMint instance found."))
     }
 }
 
@@ -390,7 +366,7 @@
 /// to connect to the legacy wrapper.
 fn connect_secureclock() -> Result<Strong<dyn ISecureClock>> {
     let secureclock_instances =
-        get_aidl_instances("android.hardware.security.secureclock", 1, "ISecureClock");
+        get_declared_instances("android.hardware.security.secureclock.ISecureClock").unwrap();
 
     let secure_clock_available =
         secureclock_instances.iter().any(|instance| *instance == "default");
@@ -399,14 +375,14 @@
 
     let secureclock = if secure_clock_available {
         map_binder_status_code(binder::get_interface(&default_time_stamp_service_name))
-            .context("In connect_secureclock: Trying to connect to genuine secure clock service.")
+            .context(ks_err!("Trying to connect to genuine secure clock service."))
     } else {
         // 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 connect_secureclock: Trying to connect to compat service.")?;
+                .context(ks_err!("Trying to connect to compat service."))?;
 
         // Legacy secure clock services were only implemented by TEE.
         map_binder_status(keystore_compat_service.getSecureClock())
@@ -416,7 +392,7 @@
                 }
                 e => e,
             })
-            .context("In connect_secureclock: Trying to get Legacy wrapper.")
+            .context(ks_err!("Trying to get Legacy wrapper."))
     }?;
 
     Ok(secureclock)
@@ -429,7 +405,7 @@
     if let Some(dev) = &*ts_device {
         Ok(dev.clone())
     } else {
-        let dev = connect_secureclock().context("In get_timestamp_service.")?;
+        let dev = connect_secureclock().context(ks_err!())?;
         *ts_device = Some(dev.clone());
         Ok(dev)
     }
@@ -438,13 +414,12 @@
 static REMOTE_PROVISIONING_HAL_SERVICE_NAME: &str =
     "android.hardware.security.keymint.IRemotelyProvisionedComponent";
 
-fn connect_remotely_provisioned_component(
-    security_level: &SecurityLevel,
-) -> Result<Strong<dyn IRemotelyProvisionedComponent>> {
+/// Get the service name of a remotely provisioned component corresponding to given security level.
+pub fn get_remotely_provisioned_component_name(security_level: &SecurityLevel) -> Result<String> {
     let remotely_prov_instances =
-        get_aidl_instances("android.hardware.security.keymint", 1, "IRemotelyProvisionedComponent");
+        get_declared_instances(REMOTE_PROVISIONING_HAL_SERVICE_NAME).unwrap();
 
-    let service_name = match *security_level {
+    match *security_level {
         SecurityLevel::TRUSTED_ENVIRONMENT => {
             if remotely_prov_instances.iter().any(|instance| *instance == "default") {
                 Some(format!("{}/default", REMOTE_PROVISIONING_HAL_SERVICE_NAME))
@@ -462,31 +437,5 @@
         _ => None,
     }
     .ok_or(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
-    .context("In connect_remotely_provisioned_component.")?;
-
-    let rem_prov_hal: Strong<dyn IRemotelyProvisionedComponent> =
-        map_binder_status_code(binder::get_interface(&service_name))
-            .context(concat!(
-                "In connect_remotely_provisioned_component: Trying to connect to",
-                " RemotelyProvisionedComponent service."
-            ))
-            .map_err(|e| e)?;
-    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<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) {
-        Ok(dev)
-    } else {
-        let dev = connect_remotely_provisioned_component(security_level)
-            .context("In get_remotely_provisioned_component.")?;
-        devices_map.insert(*security_level, dev);
-        // Unwrap must succeed because we just inserted it.
-        Ok(devices_map.dev_by_sec_level(security_level).unwrap())
-    }
+    .context(ks_err!())
 }
diff --git a/keystore2/src/id_rotation.rs b/keystore2/src/id_rotation.rs
index e3992d8..460caa7 100644
--- a/keystore2/src/id_rotation.rs
+++ b/keystore2/src/id_rotation.rs
@@ -20,6 +20,8 @@
 //! It is assumed that the timestamp file does not exist after a factory reset. So the creation
 //! time of the timestamp file provides a lower bound for the time since factory reset.
 
+use crate::ks_err;
+
 use anyhow::{Context, Result};
 use std::fs;
 use std::io::ErrorKind;
@@ -66,7 +68,7 @@
                 _ => Err(e).context("Failed to open timestamp file."),
             },
         }
-        .context("In had_factory_reset_since_id_rotation:")
+        .context(ks_err!())
     }
 }
 
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
index 9854974..5da95d9 100644
--- a/keystore2/src/key_parameter.rs
+++ b/keystore2/src/key_parameter.rs
@@ -837,6 +837,11 @@
     #[serde(serialize_with = "serialize_primitive")]
     #[key_param(tag = DIGEST, field = Digest)]
     Digest(Digest),
+    /// Digest algorithms that can be used for MGF in RSA-OAEP.
+    #[serde(deserialize_with = "deserialize_primitive")]
+    #[serde(serialize_with = "serialize_primitive")]
+    #[key_param(tag = RSA_OAEP_MGF_DIGEST, field = Digest)]
+    RsaOaepMgfDigest(Digest),
     /// Padding modes that may be used with the key.  Relevant to RSA, AES and 3DES keys.
     #[serde(deserialize_with = "deserialize_primitive")]
     #[serde(serialize_with = "serialize_primitive")]
@@ -966,9 +971,12 @@
     /// Provides the device's serial number, to attestKey()
     #[key_param(tag = ATTESTATION_ID_SERIAL, field = Blob)]
     AttestationIdSerial(Vec<u8>),
-    /// Provides the IMEIs for all radios on the device, to attestKey()
+    /// Provides the primary IMEI for the device, to attestKey()
     #[key_param(tag = ATTESTATION_ID_IMEI, field = Blob)]
     AttestationIdIMEI(Vec<u8>),
+    /// Provides a second IMEI for the device, to attestKey()
+    #[key_param(tag = ATTESTATION_ID_SECOND_IMEI, field = Blob)]
+    AttestationIdSecondIMEI(Vec<u8>),
     /// Provides the MEIDs for all radios on the device, to attestKey()
     #[key_param(tag = ATTESTATION_ID_MEID, field = Blob)]
     AttestationIdMEID(Vec<u8>),
@@ -1095,6 +1103,7 @@
             Tag::BLOCK_MODE => return KmKeyParameterValue::BlockMode(Default::default()),
             Tag::PADDING => return KmKeyParameterValue::PaddingMode(Default::default()),
             Tag::DIGEST => return KmKeyParameterValue::Digest(Default::default()),
+            Tag::RSA_OAEP_MGF_DIGEST => return KmKeyParameterValue::Digest(Default::default()),
             Tag::EC_CURVE => return KmKeyParameterValue::EcCurve(Default::default()),
             Tag::ORIGIN => return KmKeyParameterValue::Origin(Default::default()),
             Tag::PURPOSE => return KmKeyParameterValue::KeyPurpose(Default::default()),
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index 55f5d15..31c1e29 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -19,9 +19,6 @@
 use keystore2::maintenance::Maintenance;
 use keystore2::metrics::Metrics;
 use keystore2::metrics_store;
-use keystore2::remote_provisioning::{
-    RemoteProvisioningService, RemotelyProvisionedKeyPoolService,
-};
 use keystore2::service::KeystoreService;
 use keystore2::{apc::ApcManager, shared_secret_negotiation};
 use keystore2::{authorization::AuthorizationManager, id_rotation::IdRotationState};
@@ -34,9 +31,6 @@
 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";
 
@@ -47,7 +41,16 @@
         android_logger::Config::default()
             .with_tag("keystore2")
             .with_min_level(log::Level::Debug)
-            .with_log_id(android_logger::LogId::System),
+            .with_log_id(android_logger::LogId::System)
+            .format(|buf, record| {
+                writeln!(
+                    buf,
+                    "{}:{} - {}",
+                    record.file().unwrap_or("unknown"),
+                    record.line().unwrap_or(0),
+                    record.args()
+                )
+            }),
     );
     // Redirect panic messages to logcat.
     panic::set_hook(Box::new(|panic_info| {
@@ -137,40 +140,6 @@
         panic!("Failed to register service {} because of {:?}.", METRICS_SERVICE_NAME, e);
     });
 
-    // Devices with KS2 and KM 1.0 may not have any IRemotelyProvisionedComponent HALs at all. Do
-    // not panic if new_native_binder returns failure because it could not find the TEE HAL.
-    if let Ok(remote_provisioning_service) = RemoteProvisioningService::new_native_binder() {
-        binder::add_service(
-            REMOTE_PROVISIONING_SERVICE_NAME,
-            remote_provisioning_service.as_binder(),
-        )
-        .unwrap_or_else(|e| {
-            panic!(
-                "Failed to register service {} because of {:?}.",
-                REMOTE_PROVISIONING_SERVICE_NAME, e
-            );
-        });
-    }
-
-    // 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
index 788beef..035edd9 100644
--- a/keystore2/src/km_compat.rs
+++ b/keystore2/src/km_compat.rs
@@ -15,6 +15,7 @@
 //! Provide a wrapper around a KeyMint device that allows up-level features to
 //! be emulated on back-level devices.
 
+use crate::ks_err;
 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;
@@ -81,7 +82,7 @@
     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")?;
+        .context(ks_err!("failed to calculate HMAC-SHA256"))?;
     result.extend_from_slice(&tag);
     Ok(result)
 }
@@ -138,10 +139,9 @@
         // 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 keystore_compat_service: Strong<dyn IKeystoreCompatService> =
+            map_binder_status_code(binder::get_interface("android.security.compat"))
+                .context(ks_err!("Trying to connect to compat service."))?;
         let soft =
             map_binder_status(keystore_compat_service.getKeyMintDevice(SecurityLevel::SOFTWARE))
                 .map_err(|e| match e {
@@ -150,7 +150,7 @@
                     }
                     e => e,
                 })
-                .context("In BacklevelKeyMintWrapper::wrap: Trying to get software device.")?;
+                .context(ks_err!("Trying to get software device."))?;
 
         Ok(BnKeyMintDevice::new_binder(
             Self { real, soft, emu },
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index 6d0630b..e27cd1c 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -500,8 +500,30 @@
 ScopedAStatus KeyMintDevice::importKey(const std::vector<KeyParameter>& inKeyParams,
                                        KeyFormat in_inKeyFormat,
                                        const std::vector<uint8_t>& in_inKeyData,
-                                       const std::optional<AttestationKey>& /* in_attestationKey */,
+                                       const std::optional<AttestationKey>& in_attestationKey,
                                        KeyCreationResult* out_creationResult) {
+    // Since KeyMaster doesn't support ECDH, route all ECDH key import requests to
+    // soft-KeyMint.
+    //
+    // For this to work we'll need to also route begin() and deleteKey() calls to
+    // soft-KM. In order to do that, we'll prefix all keyblobs with whether it was
+    // created by the real underlying KeyMaster HAL or whether it was created by
+    // soft-KeyMint.
+    //
+    // See keyBlobPrefix() for more discussion.
+    //
+    for (const auto& keyParam : inKeyParams) {
+        if (keyParam.tag == Tag::PURPOSE &&
+            keyParam.value.get<KeyParameterValue::Tag::keyPurpose>() == KeyPurpose::AGREE_KEY) {
+            auto ret = softKeyMintDevice_->importKey(inKeyParams, in_inKeyFormat, in_inKeyData,
+                                                     in_attestationKey, out_creationResult);
+            if (ret.isOk()) {
+                out_creationResult->keyBlob = keyBlobPrefix(out_creationResult->keyBlob, true);
+            }
+            return ret;
+        }
+    }
+
     auto legacyKeyGENParams = convertKeyParametersToLegacy(extractGenerationParams(inKeyParams));
     auto legacyKeyFormat = convertKeyFormatToLegacy(in_inKeyFormat);
     KMV1::ErrorCode errorCode;
@@ -574,6 +596,17 @@
     auto legacyUpgradeParams = convertKeyParametersToLegacy(in_inUpgradeParams);
     V4_0_ErrorCode errorCode;
 
+    if (prefixedKeyBlobIsSoftKeyMint(in_inKeyBlobToUpgrade)) {
+        auto status = softKeyMintDevice_->upgradeKey(
+            prefixedKeyBlobRemovePrefix(in_inKeyBlobToUpgrade), in_inUpgradeParams, _aidl_return);
+        if (!status.isOk()) {
+            LOG(ERROR) << __func__ << " transaction failed. " << status.getDescription();
+        } else {
+            *_aidl_return = keyBlobPrefix(*_aidl_return, true);
+        }
+        return status;
+    }
+
     auto result =
         mDevice->upgradeKey(prefixedKeyBlobRemovePrefix(in_inKeyBlobToUpgrade), legacyUpgradeParams,
                             [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& upgradedKeyBlob) {
diff --git a/keystore2/src/km_compat/km_compat_type_conversion.h b/keystore2/src/km_compat/km_compat_type_conversion.h
index 33248a4..5db7e3d 100644
--- a/keystore2/src/km_compat/km_compat_type_conversion.h
+++ b/keystore2/src/km_compat/km_compat_type_conversion.h
@@ -750,6 +750,7 @@
     case KMV1::Tag::CERTIFICATE_SUBJECT:
     case KMV1::Tag::CERTIFICATE_NOT_BEFORE:
     case KMV1::Tag::CERTIFICATE_NOT_AFTER:
+    case KMV1::Tag::ATTESTATION_ID_SECOND_IMEI:
         // These tags do not exist in KM < KeyMint 1.0.
         break;
     case KMV1::Tag::MAX_BOOT_LEVEL:
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
index 13f7760..2632ec4 100644
--- a/keystore2/src/km_compat/lib.rs
+++ b/keystore2/src/km_compat/lib.rs
@@ -450,6 +450,10 @@
         )));
         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/ks_err.rs b/keystore2/src/ks_err.rs
new file mode 100644
index 0000000..c9c38c0
--- /dev/null
+++ b/keystore2/src/ks_err.rs
@@ -0,0 +1,35 @@
+// 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.
+
+//! A ks_err macro that expands error messages to include the file and line number
+
+///
+/// # Examples
+///
+/// ```
+/// use crate::ks_err;
+///
+/// ks_err!("Key is expired.");
+/// Result:
+/// "src/lib.rs:7 Key is expired."
+/// ```
+#[macro_export]
+macro_rules! ks_err {
+    { $($arg:tt)+ } => {
+        format!("{}:{}: {}", file!(), line!(), format_args!($($arg)+))
+    };
+    {} => {
+        format!("{}:{}", file!(), line!())
+    };
+}
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index d75bfd2..2ffcc71 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -14,6 +14,7 @@
 
 //! This module implements methods to load legacy keystore key blob files.
 
+use crate::ks_err;
 use crate::{
     error::{Error as KsError, ResponseCode},
     key_parameter::{KeyParameter, KeyParameterValue},
@@ -320,7 +321,7 @@
                     acc.push(c as char);
                 }
                 c => {
-                    acc.push((b'+' + (c as u8 >> 6)) as char);
+                    acc.push((b'+' + (c >> 6)) as char);
                     acc.push((b'0' + (c & 0x3F)) as char);
                 }
             };
@@ -348,24 +349,23 @@
                     None
                 }
                 _ => {
-                    return Err(Error::BadEncoding)
-                        .context("In decode_alias: could not decode filename.")
+                    return Err(Error::BadEncoding).context(ks_err!("could not decode filename."));
                 }
             };
         }
         if multi.is_some() {
-            return Err(Error::BadEncoding).context("In decode_alias: could not decode filename.");
+            return Err(Error::BadEncoding).context(ks_err!("could not decode filename."));
         }
 
-        String::from_utf8(s).context("In decode_alias: encoded alias was not valid UTF-8.")
+        String::from_utf8(s).context(ks_err!("encoded alias was not valid UTF-8."))
     }
 
     fn new_from_stream(stream: &mut dyn Read) -> Result<Blob> {
         let mut buffer = Vec::new();
-        stream.read_to_end(&mut buffer).context("In new_from_stream.")?;
+        stream.read_to_end(&mut buffer).context(ks_err!())?;
 
         if buffer.len() < Self::COMMON_HEADER_SIZE {
-            return Err(Error::BadLen).context("In new_from_stream.")?;
+            return Err(Error::BadLen).context(ks_err!())?;
         }
 
         let version: u8 = buffer[Self::VERSION_OFFSET];
@@ -380,15 +380,15 @@
 
         if version != SUPPORTED_LEGACY_BLOB_VERSION {
             return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
-                .context(format!("In new_from_stream: Unknown blob version: {}.", version));
+                .context(ks_err!("Unknown blob version: {}.", version));
         }
 
         let length = u32::from_be_bytes(
             buffer[Self::LENGTH_OFFSET..Self::LENGTH_OFFSET + 4].try_into().unwrap(),
         ) as usize;
         if buffer.len() < Self::COMMON_HEADER_SIZE + length {
-            return Err(Error::BadLen).context(format!(
-                "In new_from_stream. Expected: {} got: {}.",
+            return Err(Error::BadLen).context(ks_err!(
+                "Expected: {} got: {}.",
                 Self::COMMON_HEADER_SIZE + length,
                 buffer.len()
             ));
@@ -457,11 +457,12 @@
             }),
             (blob_types::SUPER_KEY, _, None) | (blob_types::SUPER_KEY_AES256, _, None) => {
                 Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
-                    .context("In new_from_stream: Super key without salt for key derivation.")
+                    .context(ks_err!("Super key without salt for key derivation."))
             }
-            _ => Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
-                "In new_from_stream: Unknown blob type. {} {}",
-                blob_type, is_encrypted
+            _ => Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(ks_err!(
+                "Unknown blob type. {} {}",
+                blob_type,
+                is_encrypted
             )),
         }
     }
@@ -482,30 +483,23 @@
     where
         F: FnOnce(&[u8], &[u8], &[u8], Option<&[u8]>, Option<usize>) -> Result<ZVec>,
     {
-        let blob =
-            Self::new_from_stream(&mut stream).context("In new_from_stream_decrypt_with.")?;
+        let blob = Self::new_from_stream(&mut stream).context(ks_err!())?;
 
         match blob.value() {
             BlobValue::Encrypted { iv, tag, data } => Ok(Blob {
                 flags: blob.flags,
-                value: BlobValue::Decrypted(
-                    decrypt(data, iv, tag, None, None)
-                        .context("In new_from_stream_decrypt_with.")?,
-                ),
+                value: BlobValue::Decrypted(decrypt(data, iv, tag, None, None).context(ks_err!())?),
             }),
             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))
-                        .context("In new_from_stream_decrypt_with.")?,
+                    decrypt(data, iv, tag, Some(salt), Some(*key_size)).context(ks_err!())?,
                 ),
             }),
             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(),
+                    decrypt(data, iv, tag, None, None).context(ks_err!())?[..].to_vec(),
                 ),
             }),
 
@@ -548,33 +542,30 @@
     /// | 32 bit indirect_offset |    Offset from the beginning of the indirect section.
     /// +------------------------+
     pub fn read_key_parameters(stream: &mut &[u8]) -> Result<Vec<KeyParameterValue>> {
-        let indirect_size =
-            read_ne_u32(stream).context("In read_key_parameters: While reading indirect size.")?;
+        let indirect_size = read_ne_u32(stream).context(ks_err!("While reading indirect size."))?;
 
         let indirect_buffer = stream
             .get(0..indirect_size as usize)
             .ok_or(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
-            .context("In read_key_parameters: While reading indirect buffer.")?;
+            .context(ks_err!("While reading indirect buffer."))?;
 
         // update the stream position.
         *stream = &stream[indirect_size as usize..];
 
-        let element_count =
-            read_ne_u32(stream).context("In read_key_parameters: While reading element count.")?;
-        let element_size =
-            read_ne_u32(stream).context("In read_key_parameters: While reading element size.")?;
+        let element_count = read_ne_u32(stream).context(ks_err!("While reading element count."))?;
+        let element_size = read_ne_u32(stream).context(ks_err!("While reading element size."))?;
 
         let mut element_stream = stream
             .get(0..element_size as usize)
             .ok_or(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
-            .context("In read_key_parameters: While reading elements buffer.")?;
+            .context(ks_err!("While reading elements buffer."))?;
 
         // update the stream position.
         *stream = &stream[element_size as usize..];
 
         let mut params: Vec<KeyParameterValue> = Vec::new();
         for _ in 0..element_count {
-            let tag = Tag(read_ne_i32(&mut element_stream).context("In read_key_parameters.")?);
+            let tag = Tag(read_ne_i32(&mut element_stream).context(ks_err!())?);
             let param = match Self::tag_type(tag) {
                 TagType::ENUM | TagType::ENUM_REP | TagType::UINT | TagType::UINT_REP => {
                     KeyParameterValue::new_from_tag_primitive_pair(
@@ -617,7 +608,7 @@
                 TagType::INVALID => Err(anyhow::anyhow!("Invalid.")),
                 _ => {
                     return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
-                        .context("In read_key_parameters: Encountered bogus tag type.");
+                        .context(ks_err!("Encountered bogus tag type."));
                 }
             };
             if let Ok(p) = param {
@@ -647,9 +638,11 @@
             {
                 Ok(Blob {
                     value: BlobValue::Characteristics(
-                        super_key.as_ref().unwrap().decrypt(&data, &iv, &tag).context(
-                            "In decrypt_if_required: Failed to decrypt EncryptedCharacteristics",
-                        )?[..]
+                        super_key
+                            .as_ref()
+                            .unwrap()
+                            .decrypt(&data, &iv, &tag)
+                            .context(ks_err!("Failed to decrypt EncryptedCharacteristics"))?[..]
                             .to_vec(),
                     ),
                     flags,
@@ -664,7 +657,7 @@
                             .as_ref()
                             .unwrap()
                             .decrypt(&data, &iv, &tag)
-                            .context("In decrypt_if_required: Failed to decrypt Encrypted")?,
+                            .context(ks_err!("Failed to decrypt Encrypted"))?,
                     ),
                     flags,
                 })
@@ -678,7 +671,7 @@
                             .as_ref()
                             .unwrap()
                             .decrypt(&data, &iv, &tag)
-                            .context("In decrypt_if_required: Failed to decrypt Encrypted")?[..]
+                            .context(ks_err!("Failed to decrypt Encrypted"))?[..]
                             .to_vec(),
                     ),
                     flags,
@@ -687,7 +680,7 @@
             // 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."),
+                .context(ks_err!("Encountered encrypted blob without super key.")),
         }
     }
 
@@ -700,7 +693,7 @@
         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")?;
+            .context(ks_err!())?;
 
         let blob = match blob {
             None => return Ok(LegacyKeyCharacteristics::Cache(Vec::new())),
@@ -708,16 +701,14 @@
         };
 
         let blob = Self::decrypt_if_required(super_key, blob)
-            .context("In read_characteristics_file: Trying to decrypt blob.")?;
+            .context(ks_err!("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: ",
-                    "Characteristics file does not hold key characteristics."
-                ))
+                return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
+                    .context(ks_err!("Characteristics file does not hold key characteristics."));
             }
         };
 
@@ -726,7 +717,7 @@
             // the hardware enforced list.
             BlobValue::CharacteristicsCache(_) => Some(
                 Self::read_key_parameters(&mut stream)
-                    .context("In read_characteristics_file.")?
+                    .context(ks_err!())?
                     .into_iter()
                     .map(|value| KeyParameter::new(value, hw_sec_level)),
             ),
@@ -734,7 +725,7 @@
         };
 
         let sw_list = Self::read_key_parameters(&mut stream)
-            .context("In read_characteristics_file.")?
+            .context(ks_err!())?
             .into_iter()
             .map(|value| KeyParameter::new(value, SecurityLevel::KEYSTORE));
 
@@ -786,11 +777,11 @@
             Ok(file) => file,
             Err(e) => match e.kind() {
                 ErrorKind::NotFound => return Ok(None),
-                _ => return Err(e).context("In read_generic_blob."),
+                _ => return Err(e).context(ks_err!()),
             },
         };
 
-        Ok(Some(Self::new_from_stream(&mut file).context("In read_generic_blob.")?))
+        Ok(Some(Self::new_from_stream(&mut file).context(ks_err!())?))
     }
 
     fn read_generic_blob_decrypt_with<F>(path: &Path, decrypt: F) -> Result<Option<Blob>>
@@ -801,14 +792,11 @@
             Ok(file) => file,
             Err(e) => match e.kind() {
                 ErrorKind::NotFound => return Ok(None),
-                _ => return Err(e).context("In read_generic_blob_decrypt_with."),
+                _ => return Err(e).context(ks_err!()),
             },
         };
 
-        Ok(Some(
-            Self::new_from_stream_decrypt_with(&mut file, decrypt)
-                .context("In read_generic_blob_decrypt_with.")?,
-        ))
+        Ok(Some(Self::new_from_stream_decrypt_with(&mut file, decrypt).context(ks_err!())?))
     }
 
     /// Read a legacy keystore entry blob.
@@ -827,7 +815,7 @@
         };
 
         let blob = Self::read_generic_blob_decrypt_with(&path, decrypt)
-            .context("In read_legacy_keystore_entry: Failed to read blob.")?;
+            .context(ks_err!("Failed to read blob."))?;
 
         Ok(blob.and_then(|blob| match blob.value {
             BlobValue::Generic(blob) => Some(blob),
@@ -848,13 +836,13 @@
         if let Err(e) = Self::with_retry_interrupted(|| fs::remove_file(path.as_path())) {
             match e.kind() {
                 ErrorKind::NotFound => return Ok(false),
-                _ => return Err(e).context("In remove_legacy_keystore_entry."),
+                _ => return Err(e).context(ks_err!()),
             }
         }
 
         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(ks_err!("Trying to remove empty user dir."))?;
         Ok(true)
     }
 
@@ -869,27 +857,21 @@
             Err(e) => match e.kind() {
                 ErrorKind::NotFound => return Ok(Default::default()),
                 _ => {
-                    return Err(e).context(format!(
-                        concat!(
-                            "In list_legacy_keystore_entries_for_uid: ,",
-                            "Failed to open legacy blob database: {:?}"
-                        ),
-                        path
-                    ))
+                    return Err(e)
+                        .context(ks_err!("Failed to open legacy blob database: {:?}", path));
                 }
             },
         };
         let mut result: Vec<String> = Vec::new();
         for entry in dir {
-            let file_name = entry
-                .context("In list_legacy_keystore_entries_for_uid: Trying to access dir entry")?
-                .file_name();
+            let file_name = entry.context(ks_err!("Trying to access dir entry"))?.file_name();
             if let Some(f) = file_name.to_str() {
                 let encoded_alias = &f[uid_str.len() + 1..];
                 if f.starts_with(&uid_str) && !Self::is_keystore_alias(encoded_alias) {
-                    result.push(Self::decode_alias(encoded_alias).context(
-                        "In list_legacy_keystore_entries_for_uid: Trying to decode alias.",
-                    )?)
+                    result.push(
+                        Self::decode_alias(encoded_alias)
+                            .context(ks_err!("Trying to decode alias."))?,
+                    )
                 }
             }
         }
@@ -911,9 +893,7 @@
         &self,
         user_id: u32,
     ) -> Result<HashMap<u32, HashSet<String>>> {
-        let user_entries = self
-            .list_user(user_id)
-            .context("In list_legacy_keystore_entries_for_user: Trying to list user.")?;
+        let user_entries = self.list_user(user_id).context(ks_err!("Trying to list user."))?;
 
         let result =
             user_entries.into_iter().fold(HashMap::<u32, HashSet<String>>::new(), |mut acc, v| {
@@ -986,9 +966,9 @@
     /// in the database dir.
     pub fn is_empty(&self) -> Result<bool> {
         let dir = Self::with_retry_interrupted(|| fs::read_dir(self.path.as_path()))
-            .context("In is_empty: Failed to open legacy blob database.")?;
+            .context(ks_err!("Failed to open legacy blob database."))?;
         for entry in dir {
-            if (*entry.context("In is_empty: Trying to access dir entry")?.file_name())
+            if (*entry.context(ks_err!("Trying to access dir entry"))?.file_name())
                 .to_str()
                 .map_or(false, |f| f.starts_with("user_"))
             {
@@ -1007,7 +987,7 @@
             return Ok(true);
         }
         Ok(Self::with_retry_interrupted(|| user_path.read_dir())
-            .context("In is_empty_user: Failed to open legacy user dir.")?
+            .context(ks_err!("Failed to open legacy user dir."))?
             .next()
             .is_none())
     }
@@ -1032,16 +1012,14 @@
             Err(e) => match e.kind() {
                 ErrorKind::NotFound => return Ok(Default::default()),
                 _ => {
-                    return Err(e).context(format!(
-                        "In list_user: Failed to open legacy blob database. {:?}",
-                        path
-                    ))
+                    return Err(e)
+                        .context(ks_err!("Failed to open legacy blob database. {:?}", path));
                 }
             },
         };
         let mut result: Vec<String> = Vec::new();
         for entry in dir {
-            let file_name = entry.context("In list_user: Trying to access dir entry")?.file_name();
+            let file_name = entry.context(ks_err!("Trying to access dir entry"))?.file_name();
             if let Some(f) = file_name.to_str() {
                 result.push(f.to_string())
             }
@@ -1055,9 +1033,7 @@
         &self,
         user_id: u32,
     ) -> Result<HashMap<u32, HashSet<String>>> {
-        let user_entries = self
-            .list_user(user_id)
-            .context("In list_keystore_entries_for_user: Trying to list user.")?;
+        let user_entries = self.list_user(user_id).context(ks_err!("Trying to list user."))?;
 
         let result =
             user_entries.into_iter().fold(HashMap::<u32, HashSet<String>>::new(), |mut acc, v| {
@@ -1078,9 +1054,7 @@
     pub fn list_keystore_entries_for_uid(&self, uid: u32) -> Result<Vec<String>> {
         let user_id = uid_to_android_user(uid);
 
-        let user_entries = self
-            .list_user(user_id)
-            .context("In list_keystore_entries_for_uid: Trying to list user.")?;
+        let user_entries = self.list_user(user_id).context(ks_err!("Trying to list user."))?;
 
         let uid_str = format!("{}_", uid);
 
@@ -1163,7 +1137,7 @@
         if something_was_deleted {
             let user_id = uid_to_android_user(uid);
             self.remove_user_dir_if_empty(user_id)
-                .context("In remove_keystore_entry: Trying to remove empty user dir.")?;
+                .context(ks_err!("Trying to remove empty user dir."))?;
         }
 
         Ok(something_was_deleted)
@@ -1188,7 +1162,7 @@
         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."),
+            r => r.context(ks_err!("Trying to rename.")),
         }
     }
 
@@ -1207,7 +1181,7 @@
         }
 
         if uid_to_android_user(src_uid) != uid_to_android_user(dest_uid) {
-            return Err(Error::AndroidUserMismatch).context("In move_keystore_entry.");
+            return Err(Error::AndroidUserMismatch).context(ks_err!());
         }
 
         let prefixes = ["USRPKEY", "USRSKEY", "USRCERT", "CACERT"];
@@ -1220,12 +1194,7 @@
                 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
-                )
-            })?;
+            .with_context(|| ks_err!("Trying to move blob file with prefix: \"{}\"", prefix))?;
         }
 
         let prefixes = ["USRPKEY", "USRSKEY"];
@@ -1240,8 +1209,8 @@
                 |uid, alias, prefix| self.make_chr_filename(uid, alias, prefix),
             )
             .with_context(|| {
-                format!(
-                    "In move_keystore_entry: Trying to move characteristics file with \
+                ks_err!(
+                    "Trying to move characteristics file with \
                      prefix: \"{}\"",
                     prefix
                 )
@@ -1252,10 +1221,7 @@
     }
 
     fn remove_user_dir_if_empty(&self, user_id: u32) -> Result<()> {
-        if self
-            .is_empty_user(user_id)
-            .context("In remove_user_dir_if_empty: Trying to check for empty user dir.")?
-        {
+        if self.is_empty_user(user_id).context(ks_err!("Trying to check for empty user dir."))? {
             let user_path = self.make_user_path_name(user_id);
             Self::with_retry_interrupted(|| fs::remove_dir(user_path.as_path())).ok();
         }
@@ -1273,14 +1239,14 @@
 
         let km_blob = match km_blob {
             Some((km_blob, prefix)) => {
-                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 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(ks_err!("Found wrong blob type in legacy key blob file."))
+                    }
+                };
 
                 let hw_sec_level = match km_blob.is_strongbox() {
                     true => SecurityLevel::STRONGBOX,
@@ -1288,7 +1254,7 @@
                 };
                 let key_parameters = self
                     .read_characteristics_file(uid, &prefix, alias, hw_sec_level, super_key)
-                    .context("In load_by_uid_alias.")?;
+                    .context(ks_err!())?;
                 Some((km_blob, key_parameters))
             }
             None => None,
@@ -1296,34 +1262,34 @@
 
         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.")?;
+                .context(ks_err!("While loading user cert."))?;
 
         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.")?;
+                .context(ks_err!("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 USRCERT file");
+                    .context(ks_err!("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.")?;
+            .context(ks_err!("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.")?;
+                .context(ks_err!("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");
+                    .context(ks_err!("Found unexpected blob type in CACERT file"));
             }
         } else {
             None
@@ -1340,32 +1306,26 @@
     /// Load and decrypt legacy super key blob.
     pub fn load_super_key(&self, user_id: u32, pw: &Password) -> Result<Option<ZVec>> {
         let path = self.make_super_key_filename(user_id);
-        let blob = Self::read_generic_blob(&path)
-            .context("In load_super_key: While loading super key.")?;
+        let blob = Self::read_generic_blob(&path).context(ks_err!("While loading super key."))?;
 
         let blob = match blob {
             Some(blob) => match blob {
                 Blob { flags, value: BlobValue::PwEncrypted { iv, tag, data, salt, key_size } } => {
                     if (flags & flags::ENCRYPTED) != 0 {
                         let key = pw
-                            .derive_key(Some(&salt), key_size)
-                            .context("In load_super_key: Failed to derive key from password.")?;
-                        let blob = aes_gcm_decrypt(&data, &iv, &tag, &key).context(
-                            "In load_super_key: while trying to decrypt legacy super key blob.",
-                        )?;
+                            .derive_key(&salt, key_size)
+                            .context(ks_err!("Failed to derive key from password."))?;
+                        let blob = aes_gcm_decrypt(&data, &iv, &tag, &key)
+                            .context(ks_err!("while trying to decrypt legacy super key blob."))?;
                         Some(blob)
                     } else {
                         // In 2019 we had some unencrypted super keys due to b/141955555.
-                        Some(
-                            data.try_into()
-                                .context("In load_super_key: Trying to convert key into ZVec")?,
-                        )
+                        Some(data.try_into().context(ks_err!("Trying to convert key into ZVec"))?)
                     }
                 }
                 _ => {
-                    return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(
-                        "In load_super_key: Found wrong blob type in legacy super key blob file.",
-                    )
+                    return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
+                        .context(ks_err!("Found wrong blob type in legacy super key blob file."));
                 }
             },
             None => None,
@@ -1993,7 +1953,7 @@
         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 pw_key = TestKey(pw.derive_key(SUPERKEY_SALT, 32).unwrap());
         let super_key =
             Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
 
@@ -2080,7 +2040,7 @@
         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 pw_key = TestKey(pw.derive_key(SUPERKEY_SALT, 32).unwrap());
         let super_key =
             Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
 
@@ -2168,7 +2128,7 @@
         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 pw_key = TestKey(pw.derive_key(SUPERKEY_SALT, 32).unwrap());
         let super_key =
             Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
 
diff --git a/keystore2/src/legacy_importer.rs b/keystore2/src/legacy_importer.rs
index 93e1735..9eb702d 100644
--- a/keystore2/src/legacy_importer.rs
+++ b/keystore2/src/legacy_importer.rs
@@ -20,6 +20,7 @@
 };
 use crate::error::{map_km_error, Error};
 use crate::key_parameter::{KeyParameter, KeyParameterValue};
+use crate::ks_err;
 use crate::legacy_blob::{self, Blob, BlobValue, LegacyKeyCharacteristics};
 use crate::super_key::USER_SUPER_KEY;
 use crate::utils::{
@@ -185,9 +186,8 @@
                 }
                 (Self::STATE_UNINITIALIZED, false) => {
                     // Okay, tough luck. The legacy loader was really completely uninitialized.
-                    return Err(Error::sys()).context(
-                        "In check_state: Legacy loader should not be called uninitialized.",
-                    );
+                    return Err(Error::sys())
+                        .context(ks_err!("Legacy loader should not be called uninitialized."));
                 }
                 (Self::STATE_READY, _) => return Ok(Self::STATE_READY),
                 (s, _) => panic!("Unknown legacy importer state. {} ", s),
@@ -227,7 +227,7 @@
         F: FnOnce(&mut LegacyImporterState) -> Result<T> + Send + 'static,
     {
         // Short circuit if the database is empty or not initialized (error case).
-        match self.check_state().context("In do_serialized: Checking state.") {
+        match self.check_state().context(ks_err!("Checking state.")) {
             Ok(LegacyImporter::STATE_EMPTY) => return None,
             Ok(LegacyImporter::STATE_READY) => {}
             Err(e) => return Some(Err(e)),
@@ -266,7 +266,7 @@
 
         let (new_state, result) = match receiver.recv() {
             Err(e) => {
-                return Some(Err(e).context("In do_serialized. Failed to receive from the sender."))
+                return Some(Err(e).context(ks_err!("Failed to receive from the sender.")));
             }
             Ok(r) => r,
         };
@@ -357,7 +357,7 @@
             Ok(None) => {}
             Err(e) => return Err(e),
         }
-        let pw = pw.try_clone().context("In with_try_import_super_key: Cloning password.")?;
+        let pw = pw.try_clone().context(ks_err!("Cloning password."))?;
         let result = self.do_serialized(move |importer_state| {
             importer_state.check_and_import_super_key(user_id, &pw)
         });
@@ -424,7 +424,7 @@
         };
 
         self.sec_level_to_km_uuid.get(&sec_level).copied().ok_or_else(|| {
-            anyhow::anyhow!(Error::sys()).context("In get_km_uuid: No KM instance for blob.")
+            anyhow::anyhow!(Error::sys()).context(ks_err!("No KM instance for blob."))
         })
     }
 
@@ -446,7 +446,7 @@
         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")?
+            .context(ks_err!("Failed to load super key"))?
         {
             Some((_, entry)) => Ok(entry.id()),
             None => {
@@ -460,17 +460,14 @@
                 // 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.",
-                    )
+                    Err(Error::Rc(ResponseCode::LOCKED)).context(ks_err!(
+                        "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.")
+                    self.legacy_loader
+                        .remove_keystore_entry(uid, alias)
+                        .context(ks_err!("Trying to remove obsolete key."))?;
+                    Err(Error::Rc(ResponseCode::KEY_NOT_FOUND)).context(ks_err!("Obsolete key."))
                 }
             }
         }
@@ -487,99 +484,91 @@
         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))
+                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 km_uuid =
+            self.get_km_uuid(km_blob.is_strongbox()).context(ks_err!("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.")?;
+                let blob =
+                    super_key.decrypt(data, iv, tag).context(ks_err!("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.",
-                );
+                return Err(Error::Rc(ResponseCode::LOCKED)).context(ks_err!(
+                    "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.")
+                return Err(Error::sys()).context(ks_err!("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 (km_params, upgraded_blob) = get_key_characteristics_without_app_data(&km_uuid, &blob)
+            .context(ks_err!("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 (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(ks_err!("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 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),
-                    )
+                        let (data, iv, tag) = super_key
+                            .encrypt(&upgraded_blob)
+                            .context(ks_err!("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(ks_err!(
+                            "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(ks_err!(
+                                    "Failed to convert upgraded blob to ZVec."
+                                ))?),
+                            ),
+                            Some(superseded_blob),
+                        )
+                    }
+                    _ => {
+                        return Err(Error::sys()).context(ks_err!(
+                            "This should not be reachable. \
+                         Any other variant should have resulted in a different error."
+                        ));
+                    }
                 }
-                (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)
-        };
+            } else {
+                (km_blob, None)
+            };
 
         let params =
             augment_legacy_characteristics_file_with_key_characteristics(km_params, params);
@@ -595,10 +584,10 @@
         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 \
-                 our caller must not have called us otherwise.",
-            )
+            anyhow::anyhow!(Error::sys()).context(ks_err!(
+                "Must be Some because \
+                 our caller must not have called us otherwise."
+            ))
         })?;
 
         if self.recently_imported.contains(&RecentImport::new(uid, alias.clone())) {
@@ -632,11 +621,11 @@
                     e
                 }
             })
-            .context("In check_and_import: Trying to load legacy blob.")?;
+            .context(ks_err!("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.")?;
+            .context(ks_err!("Trying to update legacy characteristics."))?;
 
         let result = match km_blob_params {
             Some((km_blob, params)) => {
@@ -647,7 +636,7 @@
                         // Get super key id for user id.
                         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.")?;
+                            .context(ks_err!("Failed to get super key id."))?;
 
                         let mut blob_metadata = BlobMetaData::new();
                         blob_metadata.add(BlobMetaEntry::Iv(iv.to_vec()));
@@ -659,18 +648,17 @@
                     BlobValue::Decrypted(data) => (LegacyBlob::ZVec(data), BlobMetaData::new()),
                     _ => {
                         return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
-                            .context("In check_and_import: Legacy key has unexpected type.")
+                            .context(ks_err!("Legacy key has unexpected type."));
                     }
                 };
 
-                let km_uuid = self
-                    .get_km_uuid(is_strongbox)
-                    .context("In check_and_import: Trying to get KM UUID")?;
+                let km_uuid =
+                    self.get_km_uuid(is_strongbox).context(ks_err!("Trying to get KM UUID"))?;
                 blob_metadata.add(BlobMetaEntry::KmUuid(km_uuid));
 
                 let mut metadata = KeyMetaData::new();
-                let creation_date = DateTime::now()
-                    .context("In check_and_import: Trying to make creation time.")?;
+                let creation_date =
+                    DateTime::now().context(ks_err!("Trying to make creation time."))?;
                 metadata.add(KeyMetaEntry::CreationDate(creation_date));
 
                 let blob_info = BlobInfo::new_with_superseded(
@@ -689,18 +677,18 @@
                         &metadata,
                         &km_uuid,
                     )
-                    .context("In check_and_import.")?;
+                    .context(ks_err!())?;
                 Ok(())
             }
             None => {
                 if let Some(ca_cert) = ca_cert {
                     self.db
                         .store_new_certificate(&key, KeyType::Client, &ca_cert, &KEYSTORE_UUID)
-                        .context("In check_and_import: Failed to insert new certificate.")?;
+                        .context(ks_err!("Failed to insert new certificate."))?;
                     Ok(())
                 } else {
                     Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
-                        .context("In check_and_import: Legacy key not found.")
+                        .context(ks_err!("Legacy key not found."))
                 }
             }
         };
@@ -712,7 +700,7 @@
                 // Delete legacy key from the file system
                 self.legacy_loader
                     .remove_keystore_entry(uid, &alias)
-                    .context("In check_and_import: Trying to remove imported key.")?;
+                    .context(ks_err!("Trying to remove imported key."))?;
                 Ok(())
             }
             Err(e) => Err(e),
@@ -727,11 +715,11 @@
         if let Some(super_key) = self
             .legacy_loader
             .load_super_key(user_id, pw)
-            .context("In check_and_import_super_key: Trying to load legacy super key.")?
+            .context(ks_err!("Trying to load legacy super key."))?
         {
             let (blob, blob_metadata) =
                 crate::super_key::SuperKeyManager::encrypt_with_password(&super_key, pw)
-                    .context("In check_and_import_super_key: Trying to encrypt super key.")?;
+                    .context(ks_err!("Trying to encrypt super key."))?;
 
             self.db
                 .store_super_key(
@@ -741,16 +729,12 @@
                     &blob_metadata,
                     &KeyMetaData::new(),
                 )
-                .context(concat!(
-                    "In check_and_import_super_key: ",
-                    "Trying to insert legacy super_key into the database."
-                ))?;
+                .context(ks_err!("Trying to insert legacy super_key into the database."))?;
             self.legacy_loader.remove_super_key(user_id);
             self.recently_imported_super_key.insert(user_id);
             Ok(())
         } else {
-            Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
-                .context("In check_and_import_super_key: No key found do import.")
+            Err(Error::Rc(ResponseCode::KEY_NOT_FOUND)).context(ks_err!("No key found do import."))
         }
     }
 
@@ -765,7 +749,7 @@
             BulkDeleteRequest::Uid(uid) => (
                 self.legacy_loader
                     .list_keystore_entries_for_uid(uid)
-                    .context("In bulk_delete: Trying to get aliases for uid.")
+                    .context(ks_err!("Trying to get aliases for uid."))
                     .map(|aliases| {
                         let mut h = HashMap::<u32, HashSet<String>>::new();
                         h.insert(uid, aliases.into_iter().collect());
@@ -776,7 +760,7 @@
             BulkDeleteRequest::User(user_id) => (
                 self.legacy_loader
                     .list_keystore_entries_for_user(user_id)
-                    .context("In bulk_delete: Trying to get aliases for user_id.")?,
+                    .context(ks_err!("Trying to get aliases for user_id."))?,
                 user_id,
             ),
         };
@@ -784,7 +768,7 @@
         let super_key_id = self
             .db
             .load_super_key(&USER_SUPER_KEY, user_id)
-            .context("In bulk_delete: Failed to load super key")?
+            .context(ks_err!("Failed to load super key"))?
             .map(|(_, entry)| entry.id());
 
         for (uid, alias) in aliases
@@ -794,7 +778,7 @@
             let (km_blob_params, _, _) = self
                 .legacy_loader
                 .load_by_uid_alias(uid, &alias, &None)
-                .context("In bulk_delete: Trying to load legacy blob.")?;
+                .context(ks_err!("Trying to load legacy blob."))?;
 
             // Determine if the key needs special handling to be deleted.
             let (need_gc, is_super_encrypted) = km_blob_params
@@ -848,16 +832,16 @@
                 };
 
                 if let Some((blob, blob_metadata)) = mark_deleted {
-                    self.db.set_deleted_blob(&blob, &blob_metadata).context(concat!(
-                        "In bulk_delete: Trying to insert deleted ",
-                        "blob into the database for garbage collection."
+                    self.db.set_deleted_blob(&blob, &blob_metadata).context(ks_err!(
+                        "Trying to insert deleted \
+                            blob into the database for garbage collection."
                     ))?;
                 }
             }
 
             self.legacy_loader
                 .remove_keystore_entry(uid, &alias)
-                .context("In bulk_delete: Trying to remove imported key.")?;
+                .context(ks_err!("Trying to remove imported key."))?;
         }
         Ok(())
     }
@@ -926,18 +910,18 @@
     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))?;
+        .with_context(|| ks_err!("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);
+            let _wd = wd::watch_millis("Calling GetKeyCharacteristics.", 500);
             map_km_error(km_dev.getKeyCharacteristics(blob, &[], &[]))
         },
         |_| Ok(()),
     )
-    .context("In foo.")?;
+    .context(ks_err!())?;
     Ok((key_characteristics_to_internal(characteristics), upgraded_blob))
 }
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 4a23843..9794889 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -28,6 +28,7 @@
 pub mod id_rotation;
 /// Internal Representation of Key Parameter and convenience functions.
 pub mod key_parameter;
+pub mod ks_err;
 pub mod legacy_blob;
 pub mod legacy_importer;
 pub mod maintenance;
@@ -37,6 +38,7 @@
 pub mod permission;
 pub mod raw_device;
 pub mod remote_provisioning;
+pub mod rkpd_client;
 pub mod security_level;
 pub mod service;
 pub mod shared_secret_negotiation;
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index 1fca5d9..5efb798 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -20,6 +20,7 @@
 use crate::error::Error;
 use crate::globals::get_keymint_device;
 use crate::globals::{DB, LEGACY_IMPORTER, SUPER_KEY};
+use crate::ks_err;
 use crate::permission::{KeyPerm, KeystorePerm};
 use crate::super_key::{SuperKeyManager, UserState};
 use crate::utils::{
@@ -71,8 +72,7 @@
     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::ChangePassword)
-            .context("In on_user_password_changed.")?;
+        check_keystore_permission(KeystorePerm::ChangePassword).context(ks_err!())?;
 
         let mut skm = SUPER_KEY.write().unwrap();
 
@@ -80,7 +80,7 @@
             DB.with(|db| {
                 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")?;
+            .context(ks_err!("unlock_screen_lock_bound_key failed"))?;
         }
 
         match DB
@@ -92,12 +92,11 @@
                     password.as_ref(),
                 )
             })
-            .context("In on_user_password_changed.")?
+            .context(ks_err!())?
         {
             UserState::LskfLocked => {
                 // Error - password can not be changed when the device is locked
-                Err(Error::Rc(ResponseCode::LOCKED))
-                    .context("In on_user_password_changed. Device is locked.")
+                Err(Error::Rc(ResponseCode::LOCKED)).context(ks_err!("Device is locked."))
             }
             _ => {
                 // LskfLocked is the only error case for password change
@@ -109,7 +108,7 @@
     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::ChangeUser).context("In add_or_remove_user.")?;
+        check_keystore_permission(KeystorePerm::ChangeUser).context(ks_err!())?;
 
         DB.with(|db| {
             SUPER_KEY.write().unwrap().reset_user(
@@ -119,10 +118,10 @@
                 false,
             )
         })
-        .context("In add_or_remove_user: Trying to delete keys from db.")?;
+        .context(ks_err!("Trying to delete keys from db."))?;
         self.delete_listener
             .delete_user(user_id as u32)
-            .context("In add_or_remove_user: While invoking the delete listener.")
+            .context(ks_err!("While invoking the delete listener."))
     }
 
     fn clear_namespace(&self, domain: Domain, nspace: i64) -> Result<()> {
@@ -131,12 +130,12 @@
 
         LEGACY_IMPORTER
             .bulk_delete_uid(domain, nspace)
-            .context("In clear_namespace: Trying to delete legacy keys.")?;
+            .context(ks_err!("Trying to delete legacy keys."))?;
         DB.with(|db| db.borrow_mut().unbind_keys_for_namespace(domain, nspace))
-            .context("In clear_namespace: Trying to delete keys from db.")?;
+            .context(ks_err!("Trying to delete keys from db."))?;
         self.delete_listener
             .delete_namespace(domain, nspace)
-            .context("In clear_namespace: While invoking the delete listener.")
+            .context(ks_err!("While invoking the delete listener."))
     }
 
     fn get_state(user_id: i32) -> Result<AidlUserState> {
@@ -151,7 +150,7 @@
                     user_id as u32,
                 )
             })
-            .context("In get_state. Trying to get UserState.")?;
+            .context(ks_err!("Trying to get UserState."))?;
 
         match state {
             UserState::Uninitialized => Ok(AidlUserState::UNINITIALIZED),
@@ -164,13 +163,13 @@
     where
         F: Fn(Strong<dyn IKeyMintDevice>) -> binder::Result<()>,
     {
-        let (km_dev, _, _) = get_keymint_device(&sec_level)
-            .context("In call_with_watchdog: getting keymint device")?;
+        let (km_dev, _, _) =
+            get_keymint_device(&sec_level).context(ks_err!("getting keymint device"))?;
 
         let _wp = wd::watch_millis_with("In call_with_watchdog", 500, move || {
             format!("Seclevel: {:?} Op: {}", sec_level, name)
         });
-        map_km_error(op(km_dev)).with_context(|| format!("In keymint device: calling {}", name))?;
+        map_km_error(op(km_dev)).with_context(|| ks_err!("calling {}", name))?;
         Ok(())
     }
 
@@ -203,7 +202,7 @@
 
     fn early_boot_ended() -> Result<()> {
         check_keystore_permission(KeystorePerm::EarlyBootEnded)
-            .context("In early_boot_ended. Checking permission")?;
+            .context(ks_err!("Checking permission"))?;
         log::info!("In early_boot_ended.");
 
         if let Err(e) =
@@ -216,7 +215,7 @@
 
     fn on_device_off_body() -> Result<()> {
         // Security critical permission check. This statement must return on fail.
-        check_keystore_permission(KeystorePerm::ReportOffBody).context("In on_device_off_body.")?;
+        check_keystore_permission(KeystorePerm::ReportOffBody).context(ks_err!())?;
 
         DB.with(|db| db.borrow_mut().update_last_off_body(MonotonicRawTime::now()));
         Ok(())
@@ -228,20 +227,16 @@
         match source.domain {
             Domain::SELINUX | Domain::KEY_ID | Domain::APP => (),
             _ => {
-                return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(
-                    "In migrate_key_namespace: \
-                     Source domain must be one of APP, SELINUX, or KEY_ID.",
-                )
+                return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT))
+                    .context(ks_err!("Source domain must be one of APP, SELINUX, or KEY_ID."));
             }
         };
 
         match destination.domain {
             Domain::SELINUX | Domain::APP => (),
             _ => {
-                return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(
-                    "In migrate_key_namespace: \
-                     Destination domain must be one of APP or SELINUX.",
-                )
+                return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT))
+                    .context(ks_err!("Destination domain must be one of APP or SELINUX."));
             }
         };
 
@@ -264,7 +259,7 @@
                         },
                     )
                 })
-                .context("In migrate_key_namespace: Failed to load key blob.")?;
+                .context(ks_err!("Failed to load key blob."))?;
             {
                 db.borrow_mut().migrate_key_namespace(key_id_guard, destination, calling_uid, |k| {
                     check_key_permission(KeyPerm::Rebind, k, &None)
@@ -276,7 +271,7 @@
     fn delete_all_keys() -> Result<()> {
         // Security critical permission check. This statement must return on fail.
         check_keystore_permission(KeystorePerm::DeleteAllKeys)
-            .context("In delete_all_keys. Checking permission")?;
+            .context(ks_err!("Checking permission"))?;
         log::info!("In delete_all_keys.");
 
         Maintenance::call_on_all_security_levels("deleteAllKeys", |dev| dev.deleteAllKeys())
diff --git a/keystore2/src/metrics.rs b/keystore2/src/metrics.rs
index 3d8d6d3..cd1cd75 100644
--- a/keystore2/src/metrics.rs
+++ b/keystore2/src/metrics.rs
@@ -15,6 +15,7 @@
 //! This module implements the IKeystoreMetrics AIDL interface, which exposes the API method for the
 //! proxy in the system server to pull the aggregated metrics in keystore.
 use crate::error::map_or_log_err;
+use crate::ks_err;
 use crate::metrics_store::METRICS_STORE;
 use crate::permission::KeystorePerm;
 use crate::utils::{check_keystore_permission, watchdog as wd};
@@ -41,7 +42,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::PullMetrics).context("In pull_metrics.")?;
+        check_keystore_permission(KeystorePerm::PullMetrics).context(ks_err!())?;
         METRICS_STORE.get_atoms(atom_id)
     }
 }
diff --git a/keystore2/src/metrics_store.rs b/keystore2/src/metrics_store.rs
index 62a7d13..77cead8 100644
--- a/keystore2/src/metrics_store.rs
+++ b/keystore2/src/metrics_store.rs
@@ -17,11 +17,11 @@
 //!    stores them in an in-memory store.
 //! 2. Returns the collected metrics when requested by the statsd proxy.
 
-use crate::error::{get_error_code, Error};
+use crate::error::get_error_code;
 use crate::globals::DB;
 use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
+use crate::ks_err;
 use crate::operation::Outcome;
-use crate::remote_provisioning::get_pool_status;
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
     HardwareAuthenticatorType::HardwareAuthenticatorType, KeyOrigin::KeyOrigin,
@@ -41,16 +41,13 @@
     KeystoreAtom::KeystoreAtom, KeystoreAtomPayload::KeystoreAtomPayload,
     Outcome::Outcome as MetricsOutcome, Purpose::Purpose as MetricsPurpose,
     RkpError::RkpError as MetricsRkpError, RkpErrorStats::RkpErrorStats,
-    RkpPoolStats::RkpPoolStats, SecurityLevel::SecurityLevel as MetricsSecurityLevel,
-    Storage::Storage as MetricsStorage,
+    SecurityLevel::SecurityLevel as MetricsSecurityLevel, Storage::Storage as MetricsStorage,
 };
-use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
 use anyhow::{Context, Result};
 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};
 
 // Note: Crash events are recorded at keystore restarts, based on the assumption that keystore only
 // gets restarted after a crash, during a boot cycle.
@@ -94,11 +91,6 @@
             return pull_storage_stats();
         }
 
-        // Process and return RKP pool stats.
-        if AtomID::RKP_POOL_STATS == atom_id {
-            return pull_attestation_pool_stats();
-        }
-
         // Process keystore crash stats.
         if AtomID::CRASH_STATS == atom_id {
             return Ok(vec![KeystoreAtom {
@@ -559,49 +551,12 @@
     Ok(atom_vec)
 }
 
-fn pull_attestation_pool_stats() -> Result<Vec<KeystoreAtom>> {
-    let mut atoms = Vec::<KeystoreAtom>::new();
-    for sec_level in &[SecurityLevel::TRUSTED_ENVIRONMENT, SecurityLevel::STRONGBOX] {
-        // set the expired_by date to be three days from now
-        let expired_by = SystemTime::now()
-            .checked_add(Duration::from_secs(60 * 60 * 24 * 3))
-            .ok_or(Error::Rc(ResponseCode::SYSTEM_ERROR))
-            .context("In pull_attestation_pool_stats: Failed to compute expired by system time.")?
-            .duration_since(UNIX_EPOCH)
-            .context("In pull_attestation_pool_stats: Failed to compute expired by duration.")?
-            .as_millis() as i64;
-
-        let result = get_pool_status(expired_by, *sec_level);
-
-        if let Ok(pool_status) = result {
-            let rkp_pool_stats = RkpPoolStats {
-                security_level: process_security_level(*sec_level),
-                expiring: pool_status.expiring,
-                unassigned: pool_status.unassigned,
-                attested: pool_status.attested,
-                total: pool_status.total,
-            };
-            atoms.push(KeystoreAtom {
-                payload: KeystoreAtomPayload::RkpPoolStats(rkp_pool_stats),
-                ..Default::default()
-            });
-        } else {
-            log::error!(
-                concat!(
-                    "In pull_attestation_pool_stats: Failed to retrieve pool status",
-                    " for security level: {:?}"
-                ),
-                sec_level
-            );
-        }
-    }
-    Ok(atoms)
-}
-
 /// Log error events related to Remote Key Provisioning (RKP).
 pub fn log_rkp_error_stats(rkp_error: MetricsRkpError, sec_level: &SecurityLevel) {
-    let rkp_error_stats = KeystoreAtomPayload::RkpErrorStats(
-        RkpErrorStats { rkpError: rkp_error, security_level: process_security_level(*sec_level) });
+    let rkp_error_stats = KeystoreAtomPayload::RkpErrorStats(RkpErrorStats {
+        rkpError: rkp_error,
+        security_level: process_security_level(*sec_level),
+    });
     METRICS_STORE.insert_atom(AtomID::RKP_ERROR_STATS, rkp_error_stats);
 }
 
@@ -649,8 +604,8 @@
 /// Read the system property: keystore.crash_count.
 pub fn read_keystore_crash_count() -> Result<i32> {
     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.")?
+        .context(ks_err!("Failed read property."))?
+        .context(ks_err!("Property not set."))?
         .parse::<i32>()
         .map_err(std::convert::Into::into)
 }
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
index 5da3b32..2034a8a 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -127,6 +127,7 @@
 
 use crate::enforcements::AuthInfo;
 use crate::error::{map_err_with, map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
+use crate::ks_err;
 use crate::metrics_store::log_key_operation_event_stats;
 use crate::utils::watchdog as wd;
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
@@ -320,10 +321,8 @@
         let guard = self.outcome.lock().expect("In check_active.");
         match *guard {
             Outcome::Unknown => Ok(guard),
-            _ => Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)).context(format!(
-                "In check_active: Call on finalized operation with outcome: {:?}.",
-                *guard
-            )),
+            _ => Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE))
+                .context(ks_err!("Call on finalized operation with outcome: {:?}.", *guard)),
         }
     }
 
@@ -358,13 +357,13 @@
             .lock()
             .unwrap()
             .before_update()
-            .context("In update_aad: Trying to get auth tokens.")?;
+            .context(ks_err!("Trying to get auth tokens."))?;
 
-        self.update_outcome(&mut *outcome, {
+        self.update_outcome(&mut outcome, {
             let _wp = wd::watch_millis("Operation::update_aad: calling updateAad", 500);
             map_km_error(self.km_op.updateAad(aad_input, hat.as_ref(), tst.as_ref()))
         })
-        .context("In update_aad: KeyMint::update failed.")?;
+        .context(ks_err!("Update failed."))?;
 
         Ok(())
     }
@@ -381,14 +380,14 @@
             .lock()
             .unwrap()
             .before_update()
-            .context("In update: Trying to get auth tokens.")?;
+            .context(ks_err!("Trying to get auth tokens."))?;
 
         let output = self
-            .update_outcome(&mut *outcome, {
+            .update_outcome(&mut outcome, {
                 let _wp = wd::watch_millis("Operation::update: calling update", 500);
                 map_km_error(self.km_op.update(input, hat.as_ref(), tst.as_ref()))
             })
-            .context("In update: KeyMint::update failed.")?;
+            .context(ks_err!("Update failed."))?;
 
         if output.is_empty() {
             Ok(None)
@@ -411,10 +410,10 @@
             .lock()
             .unwrap()
             .before_finish()
-            .context("In finish: Trying to get auth tokens.")?;
+            .context(ks_err!("Trying to get auth tokens."))?;
 
         let output = self
-            .update_outcome(&mut *outcome, {
+            .update_outcome(&mut outcome, {
                 let _wp = wd::watch_millis("Operation::finish: calling finish", 500);
                 map_km_error(self.km_op.finish(
                     input,
@@ -424,7 +423,7 @@
                     confirmation_token.as_deref(),
                 ))
             })
-            .context("In finish: KeyMint::finish failed.")?;
+            .context(ks_err!("Finish failed."))?;
 
         self.auth_info.lock().unwrap().after_finish().context("In finish.")?;
 
@@ -447,7 +446,7 @@
 
         {
             let _wp = wd::watch_millis("Operation::abort: calling abort", 500);
-            map_km_error(self.km_op.abort()).context("In abort: KeyMint::abort failed.")
+            map_km_error(self.km_op.abort()).context(ks_err!("KeyMint::abort failed."))
         }
     }
 }
@@ -790,7 +789,7 @@
             Ok(mut mutex_guard) => {
                 let result = match &*mutex_guard {
                     Some(op) => {
-                        let result = f(&*op);
+                        let result = f(op);
                         // Any error here means we can discard the operation.
                         if result.is_err() {
                             delete_op = true;
@@ -798,7 +797,7 @@
                         result
                     }
                     None => Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE))
-                        .context("In KeystoreOperation::with_locked_operation"),
+                        .context(ks_err!("KeystoreOperation::with_locked_operation")),
                 };
 
                 if delete_op {
@@ -811,7 +810,7 @@
                 result
             }
             Err(_) => Err(Error::Rc(ResponseCode::OPERATION_BUSY))
-                .context("In KeystoreOperation::with_locked_operation"),
+                .context(ks_err!("KeystoreOperation::with_locked_operation")),
         }
     }
 }
@@ -823,7 +822,7 @@
         let _wp = wd::watch_millis("IKeystoreOperation::updateAad", 500);
         map_or_log_err(
             self.with_locked_operation(
-                |op| op.update_aad(aad_input).context("In KeystoreOperation::updateAad"),
+                |op| op.update_aad(aad_input).context(ks_err!("KeystoreOperation::updateAad")),
                 false,
             ),
             Ok,
@@ -834,7 +833,7 @@
         let _wp = wd::watch_millis("IKeystoreOperation::update", 500);
         map_or_log_err(
             self.with_locked_operation(
-                |op| op.update(input).context("In KeystoreOperation::update"),
+                |op| op.update(input).context(ks_err!("KeystoreOperation::update")),
                 false,
             ),
             Ok,
@@ -848,7 +847,7 @@
         let _wp = wd::watch_millis("IKeystoreOperation::finish", 500);
         map_or_log_err(
             self.with_locked_operation(
-                |op| op.finish(input, signature).context("In KeystoreOperation::finish"),
+                |op| op.finish(input, signature).context(ks_err!("KeystoreOperation::finish")),
                 true,
             ),
             Ok,
@@ -859,7 +858,7 @@
         let _wp = wd::watch_millis("IKeystoreOperation::abort", 500);
         map_err_with(
             self.with_locked_operation(
-                |op| op.abort(Outcome::Abort).context("In KeystoreOperation::abort"),
+                |op| op.abort(Outcome::Abort).context(ks_err!("KeystoreOperation::abort")),
                 true,
             ),
             |e| {
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index 22509c4..d9bdf79 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -19,6 +19,8 @@
 //! defined by keystore2 and keystore2_key respectively.
 
 use crate::error::Error as KsError;
+use crate::error::ResponseCode;
+use crate::ks_err;
 use android_system_keystore2::aidl::android::system::keystore2::{
     Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
 };
@@ -53,7 +55,7 @@
     /// the SELinux permissions.
     #[repr(i32)]
     #[selinux(class_name = keystore2_key)]
-    #[derive(Clone, Copy, Debug, PartialEq)]
+    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
     pub enum KeyPerm {
         /// Checked when convert_storage_key_to_ephemeral is called.
         #[selinux(name = convert_storage_key_to_ephemeral)]
@@ -87,7 +89,9 @@
         /// 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.
+        /// Does nothing, and is not checked. For use of device identifiers,
+        /// the caller must hold the READ_PRIVILEGED_PHONE_STATE Android
+        /// permission.
         #[selinux(name = use_dev_id)]
         UseDevId = KeyPermission::USE_DEV_ID.0,
     }
@@ -97,7 +101,7 @@
     /// 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)]
+    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
     pub enum KeystorePerm {
         /// Checked when a new auth token is installed.
         #[selinux(name = add_auth)]
@@ -299,8 +303,8 @@
     }
 
     for p in access_vec.into_iter() {
-        selinux::check_permission(caller_ctx, &target_context, p).context(format!(
-            "check_grant_permission: check_permission failed. \
+        selinux::check_permission(caller_ctx, &target_context, p).context(ks_err!(
+            "check_permission failed. \
             The caller may have tried to grant a permission that they don't possess. {:?}",
             p
         ))?
@@ -354,10 +358,10 @@
                 return Err(selinux::Error::perm())
                     .context("Trying to access key without ownership.");
             }
-            getcon().context("check_key_permission: getcon failed.")?
+            getcon().context(ks_err!("getcon failed."))?
         }
         Domain::SELINUX => lookup_keystore2_key_context(key.nspace)
-            .context("check_key_permission: Domain::SELINUX: Failed to lookup namespace.")?,
+            .context(ks_err!("Domain::SELINUX: Failed to lookup namespace."))?,
         Domain::GRANT => {
             match access_vector {
                 Some(_) => {
@@ -366,9 +370,9 @@
                 }
                 None => {
                     // If DOMAIN_GRANT was selected an access vector must be supplied.
-                    return Err(KsError::sys()).context(
+                    return Err(KsError::sys()).context(ks_err!(
                         "Cannot check permission for Domain::GRANT without access vector.",
-                    );
+                    ));
                 }
             }
         }
@@ -376,11 +380,12 @@
             // We should never be called with `Domain::KEY_ID. The database
             // lookup should have converted this into one of `Domain::APP`
             // or `Domain::SELINUX`.
-            return Err(KsError::sys()).context("Cannot check permission for Domain::KEY_ID.");
+            return Err(KsError::sys())
+                .context(ks_err!("Cannot check permission for Domain::KEY_ID.",));
         }
         Domain::BLOB => {
             let tctx = lookup_keystore2_key_context(key.nspace)
-                .context("Domain::BLOB: Failed to lookup namespace.")?;
+                .context(ks_err!("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_permission(caller_ctx, &tctx, KeyPerm::ManageBlob)?;
@@ -388,7 +393,7 @@
             tctx
         }
         _ => {
-            return Err(KsError::sys())
+            return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
                 .context(format!("Unknown domain value: \"{:?}\".", key.domain))
         }
     };
diff --git a/keystore2/src/raw_device.rs b/keystore2/src/raw_device.rs
index 4ce9dce..fa9872a 100644
--- a/keystore2/src/raw_device.rs
+++ b/keystore2/src/raw_device.rs
@@ -22,6 +22,7 @@
     },
     error::{map_km_error, Error, ErrorCode},
     globals::get_keymint_device,
+    ks_err,
     super_key::KeyBlob,
     utils::{key_characteristics_to_internal, watchdog as wd, AID_KEYSTORE},
 };
@@ -62,11 +63,13 @@
     pub const KEY_MINT_V1: i32 = 100;
     /// Version number of KeyMintDevice@V2
     pub const KEY_MINT_V2: i32 = 200;
+    /// Version number of KeyMintDevice@V3
+    pub const KEY_MINT_V3: i32 = 300;
 
     /// Get a [`KeyMintDevice`] for the given [`SecurityLevel`]
     pub fn get(security_level: SecurityLevel) -> Result<KeyMintDevice> {
-        let (km_dev, hw_info, km_uuid) = get_keymint_device(&security_level)
-            .context("In KeyMintDevice::get: get_keymint_device failed")?;
+        let (km_dev, hw_info, km_uuid) =
+            get_keymint_device(&security_level).context(ks_err!("get_keymint_device failed"))?;
 
         Ok(KeyMintDevice {
             km_dev,
@@ -110,12 +113,11 @@
     where
         F: FnOnce(&Strong<dyn IKeyMintDevice>) -> Result<KeyCreationResult, binder::Status>,
     {
-        let creation_result = map_km_error(creator(&self.km_dev))
-            .context("In create_and_store_key: creator failed")?;
+        let creation_result =
+            map_km_error(creator(&self.km_dev)).context(ks_err!("creator failed"))?;
         let key_parameters = key_characteristics_to_internal(creation_result.keyCharacteristics);
 
-        let creation_date =
-            DateTime::now().context("In create_and_store_key: DateTime::now() failed")?;
+        let creation_date = DateTime::now().context(ks_err!("DateTime::now() failed"))?;
 
         let mut key_metadata = KeyMetaData::new();
         key_metadata.add(KeyMetaEntry::CreationDate(creation_date));
@@ -131,7 +133,7 @@
             &key_metadata,
             &self.km_uuid,
         )
-        .context("In create_and_store_key: store_new_key failed")?;
+        .context(ks_err!("store_new_key failed"))?;
         Ok(())
     }
 
@@ -152,7 +154,7 @@
         key_type: KeyType,
     ) -> Result<(KeyIdGuard, KeyEntry)> {
         db.load_key_entry(key_desc, key_type, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| Ok(()))
-            .context("In lookup_from_desc: load_key_entry failed.")
+            .context(ks_err!("load_key_entry failed."))
     }
 
     /// Look up the key in the database, and return None if it is absent.
@@ -187,7 +189,7 @@
         // - because it avoids holding database locks during slow
         //   KeyMint operations
         let lookup = Self::not_found_is_none(Self::lookup_from_desc(db, key_desc, key_type))
-            .context("In lookup_or_generate_key: first lookup failed")?;
+            .context(ks_err!("first lookup failed"))?;
 
         if let Some((key_id_guard, mut key_entry)) = lookup {
             // If the key is associated with a different km instance
@@ -220,7 +222,7 @@
                             })
                         },
                     )
-                    .context("In lookup_or_generate_key: calling getKeyCharacteristics")?;
+                    .context(ks_err!("calling getKeyCharacteristics"))?;
 
                 if validate_characteristics(&key_characteristics) {
                     return Ok((key_id_guard, key_blob));
@@ -234,7 +236,7 @@
         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")?;
+        .context(ks_err!("generate_and_store_key failed"))?;
         Self::lookup_from_desc(db, key_desc, key_type)
             .and_then(|(key_id_guard, mut key_entry)| {
                 Ok((
@@ -243,10 +245,10 @@
                         .take_key_blob_info()
                         .ok_or(Error::Rc(ResponseCode::KEY_NOT_FOUND))
                         .map(|(key_blob, _)| KeyBlob::NonSensitive(key_blob))
-                        .context("Missing key blob info.")?,
+                        .context(ks_err!("Missing key blob info."))?,
                 ))
             })
-            .context("In lookup_or_generate_key: second lookup failed")
+            .context(ks_err!("second lookup failed"))
     }
 
     /// Call the passed closure; if it returns `KEY_REQUIRES_UPGRADE`, call upgradeKey, and
@@ -270,7 +272,7 @@
                     );
                     self.km_dev.upgradeKey(&key_blob, &[])
                 })
-                .context("In upgrade_keyblob_if_required_with: Upgrade failed")?;
+                .context(ks_err!("Upgrade failed"))?;
 
                 let mut new_blob_metadata = BlobMetaData::new();
                 new_blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
@@ -281,22 +283,14 @@
                     Some(&upgraded_blob),
                     Some(&new_blob_metadata),
                 )
-                .context(concat!(
-                    "In upgrade_keyblob_if_required_with: ",
-                    "Failed to insert upgraded blob into the database"
-                ))?;
+                .context(ks_err!("Failed to insert upgraded blob into the database"))?;
 
                 Ok((
-                    f(&upgraded_blob).context(
-                        "In upgrade_keyblob_if_required_with: Closure failed after upgrade",
-                    )?,
+                    f(&upgraded_blob).context(ks_err!("Closure failed after upgrade"))?,
                     KeyBlob::NonSensitive(upgraded_blob),
                 ))
             }
-            result => Ok((
-                result.context("In upgrade_keyblob_if_required_with: Closure failed")?,
-                key_blob,
-            )),
+            result => Ok((result.context(ks_err!("Closure failed"))?, key_blob)),
         }
     }
 
@@ -322,15 +316,13 @@
                     self.km_dev.begin(purpose, blob, operation_parameters, auth_token)
                 })
             })
-            .context("In use_key_in_one_step: Failed to begin operation.")?;
-        let operation: Strong<dyn IKeyMintOperation> = begin_result
-            .operation
-            .ok_or_else(Error::sys)
-            .context("In use_key_in_one_step: Operation missing")?;
+            .context(ks_err!("Failed to begin operation."))?;
+        let operation: Strong<dyn IKeyMintOperation> =
+            begin_result.operation.ok_or_else(Error::sys).context(ks_err!("Operation missing"))?;
         map_km_error({
             let _wp = wd::watch_millis("In use_key_in_one_step: calling: finish", 500);
             operation.finish(Some(input), None, None, None, None)
         })
-        .context("In use_key_in_one_step: Failed to finish operation.")
+        .context(ks_err!("Failed to finish operation."))
     }
 }
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index ea2698f..811ad98 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -19,38 +19,21 @@
 //! certificate chains signed by some root authority and stored in a keystore SQLite
 //! DB.
 
-use std::collections::HashMap;
-
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Algorithm::Algorithm, AttestationKey::AttestationKey, Certificate::Certificate,
-    DeviceInfo::DeviceInfo, IRemotelyProvisionedComponent::IRemotelyProvisionedComponent,
-    KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue,
-    MacedPublicKey::MacedPublicKey, ProtectedData::ProtectedData, SecurityLevel::SecurityLevel,
+    KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue, SecurityLevel::SecurityLevel,
     Tag::Tag,
 };
-use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
-    AttestationPoolStatus::AttestationPoolStatus, IRemoteProvisioning::BnRemoteProvisioning,
-    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, ResponseCode::ResponseCode,
+    Domain::Domain, KeyDescriptor::KeyDescriptor,
 };
 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, 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::database::Uuid;
+use crate::ks_err;
 use crate::metrics_store::log_rkp_error_stats;
-use crate::permission::KeystorePerm;
-use crate::utils::{check_keystore_permission, watchdog as wd};
+use crate::rkpd_client::get_rkpd_attestation_key;
 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,
@@ -59,18 +42,12 @@
 pub struct RemProvState {
     security_level: SecurityLevel,
     km_uuid: Uuid,
-    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) }
+        Self { security_level, km_uuid }
     }
 
     /// Returns the uuid for the KM instance attached to this RemProvState struct.
@@ -91,30 +68,6 @@
             .unwrap_or(default_value)
     }
 
-    /// 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
-    /// make decisions about the state of remote provisioning during runtime.
-    fn check_rem_prov_enabled(&self, db: &mut KeystoreDB) -> Result<bool> {
-        if self.is_rkp_only() {
-            return Ok(true);
-        }
-        if !self.is_hal_present.load(Ordering::Relaxed)
-            || get_remotely_provisioned_component(&self.security_level).is_err()
-        {
-            self.is_hal_present.store(false, Ordering::Relaxed);
-            return Ok(false);
-        }
-        // To check if remote provisioning is enabled on a system that supports both remote
-        // provisioning and factory provisioned keys, we only need to check if there are any
-        // keys at all generated to indicate if the app has gotten the signal to begin filling
-        // the key pool from the server.
-        let pool_status = db
-            .get_attestation_pool_status(0 /* date */, &self.km_uuid)
-            .context("In check_rem_prov_enabled: failed to get attestation pool status.")?;
-        Ok(pool_status.total != 0)
-    }
-
     fn is_asymmetric_key(&self, params: &[KeyParameter]) -> bool {
         params.iter().any(|kp| {
             matches!(
@@ -130,943 +83,42 @@
         })
     }
 
-    /// Checks to see (1) if the key in question should be attested to based on the algorithm and
-    /// (2) if remote provisioning is present and enabled on the system. If these conditions are
-    /// met, it makes an attempt to fetch the attestation key assigned to the `caller_uid`.
-    ///
-    /// It returns the ResponseCode `OUT_OF_KEYS` if there is not one key currently assigned to the
-    /// `caller_uid` and there are none available to assign.
-    pub fn get_remotely_provisioned_attestation_key_and_certs(
+    /// Fetches attestation key and corresponding certificates from RKPD.
+    pub fn get_rkpd_attestation_key_and_certs(
         &self,
         key: &KeyDescriptor,
         caller_uid: u32,
         params: &[KeyParameter],
-        db: &mut KeystoreDB,
-    ) -> 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
-            // factory provisioned key instead. Alternatively, it's not an asymmetric key
-            // and therefore will not be attested.
+    ) -> Result<Option<(AttestationKey, Certificate)>> {
+        if !self.is_asymmetric_key(params) || key.domain != Domain::APP {
             Ok(None)
         } else {
-            match get_rem_prov_attest_key(key.domain, caller_uid, db, &self.km_uuid) {
+            match get_rkpd_attestation_key(&self.security_level, caller_uid) {
                 Err(e) => {
-                    log::error!(
-                        "In get_remote_provisioning_key_and_certs: Error occurred: {:?}",
-                        e
-                    );
                     if self.is_rkp_only() {
+                        log::error!("Error occurred: {:?}", e);
                         return Err(e);
                     }
-                    log_rkp_error_stats(MetricsRkpError::FALL_BACK_DURING_HYBRID,
-                            &self.security_level);
+                    log::warn!("Error occurred: {:?}", e);
+                    log_rkp_error_stats(
+                        MetricsRkpError::FALL_BACK_DURING_HYBRID,
+                        &self.security_level,
+                    );
                     Ok(None)
                 }
-                Ok(v) => match v {
-                    Some((guard, cert_chain)) => Ok(Some((
-                        guard,
-                        AttestationKey {
-                            keyBlob: cert_chain.private_key.to_vec(),
-                            attestKeyParams: vec![],
-                            issuerSubjectName: parse_subject_from_certificate(
-                                &cert_chain.batch_cert,
-                            )
-                            .context(concat!(
-                                "In get_remote_provisioning_key_and_certs: Failed to ",
-                                "parse subject."
-                            ))?,
-                        },
-                        Certificate { encodedCertificate: cert_chain.cert_chain },
-                    ))),
-                    None => Ok(None),
-                },
-            }
-        }
-    }
-}
-/// Implementation of the IRemoteProvisioning service.
-#[derive(Default)]
-pub struct RemoteProvisioningService {
-    device_by_sec_level: HashMap<SecurityLevel, Strong<dyn IRemotelyProvisionedComponent>>,
-    curve_by_sec_level: HashMap<SecurityLevel, i32>,
-}
-
-impl RemoteProvisioningService {
-    fn get_dev_by_sec_level(
-        &self,
-        sec_level: &SecurityLevel,
-    ) -> Result<&dyn IRemotelyProvisionedComponent> {
-        if let Some(dev) = self.device_by_sec_level.get(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",
-                " not found."
-            ))
-        }
-    }
-
-    /// Creates a new instance of the remote provisioning service
-    pub fn new_native_binder() -> Result<Strong<dyn IRemoteProvisioning>> {
-        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.")?;
-        result.curve_by_sec_level.insert(
-            SecurityLevel::TRUSTED_ENVIRONMENT,
-            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) {
-            result.curve_by_sec_level.insert(
-                SecurityLevel::STRONGBOX,
-                dev.getHardwareInfo()
-                    .context("In new_native_binder: Failed to get hardware info for StrongBox.")?
-                    .supportedEekCurve,
-            );
-            result.device_by_sec_level.insert(SecurityLevel::STRONGBOX, dev);
-        }
-        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,
-    /// along with a server provided `eek` and `challenge`. The endpoint encryption key will
-    /// be used to encrypt the sensitive contents being transmitted to the server, and the
-    /// challenge will ensure freshness. A `test_mode` flag will instruct the remote provisioning
-    /// HAL if it is okay to accept EEKs that aren't signed by something that chains back to the
-    /// baked in root of trust in the underlying IRemotelyProvisionedComponent instance.
-    #[allow(clippy::too_many_arguments)]
-    pub fn generate_csr(
-        &self,
-        test_mode: bool,
-        num_csr: i32,
-        eek: &[u8],
-        challenge: &[u8],
-        sec_level: SecurityLevel,
-        protected_data: &mut ProtectedData,
-        device_info: &mut DeviceInfo,
-    ) -> Result<Vec<u8>> {
-        let dev = self.get_dev_by_sec_level(&sec_level)?;
-        let (_, _, uuid) = get_keymint_device(&sec_level)?;
-        let keys_to_sign = DB.with::<_, Result<Vec<MacedPublicKey>>>(|db| {
-            let mut db = db.borrow_mut();
-            Ok(db
-                .fetch_unsigned_attestation_keys(num_csr, &uuid)?
-                .iter()
-                .map(|key| MacedPublicKey { macedKey: key.to_vec() })
-                .collect())
-        })?;
-        let mac = map_rem_prov_error(dev.generateCertificateRequest(
-            test_mode,
-            &keys_to_sign,
-            eek,
-            challenge,
-            device_info,
-            protected_data,
-        ))
-        .context("In generate_csr: Failed to generate csr")?;
-        let mut mac_and_keys: Vec<Value> = vec![Value::from(mac)];
-        for maced_public_key in keys_to_sign {
-            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")?,
-            )
-        }
-        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
-    /// `public_key` is used to index into the SQL database in order to insert the `certs` blob
-    /// which represents a PEM encoded X.509 certificate chain. The `expiration_date` is provided
-    /// as a convenience from the caller to avoid having to parse the certificates semantically
-    /// 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<()> {
-        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,
-        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).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.")?;
-        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
-    /// all levels in an array to the caller.
-    pub fn get_implementation_info(&self) -> Result<Vec<ImplInfo>> {
-        Ok(self
-            .curve_by_sec_level
-            .iter()
-            .map(|(sec_level, curve)| ImplInfo { secLevel: *sec_level, supportedCurve: *curve })
-            .collect())
-    }
-
-    /// Deletes all attestation keys generated by the IRemotelyProvisionedComponent from the device,
-    /// regardless of what state of the attestation key lifecycle they were in.
-    pub fn delete_all_keys(&self) -> Result<i64> {
-        DB.with::<_, Result<i64>>(|db| {
-            let mut db = db.borrow_mut();
-            db.delete_all_attestation_keys()
-        })
-    }
-}
-
-/// Populates the AttestationPoolStatus parcelable with information about how many
-/// certs will be expiring by the date provided in `expired_by` along with how many
-/// keys have not yet been assigned.
-pub fn get_pool_status(expired_by: i64, sec_level: SecurityLevel) -> Result<AttestationPoolStatus> {
-    let (_, _, uuid) = get_keymint_device(&sec_level)?;
-    DB.with::<_, Result<AttestationPoolStatus>>(|db| {
-        let mut db = db.borrow_mut();
-        // delete_expired_attestation_keys is always safe to call, and will remove anything
-        // older than the date at the time of calling. No work should be done on the
-        // attestation keys unless the pool status is checked first, so this call should be
-        // enough to routinely clean out expired keys.
-        db.delete_expired_attestation_keys()?;
-        db.get_attestation_pool_status(expired_by, &uuid)
-    })
-}
-
-/// 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."
-                        ))
+                Ok(rkpd_key) => Ok(Some((
+                    AttestationKey {
+                        keyBlob: rkpd_key.keyBlob,
+                        attestKeyParams: vec![],
+                        // Batch certificate is at the beginning of the certificate chain.
+                        issuerSubjectName: parse_subject_from_certificate(
+                            &rkpd_key.encodedCertChain,
+                        )
+                        .context(ks_err!("Failed to parse subject."))?,
                     },
-                    |(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
-// :aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
-impl IRemoteProvisioning for RemoteProvisioningService {
-    fn getPoolStatus(
-        &self,
-        expired_by: i64,
-        sec_level: SecurityLevel,
-    ) -> binder::Result<AttestationPoolStatus> {
-        let _wp = wd::watch_millis("IRemoteProvisioning::getPoolStatus", 500);
-        map_or_log_err(get_pool_status(expired_by, sec_level), Ok)
-    }
-
-    fn generateCsr(
-        &self,
-        test_mode: bool,
-        num_csr: i32,
-        eek: &[u8],
-        challenge: &[u8],
-        sec_level: SecurityLevel,
-        protected_data: &mut ProtectedData,
-        device_info: &mut DeviceInfo,
-    ) -> binder::Result<Vec<u8>> {
-        let _wp = wd::watch_millis("IRemoteProvisioning::generateCsr", 500);
-        map_or_log_err(
-            self.generate_csr(
-                test_mode,
-                num_csr,
-                eek,
-                challenge,
-                sec_level,
-                protected_data,
-                device_info,
-            ),
-            Ok,
-        )
-    }
-
-    fn provisionCertChain(
-        &self,
-        public_key: &[u8],
-        batch_cert: &[u8],
-        certs: &[u8],
-        expiration_date: i64,
-        sec_level: SecurityLevel,
-    ) -> binder::Result<()> {
-        let _wp = wd::watch_millis("IRemoteProvisioning::provisionCertChain", 500);
-        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::Result<()> {
-        let _wp = wd::watch_millis("IRemoteProvisioning::generateKeyPair", 500);
-        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::Result<Vec<ImplInfo>> {
-        let _wp = wd::watch_millis("IRemoteProvisioning::getSecurityLevels", 500);
-        map_or_log_err(self.get_implementation_info(), Ok)
-    }
-
-    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);
+                    Certificate { encodedCertificate: rkpd_key.encodedCertChain },
+                ))),
             }
         }
-
-        // 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/rkpd_client.rs b/keystore2/src/rkpd_client.rs
new file mode 100644
index 0000000..0ea2d39
--- /dev/null
+++ b/keystore2/src/rkpd_client.rs
@@ -0,0 +1,739 @@
+// Copyright 2022, 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.
+
+//! Helper wrapper around RKPD interface.
+
+use crate::error::{map_binder_status_code, Error, ResponseCode};
+use crate::globals::get_remotely_provisioned_component_name;
+use crate::ks_err;
+use crate::utils::watchdog as wd;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use android_security_rkp_aidl::aidl::android::security::rkp::{
+    IGetKeyCallback::BnGetKeyCallback, IGetKeyCallback::ErrorCode::ErrorCode as GetKeyErrorCode,
+    IGetKeyCallback::IGetKeyCallback, IGetRegistrationCallback::BnGetRegistrationCallback,
+    IGetRegistrationCallback::IGetRegistrationCallback, IRegistration::IRegistration,
+    IRemoteProvisioning::IRemoteProvisioning,
+    IStoreUpgradedKeyCallback::BnStoreUpgradedKeyCallback,
+    IStoreUpgradedKeyCallback::IStoreUpgradedKeyCallback,
+    RemotelyProvisionedKey::RemotelyProvisionedKey,
+};
+use android_security_rkp_aidl::binder::{BinderFeatures, Interface, Strong};
+use anyhow::{Context, Result};
+use std::sync::Mutex;
+use std::time::Duration;
+use tokio::sync::oneshot;
+use tokio::time::timeout;
+
+// Normally, we block indefinitely when making calls outside of keystore and rely on watchdog to
+// report deadlocks. However, RKPD is mainline updatable. Also, calls to RKPD may wait on network
+// for certificates. So, we err on the side of caution and timeout instead.
+static RKPD_TIMEOUT: Duration = Duration::from_secs(10);
+
+fn tokio_rt() -> tokio::runtime::Runtime {
+    tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap()
+}
+
+/// Thread-safe channel for sending a value once and only once. If a value has
+/// already been send, subsequent calls to send will noop.
+struct SafeSender<T> {
+    inner: Mutex<Option<oneshot::Sender<T>>>,
+}
+
+impl<T> SafeSender<T> {
+    fn new(sender: oneshot::Sender<T>) -> Self {
+        Self { inner: Mutex::new(Some(sender)) }
+    }
+
+    fn send(&self, value: T) {
+        if let Some(inner) = self.inner.lock().unwrap().take() {
+            // It's possible for the corresponding receiver to time out and be dropped. In this
+            // case send() will fail. This error is not actionable though, so only log the error.
+            if inner.send(value).is_err() {
+                log::error!("SafeSender::send() failed");
+            }
+        }
+    }
+}
+
+struct GetRegistrationCallback {
+    registration_tx: SafeSender<Result<binder::Strong<dyn IRegistration>>>,
+}
+
+impl GetRegistrationCallback {
+    pub fn new_native_binder(
+        registration_tx: oneshot::Sender<Result<binder::Strong<dyn IRegistration>>>,
+    ) -> Strong<dyn IGetRegistrationCallback> {
+        let result: Self =
+            GetRegistrationCallback { registration_tx: SafeSender::new(registration_tx) };
+        BnGetRegistrationCallback::new_binder(result, BinderFeatures::default())
+    }
+}
+
+impl Interface for GetRegistrationCallback {}
+
+impl IGetRegistrationCallback for GetRegistrationCallback {
+    fn onSuccess(&self, registration: &Strong<dyn IRegistration>) -> binder::Result<()> {
+        let _wp = wd::watch_millis("IGetRegistrationCallback::onSuccess", 500);
+        self.registration_tx.send(Ok(registration.clone()));
+        Ok(())
+    }
+    fn onCancel(&self) -> binder::Result<()> {
+        let _wp = wd::watch_millis("IGetRegistrationCallback::onCancel", 500);
+        log::warn!("IGetRegistrationCallback cancelled");
+        self.registration_tx.send(
+            Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
+                .context(ks_err!("GetRegistrationCallback cancelled.")),
+        );
+        Ok(())
+    }
+    fn onError(&self, description: &str) -> binder::Result<()> {
+        let _wp = wd::watch_millis("IGetRegistrationCallback::onError", 500);
+        log::error!("IGetRegistrationCallback failed: '{description}'");
+        self.registration_tx.send(
+            Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
+                .context(ks_err!("GetRegistrationCallback failed: {:?}", description)),
+        );
+        Ok(())
+    }
+}
+
+/// Make a new connection to a IRegistration service.
+async fn get_rkpd_registration(
+    security_level: &SecurityLevel,
+) -> Result<binder::Strong<dyn IRegistration>> {
+    let remote_provisioning: Strong<dyn IRemoteProvisioning> =
+        map_binder_status_code(binder::get_interface("remote_provisioning"))
+            .context(ks_err!("Trying to connect to IRemoteProvisioning service."))?;
+
+    let rpc_name = get_remotely_provisioned_component_name(security_level)
+        .context(ks_err!("Trying to get IRPC name."))?;
+
+    let (tx, rx) = oneshot::channel();
+    let cb = GetRegistrationCallback::new_native_binder(tx);
+
+    remote_provisioning
+        .getRegistration(&rpc_name, &cb)
+        .context(ks_err!("Trying to get registration."))?;
+
+    match timeout(RKPD_TIMEOUT, rx).await {
+        Err(e) => {
+            Err(Error::Rc(ResponseCode::SYSTEM_ERROR)).context(ks_err!("Waiting for RKPD: {:?}", e))
+        }
+        Ok(v) => v.unwrap(),
+    }
+}
+
+struct GetKeyCallback {
+    key_tx: SafeSender<Result<RemotelyProvisionedKey>>,
+}
+
+impl GetKeyCallback {
+    pub fn new_native_binder(
+        key_tx: oneshot::Sender<Result<RemotelyProvisionedKey>>,
+    ) -> Strong<dyn IGetKeyCallback> {
+        let result: Self = GetKeyCallback { key_tx: SafeSender::new(key_tx) };
+        BnGetKeyCallback::new_binder(result, BinderFeatures::default())
+    }
+}
+
+impl Interface for GetKeyCallback {}
+
+impl IGetKeyCallback for GetKeyCallback {
+    fn onSuccess(&self, key: &RemotelyProvisionedKey) -> binder::Result<()> {
+        let _wp = wd::watch_millis("IGetKeyCallback::onSuccess", 500);
+        self.key_tx.send(Ok(RemotelyProvisionedKey {
+            keyBlob: key.keyBlob.clone(),
+            encodedCertChain: key.encodedCertChain.clone(),
+        }));
+        Ok(())
+    }
+    fn onCancel(&self) -> binder::Result<()> {
+        let _wp = wd::watch_millis("IGetKeyCallback::onCancel", 500);
+        log::warn!("IGetKeyCallback cancelled");
+        self.key_tx.send(
+            Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
+                .context(ks_err!("GetKeyCallback cancelled.")),
+        );
+        Ok(())
+    }
+    fn onError(&self, error: GetKeyErrorCode, description: &str) -> binder::Result<()> {
+        let _wp = wd::watch_millis("IGetKeyCallback::onError", 500);
+        log::error!("IGetKeyCallback failed: {description}");
+        let rc = match error {
+            GetKeyErrorCode::ERROR_UNKNOWN => ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
+            GetKeyErrorCode::ERROR_PERMANENT => ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
+            GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY => {
+                ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY
+            }
+            GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH => {
+                ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE
+            }
+            _ => {
+                log::error!("Unexpected error from rkpd: {error:?}");
+                ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR
+            }
+        };
+        self.key_tx.send(Err(Error::Rc(rc)).context(ks_err!(
+            "GetKeyCallback failed: {:?} {:?}",
+            error,
+            description
+        )));
+        Ok(())
+    }
+}
+
+async fn get_rkpd_attestation_key_from_registration_async(
+    registration: &Strong<dyn IRegistration>,
+    caller_uid: u32,
+) -> Result<RemotelyProvisionedKey> {
+    let (tx, rx) = oneshot::channel();
+    let cb = GetKeyCallback::new_native_binder(tx);
+
+    registration
+        .getKey(caller_uid.try_into().unwrap(), &cb)
+        .context(ks_err!("Trying to get key."))?;
+
+    match timeout(RKPD_TIMEOUT, rx).await {
+        Err(e) => {
+            // Make a best effort attempt to cancel the timed out request.
+            if let Err(e) = registration.cancelGetKey(&cb) {
+                log::error!("IRegistration::cancelGetKey failed: {:?}", e);
+            }
+            Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
+                .context(ks_err!("Waiting for RKPD key timed out: {:?}", e))
+        }
+        Ok(v) => v.unwrap(),
+    }
+}
+
+async fn get_rkpd_attestation_key_async(
+    security_level: &SecurityLevel,
+    caller_uid: u32,
+) -> Result<RemotelyProvisionedKey> {
+    let registration = get_rkpd_registration(security_level)
+        .await
+        .context(ks_err!("Trying to get to IRegistration service."))?;
+    get_rkpd_attestation_key_from_registration_async(&registration, caller_uid).await
+}
+
+struct StoreUpgradedKeyCallback {
+    completer: SafeSender<Result<()>>,
+}
+
+impl StoreUpgradedKeyCallback {
+    pub fn new_native_binder(
+        completer: oneshot::Sender<Result<()>>,
+    ) -> Strong<dyn IStoreUpgradedKeyCallback> {
+        let result: Self = StoreUpgradedKeyCallback { completer: SafeSender::new(completer) };
+        BnStoreUpgradedKeyCallback::new_binder(result, BinderFeatures::default())
+    }
+}
+
+impl Interface for StoreUpgradedKeyCallback {}
+
+impl IStoreUpgradedKeyCallback for StoreUpgradedKeyCallback {
+    fn onSuccess(&self) -> binder::Result<()> {
+        let _wp = wd::watch_millis("IGetRegistrationCallback::onSuccess", 500);
+        self.completer.send(Ok(()));
+        Ok(())
+    }
+
+    fn onError(&self, error: &str) -> binder::Result<()> {
+        let _wp = wd::watch_millis("IGetRegistrationCallback::onError", 500);
+        log::error!("IGetRegistrationCallback failed: {error}");
+        self.completer.send(
+            Err(Error::Rc(ResponseCode::SYSTEM_ERROR))
+                .context(ks_err!("Failed to store upgraded key: {:?}", error)),
+        );
+        Ok(())
+    }
+}
+
+async fn store_rkpd_attestation_key_with_registration_async(
+    registration: &Strong<dyn IRegistration>,
+    key_blob: &[u8],
+    upgraded_blob: &[u8],
+) -> Result<()> {
+    let (tx, rx) = oneshot::channel();
+    let cb = StoreUpgradedKeyCallback::new_native_binder(tx);
+
+    registration
+        .storeUpgradedKeyAsync(key_blob, upgraded_blob, &cb)
+        .context(ks_err!("Failed to store upgraded blob with RKPD."))?;
+
+    match timeout(RKPD_TIMEOUT, rx).await {
+        Err(e) => Err(Error::Rc(ResponseCode::SYSTEM_ERROR))
+            .context(ks_err!("Waiting for RKPD to complete storing key: {:?}", e)),
+        Ok(v) => v.unwrap(),
+    }
+}
+
+async fn store_rkpd_attestation_key_async(
+    security_level: &SecurityLevel,
+    key_blob: &[u8],
+    upgraded_blob: &[u8],
+) -> Result<()> {
+    let registration = get_rkpd_registration(security_level)
+        .await
+        .context(ks_err!("Trying to get to IRegistration service."))?;
+    store_rkpd_attestation_key_with_registration_async(&registration, key_blob, upgraded_blob).await
+}
+
+/// Get attestation key from RKPD.
+pub fn get_rkpd_attestation_key(
+    security_level: &SecurityLevel,
+    caller_uid: u32,
+) -> Result<RemotelyProvisionedKey> {
+    let _wp = wd::watch_millis("Calling get_rkpd_attestation_key()", 500);
+    tokio_rt().block_on(get_rkpd_attestation_key_async(security_level, caller_uid))
+}
+
+/// Store attestation key in RKPD.
+pub fn store_rkpd_attestation_key(
+    security_level: &SecurityLevel,
+    key_blob: &[u8],
+    upgraded_blob: &[u8],
+) -> Result<()> {
+    let _wp = wd::watch_millis("Calling store_rkpd_attestation_key()", 500);
+    tokio_rt().block_on(store_rkpd_attestation_key_async(security_level, key_blob, upgraded_blob))
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::error::map_km_error;
+    use crate::globals::get_keymint_device;
+    use crate::utils::upgrade_keyblob_if_required_with;
+    use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+        Algorithm::Algorithm, AttestationKey::AttestationKey, KeyParameter::KeyParameter,
+        KeyParameterValue::KeyParameterValue, Tag::Tag,
+    };
+    use android_security_rkp_aidl::aidl::android::security::rkp::IRegistration::BnRegistration;
+    use keystore2_crypto::parse_subject_from_certificate;
+    use std::collections::HashMap;
+    use std::sync::atomic::{AtomicU32, Ordering};
+    use std::sync::{Arc, Mutex};
+
+    struct MockRegistrationValues {
+        key: RemotelyProvisionedKey,
+        latency: Option<Duration>,
+        thread_join_handles: Vec<Option<std::thread::JoinHandle<()>>>,
+    }
+
+    struct MockRegistration(Arc<Mutex<MockRegistrationValues>>);
+
+    impl MockRegistration {
+        pub fn new_native_binder(
+            key: &RemotelyProvisionedKey,
+            latency: Option<Duration>,
+        ) -> Strong<dyn IRegistration> {
+            let result = Self(Arc::new(Mutex::new(MockRegistrationValues {
+                key: RemotelyProvisionedKey {
+                    keyBlob: key.keyBlob.clone(),
+                    encodedCertChain: key.encodedCertChain.clone(),
+                },
+                latency,
+                thread_join_handles: Vec::new(),
+            })));
+            BnRegistration::new_binder(result, BinderFeatures::default())
+        }
+    }
+
+    impl Drop for MockRegistration {
+        fn drop(&mut self) {
+            let mut values = self.0.lock().unwrap();
+            for handle in values.thread_join_handles.iter_mut() {
+                // These are test threads. So, no need to worry too much about error handling.
+                handle.take().unwrap().join().unwrap();
+            }
+        }
+    }
+
+    impl Interface for MockRegistration {}
+
+    impl IRegistration for MockRegistration {
+        fn getKey(&self, _: i32, cb: &Strong<dyn IGetKeyCallback>) -> binder::Result<()> {
+            let mut values = self.0.lock().unwrap();
+            let key = RemotelyProvisionedKey {
+                keyBlob: values.key.keyBlob.clone(),
+                encodedCertChain: values.key.encodedCertChain.clone(),
+            };
+            let latency = values.latency;
+            let get_key_cb = cb.clone();
+
+            // Need a separate thread to trigger timeout in the caller.
+            let join_handle = std::thread::spawn(move || {
+                if let Some(duration) = latency {
+                    std::thread::sleep(duration);
+                }
+                get_key_cb.onSuccess(&key).unwrap();
+            });
+            values.thread_join_handles.push(Some(join_handle));
+            Ok(())
+        }
+
+        fn cancelGetKey(&self, _: &Strong<dyn IGetKeyCallback>) -> binder::Result<()> {
+            Ok(())
+        }
+
+        fn storeUpgradedKeyAsync(
+            &self,
+            _: &[u8],
+            _: &[u8],
+            cb: &Strong<dyn IStoreUpgradedKeyCallback>,
+        ) -> binder::Result<()> {
+            // We are primarily concerned with timing out correctly. Storing the key in this mock
+            // registration isn't particularly interesting, so skip that part.
+            let values = self.0.lock().unwrap();
+            let store_cb = cb.clone();
+            let latency = values.latency;
+
+            std::thread::spawn(move || {
+                if let Some(duration) = latency {
+                    std::thread::sleep(duration);
+                }
+                store_cb.onSuccess().unwrap();
+            });
+            Ok(())
+        }
+    }
+
+    fn get_mock_registration(
+        key: &RemotelyProvisionedKey,
+        latency: Option<Duration>,
+    ) -> Result<binder::Strong<dyn IRegistration>> {
+        let (tx, rx) = oneshot::channel();
+        let cb = GetRegistrationCallback::new_native_binder(tx);
+        let mock_registration = MockRegistration::new_native_binder(key, latency);
+
+        assert!(cb.onSuccess(&mock_registration).is_ok());
+        tokio_rt().block_on(rx).unwrap()
+    }
+
+    // Using the same key ID makes test cases race with each other. So, we use separate key IDs for
+    // different test cases.
+    fn get_next_key_id() -> u32 {
+        static ID: AtomicU32 = AtomicU32::new(0);
+        ID.fetch_add(1, Ordering::Relaxed)
+    }
+
+    #[test]
+    fn test_get_registration_cb_success() {
+        let key: RemotelyProvisionedKey = Default::default();
+        let registration = get_mock_registration(&key, /*latency=*/ None);
+        assert!(registration.is_ok());
+    }
+
+    #[test]
+    fn test_get_registration_cb_cancel() {
+        let (tx, rx) = oneshot::channel();
+        let cb = GetRegistrationCallback::new_native_binder(tx);
+        assert!(cb.onCancel().is_ok());
+
+        let result = tokio_rt().block_on(rx).unwrap();
+        assert_eq!(
+            result.unwrap_err().downcast::<Error>().unwrap(),
+            Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
+        );
+    }
+
+    #[test]
+    fn test_get_registration_cb_error() {
+        let (tx, rx) = oneshot::channel();
+        let cb = GetRegistrationCallback::new_native_binder(tx);
+        assert!(cb.onError("error").is_ok());
+
+        let result = tokio_rt().block_on(rx).unwrap();
+        assert_eq!(
+            result.unwrap_err().downcast::<Error>().unwrap(),
+            Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
+        );
+    }
+
+    #[test]
+    fn test_get_key_cb_success() {
+        let mock_key =
+            RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
+        let (tx, rx) = oneshot::channel();
+        let cb = GetKeyCallback::new_native_binder(tx);
+        assert!(cb.onSuccess(&mock_key).is_ok());
+
+        let key = tokio_rt().block_on(rx).unwrap().unwrap();
+        assert_eq!(key, mock_key);
+    }
+
+    #[test]
+    fn test_get_key_cb_cancel() {
+        let (tx, rx) = oneshot::channel();
+        let cb = GetKeyCallback::new_native_binder(tx);
+        assert!(cb.onCancel().is_ok());
+
+        let result = tokio_rt().block_on(rx).unwrap();
+        assert_eq!(
+            result.unwrap_err().downcast::<Error>().unwrap(),
+            Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
+        );
+    }
+
+    #[test]
+    fn test_get_key_cb_error() {
+        let error_mapping = HashMap::from([
+            (GetKeyErrorCode::ERROR_UNKNOWN, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+            (GetKeyErrorCode::ERROR_PERMANENT, ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR),
+            (
+                GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY,
+                ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY,
+            ),
+            (
+                GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH,
+                ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE,
+            ),
+        ]);
+
+        // Loop over the generated list of enum values to better ensure this test stays in
+        // sync with the AIDL.
+        for get_key_error in GetKeyErrorCode::enum_values() {
+            let (tx, rx) = oneshot::channel();
+            let cb = GetKeyCallback::new_native_binder(tx);
+            assert!(cb.onError(get_key_error, "error").is_ok());
+
+            let result = tokio_rt().block_on(rx).unwrap();
+            assert_eq!(
+                result.unwrap_err().downcast::<Error>().unwrap(),
+                Error::Rc(error_mapping[&get_key_error]),
+            );
+        }
+    }
+
+    #[test]
+    fn test_store_upgraded_cb_success() {
+        let (tx, rx) = oneshot::channel();
+        let cb = StoreUpgradedKeyCallback::new_native_binder(tx);
+        assert!(cb.onSuccess().is_ok());
+
+        tokio_rt().block_on(rx).unwrap().unwrap();
+    }
+
+    #[test]
+    fn test_store_upgraded_key_cb_error() {
+        let (tx, rx) = oneshot::channel();
+        let cb = StoreUpgradedKeyCallback::new_native_binder(tx);
+        assert!(cb.onError("oh no! it failed").is_ok());
+
+        let result = tokio_rt().block_on(rx).unwrap();
+        assert_eq!(
+            result.unwrap_err().downcast::<Error>().unwrap(),
+            Error::Rc(ResponseCode::SYSTEM_ERROR)
+        );
+    }
+
+    #[test]
+    fn test_get_mock_key_success() {
+        let mock_key =
+            RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
+        let registration = get_mock_registration(&mock_key, /*latency=*/ None).unwrap();
+
+        let key = tokio_rt()
+            .block_on(get_rkpd_attestation_key_from_registration_async(&registration, 0))
+            .unwrap();
+        assert_eq!(key, mock_key);
+    }
+
+    #[test]
+    fn test_get_mock_key_timeout() {
+        let mock_key =
+            RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
+        let latency = RKPD_TIMEOUT + Duration::from_secs(1);
+        let registration = get_mock_registration(&mock_key, Some(latency)).unwrap();
+
+        let result =
+            tokio_rt().block_on(get_rkpd_attestation_key_from_registration_async(&registration, 0));
+        assert_eq!(
+            result.unwrap_err().downcast::<Error>().unwrap(),
+            Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
+        );
+    }
+
+    #[test]
+    fn test_store_mock_key_success() {
+        let mock_key =
+            RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
+        let registration = get_mock_registration(&mock_key, /*latency=*/ None).unwrap();
+        tokio_rt()
+            .block_on(store_rkpd_attestation_key_with_registration_async(&registration, &[], &[]))
+            .unwrap();
+    }
+
+    #[test]
+    fn test_store_mock_key_timeout() {
+        let mock_key =
+            RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
+        let latency = RKPD_TIMEOUT + Duration::from_secs(1);
+        let registration = get_mock_registration(&mock_key, Some(latency)).unwrap();
+
+        let result = tokio_rt().block_on(store_rkpd_attestation_key_with_registration_async(
+            &registration,
+            &[],
+            &[],
+        ));
+        assert_eq!(
+            result.unwrap_err().downcast::<Error>().unwrap(),
+            Error::Rc(ResponseCode::SYSTEM_ERROR)
+        );
+    }
+
+    #[test]
+    fn test_get_rkpd_attestation_key() {
+        binder::ProcessState::start_thread_pool();
+        let key_id = get_next_key_id();
+        let key = get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, key_id).unwrap();
+        assert!(!key.keyBlob.is_empty());
+        assert!(!key.encodedCertChain.is_empty());
+    }
+
+    #[test]
+    fn test_get_rkpd_attestation_key_same_caller() {
+        binder::ProcessState::start_thread_pool();
+        let sec_level = SecurityLevel::TRUSTED_ENVIRONMENT;
+        let key_id = get_next_key_id();
+
+        // Multiple calls should return the same key.
+        let first_key = get_rkpd_attestation_key(&sec_level, key_id).unwrap();
+        let second_key = get_rkpd_attestation_key(&sec_level, key_id).unwrap();
+
+        assert_eq!(first_key.keyBlob, second_key.keyBlob);
+        assert_eq!(first_key.encodedCertChain, second_key.encodedCertChain);
+    }
+
+    #[test]
+    fn test_get_rkpd_attestation_key_different_caller() {
+        binder::ProcessState::start_thread_pool();
+        let sec_level = SecurityLevel::TRUSTED_ENVIRONMENT;
+        let first_key_id = get_next_key_id();
+        let second_key_id = get_next_key_id();
+
+        // Different callers should be getting different keys.
+        let first_key = get_rkpd_attestation_key(&sec_level, first_key_id).unwrap();
+        let second_key = get_rkpd_attestation_key(&sec_level, second_key_id).unwrap();
+
+        assert_ne!(first_key.keyBlob, second_key.keyBlob);
+        assert_ne!(first_key.encodedCertChain, second_key.encodedCertChain);
+    }
+
+    #[test]
+    // Couple of things to note:
+    // 1. This test must never run with UID of keystore. Otherwise, it can mess up keys stored by
+    //    keystore.
+    // 2. Storing and reading the stored key is prone to race condition. So, we only do this in one
+    //    test case.
+    fn test_store_rkpd_attestation_key() {
+        binder::ProcessState::start_thread_pool();
+        let sec_level = SecurityLevel::TRUSTED_ENVIRONMENT;
+        let key_id = get_next_key_id();
+        let key = get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, key_id).unwrap();
+        let new_blob: [u8; 8] = rand::random();
+
+        assert!(store_rkpd_attestation_key(&sec_level, &key.keyBlob, &new_blob).is_ok());
+
+        let new_key =
+            get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, key_id).unwrap();
+
+        // Restore original key so that we don't leave RKPD with invalid blobs.
+        assert!(store_rkpd_attestation_key(&sec_level, &new_blob, &key.keyBlob).is_ok());
+        assert_eq!(new_key.keyBlob, new_blob);
+    }
+
+    #[test]
+    // This is a helper for a manual test. We want to check that after a system upgrade RKPD
+    // attestation keys can also be upgraded and stored again with RKPD. The steps are:
+    // 1. Run this test and check in stdout that no key upgrade happened.
+    // 2. Perform a system upgrade.
+    // 3. Run this test and check in stdout that key upgrade did happen.
+    //
+    // Note that this test must be run with that same UID every time. Running as root, i.e. UID 0,
+    // should do the trick. Also, use "--nocapture" flag to get stdout.
+    fn test_rkpd_attestation_key_upgrade() {
+        binder::ProcessState::start_thread_pool();
+        let security_level = SecurityLevel::TRUSTED_ENVIRONMENT;
+        let (keymint, _, _) = get_keymint_device(&security_level).unwrap();
+        let key_id = get_next_key_id();
+        let mut key_upgraded = false;
+
+        let key = get_rkpd_attestation_key(&security_level, key_id).unwrap();
+        assert!(!key.keyBlob.is_empty());
+        assert!(!key.encodedCertChain.is_empty());
+
+        upgrade_keyblob_if_required_with(
+            &*keymint,
+            &key.keyBlob,
+            /*upgrade_params=*/ &[],
+            /*km_op=*/
+            |blob| {
+                let params = vec![
+                    KeyParameter {
+                        tag: Tag::ALGORITHM,
+                        value: KeyParameterValue::Algorithm(Algorithm::AES),
+                    },
+                    KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(128) },
+                ];
+                let attestation_key = AttestationKey {
+                    keyBlob: blob.to_vec(),
+                    attestKeyParams: vec![],
+                    issuerSubjectName: parse_subject_from_certificate(&key.encodedCertChain)
+                        .unwrap(),
+                };
+
+                map_km_error(keymint.generateKey(&params, Some(&attestation_key)))
+            },
+            /*new_blob_handler=*/
+            |new_blob| {
+                // This handler is only executed if a key upgrade was performed.
+                key_upgraded = true;
+                store_rkpd_attestation_key(&security_level, &key.keyBlob, new_blob).unwrap();
+                Ok(())
+            },
+        )
+        .unwrap();
+
+        if key_upgraded {
+            println!("RKPD key was upgraded and stored with RKPD.");
+        } else {
+            println!("RKPD key was NOT upgraded.");
+        }
+    }
+
+    #[test]
+    fn test_stress_get_rkpd_attestation_key() {
+        binder::ProcessState::start_thread_pool();
+        let key_id = get_next_key_id();
+        let mut threads = vec![];
+        const NTHREADS: u32 = 10;
+        const NCALLS: u32 = 1000;
+
+        for _ in 0..NTHREADS {
+            threads.push(std::thread::spawn(move || {
+                for _ in 0..NCALLS {
+                    let key = get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, key_id)
+                        .unwrap();
+                    assert!(!key.keyBlob.is_empty());
+                    assert!(!key.encodedCertChain.is_empty());
+                }
+            }));
+        }
+
+        for t in threads {
+            assert!(t.join().is_ok());
+        }
+    }
+}
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 28de1ec..5eed37c 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -23,8 +23,10 @@
 use crate::globals::{DB, ENFORCEMENTS, LEGACY_IMPORTER, SUPER_KEY};
 use crate::key_parameter::KeyParameter as KsKeyParam;
 use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
+use crate::ks_err;
 use crate::metrics_store::log_key_creation_event_stats;
 use crate::remote_provisioning::RemProvState;
+use crate::rkpd_client::store_rkpd_attestation_key;
 use crate::super_key::{KeyBlob, SuperKeyManager};
 use crate::utils::{
     check_device_attestation_permissions, check_key_permission,
@@ -89,7 +91,7 @@
         id_rotation_state: IdRotationState,
     ) -> Result<(Strong<dyn IKeystoreSecurityLevel>, Uuid)> {
         let (dev, hw_info, km_uuid) = get_keymint_device(&security_level)
-            .context("In KeystoreSecurityLevel::new_native_binder.")?;
+            .context(ks_err!("KeystoreSecurityLevel::new_native_binder."))?;
         let result = BnKeystoreSecurityLevel::new_binder(
             Self {
                 security_level,
@@ -147,7 +149,7 @@
             SecurityLevel::SOFTWARE,
         ));
 
-        let creation_date = DateTime::now().context("Trying to make creation time.")?;
+        let creation_date = DateTime::now().context(ks_err!("Trying to make creation time."))?;
 
         let key = match key.domain {
             Domain::BLOB => KeyDescriptor {
@@ -171,7 +173,7 @@
                             user_id,
                             &key_blob,
                         )
-                        .context("In store_new_key. Failed to handle super encryption.")?;
+                        .context(ks_err!("Failed to handle super encryption."))?;
 
                     let mut key_metadata = KeyMetaData::new();
                     key_metadata.add(KeyMetaEntry::CreationDate(creation_date));
@@ -187,14 +189,14 @@
                             &key_metadata,
                             &self.km_uuid,
                         )
-                        .context("In store_new_key.")?;
+                        .context(ks_err!())?;
                     Ok(KeyDescriptor {
                         domain: Domain::KEY_ID,
                         nspace: key_id.id(),
                         ..Default::default()
                     })
                 })
-                .context("In store_new_key.")?,
+                .context(ks_err!())?,
         };
 
         Ok(KeyMetadata {
@@ -221,20 +223,19 @@
         let (km_blob, key_properties, key_id_guard, blob_metadata) = match key.domain {
             Domain::BLOB => {
                 check_key_permission(KeyPerm::Use, key, &None)
-                    .context("In create_operation: checking use permission for Domain::BLOB.")?;
+                    .context(ks_err!("checking use permission for Domain::BLOB."))?;
                 if forced {
-                    check_key_permission(KeyPerm::ReqForcedOp, key, &None).context(
-                        "In create_operation: checking forced permission for Domain::BLOB.",
-                    )?;
+                    check_key_permission(KeyPerm::ReqForcedOp, key, &None)
+                        .context(ks_err!("checking forced permission for Domain::BLOB."))?;
                 }
                 (
                     match &key.blob {
                         Some(blob) => blob,
                         None => {
-                            return Err(Error::sys()).context(concat!(
-                                "In create_operation: Key blob must be specified when",
-                                " using Domain::BLOB."
-                            ))
+                            return Err(Error::sys()).context(ks_err!(
+                                "Key blob must be specified when \
+                                using Domain::BLOB."
+                            ));
                         }
                     },
                     None,
@@ -265,12 +266,12 @@
                             )
                         })
                     })
-                    .context("In create_operation: Failed to load key blob.")?;
+                    .context(ks_err!("Failed to load key blob."))?;
 
                 let (blob, blob_metadata) =
-                    key_entry.take_key_blob_info().ok_or_else(Error::sys).context(concat!(
-                        "In create_operation: Successfully loaded key entry, ",
-                        "but KM blob was missing."
+                    key_entry.take_key_blob_info().ok_or_else(Error::sys).context(ks_err!(
+                        "Successfully loaded key entry, \
+                        but KM blob was missing."
                     ))?;
                 scoping_blob = blob;
 
@@ -285,11 +286,11 @@
 
         let purpose = operation_parameters.iter().find(|p| p.tag == Tag::PURPOSE).map_or(
             Err(Error::Km(ErrorCode::INVALID_ARGUMENT))
-                .context("In create_operation: No operation purpose specified."),
+                .context(ks_err!("No operation purpose specified.")),
             |kp| match kp.value {
                 KeyParameterValue::KeyPurpose(p) => Ok(p),
                 _ => Err(Error::Km(ErrorCode::INVALID_ARGUMENT))
-                    .context("In create_operation: Malformed KeyParameter."),
+                    .context(ks_err!("Malformed KeyParameter.")),
             },
         )?;
 
@@ -306,13 +307,13 @@
                 operation_parameters.as_ref(),
                 self.hw_info.timestampTokenRequired,
             )
-            .context("In create_operation.")?;
+            .context(ks_err!())?;
 
         let km_blob = SUPER_KEY
             .read()
             .unwrap()
             .unwrap_key_if_required(&blob_metadata, km_blob)
-            .context("In create_operation. Failed to handle super encryption.")?;
+            .context(ks_err!("Failed to handle super encryption."))?;
 
         let (begin_result, upgraded_blob) = self
             .upgrade_keyblob_if_required_with(
@@ -354,7 +355,7 @@
                     }
                 },
             )
-            .context("In create_operation: Failed to begin operation.")?;
+            .context(ks_err!("Failed to begin operation."))?;
 
         let operation_challenge = auth_info.finalize_create_authorization(begin_result.challenge);
 
@@ -369,10 +370,10 @@
                 LoggingInfo::new(self.security_level, purpose, op_params, upgraded_blob.is_some()),
             ),
             None => {
-                return Err(Error::sys()).context(concat!(
-                    "In create_operation: Begin operation returned successfully, ",
-                    "but did not return a valid operation."
-                ))
+                return Err(Error::sys()).context(ks_err!(
+                    "Begin operation returned successfully, \
+                    but did not return a valid operation."
+                ));
             }
         };
 
@@ -380,7 +381,7 @@
             KeystoreOperation::new_native_binder(operation)
                 .as_binder()
                 .into_interface()
-                .context("In create_operation: Failed to create IKeystoreOperation.")?;
+                .context(ks_err!("Failed to create IKeystoreOperation."))?;
 
         Ok(CreateOperationResponse {
             iOperation: Some(op_binder),
@@ -407,10 +408,10 @@
         // Unconditionally add the CREATION_DATETIME tag and prevent callers from
         // specifying it.
         if params.iter().any(|kp| kp.tag == Tag::CREATION_DATETIME) {
-            return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(
-                "In KeystoreSecurityLevel::add_required_parameters: \
-                Specifying Tag::CREATION_DATETIME is not allowed.",
-            );
+            return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(ks_err!(
+                "KeystoreSecurityLevel::add_required_parameters: \
+                Specifying Tag::CREATION_DATETIME is not allowed."
+            ));
         }
 
         // Add CREATION_DATETIME only if the backend version Keymint V1 (100) or newer.
@@ -420,16 +421,16 @@
                 value: KeyParameterValue::DateTime(
                     SystemTime::now()
                         .duration_since(SystemTime::UNIX_EPOCH)
-                        .context(
-                            "In KeystoreSecurityLevel::add_required_parameters: \
-                        Failed to get epoch time.",
-                        )?
+                        .context(ks_err!(
+                            "KeystoreSecurityLevel::add_required_parameters: \
+                                Failed to get epoch time."
+                        ))?
                         .as_millis()
                         .try_into()
-                        .context(
-                            "In KeystoreSecurityLevel::add_required_parameters: \
-                        Failed to convert epoch time.",
-                        )?,
+                        .context(ks_err!(
+                            "KeystoreSecurityLevel::add_required_parameters: \
+                                Failed to convert epoch time."
+                        ))?,
                 ),
             });
         }
@@ -441,9 +442,8 @@
                     "In KeystoreSecurityLevel::add_required_parameters calling: get_aaid",
                     500,
                 );
-                keystore2_aaid::get_aaid(uid).map_err(|e| {
-                    anyhow!(format!("In add_required_parameters: get_aaid returned status {}.", e))
-                })
+                keystore2_aaid::get_aaid(uid)
+                    .map_err(|e| anyhow!(ks_err!("get_aaid returned status {}.", e)))
             }?;
 
             result.push(KeyParameter {
@@ -456,14 +456,15 @@
             if check_key_permission(KeyPerm::GenUniqueId, key, &None).is_err()
                 && check_unique_id_attestation_permissions().is_err()
             {
-                return Err(Error::perm()).context(
-                    "In add_required_parameters: \
-                    Caller does not have the permission to generate a unique ID",
-                );
+                return Err(Error::perm()).context(ks_err!(
+                    "Caller does not have the permission to generate a unique ID"
+                ));
             }
-            if self.id_rotation_state.had_factory_reset_since_id_rotation().context(
-                "In add_required_parameters: Call to had_factory_reset_since_id_rotation failed.",
-            )? {
+            if self
+                .id_rotation_state
+                .had_factory_reset_since_id_rotation()
+                .context(ks_err!("Call to had_factory_reset_since_id_rotation failed."))?
+            {
                 result.push(KeyParameter {
                     tag: Tag::RESET_SINCE_ID_ROTATION,
                     value: KeyParameterValue::BoolValue(true),
@@ -474,8 +475,7 @@
         // If the caller requests any device identifier attestation tag, check that they hold the
         // correct Android permission.
         if params.iter().any(|kp| is_device_id_attestation_tag(kp.tag)) {
-            check_device_attestation_permissions().context(concat!(
-                "In add_required_parameters: ",
+            check_device_attestation_permissions().context(ks_err!(
                 "Caller does not have the permission to attest device identifiers."
             ))?;
         }
@@ -513,7 +513,7 @@
     ) -> Result<KeyMetadata> {
         if key.domain != Domain::BLOB && key.alias.is_none() {
             return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
-                .context("In generate_key: Alias must be specified");
+                .context(ks_err!("Alias must be specified"));
         }
         let caller_uid = ThreadState::get_calling_uid();
 
@@ -529,7 +529,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(ks_err!())?;
 
         let attestation_key_info = match (key.domain, attest_key_descriptor) {
             (Domain::BLOB, _) => None,
@@ -544,11 +544,11 @@
                         &mut db.borrow_mut(),
                     )
                 })
-                .context("In generate_key: Trying to get an attestation key")?,
+                .context(ks_err!("Trying to get an attestation key"))?,
         };
         let params = self
             .add_required_parameters(caller_uid, params, &key)
-            .context("In generate_key: Trying to get aaid.")?;
+            .context(ks_err!("Trying to get aaid."))?;
 
         let creation_result = match attestation_key_info {
             Some(AttestationKeyInfo::UserGenerated {
@@ -581,42 +581,32 @@
                         })
                     },
                 )
-                .context("In generate_key: Using user generated attestation key.")
+                .context(ks_err!("Using user generated attestation key."))
                 .map(|(result, _)| result),
-            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.")
+            Some(AttestationKeyInfo::RkpdProvisioned { attestation_key, attestation_certs }) => {
+                self.upgrade_rkpd_keyblob_if_required_with(&attestation_key.keyBlob, &[], |blob| {
+                    map_km_error({
+                        let _wp = self.watch_millis(
+                            concat!(
+                                "In KeystoreSecurityLevel::generate_key (RkpdProvisioned): ",
+                                "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(ks_err!("While generating Key with remote provisioned attestation key."))
                 .map(|(mut result, _)| {
                     result.certificateChain.push(attestation_certs);
                     result
-                }),
+                })
+            }
             None => map_km_error({
                 let _wp = self.watch_millis(
                     concat!(
@@ -627,12 +617,12 @@
                 );
                 self.keymint.generateKey(&params, None)
             })
-            .context("While generating Key without explicit attestation key."),
+            .context(ks_err!("While generating Key without explicit attestation key.")),
         }
-        .context("In generate_key.")?;
+        .context(ks_err!())?;
 
         let user_id = uid_to_android_user(caller_uid);
-        self.store_new_key(key, creation_result, user_id, Some(flags)).context("In generate_key.")
+        self.store_new_key(key, creation_result, user_id, Some(flags)).context(ks_err!())
     }
 
     fn import_key(
@@ -645,7 +635,7 @@
     ) -> Result<KeyMetadata> {
         if key.domain != Domain::BLOB && key.alias.is_none() {
             return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
-                .context("In import_key: Alias must be specified");
+                .context(ks_err!("Alias must be specified"));
         }
         let caller_uid = ThreadState::get_calling_uid();
 
@@ -660,17 +650,17 @@
         };
 
         // 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(ks_err!("In import_key."))?;
 
         let params = self
             .add_required_parameters(caller_uid, params, &key)
-            .context("In import_key: Trying to get aaid.")?;
+            .context(ks_err!("Trying to get aaid."))?;
 
         let format = params
             .iter()
             .find(|p| p.tag == Tag::ALGORITHM)
             .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
-            .context("No KeyParameter 'Algorithm'.")
+            .context(ks_err!("No KeyParameter 'Algorithm'."))
             .and_then(|p| match &p.value {
                 KeyParameterValue::Algorithm(Algorithm::AES)
                 | KeyParameterValue::Algorithm(Algorithm::HMAC)
@@ -678,9 +668,9 @@
                 KeyParameterValue::Algorithm(Algorithm::RSA)
                 | KeyParameterValue::Algorithm(Algorithm::EC) => Ok(KeyFormat::PKCS8),
                 v => Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
-                    .context(format!("Unknown Algorithm {:?}.", v)),
+                    .context(ks_err!("Unknown Algorithm {:?}.", v)),
             })
-            .context("In import_key.")?;
+            .context(ks_err!())?;
 
         let km_dev = &self.keymint;
         let creation_result = map_km_error({
@@ -688,10 +678,10 @@
                 self.watch_millis("In KeystoreSecurityLevel::import_key: calling importKey.", 500);
             km_dev.importKey(&params, format, key_data, None /* attestKey */)
         })
-        .context("In import_key: Trying to call importKey")?;
+        .context(ks_err!("Trying to call importKey"))?;
 
         let user_id = uid_to_android_user(caller_uid);
-        self.store_new_key(key, creation_result, user_id, Some(flags)).context("In import_key.")
+        self.store_new_key(key, creation_result, user_id, Some(flags)).context(ks_err!())
     }
 
     fn import_wrapped_key(
@@ -708,20 +698,16 @@
                 domain: Domain::SELINUX, blob: Some(ref blob), alias: Some(_), ..
             } => blob,
             _ => {
-                return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context(format!(
-                    concat!(
-                        "In import_wrapped_key: Alias and blob must be specified ",
-                        "and domain must be APP or SELINUX. {:?}"
-                    ),
+                return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context(ks_err!(
+                    "Alias and blob must be specified and domain must be APP or SELINUX. {:?}",
                     key
-                ))
+                ));
             }
         };
 
         if wrapping_key.domain == Domain::BLOB {
-            return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context(
-                "In import_wrapped_key: Import wrapped key not supported for self managed blobs.",
-            );
+            return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+                .context(ks_err!("Import wrapped key not supported for self managed blobs."));
         }
 
         let caller_uid = ThreadState::get_calling_uid();
@@ -744,7 +730,7 @@
         };
 
         // 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(ks_err!())?;
 
         let super_key = SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(user_id);
 
@@ -760,20 +746,18 @@
                     )
                 })
             })
-            .context("Failed to load wrapping key.")?;
+            .context(ks_err!("Failed to load wrapping key."))?;
 
-        let (wrapping_key_blob, wrapping_blob_metadata) = wrapping_key_entry
-            .take_key_blob_info()
-            .ok_or_else(error::Error::sys)
-            .context("No km_blob after successfully loading key. This should never happen.")?;
+        let (wrapping_key_blob, wrapping_blob_metadata) =
+            wrapping_key_entry.take_key_blob_info().ok_or_else(error::Error::sys).context(
+                ks_err!("No km_blob after successfully loading key. This should never happen."),
+            )?;
 
         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.",
-            )?;
+            .context(ks_err!("Failed to handle super encryption for wrapping key."))?;
 
         // km_dev.importWrappedKey does not return a certificate chain.
         // TODO Do we assume that all wrapped keys are symmetric?
@@ -820,10 +804,10 @@
                     Ok(creation_result)
                 },
             )
-            .context("In import_wrapped_key.")?;
+            .context(ks_err!())?;
 
         self.store_new_key(key, creation_result, user_id, None)
-            .context("In import_wrapped_key: Trying to store the new key.")
+            .context(ks_err!("Trying to store the new key."))
     }
 
     fn store_upgraded_keyblob(
@@ -834,7 +818,7 @@
     ) -> Result<()> {
         let (upgraded_blob_to_be_stored, new_blob_metadata) =
             SuperKeyManager::reencrypt_if_required(key_blob, upgraded_blob)
-                .context("In store_upgraded_keyblob: Failed to handle super encryption.")?;
+                .context(ks_err!("Failed to handle super encryption."))?;
 
         let mut new_blob_metadata = new_blob_metadata.unwrap_or_default();
         if let Some(uuid) = km_uuid {
@@ -850,7 +834,7 @@
                 Some(&new_blob_metadata),
             )
         })
-        .context("In store_upgraded_keyblob: Failed to insert upgraded blob into the database.")
+        .context(ks_err!("Failed to insert upgraded blob into the database."))
     }
 
     fn upgrade_keyblob_if_required_with<T, F>(
@@ -874,51 +858,66 @@
                 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",
-                    )
+                    Self::store_upgraded_keyblob(kid, km_uuid, key_blob, upgraded_blob)
+                        .context(ks_err!("store_upgraded_keyblob failed"))
                 } else {
                     Ok(())
                 }
             },
         )
-        .context("In KeystoreSecurityLevel::upgrade_keyblob_if_required_with.")?;
+        .context(ks_err!())?;
 
         // 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"
-                ))?;
+                Self::store_upgraded_keyblob(kid, km_uuid, key_blob, key_blob)
+                    .context(ks_err!("store_upgraded_keyblob failed in forced reencrypt"))?;
             }
         }
         Ok((v, upgraded_blob))
     }
 
+    fn upgrade_rkpd_keyblob_if_required_with<T, F>(
+        &self,
+        key_blob: &[u8],
+        params: &[KeyParameter],
+        f: F,
+    ) -> Result<(T, Option<Vec<u8>>)>
+    where
+        F: Fn(&[u8]) -> Result<T, Error>,
+    {
+        crate::utils::upgrade_keyblob_if_required_with(
+            &*self.keymint,
+            key_blob,
+            params,
+            f,
+            |upgraded_blob| {
+                store_rkpd_attestation_key(&self.security_level, key_blob, upgraded_blob)
+                    .context(ks_err!("Failed store_rkpd_attestation_key()."))
+            },
+        )
+        .context(ks_err!())
+    }
+
     fn convert_storage_key_to_ephemeral(
         &self,
         storage_key: &KeyDescriptor,
     ) -> Result<EphemeralStorageKeyResponse> {
         if storage_key.domain != Domain::BLOB {
-            return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context(concat!(
-                "In IKeystoreSecurityLevel convert_storage_key_to_ephemeral: ",
-                "Key must be of Domain::BLOB"
-            ));
+            return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+                .context(ks_err!("Key must be of Domain::BLOB"));
         }
         let key_blob = storage_key
             .blob
             .as_ref()
             .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
-            .context(
-                "In IKeystoreSecurityLevel convert_storage_key_to_ephemeral: No key blob specified",
-            )?;
+            .context(ks_err!("No key blob specified"))?;
 
         // convert_storage_key_to_ephemeral requires the associated permission
         check_key_permission(KeyPerm::ConvertStorageKeyToEphemeral, storage_key, &None)
-            .context("In convert_storage_key_to_ephemeral: Check permission")?;
+            .context(ks_err!("Check permission"))?;
 
         let km_dev = &self.keymint;
         match {
@@ -942,7 +941,7 @@
                     );
                     map_km_error(km_dev.upgradeKey(key_blob, &[]))
                 }
-                .context("In convert_storage_key_to_ephemeral: Failed to upgrade key blob.")?;
+                .context(ks_err!("Failed to upgrade key blob."))?;
                 let ephemeral_key = {
                     let _wp = self.watch_millis(
                         "In convert_storage_key_to_ephemeral: calling convertStorageKeyToEphemeral (2)",
@@ -950,8 +949,7 @@
                     );
                     map_km_error(km_dev.convertStorageKeyToEphemeral(&upgraded_blob))
                 }
-                    .context(concat!(
-                        "In convert_storage_key_to_ephemeral: ",
+                    .context(ks_err!(
                         "Failed to retrieve ephemeral key (after upgrade)."
                     ))?;
                 Ok(EphemeralStorageKeyResponse {
@@ -959,31 +957,30 @@
                     upgradedBlob: Some(upgraded_blob),
                 })
             }
-            Err(e) => Err(e)
-                .context("In convert_storage_key_to_ephemeral: Failed to retrieve ephemeral key."),
+            Err(e) => Err(e).context(ks_err!("Failed to retrieve ephemeral key.")),
         }
     }
 
     fn delete_key(&self, key: &KeyDescriptor) -> Result<()> {
         if key.domain != Domain::BLOB {
             return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
-                .context("In IKeystoreSecurityLevel delete_key: Key must be of Domain::BLOB");
+                .context(ks_err!("delete_key: Key must be of Domain::BLOB"));
         }
 
         let key_blob = key
             .blob
             .as_ref()
             .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
-            .context("In IKeystoreSecurityLevel delete_key: No key blob specified")?;
+            .context(ks_err!("delete_key: No key blob specified"))?;
 
         check_key_permission(KeyPerm::Delete, key, &None)
-            .context("In IKeystoreSecurityLevel delete_key: Checking delete permissions")?;
+            .context(ks_err!("delete_key: Checking delete permissions"))?;
 
         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(ks_err!("keymint device deleteKey"))
         }
     }
 }
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index d634e0c..7ba8cbc 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -18,10 +18,11 @@
 use std::collections::HashMap;
 
 use crate::audit_log::log_key_deleted;
+use crate::ks_err;
 use crate::permission::{KeyPerm, KeystorePerm};
 use crate::security_level::KeystoreSecurityLevel;
 use crate::utils::{
-    check_grant_permission, check_key_permission, check_keystore_permission,
+    check_grant_permission, check_key_permission, check_keystore_permission, count_key_entries,
     key_parameters_to_authorizations, list_key_entries, uid_to_android_user, watchdog as wd,
 };
 use crate::{
@@ -65,10 +66,7 @@
             SecurityLevel::TRUSTED_ENVIRONMENT,
             id_rotation_state.clone(),
         )
-        .context(concat!(
-            "In KeystoreService::new_native_binder: ",
-            "Trying to construct mandatory security level TEE."
-        ))?;
+        .context(ks_err!("Trying to construct mandatory security level TEE."))?;
         result.i_sec_level_by_uuid.insert(uuid, dev);
         result.uuid_by_sec_level.insert(SecurityLevel::TRUSTED_ENVIRONMENT, uuid);
 
@@ -85,9 +83,7 @@
             .set_init(move || {
                 (create_thread_local_db(), uuid_by_sec_level, LEGACY_BLOB_LOADER.clone())
             })
-            .context(
-                "In KeystoreService::new_native_binder: Trying to initialize the legacy migrator.",
-            )?;
+            .context(ks_err!("Trying to initialize the legacy migrator."))?;
 
         Ok(BnKeystoreService::new_binder(
             result,
@@ -107,8 +103,7 @@
         if let Some(dev) = self.i_sec_level_by_uuid.get(uuid) {
             Ok(dev.clone())
         } else {
-            Err(error::Error::sys())
-                .context("In get_i_sec_level_by_uuid: KeyMint instance for key not found.")
+            Err(error::Error::sys()).context(ks_err!("KeyMint instance for key not found."))
         }
     }
 
@@ -124,7 +119,7 @@
             Ok(dev.clone())
         } else {
             Err(error::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
-                .context("In get_security_level: No such security level.")
+                .context(ks_err!("No such security level."))
         }
     }
 
@@ -146,12 +141,12 @@
                     )
                 })
             })
-            .context("In get_key_entry, while trying to load key info.")?;
+            .context(ks_err!("while trying to load key info."))?;
 
         let i_sec_level = if !key_entry.pure_cert() {
             Some(
                 self.get_i_sec_level_by_uuid(key_entry.km_uuid())
-                    .context("In get_key_entry: Trying to get security level proxy.")?,
+                    .context(ks_err!("Trying to get security level proxy."))?,
             )
         } else {
             None
@@ -173,7 +168,7 @@
                     .creation_date()
                     .map(|d| d.to_millis_epoch())
                     .ok_or(Error::Rc(ResponseCode::VALUE_CORRUPTED))
-                    .context("In get_key_entry: Trying to get creation date.")?,
+                    .context(ks_err!("Trying to get creation date."))?,
                 authorizations: key_parameters_to_authorizations(key_entry.into_key_parameters()),
             },
         })
@@ -196,10 +191,7 @@
                     KeyType::Client,
                     KeyEntryLoadBits::NONE,
                     caller_uid,
-                    |k, av| {
-                        check_key_permission(KeyPerm::Update, k, &av)
-                            .context("In update_subcomponent.")
-                    },
+                    |k, av| check_key_permission(KeyPerm::Update, k, &av).context(ks_err!()),
                 )
             }) {
                 Err(e) => match e.root_cause().downcast_ref::<Error>() {
@@ -208,22 +200,23 @@
                 },
                 Ok(v) => Ok(Some(v)),
             }
-            .context("Failed to load key entry.")?;
+            .context(ks_err!("Failed to load key entry."))?;
 
             let mut db = db.borrow_mut();
             if let Some((key_id_guard, _key_entry)) = entry {
                 db.set_blob(&key_id_guard, SubComponentType::CERT, public_cert, None)
-                    .context("Failed to update cert subcomponent.")?;
+                    .context(ks_err!("Failed to update cert subcomponent."))?;
 
                 db.set_blob(&key_id_guard, SubComponentType::CERT_CHAIN, certificate_chain, None)
-                    .context("Failed to update cert chain subcomponent.")?;
+                    .context(ks_err!("Failed to update cert chain subcomponent."))?;
                 return Ok(());
             }
 
             // If we reach this point we have to check the special condition where a certificate
             // entry may be made.
             if !(public_cert.is_none() && certificate_chain.is_some()) {
-                return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND)).context("No key to update.");
+                return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+                    .context(ks_err!("No key to update."));
             }
 
             // So we know that we have a certificate chain and no public cert.
@@ -238,13 +231,13 @@
                 (Domain::SELINUX, Some(_)) => key.clone(),
                 _ => {
                     return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT))
-                        .context("Domain must be APP or SELINUX to insert a certificate.")
+                        .context(ks_err!("Domain must be APP or SELINUX to insert a certificate."))
                 }
             };
 
             // Security critical: This must return on failure. Do not remove the `?`;
             check_key_permission(KeyPerm::Rebind, &key, &None)
-                .context("Caller does not have permission to insert this certificate.")?;
+                .context(ks_err!("Caller does not have permission to insert this certificate."))?;
 
             db.store_new_certificate(
                 &key,
@@ -252,23 +245,29 @@
                 certificate_chain.unwrap(),
                 &KEYSTORE_UUID,
             )
-            .context("Failed to insert new certificate.")?;
+            .context(ks_err!("Failed to insert new certificate."))?;
             Ok(())
         })
-        .context("In update_subcomponent.")
+        .context(ks_err!())
     }
 
-    fn list_entries(&self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
+    fn get_key_descriptor_for_lookup(
+        &self,
+        domain: Domain,
+        namespace: i64,
+    ) -> Result<KeyDescriptor> {
         let mut k = match domain {
             Domain::APP => KeyDescriptor {
                 domain,
                 nspace: ThreadState::get_calling_uid() as u64 as i64,
                 ..Default::default()
             },
-            Domain::SELINUX => KeyDescriptor{domain, nspace: namespace, ..Default::default()},
-            _ => return Err(Error::perm()).context(
-                "In list_entries: List entries is only supported for Domain::APP and Domain::SELINUX."
-            ),
+            Domain::SELINUX => KeyDescriptor { domain, nspace: namespace, ..Default::default() },
+            _ => {
+                return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(ks_err!(
+                    "List entries is only supported for Domain::APP and Domain::SELINUX."
+                ))
+            }
         };
 
         // First we check if the caller has the info permission for the selected domain/namespace.
@@ -278,19 +277,40 @@
         // selected.
         if let Err(e) = check_key_permission(KeyPerm::GetInfo, &k, &None) {
             if let Some(selinux::Error::PermissionDenied) =
-                e.root_cause().downcast_ref::<selinux::Error>() {
-
+                e.root_cause().downcast_ref::<selinux::Error>()
+            {
                 check_keystore_permission(KeystorePerm::List)
-                    .context("In list_entries: While checking keystore permission.")?;
+                    .context(ks_err!("While checking keystore permission."))?;
                 if namespace != -1 {
                     k.nspace = namespace;
                 }
             } else {
-                return Err(e).context("In list_entries: While checking key permission.")?;
+                return Err(e).context(ks_err!("While checking key permission."))?;
             }
         }
+        Ok(k)
+    }
 
-        DB.with(|db| list_key_entries(&mut db.borrow_mut(), k.domain, k.nspace))
+    fn list_entries(&self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
+        let k = self.get_key_descriptor_for_lookup(domain, namespace)?;
+
+        DB.with(|db| list_key_entries(&mut db.borrow_mut(), k.domain, k.nspace, None))
+    }
+
+    fn count_num_entries(&self, domain: Domain, namespace: i64) -> Result<i32> {
+        let k = self.get_key_descriptor_for_lookup(domain, namespace)?;
+
+        DB.with(|db| count_key_entries(&mut db.borrow_mut(), k.domain, k.nspace))
+    }
+
+    fn list_entries_batched(
+        &self,
+        domain: Domain,
+        namespace: i64,
+        start_past_alias: Option<&str>,
+    ) -> Result<Vec<KeyDescriptor>> {
+        let k = self.get_key_descriptor_for_lookup(domain, namespace)?;
+        DB.with(|db| list_key_entries(&mut db.borrow_mut(), k.domain, k.nspace, start_past_alias))
     }
 
     fn delete_key(&self, key: &KeyDescriptor) -> Result<()> {
@@ -301,11 +321,12 @@
         DB.with(|db| {
             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.")
+                    check_key_permission(KeyPerm::Delete, k, &av)
+                        .context(ks_err!("During delete_key."))
                 })
             })
         })
-        .context("In delete_key: Trying to unbind the key.")?;
+        .context(ks_err!("Trying to unbind the key."))?;
         Ok(())
     }
 
@@ -330,7 +351,7 @@
                 )
             })
         })
-        .context("In KeystoreService::grant.")
+        .context(ks_err!("KeystoreService::grant."))
     }
 
     fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> Result<()> {
@@ -339,7 +360,7 @@
                 check_key_permission(KeyPerm::Grant, k, &None)
             })
         })
-        .context("In KeystoreService::ungrant.")
+        .context(ks_err!("KeystoreService::ungrant."))
     }
 }
 
@@ -393,4 +414,18 @@
         let _wp = wd::watch_millis("IKeystoreService::ungrant", 500);
         map_or_log_err(self.ungrant(key, grantee_uid), Ok)
     }
+    fn listEntriesBatched(
+        &self,
+        domain: Domain,
+        namespace: i64,
+        start_past_alias: Option<&str>,
+    ) -> binder::Result<Vec<KeyDescriptor>> {
+        let _wp = wd::watch_millis("IKeystoreService::listEntriesBatched", 500);
+        map_or_log_err(self.list_entries_batched(domain, namespace, start_past_alias), Ok)
+    }
+
+    fn getNumberOfEntries(&self, domain: Domain, namespace: i64) -> binder::Result<i32> {
+        let _wp = wd::watch_millis("IKeystoreService::getNumberOfEntries", 500);
+        map_or_log_err(self.count_num_entries(domain, namespace), Ok)
+    }
 }
diff --git a/keystore2/src/shared_secret_negotiation.rs b/keystore2/src/shared_secret_negotiation.rs
index 42d38d2..739f4ba 100644
--- a/keystore2/src/shared_secret_negotiation.rs
+++ b/keystore2/src/shared_secret_negotiation.rs
@@ -23,7 +23,8 @@
 };
 use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
 use anyhow::Result;
-use keystore2_vintf::{get_aidl_instances, get_hidl_instances};
+use binder::get_declared_instances;
+use keystore2_vintf::get_hidl_instances;
 use std::fmt::{self, Display, Formatter};
 use std::time::Duration;
 
@@ -111,6 +112,8 @@
 static KEYMASTER_INTERFACE_NAME: &str = "IKeymasterDevice";
 static SHARED_SECRET_PACKAGE_NAME: &str = "android.hardware.security.sharedsecret";
 static SHARED_SECRET_INTERFACE_NAME: &str = "ISharedSecret";
+static SHARED_SECRET_PACKAGE_AND_INTERFACE_NAME: &str =
+    "android.hardware.security.sharedsecret.ISharedSecret";
 static COMPAT_PACKAGE_NAME: &str = "android.security.compat";
 
 /// Lists participants.
@@ -121,11 +124,11 @@
     let mut legacy_strongbox_found: bool = false;
     Ok([(4, 1), (4, 0)]
         .iter()
-        .map(|(ma, mi)| {
+        .flat_map(|(ma, mi)| {
             get_hidl_instances(KEYMASTER_PACKAGE_NAME, *ma, *mi, KEYMASTER_INTERFACE_NAME)
-                .into_iter()
+                .iter()
                 .filter_map(|name| {
-                    filter_map_legacy_km_instances(name, (*ma, *mi)).and_then(|sp| {
+                    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;
@@ -140,10 +143,9 @@
                 })
                 .collect::<Vec<SharedSecretParticipant>>()
         })
-        .into_iter()
-        .flatten()
         .chain({
-            get_aidl_instances(SHARED_SECRET_PACKAGE_NAME, 1, SHARED_SECRET_INTERFACE_NAME)
+            get_declared_instances(SHARED_SECRET_PACKAGE_AND_INTERFACE_NAME)
+                .unwrap()
                 .into_iter()
                 .map(SharedSecretParticipant::Aidl)
                 .collect::<Vec<_>>()
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index 74e3e56..f000213 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -25,6 +25,7 @@
     error::Error,
     error::ResponseCode,
     key_parameter::{KeyParameter, KeyParameterValue},
+    ks_err,
     legacy_blob::LegacyBlobLoader,
     legacy_importer::LegacyImporter,
     raw_device::KeyMintDevice,
@@ -156,19 +157,17 @@
 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 SuperKey::decrypt: Decryption failed.")
+            aes_gcm_decrypt(data, iv, tag, &self.key).context(ks_err!("Decryption failed."))
         } else {
-            Err(Error::sys()).context("In SuperKey::decrypt: Key is not an AES key.")
+            Err(Error::sys()).context(ks_err!("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.")
+            aes_gcm_encrypt(plaintext, &self.key).context(ks_err!("Encryption failed."))
         } else {
-            Err(Error::sys()).context("In SuperKey::encrypt: Key is not an AES key.")
+            Err(Error::sys()).context(ks_err!("Key is not an AES key."))
         }
     }
 }
@@ -203,7 +202,7 @@
             .as_ref()
             .map(|(key_blob, _)| KeyBlob::Ref(key_blob))
             .ok_or(Error::Rc(ResponseCode::KEY_NOT_FOUND))
-            .context("In LockedKey::decrypt: Missing key blob info.")?;
+            .context(ks_err!("Missing key blob info."))?;
         let key_params = vec![
             KeyParameterValue::Algorithm(Algorithm::AES),
             KeyParameterValue::KeySize(256),
@@ -270,10 +269,7 @@
             self.key_index.insert(id, Arc::downgrade(super_key));
             Ok(())
         } else {
-            Err(Error::sys()).context(format!(
-                "In add_key_to_key_index: cannot add key with ID {:?}",
-                super_key.id
-            ))
+            Err(Error::sys()).context(ks_err!("Cannot add key with ID {:?}", super_key.id))
         }
     }
 }
@@ -290,8 +286,8 @@
             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")?;
+        let level_zero_key =
+            get_level_zero_key(db).context(ks_err!("get_level_zero_key failed"))?;
         skm_guard.data.boot_level_key_cache =
             Some(Mutex::new(BootLevelKeyCache::new(level_zero_key)));
         log::info!("Starting boot level watcher.");
@@ -307,11 +303,11 @@
     /// Blocks waiting for system property changes, so must be run in its own thread.
     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")?;
+            .context(ks_err!("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")?;
+                .context(ks_err!("read of property failed"))?;
 
             // This scope limits the skm_guard life, so we don't hold the skm_guard while
             // waiting.
@@ -322,14 +318,14 @@
                     .boot_level_key_cache
                     .as_mut()
                     .ok_or_else(Error::sys)
-                    .context("In watch_boot_level: Boot level cache not initialized")?
+                    .context(ks_err!("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")?;
+                        .context(ks_err!("advance_boot_level failed"))?;
                 } else {
                     log::info!(
                         "keystore.boot_level {} hits maximum {}, finishing.",
@@ -340,7 +336,7 @@
                     break;
                 }
             }
-            w.wait().context("In watch_boot_level: property wait failed")?;
+            w.wait().context(ks_err!("property wait failed"))?;
         }
         Ok(())
     }
@@ -363,7 +359,7 @@
     ) -> Result<()> {
         self.data
             .add_key_to_key_index(&super_key)
-            .context("In install_per_boot_key_for_user: add_key_to_key_index failed")?;
+            .context(ks_err!("add_key_to_key_index failed"))?;
         self.data.user_keys.entry(user).or_default().per_boot = Some(super_key);
         Ok(())
     }
@@ -379,7 +375,7 @@
                 .as_ref()
                 .map(|b| b.lock().unwrap().aes_key(*level as usize))
                 .transpose()
-                .context("In lookup_key: aes_key failed")?
+                .context(ks_err!("aes_key failed"))?
                 .flatten()
                 .map(|key| {
                     Arc::new(SuperKey {
@@ -425,12 +421,12 @@
                     // For backward compatibility we need to check if there is a super key present.
                     let super_key = legacy_blob_loader
                         .load_super_key(user, pw)
-                        .context("In create_new_key: Failed to load legacy key blob.")?;
+                        .context(ks_err!("Failed to load legacy key blob."))?;
                     let super_key = match super_key {
                         None => {
                             // No legacy file was found. So we generate a new key.
                             generate_aes256_key()
-                                .context("In create_new_key: Failed to generate AES 256 key.")?
+                                .context(ks_err!("Failed to generate AES 256 key."))?
                         }
                         Some(key) => key,
                     };
@@ -442,10 +438,10 @@
                     Self::encrypt_with_password(&super_key, pw).context("In create_new_key.")
                 },
             )
-            .context("In unlock_user_key: Failed to get key id.")?;
+            .context(ks_err!("Failed to get key id."))?;
 
         self.populate_cache_from_super_key_blob(user, USER_SUPER_KEY.algorithm, entry, pw)
-            .context("In unlock_user_key.")?;
+            .context(ks_err!())?;
         Ok(())
     }
 
@@ -459,12 +455,12 @@
         Ok(if let Some(key_id) = SuperKeyIdentifier::from_metadata(metadata) {
             let super_key = self
                 .lookup_key(&key_id)
-                .context("In unwrap_key: lookup_key failed")?
+                .context(ks_err!("lookup_key failed"))?
                 .ok_or(Error::Rc(ResponseCode::LOCKED))
-                .context("In unwrap_key: Required super decryption key is not in memory.")?;
+                .context(ks_err!("Required super decryption key is not in memory."))?;
             KeyBlob::Sensitive {
                 key: Self::unwrap_key_with_key(blob, metadata, &super_key)
-                    .context("In unwrap_key: unwrap_key_with_key failed")?,
+                    .context(ks_err!("unwrap_key_with_key failed"))?,
                 reencrypt_with: super_key.reencrypt_with.as_ref().unwrap_or(&super_key).clone(),
                 force_reencrypt: super_key.reencrypt_with.is_some(),
             }
@@ -477,14 +473,11 @@
     fn unwrap_key_with_key(blob: &[u8], metadata: &BlobMetaData, key: &SuperKey) -> Result<ZVec> {
         match key.algorithm {
             SuperEncryptionAlgorithm::Aes256Gcm => match (metadata.iv(), metadata.aead_tag()) {
-                (Some(iv), Some(tag)) => key
-                    .decrypt(blob, iv, tag)
-                    .context("In unwrap_key_with_key: Failed to decrypt the key blob."),
-                (iv, tag) => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
-                    concat!(
-                        "In unwrap_key_with_key: Key has incomplete metadata.",
-                        "Present: iv: {}, aead_tag: {}."
-                    ),
+                (Some(iv), Some(tag)) => {
+                    key.decrypt(blob, iv, tag).context(ks_err!("Failed to decrypt the key blob."))
+                }
+                (iv, tag) => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(ks_err!(
+                    "Key has incomplete metadata. Present: iv: {}, aead_tag: {}.",
                     iv.is_some(),
                     tag.is_some(),
                 )),
@@ -494,14 +487,12 @@
                     (Some(public_key), Some(salt), Some(iv), Some(aead_tag)) => {
                         ECDHPrivateKey::from_private_key(&key.key)
                             .and_then(|k| k.decrypt_message(public_key, salt, iv, blob, aead_tag))
-                            .context(
-                                "In unwrap_key_with_key: Failed to decrypt the key blob with ECDH.",
-                            )
+                            .context(ks_err!("Failed to decrypt the key blob with ECDH."))
                     }
                     (public_key, salt, iv, aead_tag) => {
-                        Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
+                        Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(ks_err!(
                             concat!(
-                                "In unwrap_key_with_key: Key has incomplete metadata.",
+                                "Key has incomplete metadata. ",
                                 "Present: public_key: {}, salt: {}, iv: {}, aead_tag: {}."
                             ),
                             public_key.is_some(),
@@ -526,14 +517,12 @@
     ) -> Result<bool> {
         let key_in_db = db
             .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.")?;
+            .context(ks_err!())?;
 
         if key_in_db {
             Ok(key_in_db)
         } else {
-            legacy_importer
-                .has_super_key(user_id)
-                .context("In super_key_exists_in_db_for_user: Trying to query legacy db.")
+            legacy_importer.has_super_key(user_id).context(ks_err!("Trying to query legacy db."))
         }
     }
 
@@ -550,13 +539,13 @@
         let alias = &USER_SUPER_KEY;
         let result = legacy_importer
             .with_try_import_super_key(user_id, pw, || db.load_super_key(alias, user_id))
-            .context("In check_and_unlock_super_key. Failed to load super key")?;
+            .context(ks_err!("Failed to load super key"))?;
 
         match result {
             Some((_, entry)) => {
                 let super_key = self
                     .populate_cache_from_super_key_blob(user_id, alias.algorithm, entry, pw)
-                    .context("In check_and_unlock_super_key.")?;
+                    .context(ks_err!())?;
                 Ok(UserState::LskfUnlocked(super_key))
             }
             None => Ok(UserState::Uninitialized),
@@ -578,17 +567,17 @@
     ) -> 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.")?;
+            .context(ks_err!("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.
-            let super_key = generate_aes256_key()
-                .context("In check_and_initialize_super_key: Failed to generate AES 256 key.")?;
+            let super_key =
+                generate_aes256_key().context(ks_err!("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.
-            let (encrypted_super_key, blob_metadata) = Self::encrypt_with_password(&super_key, pw)
-                .context("In check_and_initialize_super_key.")?;
+            let (encrypted_super_key, blob_metadata) =
+                Self::encrypt_with_password(&super_key, pw).context(ks_err!())?;
 
             let key_entry = db
                 .store_super_key(
@@ -598,7 +587,7 @@
                     &blob_metadata,
                     &KeyMetaData::new(),
                 )
-                .context("In check_and_initialize_super_key. Failed to store super key.")?;
+                .context(ks_err!("Failed to store super key."))?;
 
             let super_key = self
                 .populate_cache_from_super_key_blob(
@@ -607,7 +596,7 @@
                     key_entry,
                     pw,
                 )
-                .context("In check_and_initialize_super_key.")?;
+                .context(ks_err!())?;
             Ok(UserState::LskfUnlocked(super_key))
         } else {
             Ok(UserState::Uninitialized)
@@ -623,9 +612,7 @@
         pw: &Password,
     ) -> Result<Arc<SuperKey>> {
         let super_key = Self::extract_super_key_from_key_entry(algorithm, entry, pw, None)
-            .context(
-                "In populate_cache_from_super_key_blob. Failed to extract super key from key entry",
-            )?;
+            .context(ks_err!("Failed to extract super key from key entry"))?;
         self.install_per_boot_key_for_user(user_id, super_key.clone())?;
         Ok(super_key)
     }
@@ -646,20 +633,19 @@
             ) {
                 (Some(&EncryptedBy::Password), Some(salt), Some(iv), Some(tag)) => {
                     // Note that password encryption is AES no matter the value of algorithm.
-                    let key = pw.derive_key(Some(salt), AES_256_KEY_LENGTH).context(
-                        "In extract_super_key_from_key_entry: Failed to generate key from password.",
-                    )?;
+                    let key = pw
+                        .derive_key(salt, AES_256_KEY_LENGTH)
+                        .context(ks_err!("Failed to generate key from password."))?;
 
-                    aes_gcm_decrypt(blob, iv, tag, &key).context(
-                        "In extract_super_key_from_key_entry: Failed to decrypt key blob.",
-                    )?
+                    aes_gcm_decrypt(blob, iv, tag, &key)
+                        .context(ks_err!("Failed to decrypt key blob."))?
                 }
                 (enc_by, salt, iv, tag) => {
-                    return Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
+                    return Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(ks_err!(
                         concat!(
-                        "In extract_super_key_from_key_entry: Super key has incomplete metadata.",
-                        "encrypted_by: {:?}; Present: salt: {}, iv: {}, aead_tag: {}."
-                    ),
+                            "Super key has incomplete metadata.",
+                            "encrypted_by: {:?}; Present: salt: {}, iv: {}, aead_tag: {}."
+                        ),
                         enc_by,
                         salt.is_some(),
                         iv.is_some(),
@@ -674,8 +660,7 @@
                 reencrypt_with,
             }))
         } else {
-            Err(Error::Rc(ResponseCode::VALUE_CORRUPTED))
-                .context("In extract_super_key_from_key_entry: No key blob info.")
+            Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(ks_err!("No key blob info."))
         }
     }
 
@@ -686,13 +671,13 @@
     ) -> Result<(Vec<u8>, BlobMetaData)> {
         let salt = generate_salt().context("In encrypt_with_password: Failed to generate salt.")?;
         let derived_key = pw
-            .derive_key(Some(&salt), AES_256_KEY_LENGTH)
-            .context("In encrypt_with_password: Failed to derive password.")?;
+            .derive_key(&salt, AES_256_KEY_LENGTH)
+            .context(ks_err!("Failed to derive password."))?;
         let mut metadata = BlobMetaData::new();
         metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
         metadata.add(BlobMetaEntry::Salt(salt));
         let (encrypted_key, iv, tag) = aes_gcm_encrypt(super_key, &derived_key)
-            .context("In encrypt_with_password: Failed to encrypt new super key.")?;
+            .context(ks_err!("Failed to encrypt new super key."))?;
         metadata.add(BlobMetaEntry::Iv(iv));
         metadata.add(BlobMetaEntry::AeadTag(tag));
         Ok((encrypted_key, metadata))
@@ -711,17 +696,17 @@
     ) -> Result<(Vec<u8>, BlobMetaData)> {
         match self
             .get_user_state(db, legacy_importer, user_id)
-            .context("In super_encrypt. Failed to get user state.")?
+            .context(ks_err!("Failed to get user state."))?
         {
             UserState::LskfUnlocked(super_key) => {
                 Self::encrypt_with_aes_super_key(key_blob, &super_key)
-                    .context("In super_encrypt_on_key_init. Failed to encrypt the key.")
+                    .context(ks_err!("Failed to encrypt the key."))
             }
             UserState::LskfLocked => {
-                Err(Error::Rc(ResponseCode::LOCKED)).context("In super_encrypt. Device is locked.")
+                Err(Error::Rc(ResponseCode::LOCKED)).context(ks_err!("Device is locked."))
             }
             UserState::Uninitialized => Err(Error::Rc(ResponseCode::UNINITIALIZED))
-                .context("In super_encrypt. LSKF is not setup for the user."),
+                .context(ks_err!("LSKF is not setup for the user.")),
         }
     }
 
@@ -733,12 +718,11 @@
         super_key: &SuperKey,
     ) -> Result<(Vec<u8>, BlobMetaData)> {
         if super_key.algorithm != SuperEncryptionAlgorithm::Aes256Gcm {
-            return Err(Error::sys())
-                .context("In encrypt_with_aes_super_key: unexpected algorithm");
+            return Err(Error::sys()).context(ks_err!("unexpected algorithm"));
         }
         let mut metadata = BlobMetaData::new();
         let (encrypted_key, iv, tag) = aes_gcm_encrypt(key_blob, &(super_key.key))
-            .context("In encrypt_with_aes_super_key: Failed to encrypt new super key.")?;
+            .context(ks_err!("Failed to encrypt new super key."))?;
         metadata.add(BlobMetaEntry::Iv(iv));
         metadata.add(BlobMetaEntry::AeadTag(tag));
         super_key.id.add_to_metadata(&mut metadata);
@@ -762,37 +746,29 @@
             SuperEncryptionType::None => Ok((key_blob.to_vec(), BlobMetaData::new())),
             SuperEncryptionType::LskfBound => self
                 .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."
-                )),
+                .context(ks_err!("Failed to super encrypt with LskfBound key.")),
             SuperEncryptionType::ScreenLockBound => {
                 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."
-                    ))
+                    Self::encrypt_with_aes_super_key(key_blob, super_key)
+                        .context(ks_err!("Failed to encrypt with ScreenLockBound key."))
                 } else {
                     // Symmetric key is not available, use public key encryption
-                    let loaded =
-                        db.load_super_key(&USER_SCREEN_LOCK_BOUND_P521_KEY, user_id).context(
-                            "In handle_super_encryption_on_key_init: load_super_key failed.",
-                        )?;
-                    let (key_id_guard, key_entry) = loaded.ok_or_else(Error::sys).context(
-                        "In handle_super_encryption_on_key_init: User ECDH key missing.",
-                    )?;
-                    let public_key =
-                        key_entry.metadata().sec1_public_key().ok_or_else(Error::sys).context(
-                            "In handle_super_encryption_on_key_init: sec1_public_key missing.",
-                        )?;
+                    let loaded = db
+                        .load_super_key(&USER_SCREEN_LOCK_BOUND_P521_KEY, user_id)
+                        .context(ks_err!("load_super_key failed."))?;
+                    let (key_id_guard, key_entry) =
+                        loaded.ok_or_else(Error::sys).context(ks_err!("User ECDH key missing."))?;
+                    let public_key = key_entry
+                        .metadata()
+                        .sec1_public_key()
+                        .ok_or_else(Error::sys)
+                        .context(ks_err!("sec1_public_key missing."))?;
                     let mut metadata = BlobMetaData::new();
                     let (ephem_key, salt, iv, encrypted_key, aead_tag) =
-                        ECDHPrivateKey::encrypt_message(public_key, key_blob).context(concat!(
-                            "In handle_super_encryption_on_key_init: ",
-                            "ECDHPrivateKey::encrypt_message failed."
-                        ))?;
+                        ECDHPrivateKey::encrypt_message(public_key, key_blob)
+                            .context(ks_err!("ECDHPrivateKey::encrypt_message failed."))?;
                     metadata.add(BlobMetaEntry::PublicKey(ephem_key));
                     metadata.add(BlobMetaEntry::Salt(salt));
                     metadata.add(BlobMetaEntry::Iv(iv));
@@ -806,13 +782,11 @@
                 let key_id = SuperKeyIdentifier::BootLevel(level);
                 let super_key = self
                     .lookup_key(&key_id)
-                    .context("In handle_super_encryption_on_key_init: lookup_key failed")?
+                    .context(ks_err!("lookup_key failed"))?
                     .ok_or(Error::Rc(ResponseCode::LOCKED))
-                    .context("In handle_super_encryption_on_key_init: Boot stage key absent")?;
-                Self::encrypt_with_aes_super_key(key_blob, &super_key).context(concat!(
-                    "In handle_super_encryption_on_key_init: ",
-                    "Failed to encrypt with BootLevel key."
-                ))
+                    .context(ks_err!("Boot stage key absent"))?;
+                Self::encrypt_with_aes_super_key(key_blob, &super_key)
+                    .context(ks_err!("Failed to encrypt with BootLevel key."))
             }
         }
     }
@@ -828,7 +802,7 @@
             KeyBlob::Sensitive { reencrypt_with: super_key, .. } => {
                 let (key, metadata) =
                     Self::encrypt_with_aes_super_key(key_after_upgrade, super_key)
-                        .context("In reencrypt_if_required: Failed to re-super-encrypt key.")?;
+                        .context(ks_err!("Failed to re-super-encrypt key."))?;
                 Ok((KeyBlob::NonSensitive(key), Some(metadata)))
             }
             _ => Ok((KeyBlob::Ref(key_after_upgrade), None)),
@@ -857,28 +831,22 @@
         } else {
             let (super_key, public_key) = match key_type.algorithm {
                 SuperEncryptionAlgorithm::Aes256Gcm => (
-                    generate_aes256_key()
-                        .context("In get_or_create_super_key: Failed to generate AES 256 key.")?,
+                    generate_aes256_key().context(ks_err!("Failed to generate AES 256 key."))?,
                     None,
                 ),
                 SuperEncryptionAlgorithm::EcdhP521 => {
                     let key = ECDHPrivateKey::generate()
-                        .context("In get_or_create_super_key: Failed to generate ECDH key")?;
+                        .context(ks_err!("Failed to generate ECDH key"))?;
                     (
-                        key.private_key()
-                            .context("In get_or_create_super_key: private_key failed")?,
-                        Some(
-                            key.public_key()
-                                .context("In get_or_create_super_key: public_key failed")?,
-                        ),
+                        key.private_key().context(ks_err!("private_key failed"))?,
+                        Some(key.public_key().context(ks_err!("public_key failed"))?),
                     )
                 }
             };
             // Derive an AES256 key from the password and re-encrypt the super key
             // before we insert it in the database.
             let (encrypted_super_key, blob_metadata) =
-                Self::encrypt_with_password(&super_key, password)
-                    .context("In get_or_create_super_key.")?;
+                Self::encrypt_with_password(&super_key, password).context(ks_err!())?;
             let mut key_metadata = KeyMetaData::new();
             if let Some(pk) = public_key {
                 key_metadata.add(KeyMetaEntry::Sec1PublicKey(pk));
@@ -891,7 +859,7 @@
                     &blob_metadata,
                     &key_metadata,
                 )
-                .context("In get_or_create_super_key. Failed to store super key.")?;
+                .context(ks_err!("Failed to store super key."))?;
             Ok(Arc::new(SuperKey {
                 algorithm: key_type.algorithm,
                 key: super_key,
@@ -926,7 +894,7 @@
             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.")?
+                .context(ks_err!("Trying to get or create symmetric key."))?
         };
 
         let ecdh = if let Some(screen_lock_bound_private) = screen_lock_bound_private {
@@ -941,7 +909,7 @@
                 password,
                 Some(aes.clone()),
             )
-            .context("In unlock_screen_lock_bound_key: Trying to get or create asymmetric key.")?
+            .context(ks_err!("Trying to get or create asymmetric key."))?
         };
 
         self.data.add_key_to_key_index(&aes)?;
@@ -974,7 +942,7 @@
                     let encrypting_key = generate_aes256_key()?;
                     let km_dev: KeyMintDevice =
                         KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
-                            .context("In lock_screen_lock_bound_key: KeyMintDevice::get failed")?;
+                            .context(ks_err!("KeyMintDevice::get failed"))?;
                     let mut key_params = vec![
                         KeyParameterValue::Algorithm(Algorithm::AES),
                         KeyParameterValue::KeySize(256),
@@ -1046,9 +1014,9 @@
                     AID_KEYSTORE,
                     |_, _| Ok(()),
                 )
-                .context("In try_unlock_user_with_biometric: load_key_entry failed")?;
+                .context(ks_err!("load_key_entry failed"))?;
             let km_dev: KeyMintDevice = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
-                .context("In try_unlock_user_with_biometric: KeyMintDevice::get failed")?;
+                .context(ks_err!("KeyMintDevice::get failed"))?;
             for sid in &biometric.sids {
                 if let Some((auth_token_entry, _)) = db.find_auth_token_entry(|entry| {
                     entry.auth_token().userId == *sid || entry.auth_token().authenticatorId == *sid
@@ -1078,14 +1046,11 @@
                             entry.screen_lock_bound_private = Some(slbp.clone());
                             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"
-                            ));
+                            log::info!("Successfully unlocked with biometric");
                             return Ok(());
                         }
                         Err(e) => {
-                            log::warn!("In try_unlock_user_with_biometric: attempt failed: {:?}", e)
+                            log::warn!("attempt failed: {:?}", e)
                         }
                     }
                 }
@@ -1110,7 +1075,7 @@
                 // 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.")?
+                    .context(ks_err!())?
                 {
                     Ok(UserState::LskfLocked)
                 } else {
@@ -1141,9 +1106,8 @@
             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.",
-                )?;
+                self.reset_user(db, legacy_importer, user_id, true)
+                    .context(ks_err!("Trying to delete keys from the db."))?;
                 // Lskf is now removed in Keystore.
                 Ok(UserState::Uninitialized)
             }
@@ -1174,7 +1138,7 @@
     ) -> 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.");
+                log::info!("Trying to unlock when already unlocked.");
                 Ok(UserState::LskfUnlocked(super_key))
             }
             None => {
@@ -1183,7 +1147,7 @@
                 // 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.")
+                    .context(ks_err!("Failed to unlock super key."))
             }
         }
     }
@@ -1201,9 +1165,9 @@
         // 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.")?;
+            .context(ks_err!("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.")?;
+            .context(ks_err!("Error in unbinding keys."))?;
 
         // Delete super key in cache, if exists.
         self.forget_all_keys_for_user(user_id);
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index 9db2eb9..acac7ee 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -17,6 +17,7 @@
 
 use crate::error::{map_binder_status, map_km_error, Error, ErrorCode};
 use crate::key_parameter::KeyParameter;
+use crate::ks_err;
 use crate::permission;
 use crate::permission::{KeyPerm, KeyPermSet, KeystorePerm};
 use crate::{
@@ -51,9 +52,9 @@
 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(
-                "In check_keystore_permission: Cannot check permission without calling_sid.",
-            )?,
+            calling_sid
+                .ok_or_else(Error::sys)
+                .context(ks_err!("Cannot check permission without calling_sid."))?,
             perm,
         )
     })
@@ -65,9 +66,9 @@
 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(
-                "In check_grant_permission: Cannot check permission without calling_sid.",
-            )?,
+            calling_sid
+                .ok_or_else(Error::sys)
+                .context(ks_err!("Cannot check permission without calling_sid."))?,
             access_vec,
             key,
         )
@@ -87,7 +88,7 @@
             ThreadState::get_calling_uid(),
             calling_sid
                 .ok_or_else(Error::sys)
-                .context("In check_key_permission: Cannot check permission without calling_sid.")?,
+                .context(ks_err!("Cannot check permission without calling_sid."))?,
             perm,
             key,
             access_vector,
@@ -103,6 +104,7 @@
             | Tag::ATTESTATION_ID_MEID
             | Tag::ATTESTATION_ID_SERIAL
             | Tag::DEVICE_UNIQUE_ATTESTATION
+            | Tag::ATTESTATION_ID_SECOND_IMEI
     )
 }
 
@@ -135,14 +137,12 @@
             ThreadState::get_calling_uid() as i32,
         )
     };
-    let has_permissions = map_binder_status(binder_result)
-        .context("In check_device_attestation_permissions: checkPermission failed")?;
+    let has_permissions =
+        map_binder_status(binder_result).context(ks_err!("checkPermission failed"))?;
     match has_permissions {
         true => Ok(()),
-        false => Err(Error::Km(ErrorCode::CANNOT_ATTEST_IDS)).context(concat!(
-            "In check_device_attestation_permissions: ",
-            "caller does not have the permission to attest device IDs"
-        )),
+        false => Err(Error::Km(ErrorCode::CANNOT_ATTEST_IDS))
+            .context(ks_err!("caller does not have the permission to attest device IDs")),
     }
 }
 
@@ -189,18 +189,15 @@
                 );
                 map_km_error(km_dev.upgradeKey(key_blob, upgrade_params))
             }
-            .context("In utils::upgrade_keyblob_if_required_with: Upgrade failed.")?;
+            .context(ks_err!("Upgrade failed."))?;
 
-            new_blob_handler(&upgraded_blob)
-                .context("In utils::upgrade_keyblob_if_required_with: calling new_blob_handler.")?;
+            new_blob_handler(&upgraded_blob).context(ks_err!("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.")
+                .context(ks_err!("Calling km_op after upgrade."))
         }
-        r => r
-            .map(|v| (v, None))
-            .context("In utils::upgrade_keyblob_if_required_with: Calling km_op."),
+        r => r.map(|v| (v, None)).context(ks_err!("Calling km_op.")),
     }
 }
 
@@ -212,6 +209,7 @@
     parameters.into_iter().map(|p| p.into_authorization()).collect()
 }
 
+#[allow(clippy::unnecessary_cast)]
 /// This returns the current time (in milliseconds) as an instance of a monotonic clock,
 /// by invoking the system call since Rust does not support getting monotonic time instance
 /// as an integer.
@@ -260,26 +258,116 @@
     rustutils::users::multiuser_get_user_id(uid)
 }
 
-/// List all key aliases for a given domain + namespace.
+/// Merges and filters two lists of key descriptors. The first input list, legacy_descriptors,
+/// is assumed to not be sorted or filtered. As such, all key descriptors in that list whose
+/// alias is less than, or equal to, start_past_alias (if provided) will be removed.
+/// This list will then be merged with the second list, db_descriptors. The db_descriptors list
+/// is assumed to be sorted and filtered so the output list will be sorted prior to returning.
+/// The returned value is a list of KeyDescriptor objects whose alias is greater than
+/// start_past_alias, sorted and de-duplicated.
+fn merge_and_filter_key_entry_lists(
+    legacy_descriptors: &[KeyDescriptor],
+    db_descriptors: &[KeyDescriptor],
+    start_past_alias: Option<&str>,
+) -> Vec<KeyDescriptor> {
+    let mut result: Vec<KeyDescriptor> =
+        match start_past_alias {
+            Some(past_alias) => legacy_descriptors
+                .iter()
+                .filter(|kd| {
+                    if let Some(alias) = &kd.alias {
+                        alias.as_str() > past_alias
+                    } else {
+                        false
+                    }
+                })
+                .cloned()
+                .collect(),
+            None => legacy_descriptors.to_vec(),
+        };
+
+    result.extend_from_slice(db_descriptors);
+    result.sort_unstable();
+    result.dedup();
+    result
+}
+
+fn estimate_safe_amount_to_return(
+    key_descriptors: &[KeyDescriptor],
+    response_size_limit: usize,
+) -> usize {
+    let mut items_to_return = 0;
+    let mut returned_bytes: usize = 0;
+    // Estimate the transaction size to avoid returning more items than what
+    // could fit in a binder transaction.
+    for kd in key_descriptors.iter() {
+        // 4 bytes for the Domain enum
+        // 8 bytes for the Namespace long.
+        returned_bytes += 4 + 8;
+        // Size of the alias string. Includes 4 bytes for length encoding.
+        if let Some(alias) = &kd.alias {
+            returned_bytes += 4 + alias.len();
+        }
+        // Size of the blob. Includes 4 bytes for length encoding.
+        if let Some(blob) = &kd.blob {
+            returned_bytes += 4 + blob.len();
+        }
+        // The binder transaction size limit is 1M. Empirical measurements show
+        // that the binder overhead is 60% (to be confirmed). So break after
+        // 350KB and return a partial list.
+        if returned_bytes > response_size_limit {
+            log::warn!(
+                "Key descriptors list ({} items) may exceed binder \
+                       size, returning {} items est {} bytes.",
+                key_descriptors.len(),
+                items_to_return,
+                returned_bytes
+            );
+            break;
+        }
+        items_to_return += 1;
+    }
+    items_to_return
+}
+
+/// List all key aliases for a given domain + namespace. whose alias is greater
+/// than start_past_alias (if provided).
 pub fn list_key_entries(
     db: &mut KeystoreDB,
     domain: Domain,
     namespace: i64,
+    start_past_alias: Option<&str>,
 ) -> 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.")?,
+    let legacy_key_descriptors: Vec<KeyDescriptor> = LEGACY_IMPORTER
+        .list_uid(domain, namespace)
+        .context(ks_err!("Trying to list legacy keys."))?;
+
+    // The results from the database will be sorted and unique
+    let db_key_descriptors: Vec<KeyDescriptor> = db
+        .list_past_alias(domain, namespace, KeyType::Client, start_past_alias)
+        .context(ks_err!("Trying to list keystore database past alias."))?;
+
+    let merged_key_entries = merge_and_filter_key_entry_lists(
+        &legacy_key_descriptors,
+        &db_key_descriptors,
+        start_past_alias,
     );
-    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)
+
+    const RESPONSE_SIZE_LIMIT: usize = 358400;
+    let safe_amount_to_return =
+        estimate_safe_amount_to_return(&merged_key_entries, RESPONSE_SIZE_LIMIT);
+    Ok(merged_key_entries[..safe_amount_to_return].to_vec())
+}
+
+/// Count all key aliases for a given domain + namespace.
+pub fn count_key_entries(db: &mut KeystoreDB, domain: Domain, namespace: i64) -> Result<i32> {
+    let legacy_keys = LEGACY_IMPORTER
+        .list_uid(domain, namespace)
+        .context(ks_err!("Trying to list legacy keys."))?;
+
+    let num_keys_in_db = db.count_keys(domain, namespace, KeyType::Client)?;
+
+    Ok((legacy_keys.len() + num_keys_in_db) as i32)
 }
 
 /// This module provides helpers for simplified use of the watchdog module.
@@ -333,12 +421,11 @@
 
 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")
+        aes_gcm_decrypt(data, iv, tag, self.key()).context(ks_err!("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.")
+        aes_gcm_encrypt(plaintext, self.key()).context(ks_err!("Encryption failed."))
     }
 }
 
@@ -377,4 +464,84 @@
             }
         })
     }
+
+    fn create_key_descriptors_from_aliases(key_aliases: &[&str]) -> Vec<KeyDescriptor> {
+        key_aliases
+            .iter()
+            .map(|key_alias| KeyDescriptor {
+                domain: Domain::APP,
+                nspace: 0,
+                alias: Some(key_alias.to_string()),
+                blob: None,
+            })
+            .collect::<Vec<KeyDescriptor>>()
+    }
+
+    fn aliases_from_key_descriptors(key_descriptors: &[KeyDescriptor]) -> Vec<String> {
+        key_descriptors
+            .iter()
+            .map(
+                |kd| {
+                    if let Some(alias) = &kd.alias {
+                        String::from(alias)
+                    } else {
+                        String::from("")
+                    }
+                },
+            )
+            .collect::<Vec<String>>()
+    }
+
+    #[test]
+    fn test_safe_amount_to_return() -> Result<()> {
+        let key_aliases = vec!["key1", "key2", "key3"];
+        let key_descriptors = create_key_descriptors_from_aliases(&key_aliases);
+
+        assert_eq!(estimate_safe_amount_to_return(&key_descriptors, 20), 1);
+        assert_eq!(estimate_safe_amount_to_return(&key_descriptors, 50), 2);
+        assert_eq!(estimate_safe_amount_to_return(&key_descriptors, 100), 3);
+        Ok(())
+    }
+
+    #[test]
+    fn test_merge_and_sort_lists_without_filtering() -> Result<()> {
+        let legacy_key_aliases = vec!["key_c", "key_a", "key_b"];
+        let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
+        let db_key_aliases = vec!["key_a", "key_d"];
+        let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
+        let result =
+            merge_and_filter_key_entry_lists(&legacy_key_descriptors, &db_key_descriptors, None);
+        assert_eq!(aliases_from_key_descriptors(&result), vec!["key_a", "key_b", "key_c", "key_d"]);
+        Ok(())
+    }
+
+    #[test]
+    fn test_merge_and_sort_lists_with_filtering() -> Result<()> {
+        let legacy_key_aliases = vec!["key_f", "key_a", "key_e", "key_b"];
+        let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
+        let db_key_aliases = vec!["key_c", "key_g"];
+        let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
+        let result = merge_and_filter_key_entry_lists(
+            &legacy_key_descriptors,
+            &db_key_descriptors,
+            Some("key_b"),
+        );
+        assert_eq!(aliases_from_key_descriptors(&result), vec!["key_c", "key_e", "key_f", "key_g"]);
+        Ok(())
+    }
+
+    #[test]
+    fn test_merge_and_sort_lists_with_filtering_and_dups() -> Result<()> {
+        let legacy_key_aliases = vec!["key_f", "key_a", "key_e", "key_b"];
+        let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
+        let db_key_aliases = vec!["key_d", "key_e", "key_g"];
+        let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
+        let result = merge_and_filter_key_entry_lists(
+            &legacy_key_descriptors,
+            &db_key_descriptors,
+            Some("key_c"),
+        );
+        assert_eq!(aliases_from_key_descriptors(&result), vec!["key_d", "key_e", "key_f", "key_g"]);
+        Ok(())
+    }
 }
diff --git a/keystore2/src/vintf/lib.rs b/keystore2/src/vintf/lib.rs
index 89e18eb..5bb015f 100644
--- a/keystore2/src/vintf/lib.rs
+++ b/keystore2/src/vintf/lib.rs
@@ -19,14 +19,6 @@
     unsafe extern "C++" {
         include!("vintf.hpp");
 
-        /// 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>;
-
-        /// 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(
@@ -35,28 +27,7 @@
             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>;
     }
 }
 
 pub use ffi::*;
-
-#[cfg(test)]
-mod tests {
-
-    use super::*;
-
-    #[test]
-    fn test() {
-        let names = get_hal_names();
-        assert_ne!(names.len(), 0);
-
-        let names_and_versions = get_hal_names_and_versions();
-        assert_ne!(names_and_versions.len(), 0);
-
-        assert!(names_and_versions.len() >= names.len());
-    }
-}
diff --git a/keystore2/src/vintf/vintf.cpp b/keystore2/src/vintf/vintf.cpp
index 00625bf..bf77f5e 100644
--- a/keystore2/src/vintf/vintf.cpp
+++ b/keystore2/src/vintf/vintf.cpp
@@ -26,18 +26,6 @@
     return result;
 }
 
-rust::Vec<rust::String> get_hal_names() {
-    const auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
-    const auto names = manifest->getHalNames();
-    return convert(names);
-}
-
-rust::Vec<rust::String> get_hal_names_and_versions() {
-    const auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
-    const auto names = manifest->getHalNamesAndVersions();
-    return convert(names);
-}
-
 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);
@@ -46,11 +34,3 @@
                                                   static_cast<std::string>(interfaceName));
     return convert(names);
 }
-
-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);
-}
diff --git a/keystore2/src/vintf/vintf.hpp b/keystore2/src/vintf/vintf.hpp
index dbc88f0..ef1e788 100644
--- a/keystore2/src/vintf/vintf.hpp
+++ b/keystore2/src/vintf/vintf.hpp
@@ -18,9 +18,5 @@
 
 #include "rust/cxx.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 a26b632..01043c5 100644
--- a/keystore2/src/watchdog.rs
+++ b/keystore2/src/watchdog.rs
@@ -141,7 +141,7 @@
             },
         );
         // Put the groups back into a vector.
-        let mut groups: Vec<Vec<(&Index, &Record)>> = groups.into_iter().map(|(_, v)| v).collect();
+        let mut groups: Vec<Vec<(&Index, &Record)>> = groups.into_values().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.
diff --git a/keystore2/test_utils/authorizations.rs b/keystore2/test_utils/authorizations.rs
index 4fbe124..4608bc5 100644
--- a/keystore2/test_utils/authorizations.rs
+++ b/keystore2/test_utils/authorizations.rs
@@ -17,11 +17,13 @@
 use std::ops::Deref;
 
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, KeyParameter::KeyParameter,
-    KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, Tag::Tag,
+    Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+    KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose,
+    PaddingMode::PaddingMode, Tag::Tag,
 };
 
 /// Helper struct to create set of Authorizations.
+#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
 pub struct AuthSetBuilder(Vec<KeyParameter>);
 
 impl Default for AuthSetBuilder {
@@ -69,14 +71,96 @@
         self
     }
 
-    /// Add Attestation-ID.
-    pub fn attestation_app_id(mut self, b: Vec<u8>) -> Self {
+    /// Add No_auth_required.
+    pub fn no_auth_required(mut self) -> Self {
         self.0.push(KeyParameter {
-            tag: Tag::ATTESTATION_APPLICATION_ID,
-            value: KeyParameterValue::Blob(b),
+            tag: Tag::NO_AUTH_REQUIRED,
+            value: KeyParameterValue::BoolValue(true),
         });
         self
     }
+
+    /// Add RSA_public_exponent.
+    pub fn rsa_public_exponent(mut self, e: i64) -> Self {
+        self.0.push(KeyParameter {
+            tag: Tag::RSA_PUBLIC_EXPONENT,
+            value: KeyParameterValue::LongInteger(e),
+        });
+        self
+    }
+
+    /// Add key size.
+    pub fn key_size(mut self, s: i32) -> Self {
+        self.0.push(KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(s) });
+        self
+    }
+
+    /// Add block mode.
+    pub fn block_mode(mut self, b: BlockMode) -> Self {
+        self.0.push(KeyParameter { tag: Tag::BLOCK_MODE, value: KeyParameterValue::BlockMode(b) });
+        self
+    }
+
+    /// Add certificate_not_before.
+    pub fn cert_not_before(mut self, b: i64) -> Self {
+        self.0.push(KeyParameter {
+            tag: Tag::CERTIFICATE_NOT_BEFORE,
+            value: KeyParameterValue::DateTime(b),
+        });
+        self
+    }
+
+    /// Add certificate_not_after.
+    pub fn cert_not_after(mut self, a: i64) -> Self {
+        self.0.push(KeyParameter {
+            tag: Tag::CERTIFICATE_NOT_AFTER,
+            value: KeyParameterValue::DateTime(a),
+        });
+        self
+    }
+
+    /// Add padding mode.
+    pub fn padding_mode(mut self, p: PaddingMode) -> Self {
+        self.0.push(KeyParameter { tag: Tag::PADDING, value: KeyParameterValue::PaddingMode(p) });
+        self
+    }
+
+    /// Add mgf_digest.
+    pub fn mgf_digest(mut self, d: Digest) -> Self {
+        self.0.push(KeyParameter {
+            tag: Tag::RSA_OAEP_MGF_DIGEST,
+            value: KeyParameterValue::Digest(d),
+        });
+        self
+    }
+
+    /// Add nonce.
+    pub fn nonce(mut self, b: Vec<u8>) -> Self {
+        self.0.push(KeyParameter { tag: Tag::NONCE, value: KeyParameterValue::Blob(b) });
+        self
+    }
+
+    /// Add CALLER_NONCE.
+    pub fn caller_nonce(mut self) -> Self {
+        self.0.push(KeyParameter {
+            tag: Tag::CALLER_NONCE,
+            value: KeyParameterValue::BoolValue(true),
+        });
+        self
+    }
+
+    /// Add MAC length.
+    pub fn mac_length(mut self, l: i32) -> Self {
+        self.0.push(KeyParameter { tag: Tag::MAC_LENGTH, value: KeyParameterValue::Integer(l) });
+        self
+    }
+
+    /// Add min MAC length.
+    pub fn min_mac_length(mut self, l: i32) -> Self {
+        self.0
+            .push(KeyParameter { tag: Tag::MIN_MAC_LENGTH, value: KeyParameterValue::Integer(l) });
+        self
+    }
 }
 
 impl Deref for AuthSetBuilder {
diff --git a/keystore2/test_utils/key_generations.rs b/keystore2/test_utils/key_generations.rs
index f49aa9f..e4c4968 100644
--- a/keystore2/test_utils/key_generations.rs
+++ b/keystore2/test_utils/key_generations.rs
@@ -14,43 +14,345 @@
 
 //! This module implements test utils to generate various types of keys.
 
+use anyhow::Result;
+
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, KeyPurpose::KeyPurpose,
+    Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+    ErrorCode::ErrorCode, HardwareAuthenticatorType::HardwareAuthenticatorType,
+    KeyOrigin::KeyOrigin, KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue,
+    KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, Tag::Tag,
 };
 use android_system_keystore2::aidl::android::system::keystore2::{
-    Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
-    KeyMetadata::KeyMetadata,
+    AuthenticatorSpec::AuthenticatorSpec, Authorization::Authorization, Domain::Domain,
+    IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+    KeyMetadata::KeyMetadata, ResponseCode::ResponseCode,
 };
 
 use crate::authorizations::AuthSetBuilder;
+use android_system_keystore2::binder::{ExceptionCode, Result as BinderResult};
 
-const SELINUX_SHELL_NAMESPACE: i64 = 1;
+/// Shell namespace.
+pub const SELINUX_SHELL_NAMESPACE: i64 = 1;
+/// Vold namespace.
+pub const SELINUX_VOLD_NAMESPACE: i64 = 100;
 
-/// Generate attested EC Key blob using given security level with below key parameters -
+/// SU context.
+pub const TARGET_SU_CTX: &str = "u:r:su:s0";
+
+/// Vold context
+pub const TARGET_VOLD_CTX: &str = "u:r:vold:s0";
+
+/// Key parameters to generate a key.
+pub struct KeyParams {
+    /// Key Size.
+    pub key_size: i32,
+    /// Key Purposes.
+    pub purpose: Vec<KeyPurpose>,
+    /// Padding Mode.
+    pub padding: Option<PaddingMode>,
+    /// Digest.
+    pub digest: Option<Digest>,
+    /// MFG Digest.
+    pub mgf_digest: Option<Digest>,
+    /// Block Mode.
+    pub block_mode: Option<BlockMode>,
+    /// Attestation challenge.
+    pub att_challenge: Option<Vec<u8>>,
+}
+
+/// DER-encoded PKCS#8 format RSA key. Generated using:
+/// openssl genrsa 2048 | openssl pkcs8 -topk8 -nocrypt -outform der | hexdump -e '30/1  "%02X" "\n"'
+pub static RSA_2048_KEY: &[u8] = &[
+    0x30, 0x82, 0x04, 0xBD, 0x02, 0x01, 0x00, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
+    0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x04, 0xA7, 0x30, 0x82, 0x04, 0xA3, 0x02, 0x01,
+    0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xE5, 0x14, 0xE3, 0xC2, 0x43, 0xF3, 0x0F, 0xCC, 0x22, 0x73,
+    0x9C, 0x84, 0xCC, 0x1B, 0x6C, 0x97, 0x4B, 0xC9, 0xDF, 0x1F, 0xE2, 0xB8, 0x80, 0x85, 0xF9, 0x27,
+    0xAB, 0x97, 0x94, 0x58, 0x4B, 0xC9, 0x40, 0x94, 0x5A, 0xB4, 0xD4, 0xF8, 0xD0, 0x36, 0xC4, 0x86,
+    0x17, 0x7D, 0xA2, 0x48, 0x6D, 0x40, 0xF0, 0xB9, 0x61, 0x4F, 0xCE, 0x65, 0x80, 0x88, 0x81, 0x59,
+    0x95, 0x11, 0x24, 0xF4, 0x36, 0xB7, 0xB7, 0x37, 0x44, 0xF4, 0x6C, 0x1C, 0xEB, 0x04, 0x19, 0x78,
+    0xB2, 0x29, 0x4D, 0x21, 0x44, 0x16, 0x57, 0x58, 0x6D, 0x7D, 0x56, 0xB5, 0x99, 0xDD, 0xD2, 0xAD,
+    0x02, 0x9A, 0x72, 0x16, 0x67, 0xD6, 0x00, 0x9F, 0x69, 0xE0, 0x25, 0xEE, 0x7C, 0x86, 0x54, 0x27,
+    0x4B, 0x50, 0xEF, 0x60, 0x52, 0x60, 0x82, 0xAA, 0x09, 0x15, 0x72, 0xD2, 0xEB, 0x01, 0x52, 0x04,
+    0x39, 0x60, 0xBC, 0x5E, 0x95, 0x07, 0xC8, 0xC2, 0x3A, 0x3A, 0xE2, 0xA4, 0x99, 0x6B, 0x27, 0xE3,
+    0xA3, 0x55, 0x69, 0xC4, 0xB3, 0x2D, 0x19, 0xC4, 0x34, 0x76, 0xFC, 0x27, 0xDA, 0x22, 0xB2, 0x62,
+    0x69, 0x25, 0xDE, 0x0D, 0xE7, 0x54, 0x3C, 0xBB, 0x61, 0xD2, 0x20, 0xDA, 0x7B, 0x6E, 0x63, 0xBD,
+    0x9A, 0x4B, 0xCD, 0x75, 0xC6, 0xA1, 0x5E, 0x1C, 0x3E, 0xD5, 0x63, 0x59, 0x22, 0x7E, 0xE0, 0x6C,
+    0x98, 0x25, 0x63, 0x97, 0x56, 0xDF, 0x71, 0xF5, 0x4C, 0x78, 0xE9, 0xE1, 0xD5, 0xFC, 0xF8, 0x5A,
+    0x5B, 0xF6, 0x1D, 0xFA, 0x5A, 0x99, 0x4C, 0x99, 0x19, 0x21, 0x1D, 0xF5, 0x24, 0x07, 0xEF, 0x8A,
+    0xC9, 0x9F, 0xE7, 0x3F, 0xBB, 0x46, 0x1A, 0x16, 0x96, 0xC6, 0xD6, 0x12, 0x7E, 0xDA, 0xCB, 0xEB,
+    0x2F, 0x1D, 0x3B, 0x31, 0xCC, 0x55, 0x63, 0xA2, 0x6F, 0x8A, 0xDE, 0x35, 0x52, 0x40, 0x04, 0xBF,
+    0xE0, 0x82, 0x32, 0xE1, 0x6D, 0x8B, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x00, 0x2D,
+    0x1F, 0x71, 0x41, 0x79, 0xBA, 0xED, 0xD8, 0xAA, 0xCC, 0x94, 0xFE, 0xFF, 0x69, 0x43, 0x79, 0x85,
+    0xBF, 0x2C, 0xC9, 0x0E, 0x12, 0x83, 0x96, 0x60, 0x1E, 0x75, 0x49, 0x35, 0x3A, 0x33, 0x2B, 0x60,
+    0x22, 0x18, 0xBF, 0xD7, 0xD7, 0x6E, 0xC3, 0xEA, 0xEF, 0xF2, 0xBE, 0x97, 0x71, 0xA6, 0xBB, 0x8C,
+    0xEF, 0x27, 0x00, 0xDE, 0x49, 0xD6, 0x08, 0x8D, 0x5A, 0x04, 0xE7, 0xCC, 0x9C, 0xA2, 0x0E, 0x8B,
+    0xF3, 0x42, 0x0C, 0xD7, 0x22, 0xD7, 0x14, 0x06, 0xA4, 0x64, 0x8B, 0x88, 0x1A, 0xCE, 0x5B, 0x8C,
+    0x36, 0xE9, 0xD2, 0x2F, 0x7B, 0x33, 0xE4, 0xA2, 0xB3, 0xDB, 0x78, 0x6A, 0x92, 0x89, 0x3F, 0x78,
+    0xFD, 0xED, 0x8F, 0xEE, 0x48, 0xCC, 0x94, 0x75, 0x0D, 0x0C, 0x63, 0xD3, 0xD2, 0xE8, 0x47, 0x04,
+    0x55, 0xD3, 0xD6, 0x3A, 0xB8, 0xDA, 0xFB, 0x76, 0x99, 0x48, 0x68, 0x0A, 0x92, 0xA2, 0xCD, 0xF7,
+    0x45, 0x8B, 0x50, 0xFE, 0xF9, 0x1A, 0x33, 0x24, 0x3C, 0x2E, 0xDE, 0x88, 0xAD, 0xB2, 0x5B, 0x9F,
+    0x44, 0xEA, 0xD1, 0x9F, 0xC7, 0x9F, 0x02, 0x5E, 0x31, 0x61, 0xB3, 0xD6, 0xE2, 0xE1, 0xBC, 0xFB,
+    0x1C, 0xDB, 0xBD, 0xB2, 0x9A, 0xE5, 0xEF, 0xDA, 0xCD, 0x29, 0xA5, 0x45, 0xCC, 0x67, 0x01, 0x8B,
+    0x1C, 0x1D, 0x0E, 0x8F, 0x73, 0x69, 0x4D, 0x4D, 0xF6, 0x9D, 0xA6, 0x6C, 0x9A, 0x1C, 0xF4, 0x5C,
+    0xE4, 0x83, 0x9A, 0x77, 0x12, 0x01, 0xBD, 0xCE, 0x66, 0x3A, 0x4B, 0x3D, 0x6E, 0xE0, 0x6E, 0x82,
+    0x98, 0xDE, 0x74, 0x11, 0x47, 0xEC, 0x7A, 0x3A, 0xA9, 0xD8, 0x48, 0x00, 0x26, 0x64, 0x47, 0x7B,
+    0xAE, 0x55, 0x9D, 0x29, 0x22, 0xB4, 0xB3, 0xB9, 0xB1, 0x64, 0xEA, 0x3B, 0x5A, 0xD3, 0x3F, 0x8D,
+    0x0F, 0x14, 0x7E, 0x4E, 0xB8, 0x1B, 0x06, 0xFC, 0xB1, 0x7E, 0xCD, 0xB9, 0x1A, 0x4E, 0xA1, 0x02,
+    0x81, 0x81, 0x00, 0xF9, 0xDE, 0xEE, 0xED, 0x13, 0x2F, 0xBB, 0xE7, 0xE2, 0xB3, 0x2D, 0x98, 0xD2,
+    0xE8, 0x25, 0x07, 0x5A, 0x1E, 0x51, 0x0A, 0xC8, 0xAD, 0x50, 0x4B, 0x80, 0xC6, 0x22, 0xF5, 0x9B,
+    0x08, 0xE6, 0x3D, 0x01, 0xC6, 0x3E, 0xC8, 0xD2, 0x54, 0x9F, 0x91, 0x77, 0x95, 0xCD, 0xCA, 0xC7,
+    0xE7, 0x47, 0x94, 0xA9, 0x5F, 0x4E, 0xBE, 0x31, 0x3D, 0xB4, 0xAF, 0x43, 0x0F, 0xDC, 0x8D, 0x9C,
+    0x1E, 0x52, 0x7B, 0x72, 0x21, 0x34, 0xB3, 0x96, 0x7C, 0x9C, 0xB8, 0x51, 0x65, 0x60, 0xAC, 0x3D,
+    0x11, 0x32, 0xB8, 0xD6, 0x34, 0x35, 0x66, 0xD0, 0x30, 0xB9, 0xE9, 0x67, 0x2C, 0x87, 0x73, 0x43,
+    0x9C, 0x12, 0x16, 0x7D, 0x4A, 0xD9, 0xA3, 0x4C, 0x24, 0x64, 0x6A, 0x32, 0x8E, 0xC3, 0xD8, 0x00,
+    0x90, 0x5C, 0x4D, 0x65, 0x01, 0x53, 0x8A, 0xD0, 0x87, 0xCE, 0x96, 0xEF, 0xFA, 0x73, 0x03, 0xF1,
+    0xDC, 0x1B, 0x9B, 0x02, 0x81, 0x81, 0x00, 0xEA, 0xB3, 0x69, 0x00, 0x11, 0x0E, 0x50, 0xAA, 0xD3,
+    0x22, 0x51, 0x78, 0x9D, 0xFF, 0x05, 0x62, 0xBC, 0x9A, 0x67, 0x86, 0xE1, 0xC5, 0x02, 0x2D, 0x14,
+    0x11, 0x29, 0x30, 0xE7, 0x90, 0x5D, 0x72, 0x6F, 0xC5, 0x62, 0xEB, 0xD4, 0xB0, 0x3F, 0x3D, 0xDC,
+    0xB9, 0xFC, 0x2B, 0x5C, 0xBD, 0x9E, 0x71, 0x81, 0x5C, 0xC5, 0xFE, 0xDF, 0x69, 0x73, 0x12, 0x66,
+    0x92, 0x06, 0xD4, 0xD5, 0x8F, 0xDF, 0x14, 0x2E, 0x9C, 0xD0, 0x4C, 0xC2, 0x4D, 0x31, 0x2E, 0x47,
+    0xA5, 0xDC, 0x8A, 0x83, 0x7B, 0xE8, 0xA5, 0xC3, 0x03, 0x98, 0xD8, 0xBF, 0xF4, 0x7D, 0x6E, 0x87,
+    0x55, 0xE4, 0x0F, 0x15, 0x10, 0xC8, 0x76, 0x4F, 0xAD, 0x1D, 0x1C, 0x95, 0x41, 0x9D, 0x88, 0xEC,
+    0x8C, 0xDA, 0xBA, 0x90, 0x7F, 0x8D, 0xD9, 0x8B, 0x47, 0x6C, 0x0C, 0xFF, 0xBA, 0x73, 0x00, 0x20,
+    0x1F, 0xF7, 0x7E, 0x5F, 0xF4, 0xEC, 0xD1, 0x02, 0x81, 0x80, 0x16, 0xB7, 0x43, 0xB5, 0x5D, 0xD7,
+    0x2B, 0x18, 0x0B, 0xAE, 0x0A, 0x69, 0x28, 0x53, 0x5E, 0x7A, 0x6A, 0xA0, 0xF2, 0xF1, 0x2E, 0x09,
+    0x43, 0x91, 0x79, 0xA5, 0x89, 0xAC, 0x16, 0x6A, 0x1A, 0xB4, 0x55, 0x22, 0xF6, 0xB6, 0x3F, 0x18,
+    0xDE, 0x60, 0xD5, 0x24, 0x53, 0x4F, 0x2A, 0x19, 0x46, 0x92, 0xA7, 0x4B, 0x38, 0xD7, 0x65, 0x96,
+    0x9C, 0x84, 0x8A, 0x6E, 0x38, 0xB8, 0xCF, 0x06, 0x9A, 0xAD, 0x0A, 0x55, 0x26, 0x7B, 0x65, 0x24,
+    0xF3, 0x02, 0x76, 0xB3, 0xE6, 0xB4, 0x01, 0xE1, 0x3C, 0x61, 0x3D, 0x68, 0x05, 0xAA, 0xD1, 0x26,
+    0x7C, 0xE0, 0x51, 0x36, 0xE5, 0x21, 0x7F, 0x76, 0x02, 0xD6, 0xF4, 0x91, 0x07, 0x74, 0x27, 0x09,
+    0xEF, 0xEF, 0x0F, 0xA5, 0x96, 0xFC, 0x5E, 0x20, 0xC1, 0xA3, 0x6F, 0x99, 0x4D, 0x45, 0x03, 0x6C,
+    0x35, 0x45, 0xD7, 0x8F, 0x47, 0x41, 0x86, 0x8D, 0x62, 0x1D, 0x02, 0x81, 0x81, 0x00, 0xC3, 0x93,
+    0x85, 0xA7, 0xFC, 0x8E, 0x85, 0x42, 0x14, 0x76, 0xC0, 0x95, 0x56, 0x73, 0xB0, 0xB5, 0x3A, 0x9D,
+    0x20, 0x30, 0x11, 0xEA, 0xED, 0x89, 0x4A, 0xF3, 0x91, 0xF3, 0xA2, 0xC3, 0x76, 0x5B, 0x6A, 0x30,
+    0x7D, 0xE2, 0x2F, 0x76, 0x3E, 0xFC, 0xF9, 0xF6, 0x31, 0xE0, 0xA0, 0x83, 0x92, 0x88, 0xDB, 0x57,
+    0xC7, 0xD6, 0x3F, 0xAD, 0xCB, 0xAA, 0x45, 0xB6, 0xE1, 0xE2, 0x71, 0xA4, 0x56, 0x2C, 0xA7, 0x3B,
+    0x1D, 0x89, 0x19, 0x50, 0xE1, 0xEE, 0xC2, 0xDD, 0xC0, 0x0D, 0xDC, 0xCB, 0x60, 0x6E, 0xE1, 0x37,
+    0x1A, 0x23, 0x64, 0xB2, 0x03, 0xE4, 0x1A, 0xFA, 0xC3, 0xF4, 0x9D, 0x85, 0x42, 0xC6, 0xF4, 0x56,
+    0x39, 0xB0, 0x1B, 0xE0, 0x75, 0xBA, 0x28, 0x04, 0xA8, 0x30, 0x57, 0x41, 0x33, 0x9F, 0x58, 0xA4,
+    0xC7, 0xB1, 0x7D, 0x58, 0x8D, 0x84, 0x49, 0x40, 0xDA, 0x28, 0x81, 0x25, 0xC4, 0x41, 0x02, 0x81,
+    0x80, 0x13, 0x20, 0x65, 0xD5, 0x96, 0x98, 0x8D, 0x16, 0x73, 0xA1, 0x31, 0x73, 0x79, 0xBA, 0xEC,
+    0xB0, 0xD9, 0x0C, 0xF6, 0xEF, 0x2F, 0xC2, 0xE7, 0x96, 0x9B, 0xA1, 0x2D, 0xE9, 0xFB, 0x45, 0xB9,
+    0xD0, 0x30, 0xE2, 0xBD, 0x30, 0x4F, 0xB6, 0xFE, 0x24, 0x02, 0xCF, 0x8D, 0x51, 0x48, 0x45, 0xD9,
+    0xF7, 0x20, 0x53, 0x1C, 0x0B, 0xA9, 0x7E, 0xC2, 0xA2, 0x65, 0xCC, 0x3E, 0x0E, 0x0D, 0xF1, 0x62,
+    0xDD, 0x5F, 0xBC, 0x55, 0x9B, 0x58, 0x26, 0x40, 0x6A, 0xEE, 0x02, 0x55, 0x36, 0xE9, 0xBA, 0x82,
+    0x5A, 0xFD, 0x3C, 0xDF, 0xA6, 0x26, 0x32, 0x81, 0xA9, 0x5E, 0x46, 0xBE, 0xBA, 0xDC, 0xD3, 0x2A,
+    0x3A, 0x3B, 0xC1, 0x4E, 0xF7, 0x1A, 0xDC, 0x4B, 0xAF, 0x67, 0x1B, 0x3A, 0x83, 0x0D, 0x04, 0xDE,
+    0x27, 0x47, 0xFC, 0xE6, 0x39, 0x89, 0x7B, 0x66, 0xF9, 0x50, 0x4D, 0xF1, 0xAC, 0x20, 0x43, 0x7E,
+    0xEE,
+];
+
+/// DER-encoded PKCS#8 format EC key. Generated using:
+/// openssl ecparam -name prime256v1 -genkey | openssl pkcs8 -topk8 -nocrypt -outform der | hexdump -e '30/1  "%02X" "\n"'
+pub static EC_P_256_KEY: &[u8] = &[
+    0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+    0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x04, 0x6D, 0x30, 0x6B, 0x02,
+    0x01, 0x01, 0x04, 0x20, 0xB9, 0x1D, 0xAF, 0x50, 0xFD, 0xD8, 0x6A, 0x40, 0xAB, 0x2C, 0xCB, 0x54,
+    0x4E, 0xED, 0xF1, 0x64, 0xBC, 0x30, 0x25, 0xFB, 0xC4, 0x69, 0x00, 0x34, 0x1A, 0x82, 0xA3, 0x72,
+    0x5D, 0xC7, 0xA9, 0x85, 0xA1, 0x44, 0x03, 0x42, 0x00, 0x04, 0xE8, 0x53, 0x0A, 0xF2, 0xD3, 0x68,
+    0x40, 0x48, 0x8C, 0xB4, 0x2F, 0x11, 0x34, 0xD7, 0xF4, 0x4A, 0x5C, 0x33, 0xFF, 0xF6, 0x2B, 0xF7,
+    0x98, 0x0F, 0x02, 0xA5, 0xD7, 0x4F, 0xF9, 0xDE, 0x60, 0x9C, 0x6E, 0xB0, 0x45, 0xDA, 0x3F, 0xF4,
+    0x34, 0x23, 0x9B, 0x4C, 0x3A, 0x09, 0x9C, 0x5E, 0x5D, 0x37, 0x96, 0xAC, 0x4A, 0xE7, 0x65, 0x2B,
+    0xD6, 0x84, 0x98, 0xEA, 0x96, 0x91, 0xFB, 0x78, 0xED, 0x86,
+];
+
+/// DER-encoded PKCS#8 format RSA key -
+///     Size: 2048
+///     Public Exponent: 65537
+///     Purpose: WRAP_KEY, ENCRYPT, DECRYPT
+///     Encryption scheme: RSAES-PKCS1-v1_5
+///         Digest: SHA_2_256
+///         Padding: RSA_OAEP
+/// This sample wrapping_key is taken from KeyMint tests
+/// (see hardware/interfaces/security/keymint/aidl/vts/functional/KeyMintTest.cpp).
+/// Similarly more test keys can be generated with below command -
+/// openssl genrsa 2048 | openssl pkcs8 -topk8 -nocrypt -outform der | hexdump -e '30/1  "%02X" "\n"'
+pub static WRAPPING_KEY: &[u8] = &[
+    0x30, 0x82, 0x04, 0xbe, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+    0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x04, 0xa8, 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01,
+    0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xae, 0xc3, 0x67, 0x93, 0x1d, 0x89, 0x00, 0xce, 0x56, 0xb0,
+    0x06, 0x7f, 0x7d, 0x70, 0xe1, 0xfc, 0x65, 0x3f, 0x3f, 0x34, 0xd1, 0x94, 0xc1, 0xfe, 0xd5, 0x00,
+    0x18, 0xfb, 0x43, 0xdb, 0x93, 0x7b, 0x06, 0xe6, 0x73, 0xa8, 0x37, 0x31, 0x3d, 0x56, 0xb1, 0xc7,
+    0x25, 0x15, 0x0a, 0x3f, 0xef, 0x86, 0xac, 0xbd, 0xdc, 0x41, 0xbb, 0x75, 0x9c, 0x28, 0x54, 0xea,
+    0xe3, 0x2d, 0x35, 0x84, 0x1e, 0xfb, 0x5c, 0x18, 0xd8, 0x2b, 0xc9, 0x0a, 0x1c, 0xb5, 0xc1, 0xd5,
+    0x5a, 0xdf, 0x24, 0x5b, 0x02, 0x91, 0x1f, 0x0b, 0x7c, 0xda, 0x88, 0xc4, 0x21, 0xff, 0x0e, 0xba,
+    0xfe, 0x7c, 0x0d, 0x23, 0xbe, 0x31, 0x2d, 0x7b, 0xd5, 0x92, 0x1f, 0xfa, 0xea, 0x13, 0x47, 0xc1,
+    0x57, 0x40, 0x6f, 0xef, 0x71, 0x8f, 0x68, 0x26, 0x43, 0xe4, 0xe5, 0xd3, 0x3c, 0x67, 0x03, 0xd6,
+    0x1c, 0x0c, 0xf7, 0xac, 0x0b, 0xf4, 0x64, 0x5c, 0x11, 0xf5, 0xc1, 0x37, 0x4c, 0x38, 0x86, 0x42,
+    0x74, 0x11, 0xc4, 0x49, 0x79, 0x67, 0x92, 0xe0, 0xbe, 0xf7, 0x5d, 0xec, 0x85, 0x8a, 0x21, 0x23,
+    0xc3, 0x67, 0x53, 0xe0, 0x2a, 0x95, 0xa9, 0x6d, 0x7c, 0x45, 0x4b, 0x50, 0x4d, 0xe3, 0x85, 0xa6,
+    0x42, 0xe0, 0xdf, 0xc3, 0xe6, 0x0a, 0xc3, 0xa7, 0xee, 0x49, 0x91, 0xd0, 0xd4, 0x8b, 0x01, 0x72,
+    0xa9, 0x5f, 0x95, 0x36, 0xf0, 0x2b, 0xa1, 0x3c, 0xec, 0xcc, 0xb9, 0x2b, 0x72, 0x7d, 0xb5, 0xc2,
+    0x7e, 0x5b, 0x2f, 0x5c, 0xec, 0x09, 0x60, 0x0b, 0x28, 0x6a, 0xf5, 0xcf, 0x14, 0xc4, 0x20, 0x24,
+    0xc6, 0x1d, 0xdf, 0xe7, 0x1c, 0x2a, 0x8d, 0x74, 0x58, 0xf1, 0x85, 0x23, 0x4c, 0xb0, 0x0e, 0x01,
+    0xd2, 0x82, 0xf1, 0x0f, 0x8f, 0xc6, 0x72, 0x1d, 0x2a, 0xed, 0x3f, 0x48, 0x33, 0xcc, 0xa2, 0xbd,
+    0x8f, 0xa6, 0x28, 0x21, 0xdd, 0x55, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x00, 0x43,
+    0x14, 0x47, 0xb6, 0x25, 0x19, 0x08, 0x11, 0x2b, 0x1e, 0xe7, 0x6f, 0x99, 0xf3, 0x71, 0x1a, 0x52,
+    0xb6, 0x63, 0x09, 0x60, 0x04, 0x6c, 0x2d, 0xe7, 0x0d, 0xe1, 0x88, 0xd8, 0x33, 0xf8, 0xb8, 0xb9,
+    0x1e, 0x4d, 0x78, 0x5c, 0xae, 0xee, 0xaf, 0x4f, 0x0f, 0x74, 0x41, 0x4e, 0x2c, 0xda, 0x40, 0x64,
+    0x1f, 0x7f, 0xe2, 0x4f, 0x14, 0xc6, 0x7a, 0x88, 0x95, 0x9b, 0xdb, 0x27, 0x76, 0x6d, 0xf9, 0xe7,
+    0x10, 0xb6, 0x30, 0xa0, 0x3a, 0xdc, 0x68, 0x3b, 0x5d, 0x2c, 0x43, 0x08, 0x0e, 0x52, 0xbe, 0xe7,
+    0x1e, 0x9e, 0xae, 0xb6, 0xde, 0x29, 0x7a, 0x5f, 0xea, 0x10, 0x72, 0x07, 0x0d, 0x18, 0x1c, 0x82,
+    0x2b, 0xcc, 0xff, 0x08, 0x7d, 0x63, 0xc9, 0x40, 0xba, 0x8a, 0x45, 0xf6, 0x70, 0xfe, 0xb2, 0x9f,
+    0xb4, 0x48, 0x4d, 0x1c, 0x95, 0xe6, 0xd2, 0x57, 0x9b, 0xa0, 0x2a, 0xae, 0x0a, 0x00, 0x90, 0x0c,
+    0x3e, 0xbf, 0x49, 0x0e, 0x3d, 0x2c, 0xd7, 0xee, 0x8d, 0x0e, 0x20, 0xc5, 0x36, 0xe4, 0xdc, 0x5a,
+    0x50, 0x97, 0x27, 0x28, 0x88, 0xcd, 0xdd, 0x7e, 0x91, 0xf2, 0x28, 0xb1, 0xc4, 0xd7, 0x47, 0x4c,
+    0x55, 0xb8, 0xfc, 0xd6, 0x18, 0xc4, 0xa9, 0x57, 0xbb, 0xdd, 0xd5, 0xad, 0x74, 0x07, 0xcc, 0x31,
+    0x2d, 0x8d, 0x98, 0xa5, 0xca, 0xf7, 0xe0, 0x8f, 0x4a, 0x0d, 0x6b, 0x45, 0xbb, 0x41, 0xc6, 0x52,
+    0x65, 0x9d, 0x5a, 0x5b, 0xa0, 0x5b, 0x66, 0x37, 0x37, 0xa8, 0x69, 0x62, 0x81, 0x86, 0x5b, 0xa2,
+    0x0f, 0xbd, 0xd7, 0xf8, 0x51, 0xe6, 0xc5, 0x6e, 0x8c, 0xbe, 0x0d, 0xdb, 0xbf, 0x24, 0xdc, 0x03,
+    0xb2, 0xd2, 0xcb, 0x4c, 0x3d, 0x54, 0x0f, 0xb0, 0xaf, 0x52, 0xe0, 0x34, 0xa2, 0xd0, 0x66, 0x98,
+    0xb1, 0x28, 0xe5, 0xf1, 0x01, 0xe3, 0xb5, 0x1a, 0x34, 0xf8, 0xd8, 0xb4, 0xf8, 0x61, 0x81, 0x02,
+    0x81, 0x81, 0x00, 0xde, 0x39, 0x2e, 0x18, 0xd6, 0x82, 0xc8, 0x29, 0x26, 0x6c, 0xc3, 0x45, 0x4e,
+    0x1d, 0x61, 0x66, 0x24, 0x2f, 0x32, 0xd9, 0xa1, 0xd1, 0x05, 0x77, 0x75, 0x3e, 0x90, 0x4e, 0xa7,
+    0xd0, 0x8b, 0xff, 0x84, 0x1b, 0xe5, 0xba, 0xc8, 0x2a, 0x16, 0x4c, 0x59, 0x70, 0x00, 0x70, 0x47,
+    0xb8, 0xc5, 0x17, 0xdb, 0x8f, 0x8f, 0x84, 0xe3, 0x7b, 0xd5, 0x98, 0x85, 0x61, 0xbd, 0xf5, 0x03,
+    0xd4, 0xdc, 0x2b, 0xdb, 0x38, 0xf8, 0x85, 0x43, 0x4a, 0xe4, 0x2c, 0x35, 0x5f, 0x72, 0x5c, 0x9a,
+    0x60, 0xf9, 0x1f, 0x07, 0x88, 0xe1, 0xf1, 0xa9, 0x72, 0x23, 0xb5, 0x24, 0xb5, 0x35, 0x7f, 0xdf,
+    0x72, 0xe2, 0xf6, 0x96, 0xba, 0xb7, 0xd7, 0x8e, 0x32, 0xbf, 0x92, 0xba, 0x8e, 0x18, 0x64, 0xea,
+    0xb1, 0x22, 0x9e, 0x91, 0x34, 0x61, 0x30, 0x74, 0x8a, 0x6e, 0x3c, 0x12, 0x4f, 0x91, 0x49, 0xd7,
+    0x1c, 0x74, 0x35, 0x02, 0x81, 0x81, 0x00, 0xc9, 0x53, 0x87, 0xc0, 0xf9, 0xd3, 0x5f, 0x13, 0x7b,
+    0x57, 0xd0, 0xd6, 0x5c, 0x39, 0x7c, 0x5e, 0x21, 0xcc, 0x25, 0x1e, 0x47, 0x00, 0x8e, 0xd6, 0x2a,
+    0x54, 0x24, 0x09, 0xc8, 0xb6, 0xb6, 0xac, 0x7f, 0x89, 0x67, 0xb3, 0x86, 0x3c, 0xa6, 0x45, 0xfc,
+    0xce, 0x49, 0x58, 0x2a, 0x9a, 0xa1, 0x73, 0x49, 0xdb, 0x6c, 0x4a, 0x95, 0xaf, 0xfd, 0xae, 0x0d,
+    0xae, 0x61, 0x2e, 0x1a, 0xfa, 0xc9, 0x9e, 0xd3, 0x9a, 0x2d, 0x93, 0x4c, 0x88, 0x04, 0x40, 0xae,
+    0xd8, 0x83, 0x2f, 0x98, 0x43, 0x16, 0x3a, 0x47, 0xf2, 0x7f, 0x39, 0x21, 0x99, 0xdc, 0x12, 0x02,
+    0xf9, 0xa0, 0xf9, 0xbd, 0x08, 0x30, 0x80, 0x07, 0xcb, 0x1e, 0x4e, 0x7f, 0x58, 0x30, 0x93, 0x66,
+    0xa7, 0xde, 0x25, 0xf7, 0xc3, 0xc9, 0xb8, 0x80, 0x67, 0x7c, 0x06, 0x8e, 0x1b, 0xe9, 0x36, 0xe8,
+    0x12, 0x88, 0x81, 0x52, 0x52, 0xa8, 0xa1, 0x02, 0x81, 0x80, 0x57, 0xff, 0x8c, 0xa1, 0x89, 0x50,
+    0x80, 0xb2, 0xca, 0xe4, 0x86, 0xef, 0x0a, 0xdf, 0xd7, 0x91, 0xfb, 0x02, 0x35, 0xc0, 0xb8, 0xb3,
+    0x6c, 0xd6, 0xc1, 0x36, 0xe5, 0x2e, 0x40, 0x85, 0xf4, 0xea, 0x5a, 0x06, 0x32, 0x12, 0xa4, 0xf1,
+    0x05, 0xa3, 0x76, 0x47, 0x43, 0xe5, 0x32, 0x81, 0x98, 0x8a, 0xba, 0x07, 0x3f, 0x6e, 0x00, 0x27,
+    0x29, 0x8e, 0x1c, 0x43, 0x78, 0x55, 0x6e, 0x0e, 0xfc, 0xa0, 0xe1, 0x4e, 0xce, 0x1a, 0xf7, 0x6a,
+    0xd0, 0xb0, 0x30, 0xf2, 0x7a, 0xf6, 0xf0, 0xab, 0x35, 0xfb, 0x73, 0xa0, 0x60, 0xd8, 0xb1, 0xa0,
+    0xe1, 0x42, 0xfa, 0x26, 0x47, 0xe9, 0x3b, 0x32, 0xe3, 0x6d, 0x82, 0x82, 0xae, 0x0a, 0x4d, 0xe5,
+    0x0a, 0xb7, 0xaf, 0xe8, 0x55, 0x00, 0xa1, 0x6f, 0x43, 0xa6, 0x47, 0x19, 0xd6, 0xe2, 0xb9, 0x43,
+    0x98, 0x23, 0x71, 0x9c, 0xd0, 0x8b, 0xcd, 0x03, 0x17, 0x81, 0x02, 0x81, 0x81, 0x00, 0xba, 0x73,
+    0xb0, 0xbb, 0x28, 0xe3, 0xf8, 0x1e, 0x9b, 0xd1, 0xc5, 0x68, 0x71, 0x3b, 0x10, 0x12, 0x41, 0xac,
+    0xc6, 0x07, 0x97, 0x6c, 0x4d, 0xdc, 0xcc, 0x90, 0xe6, 0x5b, 0x65, 0x56, 0xca, 0x31, 0x51, 0x60,
+    0x58, 0xf9, 0x2b, 0x6e, 0x09, 0xf3, 0xb1, 0x60, 0xff, 0x0e, 0x37, 0x4e, 0xc4, 0x0d, 0x78, 0xae,
+    0x4d, 0x49, 0x79, 0xfd, 0xe6, 0xac, 0x06, 0xa1, 0xa4, 0x00, 0xc6, 0x1d, 0xd3, 0x12, 0x54, 0x18,
+    0x6a, 0xf3, 0x0b, 0x22, 0xc1, 0x05, 0x82, 0xa8, 0xa4, 0x3e, 0x34, 0xfe, 0x94, 0x9c, 0x5f, 0x3b,
+    0x97, 0x55, 0xba, 0xe7, 0xba, 0xa7, 0xb7, 0xb7, 0xa6, 0xbd, 0x03, 0xb3, 0x8c, 0xef, 0x55, 0xc8,
+    0x68, 0x85, 0xfc, 0x6c, 0x19, 0x78, 0xb9, 0xce, 0xe7, 0xef, 0x33, 0xda, 0x50, 0x7c, 0x9d, 0xf6,
+    0xb9, 0x27, 0x7c, 0xff, 0x1e, 0x6a, 0xaa, 0x5d, 0x57, 0xac, 0xa5, 0x28, 0x46, 0x61, 0x02, 0x81,
+    0x81, 0x00, 0xc9, 0x31, 0x61, 0x7c, 0x77, 0x82, 0x9d, 0xfb, 0x12, 0x70, 0x50, 0x2b, 0xe9, 0x19,
+    0x5c, 0x8f, 0x28, 0x30, 0x88, 0x5f, 0x57, 0xdb, 0xa8, 0x69, 0x53, 0x68, 0x11, 0xe6, 0x86, 0x42,
+    0x36, 0xd0, 0xc4, 0x73, 0x6a, 0x00, 0x08, 0xa1, 0x45, 0xaf, 0x36, 0xb8, 0x35, 0x7a, 0x7c, 0x3d,
+    0x13, 0x99, 0x66, 0xd0, 0x4c, 0x4e, 0x00, 0x93, 0x4e, 0xa1, 0xae, 0xde, 0x3b, 0xb6, 0xb8, 0xec,
+    0x84, 0x1d, 0xc9, 0x5e, 0x3f, 0x57, 0x97, 0x51, 0xe2, 0xbf, 0xdf, 0xe2, 0x7a, 0xe7, 0x78, 0x98,
+    0x3f, 0x95, 0x93, 0x56, 0x21, 0x07, 0x23, 0x28, 0x7b, 0x0a, 0xff, 0xcc, 0x9f, 0x72, 0x70, 0x44,
+    0xd4, 0x8c, 0x37, 0x3f, 0x1b, 0xab, 0xde, 0x07, 0x24, 0xfa, 0x17, 0xa4, 0xfd, 0x4d, 0xa0, 0x90,
+    0x2c, 0x7c, 0x9b, 0x9b, 0xf2, 0x7b, 0xa6, 0x1b, 0xe6, 0xad, 0x02, 0xdf, 0xdd, 0xda, 0x8f, 0x4e,
+    0x68, 0x22,
+];
+
+/// WrappedKeyData as ASN.1 DER-encoded data corresponding to the `SecureKeyWrapper` schema
+/// specified in IKeyMintDevice.aidl. Wrapped key parameters are -
+///     Algorithm: AES
+///     Key size: 256
+///     Block mode: ECB
+///     Padding mode: PKCS7
+/// This sample wrapped_key is taken from KeyMint tests (see KeyMintTest.cpp).
+pub static WRAPPED_KEY: &[u8] = &[
+    0x30, 0x82, 0x01, 0x79, 0x02, 0x01, 0x00, 0x04, 0x82, 0x01, 0x00, 0x93, 0x4b, 0xf9, 0x4e, 0x2a,
+    0xa2, 0x8a, 0x3f, 0x83, 0xc9, 0xf7, 0x92, 0x97, 0x25, 0x02, 0x62, 0xfb, 0xe3, 0x27, 0x6b, 0x5a,
+    0x1c, 0x91, 0x15, 0x9b, 0xbf, 0xa3, 0xef, 0x89, 0x57, 0xaa, 0xc8, 0x4b, 0x59, 0xb3, 0x0b, 0x45,
+    0x5a, 0x79, 0xc2, 0x97, 0x34, 0x80, 0x82, 0x3d, 0x8b, 0x38, 0x63, 0xc3, 0xde, 0xef, 0x4a, 0x8e,
+    0x24, 0x35, 0x90, 0x26, 0x8d, 0x80, 0xe1, 0x87, 0x51, 0xa0, 0xe1, 0x30, 0xf6, 0x7c, 0xe6, 0xa1,
+    0xac, 0xe9, 0xf7, 0x9b, 0x95, 0xe0, 0x97, 0x47, 0x4f, 0xeb, 0xc9, 0x81, 0x19, 0x5b, 0x1d, 0x13,
+    0xa6, 0x90, 0x86, 0xc0, 0x86, 0x3f, 0x66, 0xa7, 0xb7, 0xfd, 0xb4, 0x87, 0x92, 0x22, 0x7b, 0x1a,
+    0xc5, 0xe2, 0x48, 0x9f, 0xeb, 0xdf, 0x08, 0x7a, 0xb5, 0x48, 0x64, 0x83, 0x03, 0x3a, 0x6f, 0x00,
+    0x1c, 0xa5, 0xd1, 0xec, 0x1e, 0x27, 0xf5, 0xc3, 0x0f, 0x4c, 0xec, 0x26, 0x42, 0x07, 0x4a, 0x39,
+    0xae, 0x68, 0xae, 0xe5, 0x52, 0xe1, 0x96, 0x62, 0x7a, 0x8e, 0x3d, 0x86, 0x7e, 0x67, 0xa8, 0xc0,
+    0x1b, 0x11, 0xe7, 0x5f, 0x13, 0xcc, 0xa0, 0xa9, 0x7a, 0xb6, 0x68, 0xb5, 0x0c, 0xda, 0x07, 0xa8,
+    0xec, 0xb7, 0xcd, 0x8e, 0x3d, 0xd7, 0x00, 0x9c, 0x96, 0x36, 0x53, 0x4f, 0x6f, 0x23, 0x9c, 0xff,
+    0xe1, 0xfc, 0x8d, 0xaa, 0x46, 0x6f, 0x78, 0xb6, 0x76, 0xc7, 0x11, 0x9e, 0xfb, 0x96, 0xbc, 0xe4,
+    0xe6, 0x9c, 0xa2, 0xa2, 0x5d, 0x0b, 0x34, 0xed, 0x9c, 0x3f, 0xf9, 0x99, 0xb8, 0x01, 0x59, 0x7d,
+    0x52, 0x20, 0xe3, 0x07, 0xea, 0xa5, 0xbe, 0xe5, 0x07, 0xfb, 0x94, 0xd1, 0xfa, 0x69, 0xf9, 0xe5,
+    0x19, 0xb2, 0xde, 0x31, 0x5b, 0xac, 0x92, 0xc3, 0x6f, 0x2e, 0xa1, 0xfa, 0x1d, 0xf4, 0x47, 0x8c,
+    0x0d, 0xde, 0xde, 0xae, 0x8c, 0x70, 0xe0, 0x23, 0x3c, 0xd0, 0x98, 0x04, 0x0c, 0xd7, 0x96, 0xb0,
+    0x2c, 0x37, 0x0f, 0x1f, 0xa4, 0xcc, 0x01, 0x24, 0xf1, 0x30, 0x2e, 0x02, 0x01, 0x03, 0x30, 0x29,
+    0xa1, 0x08, 0x31, 0x06, 0x02, 0x01, 0x00, 0x02, 0x01, 0x01, 0xa2, 0x03, 0x02, 0x01, 0x20, 0xa3,
+    0x04, 0x02, 0x02, 0x01, 0x00, 0xa4, 0x05, 0x31, 0x03, 0x02, 0x01, 0x01, 0xa6, 0x05, 0x31, 0x03,
+    0x02, 0x01, 0x40, 0xbf, 0x83, 0x77, 0x02, 0x05, 0x00, 0x04, 0x20, 0xcc, 0xd5, 0x40, 0x85, 0x5f,
+    0x83, 0x3a, 0x5e, 0x14, 0x80, 0xbf, 0xd2, 0xd3, 0x6f, 0xaf, 0x3a, 0xee, 0xe1, 0x5d, 0xf5, 0xbe,
+    0xab, 0xe2, 0x69, 0x1b, 0xc8, 0x2d, 0xde, 0x2a, 0x7a, 0xa9, 0x10, 0x04, 0x10, 0x64, 0xc9, 0xf6,
+    0x89, 0xc6, 0x0f, 0xf6, 0x22, 0x3a, 0xb6, 0xe6, 0x99, 0x9e, 0x0e, 0xb6, 0xe5,
+];
+
+/// To map Keystore errors.
+#[derive(thiserror::Error, Debug, Eq, PartialEq)]
+pub enum Error {
+    /// Keystore2 error code
+    #[error("ResponseCode {0:?}")]
+    Rc(ResponseCode),
+    /// Keymint error code
+    #[error("ErrorCode {0:?}")]
+    Km(ErrorCode),
+    /// Exception
+    #[error("Binder exception {0:?}")]
+    Binder(ExceptionCode),
+    /// This is returned if the C implementation of extractSubjectFromCertificate failed.
+    #[error("Failed to validate certificate chain.")]
+    ValidateCertChainFailed,
+    /// Error code to indicate error in ASN.1 DER-encoded data creation.
+    #[error("Failed to create and encode ASN.1 data.")]
+    DerEncodeFailed,
+}
+
+/// Keystore2 error mapping.
+pub fn map_ks_error<T>(r: BinderResult<T>) -> Result<T, Error> {
+    r.map_err(|s| {
+        match s.exception_code() {
+            ExceptionCode::SERVICE_SPECIFIC => {
+                match s.service_specific_error() {
+                    se if se < 0 => {
+                        // Negative service specific errors are KM error codes.
+                        Error::Km(ErrorCode(se))
+                    }
+                    se => {
+                        // Positive service specific errors are KS response codes.
+                        Error::Rc(ResponseCode(se))
+                    }
+                }
+            }
+            // We create `Error::Binder` to preserve the exception code
+            // for logging.
+            e_code => Error::Binder(e_code),
+        }
+    })
+}
+
+/// Generate EC Key using given security level and domain with below key parameters and
+/// optionally allow the generated key to be attested with factory provisioned attest key using
+/// given challenge and application id -
 ///     Purposes: SIGN and VERIFY
 ///     Digest: SHA_2_256
 ///     Curve: P_256
-pub fn generate_ec_p256_signing_key_with_attestation(
+pub fn generate_ec_p256_signing_key(
     sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    domain: Domain,
+    nspace: i64,
+    alias: Option<String>,
+    att_challenge: Option<&[u8]>,
 ) -> binder::Result<KeyMetadata> {
-    let att_challenge: &[u8] = b"foo";
-    let att_app_id: &[u8] = b"bar";
-    let gen_params = AuthSetBuilder::new()
+    let mut key_attest = false;
+    let mut gen_params = AuthSetBuilder::new()
+        .no_auth_required()
         .algorithm(Algorithm::EC)
         .purpose(KeyPurpose::SIGN)
         .purpose(KeyPurpose::VERIFY)
         .digest(Digest::SHA_2_256)
-        .ec_curve(EcCurve::P_256)
-        .attestation_challenge(att_challenge.to_vec())
-        .attestation_app_id(att_app_id.to_vec());
+        .ec_curve(EcCurve::P_256);
+
+    if let Some(challenge) = att_challenge {
+        key_attest = true;
+        gen_params = gen_params.clone().attestation_challenge(challenge.to_vec());
+    }
 
     match sec_level.generateKey(
-        &KeyDescriptor {
-            domain: Domain::BLOB,
-            nspace: SELINUX_SHELL_NAMESPACE,
-            alias: None,
-            blob: None,
-        },
+        &KeyDescriptor { domain, nspace, alias, blob: None },
         None,
         &gen_params,
         0,
@@ -58,8 +360,722 @@
     ) {
         Ok(key_metadata) => {
             assert!(key_metadata.certificate.is_some());
-            assert!(key_metadata.certificateChain.is_some());
-            assert!(key_metadata.key.blob.is_some());
+            if key_attest {
+                assert!(key_metadata.certificateChain.is_some());
+            }
+            if domain == Domain::BLOB {
+                assert!(key_metadata.key.blob.is_some());
+            }
+
+            Ok(key_metadata)
+        }
+        Err(e) => Err(e),
+    }
+}
+
+/// Generate EC signing key.
+pub fn generate_ec_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    domain: Domain,
+    nspace: i64,
+    alias: Option<String>,
+    ec_curve: EcCurve,
+    digest: Digest,
+) -> binder::Result<KeyMetadata> {
+    let gen_params = AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::EC)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .digest(digest)
+        .ec_curve(ec_curve);
+
+    let key_metadata = sec_level.generateKey(
+        &KeyDescriptor { domain, nspace, alias, blob: None },
+        None,
+        &gen_params,
+        0,
+        b"entropy",
+    )?;
+
+    // Must have a public key.
+    assert!(key_metadata.certificate.is_some());
+
+    // Should not have an attestation record.
+    assert!(key_metadata.certificateChain.is_none());
+
+    if domain == Domain::BLOB {
+        assert!(key_metadata.key.blob.is_some());
+    } else {
+        assert!(key_metadata.key.blob.is_none());
+    }
+    Ok(key_metadata)
+}
+
+/// Generate a RSA key with the given key parameters, alias, domain and namespace.
+pub fn generate_rsa_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    domain: Domain,
+    nspace: i64,
+    alias: Option<String>,
+    key_params: &KeyParams,
+    attest_key: Option<&KeyDescriptor>,
+) -> binder::Result<KeyMetadata> {
+    let mut gen_params = AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::RSA)
+        .rsa_public_exponent(65537)
+        .key_size(key_params.key_size);
+
+    for purpose in &key_params.purpose {
+        gen_params = gen_params.purpose(*purpose);
+    }
+    if let Some(value) = key_params.digest {
+        gen_params = gen_params.digest(value)
+    }
+    if let Some(value) = key_params.padding {
+        gen_params = gen_params.padding_mode(value);
+    }
+    if let Some(value) = key_params.mgf_digest {
+        gen_params = gen_params.mgf_digest(value);
+    }
+    if let Some(value) = key_params.block_mode {
+        gen_params = gen_params.block_mode(value)
+    }
+    if let Some(value) = &key_params.att_challenge {
+        gen_params = gen_params.attestation_challenge(value.to_vec())
+    }
+
+    let key_metadata = sec_level.generateKey(
+        &KeyDescriptor { domain, nspace, alias, blob: None },
+        attest_key,
+        &gen_params,
+        0,
+        b"entropy",
+    )?;
+
+    // Must have a public key.
+    assert!(key_metadata.certificate.is_some());
+
+    if attest_key.is_none() && key_params.att_challenge.is_some() {
+        // Should have an attestation record.
+        assert!(key_metadata.certificateChain.is_some());
+    } else {
+        // Should not have an attestation record.
+        assert!(key_metadata.certificateChain.is_none());
+    }
+
+    assert!(
+        (domain == Domain::BLOB && key_metadata.key.blob.is_some())
+            || key_metadata.key.blob.is_none()
+    );
+
+    Ok(key_metadata)
+}
+
+/// Generate AES/3DES key.
+pub fn generate_sym_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    algorithm: Algorithm,
+    size: i32,
+    alias: &str,
+    padding_mode: &PaddingMode,
+    block_mode: &BlockMode,
+    min_mac_len: Option<i32>,
+) -> binder::Result<KeyMetadata> {
+    let mut gen_params = AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(algorithm)
+        .purpose(KeyPurpose::ENCRYPT)
+        .purpose(KeyPurpose::DECRYPT)
+        .key_size(size)
+        .padding_mode(*padding_mode)
+        .block_mode(*block_mode);
+
+    if let Some(val) = min_mac_len {
+        gen_params = gen_params.min_mac_length(val);
+    }
+
+    let key_metadata = sec_level.generateKey(
+        &KeyDescriptor {
+            domain: Domain::APP,
+            nspace: -1,
+            alias: Some(alias.to_string()),
+            blob: None,
+        },
+        None,
+        &gen_params,
+        0,
+        b"entropy",
+    )?;
+
+    // Should not have public certificate.
+    assert!(key_metadata.certificate.is_none());
+
+    // Should not have an attestation record.
+    assert!(key_metadata.certificateChain.is_none());
+    Ok(key_metadata)
+}
+
+/// Generate HMAC key.
+pub fn generate_hmac_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    alias: &str,
+    key_size: i32,
+    min_mac_len: i32,
+    digest: Digest,
+) -> binder::Result<KeyMetadata> {
+    let gen_params = AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::HMAC)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .key_size(key_size)
+        .min_mac_length(min_mac_len)
+        .digest(digest);
+
+    let key_metadata = sec_level.generateKey(
+        &KeyDescriptor {
+            domain: Domain::APP,
+            nspace: -1,
+            alias: Some(alias.to_string()),
+            blob: None,
+        },
+        None,
+        &gen_params,
+        0,
+        b"entropy",
+    )?;
+
+    // Should not have public certificate.
+    assert!(key_metadata.certificate.is_none());
+
+    // Should not have an attestation record.
+    assert!(key_metadata.certificateChain.is_none());
+
+    Ok(key_metadata)
+}
+
+/// Generate RSA or EC attestation keys using below parameters -
+///     Purpose: ATTEST_KEY
+///     Digest: Digest::SHA_2_256
+///     Padding: PaddingMode::RSA_PKCS1_1_5_SIGN
+///     RSA-Key-Size: 2048
+///     EC-Curve: EcCurve::P_256
+pub fn generate_attestation_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    algorithm: Algorithm,
+    att_challenge: &[u8],
+) -> binder::Result<KeyMetadata> {
+    assert!(algorithm == Algorithm::RSA || algorithm == Algorithm::EC);
+
+    if algorithm == Algorithm::RSA {
+        let alias = "ks_rsa_attest_test_key";
+        let metadata = generate_rsa_key(
+            sec_level,
+            Domain::APP,
+            -1,
+            Some(alias.to_string()),
+            &KeyParams {
+                key_size: 2048,
+                purpose: vec![KeyPurpose::ATTEST_KEY],
+                padding: Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
+                digest: Some(Digest::SHA_2_256),
+                mgf_digest: None,
+                block_mode: None,
+                att_challenge: Some(att_challenge.to_vec()),
+            },
+            None,
+        )
+        .unwrap();
+        Ok(metadata)
+    } else {
+        let metadata = generate_ec_attestation_key(
+            sec_level,
+            att_challenge,
+            Digest::SHA_2_256,
+            EcCurve::P_256,
+        )
+        .unwrap();
+
+        Ok(metadata)
+    }
+}
+
+/// Generate EC attestation key with the given
+///    curve, attestation-challenge and attestation-app-id.
+pub fn generate_ec_attestation_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    att_challenge: &[u8],
+    digest: Digest,
+    ec_curve: EcCurve,
+) -> binder::Result<KeyMetadata> {
+    let alias = "ks_attest_ec_test_key";
+    let gen_params = AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::EC)
+        .purpose(KeyPurpose::ATTEST_KEY)
+        .ec_curve(ec_curve)
+        .digest(digest)
+        .attestation_challenge(att_challenge.to_vec());
+
+    let attestation_key_metadata = sec_level.generateKey(
+        &KeyDescriptor {
+            domain: Domain::APP,
+            nspace: -1,
+            alias: Some(alias.to_string()),
+            blob: None,
+        },
+        None,
+        &gen_params,
+        0,
+        b"entropy",
+    )?;
+
+    // Should have public certificate.
+    assert!(attestation_key_metadata.certificate.is_some());
+    // Should have an attestation record.
+    assert!(attestation_key_metadata.certificateChain.is_some());
+
+    Ok(attestation_key_metadata)
+}
+
+/// Generate EC-P-256 key and attest it with given attestation key.
+pub fn generate_ec_256_attested_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    alias: Option<String>,
+    att_challenge: &[u8],
+    attest_key: &KeyDescriptor,
+) -> binder::Result<KeyMetadata> {
+    let ec_gen_params = AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::EC)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .digest(Digest::SHA_2_256)
+        .ec_curve(EcCurve::P_256)
+        .attestation_challenge(att_challenge.to_vec());
+
+    let ec_key_metadata = sec_level
+        .generateKey(
+            &KeyDescriptor { domain: Domain::APP, nspace: -1, alias, blob: None },
+            Some(attest_key),
+            &ec_gen_params,
+            0,
+            b"entropy",
+        )
+        .unwrap();
+
+    // Should have public certificate.
+    assert!(ec_key_metadata.certificate.is_some());
+    // Shouldn't have an attestation record.
+    assert!(ec_key_metadata.certificateChain.is_none());
+
+    Ok(ec_key_metadata)
+}
+
+/// Verify that given key param is listed in given authorizations list.
+pub fn check_key_param(authorizations: &[Authorization], key_param: KeyParameter) -> bool {
+    for authrization in authorizations {
+        if authrization.keyParameter == key_param {
+            return true;
+        }
+    }
+
+    false
+}
+
+/// Imports above defined RSA key - `RSA_2048_KEY` and validates imported key parameters.
+pub fn import_rsa_2048_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    domain: Domain,
+    nspace: i64,
+    alias: Option<String>,
+    import_params: AuthSetBuilder,
+) -> binder::Result<KeyMetadata> {
+    let key_metadata = sec_level
+        .importKey(
+            &KeyDescriptor { domain, nspace, alias, blob: None },
+            None,
+            &import_params,
+            0,
+            RSA_2048_KEY,
+        )
+        .unwrap();
+
+    assert!(key_metadata.certificate.is_some());
+    assert!(key_metadata.certificateChain.is_none());
+
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::RSA) }
+    ));
+
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(2048) }
+    ));
+
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) }
+    ));
+
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter {
+            tag: Tag::RSA_PUBLIC_EXPONENT,
+            value: KeyParameterValue::LongInteger(65537)
+        }
+    ));
+
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter {
+            tag: Tag::PADDING,
+            value: KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS)
+        }
+    ));
+
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::IMPORTED) }
+    ));
+
+    Ok(key_metadata)
+}
+
+/// Imports above defined EC key - `EC_P_256_KEY` and validates imported key parameters.
+pub fn import_ec_p_256_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    domain: Domain,
+    nspace: i64,
+    alias: Option<String>,
+    import_params: AuthSetBuilder,
+) -> binder::Result<KeyMetadata> {
+    let key_metadata = sec_level
+        .importKey(
+            &KeyDescriptor { domain, nspace, alias, blob: None },
+            None,
+            &import_params,
+            0,
+            EC_P_256_KEY,
+        )
+        .unwrap();
+
+    assert!(key_metadata.certificate.is_some());
+    assert!(key_metadata.certificateChain.is_none());
+
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::EC) }
+    ));
+
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::EC_CURVE, value: KeyParameterValue::EcCurve(EcCurve::P_256) }
+    ));
+
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) }
+    ));
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::IMPORTED) }
+    ));
+
+    Ok(key_metadata)
+}
+
+/// Import sample AES key and validate its key parameters.
+pub fn import_aes_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    domain: Domain,
+    nspace: i64,
+    alias: Option<String>,
+) -> binder::Result<KeyMetadata> {
+    static AES_KEY: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+    let key_size = AES_KEY.len() * 8;
+
+    let import_params = AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::AES)
+        .block_mode(BlockMode::ECB)
+        .key_size(key_size.try_into().unwrap())
+        .purpose(KeyPurpose::ENCRYPT)
+        .purpose(KeyPurpose::DECRYPT)
+        .padding_mode(PaddingMode::PKCS7);
+
+    let key_metadata = sec_level.importKey(
+        &KeyDescriptor { domain, nspace, alias, blob: None },
+        None,
+        &import_params,
+        0,
+        AES_KEY,
+    )?;
+
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::AES) }
+    ));
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(128) }
+    ));
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter {
+            tag: Tag::PADDING,
+            value: KeyParameterValue::PaddingMode(PaddingMode::PKCS7)
+        }
+    ));
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::BLOCK_MODE, value: KeyParameterValue::BlockMode(BlockMode::ECB) }
+    ));
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::IMPORTED) }
+    ));
+
+    Ok(key_metadata)
+}
+
+/// Import sample 3DES key and validate its key parameters.
+pub fn import_3des_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    domain: Domain,
+    nspace: i64,
+    alias: Option<String>,
+) -> binder::Result<KeyMetadata> {
+    static TRIPLE_DES_KEY: &[u8] = &[
+        0xa4, 0x9d, 0x75, 0x64, 0x19, 0x9e, 0x97, 0xcb, 0x52, 0x9d, 0x2c, 0x9d, 0x97, 0xbf, 0x2f,
+        0x98, 0xd3, 0x5e, 0xdf, 0x57, 0xba, 0x1f, 0x73, 0x58,
+    ];
+
+    let import_params = AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::TRIPLE_DES)
+        .block_mode(BlockMode::ECB)
+        .key_size(168)
+        .purpose(KeyPurpose::ENCRYPT)
+        .purpose(KeyPurpose::DECRYPT)
+        .padding_mode(PaddingMode::PKCS7);
+
+    let key_metadata = sec_level.importKey(
+        &KeyDescriptor { domain, nspace, alias, blob: None },
+        None,
+        &import_params,
+        0,
+        TRIPLE_DES_KEY,
+    )?;
+
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter {
+            tag: Tag::ALGORITHM,
+            value: KeyParameterValue::Algorithm(Algorithm::TRIPLE_DES)
+        }
+    ));
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(168) }
+    ));
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter {
+            tag: Tag::PADDING,
+            value: KeyParameterValue::PaddingMode(PaddingMode::PKCS7)
+        }
+    ));
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::BLOCK_MODE, value: KeyParameterValue::BlockMode(BlockMode::ECB) }
+    ));
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::IMPORTED) }
+    ));
+
+    Ok(key_metadata)
+}
+
+/// Import sample HMAC key and validate its key parameters.
+pub fn import_hmac_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    domain: Domain,
+    nspace: i64,
+    alias: Option<String>,
+) -> binder::Result<KeyMetadata> {
+    static HMAC_KEY: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+    let key_size = HMAC_KEY.len() * 8;
+
+    let import_params = AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::HMAC)
+        .key_size(key_size.try_into().unwrap())
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .digest(Digest::SHA_2_256)
+        .min_mac_length(256);
+
+    let key_metadata = sec_level.importKey(
+        &KeyDescriptor { domain, nspace, alias, blob: None },
+        None,
+        &import_params,
+        0,
+        HMAC_KEY,
+    )?;
+
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::HMAC) }
+    ));
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(128) }
+    ));
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) }
+    ));
+    assert!(check_key_param(
+        &key_metadata.authorizations,
+        KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::IMPORTED) }
+    ));
+
+    Ok(key_metadata)
+}
+
+/// Imports RSA encryption key with WRAP_KEY purpose.
+pub fn import_wrapping_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    wrapping_key_data: &[u8],
+    wrapping_key_alias: Option<String>,
+) -> binder::Result<KeyMetadata> {
+    let wrapping_key_params = AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::RSA)
+        .digest(Digest::SHA_2_256)
+        .purpose(KeyPurpose::ENCRYPT)
+        .purpose(KeyPurpose::DECRYPT)
+        .purpose(KeyPurpose::WRAP_KEY)
+        .padding_mode(PaddingMode::RSA_OAEP)
+        .key_size(2048)
+        .rsa_public_exponent(65537)
+        .cert_not_before(0)
+        .cert_not_after(253402300799000);
+
+    sec_level.importKey(
+        &KeyDescriptor { domain: Domain::APP, nspace: -1, alias: wrapping_key_alias, blob: None },
+        None,
+        &wrapping_key_params,
+        0,
+        wrapping_key_data,
+    )
+}
+
+/// Import wrapped key using given wrapping key.
+pub fn import_wrapped_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    alias: Option<String>,
+    wrapping_key_metadata: &KeyMetadata,
+    wrapped_key: Option<Vec<u8>>,
+) -> binder::Result<KeyMetadata> {
+    let unwrap_params =
+        AuthSetBuilder::new().digest(Digest::SHA_2_256).padding_mode(PaddingMode::RSA_OAEP);
+
+    let authenticator_spec: &[AuthenticatorSpec] = &[AuthenticatorSpec {
+        authenticatorType: HardwareAuthenticatorType::NONE,
+        authenticatorId: 0,
+    }];
+
+    let key_metadata = sec_level.importWrappedKey(
+        &KeyDescriptor { domain: Domain::APP, nspace: -1, alias, blob: wrapped_key },
+        &wrapping_key_metadata.key,
+        None,
+        &unwrap_params,
+        authenticator_spec,
+    )?;
+
+    Ok(key_metadata)
+}
+
+/// Import wrapping key and then import wrapped key using wrapping key.
+pub fn import_wrapping_key_and_wrapped_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    domain: Domain,
+    nspace: i64,
+    alias: Option<String>,
+    wrapping_key_alias: Option<String>,
+    wrapping_key_params: AuthSetBuilder,
+) -> binder::Result<KeyMetadata> {
+    let wrapping_key_metadata = sec_level.importKey(
+        &KeyDescriptor { domain, nspace, alias: wrapping_key_alias, blob: None },
+        None,
+        &wrapping_key_params,
+        0,
+        WRAPPING_KEY,
+    )?;
+
+    import_wrapped_key(sec_level, alias, &wrapping_key_metadata, Some(WRAPPED_KEY.to_vec()))
+}
+
+/// Import given key material as AES-256-GCM-NONE transport key.
+pub fn import_transport_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    transport_key_alias: Option<String>,
+    transport_key: &[u8],
+) -> binder::Result<KeyMetadata> {
+    let transport_key_params = AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::AES)
+        .block_mode(BlockMode::GCM)
+        .padding_mode(PaddingMode::NONE)
+        .key_size(256)
+        .caller_nonce()
+        .min_mac_length(128)
+        .purpose(KeyPurpose::ENCRYPT)
+        .purpose(KeyPurpose::DECRYPT);
+
+    sec_level.importKey(
+        &KeyDescriptor { domain: Domain::APP, nspace: -1, alias: transport_key_alias, blob: None },
+        None,
+        &transport_key_params,
+        0,
+        transport_key,
+    )
+}
+
+/// Generate EC key with purpose AGREE_KEY.
+pub fn generate_ec_agree_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    ec_curve: EcCurve,
+    digest: Digest,
+    domain: Domain,
+    nspace: i64,
+    alias: Option<String>,
+) -> binder::Result<KeyMetadata> {
+    let gen_params = AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::EC)
+        .purpose(KeyPurpose::AGREE_KEY)
+        .digest(digest)
+        .ec_curve(ec_curve);
+
+    match sec_level.generateKey(
+        &KeyDescriptor { domain, nspace, alias, blob: None },
+        None,
+        &gen_params,
+        0,
+        b"entropy",
+    ) {
+        Ok(key_metadata) => {
+            assert!(key_metadata.certificate.is_some());
+            if domain == Domain::BLOB {
+                assert!(key_metadata.key.blob.is_some());
+            }
 
             Ok(key_metadata)
         }
diff --git a/keystore2/tests/Android.bp b/keystore2/tests/Android.bp
new file mode 100644
index 0000000..78dd2d7
--- /dev/null
+++ b/keystore2/tests/Android.bp
@@ -0,0 +1,100 @@
+// Copyright 2022, 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_test {
+    name: "keystore2_client_tests",
+    defaults: [
+        "keymint_use_latest_hal_aidl_rust",
+        "keystore2_use_latest_aidl_rust",
+    ],
+    srcs: ["keystore2_client_tests.rs"],
+    test_suites: [
+        "general-tests",
+    ],
+    test_config: "AndroidTest.xml",
+
+    rustlibs: [
+        "librustutils",
+        "libkeystore2_test_utils",
+        "packagemanager_aidl-rust",
+        "libnix",
+        "libanyhow",
+        "libbinder_rs",
+        "liblazy_static",
+        "liblibc",
+        "libserde",
+        "libthiserror",
+        "libcxx",
+        "libopenssl",
+    ],
+    static_libs: [
+        "libkeystore2_ffi_test_utils",
+        "libgtest",
+        "libkeymint_vts_test_utils",
+    ],
+    shared_libs: [
+        "libcrypto",
+        "libkeymaster_portable",
+        "libkeymaster_messages",
+        "libcppbor_external",
+    ],
+    require_root: true,
+}
+
+cc_library_static {
+    name: "libkeystore2_ffi_test_utils",
+    srcs: ["ffi_test_utils.cpp"],
+    defaults: [
+        "keymint_vts_defaults",
+        "hidl_defaults",
+    ],
+    generated_headers: [
+        "cxx-bridge-header",
+        "libkeystore2_ffi_test_utils_bridge_header",
+    ],
+    generated_sources: ["libkeystore2_ffi_test_utils_bridge_code"],
+    static_libs: [
+        "libkeymint_vts_test_utils",
+    ],
+    shared_libs: [
+        "libkeymaster_portable",
+        "libkeymaster_messages",
+        "libcppbor_external",
+    ],
+}
+
+genrule {
+    name: "libkeystore2_ffi_test_utils_bridge_code",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) >> $(out)",
+    srcs: ["ffi_test_utils.rs"],
+    out: ["libkeystore2_test_utils_cxx_generated.cc"],
+}
+
+genrule {
+    name: "libkeystore2_ffi_test_utils_bridge_header",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) --header >> $(out)",
+    srcs: ["ffi_test_utils.rs"],
+    out: ["ffi_test_utils.rs.h"],
+}
diff --git a/keystore2/tests/AndroidTest.xml b/keystore2/tests/AndroidTest.xml
new file mode 100644
index 0000000..7db36f7
--- /dev/null
+++ b/keystore2/tests/AndroidTest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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 to run keystore2_client_tests device tests.">
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option
+            name="push"
+            value="keystore2_client_tests->/data/local/tmp/keystore2_client_tests"
+        />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.rust.RustBinaryTest" >
+        <option name="test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="keystore2_client_tests" />
+        <!-- When we run run multiple tests by default they run in parallel.
+          This will create issue as we create various child/user contexts
+          in a test leading to issues with IPC.
+          Serializing tests with below configuration to avoid IPC issues.
+        -->
+        <option name="native-test-flag" value="--test-threads=1" />
+    </test>
+</configuration>
diff --git a/keystore2/tests/ffi_test_utils.cpp b/keystore2/tests/ffi_test_utils.cpp
new file mode 100644
index 0000000..de20d83
--- /dev/null
+++ b/keystore2/tests/ffi_test_utils.cpp
@@ -0,0 +1,366 @@
+#include "ffi_test_utils.hpp"
+
+#include <iostream>
+
+#include <KeyMintAidlTestBase.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <keymaster/UniquePtr.h>
+
+#include <memory>
+#include <vector>
+
+#include <hardware/keymaster_defs.h>
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/keymaster_tags.h>
+
+#include <keymaster/km_openssl/attestation_record.h>
+#include <keymaster/km_openssl/openssl_err.h>
+#include <keymaster/km_openssl/openssl_utils.h>
+#include <openssl/asn1t.h>
+
+using aidl::android::hardware::security::keymint::ErrorCode;
+
+#define TAG_SEQUENCE 0x30
+#define LENGTH_MASK 0x80
+#define LENGTH_VALUE_MASK 0x7F
+
+/**
+ * ASN.1 structure for `KeyDescription` Schema.
+ * See `IKeyMintDevice.aidl` for documentation of the `KeyDescription` schema.
+ *    KeyDescription ::= SEQUENCE(
+ *        keyFormat INTEGER,                   # Values from KeyFormat enum.
+ *        keyParams AuthorizationList,
+ *    )
+ */
+typedef struct key_description {
+    ASN1_INTEGER* key_format;
+    keymaster::KM_AUTH_LIST* key_params;
+} TEST_KEY_DESCRIPTION;
+
+ASN1_SEQUENCE(TEST_KEY_DESCRIPTION) = {
+    ASN1_SIMPLE(TEST_KEY_DESCRIPTION, key_format, ASN1_INTEGER),
+    ASN1_SIMPLE(TEST_KEY_DESCRIPTION, key_params, keymaster::KM_AUTH_LIST),
+} ASN1_SEQUENCE_END(TEST_KEY_DESCRIPTION);
+DECLARE_ASN1_FUNCTIONS(TEST_KEY_DESCRIPTION);
+
+/**
+ * ASN.1 structure for `SecureKeyWrapper` Schema.
+ * See `IKeyMintDevice.aidl` for documentation of the `SecureKeyWrapper` schema.
+ *    SecureKeyWrapper ::= SEQUENCE(
+ *        version INTEGER,                     # Contains value 0
+ *        encryptedTransportKey OCTET_STRING,
+ *        initializationVector OCTET_STRING,
+ *        keyDescription KeyDescription,
+ *        encryptedKey OCTET_STRING,
+ *        tag OCTET_STRING
+ *    )
+ */
+typedef struct secure_key_wrapper {
+    ASN1_INTEGER* version;
+    ASN1_OCTET_STRING* encrypted_transport_key;
+    ASN1_OCTET_STRING* initialization_vector;
+    TEST_KEY_DESCRIPTION* key_desc;
+    ASN1_OCTET_STRING* encrypted_key;
+    ASN1_OCTET_STRING* tag;
+} TEST_SECURE_KEY_WRAPPER;
+
+ASN1_SEQUENCE(TEST_SECURE_KEY_WRAPPER) = {
+    ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, version, ASN1_INTEGER),
+    ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, encrypted_transport_key, ASN1_OCTET_STRING),
+    ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, initialization_vector, ASN1_OCTET_STRING),
+    ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, key_desc, TEST_KEY_DESCRIPTION),
+    ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, encrypted_key, ASN1_OCTET_STRING),
+    ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, tag, ASN1_OCTET_STRING),
+} ASN1_SEQUENCE_END(TEST_SECURE_KEY_WRAPPER);
+DECLARE_ASN1_FUNCTIONS(TEST_SECURE_KEY_WRAPPER);
+
+IMPLEMENT_ASN1_FUNCTIONS(TEST_SECURE_KEY_WRAPPER);
+IMPLEMENT_ASN1_FUNCTIONS(TEST_KEY_DESCRIPTION);
+
+struct TEST_KEY_DESCRIPTION_Delete {
+    void operator()(TEST_KEY_DESCRIPTION* p) { TEST_KEY_DESCRIPTION_free(p); }
+};
+struct TEST_SECURE_KEY_WRAPPER_Delete {
+    void operator()(TEST_SECURE_KEY_WRAPPER* p) { TEST_SECURE_KEY_WRAPPER_free(p); }
+};
+
+/* This function extracts a certificate from the certs_chain_buffer at the given
+ * offset. Each DER encoded certificate starts with TAG_SEQUENCE followed by the
+ * total length of the certificate. The length of the certificate is determined
+ * as per ASN.1 encoding rules for the length octets.
+ *
+ * @param certs_chain_buffer: buffer containing DER encoded X.509 certificates
+ *                            arranged sequentially.
+ * @data_size: Length of the DER encoded X.509 certificates buffer.
+ * @index: DER encoded X.509 certificates buffer offset.
+ * @cert: Encoded certificate to be extracted from buffer as outcome.
+ * @return: ErrorCode::OK on success, otherwise ErrorCode::UNKNOWN_ERROR.
+ */
+ErrorCode
+extractCertFromCertChainBuffer(uint8_t* certs_chain_buffer, int certs_chain_buffer_size, int& index,
+                               aidl::android::hardware::security::keymint::Certificate& cert) {
+    if (index >= certs_chain_buffer_size) {
+        return ErrorCode::UNKNOWN_ERROR;
+    }
+
+    uint32_t length = 0;
+    std::vector<uint8_t> cert_bytes;
+    if (certs_chain_buffer[index] == TAG_SEQUENCE) {
+        // Short form. One octet. Bit 8 has value "0" and bits 7-1 give the length.
+        if (0 == (certs_chain_buffer[index + 1] & LENGTH_MASK)) {
+            length = (uint32_t)certs_chain_buffer[index];
+            // Add SEQ and Length fields
+            length += 2;
+        } else {
+            // Long form. Two to 127 octets. Bit 8 of first octet has value "1" and
+            // bits 7-1 give the number of additional length octets. Second and following
+            // octets give the actual length.
+            int additionalBytes = certs_chain_buffer[index + 1] & LENGTH_VALUE_MASK;
+            if (additionalBytes == 0x01) {
+                length = certs_chain_buffer[index + 2];
+                // Add SEQ and Length fields
+                length += 3;
+            } else if (additionalBytes == 0x02) {
+                length = (certs_chain_buffer[index + 2] << 8 | certs_chain_buffer[index + 3]);
+                // Add SEQ and Length fields
+                length += 4;
+            } else if (additionalBytes == 0x04) {
+                length = certs_chain_buffer[index + 2] << 24;
+                length |= certs_chain_buffer[index + 3] << 16;
+                length |= certs_chain_buffer[index + 4] << 8;
+                length |= certs_chain_buffer[index + 5];
+                // Add SEQ and Length fields
+                length += 6;
+            } else {
+                // Length is larger than uint32_t max limit.
+                return ErrorCode::UNKNOWN_ERROR;
+            }
+        }
+        cert_bytes.insert(cert_bytes.end(), (certs_chain_buffer + index),
+                          (certs_chain_buffer + index + length));
+        index += length;
+
+        for (int i = 0; i < cert_bytes.size(); i++) {
+            cert.encodedCertificate = std::move(cert_bytes);
+        }
+    } else {
+        // SEQUENCE TAG MISSING.
+        return ErrorCode::UNKNOWN_ERROR;
+    }
+
+    return ErrorCode::OK;
+}
+
+ErrorCode getCertificateChain(
+    rust::Vec<rust::u8>& chainBuffer,
+    std::vector<aidl::android::hardware::security::keymint::Certificate>& certChain) {
+    uint8_t* data = chainBuffer.data();
+    int index = 0;
+    int data_size = chainBuffer.size();
+
+    while (index < data_size) {
+        aidl::android::hardware::security::keymint::Certificate cert =
+            aidl::android::hardware::security::keymint::Certificate();
+        if (extractCertFromCertChainBuffer(data, data_size, index, cert) != ErrorCode::OK) {
+            return ErrorCode::UNKNOWN_ERROR;
+        }
+        certChain.push_back(std::move(cert));
+    }
+    return ErrorCode::OK;
+}
+
+bool validateCertChain(rust::Vec<rust::u8> cert_buf, uint32_t cert_len, bool strict_issuer_check) {
+    std::vector<aidl::android::hardware::security::keymint::Certificate> cert_chain =
+        std::vector<aidl::android::hardware::security::keymint::Certificate>();
+    if (cert_len <= 0) {
+        return false;
+    }
+    if (getCertificateChain(cert_buf, cert_chain) != ErrorCode::OK) {
+        return false;
+    }
+
+    for (int i = 0; i < cert_chain.size(); i++) {
+        std::cout << cert_chain[i].toString() << "\n";
+    }
+    auto result = aidl::android::hardware::security::keymint::test::ChainSignaturesAreValid(
+        cert_chain, strict_issuer_check);
+
+    if (result == testing::AssertionSuccess()) return true;
+
+    return false;
+}
+
+/**
+ * Below mentioned key parameters are used to create authorization list of
+ * secure key.
+ *    Algorithm: AES-256
+ *    Padding: PKCS7
+ *    Blockmode: ECB
+ *    Purpose: Encrypt, Decrypt
+ */
+keymaster::AuthorizationSet build_wrapped_key_auth_list() {
+    return keymaster::AuthorizationSet(keymaster::AuthorizationSetBuilder()
+                                           .AesEncryptionKey(256)
+                                           .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_ECB)
+                                           .Authorization(keymaster::TAG_PADDING, KM_PAD_PKCS7)
+                                           .Authorization(keymaster::TAG_NO_AUTH_REQUIRED));
+}
+
+/**
+ * Creates ASN.1 DER-encoded data corresponding to `KeyDescription` schema as
+ * AAD. See `IKeyMintDevice.aidl` for documentation of the `KeyDescription` schema.
+ */
+CxxResult buildAsn1DerEncodedWrappedKeyDescription() {
+    CxxResult cxx_result{};
+    keymaster_error_t error;
+    cxx_result.error = KM_ERROR_OK;
+
+    keymaster::UniquePtr<TEST_KEY_DESCRIPTION, TEST_KEY_DESCRIPTION_Delete> key_description(
+        TEST_KEY_DESCRIPTION_new());
+    if (!key_description.get()) {
+        cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        return cxx_result;
+    }
+
+    // Fill secure key authorizations.
+    keymaster::AuthorizationSet auth_list = build_wrapped_key_auth_list();
+    error = build_auth_list(auth_list, key_description->key_params);
+    if (error != KM_ERROR_OK) {
+        cxx_result.error = error;
+        return cxx_result;
+    }
+
+    // Fill secure key format.
+    if (!ASN1_INTEGER_set(key_description->key_format, KM_KEY_FORMAT_RAW)) {
+        cxx_result.error = keymaster::TranslateLastOpenSslError();
+        return cxx_result;
+    }
+
+    // Perform ASN.1 DER encoding of KeyDescription.
+    int asn1_data_len = i2d_TEST_KEY_DESCRIPTION(key_description.get(), nullptr);
+    if (asn1_data_len < 0) {
+        cxx_result.error = keymaster::TranslateLastOpenSslError();
+        return cxx_result;
+    }
+    std::vector<uint8_t> asn1_data(asn1_data_len, 0);
+
+    if (!asn1_data.data()) {
+        cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        return cxx_result;
+    }
+
+    uint8_t* p = asn1_data.data();
+    asn1_data_len = i2d_TEST_KEY_DESCRIPTION(key_description.get(), &p);
+    if (asn1_data_len < 0) {
+        cxx_result.error = keymaster::TranslateLastOpenSslError();
+        return cxx_result;
+    }
+
+    std::move(asn1_data.begin(), asn1_data.end(), std::back_inserter(cxx_result.data));
+
+    return cxx_result;
+}
+
+/**
+ * Creates wrapped key material to import in ASN.1 DER-encoded data corresponding to
+ * `SecureKeyWrapper` schema. See `IKeyMintDevice.aidl` for documentation of the `SecureKeyWrapper`
+ * schema.
+ */
+CxxResult createWrappedKey(rust::Vec<rust::u8> encrypted_secure_key,
+                           rust::Vec<rust::u8> encrypted_transport_key, rust::Vec<rust::u8> iv,
+                           rust::Vec<rust::u8> tag) {
+    CxxResult cxx_result{};
+    keymaster_error_t error;
+    cxx_result.error = KM_ERROR_OK;
+
+    uint8_t* enc_secure_key_data = encrypted_secure_key.data();
+    int enc_secure_key_size = encrypted_secure_key.size();
+
+    uint8_t* iv_data = iv.data();
+    int iv_size = iv.size();
+
+    uint8_t* tag_data = tag.data();
+    int tag_size = tag.size();
+
+    uint8_t* enc_transport_key_data = encrypted_transport_key.data();
+    int enc_transport_key_size = encrypted_transport_key.size();
+
+    keymaster::UniquePtr<TEST_SECURE_KEY_WRAPPER, TEST_SECURE_KEY_WRAPPER_Delete> sec_key_wrapper(
+        TEST_SECURE_KEY_WRAPPER_new());
+    if (!sec_key_wrapper.get()) {
+        cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        return cxx_result;
+    }
+
+    // Fill version = 0
+    if (!ASN1_INTEGER_set(sec_key_wrapper->version, 0)) {
+        cxx_result.error = keymaster::TranslateLastOpenSslError();
+        return cxx_result;
+    }
+
+    // Fill encrypted transport key.
+    if (enc_transport_key_size &&
+        !ASN1_OCTET_STRING_set(sec_key_wrapper->encrypted_transport_key, enc_transport_key_data,
+                               enc_transport_key_size)) {
+        cxx_result.error = keymaster::TranslateLastOpenSslError();
+        return cxx_result;
+    }
+
+    // Fill encrypted secure key.
+    if (enc_secure_key_size && !ASN1_OCTET_STRING_set(sec_key_wrapper->encrypted_key,
+                                                      enc_secure_key_data, enc_secure_key_size)) {
+        cxx_result.error = keymaster::TranslateLastOpenSslError();
+        return cxx_result;
+    }
+
+    // Fill secure key authorization list.
+    keymaster::AuthorizationSet auth_list = build_wrapped_key_auth_list();
+    error = build_auth_list(auth_list, sec_key_wrapper->key_desc->key_params);
+    if (error != KM_ERROR_OK) {
+        cxx_result.error = error;
+        return cxx_result;
+    }
+
+    // Fill secure key format.
+    if (!ASN1_INTEGER_set(sec_key_wrapper->key_desc->key_format, KM_KEY_FORMAT_RAW)) {
+        cxx_result.error = keymaster::TranslateLastOpenSslError();
+        return cxx_result;
+    }
+
+    // Fill initialization vector used for encrypting secure key.
+    if (iv_size &&
+        !ASN1_OCTET_STRING_set(sec_key_wrapper->initialization_vector, iv_data, iv_size)) {
+        cxx_result.error = keymaster::TranslateLastOpenSslError();
+        return cxx_result;
+    }
+
+    // Fill GCM-tag, extracted during secure key encryption.
+    if (tag_size && !ASN1_OCTET_STRING_set(sec_key_wrapper->tag, tag_data, tag_size)) {
+        cxx_result.error = keymaster::TranslateLastOpenSslError();
+        return cxx_result;
+    }
+
+    // ASN.1 DER-encoding of secure key wrapper.
+    int asn1_data_len = i2d_TEST_SECURE_KEY_WRAPPER(sec_key_wrapper.get(), nullptr);
+    if (asn1_data_len < 0) {
+        cxx_result.error = keymaster::TranslateLastOpenSslError();
+        return cxx_result;
+    }
+    std::vector<uint8_t> asn1_data(asn1_data_len, 0);
+
+    if (!asn1_data.data()) {
+        cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        return cxx_result;
+    }
+
+    uint8_t* p = asn1_data.data();
+    asn1_data_len = i2d_TEST_SECURE_KEY_WRAPPER(sec_key_wrapper.get(), &p);
+    if (asn1_data_len < 0) {
+        cxx_result.error = keymaster::TranslateLastOpenSslError();
+        return cxx_result;
+    }
+
+    std::move(asn1_data.begin(), asn1_data.end(), std::back_inserter(cxx_result.data));
+
+    return cxx_result;
+}
diff --git a/keystore2/tests/ffi_test_utils.hpp b/keystore2/tests/ffi_test_utils.hpp
new file mode 100644
index 0000000..b8c7c48
--- /dev/null
+++ b/keystore2/tests/ffi_test_utils.hpp
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "rust/cxx.h"
+#include "ffi_test_utils.rs.h"
+
+bool validateCertChain(rust::Vec<rust::u8> cert_buf, uint32_t cert_len, bool strict_issuer_check);
+CxxResult createWrappedKey(rust::Vec<rust::u8> encrypted_secure_key,
+                              rust::Vec<rust::u8> encrypted_transport_key,
+                              rust::Vec<rust::u8> iv,
+                              rust::Vec<rust::u8> tag);
+CxxResult buildAsn1DerEncodedWrappedKeyDescription();
diff --git a/keystore2/tests/ffi_test_utils.rs b/keystore2/tests/ffi_test_utils.rs
new file mode 100644
index 0000000..066d4a1
--- /dev/null
+++ b/keystore2/tests/ffi_test_utils.rs
@@ -0,0 +1,80 @@
+// Copyright 2022, 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_test_utils::key_generations::Error;
+
+#[cxx::bridge]
+mod ffi {
+    struct CxxResult {
+        data: Vec<u8>,
+        error: i32,
+    }
+
+    unsafe extern "C++" {
+        include!("ffi_test_utils.hpp");
+        fn validateCertChain(cert_buf: Vec<u8>, cert_len: u32, strict_issuer_check: bool) -> bool;
+        fn createWrappedKey(
+            encrypted_secure_key: Vec<u8>,
+            encrypted_transport_key: Vec<u8>,
+            iv: Vec<u8>,
+            tag: Vec<u8>,
+        ) -> CxxResult;
+        fn buildAsn1DerEncodedWrappedKeyDescription() -> CxxResult;
+    }
+}
+
+/// Validate given certificate chain.
+pub fn validate_certchain(cert_buf: &[u8]) -> Result<bool, Error> {
+    if ffi::validateCertChain(cert_buf.to_vec(), cert_buf.len().try_into().unwrap(), true) {
+        return Ok(true);
+    }
+
+    Err(Error::ValidateCertChainFailed)
+}
+
+fn get_result(result: ffi::CxxResult) -> Result<Vec<u8>, Error> {
+    if result.error == 0 && !result.data.is_empty() {
+        Ok(result.data)
+    } else {
+        Err(Error::DerEncodeFailed)
+    }
+}
+
+/// Creates wrapped key material to import in ASN.1 DER-encoded data corresponding to
+/// `SecureKeyWrapper`. See `IKeyMintDevice.aidl` for documentation of the `SecureKeyWrapper`
+/// schema.
+pub fn create_wrapped_key(
+    encrypted_secure_key: &[u8],
+    encrypted_transport_key: &[u8],
+    iv: &[u8],
+    tag: &[u8],
+) -> Result<Vec<u8>, Error> {
+    get_result(ffi::createWrappedKey(
+        encrypted_secure_key.to_vec(),
+        encrypted_transport_key.to_vec(),
+        iv.to_vec(),
+        tag.to_vec(),
+    ))
+}
+
+/// Creates ASN.1 DER-encoded data corresponding to `KeyDescription` schema.
+/// See `IKeyMintDevice.aidl` for documentation of the `KeyDescription` schema.
+/// Below mentioned key parameters are used -
+///     Algorithm: AES-256
+///     Padding: PKCS7
+///     Blockmode: ECB
+///     Purpose: Encrypt, Decrypt
+pub fn create_wrapped_key_additional_auth_data() -> Result<Vec<u8>, Error> {
+    get_result(ffi::buildAsn1DerEncodedWrappedKeyDescription())
+}
diff --git a/keystore2/tests/keystore2_client_3des_key_tests.rs b/keystore2/tests/keystore2_client_3des_key_tests.rs
new file mode 100644
index 0000000..eda24db
--- /dev/null
+++ b/keystore2/tests/keystore2_client_3des_key_tests.rs
@@ -0,0 +1,218 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, BlockMode::BlockMode, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose,
+    PaddingMode::PaddingMode, SecurityLevel::SecurityLevel,
+};
+
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+};
+
+use keystore2_test_utils::{
+    authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+use crate::keystore2_client_test_utils::{
+    perform_sample_sym_key_decrypt_op, perform_sample_sym_key_encrypt_op, SAMPLE_PLAIN_TEXT,
+};
+
+/// Generate a 3DES key. Create encryption and decryption operations using the generated key.
+fn create_3des_key_and_operation(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    padding_mode: PaddingMode,
+    block_mode: BlockMode,
+    nonce: &mut Option<Vec<u8>>,
+) -> Result<(), binder::Status> {
+    let alias = format!("ks_3des_test_key_{}{}", block_mode.0, padding_mode.0);
+
+    let key_metadata = key_generations::generate_sym_key(
+        sec_level,
+        Algorithm::TRIPLE_DES,
+        168,
+        &alias,
+        &padding_mode,
+        &block_mode,
+        None,
+    )?;
+
+    // Encrypts `SAMPLE_PLAIN_TEXT` whose length is multiple of DES block size.
+    let cipher_text = perform_sample_sym_key_encrypt_op(
+        sec_level,
+        padding_mode,
+        block_mode,
+        nonce,
+        None,
+        &key_metadata.key,
+    )?;
+    assert!(cipher_text.is_some());
+
+    let plain_text = perform_sample_sym_key_decrypt_op(
+        sec_level,
+        &cipher_text.unwrap(),
+        padding_mode,
+        block_mode,
+        nonce,
+        None,
+        &key_metadata.key,
+    )
+    .unwrap();
+    assert!(plain_text.is_some());
+    assert_eq!(plain_text.unwrap(), SAMPLE_PLAIN_TEXT.to_vec());
+    Ok(())
+}
+
+/// Generate 3DES keys with various block modes and paddings.
+///  - Block Modes: ECB, CBC
+///  - Padding Modes: NONE, PKCS7
+/// Test should generate keys and perform operation successfully.
+#[test]
+fn keystore2_3des_ecb_cbc_generate_key_success() {
+    let keystore2 = get_keystore_service();
+    let block_modes = [BlockMode::ECB, BlockMode::CBC];
+    let padding_modes = [PaddingMode::PKCS7, PaddingMode::NONE];
+
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    for block_mode in block_modes {
+        for padding_mode in padding_modes {
+            assert_eq!(
+                Ok(()),
+                create_3des_key_and_operation(&sec_level, padding_mode, block_mode, &mut None)
+            );
+        }
+    }
+}
+
+/// Try to generate 3DES key with invalid key size. Test should fail to generate a key with
+/// an error code `UNSUPPORTED_KEY_SIZE`.
+#[test]
+fn keystore2_3des_key_fails_unsupported_key_size() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = "3des_key_test_invalid_1";
+    let invalid_key_size = 128;
+
+    let result = key_generations::map_ks_error(key_generations::generate_sym_key(
+        &sec_level,
+        Algorithm::TRIPLE_DES,
+        invalid_key_size,
+        alias,
+        &PaddingMode::PKCS7,
+        &BlockMode::CBC,
+        None,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_KEY_SIZE), result.unwrap_err());
+}
+
+/// Generate a 3DES key without providing padding mode and try to use the generated key to create
+/// an operation. Test should fail to create an operation with an error code
+/// `UNSUPPORTED_PADDING_MODE`.
+#[test]
+fn keystore2_3des_key_fails_missing_padding() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = "3des_key_test_missing_padding";
+
+    let gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::TRIPLE_DES)
+        .purpose(KeyPurpose::ENCRYPT)
+        .purpose(KeyPurpose::DECRYPT)
+        .key_size(168)
+        .block_mode(BlockMode::ECB);
+
+    let key_metadata = sec_level
+        .generateKey(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: -1,
+                alias: Some(alias.to_string()),
+                blob: None,
+            },
+            None,
+            &gen_params,
+            0,
+            b"entropy",
+        )
+        .unwrap();
+
+    let op_params = authorizations::AuthSetBuilder::new()
+        .purpose(KeyPurpose::ENCRYPT)
+        .block_mode(BlockMode::ECB);
+
+    let result = key_generations::map_ks_error(sec_level.createOperation(
+        &key_metadata.key,
+        &op_params,
+        false,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_PADDING_MODE), result.unwrap_err());
+}
+
+/// Generate a 3DES key with padding mode NONE. Try to encrypt a text whose length isn't a
+/// multiple of the DES block size.
+#[test]
+fn keystore2_3des_key_encrypt_fails_invalid_input_length() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = "3des_key_test_invalid_input_len";
+
+    let key_metadata = key_generations::generate_sym_key(
+        &sec_level,
+        Algorithm::TRIPLE_DES,
+        168,
+        alias,
+        &PaddingMode::NONE,
+        &BlockMode::ECB,
+        None,
+    )
+    .unwrap();
+
+    let op_params = authorizations::AuthSetBuilder::new()
+        .purpose(KeyPurpose::ENCRYPT)
+        .padding_mode(PaddingMode::NONE)
+        .block_mode(BlockMode::ECB);
+
+    let op_response = sec_level
+        .createOperation(&key_metadata.key, &op_params, false)
+        .expect("Error in creation of operation using rebound key.");
+    assert!(op_response.iOperation.is_some());
+
+    let op = op_response.iOperation.unwrap();
+    // 3DES expects input should be multiple of DES block size (64-bits) length. Try with invalid
+    // length of input.
+    let invalid_block_size_msg = b"my message 111";
+    let result = key_generations::map_ks_error(op.finish(Some(invalid_block_size_msg), None));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INVALID_INPUT_LENGTH), result.unwrap_err());
+}
+
+/// Try to generate 3DES key with BlockMode::CTR. Test should fail to create an operation with an
+/// error code `UNSUPPORTED_BLOCK_MODE`.
+#[test]
+fn keystore2_3des_key_fails_unsupported_block_mode() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let result = key_generations::map_ks_error(create_3des_key_and_operation(
+        &sec_level,
+        PaddingMode::NONE,
+        BlockMode::CTR,
+        &mut None,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_BLOCK_MODE), result.unwrap_err());
+}
diff --git a/keystore2/tests/keystore2_client_aes_key_tests.rs b/keystore2/tests/keystore2_client_aes_key_tests.rs
new file mode 100644
index 0000000..313f596
--- /dev/null
+++ b/keystore2/tests/keystore2_client_aes_key_tests.rs
@@ -0,0 +1,477 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, BlockMode::BlockMode, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose,
+    PaddingMode::PaddingMode, SecurityLevel::SecurityLevel,
+};
+
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+};
+
+use keystore2_test_utils::{
+    authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+use crate::keystore2_client_test_utils::{
+    perform_sample_sym_key_decrypt_op, perform_sample_sym_key_encrypt_op, SAMPLE_PLAIN_TEXT,
+};
+
+/// Generate a AES key. Create encrypt and decrypt operations using the generated key.
+fn create_aes_key_and_operation(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    key_size: i32,
+    padding_mode: PaddingMode,
+    block_mode: BlockMode,
+    mac_len: Option<i32>,
+    min_mac_len: Option<i32>,
+    nonce: &mut Option<Vec<u8>>,
+) -> Result<(), binder::Status> {
+    let alias = format!("ks_aes_test_key_{}{}{}", key_size, block_mode.0, padding_mode.0);
+
+    let key_metadata = key_generations::generate_sym_key(
+        sec_level,
+        Algorithm::AES,
+        key_size,
+        &alias,
+        &padding_mode,
+        &block_mode,
+        min_mac_len,
+    )?;
+
+    let cipher_text = perform_sample_sym_key_encrypt_op(
+        sec_level,
+        padding_mode,
+        block_mode,
+        nonce,
+        mac_len,
+        &key_metadata.key,
+    )?;
+
+    assert!(cipher_text.is_some());
+
+    let plain_text = perform_sample_sym_key_decrypt_op(
+        sec_level,
+        &cipher_text.unwrap(),
+        padding_mode,
+        block_mode,
+        nonce,
+        mac_len,
+        &key_metadata.key,
+    )
+    .unwrap();
+    assert!(plain_text.is_some());
+    assert_eq!(plain_text.unwrap(), SAMPLE_PLAIN_TEXT.to_vec());
+    Ok(())
+}
+
+/// Generate AES keys with various block modes and paddings.
+///  - Block Modes: ECB, CBC
+///  - Padding Modes: NONE, PKCS7
+/// Test should generate keys and perform operation successfully.
+#[test]
+fn keystore2_aes_ecb_cbc_generate_key() {
+    let keystore2 = get_keystore_service();
+    let key_sizes = [128, 256];
+    let block_modes = [BlockMode::ECB, BlockMode::CBC];
+    let padding_modes = [PaddingMode::PKCS7, PaddingMode::NONE];
+
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    for key_size in key_sizes {
+        for block_mode in block_modes {
+            for padding_mode in padding_modes {
+                assert_eq!(
+                    Ok(()),
+                    create_aes_key_and_operation(
+                        &sec_level,
+                        key_size,
+                        padding_mode,
+                        block_mode,
+                        None,
+                        None,
+                        &mut None,
+                    )
+                );
+            }
+        }
+    }
+}
+
+/// Generate AES keys with -
+///  - Block Modes: `CTR, GCM`
+///  - Padding Modes: `NONE`
+/// Test should generate keys and perform operation successfully.
+#[test]
+fn keystore2_aes_ctr_gcm_generate_key_success() {
+    let keystore2 = get_keystore_service();
+    let key_sizes = [128, 256];
+    let key_params = [(BlockMode::CTR, None, None), (BlockMode::GCM, Some(128), Some(128))];
+
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    for key_size in key_sizes {
+        for (block_mode, mac_len, min_mac_len) in key_params {
+            let result = key_generations::map_ks_error(create_aes_key_and_operation(
+                &sec_level,
+                key_size,
+                PaddingMode::NONE,
+                block_mode,
+                mac_len,
+                min_mac_len,
+                &mut None,
+            ));
+
+            assert_eq!(Ok(()), result);
+        } // End of block mode.
+    } // End of key size.
+}
+
+/// Generate AES keys with -
+///  - Block Modes: `CTR, GCM`
+///  - Padding Modes: `PKCS7`
+/// Try to create an operation using generated keys, test should fail to create an operation
+/// with an error code `INCOMPATIBLE_PADDING_MODE`.
+#[test]
+fn keystore2_aes_ctr_gcm_generate_key_fails_incompatible() {
+    let keystore2 = get_keystore_service();
+    let key_sizes = [128, 256];
+    let key_params = [(BlockMode::CTR, None, None), (BlockMode::GCM, Some(128), Some(128))];
+
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    for key_size in key_sizes {
+        for (block_mode, mac_len, min_mac_len) in key_params {
+            let result = key_generations::map_ks_error(create_aes_key_and_operation(
+                &sec_level,
+                key_size,
+                PaddingMode::PKCS7,
+                block_mode,
+                mac_len,
+                min_mac_len,
+                &mut None,
+            ));
+
+            assert!(result.is_err());
+            assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PADDING_MODE), result.unwrap_err());
+        } // End of block mode.
+    } // End of key size.
+}
+
+/// Try to generate AES key with invalid key size. Test should fail to generate a key with
+/// an error code `UNSUPPORTED_KEY_SIZE`.
+#[test]
+fn keystore2_aes_key_fails_unsupported_key_size() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = "aes_key_test_invalid_1";
+
+    let result = key_generations::map_ks_error(key_generations::generate_sym_key(
+        &sec_level,
+        Algorithm::AES,
+        1024,
+        alias,
+        &PaddingMode::NONE,
+        &BlockMode::ECB,
+        None,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_KEY_SIZE), result.unwrap_err());
+}
+
+/// Try to generate AES key with GCM block mode without providing `MIN_MAC_LENGTH`.
+/// Test should fail to generate a key with an error code `MISSING_MIN_MAC_LENGTH`.
+#[test]
+fn keystore2_aes_gcm_key_fails_missing_min_mac_len() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = "aes_key_test_invalid_1";
+
+    let result = key_generations::map_ks_error(key_generations::generate_sym_key(
+        &sec_level,
+        Algorithm::AES,
+        128,
+        alias,
+        &PaddingMode::NONE,
+        &BlockMode::GCM,
+        None,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::MISSING_MIN_MAC_LENGTH), result.unwrap_err());
+}
+
+/// Try to create an operation using AES key with multiple block modes. Test should fail to create
+/// an operation with `UNSUPPORTED_BLOCK_MODE` error code.
+#[test]
+fn keystore2_aes_key_op_fails_multi_block_modes() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = "aes_key_test_invalid_1";
+
+    let gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::AES)
+        .purpose(KeyPurpose::ENCRYPT)
+        .purpose(KeyPurpose::DECRYPT)
+        .key_size(128)
+        .block_mode(BlockMode::ECB)
+        .block_mode(BlockMode::CBC)
+        .padding_mode(PaddingMode::NONE);
+
+    let key_metadata = sec_level
+        .generateKey(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: -1,
+                alias: Some(alias.to_string()),
+                blob: None,
+            },
+            None,
+            &gen_params,
+            0,
+            b"entropy",
+        )
+        .unwrap();
+
+    let op_params = authorizations::AuthSetBuilder::new()
+        .purpose(KeyPurpose::ENCRYPT)
+        .block_mode(BlockMode::ECB)
+        .block_mode(BlockMode::CBC)
+        .padding_mode(PaddingMode::NONE);
+
+    let result = key_generations::map_ks_error(sec_level.createOperation(
+        &key_metadata.key,
+        &op_params,
+        false,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_BLOCK_MODE), result.unwrap_err());
+}
+
+/// Try to create an operation using AES key with multiple padding modes. Test should fail to create
+/// an operation with `UNSUPPORTED_PADDING_MODE` error code.
+#[test]
+fn keystore2_aes_key_op_fails_multi_padding_modes() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = "aes_key_test_invalid_1";
+
+    let gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::AES)
+        .purpose(KeyPurpose::ENCRYPT)
+        .purpose(KeyPurpose::DECRYPT)
+        .key_size(128)
+        .block_mode(BlockMode::ECB)
+        .padding_mode(PaddingMode::PKCS7)
+        .padding_mode(PaddingMode::NONE);
+
+    let key_metadata = sec_level
+        .generateKey(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: -1,
+                alias: Some(alias.to_string()),
+                blob: None,
+            },
+            None,
+            &gen_params,
+            0,
+            b"entropy",
+        )
+        .unwrap();
+
+    let op_params = authorizations::AuthSetBuilder::new()
+        .purpose(KeyPurpose::ENCRYPT)
+        .block_mode(BlockMode::ECB)
+        .padding_mode(PaddingMode::PKCS7)
+        .padding_mode(PaddingMode::NONE);
+
+    let result = key_generations::map_ks_error(sec_level.createOperation(
+        &key_metadata.key,
+        &op_params,
+        false,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_PADDING_MODE), result.unwrap_err());
+}
+
+/// Generate a AES-ECB key with unpadded mode. Try to create an operation using generated key
+/// with PKCS7 padding mode. Test should fail to create an Operation with
+/// `INCOMPATIBLE_PADDING_MODE` error code.
+#[test]
+fn keystore2_aes_key_op_fails_incompatible_padding() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = "aes_key_test_invalid_1";
+
+    let key_metadata = key_generations::generate_sym_key(
+        &sec_level,
+        Algorithm::AES,
+        128,
+        alias,
+        &PaddingMode::NONE,
+        &BlockMode::ECB,
+        None,
+    )
+    .unwrap();
+
+    let result = key_generations::map_ks_error(perform_sample_sym_key_encrypt_op(
+        &sec_level,
+        PaddingMode::PKCS7,
+        BlockMode::ECB,
+        &mut None,
+        None,
+        &key_metadata.key,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PADDING_MODE), result.unwrap_err());
+}
+
+/// Generate a AES-ECB key with unpadded mode. Try to create an operation using generated key
+/// with CBC block mode. Test should fail to create an Operation with
+/// `INCOMPATIBLE_BLOCK_MODE` error code.
+#[test]
+fn keystore2_aes_key_op_fails_incompatible_blockmode() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = "aes_key_test_invalid_1";
+
+    let key_metadata = key_generations::generate_sym_key(
+        &sec_level,
+        Algorithm::AES,
+        128,
+        alias,
+        &PaddingMode::NONE,
+        &BlockMode::ECB,
+        None,
+    )
+    .unwrap();
+
+    let result = key_generations::map_ks_error(perform_sample_sym_key_encrypt_op(
+        &sec_level,
+        PaddingMode::NONE,
+        BlockMode::CBC,
+        &mut None,
+        None,
+        &key_metadata.key,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_BLOCK_MODE), result.unwrap_err());
+}
+
+/// Generate a AES-GCM key with `MIN_MAC_LENGTH`. Try to create an operation using this
+/// generated key without providing `MAC_LENGTH`. Test should fail to create an operation with
+/// `MISSING_MAC_LENGTH` error code.
+#[test]
+fn keystore2_aes_gcm_op_fails_missing_mac_len() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let mac_len = None;
+    let min_mac_len = Some(128);
+
+    let result = key_generations::map_ks_error(create_aes_key_and_operation(
+        &sec_level,
+        128,
+        PaddingMode::NONE,
+        BlockMode::GCM,
+        mac_len,
+        min_mac_len,
+        &mut None,
+    ));
+    assert!(result.is_err());
+
+    let e = result.unwrap_err();
+    assert!(
+        e == Error::Km(ErrorCode::MISSING_MAC_LENGTH)
+            || e == Error::Km(ErrorCode::UNSUPPORTED_MAC_LENGTH)
+    );
+}
+
+/// Generate a AES-GCM key with `MIN_MAC_LENGTH`. Try to create an operation using this
+/// generated key and  provide `MAC_LENGTH` < key's `MIN_MAC_LENGTH`. Test should fail to create
+/// an operation with `INVALID_MAC_LENGTH` error code.
+#[test]
+fn keystore2_aes_gcm_op_fails_invalid_mac_len() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let mac_len = Some(96);
+    let min_mac_len = Some(104);
+
+    let result = key_generations::map_ks_error(create_aes_key_and_operation(
+        &sec_level,
+        128,
+        PaddingMode::NONE,
+        BlockMode::GCM,
+        mac_len,
+        min_mac_len,
+        &mut None,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INVALID_MAC_LENGTH), result.unwrap_err());
+}
+
+/// Generate a AES-GCM key with `MIN_MAC_LENGTH`. Try to create an operation using this
+/// generated key and  provide `MAC_LENGTH` > 128. Test should fail to create an operation with
+/// `UNSUPPORTED_MAC_LENGTH` error code.
+#[test]
+fn keystore2_aes_gcm_op_fails_unsupported_mac_len() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let result = key_generations::map_ks_error(create_aes_key_and_operation(
+        &sec_level,
+        128,
+        PaddingMode::NONE,
+        BlockMode::GCM,
+        Some(256),
+        Some(128),
+        &mut None,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_MAC_LENGTH), result.unwrap_err());
+}
+
+/// Generate a AES-CBC-PKCS7 key without `CALLER_NONCE` authorization. Try to set nonce while
+/// creating an operation using this generated key. Test should fail to create an operation with
+/// `CALLER_NONCE_PROHIBITED` error code.
+#[test]
+fn keystore2_aes_key_op_fails_nonce_prohibited() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = "aes_key_test_nonce_1";
+    let mut nonce = Some(vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+    let key_metadata = key_generations::generate_sym_key(
+        &sec_level,
+        Algorithm::AES,
+        128,
+        alias,
+        &PaddingMode::PKCS7,
+        &BlockMode::CBC,
+        None,
+    )
+    .unwrap();
+
+    let result = key_generations::map_ks_error(perform_sample_sym_key_encrypt_op(
+        &sec_level,
+        PaddingMode::NONE,
+        BlockMode::CBC,
+        &mut nonce,
+        None,
+        &key_metadata.key,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::CALLER_NONCE_PROHIBITED), result.unwrap_err());
+}
diff --git a/keystore2/tests/keystore2_client_attest_key_tests.rs b/keystore2/tests/keystore2_client_attest_key_tests.rs
new file mode 100644
index 0000000..4febd9b
--- /dev/null
+++ b/keystore2/tests/keystore2_client_attest_key_tests.rs
@@ -0,0 +1,482 @@
+// Copyright 2022, 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 nix::unistd::getuid;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+    ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+    SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{
+    authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+use crate::ffi_test_utils::validate_certchain;
+
+use crate::{
+    keystore2_client_test_utils::app_attest_key_feature_exists,
+    skip_test_if_no_app_attest_key_feature,
+};
+
+/// Generate RSA and EC attestation keys and use them for signing RSA-signing keys.
+/// Test should be able to generate attestation keys and use them successfully.
+#[test]
+fn keystore2_attest_rsa_signing_key_success() {
+    skip_test_if_no_app_attest_key_feature!();
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let att_challenge: &[u8] = b"foo";
+
+    for algo in [Algorithm::RSA, Algorithm::EC] {
+        // Create attestation key.
+        let attestation_key_metadata =
+            key_generations::generate_attestation_key(&sec_level, algo, att_challenge).unwrap();
+
+        let mut cert_chain: Vec<u8> = Vec::new();
+        cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
+        cert_chain.extend(attestation_key_metadata.certificateChain.as_ref().unwrap());
+        validate_certchain(&cert_chain).expect("Error while validating cert chain.");
+
+        // Create RSA signing key and use attestation key to sign it.
+        let sign_key_alias = format!("ks_attest_rsa_signing_key_{}", getuid());
+        let sign_key_metadata = key_generations::generate_rsa_key(
+            &sec_level,
+            Domain::APP,
+            -1,
+            Some(sign_key_alias),
+            &key_generations::KeyParams {
+                key_size: 2048,
+                purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+                padding: Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
+                digest: Some(Digest::SHA_2_256),
+                mgf_digest: None,
+                block_mode: None,
+                att_challenge: Some(att_challenge.to_vec()),
+            },
+            Some(&attestation_key_metadata.key),
+        )
+        .unwrap();
+
+        let mut cert_chain: Vec<u8> = Vec::new();
+        cert_chain.extend(sign_key_metadata.certificate.as_ref().unwrap());
+        cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
+        cert_chain.extend(attestation_key_metadata.certificateChain.as_ref().unwrap());
+        validate_certchain(&cert_chain).expect("Error while validating cert chain");
+    }
+}
+
+/// Generate RSA and EC attestation keys and use them for signing RSA encrypt/decrypt keys.
+/// Test should be able to generate attestation keys and use them successfully.
+#[test]
+fn keystore2_attest_rsa_encrypt_key_success() {
+    skip_test_if_no_app_attest_key_feature!();
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let att_challenge: &[u8] = b"foo";
+
+    for algo in [Algorithm::RSA, Algorithm::EC] {
+        // Create attestation key.
+        let attestation_key_metadata =
+            key_generations::generate_attestation_key(&sec_level, algo, att_challenge).unwrap();
+
+        let mut cert_chain: Vec<u8> = Vec::new();
+        cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
+        cert_chain.extend(attestation_key_metadata.certificateChain.as_ref().unwrap());
+        validate_certchain(&cert_chain).expect("Error while validating cert chain.");
+
+        // Create RSA encrypt/decrypt key and use attestation key to sign it.
+        let decrypt_key_alias = format!("ks_attest_rsa_encrypt_key_{}", getuid());
+        let decrypt_key_metadata = key_generations::generate_rsa_key(
+            &sec_level,
+            Domain::APP,
+            -1,
+            Some(decrypt_key_alias),
+            &key_generations::KeyParams {
+                key_size: 2048,
+                purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+                padding: Some(PaddingMode::RSA_PKCS1_1_5_ENCRYPT),
+                digest: Some(Digest::SHA_2_256),
+                mgf_digest: None,
+                block_mode: None,
+                att_challenge: Some(att_challenge.to_vec()),
+            },
+            Some(&attestation_key_metadata.key),
+        )
+        .unwrap();
+
+        let mut cert_chain: Vec<u8> = Vec::new();
+        cert_chain.extend(decrypt_key_metadata.certificate.as_ref().unwrap());
+        cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
+        cert_chain.extend(attestation_key_metadata.certificateChain.as_ref().unwrap());
+
+        validate_certchain(&cert_chain).expect("Error while validating cert chain.");
+    }
+}
+
+/// Generate RSA and EC attestation keys and use them for signing EC keys.
+/// Test should be able to generate attestation keys and use them successfully.
+#[test]
+fn keystore2_attest_ec_key_success() {
+    skip_test_if_no_app_attest_key_feature!();
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let att_challenge: &[u8] = b"foo";
+
+    for algo in [Algorithm::RSA, Algorithm::EC] {
+        // Create attestation key.
+        let attestation_key_metadata =
+            key_generations::generate_attestation_key(&sec_level, algo, att_challenge).unwrap();
+
+        let mut cert_chain: Vec<u8> = Vec::new();
+        cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
+        cert_chain.extend(attestation_key_metadata.certificateChain.as_ref().unwrap());
+        validate_certchain(&cert_chain).expect("Error while validating cert chain.");
+
+        // Create EC key and use attestation key to sign it.
+        let ec_key_alias = format!("ks_ec_attested_test_key_{}", getuid());
+        let ec_key_metadata = key_generations::generate_ec_256_attested_key(
+            &sec_level,
+            Some(ec_key_alias),
+            att_challenge,
+            &attestation_key_metadata.key,
+        )
+        .unwrap();
+
+        let mut cert_chain: Vec<u8> = Vec::new();
+        cert_chain.extend(ec_key_metadata.certificate.as_ref().unwrap());
+        cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
+        cert_chain.extend(attestation_key_metadata.certificateChain.as_ref().unwrap());
+
+        validate_certchain(&cert_chain).expect("Error while validating cert chain.");
+    }
+}
+
+/// Generate EC-CURVE_25519 attestation key and use it for signing RSA-signing keys.
+/// Test should be able to generate RSA signing key with EC-CURVE_25519 as attestation key
+/// successfully.
+#[test]
+fn keystore2_attest_rsa_signing_key_with_ec_25519_key_success() {
+    skip_test_if_no_app_attest_key_feature!();
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let att_challenge: &[u8] = b"foo";
+
+    // Create EcCurve::CURVE_25519 attestation key.
+    let attestation_key_metadata = key_generations::generate_ec_attestation_key(
+        &sec_level,
+        att_challenge,
+        Digest::NONE,
+        EcCurve::CURVE_25519,
+    )
+    .unwrap();
+
+    let mut cert_chain: Vec<u8> = Vec::new();
+    cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
+    cert_chain.extend(attestation_key_metadata.certificateChain.as_ref().unwrap());
+    validate_certchain(&cert_chain).expect("Error while validating cert chain.");
+
+    // Create RSA signing key and use attestation key to sign it.
+    let sign_key_alias = format!("ksrsa_attested_sign_test_key_{}", getuid());
+    let sign_key_metadata = key_generations::generate_rsa_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(sign_key_alias),
+        &key_generations::KeyParams {
+            key_size: 2048,
+            purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+            padding: Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
+            digest: Some(Digest::SHA_2_256),
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: Some(att_challenge.to_vec()),
+        },
+        Some(&attestation_key_metadata.key),
+    )
+    .unwrap();
+
+    let mut cert_chain: Vec<u8> = Vec::new();
+    cert_chain.extend(sign_key_metadata.certificate.as_ref().unwrap());
+    cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
+    cert_chain.extend(attestation_key_metadata.certificateChain.as_ref().unwrap());
+    validate_certchain(&cert_chain).expect("Error while validating cert chain");
+}
+
+/// Try to generate RSA attestation key with multiple purposes. Test should fail with error code
+/// `INCOMPATIBLE_PURPOSE` to generate an attestation key.
+#[test]
+fn keystore2_generate_rsa_attest_key_with_multi_purpose_fail() {
+    skip_test_if_no_app_attest_key_feature!();
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let digest = Digest::SHA_2_256;
+    let padding = PaddingMode::RSA_PKCS1_1_5_SIGN;
+    let key_size = 2048;
+
+    let attest_key_alias =
+        format!("ksrsa_attest_multipurpose_key_{}{}{}", getuid(), key_size, digest.0);
+
+    let attest_gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::RSA)
+        .purpose(KeyPurpose::ATTEST_KEY)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .digest(digest)
+        .key_size(key_size)
+        .rsa_public_exponent(65537)
+        .padding_mode(padding);
+
+    let result = key_generations::map_ks_error(sec_level.generateKey(
+        &KeyDescriptor {
+            domain: Domain::APP,
+            nspace: -1,
+            alias: Some(attest_key_alias),
+            blob: None,
+        },
+        None,
+        &attest_gen_params,
+        0,
+        b"entropy",
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+}
+
+/// Try to generate EC attestation key with multiple purposes. Test should fail with error code
+/// `INCOMPATIBLE_PURPOSE` to generate an attestation key.
+#[test]
+fn keystore2_ec_attest_key_with_multi_purpose_fail() {
+    skip_test_if_no_app_attest_key_feature!();
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let attest_key_alias = format!("ks_ec_attest_multipurpose_key_{}", getuid());
+
+    let attest_gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::EC)
+        .purpose(KeyPurpose::ATTEST_KEY)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .digest(Digest::SHA_2_256)
+        .ec_curve(EcCurve::P_256);
+
+    let result = key_generations::map_ks_error(sec_level.generateKey(
+        &KeyDescriptor {
+            domain: Domain::APP,
+            nspace: -1,
+            alias: Some(attest_key_alias),
+            blob: None,
+        },
+        None,
+        &attest_gen_params,
+        0,
+        b"entropy",
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+}
+
+/// Generate RSA attestation key and try to use it for signing RSA key without providing
+/// attestation challenge. Test should fail to generate a key with error code
+/// `ATTESTATION_CHALLENGE_MISSING`.
+#[test]
+fn keystore2_attest_key_fails_missing_challenge() {
+    skip_test_if_no_app_attest_key_feature!();
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let att_challenge: &[u8] = b"foo";
+
+    // Create RSA attestation key.
+    let attestation_key_metadata =
+        key_generations::generate_attestation_key(&sec_level, Algorithm::RSA, att_challenge)
+            .unwrap();
+
+    let mut cert_chain: Vec<u8> = Vec::new();
+    cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
+    cert_chain.extend(attestation_key_metadata.certificateChain.as_ref().unwrap());
+    validate_certchain(&cert_chain).expect("Error while validating cert chain.");
+
+    // Try to attest RSA signing key without providing attestation challenge.
+    let sign_key_alias = format!("ksrsa_attested_test_key_missing_challenge{}", getuid());
+    let result = key_generations::map_ks_error(key_generations::generate_rsa_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(sign_key_alias),
+        &key_generations::KeyParams {
+            key_size: 2048,
+            purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+            padding: Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
+            digest: Some(Digest::SHA_2_256),
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: None,
+        },
+        Some(&attestation_key_metadata.key),
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::ATTESTATION_CHALLENGE_MISSING), result.unwrap_err());
+}
+
+/// Generate an asymmetric key which doesn't possess ATTEST_KEY purpose. Try to use this key as
+/// attestation key while generating RSA key. Test should fail to generate a key with error
+/// code `INCOMPATIBLE_PURPOSE`.
+#[test]
+fn keystore2_attest_rsa_key_with_non_attest_key_fails_incompat_purpose_error() {
+    skip_test_if_no_app_attest_key_feature!();
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let att_challenge: &[u8] = b"foo";
+
+    let alias = format!("non_attest_key_{}", getuid());
+    let non_attest_key_metadata = key_generations::generate_ec_p256_signing_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias),
+        None,
+    )
+    .unwrap();
+
+    // Try to generate RSA signing key with non-attestation key to sign it.
+    let sign_key_alias = format!("ksrsa_attested_sign_test_key_non_attest_{}", getuid());
+    let result = key_generations::map_ks_error(key_generations::generate_rsa_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(sign_key_alias),
+        &key_generations::KeyParams {
+            key_size: 2048,
+            purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+            padding: Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
+            digest: Some(Digest::SHA_2_256),
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: Some(att_challenge.to_vec()),
+        },
+        Some(&non_attest_key_metadata.key),
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+}
+
+/// Generate a symmetric key. Try to use this symmetric key as attestation key while generating RSA
+/// key. Test should fail to generate a key with response code `INVALID_ARGUMENT`.
+#[test]
+fn keystore2_attest_rsa_key_with_symmetric_key_fails_sys_error() {
+    skip_test_if_no_app_attest_key_feature!();
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let att_challenge: &[u8] = b"foo";
+
+    let alias = "aes_attest_key";
+    let sym_key_metadata = key_generations::generate_sym_key(
+        &sec_level,
+        Algorithm::AES,
+        128,
+        alias,
+        &PaddingMode::NONE,
+        &BlockMode::ECB,
+        None,
+    )
+    .unwrap();
+
+    // Try to generate RSA signing key with symmetric key as attestation key.
+    let sign_key_alias = format!("ksrsa_attested_sign_test_key_sym_attest_{}", getuid());
+    let result = key_generations::map_ks_error(key_generations::generate_rsa_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(sign_key_alias),
+        &key_generations::KeyParams {
+            key_size: 2048,
+            purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+            padding: Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
+            digest: Some(Digest::SHA_2_256),
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: Some(att_challenge.to_vec()),
+        },
+        Some(&sym_key_metadata.key),
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
+}
+
+/// Generate RSA attestation key and try to use it as attestation key while generating symmetric
+/// key. Test should generate symmetric key successfully. Verify that generated symmetric key
+/// should not have attestation record or certificate.
+#[test]
+fn keystore2_attest_symmetric_key_fail_sys_error() {
+    skip_test_if_no_app_attest_key_feature!();
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let att_challenge: &[u8] = b"foo";
+
+    // Create attestation key.
+    let attestation_key_metadata =
+        key_generations::generate_attestation_key(&sec_level, Algorithm::RSA, att_challenge)
+            .unwrap();
+
+    let mut cert_chain: Vec<u8> = Vec::new();
+    cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
+    cert_chain.extend(attestation_key_metadata.certificateChain.as_ref().unwrap());
+    validate_certchain(&cert_chain).expect("Error while validating cert chain.");
+
+    // Generate symmetric key with above generated key as attestation key.
+    let gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::AES)
+        .purpose(KeyPurpose::ENCRYPT)
+        .purpose(KeyPurpose::DECRYPT)
+        .key_size(128)
+        .padding_mode(PaddingMode::NONE)
+        .block_mode(BlockMode::ECB)
+        .attestation_challenge(att_challenge.to_vec());
+
+    let alias = format!("ks_test_sym_key_attest_{}", getuid());
+    let aes_key_metadata = sec_level
+        .generateKey(
+            &KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None },
+            Some(&attestation_key_metadata.key),
+            &gen_params,
+            0,
+            b"entropy",
+        )
+        .unwrap();
+
+    // Should not have public certificate.
+    assert!(aes_key_metadata.certificate.is_none());
+
+    // Should not have an attestation record.
+    assert!(aes_key_metadata.certificateChain.is_none());
+}
diff --git a/keystore2/tests/keystore2_client_delete_key_tests.rs b/keystore2/tests/keystore2_client_delete_key_tests.rs
new file mode 100644
index 0000000..2a06edb
--- /dev/null
+++ b/keystore2/tests/keystore2_client_delete_key_tests.rs
@@ -0,0 +1,150 @@
+// Copyright 2022, 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 nix::unistd::getuid;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    ErrorCode::ErrorCode, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{get_keystore_service, key_generations, key_generations::Error};
+
+/// Generate a key and delete it using keystore2 service `deleteKey` API. Test should successfully
+/// delete the generated key.
+#[test]
+fn keystore2_delete_key_success() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = "delete_key_success_key";
+
+    let key_metadata = key_generations::generate_ec_p256_signing_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        None,
+    )
+    .unwrap();
+
+    keystore2.deleteKey(&key_metadata.key).expect("Failed to delete a key.");
+
+    // Check wehther deleted key is removed from keystore.
+    let result = key_generations::map_ks_error(keystore2.getKeyEntry(&key_metadata.key));
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
+
+/// Try to delete non-existing key with domain other than BLOB using keystore2 service `deleteKey`
+/// API. Test should fail with an error code `KEY_NOT_FOUND`.
+#[test]
+fn keystore2_delete_key_fail() {
+    let test_alias = "delete_key_failure_key";
+    let keystore2 = get_keystore_service();
+
+    let result = key_generations::map_ks_error(keystore2.deleteKey(&KeyDescriptor {
+        domain: Domain::SELINUX,
+        nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+        alias: Some(test_alias.to_string()),
+        blob: None,
+    }));
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
+
+/// Generate a key with `Domain::BLOB`. Try to delete a key with `Domain::BLOB` using keystore2
+/// service `deleteKey` API. Test should fail to delete a key with domain BLOB with an error code
+/// `INVALID_ARGUMENT`.
+#[test]
+fn keystore2_delete_key_with_blob_domain_fail() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = "delete_key_blob_fail_key";
+
+    let key_metadata = key_generations::generate_ec_p256_signing_key(
+        &sec_level,
+        Domain::BLOB,
+        key_generations::SELINUX_SHELL_NAMESPACE,
+        Some(alias.to_string()),
+        None,
+    )
+    .unwrap();
+
+    let result = key_generations::map_ks_error(keystore2.deleteKey(&key_metadata.key));
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
+}
+
+/// Generate a key with `Domain::BLOB`. Delete generated key with `Domain::BLOB` using underlying
+/// security level `deleteKey` API. Test should delete the key successfully.
+#[test]
+fn keystore2_delete_key_blob_success() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = "delete_key_blob_success_key";
+
+    let key_metadata = key_generations::generate_ec_p256_signing_key(
+        &sec_level,
+        Domain::BLOB,
+        key_generations::SELINUX_SHELL_NAMESPACE,
+        Some(alias.to_string()),
+        None,
+    )
+    .unwrap();
+
+    let result = sec_level.deleteKey(&key_metadata.key);
+    assert!(result.is_ok());
+}
+
+/// Try to delete a key with `Domain::BLOB` without providing key-blob. Test should fail to delete a
+/// key with error code `INVALID_ARGUMENT`.
+#[test]
+fn keystore2_delete_key_fails_with_missing_key_blob() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let result = key_generations::map_ks_error(sec_level.deleteKey(&KeyDescriptor {
+        domain: Domain::BLOB,
+        nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+        alias: None,
+        blob: None,
+    }));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INVALID_ARGUMENT), result.unwrap_err());
+}
+
+/// Try to delete a key with domain other than `Domain::BLOB` using underlying security-level
+/// `deleteKey` API. Test should fail to delete a key-blob from underlying security-level backend
+/// with error code `INVALID_ARGUMENT`.
+#[test]
+fn keystore2_delete_key_blob_fail() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = format!("ks_delete_keyblob_test_key_{}", getuid());
+
+    let key_metadata = key_generations::generate_ec_p256_signing_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias),
+        None,
+    )
+    .unwrap();
+
+    let result = key_generations::map_ks_error(sec_level.deleteKey(&key_metadata.key));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INVALID_ARGUMENT), result.unwrap_err());
+}
diff --git a/keystore2/tests/keystore2_client_ec_key_tests.rs b/keystore2/tests/keystore2_client_ec_key_tests.rs
new file mode 100644
index 0000000..c2034de
--- /dev/null
+++ b/keystore2/tests/keystore2_client_ec_key_tests.rs
@@ -0,0 +1,517 @@
+// Copyright 2022, 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 nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, ErrorCode::ErrorCode,
+    KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    CreateOperationResponse::CreateOperationResponse, Domain::Domain,
+    IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+    ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{
+    authorizations, get_keystore_service, key_generations, key_generations::Error, run_as,
+};
+
+use crate::keystore2_client_test_utils::{
+    delete_app_key, execute_op_run_as_child, perform_sample_sign_operation, BarrierReached,
+    ForcedOp, TestOutcome,
+};
+
+macro_rules! test_ec_sign_key_op_success {
+    ( $test_name:ident, $digest:expr, $ec_curve:expr ) => {
+        #[test]
+        fn $test_name() {
+            perform_ec_sign_key_op_success(stringify!($test_name), $digest, $ec_curve);
+        }
+    };
+}
+
+macro_rules! test_ec_sign_key_op_with_none_or_md5_digest {
+    ( $test_name:ident, $digest:expr, $ec_curve:expr ) => {
+        #[test]
+        fn $test_name() {
+            perform_ec_sign_key_op_with_none_or_md5_digest(
+                stringify!($test_name),
+                $digest,
+                $ec_curve,
+            );
+        }
+    };
+}
+
+fn create_ec_key_and_operation(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    domain: Domain,
+    nspace: i64,
+    alias: Option<String>,
+    digest: Digest,
+    ec_curve: EcCurve,
+) -> binder::Result<CreateOperationResponse> {
+    let key_metadata =
+        key_generations::generate_ec_key(sec_level, domain, nspace, alias, ec_curve, digest)?;
+
+    sec_level.createOperation(
+        &key_metadata.key,
+        &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(digest),
+        false,
+    )
+}
+
+fn perform_ec_sign_key_op_success(alias: &str, digest: Digest, ec_curve: EcCurve) {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let op_response = create_ec_key_and_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        digest,
+        ec_curve,
+    )
+    .unwrap();
+
+    assert!(op_response.iOperation.is_some());
+    assert_eq!(
+        Ok(()),
+        key_generations::map_ks_error(perform_sample_sign_operation(
+            &op_response.iOperation.unwrap()
+        ))
+    );
+
+    delete_app_key(&keystore2, alias).unwrap();
+}
+
+fn perform_ec_sign_key_op_with_none_or_md5_digest(alias: &str, digest: Digest, ec_curve: EcCurve) {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    match key_generations::map_ks_error(create_ec_key_and_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        digest,
+        ec_curve,
+    )) {
+        Ok(op_response) => {
+            assert!(op_response.iOperation.is_some());
+            assert_eq!(
+                Ok(()),
+                key_generations::map_ks_error(perform_sample_sign_operation(
+                    &op_response.iOperation.unwrap()
+                ))
+            );
+        }
+        Err(e) => {
+            assert_eq!(e, Error::Km(ErrorCode::UNSUPPORTED_DIGEST));
+            assert!(digest == Digest::NONE || digest == Digest::MD5);
+        }
+    }
+
+    delete_app_key(&keystore2, alias).unwrap();
+}
+
+// Below macros generate tests for generating EC keys with curves EcCurve::P_224, EcCurve::P_256,
+// EcCurve::P_384, EcCurve::P_521 and various digest modes. Tests tries to create operations using
+// the generated keys. Operations with digest modes `SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and
+// SHA-2 512` should be created  successfully. Creation of operations with digest modes NONE and
+// MD5 should fail with an error code `UNSUPPORTED_DIGEST`.
+test_ec_sign_key_op_with_none_or_md5_digest!(
+    sign_ec_key_op_none_ec_p224,
+    Digest::NONE,
+    EcCurve::P_224
+);
+test_ec_sign_key_op_with_none_or_md5_digest!(
+    sign_ec_key_op_md5_ec_p224,
+    Digest::MD5,
+    EcCurve::P_224
+);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha1_ec_p224, Digest::SHA1, EcCurve::P_224);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha224_ec_p224, Digest::SHA_2_224, EcCurve::P_224);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha256_ec_p224, Digest::SHA_2_256, EcCurve::P_224);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha384_ec_p224, Digest::SHA_2_384, EcCurve::P_224);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha512_ec_p224, Digest::SHA_2_512, EcCurve::P_224);
+test_ec_sign_key_op_with_none_or_md5_digest!(
+    sign_ec_key_op_none_ec_p256,
+    Digest::NONE,
+    EcCurve::P_256
+);
+test_ec_sign_key_op_with_none_or_md5_digest!(
+    sign_ec_key_op_md5_ec_p256,
+    Digest::MD5,
+    EcCurve::P_256
+);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha1_ec_p256, Digest::SHA1, EcCurve::P_256);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha224_ec_p256, Digest::SHA_2_224, EcCurve::P_256);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha256_ec_p256, Digest::SHA_2_256, EcCurve::P_256);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha384_ec_p256, Digest::SHA_2_384, EcCurve::P_256);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha512_ec_p256, Digest::SHA_2_512, EcCurve::P_256);
+test_ec_sign_key_op_with_none_or_md5_digest!(
+    sign_ec_key_op_none_ec_p384,
+    Digest::NONE,
+    EcCurve::P_384
+);
+test_ec_sign_key_op_with_none_or_md5_digest!(
+    sign_ec_key_op_md5_ec_p384,
+    Digest::MD5,
+    EcCurve::P_384
+);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha1_ec_p384, Digest::SHA1, EcCurve::P_384);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha224_ec_p384, Digest::SHA_2_224, EcCurve::P_384);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha256_ec_p384, Digest::SHA_2_256, EcCurve::P_384);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha384_ec_p384, Digest::SHA_2_384, EcCurve::P_384);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha512_ec_p384, Digest::SHA_2_512, EcCurve::P_384);
+test_ec_sign_key_op_with_none_or_md5_digest!(
+    sign_ec_key_op_none_ec_p521,
+    Digest::NONE,
+    EcCurve::P_521
+);
+test_ec_sign_key_op_with_none_or_md5_digest!(
+    sign_ec_key_op_md5_ec_p521,
+    Digest::MD5,
+    EcCurve::P_521
+);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha1_ec_p521, Digest::SHA1, EcCurve::P_521);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha224_ec_p521, Digest::SHA_2_224, EcCurve::P_521);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha256_ec_p521, Digest::SHA_2_256, EcCurve::P_521);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha384_ec_p521, Digest::SHA_2_384, EcCurve::P_521);
+test_ec_sign_key_op_success!(sign_ec_key_op_sha512_ec_p521, Digest::SHA_2_512, EcCurve::P_521);
+
+/// This test will try to load the key with Domain::BLOB.
+/// INVALID_ARGUMENT error is expected.
+#[test]
+fn keystore2_get_key_entry_blob_fail() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    // Generate a key with domain as BLOB.
+    let key_metadata = key_generations::generate_ec_p256_signing_key(
+        &sec_level,
+        Domain::BLOB,
+        key_generations::SELINUX_SHELL_NAMESPACE,
+        None,
+        None,
+    )
+    .unwrap();
+
+    // Try to load the key using above generated KeyDescriptor.
+    let result = key_generations::map_ks_error(keystore2.getKeyEntry(&key_metadata.key));
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
+
+    // Delete the generated key blob.
+    sec_level.deleteKey(&key_metadata.key).unwrap();
+}
+
+/// Try to generate a key with invalid Domain. `INVALID_ARGUMENT` error response is expected.
+#[test]
+fn keystore2_generate_key_invalid_domain() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = format!("ks_invalid_test_key_{}", getuid());
+
+    let result = key_generations::map_ks_error(key_generations::generate_ec_key(
+        &sec_level,
+        Domain(99), // Invalid domain.
+        key_generations::SELINUX_SHELL_NAMESPACE,
+        Some(alias),
+        EcCurve::P_256,
+        Digest::SHA_2_256,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
+}
+
+/// Try to generate a EC key without providing the curve.
+/// `UNSUPPORTED_EC_CURVE or UNSUPPORTED_KEY_SIZE` error response is expected.
+#[test]
+fn keystore2_generate_ec_key_missing_curve() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = format!("ks_ec_no_curve_test_key_{}", getuid());
+
+    // Don't provide EC curve.
+    let gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::EC)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .digest(Digest::SHA_2_256);
+
+    let result = key_generations::map_ks_error(sec_level.generateKey(
+        &KeyDescriptor {
+            domain: Domain::SELINUX,
+            nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+            alias: Some(alias),
+            blob: None,
+        },
+        None,
+        &gen_params,
+        0,
+        b"entropy",
+    ));
+    assert!(result.is_err());
+    let err = result.unwrap_err();
+    assert!(matches!(
+        err,
+        Error::Km(ErrorCode::UNSUPPORTED_EC_CURVE) | Error::Km(ErrorCode::UNSUPPORTED_KEY_SIZE)
+    ));
+}
+
+/// Try to generate a EC key with curve `CURVE_25519` having `SIGN and AGREE_KEY` purposes.
+/// `INCOMPATIBLE_PURPOSE` error response is expected.
+#[test]
+fn keystore2_generate_ec_key_25519_multi_purpose() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = format!("ks_ec_no_curve_test_key_{}", getuid());
+
+    // Specify `SIGN and AGREE_KEY` purposes.
+    let gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::EC)
+        .ec_curve(EcCurve::CURVE_25519)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::AGREE_KEY)
+        .digest(Digest::SHA_2_256);
+
+    let result = key_generations::map_ks_error(sec_level.generateKey(
+        &KeyDescriptor {
+            domain: Domain::SELINUX,
+            nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+            alias: Some(alias),
+            blob: None,
+        },
+        None,
+        &gen_params,
+        0,
+        b"entropy",
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+}
+
+/// Generate EC key with curve `CURVE_25519` and digest mode NONE. Try to create an operation using
+/// generated key. `CURVE_25519` key should support `Digest::NONE` digest mode and test should be
+/// able to create an operation successfully.
+#[test]
+fn keystore2_ec_25519_generate_key_success() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = format!("ks_ec_25519_none_test_key_gen_{}", getuid());
+    let key_metadata = key_generations::generate_ec_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias),
+        EcCurve::CURVE_25519,
+        Digest::NONE,
+    )
+    .unwrap();
+
+    let op_response = sec_level
+        .createOperation(
+            &key_metadata.key,
+            &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::NONE),
+            false,
+        )
+        .unwrap();
+    assert!(op_response.iOperation.is_some());
+    assert_eq!(
+        Ok(()),
+        key_generations::map_ks_error(perform_sample_sign_operation(
+            &op_response.iOperation.unwrap()
+        ))
+    );
+}
+
+/// Generate EC keys with curve `CURVE_25519` and digest modes `MD5, SHA1, SHA-2 224, SHA-2 256,
+/// SHA-2 384 and SHA-2 512`. Try to create operations using generated keys. `CURVE_25519` keys
+/// shouldn't support these digest modes. Test should fail to create operations with an error
+/// `UNSUPPORTED_DIGEST`.
+#[test]
+fn keystore2_ec_25519_generate_key_fail() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let digests = [
+        Digest::MD5,
+        Digest::SHA1,
+        Digest::SHA_2_224,
+        Digest::SHA_2_256,
+        Digest::SHA_2_384,
+        Digest::SHA_2_512,
+    ];
+
+    for digest in digests {
+        let alias = format!("ks_ec_25519_test_key_gen_{}{}", getuid(), digest.0);
+        let key_metadata = key_generations::generate_ec_key(
+            &sec_level,
+            Domain::APP,
+            -1,
+            Some(alias.to_string()),
+            EcCurve::CURVE_25519,
+            digest,
+        )
+        .unwrap();
+
+        let result = key_generations::map_ks_error(sec_level.createOperation(
+            &key_metadata.key,
+            &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(digest),
+            false,
+        ));
+        assert!(result.is_err());
+        assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_DIGEST), result.unwrap_err());
+    }
+}
+
+/// Generate a EC key with `SHA_2_256` digest mode. Try to create an operation with digest mode
+/// other than `SHA_2_256`. Creation of an operation with generated key should fail with
+/// `INCOMPATIBLE_DIGEST` error as there is a mismatch of digest mode in key authorizations.
+#[test]
+fn keystore2_create_op_with_incompatible_key_digest() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = "ks_ec_test_incomp_key_digest";
+    let key_metadata = key_generations::generate_ec_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        EcCurve::P_256,
+        Digest::SHA_2_256,
+    )
+    .unwrap();
+
+    let digests =
+        [Digest::NONE, Digest::SHA1, Digest::SHA_2_224, Digest::SHA_2_384, Digest::SHA_2_512];
+
+    for digest in digests {
+        let result = key_generations::map_ks_error(sec_level.createOperation(
+            &key_metadata.key,
+            &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(digest),
+            false,
+        ));
+        assert!(result.is_err());
+        assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_DIGEST), result.unwrap_err());
+    }
+}
+
+/// Generate a key in client#1 and try to use it in other client#2.
+/// Client#2 should fail to load the key as the it doesn't own the client#1 generated key.
+#[test]
+fn keystore2_key_owner_validation() {
+    static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+    const USER_ID: u32 = 99;
+    const APPLICATION_ID_1: u32 = 10601;
+
+    let uid1 = USER_ID * AID_USER_OFFSET + APPLICATION_ID_1;
+    let gid1 = USER_ID * AID_USER_OFFSET + APPLICATION_ID_1;
+    let alias = "ks_owner_check_test_key";
+
+    // Client#1: Generate a key and create an operation using generated key.
+    // Wait until the parent notifies to continue. Once the parent notifies, this operation
+    // is expected to be completed successfully.
+    let mut child_handle = execute_op_run_as_child(
+        TARGET_CTX,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        Uid::from_raw(uid1),
+        Gid::from_raw(gid1),
+        ForcedOp(false),
+    );
+
+    // Wait until (client#1) child process notifies us to continue, so that there will be a key
+    // generated by client#1.
+    child_handle.recv();
+
+    // Client#2: This child will try to load the key generated by client#1.
+    const APPLICATION_ID_2: u32 = 10602;
+    let uid2 = USER_ID * AID_USER_OFFSET + APPLICATION_ID_2;
+    let gid2 = USER_ID * AID_USER_OFFSET + APPLICATION_ID_2;
+    unsafe {
+        run_as::run_as(TARGET_CTX, Uid::from_raw(uid2), Gid::from_raw(gid2), move || {
+            let keystore2_inst = get_keystore_service();
+            let result =
+                key_generations::map_ks_error(keystore2_inst.getKeyEntry(&KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: -1,
+                    alias: Some(alias.to_string()),
+                    blob: None,
+                }));
+            assert!(result.is_err());
+            assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+        });
+    };
+
+    // Notify the child process (client#1) to resume and finish.
+    child_handle.send(&BarrierReached {});
+    assert!(
+        (child_handle.get_result() == TestOutcome::Ok),
+        "Client#1 failed to complete the operation."
+    );
+}
+
+/// Generate EC key with BLOB as domain. Generated key should be returned to caller as key blob.
+/// Verify that `blob` field in the `KeyDescriptor` is not empty and should have the key blob.
+/// Try to use this key for performing a sample operation and the operation should complete
+/// successfully.
+#[test]
+fn keystore2_generate_key_with_blob_domain() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let key_metadata = key_generations::generate_ec_key(
+        &sec_level,
+        Domain::BLOB,
+        key_generations::SELINUX_SHELL_NAMESPACE,
+        None,
+        EcCurve::P_256,
+        Digest::SHA_2_256,
+    )
+    .unwrap();
+
+    assert!(key_metadata.certificate.is_some());
+    assert!(key_metadata.certificateChain.is_none());
+
+    // Must have the key blob.
+    assert!(key_metadata.key.blob.is_some());
+
+    let op_response = key_generations::map_ks_error(sec_level.createOperation(
+        &key_metadata.key,
+        &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+        false,
+    ))
+    .unwrap();
+    assert!(op_response.iOperation.is_some());
+    assert_eq!(
+        Ok(()),
+        key_generations::map_ks_error(perform_sample_sign_operation(
+            &op_response.iOperation.unwrap()
+        ))
+    );
+
+    // Delete the generated key blob.
+    sec_level.deleteKey(&key_metadata.key).unwrap();
+}
diff --git a/keystore2/tests/keystore2_client_grant_key_tests.rs b/keystore2/tests/keystore2_client_grant_key_tests.rs
new file mode 100644
index 0000000..bde872d
--- /dev/null
+++ b/keystore2/tests/keystore2_client_grant_key_tests.rs
@@ -0,0 +1,755 @@
+// Copyright 2022, 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 nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Digest::Digest, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
+    IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
+    ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{
+    authorizations, get_keystore_service, key_generations, key_generations::Error, run_as,
+};
+
+use crate::keystore2_client_test_utils::{
+    generate_ec_key_and_grant_to_users, perform_sample_sign_operation,
+};
+
+/// Generate an EC signing key and grant it to the user with given access vector.
+fn generate_ec_key_and_grant_to_user(
+    grantee_uid: i32,
+    access_vector: i32,
+) -> binder::Result<KeyDescriptor> {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = format!("{}{}", "ks_grant_test_key_1", getuid());
+
+    let key_metadata = key_generations::generate_ec_p256_signing_key(
+        &sec_level,
+        Domain::SELINUX,
+        key_generations::SELINUX_SHELL_NAMESPACE,
+        Some(alias),
+        None,
+    )
+    .unwrap();
+
+    keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
+}
+
+fn load_grant_key_and_perform_sign_operation(
+    keystore2: &binder::Strong<dyn IKeystoreService>,
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    grant_key_nspace: i64,
+) -> Result<(), binder::Status> {
+    let key_entry_response = keystore2.getKeyEntry(&KeyDescriptor {
+        domain: Domain::GRANT,
+        nspace: grant_key_nspace,
+        alias: None,
+        blob: None,
+    })?;
+
+    // Perform sample crypto operation using granted key.
+    let op_response = sec_level.createOperation(
+        &key_entry_response.metadata.key,
+        &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+        false,
+    )?;
+
+    assert!(op_response.iOperation.is_some());
+    assert_eq!(
+        Ok(()),
+        key_generations::map_ks_error(perform_sample_sign_operation(
+            &op_response.iOperation.unwrap()
+        ))
+    );
+
+    Ok(())
+}
+
+/// Try to grant a key with permission that does not map to any of the `KeyPermission` values.
+/// An error is expected with values that does not map to set of permissions listed in
+/// `KeyPermission`.
+#[test]
+fn keystore2_grant_key_with_invalid_perm_expecting_syserror() {
+    const USER_ID: u32 = 99;
+    const APPLICATION_ID: u32 = 10001;
+    let grantee_uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+    let invalid_access_vector = KeyPermission::CONVERT_STORAGE_KEY_TO_EPHEMERAL.0 << 19;
+
+    let result = key_generations::map_ks_error(generate_ec_key_and_grant_to_user(
+        grantee_uid.try_into().unwrap(),
+        invalid_access_vector,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::SYSTEM_ERROR), result.unwrap_err());
+}
+
+/// Try to grant a key with empty access vector `KeyPermission::NONE`, should be able to grant a
+/// key with empty access vector successfully. In grantee context try to use the granted key, it
+/// should fail to load the key with permission denied error.
+#[test]
+fn keystore2_grant_key_with_perm_none() {
+    static TARGET_SU_CTX: &str = "u:r:su:s0";
+
+    static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+    const USER_ID: u32 = 99;
+    const APPLICATION_ID: u32 = 10001;
+    static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_GID: u32 = GRANTEE_UID;
+
+    let grant_key_nspace = unsafe {
+        run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+            let empty_access_vector = KeyPermission::NONE.0;
+
+            let grant_key = key_generations::map_ks_error(generate_ec_key_and_grant_to_user(
+                GRANTEE_UID.try_into().unwrap(),
+                empty_access_vector,
+            ))
+            .unwrap();
+
+            assert_eq!(grant_key.domain, Domain::GRANT);
+
+            grant_key.nspace
+        })
+    };
+
+    // In grantee context try to load the key, it should fail to load the granted key as it is
+    // granted with empty access vector.
+    unsafe {
+        run_as::run_as(
+            GRANTEE_CTX,
+            Uid::from_raw(GRANTEE_UID),
+            Gid::from_raw(GRANTEE_GID),
+            move || {
+                let keystore2 = get_keystore_service();
+
+                let result = key_generations::map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
+                    domain: Domain::GRANT,
+                    nspace: grant_key_nspace,
+                    alias: None,
+                    blob: None,
+                }));
+                assert!(result.is_err());
+                assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+            },
+        )
+    };
+}
+
+/// Grant a key to the user (grantee) with `GET_INFO|USE` key permissions. Verify whether grantee
+/// can succeed in loading the granted key and try to perform simple operation using this granted
+/// key. Grantee should be able to load the key and use the key to perform crypto operation
+/// successfully. Try to delete the granted key in grantee context where it is expected to fail to
+/// delete it as `DELETE` permission is not granted.
+#[test]
+fn keystore2_grant_get_info_use_key_perm() {
+    static TARGET_SU_CTX: &str = "u:r:su:s0";
+
+    static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+    const USER_ID: u32 = 99;
+    const APPLICATION_ID: u32 = 10001;
+    static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_GID: u32 = GRANTEE_UID;
+
+    // Generate a key and grant it to a user with GET_INFO|USE key permissions.
+    let grant_key_nspace = unsafe {
+        run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+            let access_vector = KeyPermission::GET_INFO.0 | KeyPermission::USE.0;
+            let grant_key = key_generations::map_ks_error(generate_ec_key_and_grant_to_user(
+                GRANTEE_UID.try_into().unwrap(),
+                access_vector,
+            ))
+            .unwrap();
+
+            assert_eq!(grant_key.domain, Domain::GRANT);
+
+            grant_key.nspace
+        })
+    };
+
+    // In grantee context load the key and try to perform crypto operation.
+    unsafe {
+        run_as::run_as(
+            GRANTEE_CTX,
+            Uid::from_raw(GRANTEE_UID),
+            Gid::from_raw(GRANTEE_GID),
+            move || {
+                let keystore2 = get_keystore_service();
+                let sec_level =
+                    keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+                // Load the granted key.
+                let key_entry_response = keystore2
+                    .getKeyEntry(&KeyDescriptor {
+                        domain: Domain::GRANT,
+                        nspace: grant_key_nspace,
+                        alias: None,
+                        blob: None,
+                    })
+                    .unwrap();
+
+                // Perform sample crypto operation using granted key.
+                let op_response = sec_level
+                    .createOperation(
+                        &key_entry_response.metadata.key,
+                        &authorizations::AuthSetBuilder::new()
+                            .purpose(KeyPurpose::SIGN)
+                            .digest(Digest::SHA_2_256),
+                        false,
+                    )
+                    .unwrap();
+                assert!(op_response.iOperation.is_some());
+                assert_eq!(
+                    Ok(()),
+                    key_generations::map_ks_error(perform_sample_sign_operation(
+                        &op_response.iOperation.unwrap()
+                    ))
+                );
+
+                // Try to delete the key, it is expected to be fail with permission denied error.
+                let result = key_generations::map_ks_error(keystore2.deleteKey(&KeyDescriptor {
+                    domain: Domain::GRANT,
+                    nspace: grant_key_nspace,
+                    alias: None,
+                    blob: None,
+                }));
+                assert!(result.is_err());
+                assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+            },
+        )
+    };
+}
+
+/// Grant a key to the user with DELETE access. In grantee context load the key and delete it.
+/// Verify that grantee should succeed in deleting the granted key and in grantor context test
+/// should fail to find the key with error response `KEY_NOT_FOUND`.
+#[test]
+fn keystore2_grant_delete_key_success() {
+    static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+    static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+    const USER_ID: u32 = 99;
+    const APPLICATION_ID: u32 = 10001;
+    static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_GID: u32 = GRANTEE_UID;
+    static ALIAS: &str = "ks_grant_key_delete_success";
+
+    // Generate a key and grant it to a user with DELETE permission.
+    let grant_key_nspace = unsafe {
+        run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+            let keystore2 = get_keystore_service();
+            let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+            let access_vector = KeyPermission::DELETE.0;
+            let mut grant_keys = generate_ec_key_and_grant_to_users(
+                &keystore2,
+                &sec_level,
+                Some(ALIAS.to_string()),
+                vec![GRANTEE_UID.try_into().unwrap()],
+                access_vector,
+            )
+            .unwrap();
+
+            grant_keys.remove(0)
+        })
+    };
+
+    // Grantee context, delete the key.
+    unsafe {
+        run_as::run_as(
+            GRANTEE_CTX,
+            Uid::from_raw(GRANTEE_UID),
+            Gid::from_raw(GRANTEE_GID),
+            move || {
+                let keystore2 = get_keystore_service();
+                keystore2
+                    .deleteKey(&KeyDescriptor {
+                        domain: Domain::GRANT,
+                        nspace: grant_key_nspace,
+                        alias: None,
+                        blob: None,
+                    })
+                    .unwrap();
+            },
+        )
+    };
+
+    // Verify whether key got deleted in grantor's context.
+    unsafe {
+        run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), move || {
+            let keystore2_inst = get_keystore_service();
+            let result =
+                key_generations::map_ks_error(keystore2_inst.getKeyEntry(&KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: -1,
+                    alias: Some(ALIAS.to_string()),
+                    blob: None,
+                }));
+            assert!(result.is_err());
+            assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+        })
+    };
+}
+
+/// Grant a key to the user. In grantee context load the granted key and try to grant it to second
+/// user. Test should fail with a response code `PERMISSION_DENIED` to grant a key to second user
+/// from grantee context. Test should make sure second grantee should not have a access to granted
+/// key.
+#[test]
+fn keystore2_grant_key_fails_with_permission_denied() {
+    static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+    static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+    const USER_ID: u32 = 99;
+    const APPLICATION_ID: u32 = 10001;
+    static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_GID: u32 = GRANTEE_UID;
+
+    const SEC_USER_ID: u32 = 98;
+    const SEC_APPLICATION_ID: u32 = 10001;
+    static SEC_GRANTEE_UID: u32 = SEC_USER_ID * AID_USER_OFFSET + SEC_APPLICATION_ID;
+    static SEC_GRANTEE_GID: u32 = SEC_GRANTEE_UID;
+
+    // Generate a key and grant it to a user with GET_INFO permission.
+    let grant_key_nspace = unsafe {
+        run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+            let keystore2 = get_keystore_service();
+            let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+            let access_vector = KeyPermission::GET_INFO.0;
+            let alias = format!("ks_grant_perm_denied_key_{}", getuid());
+            let mut grant_keys = generate_ec_key_and_grant_to_users(
+                &keystore2,
+                &sec_level,
+                Some(alias),
+                vec![GRANTEE_UID.try_into().unwrap()],
+                access_vector,
+            )
+            .unwrap();
+
+            grant_keys.remove(0)
+        })
+    };
+
+    // Grantee context, load the granted key and try to grant it to `SEC_GRANTEE_UID` grantee.
+    unsafe {
+        run_as::run_as(
+            GRANTEE_CTX,
+            Uid::from_raw(GRANTEE_UID),
+            Gid::from_raw(GRANTEE_GID),
+            move || {
+                let keystore2 = get_keystore_service();
+                let access_vector = KeyPermission::GET_INFO.0;
+
+                let key_entry_response = keystore2
+                    .getKeyEntry(&KeyDescriptor {
+                        domain: Domain::GRANT,
+                        nspace: grant_key_nspace,
+                        alias: None,
+                        blob: None,
+                    })
+                    .unwrap();
+
+                let result = key_generations::map_ks_error(keystore2.grant(
+                    &key_entry_response.metadata.key,
+                    SEC_GRANTEE_UID.try_into().unwrap(),
+                    access_vector,
+                ));
+                assert!(result.is_err());
+                assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+            },
+        )
+    };
+
+    // Make sure second grantee shouldn't have access to the above granted key.
+    unsafe {
+        run_as::run_as(
+            GRANTEE_CTX,
+            Uid::from_raw(SEC_GRANTEE_UID),
+            Gid::from_raw(SEC_GRANTEE_GID),
+            move || {
+                let keystore2 = get_keystore_service();
+
+                let result = key_generations::map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
+                    domain: Domain::GRANT,
+                    nspace: grant_key_nspace,
+                    alias: None,
+                    blob: None,
+                }));
+
+                assert!(result.is_err());
+                assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+            },
+        )
+    };
+}
+
+/// Try to grant a key with `GRANT` access. Keystore2 system shouldn't allow to grant a key with
+/// `GRANT` access. Test should fail to grant a key with `PERMISSION_DENIED` error response code.
+#[test]
+fn keystore2_grant_key_fails_with_grant_perm_expect_perm_denied() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let access_vector = KeyPermission::GRANT.0;
+    let alias = format!("ks_grant_access_vec_key_{}", getuid());
+    let user_id = 98;
+    let application_id = 10001;
+    let grantee_uid = user_id * AID_USER_OFFSET + application_id;
+
+    let result = key_generations::map_ks_error(generate_ec_key_and_grant_to_users(
+        &keystore2,
+        &sec_level,
+        Some(alias),
+        vec![grantee_uid.try_into().unwrap()],
+        access_vector,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+}
+
+/// Try to grant a non-existing key to the user. Test should fail with `KEY_NOT_FOUND` error
+/// response.
+#[test]
+fn keystore2_grant_fails_with_non_existing_key_expect_key_not_found_err() {
+    let keystore2 = get_keystore_service();
+    let alias = format!("ks_grant_test_non_existing_key_5_{}", getuid());
+    let user_id = 98;
+    let application_id = 10001;
+    let grantee_uid = user_id * AID_USER_OFFSET + application_id;
+    let access_vector = KeyPermission::GET_INFO.0;
+
+    let result = key_generations::map_ks_error(keystore2.grant(
+        &KeyDescriptor {
+            domain: Domain::SELINUX,
+            nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+            alias: Some(alias),
+            blob: None,
+        },
+        grantee_uid.try_into().unwrap(),
+        access_vector,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
+
+/// Grant a key to the user and immediately ungrant the granted key. In grantee context try to load
+/// the key. Grantee should fail to load the ungranted key with `KEY_NOT_FOUND` error response.
+#[test]
+fn keystore2_ungrant_key_success() {
+    static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+    static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+    const USER_ID: u32 = 99;
+    const APPLICATION_ID: u32 = 10001;
+    static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_GID: u32 = GRANTEE_UID;
+
+    // Generate a key and grant it to a user with GET_INFO permission.
+    let grant_key_nspace = unsafe {
+        run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+            let keystore2 = get_keystore_service();
+            let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+            let alias = format!("ks_ungrant_test_key_1{}", getuid());
+            let access_vector = KeyPermission::GET_INFO.0;
+            let mut grant_keys = generate_ec_key_and_grant_to_users(
+                &keystore2,
+                &sec_level,
+                Some(alias.to_string()),
+                vec![GRANTEE_UID.try_into().unwrap()],
+                access_vector,
+            )
+            .unwrap();
+
+            let grant_key_nspace = grant_keys.remove(0);
+
+            //Ungrant above granted key.
+            keystore2
+                .ungrant(
+                    &KeyDescriptor {
+                        domain: Domain::APP,
+                        nspace: -1,
+                        alias: Some(alias),
+                        blob: None,
+                    },
+                    GRANTEE_UID.try_into().unwrap(),
+                )
+                .unwrap();
+
+            grant_key_nspace
+        })
+    };
+
+    // Grantee context, try to load the ungranted key.
+    unsafe {
+        run_as::run_as(
+            GRANTEE_CTX,
+            Uid::from_raw(GRANTEE_UID),
+            Gid::from_raw(GRANTEE_GID),
+            move || {
+                let keystore2 = get_keystore_service();
+                let result = key_generations::map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
+                    domain: Domain::GRANT,
+                    nspace: grant_key_nspace,
+                    alias: None,
+                    blob: None,
+                }));
+                assert!(result.is_err());
+                assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+            },
+        )
+    };
+}
+
+/// Generate a key, grant it to the user and then delete the granted key. Try to ungrant
+/// a deleted key. Test should fail to ungrant a non-existing key with `KEY_NOT_FOUND` error
+/// response. Generate a new key with the same alias and try to access the previously granted
+/// key in grantee context. Test should fail to load the granted key in grantee context as the
+/// associated key is deleted from grantor context.
+#[test]
+fn keystore2_ungrant_fails_with_non_existing_key_expect_key_not_found_error() {
+    static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+    static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+    const APPLICATION_ID: u32 = 10001;
+    const USER_ID: u32 = 99;
+    static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_GID: u32 = GRANTEE_UID;
+
+    let grant_key_nspace = unsafe {
+        run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+            let keystore2 = get_keystore_service();
+            let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+            let alias = format!("{}{}", "ks_grant_delete_ungrant_test_key_1", getuid());
+
+            let key_metadata = key_generations::generate_ec_p256_signing_key(
+                &sec_level,
+                Domain::SELINUX,
+                key_generations::SELINUX_SHELL_NAMESPACE,
+                Some(alias.to_string()),
+                None,
+            )
+            .unwrap();
+
+            let access_vector = KeyPermission::GET_INFO.0;
+            let grant_key = keystore2
+                .grant(&key_metadata.key, GRANTEE_UID.try_into().unwrap(), access_vector)
+                .unwrap();
+            assert_eq!(grant_key.domain, Domain::GRANT);
+
+            // Delete above granted key.
+            keystore2.deleteKey(&key_metadata.key).unwrap();
+
+            // Try to ungrant above granted key.
+            let result = key_generations::map_ks_error(
+                keystore2.ungrant(&key_metadata.key, GRANTEE_UID.try_into().unwrap()),
+            );
+            assert!(result.is_err());
+            assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+
+            // Generate a new key with the same alias and try to access the earlier granted key
+            // in grantee context.
+            let result = key_generations::generate_ec_p256_signing_key(
+                &sec_level,
+                Domain::SELINUX,
+                key_generations::SELINUX_SHELL_NAMESPACE,
+                Some(alias),
+                None,
+            );
+            assert!(result.is_ok());
+
+            grant_key.nspace
+        })
+    };
+
+    // Make sure grant did not persist, try to access the earlier granted key in grantee context.
+    // Grantee context should fail to load the granted key as its associated key is deleted in
+    // grantor context.
+    unsafe {
+        run_as::run_as(
+            GRANTEE_CTX,
+            Uid::from_raw(GRANTEE_UID),
+            Gid::from_raw(GRANTEE_GID),
+            move || {
+                let keystore2 = get_keystore_service();
+
+                let result = key_generations::map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
+                    domain: Domain::GRANT,
+                    nspace: grant_key_nspace,
+                    alias: None,
+                    blob: None,
+                }));
+                assert!(result.is_err());
+                assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+            },
+        )
+    };
+}
+
+/// Grant a key to multiple users. Verify that all grantees should succeed in loading the key and
+/// use it for performing an operation successfully.
+#[test]
+fn keystore2_grant_key_to_multi_users_success() {
+    static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+    static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+    const APPLICATION_ID: u32 = 10001;
+    const USER_ID_1: u32 = 99;
+    static GRANTEE_1_UID: u32 = USER_ID_1 * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_1_GID: u32 = GRANTEE_1_UID;
+
+    const USER_ID_2: u32 = 98;
+    static GRANTEE_2_UID: u32 = USER_ID_2 * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_2_GID: u32 = GRANTEE_2_UID;
+
+    // Generate a key and grant it to multiple users with GET_INFO|USE permissions.
+    let mut grant_keys = unsafe {
+        run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+            let keystore2 = get_keystore_service();
+            let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+            let alias = format!("ks_grant_test_key_2{}", getuid());
+            let access_vector = KeyPermission::GET_INFO.0 | KeyPermission::USE.0;
+
+            generate_ec_key_and_grant_to_users(
+                &keystore2,
+                &sec_level,
+                Some(alias),
+                vec![GRANTEE_1_UID.try_into().unwrap(), GRANTEE_2_UID.try_into().unwrap()],
+                access_vector,
+            )
+            .unwrap()
+        })
+    };
+
+    for (grantee_uid, grantee_gid) in
+        &[(GRANTEE_1_UID, GRANTEE_1_GID), (GRANTEE_2_UID, GRANTEE_2_GID)]
+    {
+        let grant_key_nspace = grant_keys.remove(0);
+        unsafe {
+            run_as::run_as(
+                GRANTEE_CTX,
+                Uid::from_raw(*grantee_uid),
+                Gid::from_raw(*grantee_gid),
+                move || {
+                    let keystore2 = get_keystore_service();
+                    let sec_level =
+                        keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+                    assert_eq!(
+                        Ok(()),
+                        key_generations::map_ks_error(load_grant_key_and_perform_sign_operation(
+                            &keystore2,
+                            &sec_level,
+                            grant_key_nspace
+                        ))
+                    );
+                },
+            )
+        };
+    }
+}
+
+/// Grant a key to multiple users with GET_INFO|DELETE permissions. In one of the grantee context
+/// use the key and delete it. Try to load the granted key in another grantee context. Test should
+/// fail to load the granted key with `KEY_NOT_FOUND` error response.
+#[test]
+fn keystore2_grant_key_to_multi_users_delete_fails_with_key_not_found_error() {
+    static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+    static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+    const USER_ID_1: u32 = 99;
+    const APPLICATION_ID: u32 = 10001;
+    static GRANTEE_1_UID: u32 = USER_ID_1 * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_1_GID: u32 = GRANTEE_1_UID;
+
+    const USER_ID_2: u32 = 98;
+    static GRANTEE_2_UID: u32 = USER_ID_2 * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_2_GID: u32 = GRANTEE_2_UID;
+
+    // Generate a key and grant it to multiple users with GET_INFO permission.
+    let mut grant_keys = unsafe {
+        run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+            let keystore2 = get_keystore_service();
+            let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+            let alias = format!("ks_grant_test_key_2{}", getuid());
+            let access_vector =
+                KeyPermission::GET_INFO.0 | KeyPermission::USE.0 | KeyPermission::DELETE.0;
+
+            generate_ec_key_and_grant_to_users(
+                &keystore2,
+                &sec_level,
+                Some(alias),
+                vec![GRANTEE_1_UID.try_into().unwrap(), GRANTEE_2_UID.try_into().unwrap()],
+                access_vector,
+            )
+            .unwrap()
+        })
+    };
+
+    // Grantee #1 context
+    let grant_key1_nspace = grant_keys.remove(0);
+    unsafe {
+        run_as::run_as(
+            GRANTEE_CTX,
+            Uid::from_raw(GRANTEE_1_UID),
+            Gid::from_raw(GRANTEE_1_GID),
+            move || {
+                let keystore2 = get_keystore_service();
+                let sec_level =
+                    keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+                assert_eq!(
+                    Ok(()),
+                    key_generations::map_ks_error(load_grant_key_and_perform_sign_operation(
+                        &keystore2,
+                        &sec_level,
+                        grant_key1_nspace
+                    ))
+                );
+
+                // Delete the granted key.
+                keystore2
+                    .deleteKey(&KeyDescriptor {
+                        domain: Domain::GRANT,
+                        nspace: grant_key1_nspace,
+                        alias: None,
+                        blob: None,
+                    })
+                    .unwrap();
+            },
+        )
+    };
+
+    // Grantee #2 context
+    let grant_key2_nspace = grant_keys.remove(0);
+    unsafe {
+        run_as::run_as(
+            GRANTEE_CTX,
+            Uid::from_raw(GRANTEE_2_UID),
+            Gid::from_raw(GRANTEE_2_GID),
+            move || {
+                let keystore2 = get_keystore_service();
+
+                let result = key_generations::map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
+                    domain: Domain::GRANT,
+                    nspace: grant_key2_nspace,
+                    alias: None,
+                    blob: None,
+                }));
+                assert!(result.is_err());
+                assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+            },
+        )
+    };
+}
diff --git a/keystore2/tests/keystore2_client_hmac_key_tests.rs b/keystore2/tests/keystore2_client_hmac_key_tests.rs
new file mode 100644
index 0000000..6bb8001
--- /dev/null
+++ b/keystore2/tests/keystore2_client_hmac_key_tests.rs
@@ -0,0 +1,305 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, Digest::Digest, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose,
+    SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+};
+
+use keystore2_test_utils::{
+    authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+use crate::keystore2_client_test_utils::perform_sample_sign_operation;
+
+/// Generate HMAC key with given parameters and perform a sample operation using generated key.
+fn create_hmac_key_and_operation(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    alias: &str,
+    key_size: i32,
+    mac_len: i32,
+    min_mac_len: i32,
+    digest: Digest,
+) -> Result<(), binder::Status> {
+    let key_metadata =
+        key_generations::generate_hmac_key(sec_level, alias, key_size, min_mac_len, digest)?;
+
+    let op_response = sec_level.createOperation(
+        &key_metadata.key,
+        &authorizations::AuthSetBuilder::new()
+            .purpose(KeyPurpose::SIGN)
+            .digest(digest)
+            .mac_length(mac_len),
+        false,
+    )?;
+
+    assert!(op_response.iOperation.is_some());
+    assert_eq!(
+        Ok(()),
+        key_generations::map_ks_error(perform_sample_sign_operation(
+            &op_response.iOperation.unwrap()
+        ))
+    );
+
+    Ok(())
+}
+
+/// Generate HMAC keys with various digest modes [SHA1, SHA_2_224, SHA_2_256, SHA_2_384,
+/// SHA_2_512]. Create an operation using generated keys. Test should create operations
+/// successfully.
+#[test]
+fn keystore2_hmac_key_op_success() {
+    let digests =
+        [Digest::SHA1, Digest::SHA_2_224, Digest::SHA_2_256, Digest::SHA_2_384, Digest::SHA_2_512];
+    let min_mac_len = 128;
+    let mac_len = 128;
+    let key_size = 128;
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    for digest in digests {
+        let alias = format!("ks_hmac_test_key_{}", digest.0);
+
+        assert_eq!(
+            Ok(()),
+            create_hmac_key_and_operation(
+                &sec_level,
+                &alias,
+                key_size,
+                mac_len,
+                min_mac_len,
+                digest,
+            )
+        );
+    }
+}
+
+/// Generate HMAC keys with various key lengths. For invalid key sizes, key generation
+/// should fail with an error code `UNSUPPORTED_KEY_SIZE`.
+#[test]
+fn keystore2_hmac_gen_keys_fails_expect_unsupported_key_size() {
+    let min_mac_len = 256;
+    let digest = Digest::SHA_2_256;
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    for key_size in 0..513 {
+        let alias = format!("ks_hmac_test_key_{}", key_size);
+        let result = key_generations::map_ks_error(key_generations::generate_hmac_key(
+            &sec_level,
+            &alias,
+            key_size,
+            min_mac_len,
+            digest,
+        ));
+
+        match result {
+            Ok(_) => {
+                assert!((key_size >= 64 && key_size % 8 == 0));
+            }
+            Err(e) => {
+                assert_eq!(e, Error::Km(ErrorCode::UNSUPPORTED_KEY_SIZE));
+                assert!((key_size < 64 || key_size % 8 != 0), "Unsupported KeySize: {}", key_size);
+            }
+        }
+    }
+}
+
+/// Generate HMAC keys with various min-mac-lengths. For invalid min-mac-length, key generation
+/// should fail with an error code `UNSUPPORTED_MIN_MAC_LENGTH`.
+#[test]
+fn keystore2_hmac_gen_keys_fails_expect_unsupported_min_mac_length() {
+    let digest = Digest::SHA_2_256;
+    let key_size = 128;
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    for min_mac_len in 0..257 {
+        let alias = format!("ks_hmac_test_key_mml_{}", min_mac_len);
+        match key_generations::map_ks_error(key_generations::generate_hmac_key(
+            &sec_level,
+            &alias,
+            key_size,
+            min_mac_len,
+            digest,
+        )) {
+            Ok(_) => {
+                assert!((min_mac_len >= 64 && min_mac_len % 8 == 0));
+            }
+            Err(e) => {
+                assert_eq!(e, Error::Km(ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH));
+                assert!(
+                    (min_mac_len < 64 || min_mac_len % 8 != 0),
+                    "Unsupported MinMacLength: {}",
+                    min_mac_len
+                );
+            }
+        }
+    }
+}
+
+/// Try to generate HMAC key with multiple digests in key authorizations list.
+/// Test fails to generate a key with multiple digests with an error code `UNSUPPORTED_DIGEST`.
+#[test]
+fn keystore2_hmac_gen_key_multi_digests_fails_expect_unsupported_digest() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = "ks_hmac_test_key_multi_dig";
+    let gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::HMAC)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .key_size(128)
+        .min_mac_length(128)
+        .digest(Digest::SHA1)
+        .digest(Digest::SHA_2_256);
+
+    let result = key_generations::map_ks_error(sec_level.generateKey(
+        &KeyDescriptor {
+            domain: Domain::APP,
+            nspace: -1,
+            alias: Some(alias.to_string()),
+            blob: None,
+        },
+        None,
+        &gen_params,
+        0,
+        b"entropy",
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_DIGEST), result.unwrap_err());
+}
+
+/// Try to generate HMAC key without providing digest mode. HMAC key generation with
+/// no digest should fail with an error code `UNSUPPORTED_DIGEST`.
+#[test]
+fn keystore2_hmac_gen_key_no_digests_fails_expect_unsupported_digest() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = "ks_hmac_test_key_no_dig";
+    let gen_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::HMAC)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .key_size(128)
+        .min_mac_length(128);
+
+    let result = key_generations::map_ks_error(sec_level.generateKey(
+        &KeyDescriptor {
+            domain: Domain::APP,
+            nspace: -1,
+            alias: Some(alias.to_string()),
+            blob: None,
+        },
+        None,
+        &gen_params,
+        0,
+        b"entropy",
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_DIGEST), result.unwrap_err());
+}
+
+/// Try to generate a HMAC key with NONE digest mode, it should fail with `UNSUPPORTED_DIGEST`
+/// error code.
+#[test]
+fn keystore2_hmac_gen_key_with_none_digest_fails_expect_unsupported_digest() {
+    let min_mac_len = 128;
+    let key_size = 128;
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = "ks_hmac_test_key_fail";
+    let result = key_generations::map_ks_error(key_generations::generate_hmac_key(
+        &sec_level,
+        alias,
+        key_size,
+        min_mac_len,
+        Digest::NONE,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_DIGEST), result.unwrap_err());
+}
+
+/// Generate HMAC key with min-mac-len of 128 bits for the digest modes Digest::SHA1 and
+/// Digest::SHA_2_224. Try to create an operation with generated key and mac-length greater than
+/// digest length. Test should fail to create an operation with an error code
+/// `UNSUPPORTED_MAC_LENGTH`.
+#[test]
+fn keystore2_hmac_key_op_with_mac_len_greater_than_digest_len_fail() {
+    let digests = [Digest::SHA1, Digest::SHA_2_224];
+    let min_mac_len = 128;
+    let mac_len = 256;
+    let key_size = 128;
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    for digest in digests {
+        let alias = format!("ks_hmac_test_key_{}", digest.0);
+
+        let result = key_generations::map_ks_error(create_hmac_key_and_operation(
+            &sec_level,
+            &alias,
+            key_size,
+            mac_len,
+            min_mac_len,
+            digest,
+        ));
+
+        assert!(result.is_err());
+        assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_MAC_LENGTH), result.unwrap_err());
+    }
+}
+
+/// Generate HMAC key with min-mac-len of 128 bits for the digest modes Digest::SHA1 and
+/// Digest::SHA_2_224. Try to create an operation with generated key and mac-length less than
+/// min-mac-length. Test should fail to create an operation with an error code
+/// `INVALID_MAC_LENGTH`.
+#[test]
+fn keystore2_hmac_key_op_with_mac_len_less_than_min_mac_len_fail() {
+    let digests = [Digest::SHA1, Digest::SHA_2_224];
+    let min_mac_len = 128;
+    let mac_len = 64;
+    let key_size = 128;
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    for digest in digests {
+        let alias = format!("ks_hmac_test_key_{}", digest.0);
+
+        let result = key_generations::map_ks_error(create_hmac_key_and_operation(
+            &sec_level,
+            &alias,
+            key_size,
+            mac_len,
+            min_mac_len,
+            digest,
+        ));
+
+        assert!(result.is_err());
+        assert_eq!(Error::Km(ErrorCode::INVALID_MAC_LENGTH), result.unwrap_err());
+    }
+}
diff --git a/keystore2/tests/keystore2_client_import_keys_tests.rs b/keystore2/tests/keystore2_client_import_keys_tests.rs
new file mode 100644
index 0000000..ecba402
--- /dev/null
+++ b/keystore2/tests/keystore2_client_import_keys_tests.rs
@@ -0,0 +1,635 @@
+// Copyright 2022, 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 nix::unistd::getuid;
+
+use openssl::rand::rand_bytes;
+use openssl::x509::X509;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+    ErrorCode::ErrorCode, HardwareAuthenticatorType::HardwareAuthenticatorType,
+    KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    AuthenticatorSpec::AuthenticatorSpec, Domain::Domain,
+    IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+    KeyMetadata::KeyMetadata, ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{
+    authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+use crate::ffi_test_utils::{create_wrapped_key, create_wrapped_key_additional_auth_data};
+
+use crate::keystore2_client_test_utils::{
+    encrypt_secure_key, encrypt_transport_key, has_default_keymint,
+    perform_sample_asym_sign_verify_op, perform_sample_hmac_sign_verify_op,
+    perform_sample_sym_key_decrypt_op, perform_sample_sym_key_encrypt_op, SAMPLE_PLAIN_TEXT,
+};
+
+pub fn import_rsa_sign_key_and_perform_sample_operation(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    domain: Domain,
+    nspace: i64,
+    alias: Option<String>,
+    import_params: authorizations::AuthSetBuilder,
+) {
+    let key_metadata =
+        key_generations::import_rsa_2048_key(sec_level, domain, nspace, alias, import_params)
+            .unwrap();
+
+    perform_sample_asym_sign_verify_op(
+        sec_level,
+        &key_metadata,
+        Some(PaddingMode::RSA_PSS),
+        Some(Digest::SHA_2_256),
+    );
+}
+
+fn perform_sym_key_encrypt_decrypt_op(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    key_metadata: &KeyMetadata,
+) {
+    let cipher_text = perform_sample_sym_key_encrypt_op(
+        sec_level,
+        PaddingMode::PKCS7,
+        BlockMode::ECB,
+        &mut None,
+        None,
+        &key_metadata.key,
+    )
+    .unwrap();
+
+    assert!(cipher_text.is_some());
+
+    let plain_text = perform_sample_sym_key_decrypt_op(
+        sec_level,
+        &cipher_text.unwrap(),
+        PaddingMode::PKCS7,
+        BlockMode::ECB,
+        &mut None,
+        None,
+        &key_metadata.key,
+    )
+    .unwrap();
+
+    assert!(plain_text.is_some());
+    assert_eq!(plain_text.unwrap(), SAMPLE_PLAIN_TEXT.to_vec());
+}
+
+fn build_secure_key_wrapper(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    secure_key: &[u8],
+    transport_key: &[u8],
+    nonce: &[u8],
+    aad: &[u8],
+    wrapping_key_metadata: &KeyMetadata,
+) -> Result<Vec<u8>, Error> {
+    // Encrypt secure key with transport key.
+    let transport_key_alias = format!("ks_transport_key_aes_256_key_test_{}", getuid());
+    let transport_key_metadata =
+        key_generations::import_transport_key(sec_level, Some(transport_key_alias), transport_key)
+            .unwrap();
+    let encrypted_secure_key = encrypt_secure_key(
+        sec_level,
+        secure_key,
+        aad,
+        nonce.to_vec(),
+        128,
+        &transport_key_metadata.key,
+    )
+    .unwrap();
+
+    // Extract GCM-tag and encrypted secure key data.
+    let encrypted_secure_key = encrypted_secure_key.unwrap();
+    let gcm_tag: Vec<u8> =
+        encrypted_secure_key[secure_key.len()..(encrypted_secure_key.len())].to_vec();
+    let encrypted_secure_key: Vec<u8> = encrypted_secure_key[0..secure_key.len()].to_vec();
+
+    // Get wrapping key puplic part and encrypt the transport key.
+    let cert_bytes = wrapping_key_metadata.certificate.as_ref().unwrap();
+    let cert = X509::from_der(cert_bytes.as_ref()).unwrap();
+    let public_key = cert.public_key().unwrap();
+    let encrypted_transport_key = encrypt_transport_key(transport_key, &public_key).unwrap();
+
+    // Create `SecureKeyWrapper` ASN.1 DER-encoded data.
+    create_wrapped_key(&encrypted_secure_key, &encrypted_transport_key, nonce, &gcm_tag)
+}
+
+/// Import RSA key and verify imported key parameters. Try to create an operation using the
+/// imported key. Test should be able to create an operation successfully.
+#[test]
+fn keystore2_rsa_import_key_success() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = format!("ks_rsa_key_test_import_1_{}{}", getuid(), 2048);
+
+    let import_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::RSA)
+        .digest(Digest::SHA_2_256)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .padding_mode(PaddingMode::RSA_PSS)
+        .key_size(2048)
+        .rsa_public_exponent(65537)
+        .cert_not_before(0)
+        .cert_not_after(253402300799000);
+
+    import_rsa_sign_key_and_perform_sample_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias),
+        import_params,
+    );
+}
+
+/// Import RSA key without providing key-size and public exponent in import key parameters list.
+/// Let Key-size and public-exponent to be determined from the imported key material. Verify
+/// imported key parameters. Try to create an operation using the imported key. Test should be
+/// able to create an operation successfully.
+#[test]
+fn keystore2_rsa_import_key_determine_key_size_and_pub_exponent() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = format!("ks_rsa_key_test_import_2_{}{}", getuid(), 2048);
+
+    // key-size and public-exponent shouldn't be specified in import key parameters list.
+    let import_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::RSA)
+        .digest(Digest::SHA_2_256)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .padding_mode(PaddingMode::RSA_PSS)
+        .cert_not_before(0)
+        .cert_not_after(253402300799000);
+
+    import_rsa_sign_key_and_perform_sample_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias),
+        import_params,
+    );
+}
+
+/// Try to import RSA key with wrong key size as import-key-parameter. Test should fail to import
+/// a key with `IMPORT_PARAMETER_MISMATCH` error code.
+#[test]
+fn keystore2_rsa_import_key_fails_with_keysize_param_mismatch_error() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = format!("ks_rsa_key_test_import_3_{}{}", getuid(), 2048);
+
+    let import_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::RSA)
+        .digest(Digest::SHA_2_256)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .padding_mode(PaddingMode::RSA_PSS)
+        .key_size(1024) // Wrong key size is specified, (actual key-size is 2048).
+        .rsa_public_exponent(65537)
+        .cert_not_before(0)
+        .cert_not_after(253402300799000);
+
+    let result = key_generations::map_ks_error(sec_level.importKey(
+        &KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None },
+        None,
+        &import_params,
+        0,
+        key_generations::RSA_2048_KEY,
+    ));
+
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::IMPORT_PARAMETER_MISMATCH), result.unwrap_err());
+}
+
+/// Try to import RSA key with wrong public-exponent as import-key-parameter.
+/// Test should fail to import a key with `IMPORT_PARAMETER_MISMATCH` error code.
+#[test]
+fn keystore2_rsa_import_key_fails_with_public_exponent_param_mismatch_error() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = format!("ks_rsa_key_test_import_4_{}{}", getuid(), 2048);
+
+    let import_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::RSA)
+        .digest(Digest::SHA_2_256)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .padding_mode(PaddingMode::RSA_PSS)
+        .key_size(2048)
+        .rsa_public_exponent(3) // This doesn't match the key.
+        .cert_not_before(0)
+        .cert_not_after(253402300799000);
+
+    let result = key_generations::map_ks_error(sec_level.importKey(
+        &KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None },
+        None,
+        &import_params,
+        0,
+        key_generations::RSA_2048_KEY,
+    ));
+
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::IMPORT_PARAMETER_MISMATCH), result.unwrap_err());
+}
+
+/// Try to import a key with multiple purposes. Test should fail to import a key with
+/// `INCOMPATIBLE_PURPOSE` error code. If the backend is `keymaster` then `importKey` shall be
+/// successful.
+#[test]
+fn keystore2_rsa_import_key_with_multipurpose_fails_incompt_purpose_error() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = format!("ks_rsa_key_test_import_5_{}{}", getuid(), 2048);
+
+    let import_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::RSA)
+        .digest(Digest::SHA_2_256)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::ATTEST_KEY)
+        .padding_mode(PaddingMode::RSA_PSS)
+        .key_size(2048)
+        .rsa_public_exponent(65537)
+        .cert_not_before(0)
+        .cert_not_after(253402300799000);
+
+    let result = key_generations::map_ks_error(sec_level.importKey(
+        &KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None },
+        None,
+        &import_params,
+        0,
+        key_generations::RSA_2048_KEY,
+    ));
+
+    if has_default_keymint() {
+        assert!(result.is_err());
+        assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+    } else {
+        assert!(result.is_ok());
+    }
+}
+
+/// Import EC key and verify imported key parameters. Let ec-curve to be determined from the
+/// imported key material. Try to create an operation using the imported key. Test should be
+/// able to create an operation successfully.
+#[test]
+fn keystore2_import_ec_key_success() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = format!("ks_ec_key_test_import_1_{}{}", getuid(), 256);
+
+    // Don't specify ec-curve.
+    let import_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::EC)
+        .digest(Digest::SHA_2_256)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .cert_not_before(0)
+        .cert_not_after(253402300799000);
+
+    let key_metadata = key_generations::import_ec_p_256_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias),
+        import_params,
+    )
+    .expect("Failed to import EC key.");
+
+    perform_sample_asym_sign_verify_op(&sec_level, &key_metadata, None, Some(Digest::SHA_2_256));
+}
+
+/// Try to import EC key with wrong ec-curve as import-key-parameter. Test should fail to import a
+/// key with `IMPORT_PARAMETER_MISMATCH` error code.
+#[test]
+fn keystore2_ec_import_key_fails_with_mismatch_curve_error() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = format!("ks_ec_key_test_import_1_{}{}", getuid(), 256);
+
+    let import_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::EC)
+        .digest(Digest::SHA_2_256)
+        .ec_curve(EcCurve::P_224) // It doesn't match with key material.
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .cert_not_before(0)
+        .cert_not_after(253402300799000);
+
+    let result = key_generations::map_ks_error(sec_level.importKey(
+        &KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None },
+        None,
+        &import_params,
+        0,
+        key_generations::EC_P_256_KEY,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::IMPORT_PARAMETER_MISMATCH), result.unwrap_err());
+}
+
+/// Import AES key and verify key parameters. Try to create an operation using the imported key.
+/// Test should be able to create an operation successfully.
+#[test]
+fn keystore2_import_aes_key_success() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = format!("ks_aes_key_test_import_1_{}{}", getuid(), 256);
+    let key_metadata = key_generations::import_aes_key(&sec_level, Domain::APP, -1, Some(alias))
+        .expect("Failed to import AES key.");
+
+    perform_sym_key_encrypt_decrypt_op(&sec_level, &key_metadata);
+}
+
+/// Import 3DES key and verify key parameters. Try to create an operation using the imported key.
+/// Test should be able to create an operation successfully.
+#[test]
+fn keystore2_import_3des_key_success() {
+    let keystore2 = get_keystore_service();
+    let sec_level = key_generations::map_ks_error(
+        keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT),
+    )
+    .unwrap();
+
+    let alias = format!("ks_3des_key_test_import_1_{}{}", getuid(), 168);
+
+    let key_metadata = key_generations::import_3des_key(&sec_level, Domain::APP, -1, Some(alias))
+        .expect("Failed to import 3DES key.");
+
+    perform_sym_key_encrypt_decrypt_op(&sec_level, &key_metadata);
+}
+
+/// Import HMAC key and verify key parameters. Try to create an operation using the imported key.
+/// Test should be able to create an operation successfully.
+#[test]
+fn keystore2_import_hmac_key_success() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = format!("ks_hmac_key_test_import_1_{}", getuid());
+
+    let key_metadata = key_generations::import_hmac_key(&sec_level, Domain::APP, -1, Some(alias))
+        .expect("Failed to import HMAC key.");
+
+    perform_sample_hmac_sign_verify_op(&sec_level, &key_metadata.key);
+}
+
+/// This test creates a wrapped key data and imports it. Validates the imported wrapped key.
+///     1. Create a wrapped key material to import, as ASN.1 DER-encoded data corresponding to the
+///        `SecureKeyWrapper` schema defined in IKeyMintDevice.aidl.
+///     2. Import wrapped key and use it for crypto operations.
+/// Test should successfully import the wrapped key and perform crypto operations.
+#[test]
+fn keystore2_create_wrapped_key_and_import_wrapped_key_success() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let mut secure_key = [0; 32];
+    rand_bytes(&mut secure_key).unwrap();
+
+    let mut transport_key = [0; 32];
+    rand_bytes(&mut transport_key).unwrap();
+
+    let mut nonce = [0; 12];
+    rand_bytes(&mut nonce).unwrap();
+
+    // Import wrapping key.
+    let wrapping_key_alias = format!("ks_wrapping_key_test_import_2_{}_2048", getuid());
+    let wrapping_key_metadata = key_generations::import_wrapping_key(
+        &sec_level,
+        key_generations::RSA_2048_KEY,
+        Some(wrapping_key_alias),
+    )
+    .unwrap();
+
+    // Create the DER-encoded representation of `KeyDescription` schema defined in
+    // `IKeyMintDevice.aidl` and use it as additional authenticated data.
+    let aad = create_wrapped_key_additional_auth_data().unwrap();
+
+    // Build ASN.1 DER-encoded wrapped key material as described in `SecureKeyWrapper` schema.
+    let wrapped_key_data = build_secure_key_wrapper(
+        &sec_level,
+        &secure_key,
+        &transport_key,
+        &nonce,
+        &aad,
+        &wrapping_key_metadata,
+    )
+    .unwrap();
+
+    // Unwrap the key. Import wrapped key.
+    let secured_key_alias = format!("ks_wrapped_aes_key_{}", getuid());
+    let secured_key_metadata = key_generations::import_wrapped_key(
+        &sec_level,
+        Some(secured_key_alias),
+        &wrapping_key_metadata,
+        Some(wrapped_key_data.to_vec()),
+    )
+    .unwrap();
+
+    perform_sym_key_encrypt_decrypt_op(&sec_level, &secured_key_metadata);
+}
+
+/// Create a wrapped key data with invalid Additional Authenticated Data (AAD) and
+/// try to import wrapped key.
+///     1. Create a wrapped key material with invalid AAD to import, as ASN.1 DER-encoded
+///        data corresponding to the `SecureKeyWrapper` schema defined in IKeyMintDevice.aidl.
+///     2. Import wrapped key and use it for crypto operations.
+/// Test should fail to import the wrapped key with error code `VERIFICATION_FAILED`.
+#[test]
+fn keystore2_create_wrapped_key_with_invalid_aad_and_import_wrapped_key_fail() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let mut secure_key = [0; 32];
+    rand_bytes(&mut secure_key).unwrap();
+
+    let mut transport_key = [0; 32];
+    rand_bytes(&mut transport_key).unwrap();
+
+    let mut nonce = [0; 12];
+    rand_bytes(&mut nonce).unwrap();
+
+    // Import wrapping key.
+    let wrapping_key_alias = format!("ks_wrapping_key_test_import_2_{}_2048", getuid());
+    let wrapping_key_metadata = key_generations::import_wrapping_key(
+        &sec_level,
+        key_generations::RSA_2048_KEY,
+        Some(wrapping_key_alias),
+    )
+    .unwrap();
+
+    // Use invalid value as the additional authenticated data.
+    let aad = b"foo";
+
+    // Build ASN.1 DER-encoded wrapped key material as described in `SecureKeyWrapper` schema.
+    let wrapped_key_data = build_secure_key_wrapper(
+        &sec_level,
+        &secure_key,
+        &transport_key,
+        &nonce,
+        aad,
+        &wrapping_key_metadata,
+    )
+    .unwrap();
+
+    // Unwrap the key. Import wrapped key.
+    let secured_key_alias = format!("ks_wrapped_aes_key_{}", getuid());
+    let result = key_generations::map_ks_error(key_generations::import_wrapped_key(
+        &sec_level,
+        Some(secured_key_alias),
+        &wrapping_key_metadata,
+        Some(wrapped_key_data.to_vec()),
+    ));
+
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::VERIFICATION_FAILED), result.unwrap_err());
+}
+
+/// Import wrapped AES key and use it for crypto operations. Test should import wrapped key and
+/// perform crypto operations successfully.
+#[test]
+fn keystore2_import_wrapped_key_success() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = format!("ks_wrapped_key_test_import_1_{}_256", getuid());
+    let wrapping_key_alias = format!("ks_wrapping_key_test_import_1_{}_2048", getuid());
+
+    let wrapping_key_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::RSA)
+        .digest(Digest::SHA_2_256)
+        .purpose(KeyPurpose::ENCRYPT)
+        .purpose(KeyPurpose::DECRYPT)
+        .purpose(KeyPurpose::WRAP_KEY)
+        .padding_mode(PaddingMode::RSA_OAEP)
+        .key_size(2048)
+        .rsa_public_exponent(65537)
+        .cert_not_before(0)
+        .cert_not_after(253402300799000);
+
+    let key_metadata = key_generations::import_wrapping_key_and_wrapped_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias),
+        Some(wrapping_key_alias),
+        wrapping_key_params,
+    )
+    .expect("Failed to import wrapped key.");
+
+    // Try to perform operations using wrapped key.
+    perform_sym_key_encrypt_decrypt_op(&sec_level, &key_metadata);
+}
+
+/// Import wrapping-key without specifying KeyPurpose::WRAP_KEY in import key parameters. Try to
+/// use this as wrapping-key for importing wrapped-key. Test should fail with an error code
+/// `INCOMPATIBLE_PURPOSE` to import wrapped-key using a wrapping-key which doesn't possess
+/// `WRAP_KEY` purpose.
+#[test]
+fn keystore2_import_wrapped_key_fails_with_wrong_purpose() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let wrapping_key_alias = format!("ks_wrapping_key_test_import_2_{}_2048", getuid());
+    let alias = format!("ks_wrapped_key_test_import_2_{}_256", getuid());
+
+    // In this KeyPurpose::WRAP_KEY is missing.
+    let wrapping_key_params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::RSA)
+        .digest(Digest::SHA_2_256)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .padding_mode(PaddingMode::RSA_OAEP)
+        .key_size(2048)
+        .rsa_public_exponent(65537)
+        .cert_not_before(0)
+        .cert_not_after(253402300799000);
+
+    let result =
+        key_generations::map_ks_error(key_generations::import_wrapping_key_and_wrapped_key(
+            &sec_level,
+            Domain::APP,
+            -1,
+            Some(alias),
+            Some(wrapping_key_alias),
+            wrapping_key_params,
+        ));
+
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+}
+
+/// Try to import wrapped key whose wrapping key is missing in Android Keystore.
+/// Test should fail to import wrapped key with `ResponseCode::KEY_NOT_FOUND`.
+#[test]
+fn keystore2_import_wrapped_key_fails_with_missing_wrapping_key() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let unwrap_params = authorizations::AuthSetBuilder::new()
+        .digest(Digest::SHA_2_256)
+        .padding_mode(PaddingMode::RSA_OAEP);
+
+    let authenticator_spec: &[AuthenticatorSpec] = &[AuthenticatorSpec {
+        authenticatorType: HardwareAuthenticatorType::NONE,
+        authenticatorId: 0,
+    }];
+
+    let alias = format!("ks_wrapped_key_test_import_3_{}_256", getuid());
+
+    // Wrapping key with this alias doesn't exist.
+    let wrapping_key_alias = format!("ks_wrapping_key_not_exist_{}_2048", getuid());
+
+    let result = key_generations::map_ks_error(sec_level.importWrappedKey(
+        &KeyDescriptor {
+            domain: Domain::APP,
+            nspace: -1,
+            alias: Some(alias),
+            blob: Some(key_generations::WRAPPED_KEY.to_vec()),
+        },
+        &KeyDescriptor {
+            domain: Domain::APP,
+            nspace: -1,
+            alias: Some(wrapping_key_alias),
+            blob: None,
+        },
+        None,
+        &unwrap_params,
+        authenticator_spec,
+    ));
+
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
diff --git a/keystore2/tests/keystore2_client_key_agreement_tests.rs b/keystore2/tests/keystore2_client_key_agreement_tests.rs
new file mode 100644
index 0000000..6b2e3c2
--- /dev/null
+++ b/keystore2/tests/keystore2_client_key_agreement_tests.rs
@@ -0,0 +1,179 @@
+// Copyright 2022, 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 nix::unistd::getuid;
+
+use openssl::ec::{EcGroup, EcKey};
+use openssl::error::ErrorStack;
+use openssl::nid::Nid;
+use openssl::pkey::{PKey, PKeyRef, Private, Public};
+use openssl::pkey_ctx::PkeyCtx;
+use openssl::x509::X509;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Digest::Digest, EcCurve::EcCurve, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose,
+    SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+    KeyMetadata::KeyMetadata,
+};
+
+use keystore2_test_utils::{
+    authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+/// This macro is used to verify that the key agreement works for the given curve.
+macro_rules! test_ec_key_agree {
+    ( $test_name:ident, $ec_curve:expr ) => {
+        #[test]
+        fn $test_name() {
+            perform_ec_key_agreement($ec_curve);
+        }
+    };
+}
+
+// Get the KeyMint key's public part.
+fn get_keymint_public_key(keymint_key: &KeyMetadata) -> Result<PKey<Public>, ErrorStack> {
+    let cert_bytes = keymint_key.certificate.as_ref().unwrap();
+    let cert = X509::from_der(cert_bytes.as_ref()).unwrap();
+    cert.public_key()
+}
+
+// Perform local ECDH between the two keys and check the derived secrets are the same.
+fn check_agreement(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    keymint_key: &KeyDescriptor,
+    keymint_pub_key: &PKey<Public>,
+    local_key: &PKeyRef<Private>,
+    local_pub_key: &[u8],
+) {
+    let authorizations = authorizations::AuthSetBuilder::new().purpose(KeyPurpose::AGREE_KEY);
+    let key_agree_op = sec_level.createOperation(keymint_key, &authorizations, false).unwrap();
+    assert!(key_agree_op.iOperation.is_some());
+
+    let op = key_agree_op.iOperation.unwrap();
+    let secret = op.finish(Some(local_pub_key), None).unwrap();
+    assert!(secret.is_some());
+
+    let mut ctx = PkeyCtx::new(local_key).unwrap();
+    ctx.derive_init().unwrap();
+    ctx.derive_set_peer(keymint_pub_key).unwrap();
+    let mut peer_secret = vec![];
+    ctx.derive_to_vec(&mut peer_secret).unwrap();
+
+    assert_eq!(secret.unwrap(), peer_secret);
+}
+
+fn ec_curve_to_openrssl_curve_name(ec_curve: &EcCurve) -> Nid {
+    match *ec_curve {
+        EcCurve::P_224 => Nid::SECP224R1,
+        EcCurve::P_256 => Nid::X9_62_PRIME256V1,
+        EcCurve::P_384 => Nid::SECP384R1,
+        EcCurve::P_521 => Nid::SECP521R1,
+        _ => Nid::UNDEF,
+    }
+}
+
+/// Generate two EC keys with given curve from KeyMint and OpeanSSL. Perform local ECDH between
+/// them and verify that the derived secrets are the same.
+fn perform_ec_key_agreement(ec_curve: EcCurve) {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let openssl_ec_curve = ec_curve_to_openrssl_curve_name(&ec_curve);
+
+    let alias = format!("ks_ec_test_key_agree_{}", getuid());
+    let keymint_key = key_generations::generate_ec_agree_key(
+        &sec_level,
+        ec_curve,
+        Digest::SHA_2_256,
+        Domain::APP,
+        -1,
+        Some(alias),
+    )
+    .unwrap();
+
+    let keymint_pub_key = get_keymint_public_key(&keymint_key).unwrap();
+
+    let group = EcGroup::from_curve_name(openssl_ec_curve).unwrap();
+    let ec_key = EcKey::generate(&group).unwrap();
+    let local_key = PKey::from_ec_key(ec_key).unwrap();
+    let local_pub_key = local_key.public_key_to_der().unwrap();
+
+    check_agreement(&sec_level, &keymint_key.key, &keymint_pub_key, &local_key, &local_pub_key);
+}
+
+test_ec_key_agree!(test_ec_p224_key_agreement, EcCurve::P_224);
+test_ec_key_agree!(test_ec_p256_key_agreement, EcCurve::P_256);
+test_ec_key_agree!(test_ec_p384_key_agreement, EcCurve::P_384);
+test_ec_key_agree!(test_ec_p521_key_agreement, EcCurve::P_521);
+
+/// Generate two EC keys with curve `CURVE_25519` from KeyMint and OpeanSSL.
+/// Perform local ECDH between them and verify that the derived secrets are the same.
+#[test]
+fn keystore2_ec_25519_agree_key_success() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = format!("ks_ec_25519_test_key_agree_{}", getuid());
+    let keymint_key = key_generations::generate_ec_agree_key(
+        &sec_level,
+        EcCurve::CURVE_25519,
+        Digest::NONE,
+        Domain::APP,
+        -1,
+        Some(alias),
+    )
+    .unwrap();
+
+    let keymint_pub_key = get_keymint_public_key(&keymint_key).unwrap();
+
+    let local_key = PKey::generate_x25519().unwrap();
+    let local_pub_key = local_key.public_key_to_der().unwrap();
+
+    check_agreement(&sec_level, &keymint_key.key, &keymint_pub_key, &local_key, &local_pub_key);
+}
+
+/// Generate two EC keys with different curves and try to perform local ECDH. Since keys are using
+/// different curves operation should fail with `ErrorCode:INVALID_ARGUMENT`.
+#[test]
+fn keystore2_ec_agree_key_with_different_curves_fail() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = format!("ks_test_key_agree_fail{}", getuid());
+    let keymint_key = key_generations::generate_ec_agree_key(
+        &sec_level,
+        EcCurve::P_256,
+        Digest::SHA_2_256,
+        Domain::APP,
+        -1,
+        Some(alias),
+    )
+    .unwrap();
+
+    let local_key = PKey::generate_x25519().unwrap();
+    let local_pub_key = local_key.public_key_to_der().unwrap();
+
+    // If the keys are using different curves KeyMint should fail with
+    // ErrorCode:INVALID_ARGUMENT.
+    let authorizations = authorizations::AuthSetBuilder::new().purpose(KeyPurpose::AGREE_KEY);
+    let key_agree_op = sec_level.createOperation(&keymint_key.key, &authorizations, false).unwrap();
+    assert!(key_agree_op.iOperation.is_some());
+
+    let op = key_agree_op.iOperation.unwrap();
+    let result = key_generations::map_ks_error(op.finish(Some(&local_pub_key), None));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INVALID_ARGUMENT), result.unwrap_err());
+}
diff --git a/keystore2/tests/keystore2_client_key_id_domain_tests.rs b/keystore2/tests/keystore2_client_key_id_domain_tests.rs
new file mode 100644
index 0000000..09b1378
--- /dev/null
+++ b/keystore2/tests/keystore2_client_key_id_domain_tests.rs
@@ -0,0 +1,257 @@
+// Copyright 2022, 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 nix::unistd::getuid;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Digest::Digest, EcCurve::EcCurve, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{
+    authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+use crate::keystore2_client_test_utils::perform_sample_sign_operation;
+
+/// Try to generate a key with `Domain::KEY_ID`, test should fail with an error code
+/// `SYSTEM_ERROR`. `Domain::KEY_ID` is not allowed to use for generating a key. Key id is returned
+/// by Keystore2 after a key has been mapped from an alias.
+#[test]
+fn keystore2_generate_key_with_key_id_domain_expect_sys_error() {
+    let alias = "ks_gen_key_id_test_key";
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let result = key_generations::map_ks_error(key_generations::generate_ec_key(
+        &sec_level,
+        Domain::KEY_ID,
+        key_generations::SELINUX_SHELL_NAMESPACE,
+        Some(alias.to_string()),
+        EcCurve::P_256,
+        Digest::SHA_2_256,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::SYSTEM_ERROR), result.unwrap_err());
+}
+
+/// Generate a key and try to load the generated key using KEY_ID as domain. Create an
+/// operation using key which is loaded with domain as KEY_ID. Test should create an operation
+/// successfully.
+#[test]
+fn keystore2_find_key_with_key_id_as_domain() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = "ks_key_id_test_key";
+
+    let key_metadata = key_generations::generate_ec_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        EcCurve::P_256,
+        Digest::SHA_2_256,
+    )
+    .expect("Failed to generate a EC key.");
+
+    // Try to load the above generated key with KEY_ID as domain.
+    let key_entry_response = keystore2
+        .getKeyEntry(&KeyDescriptor {
+            domain: Domain::KEY_ID,
+            nspace: key_metadata.key.nspace,
+            alias: Some(alias.to_string()),
+            blob: None,
+        })
+        .expect("Error in getKeyEntry to load a key with domain KEY_ID.");
+
+    // Verify above found key is same the one generated.
+    assert_eq!(key_metadata.key, key_entry_response.metadata.key);
+    assert_eq!(key_metadata.certificate, key_entry_response.metadata.certificate);
+    assert_eq!(key_metadata.certificateChain, key_entry_response.metadata.certificateChain);
+    assert_eq!(key_metadata.key.nspace, key_entry_response.metadata.key.nspace);
+
+    // Try to create an operation using above loaded key, operation should be created
+    // successfully.
+    let op_response = sec_level
+        .createOperation(
+            &key_entry_response.metadata.key,
+            &authorizations::AuthSetBuilder::new()
+                .purpose(KeyPurpose::SIGN)
+                .digest(Digest::SHA_2_256),
+            false,
+        )
+        .expect("Error in creation of operation.");
+
+    assert!(op_response.iOperation.is_some());
+    assert_eq!(
+        Ok(()),
+        key_generations::map_ks_error(perform_sample_sign_operation(
+            &op_response.iOperation.unwrap()
+        ))
+    );
+}
+
+/// Generate a key with an alias. Generate another key and bind it to the same alias.
+/// Try to create an operation using previously generated key. Creation of an operation should
+/// fail because previously generated key material is no longer accessible. Test should successfully
+/// create an operation using the rebound key.
+#[test]
+fn keystore2_key_id_alias_rebind_verify_by_alias() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = format!("ks_key_id_test_alias_rebind_1_{}", getuid());
+
+    let key_metadata = key_generations::generate_ec_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        EcCurve::P_256,
+        Digest::SHA_2_256,
+    )
+    .expect("Failed to generate a EC key.");
+
+    // Generate a key with same alias as above generated key, so that alias will be rebound
+    // to this key.
+    let new_key_metadata = key_generations::generate_ec_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias),
+        EcCurve::P_256,
+        Digest::SHA_2_256,
+    )
+    .expect("Failed to generate a rebound EC key.");
+
+    assert_ne!(key_metadata.key, new_key_metadata.key);
+    assert_ne!(key_metadata.certificate, new_key_metadata.certificate);
+    assert_ne!(key_metadata.key.nspace, new_key_metadata.key.nspace);
+
+    // Try to create an operation using previously generated key_metadata.
+    // It should fail as previously generated key material is no longer remains valid.
+    let result = key_generations::map_ks_error(sec_level.createOperation(
+        &key_metadata.key,
+        &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+        false,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+
+    // Try to create an operation using rebound key, operation should be created
+    // successfully.
+    let op_response = sec_level
+        .createOperation(
+            &new_key_metadata.key,
+            &authorizations::AuthSetBuilder::new()
+                .purpose(KeyPurpose::SIGN)
+                .digest(Digest::SHA_2_256),
+            false,
+        )
+        .expect("Error in creation of operation using rebound key.");
+
+    assert!(op_response.iOperation.is_some());
+    assert_eq!(
+        Ok(()),
+        key_generations::map_ks_error(perform_sample_sign_operation(
+            &op_response.iOperation.unwrap()
+        ))
+    );
+}
+
+/// Generate a key with an alias. Load the generated key with `Domain::KEY_ID`. Generate another
+/// key and bind it to the same alias. Try to create an operation using the key loaded with domain
+/// `KEY_ID`. Creation of an operation should fail because originally loaded key no longer exists.
+/// Test should successfully create an operation using the rebound key.
+#[test]
+fn keystore2_key_id_alias_rebind_verify_by_key_id() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = format!("ks_key_id_test_alias_rebind_2_{}", getuid());
+
+    let key_metadata = key_generations::generate_ec_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        EcCurve::P_256,
+        Digest::SHA_2_256,
+    )
+    .expect("Failed to generate a EC key.");
+
+    // Load the above generated key with KEY_ID as domain.
+    let key_entry_response = keystore2
+        .getKeyEntry(&KeyDescriptor {
+            domain: Domain::KEY_ID,
+            nspace: key_metadata.key.nspace,
+            alias: Some(alias.to_string()),
+            blob: None,
+        })
+        .expect("Error in getKeyEntry to load a key with domain KEY_ID.");
+
+    // Verify above found key is same the one generated.
+    assert_eq!(key_metadata.key, key_entry_response.metadata.key);
+    assert_eq!(key_metadata.certificate, key_entry_response.metadata.certificate);
+    assert_eq!(key_metadata.certificateChain, key_entry_response.metadata.certificateChain);
+    assert_eq!(key_metadata.key.nspace, key_entry_response.metadata.key.nspace);
+
+    // Generate another key with same alias as above generated key, so that alias will be rebound
+    // to this key.
+    let new_key_metadata = key_generations::generate_ec_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias),
+        EcCurve::P_256,
+        Digest::SHA_2_256,
+    )
+    .expect("Failed to generate a rebound EC key.");
+
+    // Verify that an alias is rebound to a new key.
+    assert_eq!(key_metadata.key.alias, new_key_metadata.key.alias);
+    assert_ne!(key_metadata.key, new_key_metadata.key);
+    assert_ne!(key_metadata.certificate, new_key_metadata.certificate);
+    assert_ne!(key_metadata.key.nspace, new_key_metadata.key.nspace);
+
+    // Try to create an operation using previously loaded key_entry_response.
+    // It should fail as previously generated key material is no longer valid.
+    let result = key_generations::map_ks_error(sec_level.createOperation(
+        &key_entry_response.metadata.key,
+        &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+        false,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+
+    // Try to create an operation using rebound key, operation should be created
+    // successfully.
+    let op_response = sec_level
+        .createOperation(
+            &new_key_metadata.key,
+            &authorizations::AuthSetBuilder::new()
+                .purpose(KeyPurpose::SIGN)
+                .digest(Digest::SHA_2_256),
+            false,
+        )
+        .expect("Error in creation of operation using rebound key.");
+
+    assert!(op_response.iOperation.is_some());
+    assert_eq!(
+        Ok(()),
+        key_generations::map_ks_error(perform_sample_sign_operation(
+            &op_response.iOperation.unwrap()
+        ))
+    );
+}
diff --git a/keystore2/tests/keystore2_client_list_entries_tests.rs b/keystore2/tests/keystore2_client_list_entries_tests.rs
new file mode 100644
index 0000000..3b656c3
--- /dev/null
+++ b/keystore2/tests/keystore2_client_list_entries_tests.rs
@@ -0,0 +1,253 @@
+// Copyright 2022, 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 nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+use std::collections::HashSet;
+use std::fmt::Write;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor,
+    KeyPermission::KeyPermission, ResponseCode::ResponseCode,
+};
+
+use crate::keystore2_client_test_utils::delete_app_key;
+use keystore2_test_utils::{get_keystore_service, key_generations, key_generations::Error, run_as};
+
+/// Try to find a key with given key parameters using `listEntries` API.
+fn key_alias_exists(
+    keystore2: &binder::Strong<dyn IKeystoreService>,
+    domain: Domain,
+    nspace: i64,
+    alias: String,
+) -> bool {
+    let key_descriptors = keystore2.listEntries(domain, nspace).unwrap();
+    let alias_count = key_descriptors
+        .into_iter()
+        .map(|key| key.alias.unwrap())
+        .filter(|key_alias| *key_alias == alias)
+        .count();
+
+    alias_count != 0
+}
+
+/// List key entries with domain as SELINUX and APP.
+/// 1. Generate a key with domain as SELINUX and find this key entry in list of keys retrieved from
+///    `listEntries` with domain SELINUX. Test should be able find this key entry successfully.
+/// 2. Grant above generated Key to a user.
+/// 3. In a user context, generate a new key with domain as APP. Try to list the key entries with
+///    domain APP. Test should find only one key entry that should be the key generated in user
+///    context. GRANT keys shouldn't be part of this list.
+#[test]
+fn keystore2_list_entries_success() {
+    static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+    static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+    const USER_ID: u32 = 91;
+    const APPLICATION_ID: u32 = 10006;
+    static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_GID: u32 = GRANTEE_UID;
+
+    unsafe {
+        run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+            let keystore2 = get_keystore_service();
+            let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+            let alias = format!("list_entries_grant_key1_{}", getuid());
+
+            // Make sure there is no key exist with this `alias` in `SELINUX` domain and
+            // `SELINUX_SHELL_NAMESPACE` namespace.
+            if key_alias_exists(
+                &keystore2,
+                Domain::SELINUX,
+                key_generations::SELINUX_SHELL_NAMESPACE,
+                alias.to_string(),
+            ) {
+                keystore2
+                    .deleteKey(&KeyDescriptor {
+                        domain: Domain::SELINUX,
+                        nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+                        alias: Some(alias.to_string()),
+                        blob: None,
+                    })
+                    .unwrap();
+            }
+
+            // Generate a key with above defined `alias`.
+            let key_metadata = key_generations::generate_ec_p256_signing_key(
+                &sec_level,
+                Domain::SELINUX,
+                key_generations::SELINUX_SHELL_NAMESPACE,
+                Some(alias.to_string()),
+                None,
+            )
+            .unwrap();
+
+            // Verify that above generated key entry is listed with domain SELINUX and
+            // namespace SELINUX_SHELL_NAMESPACE
+            assert!(key_alias_exists(
+                &keystore2,
+                Domain::SELINUX,
+                key_generations::SELINUX_SHELL_NAMESPACE,
+                alias,
+            ));
+
+            // Grant a key with GET_INFO permission.
+            let access_vector = KeyPermission::GET_INFO.0;
+            keystore2
+                .grant(&key_metadata.key, GRANTEE_UID.try_into().unwrap(), access_vector)
+                .unwrap();
+        })
+    };
+
+    // In user context validate list of key entries associated with it.
+    unsafe {
+        run_as::run_as(
+            GRANTEE_CTX,
+            Uid::from_raw(GRANTEE_UID),
+            Gid::from_raw(GRANTEE_GID),
+            move || {
+                let keystore2 = get_keystore_service();
+                let sec_level =
+                    keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+                let alias = format!("list_entries_success_key{}", getuid());
+
+                let key_metadata = key_generations::generate_ec_p256_signing_key(
+                    &sec_level,
+                    Domain::APP,
+                    -1,
+                    Some(alias.to_string()),
+                    None,
+                )
+                .unwrap();
+
+                // Make sure there is only one key entry exist and that should be the same key
+                // generated in this user context. Granted key shouldn't be included in this list.
+                let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
+                assert_eq!(1, key_descriptors.len());
+
+                let key = key_descriptors.get(0).unwrap();
+                assert_eq!(key.alias, Some(alias));
+                assert_eq!(key.nspace, GRANTEE_UID.try_into().unwrap());
+                assert_eq!(key.domain, Domain::APP);
+
+                keystore2.deleteKey(&key_metadata.key).unwrap();
+
+                let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
+                assert_eq!(0, key_descriptors.len());
+            },
+        )
+    };
+}
+
+/// Try to list the key entries with domain SELINUX from user context where user doesn't possesses
+/// `GET_INFO` permission for specified namespace. Test should fail to list key entries with error
+/// response code `PERMISSION_DENIED`.
+#[test]
+fn keystore2_list_entries_fails_perm_denied() {
+    let auid = 91 * AID_USER_OFFSET + 10001;
+    let agid = 91 * AID_USER_OFFSET + 10001;
+    static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+    unsafe {
+        run_as::run_as(TARGET_CTX, Uid::from_raw(auid), Gid::from_raw(agid), move || {
+            let keystore2 = get_keystore_service();
+
+            let result = key_generations::map_ks_error(
+                keystore2.listEntries(Domain::SELINUX, key_generations::SELINUX_SHELL_NAMESPACE),
+            );
+            assert!(result.is_err());
+            assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+        })
+    };
+}
+
+/// Try to list key entries with domain BLOB. Test should fail with error repose code
+/// `INVALID_ARGUMENT`.
+#[test]
+fn keystore2_list_entries_fails_invalid_arg() {
+    let keystore2 = get_keystore_service();
+
+    let result = key_generations::map_ks_error(
+        keystore2.listEntries(Domain::BLOB, key_generations::SELINUX_SHELL_NAMESPACE),
+    );
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
+}
+
+/// Import large number of Keystore entries with long aliases and try to list aliases
+/// of all the entries in the keystore.
+#[test]
+fn keystore2_list_entries_with_long_aliases_success() {
+    static CLIENT_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+    const USER_ID: u32 = 92;
+    const APPLICATION_ID: u32 = 10002;
+    static CLIENT_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+    static CLIENT_GID: u32 = CLIENT_UID;
+
+    unsafe {
+        run_as::run_as(CLIENT_CTX, Uid::from_raw(CLIENT_UID), Gid::from_raw(CLIENT_GID), || {
+            let keystore2 = get_keystore_service();
+            let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+            // Make sure there are no keystore entries exist before adding new entries.
+            let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
+            if !key_descriptors.is_empty() {
+                key_descriptors.into_iter().map(|key| key.alias.unwrap()).for_each(|alias| {
+                    delete_app_key(&keystore2, &alias).unwrap();
+                });
+            }
+
+            let mut imported_key_aliases = HashSet::new();
+
+            // Import 100 keys with aliases of length 6000.
+            for count in 1..101 {
+                let mut alias = String::new();
+                write!(alias, "{}_{}", "X".repeat(6000), count).unwrap();
+                imported_key_aliases.insert(alias.clone());
+
+                let result =
+                    key_generations::import_aes_key(&sec_level, Domain::APP, -1, Some(alias));
+                assert!(result.is_ok());
+            }
+
+            // b/222287335 Limiting Keystore `listEntries` API to return subset of the Keystore
+            // entries to avoid running out of binder buffer space.
+            // To verify that all the imported key aliases are present in Keystore,
+            //  - get the list of entries from Keystore
+            //  - check whether the retrieved key entries list is a subset of imported key aliases
+            //  - delete this subset of keystore entries from Keystore as well as from imported
+            //    list of key aliases
+            //  - continue above steps till it cleanup all the imported keystore entries.
+            while !imported_key_aliases.is_empty() {
+                let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
+
+                // Check retrieved key entries list is a subset of imported keys list.
+                assert!(key_descriptors
+                    .iter()
+                    .all(|key| imported_key_aliases.contains(key.alias.as_ref().unwrap())));
+
+                // Delete the listed key entries from Keystore as well as from imported keys list.
+                key_descriptors.into_iter().map(|key| key.alias.unwrap()).for_each(|alias| {
+                    delete_app_key(&keystore2, &alias).unwrap();
+                    assert!(imported_key_aliases.remove(&alias));
+                });
+            }
+
+            assert!(imported_key_aliases.is_empty());
+        })
+    };
+}
diff --git a/keystore2/tests/keystore2_client_operation_tests.rs b/keystore2/tests/keystore2_client_operation_tests.rs
new file mode 100644
index 0000000..19175dd
--- /dev/null
+++ b/keystore2/tests/keystore2_client_operation_tests.rs
@@ -0,0 +1,449 @@
+// Copyright 2022, 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 nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+use std::thread;
+use std::thread::JoinHandle;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Digest::Digest, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    CreateOperationResponse::CreateOperationResponse, Domain::Domain,
+    IKeystoreOperation::IKeystoreOperation, ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{
+    authorizations, get_keystore_service, key_generations, key_generations::Error, run_as,
+};
+
+use crate::keystore2_client_test_utils::{
+    create_signing_operation, execute_op_run_as_child, perform_sample_sign_operation,
+    BarrierReached, ForcedOp, TestOutcome,
+};
+
+/// Create `max_ops` number child processes with the given context and perform an operation under each
+/// child process.
+pub fn create_operations(
+    target_ctx: &'static str,
+    forced_op: ForcedOp,
+    max_ops: i32,
+) -> Vec<run_as::ChildHandle<TestOutcome, BarrierReached>> {
+    let alias = format!("ks_op_test_key_{}", getuid());
+    let base_gid = 99 * AID_USER_OFFSET + 10001;
+    let base_uid = 99 * AID_USER_OFFSET + 10001;
+    (0..max_ops)
+        .map(|i| {
+            execute_op_run_as_child(
+                target_ctx,
+                Domain::APP,
+                key_generations::SELINUX_SHELL_NAMESPACE,
+                Some(alias.to_string()),
+                Uid::from_raw(base_uid + (i as u32)),
+                Gid::from_raw(base_gid + (i as u32)),
+                forced_op,
+            )
+        })
+        .collect()
+}
+
+/// Executes an operation in a thread. Expect an `OPERATION_BUSY` error in case of operation
+/// failure. Returns True if `OPERATION_BUSY` error is encountered otherwise returns false.
+fn perform_op_busy_in_thread(op: binder::Strong<dyn IKeystoreOperation>) -> JoinHandle<bool> {
+    thread::spawn(move || {
+        for _n in 1..1000 {
+            match key_generations::map_ks_error(op.update(b"my message")) {
+                Ok(_) => continue,
+                Err(e) => {
+                    assert_eq!(Error::Rc(ResponseCode::OPERATION_BUSY), e);
+                    return true;
+                }
+            }
+        }
+        let sig = op.finish(None, None).unwrap();
+        assert!(sig.is_some());
+        false
+    })
+}
+
+/// This test verifies that backend service throws BACKEND_BUSY error when all
+/// operations slots are full. This test creates operations in child processes and
+/// collects the status of operations performed in each child proc and determines
+/// whether any child proc exited with error status.
+#[test]
+fn keystore2_backend_busy_test() {
+    const MAX_OPS: i32 = 100;
+    static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+    let mut child_handles = create_operations(TARGET_CTX, ForcedOp(false), MAX_OPS);
+
+    // Wait until all child procs notifies us to continue,
+    // so that there are definitely enough operations outstanding to trigger a BACKEND_BUSY.
+    for ch in child_handles.iter_mut() {
+        ch.recv();
+    }
+    // Notify each child to resume and finish.
+    for ch in child_handles.iter_mut() {
+        ch.send(&BarrierReached {});
+    }
+
+    // Collect the result and validate whether backend busy has occurred.
+    let mut busy_count = 0;
+    for ch in child_handles.into_iter() {
+        if ch.get_result() == TestOutcome::BackendBusy {
+            busy_count += 1;
+        }
+    }
+    assert!(busy_count > 0)
+}
+
+/// This test confirms that forced operation is having high pruning power.
+/// 1. Initially create regular operations such that there are enough operations outstanding
+///    to trigger BACKEND_BUSY.
+/// 2. Then, create a forced operation. System should be able to prune one of the regular
+///    operations and create a slot for forced operation successfully.
+#[test]
+fn keystore2_forced_op_after_backendbusy_test() {
+    const MAX_OPS: i32 = 100;
+    static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+    // Create regular operations.
+    let mut child_handles = create_operations(TARGET_CTX, ForcedOp(false), MAX_OPS);
+
+    // Wait until all child procs notifies us to continue, so that there are enough
+    // operations outstanding to trigger a BACKEND_BUSY.
+    for ch in child_handles.iter_mut() {
+        ch.recv();
+    }
+
+    // Create a forced operation.
+    let auid = 99 * AID_USER_OFFSET + 10604;
+    let agid = 99 * AID_USER_OFFSET + 10604;
+    unsafe {
+        run_as::run_as(
+            key_generations::TARGET_VOLD_CTX,
+            Uid::from_raw(auid),
+            Gid::from_raw(agid),
+            move || {
+                let alias = format!("ks_prune_forced_op_key_{}", getuid());
+
+                // To make room for this forced op, system should be able to prune one of the
+                // above created regular operations and create a slot for this forced operation
+                // successfully.
+                create_signing_operation(
+                    ForcedOp(true),
+                    KeyPurpose::SIGN,
+                    Digest::SHA_2_256,
+                    Domain::SELINUX,
+                    100,
+                    Some(alias),
+                )
+                .expect("Client failed to create forced operation after BACKEND_BUSY state.");
+            },
+        );
+    };
+
+    // Notify each child to resume and finish.
+    for ch in child_handles.iter_mut() {
+        ch.send(&BarrierReached {});
+    }
+
+    // Collect the results of above created regular operations.
+    let mut pruned_count = 0;
+    let mut busy_count = 0;
+    let mut _other_err = 0;
+    for ch in child_handles.into_iter() {
+        match ch.get_result() {
+            TestOutcome::BackendBusy => {
+                busy_count += 1;
+            }
+            TestOutcome::InvalidHandle => {
+                pruned_count += 1;
+            }
+            _ => {
+                _other_err += 1;
+            }
+        }
+    }
+    // Verify that there should be at least one backend busy has occurred while creating
+    // above regular operations.
+    assert!(busy_count > 0);
+
+    // Verify that there should be at least one pruned operation which should have failed while
+    // performing operation.
+    assert!(pruned_count > 0);
+}
+
+/// This test confirms that forced operations can't be pruned.
+///  1. Creates an initial forced operation and tries to complete the operation after BACKEND_BUSY
+///     error is triggered.
+///  2. Create MAX_OPS number of forced operations so that definitely enough number of operations
+///     outstanding to trigger a BACKEND_BUSY.
+///  3. Try to use initially created forced operation (in step #1) and able to perform the
+///     operation successfully. This confirms that none of the later forced operations evicted the
+///     initial forced operation.
+#[test]
+fn keystore2_max_forced_ops_test() {
+    const MAX_OPS: i32 = 100;
+    let auid = 99 * AID_USER_OFFSET + 10205;
+    let agid = 99 * AID_USER_OFFSET + 10205;
+
+    // Create initial forced operation in a child process
+    // and wait for the parent to notify to perform operation.
+    let alias = format!("ks_forced_op_key_{}", getuid());
+    let mut first_op_handle = execute_op_run_as_child(
+        key_generations::TARGET_SU_CTX,
+        Domain::SELINUX,
+        key_generations::SELINUX_SHELL_NAMESPACE,
+        Some(alias),
+        Uid::from_raw(auid),
+        Gid::from_raw(agid),
+        ForcedOp(true),
+    );
+
+    // Wait until above child proc notifies us to continue, so that there is definitely a forced
+    // operation outstanding to perform a operation.
+    first_op_handle.recv();
+
+    // Create MAX_OPS number of forced operations.
+    let mut child_handles =
+        create_operations(key_generations::TARGET_SU_CTX, ForcedOp(true), MAX_OPS);
+
+    // Wait until all child procs notifies us to continue, so that  there are enough operations
+    // outstanding to trigger a BACKEND_BUSY.
+    for ch in child_handles.iter_mut() {
+        ch.recv();
+    }
+
+    // Notify initial created forced operation to continue performing the operations.
+    first_op_handle.send(&BarrierReached {});
+
+    // Collect initially created forced operation result and is expected to complete operation
+    // successfully.
+    let first_op_result = first_op_handle.get_result();
+    assert_eq!(first_op_result, TestOutcome::Ok);
+
+    // Notify each child to resume and finish.
+    for ch in child_handles.iter_mut() {
+        ch.send(&BarrierReached {});
+    }
+
+    // Collect the result and validate whether backend busy has occurred with MAX_OPS number
+    // of forced operations.
+    let busy_count = child_handles
+        .into_iter()
+        .map(|ch| ch.get_result())
+        .filter(|r| *r == TestOutcome::BackendBusy)
+        .count();
+    assert!(busy_count > 0);
+}
+
+/// This test will verify the use case with the same owner(UID) requesting `n` number of operations.
+/// This test confirms that when all operation slots are full and a new operation is requested,
+/// an operation which is least recently used and lived longest will be pruned to make a room
+/// for a new operation. Pruning strategy should prevent the operations of the other owners(UID)
+/// from being pruned.
+///
+/// 1. Create an operation in a child process with `untrusted_app` context and wait for parent
+///    notification to complete the operation.
+/// 2. Let parent process create `n` number of operations such that there are enough operations
+///    outstanding to trigger cannibalizing their own sibling operations.
+/// 3. Sequentially try to use above created `n` number of operations and also add a new operation,
+///    so that it should trigger cannibalizing one of their own sibling operations.
+///    3.1 While trying to use these pruned operations an `INVALID_OPERATION_HANDLE` error is
+///        expected as they are already pruned.
+/// 4. Notify the child process to resume and complete the operation. It is expected to complete the
+///    operation successfully.
+/// 5. Try to use the latest operation of parent. It is expected to complete the operation
+///    successfully.
+#[test]
+fn keystore2_ops_prune_test() {
+    const MAX_OPS: usize = 40; // This should be at least 32 with sec_level TEE.
+
+    static TARGET_CTX: &str = "u:r:untrusted_app:s0";
+    const USER_ID: u32 = 99;
+    const APPLICATION_ID: u32 = 10601;
+
+    let uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+    let gid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+
+    // Create an operation in an untrusted_app context. Wait until the parent notifies to continue.
+    // Once the parent notifies, this operation is expected to be completed successfully.
+    let alias = format!("ks_reg_op_key_{}", getuid());
+    let mut child_handle = execute_op_run_as_child(
+        TARGET_CTX,
+        Domain::APP,
+        -1,
+        Some(alias),
+        Uid::from_raw(uid),
+        Gid::from_raw(gid),
+        ForcedOp(false),
+    );
+
+    // Wait until child process notifies us to continue, so that an operation from child process is
+    // outstanding to complete the operation.
+    child_handle.recv();
+
+    // Generate a key to use in below operations.
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let alias = format!("ks_prune_op_test_key_{}", getuid());
+    let key_metadata = key_generations::generate_ec_p256_signing_key(
+        &sec_level,
+        Domain::SELINUX,
+        key_generations::SELINUX_SHELL_NAMESPACE,
+        Some(alias),
+        None,
+    )
+    .unwrap();
+
+    // Create multiple operations in this process to trigger cannibalizing sibling operations.
+    let mut ops: Vec<binder::Result<CreateOperationResponse>> = (0..MAX_OPS)
+        .map(|_| {
+            sec_level.createOperation(
+                &key_metadata.key,
+                &authorizations::AuthSetBuilder::new()
+                    .purpose(KeyPurpose::SIGN)
+                    .digest(Digest::SHA_2_256),
+                false,
+            )
+        })
+        .collect();
+
+    // Sequentially try to use operation handles created above and also add a new operation.
+    for vec_index in 0..MAX_OPS {
+        match &ops[vec_index] {
+            Ok(CreateOperationResponse { iOperation: Some(op), .. }) => {
+                // Older operation handle is pruned, if we try to use that an error is expected.
+                assert_eq!(
+                    Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)),
+                    key_generations::map_ks_error(op.update(b"my message"))
+                );
+            }
+            _ => panic!("Operation should have created successfully."),
+        }
+
+        // Create a new operation, it should trigger to cannibalize one of their own sibling
+        // operations.
+        ops.push(
+            sec_level.createOperation(
+                &key_metadata.key,
+                &authorizations::AuthSetBuilder::new()
+                    .purpose(KeyPurpose::SIGN)
+                    .digest(Digest::SHA_2_256),
+                false,
+            ),
+        );
+    }
+
+    // Notify child process to continue the operation.
+    child_handle.send(&BarrierReached {});
+    assert!((child_handle.get_result() == TestOutcome::Ok), "Failed to perform an operation");
+
+    // Try to use the latest operation created by parent, should be able to use it successfully.
+    match ops.last() {
+        Some(Ok(CreateOperationResponse { iOperation: Some(op), .. })) => {
+            assert_eq!(Ok(()), key_generations::map_ks_error(perform_sample_sign_operation(op)));
+        }
+        _ => panic!("Operation should have created successfully."),
+    }
+}
+
+/// Try to create forced operations with various contexts -
+///   - untrusted_app
+///   - system_server
+///   - priv_app
+/// `PERMISSION_DENIED` error response is expected.
+#[test]
+fn keystore2_forced_op_perm_denied_test() {
+    static TARGET_CTXS: &[&str] =
+        &["u:r:untrusted_app:s0", "u:r:system_server:s0", "u:r:priv_app:s0"];
+    const USER_ID: u32 = 99;
+    const APPLICATION_ID: u32 = 10601;
+
+    let uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+    let gid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+
+    for context in TARGET_CTXS.iter() {
+        unsafe {
+            run_as::run_as(context, Uid::from_raw(uid), Gid::from_raw(gid), move || {
+                let alias = format!("ks_app_forced_op_test_key_{}", getuid());
+                let result = key_generations::map_ks_error(create_signing_operation(
+                    ForcedOp(true),
+                    KeyPurpose::SIGN,
+                    Digest::SHA_2_256,
+                    Domain::APP,
+                    -1,
+                    Some(alias),
+                ));
+                assert!(result.is_err());
+                assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+            });
+        }
+    }
+}
+
+/// Try to create a forced operation with `vold` context.
+/// Should be able to create forced operation with `vold` context successfully.
+#[test]
+fn keystore2_forced_op_success_test() {
+    static TARGET_CTX: &str = "u:r:vold:s0";
+    const USER_ID: u32 = 99;
+    const APPLICATION_ID: u32 = 10601;
+
+    let uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+    let gid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+
+    unsafe {
+        run_as::run_as(TARGET_CTX, Uid::from_raw(uid), Gid::from_raw(gid), move || {
+            let alias = format!("ks_vold_forced_op_key_{}", getuid());
+            create_signing_operation(
+                ForcedOp(true),
+                KeyPurpose::SIGN,
+                Digest::SHA_2_256,
+                Domain::SELINUX,
+                key_generations::SELINUX_VOLD_NAMESPACE,
+                Some(alias),
+            )
+            .expect("Client with vold context failed to create forced operation.");
+        });
+    }
+}
+
+/// Create an operation and try to use this operation handle in multiple threads to perform
+/// operations. Test should fail to perform an operation with an error response `OPERATION_BUSY`
+/// when multiple threads try to access the operation handle at same time.
+#[test]
+fn keystore2_op_fails_operation_busy() {
+    let op_response = create_signing_operation(
+        ForcedOp(false),
+        KeyPurpose::SIGN,
+        Digest::SHA_2_256,
+        Domain::APP,
+        -1,
+        Some("op_busy_alias_test_key".to_string()),
+    )
+    .unwrap();
+
+    let op: binder::Strong<dyn IKeystoreOperation> = op_response.iOperation.unwrap();
+
+    let th_handle_1 = perform_op_busy_in_thread(op.clone());
+    let th_handle_2 = perform_op_busy_in_thread(op);
+
+    let result1 = th_handle_1.join().unwrap();
+    let result2 = th_handle_2.join().unwrap();
+
+    assert!(result1 || result2);
+}
diff --git a/keystore2/tests/keystore2_client_rsa_key_tests.rs b/keystore2/tests/keystore2_client_rsa_key_tests.rs
new file mode 100644
index 0000000..ad176a4
--- /dev/null
+++ b/keystore2/tests/keystore2_client_rsa_key_tests.rs
@@ -0,0 +1,1895 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Digest::Digest, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+    SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    CreateOperationResponse::CreateOperationResponse, Domain::Domain,
+    IKeystoreSecurityLevel::IKeystoreSecurityLevel,
+};
+
+use keystore2_test_utils::{
+    authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+use crate::keystore2_client_test_utils::{delete_app_key, perform_sample_sign_operation, ForcedOp};
+
+/// This macro is used for creating signing key operation tests using digests and paddings
+/// for various key sizes.
+macro_rules! test_rsa_sign_key_op {
+    ( $test_name:ident, $digest:expr, $key_size:expr, $padding:expr ) => {
+        #[test]
+        fn $test_name() {
+            perform_rsa_sign_key_op_success($digest, $key_size, stringify!($test_name), $padding);
+        }
+    };
+
+    ( $test_name:ident, $digest:expr, $padding:expr ) => {
+        #[test]
+        fn $test_name() {
+            perform_rsa_sign_key_op_failure($digest, stringify!($test_name), $padding);
+        }
+    };
+}
+
+/// This macro is used for creating encrypt/decrypt key operation tests using digests, mgf-digests
+/// and paddings for various key sizes.
+macro_rules! test_rsa_encrypt_key_op {
+    ( $test_name:ident, $digest:expr, $key_size:expr, $padding:expr ) => {
+        #[test]
+        fn $test_name() {
+            create_rsa_encrypt_decrypt_key_op_success(
+                $digest,
+                $key_size,
+                stringify!($test_name),
+                $padding,
+                None,
+            );
+        }
+    };
+
+    ( $test_name:ident, $digest:expr, $key_size:expr, $padding:expr, $mgf_digest:expr ) => {
+        #[test]
+        fn $test_name() {
+            create_rsa_encrypt_decrypt_key_op_success(
+                $digest,
+                $key_size,
+                stringify!($test_name),
+                $padding,
+                $mgf_digest,
+            );
+        }
+    };
+}
+
+/// Generate a RSA key and create an operation using the generated key.
+fn create_rsa_key_and_operation(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    domain: Domain,
+    nspace: i64,
+    alias: Option<String>,
+    key_params: &key_generations::KeyParams,
+    op_purpose: KeyPurpose,
+    forced_op: ForcedOp,
+) -> binder::Result<CreateOperationResponse> {
+    let key_metadata =
+        key_generations::generate_rsa_key(sec_level, domain, nspace, alias, key_params, None)?;
+
+    let mut op_params = authorizations::AuthSetBuilder::new().purpose(op_purpose);
+
+    if let Some(value) = key_params.digest {
+        op_params = op_params.digest(value)
+    }
+    if let Some(value) = key_params.padding {
+        op_params = op_params.padding_mode(value);
+    }
+    if let Some(value) = key_params.mgf_digest {
+        op_params = op_params.mgf_digest(value);
+    }
+    if let Some(value) = key_params.block_mode {
+        op_params = op_params.block_mode(value)
+    }
+
+    sec_level.createOperation(&key_metadata.key, &op_params, forced_op.0)
+}
+
+/// Generate RSA signing key with given parameters and perform signing operation.
+fn perform_rsa_sign_key_op_success(
+    digest: Digest,
+    key_size: i32,
+    alias: &str,
+    padding: PaddingMode,
+) {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let op_response = create_rsa_key_and_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        &key_generations::KeyParams {
+            key_size,
+            purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+            padding: Some(padding),
+            digest: Some(digest),
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: None,
+        },
+        KeyPurpose::SIGN,
+        ForcedOp(false),
+    )
+    .expect("Failed to create an operation.");
+
+    assert!(op_response.iOperation.is_some());
+    assert_eq!(
+        Ok(()),
+        key_generations::map_ks_error(perform_sample_sign_operation(
+            &op_response.iOperation.unwrap()
+        ))
+    );
+
+    delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate RSA signing key with given parameters and try to perform signing operation.
+/// Error `INCOMPATIBLE_DIGEST | UNKNOWN_ERROR` is expected while creating an opearation.
+fn perform_rsa_sign_key_op_failure(digest: Digest, alias: &str, padding: PaddingMode) {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        &key_generations::KeyParams {
+            key_size: 2048,
+            purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+            padding: Some(padding),
+            digest: Some(digest),
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: None,
+        },
+        KeyPurpose::SIGN,
+        ForcedOp(false),
+    ));
+    assert!(result.is_err());
+
+    let e = result.unwrap_err();
+    assert!(
+        e == Error::Km(ErrorCode::UNKNOWN_ERROR) || e == Error::Km(ErrorCode::INCOMPATIBLE_DIGEST)
+    );
+
+    delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate RSA encrypt/decrypt key with given parameters and perform decrypt operation.
+fn create_rsa_encrypt_decrypt_key_op_success(
+    digest: Option<Digest>,
+    key_size: i32,
+    alias: &str,
+    padding: PaddingMode,
+    mgf_digest: Option<Digest>,
+) {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let result = create_rsa_key_and_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        &key_generations::KeyParams {
+            key_size,
+            purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+            padding: Some(padding),
+            digest,
+            mgf_digest,
+            block_mode: None,
+            att_challenge: None,
+        },
+        KeyPurpose::DECRYPT,
+        ForcedOp(false),
+    );
+
+    assert!(result.is_ok());
+
+    delete_app_key(&keystore2, alias).unwrap();
+}
+
+// Below macros generate tests for generating RSA signing keys with -
+//     Padding mode: RSA_PKCS1_1_5_SIGN
+//     Digest modes: `NONE, MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512`
+// and create operations with generated keys. Tests should create operations successfully.
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_none_2048,
+    Digest::NONE,
+    2048,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_md5_2048,
+    Digest::MD5,
+    2048,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_sha1_2048,
+    Digest::SHA1,
+    2048,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_sha224_2048,
+    Digest::SHA_2_224,
+    2048,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_sha256_2048,
+    Digest::SHA_2_256,
+    2048,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_sha384_2048,
+    Digest::SHA_2_384,
+    2048,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_sha512_2048,
+    Digest::SHA_2_512,
+    2048,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_none_3072,
+    Digest::NONE,
+    3072,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_md5_3072,
+    Digest::MD5,
+    3072,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_sha1_3072,
+    Digest::SHA1,
+    3072,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_sha224_3072,
+    Digest::SHA_2_224,
+    3072,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_sha256_3072,
+    Digest::SHA_2_256,
+    3072,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_sha384_3072,
+    Digest::SHA_2_384,
+    3072,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_sha512_3072,
+    Digest::SHA_2_512,
+    3072,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_none_4096,
+    Digest::NONE,
+    4096,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_md5_4096,
+    Digest::MD5,
+    4096,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_sha1_4096,
+    Digest::SHA1,
+    4096,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_sha224_4096,
+    Digest::SHA_2_224,
+    4096,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_sha256_4096,
+    Digest::SHA_2_256,
+    4096,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_sha384_4096,
+    Digest::SHA_2_384,
+    4096,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+test_rsa_sign_key_op!(
+    sign_key_pkcs1_1_5_sha512_4096,
+    Digest::SHA_2_512,
+    4096,
+    PaddingMode::RSA_PKCS1_1_5_SIGN
+);
+
+// Below macros generate tests for generating RSA signing keys with -
+//     Padding mode: RSA_PSS
+//     Digest modes: `MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512`
+// and create operations with generated keys. Tests should create operations
+// successfully.
+test_rsa_sign_key_op!(sign_key_pss_md5_2048, Digest::MD5, 2048, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_sha1_2048, Digest::SHA1, 2048, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_sha224_2048, Digest::SHA_2_224, 2048, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_sha256_2048, Digest::SHA_2_256, 2048, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_sha384_2048, Digest::SHA_2_384, 2048, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_sha512_2048, Digest::SHA_2_512, 2048, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_md5_3072, Digest::MD5, 3072, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_sha1_3072, Digest::SHA1, 3072, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_sha224_3072, Digest::SHA_2_224, 3072, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_sha256_3072, Digest::SHA_2_256, 3072, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_sha384_3072, Digest::SHA_2_384, 3072, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_sha512_3072, Digest::SHA_2_512, 3072, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_md5_4096, Digest::MD5, 4096, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_sha1_4096, Digest::SHA1, 4096, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_sha224_4096, Digest::SHA_2_224, 4096, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_sha256_4096, Digest::SHA_2_256, 4096, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_sha384_4096, Digest::SHA_2_384, 4096, PaddingMode::RSA_PSS);
+test_rsa_sign_key_op!(sign_key_pss_sha512_4096, Digest::SHA_2_512, 4096, PaddingMode::RSA_PSS);
+
+// Below macros generate tests for generating RSA signing keys with -
+//     Padding mode: `NONE`
+//     Digest mode `NONE`
+// and try to create operations with generated keys. Tests should create operations
+// successfully.
+test_rsa_sign_key_op!(sign_key_none_none_2048, Digest::NONE, 2048, PaddingMode::NONE);
+test_rsa_sign_key_op!(sign_key_none_none_3072, Digest::NONE, 3072, PaddingMode::NONE);
+test_rsa_sign_key_op!(sign_key_none_none_4096, Digest::NONE, 4096, PaddingMode::NONE);
+
+// Below macros generate tests for generating RSA signing keys with -
+//     Padding mode: `NONE`
+//     Digest modes: `MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512`
+// and create operations with generated keys. Tests should fail to create operations with
+// an error code `UNKNOWN_ERROR | INCOMPATIBLE_DIGEST`.
+test_rsa_sign_key_op!(sign_key_none_md5_2048, Digest::MD5, PaddingMode::NONE);
+test_rsa_sign_key_op!(sign_key_none_sha1_2048, Digest::SHA1, PaddingMode::NONE);
+test_rsa_sign_key_op!(sign_key_none_sha224_2048, Digest::SHA_2_224, PaddingMode::NONE);
+test_rsa_sign_key_op!(sign_key_none_sha256_2048, Digest::SHA_2_256, PaddingMode::NONE);
+test_rsa_sign_key_op!(sign_key_none_sha384_2048, Digest::SHA_2_384, PaddingMode::NONE);
+test_rsa_sign_key_op!(sign_key_none_sha512_2048, Digest::SHA_2_512, PaddingMode::NONE);
+
+// Below macros generate tests for generating RSA encryption keys with various digest mode
+// and padding mode combinations.
+//     Digest modes: `MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512`
+//     Padding modes: `NONE, RSA_PKCS1_1_5_ENCRYPT`
+// and try to create operations using generated keys, tests should create operations successfully.
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_none_2048,
+    Some(Digest::NONE),
+    2048,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_md5_2048,
+    Some(Digest::MD5),
+    2048,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_sha1_2048,
+    Some(Digest::SHA1),
+    2048,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_sha224_2048,
+    Some(Digest::SHA_2_224),
+    2048,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_sha256_2048,
+    Some(Digest::SHA_2_256),
+    2048,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_sha384_2048,
+    Some(Digest::SHA_2_384),
+    2048,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_sha512_2048,
+    Some(Digest::SHA_2_512),
+    2048,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_none_3072,
+    Some(Digest::NONE),
+    3072,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_md5_3072,
+    Some(Digest::MD5),
+    3072,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_sha1_3072,
+    Some(Digest::SHA1),
+    3072,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_sha224_3072,
+    Some(Digest::SHA_2_224),
+    3072,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_sha256_3072,
+    Some(Digest::SHA_2_256),
+    3072,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_sha384_3072,
+    Some(Digest::SHA_2_384),
+    3072,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_sha512_3072,
+    Some(Digest::SHA_2_512),
+    3072,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_none_4096,
+    Some(Digest::NONE),
+    4096,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_md5_4096,
+    Some(Digest::MD5),
+    4096,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_sha1_4096,
+    Some(Digest::SHA1),
+    4096,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_sha224_4096,
+    Some(Digest::SHA_2_224),
+    4096,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_sha256_4096,
+    Some(Digest::SHA_2_256),
+    4096,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_sha384_4096,
+    Some(Digest::SHA_2_384),
+    4096,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_sha512_4096,
+    Some(Digest::SHA_2_512),
+    4096,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT
+);
+test_rsa_encrypt_key_op!(encrypt_key_none_none_2048, Some(Digest::NONE), 2048, PaddingMode::NONE);
+test_rsa_encrypt_key_op!(encrypt_key_none_md5_2048, Some(Digest::MD5), 2048, PaddingMode::NONE);
+test_rsa_encrypt_key_op!(encrypt_key_none_sha1_2048, Some(Digest::SHA1), 2048, PaddingMode::NONE);
+test_rsa_encrypt_key_op!(
+    encrypt_key_none_sha224_2048,
+    Some(Digest::SHA_2_224),
+    2048,
+    PaddingMode::NONE
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_none_sha256_2048,
+    Some(Digest::SHA_2_256),
+    2048,
+    PaddingMode::NONE
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_none_sha384_2048,
+    Some(Digest::SHA_2_384),
+    2048,
+    PaddingMode::NONE
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_none_sha512_2048,
+    Some(Digest::SHA_2_512),
+    2048,
+    PaddingMode::NONE
+);
+test_rsa_encrypt_key_op!(encrypt_key_none_none_3072, Some(Digest::NONE), 3072, PaddingMode::NONE);
+test_rsa_encrypt_key_op!(encrypt_key_none_md5_3072, Some(Digest::MD5), 3072, PaddingMode::NONE);
+test_rsa_encrypt_key_op!(encrypt_key_none_sha1_3072, Some(Digest::SHA1), 3072, PaddingMode::NONE);
+test_rsa_encrypt_key_op!(
+    encrypt_key_none_sha224_3072,
+    Some(Digest::SHA_2_224),
+    3072,
+    PaddingMode::NONE
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_none_sha256_3072,
+    Some(Digest::SHA_2_256),
+    3072,
+    PaddingMode::NONE
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_none_sha384_3072,
+    Some(Digest::SHA_2_384),
+    3072,
+    PaddingMode::NONE
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_none_sha512_3072,
+    Some(Digest::SHA_2_512),
+    3072,
+    PaddingMode::NONE
+);
+test_rsa_encrypt_key_op!(encrypt_key_none_none_4096, Some(Digest::NONE), 4096, PaddingMode::NONE);
+test_rsa_encrypt_key_op!(encrypt_key_none_md5_4096, Some(Digest::MD5), 4096, PaddingMode::NONE);
+test_rsa_encrypt_key_op!(encrypt_key_none_sha1_4096, Some(Digest::SHA1), 4096, PaddingMode::NONE);
+test_rsa_encrypt_key_op!(
+    encrypt_key_none_sha224_4096,
+    Some(Digest::SHA_2_224),
+    4096,
+    PaddingMode::NONE
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_none_sha256_4096,
+    Some(Digest::SHA_2_256),
+    4096,
+    PaddingMode::NONE
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_none_sha384_4096,
+    Some(Digest::SHA_2_384),
+    4096,
+    PaddingMode::NONE
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_none_sha512_4096,
+    Some(Digest::SHA_2_512),
+    4096,
+    PaddingMode::NONE
+);
+
+// Below macros generate tests for generating RSA keys with -
+//     Padding Mode: `RSA_OAEP`
+//     Digest modes: `MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512`
+//     mgf-digests: `MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512`
+// and create a decrypt operations using generated keys. Tests should create operations
+// successfully.
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_md5_2048,
+    Some(Digest::MD5),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_sha1_2048,
+    Some(Digest::MD5),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_sha224_2048,
+    Some(Digest::MD5),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_sha256_2048,
+    Some(Digest::MD5),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_sha384_2048,
+    Some(Digest::MD5),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_sha512_2048,
+    Some(Digest::MD5),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_md5_2048,
+    Some(Digest::SHA1),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_sha1_2048,
+    Some(Digest::SHA1),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_sha224_2048,
+    Some(Digest::SHA1),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_sha256_2048,
+    Some(Digest::SHA1),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_sha384_2048,
+    Some(Digest::SHA1),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_sha512_2048,
+    Some(Digest::SHA1),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_md5_2048,
+    Some(Digest::SHA_2_224),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_sha1_2048,
+    Some(Digest::SHA_2_224),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_sha224_2048,
+    Some(Digest::SHA_2_224),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_sha256_2048,
+    Some(Digest::SHA_2_224),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_sha384_2048,
+    Some(Digest::SHA_2_224),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_sha512_2048,
+    Some(Digest::SHA_2_224),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_md5_2048,
+    Some(Digest::SHA_2_256),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_sha1_2048,
+    Some(Digest::SHA_2_256),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_sha224_2048,
+    Some(Digest::SHA_2_256),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_sha256_2048,
+    Some(Digest::SHA_2_256),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_sha384_2048,
+    Some(Digest::SHA_2_256),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_sha512_2048,
+    Some(Digest::SHA_2_256),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_md5_2048,
+    Some(Digest::SHA_2_384),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_sha1_2048,
+    Some(Digest::SHA_2_384),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_sha224_2048,
+    Some(Digest::SHA_2_384),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_sha256_2048,
+    Some(Digest::SHA_2_384),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_sha384_2048,
+    Some(Digest::SHA_2_384),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_sha512_2048,
+    Some(Digest::SHA_2_384),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_md5_2048,
+    Some(Digest::SHA_2_512),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_sha1_2048,
+    Some(Digest::SHA_2_512),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_sha224_2048,
+    Some(Digest::SHA_2_512),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_sha256_2048,
+    Some(Digest::SHA_2_512),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_sha384_2048,
+    Some(Digest::SHA_2_512),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_sha512_2048,
+    Some(Digest::SHA_2_512),
+    2048,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_md5_3072,
+    Some(Digest::MD5),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_sha1_3072,
+    Some(Digest::MD5),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_sha224_3072,
+    Some(Digest::MD5),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_sha256_3072,
+    Some(Digest::MD5),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_sha384_3072,
+    Some(Digest::MD5),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_sha512_3072,
+    Some(Digest::MD5),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_md5_3072,
+    Some(Digest::SHA1),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_sha1_3072,
+    Some(Digest::SHA1),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_sha224_3072,
+    Some(Digest::SHA1),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_sha256_3072,
+    Some(Digest::SHA1),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_sha384_3072,
+    Some(Digest::SHA1),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_sha512_3072,
+    Some(Digest::SHA1),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_md5_3072,
+    Some(Digest::SHA_2_224),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_sha1_3072,
+    Some(Digest::SHA_2_224),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_sha224_3072,
+    Some(Digest::SHA_2_224),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_sha256_3072,
+    Some(Digest::SHA_2_224),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_sha384_3072,
+    Some(Digest::SHA_2_224),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_sha512_3072,
+    Some(Digest::SHA_2_224),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_md5_3072,
+    Some(Digest::SHA_2_256),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_sha1_3072,
+    Some(Digest::SHA_2_256),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_sha224_3072,
+    Some(Digest::SHA_2_256),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_sha256_3072,
+    Some(Digest::SHA_2_256),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_sha384_3072,
+    Some(Digest::SHA_2_256),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_sha512_3072,
+    Some(Digest::SHA_2_256),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_md5_3072,
+    Some(Digest::SHA_2_384),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_sha1_3072,
+    Some(Digest::SHA_2_384),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_sha224_3072,
+    Some(Digest::SHA_2_384),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_sha256_3072,
+    Some(Digest::SHA_2_384),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_sha384_3072,
+    Some(Digest::SHA_2_384),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_sha512_3072,
+    Some(Digest::SHA_2_384),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_md5_3072,
+    Some(Digest::SHA_2_512),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_sha1_3072,
+    Some(Digest::SHA_2_512),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_sha224_3072,
+    Some(Digest::SHA_2_512),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_sha256_3072,
+    Some(Digest::SHA_2_512),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_sha384_3072,
+    Some(Digest::SHA_2_512),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_sha512_3072,
+    Some(Digest::SHA_2_512),
+    3072,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_md5_4096,
+    Some(Digest::MD5),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_sha1_4096,
+    Some(Digest::MD5),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_sha224_4096,
+    Some(Digest::MD5),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_sha256_4096,
+    Some(Digest::MD5),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_sha384_4096,
+    Some(Digest::MD5),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_sha512_4096,
+    Some(Digest::MD5),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_md5_4096,
+    Some(Digest::SHA1),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_sha1_4096,
+    Some(Digest::SHA1),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_sha224_4096,
+    Some(Digest::SHA1),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_sha256_4096,
+    Some(Digest::SHA1),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_sha384_4096,
+    Some(Digest::SHA1),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_sha512_4096,
+    Some(Digest::SHA1),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_md5_4096,
+    Some(Digest::SHA_2_224),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_sha1_4096,
+    Some(Digest::SHA_2_224),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_sha224_4096,
+    Some(Digest::SHA_2_224),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_sha256_4096,
+    Some(Digest::SHA_2_224),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_sha384_4096,
+    Some(Digest::SHA_2_224),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_sha512_4096,
+    Some(Digest::SHA_2_224),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_md5_4096,
+    Some(Digest::SHA_2_256),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_sha1_4096,
+    Some(Digest::SHA_2_256),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_sha224_4096,
+    Some(Digest::SHA_2_256),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_sha256_4096,
+    Some(Digest::SHA_2_256),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_sha384_4096,
+    Some(Digest::SHA_2_256),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_sha512_4096,
+    Some(Digest::SHA_2_256),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_md5_4096,
+    Some(Digest::SHA_2_384),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_sha1_4096,
+    Some(Digest::SHA_2_384),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_sha224_4096,
+    Some(Digest::SHA_2_384),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_sha256_4096,
+    Some(Digest::SHA_2_384),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_sha384_4096,
+    Some(Digest::SHA_2_384),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_sha512_4096,
+    Some(Digest::SHA_2_384),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_md5_4096,
+    Some(Digest::SHA_2_512),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::MD5)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_sha1_4096,
+    Some(Digest::SHA_2_512),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA1)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_sha224_4096,
+    Some(Digest::SHA_2_512),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_224)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_sha256_4096,
+    Some(Digest::SHA_2_512),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_256)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_sha384_4096,
+    Some(Digest::SHA_2_512),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_384)
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_sha512_4096,
+    Some(Digest::SHA_2_512),
+    4096,
+    PaddingMode::RSA_OAEP,
+    Some(Digest::SHA_2_512)
+);
+
+// Below macros generate tests for generating RSA keys with -
+//     Padding mode: `RSA_OAEP`
+//     Digest modes: `MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512`
+// and create a decrypt operations using generated keys. Tests should create operations
+// successfully.
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_no_mgf_2048,
+    Some(Digest::MD5),
+    2048,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_no_mgf_2048,
+    Some(Digest::SHA1),
+    2048,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_no_mgf_2048,
+    Some(Digest::SHA_2_224),
+    2048,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_no_mgf_2048,
+    Some(Digest::SHA_2_256),
+    2048,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_no_mgf_2048,
+    Some(Digest::SHA_2_384),
+    2048,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_no_mgf_2048,
+    Some(Digest::SHA_2_512),
+    2048,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_no_mgf_3072,
+    Some(Digest::MD5),
+    3072,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_no_mgf_3072,
+    Some(Digest::SHA1),
+    3072,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_no_mgf_3072,
+    Some(Digest::SHA_2_224),
+    3072,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_no_mgf_3072,
+    Some(Digest::SHA_2_256),
+    3072,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_no_mgf_3072,
+    Some(Digest::SHA_2_384),
+    3072,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_no_mgf_3072,
+    Some(Digest::SHA_2_512),
+    3072,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_md5_no_mgf_4096,
+    Some(Digest::MD5),
+    4096,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha1_no_mgf_4096,
+    Some(Digest::SHA1),
+    4096,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha224_no_mgf_4096,
+    Some(Digest::SHA_2_224),
+    4096,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha256_no_mgf_4096,
+    Some(Digest::SHA_2_256),
+    4096,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha384_no_mgf_4096,
+    Some(Digest::SHA_2_384),
+    4096,
+    PaddingMode::RSA_OAEP,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_oaep_sha512_no_mgf_4096,
+    Some(Digest::SHA_2_512),
+    4096,
+    PaddingMode::RSA_OAEP,
+    None
+);
+
+// Below macros generate tests for generating RSA encryption keys with only padding modes.
+//     Padding modes: `NONE, RSA_PKCS1_1_5_ENCRYPT`
+// and try to create operations using generated keys, tests should create operations
+// successfully.
+test_rsa_encrypt_key_op!(encrypt_key_none_pad_2048, None, 2048, PaddingMode::NONE, None);
+test_rsa_encrypt_key_op!(encrypt_key_none_pad_3072, None, 3072, PaddingMode::NONE, None);
+test_rsa_encrypt_key_op!(encrypt_key_none_pad_4096, None, 4096, PaddingMode::NONE, None);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_pad_2048,
+    None,
+    2048,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_pad_3072,
+    None,
+    3072,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT,
+    None
+);
+test_rsa_encrypt_key_op!(
+    encrypt_key_pkcs1_1_5_pad_4096,
+    None,
+    4096,
+    PaddingMode::RSA_PKCS1_1_5_ENCRYPT,
+    None
+);
+
+/// Generate RSA signing key with -
+///     Padding mode: RSA_PSS
+///     Digest mode: `NONE`.
+/// Try to create an operation with this generated key. Test should fail to create an operation with
+/// `INCOMPATIBLE_DIGEST` error code.
+#[test]
+fn keystore2_rsa_generate_signing_key_padding_pss_fail() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = "ks_rsa_pss_none_key_op_test";
+    let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        &key_generations::KeyParams {
+            key_size: 2048,
+            purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+            padding: Some(PaddingMode::RSA_PSS),
+            digest: Some(Digest::NONE),
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: None,
+        },
+        KeyPurpose::SIGN,
+        ForcedOp(false),
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_DIGEST), result.unwrap_err());
+}
+
+/// Generate RSA encryption key with -
+///     Digest mode: `NONE`
+///     Padding mode: `RSA_OAEP`
+/// Try to create an operation using generated key. Test should fail to create an operation
+/// with an error code `INCOMPATIBLE_DIGEST`.
+#[test]
+fn keystore2_rsa_generate_key_with_oaep_padding_fail() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = "ks_rsa_key_oaep_padding_fail_test";
+    let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        &key_generations::KeyParams {
+            key_size: 2048,
+            purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+            padding: Some(PaddingMode::RSA_OAEP),
+            digest: Some(Digest::NONE),
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: None,
+        },
+        KeyPurpose::DECRYPT,
+        ForcedOp(false),
+    ));
+
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_DIGEST), result.unwrap_err());
+}
+
+/// Generate RSA keys without padding and digest modes. Try to create decrypt operation without
+/// digest and padding. Creation of an operation should fail with an error code
+/// `UNSUPPORTED_PADDING_MODE`.
+#[test]
+fn keystore2_rsa_generate_keys() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = "ks_rsa_key_unsupport_padding_test";
+    let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        &key_generations::KeyParams {
+            key_size: 2048,
+            purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+            padding: None,
+            digest: None,
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: None,
+        },
+        KeyPurpose::DECRYPT,
+        ForcedOp(false),
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_PADDING_MODE), result.unwrap_err());
+}
+
+/// Generate a RSA encryption key. Try to create a signing operation with it, an error
+/// `INCOMPATIBLE_PURPOSE` is expected as the generated key doesn't support sign operation.
+#[test]
+fn keystore2_rsa_encrypt_key_op_invalid_purpose() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = "ks_rsa_test_key_1";
+    let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        &key_generations::KeyParams {
+            key_size: 2048,
+            purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+            padding: Some(PaddingMode::RSA_PKCS1_1_5_ENCRYPT),
+            digest: Some(Digest::SHA_2_256),
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: None,
+        },
+        KeyPurpose::SIGN,
+        ForcedOp(false),
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+}
+
+/// Generate a RSA signing key. Try to create a decrypt operation with it, an error
+/// `INCOMPATIBLE_PURPOSE` is expected as the generated key doesn't support decrypt operation.
+#[test]
+fn keystore2_rsa_sign_key_op_invalid_purpose() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = "ks_rsa_test_key_2";
+    let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        &key_generations::KeyParams {
+            key_size: 2048,
+            purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+            padding: Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
+            digest: Some(Digest::SHA_2_256),
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: None,
+        },
+        KeyPurpose::DECRYPT,
+        ForcedOp(false),
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+}
+
+/// Generate a RSA key with SIGN and AGREE_KEY purposes. Try to perform an operation using the
+/// generated key, an error `UNSUPPORTED_PURPOSE` is expected as RSA doesn't support AGREE_KEY.
+#[test]
+fn keystore2_rsa_key_unsupported_purpose() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = "ks_rsa_key_test_3";
+    let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        &key_generations::KeyParams {
+            key_size: 2048,
+            purpose: vec![KeyPurpose::AGREE_KEY],
+            padding: Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
+            digest: Some(Digest::SHA_2_256),
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: None,
+        },
+        KeyPurpose::AGREE_KEY,
+        ForcedOp(false),
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_PURPOSE), result.unwrap_err());
+}
+
+/// Generate a RSA encrypt key with padding mode supported for signing. Try to create an operation
+/// using generated key, an error `UNSUPPORTED_PADDING_MODE` is expected with unsupported padding
+/// mode.
+#[test]
+fn keystore2_rsa_encrypt_key_unsupported_padding() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let paddings = [PaddingMode::RSA_PKCS1_1_5_SIGN, PaddingMode::RSA_PSS];
+
+    for padding in paddings {
+        let alias = format!("ks_rsa_encrypt_key_unsupported_pad_test{}", padding.0);
+        let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+            &sec_level,
+            Domain::APP,
+            -1,
+            Some(alias.to_string()),
+            &key_generations::KeyParams {
+                key_size: 2048,
+                purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+                padding: Some(padding),
+                digest: Some(Digest::SHA_2_256),
+                mgf_digest: None,
+                block_mode: None,
+                att_challenge: None,
+            },
+            KeyPurpose::DECRYPT,
+            ForcedOp(false),
+        ));
+        assert!(result.is_err());
+        assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_PADDING_MODE), result.unwrap_err());
+    }
+}
+
+/// Generate a RSA signing key with padding mode supported for encryption. Try to create an
+/// operation using generated key, an error `UNSUPPORTED_PADDING_MODE` is expected with
+/// unsupported padding mode.
+#[test]
+fn keystore2_rsa_signing_key_unsupported_padding() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+    let paddings = [PaddingMode::RSA_PKCS1_1_5_ENCRYPT, PaddingMode::RSA_OAEP];
+
+    for padding in paddings {
+        let alias = format!("ks_rsa_sign_key_unsupported_pad_test_4_{}", padding.0);
+        let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+            &sec_level,
+            Domain::APP,
+            -1,
+            Some(alias.to_string()),
+            &key_generations::KeyParams {
+                key_size: 2048,
+                purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+                padding: Some(padding),
+                digest: Some(Digest::SHA_2_256),
+                mgf_digest: None,
+                block_mode: None,
+                att_challenge: None,
+            },
+            KeyPurpose::SIGN,
+            ForcedOp(false),
+        ));
+        assert!(result.is_err());
+        assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_PADDING_MODE), result.unwrap_err());
+    }
+}
+
+/// Generate a RSA encryption key. Try to perform encrypt operation using the generated
+/// key, an error `UNSUPPORTED_PURPOSE` is expected as encrypt operation is not supported
+/// with RSA key.
+#[test]
+fn keystore2_rsa_key_unsupported_op() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = "ks_rsa_key_test_5";
+    let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        &key_generations::KeyParams {
+            key_size: 2048,
+            purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+            padding: Some(PaddingMode::RSA_PKCS1_1_5_ENCRYPT),
+            digest: Some(Digest::SHA_2_256),
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: None,
+        },
+        KeyPurpose::ENCRYPT,
+        ForcedOp(false),
+    ));
+
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_PURPOSE), result.unwrap_err());
+}
+
+/// Generate a RSA key with encrypt, sign and verify purpose. Try to perform decrypt operation
+/// using the generated key, an error `INCOMPATIBLE_PURPOSE` is expected as the key is not
+/// generated with decrypt purpose.
+#[test]
+fn keystore2_rsa_key_missing_purpose() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = "ks_rsa_key_test_6";
+    let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        &key_generations::KeyParams {
+            key_size: 2048,
+            purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::SIGN, KeyPurpose::VERIFY],
+            padding: Some(PaddingMode::RSA_PKCS1_1_5_ENCRYPT),
+            digest: Some(Digest::SHA_2_256),
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: None,
+        },
+        KeyPurpose::DECRYPT,
+        ForcedOp(false),
+    ));
+
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+}
+
+/// Generate RSA encryption keys with OAEP padding mode and without digest mode. Try to create an
+/// operation with generated key, unsupported digest error is expected.
+#[test]
+fn keystore2_rsa_gen_keys_with_oaep_paddings_without_digest() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = "ks_rsa_key_padding_fail";
+    let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        &key_generations::KeyParams {
+            key_size: 2048,
+            purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+            padding: Some(PaddingMode::RSA_OAEP),
+            digest: None,
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: None,
+        },
+        KeyPurpose::DECRYPT,
+        ForcedOp(false),
+    ));
+
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_DIGEST), result.unwrap_err());
+}
+
+/// Generate RSA keys with unsupported key size, an error `UNSUPPORTED_KEY_SIZE` is expected.
+#[test]
+fn keystore2_rsa_gen_keys_unsupported_size() {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let alias = "ks_rsa_key_padding_fail";
+    let result = key_generations::map_ks_error(key_generations::generate_rsa_key(
+        &sec_level,
+        Domain::APP,
+        -1,
+        Some(alias.to_string()),
+        &key_generations::KeyParams {
+            key_size: 5120,
+            purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::SIGN, KeyPurpose::VERIFY],
+            padding: Some(PaddingMode::RSA_PKCS1_1_5_ENCRYPT),
+            digest: Some(Digest::SHA_2_256),
+            mgf_digest: None,
+            block_mode: None,
+            att_challenge: None,
+        },
+        None,
+    ));
+
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_KEY_SIZE), result.unwrap_err());
+}
diff --git a/keystore2/tests/keystore2_client_test_utils.rs b/keystore2/tests/keystore2_client_test_utils.rs
new file mode 100644
index 0000000..58e6b7d
--- /dev/null
+++ b/keystore2/tests/keystore2_client_test_utils.rs
@@ -0,0 +1,419 @@
+// Copyright 2022, 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 nix::unistd::{Gid, Uid};
+use serde::{Deserialize, Serialize};
+
+use openssl::encrypt::Encrypter;
+use openssl::error::ErrorStack;
+use openssl::hash::MessageDigest;
+use openssl::pkey::PKey;
+use openssl::pkey::Public;
+use openssl::rsa::Padding;
+use openssl::sign::Verifier;
+use openssl::x509::X509;
+
+use binder::wait_for_interface;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    BlockMode::BlockMode, Digest::Digest, ErrorCode::ErrorCode,
+    KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+    SecurityLevel::SecurityLevel, Tag::Tag,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    CreateOperationResponse::CreateOperationResponse, Domain::Domain,
+    IKeystoreOperation::IKeystoreOperation, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
+    IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor, KeyMetadata::KeyMetadata,
+    KeyParameters::KeyParameters, ResponseCode::ResponseCode,
+};
+
+use packagemanager_aidl::aidl::android::content::pm::IPackageManagerNative::IPackageManagerNative;
+
+use keystore2_test_utils::{
+    authorizations, get_keystore_service, key_generations, key_generations::Error, run_as,
+};
+
+/// This enum is used to communicate between parent and child processes.
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
+pub enum TestOutcome {
+    Ok,
+    BackendBusy,
+    InvalidHandle,
+    OtherErr,
+}
+
+/// This is used to notify the child or parent process that the expected state is reched.
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
+pub struct BarrierReached;
+
+/// Forced operation.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct ForcedOp(pub bool);
+
+/// Sample plain text input for encrypt operation.
+pub const SAMPLE_PLAIN_TEXT: &[u8] = b"my message 11111";
+
+pub const PACKAGE_MANAGER_NATIVE_SERVICE: &str = "package_native";
+pub const APP_ATTEST_KEY_FEATURE: &str = "android.hardware.keystore.app_attest_key";
+
+/// Determines whether app_attest_key_feature is supported or not.
+pub fn app_attest_key_feature_exists() -> bool {
+    let pm = wait_for_interface::<dyn IPackageManagerNative>(PACKAGE_MANAGER_NATIVE_SERVICE)
+        .expect("Failed to get package manager native service.");
+
+    pm.hasSystemFeature(APP_ATTEST_KEY_FEATURE, 0).expect("hasSystemFeature failed.")
+}
+
+#[macro_export]
+macro_rules! skip_test_if_no_app_attest_key_feature {
+    () => {
+        if !app_attest_key_feature_exists() {
+            return;
+        }
+    };
+}
+
+/// Indicate whether the default device is KeyMint (rather than Keymaster).
+pub fn has_default_keymint() -> bool {
+    binder::is_declared("android.hardware.security.keymint.IKeyMintDevice/default")
+        .expect("Could not check for declared keymint interface")
+}
+
+/// Generate EC key and grant it to the list of users with given access vector.
+/// Returns the list of granted keys `nspace` values in the order of given grantee uids.
+pub fn generate_ec_key_and_grant_to_users(
+    keystore2: &binder::Strong<dyn IKeystoreService>,
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    alias: Option<String>,
+    grantee_uids: Vec<i32>,
+    access_vector: i32,
+) -> Result<Vec<i64>, binder::Status> {
+    let key_metadata =
+        key_generations::generate_ec_p256_signing_key(sec_level, Domain::APP, -1, alias, None)?;
+
+    let mut granted_keys = Vec::new();
+
+    for uid in grantee_uids {
+        let granted_key = keystore2.grant(&key_metadata.key, uid, access_vector)?;
+        assert_eq!(granted_key.domain, Domain::GRANT);
+        granted_keys.push(granted_key.nspace);
+    }
+
+    Ok(granted_keys)
+}
+
+/// Generate a EC_P256 key using given domain, namespace and alias.
+/// Create an operation using the generated key and perform sample signing operation.
+pub fn create_signing_operation(
+    forced_op: ForcedOp,
+    op_purpose: KeyPurpose,
+    op_digest: Digest,
+    domain: Domain,
+    nspace: i64,
+    alias: Option<String>,
+) -> binder::Result<CreateOperationResponse> {
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let key_metadata =
+        key_generations::generate_ec_p256_signing_key(&sec_level, domain, nspace, alias, None)
+            .unwrap();
+
+    sec_level.createOperation(
+        &key_metadata.key,
+        &authorizations::AuthSetBuilder::new().purpose(op_purpose).digest(op_digest),
+        forced_op.0,
+    )
+}
+
+/// Performs sample signing operation.
+pub fn perform_sample_sign_operation(
+    op: &binder::Strong<dyn IKeystoreOperation>,
+) -> Result<(), binder::Status> {
+    op.update(b"my message")?;
+    let sig = op.finish(None, None)?;
+    assert!(sig.is_some());
+    Ok(())
+}
+
+/// Perform sample HMAC sign and verify operations.
+pub fn perform_sample_hmac_sign_verify_op(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    key: &KeyDescriptor,
+) {
+    let sign_op = sec_level
+        .createOperation(
+            key,
+            &authorizations::AuthSetBuilder::new()
+                .purpose(KeyPurpose::SIGN)
+                .digest(Digest::SHA_2_256)
+                .mac_length(256),
+            false,
+        )
+        .unwrap();
+    assert!(sign_op.iOperation.is_some());
+
+    let op = sign_op.iOperation.unwrap();
+    op.update(b"my message").unwrap();
+    let sig = op.finish(None, None).unwrap();
+    assert!(sig.is_some());
+
+    let sig = sig.unwrap();
+    let verify_op = sec_level
+        .createOperation(
+            key,
+            &authorizations::AuthSetBuilder::new()
+                .purpose(KeyPurpose::VERIFY)
+                .digest(Digest::SHA_2_256),
+            false,
+        )
+        .unwrap();
+    assert!(verify_op.iOperation.is_some());
+
+    let op = verify_op.iOperation.unwrap();
+    let result = op.finish(Some(b"my message"), Some(&sig)).unwrap();
+    assert!(result.is_none());
+}
+
+/// Map KeyMint Digest values to OpenSSL MessageDigest.
+pub fn get_openssl_digest_mode(digest: Option<Digest>) -> MessageDigest {
+    match digest {
+        Some(Digest::MD5) => MessageDigest::md5(),
+        Some(Digest::SHA1) => MessageDigest::sha1(),
+        Some(Digest::SHA_2_224) => MessageDigest::sha224(),
+        Some(Digest::SHA_2_256) => MessageDigest::sha256(),
+        Some(Digest::SHA_2_384) => MessageDigest::sha384(),
+        Some(Digest::SHA_2_512) => MessageDigest::sha512(),
+        _ => MessageDigest::sha256(),
+    }
+}
+
+/// Map KeyMint PaddingMode values to OpenSSL Padding.
+pub fn get_openssl_padding_mode(padding: PaddingMode) -> Padding {
+    match padding {
+        PaddingMode::RSA_OAEP => Padding::PKCS1_OAEP,
+        PaddingMode::RSA_PSS => Padding::PKCS1_PSS,
+        PaddingMode::RSA_PKCS1_1_5_SIGN => Padding::PKCS1,
+        PaddingMode::RSA_PKCS1_1_5_ENCRYPT => Padding::PKCS1,
+        _ => Padding::NONE,
+    }
+}
+
+/// Perform sample sign and verify operations using RSA or EC key.
+pub fn perform_sample_asym_sign_verify_op(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    key_metadata: &KeyMetadata,
+    padding: Option<PaddingMode>,
+    digest: Option<Digest>,
+) {
+    let mut authorizations = authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN);
+    if let Some(value) = padding {
+        authorizations = authorizations.padding_mode(value);
+    }
+    if let Some(value) = digest {
+        authorizations = authorizations.digest(value);
+    }
+
+    let sign_op = sec_level.createOperation(&key_metadata.key, &authorizations, false).unwrap();
+    assert!(sign_op.iOperation.is_some());
+
+    let op = sign_op.iOperation.unwrap();
+    op.update(b"my message").unwrap();
+    let sig = op.finish(None, None).unwrap();
+    assert!(sig.is_some());
+
+    let sig = sig.unwrap();
+    let cert_bytes = key_metadata.certificate.as_ref().unwrap();
+    let cert = X509::from_der(cert_bytes.as_ref()).unwrap();
+    let pub_key = cert.public_key().unwrap();
+    let mut verifier = Verifier::new(get_openssl_digest_mode(digest), pub_key.as_ref()).unwrap();
+    if let Some(value) = padding {
+        verifier.set_rsa_padding(get_openssl_padding_mode(value)).unwrap();
+    }
+    verifier.update(b"my message").unwrap();
+    assert!(verifier.verify(&sig).unwrap());
+}
+
+/// Create new operation on child proc and perform simple operation after parent notification.
+pub fn execute_op_run_as_child(
+    target_ctx: &'static str,
+    domain: Domain,
+    nspace: i64,
+    alias: Option<String>,
+    auid: Uid,
+    agid: Gid,
+    forced_op: ForcedOp,
+) -> run_as::ChildHandle<TestOutcome, BarrierReached> {
+    unsafe {
+        run_as::run_as_child(target_ctx, auid, agid, move |reader, writer| {
+            let result = key_generations::map_ks_error(create_signing_operation(
+                forced_op,
+                KeyPurpose::SIGN,
+                Digest::SHA_2_256,
+                domain,
+                nspace,
+                alias,
+            ));
+
+            // Let the parent know that an operation has been started, then
+            // wait until the parent notifies us to continue, so the operation
+            // remains open.
+            writer.send(&BarrierReached {});
+            reader.recv();
+
+            // Continue performing the operation after parent notifies.
+            match &result {
+                Ok(CreateOperationResponse { iOperation: Some(op), .. }) => {
+                    match key_generations::map_ks_error(perform_sample_sign_operation(op)) {
+                        Ok(()) => TestOutcome::Ok,
+                        Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)) => {
+                            TestOutcome::InvalidHandle
+                        }
+                        Err(e) => panic!("Error in performing op: {:#?}", e),
+                    }
+                }
+                Ok(_) => TestOutcome::OtherErr,
+                Err(Error::Rc(ResponseCode::BACKEND_BUSY)) => TestOutcome::BackendBusy,
+                _ => TestOutcome::OtherErr,
+            }
+        })
+        .expect("Failed to create an operation.")
+    }
+}
+
+/// Get NONCE value from given key parameters list.
+pub fn get_op_nonce(parameters: &KeyParameters) -> Option<Vec<u8>> {
+    for key_param in &parameters.keyParameter {
+        if key_param.tag == Tag::NONCE {
+            if let KeyParameterValue::Blob(val) = &key_param.value {
+                return Some(val.clone());
+            }
+        }
+    }
+    None
+}
+
+/// This performs sample encryption operation with given symmetric key (AES/3DES).
+/// It encrypts `SAMPLE_PLAIN_TEXT` of length 128-bits.
+pub fn perform_sample_sym_key_encrypt_op(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    padding_mode: PaddingMode,
+    block_mode: BlockMode,
+    nonce: &mut Option<Vec<u8>>,
+    mac_len: Option<i32>,
+    key: &KeyDescriptor,
+) -> binder::Result<Option<Vec<u8>>> {
+    let mut op_params = authorizations::AuthSetBuilder::new()
+        .purpose(KeyPurpose::ENCRYPT)
+        .padding_mode(padding_mode)
+        .block_mode(block_mode);
+    if let Some(value) = nonce {
+        op_params = op_params.nonce(value.to_vec());
+    }
+
+    if let Some(val) = mac_len {
+        op_params = op_params.mac_length(val);
+    }
+
+    let op_response = sec_level.createOperation(key, &op_params, false)?;
+    assert!(op_response.iOperation.is_some());
+    let op = op_response.iOperation.unwrap();
+    if op_response.parameters.is_some() && nonce.is_none() {
+        *nonce = get_op_nonce(&op_response.parameters.unwrap());
+    }
+    op.finish(Some(SAMPLE_PLAIN_TEXT), None)
+}
+
+/// This performs sample decryption operation with given symmetric key (AES/3DES).
+pub fn perform_sample_sym_key_decrypt_op(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    input: &[u8],
+    padding_mode: PaddingMode,
+    block_mode: BlockMode,
+    nonce: &mut Option<Vec<u8>>,
+    mac_len: Option<i32>,
+    key: &KeyDescriptor,
+) -> binder::Result<Option<Vec<u8>>> {
+    let mut op_params = authorizations::AuthSetBuilder::new()
+        .purpose(KeyPurpose::DECRYPT)
+        .padding_mode(padding_mode)
+        .block_mode(block_mode);
+    if let Some(value) = nonce {
+        op_params = op_params.nonce(value.to_vec());
+    }
+
+    if let Some(val) = mac_len {
+        op_params = op_params.mac_length(val);
+    }
+
+    let op_response = sec_level.createOperation(key, &op_params, false)?;
+    assert!(op_response.iOperation.is_some());
+    let op = op_response.iOperation.unwrap();
+    op.finish(Some(input), None)
+}
+
+/// Delete a key with domain APP.
+pub fn delete_app_key(
+    keystore2: &binder::Strong<dyn IKeystoreService>,
+    alias: &str,
+) -> binder::Result<()> {
+    keystore2.deleteKey(&KeyDescriptor {
+        domain: Domain::APP,
+        nspace: -1,
+        alias: Some(alias.to_string()),
+        blob: None,
+    })
+}
+
+/// Encrypt the secure key with given transport key.
+pub fn encrypt_secure_key(
+    sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+    secure_key: &[u8],
+    aad: &[u8],
+    nonce: Vec<u8>,
+    mac_len: i32,
+    key: &KeyDescriptor,
+) -> binder::Result<Option<Vec<u8>>> {
+    let op_params = authorizations::AuthSetBuilder::new()
+        .purpose(KeyPurpose::ENCRYPT)
+        .padding_mode(PaddingMode::NONE)
+        .block_mode(BlockMode::GCM)
+        .nonce(nonce)
+        .mac_length(mac_len);
+
+    let op_response = sec_level.createOperation(key, &op_params, false)?;
+
+    let op = op_response.iOperation.unwrap();
+    op.updateAad(aad)?;
+    op.finish(Some(secure_key), None)
+}
+
+/// Encrypt the transport key with given RSA wrapping key.
+pub fn encrypt_transport_key(
+    transport_key: &[u8],
+    pkey: &PKey<Public>,
+) -> Result<Vec<u8>, ErrorStack> {
+    let mut encrypter = Encrypter::new(pkey).unwrap();
+    encrypter.set_rsa_padding(Padding::PKCS1_OAEP).unwrap();
+    encrypter.set_rsa_oaep_md(MessageDigest::sha256()).unwrap();
+    encrypter.set_rsa_mgf1_md(MessageDigest::sha1()).unwrap();
+
+    let input = transport_key.to_vec();
+    let buffer_len = encrypter.encrypt_len(&input).unwrap();
+    let mut encoded = vec![0u8; buffer_len];
+    let encoded_len = encrypter.encrypt(&input, &mut encoded).unwrap();
+    let encoded = &encoded[..encoded_len];
+
+    Ok(encoded.to_vec())
+}
diff --git a/keystore2/tests/keystore2_client_tests.rs b/keystore2/tests/keystore2_client_tests.rs
new file mode 100644
index 0000000..07a298a
--- /dev/null
+++ b/keystore2/tests/keystore2_client_tests.rs
@@ -0,0 +1,30 @@
+// Copyright 2022, 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.
+
+pub mod ffi_test_utils;
+pub mod keystore2_client_3des_key_tests;
+pub mod keystore2_client_aes_key_tests;
+pub mod keystore2_client_attest_key_tests;
+pub mod keystore2_client_delete_key_tests;
+pub mod keystore2_client_ec_key_tests;
+pub mod keystore2_client_grant_key_tests;
+pub mod keystore2_client_hmac_key_tests;
+pub mod keystore2_client_import_keys_tests;
+pub mod keystore2_client_key_agreement_tests;
+pub mod keystore2_client_key_id_domain_tests;
+pub mod keystore2_client_list_entries_tests;
+pub mod keystore2_client_operation_tests;
+pub mod keystore2_client_rsa_key_tests;
+pub mod keystore2_client_test_utils;
+pub mod keystore2_client_update_subcomponent_tests;
diff --git a/keystore2/tests/keystore2_client_update_subcomponent_tests.rs b/keystore2/tests/keystore2_client_update_subcomponent_tests.rs
new file mode 100644
index 0000000..0be092f
--- /dev/null
+++ b/keystore2/tests/keystore2_client_update_subcomponent_tests.rs
@@ -0,0 +1,295 @@
+// Copyright 2022, 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 nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    ErrorCode::ErrorCode, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
+    ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{get_keystore_service, key_generations, key_generations::Error, run_as};
+
+/// Generate a key and update its public certificate and certificate chain. Test should be able to
+/// load the key and able to verify whether its certificate and cert-chain are updated successfully.
+#[test]
+fn keystore2_update_subcomponent_success() {
+    let alias = "update_subcomponent_success_key";
+
+    let keystore2 = get_keystore_service();
+    let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+    let key_metadata = key_generations::generate_ec_p256_signing_key(
+        &sec_level,
+        Domain::SELINUX,
+        key_generations::SELINUX_SHELL_NAMESPACE,
+        Some(alias.to_string()),
+        None,
+    )
+    .unwrap();
+
+    let other_cert: [u8; 32] = [123; 32];
+    let other_cert_chain: [u8; 32] = [12; 32];
+
+    keystore2
+        .updateSubcomponent(&key_metadata.key, Some(&other_cert), Some(&other_cert_chain))
+        .expect("updateSubcomponent should have succeeded.");
+
+    let key_entry_response = keystore2.getKeyEntry(&key_metadata.key).unwrap();
+    assert_eq!(Some(other_cert.to_vec()), key_entry_response.metadata.certificate);
+    assert_eq!(Some(other_cert_chain.to_vec()), key_entry_response.metadata.certificateChain);
+}
+
+/// Try to update non-existing asymmetric key public cert and certificate chain. Test should fail
+/// to update with error response code `KEY_NOT_FOUND`.
+#[test]
+fn keystore2_update_subcomponent_fail() {
+    let alias = "update_component_failure_key";
+
+    let keystore2 = get_keystore_service();
+
+    let other_cert: [u8; 32] = [123; 32];
+    let other_cert_chain: [u8; 32] = [12; 32];
+
+    let result = key_generations::map_ks_error(keystore2.updateSubcomponent(
+        &KeyDescriptor {
+            domain: Domain::SELINUX,
+            nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+            alias: Some(alias.to_string()),
+            blob: None,
+        },
+        Some(&other_cert),
+        Some(&other_cert_chain),
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
+
+/// Try to update non-existing asymmetric key public cert only. Test should fail
+/// to update with error response code `KEY_NOT_FOUND`.
+#[test]
+fn keystore2_update_subcomponent_no_key_entry_cert_fail() {
+    let alias = "update_no_key_entry_cert_only_component_fail_key";
+    let keystore2 = get_keystore_service();
+    let other_cert: [u8; 32] = [123; 32];
+
+    let result = key_generations::map_ks_error(keystore2.updateSubcomponent(
+        &KeyDescriptor {
+            domain: Domain::APP,
+            nspace: -1,
+            alias: Some(alias.to_string()),
+            blob: None,
+        },
+        Some(&other_cert),
+        None,
+    ));
+    assert!(result.is_err());
+    assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
+
+/// Try to update non existing key with the only given certificate-chain, test should succeed
+/// in creating a new keystore entry with the given certificate-chain.
+#[test]
+fn keystore2_update_subcomponent_no_key_entry_cert_chain_success() {
+    let alias = "update_no_key_entry_cert_chain_only_component_success";
+    let keystore2 = get_keystore_service();
+    let cert_entries =
+        vec![(Domain::SELINUX, key_generations::SELINUX_SHELL_NAMESPACE), (Domain::APP, -1)];
+    let other_cert_chain: [u8; 32] = [12; 32];
+
+    for (domain, nspace) in cert_entries {
+        keystore2
+            .updateSubcomponent(
+                &KeyDescriptor { domain, nspace, alias: Some(alias.to_string()), blob: None },
+                None,
+                Some(&other_cert_chain),
+            )
+            .expect("updateSubcomponent should have succeeded.");
+
+        let key_entry_response = keystore2
+            .getKeyEntry(&KeyDescriptor {
+                domain,
+                nspace,
+                alias: Some(alias.to_string()),
+                blob: None,
+            })
+            .unwrap();
+        assert_eq!(Some(other_cert_chain.to_vec()), key_entry_response.metadata.certificateChain);
+        assert!(key_entry_response.metadata.certificate.is_none(), "Unexpected certificate entry");
+        assert!(key_entry_response.metadata.authorizations.is_empty(), "Unexpected authorizations");
+        assert_eq!(key_entry_response.metadata.keySecurityLevel, SecurityLevel::SOFTWARE);
+
+        keystore2
+            .deleteKey(&KeyDescriptor {
+                domain,
+                nspace,
+                alias: Some(alias.to_string()),
+                blob: None,
+            })
+            .unwrap();
+    }
+}
+
+/// Generate a key and grant it to two users. For one user grant it with only `GET_INFO` access
+/// permission and for another user grant it with GET_INFO and UPDATE access permissions. In a
+/// grantee context where key is granted with only GET_INFO access permission, try to update
+/// key's public certificate and certificate chain. Test should fail to update with error response
+/// code `PERMISSION_DENIED` because grantee does not possess UPDATE access permission for the
+/// specified key. In a grantee context where key is granted with UPDATE and GET_INFO access
+/// permissions, test should be able to update public certificate and cert-chain successfully.
+#[test]
+fn keystore2_update_subcomponent_fails_permission_denied() {
+    static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+    static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+    const USER_ID_1: u32 = 99;
+    const APPLICATION_ID: u32 = 10001;
+    static GRANTEE_1_UID: u32 = USER_ID_1 * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_1_GID: u32 = GRANTEE_1_UID;
+
+    const USER_ID_2: u32 = 98;
+    static GRANTEE_2_UID: u32 = USER_ID_2 * AID_USER_OFFSET + APPLICATION_ID;
+    static GRANTEE_2_GID: u32 = GRANTEE_2_UID;
+
+    // Generate a key and grant it to multiple users with different access permissions.
+    let mut granted_keys = unsafe {
+        run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+            let keystore2 = get_keystore_service();
+            let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+            let alias = format!("ks_update_subcompo_test_1_{}", getuid());
+            let mut granted_keys = Vec::new();
+
+            let key_metadata = key_generations::generate_ec_p256_signing_key(
+                &sec_level,
+                Domain::APP,
+                -1,
+                Some(alias),
+                None,
+            )
+            .unwrap();
+
+            // Grant a key without update permission.
+            let access_vector = KeyPermission::GET_INFO.0;
+            let granted_key = keystore2
+                .grant(&key_metadata.key, GRANTEE_1_UID.try_into().unwrap(), access_vector)
+                .unwrap();
+            assert_eq!(granted_key.domain, Domain::GRANT);
+            granted_keys.push(granted_key.nspace);
+
+            // Grant a key with update permission.
+            let access_vector = KeyPermission::GET_INFO.0 | KeyPermission::UPDATE.0;
+            let granted_key = keystore2
+                .grant(&key_metadata.key, GRANTEE_2_UID.try_into().unwrap(), access_vector)
+                .unwrap();
+            assert_eq!(granted_key.domain, Domain::GRANT);
+            granted_keys.push(granted_key.nspace);
+
+            granted_keys
+        })
+    };
+
+    // Grantee context, try to update the key public certs, permission denied error is expected.
+    let granted_key1_nspace = granted_keys.remove(0);
+    unsafe {
+        run_as::run_as(
+            GRANTEE_CTX,
+            Uid::from_raw(GRANTEE_1_UID),
+            Gid::from_raw(GRANTEE_1_GID),
+            move || {
+                let keystore2 = get_keystore_service();
+
+                let other_cert: [u8; 32] = [123; 32];
+                let other_cert_chain: [u8; 32] = [12; 32];
+
+                let result = key_generations::map_ks_error(keystore2.updateSubcomponent(
+                    &KeyDescriptor {
+                        domain: Domain::GRANT,
+                        nspace: granted_key1_nspace,
+                        alias: None,
+                        blob: None,
+                    },
+                    Some(&other_cert),
+                    Some(&other_cert_chain),
+                ));
+                assert!(result.is_err());
+                assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+            },
+        )
+    };
+
+    // Grantee context, update granted key public certs. Update should happen successfully.
+    let granted_key2_nspace = granted_keys.remove(0);
+    unsafe {
+        run_as::run_as(
+            GRANTEE_CTX,
+            Uid::from_raw(GRANTEE_2_UID),
+            Gid::from_raw(GRANTEE_2_GID),
+            move || {
+                let keystore2 = get_keystore_service();
+
+                let other_cert: [u8; 32] = [124; 32];
+                let other_cert_chain: [u8; 32] = [13; 32];
+
+                keystore2
+                    .updateSubcomponent(
+                        &KeyDescriptor {
+                            domain: Domain::GRANT,
+                            nspace: granted_key2_nspace,
+                            alias: None,
+                            blob: None,
+                        },
+                        Some(&other_cert),
+                        Some(&other_cert_chain),
+                    )
+                    .expect("updateSubcomponent should have succeeded.");
+
+                let key_entry_response = keystore2
+                    .getKeyEntry(&KeyDescriptor {
+                        domain: Domain::GRANT,
+                        nspace: granted_key2_nspace,
+                        alias: None,
+                        blob: None,
+                    })
+                    .unwrap();
+                assert_eq!(Some(other_cert.to_vec()), key_entry_response.metadata.certificate);
+                assert_eq!(
+                    Some(other_cert_chain.to_vec()),
+                    key_entry_response.metadata.certificateChain
+                );
+            },
+        )
+    };
+}
+
+#[test]
+fn keystore2_get_security_level_success() {
+    let keystore2 = get_keystore_service();
+    assert!(
+        keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).is_ok(),
+        "getSecurityLevel with SecurityLevel::TRUSTED_ENVIRONMENT should have succeeded."
+    );
+}
+
+#[test]
+fn keystore2_get_security_level_failure() {
+    let keystore2 = get_keystore_service();
+    let result = key_generations::map_ks_error(keystore2.getSecurityLevel(SecurityLevel::SOFTWARE));
+
+    assert!(result.is_err());
+    assert_eq!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE), result.unwrap_err());
+}
diff --git a/keystore2/tests/legacy_blobs/Android.bp b/keystore2/tests/legacy_blobs/Android.bp
index 9322a41..92f2cc3 100644
--- a/keystore2/tests/legacy_blobs/Android.bp
+++ b/keystore2/tests/legacy_blobs/Android.bp
@@ -33,8 +33,6 @@
     rustlibs: [
         "libkeystore2_with_test_utils",
         "libkeystore2_crypto_rust",
-        "android.system.keystore2-V2-rust",
-        "android.hardware.security.keymint-V2-rust",
         "android.security.maintenance-rust",
         "android.security.authorization-rust",
         "librustutils",
@@ -47,5 +45,9 @@
         "libserde",
         "libthiserror",
     ],
+    defaults: [
+        "keymint_use_latest_hal_aidl_rust",
+        "keystore2_use_latest_aidl_rust",
+    ],
     require_root: true,
 }
diff --git a/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs b/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
index 6def39e..63122fe 100644
--- a/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
+++ b/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
@@ -85,7 +85,7 @@
         .expect("failed to execute pidof keystore2");
 
     let id = String::from_utf8(output.stdout).unwrap();
-    let id: String = id.chars().filter(|c| c.is_digit(10)).collect();
+    let id: String = id.chars().filter(|c| c.is_ascii_digit()).collect();
 
     let _status = std::process::Command::new("kill").arg("-9").arg(id).status().unwrap();
 
@@ -164,13 +164,19 @@
                 .getSecurityLevel(SecurityLevel::SecurityLevel::TRUSTED_ENVIRONMENT)
                 .unwrap();
             // Generate Key BLOB and prepare legacy keystore blob files.
-            let key_metadata =
-                key_generations::generate_ec_p256_signing_key_with_attestation(&sec_level)
-                    .expect("Failed to generate key blob");
+            let att_challenge: &[u8] = b"foo";
+            let key_metadata = key_generations::generate_ec_p256_signing_key(
+                &sec_level,
+                Domain::BLOB,
+                SELINUX_SHELL_NAMESPACE,
+                None,
+                Some(att_challenge),
+            )
+            .expect("Failed to generate key blob");
 
             // Create keystore file layout for user_99.
             let pw: Password = PASSWORD.into();
-            let pw_key = TestKey(pw.derive_key(Some(SUPERKEY_SALT), 32).unwrap());
+            let pw_key = TestKey(pw.derive_key(SUPERKEY_SALT, 32).unwrap());
             let super_key =
                 TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap());
 
@@ -415,13 +421,19 @@
                 .getSecurityLevel(SecurityLevel::SecurityLevel::TRUSTED_ENVIRONMENT)
                 .unwrap();
             // Generate Key BLOB and prepare legacy keystore blob files.
-            let key_metadata =
-                key_generations::generate_ec_p256_signing_key_with_attestation(&sec_level)
-                    .expect("Failed to generate key blob");
+            let att_challenge: &[u8] = b"foo";
+            let key_metadata = key_generations::generate_ec_p256_signing_key(
+                &sec_level,
+                Domain::BLOB,
+                SELINUX_SHELL_NAMESPACE,
+                None,
+                Some(att_challenge),
+            )
+            .expect("Failed to generate key blob");
 
             // Create keystore file layout for user_98.
             let pw: Password = PASSWORD.into();
-            let pw_key = TestKey(pw.derive_key(Some(SUPERKEY_SALT), 32).unwrap());
+            let pw_key = TestKey(pw.derive_key(SUPERKEY_SALT, 32).unwrap());
             let super_key =
                 TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap());
 
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
index d73f8fe..f56cfab 100644
--- a/ondevice-signing/Android.bp
+++ b/ondevice-signing/Android.bp
@@ -101,6 +101,15 @@
   recovery_available: true,
 }
 
+genrule {
+  name: "statslog_odsign.h",
+  tools: ["stats-log-api-gen"],
+  cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_odsign.h --module art --namespace art,metrics,statsd",
+  out: [
+    "statslog_odsign.h",
+  ],
+}
+
 cc_binary {
   name: "odsign",
   defaults: [
@@ -114,6 +123,7 @@
     "odsign_main.cpp",
     "StatsReporter.cpp",
   ],
+  generated_headers: ["statslog_odsign.h"],
 
   header_libs: ["odrefresh_headers"],
 
diff --git a/ondevice-signing/StatsReporter.cpp b/ondevice-signing/StatsReporter.cpp
index 65e645a..e4e4a03 100644
--- a/ondevice-signing/StatsReporter.cpp
+++ b/ondevice-signing/StatsReporter.cpp
@@ -20,12 +20,13 @@
 #include <string>
 #include <sys/stat.h>
 
-// Keep these constant in sync with COMPOS_METRIC_NAME & METRICS_FILE in OdsignStatsLogger.java.
+// Keep these constants in sync with those in OdsignStatsLogger.java.
 constexpr const char* kOdsignMetricsFile = "/data/misc/odsign/metrics/odsign-metrics.txt";
 constexpr const char* kComposMetricName = "comp_os_artifacts_check_record";
+constexpr const char* kOdsignMetricName = "odsign_record";
 
 StatsReporter::~StatsReporter() {
-    if (comp_os_artifacts_check_record_ == nullptr) {
+    if (comp_os_artifacts_check_record_ == nullptr && !odsign_record_enabled_) {
         LOG(INFO) << "Metrics report is empty";
 
         // Remove the metrics file if any old version of the file already exists
@@ -42,24 +43,31 @@
         PLOG(ERROR) << "Could not open file: " << kOdsignMetricsFile;
         return;
     }
-
-    odsign_metrics_file_ << kComposMetricName << ' ';
-    odsign_metrics_file_ << comp_os_artifacts_check_record_->current_artifacts_ok << ' ';
-    odsign_metrics_file_ << comp_os_artifacts_check_record_->comp_os_pending_artifacts_exists
-                         << ' ';
-    odsign_metrics_file_ << comp_os_artifacts_check_record_->use_comp_os_generated_artifacts
-                         << '\n';
     if (chmod(kOdsignMetricsFile, 0644) != 0) {
         PLOG(ERROR) << "Could not set correct file permissions for " << kOdsignMetricsFile;
         return;
     }
+
+    if (comp_os_artifacts_check_record_ != nullptr) {
+        odsign_metrics_file_ << kComposMetricName << ' '
+                             << comp_os_artifacts_check_record_->current_artifacts_ok << ' '
+                             << comp_os_artifacts_check_record_->comp_os_pending_artifacts_exists
+                             << ' '
+                             << comp_os_artifacts_check_record_->use_comp_os_generated_artifacts
+                             << '\n';
+    }
+
+    if (odsign_record_enabled_) {
+        odsign_metrics_file_ << kOdsignMetricName << ' ' << odsign_record_.status << '\n';
+    }
+
     odsign_metrics_file_.close();
     if (!odsign_metrics_file_) {
         PLOG(ERROR) << "Failed to close the file";
     }
 }
 
-StatsReporter::CompOsArtifactsCheckRecord* StatsReporter::GetComposArtifactsCheckRecord() {
+StatsReporter::CompOsArtifactsCheckRecord* StatsReporter::GetOrCreateComposArtifactsCheckRecord() {
     if (comp_os_artifacts_check_record_ == nullptr) {
         comp_os_artifacts_check_record_ = std::make_unique<CompOsArtifactsCheckRecord>();
     }
diff --git a/ondevice-signing/StatsReporter.h b/ondevice-signing/StatsReporter.h
index 2682b96..add7a11 100644
--- a/ondevice-signing/StatsReporter.h
+++ b/ondevice-signing/StatsReporter.h
@@ -18,27 +18,44 @@
 
 #include <fstream>
 
+#include "statslog_odsign.h"
+
 // Class to store CompOsArtifactsCheck related metrics.
 // These are flushed to a file kOdsignMetricsFile and consumed by
 // System Server (in class OdsignStatsLogger) & sent to statsd.
 class StatsReporter {
   public:
-    // Keep sync with EarlyBootCompOsArtifactsCheckReported
-    // definition in proto_logging/stats/atoms.proto.
+    // Keep in sync with the EarlyBootCompOsArtifactsCheckReported definition in
+    // proto_logging/stats/atoms.proto.
     struct CompOsArtifactsCheckRecord {
         bool current_artifacts_ok = false;
         bool comp_os_pending_artifacts_exists = false;
         bool use_comp_os_generated_artifacts = false;
     };
 
+    // Keep in sync with the OdsignReported definition in proto_logging/stats/atoms.proto.
+    struct OdsignRecord {
+        int32_t status = art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_UNSPECIFIED;
+    };
+
     // The report is flushed (from buffer) into a file by the destructor.
     ~StatsReporter();
 
-    // Get pointer to comp_os_artifacts_check_record, caller can then modify it.
-    // Note: pointer remains valid for the lifetime of this StatsReporter.
-    CompOsArtifactsCheckRecord* GetComposArtifactsCheckRecord();
+    // Returns a mutable CompOS record. The pointer remains valid for the lifetime of this
+    // StatsReporter. If this function is not called, no CompOS record will be logged.
+    CompOsArtifactsCheckRecord* GetOrCreateComposArtifactsCheckRecord();
+
+    // Returns a mutable odsign record. The pointer remains valid for the lifetime of this
+    // StatsReporter.
+    OdsignRecord* GetOdsignRecord() { return &odsign_record_; }
+
+    // Enables/disables odsign metrics.
+    void SetOdsignRecordEnabled(bool value) { odsign_record_enabled_ = value; }
 
   private:
     // Temporary buffer which stores the metrics.
     std::unique_ptr<CompOsArtifactsCheckRecord> comp_os_artifacts_check_record_;
+
+    OdsignRecord odsign_record_;
+    bool odsign_record_enabled_ = true;
 };
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
index cd9a1ea..0b631da 100644
--- a/ondevice-signing/VerityUtils.cpp
+++ b/ondevice-signing/VerityUtils.cpp
@@ -26,16 +26,15 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 
+#include "android-base/errors.h"
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/result.h>
 #include <android-base/unique_fd.h>
 #include <asm/byteorder.h>
 #include <libfsverity.h>
 #include <linux/fsverity.h>
 
-#include "CertUtils.h"
-#include "SigningKey.h"
-
 #define FS_VERITY_MAX_DIGEST_SIZE 64
 
 using android::base::ErrnoError;
@@ -58,23 +57,6 @@
     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;
@@ -127,20 +109,6 @@
     }
 };
 
-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>>;
 
 template <typename T>
@@ -150,28 +118,35 @@
     return trailing_unique_ptr<T>{ptr};
 }
 
-static Result<std::vector<uint8_t>> signDigest(const SigningKey& key,
-                                               const std::vector<uint8_t>& digest) {
-    auto d = makeUniqueWithTrailingData<fsverity_formatted_digest>(digest.size());
+static Result<std::string> measureFsVerity(int fd) {
+    auto d = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE);
+    d->digest_size = FS_VERITY_MAX_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());
-    memcpy(d->digest, digest.data(), digest.size());
-
-    auto signed_digest = key.sign(std::string((char*)d.get(), sizeof(*d) + digest.size()));
-    if (!signed_digest.ok()) {
-        return signed_digest.error();
+    if (ioctl(fd, FS_IOC_MEASURE_VERITY, d.get()) != 0) {
+        if (errno == ENODATA) {
+            return Error() << "File is not in fs-verity";
+        } else {
+            return ErrnoError() << "Failed to FS_IOC_MEASURE_VERITY";
+        }
     }
 
-    return std::vector<uint8_t>(signed_digest->begin(), signed_digest->end());
+    return toHex({&d->digest[0], &d->digest[d->digest_size]});
 }
 
-static Result<void> enableFsVerity(int fd, std::span<uint8_t> pkcs7) {
+static Result<std::string> measureFsVerity(const std::string& path) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (!fd.ok()) {
+        return ErrnoError() << "Failed to open " << path;
+    }
+
+    return measureFsVerity(fd.get());
+}
+
+}  // namespace
+
+static Result<void> enableFsVerity(int fd) {
     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;
 
@@ -184,59 +159,24 @@
     return {};
 }
 
-Result<std::string> enableFsVerity(int fd, const SigningKey& key) {
-    auto digest = createDigest(fd);
-    if (!digest.ok()) {
-        return Error() << digest.error();
-    }
-
-    auto signed_digest = signDigest(key, digest.value());
-    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();
-    }
-
-    // Return the root hash as a hex string
-    return toHex(digest.value());
-}
-
-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) {
+Result<void> enableFsVerity(const std::string& path) {
     unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
     if (!fd.ok()) {
-        return ErrnoError() << "Failed to open " << path;
+        return Error() << "Can't open " << path;
     }
 
-    auto digest = isFileInVerity(fd.get());
-    if (!digest.ok()) {
-        return Error() << digest.error() << ": " << path;
-    }
-
-    return digest;
+    return enableFsVerity(fd.get());
 }
 
-Result<std::map<std::string, std::string>> addFilesToVerityRecursive(const std::string& path,
-                                                                     const SigningKey& key) {
+static Result<bool> isFileInVerity(int fd) {
+    unsigned int flags;
+    if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0) {
+        return ErrnoError() << "ioctl(FS_IOC_GETFLAGS) failed";
+    }
+    return (flags & FS_VERITY_FL) != 0;
+}
+
+Result<std::map<std::string, std::string>> addFilesToVerityRecursive(const std::string& path) {
     std::map<std::string, std::string> digests;
 
     std::error_code ec;
@@ -247,18 +187,15 @@
             if (!fd.ok()) {
                 return ErrnoError() << "Failed to open " << path;
             }
-            auto digest = isFileInVerity(fd);
-            if (!digest.ok()) {
+            auto enabled = OR_RETURN(isFileInVerity(fd));
+            if (!enabled) {
                 LOG(INFO) << "Adding " << it->path() << " to fs-verity...";
-                auto result = enableFsVerity(fd, key);
-                if (!result.ok()) {
-                    return result.error();
-                }
-                digests[it->path()] = *result;
+                OR_RETURN(enableFsVerity(fd));
             } else {
                 LOG(INFO) << it->path() << " was already in fs-verity.";
-                digests[it->path()] = *digest;
             }
+            auto digest = OR_RETURN(measureFsVerity(fd));
+            digests[it->path()] = digest;
         }
     }
     if (ec) {
@@ -268,31 +205,6 @@
     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;
@@ -303,11 +215,8 @@
     while (!ec && it != end) {
         if (it->is_regular_file()) {
             // Verify the file is in fs-verity
-            auto result = isFileInVerity(it->path());
-            if (!result.ok()) {
-                return result.error();
-            }
-            digests[it->path()] = *result;
+            auto result = OR_RETURN(measureFsVerity(it->path()));
+            digests[it->path()] = result;
         } else if (it->is_directory()) {
             // These are fine to ignore
         } else if (it->is_symlink()) {
@@ -325,8 +234,7 @@
 }
 
 Result<void> verifyAllFilesUsingCompOs(const std::string& directory_path,
-                                       const std::map<std::string, std::string>& digests,
-                                       const SigningKey& signing_key) {
+                                       const std::map<std::string, std::string>& digests) {
     std::error_code ec;
     size_t verified_count = 0;
     auto it = std::filesystem::recursive_directory_iterator(directory_path, ec);
@@ -344,41 +252,18 @@
                 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...";
+            bool enabled = OR_RETURN(isFileInVerity(fd));
+            if (!enabled) {
+                LOG(INFO) << "Enabling fs-verity for " << path;
+                OR_RETURN(enableFsVerity(fd));
+            }
 
-                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();
-                }
+            auto actual_digest = OR_RETURN(measureFsVerity(fd));
+            // Make sure the file's fs-verity digest matches the known value.
+            if (actual_digest == compos_digest) {
                 ++verified_count;
+            } else {
+                return Error() << "fs-verity digest does not match CompOS digest: " << path;
             }
         } else if (it->is_directory()) {
             // These are fine to ignore
diff --git a/ondevice-signing/include/VerityUtils.h b/ondevice-signing/include/VerityUtils.h
index e6e49c7..626bbdb 100644
--- a/ondevice-signing/include/VerityUtils.h
+++ b/ondevice-signing/include/VerityUtils.h
@@ -22,11 +22,9 @@
 #include <string>
 #include <vector>
 
-#include "SigningKey.h"
-
 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);
+android::base::Result<std::string> enableFsVerity(int fd);
 bool SupportsFsVerity();
 android::base::Result<std::map<std::string, std::string>>
 verifyAllFilesInVerity(const std::string& path);
@@ -34,13 +32,11 @@
 // Note that this function will skip files that are already in fs-verity, and
 // 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);
+addFilesToVerityRecursive(const std::string& path);
 
 // 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> enableFsVerity(const std::string& path);
 
 android::base::Result<void>
 verifyAllFilesUsingCompOs(const std::string& directory_path,
-                          const std::map<std::string, std::string>& digests,
-                          const SigningKey& signing_key);
+                          const std::map<std::string, std::string>& digests);
diff --git a/ondevice-signing/odsign.rc b/ondevice-signing/odsign.rc
index de09fc0..b96c62f 100644
--- a/ondevice-signing/odsign.rc
+++ b/ondevice-signing/odsign.rc
@@ -3,6 +3,13 @@
     user root
     group system
     disabled # does not start with the core class
+    # Explicitly specify empty capabilities, otherwise odsign will inherit all
+    # the capabilities from init.
+    # Note: whether a process can use capabilities is controlled by SELinux, so
+    # inheriting all the capabilities from init is not a security issue.
+    # However, for defense-in-depth and just for the sake of bookkeeping it's
+    # better to explicitly state that odsign doesn't need any capabilities.
+    capabilities
 
 # Note that odsign is not oneshot, but stopped manually when it exits. This
 # ensures that if odsign crashes during a module update, apexd will detect
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
index c45e308..a688ead 100644
--- a/ondevice-signing/odsign_main.cpp
+++ b/ondevice-signing/odsign_main.cpp
@@ -35,6 +35,7 @@
 #include "KeystoreKey.h"
 #include "StatsReporter.h"
 #include "VerityUtils.h"
+#include "statslog_odsign.h"
 
 #include "odsign_info.pb.h"
 
@@ -370,7 +371,7 @@
                                                      bool* digests_verified,
                                                      StatsReporter* stats_reporter) {
     StatsReporter::CompOsArtifactsCheckRecord* compos_check_record =
-        stats_reporter->GetComposArtifactsCheckRecord();
+        stats_reporter->GetOrCreateComposArtifactsCheckRecord();
 
     if (!directoryHasContent(kCompOsPendingArtifactsDir)) {
         // No pending CompOS artifacts, all that matters is the current ones.
@@ -420,7 +421,7 @@
         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);
+        auto status = verifyAllFilesUsingCompOs(kArtArtifactsDir, compos_digests);
         if (!status.ok()) {
             LOG(WARNING) << "Faild to verify CompOS artifacts: " << status.error();
         } else {
@@ -468,12 +469,9 @@
 }  // namespace
 
 int main(int /* argc */, char** argv) {
-    // stats_reporter is a pointer so that we can explicitly delete it
-    // instead of waiting for the program to die & its destrcutor be called
-    auto stats_reporter = std::make_unique<StatsReporter>();
     android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
 
-    auto errorScopeGuard = []() {
+    auto scope_guard = android::base::make_scope_guard([]() {
         // In case we hit any error, remove the artifacts and tell Zygote not to use
         // anything
         removeDirectory(kArtArtifactsDir);
@@ -485,17 +483,24 @@
         SetProperty(kOdsignVerificationDoneProp, "1");
         // Tell init it shouldn't try to restart us - see odsign.rc
         SetProperty(kStopServiceProp, "odsign");
-    };
-    auto scope_guard = android::base::make_scope_guard(errorScopeGuard);
+    });
+
+    // `stats_reporter` must come after `scope_guard` so that its destructor is called before
+    // `scope_guard`.
+    auto stats_reporter = std::make_unique<StatsReporter>();
+    StatsReporter::OdsignRecord* odsign_record = stats_reporter->GetOdsignRecord();
 
     if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
         LOG(INFO) << "Device doesn't support updatable APEX, exiting.";
+        stats_reporter->SetOdsignRecordEnabled(false);
         return 0;
     }
     auto keystoreResult =
         KeystoreKey::getInstance(kPublicKeySignature, kKeyAlias, kKeyNspace, kKeyBootLevel);
     if (!keystoreResult.ok()) {
         LOG(ERROR) << "Could not create keystore key: " << keystoreResult.error();
+        odsign_record->status =
+            art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_KEYSTORE_FAILED;
         return -1;
     }
     SigningKey* key = keystoreResult.value();
@@ -517,17 +522,13 @@
             if (!new_cert.ok()) {
                 LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error();
                 // TODO apparently the key become invalid - delete the blob / cert
+                odsign_record->status =
+                    art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_CERT_FAILED;
                 return -1;
             }
         } else {
             LOG(INFO) << "Found and verified existing public key certificate: " << 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();
-            return -1;
-        }
     }
 
     bool digests_verified = false;
@@ -535,12 +536,6 @@
         useCompOs ? CheckCompOsPendingArtifacts(*key, &digests_verified, stats_reporter.get())
                   : checkArtifacts();
 
-    // Explicitly reset the pointer - We rely on stats_reporter's
-    // destructor for actually writing the buffered metrics. This will otherwise not be called
-    // if the program doesn't exit normally (for ex, killed by init, which actually happens
-    // because odsign (after it finishes) sets kStopServiceProp instructing init to kill it).
-    stats_reporter.reset();
-
     // The artifacts dir doesn't necessarily need to exist; if the existing
     // artifacts on the system partition are valid, those can be used.
     int err = access(kArtArtifactsDir.c_str(), F_OK);
@@ -578,6 +573,8 @@
                 // instead prevent Zygote from using them (which is taken care of
                 // in the exit handler).
                 LOG(ERROR) << "Failed to remove unknown artifacts.";
+                odsign_record->status =
+                    art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_CLEANUP_FAILED;
                 return -1;
             }
         }
@@ -591,14 +588,19 @@
     if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
         // No new artifacts generated, and we verified existing ones above, nothing left to do.
         LOG(INFO) << "odrefresh said artifacts are VALID";
+        stats_reporter->SetOdsignRecordEnabled(false);
     } else if (odrefresh_status == art::odrefresh::ExitCode::kCompilationSuccess ||
                odrefresh_status == art::odrefresh::ExitCode::kCompilationFailed) {
         const bool compiled_all = odrefresh_status == art::odrefresh::ExitCode::kCompilationSuccess;
         LOG(INFO) << "odrefresh compiled " << (compiled_all ? "all" : "partial")
                   << " artifacts, returned " << odrefresh_status;
+        // This value may be overwritten later.
+        odsign_record->status =
+            compiled_all ? art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_ALL_OK
+                         : art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_PARTIAL_OK;
         Result<std::map<std::string, std::string>> digests;
         if (supportsFsVerity) {
-            digests = addFilesToVerityRecursive(kArtArtifactsDir, *key);
+            digests = addFilesToVerityRecursive(kArtArtifactsDir);
         } else {
             // If we can't use verity, just compute the root hashes and store
             // those, so we can reverify them at the next boot.
@@ -606,24 +608,39 @@
         }
         if (!digests.ok()) {
             LOG(ERROR) << digests.error();
+            odsign_record->status =
+                art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_SIGNING_FAILED;
             return -1;
         }
         auto persistStatus = persistDigests(*digests, *key);
         if (!persistStatus.ok()) {
             LOG(ERROR) << persistStatus.error();
+            odsign_record->status =
+                art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_SIGNING_FAILED;
             return -1;
         }
     } else if (odrefresh_status == art::odrefresh::ExitCode::kCleanupFailed) {
         LOG(ERROR) << "odrefresh failed cleaning up existing artifacts";
+        odsign_record->status =
+            art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_ODREFRESH_FAILED;
         return -1;
     } else {
         LOG(ERROR) << "odrefresh exited unexpectedly, returned " << odrefresh_status;
+        odsign_record->status =
+            art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_ODREFRESH_FAILED;
         return -1;
     }
 
     LOG(INFO) << "On-device signing done.";
 
     scope_guard.Disable();
+
+    // Explicitly reset the pointer - We rely on stats_reporter's
+    // destructor for actually writing the buffered metrics. This will otherwise not be called
+    // if the program doesn't exit normally (for ex, killed by init, which actually happens
+    // because odsign (after it finishes) sets kStopServiceProp instructing init to kill it).
+    stats_reporter.reset();
+
     // At this point, we're done with the key for sure
     SetProperty(kOdsignKeyDoneProp, "1");
     // And we did a successful verification
diff --git a/prng_seeder/Android.bp b/prng_seeder/Android.bp
new file mode 100644
index 0000000..763aaa0
--- /dev/null
+++ b/prng_seeder/Android.bp
@@ -0,0 +1,82 @@
+// Copyright (C) 2022 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
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+rust_bindgen {
+    name: "libcutils_socket_bindgen",
+    crate_name: "cutils_socket_bindgen",
+    wrapper_src: "cutils_wrapper.h",
+    source_stem: "bindings",
+    bindgen_flags: [
+        "--allowlist-function=android_get_control_socket",
+    ],
+    shared_libs: [
+        "libcutils",
+    ],
+}
+
+rust_defaults {
+    name: "prng_seeder_defaults",
+    edition: "2021",
+    rustlibs: [
+        "libanyhow",
+        "libbssl_ffi",
+        "libclap",
+        "libcutils_socket_bindgen",
+        "liblogger",
+        "liblog_rust",
+        "libnix",
+        "libtokio",
+    ],
+
+    init_rc: ["prng_seeder.rc"],
+}
+
+rust_binary {
+    name: "prng_seeder",
+    defaults: ["prng_seeder_defaults"],
+    srcs: ["src/main.rs"],
+}
+
+rust_binary {
+    name: "prng_seeder_microdroid",
+    defaults: ["prng_seeder_defaults"],
+    srcs: ["src/main.rs"],
+    stem: "prng_seeder",
+    bootstrap: true,
+    installable: false,
+    prefer_rlib: true,
+}
+
+rust_test {
+    name: "prng_seeder.test",
+    edition: "2021",
+    srcs: ["src/main.rs"],
+    rustlibs: [
+        "libanyhow",
+        "libbssl_ffi",
+        "libclap",
+        "libcutils_socket_bindgen",
+        "liblogger",
+        "liblog_rust",
+        "libnix",
+        "libtokio",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/prng_seeder/OWNERS b/prng_seeder/OWNERS
new file mode 100644
index 0000000..51b7f38
--- /dev/null
+++ b/prng_seeder/OWNERS
@@ -0,0 +1,2 @@
+paulcrowley@google.com
+prb@google.com
diff --git a/diced/src/lib_vendor.rs b/prng_seeder/cutils_wrapper.h
similarity index 66%
rename from diced/src/lib_vendor.rs
rename to prng_seeder/cutils_wrapper.h
index 01c804b..9c1fe56 100644
--- a/diced/src/lib_vendor.rs
+++ b/prng_seeder/cutils_wrapper.h
@@ -1,4 +1,4 @@
-// Copyright 2021, The Android Open Source Project
+// Copyright (C) 2022 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.
@@ -12,9 +12,4 @@
 // 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;
+#include <cutils/sockets.h>
diff --git a/prng_seeder/prng_seeder.rc b/prng_seeder/prng_seeder.rc
new file mode 100644
index 0000000..9825583
--- /dev/null
+++ b/prng_seeder/prng_seeder.rc
@@ -0,0 +1,12 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Start PRNG seeder daemon from early-init
+
+on early-init
+    start prng_seeder
+
+service prng_seeder /system/bin/prng_seeder
+    user prng_seeder
+    group prng_seeder
+    stdio_to_kmsg
+    socket prng_seeder stream+listen 0666 prng_seeder prng_seeder
diff --git a/prng_seeder/src/conditioner.rs b/prng_seeder/src/conditioner.rs
new file mode 100644
index 0000000..ec1181b
--- /dev/null
+++ b/prng_seeder/src/conditioner.rs
@@ -0,0 +1,73 @@
+// Copyright (C) 2022 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 std::{fs::File, io::Read};
+
+use anyhow::{ensure, Context, Result};
+use log::debug;
+use tokio::io::AsyncReadExt;
+
+use crate::drbg;
+
+const SEED_FOR_CLIENT_LEN: usize = 496;
+const NUM_REQUESTS_PER_RESEED: u32 = 256;
+
+pub struct ConditionerBuilder {
+    hwrng: File,
+    rg: drbg::Drbg,
+}
+
+impl ConditionerBuilder {
+    pub fn new(mut hwrng: File) -> Result<ConditionerBuilder> {
+        let mut et: drbg::Entropy = [0; drbg::ENTROPY_LEN];
+        hwrng.read_exact(&mut et).context("hwrng.read_exact in new")?;
+        let rg = drbg::Drbg::new(&et)?;
+        Ok(ConditionerBuilder { hwrng, rg })
+    }
+
+    pub fn build(self) -> Conditioner {
+        Conditioner {
+            hwrng: tokio::fs::File::from_std(self.hwrng),
+            rg: self.rg,
+            requests_since_reseed: 0,
+        }
+    }
+}
+
+pub struct Conditioner {
+    hwrng: tokio::fs::File,
+    rg: drbg::Drbg,
+    requests_since_reseed: u32,
+}
+
+impl Conditioner {
+    pub async fn reseed_if_necessary(&mut self) -> Result<()> {
+        if self.requests_since_reseed >= NUM_REQUESTS_PER_RESEED {
+            debug!("Reseeding DRBG");
+            let mut et: drbg::Entropy = [0; drbg::ENTROPY_LEN];
+            self.hwrng.read_exact(&mut et).await.context("hwrng.read_exact in reseed")?;
+            self.rg.reseed(&et)?;
+            self.requests_since_reseed = 0;
+        }
+        Ok(())
+    }
+
+    pub fn request(&mut self) -> Result<[u8; SEED_FOR_CLIENT_LEN]> {
+        ensure!(self.requests_since_reseed < NUM_REQUESTS_PER_RESEED, "Not enough reseeds");
+        let mut seed_for_client = [0u8; SEED_FOR_CLIENT_LEN];
+        self.rg.generate(&mut seed_for_client)?;
+        self.requests_since_reseed += 1;
+        Ok(seed_for_client)
+    }
+}
diff --git a/prng_seeder/src/cutils_socket.rs b/prng_seeder/src/cutils_socket.rs
new file mode 100644
index 0000000..ab2c869
--- /dev/null
+++ b/prng_seeder/src/cutils_socket.rs
@@ -0,0 +1,25 @@
+// Copyright (C) 2022 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 std::ffi::CString;
+use std::os::unix::{net::UnixListener, prelude::FromRawFd};
+
+use anyhow::{ensure, Result};
+
+pub fn android_get_control_socket(name: &str) -> Result<UnixListener> {
+    let name = CString::new(name)?;
+    let fd = unsafe { cutils_socket_bindgen::android_get_control_socket(name.as_ptr()) };
+    ensure!(fd >= 0, "android_get_control_socket failed");
+    Ok(unsafe { UnixListener::from_raw_fd(fd) })
+}
diff --git a/prng_seeder/src/drbg.rs b/prng_seeder/src/drbg.rs
new file mode 100644
index 0000000..89c5a88
--- /dev/null
+++ b/prng_seeder/src/drbg.rs
@@ -0,0 +1,65 @@
+// Copyright (C) 2022 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 anyhow::{ensure, Result};
+use bssl_ffi as bssl_sys;
+
+pub const ENTROPY_LEN: usize = bssl_sys::CTR_DRBG_ENTROPY_LEN as usize;
+
+pub type Entropy = [u8; ENTROPY_LEN];
+
+pub struct Drbg(*mut bssl_sys::CTR_DRBG_STATE);
+
+impl Drbg {
+    pub fn new(entropy: &Entropy) -> Result<Drbg> {
+        let p = unsafe { bssl_sys::CTR_DRBG_new(entropy.as_ptr(), std::ptr::null(), 0) };
+        ensure!(!p.is_null(), "CTR_DRBG_new failed");
+        Ok(Drbg(p))
+    }
+
+    pub fn reseed(&mut self, entropy: &Entropy) -> Result<()> {
+        ensure!(
+            unsafe { bssl_sys::CTR_DRBG_reseed(self.0, entropy.as_ptr(), std::ptr::null(), 0) }
+                == 1,
+            "CTR_DRBG_reseed failed"
+        );
+        Ok(())
+    }
+
+    pub fn generate(&mut self, buf: &mut [u8]) -> Result<()> {
+        ensure!(
+            unsafe {
+                bssl_sys::CTR_DRBG_generate(
+                    self.0,
+                    buf.as_mut_ptr(),
+                    buf.len(),
+                    std::ptr::null(),
+                    0,
+                )
+            } == 1,
+            "CTR_DRBG_generate failed"
+        );
+        Ok(())
+    }
+}
+
+impl Drop for Drbg {
+    fn drop(&mut self) {
+        unsafe {
+            bssl_sys::CTR_DRBG_free(self.0);
+        }
+    }
+}
+
+unsafe impl Send for Drbg {}
diff --git a/prng_seeder/src/main.rs b/prng_seeder/src/main.rs
new file mode 100644
index 0000000..924481a
--- /dev/null
+++ b/prng_seeder/src/main.rs
@@ -0,0 +1,148 @@
+// Copyright (C) 2022 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.
+
+//! FIPS compliant random number conditioner. Reads from /dev/hw_random
+//! and applies the NIST SP 800-90A CTR DRBG strategy to provide
+//! pseudorandom bytes to clients which connect to a socket provided
+//! by init.
+
+mod conditioner;
+mod cutils_socket;
+mod drbg;
+
+use std::{
+    convert::Infallible,
+    fs::remove_file,
+    io::ErrorKind,
+    os::unix::net::UnixListener,
+    path::{Path, PathBuf},
+};
+
+use anyhow::{ensure, Context, Result};
+use clap::Parser;
+use log::{error, info, Level};
+use nix::sys::signal;
+use tokio::{io::AsyncWriteExt, net::UnixListener as TokioUnixListener};
+
+use crate::conditioner::ConditionerBuilder;
+
+#[derive(Debug, Parser)]
+struct Cli {
+    #[clap(long, default_value = "/dev/hw_random")]
+    source: PathBuf,
+    #[clap(long)]
+    socket: Option<PathBuf>,
+}
+
+fn configure_logging() -> Result<()> {
+    ensure!(
+        logger::init(
+            logger::Config::default().with_tag_on_device("prng_seeder").with_min_level(Level::Info)
+        ),
+        "log configuration failed"
+    );
+    Ok(())
+}
+
+fn get_socket(path: &Path) -> Result<UnixListener> {
+    if let Err(e) = remove_file(path) {
+        if e.kind() != ErrorKind::NotFound {
+            return Err(e).context(format!("Removing old socket: {}", path.display()));
+        }
+    } else {
+        info!("Deleted old {}", path.display());
+    }
+    UnixListener::bind(path)
+        .with_context(|| format!("In get_socket: binding socket to {}", path.display()))
+}
+
+fn setup() -> Result<(ConditionerBuilder, UnixListener)> {
+    configure_logging()?;
+    let cli = Cli::try_parse()?;
+    unsafe { signal::signal(signal::Signal::SIGPIPE, signal::SigHandler::SigIgn) }
+        .context("In setup, setting SIGPIPE to SIG_IGN")?;
+
+    let listener = match cli.socket {
+        Some(path) => get_socket(path.as_path())?,
+        None => cutils_socket::android_get_control_socket("prng_seeder")
+            .context("In setup, calling android_get_control_socket")?,
+    };
+    let hwrng = std::fs::File::open(&cli.source)
+        .with_context(|| format!("Unable to open hwrng {}", cli.source.display()))?;
+    let cb = ConditionerBuilder::new(hwrng)?;
+    Ok((cb, listener))
+}
+
+async fn listen_loop(cb: ConditionerBuilder, listener: UnixListener) -> Result<Infallible> {
+    let mut conditioner = cb.build();
+    listener.set_nonblocking(true).context("In listen_loop, on set_nonblocking")?;
+    let listener = TokioUnixListener::from_std(listener).context("In listen_loop, on from_std")?;
+    info!("Starting listen loop");
+    loop {
+        match listener.accept().await {
+            Ok((mut stream, _)) => {
+                let new_bytes = conditioner.request()?;
+                tokio::spawn(async move {
+                    if let Err(e) = stream.write_all(&new_bytes).await {
+                        error!("Request failed: {}", e);
+                    }
+                });
+                conditioner.reseed_if_necessary().await?;
+            }
+            Err(e) if e.kind() == ErrorKind::Interrupted => {}
+            Err(e) => return Err(e).context("accept on socket failed"),
+        }
+    }
+}
+
+fn run() -> Result<Infallible> {
+    let (cb, listener) = match setup() {
+        Ok(t) => t,
+        Err(e) => {
+            // If setup fails, just hang forever. That way init doesn't respawn us.
+            error!("Hanging forever because setup failed: {:?}", e);
+            // Logs are sometimes mysteriously not being logged, so print too
+            println!("prng_seeder: Hanging forever because setup failed: {:?}", e);
+            loop {
+                std::thread::park();
+                error!("std::thread::park() finished unexpectedly, re-parking thread");
+            }
+        }
+    };
+
+    tokio::runtime::Builder::new_current_thread()
+        .enable_all()
+        .build()
+        .context("In run, building reactor")?
+        .block_on(async { listen_loop(cb, listener).await })
+}
+
+fn main() {
+    let e = run();
+    error!("Launch terminated: {:?}", e);
+    // Logs are sometimes mysteriously not being logged, so print too
+    println!("prng_seeder: launch terminated: {:?}", e);
+    std::process::exit(-1);
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use clap::CommandFactory;
+
+    #[test]
+    fn verify_cli() {
+        Cli::command().debug_assert();
+    }
+}
diff --git a/provisioner/Android.bp b/provisioner/Android.bp
index 665a9e7..b548973 100644
--- a/provisioner/Android.bp
+++ b/provisioner/Android.bp
@@ -43,12 +43,10 @@
     },
 }
 
-cc_binary {
-    name: "rkp_factory_extraction_tool",
-    vendor: true,
-    srcs: ["rkp_factory_extraction_tool.cpp"],
+cc_defaults {
+    name: "rkp_factory_extraction_defaults",
     defaults: [
-        "keymint_use_latest_hal_aidl_ndk_shared",
+        "keymint_use_latest_hal_aidl_ndk_static",
     ],
     shared_libs: [
         "libbinder",
@@ -57,11 +55,46 @@
         "liblog",
     ],
     static_libs: [
+        "android.hardware.security.rkp-V3-ndk",
         "libbase",
         "libcppbor_external",
         "libcppcose_rkp",
-        "libgflags",
         "libjsoncpp",
         "libkeymint_remote_prov_support",
     ],
 }
+
+cc_library_static {
+    name: "librkp_factory_extraction",
+    defaults: [
+        "rkp_factory_extraction_defaults",
+    ],
+    srcs: ["rkp_factory_extraction_lib.cpp"],
+    vendor_available: true,
+}
+
+cc_test {
+    name: "librkp_factory_extraction_test",
+    defaults: [
+        "rkp_factory_extraction_defaults",
+    ],
+    srcs: ["rkp_factory_extraction_lib_test.cpp"],
+    test_suites: ["device-tests"],
+    static_libs: [
+        "libgmock",
+        "librkp_factory_extraction",
+    ],
+}
+
+cc_binary {
+    name: "rkp_factory_extraction_tool",
+    vendor: true,
+    srcs: ["rkp_factory_extraction_tool.cpp"],
+    defaults: [
+        "rkp_factory_extraction_defaults",
+    ],
+    static_libs: [
+        "libgflags",
+        "librkp_factory_extraction",
+    ],
+}
diff --git a/provisioner/TEST_MAPPING b/provisioner/TEST_MAPPING
new file mode 100644
index 0000000..de3f165
--- /dev/null
+++ b/provisioner/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "librkp_factory_extraction_test"
+    }
+  ]
+}
diff --git a/provisioner/rkp_factory_extraction_lib.cpp b/provisioner/rkp_factory_extraction_lib.cpp
new file mode 100644
index 0000000..ab7d17c
--- /dev/null
+++ b/provisioner/rkp_factory_extraction_lib.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2022 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 "rkp_factory_extraction_lib.h"
+
+#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <android-base/properties.h>
+#include <android/binder_manager.h>
+#include <cppbor.h>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+#include <keymaster/cppcose/cppcose.h>
+#include <openssl/base64.h>
+#include <remote_prov/remote_prov_utils.h>
+#include <sys/random.h>
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "cppbor_parse.h"
+
+using aidl::android::hardware::security::keymint::DeviceInfo;
+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::EekChain;
+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;
+using aidl::android::hardware::security::keymint::remote_prov::parseAndValidateFactoryDeviceInfo;
+using aidl::android::hardware::security::keymint::remote_prov::verifyFactoryCsr;
+using aidl::android::hardware::security::keymint::remote_prov::verifyFactoryProtectedData;
+
+using namespace cppbor;
+using namespace cppcose;
+
+constexpr size_t kVersionWithoutSuperencryption = 3;
+
+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);
+    }
+
+    // BoringSSL automatically adds a NUL -- remove it from the string data
+    base64.pop_back();
+
+    return base64;
+}
+
+std::vector<uint8_t> generateChallenge() {
+    std::vector<uint8_t> challenge(kChallengeSize);
+
+    ssize_t bytesRemaining = static_cast<ssize_t>(challenge.size());
+    uint8_t* writePtr = challenge.data();
+    while (bytesRemaining > 0) {
+        int bytesRead = getrandom(writePtr, bytesRemaining, /*flags=*/0);
+        if (bytesRead < 0) {
+            if (errno == EINTR) {
+                continue;
+            } else {
+                std::cerr << errno << ": " << strerror(errno) << std::endl;
+                exit(-1);
+            }
+        }
+        bytesRemaining -= bytesRead;
+        writePtr += bytesRead;
+    }
+
+    return challenge;
+}
+
+CborResult<Array> composeCertificateRequestV1(const ProtectedData& protectedData,
+                                              const DeviceInfo& verifiedDeviceInfo,
+                                              const std::vector<uint8_t>& challenge,
+                                              const std::vector<uint8_t>& keysToSignMac,
+                                              IRemotelyProvisionedComponent* provisionable) {
+    Array macedKeysToSign = Array()
+                                .add(Map().add(1, 5).encode())  // alg: hmac-sha256
+                                .add(Map())                     // empty unprotected headers
+                                .add(Null())                    // nil for the payload
+                                .add(keysToSignMac);            // MAC as returned from the HAL
+
+    ErrMsgOr<std::unique_ptr<Map>> parsedVerifiedDeviceInfo =
+        parseAndValidateFactoryDeviceInfo(verifiedDeviceInfo.deviceInfo, provisionable);
+    if (!parsedVerifiedDeviceInfo) {
+        return {nullptr, parsedVerifiedDeviceInfo.moveMessage()};
+    }
+
+    auto [parsedProtectedData, ignore2, errMsg] = parse(protectedData.protectedData);
+    if (!parsedProtectedData) {
+        std::cerr << "Error parsing protected data: '" << errMsg << "'" << std::endl;
+        return {nullptr, errMsg};
+    }
+
+    Array deviceInfo = Array().add(parsedVerifiedDeviceInfo.moveValue()).add(Map());
+
+    auto certificateRequest = std::make_unique<Array>();
+    (*certificateRequest)
+        .add(std::move(deviceInfo))
+        .add(challenge)
+        .add(std::move(parsedProtectedData))
+        .add(std::move(macedKeysToSign));
+    return {std::move(certificateRequest), ""};
+}
+
+CborResult<Array> getCsrV1(std::string_view componentName, IRemotelyProvisionedComponent* irpc) {
+    std::vector<uint8_t> keysToSignMac;
+    std::vector<MacedPublicKey> emptyKeys;
+    DeviceInfo verifiedDeviceInfo;
+    ProtectedData protectedData;
+    RpcHardwareInfo hwInfo;
+    ::ndk::ScopedAStatus status = irpc->getHardwareInfo(&hwInfo);
+    if (!status.isOk()) {
+        std::cerr << "Failed to get hardware info for '" << componentName
+                  << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+        exit(-1);
+    }
+
+    const std::vector<uint8_t> eek = getProdEekChain(hwInfo.supportedEekCurve);
+    const std::vector<uint8_t> challenge = generateChallenge();
+    status = irpc->generateCertificateRequest(
+        /*test_mode=*/false, emptyKeys, eek, challenge, &verifiedDeviceInfo, &protectedData,
+        &keysToSignMac);
+    if (!status.isOk()) {
+        std::cerr << "Bundle extraction failed for '" << componentName
+                  << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+        exit(-1);
+    }
+    return composeCertificateRequestV1(protectedData, verifiedDeviceInfo, challenge, keysToSignMac,
+                                       irpc);
+}
+
+void selfTestGetCsrV1(std::string_view componentName, IRemotelyProvisionedComponent* irpc) {
+    std::vector<uint8_t> keysToSignMac;
+    std::vector<MacedPublicKey> emptyKeys;
+    DeviceInfo verifiedDeviceInfo;
+    ProtectedData protectedData;
+    RpcHardwareInfo hwInfo;
+    ::ndk::ScopedAStatus status = irpc->getHardwareInfo(&hwInfo);
+    if (!status.isOk()) {
+        std::cerr << "Failed to get hardware info for '" << componentName
+                  << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+        exit(-1);
+    }
+
+    const std::vector<uint8_t> eekId = {0, 1, 2, 3, 4, 5, 6, 7};
+    ErrMsgOr<EekChain> eekChain = generateEekChain(hwInfo.supportedEekCurve, /*length=*/3, eekId);
+    if (!eekChain) {
+        std::cerr << "Error generating test EEK certificate chain: " << eekChain.message();
+        exit(-1);
+    }
+    const std::vector<uint8_t> challenge = generateChallenge();
+    status = irpc->generateCertificateRequest(
+        /*test_mode=*/true, emptyKeys, eekChain->chain, challenge, &verifiedDeviceInfo,
+        &protectedData, &keysToSignMac);
+    if (!status.isOk()) {
+        std::cerr << "Error generating test cert chain for '" << componentName
+                  << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+        exit(-1);
+    }
+
+    auto result = verifyFactoryProtectedData(verifiedDeviceInfo, /*keysToSign=*/{}, keysToSignMac,
+                                             protectedData, *eekChain, eekId,
+                                             hwInfo.supportedEekCurve, irpc, challenge);
+
+    if (!result) {
+        std::cerr << "Self test failed for IRemotelyProvisionedComponent '" << componentName
+                  << "'. Error message: '" << result.message() << "'." << std::endl;
+        exit(-1);
+    }
+}
+
+CborResult<Array> composeCertificateRequestV3(const std::vector<uint8_t>& csr) {
+    const std::string kFingerprintProp = "ro.build.fingerprint";
+
+    auto [parsedCsr, _, csrErrMsg] = cppbor::parse(csr);
+    if (!parsedCsr) {
+        return {nullptr, csrErrMsg};
+    }
+    if (!parsedCsr->asArray()) {
+        return {nullptr, "CSR is not a CBOR array."};
+    }
+
+    if (!::android::base::WaitForPropertyCreation(kFingerprintProp)) {
+        return {nullptr, "Unable to read build fingerprint"};
+    }
+
+    Map unverifiedDeviceInfo =
+        Map().add("fingerprint", ::android::base::GetProperty(kFingerprintProp, /*default=*/""));
+    parsedCsr->asArray()->add(std::move(unverifiedDeviceInfo));
+    return {std::unique_ptr<Array>(parsedCsr.release()->asArray()), ""};
+}
+
+CborResult<cppbor::Array> getCsrV3(std::string_view componentName,
+                                   IRemotelyProvisionedComponent* irpc, bool selfTest) {
+    std::vector<uint8_t> csr;
+    std::vector<MacedPublicKey> emptyKeys;
+    const std::vector<uint8_t> challenge = generateChallenge();
+
+    auto status = irpc->generateCertificateRequestV2(emptyKeys, challenge, &csr);
+    if (!status.isOk()) {
+        std::cerr << "Bundle extraction failed for '" << componentName
+                  << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+        exit(-1);
+    }
+
+    if (selfTest) {
+        auto result = verifyFactoryCsr(/*keysToSign=*/cppbor::Array(), csr, irpc, challenge);
+        if (!result) {
+            std::cerr << "Self test failed for IRemotelyProvisionedComponent '" << componentName
+                      << "'. Error message: '" << result.message() << "'." << std::endl;
+            exit(-1);
+        }
+    }
+
+    return composeCertificateRequestV3(csr);
+}
+
+CborResult<Array> getCsr(std::string_view componentName, IRemotelyProvisionedComponent* irpc,
+                         bool selfTest) {
+    RpcHardwareInfo hwInfo;
+    auto status = irpc->getHardwareInfo(&hwInfo);
+    if (!status.isOk()) {
+        std::cerr << "Failed to get hardware info for '" << componentName
+                  << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+        exit(-1);
+    }
+
+    if (hwInfo.versionNumber < kVersionWithoutSuperencryption) {
+        if (selfTest) {
+            selfTestGetCsrV1(componentName, irpc);
+        }
+        return getCsrV1(componentName, irpc);
+    } else {
+        return getCsrV3(componentName, irpc, selfTest);
+    }
+}
diff --git a/provisioner/rkp_factory_extraction_lib.h b/provisioner/rkp_factory_extraction_lib.h
new file mode 100644
index 0000000..ae8ea6b
--- /dev/null
+++ b/provisioner/rkp_factory_extraction_lib.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2022 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 <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <android/binder_manager.h>
+#include <cppbor.h>
+#include <keymaster/cppcose/cppcose.h>
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <vector>
+
+// Challenge size must be between 32 and 64 bytes inclusive.
+constexpr size_t kChallengeSize = 64;
+
+// Contains a the result of an operation that should return cborData on success.
+// Returns an an error message and null cborData on error.
+template <typename T> struct CborResult {
+    std::unique_ptr<T> cborData;
+    std::string errMsg;
+};
+
+// Return `buffer` encoded as a base64 string.
+std::string toBase64(const std::vector<uint8_t>& buffer);
+
+// Generate a random challenge containing `kChallengeSize` bytes.
+std::vector<uint8_t> generateChallenge();
+
+// Get a certificate signing request for the given IRemotelyProvisionedComponent.
+// On error, the csr Array is null, and the string field contains a description of
+// what went wrong.
+CborResult<cppbor::Array>
+getCsr(std::string_view componentName,
+       aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent* irpc,
+       bool selfTest);
+
+// Generates a test certificate chain and validates it, exiting the process on error.
+void selfTestGetCsr(
+    std::string_view componentName,
+    aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent* irpc);
diff --git a/provisioner/rkp_factory_extraction_lib_test.cpp b/provisioner/rkp_factory_extraction_lib_test.cpp
new file mode 100644
index 0000000..3fe88da
--- /dev/null
+++ b/provisioner/rkp_factory_extraction_lib_test.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2022 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 "rkp_factory_extraction_lib.h"
+
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock-more-matchers.h"
+#include <aidl/android/hardware/security/keymint/DeviceInfo.h>
+#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <aidl/android/hardware/security/keymint/MacedPublicKey.h>
+#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
+#include <android-base/properties.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <cstdint>
+#include <memory>
+#include <ostream>
+#include <set>
+#include <vector>
+
+#include "aidl/android/hardware/security/keymint/ProtectedData.h"
+#include "android/binder_auto_utils.h"
+#include "android/binder_interface_utils.h"
+#include "cppbor.h"
+
+using ::ndk::ScopedAStatus;
+using ::ndk::SharedRefBase;
+
+using namespace ::aidl::android::hardware::security::keymint;
+using namespace ::cppbor;
+using namespace ::testing;
+
+namespace cppbor {
+
+std::ostream& operator<<(std::ostream& os, const Item& item) {
+    return os << prettyPrint(&item);
+}
+
+std::ostream& operator<<(std::ostream& os, const std::unique_ptr<Item>& item) {
+    return os << *item;
+}
+
+std::ostream& operator<<(std::ostream& os, const Item* item) {
+    return os << *item;
+}
+
+}  // namespace cppbor
+
+class MockIRemotelyProvisionedComponent : public IRemotelyProvisionedComponentDefault {
+  public:
+    MOCK_METHOD(ScopedAStatus, getHardwareInfo, (RpcHardwareInfo * _aidl_return), (override));
+    MOCK_METHOD(ScopedAStatus, generateEcdsaP256KeyPair,
+                (bool in_testMode, MacedPublicKey* out_macedPublicKey,
+                 std::vector<uint8_t>* _aidl_return),
+                (override));
+    MOCK_METHOD(ScopedAStatus, generateCertificateRequest,
+                (bool in_testMode, const std::vector<MacedPublicKey>& in_keysToSign,
+                 const std::vector<uint8_t>& in_endpointEncryptionCertChain,
+                 const std::vector<uint8_t>& in_challenge, DeviceInfo* out_deviceInfo,
+                 ProtectedData* out_protectedData, std::vector<uint8_t>* _aidl_return),
+                (override));
+    MOCK_METHOD(ScopedAStatus, generateCertificateRequestV2,
+                (const std::vector<MacedPublicKey>& in_keysToSign,
+                 const std::vector<uint8_t>& in_challenge, std::vector<uint8_t>* _aidl_return),
+                (override));
+    MOCK_METHOD(ScopedAStatus, getInterfaceVersion, (int32_t * _aidl_return), (override));
+    MOCK_METHOD(ScopedAStatus, getInterfaceHash, (std::string * _aidl_return), (override));
+};
+
+TEST(LibRkpFactoryExtractionTests, ToBase64) {
+    std::vector<uint8_t> input(UINT8_MAX + 1);
+    for (int i = 0; i < input.size(); ++i) {
+        input[i] = i;
+    }
+
+    // Test three lengths so we get all the different paddding options
+    EXPECT_EQ("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4"
+              "vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV"
+              "5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMj"
+              "Y6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8"
+              "vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uv"
+              "s7e7v8PHy8/T19vf4+fr7/P3+/w==",
+              toBase64(input));
+
+    input.push_back(42);
+    EXPECT_EQ("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4"
+              "vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV"
+              "5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMj"
+              "Y6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8"
+              "vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uv"
+              "s7e7v8PHy8/T19vf4+fr7/P3+/yo=",
+              toBase64(input));
+
+    input.push_back(42);
+    EXPECT_EQ("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4"
+              "vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV"
+              "5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMj"
+              "Y6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8"
+              "vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uv"
+              "s7e7v8PHy8/T19vf4+fr7/P3+/yoq",
+              toBase64(input));
+}
+
+TEST(LibRkpFactoryExtractionTests, UniqueChallengeSmokeTest) {
+    // This will at least catch VERY broken implementations.
+    constexpr size_t NUM_CHALLENGES = 32;
+    std::set<std::vector<uint8_t>> challenges;
+    for (size_t i = 0; i < NUM_CHALLENGES; ++i) {
+        const std::vector<uint8_t> challenge = generateChallenge();
+        const auto [_, wasInserted] = challenges.insert(generateChallenge());
+        EXPECT_TRUE(wasInserted) << "Duplicate challenge: " << toBase64(challenge);
+    }
+}
+
+TEST(LibRkpFactoryExtractionTests, GetCsrWithV2Hal) {
+    ASSERT_TRUE(true);
+
+    const std::vector<uint8_t> kFakeMac = {1, 2, 3, 4};
+
+    Map cborDeviceInfo;
+    cborDeviceInfo.add("product", "gShoe");
+    cborDeviceInfo.add("version", 2);
+    cborDeviceInfo.add("brand", "Fake Brand");
+    cborDeviceInfo.add("manufacturer", "Fake Mfr");
+    cborDeviceInfo.add("model", "Fake Model");
+    cborDeviceInfo.add("device", "Fake Device");
+    cborDeviceInfo.add("vb_state", "orange");
+    cborDeviceInfo.add("bootloader_state", "unlocked");
+    cborDeviceInfo.add("vbmeta_digest", std::vector<uint8_t>{1, 2, 3, 4});
+    cborDeviceInfo.add("system_patch_level", 42);
+    cborDeviceInfo.add("boot_patch_level", 31415);
+    cborDeviceInfo.add("vendor_patch_level", 0);
+    cborDeviceInfo.add("fused", 0);
+    cborDeviceInfo.add("security_level", "tee");
+    cborDeviceInfo.add("os_version", "the best version");
+    const DeviceInfo kVerifiedDeviceInfo = {cborDeviceInfo.canonicalize().encode()};
+
+    Array cborProtectedData;
+    cborProtectedData.add(Bstr());   // protected
+    cborProtectedData.add(Map());    // unprotected
+    cborProtectedData.add(Bstr());   // ciphertext
+    cborProtectedData.add(Array());  // recipients
+    const ProtectedData kProtectedData = {cborProtectedData.encode()};
+
+    std::vector<uint8_t> eekChain;
+    std::vector<uint8_t> challenge;
+
+    // Set up mock, then call getSCsr
+    auto mockRpc = SharedRefBase::make<MockIRemotelyProvisionedComponent>();
+    EXPECT_CALL(*mockRpc, getHardwareInfo(NotNull())).WillRepeatedly([](RpcHardwareInfo* hwInfo) {
+        hwInfo->versionNumber = 2;
+        return ScopedAStatus::ok();
+    });
+    EXPECT_CALL(*mockRpc,
+                generateCertificateRequest(false,               // testMode
+                                           IsEmpty(),           // keysToSign
+                                           _,                   // endpointEncryptionCertChain
+                                           _,                   // challenge
+                                           NotNull(),           // deviceInfo
+                                           NotNull(),           // protectedData
+                                           NotNull()))          // _aidl_return
+        .WillOnce(DoAll(SaveArg<2>(&eekChain),                  //
+                        SaveArg<3>(&challenge),                 //
+                        SetArgPointee<4>(kVerifiedDeviceInfo),  //
+                        SetArgPointee<5>(kProtectedData),       //
+                        SetArgPointee<6>(kFakeMac),             //
+                        Return(ByMove(ScopedAStatus::ok()))));  //
+
+    auto [csr, csrErrMsg] = getCsr("mock component name", mockRpc.get(),
+                                   /*selfTest=*/false);
+    ASSERT_THAT(csr, NotNull()) << csrErrMsg;
+    ASSERT_THAT(csr->asArray(), Pointee(Property(&Array::size, Eq(4))));
+
+    // Verify the input parameters that we received
+    auto [parsedEek, ignore1, eekParseError] = parse(eekChain);
+    ASSERT_THAT(parsedEek, NotNull()) << eekParseError;
+    EXPECT_THAT(parsedEek->asArray(), Pointee(Property(&Array::size, Gt(1))));
+    EXPECT_THAT(challenge, Property(&std::vector<uint8_t>::size, Eq(kChallengeSize)));
+
+    // Device info consists of (verified info, unverified info)
+    const Array* deviceInfoArray = csr->get(0)->asArray();
+    EXPECT_THAT(deviceInfoArray, Pointee(Property(&Array::size, 2)));
+
+    // Verified device info must match our mock value
+    const Map* actualVerifiedDeviceInfo = deviceInfoArray->get(0)->asMap();
+    EXPECT_THAT(actualVerifiedDeviceInfo, Pointee(Property(&Map::size, Eq(cborDeviceInfo.size()))));
+    EXPECT_THAT(actualVerifiedDeviceInfo->get("product"), Pointee(Eq(Tstr("gShoe"))));
+    EXPECT_THAT(actualVerifiedDeviceInfo->get("version"), Pointee(Eq(Uint(2))));
+
+    // Empty unverified device info
+    const Map* actualUnverifiedDeviceInfo = deviceInfoArray->get(1)->asMap();
+    EXPECT_THAT(actualUnverifiedDeviceInfo, Pointee(Property(&Map::size, Eq(0))));
+
+    // Challenge must match the call to generateCertificateRequest
+    const Bstr* actualChallenge = csr->get(1)->asBstr();
+    EXPECT_THAT(actualChallenge, Pointee(Property(&Bstr::value, Eq(challenge))));
+
+    // Protected data must match the mock value
+    const Array* actualProtectedData = csr->get(2)->asArray();
+    EXPECT_THAT(actualProtectedData, Pointee(Eq(ByRef(cborProtectedData))));
+
+    // Ensure the maced public key matches the expected COSE_mac0
+    const Array* actualMacedKeys = csr->get(3)->asArray();
+    ASSERT_THAT(actualMacedKeys, Pointee(Property(&Array::size, Eq(4))));
+    ASSERT_THAT(actualMacedKeys->get(0)->asBstr(), NotNull());
+    auto [macProtectedParams, ignore2, macParamParseError] =
+        parse(actualMacedKeys->get(0)->asBstr());
+    ASSERT_THAT(macProtectedParams, NotNull()) << macParamParseError;
+    Map expectedMacProtectedParams;
+    expectedMacProtectedParams.add(1, 5);
+    EXPECT_THAT(macProtectedParams, Pointee(Eq(ByRef(expectedMacProtectedParams))));
+    EXPECT_THAT(actualMacedKeys->get(1)->asMap(), Pointee(Property(&Map::size, Eq(0))));
+    EXPECT_THAT(actualMacedKeys->get(2)->asNull(), NotNull());
+    EXPECT_THAT(actualMacedKeys->get(3)->asBstr(), Pointee(Eq(Bstr(kFakeMac))));
+}
+
+TEST(LibRkpFactoryExtractionTests, GetCsrWithV3Hal) {
+    const std::vector<uint8_t> kCsr = Array()
+                                          .add(3 /* version */)
+                                          .add(Map() /* UdsCerts */)
+                                          .add(Array() /* DiceCertChain */)
+                                          .add(Array() /* SignedData */)
+                                          .encode();
+    std::vector<uint8_t> challenge;
+
+    // Set up mock, then call getCsr
+    auto mockRpc = SharedRefBase::make<MockIRemotelyProvisionedComponent>();
+    EXPECT_CALL(*mockRpc, getHardwareInfo(NotNull())).WillRepeatedly([](RpcHardwareInfo* hwInfo) {
+        hwInfo->versionNumber = 3;
+        return ScopedAStatus::ok();
+    });
+    EXPECT_CALL(*mockRpc,
+                generateCertificateRequestV2(IsEmpty(),   // keysToSign
+                                             _,           // challenge
+                                             NotNull()))  // _aidl_return
+        .WillOnce(DoAll(SaveArg<1>(&challenge), SetArgPointee<2>(kCsr),
+                        Return(ByMove(ScopedAStatus::ok()))));
+
+    auto [csr, csrErrMsg] = getCsr("mock component name", mockRpc.get(),
+                                   /*selfTest=*/false);
+    ASSERT_THAT(csr, NotNull()) << csrErrMsg;
+    ASSERT_THAT(csr, Pointee(Property(&Array::size, Eq(5))));
+
+    EXPECT_THAT(csr->get(0 /* version */), Pointee(Eq(Uint(3))));
+    EXPECT_THAT(csr->get(1)->asMap(), NotNull());
+    EXPECT_THAT(csr->get(2)->asArray(), NotNull());
+    EXPECT_THAT(csr->get(3)->asArray(), NotNull());
+
+    const Map* unverifedDeviceInfo = csr->get(4)->asMap();
+    ASSERT_THAT(unverifedDeviceInfo, NotNull());
+    EXPECT_THAT(unverifedDeviceInfo->get("fingerprint"), NotNull());
+    const Tstr fingerprint(android::base::GetProperty("ro.build.fingerprint", ""));
+    EXPECT_THAT(*unverifedDeviceInfo->get("fingerprint")->asTstr(), Eq(fingerprint));
+}
diff --git a/provisioner/rkp_factory_extraction_tool.cpp b/provisioner/rkp_factory_extraction_tool.cpp
index 0f45531..5ba777e 100644
--- a/provisioner/rkp_factory_extraction_tool.cpp
+++ b/provisioner/rkp_factory_extraction_tool.cpp
@@ -14,9 +14,6 @@
  * limitations under the License.
  */
 
-#include <string>
-#include <vector>
-
 #include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
 #include <android/binder_manager.h>
 #include <cppbor.h>
@@ -26,21 +23,22 @@
 #include <remote_prov/remote_prov_utils.h>
 #include <sys/random.h>
 
-using aidl::android::hardware::security::keymint::DeviceInfo;
+#include <string>
+#include <vector>
+
+#include "rkp_factory_extraction_lib.h"
+
 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;
 
 using namespace cppbor;
 using namespace cppcose;
 
-DEFINE_bool(test_mode, false, "If enabled, a fake EEK key/cert are used.");
-
-DEFINE_string(output_format, "csr", "How to format the output. Defaults to 'csr'.");
+DEFINE_string(output_format, "build+csr", "How to format the output. Defaults to 'build+csr'.");
+DEFINE_bool(self_test, true,
+            "If true, this tool performs a self-test, validating the payload for correctness. "
+            "This checks that the device on the factory line is producing valid output "
+            "before attempting to upload the output to the device info service.");
 
 namespace {
 
@@ -49,89 +47,6 @@
 constexpr std::string_view kBuildPlusCsr = "build+csr";  // Text-encoded (JSON) build
                                                          // fingerprint plus CSR.
 
-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);
-
-    ssize_t bytesRemaining = static_cast<ssize_t>(challenge.size());
-    uint8_t* writePtr = challenge.data();
-    while (bytesRemaining > 0) {
-        int bytesRead = getrandom(writePtr, bytesRemaining, /*flags=*/0);
-        if (bytesRead < 0) {
-            if (errno == EINTR) {
-                continue;
-            } else {
-                std::cerr << errno << ": " << strerror(errno) << std::endl;
-                exit(-1);
-            }
-        }
-        bytesRemaining -= bytesRead;
-        writePtr += bytesRead;
-    }
-
-    return challenge;
-}
-
-Array composeCertificateRequest(const ProtectedData& protectedData,
-                                const DeviceInfo& verifiedDeviceInfo,
-                                const std::vector<uint8_t>& challenge,
-                                const std::vector<uint8_t>& keysToSignMac) {
-    Array macedKeysToSign = Array()
-                                .add(std::vector<uint8_t>(0))  // empty protected headers as bstr
-                                .add(Map())                    // empty unprotected headers
-                                .add(Null())                   // nil for the payload
-                                .add(keysToSignMac);           // MAC as returned from the HAL
-
-    Array deviceInfo =
-        Array().add(EncodedItem(verifiedDeviceInfo.deviceInfo)).add(Map());  // Empty device info
-
-    Array certificateRequest = Array()
-                                   .add(std::move(deviceInfo))
-                                   .add(challenge)
-                                   .add(EncodedItem(protectedData.protectedData))
-                                   .add(std::move(macedKeysToSign));
-    return certificateRequest;
-}
-
-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(curve, 3 /* chainlength */, kFakeEekId);
-        if (!eekOrErr) {
-            std::cerr << "Failed to generate test EEK somehow: " << eekOrErr.message() << std::endl;
-            exit(-1);
-        }
-        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(curve);
-}
-
 void writeOutput(const std::string instance_name, const Array& csr) {
     if (FLAGS_output_format == kBinaryCsrOutput) {
         auto bytes = csr.encode();
@@ -166,28 +81,13 @@
         exit(-1);
     }
 
-    std::vector<uint8_t> keysToSignMac;
-    std::vector<MacedPublicKey> emptyKeys;
-    DeviceInfo verifiedDeviceInfo;
-    ProtectedData protectedData;
-    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;
+    auto [request, errMsg] = getCsr(name, rkp_service.get(), FLAGS_self_test);
+    if (!request) {
+        std::cerr << "Unable to build CSR for '" << fullName << ": " << errMsg << 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;
-        exit(-1);
-    }
-    auto request =
-        composeCertificateRequest(protectedData, verifiedDeviceInfo, challenge, keysToSignMac);
-    writeOutput(std::string(name), request);
+
+    writeOutput(std::string(name), *request);
 }
 
 }  // namespace
diff --git a/provisioner/support/Android.bp b/provisioner/support/Android.bp
new file mode 100644
index 0000000..778b1e0
--- /dev/null
+++ b/provisioner/support/Android.bp
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+cc_defaults {
+    name: "librkp_support_defaults",
+    static_libs: [
+        "android.hardware.security.rkp-V3-cpp",
+        "android.security.rkp_aidl-cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libutils",
+        "libvintf",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+}
+
+cc_library {
+    name: "librkp_support",
+    defaults: ["librkp_support_defaults"],
+    srcs: [
+        "rkpd_client.cpp",
+    ],
+    export_include_dirs: ["include"],
+}
+
+cc_test {
+    name: "librkp_support_test",
+    defaults: [
+        "librkp_support_defaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: ["test.cpp"],
+    static_libs: [
+        "librkp_support",
+    ],
+    test_suites: ["general-tests"],
+    require_root: true,
+}
diff --git a/provisioner/support/TEST_MAPPING b/provisioner/support/TEST_MAPPING
new file mode 100644
index 0000000..fc30104
--- /dev/null
+++ b/provisioner/support/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "librkp_support_test"
+    }
+  ]
+}
diff --git a/provisioner/support/include/rkp/support/rkpd_client.h b/provisioner/support/include/rkp/support/rkpd_client.h
new file mode 100644
index 0000000..5a7fe6e
--- /dev/null
+++ b/provisioner/support/include/rkp/support/rkpd_client.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2022, 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 <future>
+#include <optional>
+
+#include <android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <android/security/rkp/RemotelyProvisionedKey.h>
+
+namespace android::security::rkp::support {
+
+using ::android::hardware::security::keymint::IRemotelyProvisionedComponent;
+using ::android::security::rkp::RemotelyProvisionedKey;
+
+// Callers of getRpcKeyFuture() and getRpcKey() need at least two threads to
+// retrieve the key, one to asynchronously handle binder callbacks and one to
+// wait on the future.
+std::optional<std::future<std::optional<RemotelyProvisionedKey>>>
+getRpcKeyFuture(const sp<IRemotelyProvisionedComponent>& rpc, int32_t keyId);
+
+std::optional<RemotelyProvisionedKey> getRpcKey(const sp<IRemotelyProvisionedComponent>& rpc,
+                                                int32_t keyId, int32_t timeout_sec = 10);
+
+}  // namespace android::security::rkp::support
diff --git a/provisioner/support/rkpd_client.cpp b/provisioner/support/rkpd_client.cpp
new file mode 100644
index 0000000..0643457
--- /dev/null
+++ b/provisioner/support/rkpd_client.cpp
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "rkpd_client"
+
+#include <atomic>
+
+#include <android-base/logging.h>
+#include <android/security/rkp/BnGetKeyCallback.h>
+#include <android/security/rkp/BnGetRegistrationCallback.h>
+#include <android/security/rkp/IGetKeyCallback.h>
+#include <android/security/rkp/IRemoteProvisioning.h>
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <rkp/support/rkpd_client.h>
+#include <vintf/VintfObject.h>
+
+namespace android::security::rkp::support {
+namespace {
+
+using ::android::binder::Status;
+using ::android::hardware::security::keymint::IRemotelyProvisionedComponent;
+using ::android::hardware::security::keymint::RpcHardwareInfo;
+using ::android::security::rkp::BnGetKeyCallback;
+using ::android::security::rkp::BnGetRegistrationCallback;
+using ::android::security::rkp::IGetKeyCallback;
+using ::android::security::rkp::IRegistration;
+using ::android::security::rkp::IRemoteProvisioning;
+using ::android::security::rkp::RemotelyProvisionedKey;
+
+constexpr const char* kRemoteProvisioningServiceName = "remote_provisioning";
+
+std::optional<std::string> getRpcId(const sp<IRemotelyProvisionedComponent>& rpc) {
+    RpcHardwareInfo rpcHwInfo;
+    Status status = rpc->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. "
+                   << "This is a bug in the vendor implementation.";
+        return std::nullopt;
+    }
+
+    return *rpcHwInfo.uniqueId;
+}
+
+std::optional<String16> findRpcNameById(std::string_view targetRpcId) {
+    auto deviceManifest = vintf::VintfObject::GetDeviceHalManifest();
+    auto instances = deviceManifest->getAidlInstances("android.hardware.security.keymint",
+                                                      "IRemotelyProvisionedComponent");
+    for (const std::string& instance : instances) {
+        auto rpcName =
+            IRemotelyProvisionedComponent::descriptor + String16("/") + String16(instance.c_str());
+        sp<IRemotelyProvisionedComponent> rpc =
+            android::waitForService<IRemotelyProvisionedComponent>(rpcName);
+
+        auto rpcId = getRpcId(rpc);
+        if (!rpcId) {
+            continue;
+        }
+        if (*rpcId == targetRpcId) {
+            return rpcName;
+        }
+    }
+
+    LOG(ERROR) << "Remotely provisioned component with given unique ID: " << targetRpcId
+               << " not found";
+    return std::nullopt;
+}
+
+std::optional<String16> getRpcName(const sp<IRemotelyProvisionedComponent>& rpc) {
+    std::optional<std::string> targetRpcId = getRpcId(rpc);
+    if (!targetRpcId) {
+        return std::nullopt;
+    }
+    return findRpcNameById(*targetRpcId);
+}
+
+class GetKeyCallback : public BnGetKeyCallback {
+  public:
+    GetKeyCallback(std::promise<std::optional<RemotelyProvisionedKey>> keyPromise)
+        : keyPromise_(std::move(keyPromise)), called_() {}
+
+    Status onSuccess(const RemotelyProvisionedKey& key) override {
+        if (called_.test_and_set()) {
+            return Status::ok();
+        }
+        keyPromise_.set_value(key);
+        return Status::ok();
+    }
+    Status onCancel() override {
+        if (called_.test_and_set()) {
+            return Status::ok();
+        }
+        LOG(ERROR) << "GetKeyCallback cancelled";
+        keyPromise_.set_value(std::nullopt);
+        return Status::ok();
+    }
+    Status onError(IGetKeyCallback::ErrorCode error, const String16& description) override {
+        if (called_.test_and_set()) {
+            return Status::ok();
+        }
+        LOG(ERROR) << "GetKeyCallback failed: " << static_cast<int>(error) << ", " << description;
+        keyPromise_.set_value(std::nullopt);
+        return Status::ok();
+    }
+
+  private:
+    std::promise<std::optional<RemotelyProvisionedKey>> keyPromise_;
+    // This callback can only be called into once
+    std::atomic_flag called_;
+};
+
+class GetRegistrationCallback : public BnGetRegistrationCallback {
+  public:
+    GetRegistrationCallback(std::promise<std::optional<RemotelyProvisionedKey>> keyPromise,
+                            uint32_t keyId)
+        : keyPromise_(std::move(keyPromise)), keyId_(keyId), called_() {}
+
+    Status onSuccess(const sp<IRegistration>& registration) override {
+        if (called_.test_and_set()) {
+            return Status::ok();
+        }
+        auto cb = sp<GetKeyCallback>::make(std::move(keyPromise_));
+        auto status = registration->getKey(keyId_, cb);
+        if (!status.isOk()) {
+            cb->onError(IGetKeyCallback::ErrorCode::ERROR_UNKNOWN,
+                        String16("Failed to register GetKeyCallback"));
+        }
+        return Status::ok();
+    }
+    Status onCancel() override {
+        if (called_.test_and_set()) {
+            return Status::ok();
+        }
+        LOG(ERROR) << "GetRegistrationCallback cancelled";
+        keyPromise_.set_value(std::nullopt);
+        return Status::ok();
+    }
+    Status onError(const String16& error) override {
+        if (called_.test_and_set()) {
+            return Status::ok();
+        }
+        LOG(ERROR) << "GetRegistrationCallback failed: " << error;
+        keyPromise_.set_value(std::nullopt);
+        return Status::ok();
+    }
+
+  private:
+    std::promise<std::optional<RemotelyProvisionedKey>> keyPromise_;
+    int32_t keyId_;
+    // This callback can only be called into once
+    std::atomic_flag called_;
+};
+
+}  // namespace
+
+std::optional<std::future<std::optional<RemotelyProvisionedKey>>>
+getRpcKeyFuture(const sp<IRemotelyProvisionedComponent>& rpc, int32_t keyId) {
+    std::promise<std::optional<RemotelyProvisionedKey>> keyPromise;
+    auto keyFuture = keyPromise.get_future();
+
+    auto rpcName = getRpcName(rpc);
+    if (!rpcName) {
+        LOG(ERROR) << "Failed to get IRemotelyProvisionedComponent name";
+        return std::nullopt;
+    }
+
+    sp<IRemoteProvisioning> remoteProvisioning =
+        android::waitForService<IRemoteProvisioning>(String16(kRemoteProvisioningServiceName));
+    if (!remoteProvisioning) {
+        LOG(ERROR) << "Failed to get IRemoteProvisioning HAL";
+        return std::nullopt;
+    }
+
+    auto cb = sp<GetRegistrationCallback>::make(std::move(keyPromise), keyId);
+    Status status = remoteProvisioning->getRegistration(*rpcName, cb);
+    if (!status.isOk()) {
+        LOG(ERROR) << "Failed getRegistration()";
+        return std::nullopt;
+    }
+
+    return keyFuture;
+}
+
+std::optional<RemotelyProvisionedKey> getRpcKey(const sp<IRemotelyProvisionedComponent>& rpc,
+                                                int32_t keyId, int32_t timeout_sec) {
+    auto rpcKeyFuture = getRpcKeyFuture(rpc, keyId);
+    if (!rpcKeyFuture) {
+        LOG(ERROR) << "Failed getRpcKeyFuture()";
+        return std::nullopt;
+    }
+
+    auto timeout = std::chrono::seconds(timeout_sec);
+    if (rpcKeyFuture->wait_for(timeout) != std::future_status::ready) {
+        LOG(ERROR) << "Waiting for remotely provisioned attestation key timed out";
+        return std::nullopt;
+    }
+
+    return rpcKeyFuture->get();
+}
+
+}  // namespace android::security::rkp::support
diff --git a/provisioner/support/test.cpp b/provisioner/support/test.cpp
new file mode 100644
index 0000000..418eab9
--- /dev/null
+++ b/provisioner/support/test.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <rkp/support/rkpd_client.h>
+
+using ::android::getAidlHalInstanceNames;
+using ::android::sp;
+using ::android::String16;
+using ::android::hardware::security::keymint::IRemotelyProvisionedComponent;
+using ::android::security::rkp::RemotelyProvisionedKey;
+using ::android::security::rkp::support::getRpcKey;
+
+// TODO(b/272600606): Add tests for error cases
+class RkpdClientTest : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        auto rpcName = String16(GetParam().c_str());
+        rpc_ = android::waitForService<IRemotelyProvisionedComponent>(rpcName);
+        ASSERT_NE(rpc_, nullptr);
+    }
+
+    sp<IRemotelyProvisionedComponent> rpc_;
+};
+
+TEST_P(RkpdClientTest, getRpcKey) {
+    std::optional<RemotelyProvisionedKey> key = getRpcKey(rpc_, /*keyId=*/0);
+
+    ASSERT_TRUE(key.has_value()) << "Failed to get remotely provisioned attestation key";
+    ASSERT_FALSE(key->keyBlob.empty()) << "Key blob is empty";
+    ASSERT_FALSE(key->encodedCertChain.empty()) << "Certificate is empty";
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    PerInstance, RkpdClientTest,
+    testing::ValuesIn(getAidlHalInstanceNames(IRemotelyProvisionedComponent::descriptor)),
+    ::android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    testing::InitGoogleTest(&argc, argv);
+
+    // We need one thread to issue requests to RKPD and one to handle
+    // asynchronous responses from RKPD.
+    android::ProcessState::self()->setThreadPoolMaxThreadCount(2);
+    android::ProcessState::self()->startThreadPool();
+    return RUN_ALL_TESTS();
+}