/*************************************************************************
* Copyright (C) 2005 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.
//     DL over Prime Finite Field (validate domain parameters)
//
//  Contents:
//        ippsDLPValidateDSA()
//
*/

#include "owndefs.h"
#include "owncp.h"
#include "pcpdlp.h"


/*
//    Name: DLPValidate
//
// Purpose: Validate DL Domain Parameters.
//
// Parameters:
//    nTrials     number of trials of primality test
//    pResult     pointer to the validation result
//    pDL         pointer to the DL context
//    rndFunc     external random generator
//    pRndParam   pointer to the external random generator params
*/
static IppDLResult DLPValidate(int nTrials,
                               IppsDLPState* pDL,
                               IppBitSupplier rndFunc,
                               void* pRndParam)
{
    /*
   // validate DL parameters:
   //    check that P is odd and P > 2
   //    check that R is odd and R > 2
   //    check that R | (P-1)
   //    check that 1 < G < P
   //    check that 1 == G^R (mod P)
   */

    BNU_CHUNK_T* pP = DLP_P(pDL);
    BNU_CHUNK_T* pR = DLP_R(pDL);
    cpSize lenP     = BITS_BNU_CHUNK(DLP_BITSIZEP(pDL));
    cpSize lenR     = BITS_BNU_CHUNK(DLP_BITSIZER(pDL));

    IppsPrimeState* pPrimeCtx = DLP_PRIMEGEN(pDL);

    /* allocate BN resources */
    BigNumNode* pList     = DLP_BNCTX(pDL);
    IppsBigNumState* pTmp = cpBigNumListGet(&pList);
    BNU_CHUNK_T* pT       = BN_NUMBER(pTmp);

    /* P is odd and prime */
    if (0 == (pP[0] & 1))
        return ippDLBaseIsEven;
    if (0 == cpPrimeTest(pP, lenP, nTrials, pPrimeCtx, rndFunc, pRndParam))
        return ippDLCompositeBase;

    /* R is odd and prime */
    if (0 == (pR[0] & 1))
        return ippDLOrderIsEven;
    if (0 == cpPrimeTest(pR, lenR, nTrials, pPrimeCtx, rndFunc, pRndParam))
        return ippDLCompositeOrder;

    /* R|(P-1) */
    cpDec_BNU(pT, pP, lenP, 1);
    cpMod_BNU(pT, lenP, pR, lenR);
    if (!cpEqu_BNU_CHUNK(pT, lenP, 0))
        return ippDLInvalidCofactor;

    /* 1 < G < P */
    cpMontDec_BN(pTmp, DLP_GENC(pDL), DLP_MONTP0(pDL));
    if (0 >= cpBN_cmp(pTmp, cpBN_OneRef()) || cpCmp_BNU(pT, BN_SIZE(pTmp), DLP_P(pDL), lenP) >= 0)
        return ippDLInvalidGenerator;

    /* G^R = 1 (mod P) */
    cpMontExpBin_BNU(pT, BN_NUMBER(DLP_GENC(pDL)), lenP, pR, lenR, DLP_MONTP0(pDL));
    if (cpCmp_BNU(pT, lenP, MOD_MNT_R(DLP_MONTP0(pDL)), lenP))
        return ippDLInvalidGenerator;

    return ippDLValid;
}

/*F*
//    Name: ippsDLPValidateDSA
//
// Purpose: Validate DL (DSA) Domain Parameters.
//
// Returns:                Reason:
//    ippStsNullPtrErr        NULL == pDL
//                            NULL == pResult
//                            NULL == rndFunc
//
//    ippStsContextMatchErr   illegal pDL->idCtx
//
//    ippStsIncompleteContextErr
//                            incomplete context
//
//    ippStsBadArgErr         nTrials <=0
//
//    ippStsNoErr             no errors
//
// Parameters:
//    nTrials     number of trials of primality test
//    pResult     pointer to the validation result
//    pDL         pointer to the DL context
//    rndFunc     external random generator
//    pRndParam   pointer to the external random generator params
*F*/

/* clang-format off */
IPPFUN(IppStatus, ippsDLPValidateDSA, (int nTrials,
                                       IppDLResult* pResult,
                                       IppsDLPState* pDL,
                                       IppBitSupplier rndFunc,
                                       void* pRndParam))
/* clang-format on */
{
    /* test DL context */
    IPP_BAD_PTR1_RET(pDL);
    IPP_BADARG_RET(!DLP_VALID_ID(pDL), ippStsContextMatchErr);

    /* test operation flag */
    IPP_BADARG_RET(!DLP_COMPLETE(pDL), ippStsIncompleteContextErr);

    /* test number of trials for primality check */
    IPP_BADARG_RET(nTrials <= 0, ippStsBadArgErr);

    /* test another pointers */
    IPP_BAD_PTR2_RET(pResult, rndFunc);


    /* execute genetal DL validation */
    *pResult = DLPValidate(nTrials, pDL, rndFunc, pRndParam);

    /*
   // DSA specific validation
   */
    if (ippDLValid == *pResult) {
        /* allocate BN resources */
        BigNumNode* pList   = DLP_BNCTX(pDL);
        IppsBigNumState* pT = cpBigNumListGet(&pList);

        BNU_CHUNK_T* pP   = DLP_P(pDL);
        BNU_CHUNK_T* pR   = DLP_R(pDL);
        cpSize feBitSize  = DLP_BITSIZEP(pDL);
        cpSize ordBitSize = DLP_BITSIZER(pDL);
        cpSize lenP       = BITS_BNU_CHUNK(feBitSize);
        cpSize lenR       = BITS_BNU_CHUNK(ordBitSize);

        /* 512 <= DLP_BITSIZEP() <= 1024, 64|DLP_BITSIZEP() */
        if ((MIN_DLPDSA_BITSIZE > feBitSize) || (MAX_DLPDSA_BITSIZE < feBitSize) ||
            (feBitSize % 64)) {
            *pResult = ippDLInvalidBaseRange;
            return ippStsNoErr;
        }
        /* 2^(DLP_BITSIZEP()-1) < P < 2^DLP_BITSIZEP() */
        cpBN_power2(pT, feBitSize - 1);
        if (0 <= cpCmp_BNU(BN_NUMBER(pT), BN_SIZE(pT), pP, lenP)) {
            *pResult = ippDLInvalidBaseRange;
            return ippStsNoErr;
        }
        cpBN_power2(pT, feBitSize);
        if (0 >= cpCmp_BNU(BN_NUMBER(pT), BN_SIZE(pT), pP, lenP)) {
            *pResult = ippDLInvalidBaseRange;
            return ippStsNoErr;
        }

        /* DLP_BITSIZER() == 160 */
        if ((DEF_DLPDSA_BITSIZER != ordBitSize)) {
            *pResult = ippDLInvalidOrderRange;
            return ippStsNoErr;
        }
        /* 2^(DLP_BITSIZER()-1) < R < 2^DLP_BITSIZER() */
        cpBN_power2(pT, ordBitSize - 1);
        if (0 <= cpCmp_BNU(BN_NUMBER(pT), BN_SIZE(pT), pR, lenR)) {
            *pResult = ippDLInvalidOrderRange;
            return ippStsNoErr;
        }
        cpBN_power2(pT, ordBitSize);
        if (0 >= cpCmp_BNU(BN_NUMBER(pT), BN_SIZE(pT), pR, lenR)) {
            *pResult = ippDLInvalidOrderRange;
            return ippStsNoErr;
        }

        /* 1 < G < (P-1) */
        cpMontDec_BN(pT, DLP_GENC(pDL), DLP_MONTP0(pDL));
        if (!(0 < cpBN_cmp(pT, cpBN_OneRef()) &&
              0 > cpCmp_BNU(BN_NUMBER(pT), BN_SIZE(pT), pP, lenP))) {
            *pResult = ippDLInvalidGenerator;
            return ippStsNoErr;
        }
    }

    return ippStsNoErr;
}
