Refactor free functions into RandomNumbers

This commit is contained in:
Vinnie Falco
2013-06-04 12:38:54 -07:00
parent 882102e9fd
commit 0523d6a054
16 changed files with 223 additions and 54 deletions

View File

@@ -148,5 +148,6 @@ namespace boost {
#include "types/ripple_UInt256.h"
#include "utility/ripple_HashUtilities.h" // requires UInt256
#include "types/ripple_HashTables.h"
#endif

View File

@@ -0,0 +1,95 @@
//------------------------------------------------------------------------------
#ifndef RIPPLE_HASHMAPS_H
#define RIPPLE_HASHMAPS_H
/** Management helper of hash functions used in hash map containers.
The nonce is used to prevent attackers from feeding carefully crafted
inputs in order to cause denegerate hash map data structures. This is
done by seeding the hashing function with a random number generated
at program startup.
*/
// VFALCO: TODO derive from Uncopyable
class HashMaps // : beast::Uncopayble
{
public:
/** Retrieve the singleton.
@return The global instance of the singleton.
*/
static HashMaps const& getInstance ()
{
static HashMaps instance;
return instance;
}
/** Instantiate a nonce for a type.
@note This may be used during program initialization
to avoid concurrency issues. Only C++11 provides thread
safety guarantees for function-local static objects.
*/
template <class T>
void initializeNonce () const
{
getNonceHolder <T> ();
}
/** Get the nonce for a type.
The nonces are generated when they are first used.
This code is thread safe.
*/
template <class T>
T getNonce () const
{
return getNonceHolder <T> ().getNonce ();
}
private:
HashMaps ()
{
}
~HashMaps ()
{
}
/** Creates and holds a nonce for a type.
*/
template <class T>
class NonceHolder
{
public:
NonceHolder ()
{
// VFALCO: NOTE, this can be dangerous if T is an object type
RandomNumbers::getInstance ().fill (&m_nonce);
}
inline T getNonce () const
{
return m_nonce;
}
private:
T m_nonce;
};
/** Retrieve the nonce holder for a type.
@note This routine will be called concurrently.
*/
template <class T>
NonceHolder <T> const& getNonceHolder ()
{
static NonceHolder <T> nonceHolder;
return nonceHolder;
}
};
#endif

View File

@@ -16,26 +16,72 @@
*/
//==============================================================================
void getRand(unsigned char *buf, int num)
RandomNumbers::RandomNumbers ()
: m_initialized (false)
{
}
RandomNumbers::~RandomNumbers ()
{
}
bool RandomNumbers::initialize ()
{
assert (!m_initialized);
bool success = platformAddEntropy ();
if (success)
m_initialized = true;
return success;
}
void RandomNumbers::fillBytes (void* destinationBuffer, int numberOfBytes)
{
// VFALCO: NOTE this assert is here to remind us that the code is not yet
// thread safe.
assert (m_initialized);
// VFALCO: NOTE When a spinlock is available in beast, use it here.
if (! m_initialized)
{
if (! initialize ())
{
char const* message = "Unable to add system entropy";
std::cerr << message << std::endl;
throw std::runtime_error (message);
}
}
#ifdef PURIFY
memset(buf, 0, num);
memset (destinationBuffer, 0, numberOfBytes);
#endif
if (RAND_bytes(buf, num) != 1)
if (RAND_bytes (reinterpret_cast <unsigned char*> (destinationBuffer), numberOfBytes) != 1)
{
assert(false);
throw std::runtime_error("Entropy pool not seeded");
throw std::runtime_error ("Entropy pool not seeded");
}
}
RandomNumbers& RandomNumbers::getInstance ()
{
static RandomNumbers instance;
return instance;
}
//------------------------------------------------------------------------------
// VFALCO: TODO replace WIN32 macro with VFLIB_WIN32
// VFALCO: TODO replace WIN32 macro
#ifdef WIN32
bool AddSystemEntropy()
{ // Get entropy from the Windows crypto provider
// Get entropy from the Windows crypto provider
bool RandomNumbers::platformAddEntropy ()
{
char name[512], rand[128];
DWORD count = 500;
HCRYPTPROV cryptoHandle;
@@ -73,12 +119,7 @@ bool AddSystemEntropy()
#else
#include <iostream>
#include <fstream>
#include <openssl/rand.h>
bool AddSystemEntropy()
bool RandomNumbers::platformAddEntropy ()
{
char rand[128];
std::ifstream reader;
@@ -117,7 +158,7 @@ bool AddSystemEntropy()
// - The user (asking the user to fix the system clock if the first two disagree)
//
void RandAddSeedPerfmon()
void RandomNumbers::platformAddPerformanceMonitorEntropy ()
{
// VFALCO: This is how we simulate local functions
struct

View File

@@ -19,33 +19,58 @@
#ifndef RIPPLE_RANDOMNUMBERS_H
#define RIPPLE_RANDOMNUMBERS_H
extern bool AddSystemEntropy ();
// Cryptographically secure random number source
// VFALCO: TODO Clean this up, rename stuff
// Seriously...wtf...rename "num" to bytes, or make it work
// using a template so the destination can be a vector of objects.
//
// VFALCO: Should accept void* not unsigned char*
//
extern void getRand (unsigned char *buf, int num);
inline static void getRand (char *buf, int num)
/** Cryptographically secure random number source.
*/
class RandomNumbers
{
return getRand (reinterpret_cast<unsigned char *>(buf), num);
}
public:
static RandomNumbers& getInstance ();
// VFALCO: TODO Clean this
// "num" is really bytes this should just be called getRandomBytes()
// This function is unnecessary!
//
inline static void getRand (void *buf, int num)
{
return getRand (reinterpret_cast<unsigned char *>(buf), num);
}
/** Initialize the generator.
// Lifted from BitcoinUtil.h
extern void RandAddSeedPerfmon();
If the generator is not manually initialized, it will be
automatically initialized on first use. If automatic initialization
fails, an exception is thrown.
@return `true` if enough entropy could be retrieved.
*/
bool initialize ();
/** Generate secure random numbers.
The generated data is suitable for cryptography.
@invariant The destination buffer must be large enough or undefined behavior
results.
@param destinationBuffer The place to store the bytes.
@param numberOfBytes The number of bytes to generate.
*/
void fillBytes (void* destinationBuffer, int numberOfBytes);
/** Generate secure random numbers.
The generated data is suitable for cryptography.
Fills the memory for the object with random numbers. This is a type-safe
alternative to the function above.
*/
template <class T>
void fill (T* object)
{
fillBytes (object, sizeof (T));
}
private:
RandomNumbers ();
~RandomNumbers ();
bool platformAddEntropy ();
void platformAddPerformanceMonitorEntropy ();
private:
bool m_initialized;
};
#endif

View File

@@ -105,7 +105,7 @@ std::vector<unsigned char> CKey::encryptECIES(CKey& otherKey, const std::vector<
{
ECIES_ENC_IV_TYPE iv;
getRand(static_cast<unsigned char *>(iv.begin()), ECIES_ENC_BLK_SIZE);
RandomNumbers::getInstance ().fillBytes (iv.begin (), ECIES_ENC_BLK_SIZE);
ECIES_ENC_KEY_TYPE secret;
ECIES_HMAC_KEY_TYPE hmacKey;
@@ -268,7 +268,7 @@ bool checkECIES(void)
std::vector<unsigned char> message(4096);
int msglen = i%3000;
getRand(static_cast<unsigned char *>(&message.front()), msglen);
RandomNumbers::getInstance ().fillBytes (&message.front(), msglen);
message.resize(msglen);
// encrypt message with sender's private key and recipient's public key

View File

@@ -803,7 +803,7 @@ void RippleAddress::setSeedRandom()
// XXX Maybe we should call MakeNewKey
uint128 key;
getRand(key.begin(), key.size());
RandomNumbers::getInstance ().fillBytes (key.begin(), key.size());
RippleAddress::setSeed(key);
}

View File

@@ -57,6 +57,7 @@
#include "crypto/ripple_CBigNum.h"
#include "crypto/ripple_Base58.h" // VFALCO: TODO, Can be moved to .cpp if we clean up setAlphabet stuff
#include "crypto/ripple_Base58Data.h"
// #include "src/cpp/ripple/ProofOfWork.h"
#include "protocol/ripple_FieldNames.h"
#include "protocol/ripple_RippleAddress.h"

View File

@@ -1270,6 +1270,7 @@
<ClInclude Include="modules\ripple_basics\containers\ripple_SecureAllocator.h" />
<ClInclude Include="modules\ripple_basics\containers\ripple_TaggedCache.h" />
<ClInclude Include="modules\ripple_basics\ripple_basics.h" />
<ClInclude Include="modules\ripple_basics\types\ripple_HashTables.h" />
<ClInclude Include="modules\ripple_basics\types\ripple_UInt256.h" />
<ClInclude Include="modules\ripple_basics\utility\ripple_ByteOrder.h" />
<ClInclude Include="modules\ripple_basics\utility\ripple_DiffieHellmanUtil.h" />

View File

@@ -118,9 +118,6 @@
<Filter Include="1. Modules\ripple_main\_unfactored\types">
<UniqueIdentifier>{1b463564-35d9-43d1-b3a0-21b344a3a1c7}</UniqueIdentifier>
</Filter>
<Filter Include="1. Modules\ripple_main\_unfactored\http">
<UniqueIdentifier>{29cd2103-d553-4d82-9e6a-224e3b1cb667}</UniqueIdentifier>
</Filter>
<Filter Include="1. Modules\ripple_main\_unfactored\main">
<UniqueIdentifier>{1ccfc5ad-5cd7-4a8e-b305-08f663c2397c}</UniqueIdentifier>
</Filter>
@@ -1508,6 +1505,9 @@
<ClInclude Include="modules\ripple_data\protocol\ripple_RippleSystem.h">
<Filter>1. Modules\ripple_data\protocol</Filter>
</ClInclude>
<ClInclude Include="modules\ripple_basics\types\ripple_HashTables.h">
<Filter>1. Modules\ripple_basics\types</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="SConstruct" />

View File

@@ -68,8 +68,8 @@ Application::Application ()
, mSweepTimer (mAuxService)
, mShutdown (false)
{
getRand (mNonce256.begin(), mNonce256.size());
getRand (reinterpret_cast<unsigned char *>(&mNonceST), sizeof(mNonceST));
RandomNumbers::getInstance ().fillBytes (mNonce256.begin(), mNonce256.size());
RandomNumbers::getInstance ().fill (&mNonceST);
}
extern const char *RpcDBInit[], *TxnDBInit[], *LedgerDBInit[], *WalletDBInit[], *HashNodeDBInit[],

View File

@@ -1782,7 +1782,7 @@ bool NetworkOPs::subServer(InfoSub::ref isrListener, Json::Value& jvResult)
if (theConfig.TESTNET)
jvResult["testnet"] = theConfig.TESTNET;
getRand(uRandom.begin(), uRandom.size());
RandomNumbers::getInstance ().fillBytes (uRandom.begin(), uRandom.size());
jvResult["random"] = uRandom.ToString();
jvResult["server_status"] = strOperatingMode();
jvResult["load_base"] = theApp->getFeeTrack().getLoadBase();

View File

@@ -103,7 +103,7 @@ uint256 ProofOfWork::solve(int maxIterations) const
throw std::runtime_error("invalid proof of work target/iteration");
uint256 nonce;
getRand(nonce.begin(), nonce.size());
RandomNumbers::getInstance ().fill (&nonce);
std::vector<uint256> buf2;
buf2.resize(mIterations);
@@ -162,7 +162,7 @@ bool ProofOfWork::validateToken(const std::string& strToken)
ProofOfWorkGenerator::ProofOfWorkGenerator() : mValidTime(180)
{
setDifficulty(1);
getRand(mSecret.begin(), mSecret.size());
RandomNumbers::getInstance ().fillBytes (mSecret.begin(), mSecret.size());
}
ProofOfWork ProofOfWorkGenerator::getProof()
@@ -173,7 +173,7 @@ ProofOfWork ProofOfWorkGenerator::getProof()
int now = static_cast<int>(time(NULL) / 4);
uint256 challenge;
getRand(challenge.begin(), challenge.size());
RandomNumbers::getInstance ().fillBytes (challenge.begin(), challenge.size());
boost::mutex::scoped_lock sl(mLock);

View File

@@ -1276,7 +1276,7 @@ Json::Value RPCHandler::doRandom(Json::Value jvRequest, int& cost, ScopedLock& M
try
{
getRand(uRandom.begin(), uRandom.size());
RandomNumbers::getInstance ().fillBytes (uRandom.begin(), uRandom.size());
Json::Value jvResult;

View File

@@ -76,7 +76,7 @@ void SNTPClient::resolveComplete(const boost::system::error_code& error, boost::
}
query.mReceivedReply = false;
query.mLocalTimeSent = now;
getRand(reinterpret_cast<unsigned char *>(&query.mQueryNonce), sizeof(query.mQueryNonce));
RandomNumbers::getInstance ().fill (&query.mQueryNonce);
reinterpret_cast<uint32*>(SNTPQueryData)[NTP_OFF_XMITTS_INT] = static_cast<uint32>(time(NULL)) + NTP_UNIX_OFFSET;
reinterpret_cast<uint32*>(SNTPQueryData)[NTP_OFF_XMITTS_FRAC] = query.mQueryNonce;
mSocket.async_send_to(boost::asio::buffer(SNTPQueryData, 48), *sel,

View File

@@ -160,7 +160,7 @@ int main(int argc, char* argv[])
// Prepare to run
//
if (!AddSystemEntropy())
if (! RandomNumbers::getInstance ().initialize ())
{
std::cerr << "Unable to add system entropy" << std::endl;
iResult = 2;

View File

@@ -4,6 +4,11 @@ int DatabaseCon::sCount = 0;
DatabaseCon::DatabaseCon(const std::string& strName, const char *initStrings[], int initCount)
{
++sCount;
// VFALCO: TODO, remove this dependency on the config by making it the caller's
// responsibility to pass in the path. Add a member function to Application
// or Config to compute this path.
//
boost::filesystem::path pPath = (theConfig.RUN_STANDALONE && (theConfig.START_UP != Config::LOAD))
? "" // Use temporary files.
: (theConfig.DATA_DIR / strName); // Use regular db files.