From 62a8f137e572973a5c6e2c0232937c7d6fa5b5c3 Mon Sep 17 00:00:00 2001 From: Kim Laine Date: Fri, 16 Oct 2020 00:15:19 -0700 Subject: [PATCH] Support for Shake256 extendable output function --- CHANGES.md | 5 + CMakeLists.txt | 13 +- NOTICE | 5 +- dotnet/examples/6_Serialization.cs | 36 +- native/examples/6_serialization.cpp | 28 +- native/src/seal/ciphertext.cpp | 40 +- native/src/seal/ciphertext.h | 2 +- native/src/seal/encryptionparams.h | 6 +- native/src/seal/encryptor.h | 2 - native/src/seal/modulus.h | 4 +- native/src/seal/randomgen.cpp | 105 ++- native/src/seal/randomgen.h | 409 ++++++++++-- native/src/seal/serialization.h | 10 +- native/src/seal/util/CMakeLists.txt | 2 + native/src/seal/util/blake2.h | 8 +- native/src/seal/util/blake2xb.c | 16 +- native/src/seal/util/cgmanifest.json | 9 + native/src/seal/util/common.cpp | 2 +- native/src/seal/util/common.h | 2 +- native/src/seal/util/config.h.in | 2 +- native/src/seal/util/defines.h | 12 +- native/src/seal/util/fips202.c | 610 ++++++++++++++++++ native/src/seal/util/fips202.h | 18 + native/src/seal/util/hash.h | 5 +- native/src/seal/util/rlwe.cpp | 81 +-- native/tests/seal/CMakeLists.txt | 2 +- .../tests/seal/{intarray.cpp => dynarray.cpp} | 0 native/tests/seal/galoiskeys.cpp | 8 +- native/tests/seal/randomgen.cpp | 102 ++- native/tests/seal/relinkeys.cpp | 8 +- 30 files changed, 1361 insertions(+), 191 deletions(-) create mode 100644 native/src/seal/util/fips202.c create mode 100644 native/src/seal/util/fips202.h rename native/tests/seal/{intarray.cpp => dynarray.cpp} (100%) diff --git a/CHANGES.md b/CHANGES.md index 0348d8f15..ee41570ff 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,8 @@ - Added support for [Zstandard](https://github.com/facebook/zstd) compression as a much more efficient alternative to ZLIB. The performance improvement should be expected to be around 20-30x. +- Added support for Shake256 extendable output function for pseudo-random number generation. +The user can choose to set the default to Blake2xb (faster) or Shake256, when configuring the build system. ### API Changes @@ -23,6 +25,9 @@ The `BigUInt` class was only used by the `IntegerEncoder`. These were poorly named and have been replaced with overloads of `KeyGenerator::create_relin_keys` and `KeyGenerator::create_galois_keys` that take an out-parameter of type `RelinKeys` or `GaloisKeys`. - Renamed `IntArray` to `DynArray` (dynamic array) and removed unnecessary limitations on the object type template parameter. - Added const overloads for `DynArray::begin` and `DynArray::end`. +- Added a `Shake256PRNG` and `Shake256PRNGFactory` classes. +Renamed `BlakePRNG` class to `Blake2xbPRNG`, and `BlakePRNGFactory` class to `Blake2xbPRNGFactory`. +- Added a serializable `UniformRandomGeneratorInfo` class that represents the type of an extendable output function and a seed value. ### Other diff --git a/CMakeLists.txt b/CMakeLists.txt index 680c80ba1..2bd75045b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -432,16 +432,15 @@ if(MSVC AND BUILD_SHARED_LIBS) endif() # [option] SEAL_THROW_ON_TRANSPARENT_CIPHERTEXT (default: ON) -# Throw an exception if the second polynomial of a ciphertext is zero. -set(SEAL_THROW_ON_TRANSPARENT_CIPHERTEXT_STR "Throw an exception when a member of Evaluator outputs a transparent ciphertext") +set(SEAL_THROW_ON_TRANSPARENT_CIPHERTEXT_STR "Throw an exception when Evaluator outputs a transparent ciphertext") option(SEAL_THROW_ON_TRANSPARENT_CIPHERTEXT ${SEAL_THROW_ON_TRANSPARENT_CIPHERTEXT_STR} ON) mark_as_advanced(FORCE SEAL_THROW_ON_TRANSPARENT_CIPHERTEXT) -# [option] SEAL_USE_GAUSSIAN (default: OFF) -# Use Gaussian distribution for error if set to ON, use centered binomial otherwise. -set(SEAL_USE_GAUSSIAN_STR "Use Gaussian for error distribution instead of centerd binomial") -option(SEAL_USE_GAUSSIAN ${SEAL_USE_GAUSSIAN_STR} OFF) -mark_as_advanced(FORCE SEAL_USE_GAUSSIAN) +# [option] SEAL_USE_GAUSSIAN_NOISE (default: OFF) +# Use Gaussian distribution for noise sampling if set to ON, use centered binomial otherwise. +set(SEAL_USE_GAUSSIAN_NOISE_STR "Use a rounded Gaussian distribution for noise sampling instead of a Centered Binomial Distribution") +option(SEAL_USE_GAUSSIAN_NOISE ${SEAL_USE_GAUSSIAN_NOISE_STR} OFF) +mark_as_advanced(FORCE SEAL_USE_GAUSSIAN_NOISE) # [option] SEAL_USE_INTRIN (default: ON) set(SEAL_USE_INTRIN_OPTION_STR "Use intrinsics") diff --git a/NOTICE b/NOTICE index 6ace895c3..37f24ffdc 100644 --- a/NOTICE +++ b/NOTICE @@ -1,2 +1,5 @@ -This software uses the BLAKE2 library (https://github.com/BLAKE2/BLAKE2) +This software includes parts of the BLAKE2 library (https://github.com/BLAKE2/BLAKE2). The BLAKE2 library is licensed under CC0 Universal, version 1.0. You can find a copy of this license at https://creativecommons.org/publicdomain/zero/1.0/legalcode + +This software includes parts of the Kyber library (https://github.com/pq-crystals/kyber). +The Kyber library is licensed under CC0 Universal, version 1.0. You can find a copy of this license at https://creativecommons.org/publicdomain/zero/1.0/legalcode diff --git a/dotnet/examples/6_Serialization.cs b/dotnet/examples/6_Serialization.cs index ebbcff8e7..8acd8a1cd 100644 --- a/dotnet/examples/6_Serialization.cs +++ b/dotnet/examples/6_Serialization.cs @@ -81,10 +81,6 @@ that output Serializable objects also have overloads that take a normal can be convenient for local testing where no serialization is needed and the object needs to be used at the point of construction. Such an object can no longer be transformed back to a seeded state. - - Due to the internal representation of Microsoft SEAL objects, the size saving - from using seeded objects only has an effect when a compressed serialization - method is used, as we will demonstrate in the example below. */ /* @@ -146,15 +142,27 @@ providing EncryptionParameters.Save with the desired compression mode as long size = parms.Save(sharedStream, ComprModeType.ZSTD); If Microsoft SEAL is compiled with Zstandard or ZLIB support, the default - is to use one of them. If available, Zstandard is preferred over ZLIB. + is to use one of them. If available, Zstandard is preferred over ZLIB due + to its speed. + + Compression can have a substantial impact on the serialized data size, + because ciphertext and key data consists of many uniformly random integers + modulo the CoeffModulus primes. Especially when using CKKS, the primes in + CoeffModulus can be relatively small compared to the 64-bit words used to + store the ciphertext and key data internally. Serialization writes full + 64-bit words to the destination buffer or stream, possibly leaving in many + zero bytes corresponding to the high-order bytes of the 64-bit words. One + convenient way to get rid of these zeros is to apply a general-purpose + compression algorithm on the encrypted data. The compression rate can be + significant (up to 50-60%) when using CKKS with small primes. */ /* - In many cases, when working with fixed size memory, it is necessary - to know ahead of time an upper bound on the serialized data size to - allocate enough memory. This information is returned by the - EncryptionParameters.SaveSize function. This function accepts the - desired compression mode, or uses the default option otherwise. + In many cases, when working with fixed size memory, it is necessary to know + ahead of time an upper bound on the serialized data size to allocate enough + memory. This information is returned by the EncryptionParameters.SaveSize + function. This function accepts the desired compression mode, or uses the + default option otherwise. In more detail, the output of EncryptionParameters.SaveSize is as follows: @@ -246,7 +254,9 @@ absolutely want to create as seeded objects to minimize communication /* We serialize both relinearization keys to demonstrate the concrete size - difference. + difference. If compressed serialization is used, the compression rate + will be the same in both cases. We omit specifying the compression mode + to use the default, as determined by the Microsoft SEAL build system. */ long sizeRlk = rlk.Save(dataStream); long sizeRlkBig = rlkBig.Save(dataStream); @@ -318,10 +328,6 @@ We would also like to draw attention to the fact there we could easily serialize multiple Microsoft SEAL objects sequentially in a stream. Each object writes its own size into the stream, so deserialization knows exactly how many bytes to read. We will see this working below. - - Finally, we would like to emphasize the point that none of these methods - provide any space savings when ComprModeType.None is used, e.g., when - Microsoft SEAL is not compiled with support for ZLIB or Zstandard. */ } diff --git a/native/examples/6_serialization.cpp b/native/examples/6_serialization.cpp index 3dac4062c..2016a4551 100644 --- a/native/examples/6_serialization.cpp +++ b/native/examples/6_serialization.cpp @@ -74,10 +74,6 @@ void example_serialization() can be convenient for local testing where no serialization is needed and the object needs to be used at the point of construction. Such an object can no longer be transformed back to a seeded state. - - Due to the internal representation of Microsoft SEAL objects, the size saving - from using seeded objects only has an effect when a compressed serialization - method is used, as we will demonstrate in the example below. */ /* @@ -142,7 +138,19 @@ void example_serialization() auto size = parms.save(shared_stream, compr_mode_type::zstd); If Microsoft SEAL is compiled with Zstandard or ZLIB support, the default - is to use one of them. If available, Zstandard is preferred over ZLIB. + is to use one of them. If available, Zstandard is preferred over ZLIB due + to its speed. + + Compression can have a substantial impact on the serialized data size, + because ciphertext and key data consists of many uniformly random integers + modulo the coeff_modulus primes. Especially when using CKKS, the primes in + coeff_modulus can be relatively small compared to the 64-bit words used to + store the ciphertext and key data internally. Serialization writes full + 64-bit words to the destination buffer or stream, possibly leaving in many + zero bytes corresponding to the high-order bytes of the 64-bit words. One + convenient way to get rid of these zeros is to apply a general-purpose + compression algorithm on the encrypted data. The compression rate can be + significant (up to 50-60%) when using CKKS with small primes. */ /* @@ -252,7 +260,9 @@ void example_serialization() /* We serialize both relinearization keys to demonstrate the concrete size - difference. + difference. If compressed serialization is used, the compression rate + will be the same in both cases. We omit specifying the compression mode + to use the default, as determined by the Microsoft SEAL build system. */ auto size_rlk = rlk.save(data_stream); auto size_rlk_big = rlk_big.save(data_stream); @@ -283,7 +293,7 @@ void example_serialization() The client will not compute on ciphertexts that it creates, so it can just as well create Serializable objects. In fact, we do not even need to name those objects and instead immediately call - Serializable.save. + Serializable::save. */ auto size_encrypted1 = encryptor.encrypt(plain1).save(data_stream); @@ -317,10 +327,6 @@ void example_serialization() serialize multiple Microsoft SEAL objects sequentially in a stream. Each object writes its own size into the stream, so deserialization knows exactly how many bytes to read. We will see this working below. - - Finally, we would like to emphasize the point that none of these methods - provide any space savings when compr_mode_type::none is used, e.g., when - Microsoft SEAL is not compiled with support for ZLIB or Zstandard. */ } diff --git a/native/src/seal/ciphertext.cpp b/native/src/seal/ciphertext.cpp index 84836c5cb..a7d1caf85 100644 --- a/native/src/seal/ciphertext.cpp +++ b/native/src/seal/ciphertext.cpp @@ -2,7 +2,6 @@ // Licensed under the MIT license. #include "seal/ciphertext.h" -#include "seal/randomgen.h" #include "seal/util/defines.h" #include "seal/util/pointer.h" #include "seal/util/polyarithsmallmod.h" @@ -115,13 +114,17 @@ namespace seal coeff_modulus_size_ = coeff_modulus_size; } - void Ciphertext::expand_seed(const SEALContext &context, const random_seed_type &seed) + void Ciphertext::expand_seed(const SEALContext &context, const UniformRandomGeneratorInfo &prng_info) { auto context_data_ptr = context.get_context_data(parms_id_); - // Set up the BlakePRNG with a given seed. - // Rejection sampling to generate a uniform random polynomial. - sample_poly_uniform(make_shared(seed), context_data_ptr->parms(), data(1)); + // Set up a PRNG from the given info and sample the second polynomial + auto prng = prng_info.make_prng(); + if (!prng) + { + throw logic_error("unsupported prng_type"); + } + sample_poly_uniform(prng, context_data_ptr->parms(), data(1)); } streamoff Ciphertext::save_size(compr_mode_type compr_mode) const @@ -138,8 +141,8 @@ namespace seal data_.pool()); data_size = add_safe( - safe_cast(alias_data.save_size(compr_mode_type::none)), // data_(0) - sizeof(random_seed_type)); // seed + safe_cast(alias_data.save_size(compr_mode_type::none)), // data_(0) + static_cast(UniformRandomGeneratorInfo::SaveSize(compr_mode_type::none))); // seed } else { @@ -180,8 +183,9 @@ namespace seal if (has_seed_marker()) { - random_seed_type seed; - copy_n(data(1) + 1, seed.size(), seed.begin()); + UniformRandomGeneratorInfo info; + size_t info_size = static_cast(UniformRandomGeneratorInfo::SaveSize(compr_mode_type::none)); + info.load(reinterpret_cast(data(1) + 1), info_size); size_t data_size = data_.size(); size_t half_size = data_size / 2; @@ -195,8 +199,8 @@ namespace seal swap(alias_data.data_, alias_ptr); alias_data.save(stream, compr_mode_type::none); - // Save the seed - stream.write(reinterpret_cast(&seed), sizeof(random_seed_type)); + // Save the UniformRandomGeneratorInfo + info.save(stream, compr_mode_type::none); } else { @@ -285,15 +289,17 @@ namespace seal auto seeded_uint64_count = poly_modulus_degree64 * coeff_modulus_size64; // This is the case where we need to expand a seed, otherwise full - // ciphertext data was (possibly) loaded and do nothing + // ciphertext data was already (possibly) loaded and we are done if (unsigned_eq(new_data.data_.size(), seeded_uint64_count)) { - // Single polynomial size data was loaded, so we are in the - // seeded ciphertext case. Next load the seed. - random_seed_type seed; - stream.read(reinterpret_cast(&seed), sizeof(random_seed_type)); + // Single polynomial size data was loaded, so we are in the seeded + // ciphertext case. Next load the UniformRandomGeneratorInfo. + UniformRandomGeneratorInfo prng_info; + prng_info.load(stream); + + // Set up a UniformRandomGenerator and expand new_data.data_.resize(total_uint64_count); - new_data.expand_seed(context, seed); + new_data.expand_seed(context, prng_info); } // Verify that the buffer is correct diff --git a/native/src/seal/ciphertext.h b/native/src/seal/ciphertext.h index d9d106186..4b73d42a7 100644 --- a/native/src/seal/ciphertext.h +++ b/native/src/seal/ciphertext.h @@ -668,7 +668,7 @@ namespace seal void resize_internal(std::size_t size, std::size_t poly_modulus_degree, std::size_t coeff_modulus_size); - void expand_seed(const SEALContext &context, const random_seed_type &seed); + void expand_seed(const SEALContext &context, const UniformRandomGeneratorInfo &prng_info); void save_members(std::ostream &stream) const; diff --git a/native/src/seal/encryptionparams.h b/native/src/seal/encryptionparams.h index d027ab0ee..b19a294b2 100644 --- a/native/src/seal/encryptionparams.h +++ b/native/src/seal/encryptionparams.h @@ -386,8 +386,7 @@ namespace seal @param[in] stream The stream to load the EncryptionParameters from @throws std::logic_error if the data cannot be loaded by this version of - Microsoft SEAL, if the loaded data is invalid, if decompression failed, - or if the loaded size exceeds in_size_bound + Microsoft SEAL, if the loaded data is invalid or if decompression failed @throws std::runtime_error if I/O operations failed */ inline std::streamoff load(std::istream &stream) @@ -430,8 +429,7 @@ namespace seal @throws std::invalid_argument if in is null or if size is too small to contain a SEALHeader @throws std::logic_error if the data cannot be loaded by this version of - Microsoft SEAL, if the loaded data is invalid, if decompression failed, - or if the loaded size exceeds in_size_bound + Microsoft SEAL, if the loaded data is invalid, or if decompression failed @throws std::runtime_error if I/O operations failed */ inline std::streamoff load(const seal_byte *in, std::size_t size) diff --git a/native/src/seal/encryptor.h b/native/src/seal/encryptor.h index eb61040e7..589ccedd3 100644 --- a/native/src/seal/encryptor.h +++ b/native/src/seal/encryptor.h @@ -415,8 +415,6 @@ namespace seal Encryptor &operator=(Encryptor &&assign) = delete; - MemoryPoolHandle pool_ = MemoryManager::GetPool(mm_prof_opt::FORCE_NEW, true); - void encrypt_zero_internal( parms_id_type parms_id, bool is_asymmetric, bool save_seed, Ciphertext &destination, MemoryPoolHandle pool = MemoryManager::GetPool()) const; diff --git a/native/src/seal/modulus.h b/native/src/seal/modulus.h index 172978ba7..ad464df91 100644 --- a/native/src/seal/modulus.h +++ b/native/src/seal/modulus.h @@ -169,7 +169,7 @@ namespace seal */ SEAL_NODISCARD inline bool operator!=(const Modulus &compare) const noexcept { - return !(value_ == compare.value_); + return !operator==(compare); } /** @@ -179,7 +179,7 @@ namespace seal */ SEAL_NODISCARD inline bool operator!=(std::uint64_t compare) const noexcept { - return value_ != compare; + return !operator==(compare); } /** diff --git a/native/src/seal/randomgen.cpp b/native/src/seal/randomgen.cpp index 6b2b0f796..2543b44b5 100644 --- a/native/src/seal/randomgen.cpp +++ b/native/src/seal/randomgen.cpp @@ -3,6 +3,7 @@ #include "seal/randomgen.h" #include "seal/util/blake2.h" +#include "seal/util/fips202.h" #include #include #include @@ -12,6 +13,7 @@ #endif using namespace std; +using namespace seal::util; #if (SEAL_SYSTEM == SEAL_SYSTEM_WINDOWS) @@ -55,7 +57,7 @@ namespace seal BOOLEAN genrand_result = FALSE; if (RtlGenRandom) { - genrand_result = RtlGenRandom(&result, sizeof(uint64_t)); + genrand_result = RtlGenRandom(&result, bytes_per_uint64); } DWORD dwError = GetLastError(); @@ -66,7 +68,6 @@ namespace seal last_genrandom_error = dwError; throw runtime_error("Failed to call RtlGenRandom"); } - #elif SEAL_SYSTEM == SEAL_SYSTEM_OTHER #warning "SECURITY WARNING: System detection failed; falling back to a potentially insecure randomness source!" random_device rd; @@ -75,6 +76,83 @@ namespace seal return result; } + void UniformRandomGeneratorInfo::save_members(ostream &stream) const + { + // Throw exceptions on std::ios_base::badbit and std::ios_base::failbit + auto old_except_mask = stream.exceptions(); + try + { + stream.exceptions(ios_base::badbit | ios_base::failbit); + + stream.write(reinterpret_cast(&type_), sizeof(prng_type)); + stream.write(reinterpret_cast(seed_.data()), prng_seed_byte_count); + } + catch (const ios_base::failure &) + { + stream.exceptions(old_except_mask); + throw runtime_error("I/O error"); + } + catch (...) + { + stream.exceptions(old_except_mask); + throw; + } + stream.exceptions(old_except_mask); + } + + void UniformRandomGeneratorInfo::load_members(istream &stream) + { + // Throw exceptions on std::ios_base::badbit and std::ios_base::failbit + auto old_except_mask = stream.exceptions(); + try + { + stream.exceptions(ios_base::badbit | ios_base::failbit); + + UniformRandomGeneratorInfo info; + + // Read the PRNG type + stream.read(reinterpret_cast(&info.type_), sizeof(prng_type)); + if (!info.has_valid_prng_type()) + { + throw logic_error("prng_type is invalid"); + } + + // Read the seed data + stream.read(reinterpret_cast(info.seed_.data()), prng_seed_byte_count); + + swap(*this, info); + + stream.exceptions(old_except_mask); + } + catch (const ios_base::failure &) + { + stream.exceptions(old_except_mask); + throw runtime_error("I/O error"); + } + catch (...) + { + stream.exceptions(old_except_mask); + throw; + } + stream.exceptions(old_except_mask); + } + + shared_ptr UniformRandomGeneratorInfo::make_prng() const + { + switch (type_) + { + case prng_type::blake2xb: + return make_shared(seed_); + + case prng_type::shake256: + return make_shared(seed_); + + case prng_type::unknown: + return nullptr; + } + return nullptr; + } + void UniformRandomGenerator::generate(size_t byte_count, seal_byte *destination) { lock_guard lock(mutex_); @@ -94,21 +172,34 @@ namespace seal } } - auto UniformRandomGeneratorFactory::DefaultFactory() -> const shared_ptr + auto UniformRandomGeneratorFactory::DefaultFactory() -> shared_ptr { - static const shared_ptr default_factory{ new SEAL_DEFAULT_RNG_FACTORY }; + static shared_ptr default_factory{ new SEAL_DEFAULT_PRNG_FACTORY() }; return default_factory; } - void BlakePRNG::refill_buffer() + void Blake2xbPRNG::refill_buffer() { // Fill the randomness buffer if (blake2xb( - buffer_begin_, buffer_size_, reinterpret_cast(&counter_), sizeof(counter_), - seed_.cbegin(), seed_.size() * sizeof(decltype(seed_)::type)) != 0) + buffer_begin_, buffer_size_, &counter_, sizeof(counter_), seed_.cbegin(), + seed_.size() * sizeof(decltype(seed_)::type)) != 0) { throw runtime_error("blake2xb failed"); } counter_++; } + + void Shake256PRNG::refill_buffer() + { + // Fill the randomness buffer + array seed_ext; + copy_n(seed_.cbegin(), prng_seed_uint64_count, seed_ext.begin()); + seed_ext[prng_seed_uint64_count] = counter_; + shake256( + reinterpret_cast(buffer_begin_), buffer_size_, + reinterpret_cast(seed_ext.data()), seed_ext.size() * bytes_per_uint64); + seal_memzero(seed_ext.data(), seed_ext.size() * bytes_per_uint64); + counter_++; + } } // namespace seal diff --git a/native/src/seal/randomgen.h b/native/src/seal/randomgen.h index 1172a14cb..48e5fdff8 100644 --- a/native/src/seal/randomgen.h +++ b/native/src/seal/randomgen.h @@ -5,6 +5,7 @@ #include "seal/dynarray.h" #include "seal/memorymanager.h" +#include "seal/util/common.h" #include "seal/util/defines.h" #include #include @@ -15,21 +16,263 @@ namespace seal { - using random_seed_type = std::array; + inline constexpr std::size_t prng_seed_uint64_count = 8; + + inline constexpr std::size_t prng_seed_byte_count = prng_seed_uint64_count * util::bytes_per_uint64; + + using prng_seed_type = std::array; + + /** + A type indicating a specific pseud-random number generator. + */ + enum class prng_type : std::uint8_t + { + unknown = 0, + + blake2xb = 1, + + shake256 = 2 + }; /** Returns a random 64-bit integer. */ SEAL_NODISCARD std::uint64_t random_uint64(); + class UniformRandomGenerator; + + class UniformRandomGeneratorInfo + { + friend class UniformRandomGenerator; + + public: + /** + Creates a new UniformRandomGeneratorInfo. + */ + UniformRandomGeneratorInfo() = default; + + /** + Creates a new UniformRandomGeneratorInfo by copying a given one. + + @param[in] copy The UniformRandomGeneratorInfo to copy from + */ + UniformRandomGeneratorInfo(const UniformRandomGeneratorInfo ©) = default; + + /** + Copies a given UniformRandomGeneratorInfo to the current one. + + @param[in] assign The UniformRandomGeneratorInfo to copy from + */ + UniformRandomGeneratorInfo &operator=(const UniformRandomGeneratorInfo &assign) = default; + + /** + Compares two UniformRandomGeneratorInfo instances. + + @param[in] compare The UniformRandomGeneratorInfo to compare against + */ + SEAL_NODISCARD inline bool operator==(const UniformRandomGeneratorInfo &compare) const noexcept + { + return (seed_ == compare.seed_) && (type_ == compare.type_); + } + + /** + Compares two UniformRandomGeneratorInfo instances. + + @param[in] compare The UniformRandomGeneratorInfo to compare against + */ + SEAL_NODISCARD inline bool operator!=(const UniformRandomGeneratorInfo &compare) const noexcept + { + return !operator==(compare); + } + + /** + Clears all data in the UniformRandomGeneratorInfo. + */ + void clear() noexcept + { + type_ = prng_type::unknown; + util::seal_memzero(seed_.data(), prng_seed_byte_count); + } + + /** + Destroys the UniformRandomGeneratorInfo. + */ + ~UniformRandomGeneratorInfo() + { + clear(); + } + + /** + Creates a new UniformRandomGenerator object of type indicated by the PRNG + type and seeded with the current seed. If the current PRNG type is not + an official Microsoft SEAL PRNG type, the return value is nullptr. + */ + std::shared_ptr make_prng() const; + + /** + Returns whether this object holds a valid PRNG type. + */ + SEAL_NODISCARD inline bool has_valid_prng_type() const noexcept + { + switch (type_) + { + case prng_type::blake2xb: + /* fall through */ + + case prng_type::shake256: + /* fall through */ + + case prng_type::unknown: + return true; + } + return false; + } + + /** + Returns the PRNG type. + */ + SEAL_NODISCARD inline prng_type type() const noexcept + { + return type_; + } + + /** + Returns a reference to the PRNG seed. + */ + SEAL_NODISCARD inline const prng_seed_type &seed() const noexcept + { + return seed_; + } + + /** + Returns an upper bound on the size of the UniformRandomGeneratorInfo, as + if it was written to an output stream. + + @param[in] compr_mode The compression mode + @throws std::invalid_argument if the compression mode is not supported + @throws std::logic_error if the size does not fit in the return type + */ + SEAL_NODISCARD static inline std::streamoff SaveSize( + compr_mode_type compr_mode = Serialization::compr_mode_default) + { + std::size_t members_size = + Serialization::ComprSizeEstimate(sizeof(prng_type) + prng_seed_byte_count, compr_mode); + return static_cast(sizeof(Serialization::SEALHeader) + members_size); + } + + /** + Returns an upper bound on the size of the UniformRandomGeneratorInfo, as + if it was written to an output stream. + + @param[in] compr_mode The compression mode + @throws std::invalid_argument if the compression mode is not supported + @throws std::logic_error if the size does not fit in the return type + */ + SEAL_NODISCARD inline std::streamoff save_size( + compr_mode_type compr_mode = Serialization::compr_mode_default) const + { + return UniformRandomGeneratorInfo::SaveSize(compr_mode); + } + + /** + Saves the UniformRandomGeneratorInfo to an output stream. The output is + in binary format and is not human-readable. The output stream must have + the "binary" flag set. + + @param[out] stream The stream to save the UniformRandomGeneratorInfo to + @param[in] compr_mode The desired compression mode + @throws std::invalid_argument if the compression mode is not supported + @throws std::logic_error if the data to be saved is invalid, or if + compression failed + @throws std::runtime_error if I/O operations failed + */ + inline std::streamoff save( + std::ostream &stream, compr_mode_type compr_mode = Serialization::compr_mode_default) const + { + using namespace std::placeholders; + return Serialization::Save( + std::bind(&UniformRandomGeneratorInfo::save_members, this, _1), save_size(compr_mode_type::none), + stream, compr_mode); + } + + /** + Loads a UniformRandomGeneratorInfo from an input stream overwriting the + current UniformRandomGeneratorInfo. + + @param[in] stream The stream to load the UniformRandomGeneratorInfo from + @throws std::logic_error if the data cannot be loaded by this version of + Microsoft SEAL, if the loaded data is invalid, or if decompression failed + @throws std::runtime_error if I/O operations failed + */ + inline std::streamoff load(std::istream &stream) + { + using namespace std::placeholders; + UniformRandomGeneratorInfo new_info; + auto in_size = + Serialization::Load(std::bind(&UniformRandomGeneratorInfo::load_members, &new_info, _1), stream); + std::swap(*this, new_info); + return in_size; + } + + /** + Saves the UniformRandomGeneratorInfo to a given memory location. The output + is in binary format and is not human-readable. + + @param[out] out The memory location to write the UniformRandomGeneratorInfo to + @param[in] size The number of bytes available in the given memory location + @param[in] compr_mode The desired compression mode + @throws std::invalid_argument if out is null or if size is too small to + contain a SEALHeader, or if the compression mode is not supported + @throws std::logic_error if the data to be saved is invalid, or if + compression failed + @throws std::runtime_error if I/O operations failed + */ + inline std::streamoff save( + seal_byte *out, std::size_t size, compr_mode_type compr_mode = Serialization::compr_mode_default) const + { + using namespace std::placeholders; + return Serialization::Save( + std::bind(&UniformRandomGeneratorInfo::save_members, this, _1), save_size(compr_mode_type::none), out, + size, compr_mode); + } + + /** + Loads a UniformRandomGeneratorInfo from a given memory location overwriting + the current UniformRandomGeneratorInfo. + + @param[in] in The memory location to load the UniformRandomGeneratorInfo from + @param[in] size The number of bytes available in the given memory location + @throws std::invalid_argument if in is null or if size is too small to + contain a SEALHeader + @throws std::logic_error if the data cannot be loaded by this version of + Microsoft SEAL, if the loaded data is invalid, or if decompression failed + @throws std::runtime_error if I/O operations failed + */ + inline std::streamoff load(const seal_byte *in, std::size_t size) + { + using namespace std::placeholders; + UniformRandomGeneratorInfo new_info; + auto in_size = + Serialization::Load(std::bind(&UniformRandomGeneratorInfo::load_members, &new_info, _1), in, size); + std::swap(*this, new_info); + return in_size; + } + + public: + void save_members(std::ostream &stream) const; + + void load_members(std::istream &stream); + + prng_type type_ = prng_type::unknown; + + prng_seed_type seed_ = {}; + }; + /** Provides the base class for a seeded uniform random number generator. Instances of this class are meant to be created by an instance of the factory class UniformRandomGeneratorFactory. This class is meant for users to sub-class to implement their own random number generators. - - @see UniformRandomGeneratorFactory for the base class of a factory class that - generates UniformRandomGenerator instances. */ class UniformRandomGenerator { @@ -39,7 +282,7 @@ namespace seal @param[in] seed The seed for the random number generator */ - UniformRandomGenerator(random_seed_type seed) + UniformRandomGenerator(prng_seed_type seed) : seed_([&seed]() { // Create a new seed allocation DynArray new_seed(seed.size(), MemoryManager::GetPool(mm_prof_opt::FORCE_NEW, true)); @@ -52,9 +295,9 @@ namespace seal buffer_begin_(buffer_.begin()), buffer_end_(buffer_.end()), buffer_head_(buffer_.end()) {} - SEAL_NODISCARD inline random_seed_type seed() const noexcept + SEAL_NODISCARD inline prng_seed_type seed() const noexcept { - random_seed_type ret; + prng_seed_type ret; std::copy(seed_.cbegin(), seed_.cend(), ret.begin()); return ret; } @@ -85,12 +328,25 @@ namespace seal buffer_head_ = buffer_begin_; } + /** + Returns a UniformRandomGeneratorInfo object representing this PRNG. + */ + SEAL_NODISCARD inline UniformRandomGeneratorInfo info() const noexcept + { + UniformRandomGeneratorInfo result; + std::copy_n(seed_.cbegin(), prng_seed_uint64_count, result.seed_.begin()); + result.type_ = type(); + return result; + } + /** Destroys the random number generator. */ virtual ~UniformRandomGenerator() = default; protected: + SEAL_NODISCARD virtual prng_type type() const noexcept = 0; + virtual void refill_buffer() = 0; const DynArray seed_; @@ -103,23 +359,17 @@ namespace seal std::mutex mutex_; protected: - decltype(buffer_)::type *const buffer_begin_; + seal_byte *const buffer_begin_; - decltype(buffer_)::type *const buffer_end_; + seal_byte *const buffer_end_; - decltype(buffer_)::type *buffer_head_; + seal_byte *buffer_head_; }; /** Provides the base class for a factory instance that creates instances of UniformRandomGenerator. This class is meant for users to sub-class to implement their own random number generators. - - @see UniformRandomGenerator for details relating to the random number generator - instances. - @see StandardRandomAdapterFactory for an implementation of - UniformRandomGeneratorFactory that supports the standard C++ library's - random number generators. */ class UniformRandomGeneratorFactory { @@ -143,7 +393,7 @@ namespace seal @param[in] default_seed The default value for a seed to be used by all created instances of UniformRandomGenerator */ - UniformRandomGeneratorFactory(random_seed_type default_seed) + UniformRandomGeneratorFactory(prng_seed_type default_seed) : default_seed_(default_seed), use_random_seed_(false) {} @@ -164,7 +414,7 @@ namespace seal @param[in] seed The seed to be used for the created random number generator */ - SEAL_NODISCARD auto create(random_seed_type seed) -> std::shared_ptr + SEAL_NODISCARD auto create(prng_seed_type seed) -> std::shared_ptr { return create_impl(seed); } @@ -178,7 +428,7 @@ namespace seal Returns the default random number generator factory. This instance should not be destroyed. */ - static auto DefaultFactory() -> const std::shared_ptr; + static auto DefaultFactory() -> std::shared_ptr; /** Returns whether the random number generator factory creates random number @@ -194,16 +444,16 @@ namespace seal by this random number generator factory. If use_random_seed() is false, then the returned seed has no meaning. */ - SEAL_NODISCARD inline random_seed_type default_seed() noexcept + SEAL_NODISCARD inline prng_seed_type default_seed() noexcept { return default_seed_; } protected: - SEAL_NODISCARD virtual auto create_impl(random_seed_type seed) -> std::shared_ptr = 0; + SEAL_NODISCARD virtual auto create_impl(prng_seed_type seed) -> std::shared_ptr = 0; private: - random_seed_type default_seed_ = {}; + prng_seed_type default_seed_ = {}; bool use_random_seed_ = false; }; @@ -212,64 +462,137 @@ namespace seal Provides an implementation of UniformRandomGenerator for using Blake2xb for generating randomness with given 128-bit seed. */ - class BlakePRNG : public UniformRandomGenerator + class Blake2xbPRNG : public UniformRandomGenerator { public: /** - Creates a new BlakePRNG instance initialized with the given seed. + Creates a new Blake2xbPRNG instance initialized with the given seed. @param[in] seed The seed for the random number generator */ - BlakePRNG(random_seed_type seed) : UniformRandomGenerator(seed) + Blake2xbPRNG(prng_seed_type seed) : UniformRandomGenerator(seed) {} /** Destroys the random number generator. */ - virtual ~BlakePRNG() override = default; + ~Blake2xbPRNG() = default; protected: - virtual void refill_buffer() override; + SEAL_NODISCARD prng_type type() const noexcept override + { + return prng_type::blake2xb; + } + + void refill_buffer() override; + + private: + std::uint64_t counter_ = 0; + }; + + class Blake2xbPRNGFactory : public UniformRandomGeneratorFactory + { + public: + /** + Creates a new Blake2xbPRNGFactory. The seed will be sampled randomly + for each Blake2xbPRNG instance created by the factory instance, which is + desirable in most normal use-cases. + */ + Blake2xbPRNGFactory() : UniformRandomGeneratorFactory() + {} + + /** + Creates a new Blake2xbPRNGFactory and sets the default seed to the given + value. For debugging purposes it may sometimes be convenient to have the + same randomness be used deterministically and repeatedly. Such randomness + sampling is naturally insecure and must be strictly restricted to debugging + situations. Thus, most users should never use this constructor. + + @param[in] default_seed The default value for a seed to be used by all + created instances of Blake2xbPRNG + */ + Blake2xbPRNGFactory(prng_seed_type default_seed) : UniformRandomGeneratorFactory(default_seed) + {} + + /** + Destroys the random number generator factory. + */ + ~Blake2xbPRNGFactory() = default; + + protected: + SEAL_NODISCARD auto create_impl(prng_seed_type seed) -> std::shared_ptr override + { + return std::make_shared(seed); + } + + private: + }; + + /** + Provides an implementation of UniformRandomGenerator for using SHAKE-256 for + generating randomness with given 128-bit seed. + */ + class Shake256PRNG : public UniformRandomGenerator + { + public: + /** + Creates a new Shake256PRNG instance initialized with the given seed. + + @param[in] seed The seed for the random number generator + */ + Shake256PRNG(prng_seed_type seed) : UniformRandomGenerator(seed) + {} + + /** + Destroys the random number generator. + */ + ~Shake256PRNG() = default; + + protected: + SEAL_NODISCARD prng_type type() const noexcept override + { + return prng_type::shake256; + } + + void refill_buffer() override; private: std::uint64_t counter_ = 0; }; - class BlakePRNGFactory : public UniformRandomGeneratorFactory + class Shake256PRNGFactory : public UniformRandomGeneratorFactory { public: /** - Creates a new BlakePRNGFactory. The seed will be sampled randomly for each - BlakePRNG instance created by the factory instance, which is desirable in - most normal use-cases. + Creates a new Shake256PRNGFactory. The seed will be sampled randomly for + each Shake256PRNG instance created by the factory instance, which is + desirable in most normal use-cases. */ - BlakePRNGFactory() : UniformRandomGeneratorFactory() + Shake256PRNGFactory() : UniformRandomGeneratorFactory() {} /** - Creates a new BlakePRNGFactory and sets the default seed to the given value. - For debugging purposes it may sometimes be convenient to have the same - randomness be used deterministically and repeatedly. Such randomness + Creates a new Shake256PRNGFactory and sets the default seed to the given + value. For debugging purposes it may sometimes be convenient to have the + same randomness be used deterministically and repeatedly. Such randomness sampling is naturally insecure and must be strictly restricted to debugging - situations. Thus, most users should never have a reason to use this - constructor. + situations. Thus, most users should never use this constructor. @param[in] default_seed The default value for a seed to be used by all - created instances of BlakePRNG + created instances of Shake256PRNG */ - BlakePRNGFactory(random_seed_type default_seed) : UniformRandomGeneratorFactory(default_seed) + Shake256PRNGFactory(prng_seed_type default_seed) : UniformRandomGeneratorFactory(default_seed) {} /** Destroys the random number generator factory. */ - virtual ~BlakePRNGFactory() = default; + ~Shake256PRNGFactory() = default; protected: - SEAL_NODISCARD virtual auto create_impl(random_seed_type seed) - -> std::shared_ptr override + SEAL_NODISCARD auto create_impl(prng_seed_type seed) -> std::shared_ptr override { - return std::make_shared(seed); + return std::make_shared(seed); } private: diff --git a/native/src/seal/serialization.h b/native/src/seal/serialization.h index 001ea0a59..7619eafc6 100644 --- a/native/src/seal/serialization.h +++ b/native/src/seal/serialization.h @@ -89,6 +89,8 @@ namespace seal std::uint64_t size = 0; }; + static_assert(sizeof(SEALHeader) == seal_header_size); + /** Returns true if the given byte corresponds to a supported compression mode. @@ -225,11 +227,13 @@ namespace seal For any given compression mode, raw_size must be the exact right size (in bytes) of what save_members writes to a stream in the uncompressed - mode. Otherwise the behavior of Save is unspecified. + mode plus the size of SEALHeader. Otherwise the behavior of Save is + unspecified. @param[in] save_members A function taking an std::ostream reference as an argument, possibly writing some number of bytes into it @param[in] raw_size The exact uncompressed output size of save_members + plus the size of SEALHeader @param[out] stream The stream to write to @param[in] compr_mode The desired compression mode @throws std::invalid_argument if save_members is invalid @@ -271,11 +275,13 @@ namespace seal For any given compression mode, raw_size must be the exact right size (in bytes) of what save_members writes to a stream in the uncompressed - mode. Otherwise the behavior of Save is unspecified. + mode plus the size of SEALHeader. Otherwise the behavior of Save is + unspecified. @param[in] save_members A function that takes an std::ostream reference as an argument and writes some number of bytes into it @param[in] raw_size The exact uncompressed output size of save_members + plus the size of SEALHeader @param[out] out The memory location to write to @param[in] size The number of bytes available in the given memory location @param[in] compr_mode The desired compression mode diff --git a/native/src/seal/util/CMakeLists.txt b/native/src/seal/util/CMakeLists.txt index 0196c6b1a..99e744e5d 100644 --- a/native/src/seal/util/CMakeLists.txt +++ b/native/src/seal/util/CMakeLists.txt @@ -8,6 +8,7 @@ target_sources(seal_obj PRIVATE ${CMAKE_CURRENT_LIST_DIR}/clipnormal.cpp ${CMAKE_CURRENT_LIST_DIR}/common.cpp ${CMAKE_CURRENT_LIST_DIR}/croots.cpp + ${CMAKE_CURRENT_LIST_DIR}/fips202.c ${CMAKE_CURRENT_LIST_DIR}/globals.cpp ${CMAKE_CURRENT_LIST_DIR}/galois.cpp ${CMAKE_CURRENT_LIST_DIR}/hash.cpp @@ -42,6 +43,7 @@ install( ${CMAKE_CURRENT_LIST_DIR}/croots.h ${CMAKE_CURRENT_LIST_DIR}/defines.h ${CMAKE_CURRENT_LIST_DIR}/dwthandler.h + ${CMAKE_CURRENT_LIST_DIR}/fips202.h ${CMAKE_CURRENT_LIST_DIR}/galois.h ${CMAKE_CURRENT_LIST_DIR}/gcc.h ${CMAKE_CURRENT_LIST_DIR}/globals.h diff --git a/native/src/seal/util/blake2.h b/native/src/seal/util/blake2.h index 5e839376b..990bf2005 100644 --- a/native/src/seal/util/blake2.h +++ b/native/src/seal/util/blake2.h @@ -15,8 +15,8 @@ /* Minor modifications to the original file have been made and marked -as `EDIT: ...`. The sole purpose of these edits is to silence misleading -warnings in Visual Studio. +as `Microsoft SEAL edit: ...`. The sole purpose of these edits is to silence +misleading warnings in Visual Studio. */ #ifndef BLAKE2_H @@ -141,11 +141,9 @@ extern "C" } blake2xb_state; /* Padded structs result in a compile-time error */ - /* - EDIT: explicit cast to int - */ enum { + /* Microsoft SEAL edit: explicit cast to silence warnings. */ BLAKE2_DUMMY_1 = 1 / (int)(sizeof(blake2s_param) == BLAKE2S_OUTBYTES), BLAKE2_DUMMY_2 = 1 / (int)(sizeof(blake2b_param) == BLAKE2B_OUTBYTES) }; diff --git a/native/src/seal/util/blake2xb.c b/native/src/seal/util/blake2xb.c index 188bcb761..1c2f49a9a 100644 --- a/native/src/seal/util/blake2xb.c +++ b/native/src/seal/util/blake2xb.c @@ -18,8 +18,8 @@ /* Minor modifications to the original file have been made and marked -as `EDIT: ...`. The sole purpose of these edits is to silence misleading -warnings in Visual Studio. +as `Microsoft SEAL edit: ...`. The sole purpose of these edits is to silence +misleading warnings in Visual Studio. */ #include @@ -50,9 +50,7 @@ int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, /* Initialize parameter block */ S->P->digest_length = BLAKE2B_OUTBYTES; - /* - EDIT: explicit cast to silence warnings - */ + /* Microsoft SEAL edit: explicit cast to silence warnings. */ S->P->key_length = (uint8_t)keylen; S->P->fanout = 1; @@ -60,9 +58,7 @@ int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, store32( &S->P->leaf_length, 0 ); store32( &S->P->node_offset, 0 ); - /* - EDIT: explicit cast to silence warnings - */ + /* Microsoft SEAL edit: explicit cast to silence warnings. */ store32( &S->P->xof_length, (uint32_t)outlen ); S->P->node_depth = 0; @@ -132,9 +128,7 @@ int blake2xb_final( blake2xb_state *S, void *out, size_t outlen) { const size_t block_size = (outlen < BLAKE2B_OUTBYTES) ? outlen : BLAKE2B_OUTBYTES; /* Initialize state */ - /* - EDIT: explicit cast to silence warnings. - */ + /* Microsoft SEAL edit: explicit cast to silence warnings. */ P->digest_length = (uint8_t)block_size; store32(&P->node_offset, (uint32_t)i); diff --git a/native/src/seal/util/cgmanifest.json b/native/src/seal/util/cgmanifest.json index 338c5e12c..fb30fbb56 100644 --- a/native/src/seal/util/cgmanifest.json +++ b/native/src/seal/util/cgmanifest.json @@ -8,6 +8,15 @@ "commitHash": "997fa5ba1e14b52c554fb03ce39e579e6f27b90c" } } + }, + { + "component": { + "type": "git", + "git": { + "repositoryUrl": "https://github.com/pq-crystals/kyber", + "commitHash": "844057468e69527bd15b17fbe03f4b61f9a22065" + } + } } ] } diff --git a/native/src/seal/util/common.cpp b/native/src/seal/util/common.cpp index c2d2735ab..755696cce 100644 --- a/native/src/seal/util/common.cpp +++ b/native/src/seal/util/common.cpp @@ -18,7 +18,7 @@ namespace seal { namespace util { - void seal_memzero(void *const data, size_t size) + void seal_memzero(void *data, size_t size) { #if (SEAL_SYSTEM == SEAL_SYSTEM_WINDOWS) SecureZeroMemory(data, size); diff --git a/native/src/seal/util/common.h b/native/src/seal/util/common.h index ea111ddc5..e93ce3eda 100644 --- a/native/src/seal/util/common.h +++ b/native/src/seal/util/common.h @@ -567,6 +567,6 @@ namespace seal return value == T{ 0 }; } - void seal_memzero(void *const data, std::size_t size); + void seal_memzero(void *data, std::size_t size); } // namespace util } // namespace seal diff --git a/native/src/seal/util/config.h.in b/native/src/seal/util/config.h.in index 83deb00d1..2eea035cd 100644 --- a/native/src/seal/util/config.h.in +++ b/native/src/seal/util/config.h.in @@ -21,7 +21,7 @@ // Security #cmakedefine SEAL_THROW_ON_TRANSPARENT_CIPHERTEXT -#cmakedefine SEAL_USE_GAUSSIAN +#cmakedefine SEAL_USE_GAUSSIAN_NOISE // Intrinsics #cmakedefine SEAL_USE_INTRIN diff --git a/native/src/seal/util/defines.h b/native/src/seal/util/defines.h index ad2e61767..e18170cf5 100644 --- a/native/src/seal/util/defines.h +++ b/native/src/seal/util/defines.h @@ -159,8 +159,16 @@ namespace seal #define SEAL_ITERATE std::for_each_n #endif -// Which random number generator factory to use by default -#define SEAL_DEFAULT_RNG_FACTORY BlakePRNGFactory() +// Which random number generator to use by default +#define SEAL_DEFAULT_PRNG Blake2xb +#define SEAL_DEFAULT_PRNG_FACTORY SEAL_JOIN(SEAL_DEFAULT_PRNG, PRNGFactory) + +// Which distribution to use for noise sampling: rounded Gaussian or Centered Binomial Distribution +#ifdef SEAL_USE_GAUSSIAN_NOISE +#define SEAL_NOISE_SAMPLER sample_poly_normal +#else +#define SEAL_NOISE_SAMPLER sample_poly_cbd +#endif // Use generic functions as (slower) fallback #ifndef SEAL_ADD_CARRY_UINT64 diff --git a/native/src/seal/util/fips202.c b/native/src/seal/util/fips202.c new file mode 100644 index 000000000..6cecdb9e0 --- /dev/null +++ b/native/src/seal/util/fips202.c @@ -0,0 +1,610 @@ +/* +This file is a part of the Kyber library (https://github.com/pq-crystals/kyber) +commit 844057468e69527bd15b17fbe03f4b61f9a22065. The Kyber library is licensed +under CC0 Universal, version 1.0. You can find a copy of this license at +https://creativecommons.org/publicdomain/zero/1.0/legalcode + +Minor modifications to the original file have been made and marked +as `Microsoft SEAL edit: ...`. +*/ + +/* Based on the public domain implementation in + * crypto_hash/keccakc512/simple/ from http://bench.cr.yp.to/supercop.html + * by Ronny Van Keer + * and the public domain "TweetFips202" implementation + * from https://twitter.com/tweetfips202 + * by Gilles Van Assche, Daniel J. Bernstein, and Peter Schwabe */ + +#include +#include +/* Microsoft SEAL edit: changed the header file path */ +#include "seal/util/fips202.h" + +/* Microsoft SEAL edit: moved the rate macros here from Kyber header fips202.h */ +#define SHAKE128_RATE 168 +#define SHAKE256_RATE 136 +#define SHA3_256_RATE 136 +#define SHA3_512_RATE 72 + +#define NROUNDS 24 +#define ROL(a, offset) ((a << offset) ^ (a >> (64-offset))) + +/* Microsoft SEAL edit: moved the definition of keccak_state here from Kyber + * header fips202.h */ +typedef struct +{ + uint64_t s[25]; +} keccak_state; + +/************************************************* +* Name: load64 +* +* Description: Load 8 bytes into uint64_t in little-endian order +* +* Arguments: - const uint8_t *x: pointer to input byte array +* +* Returns the loaded 64-bit unsigned integer +**************************************************/ +static uint64_t load64(const uint8_t x[8]) { + unsigned int i; + uint64_t r = 0; + + for(i=0;i<8;i++) + r |= (uint64_t)x[i] << 8*i; + + return r; +} + +/************************************************* +* Name: store64 +* +* Description: Store a 64-bit integer to array of 8 bytes in little-endian order +* +* Arguments: - uint8_t *x: pointer to the output byte array (allocated) +* - uint64_t u: input 64-bit unsigned integer +**************************************************/ +static void store64(uint8_t x[8], uint64_t u) { + unsigned int i; + + for(i=0;i<8;i++) + x[i] = u >> 8*i; +} + +/* Keccak round constants */ +static const uint64_t KeccakF_RoundConstants[NROUNDS] = { + (uint64_t)0x0000000000000001ULL, + (uint64_t)0x0000000000008082ULL, + (uint64_t)0x800000000000808aULL, + (uint64_t)0x8000000080008000ULL, + (uint64_t)0x000000000000808bULL, + (uint64_t)0x0000000080000001ULL, + (uint64_t)0x8000000080008081ULL, + (uint64_t)0x8000000000008009ULL, + (uint64_t)0x000000000000008aULL, + (uint64_t)0x0000000000000088ULL, + (uint64_t)0x0000000080008009ULL, + (uint64_t)0x000000008000000aULL, + (uint64_t)0x000000008000808bULL, + (uint64_t)0x800000000000008bULL, + (uint64_t)0x8000000000008089ULL, + (uint64_t)0x8000000000008003ULL, + (uint64_t)0x8000000000008002ULL, + (uint64_t)0x8000000000000080ULL, + (uint64_t)0x000000000000800aULL, + (uint64_t)0x800000008000000aULL, + (uint64_t)0x8000000080008081ULL, + (uint64_t)0x8000000000008080ULL, + (uint64_t)0x0000000080000001ULL, + (uint64_t)0x8000000080008008ULL +}; + +/************************************************* +* Name: KeccakF1600_StatePermute +* +* Description: The Keccak F1600 Permutation +* +* Arguments: - uint64_t *state: pointer to input/output Keccak state +**************************************************/ +static void KeccakF1600_StatePermute(uint64_t state[25]) +{ + int round; + + uint64_t Aba, Abe, Abi, Abo, Abu; + uint64_t Aga, Age, Agi, Ago, Agu; + uint64_t Aka, Ake, Aki, Ako, Aku; + uint64_t Ama, Ame, Ami, Amo, Amu; + uint64_t Asa, Ase, Asi, Aso, Asu; + uint64_t BCa, BCe, BCi, BCo, BCu; + uint64_t Da, De, Di, Do, Du; + uint64_t Eba, Ebe, Ebi, Ebo, Ebu; + uint64_t Ega, Ege, Egi, Ego, Egu; + uint64_t Eka, Eke, Eki, Eko, Eku; + uint64_t Ema, Eme, Emi, Emo, Emu; + uint64_t Esa, Ese, Esi, Eso, Esu; + + //copyFromState(A, state) + Aba = state[ 0]; + Abe = state[ 1]; + Abi = state[ 2]; + Abo = state[ 3]; + Abu = state[ 4]; + Aga = state[ 5]; + Age = state[ 6]; + Agi = state[ 7]; + Ago = state[ 8]; + Agu = state[ 9]; + Aka = state[10]; + Ake = state[11]; + Aki = state[12]; + Ako = state[13]; + Aku = state[14]; + Ama = state[15]; + Ame = state[16]; + Ami = state[17]; + Amo = state[18]; + Amu = state[19]; + Asa = state[20]; + Ase = state[21]; + Asi = state[22]; + Aso = state[23]; + Asu = state[24]; + + for( round = 0; round < NROUNDS; round += 2 ) + { + // prepareTheta + BCa = Aba^Aga^Aka^Ama^Asa; + BCe = Abe^Age^Ake^Ame^Ase; + BCi = Abi^Agi^Aki^Ami^Asi; + BCo = Abo^Ago^Ako^Amo^Aso; + BCu = Abu^Agu^Aku^Amu^Asu; + + //thetaRhoPiChiIotaPrepareTheta(round , A, E) + Da = BCu^ROL(BCe, 1); + De = BCa^ROL(BCi, 1); + Di = BCe^ROL(BCo, 1); + Do = BCi^ROL(BCu, 1); + Du = BCo^ROL(BCa, 1); + + Aba ^= Da; + BCa = Aba; + Age ^= De; + BCe = ROL(Age, 44); + Aki ^= Di; + BCi = ROL(Aki, 43); + Amo ^= Do; + BCo = ROL(Amo, 21); + Asu ^= Du; + BCu = ROL(Asu, 14); + Eba = BCa ^((~BCe)& BCi ); + Eba ^= (uint64_t)KeccakF_RoundConstants[round]; + Ebe = BCe ^((~BCi)& BCo ); + Ebi = BCi ^((~BCo)& BCu ); + Ebo = BCo ^((~BCu)& BCa ); + Ebu = BCu ^((~BCa)& BCe ); + + Abo ^= Do; + BCa = ROL(Abo, 28); + Agu ^= Du; + BCe = ROL(Agu, 20); + Aka ^= Da; + BCi = ROL(Aka, 3); + Ame ^= De; + BCo = ROL(Ame, 45); + Asi ^= Di; + BCu = ROL(Asi, 61); + Ega = BCa ^((~BCe)& BCi ); + Ege = BCe ^((~BCi)& BCo ); + Egi = BCi ^((~BCo)& BCu ); + Ego = BCo ^((~BCu)& BCa ); + Egu = BCu ^((~BCa)& BCe ); + + Abe ^= De; + BCa = ROL(Abe, 1); + Agi ^= Di; + BCe = ROL(Agi, 6); + Ako ^= Do; + BCi = ROL(Ako, 25); + Amu ^= Du; + BCo = ROL(Amu, 8); + Asa ^= Da; + BCu = ROL(Asa, 18); + Eka = BCa ^((~BCe)& BCi ); + Eke = BCe ^((~BCi)& BCo ); + Eki = BCi ^((~BCo)& BCu ); + Eko = BCo ^((~BCu)& BCa ); + Eku = BCu ^((~BCa)& BCe ); + + Abu ^= Du; + BCa = ROL(Abu, 27); + Aga ^= Da; + BCe = ROL(Aga, 36); + Ake ^= De; + BCi = ROL(Ake, 10); + Ami ^= Di; + BCo = ROL(Ami, 15); + Aso ^= Do; + BCu = ROL(Aso, 56); + Ema = BCa ^((~BCe)& BCi ); + Eme = BCe ^((~BCi)& BCo ); + Emi = BCi ^((~BCo)& BCu ); + Emo = BCo ^((~BCu)& BCa ); + Emu = BCu ^((~BCa)& BCe ); + + Abi ^= Di; + BCa = ROL(Abi, 62); + Ago ^= Do; + BCe = ROL(Ago, 55); + Aku ^= Du; + BCi = ROL(Aku, 39); + Ama ^= Da; + BCo = ROL(Ama, 41); + Ase ^= De; + BCu = ROL(Ase, 2); + Esa = BCa ^((~BCe)& BCi ); + Ese = BCe ^((~BCi)& BCo ); + Esi = BCi ^((~BCo)& BCu ); + Eso = BCo ^((~BCu)& BCa ); + Esu = BCu ^((~BCa)& BCe ); + + // prepareTheta + BCa = Eba^Ega^Eka^Ema^Esa; + BCe = Ebe^Ege^Eke^Eme^Ese; + BCi = Ebi^Egi^Eki^Emi^Esi; + BCo = Ebo^Ego^Eko^Emo^Eso; + BCu = Ebu^Egu^Eku^Emu^Esu; + + //thetaRhoPiChiIotaPrepareTheta(round+1, E, A) + Da = BCu^ROL(BCe, 1); + De = BCa^ROL(BCi, 1); + Di = BCe^ROL(BCo, 1); + Do = BCi^ROL(BCu, 1); + Du = BCo^ROL(BCa, 1); + + Eba ^= Da; + BCa = Eba; + Ege ^= De; + BCe = ROL(Ege, 44); + Eki ^= Di; + BCi = ROL(Eki, 43); + Emo ^= Do; + BCo = ROL(Emo, 21); + Esu ^= Du; + BCu = ROL(Esu, 14); + Aba = BCa ^((~BCe)& BCi ); + Aba ^= (uint64_t)KeccakF_RoundConstants[round+1]; + Abe = BCe ^((~BCi)& BCo ); + Abi = BCi ^((~BCo)& BCu ); + Abo = BCo ^((~BCu)& BCa ); + Abu = BCu ^((~BCa)& BCe ); + + Ebo ^= Do; + BCa = ROL(Ebo, 28); + Egu ^= Du; + BCe = ROL(Egu, 20); + Eka ^= Da; + BCi = ROL(Eka, 3); + Eme ^= De; + BCo = ROL(Eme, 45); + Esi ^= Di; + BCu = ROL(Esi, 61); + Aga = BCa ^((~BCe)& BCi ); + Age = BCe ^((~BCi)& BCo ); + Agi = BCi ^((~BCo)& BCu ); + Ago = BCo ^((~BCu)& BCa ); + Agu = BCu ^((~BCa)& BCe ); + + Ebe ^= De; + BCa = ROL(Ebe, 1); + Egi ^= Di; + BCe = ROL(Egi, 6); + Eko ^= Do; + BCi = ROL(Eko, 25); + Emu ^= Du; + BCo = ROL(Emu, 8); + Esa ^= Da; + BCu = ROL(Esa, 18); + Aka = BCa ^((~BCe)& BCi ); + Ake = BCe ^((~BCi)& BCo ); + Aki = BCi ^((~BCo)& BCu ); + Ako = BCo ^((~BCu)& BCa ); + Aku = BCu ^((~BCa)& BCe ); + + Ebu ^= Du; + BCa = ROL(Ebu, 27); + Ega ^= Da; + BCe = ROL(Ega, 36); + Eke ^= De; + BCi = ROL(Eke, 10); + Emi ^= Di; + BCo = ROL(Emi, 15); + Eso ^= Do; + BCu = ROL(Eso, 56); + Ama = BCa ^((~BCe)& BCi ); + Ame = BCe ^((~BCi)& BCo ); + Ami = BCi ^((~BCo)& BCu ); + Amo = BCo ^((~BCu)& BCa ); + Amu = BCu ^((~BCa)& BCe ); + + Ebi ^= Di; + BCa = ROL(Ebi, 62); + Ego ^= Do; + BCe = ROL(Ego, 55); + Eku ^= Du; + BCi = ROL(Eku, 39); + Ema ^= Da; + BCo = ROL(Ema, 41); + Ese ^= De; + BCu = ROL(Ese, 2); + Asa = BCa ^((~BCe)& BCi ); + Ase = BCe ^((~BCi)& BCo ); + Asi = BCi ^((~BCo)& BCu ); + Aso = BCo ^((~BCu)& BCa ); + Asu = BCu ^((~BCa)& BCe ); + } + + //copyToState(state, A) + state[ 0] = Aba; + state[ 1] = Abe; + state[ 2] = Abi; + state[ 3] = Abo; + state[ 4] = Abu; + state[ 5] = Aga; + state[ 6] = Age; + state[ 7] = Agi; + state[ 8] = Ago; + state[ 9] = Agu; + state[10] = Aka; + state[11] = Ake; + state[12] = Aki; + state[13] = Ako; + state[14] = Aku; + state[15] = Ama; + state[16] = Ame; + state[17] = Ami; + state[18] = Amo; + state[19] = Amu; + state[20] = Asa; + state[21] = Ase; + state[22] = Asi; + state[23] = Aso; + state[24] = Asu; +} + +/************************************************* +* Name: keccak_absorb +* +* Description: Absorb step of Keccak; +* non-incremental, starts by zeroeing the state. +* +* Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state +* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) +* - const uint8_t *m: pointer to input to be absorbed into s +* - size_t mlen: length of input in bytes +* - uint8_t p: domain-separation byte for different +* Keccak-derived functions +**************************************************/ +static void keccak_absorb(uint64_t s[25], + unsigned int r, + const uint8_t *m, + size_t mlen, + uint8_t p) +{ + size_t i; + uint8_t t[200] = {0}; + + /* Zero state */ + for(i=0;i<25;i++) + s[i] = 0; + + while(mlen >= r) { + for(i=0;i 0) { + KeccakF1600_StatePermute(s); + for(i=0;is, SHAKE128_RATE, in, inlen, 0x1F); +} + +/************************************************* +* Name: shake128_squeezeblocks +* +* Description: Squeeze step of SHAKE128 XOF. Squeezes full blocks of +* SHAKE128_RATE bytes each. Modifies the state. Can be called +* multiple times to keep squeezing, i.e., is incremental. +* +* Arguments: - uint8_t *out: pointer to output blocks +* - size_t nblocks: number of blocks to be squeezed +* (written to output) +* - keccak_state *s: pointer to input/output Keccak state +**************************************************/ +void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state) +{ + keccak_squeezeblocks(out, nblocks, state->s, SHAKE128_RATE); +} + +/************************************************* +* Name: shake256_absorb +* +* Description: Absorb step of the SHAKE256 XOF. +* non-incremental, starts by zeroeing the state. +* +* Arguments: - keccak_state *s: pointer to (uninitialized) output Keccak state +* - const uint8_t *in: pointer to input to be absorbed into s +* - size_t inlen: length of input in bytes +**************************************************/ +void shake256_absorb(keccak_state *state, const uint8_t *in, size_t inlen) +{ + keccak_absorb(state->s, SHAKE256_RATE, in, inlen, 0x1F); +} + +/************************************************* +* Name: shake256_squeezeblocks +* +* Description: Squeeze step of SHAKE256 XOF. Squeezes full blocks of +* SHAKE256_RATE bytes each. Modifies the state. Can be called +* multiple times to keep squeezing, i.e., is incremental. +* +* Arguments: - uint8_t *out: pointer to output blocks +* - size_t nblocks: number of blocks to be squeezed +* (written to output) +* - keccak_State *s: pointer to input/output Keccak state +**************************************************/ +void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state) +{ + keccak_squeezeblocks(out, nblocks, state->s, SHAKE256_RATE); +} + +/************************************************* +* Name: shake128 +* +* Description: SHAKE128 XOF with non-incremental API +* +* Arguments: - uint8_t *out: pointer to output +* - size_t outlen: requested output length in bytes +* - const uint8_t *in: pointer to input +* - size_t inlen: length of input in bytes +**************************************************/ +void shake128(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen) +{ + unsigned int i; + size_t nblocks = outlen/SHAKE128_RATE; + uint8_t t[SHAKE128_RATE]; + keccak_state state; + + shake128_absorb(&state, in, inlen); + shake128_squeezeblocks(out, nblocks, &state); + + out += nblocks*SHAKE128_RATE; + outlen -= nblocks*SHAKE128_RATE; + + if(outlen) { + shake128_squeezeblocks(t, 1, &state); + for(i=0;i +#include + +#if defined(__cplusplus) +extern "C" +{ +#endif + + void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen); + +#if defined(__cplusplus) +} +#endif diff --git a/native/src/seal/util/hash.h b/native/src/seal/util/hash.h index ea01d248b..fd8b9ab16 100644 --- a/native/src/seal/util/hash.h +++ b/native/src/seal/util/hash.h @@ -29,9 +29,8 @@ namespace seal inline static void hash(const std::uint64_t *input, std::size_t uint64_count, hash_block_type &destination) { - if (blake2b( - reinterpret_cast(&destination), hash_block_byte_count, - reinterpret_cast(input), uint64_count * bytes_per_uint64, nullptr, 0) != 0) + if (blake2b(&destination, hash_block_byte_count, input, uint64_count * bytes_per_uint64, nullptr, 0) != + 0) { throw std::runtime_error("blake2b failed"); } diff --git a/native/src/seal/util/rlwe.cpp b/native/src/seal/util/rlwe.cpp index 16a3939d9..934df0176 100644 --- a/native/src/seal/util/rlwe.cpp +++ b/native/src/seal/util/rlwe.cpp @@ -111,12 +111,12 @@ namespace seal void sample_poly_uniform( shared_ptr rng, const EncryptionParameters &parms, uint64_t *destination) { - // Extract encryption parameters. + // Extract encryption parameters auto coeff_modulus = parms.coeff_modulus(); size_t coeff_modulus_size = coeff_modulus.size(); size_t coeff_count = parms.poly_modulus_degree(); - // Set up source of randomness that produces 32 bit random things. + // Set up source of randomness that produces 32 bit random things RandomToStandardAdapter engine(rng); constexpr uint64_t max_random = static_cast(0xFFFFFFFFFFFFFFFFULL); @@ -126,7 +126,7 @@ namespace seal uint64_t max_multiple = max_random - barrett_reduce_64(max_random, modulus) - 1; for (size_t i = 0; i < coeff_count; i++) { - // This ensures uniform distribution. + // This ensures uniform distribution uint64_t rand; do { @@ -147,7 +147,7 @@ namespace seal throw invalid_argument("public key is not valid for the encryption parameters"); } #endif - // We use a fresh memory pool with `clear_on_destruction' enabled. + // We use a fresh memory pool with `clear_on_destruction' enabled MemoryPoolHandle pool = MemoryManager::GetPool(mm_prof_opt::FORCE_NEW, true); auto &context_data = *context.get_context_data(parms_id); @@ -164,14 +164,14 @@ namespace seal destination.is_ntt_form() = is_ntt_form; destination.scale() = 1.0; - // c[j] = public_key[j] * u + e[j] where e[j] <-- chi, u <-- R_3. + // c[j] = public_key[j] * u + e[j] where e[j] <-- chi, u <-- R_3 - // Create RNG, u and error share one RNG. - auto rng = parms.random_generator()->create(); + // Create a PRNG; u and the noise/error share the same PRNG + auto prng = parms.random_generator()->create(); // Generate u <-- R_3 auto u(allocate_poly(coeff_count, coeff_modulus_size, pool)); - sample_poly_ternary(rng, parms, u.get()); + sample_poly_ternary(prng, parms, u.get()); // c[j] = u * public_key[j] for (size_t i = 0; i < coeff_modulus_size; i++) @@ -183,7 +183,7 @@ namespace seal u.get() + i * coeff_count, public_key.data().data(j) + i * coeff_count, coeff_count, coeff_modulus[i], destination.data(j) + i * coeff_count); - // Addition with e_0, e_1 is in non-NTT form. + // Addition with e_0, e_1 is in non-NTT form if (!is_ntt_form) { inverse_ntt_negacyclic_harvey(destination.data(j) + i * coeff_count, ntt_tables[i]); @@ -191,18 +191,14 @@ namespace seal } } - // Generate e_j <-- chi. + // Generate e_j <-- chi // c[j] = public_key[j] * u + e[j] for (size_t j = 0; j < encrypted_size; j++) { -#ifdef SEAL_USE_GAUSSIAN - sample_poly_normal(rng, parms, u.get()); -#else - sample_poly_cbd(rng, parms, u.get()); -#endif + SEAL_NOISE_SAMPLER(prng, parms, u.get()); for (size_t i = 0; i < coeff_modulus_size; i++) { - // Addition with e_0, e_1 is in NTT form. + // Addition with e_0, e_1 is in NTT form if (is_ntt_form) { ntt_negacyclic_harvey(u.get() + i * coeff_count, ntt_tables[i]); @@ -235,9 +231,16 @@ namespace seal auto ntt_tables = context_data.small_ntt_tables(); size_t encrypted_size = 2; - // If a polynomial is too small to store a seed, disable save_seed. - auto poly_uint64_count = mul_safe(coeff_count, coeff_modulus_size); - if (save_seed && static_cast(poly_uint64_count) < (random_seed_type().size() + 1)) + // If a polynomial is too small to store UniformRandomGeneratorInfo, + // it is best to just disable save_seed. Note that the size needed is + // the size of UniformRandomGeneratorInfo plus one (uint64_t) because + // of an indicator word that indicates a seeded ciphertext. + size_t poly_uint64_count = mul_safe(coeff_count, coeff_modulus_size); + size_t prng_info_byte_count = + static_cast(UniformRandomGeneratorInfo::SaveSize(compr_mode_type::none)); + size_t prng_info_uint64_count = + divide_round_up(prng_info_byte_count, static_cast(bytes_per_uint64)); + if (save_seed && poly_uint64_count < prng_info_uint64_count + 1) { save_seed = false; } @@ -246,16 +249,17 @@ namespace seal destination.is_ntt_form() = is_ntt_form; destination.scale() = 1.0; - // Create an instance of a random number generator. We use this for sampling a seed for a second BlakePRNG - // used for sampling u (the seed can be public information. This RNG is also used for sampling the error. - auto bootstrap_rng = parms.random_generator()->create(); + // Create an instance of a random number generator. We use this for sampling + // a seed for a second PRNG used for sampling u (the seed can be public + // information. This PRNG is also used for sampling the noise/error below. + auto bootstrap_prng = parms.random_generator()->create(); - // Sample a seed for generating uniform randomness for the ciphertext; this seed is public information - random_seed_type public_rng_seed; - bootstrap_rng->generate(sizeof(random_seed_type), reinterpret_cast(public_rng_seed.data())); + // Sample a public seed for generating uniform randomness + prng_seed_type public_prng_seed; + bootstrap_prng->generate(prng_seed_byte_count, reinterpret_cast(public_prng_seed.data())); - // Create a BlakePRNG for sampling u - auto ciphertext_rng = BlakePRNGFactory(public_rng_seed).create(); + // Set up a new default PRNG for expanding u from the seed sampled above + auto ciphertext_prng = UniformRandomGeneratorFactory::DefaultFactory()->create(public_prng_seed); // Generate ciphertext: (c[0], c[1]) = ([-(as+e)]_q, a) uint64_t *c0 = destination.data(); @@ -265,26 +269,23 @@ namespace seal if (is_ntt_form || !save_seed) { // Sample the NTT form directly - sample_poly_uniform(ciphertext_rng, parms, c1); + sample_poly_uniform(ciphertext_prng, parms, c1); } else if (save_seed) { // Sample non-NTT form and store the seed - sample_poly_uniform(ciphertext_rng, parms, c1); + sample_poly_uniform(ciphertext_prng, parms, c1); for (size_t i = 0; i < coeff_modulus_size; i++) { - // Transform the c1 into NTT representation. + // Transform the c1 into NTT representation ntt_negacyclic_harvey(c1 + i * coeff_count, ntt_tables[i]); } } // Sample e <-- chi auto noise(allocate_poly(coeff_count, coeff_modulus_size, pool)); -#ifdef SEAL_USE_GAUSSIAN - sample_poly_normal(bootstrap_rng, parms, noise.get()); -#else - sample_poly_cbd(bootstrap_rng, parms, noise.get()); -#endif + SEAL_NOISE_SAMPLER(bootstrap_prng, parms, noise.get()); + // Calculate -(a*s + e) (mod q) and store in c[0] for (size_t i = 0; i < coeff_modulus_size; i++) { @@ -293,7 +294,7 @@ namespace seal c0 + i * coeff_count); if (is_ntt_form) { - // Transform the noise e into NTT representation. + // Transform the noise e into NTT representation ntt_negacyclic_harvey(noise.get() + i * coeff_count, ntt_tables[i]); } else @@ -310,16 +311,18 @@ namespace seal { for (size_t i = 0; i < coeff_modulus_size; i++) { - // Transform the c1 into non-NTT representation. + // Transform the c1 into non-NTT representation inverse_ntt_negacyclic_harvey(c1 + i * coeff_count, ntt_tables[i]); } } if (save_seed) { - // Write random seed to destination.data(1). + UniformRandomGeneratorInfo prng_info = ciphertext_prng->info(); + + // Write prng_info to destination.data(1) after an indicator word c1[0] = static_cast(0xFFFFFFFFFFFFFFFFULL); - copy_n(public_rng_seed.cbegin(), public_rng_seed.size(), c1 + 1); + prng_info.save(reinterpret_cast(c1 + 1), prng_info_byte_count, compr_mode_type::none); } } } // namespace util diff --git a/native/tests/seal/CMakeLists.txt b/native/tests/seal/CMakeLists.txt index 4c7780f8c..c01a442bd 100644 --- a/native/tests/seal/CMakeLists.txt +++ b/native/tests/seal/CMakeLists.txt @@ -10,7 +10,7 @@ target_sources(sealtest ${CMAKE_CURRENT_LIST_DIR}/encryptor.cpp ${CMAKE_CURRENT_LIST_DIR}/evaluator.cpp ${CMAKE_CURRENT_LIST_DIR}/galoiskeys.cpp - ${CMAKE_CURRENT_LIST_DIR}/intarray.cpp + ${CMAKE_CURRENT_LIST_DIR}/dynarray.cpp ${CMAKE_CURRENT_LIST_DIR}/keygenerator.cpp ${CMAKE_CURRENT_LIST_DIR}/memorymanager.cpp ${CMAKE_CURRENT_LIST_DIR}/modulus.cpp diff --git a/native/tests/seal/intarray.cpp b/native/tests/seal/dynarray.cpp similarity index 100% rename from native/tests/seal/intarray.cpp rename to native/tests/seal/dynarray.cpp diff --git a/native/tests/seal/galoiskeys.cpp b/native/tests/seal/galoiskeys.cpp index 07e67644d..6fa25d724 100644 --- a/native/tests/seal/galoiskeys.cpp +++ b/native/tests/seal/galoiskeys.cpp @@ -161,12 +161,12 @@ namespace sealtest parms.set_poly_modulus_degree(8); parms.set_plain_modulus(65537); parms.set_coeff_modulus(CoeffModulus::Create(8, { 60, 60 })); - random_seed_type seed; + prng_seed_type seed; for (auto &i : seed) { i = random_uint64(); } - auto rng = make_shared(BlakePRNGFactory(seed)); + auto rng = make_shared(Blake2xbPRNGFactory(seed)); parms.set_random_generator(rng); SEALContext context(parms, false, sec_level_type::none); KeyGenerator keygen(context); @@ -184,12 +184,12 @@ namespace sealtest parms.set_poly_modulus_degree(256); parms.set_plain_modulus(65537); parms.set_coeff_modulus(CoeffModulus::Create(256, { 60, 50 })); - random_seed_type seed; + prng_seed_type seed; for (auto &i : seed) { i = random_uint64(); } - auto rng = make_shared(BlakePRNGFactory(seed)); + auto rng = make_shared(Blake2xbPRNGFactory(seed)); parms.set_random_generator(rng); SEALContext context(parms, false, sec_level_type::none); KeyGenerator keygen(context); diff --git a/native/tests/seal/randomgen.cpp b/native/tests/seal/randomgen.cpp index 28fa09a12..3c1d118dc 100644 --- a/native/tests/seal/randomgen.cpp +++ b/native/tests/seal/randomgen.cpp @@ -4,10 +4,12 @@ #include "seal/keygenerator.h" #include "seal/randomgen.h" #include +#include #include #include #include #include +#include #include #include "gtest/gtest.h" @@ -21,6 +23,9 @@ namespace sealtest class SequentialRandomGenerator : public UniformRandomGenerator { public: + SequentialRandomGenerator(const prng_seed_type &seed) : UniformRandomGenerator(seed) + {} + SequentialRandomGenerator() : UniformRandomGenerator({}) {} @@ -34,6 +39,11 @@ namespace sealtest value = static_cast(static_cast(value) + buffer_size_); } + SEAL_NODISCARD prng_type type() const noexcept override + { + return prng_type::unknown; + } + private: uint8_t value = 0; }; @@ -41,7 +51,7 @@ namespace sealtest class SequentialRandomGeneratorFactory : public UniformRandomGeneratorFactory { private: - SEAL_NODISCARD auto create_impl(SEAL_MAYBE_UNUSED random_seed_type seed) + SEAL_NODISCARD auto create_impl(SEAL_MAYBE_UNUSED prng_seed_type seed) -> shared_ptr override { return make_shared(); @@ -86,18 +96,18 @@ namespace sealtest TEST(RandomGenerator, RandomGeneratorFactorySeed) { - shared_ptr factory(make_shared()); + shared_ptr factory(make_shared()); ASSERT_TRUE(factory->use_random_seed()); - factory = make_shared(random_seed_type{}); + factory = make_shared(prng_seed_type{}); ASSERT_FALSE(factory->use_random_seed()); - ASSERT_EQ(random_seed_type{}, factory->default_seed()); + ASSERT_EQ(prng_seed_type{}, factory->default_seed()); - factory = make_shared(random_seed_type{ 1, 2, 3, 4, 5, 6, 7, 8 }); + factory = make_shared(prng_seed_type{ 1, 2, 3, 4, 5, 6, 7, 8 }); ASSERT_FALSE(factory->use_random_seed()); - ASSERT_EQ(random_seed_type({ 1, 2, 3, 4, 5, 6, 7, 8 }), factory->default_seed()); + ASSERT_EQ(prng_seed_type({ 1, 2, 3, 4, 5, 6, 7, 8 }), factory->default_seed()); - factory = make_shared(); + factory = make_shared(); ASSERT_TRUE(factory->use_random_seed()); } @@ -215,4 +225,82 @@ namespace sealtest ASSERT_TRUE(find(results.begin(), results.end(), value) != results.end()); } } + + TEST(RandomGenerator, UniformRandomGeneratorInfo) + { + UniformRandomGeneratorInfo info; + ASSERT_EQ(prng_type::unknown, info.type()); + ASSERT_TRUE(info.has_valid_prng_type()); + prng_seed_type seed_arr = {}; + ASSERT_EQ(seed_arr, info.seed()); + + seed_arr = { 1, 2, 3, 4, 5, 6, 7, 8 }; + { + shared_ptr rg(make_unique(seed_arr)); + info = rg->info(); + + ASSERT_EQ(prng_type::blake2xb, info.type()); + ASSERT_TRUE(info.has_valid_prng_type()); + ASSERT_EQ(seed_arr, info.seed()); + + auto rg2 = info.make_prng(); + ASSERT_TRUE(rg2); + for (int i = 0; i < 100; i++) + { + ASSERT_EQ(rg->generate(), rg2->generate()); + } + } + { + shared_ptr rg(make_unique(seed_arr)); + info = rg->info(); + + ASSERT_EQ(prng_type::shake256, info.type()); + ASSERT_TRUE(info.has_valid_prng_type()); + ASSERT_EQ(seed_arr, info.seed()); + + auto rg2 = info.make_prng(); + ASSERT_TRUE(rg2); + for (int i = 0; i < 100; i++) + { + ASSERT_EQ(rg->generate(), rg2->generate()); + } + } + { + shared_ptr rg(make_unique(seed_arr)); + info = rg->info(); + + ASSERT_EQ(prng_type::unknown, info.type()); + ASSERT_TRUE(info.has_valid_prng_type()); + ASSERT_EQ(seed_arr, info.seed()); + + auto rg2 = info.make_prng(); + ASSERT_FALSE(rg2); + } + } + + TEST(RandomGenerator, UniformRandomGeneratorInfoSaveLoad) + { + UniformRandomGeneratorInfo info, info2; + stringstream ss; + auto size = info.save(ss, compr_mode_type::none); + ASSERT_EQ(size, info.save_size(compr_mode_type::none)); + info2.load(ss); + ASSERT_TRUE(info == info2); + + prng_seed_type seed_arr = { 1, 2, 3, 4, 5, 6, 7, 8 }; + { + shared_ptr rg(make_unique(seed_arr)); + info = rg->info(); + info.save(ss); + info2.load(ss); + ASSERT_TRUE(info == info2); + } + { + shared_ptr rg(make_unique(seed_arr)); + info = rg->info(); + info.save(ss); + info2.load(ss); + ASSERT_TRUE(info == info2); + } + } } // namespace sealtest diff --git a/native/tests/seal/relinkeys.cpp b/native/tests/seal/relinkeys.cpp index fa8c90a06..c939eefa4 100644 --- a/native/tests/seal/relinkeys.cpp +++ b/native/tests/seal/relinkeys.cpp @@ -148,12 +148,12 @@ namespace sealtest parms.set_poly_modulus_degree(8); parms.set_plain_modulus(65537); parms.set_coeff_modulus(CoeffModulus::Create(8, { 60, 60 })); - random_seed_type seed; + prng_seed_type seed; for (auto &i : seed) { i = random_uint64(); } - auto rng = make_shared(BlakePRNGFactory(seed)); + auto rng = make_shared(Blake2xbPRNGFactory(seed)); parms.set_random_generator(rng); SEALContext context(parms, false, sec_level_type::none); KeyGenerator keygen(context); @@ -171,12 +171,12 @@ namespace sealtest parms.set_poly_modulus_degree(256); parms.set_plain_modulus(65537); parms.set_coeff_modulus(CoeffModulus::Create(256, { 60, 50 })); - random_seed_type seed; + prng_seed_type seed; for (auto &i : seed) { i = random_uint64(); } - auto rng = make_shared(BlakePRNGFactory(seed)); + auto rng = make_shared(Blake2xbPRNGFactory(seed)); parms.set_random_generator(rng); SEALContext context(parms, false, sec_level_type::none); KeyGenerator keygen(context);