123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632 |
- /*++
- Copyright (c) 2015 Minoca Corp. All Rights Reserved
- Module Name:
- fortuna.c
- Abstract:
- This module implements support for the Fortuna Pseudo-Random Number
- Generator.
- Author:
- Evan Green 13-Jan-2015
- Environment:
- Any
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include "cryptop.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define FORTUNA_POOL0_FILL 32
- #define FORTUNA_RESEED_SIZE (1024 * 1024)
- #define FORTUNA_RESEED_INTERVAL_MILLISECONDS 100
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- VOID
- CypFortunaSpreadInitialSeed (
- PFORTUNA_CONTEXT Context
- );
- VOID
- CypFortunaRekey (
- PFORTUNA_CONTEXT Context
- );
- BOOL
- CypFortunaCheckReseedTime (
- PFORTUNA_CONTEXT Context
- );
- VOID
- CypFortunaReseed (
- PFORTUNA_CONTEXT Context
- );
- VOID
- CypFortunaEncryptCounter (
- PFORTUNA_CONTEXT Context,
- UCHAR Buffer[FORTUNA_BLOCK_SIZE]
- );
- VOID
- CypFortunaIncrementCounter (
- PFORTUNA_CONTEXT Context
- );
- UINTN
- CypFortunaGetRandomPoolIndex (
- PFORTUNA_CONTEXT Context
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // ------------------------------------------------------------------ Functions
- //
- CRYPTO_API
- VOID
- CyFortunaInitialize (
- PFORTUNA_CONTEXT Context,
- PCY_GET_TIME_COUNTER GetTimeCounterFunction,
- ULONGLONG TimeCounterFrequency
- )
- /*++
- Routine Description:
- This routine initializes a Fortuna PRNG context. It does not seed it with
- any values.
- Arguments:
- Context - Supplies a pointer to the context.
- GetTimeCounterFunction - Supplies an optional pointer to a function that
- can be used to retrieve a monotonically non-decreasing value
- representing the passage of time since some epoch.
- TimeCounterFrequency - Supplies the frequency of the time counter in
- Hertz.
- Return Value:
- None.
- --*/
- {
- UINTN Pool;
- RtlZeroMemory(Context, sizeof(FORTUNA_CONTEXT));
- for (Pool = 0; Pool < FORTUNA_POOL_COUNT; Pool += 1) {
- CySha256Initialize(&(Context->Pools[Pool]));
- }
- Context->GetTimeCounter = GetTimeCounterFunction;
- Context->TimeCounterFrequency = TimeCounterFrequency;
- return;
- }
- CRYPTO_API
- VOID
- CyFortunaGetRandomBytes (
- PFORTUNA_CONTEXT Context,
- PUCHAR Data,
- UINTN Size
- )
- /*++
- Routine Description:
- This routine returns random bytes from a Fortuna instance.
- Arguments:
- Context - Supplies a pointer to the context.
- Data - Supplies a pointer where the random bytes will be returned.
- Size - Supplies the number of bytes to return.
- Return Value:
- None.
- --*/
- {
- UINTN BlockNumber;
- UINTN CopySize;
- BlockNumber = 0;
- ASSERT(Context->Initialized != FortunaNotInitialized);
- //
- // Spread the seed around a bit if this is the first time.
- //
- if (Context->Initialized < FortunaInitialized) {
- CypFortunaSpreadInitialSeed(Context);
- Context->Initialized = FortunaInitialized;
- }
- //
- // Determine if a reseed should occur.
- //
- if ((Context->Pool0Bytes >= FORTUNA_POOL0_FILL) ||
- (Context->ReseedCount == 0)) {
- if (CypFortunaCheckReseedTime(Context) != FALSE) {
- CypFortunaReseed(Context);
- }
- }
- while (Size > 0) {
- CypFortunaEncryptCounter(Context, Context->Result);
- CopySize = Size;
- if (CopySize > FORTUNA_BLOCK_SIZE) {
- CopySize = FORTUNA_BLOCK_SIZE;
- }
- RtlCopyMemory(Data, Context->Result, CopySize);
- Data += CopySize;
- Size -= CopySize;
- //
- // Avoid giving out too many bytes from a single key.
- //
- BlockNumber += 1;
- if (BlockNumber > (FORTUNA_RESEED_SIZE / FORTUNA_BLOCK_SIZE)) {
- CypFortunaRekey(Context);
- BlockNumber = 0;
- }
- }
- //
- // Re-key for the next request.
- //
- CypFortunaRekey(Context);
- return;
- }
- CRYPTO_API
- VOID
- CyFortunaAddEntropy (
- PFORTUNA_CONTEXT Context,
- PVOID Data,
- UINTN Size
- )
- /*++
- Routine Description:
- This routine adds random data into the mix.
- Arguments:
- Context - Supplies a pointer to the context.
- Data - Supplies a pointer to the data to add.
- Size - Supplies the number of bytes of randomness in the data buffer.
- Return Value:
- None.
- --*/
- {
- UCHAR Hash[SHA256_HASH_SIZE];
- SHA256_CONTEXT HashContext;
- UINTN PoolIndex;
- //
- // Hash the data handed in.
- //
- CySha256Initialize(&HashContext);
- CySha256AddContent(&HashContext, Data, Size);
- CySha256GetHash(&HashContext, Hash);
- //
- // Make sure pool zero is initialized, otherwise update randomly.
- //
- if (Context->ReseedCount == 0) {
- PoolIndex = 0;
- if (Context->Initialized == FortunaNotInitialized) {
- Context->Initialized = FortunaInitializationSeeded;
- }
- } else {
- PoolIndex = CypFortunaGetRandomPoolIndex(Context);
- }
- CySha256AddContent(&(Context->Pools[PoolIndex]), Hash, SHA256_HASH_SIZE);
- if (PoolIndex == 0) {
- Context->Pool0Bytes += SHA256_HASH_SIZE;
- }
- return;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- VOID
- CypFortunaSpreadInitialSeed (
- PFORTUNA_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine spreads the entropy gained so far around the pools.
- Arguments:
- Context - Supplies a pointer to the context.
- Return Value:
- None.
- --*/
- {
- UCHAR Buffer[FORTUNA_HASH_KEY_SIZE];
- UINTN PoolIndex;
- //
- // Use the next block as the initial counter value.
- //
- CypFortunaEncryptCounter(Context, Context->Counter);
- //
- // Shuffle all pools except pool zero.
- //
- for (PoolIndex = 0; PoolIndex < FORTUNA_POOL_COUNT; PoolIndex += 1) {
- CypFortunaEncryptCounter(Context, Buffer);
- CypFortunaEncryptCounter(Context, Buffer + FORTUNA_BLOCK_SIZE);
- CySha256AddContent(&(Context->Pools[PoolIndex]),
- Buffer,
- FORTUNA_HASH_KEY_SIZE);
- }
- RtlZeroMemory(Buffer, sizeof(Buffer));
- //
- // Hide the key.
- //
- CypFortunaRekey(Context);
- return;
- }
- VOID
- CypFortunaRekey (
- PFORTUNA_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine selects a different cipher key for use in future block
- generation.
- Arguments:
- Context - Supplies a pointer to the context.
- Return Value:
- None.
- --*/
- {
- //
- // Use the two next blocks as the new key.
- //
- CypFortunaEncryptCounter(Context, Context->Key);
- CypFortunaEncryptCounter(Context, Context->Key + FORTUNA_BLOCK_SIZE);
- CyAesInitialize(&(Context->CipherContext),
- AesModeCbc256,
- Context->Key,
- NULL);
- return;
- }
- BOOL
- CypFortunaCheckReseedTime (
- PFORTUNA_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine determines whether it is time for the context to be reseeded
- or not.
- Arguments:
- Context - Supplies a pointer to the context.
- Return Value:
- TRUE if enough time has passed such that the context can be reseeded.
- FALSE if it hasn't been long enough since the last reseed.
- --*/
- {
- ULONGLONG CurrentTime;
- ULONGLONG DeltaTicks;
- ULONGLONG Milliseconds;
- if ((Context->GetTimeCounter == NULL) ||
- (Context->TimeCounterFrequency == 0)) {
- return TRUE;
- }
- CurrentTime = Context->GetTimeCounter();
- DeltaTicks = CurrentTime - Context->LastReseedTime;
- //
- // Compute the number of milliseconds since the last update.
- //
- Milliseconds = (DeltaTicks * 1000ULL) / Context->TimeCounterFrequency;
- if (Milliseconds >= FORTUNA_RESEED_INTERVAL_MILLISECONDS) {
- Context->LastReseedTime = CurrentTime;
- return TRUE;
- }
- return FALSE;
- }
- VOID
- CypFortunaReseed (
- PFORTUNA_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine selects a completely new cipher key using the entropy pools.
- Arguments:
- Context - Supplies a pointer to the context.
- Return Value:
- None.
- --*/
- {
- UCHAR Buffer[FORTUNA_HASH_KEY_SIZE];
- SHA256_CONTEXT KeyHashContext;
- UINTN PoolIndex;
- UINTN ReseedCount;
- //
- // Mark the pool as empty.
- //
- Context->Pool0Bytes = 0;
- Context->ReseedCount += 1;
- ReseedCount = Context->ReseedCount;
- //
- // The 0th pool is used every 2nd time, the 1st pool is used every 4th
- // time, the 2nd pool is used every 8th time, etc. Pool i is used every
- // 2^ith time.
- //
- CySha256Initialize(&KeyHashContext);
- for (PoolIndex = 0; PoolIndex < FORTUNA_POOL_COUNT; PoolIndex += 1) {
- CySha256GetHash(&(Context->Pools[PoolIndex]), Buffer);
- CySha256AddContent(&KeyHashContext, Buffer, FORTUNA_HASH_KEY_SIZE);
- if (((ReseedCount & 0x1) != 0) || (ReseedCount == 0)) {
- break;
- }
- ReseedCount >>= 1;
- }
- //
- // Add the old key into the mix too.
- //
- CySha256AddContent(&KeyHashContext, Context->Key, FORTUNA_HASH_KEY_SIZE);
- //
- // Get the new key.
- //
- CySha256GetHash(&KeyHashContext, Context->Key);
- //
- // Use the new key in future cipher blocks.
- //
- CyAesInitialize(&(Context->CipherContext),
- AesModeCbc256,
- Context->Key,
- NULL);
- //
- // Avoid leaking state onto the stack.
- //
- RtlZeroMemory(Buffer, sizeof(Buffer));
- RtlZeroMemory(&KeyHashContext, sizeof(SHA256_CONTEXT));
- return;
- }
- VOID
- CypFortunaEncryptCounter (
- PFORTUNA_CONTEXT Context,
- UCHAR Buffer[FORTUNA_BLOCK_SIZE]
- )
- /*++
- Routine Description:
- This routine simply encrypts a new counter value.
- Arguments:
- Context - Supplies a pointer to the context.
- Buffer - Supplies a pointer where the new block will be returned.
- Return Value:
- None.
- --*/
- {
- CyAesCbcEncrypt(&(Context->CipherContext),
- Context->Counter,
- Buffer,
- FORTUNA_BLOCK_SIZE);
- CypFortunaIncrementCounter(Context);
- return;
- }
- VOID
- CypFortunaIncrementCounter (
- PFORTUNA_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine simply increments the current counter value.
- Arguments:
- Context - Supplies a pointer to the context.
- Return Value:
- None.
- --*/
- {
- PUINTN Value;
- Value = (PUINTN)(Context->Counter);
- Value[0] += 1;
- if (Value[0] != 0) {
- return;
- }
- Value[1] += 1;
- if (Value[1] != 0) {
- return;
- }
- Value[2] += 1;
- if (Value[2] != 0) {
- return;
- }
- Value[3] += 1;
- return;
- }
- UINTN
- CypFortunaGetRandomPoolIndex (
- PFORTUNA_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine returns a pool index to feed entropy into.
- Arguments:
- Context - Supplies a pointer to the context.
- Return Value:
- Returns a pool index.
- --*/
- {
- UINTN Index;
- Index = Context->Key[Context->Position] % FORTUNA_POOL_COUNT;
- Context->Position += 1;
- if (Context->Position >= FORTUNA_HASH_KEY_SIZE) {
- Context->Position = 0;
- }
- return Index;
- }
|