Merge "Adding AIDL fuzzer for AuthorizationManager"
diff --git a/diced/Android.bp b/diced/Android.bp
index ddcc8c1..6e857f4 100644
--- a/diced/Android.bp
+++ b/diced/Android.bp
@@ -30,7 +30,7 @@
     rustlibs: [
         "android.hardware.security.dice-V1-rust",
         "libanyhow",
-        "libdiced_open_dice_cbor",
+        "libdiced_open_dice",
         "libkeystore2_crypto_rust",
     ],
 }
@@ -44,7 +44,7 @@
     rustlibs: [
         "android.hardware.security.dice-V1-rust",
         "libanyhow",
-        "libdiced_open_dice_cbor",
+        "libdiced_open_dice",
         "libkeystore2_crypto_rust",
     ],
 }
@@ -58,9 +58,8 @@
     rustlibs: [
         "android.hardware.security.dice-V1-rust",
         "libanyhow",
-        "libdiced_open_dice_cbor",
+        "libdiced_open_dice",
         "libdiced_utils",
-        "libkeystore2_crypto_rust",
     ],
 }
 
@@ -73,29 +72,8 @@
     rustlibs: [
         "android.hardware.security.dice-V1-rust",
         "libanyhow",
-        "libdiced_open_dice_cbor",
+        "libdiced_open_dice",
         "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",
     ],
 }
 
@@ -107,7 +85,7 @@
     vendor_available: true,
     rustlibs: [
         "android.hardware.security.dice-V1-rust",
-        "libdiced_open_dice_cbor",
+        "libdiced_open_dice",
         "libanyhow",
         "libbinder_rs",
         "libdiced_utils",
@@ -122,31 +100,6 @@
 }
 
 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"],
@@ -155,7 +108,7 @@
     rustlibs: [
         "android.hardware.security.dice-V1-rust",
         "libanyhow",
-        "libdiced_open_dice_cbor",
+        "libdiced_open_dice",
         "libdiced_sample_inputs",
         "libdiced_utils",
         "libbinder_rs",
@@ -185,7 +138,7 @@
         "android.security.dice-rust",
         "libanyhow",
         "libbinder_rs",
-        "libdiced_open_dice_cbor",
+        "libdiced_open_dice",
         "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
index b3d8ac4..8629b07 100644
--- a/diced/TEST_MAPPING
+++ b/diced/TEST_MAPPING
@@ -7,15 +7,18 @@
       "name": "libdiced_open_dice_nostd.integration_test"
     },
     {
+      "name": "libopen_dice_cbor_bindgen_test"
+    },
+    {
+      "name": "libopen_dice_bcc_bindgen_test"
+    },
+    {
       "name": "diced_utils_test"
     },
     {
       "name": "diced_sample_inputs_test"
     },
     {
-      "name": "diced_test"
-    },
-    {
       "name": "diced_vendor_test"
     }
   ]
diff --git a/diced/open_dice/Android.bp b/diced/open_dice/Android.bp
index e2c7dd1..2505b42 100644
--- a/diced/open_dice/Android.bp
+++ b/diced/open_dice/Android.bp
@@ -1,4 +1,5 @@
 package {
+    default_visibility: [":__subpackages__"],
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
@@ -22,11 +23,15 @@
     rustlibs: [
         "libopen_dice_bcc_bindgen_nostd",
         "libopen_dice_cbor_bindgen_nostd",
+        "libzeroize_nostd",
     ],
-     whole_static_libs: [
+    whole_static_libs: [
         "libopen_dice_cbor",
         "libcrypto_baremetal",
     ],
+    visibility: [
+        "//packages/modules/Virtualization:__subpackages__",
+    ],
 }
 
 rust_library {
@@ -35,15 +40,22 @@
     rustlibs: [
         "libopen_dice_bcc_bindgen",
         "libopen_dice_cbor_bindgen",
-        // For ZVec
-        "libkeystore2_crypto_rust",
+        "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 {
@@ -68,3 +80,177 @@
         "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
index 8bda225..1575113 100644
--- a/diced/open_dice/src/bcc.rs
+++ b/diced/open_dice/src/bcc.rs
@@ -14,10 +14,11 @@
 
 //! This module mirrors the content in open-dice/include/dice/android/bcc.h
 
-use crate::error::{check_result, Result};
+use crate::dice::{Cdi, CdiValues, DiceArtifacts, InputValues, CDI_SIZE};
+use crate::error::{check_result, DiceError, Result};
 use open_dice_bcc_bindgen::{
-    BccConfigValues, BccFormatConfigDescriptor, BCC_INPUT_COMPONENT_NAME,
-    BCC_INPUT_COMPONENT_VERSION, BCC_INPUT_RESETTABLE,
+    BccConfigValues, BccFormatConfigDescriptor, BccHandoverMainFlow, BccHandoverParse, BccMainFlow,
+    BCC_INPUT_COMPONENT_NAME, BCC_INPUT_COMPONENT_VERSION, BCC_INPUT_RESETTABLE,
 };
 use std::{ffi::CStr, ptr};
 
@@ -54,3 +55,138 @@
     })?;
     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
index 014d5fd..9266b6f 100644
--- a/diced/open_dice/src/dice.rs
+++ b/diced/open_dice/src/dice.rs
@@ -19,10 +19,12 @@
 pub use open_dice_cbor_bindgen::DiceMode;
 use open_dice_cbor_bindgen::{
     DiceConfigType, DiceDeriveCdiCertificateId, DiceDeriveCdiPrivateKeySeed, DiceInputValues,
-    DICE_CDI_SIZE, DICE_HASH_SIZE, DICE_HIDDEN_SIZE, DICE_ID_SIZE, DICE_INLINE_CONFIG_SIZE,
-    DICE_PRIVATE_KEY_SEED_SIZE,
+    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;
@@ -34,6 +36,12 @@
 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;
 
@@ -45,11 +53,85 @@
 pub type InlineConfig = [u8; INLINE_CONFIG_SIZE];
 /// Array type of CDIs.
 pub type Cdi = [u8; CDI_SIZE];
-/// Array type of private key seeds.
-pub type PrivateKeySeed = [u8; PRIVATE_KEY_SEED_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> {
@@ -126,7 +208,7 @@
 
 /// 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 = [0u8; PRIVATE_KEY_SEED_SIZE];
+    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 {
@@ -154,3 +236,35 @@
     })?;
     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
index 7405c51..4c67335 100644
--- a/diced/open_dice/src/error.rs
+++ b/diced/open_dice/src/error.rs
@@ -29,16 +29,6 @@
     BufferTooSmall,
     /// Platform error.
     PlatformError,
-    /// The allocation of a ZVec failed.
-    #[cfg(feature = "std")]
-    ZVecError(keystore2_crypto::zvec::Error),
-}
-
-#[cfg(feature = "std")]
-impl From<keystore2_crypto::zvec::Error> for DiceError {
-    fn from(e: keystore2_crypto::zvec::Error) -> Self {
-        Self::ZVecError(e)
-    }
 }
 
 /// This makes `DiceError` accepted by anyhow.
@@ -51,8 +41,6 @@
             Self::InvalidInput => write!(f, "invalid input"),
             Self::BufferTooSmall => write!(f, "buffer too small"),
             Self::PlatformError => write!(f, "platform error"),
-            #[cfg(feature = "std")]
-            Self::ZVecError(e) => write!(f, "ZVec allocation failed {e}"),
         }
     }
 }
diff --git a/diced/open_dice/src/lib.rs b/diced/open_dice/src/lib.rs
index 5a34698..e7ec56b 100644
--- a/diced/open_dice/src/lib.rs
+++ b/diced/open_dice/src/lib.rs
@@ -27,13 +27,19 @@
 #[cfg(feature = "std")]
 mod retry;
 
-pub use bcc::bcc_format_config_descriptor;
+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, Cdi, Config, DiceMode, Hash, Hidden,
-    InlineConfig, InputValues, PrivateKeySeed, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE, ID_SIZE,
-    PRIVATE_KEY_SEED_SIZE,
+    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::hash;
+pub use ops::{generate_certificate, hash, kdf, keypair_from_seed, sign, verify};
 #[cfg(feature = "std")]
-pub use retry::retry_bcc_format_config_descriptor;
+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
index ca0e341..8222b26 100644
--- a/diced/open_dice/src/ops.rs
+++ b/diced/open_dice/src/ops.rs
@@ -16,9 +16,14 @@
 //! It contains the set of functions that implement various operations that the
 //! main DICE functions depend on.
 
-use crate::dice::{Hash, HASH_SIZE};
+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::DiceHash;
+use open_dice_cbor_bindgen::{
+    DiceGenerateCertificate, DiceHash, DiceKdf, DiceKeypairFromSeed, DiceSign, DiceVerify,
+};
 use std::ptr;
 
 /// Hashes the provided input using DICE's hash function `DiceHash`.
@@ -36,3 +41,102 @@
     })?;
     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
index 648fd91..76a214c 100644
--- a/diced/open_dice/src/retry.rs
+++ b/diced/open_dice/src/retry.rs
@@ -17,10 +17,40 @@
 //! memory allocation on heap, currently we only expose these functions in
 //! std environment.
 
-use crate::bcc::bcc_format_config_descriptor;
+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
@@ -62,3 +92,69 @@
         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
index 5b36f8d..a47265b 100644
--- a/diced/open_dice/tests/api_test.rs
+++ b/diced/open_dice/tests/api_test.rs
@@ -14,20 +14,33 @@
  * limitations under the License.
  */
 
-use diced_open_dice::{derive_cdi_certificate_id, hash, ID_SIZE};
+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() {
-    assert_eq!(
-        hash(b"hello world").expect("hash failed"),
-        [
-            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
-        ]
-    );
+    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]
@@ -38,3 +51,57 @@
     ];
     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
index 61d4d50..a2534dc 100644
--- a/diced/open_dice_cbor/Android.bp
+++ b/diced/open_dice_cbor/Android.bp
@@ -16,29 +16,6 @@
     default_applicable_licenses: ["system_security_license"],
 }
 
-rust_library {
-    name: "libdiced_open_dice_cbor",
-    crate_name: "diced_open_dice_cbor",
-    srcs: ["lib.rs"],
-
-    rustlibs: [
-        "libdiced_open_dice",
-        // For ZVec
-        "libkeystore2_crypto_rust",
-        "libopen_dice_bcc_bindgen",
-        "libopen_dice_cbor_bindgen",
-    ],
-    static_libs: [
-        "libopen_dice_bcc",
-        "libopen_dice_cbor",
-    ],
-    vendor_available: true,
-    apex_available: [
-        "//apex_available:platform",
-        "com.android.virt",
-    ],
-}
-
 rust_test {
     name: "diced_open_dice_cbor_test",
     crate_name: "diced_open_dice_cbor_test",
@@ -48,13 +25,5 @@
     rustlibs: [
         "libdiced_open_dice",
         "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
index 7b098a8..1650d93 100644
--- a/diced/open_dice_cbor/lib.rs
+++ b/diced/open_dice_cbor/lib.rs
@@ -12,501 +12,11 @@
 // 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::InputValues::new(
-//!     [3u8, dice::HASH_SIZE], // code_hash
-//!     dice::Config::Descriptor(&"My descriptor".as_bytes().to_vec()),
-//!     [0u8, dice::HASH_SIZE], // authority_hash
-//!     dice::DiceMode::kDiceModeNormal,
-//!     [0u8, dice::HIDDEN_SIZE], // hidden
-//! };
-//! let (cdi_attest, cdi_seal, cert_chain) = context
-//!     .main_flow(&parent_cdi_attest, &parent_cdi_seal, &input_values)?;
-//! ```
-
-pub use diced_open_dice::{
-    check_result, derive_cdi_private_key_seed, hash, retry_bcc_format_config_descriptor, Config,
-    DiceError, Hash, Hidden, InputValues, Result, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE,
-    PRIVATE_KEY_SEED_SIZE,
-};
-use keystore2_crypto::ZVec;
-use open_dice_bcc_bindgen::BccMainFlow;
-pub use open_dice_cbor_bindgen::DiceMode;
-use open_dice_cbor_bindgen::{
-    DiceGenerateCertificate, DiceKdf, DiceKeypairFromSeed, DiceMainFlow, DiceSign, DiceVerify,
-    DICE_PRIVATE_KEY_SIZE, DICE_PUBLIC_KEY_SIZE, DICE_SIGNATURE_SIZE,
-};
-use std::ffi::c_void;
-
-/// 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;
-
-/// 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.
-/// TODO(b/267575445): Remove this method once we migrate all its callers to
-/// `retry_with_bigger_buffer` in `diced_open_dice`.
-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(DiceError::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 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(
-        &mut self,
-        current_cdi_attest: &[u8; CDI_SIZE],
-        current_cdi_seal: &[u8; CDI_SIZE],
-        input_values: &InputValues,
-    ) -> 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.
-        // * 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.
-        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.as_ptr(),
-                    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 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(
-        &mut self,
-        subject_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
-        authority_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
-        input_values: &InputValues,
-    ) -> Result<Vec<u8>> {
-        // SAFETY (DiceGenerateCertificate):
-        // * 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.
-        // * 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.
-        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.as_ptr(),
-                    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(
-        &mut self,
-        current_cdi_attest: &[u8; CDI_SIZE],
-        current_cdi_seal: &[u8; CDI_SIZE],
-        bcc: &[u8],
-        input_values: &InputValues,
-    ) -> 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.
-        // * 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.
-        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.as_ptr(),
-                    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))
-    }
-}
-
+// TODO(b/267575445): Move the tests to other target and remove the folder `open_dice_cbor` completely.
 #[cfg(test)]
 mod test {
-    use super::*;
+    use diced_open_dice::DiceArtifacts;
     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 = diced_open_dice::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 =
-            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,
@@ -627,29 +137,9 @@
     // 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);
+        let dice_artifacts = make_sample_bcc_and_cdis().unwrap();
+        assert_eq!(dice_artifacts.cdi_attest(), SAMPLE_CDI_ATTEST_TEST_VECTOR);
+        assert_eq!(dice_artifacts.cdi_seal(), SAMPLE_CDI_SEAL_TEST_VECTOR);
+        assert_eq!(dice_artifacts.bcc(), Some(SAMPLE_BCC_TEST_VECTOR));
     }
 }
diff --git a/diced/src/diced_client_test.rs b/diced/src/diced_client_test.rs
index 054b2d1..7957f0a 100644
--- a/diced/src/diced_client_test.rs
+++ b/diced/src/diced_client_test.rs
@@ -19,7 +19,8 @@
 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 diced_open_dice as dice;
+use diced_open_dice::DiceArtifacts;
 use nix::libc::uid_t;
 use std::convert::TryInto;
 use std::ffi::CString;
@@ -54,11 +55,10 @@
             .unwrap();
 
     let artifacts = artifacts.execute_steps(input_values.iter()).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,
+        artifacts.cdi_attest(),
+        artifacts.cdi_seal(),
+        artifacts.bcc().expect("bcc is none"),
     )
     .unwrap();
     // TODO when we have a parser/verifier, check equivalence rather
@@ -94,11 +94,10 @@
     .unwrap();
 
     let artifacts = artifacts.execute_steps(input_values.iter()).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,
+        artifacts.cdi_attest(),
+        artifacts.cdi_seal(),
+        artifacts.bcc().expect("bcc is none"),
     )
     .unwrap();
     // TODO b/204938506 when we have a parser/verifier, check equivalence rather
@@ -156,11 +155,10 @@
     let client = [client];
 
     let artifacts = artifacts.execute_steps(input_values.iter().chain(client.iter())).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,
+        artifacts.cdi_attest(),
+        artifacts.cdi_seal(),
+        artifacts.bcc().expect("bcc is none"),
     )
     .unwrap();
     // TODO b/204938506 when we have a parser/verifier, check equivalence rather
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/hal_node.rs b/diced/src/hal_node.rs
index 1efee8b..33aeb2b 100644
--- a/diced/src/hal_node.rs
+++ b/diced/src/hal_node.rs
@@ -34,8 +34,8 @@
 };
 use anyhow::{Context, Result};
 use binder::{BinderFeatures, Result as BinderResult, Strong};
-use dice::{ContextImpl, OpenDiceCborContext};
-use diced_open_dice_cbor as dice;
+use diced_open_dice as dice;
+pub use diced_open_dice::DiceArtifacts;
 use diced_utils as utils;
 use nix::sys::wait::{waitpid, WaitStatus};
 use nix::unistd::{
@@ -47,7 +47,7 @@
 use std::os::unix::io::RawFd;
 use std::sync::{Arc, RwLock};
 use utils::ResidentArtifacts;
-pub use utils::{DiceArtifacts, UpdatableDiceArtifacts};
+pub use utils::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
@@ -202,7 +202,6 @@
         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!(
@@ -212,24 +211,11 @@
                     })?,
                 )
                 .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()
-                        )
-                    })?)
+                let (_public_key, private_key) = dice::keypair_from_seed(seed.as_array())
                     .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.")
+                let signature = dice::sign(message, private_key.as_array())
+                    .context("In ResidentHal::sign: Failed to sign.")?;
+                Ok(signature.to_vec())
             })
             .context("In ResidentHal::sign:")?;
         Ok(Signature { data: signature })
@@ -330,8 +316,8 @@
         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 anyhow::{anyhow, Context, Result};
+    use diced_open_dice as dice;
     use diced_sample_inputs;
     use diced_utils as utils;
     use std::ffi::CStr;
@@ -343,6 +329,16 @@
         bcc: Vec<u8>,
     }
 
+    impl From<dice::OwnedDiceArtifacts> for InsecureSerializableArtifacts {
+        fn from(dice_artifacts: dice::OwnedDiceArtifacts) -> Self {
+            let mut cdi_attest = [0u8; dice::CDI_SIZE];
+            cdi_attest.copy_from_slice(dice_artifacts.cdi_attest());
+            let mut cdi_seal = [0u8; dice::CDI_SIZE];
+            cdi_seal.copy_from_slice(dice_artifacts.cdi_seal());
+            Self { cdi_attest, cdi_seal, bcc: dice_artifacts.bcc().expect("bcc is none").to_vec() }
+        }
+    }
+
     impl DiceArtifacts for InsecureSerializableArtifacts {
         fn cdi_attest(&self) -> &[u8; dice::CDI_SIZE] {
             &self.cdi_attest
@@ -350,8 +346,8 @@
         fn cdi_seal(&self) -> &[u8; dice::CDI_SIZE] {
             &self.cdi_seal
         }
-        fn bcc(&self) -> Vec<u8> {
-            self.bcc.clone()
+        fn bcc(&self) -> Option<&[u8]> {
+            Some(&self.bcc)
         }
     }
 
@@ -366,7 +362,7 @@
             Ok(Self {
                 cdi_attest: *new_artifacts.cdi_attest(),
                 cdi_seal: *new_artifacts.cdi_seal(),
-                bcc: new_artifacts.bcc(),
+                bcc: new_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?.to_vec(),
             })
         }
     }
@@ -394,10 +390,8 @@
     /// 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 artifacts: ResidentArtifacts =
+            diced_sample_inputs::make_sample_bcc_and_cdis()?.try_into()?;
 
         let input_values = &[
             make_input_values(
@@ -421,7 +415,7 @@
         let result = utils::make_bcc_handover(
             new_artifacts.cdi_attest(),
             new_artifacts.cdi_seal(),
-            &new_artifacts.bcc(),
+            new_artifacts.bcc().unwrap(),
         )?;
 
         assert_eq!(result, make_derive_test_vector());
@@ -433,18 +427,13 @@
     /// 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()?;
+        let dice_artifacts = 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 hal_impl =
+            unsafe { ResidentHal::new(InsecureSerializableArtifacts::from(dice_artifacts)) }
+                .expect("Failed to create ResidentHal.");
 
         let bcc_handover = hal_impl
             .derive(&[
@@ -474,18 +463,13 @@
     /// 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()?;
+        let dice_artifacts = 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 hal_impl =
+            unsafe { ResidentHal::new(InsecureSerializableArtifacts::from(dice_artifacts)) }
+                .expect("Failed to create ResidentHal.");
 
         hal_impl
             .demote(&[
diff --git a/diced/src/lib.rs b/diced/src/lib.rs
deleted file mode 100644
index 9e70ace..0000000
--- a/diced/src/lib.rs
+++ /dev/null
@@ -1,205 +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::ffi::CString;
-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> {
-    let desc = CString::new(format!("{}", uid)).unwrap();
-    Ok(BinderInputValues {
-        codeHash: [0; dice::HASH_SIZE],
-        config: BinderConfig {
-            desc: dice::retry_bcc_format_config_descriptor(Some(desc.as_c_str()), None, true)
-                .unwrap(),
-        },
-        authorityHash: [0; dice::HASH_SIZE],
-        authorityDescriptor: None,
-        hidden: [0; dice::HIDDEN_SIZE],
-        mode: Mode::NORMAL,
-    })
-}
-
-impl DiceNode {
-    /// Constructs an instance of DiceNode, wraps it with a BnDiceNode object and
-    /// returns a strong pointer to the binder. The result can be used to register
-    /// the service with service manager.
-    pub fn new_as_binder(
-        node_impl: Arc<dyn DiceNodeImpl + Sync + Send>,
-    ) -> Result<Strong<dyn IDiceNode>> {
-        let result = BnDiceNode::new_binder(
-            DiceNode { node_impl },
-            BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
-        );
-        Ok(result)
-    }
-
-    fn sign(&self, input_values: &[BinderInputValues], message: &[u8]) -> Result<Signature> {
-        check_caller_permission(Permission::UseSign).context("In DiceNode::sign:")?;
-        let client =
-            client_input_values(ThreadState::get_calling_uid()).context("In DiceNode::sign:")?;
-        self.node_impl.sign(client, input_values, message)
-    }
-    fn get_attestation_chain(&self, input_values: &[BinderInputValues]) -> Result<Bcc> {
-        check_caller_permission(Permission::GetAttestationChain)
-            .context("In DiceNode::get_attestation_chain:")?;
-        let client = client_input_values(ThreadState::get_calling_uid())
-            .context("In DiceNode::get_attestation_chain:")?;
-        self.node_impl.get_attestation_chain(client, input_values)
-    }
-    fn derive(&self, input_values: &[BinderInputValues]) -> Result<BccHandover> {
-        check_caller_permission(Permission::Derive).context("In DiceNode::derive:")?;
-        let client =
-            client_input_values(ThreadState::get_calling_uid()).context("In DiceNode::extend:")?;
-        self.node_impl.derive(client, input_values)
-    }
-    fn demote(&self, input_values: &[BinderInputValues]) -> Result<()> {
-        check_caller_permission(Permission::Demote).context("In DiceNode::demote:")?;
-        let client =
-            client_input_values(ThreadState::get_calling_uid()).context("In DiceNode::demote:")?;
-        self.node_impl.demote(client, input_values)
-    }
-}
-
-impl binder::Interface for DiceNode {}
-
-impl IDiceNode for DiceNode {
-    fn sign(&self, input_values: &[BinderInputValues], message: &[u8]) -> BinderResult<Signature> {
-        map_or_log_err(self.sign(input_values, message), Ok)
-    }
-    fn getAttestationChain(&self, input_values: &[BinderInputValues]) -> BinderResult<Bcc> {
-        map_or_log_err(self.get_attestation_chain(input_values), Ok)
-    }
-    fn derive(&self, input_values: &[BinderInputValues]) -> BinderResult<BccHandover> {
-        map_or_log_err(self.derive(input_values), Ok)
-    }
-    fn demote(&self, input_values: &[BinderInputValues]) -> BinderResult<()> {
-        map_or_log_err(self.demote(input_values), Ok)
-    }
-}
-
-/// Wraps a DiceNodeImpl and implements the IDiceMaintenance AIDL API.
-pub struct DiceMaintenance {
-    node_impl: Arc<dyn DiceNodeImpl + Sync + Send>,
-}
-
-impl DiceMaintenance {
-    /// Constructs an instance of DiceMaintenance, wraps it with a BnDiceMaintenance object and
-    /// returns a strong pointer to the binder. The result can be used to register the service
-    /// with service manager.
-    pub fn new_as_binder(
-        node_impl: Arc<dyn DiceNodeImpl + Sync + Send>,
-    ) -> Result<Strong<dyn IDiceMaintenance>> {
-        let result = BnDiceMaintenance::new_binder(
-            DiceMaintenance { node_impl },
-            BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
-        );
-        Ok(result)
-    }
-
-    fn demote_self(&self, input_values: &[BinderInputValues]) -> Result<()> {
-        check_caller_permission(Permission::DemoteSelf)
-            .context("In DiceMaintenance::demote_self:")?;
-        self.node_impl.demote_self(input_values)
-    }
-}
-
-impl binder::Interface for DiceMaintenance {}
-
-impl IDiceMaintenance for DiceMaintenance {
-    fn demoteSelf(&self, input_values: &[BinderInputValues]) -> BinderResult<()> {
-        map_or_log_err(self.demote_self(input_values), Ok)
-    }
-}
diff --git a/diced/src/lib_vendor.rs b/diced/src/lib_vendor.rs
index 01c804b..a04bb80 100644
--- a/diced/src/lib_vendor.rs
+++ b/diced/src/lib_vendor.rs
@@ -17,4 +17,4 @@
 
 mod error_vendor;
 pub mod hal_node;
-pub use diced_open_dice_cbor as dice;
+pub use diced_open_dice as dice;
diff --git a/diced/src/permission.rs b/diced/src/permission.rs
deleted file mode 100644
index 62ca653..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, Eq)]
-    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 d65f30c..0000000
--- a/diced/src/resident_node.rs
+++ /dev/null
@@ -1,178 +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, 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 = demotion_db
-            .get(&client_arr[0])
-            .map(|v| v.iter())
-            .unwrap_or_else(|| client_arr.iter())
-            .chain(input_values.iter());
-        artifacts.execute_steps(input_values).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();
-
-        *artifacts = artifacts
-            .try_clone()
-            .context("In ResidentNode::demote_self: Failed to clone resident artifacts")?
-            .execute_steps(input_values)
-            .context("In ResidentNode::demote_self:")?;
-        Ok(())
-    }
-}
diff --git a/diced/src/sample_inputs.rs b/diced/src/sample_inputs.rs
index 5b61fed..6ad8ca9 100644
--- a/diced/src/sample_inputs.rs
+++ b/diced/src/sample_inputs.rs
@@ -18,12 +18,10 @@
 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 anyhow::{anyhow, Context, Result};
+use diced_open_dice as dice;
+use diced_open_dice::DiceArtifacts;
 use diced_utils::{cbor, to_dice_input_values};
-use keystore2_crypto::ZVec;
-use std::convert::TryInto;
 use std::ffi::CStr;
 use std::io::Write;
 
@@ -66,23 +64,18 @@
 
 /// 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();
+pub fn make_sample_bcc_and_cdis() -> Result<dice::OwnedDiceArtifacts> {
     let private_key_seed = dice::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 (public_key, _) = dice::keypair_from_seed(private_key_seed.as_array())
+        .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, &to_dice_input_values(&input_values_vector[0]))
-        .context("In make_sample_bcc_and_cdis: Trying to run first main flow.")?;
+    let (cdi_values, mut cert) =
+        dice::retry_dice_main_flow(UDS, UDS, &to_dice_input_values(&input_values_vector[0]))
+            .context("In make_sample_bcc_and_cdis: Trying to run first main flow.")?;
 
     let mut bcc: Vec<u8> = vec![];
 
@@ -93,30 +86,20 @@
 
     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,
-            &to_dice_input_values(&input_values_vector[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,
-            &to_dice_input_values(&input_values_vector[2]),
-        )
-        .context("In make_sample_bcc_and_cdis: Trying to run second bcc main flow.")
+    let dice_artifacts = dice::retry_bcc_main_flow(
+        &cdi_values.cdi_attest,
+        &cdi_values.cdi_seal,
+        &bcc,
+        &to_dice_input_values(&input_values_vector[1]),
+    )
+    .context("In make_sample_bcc_and_cdis: Trying to run first bcc main flow.")?;
+    dice::retry_bcc_main_flow(
+        dice_artifacts.cdi_attest(),
+        dice_artifacts.cdi_seal(),
+        dice_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?,
+        &to_dice_input_values(&input_values_vector[2]),
+    )
+    .context("In make_sample_bcc_and_cdis: Trying to run second bcc main flow.")
 }
 
 fn make_input_values(
diff --git a/diced/src/utils.rs b/diced/src/utils.rs
index 3d35ae5..2789a60 100644
--- a/diced/src/utils.rs
+++ b/diced/src/utils.rs
@@ -18,9 +18,9 @@
     Bcc::Bcc, BccHandover::BccHandover, InputValues::InputValues as BinderInputValues,
     Mode::Mode as BinderMode,
 };
-use anyhow::{Context, Result};
-use dice::{ContextImpl, DiceMode};
-use diced_open_dice_cbor as dice;
+use anyhow::{anyhow, Context, Result};
+use diced_open_dice as dice;
+use diced_open_dice::DiceArtifacts;
 use keystore2_crypto::ZVec;
 use std::convert::TryInto;
 
@@ -38,13 +38,13 @@
     )
 }
 
-fn to_dice_mode(binder_mode: BinderMode) -> DiceMode {
+fn to_dice_mode(binder_mode: BinderMode) -> dice::DiceMode {
     match binder_mode {
-        BinderMode::NOT_INITIALIZED => DiceMode::kDiceModeNotInitialized,
-        BinderMode::NORMAL => DiceMode::kDiceModeNormal,
-        BinderMode::DEBUG => DiceMode::kDiceModeDebug,
-        BinderMode::RECOVERY => DiceMode::kDiceModeMaintenance,
-        _ => DiceMode::kDiceModeNotInitialized,
+        BinderMode::NOT_INITIALIZED => dice::DiceMode::kDiceModeNotInitialized,
+        BinderMode::NORMAL => dice::DiceMode::kDiceModeNormal,
+        BinderMode::DEBUG => dice::DiceMode::kDiceModeDebug,
+        BinderMode::RECOVERY => dice::DiceMode::kDiceModeMaintenance,
+        _ => dice::DiceMode::kDiceModeNotInitialized,
     }
 }
 
@@ -62,12 +62,25 @@
 /// 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.
+/// TODO(b/268322533): Remove this struct with the unused HAL service dice
 pub struct ResidentArtifacts {
     cdi_attest: ZVec,
     cdi_seal: ZVec,
     bcc: Vec<u8>,
 }
 
+impl TryFrom<dice::OwnedDiceArtifacts> for ResidentArtifacts {
+    type Error = anyhow::Error;
+
+    fn try_from(dice_artifacts: dice::OwnedDiceArtifacts) -> Result<Self, Self::Error> {
+        Ok(ResidentArtifacts {
+            cdi_attest: dice_artifacts.cdi_attest().to_vec().try_into()?,
+            cdi_seal: dice_artifacts.cdi_seal().to_vec().try_into()?,
+            bcc: dice_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?.to_vec(),
+        })
+    }
+}
+
 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
@@ -93,9 +106,9 @@
     /// 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(),
+            cdi_attest: artifacts.cdi_attest().to_vec().try_into()?,
+            cdi_seal: artifacts.cdi_seal().to_vec().try_into()?,
+            bcc: artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?.to_vec(),
         })
     }
 
@@ -125,19 +138,18 @@
     fn execute_step(self, input_values: &BinderInputValues) -> 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,
-                &to_dice_input_values(input_values),
-            )
-            .context("In ResidentArtifacts::execute_step:")?;
-        Ok(ResidentArtifacts { cdi_attest, cdi_seal, bcc })
+        dice::retry_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,
+            &to_dice_input_values(input_values),
+        )
+        .context("In ResidentArtifacts::execute_step:")?
+        .try_into()
     }
 
     /// Iterate through the iterator of dice input values performing one
@@ -153,20 +165,6 @@
     }
 }
 
-/// 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 {
@@ -189,8 +187,8 @@
     fn cdi_seal(&self) -> &[u8; dice::CDI_SIZE] {
         self.cdi_seal[..].try_into().unwrap()
     }
-    fn bcc(&self) -> Vec<u8> {
-        self.bcc.clone()
+    fn bcc(&self) -> Option<&[u8]> {
+        Some(&self.bcc)
     }
 }
 
diff --git a/identity/CredentialStore.cpp b/identity/CredentialStore.cpp
index fea4df9..e2b3cf4 100644
--- a/identity/CredentialStore.cpp
+++ b/identity/CredentialStore.cpp
@@ -47,7 +47,7 @@
 
 bool useRkpd() {
     return android::base::GetBoolProperty("remote_provisioning.enable_rkpd",
-                                          /*default_value=*/false);
+                                          /*default_value=*/true);
 }
 
 }  // namespace
diff --git a/keystore2/src/attestation_key_utils.rs b/keystore2/src/attestation_key_utils.rs
index d31fa82..8c4cdea 100644
--- a/keystore2/src/attestation_key_utils.rs
+++ b/keystore2/src/attestation_key_utils.rs
@@ -54,11 +54,9 @@
 }
 
 fn use_rkpd() -> bool {
-    let mutable_property = "persist.device_config.remote_key_provisioning_native.enable_rkpd";
-    let fixed_property = "remote_provisioning.enable_rkpd";
-    let default_value = false;
-    system_properties::read_bool(mutable_property, default_value).unwrap_or(default_value)
-        || system_properties::read_bool(fixed_property, default_value).unwrap_or(default_value)
+    let property = "remote_provisioning.enable_rkpd";
+    let default_value = true;
+    system_properties::read_bool(property, default_value).unwrap_or(default_value)
 }
 
 /// This function loads and, optionally, assigns the caller's remote provisioned
diff --git a/keystore2/src/rkpd_client.rs b/keystore2/src/rkpd_client.rs
index f1e8e11..0ea2d39 100644
--- a/keystore2/src/rkpd_client.rs
+++ b/keystore2/src/rkpd_client.rs
@@ -57,8 +57,11 @@
 
     fn send(&self, value: T) {
         if let Some(inner) = self.inner.lock().unwrap().take() {
-            // assert instead of unwrap, because on failure send returns Err(value)
-            assert!(inner.send(value).is_ok(), "thread state is terminally broken");
+            // 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");
+            }
         }
     }
 }
@@ -202,8 +205,14 @@
         .context(ks_err!("Trying to get key."))?;
 
     match timeout(RKPD_TIMEOUT, rx).await {
-        Err(e) => Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
-            .context(ks_err!("Waiting for RKPD key timed out: {:?}", e)),
+        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(),
     }
 }
@@ -314,52 +323,68 @@
     use keystore2_crypto::parse_subject_from_certificate;
     use std::collections::HashMap;
     use std::sync::atomic::{AtomicU32, Ordering};
+    use std::sync::{Arc, Mutex};
 
-    #[derive(Default)]
-    struct MockRegistration {
+    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 {
+            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: self.key.keyBlob.clone(),
-                encodedCertChain: self.key.encodedCertChain.clone(),
+                keyBlob: values.key.keyBlob.clone(),
+                encodedCertChain: values.key.encodedCertChain.clone(),
             };
-            let latency = self.latency;
+            let latency = values.latency;
             let get_key_cb = cb.clone();
 
             // Need a separate thread to trigger timeout in the caller.
-            std::thread::spawn(move || {
+            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<()> {
-            todo!()
+            Ok(())
         }
 
         fn storeUpgradedKeyAsync(
@@ -370,8 +395,9 @@
         ) -> 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 = self.latency;
+            let latency = values.latency;
 
             std::thread::spawn(move || {
                 if let Some(duration) = latency {
@@ -528,7 +554,7 @@
     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(10);
+        let latency = RKPD_TIMEOUT + Duration::from_secs(1);
         let registration = get_mock_registration(&mock_key, Some(latency)).unwrap();
 
         let result =
@@ -553,7 +579,7 @@
     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(10);
+        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(
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index b928fb0..cc1f816 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -612,7 +612,7 @@
                         })
                     },
                 )
-                .context("While generating Key with remote provisioned attestation key.")
+                .context(ks_err!("While generating Key with remote provisioned attestation key."))
                 .map(|(mut result, _)| {
                     result.certificateChain.push(attestation_certs);
                     result
@@ -635,7 +635,7 @@
                         self.keymint.generateKey(&params, dynamic_attest_key.as_ref())
                     })
                 })
-                .context("While generating Key with remote provisioned attestation key.")
+                .context(ks_err!("While generating Key with remote provisioned attestation key."))
                 .map(|(mut result, _)| {
                     result.certificateChain.push(attestation_certs);
                     result
@@ -651,7 +651,7 @@
                 );
                 self.keymint.generateKey(&params, None)
             })
-            .context("While generating Key without explicit attestation key."),
+            .context(ks_err!("While generating Key without explicit attestation key.")),
         }
         .context(ks_err!())?;
 
@@ -684,7 +684,7 @@
         };
 
         // import_key requires the rebind permission.
-        check_key_permission(KeyPerm::Rebind, &key, &None).context("In import_key.")?;
+        check_key_permission(KeyPerm::Rebind, &key, &None).context(ks_err!("In import_key."))?;
 
         let params = self
             .add_required_parameters(caller_uid, params, &key)
@@ -694,7 +694,7 @@
             .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)
@@ -780,7 +780,7 @@
                     )
                 })
             })
-            .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(
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index f43ba5c..1040228 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -205,17 +205,18 @@
             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.
@@ -230,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,
@@ -244,7 +245,7 @@
                 certificate_chain.unwrap(),
                 &KEYSTORE_UUID,
             )
-            .context("Failed to insert new certificate.")?;
+            .context(ks_err!("Failed to insert new certificate."))?;
             Ok(())
         })
         .context(ks_err!())
@@ -295,7 +296,8 @@
         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."))
                 })
             })
         })
diff --git a/keystore2/test_utils/key_generations.rs b/keystore2/test_utils/key_generations.rs
index f9aaabb..e4c4968 100644
--- a/keystore2/test_utils/key_generations.rs
+++ b/keystore2/test_utils/key_generations.rs
@@ -1047,3 +1047,38 @@
         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)
+        }
+        Err(e) => Err(e),
+    }
+}
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_tests.rs b/keystore2/tests/keystore2_client_tests.rs
index 45c5fb7..07a298a 100644
--- a/keystore2/tests/keystore2_client_tests.rs
+++ b/keystore2/tests/keystore2_client_tests.rs
@@ -21,6 +21,7 @@
 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;