diff --git a/CMakeLists.txt b/CMakeLists.txt index 040f3b3ed..bc178b9e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -539,7 +539,6 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/third-party/google-test/googlet include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/third-party/google-test/googletest/include ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/src/core/include ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/src/binfhe/include ) -include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/prng_libs ) include_directories( ${CMAKE_CURRENT_BINARY_DIR}/src/core ) diff --git a/prng_libs/blake2_lib/lib/blake2engine.cpp b/prng_libs/blake2_lib/lib/blake2engine.cpp deleted file mode 100644 index b434be62b..000000000 --- a/prng_libs/blake2_lib/lib/blake2engine.cpp +++ /dev/null @@ -1,57 +0,0 @@ -//================================================================================== -// © 2024 Duality Technologies, Inc. All rights reserved. -// This is a proprietary software product of Duality Technologies, Inc. -// protected under copyright laws and international copyright treaties, patent -// law, trade secret law and other intellectual property rights of general -// applicability. Any use of this software is strictly prohibited absent a -// written agreement executed by Duality Technologies, Inc., which provides -// certain limited rights to use this software. You may not copy, distribute, -// make publicly available, publicly perform, disassemble, de-compile or reverse -// engineer any part of this software, breach its security, or circumvent, -// manipulate, impair or disrupt its operation. -//================================================================================== -#include "blake2engine.h" - -Blake2Engine* createEngineInstance() { -// initialization of PRNGs -constexpr size_t maxGens = Blake2Engine::MAX_SEED_GENS; -#pragma omp critical - std::array initKey{}; - initKey[0] = std::chrono::high_resolution_clock::now().time_since_epoch().count(); - initKey[1] = std::hash{}(std::this_thread::get_id()); -#if !defined(__arm__) && !defined(__EMSCRIPTEN__) - if (sizeof(size_t) == 8) - initKey[2] = (std::hash{}(std::this_thread::get_id()) >> 32); -#endif - void* mem = malloc(1); - uint32_t counter = reinterpret_cast(mem); // NOLINT - free(mem); - - Blake2Engine gen(initKey, counter); - - std::uniform_int_distribution distribution(0); - std::array seed{}; - for (uint32_t i = 0; i < maxGens; i++) { - seed[i] = distribution(gen); - } - - std::array rdseed{}; - size_t attempts = 3; - bool rdGenPassed = false; - for(size_t i = 0; i < attempts && !rdGenPassed; ++i) { - try { - std::random_device genR; - for (uint32_t i = 0; i < maxGens; i++) { - rdseed[i] = distribution(genR); - } - rdGenPassed = true; - } - catch (std::exception& e) { - } - } - for (uint32_t i = 0; i < maxGens; i++) { - seed[i] += rdseed[i]; - } - - return new Blake2Engine(seed); -} diff --git a/prng_libs/blake2_lib/link.sh b/prng_libs/blake2_lib/link.sh deleted file mode 100755 index 94c2066eb..000000000 --- a/prng_libs/blake2_lib/link.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -# set -x - -g++ -fPIC -shared -o libblake2.so -I./include -I../ ./lib/*.c ./lib/*.cpp - diff --git a/prng_libs/prng.h b/prng_libs/prng.h deleted file mode 100644 index 778d62f37..000000000 --- a/prng_libs/prng.h +++ /dev/null @@ -1,49 +0,0 @@ -//================================================================================== -// © 2024 Duality Technologies, Inc. All rights reserved. -// This is a proprietary software product of Duality Technologies, Inc. -// protected under copyright laws and international copyright treaties, patent -// law, trade secret law and other intellectual property rights of general -// applicability. Any use of this software is strictly prohibited absent a -// written agreement executed by Duality Technologies, Inc., which provides -// certain limited rights to use this software. You may not copy, distribute, -// make publicly available, publicly perform, disassemble, de-compile or reverse -// engineer any part of this software, breach its security, or circumvent, -// manipulate, impair or disrupt its operation. -//================================================================================== -#ifndef __PRNG_H__ -#define __PRNG_H__ - -#include -#include - - -class PRNG { -public: - // all C++11 distributions used in OpenFHE work by default with uint32_t - // a different data type can be specified if needed for a particular - // architecture - using result_type = uint32_t; - - PRNG(result_type seed) {} - /** - * @brief minimum value used by C+11 distribution generators when no lower - * bound is explicitly specified by the user - */ - static constexpr result_type min() { - return std::numeric_limits::min(); - } - - /** - * @brief maximum value used by C+11 distribution generators when no upper - * bound is explicitly specified by the user - */ - static constexpr result_type max() { - return std::numeric_limits::max(); - } - - virtual result_type operator()() = 0; - virtual ~PRNG() = default; -}; - -#endif // __PRNG_H__ - diff --git a/src/core/include/math/distributiongenerator.h b/src/core/include/math/distributiongenerator.h index 7fb41e5b7..960457021 100644 --- a/src/core/include/math/distributiongenerator.h +++ b/src/core/include/math/distributiongenerator.h @@ -37,18 +37,20 @@ #ifndef LBCRYPTO_INC_MATH_DISTRIBUTIONGENERATOR_H_ #define LBCRYPTO_INC_MATH_DISTRIBUTIONGENERATOR_H_ +// #include "math/math-hal.h" -// #include "utils/parallel.h" -#include "utils/exception.h" - -#include "prng.h" +#include "utils/parallel.h" +#include "utils/prng/blake2engine.h" +#include #include // #include -// #include -// #include -// #include -#include +#include +#include + +// #define FIXED_SEED // if defined, then uses a fixed seed number for +// reproducible results during debug. Use only one OMP thread to ensure +// reproducibility namespace lbcrypto { @@ -56,139 +58,136 @@ namespace lbcrypto { // The cryptographically secure PRNG used by OpenFHE is based on BLAKE2 hash // functions. A user can replace it with a different PRNG if desired by defining // the same methods as for the Blake2Engine class. -// typedef Blake2Engine PRNG; - - +typedef Blake2Engine PRNG; + +/** + * @brief The class providing the PRNG capability to all random distribution + * generators in OpenFHE. THe security of Ring Learning With Errors (used for + * all crypto capabilities in OpenFHE) depends on the randomness of uniform, + * ternary, and Gaussian distributions, which derive their randomness from the + * PRNG. + */ class PseudoRandomNumberGenerator { public: - // static PseudoRandomNumberGenerator& getInstance() { - // std::call_once(initInstanceFlag, &PseudoRandomNumberGenerator::createInstance); - // return *instance; - // } - - static PRNG& GetPRNG() { - std::cerr << __FILE__ << ":l." << __LINE__ << "In PseudoRandomNumberGenerator:: GetPRNG()" << std::endl; - // I commented getInstance() as we use GetPRNG() instead of getInstance() and initialized this singleton class - // in GetPRNG(). TODO (dsuponit): i am curious if we may be able to get rid of - // std::unique_ptr PseudoRandomNumberGenerator::instance :))) - std::call_once(initInstanceFlag, &PseudoRandomNumberGenerator::createInstance); - return *engine; - } - -private: - PseudoRandomNumberGenerator() = default; - ~PseudoRandomNumberGenerator() { - if (engine) { - delete engine; - engine = nullptr; + /** + * @brief Returns a reference to the PRNG engine + */ + + // TODO: there may be an issue here + static void InitPRNG() { + int threads = OpenFHEParallelControls.GetNumThreads(); + if (threads == 0) { + threads = 1; } - if (singletonHandle) { - dlclose(singletonHandle); - singletonHandle = nullptr; +#pragma omp parallel for num_threads(threads) + for (int i = 0; i < threads; ++i) { + GetPRNG(); } } - - static void createInstance() { - instance.reset(new PseudoRandomNumberGenerator); - - const std::string engineLibName = "libengine.so"; - void* handle = dlopen(engineLibName.c_str(), RTLD_LAZY); - if (!handle) { - OPENFHE_THROW("Cannot open " + engineLibName); - } - // get the factory function - using CreateInstanceFunc = PRNG*(*)(); - const std::string engineLoadFuncName = "createEngineInstance"; - CreateInstanceFunc func = (CreateInstanceFunc)dlsym(handle, engineLoadFuncName.c_str()); - if (!func) { - std::string errMsg{"Cannot load symbol " + engineLoadFuncName}; - const char* dlsym_error = dlerror(); - if (dlsym_error) { - errMsg += ": "; - errMsg += dlsym_error; + static PRNG& GetPRNG() { + // initialization of PRNGs + if (m_prng == nullptr) { +#pragma omp critical + { +#if defined(FIXED_SEED) + // Only used for debugging in the single-threaded mode. + std::cerr << "**FOR DEBUGGING ONLY!!!! Using fixed initializer for " + "PRNG. Use a single thread only, e.g., OMP_NUM_THREADS=1!" + << std::endl; + + std::array seed{}; + seed[0] = 1; + m_prng = std::make_shared(seed); +#else + // A 512-bit seed is generated for each thread (this roughly corresponds + // to 256 bits of security). The seed is the sum of a random sample + // generated using std::random_device (typically works correctly in + // Linux, MacOS X, and MinGW starting with GCC 9.2) and a BLAKE2 sample + // seeded from current time stamp, a hash of the current thread, and a + // memory location of a heap variable. The BLAKE2 sample is added in + // case random_device is deterministic (happens on MinGW with GCC + // below 9.2). All future calls to PRNG use the seed generated here. + + // The code below derives randomness from time, thread id, and a memory + // location of a heap variable. This seed is relevant only if the + // implementation of random_device is deterministic (as in older + // versions of GCC in MinGW) + std::array initKey{}; + // high-resolution clock typically has a nanosecond tick period + // Arguably this may give up to 32 bits of entropy as the clock gets + // recycled every 4.3 seconds + initKey[0] = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + // A thread id is often close to being random (on most systems) + initKey[1] = std::hash{}(std::this_thread::get_id()); + // On a 64-bit machine, the thread id is 64 bits long + // skip on 32-bit arm architectures + #if !defined(__arm__) && !defined(__EMSCRIPTEN__) + if (sizeof(size_t) == 8) + initKey[2] = (std::hash{}(std::this_thread::get_id()) >> 32); + #endif + + // heap variable; we are going to use the least 32 bits of its memory + // location as the counter for BLAKE2 This will increase the entropy of + // the BLAKE2 sample + void* mem = malloc(1); + uint32_t counter = reinterpret_cast(mem); // NOLINT + free(mem); + + PRNG gen(initKey, counter); + + std::uniform_int_distribution distribution(0); + std::array seed{}; + for (uint32_t i = 0; i < 16; i++) { + seed[i] = distribution(gen); + } + + std::array rdseed{}; + size_t attempts = 3; + bool rdGenPassed = false; + size_t idx = 0; + while (!rdGenPassed && idx < attempts) { + try { + std::random_device genR; + for (uint32_t i = 0; i < 16; i++) { + // we use the fact that there is no overflow for unsigned integers + // (from C++ standard) i.e., arithmetic mod 2^32 is performed. For + // the seed to be random, it is sufficient for one of the two + // samples below to be random. In almost all practical cases, + // distribution(genR) is random. We add distribution(gen) just in + // case there is an implementation issue with random_device (as in + // older MinGW systems). + rdseed[i] = distribution(genR); + } + rdGenPassed = true; + } + catch (std::exception& e) { + } + idx++; + } + + for (uint32_t i = 0; i < 16; i++) { + seed[i] += rdseed[i]; + } + + m_prng = std::make_shared(seed); +#endif } - // dlclose(handle); - destructor will call it - OPENFHE_THROW(errMsg); - } - // create the engine instance - engine = func(); - if (!engine) { - // dlclose(handle); - destructor will call it - OPENFHE_THROW("Cannot create a PRNG engine"); } - singletonHandle = handle; // store handle for dlclose + return *m_prng; } - static std::unique_ptr instance; // Pointer to the instance - static std::once_flag initInstanceFlag; // Flag for thread-safe initialization - static PRNG* engine; // Pointer to the engine - static void* singletonHandle; // Handle for the shared library +private: + // shared pointer to a thread-specific PRNG engine + static std::shared_ptr m_prng; + +#if !defined(FIXED_SEED) + // avoid contention on m_prng + // local copies of m_prng are created for each thread + #pragma omp threadprivate(m_prng) +#endif }; -//================================================================== -// class PseudoRandomNumberGenerator { -// public: -// static PRNG& GetPRNG() { -// // initialization of PRNGs -// if (m_prng == nullptr) { -// #pragma omp critical -// { -// std::array initKey{}; -// initKey[0] = std::chrono::high_resolution_clock::now().time_since_epoch().count(); -// initKey[1] = std::hash{}(std::this_thread::get_id()); -// #if !defined(__arm__) && !defined(__EMSCRIPTEN__) -// if (sizeof(size_t) == 8) -// initKey[2] = (std::hash{}(std::this_thread::get_id()) >> 32); -// #endif -// void* mem = malloc(1); -// uint32_t counter = reinterpret_cast(mem); // NOLINT -// free(mem); - -// PRNG gen(initKey, counter); - -// std::uniform_int_distribution distribution(0); -// std::array seed{}; -// for (uint32_t i = 0; i < 16; i++) { -// seed[i] = distribution(gen); -// } - -// std::array rdseed{}; -// size_t attempts = 3; -// bool rdGenPassed = false; -// size_t idx = 0; -// while (!rdGenPassed && idx < attempts) { -// try { -// std::random_device genR; -// for (uint32_t i = 0; i < 16; i++) { -// rdseed[i] = distribution(genR); -// } -// rdGenPassed = true; -// } -// catch (std::exception& e) { -// } -// idx++; -// } - -// for (uint32_t i = 0; i < 16; i++) { -// seed[i] += rdseed[i]; -// } - -// m_prng = std::make_shared(seed); -// } -// } -// return *m_prng; -// } - -// private: -// // shared pointer to a thread-specific PRNG engine -// static std::shared_ptr m_prng; - -// // avoid contention on m_prng -// // local copies of m_prng are created for each thread -// #pragma omp threadprivate(m_prng) -// }; - } // namespace lbcrypto #endif // LBCRYPTO_INC_MATH_DISTRIBUTIONGENERATOR_H_ diff --git a/prng_libs/blake2_lib/include/CPPLINT.cfg b/src/core/include/utils/prng/CPPLINT.cfg similarity index 100% rename from prng_libs/blake2_lib/include/CPPLINT.cfg rename to src/core/include/utils/prng/CPPLINT.cfg diff --git a/prng_libs/blake2_lib/include/README.md b/src/core/include/utils/prng/README.md similarity index 100% rename from prng_libs/blake2_lib/include/README.md rename to src/core/include/utils/prng/README.md diff --git a/prng_libs/blake2_lib/include/blake2-impl.h b/src/core/include/utils/prng/blake2-impl.h similarity index 100% rename from prng_libs/blake2_lib/include/blake2-impl.h rename to src/core/include/utils/prng/blake2-impl.h diff --git a/prng_libs/blake2_lib/include/blake2.h b/src/core/include/utils/prng/blake2.h similarity index 100% rename from prng_libs/blake2_lib/include/blake2.h rename to src/core/include/utils/prng/blake2.h diff --git a/prng_libs/blake2_lib/include/blake2engine.h b/src/core/include/utils/prng/blake2engine.h similarity index 53% rename from prng_libs/blake2_lib/include/blake2engine.h rename to src/core/include/utils/prng/blake2engine.h index ae0d41386..48ff40133 100644 --- a/prng_libs/blake2_lib/include/blake2engine.h +++ b/src/core/include/utils/prng/blake2engine.h @@ -37,18 +37,17 @@ #ifndef _SRC_LIB_UTILS_BLAKE2ENGINE_H #define _SRC_LIB_UTILS_BLAKE2ENGINE_H -#include "prng.h" -#include "blake2.h" - -#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include +#include "blake2.h" + +#include "utils/exception.h" + +namespace lbcrypto { // the buffer stores 1024 samples of 32-bit integers const uint32_t PRNG_BUFFER_SIZE = 1024; @@ -57,38 +56,56 @@ const uint32_t PRNG_BUFFER_SIZE = 1024; * @brief Defines the PRNG engine used by OpenFHE. It is based on BLAKE2. Use * this as a template for adding other PRNG engines to OpenFHE. */ -class Blake2Engine : public PRNG { +class Blake2Engine { public: - enum {MAX_SEED_GENS = 16}; + // all C++11 distributions used in OpenFHE work by default with uint32_t + // a different data type can be specified if needed for a particular + // architecture + using result_type = uint32_t; /** * @brief Constructor using a small seed - used for generating a large seed */ - explicit Blake2Engine(PRNG::result_type seed) - : PRNG(seed), m_counter(0), m_buffer({}), m_bufferIndex(0) { + explicit Blake2Engine(result_type seed) + : m_counter(0), m_buffer({}), m_bufferIndex(0) { m_seed[0] = seed; } - // TODO (dsuponit): commented the constructor below and added a default paramter value to the next contructor - // /** - // * @brief Main constructor taking a vector of MAX_SEED_GENS integers as a seed - // */ - // explicit Blake2Engine(const std::array& seed) - // : m_counter(0), m_seed(seed), m_buffer({}), m_bufferIndex(0) {} + /** + * @brief Main constructor taking a vector of 16 integers as a seed + */ + explicit Blake2Engine(const std::array& seed) + : m_counter(0), m_seed(seed), m_buffer({}), m_bufferIndex(0) {} /** - * @brief Main constructor taking a vector of MAX_SEED_GENS integers as a seed and a + * @brief Main constructor taking a vector of 16 integers as a seed and a * counter */ - explicit Blake2Engine(const std::array& seed, - PRNG::result_type counter = 0) + explicit Blake2Engine(const std::array& seed, + result_type counter) : m_counter(counter), m_seed(seed), m_buffer({}), m_bufferIndex(0) {} + /** + * @brief minimum value used by C+11 distribution generators when no lower + * bound is explicitly specified by the user + */ + static constexpr result_type min() { + return std::numeric_limits::min(); + } + + /** + * @brief maximum value used by C+11 distribution generators when no upper + * bound is explicitly specified by the user + */ + static constexpr result_type max() { + return std::numeric_limits::max(); + } + /** * @brief main call to the PRNG */ - PRNG::result_type operator()() override { - PRNG::result_type result; + result_type operator()() { + result_type result; if (m_bufferIndex == PRNG_BUFFER_SIZE) m_bufferIndex = 0; @@ -125,10 +142,10 @@ class Blake2Engine : public PRNG { void Generate() { // m_counter is the input to the hash function // m_buffer is the output - if (blake2xb(m_buffer.begin(), m_buffer.size() * sizeof(PRNG::result_type), + if (blake2xb(m_buffer.begin(), m_buffer.size() * sizeof(result_type), &m_counter, sizeof(m_counter), m_seed.cbegin(), - m_seed.size() * sizeof(PRNG::result_type)) != 0) { - throw std::runtime_error("PRNG: blake2xb failed"); + m_seed.size() * sizeof(result_type)) != 0) { + OPENFHE_THROW("PRNG: blake2xb failed"); } m_counter++; return; @@ -139,64 +156,16 @@ class Blake2Engine : public PRNG { uint64_t m_counter = 0; // the seed for the BLAKE2 hash function - std::array m_seed{}; + std::array m_seed{}; // The vector that stores random samples generated using the hash function - std::array m_buffer{}; + std::array m_buffer{}; // Index in m_buffer corresponding to the current PRNG sample uint16_t m_bufferIndex = 0; }; -// the code calling createEngineInstance() should clean the memory allocated by createEngineInstance() -// TODO (dsuponit): check with Jack if createEngineInstance() can return an object instead of a pointer. We can do it -// for Blake2Engine -extern "C" { - Blake2Engine* createEngineInstance(); -} -// extern "C" Blake2Engine* createEngineInstance() { -// // initialization of PRNGs -// constexpr size_t maxGens = Blake2Engine::MAX_SEED_GENS; -// #pragma omp critical -// std::array initKey{}; -// initKey[0] = std::chrono::high_resolution_clock::now().time_since_epoch().count(); -// initKey[1] = std::hash{}(std::this_thread::get_id()); -// #if !defined(__arm__) && !defined(__EMSCRIPTEN__) -// if (sizeof(size_t) == 8) -// initKey[2] = (std::hash{}(std::this_thread::get_id()) >> 32); -// #endif -// void* mem = malloc(1); -// uint32_t counter = reinterpret_cast(mem); // NOLINT -// free(mem); - -// Blake2Engine gen(initKey, counter); - -// std::uniform_int_distribution distribution(0); -// std::array seed{}; -// for (uint32_t i = 0; i < maxGens; i++) { -// seed[i] = distribution(gen); -// } - -// std::array rdseed{}; -// size_t attempts = 3; -// bool rdGenPassed = false; -// for(size_t i = 0; i < attempts && !rdGenPassed; ++i) { -// try { -// std::random_device genR; -// for (uint32_t i = 0; i < maxGens; i++) { -// rdseed[i] = distribution(genR); -// } -// rdGenPassed = true; -// } -// catch (std::exception& e) { -// } -// } -// for (uint32_t i = 0; i < maxGens; i++) { -// seed[i] += rdseed[i]; -// } - -// return new Blake2Engine(seed); -// } +} // namespace lbcrypto #endif // clang-format on diff --git a/src/core/lib/math/distributiongenerator.cpp b/src/core/lib/math/distributiongenerator.cpp index edbac17fe..995a412a7 100644 --- a/src/core/lib/math/distributiongenerator.cpp +++ b/src/core/lib/math/distributiongenerator.cpp @@ -41,11 +41,6 @@ namespace lbcrypto { - -// Static members initialization -std::unique_ptr PseudoRandomNumberGenerator::instance = nullptr; -std::once_flag PseudoRandomNumberGenerator::initInstanceFlag; -PRNG* PseudoRandomNumberGenerator::engine = nullptr; -void* PseudoRandomNumberGenerator::singletonHandle = nullptr; +std::shared_ptr PseudoRandomNumberGenerator::m_prng = nullptr; } // namespace lbcrypto diff --git a/prng_libs/blake2_lib/lib/CPPLINT.cfg b/src/core/lib/utils/prng/CPPLINT.cfg similarity index 100% rename from prng_libs/blake2_lib/lib/CPPLINT.cfg rename to src/core/lib/utils/prng/CPPLINT.cfg diff --git a/prng_libs/blake2_lib/lib/blake2b-ref.c b/src/core/lib/utils/prng/blake2b-ref.c similarity index 99% rename from prng_libs/blake2_lib/lib/blake2b-ref.c rename to src/core/lib/utils/prng/blake2b-ref.c index 2f0787207..0e802c1bd 100644 --- a/prng_libs/blake2_lib/lib/blake2b-ref.c +++ b/src/core/lib/utils/prng/blake2b-ref.c @@ -18,8 +18,8 @@ #include #include -#include "blake2.h" -#include "blake2-impl.h" +#include "utils/prng/blake2.h" +#include "utils/prng/blake2-impl.h" static const uint64_t blake2b_IV[8] = { 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, diff --git a/prng_libs/blake2_lib/lib/blake2xb-ref.c b/src/core/lib/utils/prng/blake2xb-ref.c similarity index 98% rename from prng_libs/blake2_lib/lib/blake2xb-ref.c rename to src/core/lib/utils/prng/blake2xb-ref.c index 761add3a0..16d476a28 100644 --- a/prng_libs/blake2_lib/lib/blake2xb-ref.c +++ b/src/core/lib/utils/prng/blake2xb-ref.c @@ -21,8 +21,8 @@ #include #include -#include "blake2.h" -#include "blake2-impl.h" +#include "utils/prng/blake2.h" +#include "utils/prng/blake2-impl.h" int blake2xb_init(blake2xb_state *S, const size_t outlen) { return blake2xb_init_key(S, outlen, NULL, 0);