/*
 * 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_bytes.h"
#include "config_check.h"
#include "tls_binlog_id.h"
#include "bsl_log_internal.h"
#include "bsl_log.h"
#include "bsl_sal.h"
#include "bsl_err_internal.h"
#include "hitls_error.h"
#include "hs_common.h"
#include "config_type.h"
#include "parse_common.h"

int32_t ParseVersion(ParsePacket *pkt, uint16_t *version)
{
    int32_t ret = ParseBytesToUint16(pkt, version);
    if (ret != HITLS_SUCCESS) {
        return ParseErrorProcess(pkt->ctx, HITLS_PARSE_INVALID_MSG_LEN, BINLOG_ID15645,
            BINGLOG_STR("parse version failed"), ALERT_DECODE_ERROR);
    }
    return HITLS_SUCCESS;
}

int32_t ParseRandom(ParsePacket *pkt, uint8_t *random, uint32_t randomSize)
{
    int32_t ret = ParseCopyBytesToArray(pkt, random, randomSize);
    if (ret != HITLS_SUCCESS) {
        return ParseErrorProcess(pkt->ctx, HITLS_PARSE_INVALID_MSG_LEN, BINLOG_ID15646,
            BINGLOG_STR("parse random failed."), ALERT_DECODE_ERROR);
    }
    return HITLS_SUCCESS;
}

int32_t ParseSessionId(ParsePacket *pkt, uint8_t *idSize, uint8_t **id)
{
    int32_t ret = ParseOneByteLengthField(pkt, idSize, id);
    if (ret == HITLS_PARSE_INVALID_MSG_LEN) {
        return ParseErrorProcess(pkt->ctx, HITLS_PARSE_INVALID_MSG_LEN, BINLOG_ID15647,
            BINGLOG_STR("parse sessionId failed."), ALERT_DECODE_ERROR);
    } else if (ret == HITLS_MEMALLOC_FAIL) {
        return ParseErrorProcess(pkt->ctx, HITLS_MEMALLOC_FAIL, BINLOG_ID15651,
            BINGLOG_STR("sessionId malloc fail."), ALERT_UNKNOWN);
    }
    if (*idSize == 0u) {
        return HITLS_SUCCESS;
    }
    /* According to RFC 5246, the length of sessionId cannot exceed 32 bytes */
    if (*idSize > TLS_HS_MAX_SESSION_ID_SIZE) {
        return ParseErrorProcess(pkt->ctx, HITLS_PARSE_INVALID_MSG_LEN, BINLOG_ID15649,
            BINGLOG_STR("sessionId length over 32."), ALERT_DECODE_ERROR);
    }

    /* The session ID length must be greater than or equal to 24 bytes according to the company security redline */
    if (*idSize < TLS_HS_MIN_SESSION_ID_SIZE) {
        return ParseErrorProcess(pkt->ctx, HITLS_PARSE_INVALID_MSG_LEN, BINLOG_ID15650,
            BINGLOG_STR("sessionId length less than 24."), ALERT_DECODE_ERROR);
    }

    return HITLS_SUCCESS;
}

int32_t ParseCookie(ParsePacket *pkt, uint8_t *cookieLen, uint8_t **cookie)
{
    int32_t ret = ParseOneByteLengthField(pkt, cookieLen, cookie);
    if (ret == HITLS_PARSE_INVALID_MSG_LEN) {
        return ParseErrorProcess(pkt->ctx, HITLS_PARSE_INVALID_MSG_LEN, BINLOG_ID15652,
            BINGLOG_STR("parse cookie failed."), ALERT_DECODE_ERROR);
    } else if (ret == HITLS_MEMALLOC_FAIL) {
        return ParseErrorProcess(pkt->ctx, HITLS_MEMALLOC_FAIL, BINLOG_ID15654,
            BINGLOG_STR("cookie malloc failed."), ALERT_UNKNOWN);
    }
    return HITLS_SUCCESS;
}

int32_t ParseBytesToUint8(ParsePacket *pkt, uint8_t *object)
{
    if (pkt->bufLen - *pkt->bufOffset < sizeof(uint8_t)) {
        BSL_LOG_BINLOG_FIXLEN(BINLOG_ID16975, BSL_LOG_LEVEL_ERR, BSL_LOG_BINLOG_TYPE_RUN, "bufLen err", 0, 0, 0, 0);
        return HITLS_PARSE_INVALID_MSG_LEN;
    }
    *object = (uint8_t)pkt->buf[*pkt->bufOffset];
    *pkt->bufOffset += sizeof(uint8_t);
    return HITLS_SUCCESS;
}

int32_t ParseBytesToUint16(ParsePacket *pkt, uint16_t *object)
{
    if (pkt->bufLen - *pkt->bufOffset < sizeof(uint16_t)) {
        BSL_LOG_BINLOG_FIXLEN(BINLOG_ID16976, BSL_LOG_LEVEL_ERR, BSL_LOG_BINLOG_TYPE_RUN, "bufLen err", 0, 0, 0, 0);
        return HITLS_PARSE_INVALID_MSG_LEN;
    }
    *object = BSL_ByteToUint16(&pkt->buf[*pkt->bufOffset]);
    *pkt->bufOffset += sizeof(uint16_t);
    return HITLS_SUCCESS;
}

int32_t ParseBytesToUint24(ParsePacket *pkt, uint32_t *object)
{
    if (pkt->bufLen - *pkt->bufOffset < UINT24_SIZE) {
        BSL_LOG_BINLOG_FIXLEN(BINLOG_ID16977, BSL_LOG_LEVEL_ERR, BSL_LOG_BINLOG_TYPE_RUN, "bufLen err", 0, 0, 0, 0);
        return HITLS_PARSE_INVALID_MSG_LEN;
    }
    *object = BSL_ByteToUint24(&pkt->buf[*pkt->bufOffset]);
    *pkt->bufOffset += UINT24_SIZE;
    return HITLS_SUCCESS;
}

int32_t ParseBytesToUint32(ParsePacket *pkt, uint32_t *object)
{
    if (pkt->bufLen - *pkt->bufOffset < sizeof(uint32_t)) {
        BSL_LOG_BINLOG_FIXLEN(BINLOG_ID16978, BSL_LOG_LEVEL_ERR, BSL_LOG_BINLOG_TYPE_RUN, "bufLen err", 0, 0, 0, 0);
        return HITLS_PARSE_INVALID_MSG_LEN;
    }
    *object = BSL_ByteToUint32(&pkt->buf[*pkt->bufOffset]);
    *pkt->bufOffset += sizeof(uint32_t);
    return HITLS_SUCCESS;
}

int32_t ParseOneByteLengthField(ParsePacket *pkt, uint8_t *objectSize, uint8_t **object)
{
    int32_t ret = ParseBytesToUint8(pkt, objectSize);
    if (ret != HITLS_SUCCESS) {
        return ret;
    }

    return ParseBytesToArray(pkt, object, *objectSize);
}

int32_t ParseTwoByteLengthField(ParsePacket *pkt, uint16_t *objectSize, uint8_t **object)
{
    int32_t ret = ParseBytesToUint16(pkt, objectSize);
    if (ret != HITLS_SUCCESS) {
        return ret;
    }

    return ParseBytesToArray(pkt, object, *objectSize);
}

int32_t ParseBytesToArray(ParsePacket *pkt, uint8_t **object, uint32_t length)
{
    BSL_SAL_FREE(*object);
    if (pkt->bufLen - *pkt->bufOffset < length) {
        BSL_LOG_BINLOG_FIXLEN(BINLOG_ID16979, BSL_LOG_LEVEL_ERR, BSL_LOG_BINLOG_TYPE_RUN, "bufLen err", 0, 0, 0, 0);
        return HITLS_PARSE_INVALID_MSG_LEN;
    }
    if (length == 0) {
        return HITLS_SUCCESS;
    }

    *object = (uint8_t *)BSL_SAL_Malloc(length);
    if (*object == NULL) {
        BSL_LOG_BINLOG_FIXLEN(BINLOG_ID16980, BSL_LOG_LEVEL_ERR, BSL_LOG_BINLOG_TYPE_RUN, "Malloc fail", 0, 0, 0, 0);
        return HITLS_MEMALLOC_FAIL;
    }

    memcpy(*object, &pkt->buf[*pkt->bufOffset], length);
    *pkt->bufOffset += length;

    return HITLS_SUCCESS;
}

int32_t ParseCopyBytesToArray(ParsePacket *pkt, uint8_t *object, uint32_t length)
{
    if (pkt->bufLen - *pkt->bufOffset < length) {
        BSL_LOG_BINLOG_FIXLEN(BINLOG_ID16981, BSL_LOG_LEVEL_ERR, BSL_LOG_BINLOG_TYPE_RUN, "bufLen err", 0, 0, 0, 0);
        return HITLS_PARSE_INVALID_MSG_LEN;
    }
    memcpy(object, &pkt->buf[*pkt->bufOffset], length);
    *pkt->bufOffset += length;
    return HITLS_SUCCESS;
}

int32_t ParseErrorProcess(TLS_Ctx *ctx, int32_t err, uint32_t logId, const void *format,
    ALERT_Description description)
{
#ifndef HITLS_BSL_LOG
    (void)logId;
#endif
    BSL_ERR_PUSH_ERROR(err);
    if (format != NULL) {
        BSL_LOG_BINLOG_FIXLEN(logId, BSL_LOG_LEVEL_ERR, BSL_LOG_BINLOG_TYPE_RUN, format, 0, 0, 0, 0);
    }
    if (description != ALERT_UNKNOWN) {
        ctx->method.sendAlert(ctx, ALERT_LEVEL_FATAL, description);
    }
    return err;
}

int32_t CheckPeerSignScheme(HITLS_Ctx *ctx, CERT_Pair *peerCert, uint16_t signScheme)
{
    if (peerCert == NULL) {
        return RETURN_ERROR_NUMBER_PROCESS(HITLS_PARSE_UNSUPPORT_SIGN_ALG, BINLOG_ID17160, "peerCert null");
    }
    HITLS_Config *config = &ctx->config.tlsConfig;
    HITLS_CERT_Key *pubkey = NULL;
    int32_t ret = SAL_CERT_X509Ctrl(config, peerCert->cert, CERT_CTRL_GET_PUB_KEY, NULL, (void *)&pubkey);
    if (ret != HITLS_SUCCESS) {
        return RETURN_ERROR_NUMBER_PROCESS(ret, BINLOG_ID17140, "get pubkey fail");
    }
    uint32_t keyType = TLS_CERT_KEY_TYPE_UNKNOWN;
    (void)SAL_CERT_KeyCtrl(config, pubkey, CERT_KEY_CTRL_GET_TYPE, NULL, (void *)&keyType);

    const TLS_SigSchemeInfo *info = ConfigGetSignatureSchemeInfo(config, signScheme);
    if (info == NULL || info->keyType != (int32_t)keyType) {
        SAL_CERT_KeyFree(config->certMgrCtx, pubkey);
        return RETURN_ERROR_NUMBER_PROCESS(HITLS_PARSE_UNSUPPORT_SIGN_ALG, BINLOG_ID17156, "signScheme err");
    }
    /* Check if the negotiated TLS version is supported by this signature scheme. */
    uint32_t negotiatedVersionBit = MapVersion2VersionBit(
        IS_SUPPORT_DATAGRAM(ctx->config.tlsConfig.originVersionMask), ctx->negotiatedInfo.version);
    if (!(negotiatedVersionBit & info->certVersionBits)) {
        SAL_CERT_KeyFree(config->certMgrCtx, pubkey);
        return RETURN_ERROR_NUMBER_PROCESS(HITLS_PARSE_UNSUPPORT_SIGN_ALG, BINLOG_ID16195, "signScheme err");
    }
    if (info->keyType == TLS_CERT_KEY_TYPE_RSA_PSS) {
        int32_t hashAlgId = HITLS_HASH_BUTT;
        (void)SAL_CERT_KeyCtrl(config, pubkey, CERT_KEY_CTRL_GET_PSS_MD, NULL, (void *)&hashAlgId);
        if (hashAlgId != (int32_t)HITLS_HASH_BUTT && hashAlgId != info->hashAlgId) {
            SAL_CERT_KeyFree(config->certMgrCtx, pubkey);
            return RETURN_ERROR_NUMBER_PROCESS(HITLS_PARSE_UNSUPPORT_SIGN_ALG, BINLOG_ID17123, "hashAlgId unsupported");
        }
    }
    SAL_CERT_KeyFree(config->certMgrCtx, pubkey);
    return HITLS_SUCCESS;
}

int32_t ParseExtensionCommon(ParsePacket *pkt, uint16_t *exMsgLen)
{
    const char *logStr = BINGLOG_STR("parse extension length failed.");
    int32_t ret = ParseBytesToUint16(pkt, exMsgLen);
    if (ret != HITLS_SUCCESS) {
        return ParseErrorProcess(pkt->ctx, HITLS_PARSE_INVALID_MSG_LEN, BINLOG_ID15707,
            logStr, ALERT_DECODE_ERROR);
    }

    if (*exMsgLen != (pkt->bufLen - *pkt->bufOffset)) {
        return ParseErrorProcess(pkt->ctx, HITLS_PARSE_INVALID_MSG_LEN, BINLOG_ID15708,
            logStr, ALERT_DECODE_ERROR);
    }

    return HITLS_SUCCESS;
}