/*
 * 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_MODES

#include <string.h>
#include "bsl_sal.h"
#include "bsl_bytes.h"
#include "bsl_err_internal.h"
#include "crypt_errno.h"
#include "modes_local.h"
#include "crypt_utils.h"
#include "eal_cipher_local.h"
#ifdef HITLS_CRYPTO_CTR
#include "crypt_modes_ctr.h"
#endif
#ifdef HITLS_CRYPTO_CBC
#include "crypt_modes_cbc.h"
#endif
#ifdef HITLS_CRYPTO_ECB
#include "crypt_modes_ecb.h"
#endif
#ifdef HITLS_CRYPTO_GCM
#include "crypt_modes_gcm.h"
#endif
#ifdef HITLS_CRYPTO_CCM
#include "crypt_modes_ccm.h"
#endif
#ifdef HITLS_CRYPTO_XTS
#include "crypt_modes_xts.h"
#endif
#if defined(HITLS_CRYPTO_CHACHA20) && defined(HITLS_CRYPTO_CHACHA20POLY1305)
#include "crypt_modes_chacha20poly1305.h"
#endif
#ifdef HITLS_CRYPTO_CFB
#include "crypt_modes_cfb.h"
#endif
#ifdef HITLS_CRYPTO_OFB
#include "crypt_modes_ofb.h"
#endif
#ifdef HITLS_CRYPTO_WRAP
#include "crypt_modes_aes_wrap.h"
#endif

int32_t MODE_NewCtxInternal(MODES_CipherCtx *ctx, const EAL_SymMethod *method)
{
    ctx->commonCtx.ciphCtx = BSL_SAL_Calloc(1, method->ctxSize);
    if (ctx->commonCtx.ciphCtx  == NULL) {
        BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
        return CRYPT_MEM_ALLOC_FAIL;
    }
    // block mode blockSize equal symm blockSize
    ctx->commonCtx.blockSize = method->blockSize;
    ctx->commonCtx.ciphMeth = method;
    ctx->commonCtx.offset = 0;

    return CRYPT_SUCCESS;
}

MODES_CipherCtx *MODES_CipherNewCtx(int32_t algId)
{
    const EAL_SymMethod *method = EAL_GetSymMethod(algId);
    if (method == NULL) {
        BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
        return NULL;
    }
    
    MODES_CipherCtx *ctx = BSL_SAL_Calloc(1, sizeof(MODES_CipherCtx));
    if (ctx == NULL) {
        BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
        return ctx;
    }
    ctx->algId = algId;
    int32_t ret = MODE_NewCtxInternal(ctx, method);
    if (ret != CRYPT_SUCCESS) {
        BSL_SAL_Free(ctx);
        return NULL;
    }
    return ctx;
}

MODES_CipherCtx *MODES_CipherNewCtxEx(void *libCtx, int32_t algId)
{
    (void)libCtx;
    return MODES_CipherNewCtx(algId);
}

int32_t MODES_CipherFinalComplete(void *modeCtx, uint8_t *out, uint32_t *outLen)
{
    if (outLen == NULL) {
        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
        return CRYPT_NULL_INPUT;
    }
    (void)modeCtx;
    (void)out;
    *outLen = 0;
    return CRYPT_SUCCESS;
}

void MODES_ClearVfyTag(uint8_t *vfyTag, uint32_t *vfyTagLen, uint32_t maxTagLen)
{
    if (vfyTag == NULL || vfyTagLen == NULL) {
        return;
    }
    BSL_SAL_CleanseData(vfyTag, maxTagLen);
    *vfyTagLen = 0;
}

int32_t MODES_SetVfyTag(uint8_t *vfyTag, uint32_t *vfyTagLen, uint32_t maxTagLen,
    const uint8_t *tag, uint32_t inputTagLen)
{
    if (vfyTag == NULL || vfyTagLen == NULL) {
        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
        return CRYPT_NULL_INPUT;
    }
    if ((tag == NULL && inputTagLen > 0) || inputTagLen == 0) {
        BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
        return CRYPT_INVALID_ARG;
    }
    if (inputTagLen > maxTagLen) {
        BSL_ERR_PUSH_ERROR(CRYPT_MODES_TAGLEN_ERROR);
        return CRYPT_MODES_TAGLEN_ERROR;
    }
    (void)memcpy(vfyTag, tag, inputTagLen);
    *vfyTagLen = inputTagLen;
    return CRYPT_SUCCESS;
}

int32_t MODES_AeadCheckTag(bool enc, void *ctx, uint8_t *out, uint32_t *outLen, const uint8_t *vfyTag,
    uint32_t vfyTagLen, uint8_t *tagBuf, MODES_AeadGetTag getTag)
{
    if (ctx == NULL || out == NULL || outLen == NULL || vfyTag == NULL || getTag == NULL) {
        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
        return CRYPT_NULL_INPUT;
    }
    *outLen = 0;
    if (enc) {
        return CRYPT_SUCCESS;
    }
    int32_t ret = getTag(ctx, tagBuf, vfyTagLen);
    if (ret != CRYPT_SUCCESS) {
        BSL_ERR_PUSH_ERROR(ret);
        return ret;
    }
    if (ConstTimeMemcmp(tagBuf, vfyTag, vfyTagLen) == 0) {
        BSL_ERR_PUSH_ERROR(CRYPT_MODES_TAG_ERROR);
        return CRYPT_MODES_TAG_ERROR;
    }
    return CRYPT_SUCCESS;
}

int32_t MODES_CipherInitCommonCtx(MODES_CipherCommonCtx *modeCtx, void *setSymKey, void *keyCtx,
    const uint8_t *key, uint32_t keyLen, const uint8_t *iv, uint32_t ivLen)
{
    int32_t ret;
    if (modeCtx->ciphMeth == NULL) {
        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
        return CRYPT_NULL_INPUT;
    }
    if (iv == NULL && ivLen > 0) {
        BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
        return CRYPT_INVALID_ARG;
    }

    ret = ((SetKey)setSymKey)(keyCtx, key, keyLen);
    if (ret != CRYPT_SUCCESS) {
        BSL_ERR_PUSH_ERROR(ret);
        return ret;
    }
    ret = MODES_SetIv(modeCtx, iv, ivLen);
    if (ret != CRYPT_SUCCESS) {
        BSL_ERR_PUSH_ERROR(ret);
        return ret;
    }
    return CRYPT_SUCCESS;
}

int32_t MODES_CipherInitCtx(MODES_CipherCtx *ctx, void *setSymKey, void *keyCtx, const uint8_t *key, uint32_t keyLen,
    const uint8_t *iv, uint32_t ivLen, bool enc)
{
    ctx->enc = enc;
    return MODES_CipherInitCommonCtx(&ctx->commonCtx, setSymKey, keyCtx, key, keyLen, iv, ivLen);
}

static bool IfXts(CRYPT_CIPHER_AlgId id)
{
    return id == CRYPT_CIPHER_AES128_XTS || id == CRYPT_CIPHER_AES256_XTS || id == CRYPT_CIPHER_SM4_XTS;
}

static int32_t UnPaddingISO7816(const uint8_t *pad, uint32_t padLen, uint32_t *finLen)
{
    const uint8_t *p = pad;
    uint32_t len = padLen - 1;
    while (*(p + len) == 0 && len > 0) {
        len--;
    }
    len = (*(p + len) == 0x80) ? len : padLen;

    if (len == padLen) {
        BSL_ERR_PUSH_ERROR(CRYPT_EAL_CIPHER_DATA_ERROR);
        return CRYPT_EAL_CIPHER_DATA_ERROR;
    }
    (*finLen) = len;
    return CRYPT_SUCCESS;
}

static int32_t UnPaddingX923(const uint8_t *pad, uint32_t padLen, uint32_t *finLen)
{
    uint32_t len, pos, i;
    uint32_t check = 0;
    len = pad[padLen - 1];

    check |= (uint32_t)((len == 0) || (len > padLen));

    pos = padLen - len;
    for (i = 0; i < padLen - 1; i++) {
        check |= (pad[i] * (uint32_t)(i >= pos));
    }

    if (check != 0) {
        BSL_ERR_PUSH_ERROR(CRYPT_EAL_CIPHER_DATA_ERROR);
        return CRYPT_EAL_CIPHER_DATA_ERROR;
    }

    (*finLen) = padLen - len;
    return CRYPT_SUCCESS;
}

static int32_t UnPaddingPkcs(const uint8_t *pad, uint32_t padLen, uint32_t *finLen)
{
    uint32_t len, pos, i;
    uint32_t check = 0;

    len = pad[padLen - 1];
    check |= (uint32_t)((len == 0) || (len > padLen));

    pos = padLen - len;
    for (i = 0; i < padLen; i++) {
        check |= ((pad[i] ^ len) * (uint32_t)(i >= pos));
    }

    if (check != 0) {
        BSL_ERR_PUSH_ERROR(CRYPT_EAL_CIPHER_DATA_ERROR);
        return CRYPT_EAL_CIPHER_DATA_ERROR;
    }

    (*finLen) = padLen - len;
    return CRYPT_SUCCESS;
}

int32_t MODES_BlockUnPadding(int32_t padding, const uint8_t *pad, uint32_t padLen, uint32_t *dataLen)
{
    int32_t ret = 0;
    uint32_t len = *dataLen;
    switch (padding) {
        case CRYPT_PADDING_ISO7816:
            ret = UnPaddingISO7816(pad, padLen, &len);
            break;
        case CRYPT_PADDING_X923:
            ret = UnPaddingX923(pad, padLen, &len);
            break;
        case CRYPT_PADDING_PKCS5:
        case CRYPT_PADDING_PKCS7:
            ret = UnPaddingPkcs(pad, padLen, &len);
            break;
        default:
            break;
    }

    *dataLen = len;
    return ret;
}

int32_t MODES_BlockPadding(int32_t algId, int32_t padding, uint8_t blockSize, uint8_t *data, uint8_t *dataLen)
{
    uint8_t tempLen = *dataLen;
    uint8_t *pad = data + tempLen;
    uint8_t padLen = blockSize - tempLen;
    uint8_t i;
    *dataLen += padLen;
    switch (padding) {
        case CRYPT_PADDING_NONE:
            *dataLen = tempLen;
            if (tempLen % blockSize != 0) {
                return IfXts(algId) ? CRYPT_SUCCESS : CRYPT_MODE_ERR_INPUT_LEN;
            }
            break;
        case CRYPT_PADDING_ZEROS:
            for (i = 0; i < padLen; i++) {
                pad[i] = 0x00L;
            }
            break;
        case CRYPT_PADDING_ISO7816:
            pad[0] = 0x80;
            for (i = 1; i < padLen; i++) {
                pad[i] = 0x00L;
            }
            break;
        case CRYPT_PADDING_X923:
            for (i = 0; i < padLen - 1; i++) {
                pad[i] = 0x00L;
            }
            pad[padLen - 1] = padLen;
            break;
        case CRYPT_PADDING_PKCS5:
        case CRYPT_PADDING_PKCS7:
            for (i = 0; i < padLen; i++) {
                pad[i] = padLen;
            }
            break;
        default:
            *dataLen = tempLen;
            return CRYPT_INVALID_ARG;
    }
    return CRYPT_SUCCESS;
}

static int32_t MODES_CipherUpdateCache(MODES_CipherCtx *ctx,  void *blockUpdate, const uint8_t **in, uint32_t *inLen,
    uint8_t **out, uint32_t *outLen)
{
    int32_t ret;
    uint8_t blockSize = ctx->commonCtx.blockSize;
    // Process the cache. If there is cached data, the cache data is padded into a block first.
    if (ctx->dataLen > 0) {
        uint8_t padding = blockSize - ctx->dataLen;
        padding = (*inLen) > (padding) ? padding : (uint8_t)(*inLen);
        if (padding != 0) {
            memcpy(ctx->data + ctx->dataLen, (*in), padding);
            (*inLen) -= padding;
            (*in) += padding;
            ctx->dataLen += padding;
        }
    }
    // No block is formed, return.
    if (ctx->dataLen != blockSize) {
        (*outLen) = 0;
        return CRYPT_SUCCESS;
    }

    // If the block is padded, perform operations on this block first.
    if (ctx->enc) {
        RETURN_RET_IF((*outLen < blockSize), CRYPT_EAL_BUFF_LEN_NOT_ENOUGH);
        ret = ((EncryptBlock)blockUpdate)(&ctx->commonCtx, ctx->data, *out, blockSize);
    } else {
        // If it's decryption and the cached data + input data is equal to blockSize,
        // may be it's the last piece of data with padding, the data is cached in the ctx and left for final processing.
        if ((*inLen) == 0 && (ctx->pad != CRYPT_PADDING_NONE)) {
            (*outLen) = 0;
            return CRYPT_SUCCESS;
        }
        RETURN_RET_IF((*outLen < blockSize), CRYPT_EAL_BUFF_LEN_NOT_ENOUGH);
        ret = ((DecryptBlock)blockUpdate)(&ctx->commonCtx, ctx->data, *out, blockSize);
    }

    RETURN_RET_IF((ret != CRYPT_SUCCESS), ret);
    ctx->dataLen = 0;
    (*outLen) = blockSize;
    (*out) += blockSize;

    return CRYPT_SUCCESS;
}

int32_t MODES_CipherFinal(MODES_CipherCtx *modeCtx, void *blockUpdate, uint8_t *out, uint32_t *outLen)
{
    int32_t ret;
    uint32_t outLenTemp;
    RETURN_RET_IF((out == NULL || outLen == NULL), CRYPT_NULL_INPUT);
    if (modeCtx->enc) {
        if (modeCtx->pad != CRYPT_PADDING_NONE && (*outLen) < modeCtx->commonCtx.blockSize) {
            BSL_ERR_PUSH_ERROR(CRYPT_EAL_BUFF_LEN_NOT_ENOUGH);
            return CRYPT_EAL_BUFF_LEN_NOT_ENOUGH;
        }
        ret = MODES_BlockPadding(modeCtx->algId, modeCtx->pad,
            modeCtx->commonCtx.blockSize, modeCtx->data, &modeCtx->dataLen);
        if (ret != CRYPT_SUCCESS) {
            BSL_ERR_PUSH_ERROR(ret);
            return ret;
        }
        if (modeCtx->dataLen == 0) {
            *outLen = modeCtx->dataLen;
            return CRYPT_SUCCESS;
        }
        ret = ((EncryptBlock)blockUpdate)(&modeCtx->commonCtx, modeCtx->data, out, modeCtx->dataLen);
        if (ret != CRYPT_SUCCESS) {
            BSL_ERR_PUSH_ERROR(ret);
            return ret;
        }
        *outLen = modeCtx->dataLen;
    } else {
        if (modeCtx->dataLen == 0) {
            *outLen = modeCtx->dataLen;
            return CRYPT_SUCCESS;
        }
        uint8_t tmpBuf[EAL_MAX_BLOCK_LENGTH];
        ret = ((DecryptBlock)blockUpdate)(&modeCtx->commonCtx, modeCtx->data, tmpBuf, modeCtx->dataLen);
        if (ret != CRYPT_SUCCESS) {
            BSL_ERR_PUSH_ERROR(ret);
            return ret;
        }
        outLenTemp = modeCtx->dataLen;
        ret = MODES_BlockUnPadding(modeCtx->pad, tmpBuf, modeCtx->commonCtx.blockSize, &outLenTemp);
        if (ret != CRYPT_SUCCESS) {
            BSL_SAL_CleanseData(tmpBuf, EAL_MAX_BLOCK_LENGTH);
            BSL_ERR_PUSH_ERROR(ret);
            return ret;
        }
        if (outLenTemp > *outLen) {
            BSL_SAL_CleanseData(tmpBuf, EAL_MAX_BLOCK_LENGTH);
            BSL_ERR_PUSH_ERROR(CRYPT_EAL_BUFF_LEN_NOT_ENOUGH);
            return CRYPT_EAL_BUFF_LEN_NOT_ENOUGH;
        }
        memcpy(out, tmpBuf, outLenTemp);
        BSL_SAL_CleanseData(tmpBuf, EAL_MAX_BLOCK_LENGTH);
        *outLen = outLenTemp;
    }
    modeCtx->dataLen = 0;
    return ret;
}

int32_t MODES_CipherUpdate(MODES_CipherCtx *modeCtx, void *blockUpdate, const uint8_t *in, uint32_t inLen,
    uint8_t *out, uint32_t *outLen)
{
    int32_t ret;
    uint32_t tmpLen = inLen;
    uint32_t outBuflen = *outLen;
    const uint8_t *tmpIn = in;
    uint8_t *tmpOut = (uint8_t *)out;
    RETURN_RET_IF((inLen + modeCtx->dataLen < inLen), CRYPT_EAL_BUFF_LEN_TOO_LONG);
    ret = MODES_CipherUpdateCache(modeCtx, blockUpdate, &tmpIn, &tmpLen, &tmpOut, outLen);
    if (ret != CRYPT_SUCCESS) {
        BSL_ERR_PUSH_ERROR(ret);
        return ret;
    }
    if (tmpLen == 0) {
        return CRYPT_SUCCESS;
    }

    uint8_t left = tmpLen % modeCtx->commonCtx.blockSize;
    uint32_t len = tmpLen - left;
    if (len > 0) {
        if (modeCtx->enc) {
            RETURN_RET_IF((len > (outBuflen - *outLen)), CRYPT_EAL_BUFF_LEN_NOT_ENOUGH);
            ret = ((EncryptBlock)blockUpdate)(&modeCtx->commonCtx, tmpIn, tmpOut, len);
        } else {
            // If it's decryption and the cached data + input data is equal to blockSize,
            // may be it's the last piece of data with padding, the data is cached in the ctx and left for final processing.
            if ((modeCtx->pad != CRYPT_PADDING_NONE) && left == 0) {
                left = modeCtx->commonCtx.blockSize;
                len -= modeCtx->commonCtx.blockSize;
            }
            if (len > 0) {
                RETURN_RET_IF((len > (outBuflen - *outLen)), CRYPT_EAL_BUFF_LEN_NOT_ENOUGH);
                ret = ((DecryptBlock)blockUpdate)(&modeCtx->commonCtx, tmpIn, tmpOut, len);
            }
        }
        if (ret != CRYPT_SUCCESS) {
            BSL_ERR_PUSH_ERROR(ret);
            return ret;
        }
    }
    // Process the new cache.
    if (left > 0) {
        memcpy(modeCtx->data, tmpIn + len, left);
        modeCtx->dataLen = left;
    }

    // The encryption/decryption is successful. OutLen is updated.
    (*outLen) += len;
    return CRYPT_SUCCESS;
}

void MODES_Clean(MODES_CipherCommonCtx *ctx)
{
    if (ctx == NULL) {
        return;
    }
    BSL_SAL_CleanseData((void *)(ctx->buf), MODES_MAX_BUF_LENGTH);
    BSL_SAL_CleanseData((void *)(ctx->iv), MODES_MAX_IV_LENGTH);
    ctx->ciphMeth->cipherDeInitCtx(ctx->ciphCtx);
    ctx->offset = 0;
    ctx->ivIndex = 0;
}

int32_t MODES_CipherDeInitCtx(MODES_CipherCtx *modeCtx)
{
    if (modeCtx == NULL) {
        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
        return CRYPT_NULL_INPUT;
    }
    BSL_SAL_CleanseData(modeCtx->data, EAL_MAX_BLOCK_LENGTH);
    modeCtx->dataLen = 0;
    modeCtx->pad = CRYPT_PADDING_NONE;
    MODES_Clean(&modeCtx->commonCtx);
    return CRYPT_SUCCESS;
}

void MODES_CipherFreeCtx(MODES_CipherCtx *modeCtx)
{
    if (modeCtx == NULL) {
        return;
    }
    (void)MODES_CipherDeInitCtx(modeCtx);
    BSL_SAL_FREE(modeCtx->commonCtx.ciphCtx);
    BSL_SAL_Free(modeCtx);
}


int32_t MODES_SetIv(MODES_CipherCommonCtx *ctx, const uint8_t *val, uint32_t len)
{
    if (val == NULL) {
        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
        return CRYPT_NULL_INPUT;
    }

    if (len != ctx->blockSize) {
        BSL_ERR_PUSH_ERROR(CRYPT_MODES_IVLEN_ERROR);
        return CRYPT_MODES_IVLEN_ERROR;
    }

    (void)memcpy(ctx->iv, val, len);
    ctx->offset = 0;    // If the IV value is changed, the original offset is useless.
    ctx->ivIndex = 0;
    return CRYPT_SUCCESS;
}

int32_t MODES_GetIv(MODES_CipherCommonCtx *ctx, uint8_t *val, uint32_t len)
{
    if (val == NULL) {
        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
        return CRYPT_NULL_INPUT;
    }

    uint32_t ivLen = ctx->blockSize;

    if (len != ivLen) {
        BSL_ERR_PUSH_ERROR(CRYPT_MODE_ERR_INPUT_LEN);
        return CRYPT_MODE_ERR_INPUT_LEN;
    }

    memcpy(val, ctx->iv, ivLen);

    return CRYPT_SUCCESS;
}

int32_t MODES_CipherCtrl(MODES_CipherCtx *ctx, int32_t opt, void *val, uint32_t len)
{
    switch (opt) {
        case CRYPT_CTRL_REINIT_STATUS:
            BSL_SAL_CleanseData(ctx->data, EAL_MAX_BLOCK_LENGTH);
            ctx->dataLen = 0;
            ctx->pad = CRYPT_PADDING_NONE;
            return MODES_SetIv(&ctx->commonCtx, val, len);
        case CRYPT_CTRL_GET_IV:
            return MODES_GetIv(&ctx->commonCtx, (uint8_t *)val, len);
        default:
            if (ctx->commonCtx.ciphMeth == NULL ||
                ctx->commonCtx.ciphMeth->cipherCtrl == NULL) {
            BSL_ERR_PUSH_ERROR(CRYPT_MODES_CTRL_TYPE_ERROR);
            return CRYPT_MODES_CTRL_TYPE_ERROR;
            }
            return ctx->commonCtx.ciphMeth->cipherCtrl(ctx->commonCtx.ciphCtx, opt, val, len);
    }
}

int32_t MODES_CipherStreamProcess(void *processFuncs, void *ctx, const uint8_t *in, uint32_t inLen,
    uint8_t *out, uint32_t *outLen)
{
    int32_t ret;
    if (inLen == 0) {
        *outLen = 0;
        return CRYPT_SUCCESS;
    }
    if (inLen > *outLen) {
        BSL_ERR_PUSH_ERROR(CRYPT_MODE_BUFF_LEN_NOT_ENOUGH);
        return CRYPT_MODE_BUFF_LEN_NOT_ENOUGH;
    }
    
    ret = ((CipherStreamProcess)(processFuncs))(ctx, in, out, inLen);
    if (ret != CRYPT_SUCCESS) {
        BSL_ERR_PUSH_ERROR(ret);
        return ret;
    }
    *outLen = inLen;
    return CRYPT_SUCCESS;
}

// Note that CRYPT_PADDING_ZEROS cannot restore the plaintext length.
// If uses it, need to maintain the length themselves
int32_t MODES_SetPaddingCheck(int32_t pad)
{
    if (pad >= CRYPT_PADDING_MAX_COUNT || pad < CRYPT_PADDING_NONE) {
        BSL_ERR_PUSH_ERROR(CRYPT_MODES_PADDING_NOT_SUPPORT);
        return CRYPT_MODES_PADDING_NOT_SUPPORT;
    }
    return CRYPT_SUCCESS;
}

int32_t MODES_SetEncryptKey(MODES_CipherCommonCtx *ctx, const uint8_t *key, uint32_t len)
{
    // The ctx and key have been checked at the EAL layer and will not be checked again here.
    // The keyMethod will support registration in the future. Therefore, this check is added.
    if (ctx == NULL || ctx->ciphMeth == NULL) {
        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
        return CRYPT_NULL_INPUT;
    }
    return ctx->ciphMeth->setEncryptKey(ctx->ciphCtx, key, len);
}

int32_t MODES_SetDecryptKey(MODES_CipherCommonCtx *ctx, const uint8_t *key, uint32_t len)
{
    // The ctx and key have been checked at the EAL layer and will not be checked again here.
    // The keyMethod will support registration in the future. Therefore, this check is added.
    if (ctx == NULL || ctx->ciphMeth == NULL) {
        BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
        return CRYPT_NULL_INPUT;
    }
    return ctx->ciphMeth->setDecryptKey(ctx->ciphCtx, key, len);
}

MODES_CipherCtx *MODES_CipherDupCtx(const MODES_CipherCtx *modeCtx)
{
    if (modeCtx == NULL) {
        return NULL;
    }
    MODES_CipherCtx *ctx = BSL_SAL_Dump(modeCtx, sizeof(MODES_CipherCtx));
    if (ctx == NULL) {
        BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
        return ctx;
    }

    void *ciphCtx = BSL_SAL_Dump(modeCtx->commonCtx.ciphCtx, modeCtx->commonCtx.ciphMeth->ctxSize);
    if (ciphCtx == NULL) {
        BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
        BSL_SAL_ClearFree(ctx, sizeof(MODES_CipherCtx));
        return NULL;
    }

    ctx->commonCtx.ciphCtx = ciphCtx;
    return ctx;
}

#endif // HITLS_CRYPTO_MODES