/*
 * This file is part of the openHiTLS project.
 *
 * openHiTLS is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include "hitls_build.h"
#include <string.h>
#include "bsl_sal.h"
#include "tls_binlog_id.h"
#include "hitls_cert_type.h"
#include "cert_method.h"
#include "cert_mgr.h"
#include "cert_mgr_ctx.h"

#ifdef HITLS_TLS_PROTO_TLCP11
static int32_t TlcpCertPairDup(CERT_MgrCtx *mgrCtx, CERT_Pair *srcCertPair, CERT_Pair *destCertPair)
{
    if (srcCertPair->encCert != NULL) {
        destCertPair->encCert = SAL_CERT_X509Dup(mgrCtx, srcCertPair->encCert);
        if (destCertPair->encCert == NULL) {
            BSL_LOG_BINLOG_FIXLEN(BINLOG_ID17341, BSL_LOG_LEVEL_ERR, BSL_LOG_BINLOG_TYPE_RUN,
                "enc X509Dup fail", 0, 0, 0, 0);
            return HITLS_CERT_ERR_X509_DUP;
        }
    }

    if (srcCertPair->encPrivateKey != NULL) {
        destCertPair->encPrivateKey = SAL_CERT_KeyDup(mgrCtx, srcCertPair->encPrivateKey);
        if (destCertPair->encPrivateKey == NULL) {
            BSL_LOG_BINLOG_FIXLEN(BINLOG_ID17342, BSL_LOG_LEVEL_ERR, BSL_LOG_BINLOG_TYPE_RUN,
                "enc KeyDup fail", 0, 0, 0, 0);
            return HITLS_CERT_ERR_X509_DUP;
        }
    }
    return  HITLS_SUCCESS;
}
#endif

CERT_Pair *SAL_CERT_PairDup(CERT_MgrCtx *mgrCtx, CERT_Pair *srcCertPair)
{
    CERT_Pair *destCertPair = BSL_SAL_Calloc(1, sizeof(CERT_Pair));
    if (destCertPair == NULL) {
        BSL_LOG_BINLOG_FIXLEN(BINLOG_ID16299, BSL_LOG_LEVEL_ERR, BSL_LOG_BINLOG_TYPE_RUN, "Calloc fail", 0, 0, 0, 0);
        return NULL;
    }

    do {
#ifdef HITLS_TLS_PROTO_TLCP11
        if (TlcpCertPairDup(mgrCtx, srcCertPair, destCertPair) != HITLS_SUCCESS) {
            break;
        }
#endif

        if (srcCertPair->cert != NULL) {
            destCertPair->cert = SAL_CERT_X509Dup(mgrCtx, srcCertPair->cert);
            if (destCertPair->cert == NULL) {
                BSL_LOG_BINLOG_FIXLEN(BINLOG_ID16300, BSL_LOG_LEVEL_ERR, BSL_LOG_BINLOG_TYPE_RUN,
                    "X509Dup fail", 0, 0, 0, 0);
                break;
            }
        }

        if (srcCertPair->privateKey != NULL) {
            destCertPair->privateKey = SAL_CERT_KeyDup(mgrCtx, srcCertPair->privateKey);
            if (destCertPair->privateKey == NULL) {
                BSL_LOG_BINLOG_FIXLEN(BINLOG_ID16301, BSL_LOG_LEVEL_ERR, BSL_LOG_BINLOG_TYPE_RUN,
                    "KeyDup fail", 0, 0, 0, 0);
                break;
            }
        }

        if (srcCertPair->chain != NULL) {
            destCertPair->chain = SAL_CERT_ChainDup(mgrCtx, srcCertPair->chain);
            if (destCertPair->chain == NULL) {
                BSL_LOG_BINLOG_FIXLEN(BINLOG_ID16302, BSL_LOG_LEVEL_ERR, BSL_LOG_BINLOG_TYPE_RUN,
                    "ChainDup fail", 0, 0, 0, 0);
                break;
            }
        }
        return destCertPair;
    } while (false);
    SAL_CERT_PairFree(mgrCtx, destCertPair);
    return NULL;
}

void SAL_CERT_PairClear(CERT_MgrCtx *mgrCtx, CERT_Pair *certPair)
{
    if (mgrCtx == NULL || certPair == NULL) {
        return;
    }

    if (certPair->cert != NULL) {
        SAL_CERT_X509Free(certPair->cert);
    }
#ifdef HITLS_TLS_PROTO_TLCP11
    if (certPair->encCert != NULL) {
        SAL_CERT_X509Free(certPair->encCert);
    }
    if (certPair->encPrivateKey != NULL) {
        SAL_CERT_KeyFree(mgrCtx, certPair->encPrivateKey);
    }
#endif
    if (certPair->privateKey != NULL) {
        SAL_CERT_KeyFree(mgrCtx, certPair->privateKey);
    }

    if (certPair->chain != NULL) {
        SAL_CERT_ChainFree(certPair->chain);
    }

    memset(certPair, 0, sizeof(CERT_Pair));
    return;
}

void SAL_CERT_PairFree(CERT_MgrCtx *mgrCtx, CERT_Pair *certPair)
{
    SAL_CERT_PairClear(mgrCtx, certPair);
    BSL_SAL_Free(certPair);
    return;
}

int32_t SAL_CERT_HashDup(CERT_MgrCtx *destMgrCtx, CERT_MgrCtx *srcMgrCtx)
{
    destMgrCtx->certPairs = BSL_HASH_Create(CERT_DEFAULT_HASH_BKT_SIZE, NULL, NULL, NULL, NULL);
    if (destMgrCtx->certPairs == NULL) {
        BSL_LOG_BINLOG_FIXLEN(BINLOG_ID17347, BSL_LOG_LEVEL_ERR, BSL_LOG_BINLOG_TYPE_RUN,
            "BSL_HASH_Create fail", 0, 0, 0, 0);
        return HITLS_MEMALLOC_FAIL;
    }

    BSL_HASH_Hash *certPairs = srcMgrCtx->certPairs;
    BSL_HASH_Iterator iter = BSL_HASH_IterBegin(certPairs);
    while (iter != BSL_HASH_IterEnd(certPairs)) {
        uint32_t keyType = (uint32_t)BSL_HASH_HashIterKey(certPairs, iter);
        uintptr_t ptr = BSL_HASH_IterValue(certPairs, iter);
        CERT_Pair *certPair = (CERT_Pair *)ptr;
        if (certPair != NULL) {
            CERT_Pair *newCertPair = SAL_CERT_PairDup(srcMgrCtx, certPair);
            if (newCertPair == NULL) {
                return RETURN_ERROR_NUMBER_PROCESS(HITLS_CERT_ERR_X509_DUP, BINLOG_ID17348, "x509dup fail");
            }
            int32_t ret = BSL_HASH_Insert(destMgrCtx->certPairs, keyType, 0, (uintptr_t)newCertPair, sizeof(CERT_Pair));
            if (ret != HITLS_SUCCESS) {
                SAL_CERT_PairFree(destMgrCtx, newCertPair);
                return RETURN_ERROR_NUMBER_PROCESS(ret, BINLOG_ID17349, "insert fail");
            }
        }
        iter = BSL_HASH_IterNext(certPairs, iter);
    }
    return HITLS_SUCCESS;
}