diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index f339c1e..0000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.cpp linguist-language=Python diff --git a/.gitignore b/.gitignore index ba785c5..a622d32 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,5 @@ seal.egg-info *.pyd *.pyc temp -env +.idea +*.bin diff --git a/.gitmodules b/.gitmodules index b951d16..f8da97a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,8 @@ [submodule "SEAL"] path = SEAL url = https://github.com/microsoft/SEAL.git + branch = main [submodule "pybind11"] path = pybind11 url = https://github.com/pybind/pybind11.git + branch = stable diff --git a/README.md b/README.md index 7752125..80d2383 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,10 @@ This is a python binding for the Microsoft SEAL library. ## Build -* #### Linux - Clang++ (>= 5.0) or GNU G++ (>= 6.0), CMake (>= 3.12) + +* ### Linux + + Recommend: Clang++ (>= 10.0) or GNU G++ (>= 9.4), CMake (>= 3.16) ```shell # Optional @@ -30,42 +32,60 @@ This is a python binding for the Microsoft SEAL library. git clone https://github.com/Huelse/SEAL-Python.git cd SEAL-Python - # Numpy is essential - pip3 install -r requirements.txt + # Install dependencies + pip3 install numpy pybind11 # Init the SEAL and pybind11 git submodule update --init --recursive - # Get the newest repositories (unnecessary) + # Get the newest repositories (dev only) # git submodule update --remote # Build the SEAL lib cd SEAL - cmake -S . -B build -DSEAL_USE_MSGSL=OFF -DSEAL_USE_ZLIB=OFF -DSEAL_USE_ZSTD=OFF + cmake -S . -B build -DSEAL_USE_MSGSL=OFF -DSEAL_USE_ZLIB=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 ``` -* #### Windows + Build examples (after `cmake -S . -B`): `-DSEAL_BUILD_EXAMPLES=ON` + + Zstandard compression off: `-DSEAL_USE_ZSTD=OFF` - 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. + [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. ```shell - # 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 + # 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 cmake --build build - # Run the setup.py + # Build + pip install numpy pybind11 python setup.py build_ext -i + + # Test + cp seal.*.pyd examples + cd examples + python 4_bgv_basics.py ``` - 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). + Microsoft SEAL official [docs](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): @@ -78,51 +98,28 @@ This is a python binding for the Microsoft SEAL library. docker run -it huelse/seal ``` + + ## Note -* #### Serialize +* ### Serialize - In most situations, you can use the SEAL's native serialize API to save the data, here is an example: + See more in `examples/7_serialization.py`, here is a simple example: ```python cipher.save('cipher') - load_cipher = Ciphertext() load_cipher.load(context, 'cipher') # work if the context is valid. ``` - Support type: `Encryptionparams, Ciphertext, Plaintext, SecretKey, Publickey, Relinkeys, Galoiskeys` + Supported classes: `EncryptionParameters, 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: - ```shell - # 1. Modify the serializable object's header file in SEAL and switch the wrapper. - python helper.py +* ### Other - # 2. Rebuild the SEAL lib like above - cmake --build build + 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). - # 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) + Email: [topmaxz@protonmail.com](mailto:topmaxz@protonmail.com?subject=Github-SEAL-Python-Issues) @@ -148,9 +145,14 @@ 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 608fb1b..82b07db 160000 --- a/SEAL +++ b/SEAL @@ -1 +1 @@ -Subproject commit 608fb1b9717ec6effe707758a636e0a0df76fc26 +Subproject commit 82b07db635132e297282649e2ab5908999089ad2 diff --git a/examples/4_bgv_basics.py b/examples/4_bgv_basics.py new file mode 100644 index 0000000..ec81cf7 --- /dev/null +++ b/examples/4_bgv_basics.py @@ -0,0 +1,113 @@ +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 new file mode 100644 index 0000000..07bc5d2 --- /dev/null +++ b/examples/7_serialization.py @@ -0,0 +1,73 @@ +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 deleted file mode 100644 index c54b1fc..0000000 --- a/helper.py +++ /dev/null @@ -1,50 +0,0 @@ -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 417067e..ffa3468 160000 --- a/pybind11 +++ b/pybind11 @@ -1 +1 @@ -Subproject commit 417067eeb8d93e8ed49f3ff580712ae2b9a85259 +Subproject commit ffa346860b306c9bbfb341aed9c14c067751feb8 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..1508fa9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,9 @@ + +[build-system] +requires = [ + "setuptools>=42", + "wheel", + "pybind11>=2.9.1", +] + +build-backend = "setuptools.build_meta" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 296d654..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -numpy \ No newline at end of file diff --git a/setup.py b/setup.py index dc0054a..ed93e3c 100644 --- a/setup.py +++ b/setup.py @@ -1,47 +1,44 @@ import os import platform -from setuptools import setup, Extension +from glob import glob +from setuptools import setup from distutils.sysconfig import get_python_inc +from pybind11.setup_helpers import Pybind11Extension, build_ext +__version__ = "4.0.0" -# 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' +include_dirs = [get_python_inc(), 'pybind11/include', 'SEAL/native/src', 'SEAL/build/native/src'] -if(platform.system() == "Windows"): - cpp_args[0] = '/std:c++latest' # /std:c++1z - extra_objects[0] = './SEAL/build/lib/Release/seal-3.7.lib' +extra_objects = sorted(glob('SEAL/build/lib/*.lib') if platform.system() == "Windows" else glob('SEAL/build/lib/*.a')) -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) +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) ext_modules = [ - Extension( - name='seal', - sources=[wrapper_file, 'src/base64.cpp'], + Pybind11Extension( + "seal", + sorted(glob('src/*.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 deleted file mode 100644 index 252224b..0000000 --- a/src/base64.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - 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 deleted file mode 100644 index dd1134c..0000000 --- a/src/base64.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// 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 32dee4d..8031634 100644 --- a/src/wrapper.cpp +++ b/src/wrapper.cpp @@ -3,12 +3,9 @@ #include #include #include "seal/seal.h" -#include "base64.h" -#include #include using namespace seal; - namespace py = pybind11; PYBIND11_MAKE_OPAQUE(std::vector); @@ -16,7 +13,8 @@ PYBIND11_MAKE_OPAQUE(std::vector); PYBIND11_MODULE(seal, m) { - m.doc() = "Microsoft SEAL (3.6) for Python, from https://github.com/Huelse/SEAL-Python"; + m.doc() = "Microsoft SEAL for Python, from https://github.com/Huelse/SEAL-Python"; + m.attr("__version__") = "4.0.0"; py::bind_vector>(m, "VectorDouble", py::buffer_protocol()); py::bind_vector>(m, "VectorInt", py::buffer_protocol()); @@ -25,71 +23,66 @@ 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("ckks", scheme_type::ckks) + .value("bgv", scheme_type::bgv); // 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::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); + 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); // 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) @@ -107,94 +100,134 @@ 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); - - // 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", &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_), 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){ + // 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) + .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_<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", py::overload_cast<std::size_t, std::vector<int>>(&CoeffModulus::Create)) + .def_static("Create", py::overload_cast<std::size_t, const Modulus &, std::vector<int>>(&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_)) + .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("dumpb", [](const Plaintext &plain){ std::stringstream out(std::ios::binary | std::ios::out); plain.save(out); return py::bytes(out.str()); @@ -203,39 +236,43 @@ PYBIND11_MODULE(seal, m) 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("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("dumpb", [](const Ciphertext &cipher){ std::stringstream out(std::ios::binary | std::ios::out); cipher.save(out); @@ -245,24 +282,31 @@ PYBIND11_MODULE(seal, m) 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){ + .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("dumpb", [](const SecretKey &sk){ std::stringstream out(std::ios::binary | std::ios::out); sk.save(out); return py::bytes(out.str()); @@ -271,23 +315,27 @@ PYBIND11_MODULE(seal, m) 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("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("dumpb", [](const PublicKey &pk){ std::stringstream out(std::ios::binary | std::ios::out); pk.save(out); @@ -297,25 +345,29 @@ PYBIND11_MODULE(seal, m) 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){ + .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(); + }) + .def("dumpb", [](const KSwitchKeys &ksk){ std::stringstream out(std::ios::binary | std::ios::out); ksk.save(out); return py::bytes(out.str()); @@ -323,27 +375,26 @@ PYBIND11_MODULE(seal, m) .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(); - }) + // 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("dumpb", [](const RelinKeys &rk){ std::stringstream out(std::ios::binary | std::ios::out); rk.save(out); @@ -353,26 +404,30 @@ PYBIND11_MODULE(seal, m) 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("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("dumpb", [](const GaloisKeys &gk){ std::stringstream out(std::ios::binary | std::ios::out); gk.save(out); @@ -382,7 +437,11 @@ PYBIND11_MODULE(seal, m) std::stringstream in(bytes, std::ios::binary | std::ios::in); gk.load(context, in); }) - ; + .def("to_string", [](const GaloisKeys &galois){ + std::stringstream out(std::ios::binary | std::ios::out); + galois.save(out); + return py::bytes(out.str()); + }); // serializable.h py::class_<Serializable<GaloisKeys>>(m, "SerializableGaloisKeys") @@ -402,7 +461,7 @@ PYBIND11_MODULE(seal, m) 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("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)) @@ -427,292 +486,292 @@ PYBIND11_MODULE(seal, m) }) ; - // 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; - }); + // 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 deleted file mode 100644 index 0c57e2f..0000000 --- a/src/wrapper_with_pickle.cpp +++ /dev/null @@ -1,703 +0,0 @@ -#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; - }); -}