/////////////////////////////////////////////////////////////////////////////
//  FILE          : nt_rand.c                                              //
//  DESCRIPTION   : Crypto CP interfaces:                                  //
//                  CPGenRandom                                            //
//  AUTHOR        :                                                        //
//  HISTORY       :                                                        //
//  Jan 25 1995 larrys  Changed from Nametag                               //
//      Feb 23 1995 larrys  Changed NTag_SetLastError to SetLastError      //
//      Apr 10 1995 larrys  Fix comments                                   //
//      Oct 27 1995 rajeshk Added provider parameter to GenRandom call     //
//      Nov  3 1995 larrys  Merge for NT checkin                           //
//      Oct 14 1996 jeffspel Changed GenRandom to NewGenRandom             //
//      May  5 2000 dbarlow Clean up error return codes                    //
//                                                                         //
//  Copyright (C) 1993 - 2000, Microsoft Corporation                       //
//  All Rights Reserved                                                    //
/////////////////////////////////////////////////////////////////////////////

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <crypt.h>

#include "precomp.h"
#include "sha.h"
#include "rsa_fast.h"
#include "rsa_math.h"
#include "randlib.h"

static CONST BYTE DSSPRIVATEKEYINIT[] =
  { 0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x89,
    0x98, 0xba, 0xdc, 0xfe, 0x10, 0x32, 0x54, 0x76,
    0xc3, 0xd2, 0xe1, 0xf0 };

static CONST BYTE DSSPERMSGINIT[] =
  { 0xef, 0xcd, 0xab, 0x89, 0x98, 0xba, 0xdc, 0xfe,
    0x10, 0x32, 0x54, 0x76, 0xc3, 0xd2, 0xe1, 0xf0,
    0x67, 0x45, 0x23, 0x01 };

static CONST BYTE MODULUS[] =
  { 0xf5, 0xc1, 0x56, 0xb1, 0xd5, 0x48, 0x42, 0x2e,
    0xbd, 0xa5, 0x44, 0x41, 0xc7, 0x1c, 0x24, 0x08,
    0x3f, 0x80, 0x3c, 0x90 };

static BYTE l_rgbRNGState[A_SHA_DIGEST_LEN];


//
// Function : AddSeeds
//
// Description : This function adds the 160 bit seeds pointed to by pdwSeed1
//               and pdwSeed2, it also adds 1 to this sum and mods the sum by
//               2^160.
//

/*static*/ void
AddSeeds(
    IN CONST DWORD *pdwSeed1,
    IN OUT DWORD *pdwSeed2)
{
    DWORD   dwTmp;
    DWORD   dwOverflow = 1;
    DWORD   i;

    for (i = 0; i < 5; i++)
    {
        dwTmp = dwOverflow + pdwSeed1[i];
        dwOverflow = (dwOverflow > dwTmp);
        pdwSeed2[i] = pdwSeed2[i] + dwTmp;
        dwOverflow = ((dwTmp > pdwSeed2[i]) || dwOverflow);
    }
}


/*
    Given SHA(message), compute SHA(message) mod qdigit.
    Output is in the interval [0, qdigit-1].
    Although SHA(message) may exceed qdigit,
    it cannot exceed 2*qdigit since the leftmost bit
    of qdigit is 1.
*/

/*static*/ void
SHA_mod_q(
    CONST BYTE *pbHash,              // In
    CONST BYTE *pbQ,                 // In
    BYTE       *pbNewHash)           // Out
{
    BYTE    rgbHash[A_SHA_DIGEST_LEN];

    if (-1 != Compare((DWORD*)rgbHash,  // hash is greater so subtract
                      (DWORD*)pbQ,
                      A_SHA_DIGEST_LEN / sizeof(DWORD)))
    {
        Sub((DWORD*)pbNewHash,
            (DWORD*)rgbHash,
            (DWORD*)pbQ,
            A_SHA_DIGEST_LEN / sizeof(DWORD));
    }
    else
        memcpy(pbNewHash, pbHash, A_SHA_DIGEST_LEN / sizeof(DWORD));
} /* SHA_mod_q */


//
// Function : RNG16BitStateCheck
//
// Description : This function compares each 160 bits of the buffer with
//               the next 160 bits and if they are the same the function
//               errors out.  The IN buffer is expected to be A_SHA_DIGEST_LEN
//               bytes long.  The function fails if the RNG is gets the same
//               input buffer of 160 bits twice in a row.
//

/*static*/ BOOL
RNG16BitStateCheck(
    IN OUT DWORD *pdwOut,
    IN DWORD *pdwIn,
    IN DWORD cbNeeded)
{
    BOOL    fRet = FALSE;

    if (0 == memcmp(l_rgbRNGState, pdwIn, A_SHA_DIGEST_LEN))
    {
        memcpy(l_rgbRNGState, (BYTE*)pdwIn, A_SHA_DIGEST_LEN);
        goto ErrorExit;
    }

    memcpy(l_rgbRNGState, (BYTE*)pdwIn, A_SHA_DIGEST_LEN);
    memcpy((BYTE*)pdwOut, (BYTE*)pdwIn, cbNeeded);
    fRet = TRUE;

ErrorExit:
    return fRet;
}


//
// Function : FIPS186Gen
//
// Description : FIPS 186 RNG, the seed is generated by calling NewGenRandom.
//

/*static*/ DWORD
FIPS186Gen(
    IN HANDLE hRNGDriver,
    IN BYTE **ppbContextSeed,
    IN DWORD *pcbContextSeed,
    IN CONST BYTE *pbInitValue, // this is t, must be 20 bytes
    IN CONST BYTE *pbModulus,   // this must be a 20 byte prime
    IN OUT BYTE *pb,
    IN DWORD cb)
{
    DWORD       dwReturn = ERROR_INTERNAL_ERROR;
    DWORD       rgdwSeed[A_SHA_DIGEST_LEN/sizeof(DWORD)];    // 160 bits
    DWORD       rgdwNewSeed[A_SHA_DIGEST_LEN/sizeof(DWORD)]; // 160 bits
    A_SHA_CTX   SHACtxt;
    BYTE        rgbBuf[A_SHA_DIGEST_LEN];
    DWORD       cbBuf;
    BYTE        *pbTmp = pb;
    DWORD       cbTmp = cb;
    DWORD       i;
    DWORD       dwSts;
    DWORD       cbContextSeed = 0;

    if (ppbContextSeed && pcbContextSeed)
        cbContextSeed = *pcbContextSeed;

    while (cbTmp)
    {
#ifdef USE_HW_RNG
#ifdef _M_IX86
        // get a 160 bit random seed
        if (INVALID_HANDLE_VALUE != hRNGDriver)
        {
            dwSts = HWRNGGenRandom(hRNGDriver,
                                   (BYTE*)rgdwNewSeed,
                                   sizeof(rgdwNewSeed));
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = dwSts;
                goto ErrorExit;
            }
        }
        else
#endif // _M_IX86
#endif // USE_HW_RNG
        {
            // get a 160 bit random seed
            if (!NewGenRandom(ppbContextSeed, pcbContextSeed,
                              (BYTE*)rgdwNewSeed, sizeof(rgdwNewSeed)))
            {
                dwReturn = (DWORD)NTE_FAIL; // NewGenRandom doesn't set LastError.
                goto ErrorExit;
            }
        }

        for (i = 0; i < A_SHA_DIGEST_LEN/sizeof(DWORD); i++)
            rgdwSeed[i] ^= rgdwNewSeed[i];

        A_SHAInit (&SHACtxt);
        memcpy(SHACtxt.state, pbInitValue, A_SHA_DIGEST_LEN);

        // perform the one way function
        A_SHAUpdate(&SHACtxt, (BYTE*)rgdwSeed, sizeof(rgdwSeed));
        if (cbContextSeed)
            A_SHAUpdate(&SHACtxt, *ppbContextSeed, cbContextSeed);
        A_SHAFinal(&SHACtxt, rgbBuf);

        for (i = 0; i < cbContextSeed && i < A_SHA_DIGEST_LEN; i++)
            (*ppbContextSeed)[i] ^= rgbBuf[i];

        // continuous 16 bit state check
        if (A_SHA_DIGEST_LEN < cbTmp)
            cbBuf = A_SHA_DIGEST_LEN;
        else
            cbBuf = cbTmp;

        if (!RNG16BitStateCheck((DWORD*)pbTmp, (DWORD*)rgbBuf, cbBuf))
        {
            dwReturn = (DWORD)NTE_FAIL;
            goto ErrorExit;
        }

        pbTmp += cbBuf;
        cbTmp -= cbBuf;
        if (0 == cbTmp)
            break;

        // modular reduction with modulus Q
        SHA_mod_q(rgbBuf, pbModulus, (BYTE*)rgdwNewSeed);

        // (1 + previous seed + new random) mod 2^160
        AddSeeds(rgdwNewSeed, rgdwSeed);
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    return dwReturn;
}


DWORD
FIPS186GenRandom(
    IN HANDLE *phRNGDriver,
    IN BYTE **ppbContextSeed,
    IN DWORD *pcbContextSeed,
    IN OUT BYTE *pb,
    IN DWORD cb)
{
    return FIPS186Gen(*phRNGDriver, ppbContextSeed, pcbContextSeed,
                      DSSPRIVATEKEYINIT, MODULUS, pb, cb);
}


void
FIPS186GenRandomWithException(
    IN HANDLE *phRNGDriver,
    IN BYTE **ppbContextSeed,
    IN DWORD *pcbContextSeed,
    IN OUT BYTE *pb,
    IN DWORD cb)
{
    DWORD dwSts;

    dwSts = FIPS186Gen(*phRNGDriver, ppbContextSeed, pcbContextSeed,
                       DSSPRIVATEKEYINIT, MODULUS, pb, cb);
    if (ERROR_SUCCESS != dwSts)
    {
        // nasty way to cause an error but need either this
        // or redo rsa32.lib
        RaiseException((DWORD)NTE_FAIL, 0, 0, 0);
    }
}


/*
 -  CPGenRandom
 -
 *  Purpose:
 *                Used to fill a buffer with random bytes
 *
 *
 *  Parameters:
 *               IN  hUID       -  Handle to the user identifcation
 *               IN  dwLen      -  Number of bytes of random data requested
 *               OUT pbBuffer   -  Pointer to the buffer where the random
 *                                 bytes are to be placed
 *
 *  Returns:
 */

BOOL WINAPI
CPGenRandom(
    IN HCRYPTPROV hUID,
    IN DWORD dwLen,
    OUT BYTE *pbBuffer)
{
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    PNTAGUserList   pTmpUser;
    BOOL            fRet;
    DWORD           dwSts;

    EntryPoint
    // check the user identification
    pTmpUser = NTLCheckList(hUID, USER_HANDLE);
    if (NULL == pTmpUser)
    {
        dwReturn = (DWORD)NTE_BAD_UID;
        goto ErrorExit;
    }

    dwSts = FIPS186Gen(pTmpUser->hRNGDriver,
                       &pTmpUser->ContInfo.pbRandom,
                       &pTmpUser->ContInfo.ContLens.cbRandom,
                       DSSPRIVATEKEYINIT,
                       MODULUS,
                       pbBuffer,
                       dwLen);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    fRet = (ERROR_SUCCESS == dwReturn);
    if (!fRet)
        SetLastError(dwReturn);
    return fRet;
}

//
// Function:    NewGenRandom
//
// Description: Stub to eliminate the need to link with
//              randlib.lib.  Now using RtlGenRandom() instead.
//
// Returns:     True for success.  False for failure.
//
unsigned int
RSA32API
NewGenRandom(
    IN  OUT unsigned char **ppbRandSeed /*unused*/,
    IN      unsigned long *pcbRandSeed /*unused*/,
    IN  OUT unsigned char *pbBuffer,
    IN      unsigned long dwLength
    )
{
    return (unsigned int)RtlGenRandom( pbBuffer, dwLength );
}