diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e63a0a1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.10) + +project(SealPIR VERSION 1.1 LANGUAGES CXX) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) + +add_executable(sealpir + main.cpp + pir.cpp + pir_client.cpp + pir_server.cpp +) + +find_package(SEAL 2.3.1 EXACT REQUIRED) +find_package(Threads REQUIRED) + +target_link_libraries(sealpir + SEAL::seal + Threads::Threads +) diff --git a/Makefile b/Makefile deleted file mode 100644 index b6ed4c1..0000000 --- a/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -CXX=g++ - -IDIR =../SEAL/SEAL/ -LDIR =../SEAL/bin/ - -CFLAGS=-std=c++11 -I. -I$(IDIR) -O3 -ODIR=obj -BDIR=bin -LIBS=-L$(LDIR) -lseal - -DEPS = pir.hpp pir_server.hpp pir_client.hpp - -_OBJ = pir.o main.o pir_server.o pir_client.o -OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ)) - - -$(ODIR)/%.o: %.cpp $(DEPS) - @mkdir -p $(@D) - $(CXX) -c -o $@ $< $(CFLAGS) - -$(BDIR)/main: $(OBJ) $(DEPS) - @mkdir -p $(@D) - $(CXX) -o $@ $(OBJ) $(CFLAGS) $(LIBS) - -all: main - -.PHONY: clean - -clean: - rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~ $(BDIR)/* diff --git a/README.md b/README.md index c320451..493695e 100644 --- a/README.md +++ b/README.md @@ -5,39 +5,16 @@ SealPIR is a (research) library and should not be used in production systems. Se # Compiling SEAL -SealPIR depends on SEAL v2.3.0-4 and a patch that exposes the substitution operator. You can get SEAL v2.3.0-4 from this [link](http://sealcrypto.org). - -Once you have downloaded SEAL, apply the patch SEAL_v2.3.0-4.patch (available in this repository) to it. Here are the exact steps. - -We assume that you are in the SEAL directory, and that you have copied the patch to this directory. - -First, convert the SEAL directory into a git repo: - -```sh -$ git init -$ git add . -$ git commit -m "SEAL v2.3.0-4" -``` -Then, apply the patch: - -```sh -$ git am SEAL_v2.3.0-4.patch -``` - -Finally, compile SEAL (NOTE: gcc-8 is not currently supported): - -```sh -$ cd SEAL -$ ./configure CXXFLAGS="-O3 -march=native -fPIC" -$ make clean && make -``` +SealPIR depends on SEAL 2.3.1 ([link](http://sealcrypto.org). Download SEAL, and follow the instructions in INSTALL.txt to install it system-wide. # Compiling SealPIR -The current Makefile assumes that SEAL_v2.3.0-4 is located (relative to SealPIR) at: ../SEAL/. If this is not the case change the IDIR and LDIR variables in the Makefile accordingly. - -To compile SealPIR simply run ``make``. It should produce a binary file in ``bin/main``. +Once SEAL 2.3.1 is installed, to build SealPIR simply run: + cmake . + make + +This should produce a binary file ``bin/sealpir``. # Using SealPIR diff --git a/SEAL_v2.3.0-4.patch b/SEAL_v2.3.0-4.patch deleted file mode 100644 index dff6008..0000000 --- a/SEAL_v2.3.0-4.patch +++ /dev/null @@ -1,1801 +0,0 @@ -From e37bf6b79c81cbbeff19378ad425f987c036286b Mon Sep 17 00:00:00 2001 -From: Kim Laine -Date: Mon, 4 Dec 2017 16:09:56 -0800 -Subject: [PATCH 1/3] Explosed generic Galois automorphisms in public API - ---- - SEAL/seal/evaluator.cpp | 5 + - SEAL/seal/evaluator.h | 181 ++++++++++++++++++++++++++------ - SEAL/seal/keygenerator.cpp | 8 -- - SEAL/seal/keygenerator.h | 40 ++++--- - SEALNET/sealnet/EvaluatorWrapper.cpp | 146 ++++++++++++++++++++++++++ - SEALNET/sealnet/EvaluatorWrapper.h | 157 ++++++++++++++++++++++++--- - SEALNET/sealnet/KeyGeneratorWrapper.cpp | 33 ++++++ - SEALNET/sealnet/KeyGeneratorWrapper.h | 35 ++++++ - SEALNETTest/EvaluatorWrapper.cs | 82 +++++++++++++++ - SEALNETTest/KeyGeneratorWrapper.cs | 147 ++++++++++++++++++++++++++ - SEALTest/evaluator.cpp | 79 ++++++++++++++ - SEALTest/keygenerator.cpp | 146 ++++++++++++++++++++++++++ - 12 files changed, 990 insertions(+), 69 deletions(-) - -diff --git a/SEAL/seal/evaluator.cpp b/SEAL/seal/evaluator.cpp -index 0a5b99d..8945b5d 100644 ---- a/SEAL/seal/evaluator.cpp -+++ b/SEAL/seal/evaluator.cpp -@@ -1791,6 +1791,11 @@ namespace seal - - void Evaluator::rotate_rows(Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys, const MemoryPoolHandle &pool) - { -+ if (!qualifiers_.enable_batching) -+ { -+ throw logic_error("encryption parameters do not support batching"); -+ } -+ - // Is there anything to do? - if (steps == 0) - { -diff --git a/SEAL/seal/evaluator.h b/SEAL/seal/evaluator.h -index 5b67455..93cc1d2 100644 ---- a/SEAL/seal/evaluator.h -+++ b/SEAL/seal/evaluator.h -@@ -894,13 +894,136 @@ namespace seal - @throws std::invalid_argument if plain_ntt is zero - @throws std::logic_error if destination_ntt is aliased and needs to be reallocated - */ -- inline void multiply_plain_ntt(const Ciphertext &encrypted_ntt, const Plaintext &plain_ntt, -- Ciphertext &destination_ntt) -+ inline void multiply_plain_ntt(const Ciphertext &encrypted_ntt, -+ const Plaintext &plain_ntt, Ciphertext &destination_ntt) - { - destination_ntt = encrypted_ntt; - multiply_plain_ntt(destination_ntt, plain_ntt); - } - -+ /** -+ Applies a Galois automorphism to a ciphertext. To evaluate the Galois automorphism, -+ an appropriate set of Galois keys must also be provided. Dynamic memory allocations -+ in the process are allocated from the memory pool pointed to by the given -+ MemoryPoolHandle. -+ -+ The desired Galois automorphism is given as a Galois element, and must be an odd -+ integer in the interval [1, M-1], where M = 2*N, and N = degree(poly_modulus). Used -+ with batching, a Galois element 3^i % M corresponds to a cyclic row rotation i steps -+ to the left, and a Galois element 3^(N/2-i) % M corresponds to a cyclic row rotation -+ i steps to the right. The Galois element M-1 corresponds to a column rotation (row -+ swap). In the polynomial view (not batching), a Galois automorphism by a Galois -+ element p changes Enc(plain(x)) to Enc(plain(x^p)). -+ -+ @param[in] encrypted The ciphertext to apply the Galois automorphism to -+ @param[in] galois_elt The Galois element -+ @param[in] galois_keys The Galois keys -+ @param[in] pool The MemoryPoolHandle pointing to a valid memory pool -+ @throws std::invalid_argument if encrypted or galois_keys is not valid for the -+ encryption parameters -+ @throws std::invalid_argument if encrypted has size greater than two -+ @throws std::invalid_argument if the Galois element is not valid -+ @throws std::invalid_argument if necessary Galois keys are not present -+ @throws std::invalid_argument if pool is uninitialized -+ */ -+ void apply_galois(Ciphertext &encrypted, std::uint64_t galois_elt, -+ const GaloisKeys &galois_keys, const MemoryPoolHandle &pool); -+ -+ /** -+ Applies a Galois automorphism to a ciphertext. To evaluate the Galois automorphism, -+ an appropriate set of Galois keys must also be provided. Dynamic memory allocations -+ in the process are allocated from the memory pool pointed to by the local -+ MemoryPoolHandle. -+ -+ The desired Galois automorphism is given as a Galois element, and must be an odd -+ integer in the interval [1, M-1], where M = 2*N, and N = degree(poly_modulus). Used -+ with batching, a Galois element 3^i % M corresponds to a cyclic row rotation i steps -+ to the left, and a Galois element 3^(N/2-i) % M corresponds to a cyclic row rotation -+ i steps to the right. The Galois element M-1 corresponds to a column rotation (row -+ swap). In the polynomial view (not batching), a Galois automorphism by a Galois -+ element p changes Enc(plain(x)) to Enc(plain(x^p)). -+ -+ @param[in] encrypted The ciphertext to apply the Galois automorphism to -+ @param[in] galois_elt The Galois element -+ @param[in] galois_keys The Galois keys -+ @throws std::invalid_argument if encrypted or galois_keys is not valid for the -+ encryption parameters -+ @throws std::invalid_argument if encrypted has size greater than two -+ @throws std::invalid_argument if the Galois element is not valid -+ @throws std::invalid_argument if necessary Galois keys are not present -+ */ -+ inline void apply_galois(Ciphertext &encrypted, std::uint64_t galois_elt, -+ const GaloisKeys &galois_keys) -+ { -+ apply_galois(encrypted, galois_elt, galois_keys, pool_); -+ } -+ -+ /** -+ Applies a Galois automorphism to a ciphertext and writes the result to the -+ destination parameter. To evaluate the Galois automorphism, an appropriate set of -+ Galois keys must also be provided. Dynamic memory allocations in the process are -+ allocated from the memory pool pointed to by the given MemoryPoolHandle. -+ -+ The desired Galois automorphism is given as a Galois element, and must be an odd -+ integer in the interval [1, M-1], where M = 2*N, and N = degree(poly_modulus). Used -+ with batching, a Galois element 3^i % M corresponds to a cyclic row rotation i steps -+ to the left, and a Galois element 3^(N/2-i) % M corresponds to a cyclic row rotation -+ i steps to the right. The Galois element M-1 corresponds to a column rotation (row -+ swap). In the polynomial view (not batching), a Galois automorphism by a Galois -+ element p changes Enc(plain(x)) to Enc(plain(x^p)). -+ -+ @param[in] encrypted The ciphertext to apply the Galois automorphism to -+ @param[in] galois_elt The Galois element -+ @param[in] galois_keys The Galois keys -+ @param[out] destination The ciphertext to overwrite with the result -+ @param[in] pool The MemoryPoolHandle pointing to a valid memory pool -+ @throws std::invalid_argument if encrypted or galois_keys is not valid for the -+ encryption parameters -+ @throws std::invalid_argument if encrypted has size greater than two -+ @throws std::invalid_argument if the Galois element is not valid -+ @throws std::invalid_argument if necessary Galois keys are not present -+ @throws std::logic_error if destination is aliased and needs to be reallocated -+ @throws std::invalid_argument if pool is uninitialized -+ */ -+ inline void apply_galois(const Ciphertext &encrypted, std::uint64_t galois_elt, -+ const GaloisKeys &galois_keys, Ciphertext &destination, -+ const MemoryPoolHandle &pool) -+ { -+ destination = encrypted; -+ apply_galois(destination, galois_elt, galois_keys, pool); -+ } -+ -+ /** -+ Applies a Galois automorphism to a ciphertext and writes the result to the -+ destination parameter. To evaluate the Galois automorphism, an appropriate set of -+ Galois keys must also be provided. Dynamic memory allocations in the process are -+ allocated from the memory pool pointed to by the local MemoryPoolHandle. -+ -+ The desired Galois automorphism is given as a Galois element, and must be an odd -+ integer in the interval [1, M-1], where M = 2*N, and N = degree(poly_modulus). Used -+ with batching, a Galois element 3^i % M corresponds to a cyclic row rotation i steps -+ to the left, and a Galois element 3^(N/2-i) % M corresponds to a cyclic row rotation -+ i steps to the right. The Galois element M-1 corresponds to a column rotation (row -+ swap). In the polynomial view (not batching), a Galois automorphism by a Galois -+ element p changes Enc(plain(x)) to Enc(plain(x^p)). -+ -+ @param[in] encrypted The ciphertext to apply the Galois automorphism to -+ @param[in] galois_elt The Galois element -+ @param[in] galois_keys The Galois keys -+ @param[out] destination The ciphertext to overwrite with the result -+ @throws std::invalid_argument if encrypted or galois_keys is not valid for the -+ encryption parameters -+ @throws std::invalid_argument if encrypted has size greater than two -+ @throws std::invalid_argument if the Galois element is not valid -+ @throws std::invalid_argument if necessary Galois keys are not present -+ @throws std::logic_error if destination is aliased and needs to be reallocated -+ */ -+ inline void apply_galois(const Ciphertext &encrypted, std::uint64_t galois_elt, -+ const GaloisKeys &galois_keys, Ciphertext &destination) -+ { -+ apply_galois(encrypted, galois_elt, galois_keys, destination, pool_); -+ } -+ - /** - Rotates plaintext matrix rows cyclically. When batching is used, this function rotates - the encrypted plaintext matrix rows cyclically to the left (steps > 0) or to the right -@@ -913,6 +1036,7 @@ namespace seal - @param[in] steps The number of steps to rotate (negative left, positive right) - @param[in] galois_keys The Galois keys - @param[in] pool The MemoryPoolHandle pointing to a valid memory pool -+ @throws std::logic_error if the encryption parameters do not support batching - @throws std::invalid_argument if encrypted or galois_keys is not valid for the - encryption parameters - @throws std::invalid_argument if encrypted has size greater than two -@@ -934,13 +1058,15 @@ namespace seal - @param[in] encrypted The ciphertext to rotate - @param[in] steps The number of steps to rotate (negative left, positive right) - @param[in] galois_keys The Galois keys -+ @throws std::logic_error if the encryption parameters do not support batching - @throws std::invalid_argument if encrypted or galois_keys is not valid for the - encryption parameters - @throws std::invalid_argument if encrypted has size greater than two - @throws std::invalid_argument if steps has too big absolute value - @throws std::invalid_argument if necessary Galois keys are not present - */ -- inline void rotate_rows(Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys) -+ inline void rotate_rows(Ciphertext &encrypted, int steps, -+ const GaloisKeys &galois_keys) - { - rotate_rows(encrypted, steps, galois_keys, pool_); - } -@@ -959,6 +1085,7 @@ namespace seal - @param[in] galois_keys The Galois keys - @param[out] destination The ciphertext to overwrite with the rotated result - @param[in] pool The MemoryPoolHandle pointing to a valid memory pool -+ @throws std::logic_error if the encryption parameters do not support batching - @throws std::invalid_argument if encrypted or galois_keys is not valid for the - encryption parameters - @throws std::invalid_argument if encrypted has size greater than two -@@ -968,7 +1095,8 @@ namespace seal - @throws std::invalid_argument if pool is uninitialized - */ - inline void rotate_rows(const Ciphertext &encrypted, int steps, -- const GaloisKeys &galois_keys, Ciphertext &destination, const MemoryPoolHandle &pool) -+ const GaloisKeys &galois_keys, Ciphertext &destination, -+ const MemoryPoolHandle &pool) - { - destination = encrypted; - rotate_rows(destination, steps, galois_keys, pool); -@@ -987,6 +1115,7 @@ namespace seal - @param[in] steps The number of steps to rotate (negative left, positive right) - @param[in] galois_keys The Galois keys - @param[out] destination The ciphertext to overwrite with the rotated result -+ @throws std::logic_error if the encryption parameters do not support batching - @throws std::invalid_argument if encrypted or galois_keys is not valid for the - encryption parameters - @throws std::invalid_argument if encrypted has size greater than two -@@ -1011,6 +1140,7 @@ namespace seal - @param[in] galois_keys The Galois keys - @param[out] destination The ciphertext to overwrite with the rotated result - @param[in] pool The MemoryPoolHandle pointing to a valid memory pool -+ @throws std::logic_error if the encryption parameters do not support batching - @throws std::invalid_argument if encrypted or galois_keys is not valid for the - encryption parameters - @throws std::invalid_argument if encrypted has size greater than two -@@ -1021,6 +1151,10 @@ namespace seal - inline void rotate_columns(Ciphertext &encrypted, const GaloisKeys &galois_keys, - const MemoryPoolHandle &pool) - { -+ if (!qualifiers_.enable_batching) -+ { -+ throw std::logic_error("encryption parameters do not support batching"); -+ } - std::uint64_t m = (parms_.poly_modulus().coeff_count() - 1) << 1; - apply_galois(encrypted, m - 1, galois_keys, pool); - } -@@ -1035,6 +1169,7 @@ namespace seal - @param[in] encrypted The ciphertext to rotate - @param[in] galois_keys The Galois keys - @param[out] destination The ciphertext to overwrite with the rotated result -+ @throws std::logic_error if the encryption parameters do not support batching - @throws std::invalid_argument if encrypted or galois_keys is not valid for the - encryption parameters - @throws std::invalid_argument if encrypted has size greater than two -@@ -1058,6 +1193,7 @@ namespace seal - @param[in] galois_keys The Galois keys - @param[out] destination The ciphertext to overwrite with the rotated result - @param[in] pool The MemoryPoolHandle pointing to a valid memory pool -+ @throws std::logic_error if the encryption parameters do not support batching - @throws std::invalid_argument if encrypted or galois_keys is not valid for the - encryption parameters - @throws std::invalid_argument if encrypted has size greater than two -@@ -1084,6 +1220,7 @@ namespace seal - @param[in] encrypted The ciphertext to rotate - @param[in] galois_keys The Galois keys - @param[out] destination The ciphertext to overwrite with the rotated result -+ @throws std::logic_error if the encryption parameters do not support batching - @throws std::invalid_argument if encrypted or galois_keys is not valid for the - encryption parameters - @throws std::invalid_argument if encrypted has size greater than two -@@ -1101,10 +1238,11 @@ namespace seal - - Evaluator &operator =(Evaluator &&assign) = delete; - -- void relinearize(Ciphertext &encrypted, const EvaluationKeys &evaluation_keys, int destination_size, -- const MemoryPoolHandle &pool); -+ void relinearize(Ciphertext &encrypted, const EvaluationKeys &evaluation_keys, -+ int destination_size, const MemoryPoolHandle &pool); - -- inline void decompose_single_coeff(const std::uint64_t *value, std::uint64_t *destination, const MemoryPoolHandle &pool) -+ inline void decompose_single_coeff(const std::uint64_t *value, -+ std::uint64_t *destination, const MemoryPoolHandle &pool) - { - #ifdef SEAL_DEBUG - if (value == nullptr) -@@ -1150,7 +1288,8 @@ namespace seal - } - } - -- inline void decompose(const std::uint64_t *value, std::uint64_t *destination, const MemoryPoolHandle &pool) -+ inline void decompose(const std::uint64_t *value, std::uint64_t *destination, -+ const MemoryPoolHandle &pool) - { - #ifdef SEAL_DEBUG - if (value == nullptr) -@@ -1209,32 +1348,6 @@ namespace seal - - void populate_Zmstar_to_generator(); - -- // The apply_galois function applies a Galois automorphism to a ciphertext. -- // It is needed for slot permutations. -- // Input: encryption of M(x) and an integer p such that gcd(p, m) = 1. -- // Output: encryption of M(x^p). -- // The function requires certain GaloisKeys and auxiliary data. -- void apply_galois(Ciphertext &encrypted, std::uint64_t galois_elt, const GaloisKeys &evaluation_keys, -- const MemoryPoolHandle &pool); -- -- inline void apply_galois(Ciphertext &encrypted, std::uint64_t galois_elt, const GaloisKeys &evaluation_keys) -- { -- apply_galois(encrypted, galois_elt, evaluation_keys, pool_); -- } -- -- inline void apply_galois(const Ciphertext &encrypted, std::uint64_t galois_elt, -- const GaloisKeys &evaluation_keys, Ciphertext &destination, const MemoryPoolHandle &pool) -- { -- destination = encrypted; -- apply_galois(destination, galois_elt, evaluation_keys, pool); -- } -- -- inline void apply_galois(const Ciphertext &encrypted, std::uint64_t galois_elt, -- const GaloisKeys &evaluation_keys, Ciphertext &destination) -- { -- apply_galois(encrypted, galois_elt, evaluation_keys, destination, pool_); -- } -- - MemoryPoolHandle pool_; - - EncryptionParameters parms_; -diff --git a/SEAL/seal/keygenerator.cpp b/SEAL/seal/keygenerator.cpp -index fa7fd46..ee6338f 100644 ---- a/SEAL/seal/keygenerator.cpp -+++ b/SEAL/seal/keygenerator.cpp -@@ -293,10 +293,6 @@ namespace seal - { - throw logic_error("cannot generate galois keys for unspecified secret key"); - } -- if (!qualifiers_.enable_batching) -- { -- throw logic_error("encryption parameters are not valid for batching"); -- } - - // Check that decomposition_bit_count is in correct interval - if (decomposition_bit_count < SEAL_DBC_MIN || decomposition_bit_count > SEAL_DBC_MAX) -@@ -426,10 +422,6 @@ namespace seal - { - throw logic_error("cannot generate galois keys for unspecified secret key"); - } -- if (!qualifiers_.enable_batching) -- { -- throw logic_error("encryption parameters are not valid for batching"); -- } - - // Check that decomposition_bit_count is in correct interval - if (decomposition_bit_count < SEAL_DBC_MIN || decomposition_bit_count > SEAL_DBC_MAX) -diff --git a/SEAL/seal/keygenerator.h b/SEAL/seal/keygenerator.h -index a702a3e..393aa61 100644 ---- a/SEAL/seal/keygenerator.h -+++ b/SEAL/seal/keygenerator.h -@@ -99,15 +99,38 @@ namespace seal - } - - /** -- Generates Galois keys. -+ Generates Galois keys. This function creates logarithmically many (in degree of the -+ polynomial modulus) Galois keys that is sufficient to apply any Galois automorphism -+ (e.g. rotations) on encrypted data. Most users will want to use this overload of -+ the function. - - @param[in] decomposition_bit_count The decomposition bit count - @param[out] galois_keys The Galois keys instance to overwrite with the generated keys - @throws std::invalid_argument if decomposition_bit_count is not within [1, 60] -- @throws std::logic_error if the encryption parameters do not support batching -- */ -+ */ - void generate_galois_keys(int decomposition_bit_count, GaloisKeys &galois_keys); - -+ /** -+ Generates Galois keys. This function creates specific Galois keys that can be used to -+ apply specific Galois automorphisms on encrypted data. The user needs to give as -+ input a vector of Galois elements corresponding to the keys that are to be created. -+ -+ The Galois elements are odd integers in the interval [1, M-1], where M = 2*N, and -+ N = degree(poly_modulus). Used with batching, a Galois element 3^i % M corresponds -+ to a cyclic row rotation i steps to the left, and a Galois element 3^(N/2-i) % M -+ corresponds to a cyclic row rotation i steps to the right. The Galois element M-1 -+ corresponds to a column rotation (row swap). In the polynomial view (not batching), -+ a Galois automorphism by a Galois element p changes Enc(plain(x)) to Enc(plain(x^p)). -+ -+ @param[in] decomposition_bit_count The decomposition bit count -+ @param[in] galois_elts The Galois elements for which to generate keys -+ @param[out] galois_keys The Galois keys instance to overwrite with the generated keys -+ @throws std::invalid_argument if decomposition_bit_count is not within [1, 60] -+ @throws std::invalid_argument if the Galois elements are not valid -+ */ -+ void generate_galois_keys(int decomposition_bit_count, -+ const std::vector &galois_elts, GaloisKeys &galois_keys); -+ - private: - KeyGenerator(const KeyGenerator ©) = delete; - -@@ -141,17 +164,6 @@ namespace seal - return generated_; - } - -- void generate_galois_keys(int decomposition_bit_count, -- const std::vector &galois_elts, GaloisKeys &galois_keys); -- -- inline GaloisKeys generate_galois_keys(int decomposition_bit_count, -- const std::vector &galois_elts) -- { -- GaloisKeys keys; -- generate_galois_keys(decomposition_bit_count, galois_elts, keys); -- return keys; -- } -- - MemoryPoolHandle pool_; - - EncryptionParameters parms_; -diff --git a/SEALNET/sealnet/EvaluatorWrapper.cpp b/SEALNET/sealnet/EvaluatorWrapper.cpp -index b4868ed..648fbfd 100644 ---- a/SEALNET/sealnet/EvaluatorWrapper.cpp -+++ b/SEALNET/sealnet/EvaluatorWrapper.cpp -@@ -1513,6 +1513,152 @@ namespace Microsoft - } - } - -+ void Evaluator::ApplyGalois(Ciphertext ^encrypted, UInt64 galoisElt, GaloisKeys ^galoisKeys) -+ { -+ if (evaluator_ == nullptr) -+ { -+ throw gcnew ObjectDisposedException("Evaluator is disposed"); -+ } -+ if (encrypted == nullptr) -+ { -+ throw gcnew ArgumentNullException("encrypted cannot be null"); -+ } -+ if (galoisKeys == nullptr) -+ { -+ throw gcnew ArgumentNullException("galoisKeys cannot be null"); -+ } -+ try -+ { -+ evaluator_->apply_galois(encrypted->GetCiphertext(), galoisElt, galoisKeys->GetKeys()); -+ GC::KeepAlive(encrypted); -+ GC::KeepAlive(galoisKeys); -+ } -+ catch (const exception &e) -+ { -+ HandleException(&e); -+ } -+ catch (...) -+ { -+ HandleException(nullptr); -+ } -+ } -+ -+ void Evaluator::ApplyGalois(Ciphertext ^encrypted, UInt64 galoisElt, GaloisKeys ^galoisKeys, -+ MemoryPoolHandle ^pool) -+ { -+ if (evaluator_ == nullptr) -+ { -+ throw gcnew ObjectDisposedException("Evaluator is disposed"); -+ } -+ if (encrypted == nullptr) -+ { -+ throw gcnew ArgumentNullException("encrypted cannot be null"); -+ } -+ if (galoisKeys == nullptr) -+ { -+ throw gcnew ArgumentNullException("galoisKeys cannot be null"); -+ } -+ if (pool == nullptr) -+ { -+ throw gcnew ArgumentNullException("pool cannot be null"); -+ } -+ try -+ { -+ evaluator_->apply_galois(encrypted->GetCiphertext(), galoisElt, -+ galoisKeys->GetKeys(), pool->GetHandle()); -+ GC::KeepAlive(encrypted); -+ GC::KeepAlive(galoisKeys); -+ GC::KeepAlive(pool); -+ } -+ catch (const exception &e) -+ { -+ HandleException(&e); -+ } -+ catch (...) -+ { -+ HandleException(nullptr); -+ } -+ } -+ -+ void Evaluator::ApplyGalois(Ciphertext ^encrypted, UInt64 galoisElt, GaloisKeys ^galoisKeys, -+ Ciphertext ^destination) -+ { -+ if (evaluator_ == nullptr) -+ { -+ throw gcnew ObjectDisposedException("Evaluator is disposed"); -+ } -+ if (encrypted == nullptr) -+ { -+ throw gcnew ArgumentNullException("encrypted cannot be null"); -+ } -+ if (galoisKeys == nullptr) -+ { -+ throw gcnew ArgumentNullException("galoisKeys cannot be null"); -+ } -+ if (destination == nullptr) -+ { -+ throw gcnew ArgumentNullException("destination cannot be null"); -+ } -+ try -+ { -+ evaluator_->apply_galois(encrypted->GetCiphertext(), galoisElt, -+ galoisKeys->GetKeys(), destination->GetCiphertext()); -+ GC::KeepAlive(encrypted); -+ GC::KeepAlive(galoisKeys); -+ GC::KeepAlive(destination); -+ } -+ catch (const exception &e) -+ { -+ HandleException(&e); -+ } -+ catch (...) -+ { -+ HandleException(nullptr); -+ } -+ } -+ -+ void Evaluator::ApplyGalois(Ciphertext ^encrypted, UInt64 galoisElt, GaloisKeys ^galoisKeys, -+ Ciphertext ^destination, MemoryPoolHandle ^pool) -+ { -+ if (evaluator_ == nullptr) -+ { -+ throw gcnew ObjectDisposedException("Evaluator is disposed"); -+ } -+ if (encrypted == nullptr) -+ { -+ throw gcnew ArgumentNullException("encrypted cannot be null"); -+ } -+ if (galoisKeys == nullptr) -+ { -+ throw gcnew ArgumentNullException("galoisKeys cannot be null"); -+ } -+ if (destination == nullptr) -+ { -+ throw gcnew ArgumentNullException("destination cannot be null"); -+ } -+ if (pool == nullptr) -+ { -+ throw gcnew ArgumentNullException("pool cannot be null"); -+ } -+ try -+ { -+ evaluator_->apply_galois(encrypted->GetCiphertext(), galoisElt, -+ galoisKeys->GetKeys(), destination->GetCiphertext(), pool->GetHandle()); -+ GC::KeepAlive(encrypted); -+ GC::KeepAlive(galoisKeys); -+ GC::KeepAlive(destination); -+ GC::KeepAlive(pool); -+ } -+ catch (const exception &e) -+ { -+ HandleException(&e); -+ } -+ catch (...) -+ { -+ HandleException(nullptr); -+ } -+ } -+ - void Evaluator::RotateRows(Ciphertext ^encrypted, int steps, GaloisKeys ^galoisKeys) - { - if (evaluator_ == nullptr) -diff --git a/SEALNET/sealnet/EvaluatorWrapper.h b/SEALNET/sealnet/EvaluatorWrapper.h -index 1f99af1..7a7ef05 100644 ---- a/SEALNET/sealnet/EvaluatorWrapper.h -+++ b/SEALNET/sealnet/EvaluatorWrapper.h -@@ -113,7 +113,7 @@ namespace Microsoft - by the given . - The SEALContext -- The MemoryPoolHandle pointing to a valid memory pool/param> -+ The MemoryPoolHandle pointing to a valid memory pool - if encryption parameters are not valid - if pool is uninitialized - if context or pool is null -@@ -256,7 +256,7 @@ namespace Microsoft - - The first ciphertext to multiply - The second ciphertext to multiply -- The MemoryPoolHandle pointing to a valid memory pool/param> -+ The MemoryPoolHandle pointing to a valid memory pool - if encrypted1 or encrypted2 is not valid - for the encryption parameters - if pool is uninitialized -@@ -296,7 +296,7 @@ namespace Microsoft - The first ciphertext to multiply - The second ciphertext to multiply - The ciphertext to overwrite with the multiplication result -- The MemoryPoolHandle pointing to a valid memory pool/param> -+ The MemoryPoolHandle pointing to a valid memory pool - if encrypted1 or encrypted2 is not valid - for the encryption parameters - if pool is uninitialized -@@ -330,7 +330,7 @@ namespace Microsoft - the memory pool pointed to by the given . - - The ciphertext to square -- The MemoryPoolHandle pointing to a valid memory pool/param> -+ The MemoryPoolHandle pointing to a valid memory pool - if encrypted is not valid for the - encryption parameters - if encrypted or pool is -@@ -365,7 +365,7 @@ namespace Microsoft - - The ciphertext to square - The ciphertext to overwrite with the square -- The MemoryPoolHandle pointing to a valid memory pool/param> -+ The MemoryPoolHandle pointing to a valid memory pool - if encrypted is not valid for the - encryption parameters - if pool is uninitialized -@@ -385,7 +385,7 @@ namespace Microsoft - - The ciphertext to relinearize - The evaluation keys -- The MemoryPoolHandle pointing to a valid memory pool/param> -+ The MemoryPoolHandle pointing to a valid memory pool - if encrypted or evaluationKeys is not - valid for the encryption parameters - if the size of evaluationKeys is too -@@ -450,7 +450,7 @@ namespace Microsoft - The ciphertext to relinearize - The evaluation keys - The ciphertext to overwrite with the relinearized result -- The MemoryPoolHandle pointing to a valid memory pool/param> -+ The MemoryPoolHandle pointing to a valid memory pool - if encrypted or evaluationKeys is not valid - for the encryption parameters - if the size of evaluationKeys is too -@@ -1024,6 +1024,121 @@ namespace Microsoft - */ - void MultiplyPlainNTT(Ciphertext ^encryptedNTT, Plaintext ^plainNTT); - -+ /** -+ Applies a Galois automorphism to a ciphertext. -+ -+ -+ Applies a Galois automorphism to a ciphertext. To evaluate the Galois automorphism, -+ an appropriate set of Galois keys must also be provided. Dynamic memory allocations -+ in the process are allocated from the memory pool pointed to by the local -+ . -+ -+ The ciphertext to apply the Galois automorphism to -+ The Galois element -+ The Galois keys -+ if encrypted or galoisKeys is not valid -+ for the encryption parameters -+ if encrypted has size greater than -+ two -+ if the Galois element is not -+ valid -+ if necessary Galois keys are not -+ present -+ if encrypted or galoisKeys is -+ null -+ */ -+ void ApplyGalois(Ciphertext ^encrypted, System::UInt64 galoisElt, -+ GaloisKeys ^galoisKeys); -+ -+ /** -+ Applies a Galois automorphism to a ciphertext. -+ -+ -+ Applies a Galois automorphism to a ciphertext. To evaluate the Galois automorphism, -+ an appropriate set of Galois keys must also be provided. Dynamic memory allocations -+ in the process are allocated from the memory pool pointed to by the given -+ . -+ -+ The ciphertext to apply the Galois automorphism to -+ The Galois element -+ The Galois keys -+ The -+ The MemoryPoolHandle pointing to a valid memory pool -+ if encrypted or galoisKeys is not valid -+ for the encryption parameters -+ if encrypted has size greater than -+ two -+ if the Galois element is not -+ valid -+ if necessary Galois keys are not -+ present -+ if pool is uninitialized -+ if encrypted, galoisKeys or pool -+ is null -+ */ -+ void ApplyGalois(Ciphertext ^encrypted, System::UInt64 galoisElt, -+ GaloisKeys ^galoisKeys, MemoryPoolHandle ^pool); -+ -+ /** -+ Applies a Galois automorphism to a ciphertext and writes the result -+ to the destination parameter. -+ -+ -+ Applies a Galois automorphism to a ciphertext and writes the result to the -+ destination parameter. To evaluate the Galois automorphism, an appropriate -+ set of Galois keys must also be provided. Dynamic memory allocations in the -+ process are allocated from the memory pool pointed to by the local -+ . -+ -+ The ciphertext to apply the Galois automorphism to -+ The Galois element -+ The Galois keys -+ The ciphertext to overwrite with the result -+ if encrypted or galoisKeys is not valid -+ for the encryption parameters -+ if encrypted has size greater than -+ two -+ if the Galois element is not -+ valid -+ if necessary Galois keys are not -+ present -+ if encrypted, galoisKeys, or -+ destination is null -+ */ -+ void ApplyGalois(Ciphertext ^encrypted, System::UInt64 galoisElt, -+ GaloisKeys ^galoisKeys, Ciphertext ^destination); -+ -+ /** -+ Applies a Galois automorphism to a ciphertext and writes the result -+ to the destination parameter. -+ -+ -+ Applies a Galois automorphism to a ciphertext and writes the result to the -+ destination parameter. To evaluate the Galois automorphism, an appropriate -+ set of Galois keys must also be provided. Dynamic memory allocations in the -+ process are allocated from the memory pool pointed to by the given -+ . -+ -+ The ciphertext to apply the Galois automorphism to -+ The Galois element -+ The Galois keys -+ The ciphertext to overwrite with the result -+ The MemoryPoolHandle pointing to a valid memory pool -+ if encrypted or galoisKeys is not valid -+ for the encryption parameters -+ if encrypted has size greater than -+ two -+ if the Galois element is not -+ valid -+ if necessary Galois keys are not -+ present -+ if pool is uninitialized -+ if encrypted, galoisKeys, -+ destination, or pool is null -+ */ -+ void ApplyGalois(Ciphertext ^encrypted, System::UInt64 galoisElt, -+ GaloisKeys ^galoisKeys, Ciphertext ^destination, MemoryPoolHandle ^pool); -+ - /** - Rotates plaintext matrix rows cyclically. - -@@ -1038,6 +1153,8 @@ namespace Microsoft - The ciphertext to rotate - The number of steps to rotate (negative left, positive right) - The Galois keys -+ if the encryption parameters do -+ not support batching - if encrypted or galoisKeys is not valid - for the encryption parameters - if encrypted has size greater than -@@ -1046,7 +1163,7 @@ namespace Microsoft - value - if necessary Galois keys are not - present -- if encrypted, galoisKeys or pool -+ if encrypted, galoisKeys, or pool - is null - */ - void RotateRows(Ciphertext ^encrypted, int steps, GaloisKeys ^galoisKeys); -@@ -1065,7 +1182,9 @@ namespace Microsoft - The ciphertext to rotate - The number of steps to rotate (negative left, positive right) - The Galois keys -- The MemoryPoolHandle pointing to a valid memory pool/param> -+ The MemoryPoolHandle pointing to a valid memory pool -+ if the encryption parameters do -+ not support batching - if encrypted or galoisKeys is not valid - for the encryption parameters - if encrypted has size greater than -@@ -1075,7 +1194,7 @@ namespace Microsoft - if necessary Galois keys are not - present - if pool is uninitialized -- if encrypted, galoisKeys or pool -+ if encrypted, galoisKeys, or pool - is null - */ - void RotateRows(Ciphertext ^encrypted, int steps, GaloisKeys ^galoisKeys, -@@ -1097,6 +1216,8 @@ namespace Microsoft - The number of steps to rotate (negative left, positive right) - The Galois keys - The ciphertext to overwrite with the rotated result -+ if the encryption parameters do -+ not support batching - if encrypted or galoisKeys is not valid - for the encryption parameters - if encrypted has size greater than -@@ -1127,7 +1248,9 @@ namespace Microsoft - The number of steps to rotate (negative left, positive right) - The Galois keys - The ciphertext to overwrite with the rotated result -- The MemoryPoolHandle pointing to a valid memory pool/param> -+ The MemoryPoolHandle pointing to a valid memory param> -+ if the encryption parameters do -+ not support batching - if encrypted or galoisKeys is not valid - for the encryption parameters - if encrypted has size greater than -@@ -1156,6 +1279,8 @@ namespace Microsoft - - The ciphertext to rotate - The Galois keys -+ if the encryption parameters do -+ not support batching - if encrypted or galoisKeys is not valid - for the encryption parameters - if encrypted has size greater than -@@ -1180,7 +1305,9 @@ namespace Microsoft - - The ciphertext to rotate - The Galois keys -- The MemoryPoolHandle pointing to a valid memory pool/param> -+ The MemoryPoolHandle pointing to a valid memory pool -+ if the encryption parameters do -+ not support batching - if encrypted or galoisKeys is not valid - for the encryption parameters - if encrypted has size greater than -@@ -1208,6 +1335,8 @@ namespace Microsoft - The ciphertext to rotate - The Galois keys - The ciphertext to overwrite with the rotated result -+ if the encryption parameters do -+ not support batching - if encrypted or galoisKeys is not valid - for the encryption parameters - if encrypted has size greater than -@@ -1234,7 +1363,9 @@ namespace Microsoft - The ciphertext to rotate - The Galois keys - The ciphertext to overwrite with the rotated result -- The MemoryPoolHandle pointing to a valid memory pool/param> -+ The MemoryPoolHandle pointing to a valid memory pool -+ if the encryption parameters do -+ not support batching - if encrypted or galoisKeys is not valid - for the encryption parameters - if necessary Galois keys are not -diff --git a/SEALNET/sealnet/KeyGeneratorWrapper.cpp b/SEALNET/sealnet/KeyGeneratorWrapper.cpp -index c9d52de..e1547f1 100644 ---- a/SEALNET/sealnet/KeyGeneratorWrapper.cpp -+++ b/SEALNET/sealnet/KeyGeneratorWrapper.cpp -@@ -1,10 +1,12 @@ - #include -+#include - #include "sealnet/KeyGeneratorWrapper.h" - #include "sealnet/BigPolyWrapper.h" - #include "sealnet/BigUIntWrapper.h" - #include "sealnet/Common.h" - - using namespace System; -+using namespace System::Collections::Generic; - using namespace std; - - namespace Microsoft -@@ -205,6 +207,37 @@ namespace Microsoft - } - } - -+ void KeyGenerator::GenerateGaloisKeys(int decompositionBitCount, List ^galoisElts, -+ GaloisKeys ^galoisKeys) -+ { -+ if (generator_ == nullptr) -+ { -+ throw gcnew ObjectDisposedException("KeyGenerator is disposed"); -+ } -+ if (galoisKeys == nullptr) -+ { -+ throw gcnew ArgumentNullException("galoisKeys cannot be null"); -+ } -+ try -+ { -+ std::vector v_galois_elts; -+ for (int i = 0; i < galoisElts->Count; i++) -+ { -+ v_galois_elts.push_back(galoisElts[i]); -+ } -+ generator_->generate_galois_keys(decompositionBitCount, v_galois_elts, galoisKeys->GetKeys()); -+ GC::KeepAlive(galoisElts); -+ } -+ catch (const exception &e) -+ { -+ HandleException(&e); -+ } -+ catch (...) -+ { -+ HandleException(nullptr); -+ } -+ } -+ - Microsoft::Research::SEAL::PublicKey ^KeyGenerator::PublicKey::get() - { - if (generator_ == nullptr) -diff --git a/SEALNET/sealnet/KeyGeneratorWrapper.h b/SEALNET/sealnet/KeyGeneratorWrapper.h -index 7fd722c..cf7f1fc 100644 ---- a/SEALNET/sealnet/KeyGeneratorWrapper.h -+++ b/SEALNET/sealnet/KeyGeneratorWrapper.h -@@ -158,6 +158,12 @@ namespace Microsoft - /** - Generates Galois keys. - -+ -+ Generates Galois keys. This function creates logarithmically many (in degree of the -+ polynomial modulus) Galois keys that is sufficient to apply any Galois automorphism -+ (e.g. rotations) on encrypted data. Most users will want to use this overload of -+ the function. -+ - The decomposition bit count - The Galois keys instance to overwrite with the generated - keys -@@ -167,6 +173,35 @@ namespace Microsoft - */ - void GenerateGaloisKeys(int decompositionBitCount, GaloisKeys ^galoisKeys); - -+ /** -+ Generates Galois keys. -+ -+ -+ Generates Galois keys. This function creates specific Galois keys that can be used to -+ apply specific Galois automorphisms on encrypted data. The user needs to give as -+ input a vector of Galois elements corresponding to the keys that are to be created. -+ -+ The Galois elements are odd integers in the interval [1, M-1], where M = 2*N, and -+ N = degree(PolyModulus). Used with batching, a Galois element 3^i % M corresponds -+ to a cyclic row rotation i steps to the left, and a Galois element 3^(N/2-i) % M -+ corresponds to a cyclic row rotation i steps to the right. The Galois element M-1 -+ corresponds to a column rotation (row swap). In the polynomial view (not batching), -+ a Galois automorphism by a Galois element p changes Enc(plain(x)) to Enc(plain(x^p)). -+ -+ The decomposition bit count -+ The Galois elements for which to generate keys -+ The Galois keys instance to overwrite with the generated -+ keys -+ if decompositionBitCount is not -+ within [0, 60] -+ if the Galois elements are not -+ valid -+ if galoisKeys is null -+ */ -+ void GenerateGaloisKeys(int decompositionBitCount, -+ System::Collections::Generic::List ^galoisElts, -+ GaloisKeys ^galoisKeys); -+ - /** - Destroys the KeyGenerator. - */ -diff --git a/SEALNETTest/EvaluatorWrapper.cs b/SEALNETTest/EvaluatorWrapper.cs -index 541786c..4321ecd 100644 ---- a/SEALNETTest/EvaluatorWrapper.cs -+++ b/SEALNETTest/EvaluatorWrapper.cs -@@ -982,6 +982,88 @@ namespace SEALNETTest - Assert.AreEqual(encrypted.HashBlock, parms.HashBlock); - } - -+ [TestMethod] -+ public void FVEncryptApplyGaloisDecryptNET() -+ { -+ var parms = new EncryptionParameters(); -+ var plain_modulus = new SmallModulus(257); -+ parms.NoiseStandardDeviation = 3.19; -+ parms.PlainModulus = plain_modulus; -+ parms.PolyModulus = "1x^8 + 1"; -+ parms.CoeffModulus = new List { -+ DefaultParams.SmallMods40Bit(0), DefaultParams.SmallMods40Bit(1) -+ }; -+ var context = new SEALContext(parms); -+ var keygen = new KeyGenerator(context); -+ var glk = new GaloisKeys(); -+ keygen.GenerateGaloisKeys(24, new List { 1, 3, 5, 15 }, glk); -+ -+ var encryptor = new Encryptor(context, keygen.PublicKey); -+ var evaluator = new Evaluator(context); -+ var decryptor = new Decryptor(context, keygen.SecretKey); -+ -+ var plain = new Plaintext("1"); -+ var encrypted = new Ciphertext(); -+ encryptor.Encrypt(plain, encrypted); -+ evaluator.ApplyGalois(encrypted, 1, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("1", plain.ToString()); -+ evaluator.ApplyGalois(encrypted, 3, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("1", plain.ToString()); -+ evaluator.ApplyGalois(encrypted, 5, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("1", plain.ToString()); -+ evaluator.ApplyGalois(encrypted, 15, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("1", plain.ToString()); -+ -+ plain.Set("1x^1"); -+ encryptor.Encrypt(plain, encrypted); -+ evaluator.ApplyGalois(encrypted, 1, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("1x^1", plain.ToString()); -+ evaluator.ApplyGalois(encrypted, 3, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("1x^3", plain.ToString()); -+ evaluator.ApplyGalois(encrypted, 5, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("100x^7", plain.ToString()); -+ evaluator.ApplyGalois(encrypted, 15, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("1x^1", plain.ToString()); -+ -+ plain.Set("1x^2"); -+ encryptor.Encrypt(plain, encrypted); -+ evaluator.ApplyGalois(encrypted, 1, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("1x^2", plain.ToString()); -+ evaluator.ApplyGalois(encrypted, 3, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("1x^6", plain.ToString()); -+ evaluator.ApplyGalois(encrypted, 5, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("100x^6", plain.ToString()); -+ evaluator.ApplyGalois(encrypted, 15, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("1x^2", plain.ToString()); -+ -+ plain.Set("1x^3 + 2x^2 + 1x^1 + 1"); -+ encryptor.Encrypt(plain, encrypted); -+ evaluator.ApplyGalois(encrypted, 1, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("1x^3 + 2x^2 + 1x^1 + 1", plain.ToString()); -+ evaluator.ApplyGalois(encrypted, 3, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("2x^6 + 1x^3 + 100x^1 + 1", plain.ToString()); -+ evaluator.ApplyGalois(encrypted, 5, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("100x^7 + FFx^6 + 100x^5 + 1", plain.ToString()); -+ evaluator.ApplyGalois(encrypted, 15, glk); -+ decryptor.Decrypt(encrypted, plain); -+ Assert.AreEqual("1x^3 + 2x^2 + 1x^1 + 1", plain.ToString()); -+ } -+ - [TestMethod] - public void FVEncryptRotateMatrixDecryptNET() - { -diff --git a/SEALNETTest/KeyGeneratorWrapper.cs b/SEALNETTest/KeyGeneratorWrapper.cs -index 04f9738..2a74a89 100644 ---- a/SEALNETTest/KeyGeneratorWrapper.cs -+++ b/SEALNETTest/KeyGeneratorWrapper.cs -@@ -1,6 +1,7 @@ - using Microsoft.VisualStudio.TestTools.UnitTesting; - using Microsoft.Research.SEAL; - using System.Collections.Generic; -+using System; - - namespace SEALNETTest - { -@@ -35,6 +36,79 @@ namespace SEALNETTest - keygen.GenerateEvaluationKeys(2, 2, evk); - Assert.AreEqual(evk.HashBlock, parms.HashBlock); - Assert.AreEqual(60, evk.Key(2)[0].Size); -+ -+ var galks = new GaloisKeys(); -+ keygen.GenerateGaloisKeys(60, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.AreEqual(2, galks.Key(3)[0].Size); -+ Assert.AreEqual(10, galks.Size); -+ -+ keygen.GenerateGaloisKeys(30, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.AreEqual(4, galks.Key(3)[0].Size); -+ Assert.AreEqual(10, galks.Size); -+ -+ keygen.GenerateGaloisKeys(2, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.AreEqual(60, galks.Key(3)[0].Size); -+ Assert.AreEqual(10, galks.Size); -+ -+ keygen.GenerateGaloisKeys(60, new List { 1, 3, 5, 7 }, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.IsTrue(galks.HasKey(1)); -+ Assert.IsTrue(galks.HasKey(3)); -+ Assert.IsTrue(galks.HasKey(5)); -+ Assert.IsTrue(galks.HasKey(7)); -+ Assert.IsFalse(galks.HasKey(9)); -+ Assert.IsFalse(galks.HasKey(127)); -+ Assert.AreEqual(2, galks.Key(1)[0].Size); -+ Assert.AreEqual(2, galks.Key(3)[0].Size); -+ Assert.AreEqual(2, galks.Key(5)[0].Size); -+ Assert.AreEqual(2, galks.Key(7)[0].Size); -+ Assert.AreEqual(4, galks.Size); -+ -+ keygen.GenerateGaloisKeys(30, new List { 1, 3, 5, 7 }, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.IsTrue(galks.HasKey(1)); -+ Assert.IsTrue(galks.HasKey(3)); -+ Assert.IsTrue(galks.HasKey(5)); -+ Assert.IsTrue(galks.HasKey(7)); -+ Assert.IsFalse(galks.HasKey(9)); -+ Assert.IsFalse(galks.HasKey(127)); -+ Assert.AreEqual(4, galks.Key(1)[0].Size); -+ Assert.AreEqual(4, galks.Key(3)[0].Size); -+ Assert.AreEqual(4, galks.Key(5)[0].Size); -+ Assert.AreEqual(4, galks.Key(7)[0].Size); -+ Assert.AreEqual(4, galks.Size); -+ -+ keygen.GenerateGaloisKeys(2, new List { 1, 3, 5, 7 }, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.IsTrue(galks.HasKey(1)); -+ Assert.IsTrue(galks.HasKey(3)); -+ Assert.IsTrue(galks.HasKey(5)); -+ Assert.IsTrue(galks.HasKey(7)); -+ Assert.IsFalse(galks.HasKey(9)); -+ Assert.IsFalse(galks.HasKey(127)); -+ Assert.AreEqual(60, galks.Key(1)[0].Size); -+ Assert.AreEqual(60, galks.Key(3)[0].Size); -+ Assert.AreEqual(60, galks.Key(5)[0].Size); -+ Assert.AreEqual(60, galks.Key(7)[0].Size); -+ Assert.AreEqual(4, galks.Size); -+ -+ keygen.GenerateGaloisKeys(30, new List { 1 }, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.IsTrue(galks.HasKey(1)); -+ Assert.IsFalse(galks.HasKey(3)); -+ Assert.IsFalse(galks.HasKey(127)); -+ Assert.AreEqual(4, galks.Key(1)[0].Size); -+ Assert.AreEqual(1, galks.Size); -+ -+ keygen.GenerateGaloisKeys(30, new List { 127 }, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.IsFalse(galks.HasKey(1)); -+ Assert.IsTrue(galks.HasKey(127)); -+ Assert.AreEqual(4, galks.Key(127)[0].Size); -+ Assert.AreEqual(1, galks.Size); - } - { - parms.NoiseStandardDeviation = 3.19; -@@ -61,6 +135,79 @@ namespace SEALNETTest - keygen.GenerateEvaluationKeys(4, 1, evk); - Assert.AreEqual(evk.HashBlock, parms.HashBlock); - Assert.AreEqual(30, evk.Key(2)[0].Size); -+ -+ var galks = new GaloisKeys(); -+ keygen.GenerateGaloisKeys(60, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.AreEqual(2, galks.Key(3)[0].Size); -+ Assert.AreEqual(14, galks.Size); -+ -+ keygen.GenerateGaloisKeys(30, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.AreEqual(4, galks.Key(3)[0].Size); -+ Assert.AreEqual(14, galks.Size); -+ -+ keygen.GenerateGaloisKeys(2, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.AreEqual(60, galks.Key(3)[0].Size); -+ Assert.AreEqual(14, galks.Size); -+ -+ keygen.GenerateGaloisKeys(60, new List { 1, 3, 5, 7 }, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.IsTrue(galks.HasKey(1)); -+ Assert.IsTrue(galks.HasKey(3)); -+ Assert.IsTrue(galks.HasKey(5)); -+ Assert.IsTrue(galks.HasKey(7)); -+ Assert.IsFalse(galks.HasKey(9)); -+ Assert.IsFalse(galks.HasKey(511)); -+ Assert.AreEqual(2, galks.Key(1)[0].Size); -+ Assert.AreEqual(2, galks.Key(3)[0].Size); -+ Assert.AreEqual(2, galks.Key(5)[0].Size); -+ Assert.AreEqual(2, galks.Key(7)[0].Size); -+ Assert.AreEqual(4, galks.Size); -+ -+ keygen.GenerateGaloisKeys(30, new List { 1, 3, 5, 7 }, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.IsTrue(galks.HasKey(1)); -+ Assert.IsTrue(galks.HasKey(3)); -+ Assert.IsTrue(galks.HasKey(5)); -+ Assert.IsTrue(galks.HasKey(7)); -+ Assert.IsFalse(galks.HasKey(9)); -+ Assert.IsFalse(galks.HasKey(511)); -+ Assert.AreEqual(4, galks.Key(1)[0].Size); -+ Assert.AreEqual(4, galks.Key(3)[0].Size); -+ Assert.AreEqual(4, galks.Key(5)[0].Size); -+ Assert.AreEqual(4, galks.Key(7)[0].Size); -+ Assert.AreEqual(4, galks.Size); -+ -+ keygen.GenerateGaloisKeys(2, new List { 1, 3, 5, 7 }, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.IsTrue(galks.HasKey(1)); -+ Assert.IsTrue(galks.HasKey(3)); -+ Assert.IsTrue(galks.HasKey(5)); -+ Assert.IsTrue(galks.HasKey(7)); -+ Assert.IsFalse(galks.HasKey(9)); -+ Assert.IsFalse(galks.HasKey(511)); -+ Assert.AreEqual(60, galks.Key(1)[0].Size); -+ Assert.AreEqual(60, galks.Key(3)[0].Size); -+ Assert.AreEqual(60, galks.Key(5)[0].Size); -+ Assert.AreEqual(60, galks.Key(7)[0].Size); -+ Assert.AreEqual(4, galks.Size); -+ -+ keygen.GenerateGaloisKeys(30, new List { 1 }, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.IsTrue(galks.HasKey(1)); -+ Assert.IsFalse(galks.HasKey(3)); -+ Assert.IsFalse(galks.HasKey(511)); -+ Assert.AreEqual(4, galks.Key(1)[0].Size); -+ Assert.AreEqual(1, galks.Size); -+ -+ keygen.GenerateGaloisKeys(30, new List { 511 }, galks); -+ Assert.AreEqual(galks.HashBlock, parms.HashBlock); -+ Assert.IsFalse(galks.HasKey(1)); -+ Assert.IsTrue(galks.HasKey(511)); -+ Assert.AreEqual(4, galks.Key(511)[0].Size); -+ Assert.AreEqual(1, galks.Size); - } - } - } -diff --git a/SEALTest/evaluator.cpp b/SEALTest/evaluator.cpp -index 51e083a..edde078 100644 ---- a/SEALTest/evaluator.cpp -+++ b/SEALTest/evaluator.cpp -@@ -964,6 +964,85 @@ namespace SEALTest - Assert::IsTrue(encrypted.hash_block() == parms.hash_block()); - } - -+ TEST_METHOD(FVEncryptApplyGaloisDecrypt) -+ { -+ EncryptionParameters parms; -+ SmallModulus plain_modulus(257); -+ BigPoly poly_modulus("1x^8 + 1"); -+ parms.set_poly_modulus(poly_modulus); -+ parms.set_plain_modulus(plain_modulus); -+ parms.set_coeff_modulus({ small_mods_40bit(0), small_mods_40bit(1) }); -+ SEALContext context(parms); -+ KeyGenerator keygen(context); -+ GaloisKeys glk; -+ keygen.generate_galois_keys(24, { 1, 3, 5, 15 }, glk); -+ -+ Encryptor encryptor(context, keygen.public_key()); -+ Evaluator evaluator(context); -+ Decryptor decryptor(context, keygen.secret_key()); -+ -+ Plaintext plain("1"); -+ Ciphertext encrypted; -+ encryptor.encrypt(plain, encrypted); -+ evaluator.apply_galois(encrypted, 1, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("1" == plain.to_string()); -+ evaluator.apply_galois(encrypted, 3, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("1" == plain.to_string()); -+ evaluator.apply_galois(encrypted, 5, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("1" == plain.to_string()); -+ evaluator.apply_galois(encrypted, 15, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("1" == plain.to_string()); -+ -+ plain = "1x^1"; -+ encryptor.encrypt(plain, encrypted); -+ evaluator.apply_galois(encrypted, 1, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("1x^1" == plain.to_string()); -+ evaluator.apply_galois(encrypted, 3, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("1x^3" == plain.to_string()); -+ evaluator.apply_galois(encrypted, 5, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("100x^7" == plain.to_string()); -+ evaluator.apply_galois(encrypted, 15, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("1x^1" == plain.to_string()); -+ -+ plain = "1x^2"; -+ encryptor.encrypt(plain, encrypted); -+ evaluator.apply_galois(encrypted, 1, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("1x^2" == plain.to_string()); -+ evaluator.apply_galois(encrypted, 3, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("1x^6" == plain.to_string()); -+ evaluator.apply_galois(encrypted, 5, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("100x^6" == plain.to_string()); -+ evaluator.apply_galois(encrypted, 15, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("1x^2" == plain.to_string()); -+ -+ plain = "1x^3 + 2x^2 + 1x^1 + 1"; -+ encryptor.encrypt(plain, encrypted); -+ evaluator.apply_galois(encrypted, 1, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("1x^3 + 2x^2 + 1x^1 + 1" == plain.to_string()); -+ evaluator.apply_galois(encrypted, 3, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("2x^6 + 1x^3 + 100x^1 + 1" == plain.to_string()); -+ evaluator.apply_galois(encrypted, 5, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("100x^7 + FFx^6 + 100x^5 + 1" == plain.to_string()); -+ evaluator.apply_galois(encrypted, 15, glk); -+ decryptor.decrypt(encrypted, plain); -+ Assert::IsTrue("1x^3 + 2x^2 + 1x^1 + 1" == plain.to_string()); -+ } -+ - TEST_METHOD(FVEncryptRotateMatrixDecrypt) - { - EncryptionParameters parms; -diff --git a/SEALTest/keygenerator.cpp b/SEALTest/keygenerator.cpp -index b4e15b3..a64b1af 100644 ---- a/SEALTest/keygenerator.cpp -+++ b/SEALTest/keygenerator.cpp -@@ -68,6 +68,79 @@ namespace SEALTest - } - } - } -+ -+ GaloisKeys galks; -+ keygen.generate_galois_keys(60, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::AreEqual(2, galks.key(3)[0].size()); -+ Assert::AreEqual(10, galks.size()); -+ -+ keygen.generate_galois_keys(30, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::AreEqual(4, galks.key(3)[0].size()); -+ Assert::AreEqual(10, galks.size()); -+ -+ keygen.generate_galois_keys(2, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::AreEqual(60, galks.key(3)[0].size()); -+ Assert::AreEqual(10, galks.size()); -+ -+ keygen.generate_galois_keys(60, { 1, 3, 5, 7 }, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::IsTrue(galks.has_key(1)); -+ Assert::IsTrue(galks.has_key(3)); -+ Assert::IsTrue(galks.has_key(5)); -+ Assert::IsTrue(galks.has_key(7)); -+ Assert::IsFalse(galks.has_key(9)); -+ Assert::IsFalse(galks.has_key(127)); -+ Assert::AreEqual(2, galks.key(1)[0].size()); -+ Assert::AreEqual(2, galks.key(3)[0].size()); -+ Assert::AreEqual(2, galks.key(5)[0].size()); -+ Assert::AreEqual(2, galks.key(7)[0].size()); -+ Assert::AreEqual(4, galks.size()); -+ -+ keygen.generate_galois_keys(30, { 1, 3, 5, 7 }, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::IsTrue(galks.has_key(1)); -+ Assert::IsTrue(galks.has_key(3)); -+ Assert::IsTrue(galks.has_key(5)); -+ Assert::IsTrue(galks.has_key(7)); -+ Assert::IsFalse(galks.has_key(9)); -+ Assert::IsFalse(galks.has_key(127)); -+ Assert::AreEqual(4, galks.key(1)[0].size()); -+ Assert::AreEqual(4, galks.key(3)[0].size()); -+ Assert::AreEqual(4, galks.key(5)[0].size()); -+ Assert::AreEqual(4, galks.key(7)[0].size()); -+ Assert::AreEqual(4, galks.size()); -+ -+ keygen.generate_galois_keys(2, { 1, 3, 5, 7 }, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::IsTrue(galks.has_key(1)); -+ Assert::IsTrue(galks.has_key(3)); -+ Assert::IsTrue(galks.has_key(5)); -+ Assert::IsTrue(galks.has_key(7)); -+ Assert::IsFalse(galks.has_key(9)); -+ Assert::IsFalse(galks.has_key(127)); -+ Assert::AreEqual(60, galks.key(1)[0].size()); -+ Assert::AreEqual(60, galks.key(3)[0].size()); -+ Assert::AreEqual(60, galks.key(5)[0].size()); -+ Assert::AreEqual(60, galks.key(7)[0].size()); -+ Assert::AreEqual(4, galks.size()); -+ -+ keygen.generate_galois_keys(30, { 1 }, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::IsTrue(galks.has_key(1)); -+ Assert::IsFalse(galks.has_key(3)); -+ Assert::IsFalse(galks.has_key(127)); -+ Assert::AreEqual(4, galks.key(1)[0].size()); -+ Assert::AreEqual(1, galks.size()); -+ -+ keygen.generate_galois_keys(30, { 127 }, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::IsFalse(galks.has_key(1)); -+ Assert::IsTrue(galks.has_key(127)); -+ Assert::AreEqual(4, galks.key(127)[0].size()); -+ Assert::AreEqual(1, galks.size()); - } - { - parms.set_noise_standard_deviation(3.19); -@@ -121,6 +194,79 @@ namespace SEALTest - } - } - } -+ -+ GaloisKeys galks; -+ keygen.generate_galois_keys(60, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::AreEqual(2, galks.key(3)[0].size()); -+ Assert::AreEqual(14, galks.size()); -+ -+ keygen.generate_galois_keys(30, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::AreEqual(4, galks.key(3)[0].size()); -+ Assert::AreEqual(14, galks.size()); -+ -+ keygen.generate_galois_keys(2, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::AreEqual(60, galks.key(3)[0].size()); -+ Assert::AreEqual(14, galks.size()); -+ -+ keygen.generate_galois_keys(60, { 1, 3, 5, 7 }, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::IsTrue(galks.has_key(1)); -+ Assert::IsTrue(galks.has_key(3)); -+ Assert::IsTrue(galks.has_key(5)); -+ Assert::IsTrue(galks.has_key(7)); -+ Assert::IsFalse(galks.has_key(9)); -+ Assert::IsFalse(galks.has_key(511)); -+ Assert::AreEqual(2, galks.key(1)[0].size()); -+ Assert::AreEqual(2, galks.key(3)[0].size()); -+ Assert::AreEqual(2, galks.key(5)[0].size()); -+ Assert::AreEqual(2, galks.key(7)[0].size()); -+ Assert::AreEqual(4, galks.size()); -+ -+ keygen.generate_galois_keys(30, { 1, 3, 5, 7 }, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::IsTrue(galks.has_key(1)); -+ Assert::IsTrue(galks.has_key(3)); -+ Assert::IsTrue(galks.has_key(5)); -+ Assert::IsTrue(galks.has_key(7)); -+ Assert::IsFalse(galks.has_key(9)); -+ Assert::IsFalse(galks.has_key(511)); -+ Assert::AreEqual(4, galks.key(1)[0].size()); -+ Assert::AreEqual(4, galks.key(3)[0].size()); -+ Assert::AreEqual(4, galks.key(5)[0].size()); -+ Assert::AreEqual(4, galks.key(7)[0].size()); -+ Assert::AreEqual(4, galks.size()); -+ -+ keygen.generate_galois_keys(2, { 1, 3, 5, 7 }, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::IsTrue(galks.has_key(1)); -+ Assert::IsTrue(galks.has_key(3)); -+ Assert::IsTrue(galks.has_key(5)); -+ Assert::IsTrue(galks.has_key(7)); -+ Assert::IsFalse(galks.has_key(9)); -+ Assert::IsFalse(galks.has_key(511)); -+ Assert::AreEqual(60, galks.key(1)[0].size()); -+ Assert::AreEqual(60, galks.key(3)[0].size()); -+ Assert::AreEqual(60, galks.key(5)[0].size()); -+ Assert::AreEqual(60, galks.key(7)[0].size()); -+ Assert::AreEqual(4, galks.size()); -+ -+ keygen.generate_galois_keys(30, { 1 }, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::IsTrue(galks.has_key(1)); -+ Assert::IsFalse(galks.has_key(3)); -+ Assert::IsFalse(galks.has_key(511)); -+ Assert::AreEqual(4, galks.key(1)[0].size()); -+ Assert::AreEqual(1, galks.size()); -+ -+ keygen.generate_galois_keys(30, { 511 }, galks); -+ Assert::IsTrue(galks.hash_block() == parms.hash_block()); -+ Assert::IsFalse(galks.has_key(1)); -+ Assert::IsTrue(galks.has_key(511)); -+ Assert::AreEqual(4, galks.key(511)[0].size()); -+ Assert::AreEqual(1, galks.size()); - } - } - }; --- -2.14.1 - - -From 1fdfa03edbb50835beedefe26d74f17918c4f1e1 Mon Sep 17 00:00:00 2001 -From: Kim Laine -Date: Mon, 4 Dec 2017 17:31:18 -0800 -Subject: [PATCH 2/3] Added negacyclic_shift_poly_coeffmod - ---- - SEAL/seal/util/polyarithsmallmod.cpp | 28 ++++++--- - SEAL/seal/util/polyarithsmallmod.h | 54 +++++++++++++++++- - SEALTest/util/polyarithsmallmod.cpp | 108 +++++++++++++++++++++++++++++++++++ - 3 files changed, 181 insertions(+), 9 deletions(-) - -diff --git a/SEAL/seal/util/polyarithsmallmod.cpp b/SEAL/seal/util/polyarithsmallmod.cpp -index 5bfeede..4719348 100644 ---- a/SEAL/seal/util/polyarithsmallmod.cpp -+++ b/SEAL/seal/util/polyarithsmallmod.cpp -@@ -407,16 +407,30 @@ namespace seal - } - } - -- uint64_t poly_infty_norm_coeffmod(const std::uint64_t *poly, int poly_coeff_count, const SmallModulus &modulus) -+ uint64_t poly_infty_norm_coeffmod(const std::uint64_t *operand, int coeff_count, const SmallModulus &modulus) - { -+#ifdef SEAL_DEBUG -+ if (operand == nullptr && coeff_count > 0) -+ { -+ throw invalid_argument("operand"); -+ } -+ if (coeff_count < 0) -+ { -+ throw invalid_argument("coeff_count"); -+ } -+ if (modulus.is_zero()) -+ { -+ throw invalid_argument("modulus"); -+ } -+#endif - // Construct negative threshold (first negative modulus value) to compute absolute values of coeffs. - uint64_t modulus_neg_threshold = (modulus.value() + 1) >> 1; - - // Mod out the poly coefficients and choose a symmetric representative from [-modulus,modulus). Keep track of the max. - uint64_t result = 0; -- for (int coeff_index = 0; coeff_index < poly_coeff_count; coeff_index++) -+ for (int coeff_index = 0; coeff_index < coeff_count; coeff_index++) - { -- uint64_t poly_coeff = poly[coeff_index] % modulus.value(); -+ uint64_t poly_coeff = operand[coeff_index] % modulus.value(); - if (poly_coeff >= modulus_neg_threshold) - { - poly_coeff = modulus.value() - poly_coeff; -@@ -594,14 +608,14 @@ namespace seal - return true; - } - -- void exponentiate_poly_polymod_coeffmod(const uint64_t *poly, const uint64_t *exponent, int exponent_uint64_count, const PolyModulus &poly_modulus, const SmallModulus &modulus, uint64_t *result, MemoryPool &pool) -+ void exponentiate_poly_polymod_coeffmod(const uint64_t *operand, const uint64_t *exponent, int exponent_uint64_count, const PolyModulus &poly_modulus, const SmallModulus &modulus, uint64_t *result, MemoryPool &pool) - { - int poly_modulus_coeff_count = poly_modulus.coeff_count(); - #ifdef SEAL_DEBUG - int poly_modulus_coeff_uint64_count = poly_modulus.coeff_uint64_count(); -- if (poly == nullptr) -+ if (operand == nullptr) - { -- throw invalid_argument("poly"); -+ throw invalid_argument("operand"); - } - if (exponent == nullptr) - { -@@ -631,7 +645,7 @@ namespace seal - return; - } - -- modulo_poly(poly, poly_modulus_coeff_count, poly_modulus, modulus, result, pool); -+ modulo_poly(operand, poly_modulus_coeff_count, poly_modulus, modulus, result, pool); - - if (is_equal_uint(exponent, exponent_uint64_count, 1)) - { -diff --git a/SEAL/seal/util/polyarithsmallmod.h b/SEAL/seal/util/polyarithsmallmod.h -index d660439..f081184 100644 ---- a/SEAL/seal/util/polyarithsmallmod.h -+++ b/SEAL/seal/util/polyarithsmallmod.h -@@ -556,14 +556,64 @@ namespace seal - modulo_poly_inplace(result, result_coeff_count, poly_modulus, modulus); - } - -- std::uint64_t poly_infty_norm_coeffmod(const std::uint64_t *poly, int poly_coeff_count, -+ std::uint64_t poly_infty_norm_coeffmod(const std::uint64_t *operand, int coeff_count, - const SmallModulus &modulus); - - bool try_invert_poly_coeffmod(const std::uint64_t *operand, const std::uint64_t *poly_modulus, - int coeff_count, const SmallModulus &modulus, std::uint64_t *result, MemoryPool &pool); - -- void exponentiate_poly_polymod_coeffmod(const std::uint64_t *poly, const std::uint64_t *exponent, -+ void exponentiate_poly_polymod_coeffmod(const std::uint64_t *operand, const std::uint64_t *exponent, - int exponent_uint64_count, const PolyModulus &poly_modulus, const SmallModulus &modulus, - std::uint64_t *result, MemoryPool &pool); -+ -+ inline void negacyclic_shift_poly_coeffmod(const std::uint64_t *operand, int coeff_count, int shift, -+ const SmallModulus &modulus, std::uint64_t *result) -+ { -+#ifdef SEAL_DEBUG -+ if (operand == nullptr && coeff_count > 0) -+ { -+ throw std::invalid_argument("operand"); -+ } -+ if (result == nullptr && coeff_count > 0) -+ { -+ throw std::invalid_argument("result"); -+ } -+ if (operand == result && coeff_count > 0) -+ { -+ throw std::invalid_argument("operand cannot point to the same location as result"); -+ } -+ if (coeff_count < 0) -+ { -+ throw std::invalid_argument("coeff_count"); -+ } -+ if (modulus.is_zero()) -+ { -+ throw std::invalid_argument("modulus"); -+ } -+ if (shift < 0) -+ { -+ throw std::invalid_argument("shift"); -+ } -+ if (util::get_power_of_two(static_cast(coeff_count)) < 0) -+ { -+ throw std::invalid_argument("coeff_count"); -+ } -+#endif -+ std::uint64_t index_raw = shift; -+ std::uint64_t coeff_count_mod_mask = static_cast(coeff_count) - 1; -+ std::uint64_t index; -+ for (int i = 0; i < coeff_count; i++, operand++, index_raw++) -+ { -+ index = index_raw & coeff_count_mod_mask; -+ if (!(index_raw & static_cast(coeff_count)) || (*operand == 0)) -+ { -+ result[index] = *operand; -+ } -+ else -+ { -+ result[index] = modulus.value() - *operand; -+ } -+ } -+ } - } - } -diff --git a/SEALTest/util/polyarithsmallmod.cpp b/SEALTest/util/polyarithsmallmod.cpp -index e917034..93df00b 100644 ---- a/SEALTest/util/polyarithsmallmod.cpp -+++ b/SEALTest/util/polyarithsmallmod.cpp -@@ -470,6 +470,114 @@ namespace SEALTest - Assert::AreEqual(9ULL, result[1]); - Assert::AreEqual(0ULL, result[2]); - } -+ -+ TEST_METHOD(NegacyclicShiftPolyCoeffSmallMod) -+ { -+ MemoryPool &pool = *global_variables::global_memory_pool; -+ Pointer poly(allocate_zero_poly(4, 1, pool)); -+ Pointer result(allocate_zero_poly(4, 1, pool)); -+ -+ SmallModulus mod(10); -+ int coeff_count = 4; -+ -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 0, mod, result.get()); -+ Assert::AreEqual(0ULL, result[0]); -+ Assert::AreEqual(0ULL, result[1]); -+ Assert::AreEqual(0ULL, result[2]); -+ Assert::AreEqual(0ULL, result[3]); -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 1, mod, result.get()); -+ Assert::AreEqual(0ULL, result[0]); -+ Assert::AreEqual(0ULL, result[1]); -+ Assert::AreEqual(0ULL, result[2]); -+ Assert::AreEqual(0ULL, result[3]); -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 4, mod, result.get()); -+ Assert::AreEqual(0ULL, result[0]); -+ Assert::AreEqual(0ULL, result[1]); -+ Assert::AreEqual(0ULL, result[2]); -+ Assert::AreEqual(0ULL, result[3]); -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 5, mod, result.get()); -+ Assert::AreEqual(0ULL, result[0]); -+ Assert::AreEqual(0ULL, result[1]); -+ Assert::AreEqual(0ULL, result[2]); -+ Assert::AreEqual(0ULL, result[3]); -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 8, mod, result.get()); -+ Assert::AreEqual(0ULL, result[0]); -+ Assert::AreEqual(0ULL, result[1]); -+ Assert::AreEqual(0ULL, result[2]); -+ Assert::AreEqual(0ULL, result[3]); -+ -+ poly[0] = 1; -+ poly[1] = 2; -+ poly[2] = 3; -+ poly[3] = 4; -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 0, mod, result.get()); -+ Assert::AreEqual(1ULL, result[0]); -+ Assert::AreEqual(2ULL, result[1]); -+ Assert::AreEqual(3ULL, result[2]); -+ Assert::AreEqual(4ULL, result[3]); -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 1, mod, result.get()); -+ Assert::AreEqual(6ULL, result[0]); -+ Assert::AreEqual(1ULL, result[1]); -+ Assert::AreEqual(2ULL, result[2]); -+ Assert::AreEqual(3ULL, result[3]); -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 4, mod, result.get()); -+ Assert::AreEqual(9ULL, result[0]); -+ Assert::AreEqual(8ULL, result[1]); -+ Assert::AreEqual(7ULL, result[2]); -+ Assert::AreEqual(6ULL, result[3]); -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 5, mod, result.get()); -+ Assert::AreEqual(4ULL, result[0]); -+ Assert::AreEqual(9ULL, result[1]); -+ Assert::AreEqual(8ULL, result[2]); -+ Assert::AreEqual(7ULL, result[3]); -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 8, mod, result.get()); -+ Assert::AreEqual(1ULL, result[0]); -+ Assert::AreEqual(2ULL, result[1]); -+ Assert::AreEqual(3ULL, result[2]); -+ Assert::AreEqual(4ULL, result[3]); -+ -+ poly[0] = 1; -+ poly[1] = 2; -+ poly[2] = 0; -+ poly[3] = 4; -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 0, mod, result.get()); -+ Assert::AreEqual(1ULL, result[0]); -+ Assert::AreEqual(2ULL, result[1]); -+ Assert::AreEqual(0ULL, result[2]); -+ Assert::AreEqual(4ULL, result[3]); -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 1, mod, result.get()); -+ Assert::AreEqual(6ULL, result[0]); -+ Assert::AreEqual(1ULL, result[1]); -+ Assert::AreEqual(2ULL, result[2]); -+ Assert::AreEqual(0ULL, result[3]); -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 4, mod, result.get()); -+ Assert::AreEqual(9ULL, result[0]); -+ Assert::AreEqual(8ULL, result[1]); -+ Assert::AreEqual(0ULL, result[2]); -+ Assert::AreEqual(6ULL, result[3]); -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 5, mod, result.get()); -+ Assert::AreEqual(4ULL, result[0]); -+ Assert::AreEqual(9ULL, result[1]); -+ Assert::AreEqual(8ULL, result[2]); -+ Assert::AreEqual(0ULL, result[3]); -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 8, mod, result.get()); -+ Assert::AreEqual(1ULL, result[0]); -+ Assert::AreEqual(2ULL, result[1]); -+ Assert::AreEqual(0ULL, result[2]); -+ Assert::AreEqual(4ULL, result[3]); -+ -+ poly[0] = 1; -+ poly[1] = 2; -+ poly[2] = 3; -+ poly[3] = 4; -+ coeff_count = 2; -+ negacyclic_shift_poly_coeffmod(poly.get(), coeff_count, 1, mod, result.get()); -+ negacyclic_shift_poly_coeffmod(poly.get() + 2, coeff_count, 1, mod, result.get() + 2); -+ Assert::AreEqual(8ULL, result[0]); -+ Assert::AreEqual(1ULL, result[1]); -+ Assert::AreEqual(6ULL, result[2]); -+ Assert::AreEqual(3ULL, result[3]); -+ } - }; - } - } -\ No newline at end of file --- -2.14.1 - - -From 9c5a16fb3e8ffc5bae69ba175867d89b774091c5 Mon Sep 17 00:00:00 2001 -From: Sebastian Angel -Date: Tue, 15 May 2018 00:11:24 +0000 -Subject: [PATCH 3/3] enable mutable - ---- - SEAL/seal/util/defines.h | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/SEAL/seal/util/defines.h b/SEAL/seal/util/defines.h -index 20c0bf2..4e1b2fb 100644 ---- a/SEAL/seal/util/defines.h -+++ b/SEAL/seal/util/defines.h -@@ -19,7 +19,7 @@ - // parameter compatibility checks pass in cases where they normally - // should not pass. Please note that it is extremely easy to break - // things by doing this, and the consequences can be unexpected. --//#define SEAL_EXPOSE_MUTABLE_HASH_BLOCK -+#define SEAL_EXPOSE_MUTABLE_HASH_BLOCK - - // Allow ciphertext data to be directly modified by exposing the - // functions seal::Ciphertext::mutable_pointer(int) and -@@ -28,7 +28,7 @@ - // way of mutating ciphertext data is by allocating memory manually, - // and using aliased ciphertexts pointing to the allocated memory, - // which can then be mutated freely. --//#define SEAL_EXPOSE_MUTABLE_CIPHERTEXT -+#define SEAL_EXPOSE_MUTABLE_CIPHERTEXT - - // For security reasons one should never throw when decoding fails due - // to overflow, but in some cases this might help in diagnosing problems. --- -2.14.1 - diff --git a/pir.cpp b/pir.cpp index 509af36..f5577bf 100644 --- a/pir.cpp +++ b/pir.cpp @@ -222,7 +222,7 @@ void coeffs_to_bytes(uint32_t limit, const Plaintext &coeffs, uint8_t *output, u void vector_to_plaintext(const vector &coeffs, Plaintext &plain) { uint32_t coeff_count = coeffs.size(); plain.resize(coeff_count); - util::set_uint_uint(coeffs.data(), coeff_count, plain.pointer()); + util::set_uint_uint(coeffs.data(), coeff_count, plain.data()); } vector compute_indices(uint64_t desiredIndex, vector Nvec) { diff --git a/pir_client.cpp b/pir_client.cpp index 08bae54..27a4f69 100644 --- a/pir_client.cpp +++ b/pir_client.cpp @@ -19,7 +19,7 @@ PIRClient::PIRClient(const EncryptionParameters ¶ms, encryptor_.reset(new Encryptor(context, keygen_->public_key())); SecretKey secret_key = keygen_->secret_key(); - secret_key.mutable_hash_block() = expanded_params.hash_block(); + secret_key.hash_block() = expanded_params.hash_block(); decryptor_.reset(new Decryptor(newcontext, secret_key)); evaluator_.reset(new Evaluator(newcontext)); @@ -37,7 +37,7 @@ void PIRClient::update_parameters(const EncryptionParameters &expanded_params, SEALContext newcontext(expanded_params); SecretKey secret_key = keygen_->secret_key(); - secret_key.mutable_hash_block() = expanded_params.hash_block(); + secret_key.hash_block() = expanded_params.hash_block(); decryptor_.reset(new Decryptor(newcontext, secret_key)); evaluator_.reset(new Evaluator(newcontext)); @@ -51,7 +51,7 @@ PirQuery PIRClient::generate_query(uint64_t desiredIndex) { for (uint32_t i = 0; i < indices.size(); i++) { Ciphertext dest; encryptor_->encrypt(Plaintext("1x^" + std::to_string(indices[i])), dest); - dest.mutable_hash_block() = expanded_params_.hash_block(); + dest.hash_block() = expanded_params_.hash_block(); result.push_back(dest); } @@ -146,7 +146,7 @@ Ciphertext PIRClient::compose_to_ciphertext(vector plains) { // A triple for loop. Going over polys, moduli, and decomposed index. for (int i = 0; i < encrypted_count; i++) { - uint64_t *encrypted_pointer = result.mutable_pointer(i); + uint64_t *encrypted_pointer = result.data(i); for (int j = 0; j < coeff_mod_count; j++) { // populate one poly at a time. @@ -164,7 +164,7 @@ Ciphertext PIRClient::compose_to_ciphertext(vector<Plaintext> plains) { // Compose here const uint64_t *plain_coeff = plains[k + j * (expansion_ratio) + i * (coeff_mod_count * expansion_ratio)] - .pointer(); + .data(); for (int m = 0; m < coeff_count - 1; m++) { if (k == 0) { @@ -188,6 +188,6 @@ Ciphertext PIRClient::compose_to_ciphertext(vector<Plaintext> plains) { } } - result.mutable_hash_block() = expanded_params_.hash_block(); + result.hash_block() = expanded_params_.hash_block(); return result; } diff --git a/pir_server.cpp b/pir_server.cpp index 46cf08d..180d860 100644 --- a/pir_server.cpp +++ b/pir_server.cpp @@ -31,7 +31,7 @@ void PIRServer::update_parameters(const EncryptionParameters &expanded_params, // Update all the galois keys for (std::pair<const int, GaloisKeys> &key : galoisKeys_) { - key.second.mutable_hash_block() = expanded_params_.hash_block(); + key.second.hash_block() = expanded_params_.hash_block(); } } @@ -138,7 +138,7 @@ void PIRServer::set_database(const uint8_t *bytes, uint64_t ele_num, uint64_t el } void PIRServer::set_galois_key(std::uint32_t client_id, seal::GaloisKeys galkey) { - galkey.mutable_hash_block() = expanded_params_.hash_block(); + galkey.hash_block() = expanded_params_.hash_block(); galoisKeys_[client_id] = galkey; } @@ -308,10 +308,10 @@ inline void PIRServer::multiply_power_of_X(const Ciphertext &encrypted, Cipherte // Multiply X^index for each ciphertext polynomial for (int i = 0; i < encrypted_count; i++) { for (int j = 0; j < coeff_mod_count; j++) { - negacyclic_shift_poly_coeffmod(encrypted.pointer(i) + (j * coeff_count), + negacyclic_shift_poly_coeffmod(encrypted.data(i) + (j * coeff_count), coeff_count - 1, index, expanded_params_.coeff_modulus()[j], - destination.mutable_pointer(i) + (j * coeff_count)); + destination.data(i) + (j * coeff_count)); } } } @@ -330,7 +330,7 @@ inline void PIRServer::decompose_to_plaintexts_ptr(const Ciphertext &encrypted, // A triple for loop. Going over polys, moduli, and decomposed index. for (int i = 0; i < encrypted_count; i++) { - const uint64_t *encrypted_pointer = encrypted.pointer(i); + const uint64_t *encrypted_pointer = encrypted.data(i); for (int j = 0; j < coeff_mod_count; j++) { // populate one poly at a time. // create a polynomial to store the current decomposition value @@ -366,7 +366,7 @@ vector<Plaintext> PIRServer::decompose_to_plaintexts(const Ciphertext &encrypted // A triple for loop. Going over polys, moduli, and decomposed index. for (int i = 0; i < encrypted_count; i++) { - const uint64_t *encrypted_pointer = encrypted.pointer(i); + const uint64_t *encrypted_pointer = encrypted.data(i); for (int j = 0; j < coeff_mod_count; j++) { // populate one poly at a time. // create a polynomial to store the current decomposition value @@ -382,7 +382,7 @@ vector<Plaintext> PIRServer::decompose_to_plaintexts(const Ciphertext &encrypted BigPoly temp; temp.resize(coeff_count, plain_bit_count); temp.set_zero(); - uint64_t *plain_coeff = temp.pointer(); + uint64_t *plain_coeff = temp.data(); for (int m = 0; m < coeff_count; m++) { *(plain_coeff + m) = (*(encrypted_pointer + m + (j * coeff_count)) / cur) % plainMod;