/*************************************************************************
* 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.
//     HASH based Mask Generation Functions
//
//  Contents:
//        ippsMGF2_rmf()
//
*/

#include "owndefs.h"
#include "owncp.h"
#include "hash/pcphash_rmf.h"
#include "pcptool.h"

/*F*
//    Name: ippsMGF2
//
// Purpose: Mask Generation Function.
//
// Returns:                Reason:
//    ippStsNullPtrErr           pMask == NULL
//                               pMethod ==NULL
//    ippStsLengthErr            seedLen <0
//                               maskLen <0
//
//    ippStsMemAllocErr          MAX_HASH_RMF_CONTEXT_SIZE < contextSize
//
//    ippStsNotSupportedModeErr  method from the SHA3 family
//
//    ippStsNoErr                no errors
//
// Parameters:
//    pSeed       pointer to the input stream
//    seedLen     input stream length (bytes)
//    pMaske      pointer to the output mask
//    maskLen     desired length of mask (bytes)
//    pMethod     hash method
//
//
// Note.
//    MGF1 defined in the IEEE P1363 standard.
//    MGF1 defined in the ANSI X9.63 standard and frequently called KDF (key Generation Function).
//    The fifference between MGF1 and MGF2 is negligible - counter i runs from 0 (in MGF1) and from 1 (in MGF2)
*F*/

/* clang-format off */
IPPFUN(IppStatus, ippsMGF2_rmf, (const Ipp8u* pSeed,
                                 int seedLen,
                                 Ipp8u* pMask,
                                 int maskLen,
                                 const IppsHashMethod* pMethod))
/* clang-format on */
{
    IPP_BAD_PTR2_RET(pMask, pMethod);
    IPP_BADARG_RET((seedLen < 0) || (maskLen < 0), ippStsLengthErr);
    /* check if the algorithm is from the sha3 family (SHA3 is not supported)*/
    IPP_BADARG_RET(cpIsSHA3AlgID(pMethod->hashAlgId), ippStsNotSupportedModeErr);

    {
        /* hash specific */
        int hashSize = pMethod->hashLen;

        /* check if enough memory is allocated for the context */
        int contextSize = 0;
        ippsHashGetSizeOptimal_rmf(&contextSize, pMethod);
        IPP_BADARG_RET(((int)MAX_HASH_RMF_CONTEXT_SIZE < contextSize), ippStsMemAllocErr);

        int i, outLen;
        __ALIGN64 Ipp8u hashCtxMem[MAX_HASH_RMF_CONTEXT_SIZE];
        IppsHashState_rmf* hashCtx = (IppsHashState_rmf*)hashCtxMem;
        ippsHashInit_rmf(hashCtx, pMethod);

        if (!pSeed)
            seedLen = 0;

        for (i = 1, outLen = 0; outLen < maskLen; i++) { /* counter i runs from 1 */
            Ipp8u cnt[4];
            cnt[0] = (Ipp8u)((i >> 24) & 0xFF);
            cnt[1] = (Ipp8u)((i >> 16) & 0xFF);
            cnt[2] = (Ipp8u)((i >> 8) & 0xFF);
            cnt[3] = (Ipp8u)(i & 0xFF);

            ippsHashUpdate_rmf(pSeed, seedLen, hashCtx);
            ippsHashUpdate_rmf(cnt, 4, hashCtx);

            if ((outLen + hashSize) <= maskLen) {
                ippsHashFinal_rmf(pMask + outLen, hashCtx);
                outLen += hashSize;
            } else {
                Ipp8u md[MAX_HASH_SIZE];
                ippsHashFinal_rmf(md, hashCtx);
                CopyBlock(md, pMask + outLen, maskLen - outLen);
                outLen = maskLen;
            }
        }

        return ippStsNoErr;
    }
}
