
/*
 * =====================================================================================
 *
 *       Filename:  pebble_utils.c
 *
 *    Description:  PEBBLE utils functions
 *
 *        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_utils.h"

/**
 * @brief
 * hex2int
 * Converts hexadecimal to integer
 *
 * @param[in] *hex - hexadecimal number
 * @return Integer
 */
int64_t hex2int(uint8_t *hex) {
        int64_t val = 0;

        for(int i = 0; i < 8; i++) {
                uint8_t byte = hex[i];

                if (byte >= '0' && byte <= '9') byte = byte - '0';
                else if (byte >= 'a' && byte <='f') byte = byte - 'a' + 10;
                else if (byte >= 'A' && byte <='F') byte = byte - 'A' + 10;
                else if (byte == '"') return val;
                else {
                    PEBBLE_LOG("Invalid character received at the hex string");
                    return -1;
                }

                val = (val << 4) | (byte & 0xF);
        }

        return val;
}

/**
 * @brief
 * get_error_string
 * Get error string from error code
 *
 * @param[in] error_code - Error code
 *
 * @return The error string
 */
uint8_t *get_error_string(uint32_t error_code) {
        switch (error_code) {
                case PEBBLE_STATUS_SUCCESS:
                        return (uint8_t *) "SUCCESS";
                case PEBBLE_STATUS_FAIL:
                        return (uint8_t *) "FAIL";
                case PEBBLE_INVALID_JWS:
                        return (uint8_t *) "INVALID JWS";
                case PEBBLE_JWS_INVALID_FORMAT:
                        return (uint8_t *) "INVALID JWS FORMAT";
                case PEBBLE_JWS_NULL:
                        return (uint8_t *) "NULL JWS";
                case PEBBLE_JWS_INVALID_LENGTH:
                        return (uint8_t *) "INVALID JWS LENGTH";
                case PEBBLE_INVALID_SIGNATURE:
                        return (uint8_t *) "INVALID SIGNATURE";
                case PEBBLE_INVALID_CERTCHAIN:
                        return (uint8_t *) "INVALID CERTIFICATE CHAIN";
                case PEBBLE_INVALID_CA:
                        return (uint8_t *) "INVALID CERTIFICATE AUTHORITY";
                case PEBBLE_COMPUTE_HASH_FAIL:
                        return (uint8_t *) "COMPUTE HASH FAILED";
                case PEBBLE_GEN_DEVICE_ID_FAIL:
                        return (uint8_t *) "DEVICE ID GENERATION FAILED";
                case PEBBLE_KEY_ERROR:
                        return (uint8_t *) "KEY ERROR";
                case PEBBLE_SIGNATURE_ERROR:
                        return (uint8_t *) "SIGNATURE ERROR";
                case PEBBLE_CERT_DRK_FAIL:
                        return (uint8_t *) "DRK CERTIFICATE FAILED";
                case PEBBLE_ALLOC_ERROR:
                        return (uint8_t *) "MEMORY ALLOCATION FAILED";
                case PEBBLE_GEN_RESPONSE_FAIL:
                        return (uint8_t *) "GENERATE RESPONSE FAILED";
                case PEBBLE_BUF_SIZE_ERROR:
                        return (uint8_t *) "BUFFER SIZE ERROR";
                case PEBBLE_CONVERT_DER_CERT_FAIL:
                        return (uint8_t *) "CONVERT DER FAILED";
                case PEBBLE_DEVICE_ID_CHECK_FAIL:
                        return (uint8_t *) "DEVICE ID CHECK FAILED";
                case PEBBLE_DEVICE_OK:
                        return (uint8_t *) "DEVICE INTEGRITY OK";
                case PEBBLE_DEVICE_COMPROMISED:
                        return (uint8_t *) "DEVICE IS COMPROMISED";
                case PEBBLE_UNWRAP_APP_NAME_FAILED:
                        return (uint8_t *) "APP NAME FAILED IN UNWRAP";
                case PEBBLE_UNWRAP_FAILED_WRAP:
                        return (uint8_t *) "WRAP FAILED AFTER UNWRAPPING";
                case PEBBLE_UNWRAP_FAILED_NO_KEY:
                        return (uint8_t *) "UNWRAP FAILED NO KEY";
                case PEBBLE_UNWRAP_FAILED:
                        return (uint8_t *) "UNWRAP GENERIC FAIL";
                case PEBBLE_UNWRAP_VERSION_MISMATCH:
                        return (uint8_t *) "UNWRAP VERSION MISMATCH";
                case PEBBLE_UNWRAP_UNKNOWN:
                        return (uint8_t *) "UNWRAP UNKNOWN ERROR";
                case PEBBLE_UNWRAP_LOAD_KEY_ERROR:
                        return (uint8_t *) "UNWRAP LOAD KEY ERROR";
                case PEBBLE_UNWRAP_KEY_LENGTH_FAILED:
                        return (uint8_t *) "UNWRAP KEY LENGTH FAILED";
                case PEBBLE_JSON_FAIL:
                        return (uint8_t *) "JSON MANIPULATION FAIL";
                case PEBBLE_READ_EM_STATUS_FAIL:
                        return (uint8_t *) "EM STATUS FAIL";
                case PEBBLE_INVALID_CN_CHECK:
                        return (uint8_t *) "INAVALID CN CHECK";
        }
        return (uint8_t *) "UNKNOWN";
}


/**
 * @brief
 * get_numeric_rep
 * Get decimal value for given HEX digit
 *
 * @param[in] byte - HEX digit in byte format
 *
 * @return The decimal representation, -1 if the
 *         digit is invalid
 */
static int get_numeric_rep(unsigned char byte) {
        if (byte >= '0' && byte <= '9')
                byte = byte - '0';
        else if (byte >= 'a' && byte <='f')
                byte = byte - 'a' + 10;
        else if (byte >= 'A' && byte <='F')
                byte = byte - 'A' + 10;
        else
                byte = -1;

        return byte;
}

/**
 * @brief
 * hex_to_byte_stream
 * Transforms given HEX input string into a
 * byte formatted stream.
 * If the input string has odd length, this
 * method fills the last byte with a zeroed
 * nibble on the least significant bits.
 *
 * @param[in]  hex_string - representation of HEX number
 * @param[in]  str_len    - length of the HEX string
 * @param[out] buff       - output pointer to the byte stream
 *
 * @return nothing
 */
void hex_to_byte_stream(uint8_t hex_string[], uint32_t str_len,
                        uint8_t **buff) {
        uint8_t *out = NULL;
        uint32_t size = 0;
        uint32_t pos = 0;

        if (str_len == 0){
                goto exit;
        }

        if (str_len % 2 == 0) {
                size = str_len/2;
        } else {
                size = str_len/2 + 1;
        }

        out = (uint8_t*)TEE_Malloc(size*sizeof(uint8_t), 0);
        if (out == NULL) { // SI-16876
            PEBBLE_LOG("TEE_Malloc failed");
            goto exit;
        }

        for (int i=0; i<str_len && pos < size; i+=2, pos++) {
                uint8_t nibble_1;
                uint8_t nibble_0;

                nibble_1 = get_numeric_rep(hex_string[i]);
                if (nibble_1 < 0) {
                        goto error;
                }

                if (i+2 > str_len) {
                        nibble_0 = 0;
                } else {
                        nibble_0 = get_numeric_rep(hex_string[i+1]);
                        if (nibble_0 < 0) {
                                goto error;
                        }
                }

                out[pos] = nibble_1;
                out[pos] = out[pos] << 4;
                out[pos] = out[pos] | nibble_0;
        }
exit:
        *buff = out;
        return;

error:
        TEE_Free(out);
        out = NULL;
        *buff = out;
}

/**
 * @brief
 * change string "\x5cn" to "\n".
 *
 * @param[in/out]  certificate - pointer to certificate
 *
 * @return nothing
 */
void settle_certificate_to_openssl(uint8_t *certificate)
{
        char *psResult = (char *)certificate;

        while((psResult = strstr(psResult, "\x5cn") )!= NULL)
        {
                strncpy(psResult,"\r\n",2);
                psResult= &psResult[2];
                                
        }
        return ;        
}

/**
 * Debug print functions
 */
#ifdef DEBUG_PEBBLE
void dump_jws_header(tz_pebble_header_t *header) {
        PEBBLE_LOG_DEBUG("#####################################################");
        PEBBLE_LOG_DEBUG("#              header jws                           #");
        PEBBLE_LOG_DEBUG("#####################################################");

        PEBBLE_LOG_DEBUG("service name:  %s", header->service_name);
        PEBBLE_LOG_DEBUG("alg:           %s", header->alg);
        PEBBLE_LOG_DEBUG("version:       %s", header->protocol_version);
        PEBBLE_LOG_DEBUG("x5c.count:     %d", header->x5c_count);

//        for(int i = 0; i < header->x5c_count; i++) {
//                printf("certificate %d:\n",i);
//
//                for(int j=0; j < header->x5c[i].certificate_len; j++) {
//                        printf("%c", header->x5c[i].certificate[j]);
//                }
//                printf("\n");
//        }

        PEBBLE_LOG_DEBUG("#####################################################");
}

void dump_jws_payload(tz_pebble_payload_t *payload) {
        PEBBLE_LOG_DEBUG("#####################################################");
        PEBBLE_LOG_DEBUG("#              payload jws                          #");
        PEBBLE_LOG_DEBUG("#####################################################");

        PEBBLE_LOG_DEBUG("device_id:         %s", payload->device_id);
        PEBBLE_LOG_DEBUG("device_block:      0x%08x", payload->device_block);
        PEBBLE_LOG_DEBUG("compromise_block:  0x%08x", payload->compromise_block);
        PEBBLE_LOG_DEBUG("generic_cmd_cnt:   %d", payload->policy_version);

        PEBBLE_LOG_DEBUG("#####################################################");
}

void dump_jws_sinature(tz_pebble_signature_t *signature) {
        PEBBLE_LOG_DEBUG("#####################################################\n");
        PEBBLE_LOG_DEBUG("#              signature jws                        #\n");
        PEBBLE_LOG_DEBUG("#####################################################\n");

        PEBBLE_LOG_DEBUG("signature: %s", signature->signature);

        PEBBLE_LOG_DEBUG("#####################################################");
}

void printRange(uint8_t *buff, int len, char *tag) {
        uint8_t str[len+1];
        TEE_MemFill(str, 0, len);
        TEE_MemMove(str, buff, len);
        str[len] = '\0';
        PEBBLE_LOG("[%s] %s", tag, str);
}

void printLongRange(uint8_t *buff, int len, char *tag) {
	const int range= 120;
		for (; len > 0; len -= range) {
				printRange(buff, len < range ? len : range, tag);
				buff += range;
		}
}

void printRangeHex(uint8_t *buff, int len, char *tag) {
        uint8_t str[2*len+1];
        TEE_MemFill(str, 0, 2*len + 1);
        for (int i = 0; i < len; i++) {
                sprintf((char *)(str + i*2), "%02X", *(buff + i));
        }
        str[2*len] = '\0';
        PEBBLE_LOG("[%s]%s", tag, str);
}

void printLongRangeHex(uint8_t *buff, int len, char *tag) {
        const int range= 32;
        for (; len > 0; len -= range) {
                printRangeHex(buff, len < range ? len : range, tag);
                buff += range;
        }
}

#endif
