DPE: Create new DICE Protection Environment partition

Create the basic partition source files, manifest and build system for
the DICE Protection Environment (DPE) partition.

Implements a CBOR decoding layer for decoding DPE commands received as
CBOR objects. Provides a convenience client API that encodes the CBOR
commands from DPE API calls.

The implementation of the DPE commands inside the service is empty in
this patch.

Signed-off-by: Jamie Fox <jamie.fox@arm.com>
Change-Id: Id9c17a75ea738c77407f1c552b15b6b88dfef53e
diff --git a/partitions/dice_protection_environment/CMakeLists.txt b/partitions/dice_protection_environment/CMakeLists.txt
new file mode 100644
index 0000000..1b5413f
--- /dev/null
+++ b/partitions/dice_protection_environment/CMakeLists.txt
@@ -0,0 +1,109 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2023, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+if (NOT TFM_PARTITION_DPE)
+    return()
+endif()
+
+cmake_minimum_required(VERSION 3.15)
+cmake_policy(SET CMP0079 NEW)
+
+# The name of the target is required to be of the pattern
+# tfm_app_rot_partition_x or tfm_psa_rot_partition_x, as it affects how the
+# linker script will lay the partition in memory.
+add_library(tfm_app_rot_partition_dpe STATIC)
+
+target_sources(tfm_app_rot_partition_dpe
+    PRIVATE
+        dpe_cmd_decode.c
+        dpe_impl.c
+        dpe_req_mngr.c
+)
+
+# Add the source files generated by parse tools when building.
+# The intermedia file defines the partition stack.
+target_sources(tfm_app_rot_partition_dpe
+    PRIVATE
+        ${CMAKE_BINARY_DIR}/generated/secure_fw/partitions/dice_protection_environment/auto_generated/intermedia_tfm_dpe.c
+)
+
+# The load info file includes the static data of the partition.
+target_sources(tfm_partitions
+    INTERFACE
+        ${CMAKE_BINARY_DIR}/generated/secure_fw/partitions/dice_protection_environment/auto_generated/load_info_tfm_dpe.c
+)
+
+target_include_directories(tfm_app_rot_partition_dpe
+    PRIVATE
+        ${CMAKE_CURRENT_SOURCE_DIR}
+    PUBLIC
+        ${CMAKE_BINARY_DIR}/generated/secure_fw/partitions/dice_protection_environment
+)
+
+target_link_libraries(tfm_app_rot_partition_dpe
+    PRIVATE
+        tfm_sprt
+        qcbor
+)
+
+############################ Secure API ########################################
+
+target_sources(tfm_sprt
+    PRIVATE
+        ${CMAKE_CURRENT_SOURCE_DIR}/interface/src/dpe_cmd_encode.c
+        ${CMAKE_CURRENT_SOURCE_DIR}/interface/src/dpe_client.c
+)
+
+target_include_directories(tfm_sprt
+    PUBLIC
+        ${CMAKE_CURRENT_SOURCE_DIR}/interface/include
+)
+
+target_link_libraries(tfm_sprt
+    PRIVATE
+        qcbor
+)
+
+############################ Non-secure API ####################################
+
+add_library(dpe_api_ns INTERFACE)
+
+target_sources(dpe_api_ns
+    INTERFACE
+        ${CMAKE_CURRENT_SOURCE_DIR}/interface/src/dpe_cmd_encode.c
+        ${CMAKE_CURRENT_SOURCE_DIR}/interface/src/dpe_client.c
+)
+
+target_include_directories(dpe_api_ns
+    INTERFACE
+        ${CMAKE_CURRENT_SOURCE_DIR}/interface/include
+)
+
+target_link_libraries(dpe_api_ns
+    INTERFACE
+        tfm_qcbor_ns
+)
+
+############################ Partition Defs ####################################
+
+target_link_libraries(tfm_partitions
+    INTERFACE
+        tfm_app_rot_partition_dpe
+)
+
+target_compile_definitions(tfm_config
+    INTERFACE
+        TFM_PARTITION_DPE
+)
+
+install(FILES       ${CMAKE_CURRENT_SOURCE_DIR}/interface/src/dpe_cmd_encode.c
+                    ${CMAKE_CURRENT_SOURCE_DIR}/interface/src/dpe_client.c
+        DESTINATION ${TFM_INSTALL_PATH}/interface/src)
+
+install(FILES       ${CMAKE_CURRENT_SOURCE_DIR}/interface/include/dice_protection_environment.h
+                    ${CMAKE_CURRENT_SOURCE_DIR}/interface/include/dpe_client.h
+        DESTINATION ${TFM_INSTALL_PATH}/interface/include)
diff --git a/partitions/dice_protection_environment/dpe_cmd_decode.c b/partitions/dice_protection_environment/dpe_cmd_decode.c
new file mode 100644
index 0000000..1f14225
--- /dev/null
+++ b/partitions/dice_protection_environment/dpe_cmd_decode.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "dpe_cmd_decode.h"
+
+#include <string.h>
+
+#include "dpe_client.h"
+#include "dpe_impl.h"
+#include "qcbor/qcbor_encode.h"
+#include "qcbor/qcbor_decode.h"
+#include "qcbor/qcbor_spiffy_decode.h"
+
+static dpe_error_t decode_dice_inputs(QCBORDecodeContext *decode_ctx,
+                                      DiceInputValues *input)
+{
+    QCBORError qcbor_err;
+    UsefulBufC out = { NULL, 0 };
+    int64_t out_int;
+
+    /* The DICE inputs are encoded as a map wrapped into a byte string */
+    QCBORDecode_EnterBstrWrappedFromMapN(decode_ctx,
+                                         DPE_DERIVE_CHILD_INPUT_DATA,
+                                         QCBOR_TAG_REQUIREMENT_NOT_A_TAG, NULL);
+    QCBORDecode_EnterMap(decode_ctx, NULL);
+
+    QCBORDecode_GetByteStringInMapN(decode_ctx, DICE_CODE_HASH, &out);
+    if (out.len != sizeof(input->code_hash)) {
+        return DPE_INVALID_COMMAND;
+    }
+    memcpy(input->code_hash, out.ptr, out.len);
+
+    QCBORDecode_GetByteStringInMapN(decode_ctx, DICE_CODE_DESCRIPTOR, &out);
+    input->code_descriptor = out.ptr;
+    input->code_descriptor_size = out.len;
+
+    QCBORDecode_GetInt64InMapN(decode_ctx, DICE_CONFIG_TYPE, &out_int);
+
+    /* Check error state before interpreting config type */
+    qcbor_err = QCBORDecode_GetError(decode_ctx);
+    if (qcbor_err != QCBOR_SUCCESS) {
+        return DPE_INVALID_COMMAND;
+    }
+
+    if (out_int < kDiceConfigTypeInline ||
+        out_int > kDiceConfigTypeDescriptor) {
+        return DPE_INVALID_COMMAND;
+    }
+    input->config_type = (DiceConfigType)out_int;
+
+    /* Only one of config value or config descriptor needs to be provided */
+    if (input->config_type == kDiceConfigTypeInline) {
+        QCBORDecode_GetByteStringInMapN(decode_ctx, DICE_CONFIG_VALUE, &out);
+        if (out.len != sizeof(input->config_value)) {
+            return DPE_INVALID_COMMAND;
+        }
+        memcpy(input->config_value, out.ptr, out.len);
+
+        /* Config descriptor is not provided */
+        input->config_descriptor = NULL;
+        input->config_descriptor_size = 0;
+    } else {
+        QCBORDecode_GetByteStringInMapN(decode_ctx, DICE_CONFIG_DESCRIPTOR,
+                                        &out);
+        input->config_descriptor = out.ptr;
+        input->config_descriptor_size = out.len;
+
+        /* Config value is not provided */
+        memset(input->config_value, 0, sizeof(input->config_value));
+    }
+
+    QCBORDecode_GetByteStringInMapN(decode_ctx, DICE_AUTHORITY_HASH, &out);
+    if (out.len != sizeof(input->authority_hash)) {
+        return DPE_INVALID_COMMAND;
+    }
+    memcpy(input->authority_hash, out.ptr, out.len);
+
+    QCBORDecode_GetByteStringInMapN(decode_ctx, DICE_AUTHORITY_DESCRIPTOR,
+                                    &out);
+    input->authority_descriptor = out.ptr;
+    input->authority_descriptor_size = out.len;
+
+    QCBORDecode_GetInt64InMapN(decode_ctx, DICE_MODE, &out_int);
+    if (out_int < kDiceModeNotInitialized || out_int > kDiceModeMaintenance) {
+        return DPE_INVALID_COMMAND;
+    }
+    input->mode = (DiceMode)out_int;
+
+    QCBORDecode_GetByteStringInMapN(decode_ctx, DICE_HIDDEN, &out);
+    if (out.len != sizeof(input->hidden)) {
+        return DPE_INVALID_COMMAND;
+    }
+    memcpy(input->hidden, out.ptr, out.len);
+
+    QCBORDecode_ExitMap(decode_ctx);
+    QCBORDecode_ExitBstrWrapped(decode_ctx);
+
+    return DPE_NO_ERROR;
+}
+
+static dpe_error_t decode_derive_child(QCBORDecodeContext *decode_ctx,
+                                       QCBOREncodeContext *encode_ctx)
+{
+    dpe_error_t dpe_err;
+    QCBORError qcbor_err;
+    UsefulBufC out;
+    int context_handle;
+    bool retain_parent_context;
+    bool allow_child_to_derive;
+    bool create_certificate;
+    DiceInputValues dice_inputs;
+    int new_child_context_handle;
+    int new_parent_context_handle;
+
+    /* Decode DeriveChild command */
+    QCBORDecode_EnterMap(decode_ctx, NULL);
+
+    QCBORDecode_GetByteStringInMapN(decode_ctx, DPE_DERIVE_CHILD_CONTEXT_HANDLE,
+                                    &out);
+    if (out.len != sizeof(context_handle)) {
+        return DPE_INVALID_COMMAND;
+    }
+    memcpy(&context_handle, out.ptr, out.len);
+
+    QCBORDecode_GetBoolInMapN(decode_ctx, DPE_DERIVE_CHILD_RETAIN_PARENT_CONTEXT,
+                              &retain_parent_context);
+
+    QCBORDecode_GetBoolInMapN(decode_ctx, DPE_DERIVE_CHILD_ALLOW_CHILD_TO_DERIVE,
+                              &allow_child_to_derive);
+
+    QCBORDecode_GetBoolInMapN(decode_ctx, DPE_DERIVE_CHILD_CREATE_CERTIFICATE,
+                              &create_certificate);
+
+    dpe_err = decode_dice_inputs(decode_ctx, &dice_inputs);
+    if (dpe_err != DPE_NO_ERROR) {
+        return dpe_err;
+    }
+
+    QCBORDecode_ExitMap(decode_ctx);
+
+    /* Exit top level array */
+    QCBORDecode_ExitArray(decode_ctx);
+
+    /* Finish and check for errors before using decoded values */
+    qcbor_err = QCBORDecode_Finish(decode_ctx);
+    if (qcbor_err != QCBOR_SUCCESS) {
+        return DPE_INVALID_COMMAND;
+    }
+
+    dpe_err = dpe_derive_child_impl(context_handle, retain_parent_context,
+                                    allow_child_to_derive, create_certificate,
+                                    &dice_inputs, &new_child_context_handle,
+                                    &new_parent_context_handle);
+    if (dpe_err != DPE_NO_ERROR) {
+        return dpe_err;
+    }
+
+    /* Encode response */
+    QCBOREncode_OpenArray(encode_ctx);
+    QCBOREncode_AddInt64(encode_ctx, DPE_NO_ERROR);
+
+    QCBOREncode_OpenMap(encode_ctx);
+    QCBOREncode_AddBytesToMapN(encode_ctx, DPE_DERIVE_CHILD_NEW_CONTEXT_HANDLE,
+                               (UsefulBufC){ &new_child_context_handle,
+                                             sizeof(new_child_context_handle) });
+    QCBOREncode_AddBytesToMapN(encode_ctx,
+                               DPE_DERIVE_CHILD_PARENT_CONTEXT_HANDLE,
+                               (UsefulBufC){ &new_parent_context_handle,
+                                             sizeof(new_parent_context_handle) });
+    QCBOREncode_CloseMap(encode_ctx);
+
+    QCBOREncode_CloseArray(encode_ctx);
+
+    return DPE_NO_ERROR;
+}
+
+static dpe_error_t decode_certify_key(QCBORDecodeContext *decode_ctx,
+                                      QCBOREncodeContext *encode_ctx)
+{
+    QCBORError qcbor_err;
+    UsefulBufC out;
+    dpe_error_t dpe_err;
+    int context_handle;
+    bool retain_context;
+    const uint8_t *public_key;
+    size_t public_key_size;
+    const uint8_t *label;
+    size_t label_size;
+    uint8_t certificate_chain_buf[DPE_CERTIFICATE_CHAIN_MAX_SIZE];
+    size_t certificate_chain_actual_size;
+    uint8_t derived_public_key_buf[DPE_PUBLIC_KEY_MAX_SIZE];
+    size_t derived_public_key_actual_size;
+    int new_context_handle;
+
+    /* Decode CertifyKey command */
+    QCBORDecode_EnterMap(decode_ctx, NULL);
+
+    QCBORDecode_GetByteStringInMapN(decode_ctx, DPE_CERTIFY_KEY_CONTEXT_HANDLE,
+                                    &out);
+    if (out.len != sizeof(context_handle)) {
+        return DPE_INVALID_COMMAND;
+    }
+    memcpy(&context_handle, out.ptr, out.len);
+
+    QCBORDecode_GetBoolInMapN(decode_ctx, DPE_CERTIFY_KEY_RETAIN_CONTEXT,
+                              &retain_context);
+
+    QCBORDecode_GetByteStringInMapN(decode_ctx, DPE_CERTIFY_KEY_PUBLIC_KEY,
+                                    &out);
+    public_key = out.ptr;
+    public_key_size = out.len;
+
+    QCBORDecode_GetByteStringInMapN(decode_ctx, DPE_CERTIFY_KEY_LABEL, &out);
+    label = out.ptr;
+    label_size = out.len;
+
+    QCBORDecode_ExitMap(decode_ctx);
+
+    /* Exit top level array */
+    QCBORDecode_ExitArray(decode_ctx);
+
+    /* Finish and check for errors before using decoded values */
+    qcbor_err = QCBORDecode_Finish(decode_ctx);
+    if (qcbor_err != QCBOR_SUCCESS) {
+        return DPE_INVALID_COMMAND;
+    }
+
+    dpe_err = dpe_certify_key_impl(context_handle, retain_context, public_key,
+                                   public_key_size, label, label_size,
+                                   certificate_chain_buf,
+                                   sizeof(certificate_chain_buf),
+                                   &certificate_chain_actual_size,
+                                   derived_public_key_buf,
+                                   sizeof(derived_public_key_buf),
+                                   &derived_public_key_actual_size,
+                                   &new_context_handle);
+    if (dpe_err != DPE_NO_ERROR) {
+        return dpe_err;
+    }
+
+    /* Encode response */
+    QCBOREncode_OpenArray(encode_ctx);
+    QCBOREncode_AddInt64(encode_ctx, DPE_NO_ERROR);
+
+    QCBOREncode_OpenMap(encode_ctx);
+
+    /* The certificate chain is already encoded into a CBOR array by the certify
+     * key implementation. Add it as a byte string so that its decoding can be
+     * skipped and the CBOR returned to the caller.
+     */
+    QCBOREncode_AddBytesToMapN(encode_ctx, DPE_CERTIFY_KEY_CERTIFICATE_CHAIN,
+                               (UsefulBufC){ certificate_chain_buf,
+                                             certificate_chain_actual_size });
+
+    QCBOREncode_AddBytesToMapN(encode_ctx, DPE_CERTIFY_KEY_DERIVED_PUBLIC_KEY,
+                               (UsefulBufC){ derived_public_key_buf,
+                                             derived_public_key_actual_size });
+    QCBOREncode_AddBytesToMapN(encode_ctx, DPE_CERTIFY_KEY_NEW_CONTEXT_HANDLE,
+                               (UsefulBufC){ &new_context_handle,
+                                             sizeof(new_context_handle) });
+
+    QCBOREncode_CloseMap(encode_ctx);
+
+    QCBOREncode_CloseArray(encode_ctx);
+
+    return DPE_NO_ERROR;
+}
+
+static void encode_error_only(QCBOREncodeContext *encode_ctx,
+                              dpe_error_t dpe_err)
+{
+    QCBOREncode_OpenArray(encode_ctx);
+    QCBOREncode_AddInt64(encode_ctx, dpe_err);
+    QCBOREncode_CloseArray(encode_ctx);
+}
+
+int32_t dpe_command_decode(int32_t client_id,
+                           const char *cmd_input, size_t cmd_input_size,
+                           char *cmd_output, size_t *cmd_output_size)
+{
+    dpe_error_t dpe_err;
+    QCBORError qcbor_err;
+    QCBORDecodeContext decode_ctx;
+    QCBOREncodeContext encode_ctx;
+    UsefulBufC out;
+    uint64_t command_id;
+
+    QCBORDecode_Init(&decode_ctx, (UsefulBufC){ cmd_input, cmd_input_size },
+                     QCBOR_DECODE_MODE_NORMAL);
+    QCBOREncode_Init(&encode_ctx, (UsefulBuf){ cmd_output, *cmd_output_size });
+
+    /* Enter top level array */
+    QCBORDecode_EnterArray(&decode_ctx, NULL);
+
+    /* Get the command ID */
+    QCBORDecode_GetUInt64(&decode_ctx, &command_id);
+
+    /* Check for errors before interpreting the decoded command ID */
+    qcbor_err = QCBORDecode_GetError(&decode_ctx);
+
+    if (qcbor_err == QCBOR_SUCCESS) {
+        switch (command_id) {
+        case DPE_DERIVE_CHILD:
+            dpe_err = decode_derive_child(&decode_ctx, &encode_ctx);
+            break;
+        case DPE_CERTIFY_KEY:
+            dpe_err = decode_certify_key(&decode_ctx, &encode_ctx);
+            break;
+        default:
+            dpe_err = DPE_INVALID_COMMAND;
+            break;
+        }
+    } else {
+        dpe_err = DPE_INVALID_COMMAND;
+    }
+
+    /* If an unhandled DPE error was returned, then encode it into a response */
+    if (dpe_err != DPE_NO_ERROR) {
+        encode_error_only(&encode_ctx, dpe_err);
+    }
+
+    qcbor_err = QCBOREncode_Finish(&encode_ctx, &out);
+    if (qcbor_err != QCBOR_SUCCESS) {
+        return -1;
+    }
+
+    *cmd_output_size = out.len;
+
+    return 0;
+}
diff --git a/partitions/dice_protection_environment/dpe_cmd_decode.h b/partitions/dice_protection_environment/dpe_cmd_decode.h
new file mode 100644
index 0000000..b7ac721
--- /dev/null
+++ b/partitions/dice_protection_environment/dpe_cmd_decode.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __DPE_CMD_DECODE_H__
+#define __DPE_CMD_DECODE_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Call the DPE service with a CBOR-encoded DPE command.
+ *
+ * \param[in]     client_id        Identifier of the client calling the service.
+ * \param[in]     cmd_input        Pointer to buffer containing the input
+ *                                 CBOR-encoded DPE command.
+ * \param[in]     cmd_input_size   Size of the input command, in bytes.
+ * \param[out]    cmd_output       Pointer to buffer to write the CBOR-encoded
+ *                                 DPE command output.
+ * \param[in,out] cmd_output_size  On input, size of the command output buffer
+ *                                 in bytes. On successful return, size of the
+ *                                 response written to the buffer.
+ *
+ * \note The cmd_input and cmd_output memory areas may overlap.
+ *
+ * \return Returns 0 if call succeeded and cmd_output contains a valid response
+ *         and returns less than 0 otherwise.
+ */
+int32_t dpe_command_decode(int32_t client_id,
+                          const char *cmd_input, size_t cmd_input_size,
+                          char *cmd_output, size_t *cmd_output_size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DPE_CMD_DECODE_H__ */
diff --git a/partitions/dice_protection_environment/dpe_impl.c b/partitions/dice_protection_environment/dpe_impl.c
new file mode 100644
index 0000000..563957e
--- /dev/null
+++ b/partitions/dice_protection_environment/dpe_impl.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "dpe_impl.h"
+
+#include <string.h>
+
+dpe_error_t dpe_derive_child_impl(int context_handle,
+                                  bool retain_parent_context,
+                                  bool allow_child_to_derive,
+                                  bool create_certificate,
+                                  const DiceInputValues *dice_inputs,
+                                  int *child_context_handle,
+                                  int *new_context_handle)
+{
+    *child_context_handle = 123;
+    *new_context_handle = 456;
+
+    return DPE_NO_ERROR;
+}
+
+dpe_error_t dpe_certify_key_impl(int context_handle,
+                                 bool retain_context,
+                                 const uint8_t *public_key,
+                                 size_t public_key_size,
+                                 const uint8_t *label,
+                                 size_t label_size,
+                                 uint8_t *certificate_chain_buf,
+                                 size_t certificate_chain_buf_size,
+                                 size_t *certificate_chain_actual_size,
+                                 uint8_t *derived_public_key_buf,
+                                 size_t derived_public_key_buf_size,
+                                 size_t *derived_public_key_actual_size,
+                                 int *new_context_handle)
+{
+    memcpy(certificate_chain_buf, "abc", 4);
+    *certificate_chain_actual_size = 4;
+    memcpy(derived_public_key_buf, "def", 4);
+    *derived_public_key_actual_size = 4;
+    *new_context_handle = 789;
+
+    return DPE_NO_ERROR;
+}
diff --git a/partitions/dice_protection_environment/dpe_impl.h b/partitions/dice_protection_environment/dpe_impl.h
new file mode 100644
index 0000000..5a5fe0a
--- /dev/null
+++ b/partitions/dice_protection_environment/dpe_impl.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __DPE_IMPL_H__
+#define __DPE_IMPL_H__
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "dice_protection_environment.h"
+#include "ext/dice/dice.h"
+#include "psa/crypto.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The maximum supported public key size is for a 384-bit ECC curve */
+#define DPE_PUBLIC_KEY_MAX_SIZE PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(384)
+
+/* The maximum certificate chain size that can be output by this
+ * implementation
+ */
+#define DPE_CERTIFICATE_CHAIN_MAX_SIZE 2048
+
+/* Internal DPE service implementation of dpe_derive_child() */
+dpe_error_t dpe_derive_child_impl(int context_handle,
+                                  bool retain_parent_context,
+                                  bool allow_child_to_derive,
+                                  bool create_certificate,
+                                  const DiceInputValues *dice_inputs,
+                                  int *child_context_handle,
+                                  int *new_context_handle);
+
+/* Internal DPE service implementation of dpe_certify_key() */
+dpe_error_t dpe_certify_key_impl(int context_handle,
+                                 bool retain_context,
+                                 const uint8_t *public_key,
+                                 size_t public_key_size,
+                                 const uint8_t *label,
+                                 size_t label_size,
+                                 uint8_t *certificate_chain_buf,
+                                 size_t certificate_chain_buf_size,
+                                 size_t *certificate_chain_actual_size,
+                                 uint8_t *derived_public_key_buf,
+                                 size_t derived_public_key_buf_size,
+                                 size_t *derived_public_key_actual_size,
+                                 int *new_context_handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DPE_IMPL_H__ */
diff --git a/partitions/dice_protection_environment/dpe_manifest_list.yaml b/partitions/dice_protection_environment/dpe_manifest_list.yaml
new file mode 100644
index 0000000..2429652
--- /dev/null
+++ b/partitions/dice_protection_environment/dpe_manifest_list.yaml
@@ -0,0 +1,29 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2023, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+  "description": "DICE Protection Environment partition manifest",
+  "type": "manifest_list",
+  "version_major": 0,
+  "version_minor": 1,
+  "manifest_list": [
+    {
+      "description": "DICE Protection Environment Partition",
+      "manifest": "tfm_dpe.yaml",
+      "output_path": "secure_fw/partitions/dice_protection_environment",
+      "conditional": "TFM_PARTITION_DPE",
+      "version_major": 0,
+      "version_minor": 1,
+      "pid": 278,
+      "linker_pattern": {
+        "library_list": [
+           "*tfm_*partition_dpe.*"
+        ]
+      }
+    }
+  ]
+}
diff --git a/partitions/dice_protection_environment/dpe_req_mngr.c b/partitions/dice_protection_environment/dpe_req_mngr.c
new file mode 100644
index 0000000..c1cbe06
--- /dev/null
+++ b/partitions/dice_protection_environment/dpe_req_mngr.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <string.h>
+
+#include "dpe_cmd_decode.h"
+#include "psa/service.h"
+
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+
+#define DPE_CMD_MAX_SIZE 4096
+
+static char cmd_buf[DPE_CMD_MAX_SIZE];
+
+psa_status_t tfm_dpe_init(void)
+{
+    return PSA_SUCCESS;
+}
+
+psa_status_t tfm_dpe_service_sfn(const psa_msg_t *msg)
+{
+    int32_t err;
+    size_t in_size = msg->in_size[0];
+    size_t out_size = MIN(msg->out_size[0], sizeof(cmd_buf));
+
+    /* DPE service does not support any non-zero message types */
+    if (msg->type != 0) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    if (in_size > sizeof(cmd_buf)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (psa_read(msg->handle, 0, cmd_buf, in_size) != in_size) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    err = dpe_command_decode(msg->client_id, cmd_buf, in_size,
+                             cmd_buf, &out_size);
+
+    if (err == 0) {
+        psa_write(msg->handle, 0, cmd_buf, out_size);
+    }
+
+    /* Clear the internal command buffer between calls */
+    memset(cmd_buf, 0, sizeof(cmd_buf));
+
+    return err;
+}
diff --git a/partitions/dice_protection_environment/interface/include/dice_protection_environment.h b/partitions/dice_protection_environment/interface/include/dice_protection_environment.h
new file mode 100644
index 0000000..cc34a38
--- /dev/null
+++ b/partitions/dice_protection_environment/interface/include/dice_protection_environment.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __DICE_PROTECTION_ENVIRONMENT_H__
+#define __DICE_PROTECTION_ENVIRONMENT_H__
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "ext/dice/dice.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int32_t dpe_error_t;
+
+#define DPE_NO_ERROR               ((dpe_error_t)0)
+#define DPE_INTERNAL_ERROR         ((dpe_error_t)1)
+#define DPE_INVALID_COMMAND        ((dpe_error_t)2)
+#define DPE_INVALID_ARGUMENT       ((dpe_error_t)3)
+#define DPE_ARGUMENT_NOT_SUPPORTED ((dpe_error_t)4)
+#define DPE_SESSION_EXHAUSTED      ((dpe_error_t)5)
+
+/**
+ * \brief Performs the DICE computation to derive a child context and optionally
+ *        creates an intermediate certificate. Software component measurement
+ *        must be provided in dice_inputs.
+ *
+ * \param[in]  context_handle             Input context handle for the DPE
+ *                                        context.
+ * \param[in]  retain_parent_context      Flag to indicate whether to retain the
+ *                                        parent context. True only if a client
+ *                                        will call further DPE commands on the
+ *                                        same context.
+ * \param[in]  allow_child_to_derive      Flag to indicate whether child context
+ *                                        can derive further. True only if the
+ *                                        child will load further components.
+ * \param[in]  create_certificate         Flag to indicate whether to create an
+ *                                        intermediate certificate. True only if
+ *                                        it is the last component in the layer.
+ * \param[in]  dice_inputs                DICE input values.
+ * \param[out] new_child_context_handle   New handle for the child context.
+ * \param[out] new_parent_context_handle  New handle for the parent context.
+ *
+ * \return Returns error code of type dpe_error_t
+ */
+dpe_error_t
+dpe_derive_child(int                    context_handle,
+                 bool                   retain_parent_context,
+                 bool                   allow_child_to_derive,
+                 bool                   create_certificate,
+                 const DiceInputValues *dice_inputs,
+                 int                   *new_child_context_handle,
+                 int                   *new_parent_context_handle);
+
+/**
+ * \brief Certifies an attestation key with a new leaf certificate and returns
+ *        the certificate chain containing all certificates up to and including
+ *        the new leaf certificate.
+ *
+ * \param[in]  context_handle                  Input context handle for the DPE
+ *                                             context.
+ * \param[in]  retain_context                  Flag to indicate whether to
+ *                                             retain the context.
+ * \param[in]  public_key                      Public key to certify, or NULL to
+ *                                             derive it from the context and
+ *                                             the label argument.
+ * \param[in]  public_key_size                 Size of the public key input.
+ * \param[in]  label                           Label to use in the key
+ *                                             derivation if public key is not
+ *                                             provided.
+ * \param[in]  label_size                      Size of the label input.
+ * \param[out] certificate_chain_buf           Buffer to write the certificate
+ *                                             chain output.
+ * \param[in]  certificate_chain_buf_size      Size of the certificate chain
+ *                                             buffer.
+ * \param[out] certificate_chain_actual_size   Size of the certificate chain
+ *                                             output written to the buffer.
+ * \param[out] derived_public_key_buf          Buffer to write the derived
+ *                                             public key.
+ * \param[in]  derived_public_key_buf_size     Size of the public key buffer.
+ * \param[out] derived_public_key_actual_size  Size of the public key written to
+ *                                             the buffer.
+ * \param[out] new_context_handle              New handle for the DPE context.
+ *
+ * \return Returns error code of type dpe_error_t
+ */
+dpe_error_t
+dpe_certify_key(int            context_handle,
+                bool           retain_context,
+                const uint8_t *public_key,
+                size_t         public_key_size,
+                const uint8_t *label,
+                size_t         label_size,
+                uint8_t       *certificate_chain_buf,
+                size_t         certificate_chain_buf_size,
+                size_t        *certificate_chain_actual_size,
+                uint8_t       *derived_public_key_buf,
+                size_t         derived_public_key_buf_size,
+                size_t        *derived_public_key_actual_size,
+                int           *new_context_handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DICE_PROTECTION_ENVIRONMENT_H__ */
diff --git a/partitions/dice_protection_environment/interface/include/dpe_client.h b/partitions/dice_protection_environment/interface/include/dpe_client.h
new file mode 100644
index 0000000..8a21d26
--- /dev/null
+++ b/partitions/dice_protection_environment/interface/include/dpe_client.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/* DICE Protection Environment (DPE) Client API */
+
+#ifndef __DPE_CLIENT_H__
+#define __DPE_CLIENT_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* CBOR labels as defined in the DICE Protection Environment specification */
+enum dpe_command_id_t {
+    DPE_GET_PROFILE = 1,
+    DPE_OPEN_SESSION = 2,
+    DPE_CLOSE_SESSION = 3,
+    DPE_SYNC_SESSION = 4,
+    DPE_EXPORT_SESSION = 5,
+    DPE_IMPORT_SESSION = 6,
+    DPE_INITIALIZE_CONTEXT = 7,
+    DPE_DERIVE_CHILD = 8,
+    DPE_CERTIFY_KEY = 9,
+    DPE_SIGN = 10,
+    DPE_SEAL = 11,
+    DPE_UNSEAL = 12,
+    DPE_DERIVE_SEALING_PUBLIC_KEY = 13,
+    DPE_ROTATE_CONTEXT_HANDLE = 14,
+    DPE_DESTROY_CONTEXT = 15,
+};
+
+enum dice_input_labels_t {
+    DICE_CODE_HASH = 1,
+    DICE_CODE_DESCRIPTOR = 2,
+    DICE_CONFIG_TYPE = 3,
+    DICE_CONFIG_VALUE = 4,
+    DICE_CONFIG_DESCRIPTOR = 5,
+    DICE_AUTHORITY_HASH = 6,
+    DICE_AUTHORITY_DESCRIPTOR = 7,
+    DICE_MODE = 8,
+    DICE_HIDDEN = 9,
+};
+
+enum dpe_derive_child_input_labels_t {
+    DPE_DERIVE_CHILD_CONTEXT_HANDLE = 1,
+    DPE_DERIVE_CHILD_RETAIN_PARENT_CONTEXT = 2,
+    DPE_DERIVE_CHILD_ALLOW_CHILD_TO_DERIVE = 3,
+    DPE_DERIVE_CHILD_CREATE_CERTIFICATE = 4,
+    DPE_DERIVE_CHILD_NEW_SESSION_INITIATOR_HANDSHAKE = 5,
+    DPE_DERIVE_CHILD_NEW_SESSION_IS_MIGRATABLE = 6,
+    DPE_DERIVE_CHILD_INPUT_DATA = 7,
+    DPE_DERIVE_CHILD_INTERNAL_INPUTS = 8,
+};
+
+enum dpe_derive_child_output_labels_t {
+    DPE_DERIVE_CHILD_NEW_CONTEXT_HANDLE = 1,
+    DPE_DERIVE_CHILD_NEW_SESSION_RESPONDER_HANDSHAKE = 2,
+    DPE_DERIVE_CHILD_PARENT_CONTEXT_HANDLE = 3,
+};
+
+enum dpe_certify_key_input_labels_t {
+    DPE_CERTIFY_KEY_CONTEXT_HANDLE = 1,
+    DPE_CERTIFY_KEY_RETAIN_CONTEXT = 2,
+    DPE_CERTIFY_KEY_PUBLIC_KEY = 3,
+    DPE_CERTIFY_KEY_LABEL = 4,
+    DPE_CERTIFY_KEY_POLICIES = 5,
+};
+
+enum dpe_certify_key_output_labels_t {
+    DPE_CERTIFY_KEY_CERTIFICATE_CHAIN = 1,
+    DPE_CERTIFY_KEY_DERIVED_PUBLIC_KEY = 2,
+    DPE_CERTIFY_KEY_NEW_CONTEXT_HANDLE = 3,
+};
+
+/**
+ * \brief Dispatch a call to the DPE service with a CBOR-encoded DPE command.
+ *
+ * \param[in]     cmd_input        Pointer to buffer containing the input
+ *                                 CBOR-encoded DPE command.
+ * \param[in]     cmd_input_size   Size of the input command, in bytes.
+ * \param[out]    cmd_output       Pointer to buffer to write the CBOR-encoded
+ *                                 DPE command output.
+ * \param[in,out] cmd_output_size  On input, size of the command output buffer
+ *                                 in bytes. On successful return, size of the
+ *                                 response written to the buffer.
+ *
+ * \note The cmd_input and cmd_output memory areas may overlap.
+ *
+ * \return Returns 0 if call succeeded and cmd_output contains a valid response
+ *         and returns less than 0 otherwise.
+ */
+int32_t dpe_client_call(const char *cmd_input, size_t cmd_input_size,
+                        char *cmd_output, size_t *cmd_output_size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DPE_CLIENT_H__ */
diff --git a/partitions/dice_protection_environment/interface/include/ext/dice/dice.h b/partitions/dice_protection_environment/interface/include/ext/dice/dice.h
new file mode 100644
index 0000000..23e8690
--- /dev/null
+++ b/partitions/dice_protection_environment/interface/include/ext/dice/dice.h
@@ -0,0 +1,103 @@
+// Copyright 2020 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.
+
+#ifndef DICE_DICE_H_
+#define DICE_DICE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DICE_CDI_SIZE 32
+#define DICE_HASH_SIZE 64
+#define DICE_HIDDEN_SIZE 64
+#define DICE_INLINE_CONFIG_SIZE 64
+#define DICE_PRIVATE_KEY_SEED_SIZE 32
+#define DICE_ID_SIZE 20
+
+typedef enum {
+  kDiceResultOk,
+  kDiceResultInvalidInput,
+  kDiceResultBufferTooSmall,
+  kDiceResultPlatformError,
+} DiceResult;
+
+typedef enum {
+  kDiceModeNotInitialized,
+  kDiceModeNormal,
+  kDiceModeDebug,
+  kDiceModeMaintenance,
+} DiceMode;
+
+typedef enum {
+  kDiceConfigTypeInline,
+  kDiceConfigTypeDescriptor,
+} DiceConfigType;
+
+// Contains a full set of input values describing the target program or system.
+// See the Open Profile for DICE specification for a detailed explanation of
+// these inputs.
+//
+// Fields:
+//    code_hash: A hash or similar representation of the target code.
+//    code_descriptor: An optional descriptor to be included in the certificate.
+//        This descriptor is opaque to the DICE flow and is included verbatim
+//        in the certificate with no validation. May be null.
+//    code_descriptor_size: The size in bytes of |code_descriptor|.
+//    config_type: Indicates how to interpret the remaining config-related
+//        fields. If the type is 'inline', then the 64 byte configuration input
+//        value must be provided in |config_value| and |config_descriptor| is
+//        ignored. If the type is 'descriptor', then |config_descriptor| is
+//        hashed to get the configuration input value and |config_value| is
+//        ignored.
+//    config_value: A 64-byte configuration input value when |config_type| is
+//        kDiceConfigTypeInline. Otherwise, this field is ignored.
+//    config_descriptor: A descriptor to be hashed for the configuration input
+//        value when |config_type| is kDiceConfigTypeDescriptor. Otherwise,
+//        this field is ignored and may be null.
+//    config_descriptor_size: The size in bytes of |config_descriptor|.
+//    authority_hash: A hash or similar representation of the authority used to
+//        verify the target code. If the code is not verified or the authority
+//        is implicit, for example hard coded as part of the code currently
+//        executing, then this value should be set to all zero bytes.
+//    authority_descriptor: An optional descriptor to be included in the
+//        certificate. This descriptor is opaque to the DICE flow and is
+//        included verbatim in the certificate with no validation. May be null.
+//    authority_descriptor_size: The size in bytes of |authority_descriptor|.
+//    mode: The current operating mode.
+//    hidden: Additional input which will not appear in certificates. If this is
+//        not used it should be set to all zero bytes.
+typedef struct DiceInputValues_ {
+  uint8_t code_hash[DICE_HASH_SIZE];
+  const uint8_t* code_descriptor;
+  size_t code_descriptor_size;
+  DiceConfigType config_type;
+  uint8_t config_value[DICE_INLINE_CONFIG_SIZE];
+  const uint8_t* config_descriptor;
+  size_t config_descriptor_size;
+  uint8_t authority_hash[DICE_HASH_SIZE];
+  const uint8_t* authority_descriptor;
+  size_t authority_descriptor_size;
+  DiceMode mode;
+  uint8_t hidden[DICE_HIDDEN_SIZE];
+} DiceInputValues;
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // DICE_DICE_H_
diff --git a/partitions/dice_protection_environment/interface/src/dpe_client.c b/partitions/dice_protection_environment/interface/src/dpe_client.c
new file mode 100644
index 0000000..de6e9d2
--- /dev/null
+++ b/partitions/dice_protection_environment/interface/src/dpe_client.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "dpe_client.h"
+
+#include "psa/client.h"
+#include "psa_manifest/sid.h"
+
+int32_t dpe_client_call(const char *cmd_input, size_t cmd_input_size,
+                        char *cmd_output, size_t *cmd_output_size)
+{
+    int32_t err;
+
+    psa_invec in_vec[] = {
+        { cmd_input, cmd_input_size },
+    };
+    psa_outvec out_vec[] = {
+        { cmd_output, *cmd_output_size },
+    };
+
+    err = psa_call(TFM_DPE_SERVICE_HANDLE, 0, in_vec, IOVEC_LEN(in_vec),
+                   out_vec, IOVEC_LEN(out_vec));
+
+    *cmd_output_size = out_vec[0].len;
+
+    return err;
+}
diff --git a/partitions/dice_protection_environment/interface/src/dpe_cmd_encode.c b/partitions/dice_protection_environment/interface/src/dpe_cmd_encode.c
new file mode 100644
index 0000000..35c7631
--- /dev/null
+++ b/partitions/dice_protection_environment/interface/src/dpe_cmd_encode.c
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "dice_protection_environment.h"
+
+#include "dpe_client.h"
+#include "qcbor/qcbor_encode.h"
+#include "qcbor/qcbor_decode.h"
+#include "qcbor/qcbor_spiffy_decode.h"
+
+struct derive_child_input_t {
+    int context_handle;
+    bool retain_parent_context;
+    bool allow_child_to_derive;
+    bool create_certificate;
+    const DiceInputValues *dice_inputs;
+};
+
+struct derive_child_output_t {
+    int new_child_context_handle;
+    int new_parent_context_handle;
+};
+
+struct certify_key_input_t {
+    int context_handle;
+    bool retain_context;
+    const uint8_t *public_key;
+    size_t public_key_size;
+    const uint8_t *label;
+    size_t label_size;
+};
+
+struct certify_key_output_t {
+    const uint8_t *certificate_chain;
+    size_t certificate_chain_size;
+    const uint8_t *derived_public_key;
+    size_t derived_public_key_size;
+    int new_context_handle;
+};
+
+static void encode_dice_inputs(QCBOREncodeContext *encode_ctx,
+                               const DiceInputValues *input)
+{
+    /* Wrap the DICE inputs into a byte string */
+    QCBOREncode_BstrWrapInMapN(encode_ctx, DPE_DERIVE_CHILD_INPUT_DATA);
+
+    /* Inside the byte string the DICE inputs are encoded as a map */
+    QCBOREncode_OpenMap(encode_ctx);
+
+    QCBOREncode_AddBytesToMapN(encode_ctx, DICE_CODE_HASH,
+                               (UsefulBufC){ input->code_hash,
+                                             sizeof(input->code_hash) });
+
+    QCBOREncode_AddBytesToMapN(encode_ctx, DICE_CODE_DESCRIPTOR,
+                              (UsefulBufC){ input->code_descriptor,
+                                            input->code_descriptor_size });
+
+    QCBOREncode_AddInt64ToMapN(encode_ctx, DICE_CONFIG_TYPE,
+                               input->config_type);
+
+    if (input->config_type == kDiceConfigTypeInline) {
+        QCBOREncode_AddBytesToMapN(encode_ctx, DICE_CONFIG_VALUE,
+                                   (UsefulBufC){ input->config_value,
+                                                 sizeof(input->config_value) });
+    } else {
+        QCBOREncode_AddBytesToMapN(encode_ctx, DICE_CONFIG_DESCRIPTOR,
+                                   (UsefulBufC){ input->config_descriptor,
+                                                 input->config_descriptor_size });
+    }
+
+    QCBOREncode_AddBytesToMapN(encode_ctx, DICE_AUTHORITY_HASH,
+                               (UsefulBufC){ input->authority_hash,
+                                             sizeof(input->authority_hash) });
+
+    QCBOREncode_AddBytesToMapN(encode_ctx, DICE_AUTHORITY_DESCRIPTOR,
+                               (UsefulBufC){ input->authority_descriptor,
+                                             input->authority_descriptor_size });
+
+    QCBOREncode_AddInt64ToMapN(encode_ctx, DICE_MODE, input->mode);
+
+    QCBOREncode_AddBytesToMapN(encode_ctx, DICE_HIDDEN,
+                               (UsefulBufC){ input->hidden,
+                                             sizeof(input->hidden) });
+
+    QCBOREncode_CloseMap(encode_ctx);
+    QCBOREncode_CloseBstrWrap2(encode_ctx, true, NULL);
+}
+
+static QCBORError encode_derive_child(const struct derive_child_input_t *args,
+                                      UsefulBuf buf,
+                                      UsefulBufC *encoded_buf)
+{
+    QCBOREncodeContext encode_ctx;
+
+    QCBOREncode_Init(&encode_ctx, buf);
+
+    QCBOREncode_OpenArray(&encode_ctx);
+    QCBOREncode_AddUInt64(&encode_ctx, DPE_DERIVE_CHILD);
+
+    /* Encode DeriveChild command */
+    QCBOREncode_OpenMap(&encode_ctx);
+    QCBOREncode_AddBytesToMapN(&encode_ctx, DPE_DERIVE_CHILD_CONTEXT_HANDLE,
+                               (UsefulBufC){ &args->context_handle,
+                                             sizeof(args->context_handle) });
+    QCBOREncode_AddBoolToMapN(&encode_ctx, DPE_DERIVE_CHILD_RETAIN_PARENT_CONTEXT,
+                              args->retain_parent_context);
+    QCBOREncode_AddBoolToMapN(&encode_ctx, DPE_DERIVE_CHILD_ALLOW_CHILD_TO_DERIVE,
+                              args->allow_child_to_derive);
+    QCBOREncode_AddBoolToMapN(&encode_ctx, DPE_DERIVE_CHILD_CREATE_CERTIFICATE,
+                              args->create_certificate);
+    encode_dice_inputs(&encode_ctx, args->dice_inputs);
+    QCBOREncode_CloseMap(&encode_ctx);
+
+    QCBOREncode_CloseArray(&encode_ctx);
+
+    return QCBOREncode_Finish(&encode_ctx, encoded_buf);
+}
+
+static QCBORError decode_derive_child_response(UsefulBufC encoded_buf,
+                                               struct derive_child_output_t *args,
+                                               dpe_error_t *dpe_err)
+{
+    QCBORDecodeContext decode_ctx;
+    UsefulBufC out;
+    int64_t response_dpe_err;
+
+    QCBORDecode_Init(&decode_ctx, encoded_buf, QCBOR_DECODE_MODE_NORMAL);
+
+    QCBORDecode_EnterArray(&decode_ctx, NULL);
+
+    /* Get the error code from the response */
+    QCBORDecode_GetInt64(&decode_ctx, &response_dpe_err);
+    *dpe_err = (dpe_error_t)response_dpe_err;
+
+    /* Decode DeriveChild response if successful */
+    if (*dpe_err == DPE_NO_ERROR) {
+        QCBORDecode_EnterMap(&decode_ctx, NULL);
+
+        QCBORDecode_GetByteStringInMapN(&decode_ctx,
+                                        DPE_DERIVE_CHILD_NEW_CONTEXT_HANDLE,
+                                        &out);
+        if (out.len != sizeof(args->new_child_context_handle)) {
+            return QCBORDecode_Finish(&decode_ctx);
+        }
+        memcpy(&args->new_child_context_handle, out.ptr, out.len);
+
+        QCBORDecode_GetByteStringInMapN(&decode_ctx,
+                                        DPE_DERIVE_CHILD_PARENT_CONTEXT_HANDLE,
+                                        &out);
+        if (out.len != sizeof(args->new_parent_context_handle)) {
+            return QCBORDecode_Finish(&decode_ctx);
+        }
+        memcpy(&args->new_parent_context_handle, out.ptr, out.len);
+
+        QCBORDecode_ExitMap(&decode_ctx);
+    }
+
+    QCBORDecode_ExitArray(&decode_ctx);
+
+    return QCBORDecode_Finish(&decode_ctx);
+}
+
+static QCBORError encode_certify_key(const struct certify_key_input_t *args,
+                                     UsefulBuf buf,
+                                     UsefulBufC *encoded_buf)
+{
+    QCBOREncodeContext encode_ctx;
+
+    QCBOREncode_Init(&encode_ctx, buf);
+
+    QCBOREncode_OpenArray(&encode_ctx);
+    QCBOREncode_AddUInt64(&encode_ctx, DPE_CERTIFY_KEY);
+
+    /* Encode CertifyKey command */
+    QCBOREncode_OpenMap(&encode_ctx);
+    QCBOREncode_AddBytesToMapN(&encode_ctx, DPE_CERTIFY_KEY_CONTEXT_HANDLE,
+                               (UsefulBufC){ &args->context_handle,
+                                             sizeof(args->context_handle) });
+    QCBOREncode_AddBoolToMapN(&encode_ctx, DPE_CERTIFY_KEY_RETAIN_CONTEXT,
+                              args->retain_context);
+    QCBOREncode_AddBytesToMapN(&encode_ctx, DPE_CERTIFY_KEY_PUBLIC_KEY,
+                               (UsefulBufC){ args->public_key,
+                                             args->public_key_size });
+    QCBOREncode_AddBytesToMapN(&encode_ctx, DPE_CERTIFY_KEY_LABEL,
+                               (UsefulBufC){ args->label, args->label_size} );
+    QCBOREncode_CloseMap(&encode_ctx);
+
+    QCBOREncode_CloseArray(&encode_ctx);
+
+    return QCBOREncode_Finish(&encode_ctx, encoded_buf);
+}
+
+static QCBORError decode_certify_key_response(UsefulBufC encoded_buf,
+                                              struct certify_key_output_t *args,
+                                              dpe_error_t *dpe_err)
+{
+    QCBORDecodeContext decode_ctx;
+    UsefulBufC out;
+    int64_t response_dpe_err;
+
+    QCBORDecode_Init(&decode_ctx, encoded_buf, QCBOR_DECODE_MODE_NORMAL);
+
+    QCBORDecode_EnterArray(&decode_ctx, NULL);
+
+    /* Get the error code from the response */
+    QCBORDecode_GetInt64(&decode_ctx, &response_dpe_err);
+    *dpe_err = (dpe_error_t)response_dpe_err;
+
+    /* Decode CertifyKey response if successful */
+    if (*dpe_err == DPE_NO_ERROR) {
+        QCBORDecode_EnterMap(&decode_ctx, NULL);
+
+        QCBORDecode_GetByteStringInMapN(&decode_ctx,
+                                        DPE_CERTIFY_KEY_CERTIFICATE_CHAIN,
+                                        &out);
+        args->certificate_chain = out.ptr;
+        args->certificate_chain_size = out.len;
+
+        QCBORDecode_GetByteStringInMapN(&decode_ctx,
+                                        DPE_CERTIFY_KEY_DERIVED_PUBLIC_KEY,
+                                        &out);
+        args->derived_public_key = out.ptr;
+        args->derived_public_key_size = out.len;
+
+        QCBORDecode_GetByteStringInMapN(&decode_ctx,
+                                        DPE_CERTIFY_KEY_NEW_CONTEXT_HANDLE,
+                                        &out);
+        if (out.len != sizeof(args->new_context_handle)) {
+            return QCBORDecode_Finish(&decode_ctx);
+        }
+        memcpy(&args->new_context_handle, out.ptr, out.len);
+
+        QCBORDecode_ExitMap(&decode_ctx);
+    }
+
+    QCBORDecode_ExitArray(&decode_ctx);
+
+    return QCBORDecode_Finish(&decode_ctx);
+}
+
+dpe_error_t dpe_derive_child(int context_handle,
+                             bool retain_parent_context,
+                             bool allow_child_to_derive,
+                             bool create_certificate,
+                             const DiceInputValues *dice_inputs,
+                             int *new_child_context_handle,
+                             int *new_parent_context_handle)
+{
+    int32_t service_err;
+    dpe_error_t dpe_err;
+    QCBORError qcbor_err;
+    UsefulBufC encoded_buf;
+    UsefulBuf_MAKE_STACK_UB(cmd_buf, 512);
+
+    const struct derive_child_input_t in_args = {
+        context_handle,
+        retain_parent_context,
+        allow_child_to_derive,
+        create_certificate,
+        dice_inputs,
+    };
+    struct derive_child_output_t out_args;
+
+    qcbor_err = encode_derive_child(&in_args, cmd_buf, &encoded_buf);
+    if (qcbor_err != QCBOR_SUCCESS) {
+        return DPE_INTERNAL_ERROR;
+    }
+
+    service_err = dpe_client_call(encoded_buf.ptr, encoded_buf.len,
+                                  cmd_buf.ptr, &cmd_buf.len);
+    if (service_err != 0) {
+        return DPE_INTERNAL_ERROR;
+    }
+
+    qcbor_err = decode_derive_child_response(UsefulBuf_Const(cmd_buf),
+                                             &out_args, &dpe_err);
+    if (qcbor_err != QCBOR_SUCCESS) {
+        return DPE_INTERNAL_ERROR;
+    } else if (dpe_err != DPE_NO_ERROR) {
+        return dpe_err;
+    }
+
+    /* Copy returned values into caller's memory */
+    *new_child_context_handle = out_args.new_child_context_handle;
+    *new_parent_context_handle = out_args.new_parent_context_handle;
+
+    return DPE_NO_ERROR;
+}
+
+dpe_error_t dpe_certify_key(int context_handle,
+                            bool retain_context,
+                            const uint8_t *public_key,
+                            size_t public_key_size,
+                            const uint8_t *label,
+                            size_t label_size,
+                            uint8_t *certificate_chain_buf,
+                            size_t certificate_chain_buf_size,
+                            size_t *certificate_chain_actual_size,
+                            uint8_t *derived_public_key_buf,
+                            size_t derived_public_key_buf_size,
+                            size_t *derived_public_key_actual_size,
+                            int *new_context_handle)
+{
+    int32_t service_err;
+    dpe_error_t dpe_err;
+    QCBORError qcbor_err;
+    UsefulBufC encoded_buf;
+    UsefulBuf_MAKE_STACK_UB(cmd_buf, 1024);
+
+    const struct certify_key_input_t in_args = {
+        context_handle,
+        retain_context,
+        public_key,
+        public_key_size,
+        label,
+        label_size,
+    };
+    struct certify_key_output_t out_args;
+
+    qcbor_err = encode_certify_key(&in_args, cmd_buf, &encoded_buf);
+    if (qcbor_err != QCBOR_SUCCESS) {
+        return DPE_INTERNAL_ERROR;
+    }
+
+    service_err = dpe_client_call(encoded_buf.ptr, encoded_buf.len,
+                                  cmd_buf.ptr, &cmd_buf.len);
+    if (service_err != 0) {
+        return DPE_INTERNAL_ERROR;
+    }
+
+    qcbor_err = decode_certify_key_response(UsefulBuf_Const(cmd_buf),
+                                            &out_args, &dpe_err);
+    if (qcbor_err != QCBOR_SUCCESS) {
+        return DPE_INTERNAL_ERROR;
+    } else if (dpe_err != DPE_NO_ERROR) {
+        return dpe_err;
+    }
+
+    /* Copy returned values into caller's memory */
+    if (out_args.certificate_chain_size > certificate_chain_buf_size) {
+        return DPE_INVALID_ARGUMENT;
+    }
+    memcpy(certificate_chain_buf, out_args.certificate_chain,
+           out_args.certificate_chain_size);
+    *certificate_chain_actual_size = out_args.certificate_chain_size;
+
+    if (out_args.derived_public_key_size > derived_public_key_buf_size) {
+        return DPE_INVALID_ARGUMENT;
+    }
+    memcpy(derived_public_key_buf, out_args.derived_public_key,
+           out_args.derived_public_key_size);
+    *derived_public_key_actual_size = out_args.derived_public_key_size;
+
+    *new_context_handle = out_args.new_context_handle;
+
+    return DPE_NO_ERROR;
+}
diff --git a/partitions/dice_protection_environment/tfm_dpe.yaml b/partitions/dice_protection_environment/tfm_dpe.yaml
new file mode 100644
index 0000000..228b4e7
--- /dev/null
+++ b/partitions/dice_protection_environment/tfm_dpe.yaml
@@ -0,0 +1,30 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2023, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+  "psa_framework_version": 1.1,
+  "name": "TFM_SP_DPE",
+  "type": "APPLICATION-ROT",
+  "priority": "NORMAL",
+  "model": "SFN",
+  "entry_init": "tfm_dpe_init",
+  "stack_size": "0x1400",
+  "services" : [
+    {
+      "name": "TFM_DPE_SERVICE",
+      "sid": "0x00000120",
+      "non_secure_clients": true,
+      "connection_based": false,
+      "stateless_handle": 19,
+      "version": 1,
+      "version_policy": "STRICT",
+    }
+  ],
+  "dependencies": [
+    "TFM_CRYPTO",
+  ]
+}