From 60c6301802040f622d30c148ab49ad42f5b0e3d2 Mon Sep 17 00:00:00 2001 From: PETAce Date: Wed, 26 Jun 2024 22:09:16 +0800 Subject: [PATCH] feat: version 0.3.0 --- CHANGELOG.md | 6 + CMakeLists.txt | 51 +- README.md | 16 +- bench/CMakeLists.txt | 5 +- bench/bench.cpp | 4 + bench/bench.h | 4 + bench/prng_bench.cpp | 2 + bench/sampling_bench.cpp | 2 + cmake/ExternalGMP.cmake | 72 +++ cmake/PETAce-SoloConfig.cmake.in | 2 + example/CMakeLists.txt | 6 +- example/example.cpp | 4 +- src/solo/ahe_paillier.cpp | 908 ++++++++++++++++++++++++++----- src/solo/ahe_paillier.h | 715 +++++++++++++++++++----- src/solo/cuckoo_hashing.cpp | 6 +- src/solo/hash.cpp | 1 + src/solo/prng.cpp | 6 +- src/solo/simple_hashing.cpp | 6 +- src/solo/util/hash_table_entry.h | 1 + test/CMakeLists.txt | 14 +- test/ahe_paillier_test.cpp | 902 +++++++++++++++++++++++------- test/prng_test.cpp | 2 + test/simple_hashing_test.cpp | 6 +- 23 files changed, 2261 insertions(+), 480 deletions(-) create mode 100644 cmake/ExternalGMP.cmake diff --git a/CHANGELOG.md b/CHANGELOG.md index 368aaed..ce9e755 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,3 +15,9 @@ - Added hashing tables: Cuckoo hashing and simple hashing. - Added partially homomorphic encryption: the Paillier cryptosystem. + +## Version 0.3.0 + +### Features + +- Added a GMP-based generic implementation of the Paillier cryptosystem. diff --git a/CMakeLists.txt b/CMakeLists.txt index 79522fd..5e53924 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ if(NOT CMAKE_BUILD_TYPE) endif() message(STATUS "Build type (CMAKE_BUILD_TYPE): ${CMAKE_BUILD_TYPE}") -project(SOLO VERSION 0.2.0 LANGUAGES CXX C) +project(SOLO VERSION 0.3.0 LANGUAGES CXX C) ######################## # Global configuration # @@ -101,15 +101,15 @@ set(SOLO_THIRDPARTY_INCLUDES_INSTALL_DIR ${SOLO_INCLUDES_INSTALL_DIR}/thirdparty set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY OFF) # Supported target operating systems are Linux and macOS. -if (NOT DEFINED LINUX) +if(NOT DEFINED LINUX) if (UNIX AND NOT APPLE AND NOT CYGWIN AND NOT MINGW) set(LINUX ON) endif() endif() -if (UNIX AND APPLE) +if(UNIX AND APPLE) set(MACOS ON) endif() -if (NOT LINUX AND NOT MACOS) +if(NOT LINUX AND NOT MACOS) message(FATAL_ERROR "Supported target operating systems are Linux and macOS") endif() @@ -139,13 +139,13 @@ CHECK_CXX_SOURCE_RUNS(" SOLO_AMD64 ) set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_OLD}) -if (NOT SOLO_AMD64 AND NOT SOLO_ARM64) +if(NOT SOLO_AMD64 AND NOT SOLO_ARM64) message(FATAL_ERROR "Supported target architectures are x86_64 and arm64") endif() # AES, SSE, and AVX CHECK_INCLUDE_FILE_CXX("wmmintrin.h" SOLO_USE_AES_INTRIN) -if (SOLO_USE_AES_INTRIN) +if(SOLO_USE_AES_INTRIN) add_compile_options(-msse4.2 -mavx -maes) endif() @@ -158,8 +158,6 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND SOLO_ENABLE_GCOV) set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} -fprofile-arcs -ftest-coverage -lgcov") endif() -set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} -ldl -lrt") - ######################### # External dependencies # ######################### @@ -211,6 +209,9 @@ if(SOLO_USE_IPCL) else() message(FATAL_ERROR "IPCL: not found, please download and install manually") endif() +else() + solo_fetch_thirdparty_content(ExternalGMP) + set(SOLO_BUILD_GMP TRUE CACHE BOOL "" FORCE) endif() # Require Threads::Threads @@ -220,6 +221,18 @@ if(NOT TARGET Threads::Threads) find_package(Threads REQUIRED) endif() +if(NOT SOLO_USE_IPCL) + # OpenMP::OpenMP_CXX + if(NOT TARGET OpenMP::OpenMP_CXX) + find_package(OpenMP REQUIRED) + if(NOT OpenMP_FOUND) + message(FATAL_ERROR "OpenMP: not found") + else() + message(STATUS "OpenMP: found") + endif() + endif() +endif() + #################### # SOLO C++ library # #################### @@ -274,6 +287,13 @@ if(NOT SOLO_BUILD_SHARED_LIBS) if(SOLO_USE_IPCL) target_link_libraries(solo PUBLIC IPCL::ipcl) + else() + add_dependencies(solo gmpxx gmp) + target_include_directories(solo PUBLIC $) + solo_combine_archives(solo gmpxx) + solo_combine_archives(solo gmp) + target_link_libraries(solo PUBLIC OpenMP::OpenMP_CXX) + set(SOLO_CARRY_GMP TRUE) endif() target_link_libraries(solo PUBLIC Threads::Threads) @@ -310,6 +330,11 @@ else() if(SOLO_USE_IPCL) target_link_libraries(solo_shared PUBLIC IPCL::ipcl) + else() + add_dependencies(solo_shared gmpxx_shared gmp_shared) + target_include_directories(solo_shared PUBLIC $) + target_link_libraries(solo_shared PUBLIC gmpxx_shared gmp_shared OpenMP::OpenMP_CXX) + set(SOLO_CARRY_GMP FALSE) endif() target_link_libraries(solo_shared PUBLIC Threads::Threads) endif() @@ -369,6 +394,16 @@ if(SOLO_BUILD_DEPS) DIRECTORY ${OPENSSL_INCLUDE_DIR} DESTINATION ${SOLO_THIRDPARTY_INCLUDES_INSTALL_DIR}/openssl) endif() + if(SOLO_BUILD_GMP) + if(SOLO_BUILD_SHARED_LIBS) + install( + FILES ${GMP_C_SHARED_LIBRARY} ${GMP_CXX_SHARED_LIBRARY} + DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() + install( + FILES ${GMP_INCLUDE_DIR}/gmp.h ${GMP_INCLUDE_DIR}/gmpxx.h + DESTINATION ${SOLO_THIRDPARTY_INCLUDES_INSTALL_DIR}) + endif() endif() #################### diff --git a/README.md b/README.md index 1fed30f..ac9797d 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ PETAce-Solo implements or wraps the following primitives that involves only one | Required dependency | Tested version | Use | |-----------------------------------------------|----------------|--------------------------| | [OpenSSL](https://github.com/openssl/openssl) | 1.1.1 | Cryptographic primitives | +| [GMP](https://gmplib.org) | 6.3.0 | Bignumer operations for GMP-based Paillier| | Optional dependency | Tested version | Use | |--------------------------------------------------------|----------------|------------------------| @@ -48,13 +49,16 @@ Output binaries can be found in `build/lib/` and `build/bin/` directories. | `SOLO_BUILD_EXAMPLE` | ON/OFF | ON | Build C++ example if set to ON. | | `SOLO_BUILD_TEST` | ON/OFF | ON | Build C++ test if set to ON. | | `SOLO_BUILD_DEPS` | ON/OFF | ON | Download and build unmet dependencies if set to ON. | -| `SOLO_USE_IPCL` | ON/OFF | OFF | Add PHE and depends on IPCL if set to ON. | +| `SOLO_USE_IPCL` | ON/OFF | OFF | BUILD IPCL-based PHE if set to ON, GMP-based PHE if set to off| ### Adding Partially Homomorphic Encryption -Follow instructions of [Intel Paillier Cryptosystem Library](https://github.com/intel/pailliercryptolib) and install it to `${IPCL_INSTALL_DIR}`. +By default, the Paillier cryptosystem is a generic implementation that uses the GMP library. +For power users who seek extreme performance on supported processors, we provide the option to switch to the IPCL-based implementation. +To use the IPCL-based Paillier, follow instructions of [Intel Paillier Cryptosystem Library](https://github.com/intel/pailliercryptolib) and install it to `${IPCL_INSTALL_DIR}`. We recommend the commit `495beaad1f6e70741f2b5cf1279cb919fd66d894` instead of v2.0.0. Build PETAce-Solo library with extra configuration: + ```bash cmake -S . -B build -DSOLO_USE_IPCL=ON -DIPCL_DIR=${IPCL_INSTALL_DIR}/lib/cmake/ipcl-2.0.0 ``` @@ -75,14 +79,14 @@ This project is licensed under the [Apache-2.0 License](LICENSE). To cite PETAce in academic papers, please use the following BibTeX entries. -### Version 0.2.0 +### Version 0.3.0 ```tex @misc{petace, - title = {PETAce (release 0.2.0)}, + title = {PETAce (release 0.3.0)}, howpublished = {\url{https://github.com/tiktok-privacy-innovation/PETAce}}, - month = Oct, - year = 2023, + month = Jun, + year = 2024, note = {TikTok Pte. Ltd.}, key = {PETAce} } diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index e5f3489..9dcba74 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -14,13 +14,13 @@ cmake_minimum_required(VERSION 3.14) -project(SOLOBench VERSION 0.2.0 LANGUAGES CXX) +project(SOLOBench VERSION 0.3.0 LANGUAGES CXX) # If not called from root CMakeLists.txt if(NOT DEFINED SOLO_BUILD_BENCH) set(SOLO_BUILD_BENCH ON) - find_package(PETAce-Solo 0.2.0 EXACT REQUIRED) + find_package(PETAce-Solo 0.3.0 EXACT REQUIRED) # Must define these variables and include macros set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) @@ -70,7 +70,6 @@ if(SOLO_BUILD_BENCH) ${CMAKE_CURRENT_LIST_DIR}/sampling_bench.cpp ) - set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} -ldl -lrt") add_executable(solo_bench ${SOLO_BENCH_FILES}) if(TARGET PETAce-Solo::solo) diff --git a/bench/bench.cpp b/bench/bench.cpp index 93735e5..7a6799c 100644 --- a/bench/bench.cpp +++ b/bench/bench.cpp @@ -28,10 +28,14 @@ int main(int argc, char** argv) { PETACE_REG_BENCH(Hash, BLAKE2, bm_hash_blake2, petace::solo::Byte(0)); PETACE_REG_BENCH(petace::solo::PRNG, SHAKE_128, bm_prng_shake_128, std::size_t(4096)); PETACE_REG_BENCH(petace::solo::PRNG, BLAKE2X, bm_prng_blake2x, std::size_t(4096)); +#ifdef SOLO_USE_AES_INTRIN PETACE_REG_BENCH(petace::solo::PRNG, AES_ECB_CTR, bm_prng_aes_ctr, std::size_t(4096)); +#endif PETACE_REG_BENCH(Sampling, UNIFORM_BYTE_ARRAY_SHAKE_128, bm_sample_uniform_byte_array_shake_128, std::size_t(4096)); PETACE_REG_BENCH(Sampling, UNIFORM_BYTE_ARRAY_BLAKE2X, bm_sample_uniform_byte_array_blake2x, std::size_t(4096)); +#ifdef SOLO_USE_AES_INTRIN PETACE_REG_BENCH(Sampling, UNIFORM_BYTE_ARRAY_AES_ECB_CTR, bm_sample_uniform_byte_array_aes_ctr, std::size_t(4096)); +#endif PETACE_REG_BENCH(EC_OpenSSL, hash_to_curve, bm_ec_hash_to_curve, petace::solo::Byte(0)); PETACE_REG_BENCH(EC_OpenSSL, encrypt, bm_ec_encrypt, petace::solo::Byte(0)); PETACE_REG_BENCH(EC_OpenSSL, decrypt, bm_ec_decrypt, petace::solo::Byte(0)); diff --git a/bench/bench.h b/bench/bench.h index f358c9e..99e5c97 100644 --- a/bench/bench.h +++ b/bench/bench.h @@ -35,11 +35,15 @@ void bm_hash_blake2(benchmark::State& state, petace::solo::Byte value); // PRNG benchmark cases void bm_prng_shake_128(benchmark::State& state, std::size_t byte_count); void bm_prng_blake2x(benchmark::State& state, std::size_t byte_count); +#ifdef SOLO_USE_AES_INTRIN void bm_prng_aes_ctr(benchmark::State& state, std::size_t byte_count); +#endif // Sampling benchmark cases void bm_sample_uniform_byte_array_shake_128(benchmark::State& state, std::size_t byte_count); void bm_sample_uniform_byte_array_blake2x(benchmark::State& state, std::size_t byte_count); +#ifdef SOLO_USE_AES_INTRIN void bm_sample_uniform_byte_array_aes_ctr(benchmark::State& state, std::size_t byte_count); +#endif // EC OpenSSL benchmark cases void bm_ec_hash_to_curve(benchmark::State& state, petace::solo::Byte value); void bm_ec_encrypt(benchmark::State& state, petace::solo::Byte value); diff --git a/bench/prng_bench.cpp b/bench/prng_bench.cpp index 1f7538a..fbdc74b 100644 --- a/bench/prng_bench.cpp +++ b/bench/prng_bench.cpp @@ -42,6 +42,7 @@ void bm_prng_blake2x(benchmark::State& state, std::size_t byte_count) { } } +#ifdef SOLO_USE_AES_INTRIN void bm_prng_aes_ctr(benchmark::State& state, std::size_t byte_count) { std::vector output(byte_count); petace::solo::PRNGFactory prng_factory(petace::solo::PRNGScheme::AES_ECB_CTR, kSeedByteCount); @@ -53,3 +54,4 @@ void bm_prng_aes_ctr(benchmark::State& state, std::size_t byte_count) { prng->generate(byte_count, output.data()); } } +#endif diff --git a/bench/sampling_bench.cpp b/bench/sampling_bench.cpp index ea735e4..3d6c461 100644 --- a/bench/sampling_bench.cpp +++ b/bench/sampling_bench.cpp @@ -43,6 +43,7 @@ void bm_sample_uniform_byte_array_blake2x(benchmark::State& state, std::size_t b } } +#ifdef SOLO_USE_AES_INTRIN void bm_sample_uniform_byte_array_aes_ctr(benchmark::State& state, std::size_t byte_count) { std::vector output(byte_count); petace::solo::PRNGFactory prng_factory(petace::solo::PRNGScheme::AES_ECB_CTR, kSeedByteCount); @@ -54,3 +55,4 @@ void bm_sample_uniform_byte_array_aes_ctr(benchmark::State& state, std::size_t b sample_uniform_byte_array(*prng, byte_count, output.data()); } } +#endif diff --git a/cmake/ExternalGMP.cmake b/cmake/ExternalGMP.cmake new file mode 100644 index 0000000..3e0fd46 --- /dev/null +++ b/cmake/ExternalGMP.cmake @@ -0,0 +1,72 @@ +# Copyright 2023 TikTok Pte. Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include(ExternalProject) + +set(GMP_SOURCES_DIR ${SOLO_THIRDPARTY_DIR}/gmp) +set(GMP_INSTALL_DIR "${GMP_SOURCES_DIR}/install") +set(GMP_INCLUDE_DIR "${GMP_INSTALL_DIR}/include") +set(GMP_NAME "gmp") + +include(ProcessorCount) +ProcessorCount(NUM_OF_PROCESSOR) + +if((NOT DEFINED GMP_URL) OR (NOT DEFINED GMP_VER)) + message(STATUS "use pre defined download url") + set(GMP_VER "gmp-6.3.0" CACHE STRING "" FORCE) + set(GMP_URL "https://gmplib.org/download/gmp/${GMP_VER}.tar.bz2" CACHE STRING "" FORCE) +endif() + +ExternalProject_Add( + extern_gmp + PREFIX ${GMP_SOURCES_DIR} + DOWNLOAD_COMMAND wget --no-check-certificate ${GMP_URL} -c -q -O ${GMP_NAME}.tar.bz2 + && tar -xvf ${GMP_NAME}.tar.bz2 + SOURCE_DIR ${GMP_SOURCES_DIR}/src/${GMP_VER} + CONFIGURE_COMMAND ./configure CFLAGS=-fPIC CXXFLAGS=-fPIC --prefix=${GMP_INSTALL_DIR} + --enable-cxx --with-pic + BUILD_COMMAND make -j ${NUM_OF_PROCESSOR} + INSTALL_COMMAND make install + BUILD_IN_SOURCE 1 +) + +set(GMP_C_STATIC_LIBRARY "${GMP_INSTALL_DIR}/lib/libgmp.a") +set(GMP_CXX_STATIC_LIBRARY "${GMP_INSTALL_DIR}/lib/libgmpxx.a") + +if(LINUX) + set(GMP_C_SHARED_LIBRARY "${GMP_INSTALL_DIR}/lib/libgmp.so") + set(GMP_CXX_SHARED_LIBRARY "${GMP_INSTALL_DIR}/lib/libgmpxx.so") +elseif(MACOS) + set(GMP_C_SHARED_LIBRARY "${GMP_INSTALL_DIR}/lib/libgmp.dylib") + set(GMP_CXX_SHARED_LIBRARY "${GMP_INSTALL_DIR}/lib/libgmpxx.dylib") +endif() + +add_library(gmp STATIC IMPORTED GLOBAL) +set_property(TARGET gmp PROPERTY IMPORTED_LOCATION ${GMP_C_STATIC_LIBRARY}) + +add_library(gmp_shared SHARED IMPORTED GLOBAL) +set_property(TARGET gmp_shared PROPERTY IMPORTED_LOCATION ${GMP_C_SHARED_LIBRARY}) + +add_library(gmpxx STATIC IMPORTED GLOBAL) +set_property(TARGET gmpxx PROPERTY IMPORTED_LOCATION ${GMP_CXX_STATIC_LIBRARY}) + +add_library(gmpxx_shared SHARED IMPORTED GLOBAL) +set_property(TARGET gmpxx_shared PROPERTY IMPORTED_LOCATION ${GMP_CXX_SHARED_LIBRARY}) + +add_dependencies(gmp extern_gmp) +add_dependencies(gmpxx extern_gmp) +add_dependencies(gmp_shared extern_gmp) +add_dependencies(gmpxx_shared extern_gmp) + +include_directories(${GMP_INCLUDE_DIR}) diff --git a/cmake/PETAce-SoloConfig.cmake.in b/cmake/PETAce-SoloConfig.cmake.in index 590e429..303de77 100644 --- a/cmake/PETAce-SoloConfig.cmake.in +++ b/cmake/PETAce-SoloConfig.cmake.in @@ -45,6 +45,8 @@ if (NOT SOLO_CARRY_OPENSSL) endif() if (SOLO_USE_IPCL) solo_find_dependency(IPCL) +else() + solo_find_dependency(OpenMP) endif() include(${CMAKE_CURRENT_LIST_DIR}/PETAce-SoloTargets.cmake) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 68dac86..29f83e5 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -14,14 +14,14 @@ cmake_minimum_required(VERSION 3.14) -project(SOLOExample VERSION 0.2.0 LANGUAGES CXX) +project(SOLOExample VERSION 0.3.0 LANGUAGES CXX) # If not called from root CMakeLists.txt if(NOT DEFINED SOLO_BUILD_EXAMPLE) set(SOLO_BUILD_EXAMPLE ON) # Import PETAce-Solo - find_package(PETAce-Solo 0.2.0 EXACT REQUIRED) + find_package(PETAce-Solo 0.3.0 EXACT REQUIRED) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) endif() @@ -34,8 +34,6 @@ if(SOLO_BUILD_EXAMPLE) ${CMAKE_CURRENT_LIST_DIR}/example.cpp ) - set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} -ldl -lrt") - add_executable(solo_example ${SOLO_EXAMPLE_FILES}) if(TARGET PETAce-Solo::solo) diff --git a/example/example.cpp b/example/example.cpp index 3b0268c..d900f40 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -92,8 +92,8 @@ void example_sampling() { << std::endl; std::cout << std::endl; - std::cout << "Use AES_ECB_CTR to create our PRNG" << std::endl; - petace::solo::PRNGFactory prng_factory(petace::solo::PRNGScheme::AES_ECB_CTR, seed.size()); + std::cout << "Use BLAKE2Xb to create our PRNG" << std::endl; + petace::solo::PRNGFactory prng_factory(petace::solo::PRNGScheme::BLAKE2Xb, seed.size()); auto prng = prng_factory.create(seed); std::cout << " Generate a random 32-bit unsigned integer: " << sample_uniform_uint32(*prng) << std::endl; diff --git a/src/solo/ahe_paillier.cpp b/src/solo/ahe_paillier.cpp index 7341f02..9270716 100644 --- a/src/solo/ahe_paillier.cpp +++ b/src/solo/ahe_paillier.cpp @@ -14,7 +14,7 @@ #include "solo/ahe_paillier.h" -#ifdef SOLO_USE_IPCL +#include #include #include @@ -23,18 +23,286 @@ #include #include +#ifdef SOLO_USE_IPCL #include "ipcl/ipcl.hpp" +#else +#include "solo/sampling.h" +#endif namespace petace { namespace solo { namespace ahepaillier { -static BigNum bn_from_u64(std::uint64_t in) { +constexpr std::size_t kAHEMaxRandomBits = 8192; +constexpr std::size_t kAHEMaxRandomByteCount = 1024; + +PublicKey::PublicKey(std::size_t key_length, bool enable_djn) + : key_length_(key_length), n_byte_count_((key_length_ + 7) / 8), enable_djn_(enable_djn), pk_set_(false) { + if (key_length_ != 1024 && key_length_ != 2048) { + throw std::invalid_argument("AHE key length is invalid."); + } +} + +#ifdef SOLO_USE_IPCL +PublicKey::PublicKey(const ipcl::PublicKey& pk) : enable_djn_(pk.isDJN()) { + key_length_ = pk.getBits(); + n_byte_count_ = (key_length_ + 7) / 8; + if (key_length_ != 1024 && key_length_ != 2048) { + throw std::invalid_argument("AHE key length is invalid."); + } + pk_ = std::make_shared(); + if (enable_djn_) { + pk_->create(*(pk.getN()), pk.getBits(), pk.getHS(), pk.getRandBits()); + } else { + pk_->create(*(pk.getN()), pk.getBits(), false); + } + pk_set_ = true; +} +#else +PublicKey::PublicKey(const mpz_class& n) + : n_(n), + g_(n + 1), + n_square_(n * n), + hs_(0), + key_length_(mpz_sizeinbase(n.get_mpz_t(), 2)), + n_byte_count_((key_length_ + 7) / 8), + enable_djn_(false), + pk_set_(true) { + if (key_length_ != 1024 && key_length_ != 2048) { + throw std::invalid_argument("AHE key length is invalid."); + } +} + +PublicKey::PublicKey(const mpz_class& n, const mpz_class& hs) + : n_(n), + g_(n + 1), + n_square_(n * n), + hs_(hs), + key_length_(mpz_sizeinbase(n.get_mpz_t(), 2)), + n_byte_count_((key_length_ + 7) / 8), + enable_djn_(true), + pk_set_(true) { + if (key_length_ != 1024 && key_length_ != 2048) { + throw std::invalid_argument("AHE key length is invalid."); + } +} +#endif + +std::size_t PublicKey::public_key_byte_count() const noexcept { + return n_byte_count_ * (1 + 2 * static_cast(enable_djn_)); +} + +void PublicKey::serialize_to_bytes(Byte* out, std::size_t out_byte_count) const { + if (pk_set_ == false) { + throw std::invalid_argument("pk is not set"); + } +#ifdef SOLO_USE_IPCL + if (pk_ == nullptr) { + throw std::invalid_argument("pk is nullptr"); + } +#endif + if (out == nullptr && out_byte_count != 0) { + throw std::invalid_argument("out is nullptr"); + } + if (out_byte_count != public_key_byte_count()) { + throw std::invalid_argument("out_byte_count is not equal to public_key_byte_count"); + } +#ifdef SOLO_USE_IPCL + Serialization::ipcl_bn_to_bytes(*(pk_->getN()), out, n_byte_count_); + if (enable_djn_) { + Serialization::ipcl_bn_to_bytes(pk_->getHS(), out + n_byte_count_, out_byte_count - n_byte_count_); + } +#else + Serialization::mpz_bn_to_bytes(n_, out, n_byte_count_); + if (enable_djn_) { + Serialization::mpz_bn_to_bytes(hs_, out + n_byte_count_, out_byte_count - n_byte_count_); + } +#endif +} + +void PublicKey::deserialize_from_bytes(const Byte* in, std::size_t in_byte_count) { + if (pk_set_ == true) { + throw std::invalid_argument("pk is already set"); + } + if (in == nullptr && in_byte_count != 0) { + throw std::invalid_argument("in is nullptr"); + } + if (in_byte_count != public_key_byte_count()) { + throw std::invalid_argument("in_byte_count is not equal to public_key_byte_count"); + } +#ifdef SOLO_USE_IPCL + pk_ = std::make_shared(); + if (enable_djn_) { + BigNumber n; + Serialization::ipcl_bn_from_bytes(in, n_byte_count_, n); + BigNumber hs; + Serialization::ipcl_bn_from_bytes(in + n_byte_count_, in_byte_count - n_byte_count_, hs); + pk_->create(n, static_cast(n_byte_count_ * 8), hs, static_cast(n_byte_count_ * 4)); + } else { + BigNumber n; + Serialization::ipcl_bn_from_bytes(in, n_byte_count_, n); + pk_->create(n, static_cast(n_byte_count_ * 8), false); + } +#else + Serialization::mpz_bn_from_bytes(in, n_byte_count_, n_); + g_ = n_ + 1; + n_square_ = n_ * n_; + if (enable_djn_) { + Serialization::mpz_bn_from_bytes(in + n_byte_count_, in_byte_count - n_byte_count_, hs_); + } else { + hs_ = 0; + } +#endif + pk_set_ = true; +} + +SecretKey::SecretKey(std::size_t key_length) + : key_length_(key_length), n_byte_count_((key_length_ + 7) / 8), sk_set_(false) { + if (key_length_ != 1024 && key_length_ != 2048) { + throw std::invalid_argument("AHE key length is invalid."); + } +} + +#ifdef SOLO_USE_IPCL +SecretKey::SecretKey(const ipcl::PrivateKey& sk) { + sk_ = std::make_shared(*(sk.getN()), *(sk.getP()), *(sk.getQ())); + key_length_ = sk.getN()->BitSize(); + n_byte_count_ = (key_length_ + 7) / 8; + if (key_length_ != 1024 && key_length_ != 2048) { + throw std::invalid_argument("AHE key length is invalid."); + } + sk_set_ = true; +} +#else +SecretKey::SecretKey(const mpz_class& n, const mpz_class& p, const mpz_class& q) + : n_(n), p_(p), q_(q), key_length_(mpz_sizeinbase(n.get_mpz_t(), 2)), n_byte_count_((key_length_ + 7) / 8) { + if (key_length_ != 1024 && key_length_ != 2048) { + throw std::invalid_argument("AHE key length is invalid."); + } + if (n <= 1 || (n != p * q)) { + throw std::invalid_argument("invalid sk."); + } + initilize_sk(); + sk_set_ = true; +} + +void SecretKey::initilize_sk() { + g_ = n_ + 1; + n_square_ = n_ * n_; + lambda_ = (p_ - 1) * (q_ - 1) / 2; + p_square_ = p_ * p_; + q_square_ = q_ * q_; + mpz_invert(p_inv_.get_mpz_t(), p_.get_mpz_t(), q_.get_mpz_t()); + mpz_invert(q_inv_.get_mpz_t(), q_.get_mpz_t(), p_.get_mpz_t()); + + mpz_invert(p_square_inv_.get_mpz_t(), p_square_.get_mpz_t(), q_square_.get_mpz_t()); + mpz_invert(q_square_inv_.get_mpz_t(), q_square_.get_mpz_t(), p_square_.get_mpz_t()); + + mpz_class p_minus_one = p_ - 1; + mpz_class q_minus_one = q_ - 1; + + mpz_powm(hp_.get_mpz_t(), g_.get_mpz_t(), p_minus_one.get_mpz_t(), p_square_.get_mpz_t()); + mpz_powm(hq_.get_mpz_t(), g_.get_mpz_t(), q_minus_one.get_mpz_t(), q_square_.get_mpz_t()); + + hp_ = (hp_ - 1) / p_; + hq_ = (hq_ - 1) / q_; + + mpz_invert(hp_.get_mpz_t(), hp_.get_mpz_t(), p_.get_mpz_t()); + mpz_invert(hq_.get_mpz_t(), hq_.get_mpz_t(), q_.get_mpz_t()); +} +#endif + +std::size_t SecretKey::secret_key_byte_count() const noexcept { + return n_byte_count_ * 2; +} + +void SecretKey::serialize_to_bytes(Byte* out, std::size_t out_byte_count) const { + if (sk_set_ == false) { + throw std::invalid_argument("sk is not set"); + } +#ifdef SOLO_USE_IPCL + if (sk_ == nullptr) { + throw std::invalid_argument("sk is nullptr"); + } +#endif + if (out == nullptr && out_byte_count != 0) { + throw std::invalid_argument("out is nullptr"); + } + if (out_byte_count != secret_key_byte_count()) { + throw std::invalid_argument("out_byte_count is not equal to secret_key_byte_count"); + } +#ifdef SOLO_USE_IPCL + Serialization::ipcl_bn_to_bytes(*(sk_->getN()), out, n_byte_count_); + std::size_t pq_byte_count = n_byte_count_ / 2; + Serialization::ipcl_bn_to_bytes(*(sk_->getP()), out + n_byte_count_, pq_byte_count); + Serialization::ipcl_bn_to_bytes(*(sk_->getQ()), out + n_byte_count_ + pq_byte_count, pq_byte_count); +#else + Serialization::mpz_bn_to_bytes(n_, out, n_byte_count_); + std::size_t pq_byte_count = n_byte_count_ / 2; + Serialization::mpz_bn_to_bytes(p_, out + n_byte_count_, pq_byte_count); + Serialization::mpz_bn_to_bytes(q_, out + n_byte_count_ + pq_byte_count, pq_byte_count); +#endif +} + +void SecretKey::deserialize_from_bytes(const Byte* in, std::size_t in_byte_count) { + if (sk_set_ == true) { + throw std::invalid_argument("sk is already set"); + } + if (in == nullptr && in_byte_count != 0) { + throw std::invalid_argument("in is nullptr"); + } + if (in_byte_count != secret_key_byte_count()) { + throw std::invalid_argument("in_byte_count is not equal to secret_key_byte_count"); + } +#ifdef SOLO_USE_IPCL + BigNumber n; + Serialization::ipcl_bn_from_bytes(in, n_byte_count_, n); + BigNumber p; + BigNumber q; + std::size_t pq_byte_count = n_byte_count_ / 2; + Serialization::ipcl_bn_from_bytes(in + n_byte_count_, pq_byte_count, p); + Serialization::ipcl_bn_from_bytes(in + n_byte_count_ + pq_byte_count, pq_byte_count, q); + sk_ = std::make_shared(n, p, q); +#else + Serialization::mpz_bn_from_bytes(in, n_byte_count_, n_); + std::size_t pq_byte_count = n_byte_count_ / 2; + Serialization::mpz_bn_from_bytes(in + n_byte_count_, pq_byte_count, p_); + Serialization::mpz_bn_from_bytes(in + n_byte_count_ + pq_byte_count, pq_byte_count, q_); + initilize_sk(); +#endif + sk_set_ = true; +} + +#ifndef SOLO_USE_IPCL +void SecretKey::powm_crt(const mpz_class& base, const mpz_class& exponent, mpz_class& out) const { + mpz_class y0; + mpz_class y1; + mpz_powm(y0.get_mpz_t(), base.get_mpz_t(), exponent.get_mpz_t(), p_square_.get_mpz_t()); + mpz_powm(y1.get_mpz_t(), base.get_mpz_t(), exponent.get_mpz_t(), q_square_.get_mpz_t()); + + out = (y0 * q_square_inv_ * q_square_ + y1 * p_square_inv_ * p_square_) % n_square_; +} + +void SecretKey::decrypt(const mpz_class& cipher, mpz_class& out) const { + mpz_class c_p = p_ - 1; + mpz_powm(c_p.get_mpz_t(), cipher.get_mpz_t(), c_p.get_mpz_t(), p_square_.get_mpz_t()); + mpz_class l_p = (c_p - 1) / p_; + mpz_class m_p = l_p * hp_ % p_; + mpz_class c_q = q_ - 1; + mpz_powm(c_q.get_mpz_t(), cipher.get_mpz_t(), c_q.get_mpz_t(), q_square_.get_mpz_t()); + mpz_class l_q = (c_q - 1) / q_; + mpz_class m_q = l_q * hq_ % q_; + out = (m_p * q_inv_ * q_ + m_q * p_inv_ * p_) % n_; +} +#endif + +#ifdef SOLO_USE_IPCL +static BigNumber ipcl_bn_from_u64(std::uint64_t in) { std::vector vec_u32 = {static_cast(in), static_cast(in >> 32)}; - return BigNum(vec_u32.data(), 2); + return BigNumber(vec_u32.data(), 2); } -static std::uint64_t u64_from_bn(const BigNum& in) { +static std::uint64_t u64_from_ipcl_bn(const BigNumber& in) { std::size_t length = in.DwordSize(); if (length == 0) { return 0; @@ -48,10 +316,13 @@ static std::uint64_t u64_from_bn(const BigNum& in) { return value; } -void Serialization::bn_from_bytes(const Byte* in, std::size_t in_byte_count, BigNum& out) { +void Serialization::ipcl_bn_from_bytes(const Byte* in, std::size_t in_byte_count, BigNumber& out) { if (in == nullptr) { throw std::invalid_argument("in is nullptr"); } + if (in != nullptr && in_byte_count > kAHEMaxRandomByteCount) { + throw std::invalid_argument("in is too large."); + } std::size_t length = (in_byte_count + 3) / 4; std::vector vec_u32(length, 0); for (std::size_t i = 0; i < length; ++i) { @@ -62,14 +333,20 @@ void Serialization::bn_from_bytes(const Byte* in, std::size_t in_byte_count, Big } } } - out = BigNum(vec_u32.data(), static_cast(length)); + out = BigNumber(vec_u32.data(), static_cast(length)); } -void Serialization::bn_to_bytes(const BigNum& in, Byte* out, std::size_t out_byte_count) { +void Serialization::ipcl_bn_to_bytes(const BigNumber& in, Byte* out, std::size_t out_byte_count) { if (out == nullptr) { throw std::invalid_argument("out is nullptr"); } std::size_t length = (in.BitSize() + 7) / 8; + if (length == 0) { + throw std::invalid_argument("the length of in is zero."); + } + if (length > kAHEMaxRandomByteCount) { + throw std::invalid_argument("in is too large."); + } std::vector temp(length); in.num2char(temp); std::size_t byte_count = std::min(length, out_byte_count); @@ -80,204 +357,563 @@ void Serialization::bn_to_bytes(const BigNum& in, Byte* out, std::size_t out_byt std::fill_n(out + length, out_byte_count - length, Byte('\x00')); } } - -Serialization::Serialization(std::size_t key_length, bool enable_djn) { - if (key_length != 1024 && key_length != 2048) { - throw std::invalid_argument("AHE key length is invalid."); +#else +void Serialization::mpz_bn_from_bytes(const Byte* in, std::size_t in_byte_count, mpz_class& out) { + if (in == nullptr) { + throw std::invalid_argument("in is nullptr"); } - key_length_ = key_length; - n_byte_count_ = (key_length_ + 7) / 8; - enable_djn_ = enable_djn; -} - -std::size_t Serialization::public_key_byte_count() const noexcept { - return n_byte_count_ * (1 + 2 * static_cast(enable_djn_)); -} - -std::size_t Serialization::secret_key_byte_count() const noexcept { - return n_byte_count_ * 2; + if (in != nullptr && in_byte_count > kAHEMaxRandomByteCount) { + throw std::invalid_argument("in is too large."); + } + mpz_import(out.get_mpz_t(), in_byte_count, -1, sizeof(Byte), -1, 0, in); } -void Serialization::serialize_public_key_to_bytes( - const std::shared_ptr& pk, Byte* out, std::size_t out_byte_count) const { - if (pk == nullptr) { - throw std::invalid_argument("pk is nullptr"); - } - if (out == nullptr && out_byte_count != 0) { +void Serialization::mpz_bn_to_bytes(const mpz_class& in, Byte* out, std::size_t out_byte_count) { + if (out == nullptr) { throw std::invalid_argument("out is nullptr"); } - if (out_byte_count != public_key_byte_count()) { - throw std::invalid_argument("out_byte_count is not equal to public_key_byte_count"); - } - bn_to_bytes(*(pk->getN()), out, n_byte_count_); - if (enable_djn_) { - bn_to_bytes(pk->getHS(), out + n_byte_count_, out_byte_count - n_byte_count_); - } -} - -void Serialization::serialize_secret_key_to_bytes( - const std::shared_ptr& sk, Byte* out, std::size_t out_byte_count) const { - if (sk == nullptr) { - throw std::invalid_argument("sk is nullptr"); + std::size_t length = (mpz_sizeinbase(in.get_mpz_t(), 2) + 7) / 8; + if (length == 0) { + throw std::invalid_argument("the length of in is zero."); } - if (out == nullptr && out_byte_count != 0) { - throw std::invalid_argument("out is nullptr"); + if (length > kAHEMaxRandomByteCount) { + throw std::invalid_argument("in is too large."); } - if (out_byte_count != secret_key_byte_count()) { - throw std::invalid_argument("out_byte_count is not equal to secret_key_byte_count"); + mpz_export(out, nullptr, -1, sizeof(Byte), -1, 0, in.get_mpz_t()); + if (length < out_byte_count) { + std::fill_n(out + length, out_byte_count - length, Byte('\x00')); } - bn_to_bytes(*(sk->getN()), out, n_byte_count_); - std::size_t pq_byte_count = n_byte_count_ / 2; - bn_to_bytes(*(sk->getP()), out + n_byte_count_, pq_byte_count); - bn_to_bytes(*(sk->getQ()), out + n_byte_count_ + pq_byte_count, pq_byte_count); } +#endif -void Serialization::deserialize_public_key_from_bytes( - const Byte* in, std::size_t in_byte_count, std::shared_ptr& pk) const { - if (in == nullptr && in_byte_count != 0) { - throw std::invalid_argument("in is nullptr"); +void Plaintext::serialize_to_bytes(Byte* out, std::size_t out_byte_count) const { + if (out == nullptr) { + throw std::invalid_argument("out is nullptr"); } - if (in_byte_count != public_key_byte_count()) { - throw std::invalid_argument("in_byte_count is not equal to public_key_byte_count"); +#ifdef SOLO_USE_IPCL + std::size_t pt_byte_count = (BigNumber(pt_).BitSize() + 7) / 8; + if (pt_byte_count > out_byte_count) { + throw std::invalid_argument("the byte count of pt is bigger than out_byte_count."); } - pk = std::make_shared(); - if (enable_djn_) { - BigNum n; - bn_from_bytes(in, n_byte_count_, n); - BigNum hs; - bn_from_bytes(in + n_byte_count_, in_byte_count - n_byte_count_, hs); - pk->create(n, static_cast(n_byte_count_ * 8), hs, static_cast(n_byte_count_ * 4)); - } else { - BigNum n; - bn_from_bytes(in, n_byte_count_, n); - pk->create(n, static_cast(n_byte_count_ * 8), false); + Serialization::ipcl_bn_to_bytes(BigNumber(pt_), out, out_byte_count); +#else + std::size_t pt_byte_count = (mpz_sizeinbase(pt_.get_mpz_t(), 2) + 7) / 8; + if (pt_byte_count > out_byte_count) { + throw std::invalid_argument("the byte count of pt is bigger than out_byte_count."); } + Serialization::mpz_bn_to_bytes(pt_, out, out_byte_count); +#endif } -void Serialization::deserialize_secret_key_from_bytes( - const Byte* in, std::size_t in_byte_count, std::shared_ptr& sk) const { - if (in == nullptr && in_byte_count != 0) { +void Plaintext::deserialize_from_bytes(const Byte* in, std::size_t in_byte_count) { + if (in == nullptr) { throw std::invalid_argument("in is nullptr"); } - if (in_byte_count != secret_key_byte_count()) { - throw std::invalid_argument("in_byte_count is not equal to secret_key_byte_count"); + if (in != nullptr && in_byte_count > kAHEMaxRandomByteCount) { + throw std::invalid_argument("in is too large."); } - BigNum n; - bn_from_bytes(in, n_byte_count_, n); - BigNum p; - BigNum q; - std::size_t pq_byte_count = n_byte_count_ / 2; - bn_from_bytes(in + n_byte_count_, pq_byte_count, p); - bn_from_bytes(in + n_byte_count_ + pq_byte_count, pq_byte_count, q); - sk = std::make_shared(n, p, q); +#ifdef SOLO_USE_IPCL + BigNumber bn; + Serialization::ipcl_bn_from_bytes(in, in_byte_count, bn); + pt_ = ipcl::PlainText(bn); +#else + Serialization::mpz_bn_from_bytes(in, in_byte_count, pt_); +#endif } -Plaintext& Plaintext::operator=(const ipcl::PlainText& other) noexcept { - ipcl::PlainText::operator=(other); +Plaintext& Plaintext::operator=(const Plaintext& other) noexcept { + this->pt_ = other.pt_; return *this; } -Plaintext::operator std::vector() const { - return ipcl::PlainText::operator std::vector(); +#ifdef SOLO_USE_IPCL +Plaintext::operator ipcl::PlainText() const { + return pt_; } +#else -std::size_t Plaintext::slot_count() const noexcept { - return ipcl::PlainText::getSize(); +Plaintext::operator mpz_class() const { + return pt_; } +#endif -Ciphertext& Ciphertext::operator=(const ipcl::CipherText& other) noexcept { - ipcl::CipherText::operator=(other); +void Ciphertext::serialize_to_bytes(Byte* out, std::size_t out_byte_count) const { + if (out == nullptr) { + throw std::invalid_argument("out is nullptr"); + } +#ifdef SOLO_USE_IPCL + std::size_t ct_byte_count = (ct_.getElement(0).BitSize() + 7) / 8; + if (ct_byte_count > out_byte_count) { + throw std::invalid_argument("the byte count of ct is bigger than out_byte_count"); + } + Serialization::ipcl_bn_to_bytes(ct_.getElement(0), out, out_byte_count); +#else + std::size_t ct_byte_count = (mpz_sizeinbase(ct_.get_mpz_t(), 2) + 7) / 8; + if (ct_byte_count > out_byte_count) { + throw std::invalid_argument("the byte count of ct is bigger than out_byte_count"); + } + Serialization::mpz_bn_to_bytes(ct_, out, out_byte_count); +#endif +} + +void Ciphertext::deserialize_from_bytes(const std::shared_ptr& pk, Byte* in, std::size_t in_byte_count) { + if (pk == nullptr) { + throw std::invalid_argument("pk is nullptr"); + } + if (in == nullptr) { + throw std::invalid_argument("in is nullptr"); + } + if (in != nullptr && in_byte_count > kAHEMaxRandomByteCount) { + throw std::invalid_argument("in is too large."); + } +#ifdef SOLO_USE_IPCL + BigNumber bn; + Serialization::ipcl_bn_from_bytes(in, in_byte_count, bn); + ct_ = ipcl::CipherText(*(pk->pk()), bn); +#else + Serialization::mpz_bn_from_bytes(in, in_byte_count, ct_); +#endif +} + +Ciphertext& Ciphertext::operator=(const Ciphertext& other) noexcept { + this->ct_ = other.ct_; return *this; } -Ciphertext::operator std::vector() const { - return getTexts(); +#ifdef SOLO_USE_IPCL +Ciphertext::operator ipcl::CipherText() const { + return ct_; } -std::size_t Ciphertext::slot_count() const noexcept { - return ipcl::CipherText::getSize(); +#else +Ciphertext::operator mpz_class() const { + return ct_; } +#endif void Encoder::encode(std::uint64_t in, Plaintext& out) const noexcept { - out = ipcl::PlainText(bn_from_u64(in)); +#ifdef SOLO_USE_IPCL + out = Plaintext(ipcl_bn_from_u64(in)); +#else + mpz_class bn; + mpz_import(bn.get_mpz_t(), 1, 1, sizeof(in), 0, 0, &in); + out = Plaintext(bn); +#endif } std::uint64_t Encoder::decode(const Plaintext& in) const noexcept { - return u64_from_bn(BigNum(in)); +#ifdef SOLO_USE_IPCL + return u64_from_ipcl_bn(BigNumber(ipcl::PlainText(in))); +#else + Plaintext pt; + std::uint64_t out(0); + mpz_class mod(1); + std::size_t num_bits = 8 * sizeof(std::uint64_t); + mod <<= num_bits; + mod = mpz_class(in) % mod; + mpz_export(&out, nullptr, 1, sizeof(std::uint64_t), 0, 0, mod.get_mpz_t()); + return out; +#endif } -void Encoder::encode(const std::vector& in, Plaintext& out) const noexcept { - std::vector tmp(in.size()); - std::transform(in.begin(), in.end(), tmp.begin(), [&](std::uint64_t val) { return bn_from_u64(val); }); - out = ipcl::PlainText(tmp); +void Encoder::encode(const std::vector& in, std::vector& out) const noexcept { + out.resize(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) { + encode(in[i], out[i]); + } } -void Encoder::decode(const Plaintext& in, std::vector<std::uint64_t>& out) const noexcept { - std::vector<BigNum> tmp = std::vector<BigNum>(in); - out.resize(tmp.size()); - std::transform(tmp.begin(), tmp.end(), out.begin(), [&](BigNum bn) { return u64_from_bn(bn); }); +void Encoder::decode(const std::vector<Plaintext>& in, std::vector<std::uint64_t>& out) const noexcept { + out.resize(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) { + out[i] = decode(in[i]); + } } -void KeyGenerator::get_key_pair( - std::shared_ptr<SecretKey>& sk, std::shared_ptr<PublicKey>& pk, bool enable_djn) const noexcept { +void KeyGenerator::get_key_pair(std::shared_ptr<SecretKey>& sk, std::shared_ptr<PublicKey>& pk, bool enable_djn, + PRNGScheme prng_scheme) const noexcept { +#ifdef SOLO_USE_IPCL ipcl::KeyPair key_pair = ipcl::generateKeypair(static_cast<int64_t>(key_length_), enable_djn); - sk = std::make_shared<SecretKey>( - *(key_pair.priv_key.getN()), *(key_pair.priv_key.getP()), *(key_pair.priv_key.getQ())); - pk = std::make_shared<PublicKey>(); + sk = std::make_shared<SecretKey>(key_pair.priv_key); + pk = std::make_shared<PublicKey>(key_pair.pub_key); + (void)prng_scheme; +#else + std::size_t p_length = (key_length_ + 1) / 2; + + mpz_class n; + mpz_class p; + mpz_class q; + + mpz_class gcd = 0; + mpz_class p_minus_one; + mpz_class q_minus_one; + + PRNGFactory prng_factory = PRNGFactory(prng_scheme); + auto prng = prng_factory.create(); + + std::size_t n_len = 0; + do { + p = generate_prime(p_length, prng); + q = generate_prime(p_length, prng); + if (p != q && (p % 4) == 3 && (q % 4) == 3) { + p_minus_one = p - 1; + q_minus_one = q - 1; + mpz_gcd(gcd.get_mpz_t(), p_minus_one.get_mpz_t(), q_minus_one.get_mpz_t()); + n = p * q; + n_len = mpz_sizeinbase(n.get_mpz_t(), 2); + } + } while (gcd != 2 || n_len != key_length_); + if (enable_djn) { - pk->create(*(key_pair.pub_key.getN()), key_pair.pub_key.getBits(), key_pair.pub_key.getHS(), - key_pair.pub_key.getRandBits()); + mpz_class hs; + mpz_class rand; + mpz_class n_square = n * n; + do { + rand = utils::get_random_mpz(key_length_ + 128, prng); + mpz_gcd(gcd.get_mpz_t(), rand.get_mpz_t(), n.get_mpz_t()); + } while (gcd != 1); + mpz_class x = rand % n; + mpz_class x_square = x * x; + mpz_class h = (-1 * x_square) % n; + mpz_powm(hs.get_mpz_t(), h.get_mpz_t(), n.get_mpz_t(), n_square.get_mpz_t()); + pk = std::make_shared<PublicKey>(n, hs); } else { - pk->create(*(key_pair.pub_key.getN()), key_pair.pub_key.getBits(), false); + pk = std::make_shared<PublicKey>(n); } + sk = std::make_shared<SecretKey>(n, p, q); +#endif } -Encryptor::Encryptor(const std::shared_ptr<PublicKey>& pk, bool enable_djn) { +#ifndef SOLO_USE_IPCL +mpz_class KeyGenerator::generate_prime(std::size_t bit_count, std::shared_ptr<PRNG>& prng) { + mpz_class p = 0; + do { + p = utils::get_random_mpz(bit_count, prng); + p |= mpz_class(1) << bit_count - 1; + } while (prime_test(p) == 0 || p == 0); + return p; +} + +int KeyGenerator::prime_test(const mpz_class& in) { + std::size_t length = mpz_sizeinbase(in.get_mpz_t(), 2); + return mpz_probab_prime_p(in.get_mpz_t(), static_cast<int>(miller_rabin_iteration_num(length))); +} + +std::size_t KeyGenerator::miller_rabin_iteration_num(std::size_t prime_length) { + return prime_length >= 3747 ? 3 + : prime_length >= 1345 ? 4 + : prime_length >= 476 ? 5 + : prime_length >= 400 ? 6 + : prime_length >= 347 ? 7 + : prime_length >= 308 ? 4 + : prime_length >= 55 ? 27 + : 34; +} +#endif + +Encryptor::Encryptor(const std::shared_ptr<PublicKey>& pk, PRNGScheme prng_scheme) : sk_set_(false) { if (pk == nullptr) { throw std::invalid_argument("pk is nullptr"); } - pk_ = std::make_shared<ipcl::PublicKey>(); - if (enable_djn) { - pk_->create(*(pk->getN()), pk->getBits(), pk->getHS(), pk->getRandBits()); + std::vector<Byte> pk_bytes(pk->public_key_byte_count()); + pk->serialize_to_bytes(pk_bytes.data(), pk_bytes.size()); + pk_ = std::make_shared<PublicKey>(pk->key_length(), pk->use_djn()); + pk_->deserialize_from_bytes(pk_bytes.data(), pk_bytes.size()); +#ifdef SOLO_USE_IPCL + (void)prng_scheme; +#else + r_bit_count_ = pk->use_djn() ? pk->key_length() : (pk->key_length() >> 1); + PRNGFactory prng_factory = PRNGFactory(prng_scheme); + prng_ = prng_factory.create(); +#endif +} + +Encryptor::Encryptor(const std::shared_ptr<PublicKey>& pk, const std::shared_ptr<SecretKey>& sk, PRNGScheme prng_scheme) + : sk_set_(true) { + if (pk == nullptr) { + throw std::invalid_argument("pk is nullptr"); + } + if (sk == nullptr) { + throw std::invalid_argument("sk is nullptr"); + } + std::vector<Byte> pk_bytes(pk->public_key_byte_count()); + pk->serialize_to_bytes(pk_bytes.data(), pk_bytes.size()); + pk_ = std::make_shared<PublicKey>(pk->key_length(), pk->use_djn()); + pk_->deserialize_from_bytes(pk_bytes.data(), pk_bytes.size()); +#ifdef SOLO_USE_IPCL + (void)prng_scheme; +#else + r_bit_count_ = pk->use_djn() ? pk->key_length() : (pk->key_length() >> 1); + PRNGFactory prng_factory = PRNGFactory(prng_scheme); + prng_ = prng_factory.create(); +#endif + std::vector<Byte> sk_bytes(sk->secret_key_byte_count()); + sk->serialize_to_bytes(sk_bytes.data(), sk_bytes.size()); + sk_ = std::make_shared<SecretKey>(sk->key_length()); + sk_->deserialize_from_bytes(sk_bytes.data(), sk_bytes.size()); +} + +void Encryptor::encrypt(const Plaintext& in, Ciphertext& out) noexcept { +#ifdef SOLO_USE_IPCL + ipcl::setHybridMode(ipcl::HybridMode::IPP); + ipcl::PlainText pt(in); + out = Ciphertext(pk_->pk()->encrypt(pt, true)); + ipcl::setHybridOff(); +#else + mpz_class r = 0; + if (pk_->use_djn()) { + r = utils::get_random_mpz(r_bit_count_, prng_); + if (sk_set_) { + sk_->powm_crt(pk_->hs(), r, r); + } else { + mpz_powm(r.get_mpz_t(), pk_->hs().get_mpz_t(), r.get_mpz_t(), pk_->n_square().get_mpz_t()); + } } else { - pk_->create(*(pk->getN()), pk->getBits(), false); + do { + r = utils::get_random_mpz(r_bit_count_, prng_); + } while (r == 0 || r > pk_->n()); + if (sk_set_) { + sk_->powm_crt(r, pk_->n(), r); + } else { + mpz_powm(r.get_mpz_t(), r.get_mpz_t(), pk_->n().get_mpz_t(), pk_->n_square().get_mpz_t()); + } } + mpz_class bn_out = (1 + mpz_class(in) * pk_->n()) * r % pk_->n_square(); + out = Ciphertext(bn_out); +#endif } -void Encryptor::encrypt(const Plaintext& in, Ciphertext& out) const noexcept { +void Encryptor::encrypt_many( + const std::vector<Plaintext>& in, std::vector<Ciphertext>& out, std::size_t num_threads) noexcept { +#ifdef SOLO_USE_IPCL ipcl::setHybridMode(ipcl::HybridMode::IPP); - out = pk_->encrypt(in, true); + std::vector<BigNumber> bn_v(in.size()); + std::transform( + in.begin(), in.end(), bn_v.begin(), [](const Plaintext& pt_i) { return BigNumber(ipcl::PlainText(pt_i)); }); + ipcl::PlainText pt(bn_v); + bn_v.clear(); + std::vector<BigNumber> bn_ct = pk_->pk()->encrypt(pt, true).getTexts(); + out.resize(in.size()); + std::transform(bn_ct.begin(), bn_ct.end(), out.begin(), + [this](const BigNumber& bn_i) { return Ciphertext(*(pk_->pk()), bn_i); }); ipcl::setHybridOff(); + (void)num_threads; +#else + out.resize(in.size()); + std::vector<mpz_class> r(in.size(), 0); + for (std::size_t i = 0; i < r.size(); ++i) { + if (pk_->use_djn()) { + r[i] = utils::get_random_mpz(r_bit_count_, prng_); + } else { + do { + r[i] = utils::get_random_mpz(r_bit_count_, prng_); + } while (r[i] == 0 || r[i] > pk_->n()); + } + } +#pragma omp parallel for num_threads(num_threads) + for (std::size_t i = 0; i < in.size(); ++i) { + if (pk_->use_djn()) { + if (sk_set_) { + sk_->powm_crt(pk_->hs(), r[i], r[i]); + } else { + mpz_powm(r[i].get_mpz_t(), pk_->hs().get_mpz_t(), r[i].get_mpz_t(), pk_->n_square().get_mpz_t()); + } + } else { + if (sk_set_) { + sk_->powm_crt(r[i], pk_->n(), r[i]); + } else { + mpz_powm(r[i].get_mpz_t(), r[i].get_mpz_t(), pk_->n().get_mpz_t(), pk_->n_square().get_mpz_t()); + } + } + mpz_class bn_out = (1 + mpz_class(in[i]) * pk_->n()) * r[i] % pk_->n_square(); + out[i] = Ciphertext(bn_out); + } +#endif } Decryptor::Decryptor(const std::shared_ptr<SecretKey>& sk) { if (sk == nullptr) { throw std::invalid_argument("sk is nullptr"); } - sk_ = std::make_shared<SecretKey>(*(sk->getN()), *(sk->getP()), *(sk->getQ())); + + std::vector<Byte> sk_bytes(sk->secret_key_byte_count()); + sk->serialize_to_bytes(sk_bytes.data(), sk_bytes.size()); + sk_ = std::make_shared<SecretKey>(sk->key_length()); + sk_->deserialize_from_bytes(sk_bytes.data(), sk_bytes.size()); } void Decryptor::decrypt(const Ciphertext& in, Plaintext& out) const noexcept { +#ifdef SOLO_USE_IPCL ipcl::setHybridMode(ipcl::HybridMode::IPP); - out = sk_->decrypt(in); + ipcl::CipherText ct(in); + out = Plaintext(sk_->sk()->decrypt(ct)); ipcl::setHybridOff(); +#else + mpz_class plain; + sk_->decrypt(mpz_class(in), plain); + out = Plaintext(plain); +#endif +} + +void Decryptor::decrypt_many( + const std::vector<Ciphertext>& in, std::vector<Plaintext>& out, std::size_t num_threads) const noexcept { +#ifdef SOLO_USE_IPCL + ipcl::setHybridMode(ipcl::HybridMode::IPP); + std::vector<BigNumber> bn_v(in.size()); + std::transform( + in.begin(), in.end(), bn_v.begin(), [](const Ciphertext& pt_i) { return ipcl::CipherText(pt_i)[0]; }); + ipcl::CipherText ct_0(in[0]); + ipcl::CipherText ct(*(ct_0.getPubKey()), bn_v); + bn_v.clear(); + std::vector<BigNumber> bn_pt = sk_->sk()->decrypt(ct).getTexts(); + out.resize(in.size()); + std::transform(bn_pt.begin(), bn_pt.end(), out.begin(), [](const BigNumber& bn_i) { return Plaintext(bn_i); }); + ipcl::setHybridOff(); + (void)num_threads; +#else + out.resize(in.size()); +#pragma omp parallel for num_threads(num_threads) + for (std::size_t i = 0; i < in.size(); ++i) { + mpz_class plain; + sk_->decrypt(mpz_class(in[i]), plain); + out[i] = Plaintext(plain); + } +#endif +} + +Evaluator::Evaluator(const std::shared_ptr<PublicKey>& pk) : sk_set_(false) { + if (pk == nullptr) { + throw std::invalid_argument("pk is nullptr"); + } + std::vector<Byte> pk_bytes(pk->public_key_byte_count()); + pk->serialize_to_bytes(pk_bytes.data(), pk_bytes.size()); + pk_ = std::make_shared<PublicKey>(pk->key_length(), pk->use_djn()); + pk_->deserialize_from_bytes(pk_bytes.data(), pk_bytes.size()); + encryptor_ = std::make_shared<Encryptor>(pk_); +} + +Evaluator::Evaluator(const std::shared_ptr<PublicKey>& pk, const std::shared_ptr<SecretKey>& sk) : sk_set_(true) { + if (pk == nullptr) { + throw std::invalid_argument("pk is nullptr"); + } + if (sk == nullptr) { + throw std::invalid_argument("sk is nullptr"); + } + std::vector<Byte> pk_bytes(pk->public_key_byte_count()); + pk->serialize_to_bytes(pk_bytes.data(), pk_bytes.size()); + pk_ = std::make_shared<PublicKey>(pk->key_length(), pk->use_djn()); + pk_->deserialize_from_bytes(pk_bytes.data(), pk_bytes.size()); + + std::vector<Byte> sk_bytes(sk->secret_key_byte_count()); + sk->serialize_to_bytes(sk_bytes.data(), sk_bytes.size()); + sk_ = std::make_shared<SecretKey>(sk->key_length()); + sk_->deserialize_from_bytes(sk_bytes.data(), sk_bytes.size()); + encryptor_ = std::make_shared<Encryptor>(pk_); } void Evaluator::add(const Ciphertext& in_0, const Ciphertext& in_1, Ciphertext& out) const noexcept { - out = in_0 + in_1; +#ifdef SOLO_USE_IPCL + out = Ciphertext(ipcl::CipherText(in_0) + ipcl::CipherText(in_1)); +#else + mpz_class bn_out = mpz_class(in_0) * mpz_class(in_1) % pk_->n_square(); + out = Ciphertext(bn_out); +#endif +} + +void Evaluator::add_many(const std::vector<Ciphertext>& in_0, const std::vector<Ciphertext>& in_1, + std::vector<Ciphertext>& out, std::size_t num_threads) const { + if (in_0.size() != in_1.size()) { + throw std::invalid_argument("Input size of two vector is not equal."); + } +#ifdef SOLO_USE_IPCL + std::vector<BigNumber> bn_v0(in_0.size()); + std::vector<BigNumber> bn_v1(in_1.size()); + std::transform( + in_0.begin(), in_0.end(), bn_v0.begin(), [](const Ciphertext& pt_i) { return ipcl::CipherText(pt_i)[0]; }); + std::transform( + in_1.begin(), in_1.end(), bn_v1.begin(), [](const Ciphertext& pt_i) { return ipcl::CipherText(pt_i)[0]; }); + auto ct = ipcl::CipherText(*(ipcl::CipherText(in_0[0]).getPubKey()), bn_v0) + + ipcl::CipherText(*(ipcl::CipherText(in_1[0]).getPubKey()), bn_v1); + std::vector<BigNumber> bn_ct = ct.getTexts(); + out.resize(in_0.size()); + std::transform(bn_ct.begin(), bn_ct.end(), out.begin(), + [&ct](const BigNumber& bn_i) { return Ciphertext(*(ct.getPubKey()), bn_i); }); + (void)num_threads; +#else + out.resize(in_0.size()); +#pragma omp parallel for num_threads(num_threads) + for (std::size_t i = 0; i < in_0.size(); ++i) { + add(in_0[i], in_1[i], out[i]); + } +#endif +} + +void Evaluator::add(const Ciphertext& in_0, const Plaintext& in_1, Ciphertext& out) noexcept { + Ciphertext ct_1; + encryptor_->encrypt(in_1, ct_1); + add(in_0, ct_1, out); } -void Evaluator::add(const Ciphertext& in_0, const Plaintext& in_1, Ciphertext& out) const noexcept { - out = in_0 + in_1; +void Evaluator::add_many(const std::vector<Ciphertext>& in_0, const std::vector<Plaintext>& in_1, + std::vector<Ciphertext>& out, std::size_t num_threads) { + if (in_0.size() != in_1.size()) { + throw std::invalid_argument("Input size of two vector is not equal."); + } + std::vector<Ciphertext> ct_1; + encryptor_->encrypt_many(in_1, ct_1, num_threads); + out.resize(in_0.size()); + add_many(in_0, ct_1, out, num_threads); } void Evaluator::mul(const Ciphertext& in_0, const Plaintext& in_1, Ciphertext& out) const noexcept { - out = in_0 * in_1; +#ifdef SOLO_USE_IPCL + out = Ciphertext(ipcl::CipherText(in_0) * ipcl::PlainText(in_1)); +#else + mpz_class bn_out; + if (sk_set_) { + sk_->powm_crt(mpz_class(in_0), mpz_class(in_1), bn_out); + } else { + mpz_powm(bn_out.get_mpz_t(), mpz_class(in_0).get_mpz_t(), mpz_class(in_1).get_mpz_t(), + pk_->n_square().get_mpz_t()); + } + out = Ciphertext(bn_out); +#endif +} + +void Evaluator::mul_many(const std::vector<Ciphertext>& in_0, const std::vector<Plaintext>& in_1, + std::vector<Ciphertext>& out, std::size_t num_threads) const { + if (in_0.size() != in_1.size()) { + throw std::invalid_argument("Input vector sizes are not equal."); + } +#ifdef SOLO_USE_IPCL + std::vector<BigNumber> bn_v0(in_0.size()); + std::vector<BigNumber> bn_v1(in_1.size()); + std::transform( + in_0.begin(), in_0.end(), bn_v0.begin(), [](const Ciphertext& pt_i) { return ipcl::CipherText(pt_i)[0]; }); + std::transform(in_1.begin(), in_1.end(), bn_v1.begin(), + [](const Plaintext& pt_i) { return BigNumber(ipcl::PlainText(pt_i)); }); + auto ct = ipcl::CipherText(*(ipcl::CipherText(in_0[0]).getPubKey()), bn_v0) * ipcl::PlainText(bn_v1); + std::vector<BigNumber> bn_ct = ct.getTexts(); + out.resize(in_0.size()); + std::transform(bn_ct.begin(), bn_ct.end(), out.begin(), + [&ct](const BigNumber& bn_i) { return Ciphertext(*(ct.getPubKey()), bn_i); }); + (void)num_threads; +#else + out.resize(in_0.size()); +#pragma omp parallel for num_threads(num_threads) + for (std::size_t i = 0; i < in_0.size(); ++i) { + mul(in_0[i], in_1[i], out[i]); + } +#endif } namespace utils { -void bn_lshift(BigNum& in, const std::size_t bits) { - std::size_t length = (bits + 1 + 31) / 32; +#ifdef SOLO_USE_IPCL +void bn_lshift(BigNumber& in, const std::size_t bits) { + if (bits > kAHEMaxRandomBits) { + throw std::invalid_argument("Shift bits is too large."); + } + std::size_t length = bits / 32 + 1; std::size_t remainder = bits % 32; std::vector<std::uint32_t> temp(length, 0); temp[length - 1] = 1 << remainder; @@ -285,12 +921,30 @@ void bn_lshift(BigNum& in, const std::size_t bits) { in *= shift_bn; } -BigNum get_random_bn(std::size_t bits) { - return ipcl::getRandomBN(bits); +BigNumber get_random_bn(std::size_t bits) { + if (bits > kAHEMaxRandomBits) { + throw std::invalid_argument("random bits is too large."); + } + return ipcl::getRandomBN(static_cast<int>(bits)); } +#else +mpz_class get_random_mpz(std::size_t bits, std::shared_ptr<PRNG>& prng) { + if (bits > kAHEMaxRandomBits) { + throw std::invalid_argument("random bits is too large."); + } + mpz_class out = 0; + std::size_t byte_count = (bits + 7) / 8; + std::vector<Byte> in(byte_count); + do { + prng->generate(byte_count, in.data()); + mpz_import(out.get_mpz_t(), in.size(), 1, 1, 0, 0, in.data()); + out >>= byte_count * 8 - bits; + } while (out == 0 || mpz_sizeinbase(out.get_mpz_t(), 2) != bits); + return out; +} +#endif } // namespace utils } // namespace ahepaillier } // namespace solo } // namespace petace -#endif diff --git a/src/solo/ahe_paillier.h b/src/solo/ahe_paillier.h index c2b6cd1..2434fcc 100644 --- a/src/solo/ahe_paillier.h +++ b/src/solo/ahe_paillier.h @@ -14,58 +14,92 @@ #pragma once -#include "solo/util/defines.h" - -#ifdef SOLO_USE_IPCL - #include <cstddef> #include <cstdint> #include <memory> #include <vector> +#include "solo/prng.h" +#include "solo/util/defines.h" + +#ifdef SOLO_USE_IPCL #include "ipcl/ipcl.hpp" +#else +#include <emmintrin.h> +#include <wmmintrin.h> + +#include "gmp.h" +#include "gmpxx.h" +#endif namespace petace { namespace solo { namespace ahepaillier { -using SecretKey = ipcl::PrivateKey; -using PublicKey = ipcl::PublicKey; -using BigNum = BigNumber; - /** - * @brief Provides methods to serialize Paillier cryptosystem objects. + * @brief PublicKey in the Paillier cryptosystem. */ -class Serialization { +class PublicKey { public: /** - * @brief Creates a serialization instance. + * @brief Constructs an empty public key that can be later deserialized into. * - * @param[in] key_length The length of key in bits - * @param[in] enable_djn Enable the Damgard-Jurik-Nielsen scheme - * @throws std::invalid_argument if key_length is not 1024 or 2048 + * @param[in] key_length The length of key in bits. + * @param[in] enable_djn Enable the Damgard-Jurik-Nielsen scheme. + * @throws std::invalid_argument if key_length is not 1024 or 2048. */ - Serialization(std::size_t key_length, bool enable_djn = true); + PublicKey(std::size_t key_length, bool enable_djn); /** - * @brief Converts a big number into an array of bytes. + * @brief Default constructor is deleted. + */ + PublicKey() = delete; + +#ifdef SOLO_USE_IPCL + /** + * @brief Constructs a public key in IPCL. * - * @param[in] in The big number to be converted - * @param[out] out The pointer of byte array to write to + * @param[in] pk public key in IPCL. + * @throws std::invalid_argument if key_length is not 1024 or 2048. + */ + explicit PublicKey(const ipcl::PublicKey& pk); +#else + /** + * @brief Constructs a public key with n, where n is an admissible RSA modulus n = pq. + * + * @param[in] n n of public key in the Paillier scheme. + * @throws std::invalid_argument if key_length is not 1024 or 2048. + */ + explicit PublicKey(const mpz_class& n); + + /** + * @brief Constructs a public key with n and hs. + * + * @param[in] n n of public key in the Paillier scheme. + * @param[in] hs hs of public key in the Damgard-Jurik-Nielsen Paillier scheme. + * @throws std::invalid_argument if n is not 1024-bit or 2048-bit + */ + PublicKey(const mpz_class& n, const mpz_class& hs); +#endif + /** + * @brief Serializes public key into an array of bytes. + * + * @param[out] out The pointer of byte array to write to. * @param[out] out_byte_count The number of bytes allocated in the byte array. - * @throws std::invalid_argument if out is nullptr + * @throws std::invalid_argument if this public key is not set or out is nullptr or out_byte_count is not equal + * to the number of bytes in this public key. */ - static void bn_to_bytes(const BigNum& in, Byte* out, std::size_t out_byte_count); + void serialize_to_bytes(Byte* out, std::size_t out_byte_count) const; /** - * @brief Creates a big number from an array of bytes. + * @brief Creates public key from an array of bytes. * * @param[in] in The pointer of the byte array to read from. * @param[in] in_byte_count The number of bytes allocated in the byte array. - * @param[out] out The big number to write to. - * @throws std::invalid_argument if in is nullptr + * @throws std::invalid_argument if this public key is set or in is nullptr or in_byte_count is not equal to + * the number of bytes in this public key. */ - static void bn_from_bytes(const Byte* in, std::size_t in_byte_count, BigNum& out); + void deserialize_from_bytes(const Byte* in, std::size_t in_byte_count); /** * @brief Returns the number of bytes in a public key. @@ -73,135 +107,419 @@ class Serialization { std::size_t public_key_byte_count() const noexcept; /** - * @brief Returns the number of bytes in a secret key. + * @brief Returns the length of key in bits. */ - std::size_t secret_key_byte_count() const noexcept; + std::size_t key_length() const { + return key_length_; + } /** - * @brief Serializes a public key into an array of bytes. + * @brief Returns enabled djn or not. + */ + bool use_djn() const { + return enable_djn_; + } + +#ifdef SOLO_USE_IPCL + /** + * @brief Returns the the pointer of pk. + */ + std::shared_ptr<ipcl::PublicKey> pk() const { + return pk_; + } +#else + /** + * @brief Returns n of public key. + */ + const mpz_class& n() const { + return n_; + } + + /** + * @brief Returns generator of public key. + */ + const mpz_class& g() const { + return g_; + } + + /** + * @brief Returns n_square of public key. + */ + const mpz_class& n_square() const { + return n_square_; + } + + /** + * @brief Returns hs of public key. + */ + const mpz_class& hs() const { + return hs_; + } +#endif + +private: +#ifdef SOLO_USE_IPCL + std::shared_ptr<ipcl::PublicKey> pk_ = nullptr; +#else + mpz_class n_ = 0; + mpz_class g_ = 0; + mpz_class n_square_ = 0; + mpz_class hs_ = 0; +#endif + std::size_t key_length_ = 0; + std::size_t n_byte_count_ = 0; + const bool enable_djn_; + bool pk_set_ = false; +}; + +/** + * @brief SecretKey in the Paillier cryptosystem. + */ +class SecretKey { +public: + /** + * @brief Constructs an empty secret key that can be later deserialized into. * - * @param[in] pk The public key to be serialized - * @param[out] out The pointer of byte array to write to - * @param[out] out_byte_count The number of bytes allocated in the byte array - * @throws std::invalid_argument if pk or out is nullptr or if out_byte_count is not equal to public_key_byte_count + * @param[in] key_length The length of key in bits. + */ + explicit SecretKey(std::size_t key_length); + + /** + * @brief Default constructor is deleted. */ - void serialize_public_key_to_bytes( - const std::shared_ptr<PublicKey>& pk, Byte* out, std::size_t out_byte_count) const; + SecretKey() = delete; +#ifdef SOLO_USE_IPCL + /** + * @brief Constructs a secret key in IPCL. + * + * @param[in] sk secret key in IPCL. + */ + explicit SecretKey(const ipcl::PrivateKey& sk); +#else + /** + * @brief Constructs a secret key with (n, p, q), where n is an admissible RSA modulus n = pq, p, q are large odd + * primes. + * + * @param[in] n n of secret key in the Paillier scheme. + * @param[in] p p of secret key in the Paillier scheme. + * @param[in] q q of secret key in the Paillier scheme. + * @throws std::invalid_argument if key_length is not 1024 or 2048. + */ + SecretKey(const mpz_class& n, const mpz_class& p, const mpz_class& q); +#endif /** - * @brief Serializes a secret key into an array of bytes. + * @brief Serializes secret key into an array of bytes. * - * @param[in] sk The secret key to be serialized - * @param[out] out The pointer of byte array to write to - * @param[out] out_byte_count The number of bytes allocated in the byte array - * @throws std::invalid_argument if sk or out is nullptr or if out_byte_count is not equal to secret_key_byte_count + * @param[out] out The pointer of byte array to write to. + * @param[out] out_byte_count The number of bytes allocated in the byte array. + * @throws std::invalid_argument if this secret key is not set or out is nullptr orout_byte_count is not equal to + * the number of bytes in this secret key. */ - void serialize_secret_key_to_bytes( - const std::shared_ptr<SecretKey>& sk, Byte* out, std::size_t out_byte_count) const; + void serialize_to_bytes(Byte* out, std::size_t out_byte_count) const; /** - * @brief Creates a public key from an array of bytes. + * @brief Creates secret key from an array of bytes. * - * @param[in] in The pointer of the byte array to read from - * @param[in] in_byte_count The number of bytes allocated in the byte array - * @param[out] pk The public key to write to - * @throws std::invalid_argument if in is nullptr or in_byte_count is not equal to public_key_byte_count + * @param[in] in The pointer of the byte array to read from. + * @param[in] in_byte_count The number of bytes allocated in the byte array. + * @throws std::invalid_argument if this secret key is set or in is nullptr or in_byte_count is not equal to + * the number of bytes in this secret key. + */ + void deserialize_from_bytes(const Byte* in, std::size_t in_byte_count); + + /** + * @brief Returns the number of bytes in a secret key. */ - void deserialize_public_key_from_bytes( - const Byte* in, std::size_t in_byte_count, std::shared_ptr<PublicKey>& pk) const; + std::size_t secret_key_byte_count() const noexcept; /** - * @brief Creates a secret key from an array of bytes. + * @brief Returns the length of key in bits. + */ + std::size_t key_length() const { + return key_length_; + } +#ifdef SOLO_USE_IPCL + /** + * @brief Returns the the pointer of sk. + */ + std::shared_ptr<ipcl::PrivateKey> sk() const { + return sk_; + } +#else + /** + * @brief Decrypts a ciphertext. * - * @param[in] in The pointer of the byte array to read from - * @param[in] in_byte_count The number of bytes allocated in the byte array - * @param[out] sk The secret key to write to - * @throws std::invalid_argument if in is nullptr or in_byte_count is not equal to secret_key_byte_count + * @param[in] in The ciphertext. + * @param[in] out The decrypted result. */ - void deserialize_secret_key_from_bytes( - const Byte* in, std::size_t in_byte_count, std::shared_ptr<SecretKey>& sk) const; + void decrypt(const mpz_class& in, mpz_class& out) const; + + /** + * @brief Calculates base^exponent mod n^2 with CRT optimization. + * + * @param[in] base The base. + * @param[in] exponent The exponent. + * @param[in] out The result. + */ + void powm_crt(const mpz_class& base, const mpz_class& exponent, mpz_class& out) const; + + /** + * @brief Returns n of secret key. + */ + const mpz_class& n() const { + return n_; + } + + /** + * @brief Returns generator of secret key. + */ + const mpz_class& g() const { + return g_; + } + + /** + * @brief Returns n_square of secret key. + */ + const mpz_class& n_square() const { + return n_square_; + } +#endif private: +#ifdef SOLO_USE_IPCL + std::shared_ptr<ipcl::PrivateKey> sk_ = nullptr; +#else + void initilize_sk(); + + mpz_class n_ = 0; + mpz_class g_ = 0; + mpz_class n_square_ = 0; + + mpz_class lambda_ = 0; + mpz_class p_ = 0; + mpz_class q_ = 0; + mpz_class p_square_ = 0; + mpz_class q_square_ = 0; + + mpz_class p_square_inv_ = 0; + mpz_class q_square_inv_ = 0; + + mpz_class p_inv_ = 0; + mpz_class q_inv_ = 0; + + mpz_class hp_ = 0; + mpz_class hq_ = 0; +#endif std::size_t key_length_ = 0; std::size_t n_byte_count_ = 0; - bool enable_djn_ = false; + bool sk_set_ = false; +}; + +/** + * @brief Provides methods to serialize big number objects. + */ +class Serialization { +public: +#ifdef SOLO_USE_IPCL + /** + * @brief Converts a big number into an array of bytes. + * + * @param[in] in The big number to be converted. + * @param[out] out The pointer of byte array to write to. + * @param[out] out_byte_count The number of bytes allocated in the byte array. + * @throws std::invalid_argument if out is nullptr. + */ + static void ipcl_bn_to_bytes(const BigNumber& in, Byte* out, std::size_t out_byte_count); + + /** + * @brief Creates a big number from an array of bytes. + * + * @param[in] in The pointer of the byte array to read from. + * @param[in] in_byte_count The number of bytes allocated in the byte array. + * @param[out] out The big number to write to. + * @throws std::invalid_argument if in is nullptr. + */ + static void ipcl_bn_from_bytes(const Byte* in, std::size_t in_byte_count, BigNumber& out); +#else + /** + * @brief Converts a big number into an array of bytes. + * + * @param[in] in The big number to be converted. + * @param[out] out The pointer of byte array to write to. + * @param[out] out_byte_count The number of bytes allocated in the byte array. + * @throws std::invalid_argument if out is nullptr. + */ + static void mpz_bn_to_bytes(const mpz_class& in, Byte* out, std::size_t out_byte_count); + /** + * @brief Creates a big number from an array of bytes. + * + * @param[in] in The pointer of the byte array to read from. + * @param[in] in_byte_count The number of bytes allocated in the byte array. + * @param[out] out The big number to write to. + * @throws std::invalid_argument if in is nullptr. + */ + static void mpz_bn_from_bytes(const Byte* in, std::size_t in_byte_count, mpz_class& out); +#endif }; /** - * @brief An array of plaintexts in Paillier cryptosystem. + * @brief Plaintext in the Paillier cryptosystem. */ -class Plaintext : public ipcl::PlainText { +class Plaintext { public: /** * @brief Default constructor. */ Plaintext() = default; +#ifdef SOLO_USE_IPCL /** - * @brief Constructs a Plaintext from a vector of BigNumber elements. + * @brief Constructs a Plaintext from a BigNumber element. * - * @param[in] bn_v The vector of BigNumber elements + * @param[in] bn BigNumber element. */ - explicit Plaintext(const std::vector<BigNumber>& bn_v) : ipcl::PlainText(bn_v) { + explicit Plaintext(const BigNumber& bn) : pt_(bn) { } +#else + /** + * @brief Constructs a Plaintext from a mpz_class element. + * + * @param[in] bn mpz_class element. + */ + explicit Plaintext(const mpz_class& bn) : pt_(bn) { + } +#endif + + /** + * @brief Serializes plaintext into an array of bytes. + * + * @param[out] out The pointer of byte array to write to. + * @param[out] out_byte_count The number of bytes allocated in the byte array. + * @throws std::invalid_argument if out is nullptr. + */ + void serialize_to_bytes(Byte* out, std::size_t out_byte_count) const; /** - * @brief Copies a Plaintext from an ipcl::PlainText. + * @brief Creates plaintext from an array of bytes. * - * @param[in] other The ipcl::PlainText to copy from + * @param[in] in The pointer of the byte array to read from. + * @param[in] in_byte_count The number of bytes allocated in the byte array. + * @throws std::invalid_argument if in is nullptr. */ - Plaintext& operator=(const ipcl::PlainText& other) noexcept; + void deserialize_from_bytes(const Byte* in, std::size_t in_byte_count); /** - * @brief Converts a Plaintext into a vector of BigNumber elements. + * @brief Copies a Plaintext from an Plaintext. + * + * @param[in] other The Plaintext to copy from */ - operator std::vector<BigNumber>() const; + Plaintext& operator=(const Plaintext& other) noexcept; +#ifdef SOLO_USE_IPCL + /** + * @brief Converts a Plaintext into a BigNumber element. + */ + operator ipcl::PlainText() const; +#else /** - * @brief Returns the number of plaintexts stored in this Plaintext. + * @brief Converts a Plaintext into a mpz_class element. */ - std::size_t slot_count() const noexcept; + operator mpz_class() const; +#endif + +private: +#ifdef SOLO_USE_IPCL + ipcl::PlainText pt_; +#else + mpz_class pt_ = 0; +#endif }; /** - * @brief An array of ciphertexts in Paillier cryptosystem. + * @brief Ciphertext in the Paillier cryptosystem. */ -class Ciphertext : public ipcl::CipherText { +class Ciphertext { public: /** * @brief Default constructor. */ Ciphertext() = default; +#ifdef SOLO_USE_IPCL + /** + * @brief Constructs a Ciphertext from a BigNumber element and a public key. + * + * @param[in] pk The public key. + * @param[in] bn BigNumber element. + */ + Ciphertext(const ipcl::PublicKey& pk, const BigNumber& bn) : ct_(pk, bn) { + } /** - * @brief Constructs a Ciphertext from a vector of BigNumber elements and a public key. + * @brief Constructs a Ciphertext from a ipcl::CipherText. + * + * @param[in] ct ipcl::CipherText. + */ + explicit Ciphertext(const ipcl::CipherText ct) : ct_(ct) { + } +#else + /** + * @brief Constructs a Ciphertext from a mpz_class element. * - * @param[in] pk The public key - * @param[in] bn_vec The vector of BigNumber elements + * @param[in] bn mpz_class element. */ - explicit Ciphertext(const PublicKey& pk, const std::vector<BigNumber>& bn_vec) : ipcl::CipherText(pk, bn_vec) { + explicit Ciphertext(const mpz_class& bn) : ct_(bn) { } +#endif + + /** + * @brief Serializes the big number of ciphertext into an array of bytes. + * + * @param[out] out The pointer of byte array to write to. + * @param[out] out_byte_count The number of bytes allocated in the byte array. + * @throws std::invalid_argument if out is nullptr. + */ + void serialize_to_bytes(Byte* out, std::size_t out_byte_count) const; /** - * @brief Copies a Ciphertext from an ipcl::CipherText. + * @brief Creates ciphertext from public key and an array of bytes. * - * @param[in] other The ipcl::CipherText to copy from + * @param[in] pk The public key. + * @param[in] in The pointer of the byte array to read from. + * @param[in] in_byte_count The number of bytes allocated in the byte array. + * @throws std::invalid_argument if in is nullptr or pk is nullptr. */ - Ciphertext& operator=(const ipcl::CipherText& other) noexcept; + void deserialize_from_bytes(const std::shared_ptr<PublicKey>& pk, Byte* in, std::size_t in_byte_count); /** - * @brief Converts a Ciphertext into a vector of BigNumber elements. + * @brief Copies a Ciphertext from a Ciphertext. + * + * @param[in] other The Ciphertext to copy from. */ - operator std::vector<BigNumber>() const; + Ciphertext& operator=(const Ciphertext& other) noexcept; +#ifdef SOLO_USE_IPCL /** - * @brief Returns the number of ciphertexts stored in this Ciphertext. + * @brief Converts a Ciphertext into a ipcl::CipherText. */ - std::size_t slot_count() const noexcept; + operator ipcl::CipherText() const; +#else + /** + * @brief Converts a Ciphertext into a mpz_class element. + */ + operator mpz_class() const; +#endif + +private: +#ifdef SOLO_USE_IPCL + ipcl::CipherText ct_; +#else + mpz_class ct_ = 0; +#endif }; /** - * @brief Provides methods to encode/decode one or multiple integers to/from a Plaintext. + * @brief Provides methods to encode/decode integer to/from Plaintext. */ class Encoder { public: @@ -214,33 +532,33 @@ class Encoder { /** * @brief Encodes one integer to a Plaintext. * - * @param[in] in The integer - * @param[out] out The Plaintext to write to + * @param[in] in The integer. + * @param[out] out The Plaintext to write to. */ void encode(std::uint64_t in, Plaintext& out) const noexcept; /** * @brief Returns the integer decoded from a Plaintext. * - * @param[in] in The Plaintext + * @param[in] in The Plaintext. */ std::uint64_t decode(const Plaintext& in) const noexcept; /** - * @brief Encodes a vector of integers to a Plaintext. + * @brief Encodes a vector of integers to a vector of Plaintexts. * - * @param[in] in The integer vector - * @param[out] out The Plaintext to write to + * @param[in] in The vector of integers. + * @param[out] out The vector of Plaintexts to write to. */ - void encode(const std::vector<std::uint64_t>& in, Plaintext& out) const noexcept; + void encode(const std::vector<std::uint64_t>& in, std::vector<Plaintext>& out) const noexcept; /** - * @brief Decodes a vector of integers from a Plaintext. + * @brief Decodes a vector of integers from a vector of Plaintexts. * - * @param[in] in The Plaintext - * @param[out] out The integer vector to write to + * @param[in] in The vector of Plaintexts. + * @param[out] out The vector of integers to write to. */ - void decode(const Plaintext& in, std::vector<std::uint64_t>& out) const noexcept; + void decode(const std::vector<Plaintext>& in, std::vector<std::uint64_t>& out) const noexcept; }; /** @@ -251,8 +569,8 @@ class KeyGenerator { /** * @brief Constructs a key generator for a given key length. * - * @param[in] key_length The key length in bits - * @throws std::invalid_argument if key_length is less than 1024 or greater than 2048 + * @param[in] key_length The key length in bits. + * @throws std::invalid_argument if key_length is less than 1024 or greater than 2048. */ explicit KeyGenerator(std::size_t key_length) : key_length_(key_length) { if (key_length < 1024) { @@ -266,64 +584,126 @@ class KeyGenerator { /** * @brief Creates a pair of keys. * - * @param[out] sk The secret key - * @param[out] pk The public key - * @param[in] enable_djn Enable the Damgard-Jurik-Nielsen scheme + * @param[out] sk The secret key. + * @param[out] pk The public key. + * @param[in] enable_djn Enable the Damgard-Jurik-Nielsen scheme. + * @param[in] prng_scheme The prng scheme to decide which pseudorandom number generator to use. If SOLO_USE_IPCL is + * ON, this parameter is ignored. */ - void get_key_pair( - std::shared_ptr<SecretKey>& sk, std::shared_ptr<PublicKey>& pk, bool enable_djn = true) const noexcept; + void get_key_pair(std::shared_ptr<SecretKey>& sk, std::shared_ptr<PublicKey>& pk, bool enable_djn = true, + PRNGScheme prng_scheme = PRNGScheme::BLAKE2Xb) const noexcept; private: +#ifndef SOLO_USE_IPCL + static mpz_class generate_prime(std::size_t bit_count, std::shared_ptr<PRNG>& prng); + + static int prime_test(const mpz_class& in); + + static std::size_t miller_rabin_iteration_num(std::size_t prime_length); +#endif std::size_t key_length_ = 0; }; /** - * @brief Provides methods that encrypt a plaintext into a ciphertext. + * @brief Provides methods that encrypt plaintexts into ciphertexts. */ class Encryptor { public: /** * @brief Constructs an encryptor with a public key. * - * @param[in] pk The public key - * @param[in] enable_djn - * @throws std::invalid_argument if pk is nullptr + * @param[in] pk The public key. + * @param[in] prng_scheme The prng scheme to decide which pseudorandom number generator to use. If SOLO_USE_IPCL is + * ON, this parameter is ignored. + * @throws std::invalid_argument if pk is nullptr. + */ + explicit Encryptor(const std::shared_ptr<PublicKey>& pk, PRNGScheme prng_scheme = PRNGScheme::BLAKE2Xb); + + /** + * @brief Constructs an encryptor with a public key and a secret key. + * + * Adding a secret key can accelerate encryption. + * + * @param[in] pk The public key. + * @param[in] sk The secret key. + * @param[in] prng_scheme The prng scheme to decide which pseudorandom number generator to use. If SOLO_USE_IPCL is + * ON, this parameter is ignored. + * @throws std::invalid_argument if pk is nullptr or sk is nullptr. + */ + explicit Encryptor(const std::shared_ptr<PublicKey>& pk, const std::shared_ptr<SecretKey>& sk, + PRNGScheme prng_scheme = PRNGScheme::BLAKE2Xb); + + /** + * @brief Default constructor is deleted. */ - explicit Encryptor(const std::shared_ptr<PublicKey>& pk, bool enable_djn = true); + Encryptor() = delete; /** * @brief Encrypts a plaintext into a ciphertext. * - * @param[in] in The plaintext - * @param[out] out The resulting ciphertext + * @param[in] in The plaintext. + * @param[out] out The resulting ciphertext. + */ + void encrypt(const Plaintext& in, Ciphertext& out) noexcept; + + /** + * @brief Encrypts a vector of plaintexts into a vector of ciphertexts. + * + * @param[in] in The vector of plaintexts. + * @param[in] num_threads The number of threads utilized to execute this function. If SOLO_USE_IPCL is ON, this + * parameter is ignored. + * @param[out] out The vector of resulting ciphertexts. */ - void encrypt(const Plaintext& in, Ciphertext& out) const noexcept; + void encrypt_many( + const std::vector<Plaintext>& in, std::vector<Ciphertext>& out, std::size_t num_threads = 1) noexcept; private: std::shared_ptr<PublicKey> pk_ = nullptr; + std::shared_ptr<SecretKey> sk_ = nullptr; + bool sk_set_ = false; +#ifndef SOLO_USE_IPCL + std::size_t r_bit_count_ = 0; + std::shared_ptr<PRNG> prng_ = nullptr; +#endif }; /** - * @brief Provides methods that decrypt a ciphertext into a plaintext. + * @brief Provides methods that decrypt ciphertexts into plaintexts. */ class Decryptor { public: /** - * @brief Constructs an decryptor with a secret key. + * @brief Constructs a decryptor with a secret key. * - * @param[in] sk The secret key - * @throws std::invalid_argument if sk is nullptr + * @param[in] sk The secret key. + * @throws std::invalid_argument if sk is nullptr. */ explicit Decryptor(const std::shared_ptr<SecretKey>& sk); + /** + * @brief Default constructor is deleted. + */ + Decryptor() = delete; + /** * @brief Decrypts a ciphertext into a plaintext. * - * @param[in] in The ciphertext - * @param[out] out The resulting plaintext + * @param[in] in The ciphertext. + * @param[out] out The resulting plaintext. */ void decrypt(const Ciphertext& in, Plaintext& out) const noexcept; + /** + * @brief Decrypts a vector of ciphertexts into a vector of plaintexts. + * + * @param[in] in The vector of ciphertext + * @param[in] num_threads The number of threads utilized to execute this function. If SOLO_USE_IPCL is ON, this + * parameter is ignored. + * @param[out] out The vector of resulting plaintexts. + */ + void decrypt_many( + const std::vector<Ciphertext>& in, std::vector<Plaintext>& out, std::size_t num_threads = 1) const noexcept; + private: std::shared_ptr<SecretKey> sk_ = nullptr; }; @@ -334,57 +714,126 @@ class Decryptor { class Evaluator { public: /** - * @brief Default constructor. + * @brief Constructs an evaluator with a public key. + * + * @param[in] pk The public key. + * @throws std::invalid_argument if pk is nullptr. */ - Evaluator() { - } + explicit Evaluator(const std::shared_ptr<PublicKey>& pk); + + /** + * @brief Constructs an evaluator with a public key. + * + * @param[in] pk The public key. + * @param[in] sk The secret key. + * @throws std::invalid_argument if pk is nullptr or sk is nullptr. + */ + explicit Evaluator(const std::shared_ptr<PublicKey>& pk, const std::shared_ptr<SecretKey>& sk); + + /** + * @brief Default constructor is deleted. + */ + Evaluator() = delete; /** * @brief Addition of two ciphertexts. * - * @param[in] in_0 The first ciphertext to add - * @param[in] in_1 The second ciphertext to add - * @param[out] out The ciphertext to overwrite with the addition result + * @param[in] in_0 The first ciphertext to add. + * @param[in] in_1 The second ciphertext to add. + * @param[out] out The ciphertext to overwrite with the addition result. */ void add(const Ciphertext& in_0, const Ciphertext& in_1, Ciphertext& out) const noexcept; + /** + * @brief Element-wise addition of two vector of ciphertexts. + * + * @param[in] in_0 The first vector of ciphertexts to add. + * @param[in] in_1 The second vector of ciphertexts to add. + * @param[in] num_threads The number of threads utilized to execute this function. If SOLO_USE_IPCL is ON, this + * parameter is ignored. + * @param[out] out The vector of ciphertexts to overwrite with the addition result. + */ + void add_many(const std::vector<Ciphertext>& in_0, const std::vector<Ciphertext>& in_1, + std::vector<Ciphertext>& out, std::size_t num_threads = 1) const; + /** * @brief Addition of a ciphertext and a plaintext. * - * @param[in] in_0 The ciphertext to add - * @param[in] in_1 The plaintext to add - * @param[out] out The ciphertext to overwrite with the addition result + * @param[in] in_0 The ciphertext to add. + * @param[in] in_1 The plaintext to add. + * @param[out] out The ciphertext to overwrite with the addition result. */ - void add(const Ciphertext& in_0, const Plaintext& in_1, Ciphertext& out) const noexcept; + void add(const Ciphertext& in_0, const Plaintext& in_1, Ciphertext& out) noexcept; + + /** + * @brief Element-wise addition of a vector of ciphertexts and a vector of plaintexts. + * + * @param[in] in_0 The vector of ciphertexts to add + * @param[in] in_1 The vector of plaintexts to add + * @param[in] num_threads The number of threads utilized to execute this function. If SOLO_USE_IPCL is ON, this + * parameter is ignored. + * @param[out] out The vector of ciphertexts to overwrite with the addition result. + */ + void add_many(const std::vector<Ciphertext>& in_0, const std::vector<Plaintext>& in_1, std::vector<Ciphertext>& out, + std::size_t num_threads = 1); /** * @brief Multiplication of a ciphertext and a plaintext. * - * @param[in] in_0 The ciphertext to multiply - * @param[in] in_1 The plaintext to multiply - * @param[out] out The ciphertext to overwrite with the multiplication result + * @param[in] in_0 The ciphertext to multiply. + * @param[in] in_1 The plaintext to multiply. + * @param[out] out The ciphertext to overwrite with the multiplication result. */ void mul(const Ciphertext& in_0, const Plaintext& in_1, Ciphertext& out) const noexcept; + + /** + * @brief Element-wise multiplication of a vector of ciphertexts and a vector of plaintexts. + * + * @param[in] in_0 The vector of ciphertexts to multiply. + * @param[in] in_1 The vector of plaintexts to multiply. + * @param[in] num_threads The number of threads utilized to execute this function. If SOLO_USE_IPCL is ON, this + * parameter is ignored. + * @param[out] out The vector of ciphertexts to overwrite with the multiplication result. + */ + void mul_many(const std::vector<Ciphertext>& in_0, const std::vector<Plaintext>& in_1, std::vector<Ciphertext>& out, + std::size_t num_threads = 1) const; + +private: + std::shared_ptr<PublicKey> pk_ = nullptr; + std::shared_ptr<SecretKey> sk_ = nullptr; + std::shared_ptr<Encryptor> encryptor_ = nullptr; + bool sk_set_ = false; }; namespace utils { +#ifdef SOLO_USE_IPCL /** - * @brief Shifts a BigNum to the left. + * @brief Shifts a BigNumber to the left bits. * - * @param[in] in The BigNum - * @param[in] bits The offset + * @param[in] in The BigNumber. + * @param[in] bits The offset. + * @throws std::invalid_argument if bits is greater than 8192. */ -void bn_lshift(BigNum& in, std::size_t bits); +void bn_lshift(BigNumber& in, std::size_t bits); +/** + * @brief Returns a random BigNumber. + * + * @param[in] bits The bitsize of the random BigNumber. + * @throws std::invalid_argument if bits is greater than 8192. + */ +BigNumber get_random_bn(std::size_t bits); +#else /** * @brief Returns a random BigNum. * - * @param[in] bits The bitsize of the random BigNum + * @param[in] bits The bitsize of the random mpz_class. + * @throws std::invalid_argument if bits is greater than 8192. */ -BigNum get_random_bn(std::size_t bits); +mpz_class get_random_mpz(std::size_t bits, std::shared_ptr<PRNG>& prng); +#endif } // namespace utils } // namespace ahepaillier } // namespace solo } // namespace petace -#endif diff --git a/src/solo/cuckoo_hashing.cpp b/src/solo/cuckoo_hashing.cpp index 758c6c1..7056cc2 100644 --- a/src/solo/cuckoo_hashing.cpp +++ b/src/solo/cuckoo_hashing.cpp @@ -199,7 +199,7 @@ std::vector<bool> CuckooHashing<item_byte_count>::obtain_bin_occupancy() const n template <std::size_t item_byte_count> std::vector<std::size_t> CuckooHashing<item_byte_count>::get_num_of_elements_in_bins() const noexcept { - std::vector<std::uint64_t> num_elements_in_bins(hash_table_.size(), 0); + std::vector<std::size_t> num_elements_in_bins(hash_table_.size(), 0); for (std::size_t i = 0; i < hash_table_.size(); ++i) { if (!hash_table_.at(i).is_empty()) { ++num_elements_in_bins.at(i); @@ -225,7 +225,11 @@ CuckooHashing<item_byte_count>::CuckooHashing(double epsilon, std::size_t num_of seed_ = seed; } +#ifdef SOLO_USE_AES_INTRIN solo::PRNGFactory prng_factory(solo::PRNGScheme::AES_ECB_CTR); +#else + solo::PRNGFactory prng_factory(solo::PRNGScheme::BLAKE2Xb); +#endif generator_ = prng_factory.create(seed_); } diff --git a/src/solo/hash.cpp b/src/solo/hash.cpp index 4d4ee56..ce05fe5 100644 --- a/src/solo/hash.cpp +++ b/src/solo/hash.cpp @@ -19,6 +19,7 @@ #include <cstdint> #include <memory> #include <stdexcept> +#include <string> #include <vector> #include "openssl/err.h" diff --git a/src/solo/prng.cpp b/src/solo/prng.cpp index cd40e94..ef12d0f 100644 --- a/src/solo/prng.cpp +++ b/src/solo/prng.cpp @@ -144,7 +144,11 @@ PRNGFactory::PRNGFactory(petace::solo::PRNGScheme scheme, std::size_t seed_byte_ if (seed_byte_count == 0) { throw std::invalid_argument("seed_byte_count is zero"); } - if (scheme != PRNGScheme::SHAKE_128 && scheme != PRNGScheme::BLAKE2Xb && scheme != PRNGScheme::AES_ECB_CTR) { + if (scheme != PRNGScheme::SHAKE_128 && scheme != PRNGScheme::BLAKE2Xb +#ifdef SOLO_USE_AES_INTRIN + && scheme != PRNGScheme::AES_ECB_CTR +#endif + ) { throw std::invalid_argument("unsupported PRNGScheme"); } #ifdef SOLO_USE_AES_INTRIN diff --git a/src/solo/simple_hashing.cpp b/src/solo/simple_hashing.cpp index b922e46..fedfb3e 100644 --- a/src/solo/simple_hashing.cpp +++ b/src/solo/simple_hashing.cpp @@ -146,7 +146,7 @@ std::vector<std::vector<std::uint64_t>> SimpleHashing<item_byte_count>::obtain_b template <std::size_t item_byte_count> std::vector<std::size_t> SimpleHashing<item_byte_count>::get_num_of_elements_in_bins() const noexcept { - std::vector<std::uint64_t> num_elements_in_bins(hash_table_.size(), 0); + std::vector<std::size_t> num_elements_in_bins(hash_table_.size(), 0); for (std::size_t i = 0; i < hash_table_.size(); ++i) { num_elements_in_bins.at(i) = hash_table_.at(i).size(); } @@ -186,7 +186,11 @@ SimpleHashing<item_byte_count>::SimpleHashing(double epsilon, std::size_t num_of seed_ = seed; } +#ifdef SOLO_USE_AES_INTRIN solo::PRNGFactory prng_factory(solo::PRNGScheme::AES_ECB_CTR); +#else + solo::PRNGFactory prng_factory(solo::PRNGScheme::BLAKE2Xb); +#endif generator_ = prng_factory.create(seed_); } diff --git a/src/solo/util/hash_table_entry.h b/src/solo/util/hash_table_entry.h index ef2807e..9f0f3a4 100644 --- a/src/solo/util/hash_table_entry.h +++ b/src/solo/util/hash_table_entry.h @@ -40,6 +40,7 @@ #pragma once #include <algorithm> +#include <array> #include <cstdint> #include <limits> #include <memory> diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5e3acf3..aee4c17 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,13 +14,13 @@ cmake_minimum_required(VERSION 3.14) -project(SOLOTest VERSION 0.2.0 LANGUAGES CXX C) +project(SOLOTest VERSION 0.3.0 LANGUAGES CXX C) # If not called from root CMakeLists.txt if(NOT DEFINED SOLO_BUILD_TEST) set(SOLO_BUILD_TEST ON) - find_package(PETAce-Solo 0.2.0 EXACT REQUIRED) + find_package(PETAce-Solo 0.3.0 EXACT REQUIRED) # Must define these variables and include macros set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) @@ -71,13 +71,9 @@ if(SOLO_BUILD_TEST) ${CMAKE_CURRENT_LIST_DIR}/hash_table_entry_test.cpp ${CMAKE_CURRENT_LIST_DIR}/test_runner.cpp ) - if(SOLO_USE_IPCL) - set(SOLO_TEST_FILES ${SOLO_TEST_FILES} - ${CMAKE_CURRENT_LIST_DIR}/ahe_paillier_test.cpp - ) - endif() - - set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} -ldl -lrt") + set(SOLO_TEST_FILES ${SOLO_TEST_FILES} + ${CMAKE_CURRENT_LIST_DIR}/ahe_paillier_test.cpp + ) add_executable(solo_test ${SOLO_TEST_FILES}) diff --git a/test/ahe_paillier_test.cpp b/test/ahe_paillier_test.cpp index fd50ecc..ecf1d14 100644 --- a/test/ahe_paillier_test.cpp +++ b/test/ahe_paillier_test.cpp @@ -14,8 +14,6 @@ #include "solo/ahe_paillier.h" -#ifdef SOLO_USE_IPCL - #include <memory> #include "gtest/gtest.h" @@ -46,125 +44,304 @@ TEST(AHEPaillierTest, KeyGen) { TEST(AHEPaillierTest, Serialization) { { - auto serialization_constructor = [](std::size_t key_length) { - petace::solo::ahepaillier::Serialization serialization(key_length); - }; - ASSERT_THROW(serialization_constructor(512), std::invalid_argument); - ASSERT_THROW(serialization_constructor(3072), std::invalid_argument); + ASSERT_THROW(petace::solo::ahepaillier::PublicKey(512, true), std::invalid_argument); + ASSERT_THROW(petace::solo::ahepaillier::PublicKey(3072, true), std::invalid_argument); + ASSERT_THROW(petace::solo::ahepaillier::PublicKey(512, false), std::invalid_argument); + ASSERT_THROW(petace::solo::ahepaillier::PublicKey(3072, false), std::invalid_argument); + ASSERT_THROW(petace::solo::ahepaillier::PublicKey(512, true), std::invalid_argument); + ASSERT_THROW(petace::solo::ahepaillier::PublicKey(3072, true), std::invalid_argument); + ASSERT_THROW(petace::solo::ahepaillier::PublicKey(512, false), std::invalid_argument); + ASSERT_THROW(petace::solo::ahepaillier::PublicKey(3072, false), std::invalid_argument); } + { std::shared_ptr<petace::solo::ahepaillier::SecretKey> sk = nullptr; std::shared_ptr<petace::solo::ahepaillier::PublicKey> pk = nullptr; petace::solo::ahepaillier::KeyGenerator generator(1024); generator.get_key_pair(sk, pk); - petace::solo::ahepaillier::Serialization serialization(1024, true); - ASSERT_EQ(serialization.public_key_byte_count(), 384); - ASSERT_EQ(serialization.secret_key_byte_count(), 256); - std::size_t public_key_byte_count = serialization.public_key_byte_count(); + ASSERT_EQ(pk->public_key_byte_count(), 384); + ASSERT_EQ(sk->secret_key_byte_count(), 256); + std::size_t public_key_byte_count = pk->public_key_byte_count(); std::vector<petace::solo::Byte> pk_bytes(public_key_byte_count); - ASSERT_THROW(serialization.serialize_public_key_to_bytes(pk, nullptr, 0), std::invalid_argument); - ASSERT_THROW(serialization.serialize_public_key_to_bytes(pk, nullptr, 383), std::invalid_argument); - ASSERT_THROW(serialization.serialize_public_key_to_bytes(pk, pk_bytes.data(), 385), std::invalid_argument); - serialization.serialize_public_key_to_bytes(pk, pk_bytes.data(), public_key_byte_count); + ASSERT_THROW(pk->serialize_to_bytes(nullptr, 0), std::invalid_argument); + ASSERT_THROW(pk->serialize_to_bytes(nullptr, 383), std::invalid_argument); + ASSERT_THROW(pk->serialize_to_bytes(pk_bytes.data(), 385), std::invalid_argument); + pk->serialize_to_bytes(pk_bytes.data(), public_key_byte_count); - std::shared_ptr<petace::solo::ahepaillier::SecretKey> sk_deserialized = nullptr; - std::shared_ptr<petace::solo::ahepaillier::PublicKey> pk_deserialized = nullptr; - ASSERT_THROW( - serialization.deserialize_public_key_from_bytes(nullptr, 0, pk_deserialized), std::invalid_argument); - ASSERT_THROW( - serialization.deserialize_public_key_from_bytes(nullptr, 383, pk_deserialized), std::invalid_argument); - ASSERT_THROW(serialization.deserialize_public_key_from_bytes(pk_bytes.data(), 385, pk_deserialized), - std::invalid_argument); - serialization.deserialize_public_key_from_bytes(pk_bytes.data(), pk_bytes.size(), pk_deserialized); - ASSERT_EQ(*(pk->getN()), *(pk_deserialized->getN())); - ASSERT_EQ(pk->getHS(), pk_deserialized->getHS()); + auto sk_deserialized = std::make_shared<petace::solo::ahepaillier::SecretKey>(1024); + auto pk_deserialized = std::make_shared<petace::solo::ahepaillier::PublicKey>(1024, true); - std::size_t secret_key_byte_count = serialization.secret_key_byte_count(); + ASSERT_THROW(pk_deserialized->deserialize_from_bytes(nullptr, 0), std::invalid_argument); + ASSERT_THROW(pk_deserialized->deserialize_from_bytes(nullptr, 383), std::invalid_argument); + ASSERT_THROW(pk_deserialized->deserialize_from_bytes(pk_bytes.data(), 385), std::invalid_argument); + ASSERT_THROW( + pk_deserialized->serialize_to_bytes(pk_bytes.data(), public_key_byte_count), std::invalid_argument); + pk_deserialized->deserialize_from_bytes(pk_bytes.data(), pk_bytes.size()); + ASSERT_THROW(pk_deserialized->deserialize_from_bytes(pk_bytes.data(), pk_bytes.size()), std::invalid_argument); +#ifdef SOLO_USE_IPCL + ASSERT_EQ(BigNumber(1), BigNumber(1)); + ASSERT_EQ(*(pk->pk()->getN()), *(pk_deserialized->pk()->getN())); + ASSERT_EQ(pk->pk()->getHS(), pk_deserialized->pk()->getHS()); +#else + ASSERT_EQ(pk->n(), pk_deserialized->n()); + ASSERT_EQ(pk->g(), pk_deserialized->g()); + ASSERT_EQ(pk->n_square(), pk_deserialized->n_square()); + ASSERT_EQ(pk->hs(), pk_deserialized->hs()); +#endif + std::size_t secret_key_byte_count = sk->secret_key_byte_count(); std::vector<petace::solo::Byte> sk_bytes(secret_key_byte_count); - ASSERT_THROW(serialization.serialize_secret_key_to_bytes(sk, nullptr, 0), std::invalid_argument); - ASSERT_THROW(serialization.serialize_secret_key_to_bytes(sk, nullptr, 255), std::invalid_argument); - ASSERT_THROW(serialization.serialize_secret_key_to_bytes(sk, sk_bytes.data(), 257), std::invalid_argument); - serialization.serialize_secret_key_to_bytes(sk, sk_bytes.data(), secret_key_byte_count); + ASSERT_THROW(sk->serialize_to_bytes(nullptr, 0), std::invalid_argument); + ASSERT_THROW(sk->serialize_to_bytes(nullptr, 255), std::invalid_argument); + ASSERT_THROW(sk->serialize_to_bytes(sk_bytes.data(), 257), std::invalid_argument); + sk->serialize_to_bytes(sk_bytes.data(), secret_key_byte_count); + ASSERT_THROW(sk_deserialized->deserialize_from_bytes(nullptr, 0), std::invalid_argument); + ASSERT_THROW(sk_deserialized->deserialize_from_bytes(nullptr, 255), std::invalid_argument); + ASSERT_THROW(sk_deserialized->deserialize_from_bytes(sk_bytes.data(), 257), std::invalid_argument); ASSERT_THROW( - serialization.deserialize_secret_key_from_bytes(nullptr, 0, sk_deserialized), std::invalid_argument); - ASSERT_THROW( - serialization.deserialize_secret_key_from_bytes(nullptr, 255, sk_deserialized), std::invalid_argument); - ASSERT_THROW(serialization.deserialize_secret_key_from_bytes(sk_bytes.data(), 257, sk_deserialized), - std::invalid_argument); - serialization.deserialize_secret_key_from_bytes(sk_bytes.data(), sk_bytes.size(), sk_deserialized); - ASSERT_EQ(*(sk->getN()), *(sk_deserialized->getN())); - ASSERT_EQ(*(sk->getP()), *(sk_deserialized->getP())); - ASSERT_EQ(*(sk->getQ()), *(sk_deserialized->getQ())); + sk_deserialized->serialize_to_bytes(sk_bytes.data(), secret_key_byte_count), std::invalid_argument); + sk_deserialized->deserialize_from_bytes(sk_bytes.data(), sk_bytes.size()); + ASSERT_THROW(sk_deserialized->deserialize_from_bytes(sk_bytes.data(), sk_bytes.size()), std::invalid_argument); +#ifdef SOLO_USE_IPCL + ASSERT_EQ(*(sk->sk()->getN()), *(sk_deserialized->sk()->getN())); + ASSERT_EQ(*(sk->sk()->getP()), *(sk_deserialized->sk()->getP())); + ASSERT_EQ(*(sk->sk()->getQ()), *(sk_deserialized->sk()->getQ())); +#else + ASSERT_EQ(sk->n(), sk_deserialized->n()); + ASSERT_EQ(sk->g(), sk_deserialized->g()); + ASSERT_EQ(sk->n_square(), sk_deserialized->n_square()); +#endif } + { std::shared_ptr<petace::solo::ahepaillier::SecretKey> sk = nullptr; std::shared_ptr<petace::solo::ahepaillier::PublicKey> pk = nullptr; petace::solo::ahepaillier::KeyGenerator generator(1024); generator.get_key_pair(sk, pk, false); - petace::solo::ahepaillier::Serialization serialization(1024, false); - ASSERT_EQ(serialization.public_key_byte_count(), 128); - ASSERT_EQ(serialization.secret_key_byte_count(), 256); - std::size_t public_key_byte_count = serialization.public_key_byte_count(); + ASSERT_EQ(pk->public_key_byte_count(), 128); + ASSERT_EQ(sk->secret_key_byte_count(), 256); + std::size_t public_key_byte_count = pk->public_key_byte_count(); std::vector<petace::solo::Byte> pk_bytes(public_key_byte_count); - ASSERT_THROW(serialization.serialize_public_key_to_bytes(pk, nullptr, 0), std::invalid_argument); - ASSERT_THROW(serialization.serialize_public_key_to_bytes(pk, nullptr, 127), std::invalid_argument); - ASSERT_THROW(serialization.serialize_public_key_to_bytes(pk, pk_bytes.data(), 129), std::invalid_argument); - serialization.serialize_public_key_to_bytes(pk, pk_bytes.data(), public_key_byte_count); + ASSERT_THROW(pk->serialize_to_bytes(nullptr, 0), std::invalid_argument); + ASSERT_THROW(pk->serialize_to_bytes(nullptr, 127), std::invalid_argument); + ASSERT_THROW(pk->serialize_to_bytes(pk_bytes.data(), 129), std::invalid_argument); + pk->serialize_to_bytes(pk_bytes.data(), public_key_byte_count); - std::shared_ptr<petace::solo::ahepaillier::SecretKey> sk_deserialized = nullptr; - std::shared_ptr<petace::solo::ahepaillier::PublicKey> pk_deserialized = nullptr; - ASSERT_THROW( - serialization.deserialize_public_key_from_bytes(nullptr, 0, pk_deserialized), std::invalid_argument); - ASSERT_THROW( - serialization.deserialize_public_key_from_bytes(nullptr, 127, pk_deserialized), std::invalid_argument); - ASSERT_THROW(serialization.deserialize_public_key_from_bytes(pk_bytes.data(), 129, pk_deserialized), - std::invalid_argument); - serialization.deserialize_public_key_from_bytes(pk_bytes.data(), pk_bytes.size(), pk_deserialized); - ASSERT_EQ(*(pk->getN()), *(pk_deserialized->getN())); + auto sk_deserialized = std::make_shared<petace::solo::ahepaillier::SecretKey>(1024); + auto pk_deserialized = std::make_shared<petace::solo::ahepaillier::PublicKey>(1024, false); - std::size_t secret_key_byte_count = serialization.secret_key_byte_count(); + ASSERT_THROW(pk_deserialized->deserialize_from_bytes(nullptr, 0), std::invalid_argument); + ASSERT_THROW(pk_deserialized->deserialize_from_bytes(nullptr, 127), std::invalid_argument); + ASSERT_THROW(pk_deserialized->deserialize_from_bytes(pk_bytes.data(), 129), std::invalid_argument); + pk_deserialized->deserialize_from_bytes(pk_bytes.data(), pk_bytes.size()); +#ifdef SOLO_USE_IPCL + ASSERT_EQ(*(pk->pk()->getN()), *(pk_deserialized->pk()->getN())); + ASSERT_EQ(pk->pk()->getHS(), pk_deserialized->pk()->getHS()); +#else + ASSERT_EQ(pk->n(), pk_deserialized->n()); + ASSERT_EQ(pk->g(), pk_deserialized->g()); + ASSERT_EQ(pk->n_square(), pk_deserialized->n_square()); + ASSERT_EQ(pk->hs(), pk_deserialized->hs()); +#endif + std::size_t secret_key_byte_count = sk->secret_key_byte_count(); std::vector<petace::solo::Byte> sk_bytes(secret_key_byte_count); - ASSERT_THROW(serialization.serialize_secret_key_to_bytes(sk, nullptr, 0), std::invalid_argument); - ASSERT_THROW(serialization.serialize_secret_key_to_bytes(sk, nullptr, 255), std::invalid_argument); - ASSERT_THROW(serialization.serialize_secret_key_to_bytes(sk, sk_bytes.data(), 257), std::invalid_argument); - serialization.serialize_secret_key_to_bytes(sk, sk_bytes.data(), secret_key_byte_count); + ASSERT_THROW(sk->serialize_to_bytes(nullptr, 0), std::invalid_argument); + ASSERT_THROW(sk->serialize_to_bytes(nullptr, 255), std::invalid_argument); + ASSERT_THROW(sk->serialize_to_bytes(sk_bytes.data(), 257), std::invalid_argument); + sk->serialize_to_bytes(sk_bytes.data(), secret_key_byte_count); - ASSERT_THROW( - serialization.deserialize_secret_key_from_bytes(nullptr, 0, sk_deserialized), std::invalid_argument); - ASSERT_THROW( - serialization.deserialize_secret_key_from_bytes(nullptr, 255, sk_deserialized), std::invalid_argument); - ASSERT_THROW(serialization.deserialize_secret_key_from_bytes(sk_bytes.data(), 257, sk_deserialized), + ASSERT_THROW(sk_deserialized->deserialize_from_bytes(nullptr, 0), std::invalid_argument); + ASSERT_THROW(sk_deserialized->deserialize_from_bytes(nullptr, 255), std::invalid_argument); + ASSERT_THROW(sk_deserialized->deserialize_from_bytes(sk_bytes.data(), 257), std::invalid_argument); + sk_deserialized->deserialize_from_bytes(sk_bytes.data(), sk_bytes.size()); +#ifdef SOLO_USE_IPCL + ASSERT_EQ(*(sk->sk()->getN()), *(sk_deserialized->sk()->getN())); + ASSERT_EQ(*(sk->sk()->getP()), *(sk_deserialized->sk()->getP())); + ASSERT_EQ(*(sk->sk()->getQ()), *(sk_deserialized->sk()->getQ())); +#else + ASSERT_EQ(sk->n(), sk_deserialized->n()); + ASSERT_EQ(sk->g(), sk_deserialized->g()); + ASSERT_EQ(sk->n_square(), sk_deserialized->n_square()); +#endif + } + + { + const std::vector<int> bits_vec = {2, 31, 32, 480, 511, 512, 994, 1023, 1024, 2018, 2047, 2048}; +#ifndef SOLO_USE_IPCL + petace::solo::PRNGFactory prng_factory = petace::solo::PRNGFactory(petace::solo::PRNGScheme::BLAKE2Xb); + auto prng = prng_factory.create(); +#endif + for (std::size_t i = 0; i < bits_vec.size(); ++i) { +#ifdef SOLO_USE_IPCL + BigNumber bn = petace::solo::ahepaillier::utils::get_random_bn(bits_vec[i]); + std::vector<petace::solo::Byte> bn_bytes((bits_vec[i] + 7) / 8); + petace::solo::ahepaillier::Serialization::ipcl_bn_to_bytes(bn, bn_bytes.data(), bn_bytes.size()); + BigNumber bn_deserialized; + petace::solo::ahepaillier::Serialization::ipcl_bn_from_bytes( + bn_bytes.data(), bn_bytes.size(), bn_deserialized); +#else + mpz_class bn = petace::solo::ahepaillier::utils::get_random_mpz(bits_vec[i], prng); + std::vector<petace::solo::Byte> bn_bytes((bits_vec[i] + 7) / 8); + petace::solo::ahepaillier::Serialization::mpz_bn_to_bytes(bn, bn_bytes.data(), bn_bytes.size()); + mpz_class bn_deserialized; + petace::solo::ahepaillier::Serialization::mpz_bn_from_bytes( + bn_bytes.data(), bn_bytes.size(), bn_deserialized); +#endif + ASSERT_EQ(bn, bn_deserialized); + } + } + + { + const std::vector<int> bits_vec = {2, 31, 32, 480, 511, 512, 994, 1023, 1024, 2018, 2047, 2048}; +#ifndef SOLO_USE_IPCL + petace::solo::PRNGFactory prng_factory = petace::solo::PRNGFactory(petace::solo::PRNGScheme::BLAKE2Xb); + auto prng = prng_factory.create(); +#endif + for (std::size_t i = 0; i < bits_vec.size(); ++i) { +#ifdef SOLO_USE_IPCL + BigNumber bn = petace::solo::ahepaillier::utils::get_random_bn(bits_vec[i]); + std::vector<petace::solo::Byte> bn_bytes((bits_vec[i] + 7) / 8 + 1); + petace::solo::ahepaillier::Serialization::ipcl_bn_to_bytes(bn, bn_bytes.data(), bn_bytes.size()); + BigNumber bn_deserialized; + petace::solo::ahepaillier::Serialization::ipcl_bn_from_bytes( + bn_bytes.data(), bn_bytes.size(), bn_deserialized); +#else + mpz_class bn = petace::solo::ahepaillier::utils::get_random_mpz(bits_vec[i], prng); + std::vector<petace::solo::Byte> bn_bytes(((bits_vec[i] + 7) / 8 + 1)); + petace::solo::ahepaillier::Serialization::mpz_bn_to_bytes(bn, bn_bytes.data(), bn_bytes.size()); + mpz_class bn_deserialized; + petace::solo::ahepaillier::Serialization::mpz_bn_from_bytes( + bn_bytes.data(), bn_bytes.size(), bn_deserialized); +#endif + ASSERT_EQ(bn, bn_deserialized); + } + } + { + petace::solo::PRNGFactory prng_factory = petace::solo::PRNGFactory(petace::solo::PRNGScheme::BLAKE2Xb); + auto prng = prng_factory.create(); + std::size_t illegal_bits = 8200; + std::size_t illegal_byte_count = 1025; + std::vector<petace::solo::Byte> bn_bytes(illegal_byte_count); + prng->generate(illegal_byte_count, bn_bytes.data()); +#ifdef SOLO_USE_IPCL + ASSERT_THROW(petace::solo::ahepaillier::utils::get_random_bn(illegal_bits), std::invalid_argument); + BigNumber bn = ipcl::getRandomBN(static_cast<int>(illegal_bits)); + ASSERT_THROW(petace::solo::ahepaillier::Serialization::ipcl_bn_to_bytes(bn, bn_bytes.data(), bn_bytes.size()), + std::invalid_argument); + BigNumber bn_deserialized; + ASSERT_THROW(petace::solo::ahepaillier::Serialization::ipcl_bn_from_bytes( + bn_bytes.data(), bn_bytes.size(), bn_deserialized), std::invalid_argument); - serialization.deserialize_secret_key_from_bytes(sk_bytes.data(), sk_bytes.size(), sk_deserialized); - ASSERT_EQ(*(sk->getN()), *(sk_deserialized->getN())); - ASSERT_EQ(*(sk->getP()), *(sk_deserialized->getP())); - ASSERT_EQ(*(sk->getQ()), *(sk_deserialized->getQ())); +#else + ASSERT_THROW(petace::solo::ahepaillier::utils::get_random_mpz(illegal_bits, prng), std::invalid_argument); + mpz_class bn; + mpz_import(bn.get_mpz_t(), bn_bytes.size(), 1, 1, 0, 0, bn_bytes.data()); + ASSERT_THROW(petace::solo::ahepaillier::Serialization::mpz_bn_to_bytes(bn, bn_bytes.data(), bn_bytes.size()); + , std::invalid_argument); + mpz_class bn_deserialized; + ASSERT_THROW(petace::solo::ahepaillier::Serialization::mpz_bn_from_bytes( + bn_bytes.data(), bn_bytes.size(), bn_deserialized); + , std::invalid_argument); +#endif } { const std::vector<int> bits_vec = {2, 31, 32, 480, 511, 512, 994, 1023, 1024, 2018, 2047, 2048}; +#ifndef SOLO_USE_IPCL + petace::solo::PRNGFactory prng_factory = petace::solo::PRNGFactory(petace::solo::PRNGScheme::BLAKE2Xb); + auto prng = prng_factory.create(); +#endif for (std::size_t i = 0; i < bits_vec.size(); ++i) { - petace::solo::ahepaillier::BigNum bn = petace::solo::ahepaillier::utils::get_random_bn(bits_vec[i]); +#ifdef SOLO_USE_IPCL + BigNumber bn = petace::solo::ahepaillier::utils::get_random_bn(bits_vec[i]); std::vector<petace::solo::Byte> bn_bytes((bits_vec[i] + 7) / 8); - petace::solo::ahepaillier::Serialization::bn_to_bytes(bn, bn_bytes.data(), bn_bytes.size()); - petace::solo::ahepaillier::BigNum bn_deserialized; - petace::solo::ahepaillier::Serialization::bn_from_bytes(bn_bytes.data(), bn_bytes.size(), bn_deserialized); + petace::solo::ahepaillier::Serialization::ipcl_bn_to_bytes(bn, bn_bytes.data(), bn_bytes.size()); + petace::solo::ahepaillier::Plaintext pt; + pt.deserialize_from_bytes(bn_bytes.data(), bn_bytes.size()); + ASSERT_EQ(bn, BigNumber(ipcl::PlainText(pt))); + + pt.serialize_to_bytes(bn_bytes.data(), bn_bytes.size()); + BigNumber bn_deserialized; + petace::solo::ahepaillier::Serialization::ipcl_bn_from_bytes( + bn_bytes.data(), bn_bytes.size(), bn_deserialized); +#else + mpz_class bn = petace::solo::ahepaillier::utils::get_random_mpz(bits_vec[i], prng); + std::vector<petace::solo::Byte> bn_bytes((bits_vec[i] + 7) / 8); + petace::solo::ahepaillier::Serialization::mpz_bn_to_bytes(bn, bn_bytes.data(), bn_bytes.size()); + petace::solo::ahepaillier::Plaintext pt; + pt.deserialize_from_bytes(bn_bytes.data(), bn_bytes.size()); + ASSERT_EQ(bn, mpz_class(pt)); + + pt.serialize_to_bytes(bn_bytes.data(), bn_bytes.size()); + mpz_class bn_deserialized; + petace::solo::ahepaillier::Serialization::mpz_bn_from_bytes( + bn_bytes.data(), bn_bytes.size(), bn_deserialized); +#endif ASSERT_EQ(bn, bn_deserialized); } } { - const std::vector<int> bits_vec = {2, 31, 32, 482, 511, 512, 994, 1023, 1024, 2018, 2047, 2048}; + const std::vector<int> bits_vec = {2, 31, 32, 480, 511, 512, 994, 1023, 1024, 2018, 2047, 2048}; +#ifndef SOLO_USE_IPCL + petace::solo::PRNGFactory prng_factory = petace::solo::PRNGFactory(petace::solo::PRNGScheme::BLAKE2Xb); + auto prng = prng_factory.create(); +#endif for (std::size_t i = 0; i < bits_vec.size(); ++i) { - petace::solo::ahepaillier::BigNum bn = petace::solo::ahepaillier::utils::get_random_bn(bits_vec[i]); +#ifdef SOLO_USE_IPCL + BigNumber bn = petace::solo::ahepaillier::utils::get_random_bn(bits_vec[i]); + std::vector<petace::solo::Byte> bn_bytes((bits_vec[i] + 7) / 8 + 1); + petace::solo::ahepaillier::Serialization::ipcl_bn_to_bytes(bn, bn_bytes.data(), bn_bytes.size()); + petace::solo::ahepaillier::Plaintext pt; + pt.deserialize_from_bytes(bn_bytes.data(), bn_bytes.size()); + ASSERT_EQ(bn, BigNumber(ipcl::PlainText(pt))); + + pt.serialize_to_bytes(bn_bytes.data(), bn_bytes.size()); + BigNumber bn_deserialized; + petace::solo::ahepaillier::Serialization::ipcl_bn_from_bytes( + bn_bytes.data(), bn_bytes.size(), bn_deserialized); +#else + mpz_class bn = petace::solo::ahepaillier::utils::get_random_mpz(bits_vec[i], prng); std::vector<petace::solo::Byte> bn_bytes((bits_vec[i] + 7) / 8 + 1); - petace::solo::ahepaillier::Serialization::bn_to_bytes(bn, bn_bytes.data(), bn_bytes.size()); - petace::solo::ahepaillier::BigNum bn_deserialized; - petace::solo::ahepaillier::Serialization::bn_from_bytes(bn_bytes.data(), bn_bytes.size(), bn_deserialized); + petace::solo::ahepaillier::Serialization::mpz_bn_to_bytes(bn, bn_bytes.data(), bn_bytes.size()); + petace::solo::ahepaillier::Plaintext pt; + pt.deserialize_from_bytes(bn_bytes.data(), bn_bytes.size()); + ASSERT_EQ(bn, mpz_class(pt)); + + pt.serialize_to_bytes(bn_bytes.data(), bn_bytes.size()); + mpz_class bn_deserialized; + petace::solo::ahepaillier::Serialization::mpz_bn_from_bytes( + bn_bytes.data(), bn_bytes.size(), bn_deserialized); +#endif ASSERT_EQ(bn, bn_deserialized); } } + { + petace::solo::PRNGFactory prng_factory = petace::solo::PRNGFactory(petace::solo::PRNGScheme::BLAKE2Xb); + auto prng = prng_factory.create(); + std::size_t illegal_byte_count = 1025; + std::vector<petace::solo::Byte> bn_bytes(illegal_byte_count); + prng->generate(illegal_byte_count, bn_bytes.data()); + petace::solo::ahepaillier::Plaintext pt; + ASSERT_THROW(pt.deserialize_from_bytes(bn_bytes.data(), bn_bytes.size()), std::invalid_argument); + pt.deserialize_from_bytes(bn_bytes.data(), bn_bytes.size() - 1); + ASSERT_THROW(pt.serialize_to_bytes(bn_bytes.data(), bn_bytes.size() - 2), std::invalid_argument); + } + { + petace::solo::PRNGFactory prng_factory = petace::solo::PRNGFactory(petace::solo::PRNGScheme::BLAKE2Xb); + auto prng = prng_factory.create(); + std::size_t illegal_byte_count = 1025; + std::vector<petace::solo::Byte> bn_bytes(illegal_byte_count); + prng->generate(illegal_byte_count, bn_bytes.data()); + std::shared_ptr<petace::solo::ahepaillier::SecretKey> sk = nullptr; + std::shared_ptr<petace::solo::ahepaillier::PublicKey> pk = nullptr; + petace::solo::ahepaillier::KeyGenerator generator(1024); + generator.get_key_pair(sk, pk, true); + petace::solo::ahepaillier::Ciphertext ct; + ASSERT_THROW(ct.deserialize_from_bytes(pk, bn_bytes.data(), bn_bytes.size()), std::invalid_argument); + ct.deserialize_from_bytes(pk, bn_bytes.data(), bn_bytes.size() - 1); + ASSERT_THROW(ct.serialize_to_bytes(bn_bytes.data(), bn_bytes.size() - 2), std::invalid_argument); + } } TEST(AHEPaillierTest, Encode) { @@ -175,47 +352,54 @@ TEST(AHEPaillierTest, Encode) { encoder.encode(in, pt); std::uint64_t out = encoder.decode(pt); ASSERT_EQ(in, out); - ASSERT_EQ(pt.slot_count(), 1); - ASSERT_EQ(pt[0], petace::solo::ahepaillier::BigNum(1234)); +#ifdef SOLO_USE_IPCL + ASSERT_EQ(BigNumber(ipcl::PlainText(pt)), BigNumber(1234)); +#else + ASSERT_EQ(mpz_class(pt), 1234); +#endif } +#ifdef SOLO_USE_IPCL { - std::vector<std::uint64_t> in = {1, 2, 3, 4, 5, 6, 7, 8}; - petace::solo::ahepaillier::Encoder encoder; - petace::solo::ahepaillier::Plaintext pt; - encoder.encode(in, pt); - std::vector<std::uint64_t> out; - encoder.decode(pt, out); + BigNumber in = 16; + petace::solo::ahepaillier::Plaintext pt(in); + auto out = BigNumber(ipcl::PlainText(pt)); ASSERT_EQ(in, out); - ASSERT_EQ(pt.slot_count(), 8); - ASSERT_EQ(pt[0], petace::solo::ahepaillier::BigNum(1)); } +#else { - std::vector<petace::solo::ahepaillier::BigNum> in = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; - petace::solo::ahepaillier::Encoder encoder; + mpz_class in = 16; petace::solo::ahepaillier::Plaintext pt(in); - std::vector<petace::solo::ahepaillier::BigNum> out(pt); + mpz_class out(pt); ASSERT_EQ(in, out); - ASSERT_EQ(pt.slot_count(), 16); - ASSERT_EQ(pt[0], petace::solo::ahepaillier::BigNum(1)); } +#endif { std::shared_ptr<petace::solo::ahepaillier::SecretKey> sk = nullptr; std::shared_ptr<petace::solo::ahepaillier::PublicKey> pk = nullptr; petace::solo::ahepaillier::KeyGenerator generator(1024); generator.get_key_pair(sk, pk, true); - petace::solo::ahepaillier::Encryptor encryptor(pk, true); + petace::solo::ahepaillier::Encryptor encryptor(pk); std::vector<std::uint64_t> in = {1, 2, 3, 4, 5, 6, 7, 8}; petace::solo::ahepaillier::Encoder encoder; - petace::solo::ahepaillier::Plaintext pt; - petace::solo::ahepaillier::Ciphertext ct; + std::vector<petace::solo::ahepaillier::Plaintext> pt; + std::vector<petace::solo::ahepaillier::Ciphertext> ct; encoder.encode(in, pt); - encryptor.encrypt(pt, ct); - std::vector<petace::solo::ahepaillier::BigNum> bn_ct(ct); - petace::solo::ahepaillier::Ciphertext ct_check(*(pk.get()), bn_ct); - for (std::size_t i = 0; i < ct.slot_count(); ++i) { - ASSERT_EQ(ct[i], ct_check[i]); + + encryptor.encrypt_many(pt, ct); +#ifdef SOLO_USE_IPCL + for (std::size_t i = 0; i < ct.size(); ++i) { + BigNumber bn_ct(ipcl::CipherText(ct[i])[0]); + petace::solo::ahepaillier::Ciphertext ct_check(*(pk->pk()), bn_ct); + ASSERT_EQ(bn_ct, ipcl::CipherText(ct_check)[0]); } +#else + for (std::size_t i = 0; i < ct.size(); ++i) { + mpz_class bn_ct(ct[i]); + petace::solo::ahepaillier::Ciphertext ct_check(bn_ct); + ASSERT_EQ(bn_ct, mpz_class(ct_check)); + } +#endif } { std::uint64_t in = 1ull << 33; @@ -227,57 +411,390 @@ TEST(AHEPaillierTest, Encode) { } TEST(AHEPaillierTest, Encrypt) { - std::shared_ptr<petace::solo::ahepaillier::SecretKey> sk = nullptr; - std::shared_ptr<petace::solo::ahepaillier::PublicKey> pk = nullptr; - petace::solo::ahepaillier::KeyGenerator generator(1024); - generator.get_key_pair(sk, pk, true); - petace::solo::ahepaillier::Encryptor encryptor(pk, true); - petace::solo::ahepaillier::Decryptor decryptor(sk); - petace::solo::ahepaillier::Encoder encoder; { - std::uint64_t in = 1234; - petace::solo::ahepaillier::Plaintext pt; - petace::solo::ahepaillier::Ciphertext ct; - encoder.encode(in, pt); - encryptor.encrypt(pt, ct); - ASSERT_EQ(ct.slot_count(), 1); - ct[0]; + std::shared_ptr<petace::solo::ahepaillier::SecretKey> sk = nullptr; + std::shared_ptr<petace::solo::ahepaillier::PublicKey> pk = nullptr; + petace::solo::ahepaillier::KeyGenerator generator(1024); + generator.get_key_pair(sk, pk, true); + petace::solo::ahepaillier::Encryptor encryptor(pk); + petace::solo::ahepaillier::Encryptor encryptor_with_sk(pk, sk); + petace::solo::ahepaillier::Decryptor decryptor(sk); + petace::solo::ahepaillier::Encoder encoder; + { + std::uint64_t in = 1234; + petace::solo::ahepaillier::Plaintext pt; + petace::solo::ahepaillier::Ciphertext ct; + encoder.encode(in, pt); + encryptor.encrypt(pt, ct); - petace::solo::ahepaillier::Plaintext pt_check; - decryptor.decrypt(ct, pt_check); - std::uint64_t out = encoder.decode(pt_check); - ASSERT_EQ(in, out); - } - { - std::vector<std::uint64_t> in = {1, 2, 3, 4, 5, 6, 7, 8}; - petace::solo::ahepaillier::Plaintext pt; - petace::solo::ahepaillier::Ciphertext ct; - encoder.encode(in, pt); - encryptor.encrypt(pt, ct); - ASSERT_EQ(ct.slot_count(), 8); - ct[0]; - - petace::solo::ahepaillier::Plaintext pt_check; - decryptor.decrypt(ct, pt_check); - std::vector<std::uint64_t> out; - encoder.decode(pt_check, out); - ASSERT_EQ(in, out); + petace::solo::ahepaillier::Plaintext pt_check; + decryptor.decrypt(ct, pt_check); + std::uint64_t out = encoder.decode(pt_check); + ASSERT_EQ(in, out); + } + { + std::uint64_t in = 1234; + petace::solo::ahepaillier::Plaintext pt; + petace::solo::ahepaillier::Ciphertext ct; + encoder.encode(in, pt); + encryptor_with_sk.encrypt(pt, ct); + + petace::solo::ahepaillier::Plaintext pt_check; + decryptor.decrypt(ct, pt_check); + std::uint64_t out = encoder.decode(pt_check); + ASSERT_EQ(in, out); + } + { + std::vector<std::uint64_t> in = {1, 2, 3, 4, 5, 6, 7, 8}; + std::vector<petace::solo::ahepaillier::Plaintext> pt; + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + + encoder.encode(in, pt); + encryptor.encrypt_many(pt, ct); + + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct, pt_check); + std::vector<std::uint64_t> out; + + encoder.decode(pt_check, out); + + ASSERT_EQ(in, out); + } + { + std::vector<std::uint64_t> in = {1, 2, 3, 4, 5, 6, 7, 8}; + std::vector<petace::solo::ahepaillier::Plaintext> pt; + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + encoder.encode(in, pt); + + encryptor_with_sk.encrypt_many(pt, ct); + + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct, pt_check); + std::vector<std::uint64_t> out; + + encoder.decode(pt_check, out); + + ASSERT_EQ(in, out); + } +#ifdef SOLO_USE_IPCL + { + std::vector<BigNumber> in = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + std::vector<petace::solo::ahepaillier::Plaintext> pt(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) { + pt[i] = petace::solo::ahepaillier::Plaintext(in[i]); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + encryptor.encrypt_many(pt, ct); + + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct, pt_check); + std::vector<BigNumber> out(pt_check.size()); + for (std::size_t i = 0; i < out.size(); ++i) { + out[i] = BigNumber(ipcl::PlainText(pt_check[i])); + } + ASSERT_EQ(in, out); + } + { + std::vector<BigNumber> in = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + std::vector<petace::solo::ahepaillier::Plaintext> pt(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) { + pt[i] = petace::solo::ahepaillier::Plaintext(in[i]); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + encryptor_with_sk.encrypt_many(pt, ct); + + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct, pt_check); + std::vector<BigNumber> out(pt_check.size()); + for (std::size_t i = 0; i < out.size(); ++i) { + out[i] = BigNumber(ipcl::PlainText(pt_check[i])); + } + ASSERT_EQ(in, out); + } + { + std::vector<BigNumber> in = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + std::vector<petace::solo::ahepaillier::Plaintext> pt(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) { + pt[i] = petace::solo::ahepaillier::Plaintext(in[i]); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + encryptor.encrypt_many(pt, ct); + + std::size_t n_square_byte_count = ((pk->key_length() + 7) / 8) * 2; + std::vector<petace::solo::Byte> ct_bytes(ct.size() * n_square_byte_count); + for (std::size_t i = 0; i < ct.size(); ++i) { + ct[i].serialize_to_bytes(ct_bytes.data() + i * n_square_byte_count, n_square_byte_count); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct_deserialized(ct.size()); + for (std::size_t i = 0; i < ct.size(); ++i) { + ct_deserialized[i].deserialize_from_bytes( + pk, ct_bytes.data() + i * n_square_byte_count, n_square_byte_count); + } + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct_deserialized, pt_check); + std::vector<BigNumber> out(pt_check.size()); + for (std::size_t i = 0; i < out.size(); ++i) { + out[i] = BigNumber(ipcl::PlainText(pt_check[i])); + } + ASSERT_EQ(in, out); + } +#else + { + std::vector<mpz_class> in = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + std::vector<petace::solo::ahepaillier::Plaintext> pt(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) { + pt[i] = petace::solo::ahepaillier::Plaintext(in[i]); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + encryptor.encrypt_many(pt, ct); + + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct, pt_check); + std::vector<mpz_class> out(pt_check.size()); + for (std::size_t i = 0; i < out.size(); ++i) { + out[i] = mpz_class(pt_check[i]); + } + ASSERT_EQ(in, out); + } + { + std::vector<mpz_class> in = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + std::vector<petace::solo::ahepaillier::Plaintext> pt(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) { + pt[i] = petace::solo::ahepaillier::Plaintext(in[i]); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + encryptor_with_sk.encrypt_many(pt, ct); + + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct, pt_check); + std::vector<mpz_class> out(pt_check.size()); + for (std::size_t i = 0; i < out.size(); ++i) { + out[i] = mpz_class(pt_check[i]); + } + ASSERT_EQ(in, out); + } + { + std::vector<mpz_class> in = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + std::vector<petace::solo::ahepaillier::Plaintext> pt(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) { + pt[i] = petace::solo::ahepaillier::Plaintext(in[i]); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + encryptor.encrypt_many(pt, ct); + std::size_t n_square_byte_count = ((pk->key_length() + 7) / 8) * 2; + std::vector<petace::solo::Byte> ct_bytes(ct.size() * n_square_byte_count); + for (std::size_t i = 0; i < ct.size(); ++i) { + ct[i].serialize_to_bytes(ct_bytes.data() + i * n_square_byte_count, n_square_byte_count); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct_deserialized(ct.size()); + for (std::size_t i = 0; i < ct.size(); ++i) { + ct_deserialized[i].deserialize_from_bytes( + pk, ct_bytes.data() + i * n_square_byte_count, n_square_byte_count); + } + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct_deserialized, pt_check); + std::vector<mpz_class> out(pt_check.size()); + for (std::size_t i = 0; i < out.size(); ++i) { + out[i] = mpz_class(pt_check[i]); + } + ASSERT_EQ(in, out); + } +#endif + { ASSERT_THROW(petace::solo::ahepaillier::Encryptor(nullptr), std::invalid_argument); } + { ASSERT_THROW(petace::solo::ahepaillier::Encryptor(nullptr, nullptr), std::invalid_argument); } + { ASSERT_THROW(petace::solo::ahepaillier::Decryptor(nullptr), std::invalid_argument); } } { - std::vector<petace::solo::ahepaillier::BigNum> in = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; - petace::solo::ahepaillier::Plaintext pt(in); - petace::solo::ahepaillier::Ciphertext ct; - encryptor.encrypt(pt, ct); - ASSERT_EQ(ct.slot_count(), 16); - ct[0]; + std::shared_ptr<petace::solo::ahepaillier::SecretKey> sk = nullptr; + std::shared_ptr<petace::solo::ahepaillier::PublicKey> pk = nullptr; + petace::solo::ahepaillier::KeyGenerator generator(1024); + generator.get_key_pair(sk, pk, false); + petace::solo::ahepaillier::Encryptor encryptor(pk); + petace::solo::ahepaillier::Encryptor encryptor_with_sk(pk, sk); + petace::solo::ahepaillier::Decryptor decryptor(sk); + petace::solo::ahepaillier::Encoder encoder; + { + std::uint64_t in = 1234; + petace::solo::ahepaillier::Plaintext pt; + petace::solo::ahepaillier::Ciphertext ct; + encoder.encode(in, pt); + encryptor.encrypt(pt, ct); - petace::solo::ahepaillier::Plaintext pt_check; - decryptor.decrypt(ct, pt_check); - std::vector<petace::solo::ahepaillier::BigNum> out(pt_check); - ASSERT_EQ(in, out); + petace::solo::ahepaillier::Plaintext pt_check; + decryptor.decrypt(ct, pt_check); + std::uint64_t out = encoder.decode(pt_check); + ASSERT_EQ(in, out); + } + { + std::uint64_t in = 1234; + petace::solo::ahepaillier::Plaintext pt; + petace::solo::ahepaillier::Ciphertext ct; + encoder.encode(in, pt); + encryptor_with_sk.encrypt(pt, ct); + + petace::solo::ahepaillier::Plaintext pt_check; + decryptor.decrypt(ct, pt_check); + std::uint64_t out = encoder.decode(pt_check); + ASSERT_EQ(in, out); + } + { + std::vector<std::uint64_t> in = {1, 2, 3, 4, 5, 6, 7, 8}; + std::vector<petace::solo::ahepaillier::Plaintext> pt; + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + + encoder.encode(in, pt); + encryptor.encrypt_many(pt, ct); + + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct, pt_check); + std::vector<std::uint64_t> out; + + encoder.decode(pt_check, out); + + ASSERT_EQ(in, out); + } + { + std::vector<std::uint64_t> in = {1, 2, 3, 4, 5, 6, 7, 8}; + std::vector<petace::solo::ahepaillier::Plaintext> pt; + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + + encoder.encode(in, pt); + encryptor_with_sk.encrypt_many(pt, ct); + + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct, pt_check); + std::vector<std::uint64_t> out; + encoder.decode(pt_check, out); + ASSERT_EQ(in, out); + } +#ifdef SOLO_USE_IPCL + { + std::vector<BigNumber> in = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + std::vector<petace::solo::ahepaillier::Plaintext> pt(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) { + pt[i] = petace::solo::ahepaillier::Plaintext(in[i]); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + encryptor.encrypt_many(pt, ct); + + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct, pt_check); + std::vector<BigNumber> out(pt_check.size()); + for (std::size_t i = 0; i < out.size(); ++i) { + out[i] = BigNumber(ipcl::PlainText(pt_check[i])); + } + ASSERT_EQ(in, out); + } + { + std::vector<BigNumber> in = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + std::vector<petace::solo::ahepaillier::Plaintext> pt(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) { + pt[i] = petace::solo::ahepaillier::Plaintext(in[i]); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + encryptor_with_sk.encrypt_many(pt, ct); + + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct, pt_check); + std::vector<BigNumber> out(pt_check.size()); + for (std::size_t i = 0; i < out.size(); ++i) { + out[i] = BigNumber(ipcl::PlainText(pt_check[i])); + } + ASSERT_EQ(in, out); + } + { + std::vector<BigNumber> in = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + std::vector<petace::solo::ahepaillier::Plaintext> pt(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) { + pt[i] = petace::solo::ahepaillier::Plaintext(in[i]); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + encryptor.encrypt_many(pt, ct); + + std::size_t n_square_byte_count = ((pk->key_length() + 7) / 8) * 2; + std::vector<petace::solo::Byte> ct_bytes(ct.size() * n_square_byte_count); + for (std::size_t i = 0; i < ct.size(); ++i) { + ct[i].serialize_to_bytes(ct_bytes.data() + i * n_square_byte_count, n_square_byte_count); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct_deserialized(ct.size()); + for (std::size_t i = 0; i < ct.size(); ++i) { + ct_deserialized[i].deserialize_from_bytes( + pk, ct_bytes.data() + i * n_square_byte_count, n_square_byte_count); + } + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct_deserialized, pt_check); + std::vector<BigNumber> out(pt_check.size()); + for (std::size_t i = 0; i < out.size(); ++i) { + out[i] = BigNumber(ipcl::PlainText(pt_check[i])); + } + ASSERT_EQ(in, out); + } +#else + { + std::vector<mpz_class> in = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + std::vector<petace::solo::ahepaillier::Plaintext> pt(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) { + pt[i] = petace::solo::ahepaillier::Plaintext(in[i]); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + encryptor.encrypt_many(pt, ct); + + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct, pt_check); + std::vector<mpz_class> out(pt_check.size()); + for (std::size_t i = 0; i < out.size(); ++i) { + out[i] = mpz_class(pt_check[i]); + } + ASSERT_EQ(in, out); + } + { + std::vector<mpz_class> in = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + std::vector<petace::solo::ahepaillier::Plaintext> pt(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) { + pt[i] = petace::solo::ahepaillier::Plaintext(in[i]); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + encryptor_with_sk.encrypt_many(pt, ct); + + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct, pt_check); + std::vector<mpz_class> out(pt_check.size()); + for (std::size_t i = 0; i < out.size(); ++i) { + out[i] = mpz_class(pt_check[i]); + } + ASSERT_EQ(in, out); + } + { + std::vector<mpz_class> in = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + std::vector<petace::solo::ahepaillier::Plaintext> pt(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) { + pt[i] = petace::solo::ahepaillier::Plaintext(in[i]); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct; + encryptor.encrypt_many(pt, ct); + std::size_t n_square_byte_count = ((pk->key_length() + 7) / 8) * 2; + std::vector<petace::solo::Byte> ct_bytes(ct.size() * n_square_byte_count); + for (std::size_t i = 0; i < ct.size(); ++i) { + ct[i].serialize_to_bytes(ct_bytes.data() + i * n_square_byte_count, n_square_byte_count); + } + std::vector<petace::solo::ahepaillier::Ciphertext> ct_deserialized(ct.size()); + for (std::size_t i = 0; i < ct.size(); ++i) { + ct_deserialized[i].deserialize_from_bytes( + pk, ct_bytes.data() + i * n_square_byte_count, n_square_byte_count); + } + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + decryptor.decrypt_many(ct_deserialized, pt_check); + std::vector<mpz_class> out(pt_check.size()); + for (std::size_t i = 0; i < out.size(); ++i) { + out[i] = mpz_class(pt_check[i]); + } + ASSERT_EQ(in, out); + } +#endif + { ASSERT_THROW(petace::solo::ahepaillier::Encryptor(nullptr), std::invalid_argument); } + { ASSERT_THROW(petace::solo::ahepaillier::Encryptor(nullptr, nullptr), std::invalid_argument); } + { ASSERT_THROW(petace::solo::ahepaillier::Decryptor(nullptr), std::invalid_argument); } } - { ASSERT_THROW(petace::solo::ahepaillier::Encryptor(nullptr, true), std::invalid_argument); } - { ASSERT_THROW(petace::solo::ahepaillier::Decryptor(nullptr), std::invalid_argument); } } TEST(AHEPaillierTest, Evaluator) { @@ -285,29 +802,30 @@ TEST(AHEPaillierTest, Evaluator) { std::shared_ptr<petace::solo::ahepaillier::PublicKey> pk = nullptr; petace::solo::ahepaillier::KeyGenerator generator(1024); generator.get_key_pair(sk, pk, true); - petace::solo::ahepaillier::Encryptor encryptor(pk, true); + petace::solo::ahepaillier::Encryptor encryptor(pk, sk); petace::solo::ahepaillier::Decryptor decryptor(sk); petace::solo::ahepaillier::Encoder encoder; - petace::solo::ahepaillier::Evaluator evaluator; + petace::solo::ahepaillier::Evaluator evaluator(pk); + petace::solo::ahepaillier::Evaluator evaluator_with_sk(pk, sk); { std::vector<std::uint64_t> in_0 = {1, 2, 3, 4, 5, 6, 7, 8}; std::vector<std::uint64_t> in_1 = {8, 7, 6, 5, 4, 3, 2, 1}; std::vector<std::uint64_t> in_sum = {9, 9, 9, 9, 9, 9, 9, 9}; std::vector<std::uint64_t> in_check; - petace::solo::ahepaillier::Plaintext pt_0; - petace::solo::ahepaillier::Plaintext pt_1; - petace::solo::ahepaillier::Plaintext pt_check; - petace::solo::ahepaillier::Ciphertext ct_0; - petace::solo::ahepaillier::Ciphertext ct_1; - petace::solo::ahepaillier::Ciphertext ct_out; + std::vector<petace::solo::ahepaillier::Plaintext> pt_0; + std::vector<petace::solo::ahepaillier::Plaintext> pt_1; + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + std::vector<petace::solo::ahepaillier::Ciphertext> ct_0; + std::vector<petace::solo::ahepaillier::Ciphertext> ct_1; + std::vector<petace::solo::ahepaillier::Ciphertext> ct_out; encoder.encode(in_0, pt_0); encoder.encode(in_1, pt_1); - encryptor.encrypt(pt_0, ct_0); - encryptor.encrypt(pt_1, ct_1); + encryptor.encrypt_many(pt_0, ct_0); + encryptor.encrypt_many(pt_1, ct_1); - evaluator.add(ct_0, ct_1, ct_out); - decryptor.decrypt(ct_out, pt_check); + evaluator.add_many(ct_0, ct_1, ct_out); + decryptor.decrypt_many(ct_out, pt_check); encoder.decode(pt_check, in_check); ASSERT_EQ(in_check, in_sum); } @@ -316,19 +834,19 @@ TEST(AHEPaillierTest, Evaluator) { std::vector<std::uint64_t> in_1 = {8, 7, 6, 5, 4, 3, 2, 1}; std::vector<std::uint64_t> in_sum = {9, 9, 9, 9, 9, 9, 9, 9}; std::vector<std::uint64_t> in_check; - petace::solo::ahepaillier::Plaintext pt_0; - petace::solo::ahepaillier::Plaintext pt_1; - petace::solo::ahepaillier::Plaintext pt_check; - petace::solo::ahepaillier::Ciphertext ct_0; - petace::solo::ahepaillier::Ciphertext ct_1; - petace::solo::ahepaillier::Ciphertext ct_out; + std::vector<petace::solo::ahepaillier::Plaintext> pt_0; + std::vector<petace::solo::ahepaillier::Plaintext> pt_1; + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + std::vector<petace::solo::ahepaillier::Ciphertext> ct_0; + std::vector<petace::solo::ahepaillier::Ciphertext> ct_1; + std::vector<petace::solo::ahepaillier::Ciphertext> ct_out; encoder.encode(in_0, pt_0); encoder.encode(in_1, pt_1); - encryptor.encrypt(pt_0, ct_0); + encryptor.encrypt_many(pt_0, ct_0); - evaluator.add(ct_0, pt_1, ct_out); - decryptor.decrypt(ct_out, pt_check); + evaluator.add_many(ct_0, pt_1, ct_out); + decryptor.decrypt_many(ct_out, pt_check); encoder.decode(pt_check, in_check); ASSERT_EQ(in_check, in_sum); } @@ -337,30 +855,50 @@ TEST(AHEPaillierTest, Evaluator) { std::vector<std::uint64_t> in_1 = {8, 7, 6, 5, 4, 3, 2, 1}; std::vector<std::uint64_t> in_mul = {8, 14, 18, 20, 20, 18, 14, 8}; std::vector<std::uint64_t> in_check; - petace::solo::ahepaillier::Plaintext pt_0; - petace::solo::ahepaillier::Plaintext pt_1; - petace::solo::ahepaillier::Plaintext pt_check; - petace::solo::ahepaillier::Ciphertext ct_0; - petace::solo::ahepaillier::Ciphertext ct_1; - petace::solo::ahepaillier::Ciphertext ct_out; + std::vector<petace::solo::ahepaillier::Plaintext> pt_0; + std::vector<petace::solo::ahepaillier::Plaintext> pt_1; + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + std::vector<petace::solo::ahepaillier::Ciphertext> ct_0; + std::vector<petace::solo::ahepaillier::Ciphertext> ct_1; + std::vector<petace::solo::ahepaillier::Ciphertext> ct_out; encoder.encode(in_0, pt_0); encoder.encode(in_1, pt_1); - encryptor.encrypt(pt_0, ct_0); + encryptor.encrypt_many(pt_0, ct_0); - evaluator.mul(ct_0, pt_1, ct_out); - decryptor.decrypt(ct_out, pt_check); + evaluator.mul_many(ct_0, pt_1, ct_out); + decryptor.decrypt_many(ct_out, pt_check); encoder.decode(pt_check, in_check); ASSERT_EQ(in_check, in_mul); } -} + { + std::vector<std::uint64_t> in_0 = {1, 2, 3, 4, 5, 6, 7, 8}; + std::vector<std::uint64_t> in_1 = {8, 7, 6, 5, 4, 3, 2, 1}; + std::vector<std::uint64_t> in_mul = {8, 14, 18, 20, 20, 18, 14, 8}; + std::vector<std::uint64_t> in_check; + std::vector<petace::solo::ahepaillier::Plaintext> pt_0; + std::vector<petace::solo::ahepaillier::Plaintext> pt_1; + std::vector<petace::solo::ahepaillier::Plaintext> pt_check; + std::vector<petace::solo::ahepaillier::Ciphertext> ct_0; + std::vector<petace::solo::ahepaillier::Ciphertext> ct_1; + std::vector<petace::solo::ahepaillier::Ciphertext> ct_out; + + encoder.encode(in_0, pt_0); + encoder.encode(in_1, pt_1); + encryptor.encrypt_many(pt_0, ct_0); + evaluator_with_sk.mul_many(ct_0, pt_1, ct_out); + decryptor.decrypt_many(ct_out, pt_check); + encoder.decode(pt_check, in_check); + ASSERT_EQ(in_check, in_mul); + } +} +#ifdef SOLO_USE_IPCL TEST(AHEPaillierTest, Utils) { const std::vector<int> bits_vec = {2, 31, 32, 482, 511, 512, 994, 1023, 1024, 2018, 2047, 2048}; for (std::size_t i = 0; i < bits_vec.size(); ++i) { - auto bn = petace::solo::ahepaillier::BigNum::One(); + auto bn = BigNumber::One(); petace::solo::ahepaillier::utils::bn_lshift(bn, bits_vec[i]); } } - #endif diff --git a/test/prng_test.cpp b/test/prng_test.cpp index 3a40402..65fd4fe 100644 --- a/test/prng_test.cpp +++ b/test/prng_test.cpp @@ -112,6 +112,7 @@ TEST(PRNGTest, PRNG) { ASSERT_EQ(prng->seed(), seed); } #endif +#ifdef SOLO_USE_AES_INTRIN { ASSERT_THROW( petace::solo::PRNGImpl<petace::solo::PRNGScheme::AES_ECB_CTR>(std::size_t(0)), std::invalid_argument); @@ -158,4 +159,5 @@ TEST(PRNGTest, PRNG) { ASSERT_EQ(prng->seed(), seed); } #endif +#endif } diff --git a/test/simple_hashing_test.cpp b/test/simple_hashing_test.cpp index 39f5ea3..03c1e85 100644 --- a/test/simple_hashing_test.cpp +++ b/test/simple_hashing_test.cpp @@ -59,7 +59,7 @@ TEST(SimpleHashingTest, Default) { auto num_of_elements_in_bins = simple_hash.get_num_of_elements_in_bins(); std::size_t sum_elements = 0; std::for_each(num_of_elements_in_bins.begin(), num_of_elements_in_bins.end(), - [&sum_elements](int n) { sum_elements += n; }); + [&sum_elements](std::size_t n) { sum_elements += n; }); ASSERT_EQ(sum_elements, 3 * 1024); simple_hash.insert(elements[0]); } @@ -83,7 +83,7 @@ TEST(SimpleHashingTest, Default) { auto num_of_elements_in_bins = simple_hash.get_num_of_elements_in_bins(); std::size_t sum_elements = 0; std::for_each(num_of_elements_in_bins.begin(), num_of_elements_in_bins.end(), - [&sum_elements](int n) { sum_elements += n; }); + [&sum_elements](std::size_t n) { sum_elements += n; }); ASSERT_EQ(sum_elements, 3 * 1024); simple_hash.insert(elements[0]); } @@ -107,7 +107,7 @@ TEST(SimpleHashingTest, Default) { auto num_of_elements_in_bins = simple_hash.get_num_of_elements_in_bins(); std::size_t sum_elements = 0; std::for_each(num_of_elements_in_bins.begin(), num_of_elements_in_bins.end(), - [&sum_elements](int n) { sum_elements += n; }); + [&sum_elements](std::size_t n) { sum_elements += n; }); ASSERT_EQ(sum_elements, 3 * 1024); simple_hash.insert(elements[0]); }