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/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;
+}