From 0523d6a0546cab1e1dd8448fcfcca4ee6fdef286 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Tue, 4 Jun 2013 12:38:54 -0700 Subject: [PATCH] Refactor free functions into RandomNumbers --- modules/ripple_basics/ripple_basics.h | 1 + .../ripple_basics/types/ripple_HashTables.h | 95 +++++++++++++++++++ .../utility/ripple_RandomNumbers.cpp | 69 +++++++++++--- .../utility/ripple_RandomNumbers.h | 75 ++++++++++----- .../ripple_data/crypto/ripple_CKeyECIES.cpp | 4 +- .../protocol/ripple_RippleAddress.cpp | 2 +- modules/ripple_data/ripple_data.h | 1 + newcoin.vcxproj | 1 + newcoin.vcxproj.filters | 6 +- src/cpp/ripple/Application.cpp | 4 +- src/cpp/ripple/NetworkOPs.cpp | 2 +- src/cpp/ripple/ProofOfWork.cpp | 6 +- src/cpp/ripple/RPCHandler.cpp | 2 +- src/cpp/ripple/SNTPClient.cpp | 2 +- src/cpp/ripple/main.cpp | 2 +- src/cpp/ripple/ripple_DatabaseCon.cpp | 5 + 16 files changed, 223 insertions(+), 54 deletions(-) create mode 100644 modules/ripple_basics/types/ripple_HashTables.h diff --git a/modules/ripple_basics/ripple_basics.h b/modules/ripple_basics/ripple_basics.h index bc120b1bcc..68801f888d 100644 --- a/modules/ripple_basics/ripple_basics.h +++ b/modules/ripple_basics/ripple_basics.h @@ -148,5 +148,6 @@ namespace boost { #include "types/ripple_UInt256.h" #include "utility/ripple_HashUtilities.h" // requires UInt256 +#include "types/ripple_HashTables.h" #endif diff --git a/modules/ripple_basics/types/ripple_HashTables.h b/modules/ripple_basics/types/ripple_HashTables.h new file mode 100644 index 0000000000..10f524921d --- /dev/null +++ b/modules/ripple_basics/types/ripple_HashTables.h @@ -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 + void initializeNonce () const + { + getNonceHolder (); + } + + /** Get the nonce for a type. + + The nonces are generated when they are first used. + This code is thread safe. + */ + template + T getNonce () const + { + return getNonceHolder ().getNonce (); + } + +private: + HashMaps () + { + } + + ~HashMaps () + { + } + + /** Creates and holds a nonce for a type. + */ + template + 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 + NonceHolder const& getNonceHolder () + { + static NonceHolder nonceHolder; + + return nonceHolder; + } +}; + +#endif diff --git a/modules/ripple_basics/utility/ripple_RandomNumbers.cpp b/modules/ripple_basics/utility/ripple_RandomNumbers.cpp index 9fc67373ea..e9728ad673 100644 --- a/modules/ripple_basics/utility/ripple_RandomNumbers.cpp +++ b/modules/ripple_basics/utility/ripple_RandomNumbers.cpp @@ -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 (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 -#include - -#include - -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 diff --git a/modules/ripple_basics/utility/ripple_RandomNumbers.h b/modules/ripple_basics/utility/ripple_RandomNumbers.h index a2b7594620..17afabd3ae 100644 --- a/modules/ripple_basics/utility/ripple_RandomNumbers.h +++ b/modules/ripple_basics/utility/ripple_RandomNumbers.h @@ -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(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(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 + void fill (T* object) + { + fillBytes (object, sizeof (T)); + } + +private: + RandomNumbers (); + + ~RandomNumbers (); + + bool platformAddEntropy (); + + void platformAddPerformanceMonitorEntropy (); + +private: + bool m_initialized; +}; #endif diff --git a/modules/ripple_data/crypto/ripple_CKeyECIES.cpp b/modules/ripple_data/crypto/ripple_CKeyECIES.cpp index 43a46c2ea5..307b17b7e8 100644 --- a/modules/ripple_data/crypto/ripple_CKeyECIES.cpp +++ b/modules/ripple_data/crypto/ripple_CKeyECIES.cpp @@ -105,7 +105,7 @@ std::vector CKey::encryptECIES(CKey& otherKey, const std::vector< { ECIES_ENC_IV_TYPE iv; - getRand(static_cast(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 message(4096); int msglen = i%3000; - getRand(static_cast(&message.front()), msglen); + RandomNumbers::getInstance ().fillBytes (&message.front(), msglen); message.resize(msglen); // encrypt message with sender's private key and recipient's public key diff --git a/modules/ripple_data/protocol/ripple_RippleAddress.cpp b/modules/ripple_data/protocol/ripple_RippleAddress.cpp index 5cd4b4f2a4..fff5597b81 100644 --- a/modules/ripple_data/protocol/ripple_RippleAddress.cpp +++ b/modules/ripple_data/protocol/ripple_RippleAddress.cpp @@ -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); } diff --git a/modules/ripple_data/ripple_data.h b/modules/ripple_data/ripple_data.h index 4ba8e04a67..2dae741efa 100644 --- a/modules/ripple_data/ripple_data.h +++ b/modules/ripple_data/ripple_data.h @@ -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" diff --git a/newcoin.vcxproj b/newcoin.vcxproj index 593b9ddc87..d00aec0f6c 100644 --- a/newcoin.vcxproj +++ b/newcoin.vcxproj @@ -1270,6 +1270,7 @@ + diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters index c6d11f9358..08bc33521d 100644 --- a/newcoin.vcxproj.filters +++ b/newcoin.vcxproj.filters @@ -118,9 +118,6 @@ {1b463564-35d9-43d1-b3a0-21b344a3a1c7} - - {29cd2103-d553-4d82-9e6a-224e3b1cb667} - {1ccfc5ad-5cd7-4a8e-b305-08f663c2397c} @@ -1508,6 +1505,9 @@ 1. Modules\ripple_data\protocol + + 1. Modules\ripple_basics\types + diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index 56877484d1..40f0a527e3 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -68,8 +68,8 @@ Application::Application () , mSweepTimer (mAuxService) , mShutdown (false) { - getRand (mNonce256.begin(), mNonce256.size()); - getRand (reinterpret_cast(&mNonceST), sizeof(mNonceST)); + RandomNumbers::getInstance ().fillBytes (mNonce256.begin(), mNonce256.size()); + RandomNumbers::getInstance ().fill (&mNonceST); } extern const char *RpcDBInit[], *TxnDBInit[], *LedgerDBInit[], *WalletDBInit[], *HashNodeDBInit[], diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 493aa3ed4e..700a256582 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -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(); diff --git a/src/cpp/ripple/ProofOfWork.cpp b/src/cpp/ripple/ProofOfWork.cpp index f55bab7f7b..2d8a1e9fe5 100644 --- a/src/cpp/ripple/ProofOfWork.cpp +++ b/src/cpp/ripple/ProofOfWork.cpp @@ -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 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(time(NULL) / 4); uint256 challenge; - getRand(challenge.begin(), challenge.size()); + RandomNumbers::getInstance ().fillBytes (challenge.begin(), challenge.size()); boost::mutex::scoped_lock sl(mLock); diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index d56272bcfd..7931afefd4 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -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; diff --git a/src/cpp/ripple/SNTPClient.cpp b/src/cpp/ripple/SNTPClient.cpp index a346b2e538..e2cd384692 100644 --- a/src/cpp/ripple/SNTPClient.cpp +++ b/src/cpp/ripple/SNTPClient.cpp @@ -76,7 +76,7 @@ void SNTPClient::resolveComplete(const boost::system::error_code& error, boost:: } query.mReceivedReply = false; query.mLocalTimeSent = now; - getRand(reinterpret_cast(&query.mQueryNonce), sizeof(query.mQueryNonce)); + RandomNumbers::getInstance ().fill (&query.mQueryNonce); reinterpret_cast(SNTPQueryData)[NTP_OFF_XMITTS_INT] = static_cast(time(NULL)) + NTP_UNIX_OFFSET; reinterpret_cast(SNTPQueryData)[NTP_OFF_XMITTS_FRAC] = query.mQueryNonce; mSocket.async_send_to(boost::asio::buffer(SNTPQueryData, 48), *sel, diff --git a/src/cpp/ripple/main.cpp b/src/cpp/ripple/main.cpp index 7625b34b6f..086a054c3f 100644 --- a/src/cpp/ripple/main.cpp +++ b/src/cpp/ripple/main.cpp @@ -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; diff --git a/src/cpp/ripple/ripple_DatabaseCon.cpp b/src/cpp/ripple/ripple_DatabaseCon.cpp index 262c7d5a1c..4f27709d4d 100644 --- a/src/cpp/ripple/ripple_DatabaseCon.cpp +++ b/src/cpp/ripple/ripple_DatabaseCon.cpp @@ -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.