Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

354 lines
9.8 KiB

/////////////////////////////////////////////////////////////////////////////
// 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 );
}