273 lines
7 KiB
C++
273 lines
7 KiB
C++
|
|
||
|
// Copyright (c) 1996-1999 Microsoft Corporation
|
||
|
|
||
|
//+============================================================================
|
||
|
//
|
||
|
// File: entropy.cxx
|
||
|
//
|
||
|
// This file implements the CEntropyRecorder class. That class is used
|
||
|
// to generate truly random secrets. It does this by maintaining some
|
||
|
// state whenever the Put method is called. It is the responsibility of
|
||
|
// the caller to call this at non-predictable times, such as based on
|
||
|
// the timing of user keystrokes or on non-solid-state disk latency.
|
||
|
//
|
||
|
//+============================================================================
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#include "pch.cxx"
|
||
|
#pragma hdrstop
|
||
|
#include "trkwks.hxx"
|
||
|
|
||
|
#define THIS_FILE_NUMBER ENTROPY_CXX_FILE_NO
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// CEntropyRecord::Initialize
|
||
|
//
|
||
|
// Init the critsec and entropy array.
|
||
|
//
|
||
|
//+----------------------------------------------------------------------------
|
||
|
|
||
|
void
|
||
|
CEntropyRecorder::Initialize()
|
||
|
{
|
||
|
DWORD dwType;
|
||
|
DWORD cb;
|
||
|
HKEY hKey;
|
||
|
|
||
|
_cs.Initialize();
|
||
|
_fInitialized = TRUE;
|
||
|
|
||
|
_cbTotalEntropy = _iNext = 0;
|
||
|
|
||
|
memset(_abEntropy, 0, sizeof(_abEntropy));
|
||
|
|
||
|
}
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// CEntropyRecord::UnInitialize
|
||
|
//
|
||
|
// Delete the critsec.
|
||
|
//
|
||
|
//+----------------------------------------------------------------------------
|
||
|
|
||
|
void
|
||
|
CEntropyRecorder::UnInitialize()
|
||
|
{
|
||
|
if (_fInitialized)
|
||
|
{
|
||
|
_cs.UnInitialize();
|
||
|
_fInitialized = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// CEntropyRecorder::Put
|
||
|
//
|
||
|
// This method is called at random times. The performance counter is
|
||
|
// queries, munged, and put into the entropy array.
|
||
|
//
|
||
|
//+----------------------------------------------------------------------------
|
||
|
|
||
|
void
|
||
|
CEntropyRecorder::Put()
|
||
|
{
|
||
|
LARGE_INTEGER li;
|
||
|
|
||
|
QueryPerformanceCounter(&li);
|
||
|
|
||
|
DWORD dw = li.LowPart ^ li.HighPart;
|
||
|
WORD w = HIWORD(dw) ^ LOWORD(dw);
|
||
|
|
||
|
_cs.Enter();
|
||
|
|
||
|
PutEntropy(HIBYTE(w));
|
||
|
PutEntropy(LOBYTE(w));
|
||
|
|
||
|
_cs.Leave();
|
||
|
}
|
||
|
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// CEntropyRecorder::PutEntropy
|
||
|
//
|
||
|
// Add a byte of entropy (from Put) to the entropy array.
|
||
|
//
|
||
|
//+----------------------------------------------------------------------------
|
||
|
|
||
|
void
|
||
|
CEntropyRecorder::PutEntropy( BYTE b )
|
||
|
{
|
||
|
//
|
||
|
// Entries are written into the buffer in a circular fashion.
|
||
|
// A count, _cbTotalEntropy, records total number of writes.
|
||
|
// If _cbTotalEntropy > buffer size, then we have wrapped and
|
||
|
// the entire buffer has entropy.
|
||
|
//
|
||
|
|
||
|
|
||
|
DWORD iNext = _iNext;
|
||
|
|
||
|
iNext %= sizeof(_abEntropy);
|
||
|
|
||
|
TrkAssert(iNext < sizeof(_abEntropy));
|
||
|
|
||
|
_abEntropy[iNext] ^= b;
|
||
|
|
||
|
_iNext = iNext+1;
|
||
|
|
||
|
_cbTotalEntropy ++;
|
||
|
}
|
||
|
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// CEntropyRecorder::GetEntropy
|
||
|
//
|
||
|
// Get the specified number of bytes of entropy. If we don't already have
|
||
|
// enough bytes, we'll generate them here.
|
||
|
//
|
||
|
//+----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL
|
||
|
CEntropyRecorder::GetEntropy( void * pv, ULONG cb )
|
||
|
{
|
||
|
BOOL fGotIt = FALSE;
|
||
|
|
||
|
_cs.Enter();
|
||
|
__try
|
||
|
{
|
||
|
TrkLog(( TRKDBG_VOLUME, TEXT("Getting entropy (%d/%d)"), cb, _cbTotalEntropy ));
|
||
|
|
||
|
// Do we already have enough entropy?
|
||
|
|
||
|
if( _cbTotalEntropy <= cb )
|
||
|
{
|
||
|
// No, we need to generate it.
|
||
|
|
||
|
TrkLog(( TRKDBG_VOLUME, TEXT("Generating entropy") ));
|
||
|
TCHAR tszSysDir[ MAX_PATH + 1 ];
|
||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
ULONG cPuts = 0;
|
||
|
|
||
|
// Get the system directory.
|
||
|
|
||
|
UINT cchPath = GetSystemDirectory( tszSysDir, ELEMENTS(tszSysDir) );
|
||
|
if( 0 == cchPath || MAX_PATH < cchPath )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't get system directory (%lu, %lu)"),
|
||
|
cchPath, GetLastError() ));
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
// Keep opening the system directory, capturing entropy each time (with the call to Put),
|
||
|
// until we have enough.
|
||
|
|
||
|
while( _cbTotalEntropy <= cb )
|
||
|
{
|
||
|
HANDLE hSysDir = NULL;
|
||
|
|
||
|
Status = TrkCreateFile( tszSysDir, FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
|
||
|
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
|
||
|
FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, &hSysDir );
|
||
|
if( !NT_SUCCESS(Status) )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't open system directory (%08x)"),
|
||
|
Status ));
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
NtClose( hSysDir );
|
||
|
hSysDir = NULL;
|
||
|
Put();
|
||
|
|
||
|
// This is just a cautionary measure. Never let this loop eat all the
|
||
|
// CPU forever.
|
||
|
|
||
|
if( ++cPuts > 100 )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("CEntropy::GetEntropy in infinite loop") ));
|
||
|
__leave;
|
||
|
}
|
||
|
}
|
||
|
TrkLog(( TRKDBG_VOLUME, TEXT("Generated enough entropy") ));
|
||
|
|
||
|
} // if( _cbTotalEntropy <= cb )
|
||
|
|
||
|
|
||
|
if (_cbTotalEntropy >= ELEMENTS(_abEntropy))
|
||
|
{
|
||
|
// Never allow _cbTotalEntropy to exceed ELEMENTS(_abEntropy)
|
||
|
// if we're reading out, otherwise we'd get non-random data returned.
|
||
|
|
||
|
_cbTotalEntropy = ELEMENTS(_abEntropy);
|
||
|
}
|
||
|
|
||
|
_cbTotalEntropy -= cb;
|
||
|
_iNext = _cbTotalEntropy;
|
||
|
|
||
|
memcpy(pv, _abEntropy, cb);
|
||
|
|
||
|
memcpy(_abEntropy, _abEntropy+cb, sizeof(_abEntropy)-cb);
|
||
|
memset(_abEntropy+sizeof(_abEntropy)-cb, 0, cb);
|
||
|
|
||
|
fGotIt = TRUE;
|
||
|
}
|
||
|
__finally
|
||
|
{
|
||
|
_cs.Leave();
|
||
|
}
|
||
|
|
||
|
return(fGotIt);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// CEntropyRecorder::InitializeSecret
|
||
|
//
|
||
|
// Create a volume secret, using the entropy buffer.
|
||
|
//
|
||
|
//+----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL
|
||
|
CEntropyRecorder::InitializeSecret( CVolumeSecret * pSecret )
|
||
|
{
|
||
|
return GetEntropy( pSecret, sizeof(*pSecret) );
|
||
|
}
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// CEntropyRecorder::ReturnUnusedSecret
|
||
|
//
|
||
|
// Return entropy that was taken with InitializeSecret but not used.
|
||
|
//
|
||
|
//+----------------------------------------------------------------------------
|
||
|
|
||
|
void
|
||
|
CEntropyRecorder::ReturnUnusedSecret( CVolumeSecret * pSecret )
|
||
|
{
|
||
|
TrkAssert( *pSecret != CVolumeSecret() );
|
||
|
|
||
|
_cs.Enter();
|
||
|
|
||
|
if (_cbTotalEntropy <= sizeof(_abEntropy) - sizeof(*pSecret))
|
||
|
{
|
||
|
memcpy( _abEntropy+sizeof(*pSecret), _abEntropy, _cbTotalEntropy);
|
||
|
memcpy( _abEntropy, pSecret, sizeof(CVolumeSecret) );
|
||
|
_iNext = (_iNext + sizeof(*pSecret)) % sizeof(_abEntropy);
|
||
|
_cbTotalEntropy += sizeof(*pSecret);
|
||
|
}
|
||
|
|
||
|
*pSecret = CVolumeSecret();
|
||
|
|
||
|
_cs.Leave();
|
||
|
|
||
|
}
|
||
|
|