/*
 * =====================================================================================
 *
 *       Filename:  hdm_response.c
 *
 *    Description:  HDM response functions
 *
 *        Version:  1.0
 *        Created:  09/16/2019 15:26:11 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *        Company:  Samsung Electronics
 *        Copyright (c) 2015 by Samsung Electronics, All rights reserved.
 *
 * =====================================================================================
 */

/** Includes */
#include "hdm_response.h"

/**
 * Static functions prototypes
 */
static hdm_return_code_t hdm_generate_response_header(uint8_t *buffer, uint32_t buffer_size, uint32_t *offset, drk_parsed_object_t *drk_data);
static hdm_return_code_t hdm_generate_apply_response_payload(uint8_t *buffer, uint32_t buffer_size, uint32_t *offset, uint32_t result_code, tci_message_t *tci_msg);
static hdm_return_code_t hdm_generate_get_device_id_response_payload(uint8_t *buffer, uint32_t buffer_size, uint32_t *offset, uint32_t result_code, tci_message_t *tci_msg);
static hdm_return_code_t hdm_get_policy_payload(uint8_t *buffer, uint32_t buffer_size, uint32_t *offset, uint32_t result_code, tci_message_t *tci_msg);

/**
 * @brief
 * hdm_generate_response_header
 * Generates the header response json for a policy update.
 *
 * @param[out]    buffer   - the buffer to write the response jws
 * @param[in|out] buf_size - the size of buf as input, the number of bytes written to buf as output
 * @param[in|out] offset   - the offset from buffer to write data
 * @param[in]     drk data - contains the certificate chain to be append in the response
 *
 * @return Status Code
 */
static hdm_return_code_t hdm_generate_response_header(uint8_t *buffer, uint32_t buffer_size, uint32_t *offset, drk_parsed_object_t *drk_data)
{
        if (json_append_single_char(buffer, buffer_size, offset, '{') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_string(buffer, buffer_size, offset, JWS_HEADER_REQ_ID, header.request_id, NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
        if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_string(buffer, buffer_size, offset, JWS_HEADER_PROTOCOL_VERSION, header.protocol_version, NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
        if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_string(buffer, buffer_size, offset, JWS_HEADER_ALG, (uint8_t *)JWS_RESPONSE_ALG_VALUE, NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
        if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_certs(buffer, buffer_size, offset, JWS_HEADER_X5C, drk_data->drk_cert_chain, drk_data->num_certificates) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_single_char(buffer, buffer_size, offset, '}') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        return HDM_STATUS_SUCCESS;
}

/**
 * @brief
 * hdm_generate_apply_response_payload
 * Generates the payload response json for a policy update.
 *
 * @param[out]    buffer      - the buffer to write the response jws
 * @param[in|out] buf_size    - the size of buf as input, the number of bytes written to buf as output
 * @param[in|out] offset      - the offset from buffer to write data
 * @param[in]     result_code - the result code to be written
 * @param[in]     *tci_msg    - the applied policy_value
 *
 * @return Status Code
 */
static hdm_return_code_t hdm_generate_apply_response_payload(uint8_t *buffer, uint32_t buffer_size, uint32_t *offset, uint32_t result_code, tci_message_t *tci_msg)
{
        tz_hdm_rpmb_t rpmb_data;
        hdm_return_code_t ret = HDM_GEN_RESPONSE_FAIL;

        uint32_t need_reboot = 1;
        uint32_t device_block = 0xFFFFFFFF;
        uint32_t compromise_block = 0xFFFFFFFF;
        uint32_t policy_version = 0xFFFFFFFF;
        uint8_t dev_block_str[MAX_INT32_CHARACTERS + 1];
        uint8_t comp_block_str[MAX_INT32_CHARACTERS + 1];
        uint8_t policy_version_str[MAX_INT32_CHARACTERS + 1];
        uint8_t device_id[JWS_PAYLOAD_DEVICE_ID_LEN];
        uint8_t str_pol[MAX_INT32_CHARACTERS + 1];
        
        TEE_MemFill(dev_block_str,      0, MAX_INT32_CHARACTERS + 1);
        TEE_MemFill(comp_block_str,     0, MAX_INT32_CHARACTERS + 1);
        TEE_MemFill(policy_version_str, 0, MAX_INT32_CHARACTERS + 1);
        TEE_MemFill(device_id,          0, JWS_PAYLOAD_DEVICE_ID_LEN);
        TEE_MemFill(str_pol,   0, MAX_INT32_CHARACTERS + 1);

        /**
         * If result_code is not HDM_STATUS_SUCCESS we must get policy_version from RPMB.
         * Otherwise we can use the value from policy payload.
         */
        if (result_code == HDM_STATUS_SUCCESS) {
                policy_version = payload.policy_version;
                device_block = payload.device_block;
                compromise_block = payload.compromise_block;
                TEE_MemMove(device_id, payload.device_id, JWS_PAYLOAD_DEVICE_ID_LEN);

                switch(device_status) {
                        case HDM_DEVICE_OK:
                                snprintf((char*) str_pol, strlen(DEVICE_BLOCK_VALUE) + 1, DEVICE_BLOCK_VALUE);
                                break;
                        case HDM_DEVICE_COMPROMISED:
                                snprintf((char*) str_pol, strlen(COMPROMISE_BLOCK_VALUE) + 1, COMPROMISE_BLOCK_VALUE);
                                break;
                        case HDM_APPLY_DEFAULT_POLICY:
                                snprintf((char*) str_pol, strlen(DEFAULT_BLOCK_VALUE) + 1, DEFAULT_BLOCK_VALUE);
                                break;
                        default:
                                HDM_LOG("Invalid device_status");
                                HDM_LOG_DEBUG("Invalid device_status: %d", device_status);
                                break;
                }
        } else {
                ret = read_policy_rpmb(&rpmb_data);
                if (ret == HDM_STATUS_SUCCESS) {
                        TEE_MemMove(device_id, rpmb_data.device_id, JWS_PAYLOAD_DEVICE_ID_LEN);
                        if (current_service_index < MAX_SERVICE_NUM) {
                                device_block = rpmb_data.data[current_service_index].device_block;
                                compromise_block = rpmb_data.data[current_service_index].compromise_block;
                                policy_version = rpmb_data.data[current_service_index].policy_version;
                        }

                } else {
                        /**
                         * If RPMB read fail do not return, uses default values (-1)
                         * and continue generating the response with RPMB error result_code.
                         */
                        HDM_LOG("Fail to read stored info");
                        HDM_LOG_DEBUG("Fail to read RPMB info. ret = %d", result_code);
                        result_code = ret;
                }
        }

        sprintf((char*) dev_block_str, "0x%08X", device_block);
        sprintf((char*) comp_block_str, "0x%08X", compromise_block);
        sprintf((char*) policy_version_str, "%d", policy_version);

        if (json_append_single_char(buffer, buffer_size, offset, '{') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_DEVICE_ID, device_id, NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
        if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_SERVER_NONCE, payload.server_nonce, NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
        if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_int(buffer, buffer_size, offset, JWS_PAYLOAD_RESULT_CODE, result_code) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
        if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_RESULT_MSG, get_error_string(result_code), NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
        if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_DEVICE_BLOCK, dev_block_str, NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
        if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_COMPROMISE_BLOCK, comp_block_str, NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
        if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (result_code == HDM_STATUS_SUCCESS) {
                if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_APPLIED_POLICY, str_pol, NULL) != HDM_STATUS_SUCCESS) {
                        return HDM_GEN_RESPONSE_FAIL;
                }
                if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                        return HDM_GEN_RESPONSE_FAIL;
                }
        }

        if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_POLICY_VERSION, policy_version_str, NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (tci_msg != NULL && result_code == HDM_STATUS_SUCCESS) {
                if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                        return HDM_GEN_RESPONSE_FAIL;
                }

#ifdef CONFIG_QSEE
                if (json_append_int(buffer, buffer_size, offset, JWS_PAYLOAD_NEED_REBOOT, hdm_is_need_reboot(tci_msg->content.server_comm.jws_message.policy_value)) != HDM_STATUS_SUCCESS) {
                        return HDM_GEN_RESPONSE_FAIL;
                }
#else
                if (json_append_int(buffer, buffer_size, offset, JWS_PAYLOAD_NEED_REBOOT, hdm_is_need_reboot(tci_msg->jws_message.policy_value)) != HDM_STATUS_SUCCESS) {
                        return HDM_GEN_RESPONSE_FAIL;
                }
#endif

        }

        if (json_append_single_char(buffer, buffer_size, offset, '}') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        return HDM_STATUS_SUCCESS;
}

/**
 * @brief
 * hdm_generate_get_device_id_response_payload
 * Generates the payload response json for get device id api.
 *
 * @param[out]    buffer      - the buffer to write the response jws
 * @param[in|out] buf_size    - the size of buf as input, the number of bytes written to buf as output
 * @param[in|out] offset      - the offset from buffer to write data
 * @param[in]     result_code - the result code to be written
 *
 * @return Status Code
 */
static hdm_return_code_t hdm_generate_get_device_id_response_payload(uint8_t *buffer, uint32_t buffer_size, uint32_t *offset, uint32_t result_code, tci_message_t *tci_msg)
{
        if (json_append_single_char(buffer, buffer_size, offset, '{') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_int(buffer, buffer_size, offset, JWS_PAYLOAD_RESULT_CODE, result_code) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
        if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_RESULT_MSG, get_error_string(result_code), NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
        if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_SERVER_NONCE, payload.server_nonce, NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
        if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

#ifdef CONFIG_QSEE
        if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_HDM_ID, tci_msg->content.server_comm.nwd_info.hash_imei, NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

#else
        if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_HDM_ID, tci_msg->nwd_info.hash_imei, NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
#endif

        if (json_append_single_char(buffer, buffer_size, offset, '}') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        return HDM_STATUS_SUCCESS;
}

/**
 * @brief
 * hdm_get_policy_payload
 * Generates the payload response json for get policy api.
 *
 * @param[out]    buffer      - the buffer to write the response jws
 * @param[in|out] buf_size    - the size of buf as input, the number of bytes written to buf as output
 * @param[in|out] offset      - the offset from buffer to write data
 * @param[in]     result_code - the result code to be written
 *
 * @return Status Code
 */
static hdm_return_code_t hdm_get_policy_payload(uint8_t *buffer, uint32_t buffer_size, uint32_t *offset, uint32_t result_code, tci_message_t *tci_msg)
{
        tz_hdm_rpmb_t rpmb_data;
        hdm_return_code_t ret = HDM_GEN_RESPONSE_FAIL;
        uint32_t device_block = 0xFFFFFFFF;
        uint32_t compromise_block = 0xFFFFFFFF;
        uint32_t policy_version = 0xFFFFFFFF;
        uint8_t str_pol[15];
        uint8_t str_dev_block[15];
        uint8_t str_comp_block[15];
        uint8_t str_policy_version[15];

        if (result_code == HDM_STATUS_SUCCESS) {
                TEE_MemFill(str_pol, 0, 15);
                TEE_MemFill(str_dev_block, 0, 15);
                TEE_MemFill(str_comp_block, 0, 15);
                TEE_MemFill(str_policy_version, 0, 15);

                ret = read_policy_rpmb(&rpmb_data);
                if (ret == HDM_STATUS_SUCCESS) {
#if SUPPORT_MULTI_POLICY
                        device_block = rpmb_data.data[current_service_index].device_block;
                        compromise_block = rpmb_data.data[current_service_index].compromise_block;
                        policy_version = rpmb_data.data[current_service_index].policy_version;
#else
                        device_block = rpmb_data.device_block;
                        compromise_block = rpmb_data.compromise_block;
                        policy_version = rpmb_data.policy_version;
#endif

                        snprintf((char*) str_dev_block, 11, "0x%08X", device_block);
                        snprintf((char*) str_comp_block, 11, "0x%08X", compromise_block);
                        snprintf((char*) str_policy_version, 11, "%d", policy_version);

                        switch(device_status) {
                                case HDM_DEVICE_OK:
                                        snprintf((char*) str_pol, strlen(DEVICE_BLOCK_VALUE) + 1, DEVICE_BLOCK_VALUE);
                                        break;
                                case HDM_DEVICE_COMPROMISED:
                                        snprintf((char*) str_pol, strlen(COMPROMISE_BLOCK_VALUE) + 1, COMPROMISE_BLOCK_VALUE);
                                        break;
                                case HDM_APPLY_DEFAULT_POLICY:
                                        snprintf((char*) str_pol, strlen(DEFAULT_BLOCK_VALUE) + 1, DEFAULT_BLOCK_VALUE);
                                        break;
                                default:
                                        HDM_LOG("Invalid device_status");
                                        HDM_LOG_DEBUG("Invalid device_status: %d", device_status);
                                        break;
                        }

                } else {
                        /**
                         * If RPMB read fail do not return, uses default values (-1)
                         * and continue generating the response with RPMB error result_code.
                         */
                        HDM_LOG("Fail to read stored info");
                        HDM_LOG_DEBUG("Fail to read RPMB info. ret = %d", ret);
                        result_code = ret;
                }
        }

        if (json_append_single_char(buffer, buffer_size, offset, '{') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_DEVICE_ID, payload.device_id, NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
        if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_SERVER_NONCE, payload.server_nonce, NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
        if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_int(buffer, buffer_size, offset, JWS_PAYLOAD_RESULT_CODE, result_code) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }
        if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_RESULT_MSG, get_error_string(result_code), NULL) != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        if (result_code == HDM_STATUS_SUCCESS) {
                if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                        return HDM_GEN_RESPONSE_FAIL;
                }

                if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_DEVICE_BLOCK, str_dev_block, NULL) != HDM_STATUS_SUCCESS) {
                        return HDM_GEN_RESPONSE_FAIL;
                }
                if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                        return HDM_GEN_RESPONSE_FAIL;
                }

                if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_COMPROMISE_BLOCK, str_comp_block, NULL) != HDM_STATUS_SUCCESS) {
                        return HDM_GEN_RESPONSE_FAIL;
                }
                if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                        return HDM_GEN_RESPONSE_FAIL;
                }

                if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_APPLIED_POLICY, str_pol, NULL) != HDM_STATUS_SUCCESS) {
                        return HDM_GEN_RESPONSE_FAIL;
                }
                if (json_append_single_char(buffer, buffer_size, offset, ',') != HDM_STATUS_SUCCESS) {
                        return HDM_GEN_RESPONSE_FAIL;
                }

                if (json_append_string(buffer, buffer_size, offset, JWS_PAYLOAD_POLICY_VERSION, str_policy_version, NULL) != HDM_STATUS_SUCCESS) {
                        return HDM_GEN_RESPONSE_FAIL;
                }
        }

        if (json_append_single_char(buffer, buffer_size, offset, '}') != HDM_STATUS_SUCCESS) {
                return HDM_GEN_RESPONSE_FAIL;
        }

        return HDM_STATUS_SUCCESS;
}

/**
 * @brief
 * hdm_generate_response
 * Generates the response jws for a policy update.
 *
 * @param[in]      command_id - command id
 * @param[in]     *tci_msg    - tci message
 * @param[in]      res_code   - the response code
 * @param[out]     buf        - the buffer to write the response jws
 * @param[in|out] *buf_size   - the size of buf as input, the number of bytes written to buf as output
 *
 * @return Status Code
 */
hdm_return_code_t hdm_generate_response(uint32_t command_id, tci_message_t *tci_msg, uint32_t res_code, uint8_t buffer[], uint32_t *buffer_size)
{
        uint32_t ret = HDM_GEN_RESPONSE_FAIL;
        uint32_t offset = 0;
        uint32_t header_len = 0;
        uint32_t payload_len = 0;
        uint8_t *unwrap_object = NULL;
        uint32_t unwrap_object_len = 0;
        drk_parsed_object_t drk_data;
        uint8_t signature[RESPONSE_SIGNATURE_LEN];
        uint32_t signature_len = RESPONSE_SIGNATURE_LEN;

        HDM_LOG("hdm_generate_response()");
        TEE_MemFill(buffer, 0, *buffer_size);

        // Check DRK certificate
        unwrap_object = TEE_Malloc(HDM_DRK_MAX_BUF_LEN, 0);

        if(unwrap_object == NULL)
        {
                ret = HDM_ALLOC_ERROR;
                HDM_LOG("Error to alloc memory");
                goto exit;
        }

        unwrap_object_len = HDM_DRK_MAX_BUF_LEN;
        
#ifdef CONFIG_QSEE
        uint32_t wrap_len = tci_msg->content.server_comm.nwd_info.hdm_key_len;

        ret = unwrap(tci_msg->content.server_comm.nwd_info.hdm_key, &wrap_len, tci_msg->content.server_comm.nwd_info.is_wrapped_key, unwrap_object, &unwrap_object_len);
        tci_msg->content.server_comm.nwd_info.hdm_key_len = wrap_len;
#else
        uint32_t wrap_len = tci_msg->nwd_info.hdm_key_len;

        ret = unwrap(tci_msg->nwd_info.hdm_key, &wrap_len, tci_msg->nwd_info.is_wrapped_key, unwrap_object, &unwrap_object_len);
        tci_msg->nwd_info.hdm_key_len = wrap_len;
#endif

        if (ret != HDM_STATUS_SUCCESS)
        {
                HDM_LOG("DRK unwrap failed");
                goto exit;
        }

        // Get certificates chain RSA key
        ret = get_cert_chain_rsakey(unwrap_object, unwrap_object_len, &drk_data);
        if (ret != HDM_STATUS_SUCCESS)
        {
                HDM_LOG("get_cert_chain_rsakey FAIL");
                HDM_LOG_DEBUG("get_cert_chain_rsakey FAIL ret = %d", ret);
                goto exit;
        }

        // Convert DER to PEM
        ret = convert_der_to_b64(drk_data.drk_cert_chain, drk_data.num_certificates);
        if (ret != HDM_STATUS_SUCCESS)
        {
                HDM_LOG("convert_der_to_b64 FAIL");
                HDM_LOG_DEBUG("convert_der_to_b64 FAIL ret = %d", ret);
                goto exit;
        }

        // buffer = header
        ret = hdm_generate_response_header(buffer, *buffer_size, &offset, &drk_data);
        if (ret != HDM_STATUS_SUCCESS)
        {
                HDM_LOG("hdm_generate_response_header FAIL");
                HDM_LOG_DEBUG("hdm_generate_response_header FAIL ret = %d", ret);
                goto exit;
        }

        // buffer = b64(header)
        ret = base64url_encode_in_place(buffer, &offset, *buffer_size);
        if (ret != BASE64_OK)
        {
                HDM_LOG("base64url_encode_in_place FAIL");
                HDM_LOG_DEBUG("base64url_encode_in_place FAIL ret = %d", ret);
                ret = HDM_GEN_RESPONSE_FAIL;
                goto exit;
        }
        header_len = offset;

        // buffer = b64(header).
        ret = json_append_single_char(buffer, *buffer_size, &offset, '.');
        if (ret != HDM_STATUS_SUCCESS)
        {
                HDM_LOG("json_append_single_char FAIL");
                HDM_LOG_DEBUG("json_append_single_char FAIL ret = %d", ret);
                goto exit;
        }

        // buffer = b64(header).payload
        switch (command_id) {
                case CMD_VERIFY_POLICY:
                        ret = hdm_generate_apply_response_payload(buffer, *buffer_size, &offset, res_code, NULL);
                        break;

                case CMD_RELEASE_SHARED_BUF_MEM:
                case CMD_APPLY_POLICY:
                        ret = hdm_generate_apply_response_payload(buffer, *buffer_size, &offset, res_code, tci_msg);
                        break;

                case CMD_GET_DEVICE_ID:
                        ret = hdm_generate_get_device_id_response_payload(buffer, *buffer_size, &offset, res_code, tci_msg);
                        break;

                case CMD_GET_POLICY:
                        ret = hdm_get_policy_payload(buffer, *buffer_size, &offset, res_code, tci_msg);
                        break;
                default:
                        HDM_LOG("Invalid command id");
                        HDM_LOG_DEBUG("Invalid command id = %d", command_id);
                        ret = HDM_GEN_RESPONSE_FAIL;
                        goto exit;
        }

        if (ret != HDM_STATUS_SUCCESS)
        {
                HDM_LOG("hdm_generate_response FAIL");
                HDM_LOG_DEBUG("hdm_generate_response FAIL ret = %d", ret);
                goto exit;
        }

        // +1 is to count the . character
        payload_len = offset - (header_len + 1);

        // buffer = b64(header).b64(payload)
        ret = base64url_encode_in_place(&buffer[header_len + 1], &payload_len, *buffer_size - (header_len + 1));
        if (ret != BASE64_OK)
        {
                HDM_LOG("base64url_encode_in_place FAIL");
                HDM_LOG_DEBUG("base64url_encode_in_place FAIL ret = %d", ret);
                ret = HDM_GEN_RESPONSE_FAIL;
                goto exit;
        }
        offset = header_len + payload_len + 1;

        ret = generate_signature(buffer, offset, signature, signature_len, &(drk_data.drk_rsa_private_key));
        if (ret != HDM_STATUS_SUCCESS)
        {
                HDM_LOG("generate_signature FAIL");
                HDM_LOG_DEBUG("generate_signature FAIL ret = %d", ret);
                goto exit;
        }

        // buffer = b64(header).b64(payload).
        ret = json_append_single_char(buffer, *buffer_size, &offset, '.');
        if (ret != HDM_STATUS_SUCCESS)
        {
                HDM_LOG("json_append_single_char FAIL");
                HDM_LOG_DEBUG("json_append_single_char FAIL ret = %d", ret);
                goto exit;
        }

        if ((offset + signature_len) > *buffer_size)
        {
                HDM_LOG_DEBUG("Buffer is not big enough to fit response JWS signature");
                ret = HDM_BUF_SIZE_ERROR;
                goto exit;
        }

        // buffer = b64(header).b64(payload).signature
        TEE_MemMove(&buffer[offset], signature, signature_len);

        // buffer = b64(header).b64(payload).b64(signature)
        ret = base64url_encode_in_place(&buffer[offset], &signature_len, *buffer_size - (offset));
        if (ret != BASE64_OK)
        {
                HDM_LOG("base64url_encode_in_place FAIL");
                HDM_LOG_DEBUG("base64url_encode_in_place FAIL ret = %d", ret);
                ret = HDM_GEN_RESPONSE_FAIL;
                goto exit;
        }

        *buffer_size = offset + signature_len;
        ret = HDM_STATUS_SUCCESS;
exit:
        //SRR-14305, SRR-14494 - Cleaning sensitive data
        TEE_MemFill(&drk_data, 0, sizeof(drk_parsed_object_t));

        if (unwrap_object != NULL) {
                TEE_Free(unwrap_object);
        }

        return ret;
}
