/*

 * 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"

#ifdef HITLS_CRYPTO_CBC_MAC

#include <stdint.h>

#include "bsl_sal.h"

#include "crypt_types.h"

#include "crypt_utils.h"

#include "bsl_err_internal.h"

#include "cipher_mac_common.h"

#include "crypt_errno.h"

#include "crypt_cbc_mac.h"

#include "eal_mac_local.h"



CRYPT_CBC_MAC_Ctx *CRYPT_CBC_MAC_NewCtx(CRYPT_MAC_AlgId id)

{

    EAL_MacDepMethod method = {0};

    int32_t ret = EAL_MacFindDepMethod(id, NULL, NULL, &method, NULL, false);

    if (ret != CRYPT_SUCCESS) {

        return NULL;

    }

    CRYPT_CBC_MAC_Ctx *ctx = BSL_SAL_Calloc(1, sizeof(CRYPT_CBC_MAC_Ctx));

    if (ctx == NULL) {

        BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);

        return NULL;

    }

    ret = CipherMacInitCtx(&ctx->common, method.method.sym);

    if (ret != CRYPT_SUCCESS) {

        BSL_SAL_Free(ctx);

        return NULL;

    }

    ctx->paddingType = CRYPT_PADDING_MAX_COUNT;

    return ctx;

}



CRYPT_CBC_MAC_Ctx *CRYPT_CBC_MAC_NewCtxEx(void *libCtx, CRYPT_MAC_AlgId id)

{

    (void)libCtx;

    return CRYPT_CBC_MAC_NewCtx(id);

}



int32_t CRYPT_CBC_MAC_Init(CRYPT_CBC_MAC_Ctx *ctx, const uint8_t *key, uint32_t len)

{

    if (ctx == NULL) {

        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);

        return CRYPT_NULL_INPUT;

    }

    return CipherMacInit(&ctx->common, key, len);

}



int32_t CRYPT_CBC_MAC_InitEx(CRYPT_CBC_MAC_Ctx *ctx, const uint8_t *key, uint32_t len, void *param)

{

    (void)param;

    if (ctx == NULL) {

        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);

        return CRYPT_NULL_INPUT;

    }

    return CipherMacInit(&ctx->common, key, len);

}



int32_t CRYPT_CBC_MAC_Update(CRYPT_CBC_MAC_Ctx *ctx, const uint8_t *in, uint32_t len)

{

    if (ctx == NULL) {

        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);

        return CRYPT_NULL_INPUT;

    }

    if (ctx->paddingType == CRYPT_PADDING_MAX_COUNT) {

        BSL_ERR_PUSH_ERROR(CRYPT_CBC_MAC_PADDING_NOT_SET);

        return CRYPT_CBC_MAC_PADDING_NOT_SET;

    }

    return CipherMacUpdate(&ctx->common, in, len);

}



static int32_t CbcMacPadding(CRYPT_CBC_MAC_Ctx *ctx)

{

    const EAL_SymMethod *method = ctx->common.method;

    uint32_t length = ctx->common.len;

    uint32_t padLen = method->blockSize - length;

    switch (ctx->paddingType) {

        case CRYPT_PADDING_ZEROS:

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

                ctx->common.left[length++] = 0;

            }

            ctx->common.len = length;

            return CRYPT_SUCCESS;

        default:

            BSL_ERR_PUSH_ERROR(CRYPT_CBC_MAC_PADDING_NOT_SUPPORT);

            return CRYPT_CBC_MAC_PADDING_NOT_SUPPORT;

    }

}



int32_t CRYPT_CBC_MAC_Final(CRYPT_CBC_MAC_Ctx *ctx, uint8_t *out, uint32_t *len)

{

    if (ctx == NULL || ctx->common.method == NULL || len == NULL || out == NULL) {

        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);

        return CRYPT_NULL_INPUT;

    }

    const EAL_SymMethod *method = ctx->common.method;

    uint32_t blockSize = method->blockSize;

    if (*len < blockSize) {

        BSL_ERR_PUSH_ERROR(CRYPT_CBC_MAC_OUT_BUFF_LEN_NOT_ENOUGH);

        return CRYPT_CBC_MAC_OUT_BUFF_LEN_NOT_ENOUGH;

    }



    int32_t ret = CbcMacPadding(ctx);

    if (ret != CRYPT_SUCCESS) {

        return ret;

    }

    DATA_XOR(ctx->common.left, ctx->common.data, ctx->common.left, blockSize);

    ret = method->encryptBlock(ctx->common.key, ctx->common.left, out, blockSize);

    if (ret != CRYPT_SUCCESS) {

        BSL_ERR_PUSH_ERROR(ret);

        return ret;

    }

    *len = blockSize;

    return CRYPT_SUCCESS;

}



int32_t CRYPT_CBC_MAC_Reinit(CRYPT_CBC_MAC_Ctx *ctx)

{

    if (ctx == NULL) {

        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);

        return CRYPT_NULL_INPUT;

    }

    return CipherMacReinit(&ctx->common);

}



int32_t CRYPT_CBC_MAC_Deinit(CRYPT_CBC_MAC_Ctx *ctx)

{

    if (ctx == NULL) {

        return CRYPT_NULL_INPUT;

    }

    return CipherMacDeinit(&ctx->common);

}



int32_t CRYPT_CBC_MAC_Ctrl(CRYPT_CBC_MAC_Ctx *ctx, uint32_t opt, void *val, uint32_t len)

{

    if (ctx == NULL || val == NULL) {

        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);

        return CRYPT_NULL_INPUT;

    }

    switch (opt) {

        case CRYPT_CTRL_SET_CBC_MAC_PADDING:

            if (len != sizeof(int32_t)) {

                BSL_ERR_PUSH_ERROR(CRYPT_CBC_MAC_ERR_CTRL_LEN);

                return CRYPT_CBC_MAC_ERR_CTRL_LEN;

            }

            ctx->paddingType = *(int32_t*)val;

            return CRYPT_SUCCESS;

        case CRYPT_CTRL_GET_MACLEN:

            return CipherMacGetMacLen(&ctx->common, val, len);

        default:

            BSL_ERR_PUSH_ERROR(CRYPT_CBC_MAC_ERR_UNSUPPORTED_CTRL_OPTION);

            return CRYPT_CBC_MAC_ERR_UNSUPPORTED_CTRL_OPTION;

    }

}



void CRYPT_CBC_MAC_FreeCtx(CRYPT_CBC_MAC_Ctx *ctx)

{

    if (ctx == NULL) {

        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);

        return;

    }

    CipherMacDeinitCtx(&ctx->common);

    BSL_SAL_Free(ctx);

}



CRYPT_CBC_MAC_Ctx *CRYPT_CBC_MAC_DupCtx(const CRYPT_CBC_MAC_Ctx *ctx)

{

    if (ctx == NULL) {

        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);

        return NULL;

    }



    CRYPT_CBC_MAC_Ctx *newCtx = BSL_SAL_Dump(ctx, sizeof(CRYPT_CBC_MAC_Ctx));

    if (newCtx == NULL) {

        BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);

        return NULL;

    }



    void *key = BSL_SAL_Dump(ctx->common.key, ctx->common.method->ctxSize);

    if (key == NULL) {

        BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);

        BSL_SAL_Free(newCtx);

        return NULL;

    }

    newCtx->common.key = key;

    return newCtx;

}

#endif