/*===========================================================================
                    Combined Charging System (CCS): SECC
                                NidNmk.c

                        initiated by Vern, Joseph
                           (since 2019/07/19)
=============================================================================*/
#include "NidNmk.h"
#include "define.h"
#include <time.h>
#include<sys/time.h>
unsigned char buf_log_nidmmk[SIZE_OF_LOG_BUFFER];
/*===========================================================================
FUNCTION: StoreLogMsg
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
#if SAVE_SYS_LOG_MSG_NIDNMK_SWITCH == ENABLE
int StoreLogMsg(unsigned char *DataString)
{
    static unsigned char Buf[1024];
    static time_t CurrentTime;
    static struct tm *tm;
    static struct timeval tv;

    memset(Buf, 0, sizeof(Buf));
    CurrentTime = time(NULL);
    tm = localtime(&CurrentTime);
    gettimeofday(&tv, NULL); // get microseconds, 10^-6

    sprintf(Buf, "echo \"[%04d%02d%02d: %02d:%02d:%02d.%06d][NidNmk]%s\" >> /Storage/SystemLog/[%04d.%02d]SystemLog",
            tm->tm_year + 1900,
            tm->tm_mon + 1,
            tm->tm_mday,
            tm->tm_hour,
            tm->tm_min,
            tm->tm_sec,
            tv.tv_usec,
            DataString,
            tm->tm_year + 1900,
            tm->tm_mon + 1);
    system(Buf);

    DEBUG_PRINTF_NIDNMK_SYSTEM_LOG("[%02d:%02d:%02d.%06d][NidNmk]%s \n",
            tm->tm_hour,
            tm->tm_min,
            tm->tm_sec,
            tv.tv_usec,
            DataString);

    //Reset the buf_log_nidmmk Buffer, i.e. DataString
    memset(buf_log_nidmmk, 0, SIZE_OF_LOG_BUFFER);
}
#endif

void HPAVKeyNMK(uint8_t NMK [], const char *string)

{
    struct sha256 sha256;
    uint8_t digest [SHA256_DIGEST_LENGTH];
    const uint8_t secret [] =
    {
        0x08,
        0x85,
        0x6D,
        0xAF,
        0x7C,
        0xF5,
        0x81,
        0x86
    };
    unsigned rehash = 999;

    SHA256Reset(&sha256);
    SHA256Write(&sha256, string, strlen(string));
    SHA256Write(&sha256, secret, sizeof(secret));
    SHA256Fetch(&sha256, digest);

    while(rehash--)
    {
        SHA256Reset(&sha256);
        SHA256Write(&sha256, digest, sizeof(digest));
        SHA256Fetch(&sha256, digest);
    }

    memcpy(NMK, digest, HPAVKEY_NMK_LEN);
    return;
}


void HPAVKeyNID(uint8_t NID[], const uint8_t NMK[], uint8_t level)

{
    struct sha256 sha256;
    uint8_t digest [SHA256_DIGEST_LENGTH];
    unsigned int rehash = 4;

    SHA256Reset(&sha256);
    SHA256Write(&sha256, NMK, HPAVKEY_NMK_LEN);
    SHA256Fetch(&sha256, digest);

    while(rehash--)
    {
        SHA256Reset(&sha256);
        SHA256Write(&sha256, digest, sizeof(digest));
        SHA256Fetch(&sha256, digest);
    }

    #if 1

        level <<= 4;
        digest [HPAVKEY_NID_LEN - 1] >>= 4;
        digest [HPAVKEY_NID_LEN - 1] |= level;

    #else

        digest [HPAVKEY_NID_LEN - 1] &= ~0xC0;
        digest [HPAVKEY_NID_LEN - 1] |= level << 6;

    #endif

    memcpy(NID, digest, HPAVKEY_NID_LEN);

    return;
}

void SHA256Reset(struct sha256 *sha256)

{
    memset(sha256, 0, sizeof(struct sha256));
    sha256->state [0] = 0x6A09E667;
    sha256->state [1] = 0xBB67AE85;
    sha256->state [2] = 0x3C6EF372;
    sha256->state [3] = 0xA54FF53A;
    sha256->state [4] = 0x510E527F;
    sha256->state [5] = 0x9B05688C;
    sha256->state [6] = 0x1F83D9AB;
    sha256->state [7] = 0x5BE0CD19;
    sha256->extra [0] = 0x80;
    return;
}
void SHA256Block(struct sha256 *sha256, void const *memory)

{
    static const uint32_t K [sizeof(sha256->block)] =
    {
        0x428A2F98,
        0x71374491,
        0xB5C0FBCF,
        0xE9B5DBA5,
        0x3956C25B,
        0x59F111F1,
        0x923F82A4,
        0xAB1C5ED5,
        0xD807AA98,
        0x12835B01,
        0x243185BE,
        0x550C7DC3,
        0x72BE5D74,
        0x80DEB1FE,
        0x9BDC06A7,
        0xC19BF174,
        0xE49B69C1,
        0xEFBE4786,
        0x0FC19DC6,
        0x240CA1CC,
        0x2DE92C6F,
        0x4A7484AA,
        0x5CB0A9DC,
        0x76F988DA,
        0x983E5152,
        0xA831C66D,
        0xB00327C8,
        0xBF597FC7,
        0xC6E00BF3,
        0xD5A79147,
        0x06CA6351,
        0x14292967,
        0x27B70A85,
        0x2E1B2138,
        0x4D2C6DFC,
        0x53380D13,
        0x650A7354,
        0x766A0ABB,
        0x81C2C92E,
        0x92722C85,
        0xA2BFE8A1,
        0xA81A664B,
        0xC24B8B70,
        0xC76C51A3,
        0xD192E819,
        0xD6990624,
        0xF40E3585,
        0x106AA070,
        0x19A4C116,
        0x1E376C08,
        0x2748774C,
        0x34B0BCB5,
        0x391C0CB3,
        0x4ED8AA4A,
        0x5B9CCA4F,
        0x682E6FF3,
        0x748F82EE,
        0x78A5636F,
        0x84C87814,
        0x8CC70208,
        0x90BEFFFA,
        0xA4506CEB,
        0xBEF9A3F7,
        0xC67178F2
    };
    unsigned int pass;
    unsigned int word;
    uint32_t H [sizeof(sha256->state) / sizeof(uint32_t)];
    uint32_t W [sizeof(sha256->block)];
    uint8_t *buffer = (uint8_t *)(memory);

    for(word = 0; word < 16; word++)
    {
        W [word] = 0;
        W [word] |= (uint32_t)(*buffer++) << 24;
        W [word] |= (uint32_t)(*buffer++) << 16;
        W [word] |= (uint32_t)(*buffer++) << 8;
        W [word] |= (uint32_t)(*buffer++) << 0;;
    }

    for(word = word; word < sizeof(sha256->block); word++)
    {
        uint32_t s0 = ROTR(W [word - 15], 7) ^ ROTR(W [word - 15], 18) ^ SHR(W [word - 15], 3);
        uint32_t s1 = ROTR(W [word - 2], 17) ^ ROTR(W [word - 2], 19) ^ SHR(W [word - 2], 10);
        W [word] = W [word - 16] + s0 + W [word - 7] + s1;
    }

    for(word = 0; word < (sizeof(sha256->state) / sizeof(uint32_t)); word++)
    {
        H [word] = sha256->state [word];
    }

    for(pass = 0; pass < sizeof(sha256->block); pass++)
    {
        uint32_t s2 = ROTR(H [0], 2) ^ ROTR(H [0], 13) ^ ROTR(H [0], 22);
        uint32_t maj = (H [0] & H [1]) ^ (H [0] & H [2]) ^ (H [1] & H [2]);
        uint32_t t2 = s2 + maj;
        uint32_t s3 = ROTR(H [4], 6) ^ ROTR(H [4], 11) ^ ROTR(H [4], 25);
        uint32_t ch = (H [4] & H [5]) ^ ((~H [4]) & H [6]);
        uint32_t t1 = H [7] + s3 + ch + K [pass] + W [pass];

        for(word = (sizeof(sha256->state) / sizeof(uint32_t)) - 1; word > 0; word--)
        {
            H [word] = H [word - 1];
        }

        H [0] = t1 + t2;
        H [4] += t1;
    }

    for(word = 0; word < (sizeof(sha256->state) / sizeof(uint32_t)); word++)
    {
        sha256->state [word] += H [word];
    }

    return;
}

void SHA256Write(struct sha256 *sha256, void const *memory, uint16_t extent)

{
    if(extent)
    {
        uint8_t *buffer = (uint8_t *)(memory);
        unsigned int left = sha256->count [0] & 0x3F;
        unsigned int fill = sizeof(sha256->block) - left;
        sha256->count [0] += (uint32_t)(extent);
        sha256->count [0] &= 0xFFFFFFFF;

        if(sha256->count [0] < extent)
        {
            sha256->count [1]++;
        }

        if((left) && (extent >= fill))
        {
            memcpy(sha256->block + left, buffer, fill);
            SHA256Block(sha256, sha256->block);
            extent -= fill;
            buffer += fill;
            left = 0;
        }

        while(extent >= sizeof(sha256->block))
        {
            SHA256Block(sha256, buffer);
            extent -= sizeof(sha256->block);
            buffer += sizeof(sha256->block);
        }

        if(extent)
        {
            memcpy(sha256->block + left, buffer, extent);
        }
    }

    return;
}
void SHAEncode(uint8_t memory[], uint32_t number)

{
    *memory++ = (uint8_t)(number >> 24);
    *memory++ = (uint8_t)(number >> 16);
    *memory++ = (uint8_t)(number >> 8);
    *memory++ = (uint8_t)(number >> 0);
    return;
}

void SHA256Fetch(struct sha256 *sha256, uint8_t digest[])

{
    unsigned int word;
    uint8_t bits [8];
    uint32_t upper = (sha256->count [0] >> 29) | (sha256->count [1] << 3);
    uint32_t lower = (sha256->count [0] << 3);
    uint32_t final = (sha256->count [0] & 0x3F);
    uint32_t extra = (final < 56) ? (56 - final) : (120 - final);

    SHAEncode(&bits[0], upper);
    SHAEncode(&bits[4], lower);
    SHA256Write(sha256, sha256->extra, extra);
    SHA256Write(sha256, bits, sizeof(bits));

    for(word = 0; word < sizeof(sha256->state) / sizeof(uint32_t); word++)
    {
        SHAEncode(digest, sha256->state [word]);
        digest += sizeof(uint32_t);
    }

    memset(sha256, 0, sizeof(struct sha256));
    return;
}