#include "protocol.h"


using namespace zigbee_protocol;

Protocol::Protocol(){
    _config = Config::instance();
    if (_config->Get("Protocol","hmac_verify_key").toString().isEmpty())
        _config->Set("Protocol","hmac_verify_key",hmac_verify_key);
    else
        hmac_verify_key = _config->Get("Protocol","hmac_verify_key").toString();
    if (_config->GetArray("Protocol","pre_hmac_verify_key").toStringList().isEmpty())
        _config->SetArray("Protocol","pre_hmac_verify_key",pre_hmac_verify_key);
    else
        pre_hmac_verify_key = _config->GetArray("Protocol","pre_hmac_verify_key").toStringList();
}

Protocol::~Protocol()
{
    _config->Set("Protocol","hmac_verify_key",hmac_verify_key);
    _config->SetArray("Protocol","pre_hmac_verify_key",pre_hmac_verify_key);
}

Protocol* Protocol::getInstance()
{
    static Protocol protocol_instance;
    return &protocol_instance;
}

void Protocol::protocal_wrapper(data_frame *frame, u8 type, u16 length, u8 *data, bool use_crc)
{
    frame->head=DATA_FRAME_HEAD;
    frame->type=type;
    frame->data_length = length;
    if (use_crc)
    {
        frame->use_crc=0xDD;
        u16 crc = crc16_xmodem(data, length);
        frame->crc = crc;
    }
    else
    {
        frame->use_crc=0xFF;
        frame->crc = 0xFFFF;
    }
    memcpy(frame->data,data,length);
}

void Protocol::base_frame_maker(void *in_frame, base_frame *out_frame, u16 dest_addr,device *dev,u16 node_addr)
{
    out_frame->head = BASE_FRAME_HEAD;
    out_frame->ori_addr= dev->addr;
    out_frame->des_addr = dest_addr;
    out_frame->node_addr = node_addr;
    if (dev->id < BASE_FRAME_RESET_NUM)
    {
        if (dev->id == 0)
        {
            out_frame->reset_num = 0xFF;
            do{
                srand(static_cast<u16>(time(nullptr)));
                out_frame->rand_num = rand() & 0xFF;
            }while(out_frame->rand_num==0);
            dev->rand_num = out_frame->rand_num;
            out_frame->reset_num = 0xDD;
        }
        else
        {
            out_frame->rand_num = dev->rand_num;
            out_frame->reset_num = 0xFF;
        }
        out_frame->id = ++(dev->id);
    }
    else
    {
        out_frame->id = dev->id = 0;
        out_frame->reset_num = 0xDD;
        do{
            srand(static_cast<u16>(time(nullptr)));
            out_frame->rand_num = rand() & 0xFF;
        }while(out_frame->rand_num==0);
    }
    switch (*(u16 *)in_frame)
    {
    case 0xAAAB:
    {
        out_frame->length = ((digi_env *)in_frame)->length + BASE_FRAME_PREFIX_LEN;
        break;
    }
    case 0xAAAC:
    {
        out_frame->length = ((ssl_frame *)in_frame)->length + BASE_FRAME_PREFIX_LEN;
        break;
    }
    case 0xAAAA:
    {
        out_frame->length = ((data_frame *)in_frame)->data_length + DATA_FRAME_PREFIX_LEN + BASE_FRAME_PREFIX_LEN;
        break;
    }
    case 0xAAAE:
    {
        out_frame->length = ((hmac_frame *)in_frame)->length + BASE_FRAME_PREFIX_LEN;
        break;
    }
    case 0xAAAF:
    {
        out_frame->length = ((crypto_zdata_frame *)in_frame)->length + BASE_FRAME_PREFIX_LEN;
        break;
    }
    }
    memcpy(out_frame->data, in_frame ,out_frame->length - BASE_FRAME_PREFIX_LEN);
}

bool Protocol::base_frame_parser(base_frame *in_frame, void **out_frame , device *dev)
{
    if (in_frame->des_addr == SELF_ADDR && (dev->id == 0||in_frame->reset_num==0xDD||((in_frame->id > dev->id && in_frame->id<=BASE_FRAME_RESET_NUM))))
    {
        dev->addr = in_frame->ori_addr;
        dev->id = in_frame->id;
        dev->rand_num = in_frame->rand_num;
        *out_frame = (void *)in_frame->data;
        return true;
    }
    else
        return false;
}

uint16_t Protocol::crc16_xmodem(const uint8_t *buffer, uint32_t buffer_length)
{
    uint8_t c, treat, bcrc;
    uint16_t wcrc = 0;

    for(uint32_t i = 0; i < buffer_length; i++)
    {
        c = buffer[i];
        for(uint8_t j = 0; j < 8; j++)
        {
            treat = c & 0x80;
            c <<= 1;
            bcrc = (wcrc >> 8) & 0x80;
            wcrc <<= 1;
            if(treat != bcrc)
                wcrc ^= 0x1021;
        }
    }
    return wcrc;
}

bool Protocol::bytecmp(u8 *a, u8 *b, u16 length)
{
    return memcmp(a,b,length)==0;
}

bool Protocol::data_frame_verify(data_frame *frame)
{
    if(frame->use_crc!=0xFF)
        return !(frame->crc-crc16_xmodem(frame->data,frame->data_length));
    else
        return true;
}

void Protocol::zigbee_data_encrypt(data_frame *data, crypto_zdata_frame *zdata,
                                     bool (* SM4_encrypt)(u8 *key_origin, u32 key_len, u8 *in_origin, u32 in_len, u8 *out, u32 *out_len),
                                   QString en_key)
{
    u32 len;
    zdata->head=CRYPTO_ZDATA_FRAME_HEAD;
    QByteArray key = QByteArray::fromHex(en_key == "" ? hmac_verify_key.toLatin1() : en_key.toLatin1());
    SM4_encrypt((u8 *)key.data(), 16, (u8 *)data, data->data_length + DATA_FRAME_PREFIX_LEN, zdata->data,&len);
    zdata->length = len + CRYPTO_ZDATA_FRAME_PREFIX_LEN;
}

bool Protocol::zigbee_data_dectypt(data_frame *data, crypto_zdata_frame *zdata,
                                       bool (* SM4_decrypt)(u8 *key_origin, u32 key_len, u8 *in, u32 in_len, u8 *out, u32 *out_len),
                                   QString en_key)
{
    int total_len = zdata->length;
    if (total_len - CRYPTO_ZDATA_FRAME_PREFIX_LEN<=0)
        return false;
    u32 len, msglen = total_len - CRYPTO_ZDATA_FRAME_PREFIX_LEN;
    QByteArray key = QByteArray::fromHex(en_key == "" ? hmac_verify_key.toLatin1() : en_key.toLatin1());
    SM4_decrypt((u8 *)key.data(), 16, zdata->data, msglen, (u8 *)data, &len);
    return true;
}

void Protocol::HMAC_identify(device *self, device *node, hmac_frame *hframe,
                             void (*sendTonode)(ZigbeeFrame &data),
                             void (*SM3_HMAC)(u8 *key, int keylen,u8 *input, int ilen,u8 output[32] ))
{
    if (hframe==NULL)
        return;
    u8 hmac[33]="";
    ZigbeeFrame zf(0x81,0x81,node->addr);
    QByteArray key = QByteArray::fromHex(hmac_verify_key.toLatin1());
    SM3_HMAC((u8 *)key.data(),16,&hframe->value,1,hmac);
    if (bytecmp(hframe->hmac,hmac,32))
    {
        new_data_frame(15) data;
        memset(&data, 0, sizeof(data));
        protocal_wrapper((data_frame *)&data, 0, 10, (u8 *)"Identified", 0);
        node->verified = 1;
        new_base_frame(15 + BASE_FRAME_PREFIX_LEN) bframe;
        memset(&bframe, 0, sizeof(bframe));
        base_frame_maker(&data, (base_frame *)&bframe, node->addr, self);
        zf.setData((u8 *)&bframe, bframe.length);
        sendTonode(zf);
        printf("Node %X had passed HMAC identification.", node->addr);
    }
}

void Protocol::HMAC_changeVerifykey(u8 key[16], device* self, device *node, void (*sendTonode)(ZigbeeFrame &data),
                                    bool (* SM4_encrypt)(u8 *key_origin, u32 key_len, u8 *in_origin, u32 in_len, u8 *out, u32 *out_len))
{
    ZigbeeFrame zf(0x82,0x82,node->addr);
    new_data_frame(16) data;
    memset(&data, 0, sizeof(data));
    protocal_wrapper((data_frame *)&data, 0, 16, key, 0);
    new_crypto_zdata_frame(48) zdata;
    memset(&zdata, 0, sizeof(zdata));
    zigbee_data_encrypt((data_frame *)&data, (crypto_zdata_frame *)&zdata, SM4_encrypt);
    new_base_frame(48 + BASE_FRAME_PREFIX_LEN) bframe;
    memset(&bframe, 0, sizeof(bframe));
    base_frame_maker(&zdata, (base_frame *)&bframe, node->addr, self);
    zf.setData((u8 *)&bframe, bframe.length);
    sendTonode(zf);
    printf("Node %X's verify key has changed.", node->addr);
}
