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