/***************************************************************************
 *
 * This file is provided under a dual BSD/GPLv2 license.  When using or
 *   redistributing this file, you may do so under either license.
 * 
 *   GPL LICENSE SUMMARY
 * 
 *   Copyright(c) 2007-2022 Intel Corporation. All rights reserved.
 * 
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of version 2 of the GNU General Public License as
 *   published by the Free Software Foundation.
 * 
 *   This program is distributed in the hope that it will be useful, but
 *   WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   General Public License for more details.
 * 
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *   The full GNU General Public License is included in this distribution
 *   in the file called LICENSE.GPL.
 * 
 *   Contact Information:
 *   Intel Corporation
 * 
 *   BSD LICENSE
 * 
 *   Copyright(c) 2007-2022 Intel Corporation. All rights reserved.
 *   All rights reserved.
 * 
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 * 
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 * 
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * 
 *
 ***************************************************************************/

/**
 ***************************************************************************
 * @file lac_sym_qat_cipher.c      QAT-related support functions for Cipher
 *
 * @ingroup LacSymQat_Cipher
 *
 * @description Functions to support the QAT related operations for Cipher
 ***************************************************************************/

/*
*******************************************************************************
* Include public/global header files
*******************************************************************************
*/

#include "Osal.h"

#include "cpa.h"
#include "icp_accel_devices.h"
#include "icp_adf_debug.h"
#include "lac_sym_qat.h"
#include "lac_sym_qat_cipher.h"
#include "lac_mem.h"
#include "lac_common.h"
#include "cpa_cy_sym.h"
#include "lac_sym_qat.h"
#include "lac_sym_cipher_defs.h"
#include "icp_qat_hw.h"
#include "icp_qat_fw_la.h"

#define LAC_UNUSED_POS_MASK 0x3

/*****************************************************************************
 *  Internal data
 *****************************************************************************/

typedef enum _icp_qat_hw_key_depend
{
    IS_KEY_DEP_NO = 0,
    IS_KEY_DEP_YES,
} icp_qat_hw_key_depend;

/* LAC_CIPHER_IS_XTS_MODE */
static const uint8_t key_size_xts[] = {
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    ICP_QAT_HW_CIPHER_ALGO_AES128, /* ICP_QAT_HW_AES_128_XTS_KEY_SZ */
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    ICP_QAT_HW_CIPHER_ALGO_AES256 /* ICP_QAT_HW_AES_256_XTS_KEY_SZ */
};
/* LAC_CIPHER_IS_AES */
static const uint8_t key_size_aes[] = {
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    ICP_QAT_HW_CIPHER_ALGO_AES128, /* ICP_QAT_HW_AES_128_KEY_SZ */
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    ICP_QAT_HW_CIPHER_ALGO_AES192, /* ICP_QAT_HW_AES_192_KEY_SZ */
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    ICP_QAT_HW_CIPHER_ALGO_AES256 /* ICP_QAT_HW_AES_256_KEY_SZ */
};
/* LAC_CIPHER_IS_AES_F8 */
static const uint8_t key_size_f8[] = {
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    ICP_QAT_HW_CIPHER_ALGO_AES128, /* ICP_QAT_HW_AES_128_F8_KEY_SZ */
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    ICP_QAT_HW_CIPHER_ALGO_AES192, /* ICP_QAT_HW_AES_192_F8_KEY_SZ */
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    ICP_QAT_HW_CIPHER_ALGO_AES256 /* ICP_QAT_HW_AES_256_F8_KEY_SZ */
};

typedef struct _icp_qat_hw_cipher_info
{
    icp_qat_hw_cipher_algo_t algorithm;
    icp_qat_hw_cipher_mode_t mode;
    icp_qat_hw_cipher_convert_t key_convert[2];
    icp_qat_hw_cipher_dir_t dir[2];
    icp_qat_hw_key_depend isKeyLenDepend;
    const uint8_t *pAlgByKeySize;
} icp_qat_hw_cipher_info;

static const icp_qat_hw_cipher_info icp_qat_alg_info[] = {
    /* CPA_CY_SYM_CIPHER_NULL */
    {
        ICP_QAT_HW_CIPHER_ALGO_NULL,
        ICP_QAT_HW_CIPHER_ECB_MODE,
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_NO_CONVERT},
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_DECRYPT},
        IS_KEY_DEP_NO,
        NULL,
    },
    /* CPA_CY_SYM_CIPHER_ARC4 */
    {
        ICP_QAT_HW_CIPHER_ALGO_ARC4,
        ICP_QAT_HW_CIPHER_ECB_MODE,
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_NO_CONVERT},
        /* Streaming ciphers are a special case. Decrypt = encrypt */
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_ENCRYPT},
        IS_KEY_DEP_NO,
        NULL,
    },
    /* CPA_CY_SYM_CIPHER_AES_ECB */
    {
        ICP_QAT_HW_CIPHER_ALGO_AES128,
        ICP_QAT_HW_CIPHER_ECB_MODE,
        /* AES decrypt key needs to be reversed.  Instead of reversing the key
         * at session registration, it is instead reversed on-the-fly by
         * setting the KEY_CONVERT bit here
         */
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_KEY_CONVERT},
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_DECRYPT},
        IS_KEY_DEP_YES,
        key_size_aes,
    },
    /* CPA_CY_SYM_CIPHER_AES_CBC */
    {
        ICP_QAT_HW_CIPHER_ALGO_AES128,
        ICP_QAT_HW_CIPHER_CBC_MODE,
        /* AES decrypt key needs to be reversed.  Instead of reversing the key
         * at session registration, it is instead reversed on-the-fly by
         * setting the KEY_CONVERT bit here
         */
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_KEY_CONVERT},
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_DECRYPT},
        IS_KEY_DEP_YES,
        key_size_aes,
    },
    /* CPA_CY_SYM_CIPHER_AES_CTR */
    {
        ICP_QAT_HW_CIPHER_ALGO_AES128,
        ICP_QAT_HW_CIPHER_CTR_MODE,
        /* AES decrypt key needs to be reversed.  Instead of reversing the key
         * at session registration, it is instead reversed on-the-fly by
         * setting the KEY_CONVERT bit here
         */
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_NO_CONVERT},
        /* Streaming ciphers are a special case. Decrypt = encrypt
         * Overriding default values previously set for AES
         */
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_ENCRYPT},
        IS_KEY_DEP_YES,
        key_size_aes,
    },
    /* CPA_CY_SYM_CIPHER_AES_CCM */
    {
        ICP_QAT_HW_CIPHER_ALGO_AES128,
        ICP_QAT_HW_CIPHER_CTR_MODE,
        /* AES decrypt key needs to be reversed.  Instead of reversing the key
         * at session registration, it is instead reversed on-the-fly by
         * setting the KEY_CONVERT bit here
         */
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_NO_CONVERT},
        /* Streaming ciphers are a special case. Decrypt = encrypt
         * Overriding default values previously set for AES
         */
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_ENCRYPT},
        IS_KEY_DEP_YES,
        key_size_aes,
    },
    /* CPA_CY_SYM_CIPHER_AES_GCM */
    {
        ICP_QAT_HW_CIPHER_ALGO_AES128,
        ICP_QAT_HW_CIPHER_CTR_MODE,
        /* AES decrypt key needs to be reversed.  Instead of reversing the key
         * at session registration, it is instead reversed on-the-fly by
         * setting the KEY_CONVERT bit here
         */
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_NO_CONVERT},
        /* Streaming ciphers are a special case. Decrypt = encrypt
         * Overriding default values previously set for AES
         */
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_ENCRYPT},
        IS_KEY_DEP_YES,
        key_size_aes,
    },
    /* CPA_CY_SYM_CIPHER_DES_ECB */
    {
        ICP_QAT_HW_CIPHER_ALGO_DES,
        ICP_QAT_HW_CIPHER_ECB_MODE,
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_NO_CONVERT},
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_DECRYPT},
        IS_KEY_DEP_NO,
        NULL,
    },
    /* CPA_CY_SYM_CIPHER_DES_CBC */
    {
        ICP_QAT_HW_CIPHER_ALGO_DES,
        ICP_QAT_HW_CIPHER_CBC_MODE,
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_NO_CONVERT},
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_DECRYPT},
        IS_KEY_DEP_NO,
        NULL,
    },
    /* CPA_CY_SYM_CIPHER_3DES_ECB */
    {
        ICP_QAT_HW_CIPHER_ALGO_3DES,
        ICP_QAT_HW_CIPHER_ECB_MODE,
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_NO_CONVERT},
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_DECRYPT},
        IS_KEY_DEP_NO,
        NULL,
    },
    /* CPA_CY_SYM_CIPHER_3DES_CBC */
    {
        ICP_QAT_HW_CIPHER_ALGO_3DES,
        ICP_QAT_HW_CIPHER_CBC_MODE,
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_NO_CONVERT},
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_DECRYPT},
        IS_KEY_DEP_NO,
        NULL,
    },
    /* CPA_CY_SYM_CIPHER_3DES_CTR */
    {
        ICP_QAT_HW_CIPHER_ALGO_3DES,
        ICP_QAT_HW_CIPHER_CTR_MODE,
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_NO_CONVERT},
        /* Streaming ciphers are a special case. Decrypt = encrypt
         * Overriding default values previously set for AES
         */
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_ENCRYPT},
        IS_KEY_DEP_NO,
        NULL,
    },
    /* CPA_CY_SYM_CIPHER_KASUMI_F8 */
    {
        ICP_QAT_HW_CIPHER_ALGO_KASUMI,
        ICP_QAT_HW_CIPHER_F8_MODE,
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_NO_CONVERT},
        /* Streaming ciphers are a special case. Decrypt = encrypt */
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_ENCRYPT},
        IS_KEY_DEP_NO,
        NULL,
    },
    /* CPA_CY_SYM_CIPHER_SNOW3G_UEA2 */
    {
        /* The KEY_CONVERT bit has to be set for Snow_3G operation */
        ICP_QAT_HW_CIPHER_ALGO_SNOW_3G_UEA2,
        ICP_QAT_HW_CIPHER_ECB_MODE,
        {ICP_QAT_HW_CIPHER_KEY_CONVERT, ICP_QAT_HW_CIPHER_KEY_CONVERT},
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_DECRYPT},
        IS_KEY_DEP_NO,
        NULL,
    },
    /* CPA_CY_SYM_CIPHER_AES_F8 */
    {
        ICP_QAT_HW_CIPHER_ALGO_AES128,
        ICP_QAT_HW_CIPHER_F8_MODE,
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_NO_CONVERT},
        /* Streaming ciphers are a special case. Decrypt = encrypt */
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_ENCRYPT},
        IS_KEY_DEP_YES,
        key_size_f8,
    },
    /* CPA_CY_SYM_CIPHER_AES_XTS */
    {
        ICP_QAT_HW_CIPHER_ALGO_AES128,
        ICP_QAT_HW_CIPHER_XTS_MODE,
        /* AES decrypt key needs to be reversed.  Instead of reversing the key
         * at session registration, it is instead reversed on-the-fly by
         * setting the KEY_CONVERT bit here
         */
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_KEY_CONVERT},
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_DECRYPT},
        IS_KEY_DEP_YES,
        key_size_xts,
    },
    /* CPA_CY_SYM_CIPHER_ZUC_EEA3 */
    {
        ICP_QAT_HW_CIPHER_ALGO_ZUC_3G_128_EEA3,
        ICP_QAT_HW_CIPHER_ECB_MODE,
        {ICP_QAT_HW_CIPHER_KEY_CONVERT, ICP_QAT_HW_CIPHER_KEY_CONVERT},
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_DECRYPT},
        IS_KEY_DEP_NO,
        NULL,
    },
    /* CPA_CY_SYM_CIPHER_CHACHA */
    {
        ICP_QAT_HW_CIPHER_ALGO_CHACHA20_POLY1305,
        ICP_QAT_HW_CIPHER_CTR_MODE,
        {ICP_QAT_HW_CIPHER_NO_CONVERT, ICP_QAT_HW_CIPHER_NO_CONVERT},
        {ICP_QAT_HW_CIPHER_ENCRYPT, ICP_QAT_HW_CIPHER_DECRYPT},
        IS_KEY_DEP_NO,
        NULL,
    },
    /* RESERVED#1 in order to alinged with unsupported Algo in API repo */
    {0},
    /* RESERVED#2 in order to alinged with unsupported Algo in API repo */
    {0},
    /* RESERVED#3 in order to alinged with unsupported Algo in API repo */
    {0},
};

/*****************************************************************************
 *  Internal functions
 *****************************************************************************/

void LacSymQat_CipherCtrlBlockWrite(icp_qat_la_bulk_req_ftr_t *pMsg,
                                    Cpa32U cipherAlgorithm,
                                    Cpa32U targetKeyLenInBytes,
                                    Cpa32U sliceType,
                                    icp_qat_fw_slice_t nextSlice,
                                    Cpa8U cipherCfgOffsetInQuadWord)
{
    icp_qat_fw_cipher_cd_ctrl_hdr_t *cd_ctrl =
        (icp_qat_fw_cipher_cd_ctrl_hdr_t *)&(pMsg->cd_ctrl);

    /* state_padding_sz is nonzero for f8 mode only */
    cd_ctrl->cipher_padding_sz = 0;

    /* Special handling of AES 192 key for UCS slice.
       UCS requires it to have 32 bytes - set is as targetKeyLen
       in this case, and add padding. It makes no sense
       to force applications to provide such key length for couple reasons:
       1. It won't be possible to distinguish between AES 192 and 256 based
          on key lenght only
       2. Only some modes of AES will use UCS slice, then application will
          have to know which ones */
    if (ICP_QAT_FW_LA_USE_UCS_SLICE_TYPE == sliceType &&
        ICP_QAT_HW_AES_192_KEY_SZ == targetKeyLenInBytes)
    {
        targetKeyLenInBytes = ICP_QAT_HW_UCS_AES_192_KEY_SZ;
    }

    switch (cipherAlgorithm)
    {
        /* Base Key is not passed down to QAT in the case of ARC4 or NULL */
        case CPA_CY_SYM_CIPHER_ARC4:
        case CPA_CY_SYM_CIPHER_NULL:
            cd_ctrl->cipher_key_sz = 0;
            break;
        case CPA_CY_SYM_CIPHER_KASUMI_F8:
            cd_ctrl->cipher_key_sz =
                LAC_BYTES_TO_QUADWORDS(ICP_QAT_HW_KASUMI_F8_KEY_SZ);
            cd_ctrl->cipher_padding_sz = ICP_QAT_HW_MODE_F8_NUM_REG_TO_CLEAR;
            break;
        /* For Snow3G UEA2 content descriptor key size is
           key size plus iv size */
        case CPA_CY_SYM_CIPHER_SNOW3G_UEA2:
            cd_ctrl->cipher_key_sz = LAC_BYTES_TO_QUADWORDS(
                ICP_QAT_HW_SNOW_3G_UEA2_KEY_SZ + ICP_QAT_HW_SNOW_3G_UEA2_IV_SZ);
            break;
        case CPA_CY_SYM_CIPHER_AES_F8:
            cd_ctrl->cipher_key_sz =
                LAC_BYTES_TO_QUADWORDS(targetKeyLenInBytes);
            cd_ctrl->cipher_padding_sz =
                (2 * ICP_QAT_HW_MODE_F8_NUM_REG_TO_CLEAR);
            break;
        /* For ZUC EEA3 content descriptor key size is
           key size plus iv size */
        case CPA_CY_SYM_CIPHER_ZUC_EEA3:
            cd_ctrl->cipher_key_sz = LAC_BYTES_TO_QUADWORDS(
                ICP_QAT_HW_ZUC_3G_EEA3_KEY_SZ + ICP_QAT_HW_ZUC_3G_EEA3_IV_SZ);
            break;
        default:
            cd_ctrl->cipher_key_sz =
                LAC_BYTES_TO_QUADWORDS(targetKeyLenInBytes);
    }

    cd_ctrl->cipher_state_sz =
        LAC_BYTES_TO_QUADWORDS(LacSymQat_CipherIvSizeBytesGet(cipherAlgorithm));

    cd_ctrl->cipher_cfg_offset = cipherCfgOffsetInQuadWord;

    ICP_QAT_FW_COMN_NEXT_ID_SET(cd_ctrl, nextSlice);
    ICP_QAT_FW_COMN_CURR_ID_SET(cd_ctrl, ICP_QAT_FW_SLICE_CIPHER);
}

void LacSymQat_CipherGetCfgData(lac_session_desc_t *pSession,
                                icp_qat_hw_cipher_algo_t *pAlgorithm,
                                icp_qat_hw_cipher_mode_t *pMode,
                                icp_qat_hw_cipher_dir_t *pDir,
                                icp_qat_hw_cipher_convert_t *pKey_convert)
{
    CpaCySymCipherAlgorithm cipherAlgorithm = 0;
    icp_qat_hw_cipher_dir_t cipherDirection = 0;

    LAC_ENSURE_NOT_NULL(pSession);
    LAC_ENSURE_NOT_NULL(pAlgorithm);
    LAC_ENSURE_NOT_NULL(pMode);
    LAC_ENSURE_NOT_NULL(pDir);
    LAC_ENSURE_NOT_NULL(pKey_convert);

    /* Set defaults */
    *pKey_convert = ICP_QAT_HW_CIPHER_NO_CONVERT;
    *pAlgorithm = ICP_QAT_HW_CIPHER_ALGO_NULL;
    *pMode = ICP_QAT_HW_CIPHER_ECB_MODE;
    *pDir = ICP_QAT_HW_CIPHER_ENCRYPT;

    /* decrease since it's numbered from 1 instead of 0 */
    cipherAlgorithm = pSession->cipherAlgorithm - 1;
    cipherDirection =
        pSession->cipherDirection == CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT
            ? ICP_QAT_HW_CIPHER_ENCRYPT
            : ICP_QAT_HW_CIPHER_DECRYPT;

    /* clang-format off */
    LAC_ENSURE_RETURN_VOID(cipherAlgorithm < CPA_CY_SYM_CIPHER_CHACHA, "Invalid cipherAlgorithm value\n");
    LAC_ENSURE_RETURN_VOID(cipherDirection <= ICP_QAT_HW_CIPHER_DECRYPT, "Invalid cipherDirection value\n");
    /* clang-format on */

    *pAlgorithm = icp_qat_alg_info[cipherAlgorithm].algorithm;
    *pMode = icp_qat_alg_info[cipherAlgorithm].mode;
    *pDir = icp_qat_alg_info[cipherAlgorithm].dir[cipherDirection];
    *pKey_convert =
        icp_qat_alg_info[cipherAlgorithm].key_convert[cipherDirection];

    if (IS_KEY_DEP_NO != icp_qat_alg_info[cipherAlgorithm].isKeyLenDepend)
    {
        *pAlgorithm = icp_qat_alg_info[cipherAlgorithm]
                          .pAlgByKeySize[pSession->cipherKeyLenInBytes];
        /* clang-format off */
        LAC_ENSURE(ICP_QAT_HW_CIPHER_ALGO_NULL != *pAlgorithm, "Invalid AES key size\n");
        /* clang-format on */
    }

    /* CCP and AES_GCM single pass, despite being limited to CTR/AEAD mode,
     * support both Encrypt/Decrypt modes - this is because of the
     * differences in the hash computation/verification paths in
     * encrypt/decrypt modes respectively.
     * By default CCP is set as CTR Mode.Set AEAD Mode for AES_GCM.
     */
    if (SPC_YES == pSession->singlePassState)
    {
        if (LAC_CIPHER_IS_GCM(pSession->cipherAlgorithm))
            *pMode = ICP_QAT_HW_CIPHER_GCM_MODE;
        else if (LAC_CIPHER_IS_CCM(pSession->cipherAlgorithm))
            *pMode = ICP_QAT_HW_CIPHER_CCM_MODE;

        if (cipherDirection == ICP_QAT_HW_CIPHER_DECRYPT)
            *pDir = ICP_QAT_HW_CIPHER_DECRYPT;
    }
}

void LacSymQat_CipherHwBlockPopulateCfgData(lac_session_desc_t *pSession,
                                            const void *pCipherHwBlock,
                                            Cpa32U *pSizeInBytes)
{
    icp_qat_hw_cipher_algo_t algorithm = ICP_QAT_HW_CIPHER_ALGO_NULL;
    icp_qat_hw_cipher_mode_t mode = ICP_QAT_HW_CIPHER_ECB_MODE;
    icp_qat_hw_cipher_dir_t dir = ICP_QAT_HW_CIPHER_ENCRYPT;
    icp_qat_hw_cipher_convert_t key_convert;
    icp_qat_hw_cipher_config_t *pCipherConfig =
        (icp_qat_hw_cipher_config_t *)pCipherHwBlock;
    icp_qat_hw_ucs_cipher_config_t *pUCSCipherConfig =
        (icp_qat_hw_ucs_cipher_config_t *)pCipherHwBlock;
    Cpa32U aed_hash_cmp_length = 0;
    Cpa32U val, reserved;

    LAC_ENSURE_NOT_NULL(pCipherConfig);
    LAC_ENSURE_NOT_NULL(pSizeInBytes);

    *pSizeInBytes = 0;

    LacSymQat_CipherGetCfgData(pSession, &algorithm, &mode, &dir, &key_convert);

    /* Build the cipher config into the hardware setup block */
    if (SPC_YES == pSession->singlePassState)
    {
        aed_hash_cmp_length = pSession->hashResultSize;
        reserved =
            ICP_QAT_HW_CIPHER_CONFIG_BUILD_UPPER(pSession->aadLenInBytes);
    }
    else
    {
        reserved = 0;
    }

    val = ICP_QAT_HW_CIPHER_CONFIG_BUILD(
        mode, algorithm, key_convert, dir, aed_hash_cmp_length);

    *pSizeInBytes = sizeof(icp_qat_hw_cipher_config_t);

    /* UCS slice has 128-bit configuration register.
     * Legacy cipher slice has 64-bit config register */
    if (ICP_QAT_FW_LA_USE_UCS_SLICE_TYPE == pSession->cipherSliceType)
    {
        pUCSCipherConfig->val = val;
        pUCSCipherConfig->reserved[0] = reserved;
        pUCSCipherConfig->reserved[1] = 0;
        pUCSCipherConfig->reserved[2] = 0;
        *pSizeInBytes = sizeof(icp_qat_hw_ucs_cipher_config_t);

        /* For XTS mode use cached forward/reverse key depending on direction */
        if (LAC_CIPHER_IS_XTS_MODE(pSession->cipherAlgorithm))
        {
            Cpa8U *pCipherKey = (Cpa8U *)pCipherHwBlock + *pSizeInBytes;

            if (ICP_QAT_HW_CIPHER_DECRYPT == dir)
            {
                osalMemCopy(pCipherKey,
                            pSession->cipherAesXtsKey1Reverse,
                            pSession->cipherKeyLenInBytes / 2);
            }
            else
            {
                osalMemCopy(pCipherKey,
                            pSession->cipherAesXtsKey1Forward,
                            pSession->cipherKeyLenInBytes / 2);
            }
        }
    }
    else
    {
        pCipherConfig->val = val;
        pCipherConfig->reserved = reserved;
        *pSizeInBytes = sizeof(icp_qat_hw_cipher_config_t);
    }
}

void LacSymQat_CipherHwBlockPopulateKeySetup(
    lac_session_desc_t *pSessionDesc,
    const CpaCySymCipherSetupData *pCipherSetupData,
    Cpa32U targetKeyLenInBytes,
    Cpa32U sliceType,
    const void *pCipherHwBlock,
    Cpa32U *pSizeInBytes)
{
    Cpa8U *pCipherKey = (Cpa8U *)pCipherHwBlock;
    Cpa32U actualKeyLenInBytes = pCipherSetupData->cipherKeyLenInBytes;

    *pSizeInBytes = 0;

    /* Key is copied into content descriptor for all cases except for
     * Arc4 and Null cipher */
    if (!(LAC_CIPHER_IS_ARC4(pCipherSetupData->cipherAlgorithm) ||
          LAC_CIPHER_IS_NULL(pCipherSetupData->cipherAlgorithm)))
    {
        /* Special handling of AES 192 key for UCS slice.
           UCS requires it to have 32 bytes - set is as targetKeyLen
           in this case, and add padding. It makes no sense
           to force applications to provide such key length for couple reasons:
           1. It won't be possible to distinguish between AES 192 and 256 based
              on key lenght only
           2. Only some modes of AES will use UCS slice, then application will
              have to know which ones */
        if (ICP_QAT_FW_LA_USE_UCS_SLICE_TYPE == sliceType &&
            ICP_QAT_HW_AES_192_KEY_SZ == targetKeyLenInBytes)
        {
            targetKeyLenInBytes = ICP_QAT_HW_UCS_AES_192_KEY_SZ;
        }

        /* Set the Cipher key field in the cipher block */
        memcpy(pCipherKey, pCipherSetupData->pCipherKey, actualKeyLenInBytes);
        /* Pad the key with 0's if required */
        if (0 < (targetKeyLenInBytes - actualKeyLenInBytes))
        {
            LAC_OS_BZERO(pCipherKey + actualKeyLenInBytes,
                         targetKeyLenInBytes - actualKeyLenInBytes);
        }
        *pSizeInBytes += targetKeyLenInBytes;

        switch (pCipherSetupData->cipherAlgorithm)
        {
                /* For Kasumi in F8 mode Cipher Key is concatenated with
                 * Cipher Key XOR-ed with Key Modifier (CK||CK^KM) */
            case CPA_CY_SYM_CIPHER_KASUMI_F8:
            {
                Cpa32U wordIndex = 0;
                Cpa32U *pu32CipherKey = (Cpa32U *)pCipherSetupData->pCipherKey;
                Cpa32U *pTempKey = (Cpa32U *)(pCipherKey + targetKeyLenInBytes);

                /* XOR Key with KASUMI F8 key modifier at 4 bytes level */
                for (wordIndex = 0;
                     wordIndex < LAC_BYTES_TO_LONGWORDS(targetKeyLenInBytes);
                     wordIndex++)
                {
                    pTempKey[wordIndex] =
                        pu32CipherKey[wordIndex] ^
                        LAC_CIPHER_KASUMI_F8_KEY_MODIFIER_4_BYTES;
                }

                *pSizeInBytes += targetKeyLenInBytes;

                /* also add padding for F8 */
                *pSizeInBytes +=
                    LAC_QUADWORDS_TO_BYTES(ICP_QAT_HW_MODE_F8_NUM_REG_TO_CLEAR);
                LAC_OS_BZERO((Cpa8U *)pTempKey + targetKeyLenInBytes,
                             LAC_QUADWORDS_TO_BYTES(
                                 ICP_QAT_HW_MODE_F8_NUM_REG_TO_CLEAR));
            }
            break;
                /* For AES in F8 mode Cipher Key is concatenated with
                 * Cipher Key XOR-ed with Key Mask (CK||CK^KM) */
            case CPA_CY_SYM_CIPHER_AES_F8:
            {
                Cpa32U index = 0;
                Cpa8U *pTempKey = pCipherKey + (targetKeyLenInBytes / 2);
                *pSizeInBytes += targetKeyLenInBytes;
                /* XOR Key with key Mask */
                for (index = 0; index < targetKeyLenInBytes; index++)
                {
                    pTempKey[index] = pCipherKey[index] ^ pTempKey[index];
                }
                pTempKey = (pCipherKey + targetKeyLenInBytes);
                /* also add padding for AES F8 */
                *pSizeInBytes += 2 * targetKeyLenInBytes;
                LAC_OS_BZERO(pTempKey, 2 * targetKeyLenInBytes);
            }
            break;
            case CPA_CY_SYM_CIPHER_SNOW3G_UEA2:
            {
                /* For Snow3G zero area after the key for FW */
                LAC_OS_BZERO(pCipherKey + targetKeyLenInBytes,
                             ICP_QAT_HW_SNOW_3G_UEA2_IV_SZ);

                *pSizeInBytes += ICP_QAT_HW_SNOW_3G_UEA2_IV_SZ;
            }
            break;
            case CPA_CY_SYM_CIPHER_ZUC_EEA3:
            {
                /* For ZUC zero area after the key for FW */
                LAC_OS_BZERO(pCipherKey + targetKeyLenInBytes,
                             ICP_QAT_HW_ZUC_3G_EEA3_IV_SZ);

                *pSizeInBytes += ICP_QAT_HW_ZUC_3G_EEA3_IV_SZ;
            }
            break;
            case CPA_CY_SYM_CIPHER_AES_XTS:
            {
                /* For AES in XTS mode Cipher Key is concatenated with
                 * second Cipher Key which is used for tweak calculation
                 * (CK1||CK2). For decryption Cipher Key needs to be converted
                 * to reverse key.*/
                if (ICP_QAT_FW_LA_USE_UCS_SLICE_TYPE == sliceType)
                {
                    Cpa32U key_len = pCipherSetupData->cipherKeyLenInBytes / 2;
                    osalMemCopy(pSessionDesc->cipherAesXtsKey1Forward,
                                pCipherSetupData->pCipherKey,
                                key_len);

                    osalAESKeyExpansionForward(
                        pSessionDesc->cipherAesXtsKey1Forward,
                        key_len,
                        (UINT32 *)pSessionDesc->cipherAesXtsKey1Reverse);

                    osalMemCopy(pSessionDesc->cipherAesXtsKey2,
                                pCipherSetupData->pCipherKey + key_len,
                                key_len);

                    if (CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT ==
                        pCipherSetupData->cipherDirection)
                    {
                        osalMemCopy(pCipherKey,
                                    pSessionDesc->cipherAesXtsKey1Reverse,
                                    key_len);
                    }
                    else
                    {
                        osalMemCopy(pCipherKey,
                                    pSessionDesc->cipherAesXtsKey1Forward,
                                    key_len);
                    }
                }
            }
            break;
            default:
                break;
        }
    }
}

/*****************************************************************************
 *  External functions
 *****************************************************************************/

Cpa8U LacSymQat_CipherBlockSizeBytesGet(CpaCySymCipherAlgorithm cipherAlgorithm)
{
    Cpa8U blockSize = 0;
    switch (cipherAlgorithm)
    {
        case CPA_CY_SYM_CIPHER_ARC4:
            blockSize = LAC_CIPHER_ARC4_BLOCK_LEN_BYTES;
            break;
        /* Handle AES or AES_F8 */
        case CPA_CY_SYM_CIPHER_AES_ECB:
        case CPA_CY_SYM_CIPHER_AES_CBC:
        case CPA_CY_SYM_CIPHER_AES_CTR:
        case CPA_CY_SYM_CIPHER_AES_CCM:
        case CPA_CY_SYM_CIPHER_AES_GCM:
        case CPA_CY_SYM_CIPHER_AES_XTS:
        case CPA_CY_SYM_CIPHER_AES_F8:
            blockSize = ICP_QAT_HW_AES_BLK_SZ;
            break;
        /* Handle DES */
        case CPA_CY_SYM_CIPHER_DES_ECB:
        case CPA_CY_SYM_CIPHER_DES_CBC:
            blockSize = ICP_QAT_HW_DES_BLK_SZ;
            break;
        /* Handle TRIPLE DES */
        case CPA_CY_SYM_CIPHER_3DES_ECB:
        case CPA_CY_SYM_CIPHER_3DES_CBC:
        case CPA_CY_SYM_CIPHER_3DES_CTR:
            blockSize = ICP_QAT_HW_3DES_BLK_SZ;
            break;
        case CPA_CY_SYM_CIPHER_KASUMI_F8:
            blockSize = ICP_QAT_HW_KASUMI_BLK_SZ;
            break;
        case CPA_CY_SYM_CIPHER_SNOW3G_UEA2:
            blockSize = ICP_QAT_HW_SNOW_3G_BLK_SZ;
            break;
        case CPA_CY_SYM_CIPHER_ZUC_EEA3:
            blockSize = ICP_QAT_HW_ZUC_3G_BLK_SZ;
            break;
        case CPA_CY_SYM_CIPHER_NULL:
            blockSize = LAC_CIPHER_NULL_BLOCK_LEN_BYTES;
            break;
        case CPA_CY_SYM_CIPHER_CHACHA:
            blockSize = ICP_QAT_HW_CHACHAPOLY_BLK_SZ;
            break;
        default:
            LAC_ENSURE(CPA_FALSE, "Algorithm not supported in Cipher");
    }
    return blockSize;
}

Cpa32U LacSymQat_CipherIvSizeBytesGet(CpaCySymCipherAlgorithm cipherAlgorithm)
{
    Cpa32U ivSize = 0;
    switch (cipherAlgorithm)
    {
        case CPA_CY_SYM_CIPHER_ARC4:
            ivSize = LAC_CIPHER_ARC4_STATE_LEN_BYTES;
            break;
        case CPA_CY_SYM_CIPHER_KASUMI_F8:
            ivSize = ICP_QAT_HW_KASUMI_BLK_SZ;
            break;
        case CPA_CY_SYM_CIPHER_SNOW3G_UEA2:
            ivSize = ICP_QAT_HW_SNOW_3G_UEA2_IV_SZ;
            break;
        case CPA_CY_SYM_CIPHER_ZUC_EEA3:
            ivSize = ICP_QAT_HW_ZUC_3G_EEA3_IV_SZ;
            break;
        case CPA_CY_SYM_CIPHER_CHACHA:
            ivSize = ICP_QAT_HW_CHACHAPOLY_IV_SZ;
            break;
        case CPA_CY_SYM_CIPHER_AES_ECB:
        case CPA_CY_SYM_CIPHER_DES_ECB:
        case CPA_CY_SYM_CIPHER_3DES_ECB:
        case CPA_CY_SYM_CIPHER_NULL:
            /* for all ECB Mode IV size is 0 */
            break;
        default:
            ivSize = LacSymQat_CipherBlockSizeBytesGet(cipherAlgorithm);
    }
    return ivSize;
}

inline CpaStatus LacSymQat_CipherRequestParamsPopulate(
    lac_session_desc_t *pSessionDesc,
    icp_qat_fw_la_bulk_req_t *pReq,
    Cpa32U cipherOffsetInBytes,
    Cpa32U cipherLenInBytes,
    Cpa64U ivBufferPhysAddr,
    Cpa8U *pIvBufferVirt)
{
    icp_qat_fw_la_cipher_req_params_t *pCipherReqParams;
    icp_qat_fw_cipher_cd_ctrl_hdr_t *pCipherCdCtrlHdr;
    icp_qat_fw_serv_specif_flags *pCipherSpecificFlags;
    Cpa32U usedBufSize = 0;
    Cpa32U totalBufSize = 0;

#ifdef ICP_PARAM_CHECK

    LAC_ENSURE(NULL != pReq,
               "LacSymQat_CipherRequestParamsPopulate - "
               "pReq is NULL\n");
#endif

    pCipherReqParams = (icp_qat_fw_la_cipher_req_params_t
                            *)((Cpa8U *)&(pReq->serv_specif_rqpars) +
                               ICP_QAT_FW_CIPHER_REQUEST_PARAMETERS_OFFSET);
    pCipherCdCtrlHdr = (icp_qat_fw_cipher_cd_ctrl_hdr_t *)&(pReq->cd_ctrl);
    pCipherSpecificFlags = &(pReq->comn_hdr.serv_specif_flags);

    pCipherReqParams->cipher_offset = cipherOffsetInBytes;
    pCipherReqParams->cipher_length = cipherLenInBytes;

    /* Don't copy the buffer into the Msg if
     * it's too big for the cipher_IV_array
     * OR if the FW needs to update it
     * OR if there's no buffer supplied
     * OR if last partial
     */
    if ((pCipherCdCtrlHdr->cipher_state_sz >
         LAC_SYM_QAT_HASH_IV_REQ_MAX_SIZE_QW) ||
        (ICP_QAT_FW_LA_UPDATE_STATE_GET(*pCipherSpecificFlags) ==
         ICP_QAT_FW_LA_UPDATE_STATE) ||
        (pIvBufferVirt == NULL) ||
        (ICP_QAT_FW_LA_PARTIAL_GET(*pCipherSpecificFlags) ==
         ICP_QAT_FW_LA_PARTIAL_END))
    {
        /* Populate the field with a ptr to the flat buffer */
        pCipherReqParams->u.s.cipher_IV_ptr = ivBufferPhysAddr;
        pCipherReqParams->u.s.resrvd1 = 0;
        /* Set the flag indicating the field format */
        ICP_QAT_FW_LA_CIPH_IV_FLD_FLAG_SET(*pCipherSpecificFlags,
                                           ICP_QAT_FW_CIPH_IV_64BIT_PTR);
    }
    else
    {
        /* Populate the field with the contents of the buffer,
         * zero field first as data may be smaller than the field */
        totalBufSize = LAC_LONGWORDS_TO_BYTES(ICP_QAT_FW_NUM_LONGWORDS_4);
        usedBufSize = LAC_QUADWORDS_TO_BYTES(pCipherCdCtrlHdr->cipher_state_sz);
        /* Only initialise unused buffer if applicable*/
        if (usedBufSize < totalBufSize)
        {
            osalMemSet(
                (&pCipherReqParams->u
                      .cipher_IV_array[usedBufSize & LAC_UNUSED_POS_MASK]),
                0,
                totalBufSize - usedBufSize);
        }
        /* In case of XTS mode using UCS slice always embedd IV.
         * IV provided by user needs to be encrypted to calculate initial tweak,
         * use pCipherReqParams->u.cipher_IV_array as destination buffer for
         * tweak value */
        if (ICP_QAT_FW_LA_USE_UCS_SLICE_TYPE == pSessionDesc->cipherSliceType &&
            LAC_CIPHER_IS_XTS_MODE(pSessionDesc->cipherAlgorithm))
        {
            osalAESEncrypt(pSessionDesc->cipherAesXtsKey2,
                           pSessionDesc->cipherKeyLenInBytes / 2,
                           pIvBufferVirt,
                           (Cpa8U *)pCipherReqParams->u.cipher_IV_array);
        }
        else
        {
            osalMemCopy(pCipherReqParams->u.cipher_IV_array,
                        pIvBufferVirt,
                        usedBufSize);
        }
        /* Set the flag indicating the field format */
        ICP_QAT_FW_LA_CIPH_IV_FLD_FLAG_SET(*pCipherSpecificFlags,
                                           ICP_QAT_FW_CIPH_IV_16BYTE_DATA);
    }

    return CPA_STATUS_SUCCESS;
}

void LacSymQat_CipherArc4StateInit(const Cpa8U *pKey,
                                   Cpa32U keyLenInBytes,
                                   Cpa8U *pArc4CipherState)
{
    Cpa32U i = 0;
    Cpa32U j = 0;
    Cpa32U k = 0;

    for (i = 0; i < LAC_CIPHER_ARC4_KEY_MATRIX_LEN_BYTES; ++i)
    {
        pArc4CipherState[i] = (Cpa8U)i;
    }

    for (i = 0, k = 0; i < LAC_CIPHER_ARC4_KEY_MATRIX_LEN_BYTES; ++i, ++k)
    {
        Cpa8U swap = 0;

        if (k >= keyLenInBytes)
            k -= keyLenInBytes;

        j = (j + pArc4CipherState[i] + pKey[k]);
        if (j >= LAC_CIPHER_ARC4_KEY_MATRIX_LEN_BYTES)
            j %= LAC_CIPHER_ARC4_KEY_MATRIX_LEN_BYTES;

        /* Swap state[i] & state[j] */
        swap = pArc4CipherState[i];
        pArc4CipherState[i] = pArc4CipherState[j];
        pArc4CipherState[j] = swap;
    }

    /* Initialise i & j values for QAT */
    pArc4CipherState[LAC_CIPHER_ARC4_KEY_MATRIX_LEN_BYTES] = 0;
    pArc4CipherState[LAC_CIPHER_ARC4_KEY_MATRIX_LEN_BYTES + 1] = 0;
}

/* Update the cipher_key_sz in the Request cache prepared and stored
 * in the session */
void LacSymQat_CipherXTSModeUpdateKeyLen(lac_session_desc_t *pSessionDesc,
                                         Cpa32U newKeySizeInBytes)
{
    icp_qat_fw_cipher_cd_ctrl_hdr_t *pCipherControlBlock = NULL;

    pCipherControlBlock =
        (icp_qat_fw_cipher_cd_ctrl_hdr_t *)&(pSessionDesc->reqCacheFtr.cd_ctrl);

    pCipherControlBlock->cipher_key_sz =
        LAC_BYTES_TO_QUADWORDS(newKeySizeInBytes);
}
