DPE: Create intermediate certificate for the layer
Signed-off-by: Maulik Patel <maulik.patel@arm.com>
Change-Id: Ib7df067daa3674f42bbff3b0296b7df192936caa
diff --git a/partitions/dice_protection_environment/dpe_certificate.c b/partitions/dice_protection_environment/dpe_certificate.c
new file mode 100644
index 0000000..d66d599
--- /dev/null
+++ b/partitions/dice_protection_environment/dpe_certificate.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <assert.h>
+#include "dpe_certificate.h"
+#include "dpe_context_mngr.h"
+#include "dpe_crypto_config.h"
+#include "dpe_crypto_interface.h"
+#include "qcbor/qcbor_encode.h"
+#include "t_cose_common.h"
+#include "t_cose_sign1_sign.h"
+
+#define ID_HEX_SIZE (2 * DICE_ID_SIZE) /* Size of CDI encoded to ascii hex */
+
+struct dpe_cert_encode_ctx {
+ QCBOREncodeContext cbor_enc_ctx;
+ struct t_cose_sign1_sign_ctx signer_ctx;
+};
+
+static void convert_to_ascii_hex(const uint8_t *in,
+ size_t in_size,
+ char *out,
+ size_t out_size)
+{
+ const char hex_table[] = "0123456789abcdef";
+ size_t in_pos = 0;
+ size_t out_pos = 0;
+
+ for (in_pos = 0; in_pos < in_size && out_pos < out_size; in_pos++) {
+ out[out_pos++] = hex_table[(in[in_pos] & 0xF0 >> 4)];
+ out[out_pos++] = hex_table[in[in_pos] & 0x0F];
+ }
+}
+
+static dpe_error_t t_cose_err_to_dpe_err(enum t_cose_err_t err)
+{
+ switch(err) {
+
+ case T_COSE_SUCCESS:
+ return DPE_NO_ERROR;
+
+ case T_COSE_ERR_TOO_SMALL:
+ return DPE_INSUFFICIENT_MEMORY;
+
+ default:
+ /* A lot of the errors are not mapped because they are
+ * primarily internal errors that should never happen. They
+ * end up here.
+ */
+ return DPE_INTERNAL_ERROR;
+ }
+}
+
+static dpe_error_t certificate_encode_start(struct dpe_cert_encode_ctx *me,
+ const UsefulBuf out_buf,
+ psa_key_handle_t private_key)
+{
+ enum t_cose_err_t t_cose_err;
+ struct t_cose_key attest_key;
+ UsefulBufC attest_key_id = {NULL, 0};
+
+ /* DPE Certificate is untagged COSE_Sign1 message */
+ t_cose_sign1_sign_init(&(me->signer_ctx), T_COSE_OPT_OMIT_CBOR_TAG, DPE_T_COSE_ALG);
+
+ attest_key.crypto_lib = T_COSE_CRYPTO_LIB_PSA;
+ attest_key.k.key_handle = private_key;
+
+ t_cose_sign1_set_signing_key(&(me->signer_ctx),
+ attest_key,
+ attest_key_id);
+
+ /* Spin up the CBOR encoder */
+ QCBOREncode_Init(&(me->cbor_enc_ctx), out_buf);
+
+ /* This will cause the cose headers to be encoded and written into
+ * out_buf using me->cbor_enc_ctx
+ */
+ t_cose_err = t_cose_sign1_encode_parameters(&(me->signer_ctx),
+ &(me->cbor_enc_ctx));
+ if (t_cose_err) {
+ return t_cose_err_to_dpe_err(t_cose_err);
+ }
+
+ QCBOREncode_OpenMap(&(me->cbor_enc_ctx));
+
+ return DPE_NO_ERROR;
+}
+
+static void add_key_usage_claim(struct dpe_cert_encode_ctx *me)
+{
+ uint8_t key_usage = DPE_CERT_KEY_USAGE_CERT_SIGN;
+
+ /* Encode key usage as byte string */
+ QCBOREncode_AddBytesToMapN(&me->cbor_enc_ctx,
+ DPE_CERT_LABEL_KEY_USAGE,
+ (UsefulBufC){ &key_usage,
+ sizeof(key_usage) });
+}
+
+static void add_subject_claim(struct dpe_cert_encode_ctx *me,
+ struct layer_context_t *layer_ctx)
+{
+ char cdi_id_hex[ID_HEX_SIZE];
+
+ convert_to_ascii_hex(&layer_ctx->data.cdi_id[0],
+ sizeof(layer_ctx->data.cdi_id),
+ &cdi_id_hex[0],
+ sizeof(cdi_id_hex));
+ /* Encode subject as text string */
+ QCBOREncode_AddTextToMapN(&me->cbor_enc_ctx,
+ DPE_CERT_LABEL_SUBJECT,
+ (UsefulBufC){ &cdi_id_hex[0],
+ sizeof(cdi_id_hex) });
+}
+
+static void add_issuer_claim(struct dpe_cert_encode_ctx *me,
+ const struct layer_context_t *parent_layer_ctx)
+{
+
+ char cdi_id_hex[ID_HEX_SIZE];
+
+ convert_to_ascii_hex(&parent_layer_ctx->data.cdi_id[0],
+ sizeof(parent_layer_ctx->data.cdi_id),
+ &cdi_id_hex[0],
+ sizeof(cdi_id_hex));
+
+ /* Encode issuer as text string */
+ QCBOREncode_AddTextToMapN(&me->cbor_enc_ctx,
+ DPE_CERT_LABEL_ISSUER,
+ (UsefulBufC){ &cdi_id_hex[0],
+ sizeof(cdi_id_hex) });
+}
+
+static void add_public_key_claim(struct dpe_cert_encode_ctx *me,
+ const struct layer_context_t *layer_ctx)
+{
+ /* As per RFC8152 */
+ const int64_t cose_key_type_value = DPE_T_COSE_KEY_TYPE_VAL;
+ const int64_t cose_key_ops_value = DPE_T_COSE_KEY_OPS_VAL;
+ const int64_t cose_key_ec2_curve_value = DPE_T_COSE_KEY_EC2_CURVE_VAL;
+ const int64_t cose_key_alg_value = DPE_T_COSE_KEY_ALG_VAL;
+ size_t pub_key_size = layer_ctx->data.attest_pub_key_len;
+ UsefulBufC wrapped;
+
+ /* Cose key is encoded as a map wrapped into a byte string */
+ QCBOREncode_BstrWrapInMapN(&me->cbor_enc_ctx, DPE_CERT_LABEL_SUBJECT_PUBLIC_KEY);
+ QCBOREncode_OpenMap(&me->cbor_enc_ctx);
+
+ /* Add the key type as int */
+ QCBOREncode_AddInt64ToMapN(&me->cbor_enc_ctx,
+ DPE_CERT_LABEL_COSE_KEY_TYPE,
+ cose_key_type_value);
+
+ /* Add the algorithm as int */
+ QCBOREncode_AddInt64ToMapN(&me->cbor_enc_ctx,
+ DPE_CERT_LABEL_COSE_KEY_ALG,
+ cose_key_alg_value);
+
+ /* Add the key operation as [+ (tstr/int)] */
+ QCBOREncode_OpenArrayInMapN(&me->cbor_enc_ctx, DPE_CERT_LABEL_COSE_KEY_OPS);
+ QCBOREncode_AddInt64(&me->cbor_enc_ctx,
+ cose_key_ops_value);
+ QCBOREncode_CloseArray(&me->cbor_enc_ctx);
+
+ /* Add the curve */
+ QCBOREncode_AddInt64ToMapN(&me->cbor_enc_ctx,
+ DPE_CERT_LABEL_COSE_KEY_EC2_CURVE,
+ cose_key_ec2_curve_value);
+
+ /* Add the subject public key x and y coordinates */
+ QCBOREncode_AddBytesToMapN(&me->cbor_enc_ctx,
+ DPE_CERT_LABEL_COSE_KEY_EC2_X,
+ (UsefulBufC){ &layer_ctx->data.attest_pub_key[0],
+ pub_key_size/2 });
+
+ QCBOREncode_AddBytesToMapN(&me->cbor_enc_ctx,
+ DPE_CERT_LABEL_COSE_KEY_EC2_Y,
+ (UsefulBufC){ &layer_ctx->data.attest_pub_key[pub_key_size/2],
+ pub_key_size/2 });
+
+ QCBOREncode_CloseMap(&me->cbor_enc_ctx);
+ QCBOREncode_CloseBstrWrap2(&me->cbor_enc_ctx, true, &wrapped);
+
+ assert(wrapped.len <= DICE_MAX_ENCODED_PUBLIC_KEY_SIZE);
+}
+
+static dpe_error_t certificate_encode_finish(struct dpe_cert_encode_ctx *me,
+ UsefulBufC *completed_cert)
+{
+ QCBORError qcbor_result;
+ enum t_cose_err_t cose_return_value;
+
+ QCBOREncode_CloseMap(&(me->cbor_enc_ctx));
+
+ /* -- Finish up the COSE_Sign1. This is where the signing happens -- */
+ cose_return_value = t_cose_sign1_encode_signature(&(me->signer_ctx),
+ &(me->cbor_enc_ctx));
+ if (cose_return_value) {
+ /* Main errors are invoking the hash or signature */
+ return t_cose_err_to_dpe_err(cose_return_value);
+ }
+
+ /* Finally close off the CBOR formatting and get the pointer and length
+ * of the resulting COSE_Sign1
+ */
+ qcbor_result = QCBOREncode_Finish(&(me->cbor_enc_ctx), completed_cert);
+ if (qcbor_result == QCBOR_ERR_BUFFER_TOO_SMALL) {
+ return DPE_INSUFFICIENT_MEMORY;
+
+ } else if (qcbor_result != QCBOR_SUCCESS) {
+ /* likely from array not closed, too many closes, ... */
+ return DPE_INTERNAL_ERROR;
+
+ } else {
+ return DPE_NO_ERROR;
+ }
+}
+
+static void encode_sw_component_measurements(QCBOREncodeContext *encode_ctx,
+ struct component_context_t *component_ctx)
+{
+ QCBOREncode_OpenMap(encode_ctx);
+
+ /* Encode measurement value as byte string */
+ QCBOREncode_AddBytesToMapN(encode_ctx,
+ DPE_CERT_LABEL_CODE_HASH,
+ (UsefulBufC){ &component_ctx->data.measurement_value,
+ DICE_HASH_SIZE });
+
+ /* Encode measurement descriptor version as byte string */
+ QCBOREncode_AddBytesToMapN(encode_ctx,
+ DPE_CERT_LABEL_CODE_DESCRIPTOR,
+ (UsefulBufC){ &component_ctx->data.measurement_descriptor,
+ component_ctx->data.measurement_descriptor_size });
+
+ /* Encode signer ID Hash as byte string */
+ QCBOREncode_AddBytesToMapN(encode_ctx,
+ DPE_CERT_LABEL_AUTHORITY_HASH,
+ (UsefulBufC){ &component_ctx->data.signer_id,
+ DICE_HASH_SIZE });
+
+ /* Encode signer ID descriptor as byte string */
+ QCBOREncode_AddBytesToMapN(encode_ctx,
+ DPE_CERT_LABEL_AUTHORITY_DESCRIPTOR,
+ (UsefulBufC){ &component_ctx->data.signer_id_descriptor,
+ component_ctx->data.signer_id_descriptor_size });
+
+ if (component_ctx->data.config_descriptor_size > 0) {
+ /* Encode config descriptor as byte string */
+ QCBOREncode_AddBytesToMapN(encode_ctx,
+ DPE_CERT_LABEL_CONFIGURATION_DESCRIPTOR,
+ (UsefulBufC){ &component_ctx->data.config_descriptor,
+ component_ctx->data.config_descriptor_size });
+ /* Encode config value as byte string */
+ QCBOREncode_AddBytesToMapN(encode_ctx,
+ DPE_CERT_LABEL_CONFIGURATION_HASH,
+ (UsefulBufC){ &component_ctx->data.config_value,
+ DICE_INLINE_CONFIG_SIZE });
+ } else {
+ /* Encode config value as byte string */
+ QCBOREncode_AddBytesToMapN(encode_ctx,
+ DPE_CERT_LABEL_CONFIGURATION_DESCRIPTOR,
+ (UsefulBufC){ &component_ctx->data.config_value,
+ DICE_INLINE_CONFIG_SIZE });
+ }
+
+ /* Encode mode value as byte string */
+ QCBOREncode_AddBytesToMapN(encode_ctx,
+ DPE_CERT_LABEL_MODE,
+ (UsefulBufC){ &component_ctx->data.mode,
+ sizeof(DiceMode) });
+
+ QCBOREncode_CloseMap(encode_ctx);
+}
+
+static void encode_layer_sw_components_array(uint16_t layer_idx,
+ struct dpe_cert_encode_ctx *me)
+{
+ int i, cnt;
+ struct component_context_t *component_ctx;
+
+ for (i = 0, cnt = 0; i < MAX_NUM_OF_COMPONENTS; i++) {
+ component_ctx = get_component_if_linked_to_layer(layer_idx, i);
+ if (component_ctx != NULL) {
+ /* This component belongs to current layer */
+ cnt++;
+
+ if (cnt == 1) {
+ /* Open array which stores SW components claims. */
+ QCBOREncode_OpenArrayInMapN(&me->cbor_enc_ctx,
+ DPE_CERT_LABEL_SW_COMPONENTS);
+ }
+ encode_sw_component_measurements(&me->cbor_enc_ctx, component_ctx);
+ }
+ }
+
+ if (cnt != 0) {
+ /* Close array which stores SW components claims. */
+ QCBOREncode_CloseArray(&me->cbor_enc_ctx);
+ }
+}
+
+
+dpe_error_t encode_layer_certificate(uint16_t layer_idx,
+ struct layer_context_t *layer_ctx,
+ const struct layer_context_t *parent_layer_ctx)
+{
+ dpe_error_t err;
+ struct dpe_cert_encode_ctx dpe_cert_ctx;
+ UsefulBuf cert;
+ UsefulBufC completed_cert;
+
+ psa_key_id_t attest_key_id = parent_layer_ctx->data.attest_key_id;
+
+ /* Get started creating the certificate/token. This sets up the CBOR and
+ * COSE contexts which causes the COSE headers to be constructed.
+ */
+ cert.ptr = &layer_ctx->data.cert_buf[0];
+ cert.len = sizeof(layer_ctx->data.cert_buf);
+
+ err = certificate_encode_start(&dpe_cert_ctx,
+ cert,
+ attest_key_id);
+ if (err != DPE_NO_ERROR) {
+ return err;
+ }
+
+ /* Add all the required claims */
+ /* Add issuer/authority claim */
+ add_issuer_claim(&dpe_cert_ctx, parent_layer_ctx);
+
+ /* Add subject claim */
+ add_subject_claim(&dpe_cert_ctx, layer_ctx);
+
+ /* Encode all firmware measurements for the components linked to this layer */
+ //TODO:
+ /* It is not yet defined in the open-dice profile how to represent
+ * multiple SW components in a single certificate; In current implementation,
+ * an array is created for all the components' measurements and within the
+ * array, there are multiple maps, one for each SW component
+ */
+ encode_layer_sw_components_array(layer_idx, &dpe_cert_ctx);
+
+ /* Add public key claim */
+ add_public_key_claim(&dpe_cert_ctx, layer_ctx);
+
+ /* Add key usage claim */
+ add_key_usage_claim(&dpe_cert_ctx);
+
+ /* Finish up creating the token. This is where the actual signature
+ * is generated. This finishes up the CBOR encoding too.
+ */
+ err = certificate_encode_finish(&dpe_cert_ctx, &completed_cert);
+ if (err != DPE_NO_ERROR) {
+ return err;
+ }
+
+ /* Update the final size of the token/certificate */
+ layer_ctx->data.cert_buf_len = completed_cert.len;
+
+ return err;
+}
+
+dpe_error_t store_layer_certificate(struct layer_context_t *layer_ctx)
+{
+ //TODO:
+ (void)layer_ctx;
+ return DPE_NO_ERROR;
+}