/*

 * Copyright (C) 2023-2024 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */



#include "attribute.h"



#include "securec.h"



#include "adaptor_log.h"

#include "adaptor_memory.h"



#ifdef IAM_TEST_ENABLE

#define IAM_STATIC

#else

#define IAM_STATIC static

#endif



AttributeKey g_attributeKeyArray[] = {

    ATTR_RESULT_CODE,

    ATTR_SIGNATURE,

    ATTR_TEMPLATE_ID,

    ATTR_REMAIN_ATTEMPTS,

    ATTR_LOCKOUT_DURATION,

    ATTR_SCHEDULE_ID,

    ATTR_DATA,

    ATTR_PIN_SUB_TYPE,

    ATTR_ACL,

    ATTR_TIME_STAMP,

    ATTR_ROOT_SECRET,

    ATTR_ROOT,

    ATTR_LOCAL_UDID,

    ATTR_PEER_UDID,

    ATTR_PUBLIC_KEY,

    ATTR_CHALLENGE,

    ATTR_TEMPLATE_ID_LIST,

    ATTR_OLD_ROOT_SECRET,

    ATTR_AUTH_PURPOSE,

    PIN_ATTR_MSG_ID,

    PIN_ATTR_ALGO_VERSION,

    PIN_ATTR_ALGO_PARAM,

    PIN_ATTR_KEK_SALT,

    PIN_ATTR_KEK_IV,

    PIN_ATTR_KEK_SECRET,

    PIN_ATTR_KEK_TAG,

};



#define ARRAY_LENGTH(array) (uint32_t)(sizeof(array) / sizeof((array)[0]))

#define ATTRIBUTE_LEN (ARRAY_LENGTH(g_attributeKeyArray))



typedef struct {

    Uint8Array *values[ATTRIBUTE_LEN];

} AttributeImpl;



IAM_STATIC uint32_t Ntohl32(uint32_t in)

{

    return in;

}



IAM_STATIC uint32_t Htonl32(uint32_t in)

{

    return in;

}



IAM_STATIC uint64_t Ntohl64(uint64_t in)

{

    return in;

}



IAM_STATIC uint64_t Htonl64(uint64_t in)

{

    return in;

}



IAM_STATIC void Ntohl64Array(Uint64Array *array)

{

    for (uint32_t i = 0; i < array->len; i++) {

        array->data[i] = Ntohl64(array->data[i]);

    }

}



IAM_STATIC void Htonl64Array(Uint64Array *array)

{

    for (uint32_t i = 0; i < array->len; i++) {

        array->data[i] = Htonl64(array->data[i]);

    }

}



IAM_STATIC ResultCode GetAttributeIndex(AttributeKey key, uint32_t *index)

{

    for (uint32_t i = 0; i < ATTRIBUTE_LEN; ++i) {

        if (g_attributeKeyArray[i] == key) {

            *index = i;

            return RESULT_SUCCESS;

        }

    }



    return RESULT_GENERAL_ERROR;

}



IAM_STATIC ResultCode ReadDataFromMsg(const Uint8Array msg, uint32_t *readIndex, Uint8Array *retData)

{

    if (msg.len <= *readIndex) {

        LOG_ERROR("msg length is not enough");

        return RESULT_GENERAL_ERROR;

    }



    if (msg.len - *readIndex < retData->len) {

        LOG_ERROR("remain data length is not enough");

        return RESULT_GENERAL_ERROR;

    }



    if (memcpy_s(retData->data, retData->len, msg.data + *readIndex, retData->len) != EOK) {

        LOG_ERROR("memcpy_s fail");

        return RESULT_GENERAL_ERROR;

    }



    *readIndex += retData->len;

    return RESULT_SUCCESS;

}



IAM_STATIC ResultCode ReadUint32FromMsg(const Uint8Array msg, uint32_t *readIndex, uint32_t *retValue)

{

    uint32_t netOrderValue;

    Uint8Array uint8Data = { (uint8_t *)&netOrderValue, sizeof(netOrderValue) };

    ResultCode result = ReadDataFromMsg(msg, readIndex, &uint8Data);

    if (result != RESULT_SUCCESS) {

        LOG_ERROR("read data fail");

        return RESULT_GENERAL_ERROR;

    }



    *retValue = Ntohl32(netOrderValue);

    return RESULT_SUCCESS;

}



IAM_STATIC ResultCode WriteDataToMsg(Uint8Array *msg, uint32_t *writeIndex, const Uint8Array data)

{

    if (msg->len <= *writeIndex) {

        LOG_ERROR("msg length is not enough");

        return RESULT_GENERAL_ERROR;

    }



    if (msg->len - *writeIndex < data.len) {

        LOG_ERROR("remain data size is not enough");

        return RESULT_GENERAL_ERROR;

    }



    if (memcpy_s(msg->data + *writeIndex, msg->len - *writeIndex, data.data, data.len) != EOK) {

        LOG_ERROR("memcpy_s fail");

        return RESULT_GENERAL_ERROR;

    }



    *writeIndex += data.len;

    return RESULT_SUCCESS;

}



IAM_STATIC ResultCode WriteUInt32ToMsg(Uint8Array *msg, uint32_t *writeIndex, uint32_t value)

{

    uint32_t netOrderValue = Htonl32(value);

    ResultCode result =

        WriteDataToMsg(msg, writeIndex, (Uint8Array){ (uint8_t *)&netOrderValue, sizeof(netOrderValue) });

    if (result != RESULT_SUCCESS) {

        LOG_ERROR("write data fail");

        return RESULT_GENERAL_ERROR;

    }

    return RESULT_SUCCESS;

}



IAM_STATIC ResultCode CheckAddReadIndex(const Uint8Array msg, uint32_t *readIndex, uint32_t length)

{

    if (msg.len <= (*readIndex)) {

        LOG_ERROR("msg length is not enough");

        return RESULT_GENERAL_ERROR;

    }



    if ((msg.len - (*readIndex)) < length) {

        LOG_ERROR("remain data length is not enough");

        return RESULT_GENERAL_ERROR;

    }



    (*readIndex) += length;

    return RESULT_SUCCESS;

}



IAM_STATIC ResultCode ParseAttributeSerializedMsgInner(Attribute *attribute, const Uint8Array msg,

    const Uint8Array *readBuffer)

{

    uint32_t readIndex = 0;

    while (readIndex < msg.len) {

        uint32_t type;

        ResultCode readTypeResult = ReadUint32FromMsg(msg, &readIndex, &type);

        IF_TRUE_LOGE_AND_RETURN_VAL(readTypeResult != RESULT_SUCCESS, RESULT_GENERAL_ERROR);



        uint32_t length;

        ResultCode readLengthResult = ReadUint32FromMsg(msg, &readIndex, &length);

        IF_TRUE_LOGE_AND_RETURN_VAL(readLengthResult != RESULT_SUCCESS, RESULT_GENERAL_ERROR);



        if (length > readBuffer->len) {

            LOG_INFO("attribute:%{public}u too long:%{public}u, skip", type, length);

            ResultCode checkAddReadIndexResult = CheckAddReadIndex(msg, &readIndex, length);

            IF_TRUE_LOGE_AND_RETURN_VAL(checkAddReadIndexResult != RESULT_SUCCESS, RESULT_GENERAL_ERROR);

            continue;

        }



        Uint8Array readData = { readBuffer->data, length };

        if (length > 0) {

            ResultCode readDataResult = ReadDataFromMsg(msg, &readIndex, &readData);

            IF_TRUE_LOGE_AND_RETURN_VAL(readDataResult != RESULT_SUCCESS, RESULT_GENERAL_ERROR);

        }



        uint32_t attributeIndex;

        if (GetAttributeIndex(type, &attributeIndex) != RESULT_SUCCESS) {

            LOG_INFO("attribute:%{public}u not found, skip", type);

            continue;

        }



        ResultCode setAttrResult = SetAttributeUint8Array(attribute, type, readData);

        IF_TRUE_LOGE_AND_RETURN_VAL(setAttrResult != RESULT_SUCCESS, RESULT_GENERAL_ERROR);

    }



    return RESULT_SUCCESS;

}



IAM_STATIC ResultCode ParseAttributeSerializedMsg(Attribute *attribute, const Uint8Array msg)

{

    Uint8Array *readBuffer = CreateUint8ArrayBySize(MAX_EXECUTOR_MSG_LEN);

    IF_TRUE_LOGE_AND_RETURN_VAL(readBuffer == NULL, RESULT_GENERAL_ERROR);



    ResultCode result = ParseAttributeSerializedMsgInner(attribute, msg, readBuffer);

    if (result != RESULT_SUCCESS) {

        LOG_ERROR("ParseAttributeSerializedMsgInner fail");

    }

    DestroyUint8Array(&readBuffer);



    return result;

}



Attribute *CreateEmptyAttribute(void)

{

    AttributeImpl *attribute = Malloc(sizeof(AttributeImpl));

    IF_TRUE_LOGE_AND_RETURN_VAL(attribute == NULL, NULL);

    (void)memset_s(attribute, sizeof(AttributeImpl), 0, sizeof(AttributeImpl));



    return attribute;

}



Attribute *CreateAttributeFromSerializedMsg(const Uint8Array msg)

{

    IF_TRUE_LOGE_AND_RETURN_VAL(IS_ARRAY_NULL(msg), NULL);



    Attribute *attribute = CreateEmptyAttribute();

    if (attribute == NULL) {

        LOG_ERROR("CreateEmptyAttribute failed");

        return NULL;

    }



    ResultCode result = ParseAttributeSerializedMsg(attribute, msg);

    if (result != RESULT_SUCCESS) {

        LOG_ERROR("ParseAttributeSerializedMsg failed");

        FreeAttribute((Attribute **)&attribute);

        return NULL;

    }



    return attribute;

}



ResultCode GetAttributeSerializedMsg(const Attribute *attributePublic, Uint8Array *retMsg)

{

    IF_TRUE_LOGE_AND_RETURN_VAL(attributePublic == NULL, RESULT_BAD_PARAM);

    IF_TRUE_LOGE_AND_RETURN_VAL(retMsg == NULL, RESULT_BAD_PARAM);

    IF_TRUE_LOGE_AND_RETURN_VAL(IS_ARRAY_NULL(*retMsg), RESULT_BAD_PARAM);



    const AttributeImpl *attribute = (const AttributeImpl *)attributePublic;

    uint32_t writeIndex = 0;

    for (uint32_t i = 0; i < ATTRIBUTE_LEN; i++) {

        Uint8Array *array = attribute->values[i];

        if (array == NULL) {

            continue;

        }



        ResultCode writeTypeResult = WriteUInt32ToMsg(retMsg, &writeIndex, g_attributeKeyArray[i]);

        IF_TRUE_LOGE_AND_RETURN_VAL(writeTypeResult != RESULT_SUCCESS, RESULT_GENERAL_ERROR);



        ResultCode writeLengthResult = WriteUInt32ToMsg(retMsg, &writeIndex, array->len);

        IF_TRUE_LOGE_AND_RETURN_VAL(writeLengthResult != RESULT_SUCCESS, RESULT_GENERAL_ERROR);



        if (array->len == 0) {

            continue;

        }

        ResultCode writeDataResult =

            WriteDataToMsg(retMsg, &writeIndex, *array);

        IF_TRUE_LOGE_AND_RETURN_VAL(writeDataResult != RESULT_SUCCESS, RESULT_GENERAL_ERROR);

    }



    retMsg->len = writeIndex;

    return RESULT_SUCCESS;

}



void FreeAttribute(Attribute **attribute)

{

    IF_TRUE_LOGE_AND_RETURN(attribute == NULL);

    IF_TRUE_LOGE_AND_RETURN(*attribute == NULL);

    AttributeImpl *impl = (AttributeImpl *)*attribute;

    for (uint32_t i = 0; i < ATTRIBUTE_LEN; ++i) {

        DestroyUint8Array(&impl->values[i]);

    }



    IAM_FREE_AND_SET_NULL(*attribute);

}



ResultCode GetAttributeUint32(const Attribute *attribute, AttributeKey key, uint32_t *value)

{

    IF_TRUE_LOGE_AND_RETURN_VAL(attribute == NULL, RESULT_BAD_PARAM);

    IF_TRUE_LOGE_AND_RETURN_VAL(value == NULL, RESULT_BAD_PARAM);



    uint32_t netOrderValue;

    Uint8Array uint32Data = { (uint8_t *)&netOrderValue, sizeof(netOrderValue) };

    ResultCode getAttrResult = GetAttributeUint8Array(attribute, key, &uint32Data);

    IF_TRUE_LOGE_AND_RETURN_VAL(getAttrResult != RESULT_SUCCESS, RESULT_GENERAL_ERROR);

    IF_TRUE_LOGE_AND_RETURN_VAL(uint32Data.len != sizeof(netOrderValue), RESULT_GENERAL_ERROR);



    *value = Ntohl32(netOrderValue);

    return RESULT_SUCCESS;

}



ResultCode SetAttributeUint32(Attribute *attribute, AttributeKey key, const uint32_t value)

{

    IF_TRUE_LOGE_AND_RETURN_VAL(attribute == NULL, RESULT_BAD_PARAM);



    uint32_t netOrderValue = Htonl32(value);

    ResultCode result =

        SetAttributeUint8Array(attribute, key, (Uint8Array) { (uint8_t *)&netOrderValue, sizeof(netOrderValue) });

    IF_TRUE_LOGE_AND_RETURN_VAL(result != RESULT_SUCCESS, RESULT_GENERAL_ERROR);



    return result;

}



ResultCode GetAttributeInt32(const Attribute *attribute, AttributeKey key, int32_t *retValue)

{

    IF_TRUE_LOGE_AND_RETURN_VAL(attribute == NULL, RESULT_BAD_PARAM);

    IF_TRUE_LOGE_AND_RETURN_VAL(retValue == NULL, RESULT_BAD_PARAM);



    return GetAttributeUint32(attribute, key, (uint32_t *)retValue);

}



ResultCode SetAttributeInt32(Attribute *attribute, AttributeKey key, const int32_t value)

{

    IF_TRUE_LOGE_AND_RETURN_VAL(attribute == NULL, RESULT_BAD_PARAM);



    return SetAttributeUint32(attribute, key, (uint32_t)value);

}



ResultCode GetAttributeUint64(const Attribute *attribute, AttributeKey key, uint64_t *retValue)

{

    IF_TRUE_LOGE_AND_RETURN_VAL(attribute == NULL, RESULT_BAD_PARAM);

    IF_TRUE_LOGE_AND_RETURN_VAL(retValue == NULL, RESULT_BAD_PARAM);



    uint64_t netOrderValue;

    Uint8Array uint64Data = { (uint8_t *)&netOrderValue, sizeof(netOrderValue) };

    ResultCode getAttrResult = GetAttributeUint8Array(attribute, key, &uint64Data);

    IF_TRUE_LOGE_AND_RETURN_VAL(getAttrResult != RESULT_SUCCESS, RESULT_GENERAL_ERROR);

    IF_TRUE_LOGE_AND_RETURN_VAL(uint64Data.len != sizeof(netOrderValue), RESULT_GENERAL_ERROR);



    *retValue = Ntohl64(netOrderValue);

    return RESULT_SUCCESS;

}



ResultCode SetAttributeUint64(Attribute *attribute, AttributeKey key, const uint64_t value)

{

    IF_TRUE_LOGE_AND_RETURN_VAL(attribute == NULL, RESULT_BAD_PARAM);



    uint64_t netOrderValue = Htonl64(value);

    ResultCode setAttrResult =

        SetAttributeUint8Array(attribute, key, (Uint8Array){ (uint8_t *)&netOrderValue, sizeof(netOrderValue) });

    IF_TRUE_LOGE_AND_RETURN_VAL(setAttrResult != RESULT_SUCCESS, RESULT_GENERAL_ERROR);



    return RESULT_SUCCESS;

}



ResultCode GetAttributeLength(const Attribute *attribute, AttributeKey key, uint32_t *len)

{

    IF_TRUE_LOGE_AND_RETURN_VAL(attribute == NULL, RESULT_BAD_PARAM);

    IF_TRUE_LOGE_AND_RETURN_VAL(len == NULL, RESULT_BAD_PARAM);

    

    const AttributeImpl *attributePri = (const AttributeImpl *)attribute;

    uint32_t attributeIndex;

    ResultCode getAttrIndexResult = GetAttributeIndex(key, &attributeIndex);

    IF_TRUE_LOGE_AND_RETURN_VAL(getAttrIndexResult != RESULT_SUCCESS, RESULT_GENERAL_ERROR);



    if (attributePri->values[attributeIndex] == NULL) {

        LOG_ERROR("data is not set");

        return RESULT_GENERAL_ERROR;

    }

    *len = attributePri->values[attributeIndex]->len;

    return RESULT_SUCCESS;

}



ResultCode GetAttributeUint8Array(const Attribute *attributePub, AttributeKey key, Uint8Array *retData)

{

    IF_TRUE_LOGE_AND_RETURN_VAL(attributePub == NULL, RESULT_BAD_PARAM);

    IF_TRUE_LOGE_AND_RETURN_VAL(retData == NULL, RESULT_BAD_PARAM);

    IF_TRUE_LOGE_AND_RETURN_VAL(IS_ARRAY_NULL(*retData), RESULT_BAD_PARAM);



    const AttributeImpl *attribute = (const AttributeImpl *)attributePub;



    uint32_t attributeIndex;

    ResultCode getAttrIndexResult = GetAttributeIndex(key, &attributeIndex);

    IF_TRUE_LOGE_AND_RETURN_VAL(getAttrIndexResult != RESULT_SUCCESS, RESULT_GENERAL_ERROR);



    if (attribute->values[attributeIndex] == NULL) {

        LOG_ERROR("data is not set");

        return RESULT_GENERAL_ERROR;

    }



    if (attribute->values[attributeIndex]->data != NULL && attribute->values[attributeIndex]->len != 0) {

        errno_t memcpyRet = memcpy_s(retData->data, retData->len, attribute->values[attributeIndex]->data,

            attribute->values[attributeIndex]->len);

        IF_TRUE_LOGE_AND_RETURN_VAL(memcpyRet != EOK, RESULT_GENERAL_ERROR);

        retData->len = attribute->values[attributeIndex]->len;

    } else {

        LOG_INFO("the current data is an empty array");

        retData->len = 0;

    }



    return RESULT_SUCCESS;

}



ResultCode SetAttributeUint8Array(Attribute *attributePub, AttributeKey key, const Uint8Array data)

{

    IF_TRUE_LOGE_AND_RETURN_VAL(attributePub == NULL, RESULT_BAD_PARAM);

    IF_TRUE_LOGE_AND_RETURN_VAL(!IS_ARRAY_VALID(data), RESULT_BAD_PARAM);



    AttributeImpl *attribute = (AttributeImpl *)attributePub;



    uint32_t attributeIndex;

    ResultCode getAttrIndexResult = GetAttributeIndex(key, &attributeIndex);

    IF_TRUE_LOGE_AND_RETURN_VAL(getAttrIndexResult != RESULT_SUCCESS, RESULT_GENERAL_ERROR);



    DestroyUint8Array(&attribute->values[attributeIndex]);

    attribute->values[attributeIndex] = CreateUint8ArrayByData(data.data, data.len);

    if (attribute->values[attributeIndex] == NULL) {

        LOG_ERROR("CreateUint8ArrayByData fail");

        return RESULT_GENERAL_ERROR;

    }



    return RESULT_SUCCESS;

}



ResultCode GetAttributeUint64Array(const Attribute *attribute, AttributeKey key, Uint64Array *retData)

{

    IF_TRUE_LOGE_AND_RETURN_VAL(attribute == NULL, RESULT_BAD_PARAM);

    IF_TRUE_LOGE_AND_RETURN_VAL(retData == NULL, RESULT_BAD_PARAM);

    IF_TRUE_LOGE_AND_RETURN_VAL(IS_ARRAY_NULL(*retData), RESULT_BAD_PARAM);



    Uint8Array uint64ArrayData = { (uint8_t *)retData->data, retData->len * sizeof(uint64_t) };

    ResultCode getAttrResult = GetAttributeUint8Array(attribute, key, &uint64ArrayData);

    IF_TRUE_LOGE_AND_RETURN_VAL(getAttrResult != RESULT_SUCCESS, RESULT_GENERAL_ERROR);

    if (uint64ArrayData.len % sizeof(uint64_t) != 0) {

        LOG_ERROR("uint8 length %u is incorrect", uint64ArrayData.len);

        return RESULT_GENERAL_ERROR;

    }

    Ntohl64Array(retData);



    retData->len = uint64ArrayData.len / sizeof(uint64_t);

    return RESULT_SUCCESS;

}



ResultCode SetAttributeUint64Array(Attribute *attribute, AttributeKey key, const Uint64Array data)

{

    IF_TRUE_LOGE_AND_RETURN_VAL(attribute == NULL, RESULT_BAD_PARAM);

    IF_TRUE_LOGE_AND_RETURN_VAL(!IS_ARRAY_VALID(data), RESULT_BAD_PARAM);



    Uint64Array *netOrderData = CreateUint64ArrayByData(data.data, data.len);

    IF_TRUE_LOGE_AND_RETURN_VAL(netOrderData == NULL, RESULT_GENERAL_ERROR);



    ResultCode result = RESULT_GENERAL_ERROR;

    do {

        Htonl64Array(netOrderData);

        if (netOrderData->len > UINT32_MAX / sizeof(uint64_t)) {

            LOG_ERROR("netOrderData->len is invalid");

            break;

        }

        result = SetAttributeUint8Array(attribute, key,

            (Uint8Array) { (uint8_t *)netOrderData->data, netOrderData->len * sizeof(uint64_t) });

        if (result != RESULT_SUCCESS) {

            LOG_ERROR("SetAttributeUint8Array fail");

            break;

        }

    } while (0);



    DestroyUint64Array(&netOrderData);

    return result;

}