
/*
 * =====================================================================================
 *
 *       Filename:  pebble_hash.c
 *
 *    Description:  PEBBLE hash manipulation
 *
 *        Version:  1.0
 *        Created:  06/03/2020
 *       Revision:  none
 *       Compiler:  gcc
 *
 *        Company:  Samsung Electronics
 *        Copyright (c) 2020 by Samsung Electronics, All rights reserved.
 *
 * =====================================================================================
 */

/** Includes */
#include "pebble_hash.h"

/**
 * @brief
 * compute_hash
 * Computes hash of given type for the given input string.
 *
 * @param[in]  msg     - message to hash
 * @param[in]  msg_len - length of the message
 * @param[out] out     - hashed message
 * @param[out] out_len - length of the hashed message
 * @param[in]  evp_md  - hash function to use
 *
 * @return PEBBLE status code
 */
pebble_return_code_t compute_hash(uint8_t *msg, uint32_t msg_len, uint8_t *out, uint32_t *out_len, const EVP_MD *evp_md) {
        PEBBLE_LOG_DEBUG("compute_hash()");

        pebble_return_code_t ret = PEBBLE_COMPUTE_HASH_FAIL;
        EVP_MD_CTX *evp_md_ctx;
        evp_md_ctx = EVP_MD_CTX_new();

        if (evp_md_ctx == NULL) { // SI-16863
            PEBBLE_LOG("FAILED to new evp_md_ctx");
            return PEBBLE_COMPUTE_HASH_FAIL;
        }

        if (!EVP_DigestInit_ex(evp_md_ctx, evp_md, NULL)) {
                PEBBLE_LOG("FAILED to initialize context to alg");
                ret = PEBBLE_COMPUTE_HASH_FAIL;
                goto error;
        }

        if (!EVP_DigestUpdate(evp_md_ctx, msg, msg_len)) {
                PEBBLE_LOG("FAILED to compute hash");
                ret = PEBBLE_COMPUTE_HASH_FAIL;
                goto error;
        }

        if (!EVP_DigestFinal_ex(evp_md_ctx, out, out_len)) {
                PEBBLE_LOG("FAILED to copy hashed value to output");
                ret = PEBBLE_COMPUTE_HASH_FAIL;
                goto error;
        }

        ret = PEBBLE_STATUS_SUCCESS;
error:
        EVP_MD_CTX_free(evp_md_ctx);
        return ret;
}

/**
 * @brief
 * gen_drk_device_id
 * Generates Device ID to match with DRKv2 Certificate
 *
 * @param[in]     hash_imei     - Hashed IMEI/MAC Address
 * @param[in]     serial_number - Serial Number
 * @param[out]    device_id     - B64(H(H(ID) | H(SN))), ID = ((IMEI_1 | IMEI_2) or MAC_ADDR)
 * @param[in|out] device_id_len - B64 device_id length is fixed value 44 (DEVICE_ID_B64_LEN)
 *
 * @return PEBBLE status code
 */
pebble_return_code_t gen_drk_device_id(uint8_t *hash_imei, uint8_t *serial_number, uint8_t *device_id, uint32_t *device_id_len) {

        PEBBLE_LOG_DEBUG("gen_drk_device_id()");

        pebble_return_code_t ret = PEBBLE_STATUS_FAIL;
        uint32_t device_id_b64_len;
        uint32_t sha256_id_len = SHA256_DIGEST_LENGTH;
        uint32_t sha256_serial_number_len = SHA256_DIGEST_LENGTH;

        SHA256_CTX sha256;
        uint8_t imei_concat[(2 * IMEI_LEN) + 1] = {0,};
        uint8_t sha256_id[SHA256_DIGEST_LENGTH + 1] = {0,};
        uint8_t sha256_serial_number[SHA256_DIGEST_LENGTH + 1] = {0,};
        uint8_t sha256_device_id[SHA256_DIGEST_LENGTH + 1] = {0,};
        uint8_t b64_sha256_device_id[DEVICE_ID_B64_LEN + 1] = {0,};

        /* H(ID) */
        TEE_MemMove(sha256_id, hash_imei, SHA256_DIGEST_LENGTH);

        /* H(SN) */
        ret = compute_hash(serial_number, strlen((char*)serial_number), sha256_serial_number, &sha256_serial_number_len, EVP_sha256());
        if (ret != PEBBLE_STATUS_SUCCESS || sha256_serial_number_len != SHA256_DIGEST_LENGTH) {
                PEBBLE_LOG("Fail to compute H(SN)");
                PEBBLE_LOG_DEBUG("Fail sha256_serial_number_len = %d", sha256_serial_number_len);
                ret = PEBBLE_GEN_DEVICE_ID_FAIL;
                goto exit;
        }

        /* H(H(ID) | H(SN)) */
        SHA256_Init(&sha256);
        SHA256_Update(&sha256, sha256_id, sha256_id_len);
        SHA256_Update(&sha256, sha256_serial_number, sha256_serial_number_len);
        ret = SHA256_Final(sha256_device_id, &sha256);
        if (ret == 0) {
                PEBBLE_LOG("Fail to compute H(H(ID), H(SN))");
                PEBBLE_LOG_DEBUG("Fail SHA256_Final ret = %d", ret);
                ret = PEBBLE_GEN_DEVICE_ID_FAIL;
                goto exit;
        }

        /* B64(H(H(ID), H(SN))) */
        ret = base64url_encode(sha256_device_id, SHA256_DIGEST_LENGTH, b64_sha256_device_id, &device_id_b64_len);
        if (device_id_b64_len != DEVICE_ID_B64_LEN) {
                PEBBLE_LOG("Fail to compute B64(H(H(ID), H(SN)))");
                PEBBLE_LOG_DEBUG("device_id_b64_len = %d", device_id_b64_len);
                ret = PEBBLE_GEN_DEVICE_ID_FAIL;
                goto exit;
        }

        /* Check external device_id allocation size */
        if (*device_id_len < DEVICE_ID_B64_LEN) {
                PEBBLE_LOG("device_id Out of Memory");
                PEBBLE_LOG_DEBUG("device_id_len = %d", *device_id_len);
                ret = PEBBLE_COMPUTE_HASH_FAIL;
                goto exit;
        }

        TEE_MemMove(device_id, b64_sha256_device_id, device_id_b64_len);
        *device_id_len = device_id_b64_len;

        PEBBLE_LOG_DEBUG("calculed device_id = %s", device_id);
        ret = PEBBLE_STATUS_SUCCESS;
exit:
        return ret;
}
