
/*
 * =====================================================================================
 *
 *       Filename:  process_cmd.c
 *
 *    Description:  HDM process command
 *
 *        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 "process_cmd.h"

/**
 * @brief
 * generate_jws_response
 * Aux function to generates the response JWS for a policy update
 *
 * @param[in]     response_type - response type
 * @param[in]    *tci_req       - TA request buffer
 * @param[out]   *tci_resp      - TA response buffer
 * @param[in]     res_code      - the response code
 *
 * @return Status Code
*/
hdm_return_code_t generate_jws_response(uint32_t response_type, tci_message_t *tci_req, tci_message_t *tci_resp, uint32_t res_code) {
        uint32_t ret = HDM_STATUS_FAIL;
        uint32_t buffer_size = JWS_LEN;

        switch(res_code) {
                case HDM_INVALID_JWS:
                case HDM_JWS_INVALID_FORMAT:
                case HDM_JWS_INVALID_LENGTH:
                        /*
                         * Error parsing JWS policy, not possible to generate the JWS response.
                         */
                        break;
                default:
                        ret = hdm_generate_response(response_type, tci_req, res_code, tci_resp->jws_message.data, &buffer_size);
                        tci_resp->jws_message.len = buffer_size;
                        if (ret != HDM_STATUS_SUCCESS) {
                                HDM_LOG("hdm_generate_response FAIL");
                                HDM_LOG_DEBUG("hdm_generate_response FAIL ret = %d", ret);
                                return ret;
                        }
                        break;
        }

        return res_code;
}

/**
 * @brief
 * process_cmd
 * Process command
 *
 * @param[in] commandId - command id
 * @param[in] tci_msg   - tci message
 *
 * @return HDM status code
 */
hdm_return_code_t process_cmd(uint32_t commandId, tci_message_t *tci_req, tci_message_t *tci_resp) {
        HDM_LOG("process_cmd()");

        hdm_return_code_t ret = HDM_STATUS_SUCCESS;
        tz_hdm_rpmb_t rpmb_data;
        uint32_t resp_len = JWS_LEN;
        uint32_t current_policy;
        uint32_t index = 0, offset = 0;
        current_service_index = MAX_SERVICE_NUM;

        // RPMB Data Initialization
        TEE_MemFill(&rpmb_data, 0, sizeof(tz_hdm_rpmb_t));

        // Device integrity check
        device_status = hdm_ICCC_check();
        if (device_status != HDM_DEVICE_OK) {
                HDM_LOG("hdm_ICCC_check FAIL");
        }

        switch (commandId) {
                case CMD_VERIFY_POLICY:
                        HDM_LOG("CMD_VERIFY_POLICY");

                        ret = check_jws_struct(tci_req);
                        if (ret != HDM_STATUS_SUCCESS) {
                                HDM_LOG("fill_jws_struct FAIL");
                                HDM_LOG_DEBUG("fill_jws_struct FAIL ret = %d", ret);
                                return generate_jws_response(commandId, tci_req, tci_resp, ret);
                        }

                        // Copy wrapped_key from tci_req to tci_resp
                        tci_req->nwd_info.is_wrapped_key = 1;
                        tci_resp->nwd_info.is_wrapped_key = 1;
                        tci_resp->nwd_info.hdm_key_len = tci_req->nwd_info.hdm_key_len;

                        //SRR-14490 SRR-14495
                        if (tci_resp->nwd_info.hdm_key_len > HDM_DRK_MAX_BUF_LEN) {
                                HDM_LOG("hdm_key error");
                                HDM_LOG_DEBUG("key_len has size very big");
                                ret = HDM_KEY_ERROR;
                                return generate_jws_response(commandId, tci_req, tci_resp, ret);
                        }

                        // Checking service_name JWS field
                        ret = hdm_check_jws_string_field(header.service_name, (uint8_t *) JWS_HEADER_SERVICE_NAME);
                        if (ret != HDM_STATUS_SUCCESS) {
                                HDM_LOG("Invalid service_name");
                                HDM_LOG_DEBUG("Invalid service_name: %s", header.service_name);
                                return generate_jws_response(commandId, tci_req, tci_resp, HDM_INVALID_SERVICE_NAME);
                        }

                        // Checking request_id JWS field
                        ret = hdm_check_jws_string_field(header.request_id, (uint8_t *) JWS_HEADER_REQ_ID);
                        if (ret != HDM_STATUS_SUCCESS) {
                                HDM_LOG("Invalid request_id");
                                HDM_LOG_DEBUG("Invalid request_id: %s", header.request_id);
                                return generate_jws_response(commandId, tci_req, tci_resp, HDM_INVALID_REQ_ID);
                        }

                        TEE_MemMove(tci_resp->nwd_info.hdm_key, tci_req->nwd_info.hdm_key, tci_resp->nwd_info.hdm_key_len);

                        ret = read_policy_rpmb(&rpmb_data);
                        // Only treat RPMB fail cases
                        if (ret == HDM_RPMB_FAIL) {
                                HDM_LOG("RPMB Fail");
                                HDM_LOG_DEBUG("RPMB Fail ret = %d", ret);

                                // Applying default policy
                                device_status = HDM_APPLY_DEFAULT_POLICY;
                                return generate_jws_response(commandId, tci_req, tci_resp, ret);
                        }

                        if (ret == HDM_RPMB_MAGIC_FAIL) {

                                uint8_t device_id_hash[DEVICE_ID_B64_LEN + 1] = {0,};
                                uint32_t device_id_hash_len = DEVICE_ID_B64_LEN;

                                ret = register_device_id(tci_req->nwd_info.imei_0, rpmb_data, device_id_hash, &device_id_hash_len);
                                if (ret != HDM_STATUS_SUCCESS) {
                                        HDM_LOG("Registering device id FAIL");
                                        HDM_LOG_DEBUG("Registering device id FAIL ret = %d", ret);
                                        return generate_jws_response(commandId, tci_req, tci_resp, ret);
                                }
                        }

                        ret = check_device_id(payload, &rpmb_data);
                        if (ret != HDM_STATUS_SUCCESS) {
                                HDM_LOG("Checking device id FAIL");
                                HDM_LOG_DEBUG("Checking device id FAIL ret = %d", ret);
                                return generate_jws_response(commandId, tci_req, tci_resp, ret);
                        }

                        if (payload.device_block & HDM_DEACTIVATION_BIT) {

                                ret = remove_policy(&rpmb_data);
                                if (ret != HDM_STATUS_SUCCESS) {
                                        HDM_LOG("remove policy FAIL");
                                        HDM_LOG_DEBUG("remove_policy FAIL ret = %d", ret);
                                        return generate_jws_response(commandId, tci_req, tci_resp, ret);
                                }

                                ret = save_rpmb_data(rpmb_data);
                                if (ret != HDM_STATUS_SUCCESS) {
                                        HDM_LOG("Error save_rpmb_data");
                                        HDM_LOG_DEBUG("save_rpmb_data FAIL ret = %d", ret);
                                        return generate_jws_response(commandId, tci_req, tci_resp, ret);
                                }

                        } else {

                                ret = get_curr_service_index(rpmb_data, header.service_name);
                                if (ret == HDM_RPMB_IS_FULL) {
                                        HDM_LOG("Can not store new policy because HDM RPMB is full");
                                        HDM_LOG_DEBUG("get_curr_service_index FAIL ret = %d", ret);
                                        return generate_jws_response(commandId, tci_req, tci_resp, ret);
                                }

                                ret = check_policy_version(payload, &rpmb_data);
                                if (ret != HDM_STATUS_SUCCESS) {
                                        HDM_LOG("Checking policy version FAIL");
                                        HDM_LOG_DEBUG("Checking policy version FAIL ret = %d", ret);
                                        return generate_jws_response(commandId, tci_req, tci_resp, ret);
                                }

                                ret = save_policy_rpmb(rpmb_data);
                                if (ret != HDM_STATUS_SUCCESS) {
                                        HDM_LOG("Error save_policy_rpmb");
                                        HDM_LOG_DEBUG("save_policy_rpmb FAIL ret = %d", ret);
                                        return generate_jws_response(commandId, tci_req, tci_resp, ret);
                                }
                        }

                        ret = generate_jws_response(commandId, tci_req, tci_resp, ret);
                        break;

                case CMD_APPLY_POLICY:
                        HDM_LOG("CMD_APPLY_POLICY");

                        ret = check_jws_struct(tci_req);
                        if (ret != HDM_STATUS_SUCCESS) {
                                HDM_LOG("Error check_jws_struct");
                                HDM_LOG_DEBUG("check_jws_struct FAIL ret = %d", ret);
                                return generate_jws_response(commandId, tci_req, tci_resp, ret);
                        }

                        ret = read_policy_rpmb(&rpmb_data);
                        if (ret != HDM_STATUS_SUCCESS) {
                                HDM_LOG("Error read_policy_rpmb");
                                HDM_LOG_DEBUG("read_policy_rpmb FAIL ret = %d", ret);
                                if (ret == HDM_RPMB_FAIL) {
                                        // Apply default policy
                                        device_status = HDM_APPLY_DEFAULT_POLICY;
                                } else {
                                        // when magic check failed or hash check failed.
                                        return generate_jws_response(commandId, tci_req, tci_resp, ret);
                                }
                        }

                        //In case of deactivation we shouldn't look for removed services (removed in verify)
                        if (!(payload.device_block & HDM_DEACTIVATION_BIT)) {
                                ret = get_curr_service_index(rpmb_data, header.service_name);
                                if (ret == HDM_RPMB_IS_FULL || ret == HDM_RPMB_SERVICE_NAME_MISMATCH) {
                                        // Need to check if policy should be applied when apply policy service name is mismatched,
                                        HDM_LOG("Can not find service name : %s", header.service_name);
                                        return generate_jws_response(commandId, tci_req, tci_resp, ret);
                                }
                        }

                        ret = hdm_apply_policy(&rpmb_data, &current_policy);
                        if (ret == HDM_APPLY_POLICY_FAIL) {
                                HDM_LOG("hdm_apply_policy FAIL");
                                HDM_LOG_DEBUG("hdm_apply_policy FAIL ret = %d", ret);
                                return generate_jws_response(commandId, tci_req, tci_resp, ret);
                        }

                        tci_req->jws_message.policy_value = current_policy;
                        tci_resp->jws_message.policy_value = current_policy;

                        ret = generate_jws_response(commandId, tci_req, tci_resp, ret);
                        break;

                case CMD_GET_DEVICE_ID:
                        HDM_LOG("CMD_GET_DEVICE_ID");

                        uint8_t device_id_hash[DEVICE_ID_B64_LEN + 1] = {0,};
                        uint32_t device_id_hash_len = DEVICE_ID_B64_LEN;


                        TEE_MemFill(tci_req->nwd_info.hash_imei, 0, NWS_INFO_LEN);
                        TEE_MemFill(header.protocol_version, 0, JWS_HEADER_PROTOCOL_VERSION_LEN);
                        TEE_MemFill(payload.server_nonce, 0, JWS_PAYLOAD_SERVER_NONCE_LEN);
                        TEE_MemFill(header.request_id, 0, JWS_HEADER_REQ_ID_LEN);

                        TEE_MemMove(header.request_id, tci_req->nwd_info.mac_addr, (JWS_HEADER_REQ_ID_LEN - 1));
                        TEE_MemMove(payload.server_nonce, (tci_req->nwd_info.mac_addr + JWS_HEADER_REQ_ID_LEN), (JWS_PAYLOAD_SERVER_NONCE_LEN - 1));
                        TEE_MemMove(header.protocol_version, HDM_PROTOCOL_VERSION, strlen(HDM_PROTOCOL_VERSION));

                        ret = read_policy_rpmb(&rpmb_data);
                        if (ret != HDM_STATUS_SUCCESS && ret != HDM_RPMB_MAGIC_FAIL) {
                                HDM_LOG("Error read_policy_rpmb");
                                HDM_LOG_DEBUG("read_policy_rpmb FAIL ret = %d", ret);

                                TEE_MemFill(tci_req->nwd_info.imei_0, 0, NWS_INFO_LEN);
                                return generate_jws_response(commandId, tci_req, tci_resp, ret);
                        }

                        if (ret == HDM_STATUS_SUCCESS) {
                                ret = gen_device_id(tci_req->nwd_info.imei_0, rpmb_data.device_id, device_id_hash, &device_id_hash_len);
                                if (ret != HDM_STATUS_SUCCESS) {
                                        HDM_LOG("Error gen_device_id");
                                        HDM_LOG_DEBUG("gen_device_id FAIL ret = %d", ret);
                                        return generate_jws_response(commandId, tci_req, tci_resp, ret);
                                }
                        } else if (ret == HDM_RPMB_MAGIC_FAIL) {
                                ret = register_device_id(tci_req->nwd_info.imei_0, rpmb_data, device_id_hash, &device_id_hash_len);
                                if (ret != HDM_STATUS_SUCCESS) {
                                        HDM_LOG("Registering device id FAIL");
                                        HDM_LOG_DEBUG("Registering device id FAIL ret = %d", ret);
                                        return generate_jws_response(commandId, tci_req, tci_resp, ret);
                                }
                        }

                        if (device_id_hash_len != DEVICE_ID_B64_LEN) {
                                HDM_LOG("Fail to calcule deivce ID");
                                HDM_LOG_DEBUG("Fail to calcule deivce ID ret = %d", ret);

                                TEE_MemFill(tci_req->nwd_info.imei_0, 0, NWS_INFO_LEN);
                                return generate_jws_response(commandId, tci_req, tci_resp, ret);
                        }

                        TEE_MemMove(tci_req->nwd_info.hash_imei, device_id_hash, device_id_hash_len);
                        ret = generate_jws_response(commandId, tci_req, tci_resp, ret);
                        break;

                case CMD_GET_POLICY:
                        HDM_LOG("CMD_GET_POLICY");

                        /* read rpmb data */
                        ret = read_policy_rpmb(&rpmb_data);
                        if (ret != HDM_STATUS_SUCCESS && ret != HDM_RPMB_MAGIC_FAIL) {
                                HDM_LOG("Error read_policy_rpmb");
                                HDM_LOG_DEBUG("read_policy_rpmb FAIL ret = %d", ret);

                                TEE_MemFill(tci_req->nwd_info.imei_0, 0, NWS_INFO_LEN);
                                return generate_jws_response(commandId, tci_req, tci_resp, ret);
                        }

                        /* check request length */
                        if (tci_req->service_info.service_request_len > JWS_HEADER_REQ_ID_LEN + JWS_PAYLOAD_SERVER_NONCE_LEN) {
                                HDM_LOG("Invalid request len received: %u", tci_req->service_info.service_request_len);
                                return generate_jws_response(commandId, tci_req, tci_resp, HDM_INVALID_REQUEST_LEN);
                        }

                        /* build response JWS */
                        TEE_MemFill(header.request_id, 0, JWS_HEADER_REQ_ID_LEN);
                        TEE_MemFill(header.protocol_version, 0, JWS_HEADER_PROTOCOL_VERSION_LEN);
                        TEE_MemFill(payload.server_nonce, 0, JWS_PAYLOAD_SERVER_NONCE_LEN);
                        TEE_MemFill(payload.device_id, 0, JWS_PAYLOAD_DEVICE_ID_LEN);

                        TEE_MemMove(payload.device_id, rpmb_data.device_id, JWS_PAYLOAD_DEVICE_ID_LEN);
                        TEE_MemMove(payload.server_nonce, (tci_req->service_info.service_request_id + JWS_HEADER_REQ_ID_LEN), (JWS_PAYLOAD_SERVER_NONCE_LEN - 1));
                        TEE_MemMove(header.protocol_version, HDM_PROTOCOL_VERSION, strlen(HDM_PROTOCOL_VERSION));
                        TEE_MemMove(header.request_id, tci_req->service_info.service_request_id, (JWS_HEADER_REQ_ID_LEN - 1));

#if SUPPORT_MULTI_POLICY
                        /* set global index */
                        ret = get_curr_service_index(rpmb_data, tci_req->service_info.service_name);
                        if (ret != HDM_STATUS_SUCCESS) {
                                if (ret == HDM_RPMB_IS_FULL)
                                        ret = HDM_RPMB_SERVICE_NAME_MISMATCH;
                                HDM_LOG_DEBUG("get_curr_service_index FAIL ret = %d", ret);
                                return generate_jws_response(commandId, tci_req, tci_resp, ret);
                        }
#endif

                        /* set response */
                        ret = generate_jws_response(commandId, tci_req, tci_resp, ret);
                        break;

#ifdef DEBUG_HDM
                case CMD_GET_ALL_DATA:
                        HDM_LOG("CMD_GET_ALL_DATA");
                        ret = read_policy_rpmb(&rpmb_data);
                        TEE_MemFill(tci_resp->jws_message.data, 0, JWS_LEN);
                        TEE_MemMove(tci_resp->jws_message.data, &rpmb_data, sizeof(tz_hdm_rpmb_t));
                        tci_resp->jws_message.len = sizeof(tz_hdm_rpmb_t);
                        ret = HDM_STATUS_SUCCESS;
                        break;

                case CMD_SET_TEST_POLICY:
                        HDM_LOG("CMD_SET_TEST_POLICY");
                        ret = read_policy_rpmb(&rpmb_data);
                        if (ret == HDM_RPMB_FAIL) {
                                HDM_LOG("RPMB Fail");
                                HDM_LOG_DEBUG("RPMB Fail ret = %d", ret);
                        }
                        else {
                                if (rpmb_data.data[15].isActivated != 1) {
                                        rpmb_data.data[15].isActivated = 1;
                                        memcpy(rpmb_data.data[15].service_name, "TEST_POLICY", 12);
                                        rpmb_data.data[15].device_block = 1;
                                        rpmb_data.data[15].compromise_block = 1;
                                        rpmb_data.data[15].policy_version = 0xFF;
                                        rpmb_data.data[15].nextReboot = 0;
                                        ret = save_rpmb_data(rpmb_data);
                                        if (ret != HDM_STATUS_SUCCESS) {
                                                HDM_LOG("Failed to save rpmb data for test");
                                        }
                                        else {
                                                uint32_t test_policty = 0;
                                                hdm_apply_policy(&rpmb_data, &test_policty);
                                                if (ret == HDM_APPLY_POLICY_FAIL) {
                                                        HDM_LOG("hdm_apply_policy FAIL");
                                                        HDM_LOG_DEBUG("hdm_apply_policy FAIL ret = %d", ret);
                                                }
                                        }
                                }
                                else {
                                        HDM_LOG("TEST Policy is already set");
                                        ret = HDM_RPMB_IS_FULL;
                                }
                        }
                        break;
                case CMD_REMOVE_TEST_POLICY:
                        HDM_LOG("CMD_REMOVE_TEST_POLICY");
                        ret = read_policy_rpmb(&rpmb_data);
                        if (ret == HDM_RPMB_FAIL) {
                                HDM_LOG("RPMB Fail");
                                HDM_LOG_DEBUG("RPMB Fail ret = %d", ret);
                        }
                        else {
                                if (rpmb_data.data[15].isActivated == 1
                                  && !memcmp(rpmb_data.data[15].service_name, "TEST_POLICY", 11)) {
                                        rpmb_data.data[15].isActivated = 0;
                                        memset(rpmb_data.data[15].service_name, 0, JWS_HEADER_SERVICE_NAME_LEN);
                                        rpmb_data.data[15].device_block = 0;
                                        rpmb_data.data[15].compromise_block = 0;
                                        rpmb_data.data[15].policy_version = 0;
                                        rpmb_data.data[15].nextReboot = 0;
                                        ret = save_rpmb_data(rpmb_data);
                                        if (ret != HDM_STATUS_SUCCESS) {
                                                HDM_LOG("Failed to save rpmb data for test");
                                        }
                                        else {
                                                uint32_t test_policty = 0;
                                                hdm_apply_policy(&rpmb_data, &test_policty);
                                                if (ret == HDM_APPLY_POLICY_FAIL) {
                                                        HDM_LOG("hdm_apply_policy FAIL");
                                                        HDM_LOG_DEBUG("hdm_apply_policy FAIL ret = %d", ret);
                                                }
                                        }
                                }
                                else {
                                        HDM_LOG("TEST Policy is already set");
                                        ret = HDM_RPMB_IS_FULL;
                                }
                        }
                        break;
#endif
                default:
                        HDM_LOG("received unknown command!");
                        HDM_LOG_DEBUG("received unknown command: %d", commandId);
                        /* Unknown command ID */
                        ret = HDM_STATUS_FAIL;
                        break;
        }

        return ret;
}
