/*************************************************************************
* Copyright (C) 2016 Intel Corporation
*
* Licensed under the Apache License,  Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 	http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law  or agreed  to  in  writing,  software
* distributed under  the License  is  distributed  on  an  "AS IS"  BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the  specific  language  governing  permissions  and
* limitations under the License.
*************************************************************************/

/*
//
//  Purpose:
//     Cryptography Primitive.
//     AES-XTS Functions (IEEE P1619)
//
//  Contents:
//        ippsAES_XTSDecrypt()
//
*/

#include "owncp.h"
#include "pcpaesmxts.h"
#include "pcptool.h"
#include "pcpaesmxtsstuff.h"


#if (_ALG_AES_SAFE_ == _ALG_AES_SAFE_COMPACT_SBOX_)
#include "pcprijtables.h"
#endif

//
//
static void cpAES_XTS_DecBlock(Ipp8u* ctxt,
                               const Ipp8u* ptxt,
                               const Ipp8u* tweak,
                               const IppsAESSpec* pEncCtx)
{
    /* pre-whitening */
    XorBlock16(ptxt, tweak, ctxt);
    /* encryption */
    ippsAESDecryptECB(ctxt, ctxt, AES_BLK_SIZE, pEncCtx);
    /* post-whitening */
    XorBlock16(ctxt, tweak, ctxt);
}

/*F*
//    Name: ippsAES_XTSDecrypt
//
// Purpose: AES-XTS decryption.
//
// Returns:                Reason:
//    ippStsNullPtrErr        pSrc == NULL
//                            pDst == NULL
//                            pTweak ==NULL
//                            pCtx == NULL
//    ippStsLengthErr         bitLen <128
//    ippStsContextMatchErr   !VALID_AES_XTS_ID(pCtx)
//    ippStsBadArgErr         !IsLegalGeometry(startCipherBlkNo,
//                               bitLen, pCtx->duBitsize)
//    ippStsNoErr             no errors
//
// Parameters:
//    pSrc              points input buffer
//    pDst              points output buffer
//    bitLen            length of the input buffer in bits
//    startCipherBlkNo  number of the first block for data unit
//    pTweak            points tweak value
//    pCtx              points AES_XTS context
//
*F*/

/* clang-format off */
IPPFUN(IppStatus, ippsAES_XTSDecrypt, (const Ipp8u* pSrc,
                                       Ipp8u* pDst,
                                       int bitLen,
                                       const IppsAES_XTSSpec* pCtx,
                                       const Ipp8u* pTweak,
                                       int startCipherBlkNo))
/* clang-format on */
{
    /* test pointers */
    IPP_BAD_PTR1_RET(pCtx);
    /* test the context ID */
    IPP_BADARG_RET(!VALID_AES_XTS_ID(pCtx), ippStsContextMatchErr);

    /* test data pointers */
    IPP_BAD_PTR3_RET(pSrc, pDst, pTweak);

    /* test startCipherBlkNo and bitLen */
    IPP_BADARG_RET(bitLen < IPP_AES_BLOCK_BITSIZE, ippStsLengthErr);
    IPP_BADARG_RET(!IsLegalGeometry(startCipherBlkNo, bitLen, pCtx->duBitsize), ippStsBadArgErr);

    {
        __ALIGN16 Ipp8u tweakCT[AES_BLK_SIZE];

        { /* encrypt tweak */
            const IppsAESSpec* ptwkAES = &pCtx->tweakAES;

            RijnCipher encoder = RIJ_ENCODER(ptwkAES);
#if (_ALG_AES_SAFE_ == _ALG_AES_SAFE_COMPACT_SBOX_)
            encoder(pTweak, tweakCT, RIJ_NR(ptwkAES), RIJ_EKEYS(ptwkAES), RijEncSbox /*NULL*/);
#else
            encoder(pTweak, tweakCT, RIJ_NR(ptwkAES), RIJ_EKEYS(ptwkAES), NULL);
#endif

            /* update tweakCT */
            for (; startCipherBlkNo > 0; startCipherBlkNo--)
                gf_mul_by_primitive(tweakCT);
        }

        /* XTS decryption */
        {
            const IppsAESSpec* pdatAES = &pCtx->datumAES;

            int encBlocks    = bitLen / IPP_AES_BLOCK_BITSIZE;
            int encBlocklast = bitLen % IPP_AES_BLOCK_BITSIZE;
            if (encBlocklast)
                encBlocks--;

            /* decrypt data blocks */
            if (encBlocks > 0) {
#if (_IPP >= _IPP_P8) || (_IPP32E >= _IPP32E_Y8)
                /* use Intel(R) AES New Instructions version if possible */
                if (AES_NI_ENABLED == RIJ_AESNI(pdatAES)) {
#if (_IPP32E >= _IPP32E_K1)
                    if (IsFeatureEnabled(ippCPUID_AVX512VAES)) {
                        cpAESDecryptXTS_VAES(pDst,
                                             pSrc,
                                             encBlocks,
                                             RIJ_DKEYS(pdatAES),
                                             RIJ_NR(pdatAES),
                                             tweakCT);
                    } else
#endif
                        cpAESDecryptXTS_AES_NI(pDst,
                                               pSrc,
                                               encBlocks,
                                               RIJ_DKEYS(pdatAES),
                                               RIJ_NR(pdatAES),
                                               tweakCT);
                    pSrc += encBlocks * AES_BLK_SIZE;
                    pDst += encBlocks * AES_BLK_SIZE;
                } else
#endif
                {
                    for (; encBlocks > 0; encBlocks--) {
                        cpAES_XTS_DecBlock(pDst, pSrc, tweakCT, pdatAES);
                        gf_mul_by_primitive(tweakCT);
                        pSrc += AES_BLK_SIZE;
                        pDst += AES_BLK_SIZE;
                    }
                }
            }

            /* "stealing" - decrypt last partial block if is */
            if (encBlocklast) {
                int partBlockSize = encBlocklast / BYTESIZE;

                __ALIGN16 Ipp8u cc[AES_BLK_SIZE];
                __ALIGN16 Ipp8u pp[AES_BLK_SIZE];
                CopyBlock16(tweakCT, cc);
                gf_mul_by_primitive(cc);
                cpAES_XTS_DecBlock(pp, pSrc, cc, pdatAES);

                CopyBlock16(pp, cc);
                CopyBlock(pSrc + AES_BLK_SIZE, cc, partBlockSize);

                encBlocklast %= BYTESIZE;
                if (encBlocklast) {
                    Ipp8u partBlockMask = (Ipp8u)((0xFF) << ((BYTESIZE - encBlocklast) % BYTESIZE));
                    Ipp8u x             = pSrc[AES_BLK_SIZE + partBlockSize];
                    Ipp8u y             = cc[partBlockSize];
                    x                   = (x & partBlockMask) | (y & ~partBlockMask);
                    cc[partBlockSize]   = x;
                    pp[partBlockSize] &= partBlockMask;
                    partBlockSize++;
                }
                cpAES_XTS_DecBlock(pDst, cc, tweakCT, pdatAES);

                CopyBlock(pp, pDst + AES_BLK_SIZE, partBlockSize);

                /* clear secret data */
                PurgeBlock(pp, sizeof(pp));
            }
            return ippStsNoErr;
        }
    }
}
