/*
 * Copyright (C) 2022-2023 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 "executor_message.h"

#include "securec.h"
#include "adaptor_algorithm.h"
#include "adaptor_log.h"
#include "adaptor_memory.h"
#include "adaptor_time.h"
#include "coauth.h"
#include "ed25519_key.h"
#include "hmac_key.h"
#include "idm_database.h"
#include "udid_manager.h"
#include "user_sign_centre.h"

#ifdef IAM_TEST_ENABLE
#define IAM_STATIC
#else
#define IAM_STATIC static
#endif

IAM_STATIC ResultCode SignData(const Uint8Array *dataTlv, Uint8Array *signDataTlv, SignParam signParam)
{
    Buffer data = GetTmpBuffer(dataTlv->data, dataTlv->len, dataTlv->len);
    if (!IsBufferValid(&data)) {
        LOG_ERROR("data is invalid");
        return RESULT_GENERAL_ERROR;
    }
    ResultCode result = RESULT_SUCCESS;
    Buffer *signData = NULL;
    if (signParam.keyType == KEY_TYPE_CROSS_DEVICE) {
        signData = HmacSign(&data, signParam);
    } else {
        signData = ExecutorMsgSign(&data);
    }
    if (!IsBufferValid(signData)) {
        LOG_ERROR("signData is invalid");
        return RESULT_GENERAL_ERROR;
    }
    if (memcpy_s(signDataTlv->data, signDataTlv->len, signData->buf, signData->contentSize) != EOK) {
        LOG_ERROR("copy sign to signDtaTlv failed");
        result = RESULT_GENERAL_ERROR;
        goto FAIL;
    }
    signDataTlv->len = signData->contentSize;
    LOG_INFO("sign data success");

FAIL:
    DestoryBuffer(signData);
    return result;
}

IAM_STATIC ResultCode GetAttributeDataAndSignTlv(const Attribute *attribute, Uint8Array *retDataAndSignTlv,
    SignParam signParam)
{
    Attribute *dataAndSignAttribute = CreateEmptyAttribute();
    Uint8Array dataTlv = { Malloc(MAX_EXECUTOR_MSG_LEN), MAX_EXECUTOR_MSG_LEN };
    Uint8Array signTlv = { Malloc(ED25519_FIX_SIGN_BUFFER_SIZE), ED25519_FIX_SIGN_BUFFER_SIZE };

    ResultCode result = RESULT_GENERAL_ERROR;
    do {
        if (dataAndSignAttribute == NULL || IS_ARRAY_NULL(dataTlv) || IS_ARRAY_NULL(signTlv)) {
            LOG_ERROR("dataAndSignAttribute or dataTlv or signTlv is NULL");
            break;
        }
        result = GetAttributeSerializedMsg(attribute, &dataTlv);
        if (result != RESULT_SUCCESS) {
            LOG_ERROR("GetAttributeSerializedMsg for data fail");
            break;
        }

        result = SetAttributeUint8Array(dataAndSignAttribute, ATTR_DATA, dataTlv);
        if (result != RESULT_SUCCESS) {
            LOG_ERROR("SetAttributeUint8Array for data fail");
            break;
        }
        if (signParam.needSignature) {
            result = SignData(&dataTlv, &signTlv, signParam);
            if (result != RESULT_SUCCESS) {
                LOG_ERROR("SignData fail");
                break;
            }
            result = SetAttributeUint8Array(dataAndSignAttribute, ATTR_SIGNATURE, signTlv);
            if (result != RESULT_SUCCESS) {
                LOG_ERROR("SetAttributeUint8Array for signature fail");
                break;
            }
        }
        result = GetAttributeSerializedMsg(dataAndSignAttribute, retDataAndSignTlv);
        if (result != RESULT_SUCCESS) {
            LOG_ERROR("GetAttributeSerializedMsg fail");
            break;
        }
    } while (0);

    Free(signTlv.data);
    Free(dataTlv.data);
    FreeAttribute(&dataAndSignAttribute);
    return result;
}

ResultCode GetAttributeExecutorMsg(const Attribute *attribute, Uint8Array *retMsg, SignParam signParam)
{
    IF_TRUE_LOGE_AND_RETURN_VAL(attribute == NULL, RESULT_GENERAL_ERROR);
    IF_TRUE_LOGE_AND_RETURN_VAL(retMsg == NULL, RESULT_GENERAL_ERROR);
    IF_TRUE_LOGE_AND_RETURN_VAL(IS_ARRAY_NULL(*retMsg), RESULT_GENERAL_ERROR);

    Attribute *rootAttribute = CreateEmptyAttribute();
    Uint8Array dataAndSignTlv = { Malloc(MAX_EXECUTOR_MSG_LEN), MAX_EXECUTOR_MSG_LEN };

    ResultCode result = RESULT_GENERAL_ERROR;
    do {
        if (rootAttribute == NULL || IS_ARRAY_NULL(dataAndSignTlv)) {
            LOG_ERROR("rootAttribute or dataAndSignTlv is NULL");
            break;
        }

        result = GetAttributeDataAndSignTlv(attribute, &dataAndSignTlv, signParam);
        if (result != RESULT_SUCCESS) {
            LOG_ERROR("GetAttributeDataAndSignTlv fail");
            break;
        }
        result = SetAttributeUint8Array(rootAttribute, ATTR_ROOT, dataAndSignTlv);
        if (result != RESULT_SUCCESS) {
            LOG_ERROR("SetAttributeUint8Array fail");
            break;
        }
        result = GetAttributeSerializedMsg(rootAttribute, retMsg);
        if (result != RESULT_SUCCESS) {
            LOG_ERROR("GetAttributeSerializedMsg fail");
            break;
        }
    } while (0);

    Free(dataAndSignTlv.data);
    FreeAttribute(&rootAttribute);
    return result;
}

IAM_STATIC ResultCode Ed25519VerifyData(uint64_t scheduleId, Uint8Array dataTlv, Uint8Array signTlv)
{
    ResultCode result = RESULT_GENERAL_ERROR;
    const CoAuthSchedule *currentSchedule = GetCoAuthSchedule(scheduleId);
    IF_TRUE_LOGE_AND_RETURN_VAL(currentSchedule == NULL, result);
    Buffer *publicKey = NULL;
    for (uint32_t index = 0; index < currentSchedule->executorSize; ++index) {
        const ExecutorInfoHal *executor = &((currentSchedule->executors)[index]);
        if (executor->executorRole == VERIFIER || executor->executorRole == ALL_IN_ONE) {
            publicKey = CreateBufferByData(executor->pubKey, PUBLIC_KEY_LEN);
            break;
        }
    }
    Buffer data = GetTmpBuffer(dataTlv.data, dataTlv.len, dataTlv.len);
    Buffer sign = GetTmpBuffer(signTlv.data, signTlv.len, signTlv.len);
    if (!IsBufferValid(publicKey) || !IsBufferValid(&data) || !IsBufferValid(&sign)) {
        LOG_ERROR("data or sign is invalid");
        goto FAIL;
    }
    result = (ResultCode)Ed25519Verify(publicKey, &data, &sign);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("verify sign failed");
        goto FAIL;
    }
    LOG_INFO("Ed25519 verify success");

FAIL:
    DestoryBuffer(publicKey);
    return result;
}

IAM_STATIC ResultCode VerifyDataTlvSignature(const Attribute *dataAndSignAttribute, const Uint8Array dataTlv,
    SignParam signParam)
{
    Attribute *dataAttribute = CreateAttributeFromSerializedMsg(dataTlv);
    Uint8Array signTlv = { Malloc(MAX_EXECUTOR_MSG_LEN), MAX_EXECUTOR_MSG_LEN };

    ResultCode result = RESULT_GENERAL_ERROR;
    do {
        if (dataAttribute == NULL || IS_ARRAY_NULL(signTlv)) {
            LOG_ERROR("dataAttribute or signTlv is null");
            break;
        }
        result = GetAttributeUint8Array(dataAndSignAttribute, ATTR_SIGNATURE, &signTlv);
        if (result != RESULT_SUCCESS) {
            LOG_ERROR("GetAttributeUint8Array fail");
            break;
        }
        if (signParam.keyType == KEY_TYPE_CROSS_DEVICE) {
            Buffer data = GetTmpBuffer(dataTlv.data, dataTlv.len, dataTlv.len);
            Buffer sign = GetTmpBuffer(signTlv.data, signTlv.len, signTlv.len);
            result = HmacVerify(&data, &sign, signParam);
            if (result != RESULT_SUCCESS) {
                LOG_ERROR("HmacVerify fail");
                break;
            }
        } else {
            uint64_t scheduleId;
            result = GetAttributeUint64(dataAttribute, ATTR_SCHEDULE_ID, &scheduleId);
            if (result != RESULT_SUCCESS) {
                LOG_ERROR("GetAttributeUint64 scheduleId fail");
                break;
            }
            result = Ed25519VerifyData(scheduleId, dataTlv, signTlv);
            if (result != RESULT_SUCCESS) {
                LOG_ERROR("Ed25519VerifyData fail");
                break;
            }
        }
    } while (0);

    Free(signTlv.data);
    FreeAttribute(&dataAttribute);
    return result;
}

IAM_STATIC Attribute *CreateAttributeFromDataAndSignTlv(const Uint8Array dataAndSignTlv, SignParam signParam)
{
    Attribute *dataAndSignAttribute = CreateAttributeFromSerializedMsg(dataAndSignTlv);
    Uint8Array dataTlv = { Malloc(MAX_EXECUTOR_MSG_LEN), MAX_EXECUTOR_MSG_LEN };

    Attribute *attribute = NULL;
    do {
        if (dataAndSignAttribute == NULL || IS_ARRAY_NULL(dataTlv)) {
            LOG_ERROR("dataAndSignAttribute or dataTlv is null");
            break;
        }
        if (GetAttributeUint8Array(dataAndSignAttribute, ATTR_DATA, &dataTlv) != RESULT_SUCCESS) {
            LOG_ERROR("GetAttributeUint8Array fail");
            break;
        }
        if (signParam.needSignature) {
            if (VerifyDataTlvSignature(dataAndSignAttribute, dataTlv, signParam) != RESULT_SUCCESS) {
                LOG_ERROR("VerifyDataTlvSignature fail");
                break;
            }
        }
        attribute = CreateAttributeFromSerializedMsg(dataTlv);
        if (attribute == NULL) {
            LOG_ERROR("CreateAttributeFromSerializedMsg fail");
            break;
        }
    } while (0);

    Free(dataTlv.data);
    FreeAttribute(&dataAndSignAttribute);
    return attribute;
}

IAM_STATIC Attribute *CreateAttributeFromExecutorMsg(const Uint8Array msg, SignParam signParam)
{
    IF_TRUE_LOGE_AND_RETURN_VAL(IS_ARRAY_NULL(msg), NULL);

    Attribute *msgAttribute = CreateAttributeFromSerializedMsg(msg);
    Uint8Array dataAndSignTlv = { Malloc(MAX_EXECUTOR_MSG_LEN), MAX_EXECUTOR_MSG_LEN };

    Attribute *attribute = NULL;
    do {
        if (msgAttribute == NULL || IS_ARRAY_NULL(dataAndSignTlv)) {
            LOG_ERROR("msgAttribute or dataAndSignTlv is null");
            break;
        }

        ResultCode result = GetAttributeUint8Array(msgAttribute, ATTR_ROOT, &dataAndSignTlv);
        if (result != RESULT_SUCCESS) {
            LOG_ERROR("GetAttributeUint8Array fail");
            break;
        }

        attribute = CreateAttributeFromDataAndSignTlv(dataAndSignTlv, signParam);
        if (attribute == NULL) {
            LOG_ERROR("CreateAttributeFromDataAndSignTlv fail");
            break;
        }
    } while (0);

    Free(dataAndSignTlv.data);
    FreeAttribute(&msgAttribute);
    return attribute;
}

IAM_STATIC void GetRootSecretFromAttribute(const Attribute *attribute, ExecutorResultInfo *resultInfo)
{
    Uint8Array array = { Malloc(ROOT_SECRET_LEN), ROOT_SECRET_LEN };
    IF_TRUE_LOGE_AND_RETURN(IS_ARRAY_NULL(array));
    ResultCode result = GetAttributeUint8Array(attribute, ATTR_ROOT_SECRET, &(array));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("There is no rootSecret in this attribute");
        goto EXIT;
    }
    if (array.len != ROOT_SECRET_LEN) {
        LOG_ERROR("rootSecret len is invalid");
        goto EXIT;
    }
    resultInfo->rootSecret = CreateBufferByData(array.data, array.len);
    if (!IsBufferValid(resultInfo->rootSecret)) {
        LOG_ERROR("Generate rootSecret buffer failed");
        goto EXIT;
    }
    LOG_INFO("get rootSecret success");

EXIT:
    (void)memset_s(array.data, ROOT_SECRET_LEN, 0, ROOT_SECRET_LEN);
    Free(array.data);
}

IAM_STATIC ResultCode GetExecutorResultInfoFromAttribute(const Attribute *attribute, ExecutorResultInfo *resultInfo)
{
    ResultCode result = RESULT_GENERAL_ERROR;
    result = GetAttributeInt32(attribute, ATTR_RESULT_CODE, &(resultInfo->result));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeInt32 result failed");
        return result;
    }
    result = GetAttributeUint64(attribute, ATTR_TEMPLATE_ID, &(resultInfo->templateId));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint64 templateId failed");
        return result;
    }
    result = GetAttributeUint64(attribute, ATTR_SCHEDULE_ID, &(resultInfo->scheduleId));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint64 scheduleId failed");
        return result;
    }
    result = GetAttributeUint64(attribute, ATTR_PIN_SUB_TYPE, &(resultInfo->authSubType));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint64 authSubType failed");
        return result;
    }
    result = GetAttributeInt32(attribute, ATTR_REMAIN_ATTEMPTS, &(resultInfo->remainTimes));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeInt32 remainTimes failed");
        return result;
    }
    result = GetAttributeInt32(attribute, ATTR_LOCKOUT_DURATION, &(resultInfo->freezingTime));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeInt32 freezingTime failed");
        return result;
    }
    result = GetAttributeUint32(attribute, ATTR_CAPABILITY_LEVEL, &(resultInfo->capabilityLevel));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint32 capabilityLevel failed");
        return result;
    }

    // Only pin auth has rootSecret
    GetRootSecretFromAttribute(attribute, resultInfo);
    return RESULT_SUCCESS;
}

ExecutorResultInfo *CreateExecutorResultInfo(const Buffer *tlv)
{
    if (!IsBufferValid(tlv)) {
        LOG_ERROR("param is invalid");
        return NULL;
    }

    Uint8Array msg = { tlv->buf, tlv->contentSize };
    SignParam signParam = { .needSignature = true, .keyType = KEY_TYPE_EXECUTOR };
    Attribute *attribute = CreateAttributeFromExecutorMsg(msg, signParam);
    if (attribute == NULL) {
        LOG_ERROR("CreateAttributeFromExecutorMsg failed");
        return NULL;
    }

    ExecutorResultInfo *result = Malloc(sizeof(ExecutorResultInfo));
    if (result == NULL) {
        LOG_ERROR("malloc failed");
        FreeAttribute(&attribute);
        return result;
    }
    (void)memset_s(result, sizeof(ExecutorResultInfo), 0, sizeof(ExecutorResultInfo));

    if (GetExecutorResultInfoFromAttribute(attribute, result) != RESULT_SUCCESS) {
        LOG_ERROR("GetExecutorResultInfoFromAttribute failed");
        FreeAttribute(&attribute);
        DestroyExecutorResultInfo(result);
        return NULL;
    }

    FreeAttribute(&attribute);
    return result;
}

void DestroyExecutorResultInfo(ExecutorResultInfo *result)
{
    if (result == NULL) {
        return;
    }
    if (result->rootSecret != NULL) {
        DestoryBuffer(result->rootSecret);
    }
    Free(result);
}

IAM_STATIC ResultCode SetExecutorMsgToAttribute(uint32_t authType, uint32_t authPropertyMode,
    const Uint64Array *templateIds, Attribute *attribute)
{
    ResultCode result = SetAttributeUint32(attribute, ATTR_PROPERTY_MODE, authPropertyMode);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("SetAttributeUint32 propertyMode failed");
        return RESULT_GENERAL_ERROR;
    }
    result = SetAttributeUint32(attribute, ATTR_TYPE, authType);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("SetAttributeUint32 authType failed");
        return RESULT_GENERAL_ERROR;
    }
    Uint64Array templateIdsIn = { templateIds->data, templateIds->len };
    result = SetAttributeUint64Array(attribute, ATTR_TEMPLATE_ID_LIST, templateIdsIn);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("SetAttributeUint64Array templateIdsIn failed");
        return RESULT_GENERAL_ERROR;
    }
    uint64_t time = GetSystemTime();
    result = SetAttributeUint64(attribute, ATTR_TIME_STAMP, time);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("SetAttributeUint64 time failed");
        return RESULT_GENERAL_ERROR;
    }

    return RESULT_SUCCESS;
}


IAM_STATIC Buffer *CreateExecutorMsg(uint32_t authType, uint32_t authPropertyMode, const Uint64Array *templateIds)
{
    if (templateIds == NULL) {
        LOG_ERROR("templateIds is null");
        return NULL;
    }
    Buffer *retBuffer = NULL;
    Uint8Array retInfo = { Malloc(MAX_EXECUTOR_MSG_LEN), MAX_EXECUTOR_MSG_LEN };
    Attribute *attribute = CreateEmptyAttribute();
    if (attribute == NULL || IS_ARRAY_NULL(retInfo)) {
        LOG_ERROR("generate attribute or retInfo failed");
        goto FAIL;
    }

    ResultCode result = SetExecutorMsgToAttribute(authType, authPropertyMode, templateIds, attribute);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("set msg to attribute failed");
        goto FAIL;
    }

    if (authPropertyMode == PROPERTY_MODE_UNFREEZE) {
        SignParam signParam = { .needSignature = true, .keyType = KEY_TYPE_EXECUTOR };
        result = GetAttributeExecutorMsg(attribute, &retInfo, signParam);
    } else {
        SignParam signParam = { .needSignature = false };
        result = GetAttributeExecutorMsg(attribute, &retInfo, signParam);
    }
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeExecutorMsg failed");
        goto FAIL;
    }

    retBuffer = CreateBufferByData(retInfo.data, retInfo.len);
    if (!IsBufferValid(retBuffer)) {
        LOG_ERROR("generate result buffer failed");
        goto FAIL;
    }
    LOG_INFO("CreateExecutorMsg success");

FAIL:
    FreeAttribute(&attribute);
    Free(retInfo.data);
    return retBuffer;
}

IAM_STATIC void DestoryExecutorMsg(void *data)
{
    if (data == NULL) {
        return;
    }
    ExecutorMsg *msg = (ExecutorMsg *)data;
    DestoryBuffer(msg->msg);
    Free(msg);
}

IAM_STATIC ResultCode GetExecutorTemplateList(
    int32_t userId, const ExecutorInfoHal *executorNode, Uint64Array *templateIds)
{
    CredentialCondition condition = {};
    SetCredentialConditionUserId(&condition, userId);
    SetCredentialConditionAuthType(&condition, executorNode->authType);
    SetCredentialConditionExecutorSensorHint(&condition, executorNode->executorSensorHint);
    LinkedList *credList = QueryCredentialLimit(&condition);
    if (credList == NULL) {
        LOG_ERROR("query credential failed");
        DestroyLinkedList(credList);
        return RESULT_UNKNOWN;
    }
    uint32_t credListNum = credList->getSize(credList);
    if (credListNum > MAX_CREDENTIAL) {
        LOG_ERROR("cred num is invalid");
        DestroyLinkedList(credList);
        return RESULT_REACH_LIMIT;
    }
    if (credListNum == 0) {
        templateIds->data = NULL;
        templateIds->len = 0;
        DestroyLinkedList(credList);
        return RESULT_SUCCESS;
    }
    templateIds->data = (uint64_t *)Malloc(sizeof(uint64_t) * credListNum);
    if (templateIds->data == NULL) {
        LOG_ERROR("data malloc failed");
        DestroyLinkedList(credList);
        return RESULT_NO_MEMORY;
    }
    templateIds->len = 0;
    LinkedListNode *temp = credList->head;
    while (temp != NULL) {
        if (temp->data == NULL) {
            LOG_ERROR("link node is invalid");
            DestroyLinkedList(credList);
            Free(templateIds->data);
            templateIds->data = NULL;
            return RESULT_UNKNOWN;
        }
        CredentialInfoHal *credentialHal = (CredentialInfoHal *)temp->data;
        templateIds->data[templateIds->len] = credentialHal->templateId;
        ++(templateIds->len);
        temp = temp->next;
    }
    DestroyLinkedList(credList);
    return RESULT_SUCCESS;
}

IAM_STATIC ResultCode AssemblyMessage(
    int32_t userId, const ExecutorInfoHal *executorNode, uint32_t authPropertyMode, LinkedList *executorMsg)
{
    Uint64Array templateIds;
    ResultCode ret = GetExecutorTemplateList(userId, executorNode, &templateIds);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("get template list failed");
        return ret;
    }
    if (templateIds.len == 0) {
        return RESULT_SUCCESS;
    }
    ExecutorMsg *msg = (ExecutorMsg *)Malloc(sizeof(ExecutorMsg));
    if (msg == NULL) {
        LOG_ERROR("msg is null");
        Free(templateIds.data);
        return RESULT_NO_MEMORY;
    }
    msg->executorIndex = executorNode->executorIndex;
    msg->msg = CreateExecutorMsg(executorNode->authType, authPropertyMode, &templateIds);
    if (msg->msg == NULL) {
        LOG_ERROR("msg's msg is null");
        Free(templateIds.data);
        DestoryExecutorMsg(msg);
        return RESULT_NO_MEMORY;
    }
    ret = executorMsg->insert(executorMsg, msg);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("insert msg failed");
        DestoryExecutorMsg(msg);
    }
    Free(templateIds.data);
    return ret;
}

IAM_STATIC ResultCode TraverseExecutor(
    int32_t userId, uint32_t executorRole, uint32_t authPropertyMode, LinkedList *executorMsg)
{
    ExecutorCondition condition = {};
    SetExecutorConditionExecutorRole(&condition, executorRole);
    LinkedList *executors = QueryExecutor(&condition);
    if (executors == NULL) {
        LOG_ERROR("query executor failed");
        return RESULT_UNKNOWN;
    }
    LinkedListNode *temp = executors->head;
    while (temp != NULL) {
        if (temp->data == NULL) {
            LOG_ERROR("list node is invalid");
            DestroyLinkedList(executors);
            return RESULT_UNKNOWN;
        }
        ExecutorInfoHal *executorNode = (ExecutorInfoHal *)temp->data;
        if (executorNode->authType != PIN_AUTH) {
            ResultCode ret = AssemblyMessage(userId, executorNode, authPropertyMode, executorMsg);
            if (ret != RESULT_SUCCESS) {
                LOG_ERROR("assembly message failed");
                DestroyLinkedList(executors);
                return ret;
            }
        }
        temp = temp->next;
    }
    DestroyLinkedList(executors);
    return RESULT_SUCCESS;
}

ResultCode GetExecutorMsgList(int32_t userId, uint32_t authPropertyMode, LinkedList **executorMsg)
{
    if (executorMsg == NULL) {
        LOG_ERROR("executorMsg is null");
        return RESULT_BAD_PARAM;
    }
    *executorMsg = CreateLinkedList(DestoryExecutorMsg);
    if (*executorMsg == NULL) {
        LOG_ERROR("create list failed");
        return RESULT_NO_MEMORY;
    }
    ResultCode ret = TraverseExecutor(userId, ALL_IN_ONE, authPropertyMode, *executorMsg);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("traverse allInOne executor failed");
        DestroyLinkedList(*executorMsg);
        *executorMsg = NULL;
    }
    return ret;
}

IAM_STATIC ResultCode GetExecutorInfoHalFromAttribute(const Attribute *attribute, ExecutorInfoHal *resultInfo)
{
    ResultCode result = RESULT_GENERAL_ERROR;
    result = GetAttributeUint64(attribute, ATTR_EXECUTOR_INDEX, &(resultInfo->executorIndex));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint64 executorIndex failed");
        return result;
    }
    result = GetAttributeUint32(attribute, ATTR_TYPE, &(resultInfo->authType));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint32 authType failed");
        return result;
    }
    result = GetAttributeUint32(attribute, ATTR_EXECUTOR_ROLE, &(resultInfo->executorRole));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint32 executorRole failed");
        return result;
    }
    result = GetAttributeUint32(attribute, ATTR_ESL, &(resultInfo->esl));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint32 esl failed");
        return result;
    }
    Uint8Array pubKeyTlv = { resultInfo->pubKey, PUBLIC_KEY_LEN };
    result = GetAttributeUint8Array(attribute, ATTR_PUBLIC_KEY, &pubKeyTlv);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint8Array pubKey fail");
        return result;
    }
    return RESULT_SUCCESS;
}

IAM_STATIC ResultCode GetExecutorInfoHal(Uint8Array tlv, ExecutorInfoHal *executorInfo)
{
    Attribute *attribute = CreateAttributeFromSerializedMsg(tlv);
    if (attribute == NULL) {
        LOG_ERROR("CreateAttributeFromSerializedMsg fail");
        return RESULT_GENERAL_ERROR;
    }

    ResultCode result = GetExecutorInfoHalFromAttribute(attribute, executorInfo);
    FreeAttribute(&attribute);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetExecutorInfoHalFromAttribute failed");
        return RESULT_GENERAL_ERROR;
    }

    return RESULT_SUCCESS;
}

IAM_STATIC ResultCode GetAuthAttrsFromAttribute(const Attribute *attribute, Uint8Array *authAttrs)
{
    ResultCode result = GetAttributeUint8Array(attribute, ATTR_ATTRS, authAttrs);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint8Array authAttrs fail");
    }
    return result;
}

IAM_STATIC ResultCode GetRemoteExecutorInfoInner(Attribute *attribute, Uint8Array *authAttrs, Uint8Array *subMsgs,
    int *subMsgSize)
{
    if (GetAuthAttrsFromAttribute(attribute, authAttrs) != RESULT_SUCCESS) {
        LOG_ERROR("GetAuthAttrsFromAttribute failed");
        return RESULT_GENERAL_ERROR;
    }
    if (ParseMultiDataSerializedMsg(*authAttrs, subMsgs, subMsgSize) != RESULT_SUCCESS) {
        LOG_ERROR("ParseMultiDataSerializedMsg failed");
        return RESULT_GENERAL_ERROR;
    }
    return RESULT_SUCCESS;
}

IAM_STATIC ResultCode GetRemoteExecutorInfo(const Buffer *msg, Uint8Array peerUdid, Uint8Array *subMsgs,
    int *subMsgSize)
{
    Uint8Array msgArray = { msg->buf, msg->contentSize };
    SignParam signParam = { .needSignature = true, .keyType = KEY_TYPE_CROSS_DEVICE, .peerUdid = peerUdid};
    Attribute *attribute = CreateAttributeFromExecutorMsg(msgArray, signParam);
    Uint8Array authAttrs = { Malloc(MAX_EXECUTOR_MSG_LEN), MAX_EXECUTOR_MSG_LEN };
    if (attribute == NULL || authAttrs.data == NULL) {
        LOG_ERROR("authAttrs malloc failed");
        FreeAttribute(&attribute);
        Free(authAttrs.data);
        return RESULT_GENERAL_ERROR;
    }

    ResultCode result = GetRemoteExecutorInfoInner(attribute, &authAttrs, subMsgs, subMsgSize);

    FreeAttribute(&attribute);
    Free(authAttrs.data);
    return result;
}

static bool CheckRemoteExecutorInfoInner(Uint8Array *subMsgs, int subMsgSize, ExecutorInfoHal *infoToCheck)
{
    for (int i = 0; i < subMsgSize; i++) {
        Uint8Array subMsg = subMsgs[i];
        ExecutorInfoHal executorInfo = {};
        ResultCode result = GetExecutorInfoHal(subMsg, &executorInfo);
        if (result != RESULT_SUCCESS) {
            LOG_ERROR("GetExecutorInfoHal failed");
            return false;
        }
        if ((executorInfo.authType == infoToCheck->authType) &&
            (executorInfo.executorRole == infoToCheck->executorRole) &&
            (executorInfo.esl == infoToCheck->esl) &&
            (memcmp(executorInfo.pubKey, infoToCheck->pubKey, PUBLIC_KEY_LEN) == 0)) {
            return true;
        }
    }
    LOG_ERROR("no matching executor info found");
    return false;
}

bool CheckRemoteExecutorInfo(const Buffer *msg, ExecutorInfoHal *infoToCheck)
{
    if (!IsBufferValid(msg) || (infoToCheck == NULL)) {
        LOG_ERROR("param is invalid");
        return RESULT_BAD_PARAM;
    }
    Uint8Array peerUdid = { infoToCheck->deviceUdid, sizeof(infoToCheck->deviceUdid) };
    Uint8Array subMsgs[MAX_SUB_MSG_NUM] = {0};
    int subMsgSize = MAX_SUB_MSG_NUM;
    ResultCode result = GetRemoteExecutorInfo(msg, peerUdid, &subMsgs[0], &subMsgSize);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetRemoteExecutorInfo failed");
        return false;
    }

    bool checkPass = CheckRemoteExecutorInfoInner(subMsgs, subMsgSize, infoToCheck);

    for (int i = 0; i < MAX_SUB_MSG_NUM; i++) {
        Free(subMsgs[i].data);
    }

    return checkPass;
}

IAM_STATIC ResultCode SetExecutorCollectMsgToAttribute(ScheduleInfoParam *scheduleInfo, const Uint8Array *publicKey,
    const Uint8Array challenge, Attribute *attribute)
{
    Uint8Array localUdid = { scheduleInfo->localUdid, sizeof(scheduleInfo->localUdid) };
    Uint8Array remoteUdid = { scheduleInfo->remoteUdid, sizeof(scheduleInfo->remoteUdid) };

    ResultCode result = SetAttributeUint64(attribute, ATTR_SCHEDULE_ID, scheduleInfo->scheduleId);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("SetAttributeUint64 scheduleId failed");
        return RESULT_GENERAL_ERROR;
    }

    result = SetAttributeUint8Array(attribute, ATTR_LOCAL_UDID, localUdid);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("SetAttributeUint8Array for localUdid fail");
        return RESULT_GENERAL_ERROR;
    }
    result = SetAttributeUint8Array(attribute, ATTR_PEER_UDID, remoteUdid);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("SetAttributeUint8Array for peerUdid fail");
        return RESULT_GENERAL_ERROR;
    }
    Uint8Array publicKeyIn = { publicKey->data, publicKey->len };
    result = SetAttributeUint8Array(attribute, ATTR_PUBLIC_KEY, publicKeyIn);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("SetAttributeUint8Array for publicKey fail");
        return RESULT_GENERAL_ERROR;
    }
    result = SetAttributeUint8Array(attribute, ATTR_CHALLENGE, challenge);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("SetAttributeUint8Array for challenge fail");
        return RESULT_GENERAL_ERROR;
    }
    return RESULT_SUCCESS;
}

IAM_STATIC Buffer *CreateExecutorCollectMsg(const Attribute *attributeSchedule, ScheduleInfoParam *scheduleInfo)
{
    uint8_t publicKeyData[PUBLIC_KEY_LEN] = {};
    Uint8Array publicKey = { publicKeyData, PUBLIC_KEY_LEN };
    ResultCode result = GetAttributeUint8Array(attributeSchedule, ATTR_PUBLIC_KEY, &publicKey);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint8Array publicKey fail");
        return NULL;
    }

    uint8_t challengeData[CHALLENGE_LEN] = {};
    Uint8Array challenge = { challengeData, CHALLENGE_LEN };
    ResultCode getChallengeRet = GetAttributeUint8Array(attributeSchedule, ATTR_CHALLENGE, &challenge);
    if (getChallengeRet != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint8Array challenge fail");
        return NULL;
    }

    Buffer *retBuffer = NULL;
    Attribute *attribute = CreateEmptyAttribute();
    Uint8Array retInfo = { Malloc(MAX_EXECUTOR_MSG_LEN), MAX_EXECUTOR_MSG_LEN };
    if (attribute == NULL || IS_ARRAY_NULL(retInfo)) {
        LOG_ERROR("attribute is null");
        goto FAIL;
    }
    result = SetExecutorCollectMsgToAttribute(scheduleInfo, &publicKey, challenge, attribute);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("set msg to attribute failed");
        goto FAIL;
    }

    SignParam signParam = { .needSignature = true, .keyType = KEY_TYPE_EXECUTOR };
    result = GetAttributeExecutorMsg(attribute, &retInfo, signParam);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeExecutorMsg failed");
        goto FAIL;
    }

    retBuffer = CreateBufferByData(retInfo.data, retInfo.len);
    if (!IsBufferValid(retBuffer)) {
        LOG_ERROR("generate result buffer failed");
        goto FAIL;
    }
    LOG_INFO("CreateExecutorCollectMsg success");

FAIL:
    FreeAttribute(&attribute);
    Free(retInfo.data);
    return retBuffer;
}

static ResultCode GetExecutorIndexByCondition(uint32_t authType, uint32_t executorMatcher,
    Uint8Array deviceUdid, uint32_t executorRole, uint64_t *executorIndex)
{
    ExecutorCondition condition = {};
    SetExecutorConditionAuthType(&condition, authType);
    SetExecutorConditionExecutorMatcher(&condition, executorMatcher);
    SetExecutorConditionExecutorRole(&condition, executorRole);
    SetExecutorConditionDeviceUdid(&condition, deviceUdid);

    LinkedList *executorList = QueryExecutor(&condition);
    if (executorList == NULL) {
        LOG_ERROR("query executor failed");
        return RESULT_UNKNOWN;
    }

    if (executorList->getSize(executorList) != 1) {
        LOG_ERROR("executor list len is invalid");
        DestroyLinkedList(executorList);
        return RESULT_TYPE_NOT_SUPPORT;
    }

    LinkedListNode *temp = executorList->head;
    if (temp == NULL) {
        LOG_ERROR("get executorList head failed");
        DestroyLinkedList(executorList);
        return RESULT_UNKNOWN;
    }
    ExecutorInfoHal *executorInfo = (ExecutorInfoHal *)temp->data;
    if (executorInfo == NULL) {
        LOG_ERROR("executorInfo is invalid");
        DestroyLinkedList(executorList);
        return RESULT_UNKNOWN;
    }

    *executorIndex = executorInfo->executorIndex;
    DestroyLinkedList(executorList);
    return RESULT_SUCCESS;
}

IAM_STATIC ResultCode GetScheduleInfoFromAttributeInner(const Attribute *attribute, ScheduleInfoParam *scheduleInfo)
{
    ResultCode result = GetAttributeUint64(attribute, ATTR_SCHEDULE_ID, &(scheduleInfo->scheduleId));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint64 scheduleId failed");
        return result;
    }
    result = GetAttributeUint32(attribute, ATTR_TYPE, &(scheduleInfo->authType));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint32 authType failed");
        return result;
    }
    result = GetAttributeUint32(attribute, ATTR_EXECUTOR_MATCHER, &(scheduleInfo->executorMatcher));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint32 executorMatcher failed");
        return result;
    }
    result = GetAttributeInt32(attribute, ATTR_SCHEDULE_MODE, &(scheduleInfo->scheduleMode));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeInt32 scheduleMode failed");
        return result;
    }
    Uint8Array remoteUdid = { scheduleInfo->remoteUdid, UDID_LEN };
    result = GetAttributeUint8Array(attribute, ATTR_VERIFIER_UDID, &remoteUdid);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint8Array remoteUdid failed");
    }
    return result;
}
IAM_STATIC ResultCode GetScheduleInfoFromAttribute(const Attribute *attribute, ScheduleInfoParam *scheduleInfo)
{
    ResultCode result = GetScheduleInfoFromAttributeInner(attribute, scheduleInfo);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetScheduleInfoFromAttributeInner failed");
        return result;
    }
    uint32_t executorRole;
    result = GetAttributeUint32(attribute, ATTR_EXECUTOR_ROLE, &executorRole);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint32 scheduleMode failed");
        return result;
    }

    Uint8Array collectorUdid = { scheduleInfo->localUdid, UDID_LEN };
    result = GetAttributeUint8Array(attribute, ATTR_COLLECTOR_UDID, &collectorUdid);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint8Array localUdid failed");
        return result;
    }

    if (!IsLocalUdid(collectorUdid)) {
        LOG_ERROR("collector udid is not local udid");
        return RESULT_GENERAL_ERROR;
    }

    result = GetExecutorIndexByCondition(scheduleInfo->authType, scheduleInfo->executorMatcher,
        collectorUdid, executorRole, &scheduleInfo->executorIndex);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetExecutorIndex failed");
        return result;
    }

    scheduleInfo->executorMessages = CreateExecutorCollectMsg(attribute, scheduleInfo);
    if (!IsBufferValid(scheduleInfo->executorMessages)) {
        LOG_ERROR("create executorMessages failed");
        result = RESULT_GENERAL_ERROR;
    }
    return result;
}

ResultCode CreateScheduleInfo(const Buffer *tlv, Uint8Array peerUdid, ScheduleInfoParam *scheduleInfo)
{
    if (!IsBufferValid(tlv) || IS_ARRAY_NULL(peerUdid) || (scheduleInfo == NULL)) {
        LOG_ERROR("param is invalid");
        return RESULT_BAD_PARAM;
    }

    Uint8Array msg = { tlv->buf, tlv->contentSize };
    SignParam signParam = { .needSignature = true, .keyType = KEY_TYPE_CROSS_DEVICE, .peerUdid =  peerUdid };
    Attribute *attribute = CreateAttributeFromExecutorMsg(msg, signParam);
    if (attribute == NULL) {
        LOG_ERROR("CreateAttributeFromExecutorMsg failed");
        return RESULT_GENERAL_ERROR;
    }

    ResultCode result = GetScheduleInfoFromAttribute(attribute, scheduleInfo);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetScheduleInfoFromAttribute failed");
    }
    FreeAttribute(&attribute);
    return result;
}

IAM_STATIC ResultCode GetAuthResultInfoFromAttribute(const Attribute *attribute, AuthResultParam *authResultInfo)
{
    ResultCode result = GetAttributeInt32(attribute, ATTR_RESULT, &(authResultInfo->result));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeInt32 result failed");
        return result;
    }
    result = GetAttributeInt32(attribute, ATTR_LOCKOUT_DURATION, &(authResultInfo->lockoutDuration));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeInt32 lockoutDuration failed");
        return result;
    }
    result = GetAttributeInt32(attribute, ATTR_REMAIN_ATTEMPTS, &(authResultInfo->remainAttempts));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeInt32 remainAttempts failed");
        return result;
    }
    result = GetAttributeInt32(attribute, ATTR_USER_ID, &(authResultInfo->userId));
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeInt32 userId failed");
        return result;
    }

    uint8_t tokenData[AUTH_TOKEN_LEN] = {};
    Uint8Array tokenArray = { tokenData, AUTH_TOKEN_LEN };
    result = GetAttributeUint8Array(attribute, ATTR_TOKEN, &tokenArray);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeUint8Array token fail");
        return result;
    }
    authResultInfo->token = CreateBufferByData(tokenArray.data, tokenArray.len);
    if (!IsBufferValid(authResultInfo->token)) {
        LOG_ERROR("CreateBuffer token fail");
        return RESULT_BAD_COPY;
    }
    return RESULT_SUCCESS;
}

ResultCode CreateAuthResultInfo(const Buffer *tlv, AuthResultParam *authResultInfo)
{
    if (!IsBufferValid(tlv) || (authResultInfo == NULL)) {
        LOG_ERROR("param is invalid");
        return RESULT_BAD_PARAM;
    }

    Uint8Array msg = { tlv->buf, tlv->contentSize };
    Uint8Array peerUdid = { .data = authResultInfo->remoteUdid, .len = UDID_LEN };
    SignParam signParam = { .needSignature = true, .keyType = KEY_TYPE_CROSS_DEVICE, .peerUdid = peerUdid };
    Attribute *attribute = CreateAttributeFromExecutorMsg(msg, signParam);
    if (attribute == NULL) {
        LOG_ERROR("CreateAttributeFromExecutorMsg failed");
        return RESULT_GENERAL_ERROR;
    }

    ResultCode result = GetAuthResultInfoFromAttribute(attribute, authResultInfo);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAuthResultInfoFromAttribute failed");
    }
    FreeAttribute(&attribute);
    return result;
}

IAM_STATIC ResultCode SetExecutorInfoMsgToAttribute(ExecutorInfoHal *executorInfo, Attribute *attribute)
{
    ResultCode result = SetAttributeUint64(attribute, ATTR_EXECUTOR_INDEX, executorInfo->executorIndex);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("SetAttributeUint64 executorIndex failed");
        return RESULT_GENERAL_ERROR;
    }
    result = SetAttributeUint32(attribute, ATTR_TYPE, executorInfo->authType);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("SetAttributeUint32 authType failed");
        return RESULT_GENERAL_ERROR;
    }
    result = SetAttributeUint32(attribute, ATTR_EXECUTOR_ROLE, executorInfo->executorRole);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("SetAttributeUint32 executorRole failed");
        return RESULT_GENERAL_ERROR;
    }
    result = SetAttributeUint32(attribute, ATTR_ESL, executorInfo->esl);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("SetAttributeUint32 esl failed");
        return RESULT_GENERAL_ERROR;
    }
    Uint8Array publicKeyIn = { executorInfo->pubKey, PUBLIC_KEY_LEN };
    result = SetAttributeUint8Array(attribute, ATTR_PUBLIC_KEY, publicKeyIn);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("SetAttributeUint8Array for pubKey fail");
        return RESULT_GENERAL_ERROR;
    }
    return RESULT_SUCCESS;
}

ResultCode GetExecutorInfoMsg(ExecutorInfoHal *executorInfo, Uint8Array *retMsg)
{
    ResultCode result = RESULT_GENERAL_ERROR;
    Attribute *attribute = CreateEmptyAttribute();
    if (attribute == NULL) {
        LOG_ERROR("CreateEmptyAttribute failed");
        return RESULT_GENERAL_ERROR;
    }
    result = SetExecutorInfoMsgToAttribute(executorInfo, attribute);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("set msg to attribute failed");
        goto FAIL;
    }
    result = GetAttributeSerializedMsg(attribute, retMsg);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeSerializedMsg fail");
        goto FAIL;
    }

FAIL:
    FreeAttribute(&attribute);
    return result;
}

Buffer *GetExecutorInfoTlv(Uint8Array attrsTlv, Uint8Array peerUdid)
{
    if (IS_ARRAY_NULL(attrsTlv) || IS_ARRAY_NULL(peerUdid)) {
        LOG_ERROR("attrsTlv is NULL");
        return NULL;
    }

    Buffer *retBuffer = NULL;
    Attribute *attribute = CreateEmptyAttribute();
    Uint8Array retInfo = { Malloc(MAX_EXECUTOR_MSG_LEN), MAX_EXECUTOR_MSG_LEN };
    if (attribute == NULL || IS_ARRAY_NULL(retInfo)) {
        LOG_ERROR("attribute or retInfo is NULL");
        goto FAIL;
    }
    ResultCode result = SetAttributeUint8Array(attribute, ATTR_ATTRS, attrsTlv);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("SetAttributeUint8Array for attrs fail");
        goto FAIL;
    }

    SignParam signParam = { .needSignature = true, .keyType = KEY_TYPE_CROSS_DEVICE, .peerUdid = peerUdid };
    result = GetAttributeExecutorMsg(attribute, &retInfo, signParam);
    if (result != RESULT_SUCCESS) {
        LOG_ERROR("GetAttributeExecutorMsg failed");
        goto FAIL;
    }

    retBuffer = CreateBufferByData(retInfo.data, retInfo.len);
    if (!IsBufferValid(retBuffer)) {
        LOG_ERROR("generate result buffer failed");
        goto FAIL;
    }
    LOG_INFO("GetExecutorInfoTlv success");

FAIL:
    FreeAttribute(&attribute);
    Free(retInfo.data);
    return retBuffer;
}