/*
 * Copyright 2024 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "cbor_boot_param.h"
#include "cdi.h"
#include "boot_param.h"
#include "boot_param_platform.h"

/* Common structure to build Sig_structure or DICE Handover structure
 */

/* Combined headers before cert payload (CWT claims) in DICE handover
 */
struct combined_hdr_s {
	struct dice_cert_chain_hdr_s cert_chain;
	struct cdi_cert_hdr_s cert;
};

union header_options_s {
	uint8_t sig_struct[sizeof(struct combined_hdr_s)];
	struct combined_hdr_s dice_handover;
};

/* We need the following to be able to fill CDIs in dice_handover_s::hdr
 * before we temporarily use dice_handover_s::options.sig_struct to calc
 * signature.
 */
_Static_assert(
	sizeof(struct combined_hdr_s) >= sizeof(struct cdi_sig_struct_hdr_s)
);

struct dice_handover_s {
	struct dice_handover_hdr_s hdr;
	union header_options_s options;
	struct cwt_claims_bstr_s payload;
	struct cbor_bstr64_s signature;
};
_Static_assert(
	sizeof(struct dice_handover_s) - sizeof(struct dice_handover_hdr_s) ==
	DICE_CHAIN_SIZE
);

/* BootParam = {
 *   1  : uint,               ; structure version (0)
 *   2  : GSCBootParam,
 *   3  : AndroidDiceHandover,
 * }
 */
#define BOOT_PARAM_VERSION 0
struct boot_param_s {
	/* Map header: 3 entries */
	uint8_t map_hdr;
	/* 1. Version: uint(1, 0bytes) => uint(BOOT_PARAM_VERSION, 0bytes) */
	uint8_t version_label;
	uint8_t version;
	/* 2. GSCBootParam: uint(2, 0bytes) => GSCBootParam */
	uint8_t gsc_boot_param_label;
	struct gsc_boot_param_s gsc_boot_param;
	/* 3. AndroidDiceHandover: uint(3, 0bytes) => AndroidDiceHandover */
	uint8_t dice_handover_label;
	struct dice_handover_s dice_handover;
};
_Static_assert(sizeof(struct boot_param_s) == BOOT_PARAM_SIZE);

/* Context to pass between functions that build the DICE handover structure
 */
struct dice_ctx_s {
	struct boot_param_s output;
	struct dice_config_s cfg;
};

/* PCR0 values for various modes - see go/pcr0-tpm2 */
static const uint8_t kPcr0NormalMode[DIGEST_BYTES] = {
	0x89, 0xEA, 0xF3, 0x51, 0x34, 0xB4, 0xB3, 0xC6,
	0x49, 0xF4, 0x4C, 0x0C, 0x76, 0x5B, 0x96, 0xAE,
	0xAB, 0x8B, 0xB3, 0x4E, 0xE8, 0x3C, 0xC7, 0xA6,
	0x83, 0xC4, 0xE5, 0x3D, 0x15, 0x81, 0xC8, 0xC7
};
static const uint8_t kPcr0RecoveryNormalMode[DIGEST_BYTES] = {
	0x9F, 0x9E, 0xA8, 0x66, 0xD3, 0xF3, 0x4F, 0xE3,
	0xA3, 0x11, 0x2A, 0xE9, 0xCB, 0x1F, 0xBA, 0xBC,
	0x6F, 0xFE, 0x8C, 0xD2, 0x61, 0xD4, 0x24, 0x93,
	0xBC, 0x68, 0x42, 0xA9, 0xE4, 0xF9, 0x3B, 0x3D
};

/* Const salts - see go/gsc-dice */
static const uint8_t kIdSalt[64] = {
	0xDB, 0xDB, 0xAE, 0xBC, 0x80, 0x20, 0xDA, 0x9F,
	0xF0, 0xDD, 0x5A, 0x24, 0xC8, 0x3A, 0xA5, 0xA5,
	0x42, 0x86, 0xDF, 0xC2, 0x63, 0x03, 0x1E, 0x32,
	0x9B, 0x4D, 0xA1, 0x48, 0x43, 0x06, 0x59, 0xFE,
	0x62, 0xCD, 0xB5, 0xB7, 0xE1, 0xE0, 0x0F, 0xC6,
	0x80, 0x30, 0x67, 0x11, 0xEB, 0x44, 0x4A, 0xF7,
	0x72, 0x09, 0x35, 0x94, 0x96, 0xFC, 0xFF, 0x1D,
	0xB9, 0x52, 0x0B, 0xA5, 0x1C, 0x7B, 0x29, 0xEA
};
static const uint8_t kAsymSalt[64] = {
	0x63, 0xB6, 0xA0, 0x4D, 0x2C, 0x07, 0x7F, 0xC1,
	0x0F, 0x63, 0x9F, 0x21, 0xDA, 0x79, 0x38, 0x44,
	0x35, 0x6C, 0xC2, 0xB0, 0xB4, 0x41, 0xB3, 0xA7,
	0x71, 0x24, 0x03, 0x5C, 0x03, 0xF8, 0xE1, 0xBE,
	0x60, 0x35, 0xD3, 0x1F, 0x28, 0x28, 0x21, 0xA7,
	0x45, 0x0A, 0x02, 0x22, 0x2A, 0xB1, 0xB3, 0xCF,
	0xF1, 0x67, 0x9B, 0x05, 0xAB, 0x1C, 0xA5, 0xD1,
	0xAF, 0xFB, 0x78, 0x9C, 0xCD, 0x2B, 0x0B, 0x3B
};
static const uint8_t kSigHdr[2] = CBOR_BSTR64_HDR;

static const struct cwt_claims_bstr_s kCwtClaimsTemplate = {
	CWT_CLAIMS_BSTR_HDR,
	{
		/* Map header: 10 entries */
		CBOR_HDR1(CBOR_MAJOR_MAP, 10),
		/* 1. ISS: uint(1, 0bytes) => tstr(hex(UDS_ID)) */
		CWT_LABEL_ISS,
		CBOR_TSTR40_EMPTY, /* CALC - calc from UDS */
		/* 2. SUB: uint(2, 0bytes) =>
		 *    tstr(hex(CDI_ID))
		 */
		CWT_LABEL_SUB,
		CBOR_TSTR40_EMPTY, /* CALC - calc from CDI */
		/* 3. Code Hash: nint(-4670545, 4bytes) =>
		 *    bstr(32bytes)
		 */
		CWT_LABEL_CODE_HASH,
		CBOR_BSTR32_EMPTY, /* VARIABLE */
		/* 4. Cfg Hash: nint(-4670547, 4bytes) =>
		 *    bstr(32bytes)
		 */
		CWT_LABEL_CFG_HASH,
		CBOR_BSTR32_EMPTY, /* CALC - calc from CfgDescr */
		/* 5. Cfg Descr: nint(-4670548, 4bytes) =>
		 *    bstr(struct cfg_descr_s)
		 */
		CWT_LABEL_CFG_DESCR,
		/* struct cfg_descr_bstr_s */
		{
			CFG_DESCR_BSTR_HDR,
			/* struct cfg_descr_s */
			{
				/* Map header: 6 entries */
				CBOR_HDR1(CBOR_MAJOR_MAP, 6),
				/* 1. Comp name: nint(-70002, 4bytes) =>
				 * tstr("CrOS AP FW")
				 */
				CFG_DESCR_LABEL_COMP_NAME,
				CFG_DESCR_COMP_NAME,
				/* 2. Resettable: nint(-70004, 4bytes) => null
				 */
				CFG_DESCR_LABEL_RESETTABLE,
				CBOR_NULL,
				/* 3. Sec ver: nint(-70005, 4bytes) =>
				 *    uint(Security ver, 4bytes)
				 */
				CFG_DESCR_LABEL_SEC_VER,
				CBOR_UINT32_ZERO, /* VARIABLE */
				/* 4. APROV status:
				 *    nint(-71000, 4bytes) =>
				 *    uint(APROV status, 4bytes)
				 */
				CFG_DESCR_LABEL_APROV_STATUS,
				CBOR_UINT32_ZERO, /* VARIABLE */
				/* 5. Vboot status: */
				/* nint(-71000, 4bytes) => */
				/* bstr(PCR0, 32bytes) */
				CFG_DESCR_LABEL_VBOOT_STATUS,
				CBOR_BSTR32_EMPTY, /* VARIABLE */
				/* 6. AP FW version: */
				/* nint(-71002, 4bytes) => */
				/* bstr(PCR10, 32bytes) */
				CFG_DESCR_LABEL_AP_FW_VERSION,
				CBOR_BSTR32_EMPTY, /* VARIABLE */
			},
		},
		/* 6. Auth Hash: nint(-4670549, 4bytes) => bstr(32bytes) */
		CWT_LABEL_AUTH_HASH,
		CBOR_BSTR32_EMPTY, /* always zero */
		/* 7. Mode: nint(-4670551, 4bytes) => bstr(1byte) */
		CWT_LABEL_MODE,
		CBOR_BSTR1_EMPTY, /* VARIABLE */
		/* 8. Subject PK: nint(-4670552, 4bytes) => bstr(COSE_Key) */
		CWT_LABEL_SUBJECT_PK,
		/* struct cose_key_ecdsa_bstr_s */
		{
			COSE_KEY_ECDSA_BSTR_HDR,
			/* struct cose_key_ecdsa_s */
			{
				/* Map header: 6 entries */
				CBOR_HDR1(CBOR_MAJOR_MAP, 6),
				/* 1. Key type: uint(1, 0bytes) =>
				 * uint(2, 0bytes)
				 */
				COSE_KEY_LABEL_KTY,
				CBOR_UINT0(2), /* EC2 */
				/* 2. Algorithm: uint(3, 0bytes) =>
				 *    nint(-1, 0bytes)
				 */
				COSE_KEY_LABEL_ALG,
				CBOR_NINT0(-7), /* ECDSA w/SHA-256 */
				/* 3. Key ops: uint(4, 0bytes) =>
				 *    array(1) { uint(2, 0bytes) }
				 */
				COSE_KEY_LABEL_KEY_OPS,
				CBOR_HDR1(CBOR_MAJOR_ARR, 1),
				CBOR_UINT0(2),
				/* 4. Curve: nint(-1, 0bytes) => uint(1, 0bytes)
				 */
				COSE_KEY_LABEL_CRV,
				CBOR_UINT0(1), /* P-256 */
				/* 5. X: nint(-2, 0bytes) =>
				 *    bstr(X, 32bytes)
				 */
				COSE_KEY_LABEL_X,
				CBOR_BSTR32_EMPTY, /* VARIABLE */
				/* 6. X: nint(-3, 0bytes) =>
				 *    bstr(Y, 32bytes)
				 */
				COSE_KEY_LABEL_Y,
				CBOR_BSTR32_EMPTY, /* VARIABLE */
			},
		},
		/* 9. Key Usage: nint(-4670553, 4bytes) => bstr(1byte) */
		CWT_LABEL_KEY_USAGE,
		{
			CBOR_HDR1(CBOR_MAJOR_BSTR, 1),
			0x20, /* keyCertSign */
		},
		/* 10. Profile name: nint(-4670554, 4bytes) => */
		/* tstr("android.16") */
		CWT_LABEL_PROFILE_NAME,
		CWT_PROFILE_NAME,
	},
};

static const struct dice_handover_hdr_s kDiceHandoverHdrTemplate = {
	/* Map header: 3 elements */
	CBOR_HDR1(CBOR_MAJOR_MAP, 3),
	/* 1. CDI_Attest: uint(1, 0bytes) => bstr(32bytes) */
	DICE_HANDOVER_LABEL_CDI_ATTEST,
	CBOR_BSTR32_EMPTY, /* CALC - CDI_attest */
	/* 2. CDI_Seal: uint(2, 0bytes) => bstr(32bytes) */
	DICE_HANDOVER_LABEL_CDI_SEAL,
	CBOR_BSTR32_EMPTY, /* CALC - CDI_seal */
	/* 3. DICE chain: uint(3, 0bytes) => DICE cert chain */
	DICE_HANDOVER_LABEL_DICE_CHAIN
	/* DICE cert chain is not included */
};

static const struct cdi_sig_struct_hdr_s kSigStructFixedHdr = {
	/* Array header: 4 elements */
	CBOR_HDR1(CBOR_MAJOR_ARR, 4),
	/* 1. Context: tstr("Signature1") */
	CDI_SIG_STRUCT_CONTEXT,
	/* 2. Body protected: bstr(COSE param) */
	COSE_PARAM_BSTR,
	/* 3. External AAD: Bstr(0 bytes) */
	CBOR_HDR1(CBOR_MAJOR_BSTR, 0),
	/* 4. Payload - not fixed, contained in cdi_sig_struct_t */
};

static const struct combined_hdr_s kCombinedHdrTemplate = {
	/* struct dice_cert_chain_hdr_s cert_chain */
	{
		/* Array header: 2 elements */
		CBOR_HDR1(CBOR_MAJOR_ARR, 2),
		/* 1. UDS pub key: COSE_Key
		 * struct cose_key_ecdsa_s
		 */
		{
			/* Map header: 6 entries */
			CBOR_HDR1(CBOR_MAJOR_MAP, 6),
			/* 1. Key type: uint(1, 0bytes) => uint(2, 0bytes) */
			COSE_KEY_LABEL_KTY,
			CBOR_UINT0(2), /* EC2 */
			/* 2. Algorithm: uint(3, 0bytes) => nint(-1, 0bytes) */
			COSE_KEY_LABEL_ALG,
			CBOR_NINT0(-7), /* ECDSA w/ SHA-256 */
			/* 3. Key ops: uint(4, 0bytes) =>
			 *    array(1) { uint(2, 0bytes) }
			 */
			COSE_KEY_LABEL_KEY_OPS,
			CBOR_HDR1(CBOR_MAJOR_ARR, 1),
			CBOR_UINT0(2),
			/* 4. Curve: nint(-1, 0bytes) => uint(1, 0bytes) */
			COSE_KEY_LABEL_CRV,
			CBOR_UINT0(1), /* P-256 */
			/* 5. X: nint(-2, 0bytes) => bstr(X, 32bytes) */
			COSE_KEY_LABEL_X,
			CBOR_BSTR32_EMPTY, /* VARIABLE */
			/* 6. X: nint(-3, 0bytes) => bstr(Y, 32bytes) */
			COSE_KEY_LABEL_Y,
			CBOR_BSTR32_EMPTY, /* VARIABLE */
		},
		/* 2. CDI DICE cert: - not included, */
		/* consists of hdr=cdi_cert_hdr_s, payload=cwt_claims_bstr_s, */
		/* sig=cbor_bstr64_s */
	},
	/* struct cdi_cert_hdr_s cert */
	{
		/* Array header: 4 elements */
		CBOR_HDR1(CBOR_MAJOR_ARR, 4),
		/* 1. Protected: bstr(COSE param) */
		COSE_PARAM_BSTR,
		/* 2. Unprotected: empty map */
		CBOR_HDR1(CBOR_MAJOR_MAP, 0),
		/* 3. Payload: bstr(CWT claims) - not fixed, contained in */
		/* cdi_cert_t */
		/* 4. Signature: bstr(64 bytes) - not fixed, contained in */
		/* cdi_cert_t */
	}
};

static const struct slice_ref_s kCdiAttestLabel = {
	10 /* strlen("CDI_Attest") */,
	(const uint8_t *)"CDI_Attest"
};
static const struct slice_ref_s kCdiSealLabel = {
	8 /* strlen("CDI_Seal") */,
	(const uint8_t *)"CDI_Seal"
};
static const struct slice_ref_s kIdSaltSlice = {
	64,
	(const uint8_t *)kIdSalt
};
static const struct slice_ref_s kAsymSaltSlice = {
	64,
	(const uint8_t *)kAsymSalt
};
static const struct slice_ref_s kIdLabel = {
	2, /* streln("ID") */
	(const uint8_t *)"ID"
};
static const struct slice_ref_s kKeyPairLabel = {
	8, /* strelen("Key Pair") */
	(const uint8_t *)"Key Pair"
};

/* Calculates CDI from {UDS, inputs_digest, label}
 */
static inline bool calc_cdi_from_digest(
	/* [IN] UDS */
	const uint8_t uds[DIGEST_BYTES],
	/* [IN] digest of inputs */
	const uint8_t inputs_digest[DIGEST_BYTES],
	/* [IN] label */
	const struct slice_ref_s label,
	/* [OUT] CDI */
	uint8_t cdi[DIGEST_BYTES]
)
{
	const struct slice_ref_s uds_slice = digest_as_slice(uds);
	const struct slice_ref_s inputs_digest_slice =
		digest_as_slice(inputs_digest);
	const struct slice_mut_s cdi_slice = digest_as_slice_mut(cdi);

	return __platform_hkdf_sha256(uds_slice, inputs_digest_slice, label,
				      cdi_slice);
}

/* Calculates CDI from {UDS, inputs, label}
 */
static bool calc_cdi(
	/* [IN] UDS */
	const uint8_t uds[DIGEST_BYTES],
	/* [IN] inputs */
	const struct slice_ref_s inputs,
	/* [IN] label */
	const struct slice_ref_s label,
	/* [OUT] CDI */
	uint8_t cdi[DIGEST_BYTES]
)
{
	uint8_t inputs_digest[DIGEST_BYTES];

	if (!__platform_sha256(inputs, inputs_digest)) {
		__platform_log_str("Failed to hash inputs");
		return false;
	}
	return calc_cdi_from_digest(uds, inputs_digest, label, cdi);
}

/* Fills inputs for sealing CDI.
 * Assumes that ctx->cfg and CfgDescr in ctx->output are already filled.
 */
static inline void fill_inputs_seal(
	/* [IN] dice context */
	const struct dice_ctx_s *ctx,
	/* [OUT] inputs */
	struct cdi_seal_inputs_s *inputs
)
{
	__platform_memset(inputs->auth_data_digest, 0, DIGEST_BYTES);
	__platform_memcpy(inputs->hidden_digest, ctx->cfg.hidden_digest,
			  DIGEST_BYTES);
	inputs->mode = ctx->output.dice_handover.payload.data.mode.value;
}

/* Fills inputs for attestation CDI
 * Assumes that ctx->cfg and CfgDescr in ctx->output are already filled.
 */
static inline void fill_inputs_attest(
	/* [IN] dice context */
	const struct dice_ctx_s *ctx,
	/* [OUT] inputs */
	struct cdi_attest_inputs_s *inputs
)
{
	const struct cwt_claims_s *cwt_claims =
		&ctx->output.dice_handover.payload.data;

	__platform_memcpy(inputs->code_digest,
			  cwt_claims->code_hash.value,
			  DIGEST_BYTES);
	__platform_memcpy(inputs->cfg_desr_digest,
			  cwt_claims->cfg_hash.value,
			  DIGEST_BYTES);
	fill_inputs_seal(ctx, &inputs->seal_inputs);
}

/* Calculates attestation CDI.
 * Assumes that ctx->cfg and CfgDescr in ctx->output are already filled.
 */
static inline bool calc_cdi_attest(
	/* [IN] dice context */
	const struct dice_ctx_s *ctx,
	/* [OUT] CDI */
	uint8_t cdi[DIGEST_BYTES]
)
{
	struct cdi_attest_inputs_s inputs;
	const struct slice_ref_s inputs_slice = {
		sizeof(inputs), (uint8_t *)&inputs
	};

	fill_inputs_attest(ctx, &inputs);
	return calc_cdi(ctx->cfg.uds, inputs_slice, kCdiAttestLabel, cdi);
}

/* Calculates sealing CDI.
 * Assumes that ctx->cfg and CfgDescr in ctx->output are already filled.
 */
static inline bool calc_cdi_seal(
	/* [IN] dice context */
	const struct dice_ctx_s *ctx,
	/* [OUT] CDI */
	uint8_t cdi[DIGEST_BYTES]
)
{
	struct cdi_seal_inputs_s inputs;
	const struct slice_ref_s inputs_slice = {
		sizeof(inputs), (uint8_t *)&inputs
	};

	fill_inputs_seal(ctx, &inputs);
	return calc_cdi(ctx->cfg.uds, inputs_slice, kCdiSealLabel, cdi);
}

/* Calculates boot mode from the data in ctx->cfg
 * On DT/OT with ti50:
 * - Normal if
 * - APROV succeeded or not configured from factory (for legacy devices).
 * Note: AllowUnverifiedRo counts as a failure AND
 * - Coreboot reported 'normal' boot mode
 * - Recovery if
 * - APROV succeeded or not configured from factory (for legacy devices).
 * Note: AllowUnverifiedRo counts as a failure AND
 * - Coreboot reported 'recovery-normal' boot mode
 * - Debug - in all other cases
 *
 * On H1 with Cr50:
 * - Normal if Coreboot reported 'normal' boot mode
 * - Recovery if Coreboot reported 'recovery-normal' boot mode
 * - Debug - in all other cases
 */
static inline uint8_t calc_mode(
	/* [IN] dice context */
	const struct dice_ctx_s *ctx
)
{
	if (__platform_aprov_status_allows_normal(ctx->cfg.aprov_status)) {
		if (__platform_memcmp(ctx->cfg.pcr0, kPcr0NormalMode,
				      DIGEST_BYTES) == 0) {
			return BOOT_MODE_NORMAL;
		}
		if (__platform_memcmp(ctx->cfg.pcr0, kPcr0RecoveryNormalMode,
				      DIGEST_BYTES) == 0) {
			return BOOT_MODE_RECOVERY;
		}
	}
	return BOOT_MODE_DEBUG;
}

/* Generates CDI cert signature for the initialized builder with pre-filled
 * CWT claims.
 */
static inline bool fill_cdi_cert_signature(
	/* [IN/OUT] dice context */
	struct dice_ctx_s *ctx,
	/* [IN] key handle to sign */
	const void *key
)
{
	uint8_t *sig_struct = ((uint8_t *)&ctx->output.dice_handover.payload) -
			      sizeof(struct cdi_sig_struct_hdr_s);
	const struct slice_ref_s data_to_sign = {
		CDI_SIG_STRUCT_LEN, sig_struct
	};
	struct cbor_bstr64_s *sig_bstr64 =
		&ctx->output.dice_handover.signature;

	__platform_memcpy(sig_struct, &kSigStructFixedHdr,
			  sizeof(struct cdi_sig_struct_hdr_s));
	__platform_memcpy(sig_bstr64->cbor_hdr, kSigHdr, 2);
	return __platform_ecdsa_p256_sign(key, data_to_sign,
					  sig_bstr64->value);
}

/* Generates key from UDS or CDI_Attest value.
 */
static bool generate_key(
	/* [IN] CDI_attest or UDS */
	const uint8_t input[DIGEST_BYTES],
	/* [OUT] key handle */
	const void **key
)
{
	uint8_t drbg_seed[DIGEST_BYTES];
	const struct slice_ref_s input_slice = digest_as_slice(input);
	const struct slice_mut_s drbg_seed_slice =
		digest_as_slice_mut(drbg_seed);

	if (!__platform_hkdf_sha256(input_slice, kAsymSaltSlice,
				    kKeyPairLabel, drbg_seed_slice)) {
		__platform_log_str("ASYM_KDF failed");
		return false;
	}
	return __platform_ecdsa_p256_keygen_hmac_drbg(drbg_seed, key);
}

/* Generates {UDS, CDI}_ID from {UDS, CDI} public key.
 */
static bool generate_id_from_pub_key(
	/* [IN] public key */
	const struct ecdsa_public_s *pub_key,
	/* [OUT] generated id */
	uint8_t dice_id[DICE_ID_BYTES]
)
{
	const struct slice_ref_s pub_key_slice = {
		sizeof(struct ecdsa_public_s), (const uint8_t *)pub_key
	};
	const struct slice_mut_s dice_id_slice = {
		DICE_ID_BYTES, (uint8_t *)dice_id
	};

	return __platform_hkdf_sha256(pub_key_slice, kIdSaltSlice, kIdLabel,
				      dice_id_slice);
}

/* Returns hexdump character for the half-byte.
 */
static inline uint8_t hexdump_halfbyte(uint8_t half_byte)
{
	if (half_byte < 10)
		return '0' + half_byte;
	else
		return 'a' + half_byte - 10;
}

/* Fills hexdump of the byte (lowercase).
 */
static inline void hexdump_byte(
	/* [IN] byte to hexdump */
	uint8_t byte,
	/* [OUT] str (always 2 bytes) with hexdump */
	uint8_t *str
)
{
	str[0] = hexdump_halfbyte((byte & 0xF0) >> 4);
	str[1] = hexdump_halfbyte(byte & 0x0F);
}

/* Fills {CDI, UDS} ID string from ID bytes.
 */
static void fill_dice_id_string(
	/* [IN] IDS_ID or CDI_ID */
	const uint8_t dice_id[DICE_ID_BYTES],
	/* [OUT] hexdump of this ID */
	uint8_t dice_id_str[DICE_ID_HEX_BYTES]
)
{
	size_t idx;

	for (idx = 0; idx < DICE_ID_BYTES; idx++, dice_id_str += 2)
		hexdump_byte(dice_id[idx], dice_id_str);
}

/* Fills COSE_Key structure from pubkey.
 * Assumes that all fields in COSE_Key except for X, y are already filled
 * from template.
 */
static inline void fill_cose_pubkey(
	/* [IN] pub key */
	const struct ecdsa_public_s *pub_key,
	/* [IN/OUT] COSE key structure */
	struct cose_key_ecdsa_s *cose_key
)
{
	__platform_memcpy(cose_key->x.value, pub_key->x, ECDSA_POINT_BYTES);
	__platform_memcpy(cose_key->y.value, pub_key->y, ECDSA_POINT_BYTES);
}

/* Fills CDI_attest pubkey, CDI_ID in the CDI certificate using generated
 * CDI_attest key.
 */
static inline bool fill_cdi_details_with_key(
	/* [IN/OUT] dice context */
	struct dice_ctx_s *ctx,
	/* [IN] CDI_attest key handle */
	const void *cdi_key
)
{
	struct ecdsa_public_s cdi_pub_key;
	uint8_t cdi_id[DICE_ID_BYTES];
	struct cwt_claims_s *cwt_claims =
		&ctx->output.dice_handover.payload.data;

	if (!__platform_ecdsa_p256_get_pub_key(cdi_key, &cdi_pub_key)) {
		__platform_log_str("Failed to get CDI pubkey");
		return false;
	}
	fill_cose_pubkey(&cdi_pub_key, &cwt_claims->subject_pk.data);
	if (!generate_id_from_pub_key(&cdi_pub_key, cdi_id)) {
		__platform_log_str("Failed to generate CDI_ID");
		return false;
	}
	/* SUB = hex(CDI_ID) */
	fill_dice_id_string(cdi_id, cwt_claims->sub.value);

	return true;
}

/* Fills CDI_attest pubkey, CDI_ID in the CDI certificate.
 * Assumes that ctx->cfg and CfgDescr in ctx->output are already filled.
 */
static inline bool fill_cdi_details(
	/* [IN/OUT] dice context */
	struct dice_ctx_s *ctx
)
{
	const void *cdi_key;
	bool result;
	struct dice_handover_hdr_s *hdr = &ctx->output.dice_handover.hdr;

	__platform_memcpy(hdr, &kDiceHandoverHdrTemplate,
			  sizeof(struct dice_handover_hdr_s));
	if (!calc_cdi_attest(ctx, hdr->cdi_attest.value)) {
		__platform_log_str("Failed to calc CDI_attest");
		return false;
	}
	if (!calc_cdi_seal(ctx, hdr->cdi_seal.value)) {
		__platform_log_str("Failed to calc CDI_seal");
		return false;
	}
	if (!generate_key(hdr->cdi_attest.value, &cdi_key)) {
		__platform_log_str("Failed to generate CDI key");
		return false;
	}
	result = fill_cdi_details_with_key(ctx, cdi_key);
	__platform_ecdsa_p256_free(cdi_key);

	return result;
}

/* Fills UDS_ID, signature into the certificate using generated UDS key.
 * Assumes that all other fields of CDI certificate are filled already.
 */
static inline bool fill_uds_details_with_key(
	/* [IN/OUT] dice context */
	struct dice_ctx_s *ctx,
	/* [IN] UDS key handle */
	const void *uds_key
)
{
	struct ecdsa_public_s uds_pub_key;
	uint8_t uds_id[DICE_ID_BYTES];
	struct cwt_claims_s *cwt_claims =
		&ctx->output.dice_handover.payload.data;
	struct combined_hdr_s *combined_hdr =
		&ctx->output.dice_handover.options.dice_handover;

	if (!__platform_ecdsa_p256_get_pub_key(uds_key, &uds_pub_key)) {
		__platform_log_str("Failed to get UDS pubkey");
		return false;
	}
	if (!generate_id_from_pub_key(&uds_pub_key, uds_id)) {
		__platform_log_str("Failed to generate UDS_ID");
		return false;
	}
	/* ISS = hex(UDS_ID) */
	fill_dice_id_string(uds_id, cwt_claims->iss.value);
	if (!fill_cdi_cert_signature(ctx, uds_key)) {
		__platform_log_str("Failed to sign CDI cert");
		return false;
	}

	/* We can do the rest only after we generated the signature because */
	/* signature generation uses ctx->output.hdr temporarily to build */
	/* Sig_struct for signing. */
	__platform_memcpy(combined_hdr,
			  &kCombinedHdrTemplate,
			  sizeof(struct combined_hdr_s));
	fill_cose_pubkey(
		&uds_pub_key,
		&combined_hdr->cert_chain.uds_pub_key);

	return true;
}

/* Fills UDS_ID, signature into the certificate. */
/* Assumes that all other fields of CDI certificate were filled already. */
static inline bool fill_uds_details(
	struct dice_ctx_s *ctx /* [IN/OUT] dice context */
)
{
	const void *uds_key;
	bool result;

	if (!generate_key(ctx->cfg.uds, &uds_key)) {
		__platform_log_str("Failed to generate UDS key");
		return false;
	}
	result = fill_uds_details_with_key(ctx, uds_key);
	__platform_ecdsa_p256_free(uds_key);

	return result;
}

/* Fills value in struct cbor_uint32_s */
/* Assumes that `cbor_var->cbor_hdr` is already pre-set */
static inline void set_cbor_u32(
	uint32_t value, /* [IN] value to set */
	struct cbor_uint32_s *cbor_var /* [OUT] CBOR UINT32 variable to fill */
)
{
	cbor_var->value[0] = (uint8_t)(((value) & 0xFF000000) >> 24);
	cbor_var->value[1] = (uint8_t)(((value) & 0x00FF0000) >> 16);
	cbor_var->value[2] = (uint8_t)(((value) & 0x0000FF00) >> 8);
	cbor_var->value[3] = (uint8_t)((value) & 0x000000FF);
}

/* Fills CfgDescr, CfgDescr digest and boot mode in CDI certificate */
static inline bool fill_config_details(
	struct dice_ctx_s *ctx /* [IN/OUT] dice context */
)
{
	struct cwt_claims_bstr_s *payload = &ctx->output.dice_handover.payload;
	struct cwt_claims_s *cwt_claims = &payload->data;
	struct cfg_descr_s *cfg_descr = &payload->data.cfg_descr.data;
	const struct slice_ref_s cfg_descr_slice = { sizeof(struct cfg_descr_s),
					(uint8_t *)cfg_descr };

	/* Copy fixed data from the template */
	__platform_memcpy(payload, &kCwtClaimsTemplate,
			  sizeof(struct cwt_claims_bstr_s));

	/* Fill Cfg Descriptor variables based on ctx->cfg */
	set_cbor_u32(ctx->cfg.aprov_status, &cfg_descr->aprov_status);
	set_cbor_u32(ctx->cfg.sec_ver, &cfg_descr->sec_ver);
	__platform_memcpy(cfg_descr->vboot_status.value, ctx->cfg.pcr0,
			  DIGEST_BYTES);
	__platform_memcpy(cfg_descr->ap_fw_version.value, ctx->cfg.pcr10,
			  DIGEST_BYTES);

	/* Calculate Cfg Descriptor digest */
	if (!__platform_sha256(cfg_descr_slice, cwt_claims->cfg_hash.value)) {
		__platform_log_str("Failed to calc CfgDescr digest");
		return false;
	}

	/* Calculate boot mode */
	cwt_claims->mode.value = calc_mode(ctx);

	return true;
}

/* Fills DICE handover structure in struct dice_ctx_s. */
/* Assumes ctx.cfg is already filled */
static inline bool generate_dice_handover(
	struct dice_ctx_s *ctx /* [IN/OUT] dice context */
)
{
	/* 1. Fill device configuration details in CDI certificate: CfgDescr and
	 * its digest, boot mode.
	 * 2. Fill CDI details in CDI certificate (CDI pubkey, CDI_ID) and DICE
	 * handover (CDIs) Relies on config details to be filled already
	 * 3. Fill UDS details in CDI certificate (UDS_ID, signature by UDS key)
	 * and DICE chain (UDS pubkey) Relies on the rest of CDI certificate to
	 * be filled already.
	 */

	return fill_config_details(ctx) &&
		fill_cdi_details(ctx) &&
		fill_uds_details(ctx);
}

/* Fills GSCBootParam. */
static inline bool fill_gsc_boot_param(
	struct gsc_boot_param_s *gsc_boot_param /* [IN/OUT] GSCBootParam */
)
{
	/* GSCBootParam: Map header: 3 entries */
	gsc_boot_param->map_hdr = CBOR_HDR1(CBOR_MAJOR_MAP, 3);

	/* GSCBootParam entry 1: EarlyEntropy:
	 * uint(1, 0bytes) => bstr(entropy, 64bytes)
	 */
	gsc_boot_param->early_entropy_label = CBOR_UINT0(1);
	gsc_boot_param->early_entropy.cbor_hdr[0] =
		CBOR_HDR1(CBOR_MAJOR_BSTR, CBOR_BYTES1);
	gsc_boot_param->early_entropy.cbor_hdr[1] = EARLY_ENTROPY_BYTES;

	/* GSCBootParam entry 2: SessionKeySeed:
	 * uint(2, 0bytes) => bstr(entropy, 32bytes)
	 */
	gsc_boot_param->session_key_seed_label = CBOR_UINT0(2);
	gsc_boot_param->session_key_seed.cbor_hdr[0] =
		CBOR_HDR1(CBOR_MAJOR_BSTR, CBOR_BYTES1);
	gsc_boot_param->session_key_seed.cbor_hdr[1] = KEY_SEED_BYTES;

	/* GSCBootParam entry 3: AuthTokenKeySeed:
	 * uint(3, 0bytes) => bstr(entropy, 32bytes)
	 */
	gsc_boot_param->auth_token_key_seed_label = CBOR_UINT0(3);
	gsc_boot_param->auth_token_key_seed.cbor_hdr[0] =
		CBOR_HDR1(CBOR_MAJOR_BSTR, CBOR_BYTES1);
	gsc_boot_param->auth_token_key_seed.cbor_hdr[1] = KEY_SEED_BYTES;

	if (!__platform_get_gsc_boot_param(
			gsc_boot_param->early_entropy.value,
			gsc_boot_param->session_key_seed.value,
			gsc_boot_param->auth_token_key_seed.value)) {
		__platform_log_str("Failed to get GSC boot param");
		return false;
	}
	return true;
}

/* Fills GSCBootParam and BootParam header in struct dice_ctx_s. */
/* Doesn't touch DICE handover structure */
static inline bool fill_boot_param(
	struct dice_ctx_s *ctx /* [IN/OUT] dice context */
)
{
	/* BootParam: Map header: 3 entries */
	ctx->output.map_hdr = CBOR_HDR1(CBOR_MAJOR_MAP, 3);

	/* BootParam entry 1: Version:
	 * uint(1, 0bytes) => uint(BOOT_PARAM_VERSION, 0bytes)
	 */
	ctx->output.version_label = CBOR_UINT0(1);
	ctx->output.version = CBOR_UINT0(0);

	/* BootParam entry 2: GSCBootParam:
	 * uint(2, 0bytes) => GSCBootParam (filled in fill_gsc_boot_param)
	 */
	ctx->output.gsc_boot_param_label = CBOR_UINT0(2);

	/* BootParam entry 3: AndroidDiceHandover:
	 * uint(3, 0bytes) => AndroidDiceHandover (not touched in this func)
	 */
	ctx->output.dice_handover_label = CBOR_UINT0(3);

	return fill_gsc_boot_param(&ctx->output.gsc_boot_param);
}

/* Get (part of) BootParam structure: [offset .. offset + size). */
size_t get_boot_param_bytes(
	/* [OUT] destination buffer to fill */
	uint8_t *dest,
	/* [IN] starting offset in the BootParam struct */
	size_t offset,
	/* [IN] size of the BootParam struct to copy */
	size_t size
)
{
	struct dice_ctx_s ctx;
	uint8_t *src = (uint8_t *)&ctx.output;

	if (size == 0 || offset >= BOOT_PARAM_SIZE)
		return 0;
	if (size > BOOT_PARAM_SIZE - offset)
		size = BOOT_PARAM_SIZE - offset;

	if (!__platform_get_dice_config(&ctx.cfg)) {
		__platform_log_str("Failed to get DICE config");
		return 0;
	}
	if (!generate_dice_handover(&ctx))
		return 0;

	if (!fill_boot_param(&ctx))
		return 0;

	__platform_memcpy(dest, src + offset, size);
	return size;
}

/* Get (part of) DiceChain structure: [offset .. offset + size) */
size_t get_dice_chain_bytes(
	/* [OUT] destination buffer to fill */
	uint8_t *dest,
	/* [IN] starting offset in the DiceChain struct */
	size_t offset,
	/* [IN] size of the data to copy */
	size_t size
)
{
	struct dice_ctx_s ctx;
	uint8_t *src = (uint8_t *)&ctx.output.dice_handover.options;

	if (size == 0 || offset >= DICE_CHAIN_SIZE)
		return 0;
	if (size > DICE_CHAIN_SIZE - offset)
		size = DICE_CHAIN_SIZE - offset;

	if (!__platform_get_dice_config(&ctx.cfg)) {
		__platform_log_str("Failed to get DICE config");
		return 0;
	}
	if (!generate_dice_handover(&ctx))
		return 0;

	__platform_memcpy(dest, src + offset, size);
	return size;
}
