diff options
Diffstat (limited to 'Core/RandomNumberGenerator.cpp')
-rw-r--r-- | Core/RandomNumberGenerator.cpp | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/Core/RandomNumberGenerator.cpp b/Core/RandomNumberGenerator.cpp new file mode 100644 index 0000000..a010e7c --- /dev/null +++ b/Core/RandomNumberGenerator.cpp @@ -0,0 +1,213 @@ +/* + Copyright (c) 2008-2009 TrueCrypt Developers Association. All rights reserved. + + Governed by the TrueCrypt License 3.0 the full text of which is contained in + the file License.txt included in TrueCrypt binary and source code distribution + packages. +*/ + +#ifndef TC_WINDOWS +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#endif + +#include "RandomNumberGenerator.h" +#include "Volume/Crc32.h" + +namespace TrueCrypt +{ + void RandomNumberGenerator::AddSystemDataToPool (bool fast) + { + SecureBuffer buffer (PoolSize); + +#ifdef TC_WINDOWS +#ifndef DEBUG + throw NotImplemented (SRC_POS); +#endif +#else + int urandom = open ("/dev/urandom", O_RDONLY); + throw_sys_sub_if (urandom == -1, L"/dev/urandom"); + finally_do_arg (int, urandom, { close (finally_arg); }); + + throw_sys_sub_if (read (urandom, buffer, buffer.Size()) == -1, L"/dev/urandom"); + AddToPool (buffer); + + if (!fast) + { + // Read all bytes available in /dev/random up to buffer size + int random = open ("/dev/random", O_RDONLY | O_NONBLOCK); + throw_sys_sub_if (random == -1, L"/dev/random"); + finally_do_arg (int, random, { close (finally_arg); }); + + throw_sys_sub_if (read (random, buffer, buffer.Size()) == -1 && errno != EAGAIN, L"/dev/random"); + AddToPool (buffer); + } +#endif + } + + void RandomNumberGenerator::AddToPool (const ConstBufferPtr &data) + { + if (!Running) + throw NotInitialized (SRC_POS); + + ScopeLock lock (AccessMutex); + + for (size_t i = 0; i < data.Size(); ++i) + { + Pool[WriteOffset++] += data[i]; + + if (WriteOffset >= PoolSize) + WriteOffset = 0; + + if (++BytesAddedSincePoolHashMix >= MaxBytesAddedBeforePoolHashMix) + HashMixPool(); + } + } + + void RandomNumberGenerator::GetData (const BufferPtr &buffer, bool fast) + { + if (!Running) + throw NotInitialized (SRC_POS); + + if (buffer.Size() > PoolSize) + throw ParameterIncorrect (SRC_POS); + + ScopeLock lock (AccessMutex); + + // Poll system for data + AddSystemDataToPool (fast); + HashMixPool(); + + // Transfer bytes from pool to output buffer + for (size_t i = 0; i < buffer.Size(); ++i) + { + buffer[i] += Pool[ReadOffset++]; + + if (ReadOffset >= PoolSize) + ReadOffset = 0; + } + + // Invert and mix the pool + for (size_t i = 0; i < Pool.Size(); ++i) + { + Pool[i] = ~Pool[i]; + } + + AddSystemDataToPool (true); + HashMixPool(); + + // XOR the current pool content into the output buffer to prevent pool state leaks + for (size_t i = 0; i < buffer.Size(); ++i) + { + buffer[i] ^= Pool[ReadOffset++]; + + if (ReadOffset >= PoolSize) + ReadOffset = 0; + } + } + + shared_ptr <Hash> RandomNumberGenerator::GetHash () + { + ScopeLock lock (AccessMutex); + return PoolHash; + } + + void RandomNumberGenerator::HashMixPool () + { + BytesAddedSincePoolHashMix = 0; + + for (size_t poolPos = 0; poolPos < Pool.Size(); ) + { + // Compute the message digest of the entire pool using the selected hash function + SecureBuffer digest (PoolHash->GetDigestSize()); + PoolHash->ProcessData (Pool); + PoolHash->GetDigest (digest); + + // Add the message digest to the pool + for (size_t digestPos = 0; digestPos < digest.Size() && poolPos < Pool.Size(); ++digestPos) + { + Pool[poolPos++] += digest[digestPos]; + } + } + } + + void RandomNumberGenerator::SetHash (shared_ptr <Hash> hash) + { + ScopeLock lock (AccessMutex); + PoolHash = hash; + } + + void RandomNumberGenerator::Start () + { + ScopeLock lock (AccessMutex); + + if (IsRunning()) + return; + + BytesAddedSincePoolHashMix = 0; + ReadOffset = 0; + WriteOffset = 0; + Running = true; + EnrichedByUser = false; + + Pool.Allocate (PoolSize); + Test(); + + if (!PoolHash) + { + // First hash algorithm is the default one + PoolHash = Hash::GetAvailableAlgorithms().front(); + } + + AddSystemDataToPool (true); + } + + void RandomNumberGenerator::Stop () + { + ScopeLock lock (AccessMutex); + + if (Pool.IsAllocated()) + Pool.Free (); + + PoolHash.reset(); + + EnrichedByUser = false; + Running = false; + } + + void RandomNumberGenerator::Test () + { + shared_ptr <Hash> origPoolHash = PoolHash; + PoolHash.reset (new Ripemd160()); + + Pool.Zero(); + Buffer buffer (1); + for (size_t i = 0; i < PoolSize * 10; ++i) + { + buffer[0] = (byte) i; + AddToPool (buffer); + } + + if (Crc32::ProcessBuffer (Pool) != 0x2de46d17) + throw TestFailed (SRC_POS); + + buffer.Allocate (PoolSize); + buffer.CopyFrom (PeekPool()); + AddToPool (buffer); + + if (Crc32::ProcessBuffer (Pool) != 0xcb88e019) + throw TestFailed (SRC_POS); + + PoolHash = origPoolHash; + } + + Mutex RandomNumberGenerator::AccessMutex; + size_t RandomNumberGenerator::BytesAddedSincePoolHashMix; + bool RandomNumberGenerator::EnrichedByUser; + SecureBuffer RandomNumberGenerator::Pool; + shared_ptr <Hash> RandomNumberGenerator::PoolHash; + size_t RandomNumberGenerator::ReadOffset; + bool RandomNumberGenerator::Running = false; + size_t RandomNumberGenerator::WriteOffset; +} |