/*
 * Copyright (c) 2024-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 "security_key.h"
#include <stddef.h>
#include "iotc_log.h"
#include "utils_mutex_global.h"
#include "iotc_aes.h"
#include "iotc_kdf.h"
#include "securec.h"
#include "utils_assert.h"
#include "iotc_errcode.h"

static SecurityGetPskCallback g_pskCb = NULL;
static SecurityGetAcKey g_acCb = NULL;
static SecurityGetUdid g_udidCb = NULL;

void SecurityRegUdidCallback(SecurityGetUdid udidCb)
{
    if (udidCb != NULL) {
        GLOBAL_LOCK_PROTECT(g_udidCb = udidCb);
        IOTC_LOGN("uuid callback reg");
    }
    return;
}

void SecurityRegPskCallback(SecurityGetPskCallback pskCb)
{
    if (pskCb != NULL) {
        GLOBAL_LOCK_PROTECT(g_pskCb = pskCb);
        IOTC_LOGN("psk callback reg");
    }
    return;
}

void SecurityRegAcKeyCallback(SecurityGetAcKey acCb)
{
    if (acCb != NULL) {
        GLOBAL_LOCK_PROTECT(g_acCb = acCb);
        IOTC_LOGN("psk callback reg");
    }
    return;
}

int32_t SecurityGetPsk(uint8_t *buf, uint32_t len)
{
    if (buf == NULL || len < SECURITY_PSK_LEN) {
        IOTC_LOGW("invalid param");
        return IOTC_ERR_PARAM_INVALID;
    }

    UtilsGlobalMutexLock();
    SecurityGetPskCallback pskCallback = g_pskCb;
    UtilsGlobalMutexUnlock();

    CHECK_RETURN_LOGW(pskCallback != NULL, IOTC_CORE_COMM_SEC_ERR_NO_PSK_CALLBACK, "no psk cb");

    int32_t ret = pskCallback(buf, len);
    if (ret != IOTC_OK) {
        IOTC_LOGW("get psk error %d", ret);
        return IOTC_CORE_COMM_SEC_ERR_GET_PSK;
    }

    return IOTC_OK;
}

int32_t SecurityGenHkdfLocalKey(const uint8_t *salt, uint32_t saltLen, uint8_t out[SECURITY_HKDF_LOCAL_KEY_LEN])
{
    if (salt == NULL || saltLen == 0 || out == NULL) {
        IOTC_LOGW("invalid param");
        return IOTC_ERR_PARAM_INVALID;
    }
    UtilsGlobalMutexLock();
    SecurityGetAcKey acKeyCallback = g_acCb;
    SecurityGetUdid udidCallback = g_udidCb;
    UtilsGlobalMutexUnlock();

    CHECK_RETURN_LOGW(udidCallback != NULL, IOTC_CORE_COMM_SEC_ERR_NO_UDID, "no udid cb");
    CHECK_RETURN_LOGW(acKeyCallback != NULL, IOTC_CORE_COMM_SEC_ERR_NO_AC_KEY, "no ac key cb");

    uint8_t udid[SECURITY_UDID_LEN] = {0};
    int32_t ret = udidCallback(udid, sizeof(udid));
    if (ret != 0) {
        IOTC_LOGW("get udid error %d", ret);
        return IOTC_CORE_COMM_SEC_ERR_GET_UDID;
    }

    uint8_t acKey[IOTC_AC_KEY_LEN] = {0};
    ret = acKeyCallback(acKey, sizeof(acKey));
    if (ret != 0) {
        IOTC_LOGW("get ac key error %d", ret);
        return IOTC_CORE_COMM_SEC_ERR_GET_AC_KEY;
    }

    IotcHkdfParam hkdf = {
        .md = IOTC_MD_SHA256,
        .salt = salt,
        .saltLen = saltLen,
        .info = udid,
        .infoLen = sizeof(udid),
        .material = acKey,
        .materialLen = sizeof(acKey),
    };
    ret = IotcHkdf(&hkdf, out, SECURITY_HKDF_LOCAL_KEY_LEN);
    (void)memset_s(acKey, sizeof(acKey), 0, sizeof(acKey));
    return ret;
}