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",
+ ]
+}