├── .dockerignore ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── other-issue.md └── workflows │ └── python-package.yml ├── .gitignore ├── .gitmodules ├── Dockerfile ├── LICENSE ├── README.md ├── examples ├── 4_bgv_basics.py ├── 7_serialization.py ├── matrix_operations.py └── seal_helper.py ├── pyproject.toml ├── setup.py └── src └── wrapper.cpp /.dockerignore: -------------------------------------------------------------------------------- 1 | # preventing .git files from entering context 2 | .github 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['https://paypal.me/huelse99'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | [//]: # Before submitting this issue, make sure you have already searched and still have problems. 11 | **Describe the bug** 12 | A clear and concise description of what the bug is. 13 | 14 | **Device\Environment** 15 | System: 16 | Python: 17 | 18 | **To Reproduce** 19 | Steps to reproduce the behavior: 20 | 1. 21 | 2. 22 | 23 | **Expected behavior** 24 | A clear and concise description of what you expected to happen. 25 | 26 | **Screenshots** 27 | If applicable, add screenshots to help explain your problem. 28 | 29 | **Additional context** 30 | Add any other context about the problem here. 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other issue 3 | about: Other issue template 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Type** 11 | illustrate your original intention. 12 | 13 | **Descripe** 14 | write what you want. 15 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python package 5 | 6 | on: 7 | push: 8 | branches: [ 3.3.2 ] 9 | pull_request: 10 | branches: [ 3.3.2 ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | python-version: [3.5, 3.6, 3.7, 3.8] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Set up Python ${{ matrix.python-version }} 23 | uses: actions/setup-python@v2 24 | with: 25 | python-version: ${{ matrix.python-version }} 26 | - name: Install dependencies 27 | run: | 28 | sudo apt-get install python3 python3-pip build-essential cmake 29 | python -m pip install --upgrade pip 30 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 31 | - name: build seal 32 | run: | 33 | cd SEAL/native/src 34 | cmake . 35 | make 36 | - name: build seal-python 37 | run: | 38 | python3 setup.py build_ext -i 39 | python3 setup.py install 40 | 41 | 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | src/*.so 19 | src/*.pyd 20 | 21 | # Fortran module files 22 | *.mod 23 | *.smod 24 | 25 | # Compiled Static libraries 26 | *.lai 27 | *.la 28 | *.a 29 | *.lib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | 36 | # Others 37 | *.zip 38 | build 39 | *.lib 40 | *.pyd 41 | *.pyc 42 | temp 43 | .idea 44 | *.bin 45 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "SEAL"] 2 | path = SEAL 3 | url = https://github.com/microsoft/SEAL.git 4 | branch = main 5 | [submodule "pybind11"] 6 | path = pybind11 7 | url = https://github.com/pybind/pybind11.git 8 | branch = stable 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | # define the folder where our src should exist/ be deposited 4 | ARG SRC=/python-seal 5 | 6 | # prevents update and install asking for tz 7 | ENV DEBIAN_FRONTEND=noninteractive 8 | 9 | # install dependencies 10 | RUN apt update && \ 11 | apt install -y git build-essential cmake python3 python3-dev python3-pip && \ 12 | mkdir -p ${SRC} 13 | 14 | # copy into container requirements and install them before rest of code 15 | RUN pip3 install numpy pybind11 16 | 17 | # copy everything into container now that requirements stage is complete 18 | COPY . ${SRC} 19 | 20 | # setting our default directory to the one specified above 21 | WORKDIR ${SRC} 22 | 23 | # update submodules 24 | RUN cd ${SRC} && \ 25 | git submodule update --init --recursive 26 | # git submodule update --remote 27 | 28 | # build and install seal + bindings 29 | RUN cd ${SRC}/SEAL && \ 30 | cmake -S . -B build -DSEAL_USE_MSGSL=OFF -DSEAL_USE_ZLIB=OFF -DSEAL_USE_ZSTD=OFF && \ 31 | cmake --build build && \ 32 | cd ${SRC} && \ 33 | python3 setup.py build_ext -i 34 | 35 | CMD ["/usr/bin/python3"] 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 HuGang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Microsoft SEAL For Python 2 | 3 | Microsoft [**SEAL**](https://github.com/microsoft/SEAL) is an easy-to-use open-source ([MIT licensed](https://github.com/microsoft/SEAL/blob/master/LICENSE)) homomorphic encryption library developed by the Cryptography Research group at Microsoft. 4 | 5 | [**pybind11**](https://github.com/pybind/pybind11) is a lightweight header-only library that exposes C++ types in Python and vice versa, mainly to create Python bindings of existing C++ code. 6 | 7 | This is a python binding for the Microsoft SEAL library. 8 | 9 | 10 | 11 | ## Contents 12 | 13 | * [Build](#build) 14 | * [Note](#note) 15 | * [Serialize](#serialize) 16 | * [Other](#other) 17 | * [FAQ](#faq) 18 | 19 | 20 | 21 | ## Build 22 | 23 | * ### Linux 24 | 25 | Recommend: Clang++ (>= 10.0) or GNU G++ (>= 9.4), CMake (>= 3.16) 26 | 27 | ```shell 28 | # Optional 29 | sudo apt-get install git build-essential cmake python3 python3-dev python3-pip 30 | 31 | # Get the repository or download from the releases 32 | git clone https://github.com/Huelse/SEAL-Python.git 33 | cd SEAL-Python 34 | 35 | # Install dependencies 36 | pip3 install numpy pybind11 37 | 38 | # Init the SEAL and pybind11 39 | git submodule update --init --recursive 40 | # Get the newest repositories (dev only) 41 | # git submodule update --remote 42 | 43 | # Build the SEAL lib without the msgsl zlib and zstandard compression 44 | cd SEAL 45 | cmake -S . -B build -DSEAL_USE_MSGSL=OFF -DSEAL_USE_ZLIB=OFF -DSEAL_USE_ZSTD=OFF 46 | cmake --build build 47 | cd .. 48 | 49 | # Run the setup.py, the dynamic library will be generated in the current directory 50 | python3 setup.py build_ext -i 51 | 52 | # Test 53 | cp seal.*.so examples 54 | cd examples 55 | python3 4_bgv_basics.py 56 | ``` 57 | 58 | Build examples: `-DSEAL_BUILD_EXAMPLES=ON` 59 | 60 | [More cmake options](https://github.com/microsoft/SEAL#basic-cmake-options) 61 | 62 | 63 | * ### Windows 64 | 65 | 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. 66 | 67 | ```shell 68 | # Run in "x64 Native Tools Command Prompt for VS" command prompt 69 | cmake -S . -B build -G Ninja -DSEAL_USE_MSGSL=OFF -DSEAL_USE_ZLIB=OFF 70 | cmake --build build 71 | 72 | # Build 73 | pip install numpy pybind11 74 | python setup.py build_ext -i 75 | 76 | # Test 77 | cp seal.*.pyd examples 78 | cd examples 79 | python 4_bgv_basics.py 80 | ``` 81 | 82 | Microsoft SEAL official [docs](https://github.com/microsoft/SEAL#building-microsoft-seal-manually). 83 | 84 | 85 | * ### Docker 86 | 87 | requires: [Docker](https://www.docker.com/) 88 | 89 | To build source code into a docker image (from this directory): 90 | ```shell 91 | docker build -t huelse/seal -f Dockerfile . 92 | ``` 93 | 94 | To use the image by running it as an interactive container: 95 | ```shell 96 | docker run -it huelse/seal 97 | ``` 98 | 99 | 100 | 101 | ## Note 102 | 103 | * ### Serialize 104 | 105 | See more in `examples/7_serialization.py`, here is a simple example: 106 | 107 | ```python 108 | cipher.save('cipher') 109 | load_cipher = Ciphertext() 110 | load_cipher.load(context, 'cipher') # work if the context is valid. 111 | ``` 112 | 113 | Supported classes: `EncryptionParameters, Ciphertext, Plaintext, SecretKey, PublicKey, RelinKeys, GaloisKeys` 114 | 115 | 116 | * ### Other 117 | 118 | 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). 119 | 120 | Email: [topmaxz@protonmail.com](mailto:topmaxz@protonmail.com?subject=Github-SEAL-Python-Issues) 121 | 122 | 123 | 124 | ## FAQ 125 | 126 | 1. ImportError: undefined symbol 127 | 128 | Build a shared SEAL library `cmake . -DBUILD_SHARED_LIBS=ON`, and get the `libseal.so`, 129 | 130 | then change the path in `setup.py`, and rebuild. 131 | 132 | 133 | 2. ImportError: libseal.so... cannot find 134 | 135 | a. `sudo ln -s /path/to/libseal.so /usr/lib` 136 | 137 | b. add `/usr/local/lib` or the `SEAL/native/lib` to `/etc/ld.so.conf` and refresh it `sudo ldconfig` 138 | 139 | c. build in cmake. 140 | 141 | 142 | 3. BuildError: 143 | 144 | 1. C++17 at least 145 | 146 | 2. x86_64 is required, which `x86_32` is not supported 147 | 148 | 149 | 4. ModuleNotFoundError: No module named 'seal' 150 | 151 | The `.so` or `.pyd` file must be in the current directory, or you have `install` it already. 152 | 153 | 154 | 5. Windows Error LNK2001, RuntimeLibrary and MT_StaticRelease mismatch 155 | 156 | Only `x64` is supported, Choose `x64 Native Tools Command Prompt for VS`. 157 | 158 | 159 | 6. Warning about building the dynamic library with static library in MacOS, etc. 160 | 161 | 1. Build a shared SEAL library by adding a CMake option `-DBUILD_SHARED_LIBS=ON` 162 | 163 | 2. Edit `extra_objects` in setup.py to `*.dylib` or else. 164 | 165 | 166 | 167 | ## Contributing 168 | 169 | * Professor: [Dr. Chen](https://zhigang-chen.github.io/) 170 | 171 | * [Contributors](https://github.com/Huelse/SEAL-Python/graphs/contributors) 172 | -------------------------------------------------------------------------------- /examples/4_bgv_basics.py: -------------------------------------------------------------------------------- 1 | from seal import * 2 | import numpy as np 3 | 4 | def print_vector(vector): 5 | print('[ ', end='') 6 | for i in range(0, 8): 7 | print(vector[i], end=', ') 8 | print('... ]') 9 | 10 | 11 | def example_bgv_basics(): 12 | parms = EncryptionParameters (scheme_type.bgv) 13 | poly_modulus_degree = 8192 14 | parms.set_poly_modulus_degree(poly_modulus_degree) 15 | parms.set_coeff_modulus(CoeffModulus.BFVDefault(poly_modulus_degree)) 16 | parms.set_plain_modulus(PlainModulus.Batching(poly_modulus_degree, 20)) 17 | context = SEALContext(parms) 18 | 19 | keygen = KeyGenerator(context) 20 | secret_key = keygen.secret_key() 21 | public_key = keygen.create_public_key() 22 | relin_keys = keygen.create_relin_keys() 23 | 24 | encryptor = Encryptor(context, public_key) 25 | evaluator = Evaluator(context) 26 | decryptor = Decryptor(context, secret_key) 27 | 28 | batch_encoder = BatchEncoder(context) 29 | slot_count = batch_encoder.slot_count() 30 | row_size = slot_count / 2 31 | print(f'Plaintext matrix row size: {row_size}') 32 | 33 | pod_matrix = [0] * slot_count 34 | pod_matrix[0] = 1 35 | pod_matrix[1] = 2 36 | pod_matrix[2] = 3 37 | pod_matrix[3] = 4 38 | 39 | x_plain = batch_encoder.encode(pod_matrix) 40 | 41 | x_encrypted = encryptor.encrypt(x_plain) 42 | print(f'noise budget in freshly encrypted x: {decryptor.invariant_noise_budget(x_encrypted)}') 43 | print('-'*50) 44 | 45 | x_squared = evaluator.square(x_encrypted) 46 | print(f'size of x_squared: {x_squared.size()}') 47 | evaluator.relinearize_inplace(x_squared, relin_keys) 48 | print(f'size of x_squared (after relinearization): {x_squared.size()}') 49 | print(f'noise budget in x_squared: {decryptor.invariant_noise_budget(x_squared)} bits') 50 | decrypted_result = decryptor.decrypt(x_squared) 51 | pod_result = batch_encoder.decode(decrypted_result) 52 | print_vector(pod_result) 53 | print('-'*50) 54 | 55 | x_4th = evaluator.square(x_squared) 56 | print(f'size of x_4th: {x_4th.size()}') 57 | evaluator.relinearize_inplace(x_4th, relin_keys) 58 | print(f'size of x_4th (after relinearization): { x_4th.size()}') 59 | print(f'noise budget in x_4th: {decryptor.invariant_noise_budget(x_4th)} bits') 60 | decrypted_result = decryptor.decrypt(x_4th) 61 | pod_result = batch_encoder.decode(decrypted_result) 62 | print_vector(pod_result) 63 | print('-'*50) 64 | 65 | x_8th = evaluator.square(x_4th) 66 | print(f'size of x_8th: {x_8th.size()}') 67 | evaluator.relinearize_inplace(x_8th, relin_keys) 68 | print(f'size of x_8th (after relinearization): { x_8th.size()}') 69 | print(f'noise budget in x_8th: {decryptor.invariant_noise_budget(x_8th)} bits') 70 | decrypted_result = decryptor.decrypt(x_8th) 71 | pod_result = batch_encoder.decode(decrypted_result) 72 | print_vector(pod_result) 73 | print('run out of noise budget') 74 | print('-'*100) 75 | 76 | x_encrypted = encryptor.encrypt(x_plain) 77 | print(f'noise budget in freshly encrypted x: {decryptor.invariant_noise_budget(x_encrypted)}') 78 | print('-'*50) 79 | 80 | x_squared = evaluator.square(x_encrypted) 81 | print(f'size of x_squared: {x_squared.size()}') 82 | evaluator.relinearize_inplace(x_squared, relin_keys) 83 | evaluator.mod_switch_to_next_inplace(x_squared) 84 | print(f'noise budget in x_squared (with modulus switching): {decryptor.invariant_noise_budget(x_squared)} bits') 85 | decrypted_result = decryptor.decrypt(x_squared) 86 | pod_result = batch_encoder.decode(decrypted_result) 87 | print_vector(pod_result) 88 | print('-'*50) 89 | 90 | x_4th = evaluator.square(x_squared) 91 | print(f'size of x_4th: {x_4th.size()}') 92 | evaluator.relinearize_inplace(x_4th, relin_keys) 93 | evaluator.mod_switch_to_next_inplace(x_4th) 94 | print(f'size of x_4th (after relinearization): { x_4th.size()}') 95 | print(f'noise budget in x_4th (with modulus switching): {decryptor.invariant_noise_budget(x_4th)} bits') 96 | decrypted_result = decryptor.decrypt(x_4th) 97 | pod_result = batch_encoder.decode(decrypted_result) 98 | print_vector(pod_result) 99 | print('-'*50) 100 | 101 | x_8th = evaluator.square(x_4th) 102 | print(f'size of x_8th: {x_8th.size()}') 103 | evaluator.relinearize_inplace(x_8th, relin_keys) 104 | evaluator.mod_switch_to_next_inplace(x_8th) 105 | print(f'size of x_8th (after relinearization): { x_8th.size()}') 106 | print(f'noise budget in x_8th (with modulus switching): {decryptor.invariant_noise_budget(x_8th)} bits') 107 | decrypted_result = decryptor.decrypt(x_8th) 108 | pod_result = batch_encoder.decode(decrypted_result) 109 | print_vector(pod_result) 110 | 111 | 112 | if __name__ == "__main__": 113 | example_bgv_basics() 114 | -------------------------------------------------------------------------------- /examples/7_serialization.py: -------------------------------------------------------------------------------- 1 | from seal import * 2 | import pickle 3 | import time 4 | 5 | 6 | def get_seal(): 7 | parms = EncryptionParameters(scheme_type.ckks) 8 | poly_modulus_degree = 8192 9 | parms.set_poly_modulus_degree(poly_modulus_degree) 10 | parms.set_coeff_modulus(CoeffModulus.Create(poly_modulus_degree, [60, 40, 40, 60])) 11 | scale = 2.0 ** 40 12 | 13 | context = SEALContext(parms) 14 | ckks_encoder = CKKSEncoder(context) 15 | slot_count = ckks_encoder.slot_count() 16 | 17 | keygen = KeyGenerator(context) 18 | public_key = keygen.create_public_key() 19 | secret_key = keygen.secret_key() 20 | 21 | encryptor = Encryptor(context, public_key) 22 | # evaluator = Evaluator(context) 23 | decryptor = Decryptor(context, secret_key) 24 | 25 | data = [3.1415926] * slot_count 26 | plain = ckks_encoder.encode(data, scale) 27 | cipher = encryptor.encrypt(plain) 28 | 29 | return cipher, context, ckks_encoder, decryptor 30 | 31 | 32 | def serialization_example(): 33 | print('serialization example') 34 | print('-' * 70) 35 | cipher2, context2, ckks_encoder2, decryptor2 = get_seal() 36 | cipher2.save('cipher2.bin') 37 | print('save cipher2 data success') 38 | 39 | time.sleep(.5) 40 | 41 | cipher3 = Ciphertext() 42 | cipher3.load(context2, 'cipher2.bin') 43 | print('load cipher2 data success') 44 | plain3 = decryptor2.decrypt(cipher3) 45 | data3 = ckks_encoder2.decode(plain3) 46 | print(data3) 47 | print('-' * 70) 48 | 49 | 50 | def pickle_example(): 51 | print('pickle example') 52 | print('-' * 70) 53 | cipher1, context1, ckks_encoder1, decryptor1 = get_seal() 54 | with open('cipher1.bin', 'wb') as f: 55 | pickle.dump(cipher1.to_string(), f) 56 | print('write cipher1 data success') 57 | 58 | time.sleep(.5) 59 | 60 | with open('cipher1.bin', 'rb') as f: 61 | temp = pickle.load(f) 62 | cipher2 = context1.from_cipher_str(temp) 63 | plain2 = decryptor1.decrypt(cipher2) 64 | data = ckks_encoder1.decode(plain2) 65 | print('read cipher1 data success') 66 | print(data) 67 | 68 | print('-' * 70) 69 | 70 | 71 | if __name__ == "__main__": 72 | serialization_example() 73 | pickle_example() 74 | -------------------------------------------------------------------------------- /examples/matrix_operations.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | import math 4 | import numpy as np 5 | from seal import * 6 | from seal_helper import * 7 | 8 | 9 | def get_diagonal(position, matrix): 10 | n = matrix.shape[0] 11 | diagonal = np.zeros(n) 12 | 13 | k = 0 14 | i = 0 15 | j = position 16 | while i < n-position and j < n: 17 | diagonal[k] = matrix[i][j] 18 | i += 1 19 | j += 1 20 | k += 1 21 | 22 | i = n - position 23 | j = 0 24 | while i < n and j < position: 25 | diagonal[k] = matrix[i][j] 26 | i += 1 27 | j += 1 28 | k += 1 29 | 30 | return diagonal 31 | 32 | 33 | def get_all_diagonals(matrix): 34 | matrix_diagonals = [] 35 | for i in range(matrix.shape[0]): 36 | matrix_diagonals.append(get_diagonal(i, matrix)) 37 | 38 | return np.array(matrix_diagonals) 39 | 40 | 41 | def get_u_transpose(shape): 42 | u_transpose = np.zeros((shape[0]**2, shape[1]**2)) 43 | n = shape[0] 44 | k = 0 45 | i = 0 46 | for row in u_transpose: 47 | row[k+i] = 1 48 | k += n 49 | if k >= n*n: 50 | k = 0 51 | i += 1 52 | 53 | return u_transpose 54 | 55 | 56 | def get_transposed_diagonals(u_transposed): 57 | transposed_diagonals = np.zeros(u_transposed.shape) 58 | for i in range(u_transposed.shape[0]): 59 | a = np.diagonal(u_transposed, offset=i) 60 | b = np.diagonal(u_transposed, offset=u_transposed.shape[0]-i) 61 | transposed_diagonals[i] = np.concatenate([a, b]) 62 | 63 | return transposed_diagonals 64 | 65 | 66 | def linear_transform_plain(cipher_matrix, plain_diags, galois_keys, evaluator): 67 | cipher_rot = evaluator.rotate_vector(cipher_matrix, -len(plain_diags), galois_keys) 68 | cipher_temp = evaluator.add(cipher_matrix, cipher_rot) 69 | cipher_results = [] 70 | temp = evaluator.multiply_plain(cipher_temp, plain_diags[0]) 71 | cipher_results.append(temp) 72 | 73 | i = 1 74 | while i < len(plain_diags): 75 | temp_rot = evaluator.rotate_vector(cipher_temp, i, galois_keys) 76 | temp = evaluator.multiply_plain(temp_rot, plain_diags[i]) 77 | cipher_results.append(temp) 78 | i += 1 79 | 80 | cipher_prime = evaluator.add_many(cipher_results) 81 | 82 | return cipher_prime 83 | 84 | 85 | def get_u_sigma(shape): 86 | u_sigma_ = np.zeros(shape) 87 | indices_diagonal = np.diag_indices(shape[0]) 88 | u_sigma_[indices_diagonal] = 1. 89 | 90 | for i in range(shape[0]-1): 91 | u_sigma_ = np.pad(u_sigma_, (0, shape[0]), 'constant') 92 | temp = np.zeros(shape) 93 | j = np.arange(0, shape[0]) 94 | temp[j, j-(shape[0]-1-i)] = 1. 95 | temp = np.pad(temp, ((i+1)*shape[0], 0), 'constant') 96 | u_sigma_ += temp 97 | 98 | return u_sigma_ 99 | 100 | 101 | def get_u_tau(shape): 102 | u_tau_ = np.zeros((shape[0], shape[0]**2)) 103 | index = np.arange(shape[0]) 104 | for i in range(shape[0], 0, -1): 105 | idx = np.concatenate([index[i:], index[:i]], axis=0) 106 | row = np.zeros(shape) 107 | for j in range(shape[0]): 108 | temp = np.zeros(shape) 109 | temp[idx[j], idx[j]] = 1. 110 | if j == 0: 111 | row += temp 112 | else: 113 | row = np.concatenate([row, temp], axis=1) 114 | 115 | if i == shape[0]: 116 | u_tau_ += row 117 | else: 118 | u_tau_ = np.concatenate([u_tau_, row], axis=0) 119 | 120 | return u_tau_ 121 | 122 | 123 | def get_v_k(shape): 124 | v_k_ = [] 125 | index = np.arange(0, shape[0]) 126 | for j in range(1, shape[0]): 127 | temp = np.zeros(shape) 128 | temp[index, index-(shape[0]-j)] = 1. 129 | mat = temp 130 | for i in range(shape[0]-1): 131 | mat = np.pad(mat, (0, shape[0]), 'constant') 132 | temp2 = np.pad(temp, ((i+1)*shape[0], 0), 'constant') 133 | mat += temp2 134 | 135 | v_k_.append(mat) 136 | 137 | return v_k_ 138 | 139 | 140 | def get_w_k(shape): 141 | w_k_ = [] 142 | index = np.arange(shape[0]**2) 143 | for i in range(shape[0]-1): 144 | temp = np.zeros((shape[0]**2, shape[1]**2)) 145 | temp[index-(i+1)*shape[0], index] = 1. 146 | w_k_.append(temp) 147 | 148 | return w_k_ 149 | 150 | 151 | def matrix_multiplication(n, cm1, cm2, sigma, tau, v, w, galois_keys, evaluator): 152 | cipher_result1 = [] 153 | cipher_result2 = [] 154 | 155 | cipher_result1.append(linear_transform_plain(cm1, sigma, galois_keys, evaluator)) 156 | cipher_result2.append(linear_transform_plain(cm2, tau, galois_keys, evaluator)) 157 | 158 | for i in range(1, n): 159 | cipher_result1.append(linear_transform_plain(cipher_result1[0], v[i-1], galois_keys, evaluator)) 160 | cipher_result2.append(linear_transform_plain(cipher_result2[0], w[i-1], galois_keys, evaluator)) 161 | 162 | for i in range(1, n): 163 | evaluator.rescale_to_next_inplace(cipher_result1[i]) 164 | evaluator.rescale_to_next_inplace(cipher_result2[i]) 165 | 166 | cipher_mult = evaluator.multiply(cipher_result1[0], cipher_result2[0]) 167 | evaluator.mod_switch_to_next_inplace(cipher_mult) 168 | 169 | for i in range(1, n): 170 | cipher_result1[i].scale(2**int(math.log2(cipher_result1[i].scale()))) 171 | cipher_result2[i].scale(2**int(math.log2(cipher_result2[i].scale()))) 172 | 173 | for i in range(1, n): 174 | temp = evaluator.multiply(cipher_result1[i], cipher_result2[i]) 175 | evaluator.add_inplace(cipher_mult, temp) 176 | 177 | return cipher_mult 178 | 179 | 180 | def matrix_mult_test(n=4): 181 | parms = EncryptionParameters(scheme_type.ckks) 182 | poly_modulus_degree = 16384 183 | parms.set_poly_modulus_degree(poly_modulus_degree) 184 | parms.set_coeff_modulus(CoeffModulus.Create( 185 | poly_modulus_degree, [60, 40, 40, 40, 40, 60])) 186 | scale = 2.0**40 187 | context = SEALContext(parms) 188 | print_parameters(context) 189 | 190 | ckks_encoder = CKKSEncoder(context) 191 | slot_count = ckks_encoder.slot_count() 192 | print(f'Number of slots: {slot_count}') 193 | 194 | keygen = KeyGenerator(context) 195 | public_key = keygen.create_public_key() 196 | secret_key = keygen.secret_key() 197 | galois_keys = keygen.create_galois_keys() 198 | 199 | encryptor = Encryptor(context, public_key) 200 | evaluator = Evaluator(context) 201 | decryptor = Decryptor(context, secret_key) 202 | 203 | # --------------------------------------------------------- 204 | u_sigma = get_u_sigma((n,n)) 205 | u_tau = get_u_tau((n,n)) 206 | v_k = get_v_k((n, n)) 207 | w_k = get_w_k((n, n)) 208 | 209 | u_sigma_diagonals = get_all_diagonals(u_sigma) 210 | u_sigma_diagonals += 0.00000001 # prevent is_transparent 211 | 212 | u_tau_diagonals = get_all_diagonals(u_tau) 213 | u_tau_diagonals += 0.00000001 214 | 215 | v_k_diagonals = [] 216 | for v in v_k: 217 | diags = get_all_diagonals(v) 218 | diags += 0.00000001 219 | v_k_diagonals.append(diags) 220 | 221 | w_k_diagonals = [] 222 | for w in w_k: 223 | diags = get_all_diagonals(w) 224 | diags += 0.00000001 225 | w_k_diagonals.append(diags) 226 | 227 | plain_u_sigma_diagonals = [] 228 | plain_u_tau_diagonals = [] 229 | plain_v_k_diagonals = [] 230 | plain_w_k_diagonals = [] 231 | 232 | # --------------------------------------------------------- 233 | for i in range(n**2): 234 | plain_u_sigma_diagonals.append(ckks_encoder.encode(u_sigma_diagonals[i], scale)) 235 | plain_u_tau_diagonals.append(ckks_encoder.encode(u_tau_diagonals[i], scale)) 236 | 237 | for i in range(n-1): 238 | temp1 = [] 239 | temp2 = [] 240 | for j in range(n**2): 241 | temp1.append(ckks_encoder.encode(v_k_diagonals[i][j], scale)) 242 | temp2.append(ckks_encoder.encode(w_k_diagonals[i][j], scale)) 243 | 244 | plain_v_k_diagonals.append(temp1) 245 | plain_w_k_diagonals.append(temp2) 246 | 247 | # matrix1 = np.random.rand(n, n) 248 | matrix1 = np.arange(1, n*n+1).reshape(n, n) 249 | matrix2 = matrix1 250 | print('Plaintext result:') 251 | print(np.dot(matrix1, matrix2)) 252 | 253 | plain_matrix1 = ckks_encoder.encode(matrix1.flatten(), scale) 254 | plain_matrix2 = ckks_encoder.encode(matrix2.flatten(), scale) 255 | cipher_matrix1 = encryptor.encrypt(plain_matrix1) 256 | cipher_matrix2 = encryptor.encrypt(plain_matrix2) 257 | 258 | # --------------------------------------------------------- 259 | start = time.time() 260 | cipher_result = matrix_multiplication(n, cipher_matrix1, cipher_matrix2, plain_u_sigma_diagonals, plain_u_tau_diagonals, plain_v_k_diagonals, plain_w_k_diagonals, galois_keys, evaluator) 261 | end = time.time() 262 | 263 | # --------------------------------------------------------- 264 | plain = decryptor.decrypt(cipher_result) 265 | vec = ckks_encoder.decode(plain) 266 | print('Ciphertext result:') 267 | print(vec[:n**2].reshape(n, n)) 268 | print(f'Mult Time: {(end-start):.3f}s') 269 | 270 | 271 | def matrix_transpose_test(n=4): 272 | parms = EncryptionParameters(scheme_type.ckks) 273 | poly_modulus_degree = 8192 274 | parms.set_poly_modulus_degree(poly_modulus_degree) 275 | parms.set_coeff_modulus(CoeffModulus.Create( 276 | poly_modulus_degree, [60, 40, 40, 60])) 277 | scale = 2.0**40 278 | context = SEALContext(parms) 279 | print_parameters(context) 280 | 281 | ckks_encoder = CKKSEncoder(context) 282 | slot_count = ckks_encoder.slot_count() 283 | print(f'Number of slots: {slot_count}') 284 | 285 | keygen = KeyGenerator(context) 286 | public_key = keygen.create_public_key() 287 | secret_key = keygen.secret_key() 288 | galois_keys = keygen.create_galois_keys() 289 | 290 | encryptor = Encryptor(context, public_key) 291 | evaluator = Evaluator(context) 292 | decryptor = Decryptor(context, secret_key) 293 | 294 | # --------------------------------------------------------- 295 | # matrix = np.random.rand(n, n) 296 | matrix = np.arange(1, n*n+1).reshape(n, n) 297 | print('Plaintext result:') 298 | print(matrix) 299 | 300 | u_transposed = get_u_transpose(matrix.shape) 301 | u_transposed_diagonals = get_transposed_diagonals(u_transposed) 302 | u_transposed_diagonals += 0.00000001 # Prevent is_transparent 303 | 304 | # --------------------------------------------------------- 305 | plain_u_diag = [] 306 | for row in u_transposed_diagonals: 307 | plain_u_diag.append(ckks_encoder.encode(row, scale)) 308 | 309 | plain_matrix = ckks_encoder.encode(matrix.flatten(), scale) 310 | cipher_matrix = encryptor.encrypt(plain_matrix) 311 | 312 | # --------------------------------------------------------- 313 | start = time.time() 314 | cipher_result = linear_transform_plain( 315 | cipher_matrix, plain_u_diag, galois_keys, evaluator) 316 | end = time.time() 317 | 318 | # --------------------------------------------------------- 319 | p1 = decryptor.decrypt(cipher_result) 320 | vec = ckks_encoder.decode(p1) 321 | print('Ciphertext result:') 322 | print(vec[:n**2].reshape(n, n)) 323 | print(f'Trans Time: {(end-start):.3f}s') 324 | 325 | 326 | if __name__ == "__main__": 327 | args = sys.argv[1:] 328 | n = int(args[0]) if args else 4 329 | print(f'n: {n}') 330 | print('-'*18 + 'Matrix Transpose:' + '-'*18) 331 | matrix_transpose_test(n) 332 | 333 | print('-'*18 + 'Matrix Multiplication:' + '-'*18) 334 | matrix_mult_test(n) 335 | -------------------------------------------------------------------------------- /examples/seal_helper.py: -------------------------------------------------------------------------------- 1 | from seal import scheme_type 2 | 3 | 4 | def print_example_banner(title): 5 | title_length = len(title) 6 | banner_length = title_length + 2 * 10 7 | banner_top = '+' + '-' * (banner_length - 2) + '+' 8 | banner_middle = '|' + ' ' * 9 + title + ' ' * 9 + '|' 9 | print(banner_top) 10 | print(banner_middle) 11 | print(banner_top) 12 | 13 | 14 | def print_parameters(context): 15 | context_data = context.key_context_data() 16 | if context_data.parms().scheme() == scheme_type.bfv: 17 | scheme_name = 'bfv' 18 | elif context_data.parms().scheme() == scheme_type.ckks: 19 | scheme_name = 'ckks' 20 | else: 21 | scheme_name = 'none' 22 | print('/') 23 | print('| Encryption parameters') 24 | print('| scheme: ' + scheme_name) 25 | print(f'| poly_modulus_degree: {context_data.parms().poly_modulus_degree()}') 26 | coeff_modulus = context_data.parms().coeff_modulus() 27 | coeff_modulus_sum = 0 28 | for j in coeff_modulus: 29 | coeff_modulus_sum += j.bit_count() 30 | print(f'| coeff_modulus size: {coeff_modulus_sum}(', end='') 31 | for i in range(len(coeff_modulus) - 1): 32 | print(f'{coeff_modulus[i].bit_count()} + ', end='') 33 | print(f'{coeff_modulus[-1].bit_count()}) bits') 34 | if context_data.parms().scheme() == scheme_type.bfv: 35 | print(f'| plain_modulus: {context_data.parms().plain_modulus().value()}') 36 | print('\\') 37 | 38 | 39 | def print_vector(vec, print_size=4, prec=3): 40 | slot_count = len(vec) 41 | print() 42 | if slot_count <= 2*print_size: 43 | print(' [', end='') 44 | for i in range(slot_count): 45 | print(f' {vec[i]:.{prec}f}' + (',' if (i != slot_count - 1) else ' ]\n'), end='') 46 | else: 47 | print(' [', end='') 48 | for i in range(print_size): 49 | print(f' {vec[i]:.{prec}f},', end='') 50 | if slot_count > 2*print_size: 51 | print(' ...,', end='') 52 | for i in range(slot_count - print_size, slot_count): 53 | print(f' {vec[i]:.{prec}f}' + (',' if (i != slot_count - 1) else ' ]\n'), end='') 54 | print() 55 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | 2 | [build-system] 3 | requires = [ 4 | "setuptools>=42", 5 | "wheel", 6 | "pybind11>=2.9.1", 7 | ] 8 | 9 | build-backend = "setuptools.build_meta" 10 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | from glob import glob 4 | from setuptools import setup 5 | from distutils.sysconfig import get_python_inc 6 | from pybind11.setup_helpers import Pybind11Extension, build_ext 7 | 8 | __version__ = "4.0.0" 9 | 10 | include_dirs = [get_python_inc(), 'pybind11/include', 'SEAL/native/src', 'SEAL/build/native/src'] 11 | 12 | extra_objects = sorted(glob('SEAL/build/lib/*.lib') if platform.system() == "Windows" else glob('SEAL/build/lib/*.a')) 13 | 14 | cpp_args = ['/std:c++latest'] if platform.system() == "Windows" else ['-std=c++17'] 15 | 16 | if len(extra_objects) < 1 or not os.path.exists(extra_objects[0]): 17 | print('Not found the seal lib file, check the `SEAL/build/lib`') 18 | exit(0) 19 | 20 | ext_modules = [ 21 | Pybind11Extension( 22 | "seal", 23 | sorted(glob('src/*.cpp')), 24 | include_dirs=include_dirs, 25 | extra_compile_args=cpp_args, 26 | extra_objects=extra_objects, 27 | define_macros = [('VERSION_INFO', __version__)], 28 | ), 29 | ] 30 | 31 | setup( 32 | name="seal", 33 | version=__version__, 34 | author="Huelse", 35 | author_email="topmaxz@protonmail.com", 36 | url="https://github.com/Huelse/SEAL-Python", 37 | description="Python wrapper for the Microsoft SEAL", 38 | long_description="", 39 | ext_modules=ext_modules, 40 | cmdclass={"build_ext": build_ext}, 41 | zip_safe=False, 42 | license='MIT', 43 | python_requires=">=3.6", 44 | ) 45 | -------------------------------------------------------------------------------- /src/wrapper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "seal/seal.h" 6 | #include 7 | 8 | using namespace seal; 9 | namespace py = pybind11; 10 | 11 | PYBIND11_MAKE_OPAQUE(std::vector); 12 | PYBIND11_MAKE_OPAQUE(std::vector); 13 | 14 | PYBIND11_MODULE(seal, m) 15 | { 16 | m.doc() = "Microsoft SEAL for Python, from https://github.com/Huelse/SEAL-Python"; 17 | m.attr("__version__") = "4.0.0"; 18 | 19 | py::bind_vector>(m, "VectorDouble", py::buffer_protocol()); 20 | py::bind_vector>(m, "VectorInt", py::buffer_protocol()); 21 | 22 | // encryptionparams.h 23 | py::enum_(m, "scheme_type") 24 | .value("none", scheme_type::none) 25 | .value("bfv", scheme_type::bfv) 26 | .value("ckks", scheme_type::ckks) 27 | .value("bgv", scheme_type::bgv); 28 | 29 | // encryptionparams.h 30 | py::class_(m, "EncryptionParameters") 31 | .def(py::init()) 32 | .def(py::init()) 33 | .def("set_poly_modulus_degree", &EncryptionParameters::set_poly_modulus_degree) 34 | .def("set_coeff_modulus", &EncryptionParameters::set_coeff_modulus) 35 | .def("set_plain_modulus", py::overload_cast(&EncryptionParameters::set_plain_modulus)) 36 | .def("set_plain_modulus", py::overload_cast(&EncryptionParameters::set_plain_modulus)) 37 | .def("scheme", &EncryptionParameters::scheme) 38 | .def("poly_modulus_degree", &EncryptionParameters::poly_modulus_degree) 39 | .def("coeff_modulus", &EncryptionParameters::coeff_modulus) 40 | .def("plain_modulus", &EncryptionParameters::plain_modulus) 41 | .def("save", [](const EncryptionParameters &parms, std::string &path){ 42 | std::ofstream out(path, std::ios::binary); 43 | parms.save(out); 44 | out.close(); 45 | }) 46 | .def("load", [](EncryptionParameters &parms, std::string &path){ 47 | std::ifstream in(path, std::ios::binary); 48 | parms.load(in); 49 | in.close(); 50 | }) 51 | .def(py::pickle( 52 | [](const EncryptionParameters &parms){ 53 | std::stringstream out(std::ios::binary | std::ios::out); 54 | parms.save(out); 55 | return py::make_tuple(py::bytes(out.str())); 56 | }, 57 | [](py::tuple t){ 58 | if (t.size() != 1) 59 | throw std::runtime_error("(Pickle) Invalid input tuple!"); 60 | std::string str = t[0].cast(); 61 | std::stringstream in(std::ios::binary | std::ios::in); 62 | EncryptionParameters parms; 63 | parms.load(in); 64 | return parms; 65 | } 66 | )); 67 | 68 | // modulus.h 69 | py::enum_(m, "sec_level_type") 70 | .value("none", sec_level_type::none) 71 | .value("tc128", sec_level_type::tc128) 72 | .value("tc192", sec_level_type::tc192) 73 | .value("tc256", sec_level_type::tc256); 74 | 75 | // context.h 76 | py::enum_(m, "error_type") 77 | .value("none", EncryptionParameterQualifiers::error_type::none) 78 | .value("success", EncryptionParameterQualifiers::error_type::success) 79 | .value("invalid_scheme", EncryptionParameterQualifiers::error_type::invalid_scheme) 80 | .value("invalid_coeff_modulus_size", EncryptionParameterQualifiers::error_type::invalid_coeff_modulus_size) 81 | .value("invalid_coeff_modulus_bit_count", EncryptionParameterQualifiers::error_type::invalid_coeff_modulus_bit_count) 82 | .value("invalid_coeff_modulus_no_ntt", EncryptionParameterQualifiers::error_type::invalid_coeff_modulus_no_ntt) 83 | .value("invalid_poly_modulus_degree", EncryptionParameterQualifiers::error_type::invalid_poly_modulus_degree) 84 | .value("invalid_poly_modulus_degree_non_power_of_two", EncryptionParameterQualifiers::error_type::invalid_poly_modulus_degree_non_power_of_two) 85 | .value("invalid_parameters_too_large", EncryptionParameterQualifiers::error_type::invalid_parameters_too_large) 86 | .value("invalid_parameters_insecure", EncryptionParameterQualifiers::error_type::invalid_parameters_insecure) 87 | .value("failed_creating_rns_base", EncryptionParameterQualifiers::error_type::failed_creating_rns_base) 88 | .value("invalid_plain_modulus_bit_count", EncryptionParameterQualifiers::error_type::invalid_plain_modulus_bit_count) 89 | .value("invalid_plain_modulus_coprimality", EncryptionParameterQualifiers::error_type::invalid_plain_modulus_coprimality) 90 | .value("invalid_plain_modulus_too_large", EncryptionParameterQualifiers::error_type::invalid_plain_modulus_too_large) 91 | .value("invalid_plain_modulus_nonzero", EncryptionParameterQualifiers::error_type::invalid_plain_modulus_nonzero) 92 | .value("failed_creating_rns_tool", EncryptionParameterQualifiers::error_type::failed_creating_rns_tool); 93 | 94 | // context.h 95 | py::class_>(m, "EncryptionParameterQualifiers") 96 | .def("parameters_set", &EncryptionParameterQualifiers::parameters_set) 97 | .def_readwrite("using_fft", &EncryptionParameterQualifiers::using_fft) 98 | .def_readwrite("using_ntt", &EncryptionParameterQualifiers::using_ntt) 99 | .def_readwrite("using_batching", &EncryptionParameterQualifiers::using_batching) 100 | .def_readwrite("using_fast_plain_lift", &EncryptionParameterQualifiers::using_fast_plain_lift) 101 | .def_readwrite("using_descending_modulus_chain", &EncryptionParameterQualifiers::using_descending_modulus_chain) 102 | .def_readwrite("sec_level", &EncryptionParameterQualifiers::sec_level); 103 | 104 | // context.h 105 | py::class_>(m, "ContextData") 106 | .def("parms", &SEALContext::ContextData::parms) 107 | .def("parms_id", &SEALContext::ContextData::parms_id) 108 | .def("qualifiers", &SEALContext::ContextData::qualifiers) 109 | .def("total_coeff_modulus", &SEALContext::ContextData::total_coeff_modulus) 110 | .def("total_coeff_modulus_bit_count", &SEALContext::ContextData::total_coeff_modulus_bit_count) 111 | .def("next_context_data", &SEALContext::ContextData::next_context_data) 112 | .def("chain_index", &SEALContext::ContextData::chain_index); 113 | 114 | // context.h 115 | py::class_>(m, "SEALContext") 116 | .def(py::init(), py::arg(), py::arg()=true, py::arg()=sec_level_type::tc128) 117 | .def("get_context_data", &SEALContext::get_context_data) 118 | .def("key_context_data", &SEALContext::key_context_data) 119 | .def("first_context_data", &SEALContext::first_context_data) 120 | .def("last_context_data", &SEALContext::last_context_data) 121 | .def("parameters_set", &SEALContext::parameters_set) 122 | .def("first_parms_id", &SEALContext::first_parms_id) 123 | .def("last_parms_id", &SEALContext::last_parms_id) 124 | .def("using_keyswitching", &SEALContext::using_keyswitching) 125 | .def("from_cipher_str", [](const SEALContext &context, const std::string &str){ 126 | Ciphertext cipher; 127 | std::stringstream in(std::ios::binary | std::ios::in); 128 | in.str(str); 129 | cipher.load(context, in); 130 | return cipher; 131 | }) 132 | .def("from_plain_str", [](const SEALContext &context, const std::string &str){ 133 | Plaintext plain; 134 | std::stringstream in(std::ios::binary | std::ios::in); 135 | in.str(str); 136 | plain.load(context, in); 137 | return plain; 138 | }) 139 | .def("from_secret_str", [](const SEALContext &context, const std::string &str){ 140 | SecretKey secret; 141 | std::stringstream in(std::ios::binary | std::ios::in); 142 | in.str(str); 143 | secret.load(context, in); 144 | return secret; 145 | }) 146 | .def("from_public_str", [](const SEALContext &context, const std::string &str){ 147 | PublicKey public_; 148 | std::stringstream in(std::ios::binary | std::ios::in); 149 | in.str(str); 150 | public_.load(context, in); 151 | return public_; 152 | }) 153 | .def("from_relin_str", [](const SEALContext &context, const std::string &str){ 154 | RelinKeys relin; 155 | std::stringstream in(std::ios::binary | std::ios::in); 156 | in.str(str); 157 | relin.load(context, in); 158 | return relin; 159 | }) 160 | .def("from_galois_str", [](const SEALContext &context, const std::string &str){ 161 | GaloisKeys galois; 162 | std::stringstream in(std::ios::binary | std::ios::in); 163 | in.str(str); 164 | galois.load(context, in); 165 | return galois; 166 | }); 167 | 168 | // modulus.h 169 | py::class_(m, "Modulus") 170 | .def(py::init()) 171 | .def("bit_count", &Modulus::bit_count) 172 | .def("value", &Modulus::value) 173 | .def("is_zero", &Modulus::is_zero) 174 | .def("is_prime", &Modulus::is_prime); 175 | //save & load 176 | 177 | // modulus.h 178 | py::class_(m, "CoeffModulus") 179 | .def_static("MaxBitCount", &CoeffModulus::MaxBitCount, py::arg(), py::arg()=sec_level_type::tc128) 180 | .def_static("BFVDefault", &CoeffModulus::BFVDefault, py::arg(), py::arg()=sec_level_type::tc128) 181 | .def_static("Create", py::overload_cast>(&CoeffModulus::Create)) 182 | .def_static("Create", py::overload_cast>(&CoeffModulus::Create)); 183 | 184 | // modulus.h 185 | py::class_(m, "PlainModulus") 186 | .def_static("Batching", py::overload_cast(&PlainModulus::Batching)) 187 | .def_static("Batching", py::overload_cast>(&PlainModulus::Batching)); 188 | 189 | // plaintext.h 190 | py::class_(m, "Plaintext") 191 | .def(py::init<>()) 192 | .def(py::init<std::size_t>()) 193 | .def(py::init<std::size_t, std::size_t>()) 194 | .def(py::init<const std::string &>()) 195 | .def(py::init<const Plaintext &>()) 196 | .def("set_zero", py::overload_cast<std::size_t, std::size_t>(&Plaintext::set_zero)) 197 | .def("set_zero", py::overload_cast<std::size_t>(&Plaintext::set_zero)) 198 | .def("set_zero", py::overload_cast<>(&Plaintext::set_zero)) 199 | .def("is_zero", &Plaintext::is_zero) 200 | .def("capacity", &Plaintext::capacity) 201 | .def("coeff_count", &Plaintext::coeff_count) 202 | .def("significant_coeff_count", &Plaintext::significant_coeff_count) 203 | .def("nonzero_coeff_count", &Plaintext::nonzero_coeff_count) 204 | .def("to_string", &Plaintext::to_string) 205 | .def("is_ntt_form", &Plaintext::is_ntt_form) 206 | .def("parms_id", py::overload_cast<>(&Plaintext::parms_id, py::const_)) 207 | .def("scale", py::overload_cast<>(&Plaintext::scale, py::const_)) 208 | .def("scale", [](Plaintext &plain, double scale){ 209 | plain.scale() = scale; 210 | }) 211 | .def("save", [](const Plaintext &plain, const std::string &path){ 212 | std::ofstream out(path, std::ios::binary); 213 | plain.save(out); 214 | out.close(); 215 | }) 216 | .def("load", [](Plaintext &plain, const SEALContext &context, const std::string &path){ 217 | std::ifstream in(path, std::ios::binary); 218 | plain.load(context, in); 219 | in.close(); 220 | }) 221 | .def("save_size", [](const Plaintext &plain){ 222 | return plain.save_size(); 223 | }) 224 | .def("to_string", [](const Plaintext &plain){ 225 | std::stringstream out(std::ios::binary | std::ios::out); 226 | plain.save(out); 227 | return py::bytes(out.str()); 228 | }); 229 | 230 | // ciphertext.h 231 | py::class_<Ciphertext>(m, "Ciphertext") 232 | .def(py::init<>()) 233 | .def(py::init<const SEALContext &>()) 234 | .def(py::init<const SEALContext &, parms_id_type>()) 235 | .def(py::init<const SEALContext &, parms_id_type, std::size_t>()) 236 | .def(py::init<const Ciphertext &>()) 237 | .def("coeff_modulus_size", &Ciphertext::coeff_modulus_size) 238 | .def("poly_modulus_degree", &Ciphertext::poly_modulus_degree) 239 | .def("size", &Ciphertext::size) 240 | .def("size_capacity", &Ciphertext::size_capacity) 241 | .def("is_transparent", &Ciphertext::is_transparent) 242 | .def("is_ntt_form", py::overload_cast<>(&Ciphertext::is_ntt_form, py::const_)) 243 | .def("parms_id", py::overload_cast<>(&Ciphertext::parms_id, py::const_)) 244 | .def("scale", py::overload_cast<>(&Ciphertext::scale, py::const_)) 245 | .def("scale", [](Ciphertext &cipher, double scale){ 246 | cipher.scale() = scale; 247 | }) 248 | .def("save", [](const Ciphertext &cipher, const std::string &path){ 249 | std::ofstream out(path, std::ios::binary); 250 | cipher.save(out); 251 | out.close(); 252 | }) 253 | .def("load", [](Ciphertext &cipher, const SEALContext &context, const std::string &path){ 254 | std::ifstream in(path, std::ios::binary); 255 | cipher.load(context, in); 256 | in.close(); 257 | }) 258 | .def("save_size", [](const Ciphertext &cipher){ 259 | return cipher.save_size(); 260 | }) 261 | .def("to_string", [](const Ciphertext &cipher){ 262 | std::stringstream out(std::ios::binary | std::ios::out); 263 | cipher.save(out); 264 | return py::bytes(out.str()); 265 | }); 266 | 267 | // secretkey.h 268 | py::class_<SecretKey>(m, "SecretKey") 269 | .def(py::init<>()) 270 | .def(py::init<const SecretKey &>()) 271 | .def("parms_id", py::overload_cast<>(&SecretKey::parms_id, py::const_)) 272 | .def("save", [](const SecretKey &sk, const std::string &path){ 273 | std::ofstream out(path, std::ios::binary); 274 | sk.save(out); 275 | out.close(); 276 | }) 277 | .def("load", [](SecretKey &sk, const SEALContext &context, const std::string &path){ 278 | std::ifstream in(path, std::ios::binary); 279 | sk.load(context, in); 280 | in.close(); 281 | }) 282 | .def("to_string", [](const SecretKey &secret){ 283 | std::stringstream out(std::ios::binary | std::ios::out); 284 | secret.save(out); 285 | return py::bytes(out.str()); 286 | }); 287 | 288 | // publickey.h 289 | py::class_<PublicKey>(m, "PublicKey") 290 | .def(py::init<>()) 291 | .def(py::init<const PublicKey &>()) 292 | .def("parms_id", py::overload_cast<>(&PublicKey::parms_id, py::const_)) 293 | .def("save", [](const PublicKey &pk, const std::string &path){ 294 | std::ofstream out(path, std::ios::binary); 295 | pk.save(out); 296 | out.close(); 297 | }) 298 | .def("load", [](PublicKey &pk, const SEALContext &context, const std::string &path){ 299 | std::ifstream in(path, std::ios::binary); 300 | pk.load(context, in); 301 | in.close(); 302 | }) 303 | .def("to_string", [](const PublicKey &public_){ 304 | std::stringstream out(std::ios::binary | std::ios::out); 305 | public_.save(out); 306 | return py::bytes(out.str()); 307 | }); 308 | 309 | // kswitchkeys.h 310 | py::class_<KSwitchKeys>(m, "KSwitchKeys") 311 | .def(py::init<>()) 312 | .def(py::init<const KSwitchKeys &>()) 313 | .def("size", &KSwitchKeys::size) 314 | .def("parms_id", py::overload_cast<>(&KSwitchKeys::parms_id, py::const_)) 315 | .def("save", [](const KSwitchKeys &ksk, const std::string &path){ 316 | std::ofstream out(path, std::ios::binary); 317 | ksk.save(out); 318 | out.close(); 319 | }) 320 | .def("load", [](KSwitchKeys &ksk, const SEALContext &context, const std::string &path){ 321 | std::ifstream in(path, std::ios::binary); 322 | ksk.load(context, in); 323 | in.close(); 324 | }); 325 | 326 | // relinkeys.h 327 | py::class_<RelinKeys, KSwitchKeys>(m, "RelinKeys") 328 | .def(py::init<>()) 329 | .def(py::init<const RelinKeys::KSwitchKeys &>()) 330 | .def("size", &RelinKeys::KSwitchKeys::size) 331 | .def("parms_id", py::overload_cast<>(&RelinKeys::KSwitchKeys::parms_id, py::const_)) 332 | .def_static("get_index", &RelinKeys::get_index) 333 | .def("has_key", &RelinKeys::has_key) 334 | .def("save", [](const RelinKeys &rk, const std::string &path){ 335 | std::ofstream out(path, std::ios::binary); 336 | rk.save(out); 337 | out.close(); 338 | }) 339 | .def("load", [](RelinKeys &rk, const SEALContext &context, const std::string &path){ 340 | std::ifstream in(path, std::ios::binary); 341 | rk.load(context, in); 342 | in.close(); 343 | }) 344 | .def("to_string", [](const RelinKeys &relin){ 345 | std::stringstream out(std::ios::binary | std::ios::out); 346 | relin.save(out); 347 | return py::bytes(out.str()); 348 | }); 349 | 350 | // galoiskeys.h 351 | py::class_<GaloisKeys, KSwitchKeys>(m, "GaloisKeys") 352 | .def(py::init<>()) 353 | .def(py::init<const GaloisKeys::KSwitchKeys &>()) 354 | .def("size", &GaloisKeys::KSwitchKeys::size) 355 | .def("parms_id", py::overload_cast<>(&GaloisKeys::KSwitchKeys::parms_id, py::const_)) 356 | .def_static("get_index", &GaloisKeys::get_index) 357 | .def("has_key", &GaloisKeys::has_key) 358 | .def("save", [](const GaloisKeys &gk, const std::string &path){ 359 | std::ofstream out(path, std::ios::binary); 360 | gk.save(out); 361 | out.close(); 362 | }) 363 | .def("load", [](GaloisKeys &gk, const SEALContext &context, const std::string &path){ 364 | std::ifstream in(path, std::ios::binary); 365 | gk.load(context, in); 366 | in.close(); 367 | }) 368 | .def("to_string", [](const GaloisKeys &galois){ 369 | std::stringstream out(std::ios::binary | std::ios::out); 370 | galois.save(out); 371 | return py::bytes(out.str()); 372 | }); 373 | 374 | // keygenerator.h 375 | py::class_<KeyGenerator>(m, "KeyGenerator") 376 | .def(py::init<const SEALContext &>()) 377 | .def(py::init<const SEALContext &, const SecretKey &>()) 378 | .def("secret_key", &KeyGenerator::secret_key) 379 | .def("create_public_key", py::overload_cast<PublicKey &>(&KeyGenerator::create_public_key, py::const_)) 380 | .def("create_relin_keys", py::overload_cast<RelinKeys &>(&KeyGenerator::create_relin_keys)) 381 | .def("create_galois_keys", py::overload_cast<const std::vector<int> &, GaloisKeys &>(&KeyGenerator::create_galois_keys)) 382 | .def("create_galois_keys", py::overload_cast<GaloisKeys &>(&KeyGenerator::create_galois_keys)) 383 | .def("create_public_key", [](KeyGenerator &keygen){ 384 | PublicKey pk; 385 | keygen.create_public_key(pk); 386 | return pk; 387 | }) 388 | .def("create_relin_keys", [](KeyGenerator &keygen){ 389 | RelinKeys rk; 390 | keygen.create_relin_keys(rk); 391 | return rk; 392 | }) 393 | .def("create_galois_keys", [](KeyGenerator &keygen){ 394 | GaloisKeys gk; 395 | keygen.create_galois_keys(gk); 396 | return gk; 397 | }); 398 | 399 | // encryptor.h 400 | py::class_<Encryptor>(m, "Encryptor") 401 | .def(py::init<const SEALContext &, const PublicKey &>()) 402 | .def(py::init<const SEALContext &, const SecretKey &>()) 403 | .def(py::init<const SEALContext &, const PublicKey &, const SecretKey &>()) 404 | .def("set_public_key", &Encryptor::set_public_key) 405 | .def("set_secret_key", &Encryptor::set_secret_key) 406 | .def("encrypt_zero", [](const Encryptor &encryptor){ 407 | Ciphertext encrypted; 408 | encryptor.encrypt_zero(encrypted); 409 | return encrypted; 410 | }) 411 | .def("encrypt", [](const Encryptor &encryptor, const Plaintext &plain){ 412 | Ciphertext encrypted; 413 | encryptor.encrypt(plain, encrypted); 414 | return encrypted; 415 | }) 416 | .def("encrypt_symmetric", [](const Encryptor &encryptor, const Plaintext &plain){ 417 | Ciphertext encrypted; 418 | encryptor.encrypt_symmetric(plain, encrypted); 419 | return encrypted; 420 | }); 421 | 422 | // evaluator.h 423 | py::class_<Evaluator>(m, "Evaluator") 424 | .def(py::init<const SEALContext &>()) 425 | .def("negate_inplace", &Evaluator::negate_inplace) 426 | .def("negate", [](Evaluator &evaluator, const Ciphertext &encrypted1){ 427 | Ciphertext destination; 428 | evaluator.negate(encrypted1, destination); 429 | return destination; 430 | }) 431 | .def("add_inplace", &Evaluator::add_inplace) 432 | .def("add", [](Evaluator &evaluator, const Ciphertext &encrypted1, const Ciphertext &encrypted2){ 433 | Ciphertext destination; 434 | evaluator.add(encrypted1, encrypted2, destination); 435 | return destination; 436 | }) 437 | .def("add_many", [](Evaluator &evaluator, const std::vector<Ciphertext> &encrypteds){ 438 | Ciphertext destination; 439 | evaluator.add_many(encrypteds, destination); 440 | return destination; 441 | }) 442 | .def("sub_inplace", &Evaluator::sub_inplace) 443 | .def("sub", [](Evaluator &evaluator, const Ciphertext &encrypted1, const Ciphertext &encrypted2){ 444 | Ciphertext destination; 445 | evaluator.sub(encrypted1, encrypted2, destination); 446 | return destination; 447 | }) 448 | .def("multiply_inplace", [](Evaluator &evaluator, Ciphertext &encrypted1, const Ciphertext &encrypted2){ 449 | evaluator.multiply_inplace(encrypted1, encrypted2); 450 | }) 451 | .def("multiply", [](Evaluator &evaluator, const Ciphertext &encrypted1, const Ciphertext &encrypted2){ 452 | Ciphertext destination; 453 | evaluator.multiply(encrypted1, encrypted2, destination); 454 | return destination; 455 | }) 456 | .def("square_inplace", [](Evaluator &evaluator, Ciphertext &encrypted1){ 457 | evaluator.square_inplace(encrypted1); 458 | }) 459 | .def("square", [](Evaluator &evaluator, const Ciphertext &encrypted1){ 460 | Ciphertext destination; 461 | evaluator.square(encrypted1, destination); 462 | return destination; 463 | }) 464 | .def("relinearize_inplace", [](Evaluator &evaluator, Ciphertext &encrypted1, const RelinKeys &relin_keys){ 465 | evaluator.relinearize_inplace(encrypted1, relin_keys); 466 | }) 467 | .def("relinearize", [](Evaluator &evaluator, const Ciphertext &encrypted1, const RelinKeys &relin_keys){ 468 | Ciphertext destination; 469 | evaluator.relinearize(encrypted1, relin_keys, destination); 470 | return destination; 471 | }) 472 | .def("mod_switch_to_next", [](Evaluator &evaluator, const Ciphertext &encrypted){ 473 | Ciphertext destination; 474 | evaluator.mod_switch_to_next(encrypted, destination); 475 | return destination; 476 | }) 477 | .def("mod_switch_to_next_inplace", [](Evaluator &evaluator, Ciphertext &encrypted){ 478 | evaluator.mod_switch_to_next_inplace(encrypted); 479 | }) 480 | .def("mod_switch_to_next_inplace", py::overload_cast<Plaintext &>(&Evaluator::mod_switch_to_next_inplace, py::const_)) 481 | .def("mod_switch_to_next", [](Evaluator &evaluator, const Plaintext &plain){ 482 | Plaintext destination; 483 | evaluator.mod_switch_to_next(plain, destination); 484 | return destination; 485 | }) 486 | .def("mod_switch_to_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, parms_id_type parms_id){ 487 | evaluator.mod_switch_to_inplace(encrypted, parms_id); 488 | }) 489 | .def("mod_switch_to", [](Evaluator &evaluator, const Ciphertext &encrypted, parms_id_type parms_id){ 490 | Ciphertext destination; 491 | evaluator.mod_switch_to(encrypted, parms_id, destination); 492 | return destination; 493 | }) 494 | .def("mod_switch_to_inplace", py::overload_cast<Plaintext &, parms_id_type>(&Evaluator::mod_switch_to_inplace, py::const_)) 495 | .def("mod_switch_to", [](Evaluator &evaluator, const Plaintext &plain, parms_id_type parms_id){ 496 | Plaintext destination; 497 | evaluator.mod_switch_to(plain, parms_id, destination); 498 | return destination; 499 | }) 500 | .def("rescale_to_next", [](Evaluator &evaluator, const Ciphertext &encrypted){ 501 | Ciphertext destination; 502 | evaluator.rescale_to_next(encrypted, destination); 503 | return destination; 504 | }) 505 | .def("rescale_to_next_inplace", [](Evaluator &evaluator, Ciphertext &encrypted){ 506 | evaluator.rescale_to_next_inplace(encrypted); 507 | }) 508 | .def("rescale_to_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, parms_id_type parms_id){ 509 | evaluator.rescale_to_inplace(encrypted, parms_id); 510 | }) 511 | .def("rescale_to", [](Evaluator &evaluator, const Ciphertext &encrypted, parms_id_type parms_id){ 512 | Ciphertext destination; 513 | evaluator.rescale_to(encrypted, parms_id, destination); 514 | return destination; 515 | }) 516 | .def("multiply_many", [](Evaluator &evaluator, const std::vector<Ciphertext> &encrypteds, const RelinKeys &relin_keys){ 517 | Ciphertext destination; 518 | evaluator.multiply_many(encrypteds, relin_keys, destination); 519 | return destination; 520 | }) 521 | .def("exponentiate_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, std::uint64_t exponent, const RelinKeys &relin_keys){ 522 | evaluator.exponentiate_inplace(encrypted, exponent, relin_keys); 523 | }) 524 | .def("exponentiate", [](Evaluator &evaluator, const Ciphertext &encrypted, std::uint64_t exponent, const RelinKeys &relin_keys){ 525 | Ciphertext destination; 526 | evaluator.exponentiate(encrypted, exponent, relin_keys, destination); 527 | return destination; 528 | }) 529 | .def("add_plain_inplace", &Evaluator::add_plain_inplace) 530 | .def("add_plain", [](Evaluator &evaluator, const Ciphertext &encrypted, const Plaintext &plain){ 531 | Ciphertext destination; 532 | evaluator.add_plain(encrypted, plain, destination); 533 | return destination; 534 | }) 535 | .def("sub_plain_inplace", &Evaluator::sub_plain_inplace) 536 | .def("sub_plain", [](Evaluator &evaluator, const Ciphertext &encrypted, const Plaintext &plain){ 537 | Ciphertext destination; 538 | evaluator.sub_plain(encrypted, plain, destination); 539 | return destination; 540 | }) 541 | .def("multiply_plain_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, const Plaintext &plain){ 542 | evaluator.multiply_plain_inplace(encrypted, plain); 543 | }) 544 | .def("multiply_plain", [](Evaluator &evaluator, const Ciphertext &encrypted, const Plaintext &plain){ 545 | Ciphertext destination; 546 | evaluator.multiply_plain(encrypted, plain, destination); 547 | return destination; 548 | }) 549 | .def("transform_to_ntt_inplace", [](Evaluator &evaluator, Plaintext &plain, parms_id_type parms_id){ 550 | evaluator.transform_to_ntt_inplace(plain,parms_id); 551 | }) 552 | .def("transform_to_ntt", [](Evaluator &evaluator, const Plaintext &plain, parms_id_type parms_id){ 553 | Plaintext destination_ntt; 554 | evaluator.transform_to_ntt(plain, parms_id, destination_ntt); 555 | return destination_ntt; 556 | }) 557 | .def("transform_to_ntt_inplace", py::overload_cast<Ciphertext &>(&Evaluator::transform_to_ntt_inplace, py::const_)) 558 | .def("transform_to_ntt", [](Evaluator &evaluator, const Ciphertext &encrypted){ 559 | Ciphertext destination_ntt; 560 | evaluator.transform_to_ntt(encrypted, destination_ntt); 561 | return destination_ntt; 562 | }) 563 | .def("transform_from_ntt_inplace", &Evaluator::transform_from_ntt_inplace) 564 | .def("transform_from_ntt", [](Evaluator &evaluator, const Ciphertext &encrypted_ntt){ 565 | Ciphertext destination; 566 | evaluator.transform_from_ntt(encrypted_ntt, destination); 567 | return destination; 568 | }) 569 | .def("apply_galois_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, std::uint32_t galois_elt, const GaloisKeys &galois_keys){ 570 | evaluator.apply_galois_inplace(encrypted, galois_elt, galois_keys); 571 | }) 572 | .def("apply_galois", [](Evaluator &evaluator, const Ciphertext &encrypted, std::uint32_t galois_elt, const GaloisKeys &galois_keys){ 573 | Ciphertext destination; 574 | evaluator.apply_galois(encrypted, galois_elt, galois_keys, destination); 575 | return destination; 576 | }) 577 | .def("rotate_rows_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ 578 | evaluator.rotate_rows_inplace(encrypted, steps, galois_keys); 579 | }) 580 | .def("rotate_rows", [](Evaluator &evaluator, const Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ 581 | Ciphertext destination; 582 | evaluator.rotate_rows(encrypted, steps, galois_keys, destination); 583 | return destination; 584 | }) 585 | .def("rotate_columns_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, const GaloisKeys &galois_keys){ 586 | evaluator.rotate_columns_inplace(encrypted, galois_keys); 587 | }) 588 | .def("rotate_columns", [](Evaluator &evaluator, const Ciphertext &encrypted, const GaloisKeys &galois_keys){ 589 | Ciphertext destination; 590 | evaluator.rotate_columns(encrypted, galois_keys, destination); 591 | return destination; 592 | }) 593 | .def("rotate_vector_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ 594 | evaluator.rotate_vector_inplace(encrypted, steps, galois_keys); 595 | }) 596 | .def("rotate_vector", [](Evaluator &evaluator, const Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys){ 597 | Ciphertext destination; 598 | evaluator.rotate_vector(encrypted, steps, galois_keys, destination); 599 | return destination; 600 | }) 601 | .def("complex_conjugate_inplace", [](Evaluator &evaluator, Ciphertext &encrypted, const GaloisKeys &galois_keys){ 602 | evaluator.complex_conjugate_inplace(encrypted, galois_keys); 603 | }) 604 | .def("complex_conjugate", [](Evaluator &evaluator, const Ciphertext &encrypted, const GaloisKeys &galois_keys){ 605 | Ciphertext destination; 606 | evaluator.complex_conjugate(encrypted, galois_keys, destination); 607 | return destination; 608 | }); 609 | 610 | // ckks.h 611 | py::class_<CKKSEncoder>(m, "CKKSEncoder") 612 | .def(py::init<const SEALContext &>()) 613 | .def("slot_count", &CKKSEncoder::slot_count) 614 | .def("encode", [](CKKSEncoder &encoder, py::array_t<double> values, double scale){ 615 | py::buffer_info buf = values.request(); 616 | if (buf.ndim != 1) 617 | throw std::runtime_error("E101: Number of dimensions must be one"); 618 | 619 | double *ptr = (double *)buf.ptr; 620 | std::vector<double> vec(buf.shape[0]); 621 | 622 | for (auto i = 0; i < buf.shape[0]; i++) 623 | vec[i] = ptr[i]; 624 | 625 | Plaintext pt; 626 | encoder.encode(vec, scale, pt); 627 | return pt; 628 | }) 629 | .def("encode", [](CKKSEncoder &encoder, double value, double scale){ 630 | Plaintext pt; 631 | encoder.encode(value, scale, pt); 632 | return pt; 633 | }) 634 | .def("decode", [](CKKSEncoder &encoder, const Plaintext &plain){ 635 | std::vector<double> destination; 636 | encoder.decode(plain, destination); 637 | 638 | py::array_t<double> values(destination.size()); 639 | py::buffer_info buf = values.request(); 640 | double *ptr = (double *)buf.ptr; 641 | 642 | for (auto i = 0; i < buf.shape[0]; i++) 643 | ptr[i] = destination[i]; 644 | 645 | return values; 646 | }); 647 | 648 | // decryptor.h 649 | py::class_<Decryptor>(m, "Decryptor") 650 | .def(py::init<const SEALContext &, const SecretKey &>()) 651 | .def("decrypt", &Decryptor::decrypt) 652 | .def("invariant_noise_budget", &Decryptor::invariant_noise_budget) 653 | .def("decrypt", [](Decryptor &decryptor, const Ciphertext &encrypted){ 654 | Plaintext pt; 655 | decryptor.decrypt(encrypted, pt); 656 | return pt; 657 | }); 658 | 659 | // batchencoder.h 660 | py::class_<BatchEncoder>(m, "BatchEncoder") 661 | .def(py::init<const SEALContext &>()) 662 | .def("slot_count", &BatchEncoder::slot_count) 663 | .def("encode", [](BatchEncoder &encoder, py::array_t<std::int64_t> values){ 664 | py::buffer_info buf = values.request(); 665 | if (buf.ndim != 1) 666 | throw std::runtime_error("E101: Number of dimensions must be one"); 667 | 668 | std::int64_t *ptr = (std::int64_t *)buf.ptr; 669 | std::vector<std::int64_t> vec(buf.shape[0]); 670 | 671 | for (auto i = 0; i < buf.shape[0]; i++) 672 | vec[i] = ptr[i]; 673 | 674 | Plaintext pt; 675 | encoder.encode(vec, pt); 676 | return pt; 677 | }) 678 | .def("decode", [](BatchEncoder &encoder, const Plaintext &plain){ 679 | std::vector<std::int64_t> destination; 680 | encoder.decode(plain, destination); 681 | 682 | py::array_t<std::int64_t> values(destination.size()); 683 | py::buffer_info buf = values.request(); 684 | std::int64_t *ptr = (std::int64_t *)buf.ptr; 685 | 686 | for (auto i = 0; i < buf.shape[0]; i++) 687 | ptr[i] = destination[i]; 688 | 689 | return values; 690 | }); 691 | } 692 | --------------------------------------------------------------------------------