diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f339c1e --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.cpp linguist-language=Python diff --git a/.gitignore b/.gitignore index a622d32..ba785c5 100644 --- a/.gitignore +++ b/.gitignore @@ -42,5 +42,4 @@ seal.egg-info *.pyd *.pyc temp -.idea -*.bin +env diff --git a/.gitmodules b/.gitmodules index c8a8ee7..b951d16 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,4 +4,3 @@ [submodule "pybind11"] path = pybind11 url = https://github.com/pybind/pybind11.git - branch = stable diff --git a/README.md b/README.md index 80d2383..7752125 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,8 @@ This is a python binding for the Microsoft SEAL library. ## Build - -* ### Linux - - Recommend: Clang++ (>= 10.0) or GNU G++ (>= 9.4), CMake (>= 3.16) +* #### Linux + Clang++ (>= 5.0) or GNU G++ (>= 6.0), CMake (>= 3.12) ```shell # Optional @@ -32,60 +30,42 @@ This is a python binding for the Microsoft SEAL library. git clone https://github.com/Huelse/SEAL-Python.git cd SEAL-Python - # Install dependencies - pip3 install numpy pybind11 + # Numpy is essential + pip3 install -r requirements.txt # Init the SEAL and pybind11 git submodule update --init --recursive - # Get the newest repositories (dev only) + # Get the newest repositories (unnecessary) # git submodule update --remote # Build the SEAL lib cd SEAL - cmake -S . -B build -DSEAL_USE_MSGSL=OFF -DSEAL_USE_ZLIB=OFF + cmake -S . -B build -DSEAL_USE_MSGSL=OFF -DSEAL_USE_ZLIB=OFF -DSEAL_USE_ZSTD=OFF cmake --build build cd .. # Run the setup.py python3 setup.py build_ext -i - - # Test - cp seal.*.so examples - cd examples - python3 4_bgv_basics.py ``` - Build examples (after `cmake -S . -B`): `-DSEAL_BUILD_EXAMPLES=ON` - - Zstandard compression off: `-DSEAL_USE_ZSTD=OFF` +* #### Windows - [More cmake options](https://github.com/microsoft/SEAL#basic-cmake-options) - - -* ### Windows - - Visual Studio 2019 or newer is required. x64 support only! And use the **x64 Native Tools Command Prompt for VS** command prompt to configure and build the Microsoft SEAL library. It's usually can be found in your Start Menu. + Visual Studio 2019 or newer is required. And use the **x64 Native Tools Command Prompt for Visual Studio 2019** command prompt to configure and build the Microsoft SEAL library. It's usually can be found in your Start Menu. ```shell - # Run in "x64 Native Tools Command Prompt for VS" command prompt - cmake -S . -B build -G Ninja -DSEAL_USE_MSGSL=OFF -DSEAL_USE_ZLIB=OFF + # Same as above + # Build the SEAL library + cmake -S . -B build -G Ninja -DSEAL_USE_MSGSL=OFF -DSEAL_USE_ZLIB=OFF -DSEAL_USE_ZSTD=OFF cmake --build build - # Build - pip install numpy pybind11 + # Run the setup.py python setup.py build_ext -i - - # Test - cp seal.*.pyd examples - cd examples - python 4_bgv_basics.py ``` - Microsoft SEAL official [docs](https://github.com/microsoft/SEAL#building-microsoft-seal-manually). - + Generally, the Ninja generator is better than the "Visual Studio 16 2019" generator, and there is more information in the Microsoft SEAL official [illustrate](https://github.com/microsoft/SEAL#building-microsoft-seal-manually). -* ### Docker +* #### Docker requires: [Docker](https://www.docker.com/) To build source code into a docker image (from this directory): @@ -98,28 +78,51 @@ This is a python binding for the Microsoft SEAL library. docker run -it huelse/seal ``` - - ## Note -* ### Serialize +* #### Serialize - See more in `examples/7_serialization.py`, here is a simple example: + In most situations, you can use the SEAL's native serialize API to save the data, here is an example: ```python cipher.save('cipher') + load_cipher = Ciphertext() load_cipher.load(context, 'cipher') # work if the context is valid. ``` - Supported classes: `EncryptionParameters, Ciphertext, Plaintext, SecretKey, PublicKey, RelinKeys, GaloisKeys` + Support type: `Encryptionparams, Ciphertext, Plaintext, SecretKey, Publickey, Relinkeys, Galoiskeys` + Particularly, if you want to use the pickle to serialize your data, you need to do these things like below: -* ### Other + ```shell + # 1. Modify the serializable object's header file in SEAL and switch the wrapper. + python helper.py - There are a lot of changes in the latest SEAL lib, we try to make the API in python can be used easier, but it may remain some problems unknown, if any problems or bugs, report [issues](https://github.com/Huelse/SEAL-Python/issues). + # 2. Rebuild the SEAL lib like above + cmake --build build - Email: [topmaxz@protonmail.com](mailto:topmaxz@protonmail.com?subject=Github-SEAL-Python-Issues) + # 3. Run the setup.py + python setup.py build_ext -i + ``` + + Then, you can pickle the data object like this: + + ```python + import pickle + + cipher.set_parms(parms) # necessary + cipher_dump = pickle.dumps(cipher) + cipher_load = pickle.loads(cipher_dump) + ``` + + Generally, we don't use compression library. + +* #### Other + + There are a lot of changes in the latest SEAL lib, we try to make the API in python can be used easier, it may remain some problems we unknown, if any problems(bugs), [Issue](https://github.com/Huelse/SEAL-Python/issues) please. + + Email: [huelse@oini.top](mailto:huelse@oini.top?subject=Github-SEAL-Python-Issues) @@ -145,14 +148,9 @@ This is a python binding for the Microsoft SEAL library. The `.so` or `.pyd` file must be in the current directory, or you have `install` it already. -5. Windows Error LNK2001, RuntimeLibrary and MT_StaticRelease mismatch - - Only `x64` is supported, Choose `x64 Native Tools Command Prompt for VS`. - ## Contributing - * Professor: [Dr. Chen](https://zhigang-chen.github.io/) * [Contributors](https://github.com/Huelse/SEAL-Python/graphs/contributors) diff --git a/SEAL b/SEAL index 82b07db..608fb1b 160000 --- a/SEAL +++ b/SEAL @@ -1 +1 @@ -Subproject commit 82b07db635132e297282649e2ab5908999089ad2 +Subproject commit 608fb1b9717ec6effe707758a636e0a0df76fc26 diff --git a/examples/4_bgv_basics.py b/examples/4_bgv_basics.py deleted file mode 100644 index ec81cf7..0000000 --- a/examples/4_bgv_basics.py +++ /dev/null @@ -1,113 +0,0 @@ -from seal import * -import numpy as np - -def print_vector(vector): - print('[ ', end='') - for i in range(0, 8): - print(vector[i], end=', ') - print('... ]') - - -def example_bgv_basics(): - parms = EncryptionParameters (scheme_type.bgv) - poly_modulus_degree = 8192 - parms.set_poly_modulus_degree(poly_modulus_degree) - parms.set_coeff_modulus(CoeffModulus.BFVDefault(poly_modulus_degree)) - parms.set_plain_modulus(PlainModulus.Batching(poly_modulus_degree, 20)) - context = SEALContext(parms) - - keygen = KeyGenerator(context) - secret_key = keygen.secret_key() - public_key = keygen.create_public_key() - relin_keys = keygen.create_relin_keys() - - encryptor = Encryptor(context, public_key) - evaluator = Evaluator(context) - decryptor = Decryptor(context, secret_key) - - batch_encoder = BatchEncoder(context) - slot_count = batch_encoder.slot_count() - row_size = slot_count / 2 - print(f'Plaintext matrix row size: {row_size}') - - pod_matrix = [0] * slot_count - pod_matrix[0] = 1 - pod_matrix[1] = 2 - pod_matrix[2] = 3 - pod_matrix[3] = 4 - - x_plain = batch_encoder.encode(pod_matrix) - - x_encrypted = encryptor.encrypt(x_plain) - print(f'noise budget in freshly encrypted x: {decryptor.invariant_noise_budget(x_encrypted)}') - print('-'*50) - - x_squared = evaluator.square(x_encrypted) - print(f'size of x_squared: {x_squared.size()}') - evaluator.relinearize_inplace(x_squared, relin_keys) - print(f'size of x_squared (after relinearization): {x_squared.size()}') - print(f'noise budget in x_squared: {decryptor.invariant_noise_budget(x_squared)} bits') - decrypted_result = decryptor.decrypt(x_squared) - pod_result = batch_encoder.decode(decrypted_result) - print_vector(pod_result) - print('-'*50) - - x_4th = evaluator.square(x_squared) - print(f'size of x_4th: {x_4th.size()}') - evaluator.relinearize_inplace(x_4th, relin_keys) - print(f'size of x_4th (after relinearization): { x_4th.size()}') - print(f'noise budget in x_4th: {decryptor.invariant_noise_budget(x_4th)} bits') - decrypted_result = decryptor.decrypt(x_4th) - pod_result = batch_encoder.decode(decrypted_result) - print_vector(pod_result) - print('-'*50) - - x_8th = evaluator.square(x_4th) - print(f'size of x_8th: {x_8th.size()}') - evaluator.relinearize_inplace(x_8th, relin_keys) - print(f'size of x_8th (after relinearization): { x_8th.size()}') - print(f'noise budget in x_8th: {decryptor.invariant_noise_budget(x_8th)} bits') - decrypted_result = decryptor.decrypt(x_8th) - pod_result = batch_encoder.decode(decrypted_result) - print_vector(pod_result) - print('run out of noise budget') - print('-'*100) - - x_encrypted = encryptor.encrypt(x_plain) - print(f'noise budget in freshly encrypted x: {decryptor.invariant_noise_budget(x_encrypted)}') - print('-'*50) - - x_squared = evaluator.square(x_encrypted) - print(f'size of x_squared: {x_squared.size()}') - evaluator.relinearize_inplace(x_squared, relin_keys) - evaluator.mod_switch_to_next_inplace(x_squared) - print(f'noise budget in x_squared (with modulus switching): {decryptor.invariant_noise_budget(x_squared)} bits') - decrypted_result = decryptor.decrypt(x_squared) - pod_result = batch_encoder.decode(decrypted_result) - print_vector(pod_result) - print('-'*50) - - x_4th = evaluator.square(x_squared) - print(f'size of x_4th: {x_4th.size()}') - evaluator.relinearize_inplace(x_4th, relin_keys) - evaluator.mod_switch_to_next_inplace(x_4th) - print(f'size of x_4th (after relinearization): { x_4th.size()}') - print(f'noise budget in x_4th (with modulus switching): {decryptor.invariant_noise_budget(x_4th)} bits') - decrypted_result = decryptor.decrypt(x_4th) - pod_result = batch_encoder.decode(decrypted_result) - print_vector(pod_result) - print('-'*50) - - x_8th = evaluator.square(x_4th) - print(f'size of x_8th: {x_8th.size()}') - evaluator.relinearize_inplace(x_8th, relin_keys) - evaluator.mod_switch_to_next_inplace(x_8th) - print(f'size of x_8th (after relinearization): { x_8th.size()}') - print(f'noise budget in x_8th (with modulus switching): {decryptor.invariant_noise_budget(x_8th)} bits') - decrypted_result = decryptor.decrypt(x_8th) - pod_result = batch_encoder.decode(decrypted_result) - print_vector(pod_result) - - -if __name__ == "__main__": - example_bgv_basics() diff --git a/examples/7_serialization.py b/examples/7_serialization.py deleted file mode 100644 index 07bc5d2..0000000 --- a/examples/7_serialization.py +++ /dev/null @@ -1,73 +0,0 @@ -from seal import * -import pickle -import time - - -def get_seal(): - parms = EncryptionParameters(scheme_type.ckks) - poly_modulus_degree = 8192 - parms.set_poly_modulus_degree(poly_modulus_degree) - parms.set_coeff_modulus(CoeffModulus.Create(poly_modulus_degree, [60, 40, 40, 60])) - scale = 2.0 ** 40 - - context = SEALContext(parms) - ckks_encoder = CKKSEncoder(context) - slot_count = ckks_encoder.slot_count() - - keygen = KeyGenerator(context) - public_key = keygen.create_public_key() - secret_key = keygen.secret_key() - - encryptor = Encryptor(context, public_key) - # evaluator = Evaluator(context) - decryptor = Decryptor(context, secret_key) - - data = [3.1415926] * slot_count - plain = ckks_encoder.encode(data, scale) - cipher = encryptor.encrypt(plain) - - return cipher, context, ckks_encoder, decryptor - - -def serialization_example(): - print('serialization example') - print('-' * 70) - cipher2, context2, ckks_encoder2, decryptor2 = get_seal() - cipher2.save('cipher2.bin') - print('save cipher2 data success') - - time.sleep(.5) - - cipher3 = Ciphertext() - cipher3.load(context2, 'cipher2.bin') - print('load cipher2 data success') - plain3 = decryptor2.decrypt(cipher3) - data3 = ckks_encoder2.decode(plain3) - print(data3) - print('-' * 70) - - -def pickle_example(): - print('pickle example') - print('-' * 70) - cipher1, context1, ckks_encoder1, decryptor1 = get_seal() - with open('cipher1.bin', 'wb') as f: - pickle.dump(cipher1.to_string(), f) - print('write cipher1 data success') - - time.sleep(.5) - - with open('cipher1.bin', 'rb') as f: - temp = pickle.load(f) - cipher2 = context1.from_cipher_str(temp) - plain2 = decryptor1.decrypt(cipher2) - data = ckks_encoder1.decode(plain2) - print('read cipher1 data success') - print(data) - - print('-' * 70) - - -if __name__ == "__main__": - serialization_example() - pickle_example() diff --git a/helper.py b/helper.py new file mode 100644 index 0000000..c54b1fc --- /dev/null +++ b/helper.py @@ -0,0 +1,50 @@ +import os + +root_path = './SEAL/native/src/seal/' +files = ['plaintext.h', 'ciphertext.h', 'kswitchkeys.h', 'secretkey.h', 'publickey.h'] +keyword = 'private:' +new_line = 'EncryptionParameters parms;' + +def add_parms_to_header(): + for file_name in files: + file_path = root_path + file_name + if os.path.exists(file_path): + new_line_exists = True + with open(file_path, 'r', encoding='utf-8') as f: + lines = f.readlines() + for line in lines: + if new_line in line: + new_line_exists = False + + if new_line_exists: + with open(file_path, 'w', encoding='utf-8') as fs: + for line in lines: + if keyword in line: + line = line.replace(line, '\t\t{}\n\n\t{}\n'.format(new_line, keyword)) + fs.write(line) + print('Add parms to {} success.'. format(file_path)) + else: + print('Can not find the {}, please check the file integrity.'.format(file_path)) + + +def switch_wrapper(): + ifswitch = False + with open('./setup.py', 'r', encoding='utf-8') as f: + lines = f.readlines() + for line in lines: + if "wrapper_file = 'src/wrapper.cpp'" in line: + ifswitch = True + if ifswitch: + with open('./setup.py', 'w', encoding='utf-8') as fs: + for line in lines: + if "wrapper_file = 'src/wrapper.cpp'" in line: + line = line.replace(line, "wrapper_file = 'src/wrapper_with_pickle.cpp'\n") + fs.write(line) + print("Switch wrapper success.") + else: + print('Already switch to wrapper with pickle.') + + +if __name__ == '__main__': + add_parms_to_header() + switch_wrapper() diff --git a/pybind11 b/pybind11 index ffa3468..417067e 160000 --- a/pybind11 +++ b/pybind11 @@ -1 +1 @@ -Subproject commit ffa346860b306c9bbfb341aed9c14c067751feb8 +Subproject commit 417067eeb8d93e8ed49f3ff580712ae2b9a85259 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..296d654 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +numpy \ No newline at end of file diff --git a/setup.py b/setup.py index ed93e3c..dc0054a 100644 --- a/setup.py +++ b/setup.py @@ -1,44 +1,47 @@ import os import platform -from glob import glob -from setuptools import setup +from setuptools import setup, Extension from distutils.sysconfig import get_python_inc -from pybind11.setup_helpers import Pybind11Extension, build_ext -__version__ = "4.0.0" -include_dirs = [get_python_inc(), 'pybind11/include', 'SEAL/native/src', 'SEAL/build/native/src'] +# python include dir +py_include_dir = os.path.join(get_python_inc()) +# cpp flags +cpp_args = ['-std=c++17'] +# include directories +include_dirs = [py_include_dir, './pybind11/include', './SEAL/native/src', './SEAL/build/native/src'] +# library path +extra_objects = ['./SEAL/build/lib/libseal-3.7.a'] +# available wrapper: src/wrapper.cpp, src/wrapper_with_pickle.cpp +wrapper_file = 'src/wrapper.cpp' -extra_objects = sorted(glob('SEAL/build/lib/*.lib') if platform.system() == "Windows" else glob('SEAL/build/lib/*.a')) +if(platform.system() == "Windows"): + cpp_args[0] = '/std:c++latest' # /std:c++1z + extra_objects[0] = './SEAL/build/lib/Release/seal-3.7.lib' -cpp_args = ['/std:c++latest'] if platform.system() == "Windows" else ['-std=c++17'] - -if len(extra_objects) < 1 or not os.path.exists(extra_objects[0]): - print('Not found the seal lib file, check the `SEAL/build/lib`') - exit(0) +if not os.path.exists(extra_objects[0]): + print('Can not find the seal lib,') + print('Compile the seal lib first or check the path.') + exit(1) ext_modules = [ - Pybind11Extension( - "seal", - sorted(glob('src/*.cpp')), + Extension( + name='seal', + sources=[wrapper_file, 'src/base64.cpp'], include_dirs=include_dirs, + language='c++', extra_compile_args=cpp_args, extra_objects=extra_objects, - define_macros = [('VERSION_INFO', __version__)], ), ] setup( name='seal', + version='3.7', author='Desilo', author_email='engineering.dev@desilo.ai', description='Python wrapper for the Microsoft SEAL', url='https://github.com/Desilo/SEAL-Python', license='MIT', - version=__version__, - long_description="", ext_modules=ext_modules, - cmdclass={"build_ext": build_ext}, - zip_safe=False, - python_requires=">=3.6", ) diff --git a/src/base64.cpp b/src/base64.cpp new file mode 100644 index 0000000..252224b --- /dev/null +++ b/src/base64.cpp @@ -0,0 +1,112 @@ +/* + base64.cpp and base64.h + base64 encoding and decoding with C++. + Version: 1.01.00 + Copyright (C) 2004-2017 René Nyffenegger + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + 3. This notice may not be removed or altered from any source distribution. + René Nyffenegger rene.nyffenegger@adp-gmbh.ch +*/ + +#include "base64.h" +#include + +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) + ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = ( char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + + for (j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + + } + + return ret; + +} + +std::string base64_decode(std::string const& encoded_string) { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = base64_chars.find(char_array_4[i]); + + char_array_3[0] = ( char_array_4[0] << 2 ) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = 0; j < i; j++) + char_array_4[j] = base64_chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +} diff --git a/src/base64.h b/src/base64.h new file mode 100644 index 0000000..dd1134c --- /dev/null +++ b/src/base64.h @@ -0,0 +1,14 @@ +// +// base64 encoding and decoding with C++. +// Version: 1.01.00 +// + +#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A +#define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A + +#include + +std::string base64_encode(unsigned char const* , unsigned int len); +std::string base64_decode(std::string const& s); + +#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */ diff --git a/src/wrapper.cpp b/src/wrapper.cpp index 73b78ef..bd1e602 100644 --- a/src/wrapper.cpp +++ b/src/wrapper.cpp @@ -3,9 +3,12 @@ #include #include #include "seal/seal.h" +#include "base64.h" +#include #include using namespace seal; + namespace py = pybind11; PYBIND11_MAKE_OPAQUE(std::vector); @@ -13,8 +16,7 @@ PYBIND11_MAKE_OPAQUE(std::vector); PYBIND11_MODULE(seal, m) { - m.doc() = "Microsoft SEAL for Python, from https://github.com/Huelse/SEAL-Python"; - m.attr("__version__") = "4.0.0"; + m.doc() = "Microsoft SEAL (3.6) for Python, from https://github.com/Huelse/SEAL-Python"; py::bind_vector>(m, "VectorDouble", py::buffer_protocol()); py::bind_vector>(m, "VectorInt", py::buffer_protocol()); @@ -23,66 +25,71 @@ PYBIND11_MODULE(seal, m) py::enum_(m, "scheme_type") .value("none", scheme_type::none) .value("bfv", scheme_type::bfv) - .value("ckks", scheme_type::ckks) - .value("bgv", scheme_type::bgv); + .value("ckks", scheme_type::ckks); // encryptionparams.h py::class_(m, "EncryptionParameters") .def(py::init()) .def(py::init()) .def("set_poly_modulus_degree", &EncryptionParameters::set_poly_modulus_degree) - .def("set_coeff_modulus", &EncryptionParameters::set_coeff_modulus) + .def("set_coeff_modulus", &EncryptionParameters::set_coeff_modulus) .def("set_plain_modulus", py::overload_cast(&EncryptionParameters::set_plain_modulus)) - .def("set_plain_modulus", py::overload_cast(&EncryptionParameters::set_plain_modulus)) - .def("scheme", &EncryptionParameters::scheme) + .def("set_plain_modulus", py::overload_cast(&EncryptionParameters::set_plain_modulus)) + .def("scheme", &EncryptionParameters::scheme) .def("poly_modulus_degree", &EncryptionParameters::poly_modulus_degree) - .def("coeff_modulus", &EncryptionParameters::coeff_modulus) - .def("plain_modulus", &EncryptionParameters::plain_modulus) + .def("coeff_modulus", &EncryptionParameters::coeff_modulus) + .def("plain_modulus", &EncryptionParameters::plain_modulus) .def("save", [](const EncryptionParameters &parms, std::string &path){ - std::ofstream out(path, std::ios::binary); - parms.save(out); - out.close(); - }) - .def("load", [](EncryptionParameters &parms, std::string &path){ - std::ifstream in(path, std::ios::binary); - parms.load(in); - in.close(); - }) - .def("dumpb", [](const EncryptionParameters &parms){ - std::stringstream out(std::ios::binary | std::ios::out); - parms.save(out); - return py::bytes(out.str()); - }) - .def("loadb", [](EncryptionParameters &parms, const py::bytes &bytes){ - std::stringstream in(bytes, std::ios::binary | std::ios::in); - parms.load(in); - }) - .def(py::pickle( - [](const EncryptionParameters &parms){ - std::stringstream out(std::ios::binary | std::ios::out); - parms.save(out); - return py::make_tuple(py::bytes(out.str())); - }, - [](py::tuple t){ - if (t.size() != 1) - throw std::runtime_error("(Pickle) Invalid input tuple!"); - std::string str = t[0].cast(); - std::stringstream in(std::ios::binary | std::ios::in); - EncryptionParameters parms; - parms.load(in); - return parms; - } - )); - - // modulus.h - py::enum_(m, "sec_level_type") - .value("none", sec_level_type::none) - .value("tc128", sec_level_type::tc128) - .value("tc192", sec_level_type::tc192) - .value("tc256", sec_level_type::tc256); + std::ofstream out(path, std::ofstream::binary); + parms.save(out); + out.close(); + }) + .def("load", [](EncryptionParameters &parms, std::string &path){ + std::ifstream in(path, std::ifstream::binary); + parms.load(in); + in.close(); + }) + .def("dumpb", [](const EncryptionParameters &parms){ + std::stringstream out(std::ios::binary | std::ios::out); + parms.save(out); + return py::bytes(out.str()); + }) + .def("loadb", [](EncryptionParameters &parms, const py::bytes &bytes){ + std::stringstream in(bytes, std::ios::binary | std::ios::in); + parms.load(in); + }) + // remove pickle support? + .def(py::pickle( + [](const EncryptionParameters &parms){ + std::stringstream out_stream(std::ios::binary | std::ios::out); + parms.save(out_stream); + std::string str_buf = out_stream.str(); + std::string encoded_str = base64_encode(reinterpret_cast(str_buf.c_str()), (unsigned int)str_buf.length()); + return py::make_tuple(encoded_str); + }, + [](py::tuple t){ + if (t.size() != 1) + throw std::runtime_error("E002: Invalid state!"); + + std::string encoded_str = t[0].cast(); + std::string decoded_str = base64_decode(encoded_str); + std::stringstream in_stream(std::ios::binary | std::ios::in); + in_stream.str(decoded_str); + EncryptionParameters parms; + parms.load(in_stream); + return parms; + } + )); + + // modulus.h + py::enum_(m, "sec_level_type") + .value("none", sec_level_type::none) + .value("tc128", sec_level_type::tc128) + .value("tc192", sec_level_type::tc192) + .value("tc256", sec_level_type::tc256); // context.h - py::enum_(m, "error_type") + py::enum_(m, "error_type") .value("none", EncryptionParameterQualifiers::error_type::none) .value("success", EncryptionParameterQualifiers::error_type::success) .value("invalid_scheme", EncryptionParameterQualifiers::error_type::invalid_scheme) @@ -100,597 +107,594 @@ PYBIND11_MODULE(seal, m) .value("invalid_plain_modulus_nonzero", EncryptionParameterQualifiers::error_type::invalid_plain_modulus_nonzero) .value("failed_creating_rns_tool", EncryptionParameterQualifiers::error_type::failed_creating_rns_tool); - // context.h - py::class_>(m, "EncryptionParameterQualifiers") - .def("parameters_set", &EncryptionParameterQualifiers::parameters_set) - .def_readwrite("using_fft", &EncryptionParameterQualifiers::using_fft) - .def_readwrite("using_ntt", &EncryptionParameterQualifiers::using_ntt) - .def_readwrite("using_batching", &EncryptionParameterQualifiers::using_batching) - .def_readwrite("using_fast_plain_lift", &EncryptionParameterQualifiers::using_fast_plain_lift) - .def_readwrite("using_descending_modulus_chain", &EncryptionParameterQualifiers::using_descending_modulus_chain) - .def_readwrite("sec_level", &EncryptionParameterQualifiers::sec_level); - - // context.h - py::class_>(m, "ContextData") - .def("parms", &SEALContext::ContextData::parms) - .def("parms_id", &SEALContext::ContextData::parms_id) - .def("qualifiers", &SEALContext::ContextData::qualifiers) - .def("total_coeff_modulus", &SEALContext::ContextData::total_coeff_modulus) - .def("total_coeff_modulus_bit_count", &SEALContext::ContextData::total_coeff_modulus_bit_count) - .def("next_context_data", &SEALContext::ContextData::next_context_data) - .def("chain_index", &SEALContext::ContextData::chain_index); - - // context.h - py::class_>(m, "SEALContext") - .def(py::init(), py::arg(), py::arg()=true, py::arg()=sec_level_type::tc128) - .def("get_context_data", &SEALContext::get_context_data) - .def("key_context_data", &SEALContext::key_context_data) - .def("first_context_data", &SEALContext::first_context_data) - .def("last_context_data", &SEALContext::last_context_data) - .def("parameters_set", &SEALContext::parameters_set) - .def("first_parms_id", &SEALContext::first_parms_id) - .def("last_parms_id", &SEALContext::last_parms_id) - .def("using_keyswitching", &SEALContext::using_keyswitching) - .def("from_cipher_str", [](const SEALContext &context, const std::string &str){ - Ciphertext cipher; - std::stringstream in(std::ios::binary | std::ios::in); - in.str(str); - cipher.load(context, in); - return cipher; - }) - .def("from_plain_str", [](const SEALContext &context, const std::string &str){ - Plaintext plain; - std::stringstream in(std::ios::binary | std::ios::in); - in.str(str); - plain.load(context, in); - return plain; - }) - .def("from_secret_str", [](const SEALContext &context, const std::string &str){ - SecretKey secret; - std::stringstream in(std::ios::binary | std::ios::in); - in.str(str); - secret.load(context, in); - return secret; - }) - .def("from_public_str", [](const SEALContext &context, const std::string &str){ - PublicKey public_; - std::stringstream in(std::ios::binary | std::ios::in); - in.str(str); - public_.load(context, in); - return public_; - }) - .def("from_relin_str", [](const SEALContext &context, const std::string &str){ - RelinKeys relin; - std::stringstream in(std::ios::binary | std::ios::in); - in.str(str); - relin.load(context, in); - return relin; - }) - .def("from_galois_str", [](const SEALContext &context, const std::string &str){ - GaloisKeys galois; - std::stringstream in(std::ios::binary | std::ios::in); - in.str(str); - galois.load(context, in); - return galois; - }); - - // modulus.h - py::class_(m, "Modulus") - .def(py::init()) - .def("bit_count", &Modulus::bit_count) - .def("value", &Modulus::value) - .def("is_zero", &Modulus::is_zero) - .def("is_prime", &Modulus::is_prime); - //save & load - - // modulus.h - py::class_(m, "CoeffModulus") - .def_static("MaxBitCount", &CoeffModulus::MaxBitCount, py::arg(), py::arg()=sec_level_type::tc128) - .def_static("BFVDefault", &CoeffModulus::BFVDefault, py::arg(), py::arg()=sec_level_type::tc128) - .def_static("Create", py::overload_cast>(&CoeffModulus::Create)) - .def_static("Create", py::overload_cast>(&CoeffModulus::Create)); - - // modulus.h - py::class_(m, "PlainModulus") - .def_static("Batching", py::overload_cast(&PlainModulus::Batching)) - .def_static("Batching", py::overload_cast>(&PlainModulus::Batching)); - - // plaintext.h - py::class_(m, "Plaintext") - .def(py::init<>()) - .def(py::init<std::size_t>()) - .def(py::init<std::size_t, std::size_t>()) - .def(py::init<const std::string &>()) - .def(py::init<const Plaintext &>()) - .def("set_zero", py::overload_cast<std::size_t, std::size_t>(&Plaintext::set_zero)) - .def("set_zero", py::overload_cast<std::size_t>(&Plaintext::set_zero)) - .def("set_zero", py::overload_cast<>(&Plaintext::set_zero)) - .def("is_zero", &Plaintext::is_zero) - .def("capacity", &Plaintext::capacity) - .def("coeff_count", &Plaintext::coeff_count) - .def("significant_coeff_count", &Plaintext::significant_coeff_count) - .def("nonzero_coeff_count", &Plaintext::nonzero_coeff_count) - .def("to_string", &Plaintext::to_string) - .def("is_ntt_form", &Plaintext::is_ntt_form) - .def("parms_id", py::overload_cast<>(&Plaintext::parms_id, py::const_)) - .def("scale", py::overload_cast<>(&Plaintext::scale, py::const_)) - .def("scale", [](Plaintext &plain, double scale){ - plain.scale() = scale; - }) - .def("save", [](const Plaintext &plain, const std::string &path){ - std::ofstream out(path, std::ios::binary); - plain.save(out); - out.close(); - }) - .def("load", [](Plaintext &plain, const SEALContext &context, const std::string &path){ - std::ifstream in(path, std::ios::binary); - plain.load(context, in); - in.close(); - }) - .def("save_size", [](const Plaintext &plain){ - return plain.save_size(); - }) - .def("to_string", [](const Plaintext &plain){ - std::stringstream out(std::ios::binary | std::ios::out); - plain.save(out); - return py::bytes(out.str()); - }); - - // ciphertext.h - py::class_<Ciphertext>(m, "Ciphertext") - .def(py::init<>()) - .def(py::init<const SEALContext &>()) - .def(py::init<const SEALContext &, parms_id_type>()) - .def(py::init<const SEALContext &, parms_id_type, std::size_t>()) - .def(py::init<const Ciphertext &>()) - .def("coeff_modulus_size", &Ciphertext::coeff_modulus_size) - .def("poly_modulus_degree", &Ciphertext::poly_modulus_degree) - .def("size", &Ciphertext::size) - .def("size_capacity", &Ciphertext::size_capacity) - .def("is_transparent", &Ciphertext::is_transparent) - .def("is_ntt_form", py::overload_cast<>(&Ciphertext::is_ntt_form, py::const_)) - .def("parms_id", py::overload_cast<>(&Ciphertext::parms_id, py::const_)) - .def("scale", py::overload_cast<>(&Ciphertext::scale, py::const_)) - .def("scale", [](Ciphertext &cipher, double scale){ - cipher.scale() = scale; - }) - .def("save", [](const Ciphertext &cipher, const std::string &path){ - std::ofstream out(path, std::ios::binary); - cipher.save(out); - out.close(); - }) - .def("load", [](Ciphertext &cipher, const SEALContext &context, const std::string &path){ - std::ifstream in(path, std::ios::binary); - cipher.load(context, in); - in.close(); - }) - .def("save_size", [](const Ciphertext &cipher){ - return cipher.save_size(); - }) - .def("to_string", [](const Ciphertext &cipher){ - std::stringstream out(std::ios::binary | std::ios::out); - cipher.save(out); - return py::bytes(out.str()); - }); - - // secretkey.h - py::class_<SecretKey>(m, "SecretKey") - .def(py::init<>()) - .def(py::init<const SecretKey &>()) - .def("parms_id", py::overload_cast<>(&SecretKey::parms_id, py::const_)) - .def("save", [](const SecretKey &sk, const std::string &path){ - std::ofstream out(path, std::ios::binary); - sk.save(out); - out.close(); - }) - .def("load", [](SecretKey &sk, const SEALContext &context, const std::string &path){ - std::ifstream in(path, std::ios::binary); - sk.load(context, in); - in.close(); - }) - .def("to_string", [](const SecretKey &secret){ - std::stringstream out(std::ios::binary | std::ios::out); - secret.save(out); - return py::bytes(out.str()); - }); - - // publickey.h - py::class_<PublicKey>(m, "PublicKey") - .def(py::init<>()) - .def(py::init<const PublicKey &>()) - .def("parms_id", py::overload_cast<>(&PublicKey::parms_id, py::const_)) - .def("save", [](const PublicKey &pk, const std::string &path){ - std::ofstream out(path, std::ios::binary); - pk.save(out); - out.close(); - }) - .def("load", [](PublicKey &pk, const SEALContext &context, const std::string &path){ - std::ifstream in(path, std::ios::binary); - pk.load(context, in); - in.close(); - }) - .def("to_string", [](const PublicKey &public_){ - std::stringstream out(std::ios::binary | std::ios::out); - public_.save(out); - return py::bytes(out.str()); - }); - - // kswitchkeys.h - py::class_<KSwitchKeys>(m, "KSwitchKeys") - .def(py::init<>()) - .def(py::init<const KSwitchKeys &>()) - .def("size", &KSwitchKeys::size) - .def("parms_id", py::overload_cast<>(&KSwitchKeys::parms_id, py::const_)) - .def("save", [](const KSwitchKeys &ksk, const std::string &path){ - std::ofstream out(path, std::ios::binary); - ksk.save(out); - out.close(); - }) - .def("load", [](KSwitchKeys &ksk, const SEALContext &context, const std::string &path){ - std::ifstream in(path, std::ios::binary); - ksk.load(context, in); - in.close(); - }); - - // relinkeys.h - py::class_<RelinKeys, KSwitchKeys>(m, "RelinKeys") - .def(py::init<>()) - .def(py::init<const RelinKeys::KSwitchKeys &>()) - .def("size", &RelinKeys::KSwitchKeys::size) - .def("parms_id", py::overload_cast<>(&RelinKeys::KSwitchKeys::parms_id, py::const_)) - .def_static("get_index", &RelinKeys::get_index) - .def("has_key", &RelinKeys::has_key) - .def("save", [](const RelinKeys &rk, const std::string &path){ - std::ofstream out(path, std::ios::binary); - rk.save(out); - out.close(); - }) - .def("load", [](RelinKeys &rk, const SEALContext &context, const std::string &path){ - std::ifstream in(path, std::ios::binary); - rk.load(context, in); - in.close(); - }) - .def("to_string", [](const RelinKeys &relin){ - std::stringstream out(std::ios::binary | std::ios::out); - relin.save(out); - return py::bytes(out.str()); - }); - - // galoiskeys.h - py::class_<GaloisKeys, KSwitchKeys>(m, "GaloisKeys") - .def(py::init<>()) - .def(py::init<const GaloisKeys::KSwitchKeys &>()) - .def("size", &GaloisKeys::KSwitchKeys::size) - .def("parms_id", py::overload_cast<>(&GaloisKeys::KSwitchKeys::parms_id, py::const_)) - .def_static("get_index", &GaloisKeys::get_index) - .def("has_key", &GaloisKeys::has_key) - .def("save", [](const GaloisKeys &gk, const std::string &path){ - std::ofstream out(path, std::ios::binary); - gk.save(out); - out.close(); - }) - .def("load", [](GaloisKeys &gk, const SEALContext &context, const std::string &path){ - std::ifstream in(path, std::ios::binary); - gk.load(context, in); - in.close(); - }) - .def("to_string", [](const GaloisKeys &galois){ - std::stringstream out(std::ios::binary | std::ios::out); - galois.save(out); - return py::bytes(out.str()); - }); - - // keygenerator.h - py::class_<KeyGenerator>(m, "KeyGenerator") - .def(py::init<const SEALContext &>()) - .def(py::init<const SEALContext &, const SecretKey &>()) - .def("secret_key", &KeyGenerator::secret_key) - .def("create_public_key", py::overload_cast<PublicKey &>(&KeyGenerator::create_public_key, py::const_)) - .def("create_relin_keys", py::overload_cast<RelinKeys &>(&KeyGenerator::create_relin_keys)) - .def("create_galois_keys", py::overload_cast<const std::vector<int> &, GaloisKeys &>(&KeyGenerator::create_galois_keys)) - .def("create_galois_keys", py::overload_cast<GaloisKeys &>(&KeyGenerator::create_galois_keys)) - .def("create_public_key", [](KeyGenerator &keygen){ - PublicKey pk; - keygen.create_public_key(pk); - return pk; - }) - .def("create_relin_keys", [](KeyGenerator &keygen){ - RelinKeys rk; - keygen.create_relin_keys(rk); - return rk; - }) - .def("create_galois_keys", [](KeyGenerator &keygen){ - GaloisKeys gk; - keygen.create_galois_keys(gk); - return gk; - }); - - // encryptor.h - py::class_<Encryptor>(m, "Encryptor") - .def(py::init<const SEALContext &, const PublicKey &>()) - .def(py::init<const SEALContext &, const SecretKey &>()) - .def(py::init<const SEALContext &, const PublicKey &, const SecretKey &>()) - .def("set_public_key", &Encryptor::set_public_key) - .def("set_secret_key", &Encryptor::set_secret_key) - .def("encrypt_zero", [](const Encryptor &encryptor){ - Ciphertext encrypted; - encryptor.encrypt_zero(encrypted); - return encrypted; - }) - .def("encrypt", [](const Encryptor &encryptor, const Plaintext &plain){ - Ciphertext encrypted; - encryptor.encrypt(plain, encrypted); - return encrypted; - }); - // symmetric - - // evaluator.h - py::class_<Evaluator>(m, "Evaluator") - .def(py::init<const SEALContext &>()) - .def("negate_inplace", &Evaluator::negate_inplace) - .def("negate", [](Evaluator &evaluator, const Ciphertext &encrypted1){ - Ciphertext destination; - evaluator.negate(encrypted1, destination); - return destination; - }) - .def("add_inplace", &Evaluator::add_inplace) - .def("add", [](Evaluator &evaluator, const Ciphertext &encrypted1, const Ciphertext &encrypted2){ - Ciphertext destination; - evaluator.add(encrypted1, encrypted2, destination); - return destination; - }) - .def("add_many", [](Evaluator &evaluator, const std::vector<Ciphertext> &encrypteds){ - Ciphertext destination; - evaluator.add_many(encrypteds, destination); - return destination; - }) - .def("sub_inplace", &Evaluator::sub_inplace) - .def("sub", [](Evaluator &evaluator, const Ciphertext &encrypted1, const Ciphertext &encrypted2){ - Ciphertext destination; - evaluator.sub(encrypted1, encrypted2, destination); - return destination; - }) - .def("multiply_inplace", [](Evaluator &evaluator, Ciphertext &encrypted1, const Ciphertext &encrypted2){ - evaluator.multiply_inplace(encrypted1, encrypted2); - }) - .def("multiply", [](Evaluator &evaluator, const Ciphertext &encrypted1, const Ciphertext &encrypted2){ - Ciphertext destination; - evaluator.multiply(encrypted1, encrypted2, destination); - return destination; - }) - .def("square_inplace", [](Evaluator &evaluator, Ciphertext &encrypted1){ - evaluator.square_inplace(encrypted1); - }) - .def("square", [](Evaluator &evaluator, const Ciphertext &encrypted1){ - Ciphertext destination; - evaluator.square(encrypted1, destination); - return destination; - }) - .def("relinearize_inplace", [](Evaluator &evaluator, Ciphertext &encrypted1, const RelinKeys &relin_keys){ - evaluator.relinearize_inplace(encrypted1, relin_keys); - }) - .def("relinearize", [](Evaluator &evaluator, const Ciphertext &encrypted1, const RelinKeys &relin_keys){ - Ciphertext destination; - evaluator.relinearize(encrypted1, relin_keys, destination); - return destination; - }) - .def("mod_switch_to_next", [](Evaluator &evaluator, const Ciphertext &encrypted){ - Ciphertext destination; - evaluator.mod_switch_to_next(encrypted, destination); - return destination; - }) - .def("mod_switch_to_next_inplace", [](Evaluator &evaluator, Ciphertext &encrypted){ - evaluator.mod_switch_to_next_inplace(encrypted); - }) - .def("mod_switch_to_next_inplace", py::overload_cast<Plaintext &>(&Evaluator::mod_switch_to_next_inplace, py::const_)) - .def("mod_switch_to_next", [](Evaluator &evaluator, const Plaintext &plain){ - Plaintext destination; - evaluator.mod_switch_to_next(plain, destination); - return destination; - }) - .def("mod_switch_to_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, parms_id_type parms_id){ - evaluator.mod_switch_to_inplace(encrypted, parms_id); - }) - .def("mod_switch_to", [](Evaluator &evaluator, const Ciphertext &encrypted, parms_id_type parms_id){ - Ciphertext destination; - evaluator.mod_switch_to(encrypted, parms_id, destination); - return destination; - }) - .def("mod_switch_to_inplace", py::overload_cast<Plaintext &, parms_id_type>(&Evaluator::mod_switch_to_inplace, py::const_)) - .def("mod_switch_to", [](Evaluator &evaluator, const Plaintext &plain, parms_id_type parms_id){ - Plaintext destination; - evaluator.mod_switch_to(plain, parms_id, destination); - return destination; - }) - .def("rescale_to_next", [](Evaluator &evaluator, const Ciphertext &encrypted){ - Ciphertext destination; - evaluator.rescale_to_next(encrypted, destination); - return destination; - }) - .def("rescale_to_next_inplace", [](Evaluator &evaluator, Ciphertext &encrypted){ - evaluator.rescale_to_next_inplace(encrypted); - }) - .def("rescale_to_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, parms_id_type parms_id){ - evaluator.rescale_to_inplace(encrypted, parms_id); - }) - .def("rescale_to", [](Evaluator &evaluator, const Ciphertext &encrypted, parms_id_type parms_id){ - Ciphertext destination; - evaluator.rescale_to(encrypted, parms_id, destination); - return destination; - }) - .def("multiply_many", [](Evaluator &evaluator, const std::vector<Ciphertext> &encrypteds, const RelinKeys &relin_keys){ - Ciphertext destination; - evaluator.multiply_many(encrypteds, relin_keys, destination); - return destination; - }) - .def("exponentiate_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, std::uint64_t exponent, const RelinKeys &relin_keys){ - evaluator.exponentiate_inplace(encrypted, exponent, relin_keys); - }) - .def("exponentiate", [](Evaluator &evaluator, const Ciphertext &encrypted, std::uint64_t exponent, const RelinKeys &relin_keys){ - Ciphertext destination; - evaluator.exponentiate(encrypted, exponent, relin_keys, destination); - return destination; - }) - .def("add_plain_inplace", &Evaluator::add_plain_inplace) - .def("add_plain", [](Evaluator &evaluator, const Ciphertext &encrypted, const Plaintext &plain){ - Ciphertext destination; - evaluator.add_plain(encrypted, plain, destination); - return destination; - }) - .def("sub_plain_inplace", &Evaluator::sub_plain_inplace) - .def("sub_plain", [](Evaluator &evaluator, const Ciphertext &encrypted, const Plaintext &plain){ - Ciphertext destination; - evaluator.sub_plain(encrypted, plain, destination); - return destination; - }) - .def("multiply_plain_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, const Plaintext &plain){ - evaluator.multiply_plain_inplace(encrypted, plain); - }) - .def("multiply_plain", [](Evaluator &evaluator, const Ciphertext &encrypted, const Plaintext &plain){ - Ciphertext destination; - evaluator.multiply_plain(encrypted, plain, destination); - return destination; - }) - .def("transform_to_ntt_inplace", [](Evaluator &evaluator, Plaintext &plain, parms_id_type parms_id){ - evaluator.transform_to_ntt_inplace(plain,parms_id); - }) - .def("transform_to_ntt", [](Evaluator &evaluator, const Plaintext &plain, parms_id_type parms_id){ - Plaintext destination_ntt; - evaluator.transform_to_ntt(plain, parms_id, destination_ntt); - return destination_ntt; - }) - .def("transform_to_ntt_inplace", py::overload_cast<Ciphertext &>(&Evaluator::transform_to_ntt_inplace, py::const_)) - .def("transform_to_ntt", [](Evaluator &evaluator, const Ciphertext &encrypted){ - Ciphertext destination_ntt; - evaluator.transform_to_ntt(encrypted, destination_ntt); - return destination_ntt; - }) - .def("transform_from_ntt_inplace", &Evaluator::transform_from_ntt_inplace) - .def("transform_from_ntt", [](Evaluator &evaluator, const Ciphertext &encrypted_ntt){ - Ciphertext destination; - evaluator.transform_from_ntt(encrypted_ntt, destination); - return destination; - }) - .def("apply_galois_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, std::uint32_t galois_elt, const GaloisKeys &galois_keys){ - evaluator.apply_galois_inplace(encrypted, galois_elt, galois_keys); - }) - .def("apply_galois", [](Evaluator &evaluator, const Ciphertext &encrypted, std::uint32_t galois_elt, const GaloisKeys &galois_keys){ - Ciphertext destination; - evaluator.apply_galois(encrypted, galois_elt, galois_keys, destination); - return destination; - }) - .def("rotate_rows_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ - evaluator.rotate_rows_inplace(encrypted, steps, galois_keys); - }) - .def("rotate_rows", [](Evaluator &evaluator, const Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ - Ciphertext destination; - evaluator.rotate_rows(encrypted, steps, galois_keys, destination); - return destination; - }) - .def("rotate_columns_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, const GaloisKeys &galois_keys){ - evaluator.rotate_columns_inplace(encrypted, galois_keys); - }) - .def("rotate_columns", [](Evaluator &evaluator, const Ciphertext &encrypted, const GaloisKeys &galois_keys){ - Ciphertext destination; - evaluator.rotate_columns(encrypted, galois_keys, destination); - return destination; - }) - .def("rotate_vector_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ - evaluator.rotate_vector_inplace(encrypted, steps, galois_keys); - }) - .def("rotate_vector", [](Evaluator &evaluator, const Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ - Ciphertext destination; - evaluator.rotate_vector(encrypted, steps, galois_keys, destination); - return destination; - }) - .def("complex_conjugate_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, const GaloisKeys &galois_keys){ - evaluator.complex_conjugate_inplace(encrypted, galois_keys); - }) - .def("complex_conjugate", [](Evaluator &evaluator, const Ciphertext &encrypted, const GaloisKeys &galois_keys){ - Ciphertext destination; - evaluator.complex_conjugate(encrypted, galois_keys, destination); - return destination; - }); - - // ckks.h - py::class_<CKKSEncoder>(m, "CKKSEncoder") - .def(py::init<const SEALContext &>()) - .def("slot_count", &CKKSEncoder::slot_count) - .def("encode", [](CKKSEncoder &encoder, py::array_t<double> values, double scale){ - py::buffer_info buf = values.request(); - if (buf.ndim != 1) - throw std::runtime_error("E101: Number of dimensions must be one"); - - double *ptr = (double *)buf.ptr; - std::vector<double> vec(buf.shape[0]); - - for (auto i = 0; i < buf.shape[0]; i++) - vec[i] = ptr[i]; - - Plaintext pt; - encoder.encode(vec, scale, pt); - return pt; - }) - .def("encode", [](CKKSEncoder &encoder, double value, double scale){ - Plaintext pt; - encoder.encode(value, scale, pt); - return pt; - }) - .def("decode", [](CKKSEncoder &encoder, const Plaintext &plain){ - std::vector<double> destination; - encoder.decode(plain, destination); - - py::array_t<double> values(destination.size()); - py::buffer_info buf = values.request(); - double *ptr = (double *)buf.ptr; - - for (auto i = 0; i < buf.shape[0]; i++) - ptr[i] = destination[i]; - - return values; - }); - - // decryptor.h - py::class_<Decryptor>(m, "Decryptor") - .def(py::init<const SEALContext &, const SecretKey &>()) - .def("decrypt", &Decryptor::decrypt) - .def("invariant_noise_budget", &Decryptor::invariant_noise_budget) - .def("decrypt", [](Decryptor &decryptor, const Ciphertext &encrypted){ - Plaintext pt; - decryptor.decrypt(encrypted, pt); - return pt; - }); - - // batchencoder.h - py::class_<BatchEncoder>(m, "BatchEncoder") - .def(py::init<const SEALContext &>()) - .def("slot_count", &BatchEncoder::slot_count) - .def("encode", [](BatchEncoder &encoder, py::array_t<std::int64_t> values){ - py::buffer_info buf = values.request(); - if (buf.ndim != 1) - throw std::runtime_error("E101: Number of dimensions must be one"); - - std::int64_t *ptr = (std::int64_t *)buf.ptr; - std::vector<std::int64_t> vec(buf.shape[0]); - - for (auto i = 0; i < buf.shape[0]; i++) - vec[i] = ptr[i]; - - Plaintext pt; - encoder.encode(vec, pt); - return pt; - }) - .def("decode", [](BatchEncoder &encoder, const Plaintext &plain){ - std::vector<std::int64_t> destination; - encoder.decode(plain, destination); - - py::array_t<std::int64_t> values(destination.size()); - py::buffer_info buf = values.request(); - std::int64_t *ptr = (std::int64_t *)buf.ptr; - - for (auto i = 0; i < buf.shape[0]; i++) - ptr[i] = destination[i]; - - return values; - }); + // context.h + py::class_<EncryptionParameterQualifiers, std::unique_ptr<EncryptionParameterQualifiers, py::nodelete>>(m, "EncryptionParameterQualifiers") + .def("parameters_set", &EncryptionParameterQualifiers::parameters_set) + .def_readwrite("using_fft", &EncryptionParameterQualifiers::using_fft) + .def_readwrite("using_ntt", &EncryptionParameterQualifiers::using_ntt) + .def_readwrite("using_batching", &EncryptionParameterQualifiers::using_batching) + .def_readwrite("using_fast_plain_lift", &EncryptionParameterQualifiers::using_fast_plain_lift) + .def_readwrite("using_descending_modulus_chain", &EncryptionParameterQualifiers::using_descending_modulus_chain) + .def_readwrite("sec_level", &EncryptionParameterQualifiers::sec_level); + + // context.h + py::class_<SEALContext::ContextData, std::shared_ptr<SEALContext::ContextData>>(m, "ContextData") + .def("parms", &SEALContext::ContextData::parms) + .def("parms_id", &SEALContext::ContextData::parms_id) + .def("qualifiers", &SEALContext::ContextData::qualifiers) + .def("total_coeff_modulus", &SEALContext::ContextData::total_coeff_modulus) + .def("total_coeff_modulus_bit_count", &SEALContext::ContextData::total_coeff_modulus_bit_count) + .def("next_context_data", &SEALContext::ContextData::next_context_data) + .def("chain_index", &SEALContext::ContextData::chain_index); + + // context.h + py::class_<SEALContext, std::shared_ptr<SEALContext>>(m, "SEALContext") + .def(py::init<const EncryptionParameters &, bool, sec_level_type>(), py::arg(), py::arg()=true, py::arg()=sec_level_type::tc128) + .def("get_context_data", &SEALContext::get_context_data) + .def("key_context_data", &SEALContext::key_context_data) + .def("first_context_data", &SEALContext::first_context_data) + .def("last_context_data", &SEALContext::last_context_data) + .def("parameters_set", &SEALContext::parameters_set) + .def("first_parms_id", &SEALContext::first_parms_id) + .def("last_parms_id", &SEALContext::last_parms_id) + .def("using_keyswitching", &SEALContext::using_keyswitching); + + // modulus.h + py::class_<Modulus>(m, "Modulus") + .def(py::init<std::uint64_t>()) + .def("bit_count", &Modulus::bit_count) + .def("value", &Modulus::value) + .def("is_zero", &Modulus::is_zero) + .def("is_prime", &Modulus::is_prime); + //save & load + + // modulus.h + py::class_<CoeffModulus>(m, "CoeffModulus") + .def_static("MaxBitCount", &CoeffModulus::MaxBitCount, py::arg(), py::arg()=sec_level_type::tc128) + .def_static("BFVDefault", &CoeffModulus::BFVDefault, py::arg(), py::arg()=sec_level_type::tc128) + .def_static("Create", &CoeffModulus::Create); + + // modulus.h + py::class_<PlainModulus>(m, "PlainModulus") + .def_static("Batching", py::overload_cast<std::size_t, int>(&PlainModulus::Batching)) + .def_static("Batching", py::overload_cast<std::size_t, std::vector<int>>(&PlainModulus::Batching)); + + // plaintext.h + py::class_<Plaintext>(m, "Plaintext") + .def(py::init<>()) + .def(py::init<std::size_t>()) + .def(py::init<std::size_t, std::size_t>()) + .def(py::init<const std::string &>()) + .def(py::init<const Plaintext &>()) + .def("set_zero", py::overload_cast<std::size_t, std::size_t>(&Plaintext::set_zero)) + .def("set_zero", py::overload_cast<std::size_t>(&Plaintext::set_zero)) + .def("set_zero", py::overload_cast<>(&Plaintext::set_zero)) + .def("is_zero", &Plaintext::is_zero) + .def("capacity", &Plaintext::capacity) + .def("coeff_count", &Plaintext::coeff_count) + .def("significant_coeff_count", &Plaintext::significant_coeff_count) + .def("nonzero_coeff_count", &Plaintext::nonzero_coeff_count) + .def("to_string", &Plaintext::to_string) + .def("is_ntt_form", &Plaintext::is_ntt_form) + .def("parms_id", py::overload_cast<>(&Plaintext::parms_id, py::const_), py::return_value_policy::reference) + .def("scale", py::overload_cast<>(&Plaintext::scale, py::const_), py::return_value_policy::reference) + .def("scale", [](Plaintext &plain, double scale){ + plain.scale() = scale; + }) + .def("save", [](const Plaintext &plain, const std::string &path){ + std::ofstream out(path, std::ofstream::binary); + plain.save(out); + out.close(); + }) + .def("load", [](Plaintext &plain, const SEALContext &context, const std::string &path){ + std::ifstream in(path, std::ifstream::binary); + plain.load(context, in); + in.close(); + }) + .def("save_size", [](const Plaintext &plain){ + return plain.save_size(); + }) + .def("dumpb", [](const Plaintext &plain){ + std::stringstream out(std::ios::binary | std::ios::out); + plain.save(out); + return py::bytes(out.str()); + }) + .def("loadb", [](Plaintext &plain, const SEALContext &context, const py::bytes &bytes){ + std::stringstream in(bytes, std::ios::binary | std::ios::in); + plain.load(context, in); + }) + ; + + // ciphertext.h + py::class_<Ciphertext>(m, "Ciphertext") + .def(py::init<>()) + .def(py::init<const SEALContext &>()) + .def(py::init<const SEALContext &, parms_id_type>()) + .def(py::init<const SEALContext &, parms_id_type, std::size_t>()) + .def(py::init<const Ciphertext &>()) + .def("coeff_modulus_size", &Ciphertext::coeff_modulus_size) + .def("poly_modulus_degree", &Ciphertext::poly_modulus_degree) + .def("size", &Ciphertext::size) + .def("size_capacity", &Ciphertext::size_capacity) + .def("is_transparent", &Ciphertext::is_transparent) + .def("is_ntt_form", py::overload_cast<>(&Ciphertext::is_ntt_form, py::const_)) + .def("parms_id", py::overload_cast<>(&Ciphertext::parms_id, py::const_), py::return_value_policy::reference) + .def("scale", py::overload_cast<>(&Ciphertext::scale, py::const_), py::return_value_policy::reference) + .def("scale", [](Ciphertext &cipher, double scale){ + cipher.scale() = scale; + }) + .def("save", [](const Ciphertext &cipher, const std::string &path){ + std::ofstream out(path, std::ofstream::binary); + cipher.save(out); + out.close(); + }) + .def("load", [](Ciphertext &cipher, const SEALContext &context, const std::string &path){ + std::ifstream in(path, std::ifstream::binary); + cipher.load(context, in); + in.close(); + }) + .def("save_size", [](const Ciphertext &cipher){ + return cipher.save_size(); + }) + .def("dumpb", [](const Ciphertext &cipher){ + std::stringstream out(std::ios::binary | std::ios::out); + cipher.save(out); + return py::bytes(out.str()); + }) + .def("loadb", [](Ciphertext &cipher, const SEALContext &context, const py::bytes &bytes){ + std::stringstream in(bytes, std::ios::binary | std::ios::in); + cipher.load(context, in); + }) + ; + + // secretkey.h + py::class_<SecretKey>(m, "SecretKey") + .def(py::init<>()) + .def(py::init<const SecretKey &>()) + .def("parms_id", py::overload_cast<>(&SecretKey::parms_id, py::const_), py::return_value_policy::reference) + .def("save", [](const SecretKey &sk, const std::string &path){ + std::ofstream out(path, std::ofstream::binary); + sk.save(out); + out.close(); + }) + .def("load", [](SecretKey &sk, const SEALContext &context, const std::string &path){ + std::ifstream in(path, std::ifstream::binary); + sk.load(context, in); + in.close(); + }) + .def("dumpb", [](const SecretKey &sk){ + std::stringstream out(std::ios::binary | std::ios::out); + sk.save(out); + return py::bytes(out.str()); + }) + .def("loadb", [](SecretKey &sk, const SEALContext &context, const py::bytes &bytes){ + std::stringstream in(bytes, std::ios::binary | std::ios::in); + sk.load(context, in); + }) + ; + + // publickey.h + py::class_<PublicKey>(m, "PublicKey") + .def(py::init<>()) + .def(py::init<const PublicKey &>()) + .def("parms_id", py::overload_cast<>(&PublicKey::parms_id, py::const_), py::return_value_policy::reference) + .def("save", [](const PublicKey &pk, const std::string &path){ + std::ofstream out(path, std::ofstream::binary); + pk.save(out); + out.close(); + }) + .def("load", [](PublicKey &pk, const SEALContext &context, const std::string &path){ + std::ifstream in(path, std::ifstream::binary); + pk.load(context, in); + in.close(); + }) + .def("dumpb", [](const PublicKey &pk){ + std::stringstream out(std::ios::binary | std::ios::out); + pk.save(out); + return py::bytes(out.str()); + }) + .def("loadb", [](PublicKey &pk, const SEALContext &context, const py::bytes &bytes){ + std::stringstream in(bytes, std::ios::binary | std::ios::in); + pk.load(context, in); + }) + ; + + // kswitchkeys.h + py::class_<KSwitchKeys>(m, "KSwitchKeys") + .def(py::init<>()) + .def(py::init<const KSwitchKeys &>()) + .def("size", &KSwitchKeys::size) + .def("parms_id", py::overload_cast<>(&KSwitchKeys::parms_id, py::const_), py::return_value_policy::reference) + .def("save", [](const KSwitchKeys &ksk, const std::string &path){ + std::ofstream out(path, std::ofstream::binary); + ksk.save(out); + out.close(); + }) + .def("load", [](KSwitchKeys &ksk, const SEALContext &context, const std::string &path){ + std::ifstream in(path, std::ifstream::binary); + ksk.load(context, in); + in.close(); + }) + .def("dumpb", [](const KSwitchKeys &ksk){ + std::stringstream out(std::ios::binary | std::ios::out); + ksk.save(out); + return py::bytes(out.str()); + }) + .def("loadb", [](KSwitchKeys &ksk, const SEALContext &context, const py::bytes &bytes){ + std::stringstream in(bytes, std::ios::binary | std::ios::in); + ksk.load(context, in); + }) + ; + + // relinKeys.h + py::class_<RelinKeys, KSwitchKeys>(m, "RelinKeys") + .def(py::init<>()) + .def(py::init<const RelinKeys::KSwitchKeys &>()) + .def("size", &RelinKeys::KSwitchKeys::size) + .def("parms_id", py::overload_cast<>(&RelinKeys::KSwitchKeys::parms_id, py::const_), py::return_value_policy::reference) + .def_static("get_index", &RelinKeys::get_index) + .def("has_key", &RelinKeys::has_key) + .def("save", [](const RelinKeys &rk, const std::string &path){ + std::ofstream out(path, std::ofstream::binary); + rk.save(out); + out.close(); + }) + .def("load", [](RelinKeys &rk, const SEALContext &context, const std::string &path){ + std::ifstream in(path, std::ifstream::binary); + rk.load(context, in); + in.close(); + }) + .def("dumpb", [](const RelinKeys &rk){ + std::stringstream out(std::ios::binary | std::ios::out); + rk.save(out); + return py::bytes(out.str()); + }) + .def("loadb", [](RelinKeys &rk, const SEALContext &context, const py::bytes &bytes){ + std::stringstream in(bytes, std::ios::binary | std::ios::in); + rk.load(context, in); + }) + ; + + // galoisKeys.h + py::class_<GaloisKeys, KSwitchKeys>(m, "GaloisKeys") + .def(py::init<>()) + .def(py::init<const GaloisKeys::KSwitchKeys &>()) + .def("size", &GaloisKeys::KSwitchKeys::size) + .def("parms_id", py::overload_cast<>(&GaloisKeys::KSwitchKeys::parms_id, py::const_), py::return_value_policy::reference) + .def_static("get_index", &GaloisKeys::get_index) + .def("has_key", &GaloisKeys::has_key) + .def("save", [](const GaloisKeys &gk, const std::string &path){ + std::ofstream out(path, std::ofstream::binary); + gk.save(out); + out.close(); + }) + .def("load", [](GaloisKeys &gk, const SEALContext &context, const std::string &path){ + std::ifstream in(path, std::ifstream::binary); + gk.load(context, in); + in.close(); + }) + .def("dumpb", [](const GaloisKeys &gk){ + std::stringstream out(std::ios::binary | std::ios::out); + gk.save(out); + return py::bytes(out.str()); + }) + .def("loadb", [](GaloisKeys &gk, const SEALContext &context, const py::bytes &bytes){ + std::stringstream in(bytes, std::ios::binary | std::ios::in); + gk.load(context, in); + }) + ; + + // keygenerator.h + py::class_<KeyGenerator>(m, "KeyGenerator") + .def(py::init<const SEALContext &>()) + .def(py::init<const SEALContext &, const SecretKey &>()) + .def("secret_key", &KeyGenerator::secret_key, py::return_value_policy::reference) + .def("create_public_key", py::overload_cast<PublicKey &>(&KeyGenerator::create_public_key, py::const_)) + .def("create_relin_keys", py::overload_cast<RelinKeys &>(&KeyGenerator::create_relin_keys)) + .def("create_galois_keys", py::overload_cast<const std::vector<int> &, GaloisKeys &>(&KeyGenerator::create_galois_keys)) + .def("create_galois_keys", py::overload_cast<GaloisKeys &>(&KeyGenerator::create_galois_keys)) + .def("create_public_key", [](KeyGenerator &keygen){ + PublicKey pk; + keygen.create_public_key(pk); + return pk; + }) + .def("create_relin_keys", [](KeyGenerator &keygen){ + RelinKeys rk; + keygen.create_relin_keys(rk); + return rk; + }) + .def("create_galois_keys", [](KeyGenerator &keygen){ + GaloisKeys gk; + keygen.create_galois_keys(gk); + return gk; + }); + + // encryptor.h + py::class_<Encryptor>(m, "Encryptor") + .def(py::init<const SEALContext &, const PublicKey &>()) + .def(py::init<const SEALContext &, const SecretKey &>()) + .def(py::init<const SEALContext &, const PublicKey &, const SecretKey &>()) + .def("set_public_key", &Encryptor::set_public_key) + .def("set_secret_key", &Encryptor::set_secret_key) + .def("encrypt_zero", [](const Encryptor &encryptor){ + Ciphertext encrypted; + encryptor.encrypt_zero(encrypted); + return encrypted; + }) + .def("encrypt", [](const Encryptor &encryptor, const Plaintext &plain){ + Ciphertext encrypted; + encryptor.encrypt(plain, encrypted); + return encrypted; + }); + // symmetric + + // evaluator.h + py::class_<Evaluator>(m, "Evaluator") + .def(py::init<const SEALContext &>()) + .def("negate_inplace", &Evaluator::negate_inplace) + .def("negate", [](Evaluator &evaluator, const Ciphertext &encrypted1){ + Ciphertext destination; + evaluator.negate(encrypted1, destination); + return destination; + }) + .def("add_inplace", &Evaluator::add_inplace) + .def("add", [](Evaluator &evaluator, const Ciphertext &encrypted1, const Ciphertext &encrypted2){ + Ciphertext destination; + evaluator.add(encrypted1, encrypted2, destination); + return destination; + }) + .def("add_many", [](Evaluator &evaluator, const std::vector<Ciphertext> &encrypteds){ + Ciphertext destination; + evaluator.add_many(encrypteds, destination); + return destination; + }) + .def("sub_inplace", &Evaluator::sub_inplace) + .def("sub", [](Evaluator &evaluator, const Ciphertext &encrypted1, const Ciphertext &encrypted2){ + Ciphertext destination; + evaluator.sub(encrypted1, encrypted2, destination); + return destination; + }) + .def("multiply_inplace", [](Evaluator &evaluator, Ciphertext &encrypted1, const Ciphertext &encrypted2){ + evaluator.multiply_inplace(encrypted1, encrypted2); + }) + .def("multiply", [](Evaluator &evaluator, const Ciphertext &encrypted1, const Ciphertext &encrypted2){ + Ciphertext destination; + evaluator.multiply(encrypted1, encrypted2, destination); + return destination; + }) + .def("square_inplace", [](Evaluator &evaluator, Ciphertext &encrypted1){ + evaluator.square_inplace(encrypted1); + }) + .def("square", [](Evaluator &evaluator, const Ciphertext &encrypted1){ + Ciphertext destination; + evaluator.square(encrypted1, destination); + return destination; + }) + .def("relinearize_inplace", [](Evaluator &evaluator, Ciphertext &encrypted1, const RelinKeys &relin_keys){ + evaluator.relinearize_inplace(encrypted1, relin_keys); + }) + .def("relinearize", [](Evaluator &evaluator, const Ciphertext &encrypted1, const RelinKeys &relin_keys){ + Ciphertext destination; + evaluator.relinearize(encrypted1, relin_keys, destination); + return destination; + }) + .def("mod_switch_to_next", [](Evaluator &evaluator, const Ciphertext &encrypted){ + Ciphertext destination; + evaluator.mod_switch_to_next(encrypted, destination); + return destination; + }) + .def("mod_switch_to_next_inplace", [](Evaluator &evaluator, Ciphertext &encrypted){ + evaluator.mod_switch_to_next_inplace(encrypted); + }) + .def("mod_switch_to_next_inplace", py::overload_cast<Plaintext &>(&Evaluator::mod_switch_to_next_inplace, py::const_)) + .def("mod_switch_to_next", [](Evaluator &evaluator, const Plaintext &plain){ + Plaintext destination; + evaluator.mod_switch_to_next(plain, destination); + return destination; + }) + .def("mod_switch_to_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, parms_id_type parms_id){ + evaluator.mod_switch_to_inplace(encrypted, parms_id); + }) + .def("mod_switch_to", [](Evaluator &evaluator, const Ciphertext &encrypted, parms_id_type parms_id){ + Ciphertext destination; + evaluator.mod_switch_to(encrypted, parms_id, destination); + return destination; + }) + .def("mod_switch_to_inplace", py::overload_cast<Plaintext &, parms_id_type>(&Evaluator::mod_switch_to_inplace, py::const_)) + .def("mod_switch_to", [](Evaluator &evaluator, const Plaintext &plain, parms_id_type parms_id){ + Plaintext destination; + evaluator.mod_switch_to(plain, parms_id, destination); + return destination; + }) + .def("rescale_to_next", [](Evaluator &evaluator, const Ciphertext &encrypted){ + Ciphertext destination; + evaluator.rescale_to_next(encrypted, destination); + return destination; + }) + .def("rescale_to_next_inplace", [](Evaluator &evaluator, Ciphertext &encrypted){ + evaluator.rescale_to_next_inplace(encrypted); + }) + .def("rescale_to_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, parms_id_type parms_id){ + evaluator.rescale_to_inplace(encrypted, parms_id); + }) + .def("rescale_to", [](Evaluator &evaluator, const Ciphertext &encrypted, parms_id_type parms_id){ + Ciphertext destination; + evaluator.rescale_to(encrypted, parms_id, destination); + return destination; + }) + .def("multiply_many", [](Evaluator &evaluator, const std::vector<Ciphertext> &encrypteds, const RelinKeys &relin_keys){ + Ciphertext destination; + evaluator.multiply_many(encrypteds, relin_keys, destination); + return destination; + }) + .def("exponentiate_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, std::uint64_t exponent, const RelinKeys &relin_keys){ + evaluator.exponentiate_inplace(encrypted, exponent, relin_keys); + }) + .def("exponentiate", [](Evaluator &evaluator, const Ciphertext &encrypted, std::uint64_t exponent, const RelinKeys &relin_keys){ + Ciphertext destination; + evaluator.exponentiate(encrypted, exponent, relin_keys, destination); + return destination; + }) + .def("add_plain_inplace", &Evaluator::add_plain_inplace) + .def("add_plain", [](Evaluator &evaluator, const Ciphertext &encrypted, const Plaintext &plain){ + Ciphertext destination; + evaluator.add_plain(encrypted, plain, destination); + return destination; + }) + .def("sub_plain_inplace", &Evaluator::sub_plain_inplace) + .def("sub_plain", [](Evaluator &evaluator, const Ciphertext &encrypted, const Plaintext &plain){ + Ciphertext destination; + evaluator.sub_plain(encrypted, plain, destination); + return destination; + }) + .def("multiply_plain_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, const Plaintext &plain){ + evaluator.multiply_plain_inplace(encrypted, plain); + }) + .def("multiply_plain", [](Evaluator &evaluator, const Ciphertext &encrypted, const Plaintext &plain){ + Ciphertext destination; + evaluator.multiply_plain(encrypted, plain, destination); + return destination; + }) + .def("transform_to_ntt_inplace", [](Evaluator &evaluator, Plaintext &plain, parms_id_type parms_id){ + evaluator.transform_to_ntt_inplace(plain,parms_id); + }) + .def("transform_to_ntt", [](Evaluator &evaluator, const Plaintext &plain, parms_id_type parms_id){ + Plaintext destination_ntt; + evaluator.transform_to_ntt(plain, parms_id, destination_ntt); + return destination_ntt; + }) + .def("transform_to_ntt_inplace", py::overload_cast<Ciphertext &>(&Evaluator::transform_to_ntt_inplace, py::const_)) + .def("transform_to_ntt", [](Evaluator &evaluator, const Ciphertext &encrypted){ + Ciphertext destination_ntt; + evaluator.transform_to_ntt(encrypted, destination_ntt); + return destination_ntt; + }) + .def("transform_from_ntt_inplace", &Evaluator::transform_from_ntt_inplace) + .def("transform_from_ntt", [](Evaluator &evaluator, const Ciphertext &encrypted_ntt){ + Ciphertext destination; + evaluator.transform_from_ntt(encrypted_ntt, destination); + return destination; + }) + .def("apply_galois_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, std::uint32_t galois_elt, const GaloisKeys &galois_keys){ + evaluator.apply_galois_inplace(encrypted, galois_elt, galois_keys); + }) + .def("apply_galois", [](Evaluator &evaluator, const Ciphertext &encrypted, std::uint32_t galois_elt, const GaloisKeys &galois_keys){ + Ciphertext destination; + evaluator.apply_galois(encrypted, galois_elt, galois_keys, destination); + return destination; + }) + .def("rotate_rows_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ + evaluator.rotate_rows_inplace(encrypted, steps, galois_keys); + }) + .def("rotate_rows", [](Evaluator &evaluator, const Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ + Ciphertext destination; + evaluator.rotate_rows(encrypted, steps, galois_keys, destination); + return destination; + }) + .def("rotate_columns_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, const GaloisKeys &galois_keys){ + evaluator.rotate_columns_inplace(encrypted, galois_keys); + }) + .def("rotate_columns", [](Evaluator &evaluator, const Ciphertext &encrypted, const GaloisKeys &galois_keys){ + Ciphertext destination; + evaluator.rotate_columns(encrypted, galois_keys, destination); + return destination; + }) + .def("rotate_vector_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ + evaluator.rotate_vector_inplace(encrypted, steps, galois_keys); + }) + .def("rotate_vector", [](Evaluator &evaluator, const Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ + Ciphertext destination; + evaluator.rotate_vector(encrypted, steps, galois_keys, destination); + return destination; + }) + .def("complex_conjugate_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, const GaloisKeys &galois_keys){ + evaluator.complex_conjugate_inplace(encrypted, galois_keys); + }) + .def("complex_conjugate", [](Evaluator &evaluator, const Ciphertext &encrypted, const GaloisKeys &galois_keys){ + Ciphertext destination; + evaluator.complex_conjugate(encrypted, galois_keys, destination); + return destination; + }); + + // ckks.h + py::class_<CKKSEncoder>(m, "CKKSEncoder") + .def(py::init<const SEALContext &>()) + .def("slot_count", &CKKSEncoder::slot_count) + .def("encode", [](CKKSEncoder &encoder, py::array_t<double> values, double scale){ + py::buffer_info buf = values.request(); + if (buf.ndim != 1) + throw std::runtime_error("E101: Number of dimensions must be one"); + + double *ptr = (double *)buf.ptr; + std::vector<double> vec(buf.shape[0]); + + for (auto i = 0; i < buf.shape[0]; i++) + vec[i] = ptr[i]; + + Plaintext pt; + encoder.encode(vec, scale, pt); + return pt; + }) + .def("encode", [](CKKSEncoder &encoder, double value, double scale){ + Plaintext pt; + encoder.encode(value, scale, pt); + return pt; + }) + .def("decode", [](CKKSEncoder &encoder, const Plaintext &plain){ + std::vector<double> destination; + encoder.decode(plain, destination); + + py::array_t<double> values(destination.size()); + py::buffer_info buf = values.request(); + double *ptr = (double *)buf.ptr; + + for (auto i = 0; i < buf.shape[0]; i++) + ptr[i] = destination[i]; + + return values; + }); + + // decryptor.h + py::class_<Decryptor>(m, "Decryptor") + .def(py::init<const SEALContext &, const SecretKey &>()) + .def("decrypt", &Decryptor::decrypt) + .def("invariant_noise_budget", &Decryptor::invariant_noise_budget) + .def("decrypt", [](Decryptor &decryptor, const Ciphertext &encrypted){ + Plaintext pt; + decryptor.decrypt(encrypted, pt); + return pt; + }); + + // batchencoder.h + py::class_<BatchEncoder>(m, "BatchEncoder") + .def(py::init<const SEALContext &>()) + .def("slot_count", &BatchEncoder::slot_count) + .def("encode", [](BatchEncoder &encoder, py::array_t<std::int64_t> values){ + py::buffer_info buf = values.request(); + if (buf.ndim != 1) + throw std::runtime_error("E101: Number of dimensions must be one"); + + std::int64_t *ptr = (std::int64_t *)buf.ptr; + std::vector<std::int64_t> vec(buf.shape[0]); + + for (auto i = 0; i < buf.shape[0]; i++) + vec[i] = ptr[i]; + + Plaintext pt; + encoder.encode(vec, pt); + return pt; + }) + .def("decode", [](BatchEncoder &encoder, const Plaintext &plain){ + std::vector<std::int64_t> destination; + encoder.decode(plain, destination); + + py::array_t<std::int64_t> values(destination.size()); + py::buffer_info buf = values.request(); + std::int64_t *ptr = (std::int64_t *)buf.ptr; + + for (auto i = 0; i < buf.shape[0]; i++) + ptr[i] = destination[i]; + + return values; + }); } diff --git a/src/wrapper_with_pickle.cpp b/src/wrapper_with_pickle.cpp new file mode 100644 index 0000000..0c57e2f --- /dev/null +++ b/src/wrapper_with_pickle.cpp @@ -0,0 +1,703 @@ +#include <pybind11/pybind11.h> +#include <pybind11/stl_bind.h> +#include <pybind11/numpy.h> +#include <pybind11/stl.h> +#include "seal/seal.h" +#include "base64.h" +#include <iostream> +#include <fstream> +#include <type_traits> + +using namespace seal; + +namespace py = pybind11; + +PYBIND11_MAKE_OPAQUE(std::vector<double>); +PYBIND11_MAKE_OPAQUE(std::vector<std::int64_t>); + +template <typename T, typename = void> +struct HasParms : std::false_type { }; + +template <typename T> +struct HasParms <T, decltype((void)T::parms, void())> : std::true_type { }; + +template <class T> +py::tuple serialize(T &c) +{ + if (!HasParms<T>::value) + throw std::runtime_error("E001: Invalid state! set parms first."); + + // EncryptionParameters + std::stringstream out_stream1(std::ios::binary | std::ios::out); + // if parms exists + c.parms.save(out_stream1); + std::string str_buf1 = out_stream1.str(); + std::string encoded_str1 = base64_encode(reinterpret_cast<const unsigned char *>(str_buf1.c_str()), (unsigned int)str_buf1.length()); + + // T + std::stringstream out_stream2(std::ios::binary | std::ios::out); + c.save(out_stream2); + std::string str_buf2 = out_stream2.str(); + std::string encoded_str2 = base64_encode(reinterpret_cast<const unsigned char *>(str_buf2.c_str()), (unsigned int)str_buf2.length()); + + return py::make_tuple(encoded_str1, encoded_str2); +} + +template <class T> +T deserialize(py::tuple t) +{ + if (t.size() != 2) + throw std::runtime_error("E002: Invalid state!"); + + // EncryptionParameters + std::string encoded_str1 = t[0].cast<std::string>(); + std::string decoded_str1 = base64_decode(encoded_str1); + std::stringstream in_stream1(std::ios::binary | std::ios::in); + in_stream1.str(decoded_str1); + EncryptionParameters parms; + parms.load(in_stream1); + SEALContext context(parms); + + // T + std::string encoded_str2 = t[1].cast<std::string>(); + std::string decoded_str2 = base64_decode(encoded_str2); + std::stringstream in_stream2(std::ios::binary | std::ios::in); + in_stream2.str(decoded_str2); + T c; + c.load(context, in_stream2); + + return c; +} + +PYBIND11_MODULE(seal, m) +{ + m.doc() = "Microsoft SEAL (3.6) for Python, from https://github.com/Huelse/SEAL-Python"; + + py::bind_vector<std::vector<double>>(m, "VectorDouble", py::buffer_protocol()); + py::bind_vector<std::vector<std::int64_t>>(m, "VectorInt", py::buffer_protocol()); + + // encryptionparams.h + py::enum_<scheme_type>(m, "scheme_type") + .value("none", scheme_type::none) + .value("bfv", scheme_type::bfv) + .value("ckks", scheme_type::ckks); + + // encryptionparams.h + py::class_<EncryptionParameters>(m, "EncryptionParameters") + .def(py::init<scheme_type>()) + .def(py::init<EncryptionParameters>()) + .def("set_poly_modulus_degree", &EncryptionParameters::set_poly_modulus_degree) + .def("set_coeff_modulus", &EncryptionParameters::set_coeff_modulus) + .def("set_plain_modulus", py::overload_cast<const Modulus &>(&EncryptionParameters::set_plain_modulus)) + .def("set_plain_modulus", py::overload_cast<std::uint64_t>(&EncryptionParameters::set_plain_modulus)) + .def("scheme", &EncryptionParameters::scheme) + .def("poly_modulus_degree", &EncryptionParameters::poly_modulus_degree) + .def("coeff_modulus", &EncryptionParameters::coeff_modulus) + .def("plain_modulus", &EncryptionParameters::plain_modulus) + .def("save", [](const EncryptionParameters &parms, std::string &path){ + std::ofstream out(path, std::ofstream::binary); + parms.save(out); + out.close(); + }) + .def("load", [](EncryptionParameters &parms, std::string &path){ + std::ifstream in(path, std::ifstream::binary); + parms.load(in); + in.close(); + }) + .def(py::pickle( + [](const EncryptionParameters &parms){ + std::stringstream out_stream(std::ios::binary | std::ios::out); + parms.save(out_stream); + std::string str_buf = out_stream.str(); + std::string encoded_str = base64_encode(reinterpret_cast<const unsigned char *>(str_buf.c_str()), (unsigned int)str_buf.length()); + return py::make_tuple(encoded_str); + }, + [](py::tuple t){ + if (t.size() != 1) + throw std::runtime_error("E002: Invalid state!"); + + std::string encoded_str = t[0].cast<std::string>(); + std::string decoded_str = base64_decode(encoded_str); + std::stringstream in_stream(std::ios::binary | std::ios::in); + in_stream.str(decoded_str); + EncryptionParameters parms; + parms.load(in_stream); + return parms; + } + )); + + // modulus.h + py::enum_<sec_level_type>(m, "sec_level_type") + .value("none", sec_level_type::none) + .value("tc128", sec_level_type::tc128) + .value("tc192", sec_level_type::tc192) + .value("tc256", sec_level_type::tc256); + + // context.h + py::enum_<EncryptionParameterQualifiers::error_type>(m, "error_type") + .value("none", EncryptionParameterQualifiers::error_type::none) + .value("success", EncryptionParameterQualifiers::error_type::success) + .value("invalid_scheme", EncryptionParameterQualifiers::error_type::invalid_scheme) + .value("invalid_coeff_modulus_size", EncryptionParameterQualifiers::error_type::invalid_coeff_modulus_size) + .value("invalid_coeff_modulus_bit_count", EncryptionParameterQualifiers::error_type::invalid_coeff_modulus_bit_count) + .value("invalid_coeff_modulus_no_ntt", EncryptionParameterQualifiers::error_type::invalid_coeff_modulus_no_ntt) + .value("invalid_poly_modulus_degree", EncryptionParameterQualifiers::error_type::invalid_poly_modulus_degree) + .value("invalid_poly_modulus_degree_non_power_of_two", EncryptionParameterQualifiers::error_type::invalid_poly_modulus_degree_non_power_of_two) + .value("invalid_parameters_too_large", EncryptionParameterQualifiers::error_type::invalid_parameters_too_large) + .value("invalid_parameters_insecure", EncryptionParameterQualifiers::error_type::invalid_parameters_insecure) + .value("failed_creating_rns_base", EncryptionParameterQualifiers::error_type::failed_creating_rns_base) + .value("invalid_plain_modulus_bit_count", EncryptionParameterQualifiers::error_type::invalid_plain_modulus_bit_count) + .value("invalid_plain_modulus_coprimality", EncryptionParameterQualifiers::error_type::invalid_plain_modulus_coprimality) + .value("invalid_plain_modulus_too_large", EncryptionParameterQualifiers::error_type::invalid_plain_modulus_too_large) + .value("invalid_plain_modulus_nonzero", EncryptionParameterQualifiers::error_type::invalid_plain_modulus_nonzero) + .value("failed_creating_rns_tool", EncryptionParameterQualifiers::error_type::failed_creating_rns_tool); + + // context.h + py::class_<EncryptionParameterQualifiers, std::unique_ptr<EncryptionParameterQualifiers, py::nodelete>>(m, "EncryptionParameterQualifiers") + .def("parameters_set", &EncryptionParameterQualifiers::parameters_set) + .def_readwrite("using_fft", &EncryptionParameterQualifiers::using_fft) + .def_readwrite("using_ntt", &EncryptionParameterQualifiers::using_ntt) + .def_readwrite("using_batching", &EncryptionParameterQualifiers::using_batching) + .def_readwrite("using_fast_plain_lift", &EncryptionParameterQualifiers::using_fast_plain_lift) + .def_readwrite("using_descending_modulus_chain", &EncryptionParameterQualifiers::using_descending_modulus_chain) + .def_readwrite("sec_level", &EncryptionParameterQualifiers::sec_level); + + // context.h + py::class_<SEALContext::ContextData, std::shared_ptr<SEALContext::ContextData>>(m, "ContextData") + .def("parms", &SEALContext::ContextData::parms) + .def("parms_id", &SEALContext::ContextData::parms_id) + .def("qualifiers", &SEALContext::ContextData::qualifiers) + .def("total_coeff_modulus", &SEALContext::ContextData::total_coeff_modulus) + .def("total_coeff_modulus_bit_count", &SEALContext::ContextData::total_coeff_modulus_bit_count) + .def("next_context_data", &SEALContext::ContextData::next_context_data) + .def("chain_index", &SEALContext::ContextData::chain_index); + + // context.h + py::class_<SEALContext, std::shared_ptr<SEALContext>>(m, "SEALContext") + .def(py::init<const EncryptionParameters &, bool, sec_level_type>(), py::arg(), py::arg()=true, py::arg()=sec_level_type::tc128) + .def("get_context_data", &SEALContext::get_context_data) + .def("key_context_data", &SEALContext::key_context_data) + .def("first_context_data", &SEALContext::first_context_data) + .def("last_context_data", &SEALContext::last_context_data) + .def("parameters_set", &SEALContext::parameters_set) + .def("first_parms_id", &SEALContext::first_parms_id) + .def("last_parms_id", &SEALContext::last_parms_id) + .def("using_keyswitching", &SEALContext::using_keyswitching); + + // modulus.h + py::class_<Modulus>(m, "Modulus") + .def(py::init<std::uint64_t>()) + .def("bit_count", &Modulus::bit_count) + .def("value", &Modulus::value) + .def("is_zero", &Modulus::is_zero) + .def("is_prime", &Modulus::is_prime); + //save & load + + // modulus.h + py::class_<CoeffModulus>(m, "CoeffModulus") + .def_static("MaxBitCount", &CoeffModulus::MaxBitCount, py::arg(), py::arg()=sec_level_type::tc128) + .def_static("BFVDefault", &CoeffModulus::BFVDefault, py::arg(), py::arg()=sec_level_type::tc128) + .def_static("Create", &CoeffModulus::Create); + + // modulus.h + py::class_<PlainModulus>(m, "PlainModulus") + .def_static("Batching", py::overload_cast<std::size_t, int>(&PlainModulus::Batching)) + .def_static("Batching", py::overload_cast<std::size_t, std::vector<int>>(&PlainModulus::Batching)); + + // plaintext.h + py::class_<Plaintext>(m, "Plaintext") + .def(py::init<>()) + .def(py::init<std::size_t>()) + .def(py::init<std::size_t, std::size_t>()) + .def(py::init<const std::string &>()) + .def(py::init<const Plaintext &>()) + .def("set_zero", py::overload_cast<std::size_t, std::size_t>(&Plaintext::set_zero)) + .def("set_zero", py::overload_cast<std::size_t>(&Plaintext::set_zero)) + .def("set_zero", py::overload_cast<>(&Plaintext::set_zero)) + .def("is_zero", &Plaintext::is_zero) + .def("capacity", &Plaintext::capacity) + .def("coeff_count", &Plaintext::coeff_count) + .def("significant_coeff_count", &Plaintext::significant_coeff_count) + .def("nonzero_coeff_count", &Plaintext::nonzero_coeff_count) + .def("to_string", &Plaintext::to_string) + .def("is_ntt_form", &Plaintext::is_ntt_form) + .def("parms_id", py::overload_cast<>(&Plaintext::parms_id, py::const_), py::return_value_policy::reference) + .def("scale", py::overload_cast<>(&Plaintext::scale, py::const_), py::return_value_policy::reference) + .def("scale", [](Plaintext &plain, double scale){ + plain.scale() = scale; + }) + .def("save", [](const Plaintext &plain, const std::string &path){ + std::ofstream out(path, std::ofstream::binary); + plain.save(out); + out.close(); + }) + .def("load", [](Plaintext &plain, const SEALContext &context, const std::string &path){ + std::ifstream in(path, std::ifstream::binary); + plain.load(context, in); + in.close(); + }) + .def("set_parms", [](Plaintext &plain, const EncryptionParameters &parms){ + plain.parms = parms; + }) + .def("save_size", [](const Plaintext &plain){ + return plain.save_size(); + }) + .def(py::pickle(&serialize<Plaintext>, &deserialize<Plaintext>)); + + // ciphertext.h + py::class_<Ciphertext>(m, "Ciphertext") + .def(py::init<>()) + .def(py::init<const SEALContext &>()) + .def(py::init<const SEALContext &, parms_id_type>()) + .def(py::init<const SEALContext &, parms_id_type, std::size_t>()) + .def(py::init<const Ciphertext &>()) + .def("coeff_modulus_size", &Ciphertext::coeff_modulus_size) + .def("poly_modulus_degree", &Ciphertext::poly_modulus_degree) + .def("size", &Ciphertext::size) + .def("size_capacity", &Ciphertext::size_capacity) + .def("is_transparent", &Ciphertext::is_transparent) + .def("is_ntt_form", py::overload_cast<>(&Ciphertext::is_ntt_form, py::const_)) + .def("parms_id", py::overload_cast<>(&Ciphertext::parms_id, py::const_), py::return_value_policy::reference) + .def("scale", py::overload_cast<>(&Ciphertext::scale, py::const_), py::return_value_policy::reference) + .def("scale", [](Ciphertext &cipher, double scale){ + cipher.scale() = scale; + }) + .def("save", [](const Ciphertext &cipher, const std::string &path){ + std::ofstream out(path, std::ofstream::binary); + cipher.save(out); + out.close(); + }) + .def("load", [](Ciphertext &cipher, const SEALContext &context, const std::string &path){ + std::ifstream in(path, std::ifstream::binary); + cipher.load(context, in); + in.close(); + }) + .def("set_parms", [](Ciphertext &cipher, const EncryptionParameters &parms){ + cipher.parms = parms; + }) + .def("save_size", [](const Ciphertext &cipher){ + return cipher.save_size(); + }) + .def(py::pickle(&serialize<Ciphertext>, &deserialize<Ciphertext>)); + + // secretkey.h + py::class_<SecretKey>(m, "SecretKey") + .def(py::init<>()) + .def(py::init<const SecretKey &>()) + .def("parms_id", py::overload_cast<>(&SecretKey::parms_id, py::const_), py::return_value_policy::reference) + .def("save", [](const SecretKey &sk, const std::string &path){ + std::ofstream out(path, std::ofstream::binary); + sk.save(out); + out.close(); + }) + .def("load", [](SecretKey &sk, const SEALContext &context, const std::string &path){ + std::ifstream in(path, std::ifstream::binary); + sk.load(context, in); + in.close(); + }) + .def("set_parms", [](SecretKey &sk, const EncryptionParameters &parms){ + sk.parms = parms; + }) + .def(py::pickle(&serialize<SecretKey>, &deserialize<SecretKey>)); + + // publickey.h + py::class_<PublicKey>(m, "PublicKey") + .def(py::init<>()) + .def(py::init<const PublicKey &>()) + .def("parms_id", py::overload_cast<>(&PublicKey::parms_id, py::const_), py::return_value_policy::reference) + .def("save", [](const PublicKey &pk, const std::string &path){ + std::ofstream out(path, std::ofstream::binary); + pk.save(out); + out.close(); + }) + .def("load", [](PublicKey &pk, const SEALContext &context, const std::string &path){ + std::ifstream in(path, std::ifstream::binary); + pk.load(context, in); + in.close(); + }) + .def("set_parms", [](PublicKey &pk, const EncryptionParameters &parms){ + pk.parms = parms; + }) + .def(py::pickle(&serialize<PublicKey>, &deserialize<PublicKey>)); + + // kswitchkeys.h + py::class_<KSwitchKeys>(m, "KSwitchKeys") + .def(py::init<>()) + .def(py::init<const KSwitchKeys &>()) + .def("size", &KSwitchKeys::size) + .def("parms_id", py::overload_cast<>(&KSwitchKeys::parms_id, py::const_), py::return_value_policy::reference) + .def("save", [](const KSwitchKeys &ksk, const std::string &path){ + std::ofstream out(path, std::ofstream::binary); + ksk.save(out); + out.close(); + }) + .def("load", [](KSwitchKeys &ksk, const SEALContext &context, const std::string &path){ + std::ifstream in(path, std::ifstream::binary); + ksk.load(context, in); + in.close(); + }) + .def("set_parms", [](KSwitchKeys &ksk, const EncryptionParameters &parms){ + ksk.parms = parms; + }) + .def(py::pickle(&serialize<KSwitchKeys>, &deserialize<KSwitchKeys>)); + + // relinKeys.h + py::class_<RelinKeys, KSwitchKeys>(m, "RelinKeys") + .def(py::init<>()) + .def(py::init<const RelinKeys::KSwitchKeys &>()) + .def("size", &RelinKeys::KSwitchKeys::size) + .def("parms_id", py::overload_cast<>(&RelinKeys::KSwitchKeys::parms_id, py::const_), py::return_value_policy::reference) + .def("save", [](const RelinKeys &rk, const std::string &path){ + std::ofstream out(path, std::ofstream::binary); + rk.save(out); + out.close(); + }) + .def("load", [](RelinKeys &rk, const SEALContext &context, const std::string &path){ + std::ifstream in(path, std::ifstream::binary); + rk.load(context, in); + in.close(); + }) + .def_static("get_index", &RelinKeys::get_index) + .def("has_key", &RelinKeys::has_key) + .def("set_parms", [](RelinKeys &rk, const EncryptionParameters &parms){ + rk.parms = parms; + }) + .def(py::pickle(&serialize<RelinKeys>, &deserialize<RelinKeys>)); + + // galoisKeys.h + py::class_<GaloisKeys, KSwitchKeys>(m, "GaloisKeys") + .def(py::init<>()) + .def(py::init<const GaloisKeys::KSwitchKeys &>()) + .def("size", &GaloisKeys::KSwitchKeys::size) + .def("parms_id", py::overload_cast<>(&GaloisKeys::KSwitchKeys::parms_id, py::const_), py::return_value_policy::reference) + .def("save", [](const GaloisKeys &gk, const std::string &path){ + std::ofstream out(path, std::ofstream::binary); + gk.save(out); + out.close(); + }) + .def("load", [](GaloisKeys &gk, const SEALContext &context, const std::string &path){ + std::ifstream in(path, std::ifstream::binary); + gk.load(context, in); + in.close(); + }) + .def_static("get_index", &GaloisKeys::get_index) + .def("has_key", &GaloisKeys::has_key) + .def("set_parms", [](GaloisKeys &gk, const EncryptionParameters &parms){ + gk.parms = parms; + }) + .def(py::pickle(&serialize<GaloisKeys>, &deserialize<GaloisKeys>)); + + // keygenerator.h + py::class_<KeyGenerator>(m, "KeyGenerator") + .def(py::init<const SEALContext &>()) + .def(py::init<const SEALContext &, const SecretKey &>()) + .def("secret_key", &KeyGenerator::secret_key, py::return_value_policy::reference) + .def("create_public_key", py::overload_cast<PublicKey &>(&KeyGenerator::create_public_key, py::const_)) + .def("create_relin_keys", py::overload_cast<RelinKeys &>(&KeyGenerator::create_relin_keys)) + .def("create_galois_keys", py::overload_cast<const std::vector<int> &, GaloisKeys &>(&KeyGenerator::create_galois_keys)) + .def("create_galois_keys", py::overload_cast<GaloisKeys &>(&KeyGenerator::create_galois_keys)) + .def("create_public_key", [](KeyGenerator &keygen){ + PublicKey pk; + keygen.create_public_key(pk); + return pk; + }) + .def("create_relin_keys", [](KeyGenerator &keygen){ + RelinKeys rk; + keygen.create_relin_keys(rk); + return rk; + }) + .def("create_galois_keys", [](KeyGenerator &keygen){ + GaloisKeys gk; + keygen.create_galois_keys(gk); + return gk; + }); + + // encryptor.h + py::class_<Encryptor>(m, "Encryptor") + .def(py::init<const SEALContext &, const PublicKey &>()) + .def(py::init<const SEALContext &, const SecretKey &>()) + .def(py::init<const SEALContext &, const PublicKey &, const SecretKey &>()) + .def("set_public_key", &Encryptor::set_public_key) + .def("set_secret_key", &Encryptor::set_secret_key) + .def("encrypt_zero", [](const Encryptor &encryptor){ + Ciphertext encrypted; + encryptor.encrypt_zero(encrypted); + return encrypted; + }) + .def("encrypt", [](const Encryptor &encryptor, const Plaintext &plain){ + Ciphertext encrypted; + encryptor.encrypt(plain, encrypted); + return encrypted; + }); + // symmetric + + // evaluator.h + py::class_<Evaluator>(m, "Evaluator") + .def(py::init<const SEALContext &>()) + .def("negate_inplace", &Evaluator::negate_inplace) + .def("negate", [](Evaluator &evaluator, const Ciphertext &encrypted1){ + Ciphertext destination; + evaluator.negate(encrypted1, destination); + return destination; + }) + .def("add_inplace", &Evaluator::add_inplace) + .def("add", [](Evaluator &evaluator, const Ciphertext &encrypted1, const Ciphertext &encrypted2){ + Ciphertext destination; + evaluator.add(encrypted1, encrypted2, destination); + return destination; + }) + .def("add_many", [](Evaluator &evaluator, const std::vector<Ciphertext> &encrypteds){ + Ciphertext destination; + evaluator.add_many(encrypteds, destination); + return destination; + }) + .def("sub_inplace", &Evaluator::sub_inplace) + .def("sub", [](Evaluator &evaluator, const Ciphertext &encrypted1, const Ciphertext &encrypted2){ + Ciphertext destination; + evaluator.sub(encrypted1, encrypted2, destination); + return destination; + }) + .def("multiply_inplace", [](Evaluator &evaluator, Ciphertext &encrypted1, const Ciphertext &encrypted2){ + evaluator.multiply_inplace(encrypted1, encrypted2); + }) + .def("multiply", [](Evaluator &evaluator, const Ciphertext &encrypted1, const Ciphertext &encrypted2){ + Ciphertext destination; + evaluator.multiply(encrypted1, encrypted2, destination); + return destination; + }) + .def("square_inplace", [](Evaluator &evaluator, Ciphertext &encrypted1){ + evaluator.square_inplace(encrypted1); + }) + .def("square", [](Evaluator &evaluator, const Ciphertext &encrypted1){ + Ciphertext destination; + evaluator.square(encrypted1, destination); + return destination; + }) + .def("relinearize_inplace", [](Evaluator &evaluator, Ciphertext &encrypted1, const RelinKeys &relin_keys){ + evaluator.relinearize_inplace(encrypted1, relin_keys); + }) + .def("relinearize", [](Evaluator &evaluator, const Ciphertext &encrypted1, const RelinKeys &relin_keys){ + Ciphertext destination; + evaluator.relinearize(encrypted1, relin_keys, destination); + return destination; + }) + .def("mod_switch_to_next", [](Evaluator &evaluator, const Ciphertext &encrypted){ + Ciphertext destination; + evaluator.mod_switch_to_next(encrypted, destination); + return destination; + }) + .def("mod_switch_to_next_inplace", [](Evaluator &evaluator, Ciphertext &encrypted){ + evaluator.mod_switch_to_next_inplace(encrypted); + }) + .def("mod_switch_to_next_inplace", py::overload_cast<Plaintext &>(&Evaluator::mod_switch_to_next_inplace)) + .def("mod_switch_to_next", [](Evaluator &evaluator, const Plaintext &plain){ + Plaintext destination; + evaluator.mod_switch_to_next(plain, destination); + return destination; + }) + .def("mod_switch_to_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, parms_id_type parms_id){ + evaluator.mod_switch_to_inplace(encrypted, parms_id); + }) + .def("mod_switch_to", [](Evaluator &evaluator, const Ciphertext &encrypted, parms_id_type parms_id){ + Ciphertext destination; + evaluator.mod_switch_to(encrypted, parms_id, destination); + return destination; + }) + .def("mod_switch_to_inplace", py::overload_cast<Plaintext &, parms_id_type>(&Evaluator::mod_switch_to_inplace)) + .def("mod_switch_to", [](Evaluator &evaluator, const Plaintext &plain, parms_id_type parms_id){ + Plaintext destination; + evaluator.mod_switch_to(plain, parms_id, destination); + return destination; + }) + .def("rescale_to_next", [](Evaluator &evaluator, const Ciphertext &encrypted){ + Ciphertext destination; + evaluator.rescale_to_next(encrypted, destination); + return destination; + }) + .def("rescale_to_next_inplace", [](Evaluator &evaluator, Ciphertext &encrypted){ + evaluator.rescale_to_next_inplace(encrypted); + }) + .def("rescale_to_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, parms_id_type parms_id){ + evaluator.rescale_to_inplace(encrypted, parms_id); + }) + .def("rescale_to", [](Evaluator &evaluator, const Ciphertext &encrypted, parms_id_type parms_id){ + Ciphertext destination; + evaluator.rescale_to(encrypted, parms_id, destination); + return destination; + }) + .def("multiply_many", [](Evaluator &evaluator, const std::vector<Ciphertext> &encrypteds, const RelinKeys &relin_keys){ + Ciphertext destination; + evaluator.multiply_many(encrypteds, relin_keys, destination); + return destination; + }) + .def("exponentiate_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, std::uint64_t exponent, const RelinKeys &relin_keys){ + evaluator.exponentiate_inplace(encrypted, exponent, relin_keys); + }) + .def("exponentiate", [](Evaluator &evaluator, const Ciphertext &encrypted, std::uint64_t exponent, const RelinKeys &relin_keys){ + Ciphertext destination; + evaluator.exponentiate(encrypted, exponent, relin_keys, destination); + return destination; + }) + .def("add_plain_inplace", &Evaluator::add_plain_inplace) + .def("add_plain", [](Evaluator &evaluator, const Ciphertext &encrypted, const Plaintext &plain){ + Ciphertext destination; + evaluator.add_plain(encrypted, plain, destination); + return destination; + }) + .def("sub_plain_inplace", &Evaluator::sub_plain_inplace) + .def("sub_plain", [](Evaluator &evaluator, const Ciphertext &encrypted, const Plaintext &plain){ + Ciphertext destination; + evaluator.sub_plain(encrypted, plain, destination); + return destination; + }) + .def("multiply_plain_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, const Plaintext &plain){ + evaluator.multiply_plain_inplace(encrypted, plain); + }) + .def("multiply_plain", [](Evaluator &evaluator, const Ciphertext &encrypted, const Plaintext &plain){ + Ciphertext destination; + evaluator.multiply_plain(encrypted, plain, destination); + return destination; + }) + .def("transform_to_ntt_inplace", [](Evaluator &evaluator, Plaintext &plain, parms_id_type parms_id){ + evaluator.transform_to_ntt_inplace(plain,parms_id); + }) + .def("transform_to_ntt", [](Evaluator &evaluator, const Plaintext &plain, parms_id_type parms_id){ + Plaintext destination_ntt; + evaluator.transform_to_ntt(plain, parms_id, destination_ntt); + return destination_ntt; + }) + .def("transform_to_ntt_inplace", py::overload_cast<Ciphertext &>(&Evaluator::transform_to_ntt_inplace)) + .def("transform_to_ntt", [](Evaluator &evaluator, const Ciphertext &encrypted){ + Ciphertext destination_ntt; + evaluator.transform_to_ntt(encrypted, destination_ntt); + return destination_ntt; + }) + .def("transform_from_ntt_inplace", &Evaluator::transform_from_ntt_inplace) + .def("transform_from_ntt", [](Evaluator &evaluator, const Ciphertext &encrypted_ntt){ + Ciphertext destination; + evaluator.transform_from_ntt(encrypted_ntt, destination); + return destination; + }) + .def("apply_galois_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, std::uint32_t galois_elt, const GaloisKeys &galois_keys){ + evaluator.apply_galois_inplace(encrypted, galois_elt, galois_keys); + }) + .def("apply_galois", [](Evaluator &evaluator, const Ciphertext &encrypted, std::uint32_t galois_elt, const GaloisKeys &galois_keys){ + Ciphertext destination; + evaluator.apply_galois(encrypted, galois_elt, galois_keys, destination); + return destination; + }) + .def("rotate_rows_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ + evaluator.rotate_rows_inplace(encrypted, steps, galois_keys); + }) + .def("rotate_rows", [](Evaluator &evaluator, const Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ + Ciphertext destination; + evaluator.rotate_rows(encrypted, steps, galois_keys, destination); + return destination; + }) + .def("rotate_columns_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, const GaloisKeys &galois_keys){ + evaluator.rotate_columns_inplace(encrypted, galois_keys); + }) + .def("rotate_columns", [](Evaluator &evaluator, const Ciphertext &encrypted, const GaloisKeys &galois_keys){ + Ciphertext destination; + evaluator.rotate_columns(encrypted, galois_keys, destination); + return destination; + }) + .def("rotate_vector_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ + evaluator.rotate_vector_inplace(encrypted, steps, galois_keys); + }) + .def("rotate_vector", [](Evaluator &evaluator, const Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ + Ciphertext destination; + evaluator.rotate_vector(encrypted, steps, galois_keys, destination); + return destination; + }) + .def("complex_conjugate_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, const GaloisKeys &galois_keys){ + evaluator.complex_conjugate_inplace(encrypted, galois_keys); + }) + .def("complex_conjugate", [](Evaluator &evaluator, const Ciphertext &encrypted, const GaloisKeys &galois_keys){ + Ciphertext destination; + evaluator.complex_conjugate(encrypted, galois_keys, destination); + return destination; + }); + + // ckks.h + py::class_<CKKSEncoder>(m, "CKKSEncoder") + .def(py::init<const SEALContext &>()) + .def("slot_count", &CKKSEncoder::slot_count) + .def("encode", [](CKKSEncoder &encoder, py::array_t<double> values, double scale){ + py::buffer_info buf = values.request(); + if (buf.ndim != 1) + throw std::runtime_error("E101: Number of dimensions must be one"); + + double *ptr = (double *)buf.ptr; + std::vector<double> vec(buf.shape[0]); + + for (auto i = 0; i < buf.shape[0]; i++) + vec[i] = ptr[i]; + + Plaintext pt; + encoder.encode(vec, scale, pt); + return pt; + }) + .def("encode", [](CKKSEncoder &encoder, double value, double scale){ + Plaintext pt; + encoder.encode(value, scale, pt); + return pt; + }) + .def("decode", [](CKKSEncoder &encoder, const Plaintext &plain){ + std::vector<double> destination; + encoder.decode(plain, destination); + + py::array_t<double> values(destination.size()); + py::buffer_info buf = values.request(); + double *ptr = (double *)buf.ptr; + + for (auto i = 0; i < buf.shape[0]; i++) + ptr[i] = destination[i]; + + return values; + }); + + // decryptor.h + py::class_<Decryptor>(m, "Decryptor") + .def(py::init<const SEALContext &, const SecretKey &>()) + .def("decrypt", &Decryptor::decrypt) + .def("invariant_noise_budget", &Decryptor::invariant_noise_budget) + .def("decrypt", [](Decryptor &decryptor, const Ciphertext &encrypted){ + Plaintext pt; + decryptor.decrypt(encrypted, pt); + return pt; + }); + + // batchencoder.h + py::class_<BatchEncoder>(m, "BatchEncoder") + .def(py::init<const SEALContext &>()) + .def("slot_count", &BatchEncoder::slot_count) + .def("encode", [](BatchEncoder &encoder, py::array_t<std::int64_t> values){ + py::buffer_info buf = values.request(); + if (buf.ndim != 1) + throw std::runtime_error("E101: Number of dimensions must be one"); + + std::int64_t *ptr = (std::int64_t *)buf.ptr; + std::vector<std::int64_t> vec(buf.shape[0]); + + for (auto i = 0; i < buf.shape[0]; i++) + vec[i] = ptr[i]; + + Plaintext pt; + encoder.encode(vec, pt); + return pt; + }) + .def("decode", [](BatchEncoder &encoder, const Plaintext &plain){ + std::vector<std::int64_t> destination; + encoder.decode(plain, destination); + + py::array_t<std::int64_t> values(destination.size()); + py::buffer_info buf = values.request(); + std::int64_t *ptr = (std::int64_t *)buf.ptr; + + for (auto i = 0; i < buf.shape[0]; i++) + ptr[i] = destination[i]; + + return values; + }); +}