├── .bandit ├── .clang-format ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── hekit-recipes │ ├── helib.toml │ ├── palisade.toml │ └── seal.toml ├── scripts │ ├── check-repo-contents-file.sh │ ├── run-bandit.sh │ ├── run-mypy.sh │ └── run-pylint.sh └── workflows │ ├── README.md │ └── github-ci.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .pylintrc ├── .shellcheckrc ├── .typos.toml ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CPPLINT.cfg ├── LICENSE ├── QUICK_START.md ├── README.md ├── default.config ├── deployments ├── README.md └── config_psi │ ├── README.md │ ├── psi │ ├── CMakeLists.txt │ ├── src │ │ └── configurable_psi.cpp │ └── tests │ │ ├── CMakeLists.txt │ │ ├── TestLookup.cpp │ │ └── main.cpp │ ├── scripts │ ├── README.md │ ├── columns.toml │ ├── config.py │ ├── config.toml │ ├── datagen.py │ ├── decode.py │ ├── encode.py │ ├── example_result.ptxt │ ├── natural.py │ ├── ptxt.py │ └── tests │ │ ├── test_datagen.py │ │ ├── test_decode.py │ │ └── test_encode.py │ ├── setenv.sh │ └── tests │ └── test_config_psi.py ├── dev_reqs.txt ├── docker ├── Dockerfile.base ├── Dockerfile.samples ├── Dockerfile.toolkit ├── Dockerfile.vscode ├── README.md ├── basic-docker-test.sh ├── dockerfiles.toml ├── ide-config │ └── settings.json ├── repo-inventory.txt └── runners.sh ├── he-samples ├── cmake │ ├── gbenchmark.cmake │ ├── gflags.cmake │ ├── gtest.cmake │ └── palisade.cmake ├── examples │ ├── logistic-regression │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── datasets │ │ │ ├── .gitignore │ │ │ ├── CMakeLists.txt │ │ │ ├── example_kaggle_homecreditdefaultrisk.ipynb │ │ │ ├── example_synthetic_data.ipynb │ │ │ ├── generate_data.py │ │ │ ├── lr_base.py │ │ │ └── requirements.txt │ │ ├── include │ │ │ ├── data_loader.hpp │ │ │ ├── logger.hpp │ │ │ ├── lr_helper.hpp │ │ │ ├── lrhe.hpp │ │ │ ├── lrhe_kernel.hpp │ │ │ ├── omp_utils.h │ │ │ └── timer.hpp │ │ └── src │ │ │ ├── data_loader.cpp │ │ │ ├── lr_helper.cpp │ │ │ ├── lr_test.cpp │ │ │ ├── lrhe.cpp │ │ │ ├── lrhe_kernel.cpp │ │ │ ├── omp_utils.cpp │ │ │ └── timer.cpp │ ├── psi │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── datasets │ │ │ ├── ancient_egyptian_gods.set │ │ │ ├── fruits.set │ │ │ └── us_states.set │ │ └── src │ │ │ └── psi.cpp │ └── secure-query │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── datasets │ │ └── us_state_capitals.csv │ │ ├── images │ │ ├── encoding_diagram.png │ │ ├── mask_comparison.png │ │ └── query_key_comparison.png │ │ ├── include │ │ ├── sq_helper_functions.h │ │ ├── sq_types.h │ │ ├── sqclient.h │ │ ├── sqserver.h │ │ └── timer.h │ │ └── src │ │ ├── main.cpp │ │ ├── sqclient.cpp │ │ └── sqserver.cpp └── sample-kernels │ ├── CMakeLists.txt │ ├── README.md │ ├── kernel_util.h │ ├── kernels │ ├── CMakeLists.txt │ ├── omp_utils.h │ ├── palisade │ │ ├── omp_utils_palisade.cpp │ │ ├── omp_utils_palisade.h │ │ ├── palisade_bfv_kernel_executor.cpp │ │ ├── palisade_bfv_kernel_executor.h │ │ ├── palisade_ckks_kernel_executor.cpp │ │ ├── palisade_ckks_kernel_executor.h │ │ ├── palisade_omp_utils.cpp │ │ ├── palisade_omp_utils.h │ │ └── palisade_util.h │ └── seal │ │ ├── seal_bfv_context.cpp │ │ ├── seal_bfv_context.h │ │ ├── seal_bfv_kernel_executor.cpp │ │ ├── seal_bfv_kernel_executor.h │ │ ├── seal_ckks_context.cpp │ │ ├── seal_ckks_context.h │ │ ├── seal_ckks_kernel_executor.cpp │ │ ├── seal_ckks_kernel_executor.h │ │ ├── seal_omp_utils.cpp │ │ └── seal_omp_utils.h │ ├── main.cpp │ ├── palisade │ ├── palisade_bfv_sample.cpp │ ├── palisade_ckks_sample.cpp │ └── palisade_util.h │ ├── seal │ ├── seal_bfv_sample.cpp │ └── seal_ckks_sample.cpp │ └── test │ ├── CMakeLists.txt │ ├── README.md │ ├── main.cpp │ ├── test_palisade.cpp │ ├── test_palisade_bfv_kernel_executor.cpp │ ├── test_palisade_ckks_kernel_executor.cpp │ ├── test_seal.cpp │ ├── test_seal_bfv_kernel_executor.cpp │ ├── test_seal_ckks_kernel_executor.cpp │ └── test_util.h ├── hekit ├── kit ├── PLUGINS.md ├── README.md ├── __init__.py ├── commands │ ├── __init__.py │ ├── check_deps.py │ ├── docker_build.py │ ├── init.py │ ├── install.py │ ├── list_cmd.py │ ├── new.py │ ├── plugin.py │ └── remove.py ├── hekit.py ├── tools │ ├── README.md │ ├── __init__.py │ ├── algebras.py │ └── gen_primes.py └── utils │ ├── __init__.py │ ├── archive.py │ ├── component_builder.py │ ├── config.py │ ├── constants.py │ ├── docker_tools.py │ ├── files.py │ ├── primes.py │ ├── spec.py │ ├── subparsers.py │ ├── tab_completion.py │ ├── tsort.py │ ├── typing.py │ └── yes.py ├── mypy.ini ├── notes ├── .gitignore ├── AdvancedTopics.tex ├── CyclotomicsGaloisNotes.tex ├── GroupsNotes.tex ├── GroupsPresentation.tex ├── InauguralHEPresentation.tex ├── NumberTheoryNotes.tex ├── NumberTheoryPresentation.tex ├── README.md ├── RingsAndFieldsNotes.tex ├── RingsAndFieldsPresentation.tex └── images │ ├── Gauss.pdf │ ├── HomomorphicData.pdf │ ├── LatticeWithBasis.pdf │ ├── Leonhard-Euler.pdf │ ├── StandardData.pdf │ ├── clock.pdf │ ├── erat.pdf │ ├── fermat.pdf │ ├── png2pdf.pdf │ └── poincare_henri1.pdf ├── recipes ├── config-psi.toml ├── default.toml ├── examples.toml ├── sample-kernels.toml └── test.toml ├── requirements.txt ├── tests ├── __init__.py ├── common_utils.py ├── input_files │ ├── default.config │ ├── default_symlink.config │ ├── default_wrong.config │ ├── depend_wrong.txt │ ├── dependencies.txt │ ├── test.toml │ ├── test_missing_quotes.toml │ ├── test_missing_value.toml │ ├── test_symlink.toml │ ├── test_tsort.toml │ ├── test_two_instances.toml │ └── test_wrong_format.toml ├── test_cmd_check_deps.py ├── test_cmd_docker_build.py ├── test_cmd_init.py ├── test_cmd_install.py ├── test_cmd_list.py ├── test_cmd_new.py ├── test_cmd_plugins.py ├── test_cmd_remove.py ├── test_integration_algebras.py ├── test_integration_check_deps.py ├── test_integration_docker.py ├── test_integration_gen_primes.py ├── test_integration_init.py ├── test_integration_install.py ├── test_integration_wrong_inputs.py ├── test_tool_algebras.py ├── test_util_comp_builder.py ├── test_util_config.py ├── test_util_docker_tool.py ├── test_util_files.py ├── test_util_primes.py ├── test_util_spec.py ├── test_util_subparsers.py ├── test_util_tab_comp.py └── test_util_tsort.py ├── third-party ├── README.md └── hello_world_plugin │ ├── __init__.py │ ├── hello.py │ ├── plugin.toml │ └── start.py └── tools └── README.md /.bandit: -------------------------------------------------------------------------------- 1 | [bandit] 2 | exclude: /tests 3 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | BasedOnStyle: Google 5 | Language: Cpp 6 | DerivePointerAlignment: false 7 | PointerAlignment: Left 8 | # Let cpplint enforce header order 9 | SortIncludes: false 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Version Information (Put N/A for Not Applicable)** 11 | | Software | Version(s) | 12 | | ---------------------------| --------------- | 13 | | Linux | | 14 | | Git | | 15 | | CMake | | 16 | | Compiler (GCC, Clang, etc.)| | 17 | | Python | | 18 | | Doxygen | | 19 | | HE Toolkit | | 20 | | Recipe File(s) Used | | 21 | 22 | **Describe the bug**\ 23 | A clear and concise description of what the bug is. 24 | 25 | **To Reproduce**\ 26 | Steps to reproduce the behavior: 27 | 1. Go to '...' 28 | 2. Click on '....' 29 | 3. Scroll down to '....' 30 | 4. etc. 31 | 5. See error 32 | 33 | **Expected behavior**\ 34 | A clear and concise description of what you expected to happen. 35 | 36 | **Screenshots**\ 37 | If applicable, add screenshots to help explain your problem. 38 | 39 | **Additional context**\ 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 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 12 | frustrated when [...]_ 13 | 14 | **Describe the solution you would like**\ 15 | A clear and concise description of what you want to happen. 16 | 17 | **Describe alternatives you have considered**\ 18 | A clear and concise description of any alternative solutions or features you 19 | have considered. 20 | 21 | **Additional context**\ 22 | Add any other context or screenshots about the feature request here. 23 | 24 | **If the feature request is approved, would you be willing to submit a PR**\ 25 | Yes / No _(Help can be provided if you need assistance submitting a PR)_ 26 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Proposed changes 2 | 3 | Describe the big picture of your changes here to communicate to the maintainers 4 | why we should accept this pull request. If it fixes a bug or resolves a feature 5 | request, be sure to link to that issue. 6 | 7 | ## Types of changes 8 | 9 | What types of changes does your code introduce to the HE Toolkit project? 10 | _Put an `x` in the boxes that apply_ 11 | 12 | - [ ] Bugfix (non-breaking change which fixes an issue) 13 | - [ ] New feature (non-breaking change which adds functionality) 14 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 15 | - [ ] Documentation update (if none of the other choices apply) 16 | 17 | ## Checklist 18 | 19 | _Put an `x` in the boxes that apply. You can also fill these out after creating 20 | the PR. If you are unsure about any of them, do not hesitate to ask. We are 21 | here to help! This is simply a reminder of what we are going to look for before 22 | merging your code._ 23 | 24 | - [ ] I have read the [CONTRIBUTING](https://github.com/intel/he-toolkit/tree/main#contributing) section 25 | - [ ] Current formatting and unit tests / base functionality passes locally with my changes 26 | - [ ] I have added tests that prove my fix is effective or that my feature works (if appropriate) 27 | - [ ] I have added necessary documentation (if appropriate) 28 | - [ ] Any dependent changes have been merged and published in downstream modules 29 | 30 | ## Further comments 31 | 32 | If this is a relatively large or complex change, kick off the discussion by 33 | explaining why you chose the solution you did, what alternatives you 34 | considered, etc. 35 | -------------------------------------------------------------------------------- /.github/hekit-recipes/helib.toml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | [[hexl]] 5 | skip = false 6 | version = "1.2.5" 7 | name = "v1.2.5" 8 | fetch = "git clone https://github.com/intel/hexl.git --branch v%version%" 9 | init_fetch_dir = "fetch" 10 | init_build_dir = "build" 11 | init_install_dir = "build" 12 | pre-build = """cmake -S %init_fetch_dir%/hexl -B %init_build_dir% 13 | -DCMAKE_INSTALL_PREFIX=%export_install_dir% 14 | -DHEXL_BENCHMARK=OFF 15 | -DHEXL_TESTING=OFF""" 16 | build = "cmake --build %init_build_dir% -j" 17 | install = "cmake --install %init_install_dir%" 18 | export_install_dir = "install" 19 | export_cmake = "install/lib/cmake/hexl-%version%" 20 | 21 | 22 | [[ntl]] 23 | skip = false 24 | version = "11.5.1" 25 | name = "v11.5.1" 26 | init_fetch_dir = "fetch" 27 | init_pre_build_dir = "build" 28 | init_build_dir = "build/ntl-%version%/src" 29 | init_install_dir = "build/ntl-%version%/src" 30 | fetch = "wget https://libntl.org/ntl-%version%.tar.gz" 31 | post-fetch = "bash -c 'tar -xvf %init_fetch_dir%/ntl-%version%.tar.gz -C %init_pre_build_dir%'" 32 | build = "bash -c './configure NTL_GMP_LIP=on NTL_THREADS=on NTL_THREAD_BOOST=on TUNE=generic PREFIX=%export_install_dir% && make -j'" 33 | install = "make install" 34 | export_install_dir = "install" 35 | 36 | 37 | [[helib]] 38 | skip = false 39 | version = "v2.2.2" 40 | name = "v2.2.2" 41 | root = "%name%" 42 | init_fetch_dir = "fetch" 43 | init_build_dir = "build" 44 | init_install_dir = "build" 45 | export_install_dir = "install" 46 | fetch = "git clone https://github.com/homenc/HElib.git --branch %version%" 47 | pre-build = """cmake -S %init_fetch_dir%/HElib -B %init_build_dir% 48 | -DCMAKE_INSTALL_PREFIX=%export_install_dir% 49 | -DNTL_DIR=$%ntl%/export_install_dir$ 50 | -DUSE_INTEL_HEXL=ON 51 | -DNTL_DIR=$%ntl%/export_install_dir$ 52 | -DHEXL_DIR=$%hexl%/export_cmake$""" 53 | build = "cmake --build %init_build_dir% -j" 54 | post-build = "" 55 | install = "cmake --install %init_install_dir%" 56 | export_cmake = "install/share/cmake/helib" 57 | # Dependencies 58 | ntl = "ntl/v11.5.1" 59 | hexl = "hexl/v1.2.5" 60 | 61 | 62 | [[examples]] 63 | skip = false 64 | name = "psi" 65 | fetch = "" 66 | init_fetch_dir = "" 67 | src_dir = "!toolkit-path!/he-samples/examples/psi" 68 | init_build_dir = "build" 69 | pre-build = """cmake -S %src_dir% -B %init_build_dir% 70 | -Dhelib_DIR=$%helib%/export_cmake$""" 71 | build = "cmake --build %init_build_dir% -j" 72 | post-build = "" 73 | init_install_dir = "" 74 | install = "" 75 | # Dependencies 76 | helib = "helib/v2.2.2" 77 | -------------------------------------------------------------------------------- /.github/hekit-recipes/palisade.toml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | [[hexl]] 5 | skip = false 6 | version = "1.2.3" 7 | name = "v1.2.3" 8 | fetch = "git clone https://github.com/intel/hexl.git --branch v%version%" 9 | init_fetch_dir = "fetch" 10 | init_build_dir = "build" 11 | init_install_dir = "build" 12 | pre-build = """cmake -S %init_fetch_dir%/hexl -B %init_build_dir% 13 | -DCMAKE_INSTALL_PREFIX=%export_install_dir% 14 | -DHEXL_BENCHMARK=OFF 15 | -DHEXL_TESTING=OFF""" 16 | build = "cmake --build %init_build_dir% -j" 17 | install = "cmake --install %init_install_dir%" 18 | export_install_dir = "install" 19 | export_cmake = "install/lib/cmake/hexl-%version%" 20 | 21 | 22 | [[palisade]] 23 | skip = false 24 | version = "v1.11.6" 25 | name = "v1.11.6" 26 | root = "%name%" 27 | init_fetch_dir = "fetch" 28 | init_build_dir = "build" 29 | init_install_dir = "build" 30 | export_install_dir = "install" 31 | fetch = "git clone https://gitlab.com/palisade/palisade-release.git --branch %version%" 32 | pre-build = """cmake -S %init_fetch_dir%/palisade-release -B %init_build_dir% 33 | -DCMAKE_INSTALL_PREFIX=%export_install_dir% 34 | -DBUILD_UNITTESTS=OFF 35 | -DBUILD_EXAMPLES=OFF 36 | -DBUILD_BENCHMARKS=OFF 37 | -DWITH_INTEL_HEXL=ON 38 | -DINTEL_HEXL_PREBUILT=ON 39 | -DINTEL_HEXL_HINT_DIR=$%hexl%/export_cmake$""" 40 | build = "cmake --build %init_build_dir% -j" 41 | post-build = "" 42 | install = "cmake --install %init_install_dir%" 43 | export_cmake = "install/lib/Palisade" 44 | # Dependencies 45 | hexl = "hexl/v1.2.3" 46 | 47 | 48 | [[sample-kernels]] 49 | skip = false 50 | name = "palisade" 51 | fetch = "" 52 | init_fetch_dir = "" 53 | src_dir = "!toolkit-path!/he-samples/sample-kernels" 54 | init_build_dir = "build" 55 | pre-build = """cmake -S %src_dir% -B %init_build_dir% 56 | -DENABLE_PALISADE=ON 57 | -DPalisade_DIR=$%palisade%/export_cmake$ 58 | -DHEXL_DIR=$%hexl%/export_cmake$""" 59 | build = "cmake --build %init_build_dir% -j" 60 | post-build = "" 61 | init_install_dir = "" 62 | install = "" 63 | # Dependencies 64 | palisade = "palisade/v1.11.6" 65 | hexl = "hexl/v1.2.3" 66 | -------------------------------------------------------------------------------- /.github/scripts/check-repo-contents-file.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (C) 2022 Intel Corporation 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | SUCCESS=0 6 | FAILURE=1 7 | 8 | REPO_CONTENTS_FILE="$1" 9 | 10 | # Check file exists 11 | if [ ! -f "$REPO_CONTENTS_FILE" ]; then 12 | echo "FAILURE: File '$REPO_CONTENTS_FILE' does not exist." 13 | exit $FAILURE 14 | fi 15 | 16 | # Generate new file, overwrite old file 17 | if ! git ls-files > "$REPO_CONTENTS_FILE"; then 18 | echo "FAILURE: Something is wrong with git ls-files command." 19 | exit $FAILURE 20 | fi 21 | 22 | # Check if file has changed 23 | if [ -n "$(git diff -- "$REPO_CONTENTS_FILE")" ]; then 24 | echo "FAILURE: $REPO_CONTENTS_FILE was not up to date. Updated now!" 25 | exit $FAILURE 26 | fi 27 | 28 | exit $SUCCESS 29 | -------------------------------------------------------------------------------- /.github/scripts/run-bandit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (C) 2022 Intel Corporation 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | SUCCESS=0 6 | FAILURE=1 7 | 8 | if ! bandit -r .; then 9 | echo "FAILURE: bandit failed. You need to manually correct the errors." 10 | exit $FAILURE 11 | fi 12 | 13 | exit $SUCCESS 14 | -------------------------------------------------------------------------------- /.github/scripts/run-mypy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (C) 2022 Intel Corporation 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | SUCCESS=0 6 | FAILURE=1 7 | 8 | if ! type -p mypy > /dev/null; then 9 | echo "FAILURE: mypy not found." 10 | exit $FAILURE 11 | fi 12 | 13 | # shellcheck disable=SC2068 14 | if ! MYPYPATH=kit mypy $@ --ignore-missing-imports; then 15 | echo "FAILURE: mypy failed. You need to manually correct the errors." 16 | exit $FAILURE 17 | fi 18 | 19 | exit $SUCCESS 20 | -------------------------------------------------------------------------------- /.github/scripts/run-pylint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (C) 2022 Intel Corporation 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | SUCCESS=0 6 | FAILURE=1 7 | 8 | # shellcheck disable=SC2068 9 | if ! pylint $@; then 10 | echo "FAILURE: pylint failed. You need to manually correct the errors." 11 | exit $FAILURE 12 | fi 13 | 14 | exit $SUCCESS 15 | -------------------------------------------------------------------------------- /.github/workflows/README.md: -------------------------------------------------------------------------------- 1 | # CI Notes 2 | 3 | CI runs with Python 3.10 on Ubuntu 22.04 4 | 5 | Check the [github-ci.yml](./github-ci.yml) for the versions of the tooling used. 6 | 7 | ## Compilers tested 8 | 9 | ### Ubuntu 22.04 10 | | | HELIB | SEAL | 11 | |:------------:|:-----:|:-----:| 12 | | **clang-14** | Pass | Pass | 13 | | **gcc-11** | Pass | Pass | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # Visual Studio 5 | .vscode/ 6 | 7 | # Python notebook 8 | .ipynb_checkpoints/ 9 | 10 | build/ 11 | __pycache__ 12 | he-benchmarks-venv/ 13 | 14 | # MyPy 15 | .mypy_cache/ 16 | 17 | # vim 18 | **.swp 19 | 20 | # MacOS 21 | .DS_Store 22 | 23 | # toolkit docker build artifacts 24 | __staging__/ 25 | 26 | # HElib 27 | **/helib.log 28 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | repos: 5 | - repo: https://github.com/pre-commit/pre-commit-hooks 6 | rev: v4.4.0 7 | hooks: 8 | - id: trailing-whitespace 9 | - id: end-of-file-fixer 10 | - id: check-merge-conflict 11 | - id: mixed-line-ending 12 | - id: fix-byte-order-marker 13 | - id: check-yaml 14 | - repo: https://github.com/crate-ci/typos 15 | rev: v1.16.8 16 | hooks: 17 | - id: typos 18 | - repo: local 19 | hooks: 20 | - id: clang-format 21 | name: clang-format 22 | entry: clang-format 23 | language: system 24 | files: \.(c|cc|cxx|cpp|h|hpp|hxx|js|proto)$ 25 | args: 26 | - -i 27 | - id: shfmt 28 | name: shfmt 29 | entry: shfmt 30 | language: system 31 | files: \.sh$ 32 | args: 33 | - -d 34 | - -i 35 | - '2' 36 | - -ci 37 | - -sr 38 | - -bn 39 | - -w 40 | - id: cpplint 41 | name: cpplint 42 | entry: cpplint 43 | language: system 44 | files: \.(c|cc|cxx|cpp|h|hpp|hxx)$ 45 | args: 46 | - --recursive 47 | - id: shellcheck 48 | name: shellcheck 49 | entry: shellcheck 50 | language: system 51 | files: \.sh$ 52 | args: 53 | - -s 54 | - bash 55 | - -e 56 | - SC1091 57 | - id: black 58 | name: black 59 | entry: black 60 | language: system 61 | files: \.py$ 62 | - id: repo-inventory 63 | name: repo-inventory 64 | entry: .github/scripts/check-repo-contents-file.sh 65 | language: script 66 | # Always run because other renamed, modified, or deleted files need to 67 | # be reflected in the contents file. 68 | always_run: true 69 | # This forces only docker/repo-inventory.txt to be considered and passed 70 | # in the an arg. Not having at least 'files' defined can lead to 71 | # overwrite of other files. 72 | pass_filenames: false 73 | files: docker/repo-inventory.txt 74 | args: [docker/repo-inventory.txt] 75 | - id: bandit 76 | name: bandit 77 | entry: .github/scripts/run-bandit.sh 78 | language: script 79 | pass_filenames: false 80 | - id: mypy-deployments 81 | name: mypy-deployments 82 | entry: .github/scripts/run-mypy.sh 83 | language: script 84 | pass_filenames: false 85 | args: 86 | - deployments 87 | - id: mypy-kit 88 | name: mypy-kit 89 | entry: .github/scripts/run-mypy.sh 90 | language: script 91 | pass_filenames: false 92 | args: 93 | - -p 94 | - kit 95 | - id: pylint 96 | name: pylint 97 | entry: .github/scripts/run-pylint.sh 98 | language: script 99 | pass_filenames: false 100 | args: 101 | - kit 102 | - deployments 103 | -------------------------------------------------------------------------------- /.shellcheckrc: -------------------------------------------------------------------------------- 1 | disable=SC2269 # Disable SC2269 (info): This variable is assigned to itself, so the assignment does nothing. 2 | disable=SC2294 (warning): eval negates the benefit of arrays. Drop eval to preserve whitespace/symbols (or eval as string). 3 | -------------------------------------------------------------------------------- /.typos.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | extend-ignore-re = ["helo byd"] 3 | 4 | [default.extend-identifiers] 5 | 6 | 7 | [default.extend-words] 8 | # variation of params 9 | parms = "parms" 10 | # It would be best to ignore the dataset file. 11 | # However, known issue https://github.com/crate-ci/typos/issues/347 12 | # typos cannot ignore specified files when used with pre-commit 13 | # Ancient Egyption God 14 | Ba = "Ba" 15 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # Default codeowner for all files 5 | # * @intel/he-toolkit-maintain 6 | * @faberga @kylanerace @jobottle @hamishun @jlhcrawford @skmono 7 | -------------------------------------------------------------------------------- /CPPLINT.cfg: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | filter=+build,-build/c++11,-build/include_subdir 5 | filter=+legal 6 | 7 | # Readability 8 | filter=+readability 9 | 10 | # Runtime 11 | filter=-runtime 12 | 13 | # Whitespace 14 | filter=+whitespace 15 | -------------------------------------------------------------------------------- /QUICK_START.md: -------------------------------------------------------------------------------- 1 | # Quick Start Guide 2 | 3 | This guide is for those who wish to get set up quickly with Intel HE Toolkit. 4 | 5 | In this guide, you will get up and running with a VS Code enabled docker 6 | container. 7 | 8 | ## HE Toolkit installation 9 | 10 | ### Step 1 Clone the repo 11 | Make sure you have `git` on your system. 12 | ```bash 13 | git clone https://github.com/intel/he-toolkit.git 14 | ``` 15 | 16 | ### Step 2 Install the system dependencies for the toolkit 17 | Move into your `he-toolkit` directory. 18 | ```bash 19 | cd he-toolkit 20 | ``` 21 | 22 | Make sure your python >= 3.10 and that you have pip. 23 | ```bash 24 | python --version 25 | pip install -r requirements.txt 26 | ``` 27 | 28 | ### Step 3: Initialize the `hekit` command 29 | For a default initialization of the toolkit, you will be asked whether to 30 | modify your shell configuration file which you may wish to do yourself instead. 31 | ```bash 32 | ./hekit init --default-config 33 | ``` 34 | 35 | ## Running the docker build 36 | 37 | ### Step 1 Sanity check 38 | Check that the `docker` command is on your system. 39 | 40 | Moreover, check that you are in the `docker` group. You can use the `id` 41 | command to check. The `docker` group should appear as one of your groups. 42 | 43 | ### Step 2 Using VS Code Server 44 | For an IDE experience, we recommend building the VS Code enabled docker 45 | container. 46 | ```bash 47 | hekit docker-build --enable vscode 48 | ``` 49 | 50 | ### Step 3 Run your container 51 | To run the container run 52 | ```bash 53 | docker run -d -p ::8888 /ubuntu_he_vscode: 54 | ``` 55 | Note if you do not specify an IP address, it will default to `localhost` 56 | (0.0.0.0). 57 | 58 | ### Step 4 Connect to the VSCode IDE through the browser 59 | Using the browser of your choice, navigate to `:`. eg., 60 | `localhost:1234`. 61 | 62 | If you cannot remember what IP/port was assigned to the docker container, run 63 | ```bash 64 | docker ps 65 | ``` 66 | to list the current running containers and it should list the IP/port used by 67 | your container. 68 | 69 | ## Run an HE example program 70 | 71 | ### Step 1 View available compiled example programs 72 | Through your browser, go to the terminal pane in VS Code and type, 73 | ```bash 74 | source ~/he-toolkit/docker/runners.sh 75 | ``` 76 | This will provide access to a list of example programs that can be run from the 77 | command line. 78 | 79 | To view the list of programs run 80 | ```bash 81 | welcome_message 82 | ``` 83 | -------------------------------------------------------------------------------- /default.config: -------------------------------------------------------------------------------- 1 | repo_location = "~/.hekit/components" 2 | -------------------------------------------------------------------------------- /deployments/README.md: -------------------------------------------------------------------------------- 1 | # Deployments 2 | 3 | The top level `deployment` directory is to contain standalone programs that can 4 | be deployed to perform useful HE operations. 5 | 6 | All code in the top level `deployment` directory should be considered currently 7 | experimental. As such, APIs are subject to change even between minor and 8 | patch version. 9 | -------------------------------------------------------------------------------- /deployments/config_psi/psi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | ##################################################################### 5 | ## Configurable Private Set Intersection (PSI) Engine 6 | ##################################################################### 7 | 8 | project(psi LANGUAGES CXX) 9 | 10 | cmake_minimum_required(VERSION 3.22 FATAL_ERROR) 11 | 12 | ## Use -std=c++17 as default. 13 | set(CMAKE_CXX_STANDARD 17) 14 | ## Disable C++ extensions 15 | set(CMAKE_CXX_EXTENSIONS OFF) 16 | ## Require full C++ standard 17 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 18 | 19 | # Define standard installation directories (GNU) 20 | include(GNUInstallDirs) 21 | 22 | # Set default output folder 23 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY 24 | "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}") 25 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY 26 | "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}") 27 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY 28 | "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}") 29 | 30 | option(ENABLE_TESTS "Enable GTests" OFF) 31 | 32 | find_package(helib REQUIRED) 33 | 34 | set(UTILS_DIR "${helib_DIR}/../../../../fetch/HElib/utils/common") 35 | set(PSI_DIR "${helib_DIR}/../../../../fetch/HElib/misc/psi/io") 36 | 37 | add_executable(psi ${CMAKE_CURRENT_SOURCE_DIR}/src/configurable_psi.cpp) 38 | 39 | target_include_directories(psi PRIVATE ${UTILS_DIR} ${PSI_DIR}) 40 | 41 | target_link_libraries(psi helib) 42 | 43 | if (ENABLE_TESTS) 44 | add_subdirectory(tests) 45 | endif() 46 | -------------------------------------------------------------------------------- /deployments/config_psi/psi/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | set(GTEST_SRC 5 | TestLookup.cpp 6 | ) 7 | 8 | add_executable(unit-test main.cpp ${GTEST_SRC}) 9 | 10 | target_include_directories(unit-test PRIVATE ${UTILS_DIR} ${PSI_DIR}) 11 | 12 | find_package(GTest REQUIRED) 13 | target_link_libraries(unit-test PRIVATE GTest::gtest) 14 | target_link_libraries(unit-test PRIVATE helib) 15 | -------------------------------------------------------------------------------- /deployments/config_psi/psi/tests/main.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation 2 | * SPDX-License-Identifier: Apache-2.0 3 | */ 4 | 5 | #include 6 | 7 | int main(int argc, char* argv[]) { 8 | ::testing::InitGoogleTest(&argc, argv); 9 | return RUN_ALL_TESTS(); 10 | } 11 | -------------------------------------------------------------------------------- /deployments/config_psi/scripts/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Introduction 3 | 4 | This directory contains helper scripts for encoding and decoding data related 5 | to the configurable PSI deployment. 6 | 7 | ## Encode 8 | The `encode.py` script is used for encoding raw data. To view the usage 9 | description, run the script with the help option 10 | ```bash 11 | ./encode.py -h 12 | ``` 13 | 14 | The encoder expects raw data in CSV format as input. This data will be rows of 15 | space separated entries where the first row contains column headings and the 16 | subsequent rows contain the raw data entries. These entries can be 17 | alphanumeric, alphabetical, or numeric. 18 | 19 | The encoder can be used to encode both query (client) and database (server) 20 | data. By default the encoder will perform client encoding but using the 21 | `--server` flag, the encoder will perform server encoding instead. 22 | 23 | Server encoding attempts to encode each row and column entry as a single 24 | plaintext whereas the client encoding attempts to pack each raw query row into 25 | a single row of plaintexts. 26 | 27 | The encoder additionally accepts an optional config file via `--config 28 | `. If none is provided then the encoder will use the default 29 | config file provided in [config.toml](config.toml) which can be used as an 30 | example. 31 | 32 | This config file specifies information on how to encode the data through the 33 | specification of the main HE parameters (`m` and `p`) as well as descriptors 34 | about the column and data entry formats. 35 | 36 | By default the encoder outputs to stdout, thus to save the output to a file one 37 | can use 38 | ```bash 39 | ./encode.py data.raw > data.encoded 40 | ``` 41 | This will encode the data in `data.raw` into plaintext(s) written to 42 | `data.encoded`. To see an example plaintext output of the encoder see 43 | [example_result.ptxt](example_result.ptxt). 44 | 45 | ## Decode 46 | The `decode.py` script is used for decoding plaintext results from the 47 | configurable PSI program. To view the usage description, run the script with 48 | the help option 49 | ```bash 50 | ./decode.py -h 51 | ``` 52 | 53 | The decoder script reads in plaintext data which is expected to only contain 54 | the values {0,1} in its valid form. Like the encoder, this script also accepts 55 | an optional config file via `--config `. 56 | 57 | Additionally, one can specify the number of plaintext entries for the decoder 58 | to read from the file with `--entries ENTRIES`. **NOTE:** This number cannot 59 | exceed the total number of plaintext entries contained in the input file. 60 | 61 | Given a plaintext entry like below 62 | ``` 63 | 1 64 | {"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[1],[0],[1],[0]]},"serializationVersion":"0.0.1","type":"Ptxt"} 65 | ``` 66 | the decoder will read the values of the `slots` entry of the JSON object and 67 | will return a positive result every time it detects a 1. 68 | 69 | For example, given the plaintext provided above the decoder will produce 70 | ``` 71 | Match on line '1' 72 | Match on line '3' 73 | ``` 74 | 75 | The decoder will notify the user of any entries it views as corrupted if it 76 | detects any value greater than 1. 77 | -------------------------------------------------------------------------------- /deployments/config_psi/scripts/columns.toml: -------------------------------------------------------------------------------- 1 | [columnA] 2 | datatype = "alphabetic" 3 | char_size = 4 4 | 5 | 6 | [columnB] 7 | datatype = "numeric" 8 | char_size = 5 9 | 10 | 11 | [columnC] 12 | datatype = "alphanumeric" 13 | char_size = 6 14 | 15 | 16 | [columnD] 17 | datatype = ['AB', 'CD', 'EF', 'GH', 'IJ', 'KL', 'MN', 'OP'] 18 | char_size = 1 19 | -------------------------------------------------------------------------------- /deployments/config_psi/scripts/config.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """Module for Config helper object""" 5 | 6 | 7 | from __future__ import annotations 8 | from dataclasses import dataclass 9 | from typing import Dict, Optional 10 | 11 | import toml 12 | from ptxt import Params 13 | 14 | 15 | class ConfigError(Exception): 16 | """Errors related to configuration""" 17 | 18 | 19 | @dataclass(frozen=True) 20 | class Config: 21 | """The configuration of the encoded data""" 22 | 23 | params: Params 24 | columns: int 25 | segments: int 26 | encodings: Optional[Dict[str, str]] 27 | composites: Optional[Dict[str, int]] 28 | 29 | @classmethod 30 | def from_toml(cls, filename: str, params_only: bool = False) -> Config: 31 | """Reads in a params file equiv a simple TOML file""" 32 | with open(filename, encoding="UTF-8") as f: 33 | data: Dict = toml.load(f) 34 | 35 | # params 36 | params_data = data["params"] 37 | m, p = params_data["m"], params_data["p"] 38 | params = Params(m, p) 39 | 40 | # config 41 | config_data = data["config"] 42 | columns = config_data["columns"] 43 | segments = config_data["segments"] 44 | 45 | # sanity check 46 | if params.nslots % segments != 0: 47 | raise ConfigError( 48 | f"Segmentation divisor '{segments}' does not divide number of slots '{params.nslots}'" 49 | ) 50 | 51 | if params_only is True: 52 | return cls( 53 | params=params, 54 | columns=columns, 55 | segments=segments, 56 | encodings=None, 57 | composites=None, 58 | ) 59 | 60 | # column policies 61 | encodings = data["columns"]["encoding"] 62 | composites = data["columns"]["composite"] 63 | return cls( 64 | columns=columns, 65 | segments=segments, 66 | params=params, 67 | encodings=encodings, 68 | composites=composites, 69 | ) 70 | -------------------------------------------------------------------------------- /deployments/config_psi/scripts/config.toml: -------------------------------------------------------------------------------- 1 | [params] 2 | p = 257 3 | m = 80 4 | 5 | [config] 6 | columns = 3 7 | segments = 1 8 | 9 | [columns.encoding] 10 | column1 = "alphanumeric" 11 | column2 = "alphabetical" 12 | column3 = "numeric" 13 | 14 | [columns.composite] 15 | column2 = 2 16 | -------------------------------------------------------------------------------- /deployments/config_psi/scripts/datagen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (C) 2022 Intel Corporation 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | """Simple program to generate dummy data for configurable PSI""" 7 | 8 | from __future__ import annotations 9 | 10 | import argparse 11 | import random 12 | import sys 13 | import string 14 | from typing import Generator, List, Union 15 | 16 | import toml 17 | from pydantic import BaseModel, validator 18 | 19 | 20 | class ColumnsDescription(BaseModel): 21 | """Description on columns to generate.""" 22 | 23 | datatype: Union[str, List[str]] 24 | char_size: int = 1 25 | 26 | @validator("datatype") 27 | def _set_known_collections( 28 | cls, collection: Union[str, List[str]] 29 | ) -> Union[str, List[str]]: 30 | """Converts known collection names to the collections themselves.""" 31 | if isinstance(collection, list): 32 | return collection 33 | if collection == "alphabetic": 34 | return string.ascii_letters[26:] 35 | if collection == "alphanumeric": 36 | return string.ascii_letters[26:] + string.digits 37 | if collection == "numeric": 38 | return string.digits 39 | 40 | raise ValueError(f"Unknown collection name '{collection}'") 41 | 42 | @validator("char_size") 43 | def _greater_than_or_equal_to_one(cls, size: int): 44 | """check size is greater than or equal to one""" 45 | if size < 1: 46 | raise ValueError(f"size must be greater than or equal to one: '{size}'") 47 | return size 48 | 49 | 50 | def real_matches(filename: str, pick: int) -> List[str]: 51 | """Read in file and pick n lines at random. Return list.""" 52 | with open(filename, encoding="utf-8") as fstrm: 53 | fstrm.readline() # ignore header 54 | lines = [line.strip() for line in fstrm.readlines()] # slurp file 55 | return random.sample(lines, pick) 56 | 57 | 58 | def fake_rows(column_descriptions: List[ColumnsDescription], rows: int) -> Generator: 59 | """Generate fake entries.""" 60 | 61 | def generate_column(collection, size): 62 | return "".join(random.choices(collection, k=size)) # nosec B311 63 | 64 | return ( 65 | " ".join( 66 | generate_column(desc.datatype, desc.char_size) 67 | for desc in column_descriptions 68 | ) 69 | for _ in range(rows) 70 | ) 71 | 72 | 73 | def main(): 74 | """./datagen --total x 75 | ./datagen --total x --dbfile y --real z""" 76 | parser = argparse.ArgumentParser(description="Generate dummy data") 77 | parser.add_argument( 78 | "columns_description", 79 | metavar="column-description", 80 | type=str, 81 | help="description of columns", 82 | ) 83 | parser.add_argument( 84 | "dbfile", nargs="?", type=str, help="data file to take real matches from" 85 | ) 86 | parser.add_argument("--real", type=int, default=5, help="number of real matches") 87 | parser.add_argument("--total", type=int, default=10, help="total number of queries") 88 | args = parser.parse_args() 89 | 90 | tobj = toml.load(args.columns_description) 91 | columns_descriptions = [ColumnsDescription(**desc) for desc in tobj.values()] 92 | 93 | # print column headers 94 | print(" ".join(tobj.keys())) 95 | 96 | if args.dbfile is None: 97 | for row in fake_rows(columns_descriptions, args.total): 98 | print(row) 99 | sys.exit(0) 100 | 101 | # sanity check 102 | diff = args.total - args.real 103 | if diff < 0: 104 | print( 105 | f"Number of chosen real matches '{args.real}' must not exceed total '{args.total}'", 106 | file=sys.stderr, 107 | ) 108 | sys.exit(1) 109 | 110 | queries = real_matches(args.dbfile, args.real) 111 | queries.extend(fake_rows(columns_descriptions, diff)) 112 | random.shuffle(queries) # works in place 113 | 114 | for line in queries: 115 | print(line) 116 | 117 | 118 | if __name__ == "__main__": 119 | main() 120 | -------------------------------------------------------------------------------- /deployments/config_psi/scripts/decode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (C) 2022 Intel Corporation 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | """Decoder Program""" 7 | 8 | import sys 9 | import argparse 10 | from itertools import islice 11 | from functools import partial 12 | from typing import Iterable, List, Optional, Tuple, Union 13 | 14 | from ptxt import Ptxt 15 | from config import Config 16 | from natural import Natural 17 | 18 | 19 | def sum_vectors(*vectors: Iterable[int]) -> List: 20 | """Return as list sum of vectors""" 21 | return [sum(elems) for elems in zip(*vectors)] 22 | 23 | 24 | def sum_segments(slots, segment_divisor: int): 25 | """Returns a map object that segments the list of slots and return the sum.""" 26 | if segment_divisor < 1: 27 | raise ValueError( 28 | f"segment divisor must be a positive integer, not '{segment_divisor}'" 29 | ) 30 | if segment_divisor == 1: 31 | return slots 32 | 33 | quot, rem = divmod(len(slots), segment_divisor) 34 | if rem != 0: 35 | raise ValueError( 36 | f"Segment divisor '{segment_divisor}' does not divide list length '{len(slots)}'" 37 | ) 38 | 39 | segments = [islice(slots, i * quot, (i + 1) * quot) for i in range(segment_divisor)] 40 | 41 | return map(sum_vectors, *segments) 42 | 43 | 44 | def parse_header(header_line: str) -> Tuple[int, int]: 45 | """Return the number of rows and columns from the header line""" 46 | num_cols: Union[int, str] = 1 47 | dims = header_line.split() 48 | len_dims = len(dims) 49 | if len_dims == 1: 50 | num_rows = dims[0] 51 | elif len_dims == 2: 52 | num_rows, num_cols = dims 53 | else: 54 | raise ValueError("Too many dimensions in header line") 55 | 56 | return int(num_rows), int(num_cols) 57 | 58 | 59 | def parse_args(argv: Optional[List[str]] = None): 60 | """Parse argv either passed in or from cmdline""" 61 | parser = argparse.ArgumentParser(description="Decode result") 62 | parser.add_argument("datafile", type=str, help="data file to decode") 63 | parser.add_argument( 64 | "--config", 65 | type=partial(Config.from_toml, params_only=True), 66 | default="config.toml", 67 | help="set ptxt params and composite columns", 68 | ) 69 | parser.add_argument( 70 | "--entries", type=Natural, default=0, help="number of ptxt input queries" 71 | ) 72 | return parser.parse_args(argv) if argv else parser.parse_args() 73 | 74 | 75 | def main(args): 76 | """Decoder Program""" 77 | try: 78 | with open(args.datafile, encoding="UTF-8", newline="") as jobjs: 79 | parse_header(jobjs.readline()) # first line not json 80 | ptxt = Ptxt(args.config.params) 81 | line_num = 1 82 | for jobj in jobjs: 83 | ptxt.from_json(jobj) 84 | summed_segments = sum_segments(ptxt.slots(), args.config.segments) 85 | for line_num, slot in enumerate(summed_segments, line_num): 86 | if args.entries == 0 or args.entries >= line_num: 87 | value = sum(slot) 88 | if value == 1: 89 | print(f"Match on line '{line_num}'") 90 | continue 91 | if value > 1: 92 | print( 93 | f"Corruption line result '{line_num}' with slot '{slot}'" 94 | ) 95 | except ValueError as error: 96 | sys.stderr.write(f"{error!r}\n") 97 | sys.exit(1) 98 | 99 | 100 | if __name__ == "__main__": 101 | cmdline_args = parse_args() 102 | main(cmdline_args) 103 | -------------------------------------------------------------------------------- /deployments/config_psi/scripts/example_result.ptxt: -------------------------------------------------------------------------------- 1 | 1 1 2 | {"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[0,1],[2,3],[4,5],[6,7]]},"serializationVersion":"0.0.1","type":"Ptxt"} 3 | -------------------------------------------------------------------------------- /deployments/config_psi/scripts/natural.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """Module simple class representing the non-negative integers""" 5 | 6 | from __future__ import annotations 7 | from typing import Union 8 | 9 | 10 | class Natural: 11 | """Represent natural numbers including zero a.k.a non-negative integers.""" 12 | 13 | def __init__(self, number: Union[int, str]) -> None: 14 | self.number = int(number) 15 | if self.number < 0: 16 | raise ValueError( 17 | f"Number of entries must be a non-negative integer, not '{self.number}'" 18 | ) 19 | 20 | def __int__(self) -> int: 21 | return self.number 22 | 23 | def __le__(self, other: Union[Natural, int]) -> bool: 24 | return self.number <= int(other) 25 | 26 | def __ge__(self, other: Union[Natural, int]) -> bool: 27 | return self.number >= int(other) 28 | 29 | def __lt__(self, other: Union[Natural, int]) -> bool: 30 | return self.number < int(other) 31 | 32 | def __gt__(self, other: Union[Natural, int]) -> bool: 33 | return self.number > int(other) 34 | 35 | # mypy says arg for __eq__ should be type object 36 | def __eq__(self, other: object) -> bool: 37 | if not isinstance(other, (Natural, int)): 38 | raise NotImplementedError 39 | return self.number == int(other) 40 | -------------------------------------------------------------------------------- /deployments/config_psi/setenv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (C) 2022 Intel Corporation 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | progpath="${BASH_SOURCE[0]}" 7 | if [ "$progpath" == "$0" ]; then 8 | echo >&2 "$progpath must be sourced" 9 | exit 1 10 | fi 11 | 12 | progdir="$(dirname "$progpath")" 13 | 14 | CONFIG_PSI_DIR="$(realpath "$progdir")" 15 | export CONFIG_PSI_DIR 16 | 17 | PYTHONPATH="$PYTHONPATH:$CONFIG_PSI_DIR/scripts" 18 | export PYTHONPATH 19 | -------------------------------------------------------------------------------- /dev_reqs.txt: -------------------------------------------------------------------------------- 1 | # Recommended requirements 2 | -r requirements.txt 3 | 4 | # Testing 5 | pytest 6 | pytest-mock 7 | 8 | # Linting 9 | pylint 10 | bandit 11 | 12 | # Type analysis 13 | types-toml 14 | mypy 15 | -------------------------------------------------------------------------------- /docker/Dockerfile.base: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | ARG CUSTOM_FROM 5 | 6 | FROM $CUSTOM_FROM 7 | 8 | LABEL maintainer="HE Toolkit https://github.com/intel/he-toolkit" 9 | 10 | # Proxies, user configured 11 | ARG http_proxy 12 | ARG https_proxy 13 | ARG socks_proxy 14 | ARG ftp_proxy 15 | ARG no_proxy 16 | 17 | ARG TZ=UTC 18 | ARG DEBIAN_FRONTEND=noninteractive 19 | 20 | ENV http_proxy=$http_proxy \ 21 | https_proxy=$https_proxy \ 22 | socks_proxy=$socks_proxy \ 23 | ftp_proxy=$ftp_proxy \ 24 | no_proxy=$no_proxy 25 | 26 | RUN apt update && \ 27 | apt -y dist-upgrade && \ 28 | apt install -y apt-utils \ 29 | autoconf \ 30 | build-essential \ 31 | byobu \ 32 | clang-14 \ 33 | clang-format-14 \ 34 | cmake \ 35 | curl \ 36 | file \ 37 | g++-11 \ 38 | gcc-11 \ 39 | git \ 40 | htop \ 41 | jq \ 42 | libgmp-dev \ 43 | libomp-dev \ 44 | libterm-readline-gnu-perl \ 45 | m4 \ 46 | man \ 47 | --no-install-recommends tzdata \ 48 | patchelf \ 49 | # Workaround for CVE-2023-5752 will install upstream pip as user 50 | # python3-pip \ 51 | python-is-python3 \ 52 | software-properties-common \ 53 | sudo \ 54 | unzip \ 55 | vim \ 56 | virtualenv \ 57 | wget \ 58 | xz-utils && \ 59 | apt autoremove -y && \ 60 | apt autoclean -y && \ 61 | rm -rf /var/lib/apt/lists/* 62 | 63 | # Update alternatives clean up cpp configuration 64 | RUN update-alternatives --remove-all cpp 65 | # Update alternatives for GCC-11 66 | RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 11 \ 67 | --slave /usr/bin/g++ g++ /usr/bin/g++-11 \ 68 | --slave /usr/bin/gcov gcov /usr/bin/gcov-11 \ 69 | --slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-11 \ 70 | --slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-11 \ 71 | --slave /usr/bin/cpp cpp /usr/bin/cpp-11 \ 72 | --slave /usr/bin/gcov-tool gcov-tool /usr/bin/gcov-tool-11 \ 73 | --slave /usr/bin/gcov-dump gcov-dump /usr/bin/gcov-dump-11 \ 74 | --slave /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-11 \ 75 | --slave /usr/bin/lto-dump lto-dump /usr/bin/lto-dump-11 76 | # Set GCC-11 as default 77 | RUN update-alternatives --set gcc /usr/bin/gcc-11 78 | # Update alternatives for CLANG-14 79 | RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-14 14 \ 80 | --slave /usr/bin/clang++ clang++ /usr/bin/clang++-14 \ 81 | --slave /usr/bin/clang-format clang-format /usr/bin/clang-format-14 \ 82 | --slave /usr/bin/clang-cpp clang-cpp /usr/bin/clang-cpp-14 \ 83 | --slave /usr/bin/clang-format-diff clang-format-diff /usr/bin/clang-format-diff-14 84 | # Set CLANG-14 as default 85 | RUN update-alternatives --set clang /usr/bin/clang-14 86 | # Set timezone to UTC \ 87 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \ 88 | echo $TZ > /etc/timezone && \ 89 | dpkg-reconfigure --frontend noninteractive tzdata 90 | 91 | CMD /bin/bash 92 | -------------------------------------------------------------------------------- /docker/Dockerfile.samples: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | ARG CUSTOM_FROM 5 | 6 | FROM $CUSTOM_FROM 7 | 8 | LABEL maintainer="https://github.com/intel/he-toolkit" 9 | 10 | # User setup 11 | ARG UNAME 12 | ARG UID 13 | ARG GID 14 | ARG HOME="/home/$UNAME" 15 | ENV SHELL="/bin/bash" 16 | 17 | # Switch user to $UNAME 18 | USER $UNAME 19 | 20 | # Change directories to $HOME/he-toolkit 21 | WORKDIR $HOME/he-toolkit 22 | 23 | # Build and install libraries, examples, and kernels 24 | RUN ./hekit install recipes/default.toml && \ 25 | ./hekit build recipes/examples.toml --recipe_arg "toolkit-path=${HOME}/he-toolkit" && \ 26 | ./hekit build recipes/sample-kernels.toml --recipe_arg "toolkit-path=${HOME}/he-toolkit" 27 | 28 | ENTRYPOINT . docker/runners.sh && welcome_message && cd $HOME && /bin/bash 29 | -------------------------------------------------------------------------------- /docker/Dockerfile.toolkit: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # Username 5 | ARG TOOLKIT_BASE_IMAGE 6 | 7 | FROM $TOOLKIT_BASE_IMAGE 8 | 9 | LABEL maintainer="HE Toolkit github.com/intel/he-toolkit" 10 | 11 | # User-setup 12 | ARG UNAME 13 | ARG UID 14 | ARG GID 15 | ARG HOME="/home/$UNAME" 16 | ENV SHELL="/bin/bash" 17 | 18 | RUN groupadd -g $GID $UNAME && \ 19 | useradd --no-log-init -m -u $UID -g $GID -s /bin/bash $UNAME && \ 20 | echo "$UNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers 21 | 22 | # ADD copies and untars for us 23 | ADD toolkit.tar.gz $HOME/he-toolkit 24 | RUN chown -R $UNAME:$UNAME $HOME/he-toolkit 25 | 26 | # Switch user to $UNAME 27 | USER $UNAME 28 | 29 | ################################################### 30 | # CVE-2023-5752 31 | # Install upstream pip (version >=23.3.1) as user 32 | RUN mkdir -p $HOME/pip-install-package \ 33 | $HOME/.local/bin \ 34 | $HOME/.local/lib 35 | WORKDIR $HOME/pip-install-package 36 | RUN wget https://bootstrap.pypa.io/get-pip.py && \ 37 | python get-pip.py && \ 38 | . $HOME/.profile && \ 39 | pip install pip --upgrade 40 | ################################################### 41 | 42 | RUN ["pip", "install", "toml", "argcomplete"] 43 | 44 | # Change directories to $HOME/Intel-HE-Toolkit 45 | WORKDIR $HOME/he-toolkit 46 | 47 | # Initialize hekit and install librares, examples, and kernels 48 | RUN echo "y" | ./hekit init --default-config 49 | 50 | ENTRYPOINT . docker/runners.sh && welcome_message && cd $HOME && /bin/bash 51 | -------------------------------------------------------------------------------- /docker/Dockerfile.vscode: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # Username 5 | ARG CUSTOM_FROM 6 | 7 | FROM $CUSTOM_FROM 8 | 9 | ARG UNAME 10 | ARG HOME="/home/$UNAME" 11 | 12 | LABEL maintainer="https://github.com/intel/he-toolkit" 13 | 14 | # code-server runs on HTTPS port 8888 15 | EXPOSE 8888/tcp 16 | 17 | # Download and install the latest version of code-server 18 | RUN curl -fsSL https://code-server.dev/install.sh | sh 19 | 20 | # Create space for IDE settings and workspace 21 | RUN mkdir -p $HOME/User $HOME/he-workspace/.vscode && \ 22 | # Move code-server IDE settings into correct location 23 | cp $HOME/he-toolkit/docker/ide-config/settings.json $HOME/he-workspace/.vscode/ 24 | 25 | # Install code-server extensions 26 | RUN for e in vscode.cpp twxs.cmake ms-vscode.cmake-tools vadimcn.vscode-lldb; do \ 27 | code-server --user-data-dir=$HOME/ --install-extension $e; \ 28 | done 29 | 30 | # Update code-server user settings 31 | RUN echo '{"extensions.autoUpdate": false, "workbench.colorTheme": "Default Dark+"}' |\ 32 | jq . > $HOME/User/settings.json && \ 33 | # Create a randomly generated self-signed certificate for code-server \ 34 | sed -i.bak 's/cert: false/cert: true/' $HOME/.config/code-server/config.yaml 35 | 36 | ENTRYPOINT code-server $HOME/he-workspace \ 37 | --bind-addr 0.0.0.0:8888 \ 38 | --user-data-dir $HOME \ 39 | --auth none \ 40 | --disable-telemetry 41 | -------------------------------------------------------------------------------- /docker/basic-docker-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (C) 2020 Intel Corporation 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | set -e 7 | 8 | ftp_proxy="$ftp_proxy" 9 | socks_proxy="$socks_proxy" 10 | http_proxy="$http_proxy" 11 | http_proxy="$http_proxy" 12 | 13 | echo "Running as user $USER..." 14 | 15 | echo -e "\nTesting apt-get Config:" 16 | cat > /etc/apt/apt.conf << EOF 17 | Acquire::ftp::proxy "$ftp_proxy"; 18 | Acquire::socks::proxy "$socks_proxy"; 19 | Acquire::http::proxy "$http_proxy"; 20 | Acquire::https::proxy "$http_proxy"; 21 | EOF 22 | 23 | cat /etc/apt/apt.conf 24 | 25 | echo -e "\nTesting apt-get:" 26 | apt-get update && apt-get install -y wget 27 | 28 | echo -e "\nTesting wget:" 29 | wget http://www.google.com 30 | 31 | echo "DONE" 32 | -------------------------------------------------------------------------------- /docker/dockerfiles.toml: -------------------------------------------------------------------------------- 1 | # List of feature names with associated path to the relevant dockerfile. 2 | # Add to this list to enable building of custom dockerfiles via 3 | # `hekit docker-build --enable ` 4 | # NOTE: Paths must be absolute 5 | 6 | samples = "${HEKITPATH}/docker/Dockerfile.samples" 7 | vscode = "${HEKITPATH}/docker/Dockerfile.vscode" 8 | -------------------------------------------------------------------------------- /docker/ide-config/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "terminal.integrated.defaultProfile.linux": "bash", 3 | "cmake.sourceDirectory": "${workspaceFolder}", 4 | "cmake.configureOnOpen": true, 5 | "cmake.configureSettings": { 6 | "SEAL_PREBUILT": "ON", 7 | "PALISADE_PREBUILT": "ON", 8 | "HELIB_PREBUILT": "ON", 9 | "SEAL_DIR": "~/.hekit/components/seal/v3.7.2/install/lib/cmake/SEAL-3.7/", 10 | "PALISADE_DIR": "~/.hekit/components/palisade/v1.11.6/install/lib/Palisade/", 11 | "helib_DIR": "~/.hekit/components/helib/v2.2.1/install/share/cmake/helib/", 12 | "HEXL_DIR": "~/.hekit/components/hexl/1.2.3/install/lib/cmake/hexl-1.2.3/", 13 | "Microsoft.GSL_DIR": "~/.hekit/components/gsl/v3.1.0/install/share/cmake/Microsoft.GSL", 14 | "zstd_DIR": "~/.hekit/components/zstd/v1.4.5/install/lib/cmake/zstd" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docker/runners.sh: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | HEKIT_DIR="$HOME/.hekit/components" 5 | HEKIT_EXAMPLES="$HEKIT_DIR/examples" 6 | HEKIT_SAMPLE_KERNELS="$HEKIT_DIR/sample-kernels" 7 | 8 | __run_cmd() { 9 | ( 10 | set -e 11 | eval "$@" 12 | ) 13 | } 14 | 15 | run_lr_example() { 16 | ( 17 | set -e 18 | cd "$HEKIT_EXAMPLES/logistic-regression/build" 19 | OMP_NUM_THREADS="$(nproc)" ./lr_test "$@" 20 | ) 21 | } 22 | 23 | run_psi_example() { 24 | __run_cmd "$HEKIT_EXAMPLES"/psi/build/psi --server "$HEKIT_EXAMPLES"/psi/build/datasets/fruits.set "$@" 25 | } 26 | 27 | run_query_example() { 28 | ( 29 | echo -n "Initialize SEAL BFV scheme with default parameters[Y(default)|N]: " 30 | read -r params 31 | echo -n "Input file to use for database or press enter to use default[us_state_capitals.csv]: " 32 | read -r database 33 | echo -n "Input key value to use for database query: " 34 | read -r query 35 | set -e 36 | OMP_NUM_THREADS="$(nproc)" "$HEKIT_EXAMPLES"/secure-query/build/secure-query <<- EOF 37 | ${params} 38 | ${database} 39 | ${query} 40 | EOF 41 | ) 42 | } 43 | 44 | run_sample_kernels_palisade() { 45 | __run_cmd KMP_WARNINGS=0 OMP_NUM_THREADS="$(nproc)" "$HEKIT_SAMPLE_KERNELS"/palisade/build/sample-kernels-palisade --benchmark_min_time=2 46 | } 47 | 48 | run_sample_kernels_seal() { 49 | __run_cmd KMP_WARNINGS=0 OMP_NUM_THREADS="$(nproc)" "$HEKIT_SAMPLE_KERNELS"/seal/build/sample-kernels-seal --benchmark_min_time=2 50 | } 51 | 52 | run_tests() { 53 | __run_cmd OMP_NUM_THREADS="$(nproc)" "$HEKIT_SAMPLE_KERNELS"/palisade/build/test/unit-test 54 | __run_cmd OMP_NUM_THREADS="$(nproc)" "$HEKIT_SAMPLE_KERNELS"/seal/build/test/unit-test 55 | } 56 | 57 | welcome_message() { 58 | cat << EOF 59 | 60 | There are several bash functions sourced that you can run, 61 | 1. run_sample_kernels_[palisade|seal]: This will run several HE sample 62 | kernels including Matrix Multiplication and Logistic Regression. The 63 | script will display Wall time, CPU time, and the number of iterations that 64 | were run. 65 | 2. run_tests: This will run several unit tests to confirm the validity of the 66 | above sample kernels by comparing against the same operation in the non-HE 67 | space. 68 | 3. run_query_example: This will run a "Secure Query" example allowing users 69 | to query on a database of the 50 U.S. States while controlling 70 | (optionally) the crypto-parameters used. When prompted, enter a State and, 71 | if present, the corresponding City will be decoded and printed. 72 | 4. run_lr_example: This will run a "Logistic Regression" (LR) example 73 | allowing users to see a faster and more scalable method for LR in HE. 74 | Unlike the LR code available before in the sample-kernels, this version 75 | takes extra steps to utilize as many slots as possible in the ciphertexts. 76 | 5. run_psi_example: this will execute an example that uses the HElib library 77 | and the BGV scheme to compute the intersection of two given sets. The 78 | program requires as input, a file with the client set, this is mandatory 79 | parameter, so the user must create this file before executing the example. 80 | Use the flag "-h" for usage information. 81 | 82 | The bash functions are run via command line by name, e.g. run_tests. When done, 83 | you can terminate the docker container with the "exit" command or temporarily 84 | leave the docker container typing Ctrl+p then Ctrl+q. To re-enter a detached 85 | docker container you can reattach using \`docker attach \`. 86 | 87 | 88 | EOF 89 | } 90 | -------------------------------------------------------------------------------- /he-samples/cmake/gbenchmark.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | include(ExternalProject) 5 | 6 | # If user has not specified an install path, override the default usr/local to 7 | # be the build directory of the original target. 8 | if (NOT ${CMAKE_INSTALL_PREFIX}) 9 | set (CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}) 10 | endif() 11 | 12 | set(GBENCHMARK_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/ext_gbenchmark) 13 | 14 | set(GBENCHMARK_SRC_DIR ${GBENCHMARK_PREFIX}/src/ext_gbenchmark/) 15 | set(GBENCHMARK_BUILD_DIR ${GBENCHMARK_PREFIX}/src/ext_gbenchmark-build/) 16 | set(GBENCHMARK_REPO_URL https://github.com/google/benchmark.git) 17 | set(GBENCHMARK_GIT_TAG v1.5.6) 18 | 19 | set(GBENCHMARK_PATHS ${GBENCHMARK_SRC_DIR} ${GBENCHMARK_BUILD_DIR}/src/libbenchmark.a) 20 | 21 | ExternalProject_Add( 22 | ext_gbenchmark 23 | GIT_REPOSITORY ${GBENCHMARK_REPO_URL} 24 | GIT_TAG ${GBENCHMARK_GIT_TAG} 25 | PREFIX ${GBENCHMARK_PREFIX} 26 | CMAKE_ARGS ${BENCHMARK_FORWARD_CMAKE_ARGS} 27 | -DCMAKE_INSTALL_LIBDIR=${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} 28 | -DCMAKE_INSTALL_INCLUDEDIR=${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR} 29 | -DBENCHMARK_ENABLE_GTEST_TESTS=OFF 30 | -DBENCHMARK_ENABLE_TESTING=OFF 31 | -DCMAKE_BUILD_TYPE=Release 32 | BUILD_BYPRODUCTS ${GBENCHMARK_PATHS} 33 | # Skip updates 34 | UPDATE_COMMAND "" 35 | ) 36 | 37 | add_library(libgbenchmark INTERFACE) 38 | add_dependencies(libgbenchmark ext_gbenchmark) 39 | 40 | ExternalProject_Get_Property(ext_gbenchmark SOURCE_DIR BINARY_DIR) 41 | 42 | target_link_libraries(libgbenchmark INTERFACE ${GBENCHMARK_BUILD_DIR}/src/libbenchmark.a) 43 | 44 | target_include_directories(libgbenchmark SYSTEM 45 | INTERFACE ${GBENCHMARK_SRC_DIR}/include) 46 | -------------------------------------------------------------------------------- /he-samples/cmake/gflags.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | include(ExternalProject) 5 | 6 | # If user has not specified an install path, override the default usr/local to 7 | # be the build directory of the original target. 8 | if (NOT ${CMAKE_INSTALL_PREFIX}) 9 | set (CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}) 10 | endif() 11 | 12 | set(GFLAGS_GIT_REPO_URL https://github.com/gflags/gflags.git) 13 | set(GFLAGS_GIT_LABEL v2.2.2) 14 | 15 | ExternalProject_Add( 16 | ext_gflags 17 | PREFIX ext_gflags 18 | GIT_REPOSITORY ${GFLAGS_GIT_REPO_URL} 19 | GIT_TAG ${GFLAGS_GIT_LABEL} 20 | INSTALL_COMMAND "" 21 | UPDATE_COMMAND "" 22 | EXCLUDE_FROM_ALL TRUE) 23 | 24 | # ------------------------------------------------------------------------------ 25 | 26 | ExternalProject_Get_Property(ext_gflags SOURCE_DIR BINARY_DIR) 27 | 28 | add_library(libgflags INTERFACE) 29 | add_dependencies(libgflags ext_gflags) 30 | message(STATUS "libgflags include: ${BINARY_DIR}/include/") 31 | message(STATUS "libgflags library: ${BINARY_DIR}/lib/") 32 | 33 | target_include_directories(libgflags SYSTEM 34 | INTERFACE ${BINARY_DIR}/include) 35 | target_link_libraries(libgflags 36 | INTERFACE ${BINARY_DIR}/lib/libgflags.a) 37 | -------------------------------------------------------------------------------- /he-samples/cmake/gtest.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | include(ExternalProject) 5 | 6 | # If user has not specified an install path, override the default usr/local to 7 | # be the build directory of the original target. 8 | if (NOT ${CMAKE_INSTALL_PREFIX}) 9 | set (CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}) 10 | endif() 11 | 12 | set(GTEST_GIT_REPO_URL https://github.com/google/googletest.git) 13 | set(GTEST_GIT_LABEL release-1.12.1) 14 | set(GTEST_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") 15 | 16 | ExternalProject_Add( 17 | ext_gtest 18 | PREFIX ext_gtest 19 | GIT_REPOSITORY ${GTEST_GIT_REPO_URL} 20 | GIT_TAG ${GTEST_GIT_LABEL} 21 | CMAKE_ARGS ${BENCHMARK_FORWARD_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=Release 22 | INSTALL_COMMAND "" 23 | UPDATE_COMMAND "" 24 | EXCLUDE_FROM_ALL TRUE) 25 | 26 | ExternalProject_Get_Property(ext_gtest SOURCE_DIR BINARY_DIR) 27 | 28 | add_library(libgtest INTERFACE) 29 | add_dependencies(libgtest ext_gtest) 30 | 31 | target_include_directories(libgtest SYSTEM 32 | INTERFACE ${SOURCE_DIR}/googletest/include) 33 | target_link_libraries(libgtest 34 | INTERFACE ${BINARY_DIR}/lib/libgtest.a) 35 | -------------------------------------------------------------------------------- /he-samples/cmake/palisade.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | find_package(Palisade REQUIRED) 5 | 6 | message(STATUS "PALISADE_INCLUDE ${PALISADE_INCLUDE}") 7 | message(STATUS "PALISADE_LIBRARIES ${PALISADE_LIBRARIES}") 8 | message(STATUS "PALISADE_LIBDIR ${PALISADE_LIBDIR}") 9 | message(STATUS "OPENMP_INCLUDES ${OPENMP_INCLUDES}") 10 | message(STATUS "OPENMP_LIBRARIES ${OPENMP_LIBRARIES}") 11 | 12 | add_library(libpalisade INTERFACE) 13 | target_include_directories(libpalisade INTERFACE 14 | ${OPENMP_INCLUDES} # Will be empty if PALISADE is built single-threaded 15 | ${PALISADE_INCLUDE} 16 | ${PALISADE_INCLUDE}/third-party/include 17 | ${PALISADE_INCLUDE}/core 18 | ${PALISADE_INCLUDE}/pke 19 | ${PALISADE_INCLUDE}/binfhe 20 | ) 21 | 22 | target_link_directories(libpalisade INTERFACE ${PALISADE_LIBDIR}) 23 | target_link_libraries(libpalisade INTERFACE ${PALISADE_LIBRARIES} ${OPENMP_LIBRARIES}) 24 | 25 | # Ignore errors from PALISADE 26 | add_compile_options(-Wno-error=unused-parameter -Wno-error=ignored-qualifiers -Wno-error=deprecated-copy) 27 | -------------------------------------------------------------------------------- /he-samples/examples/logistic-regression/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | ######################################################## 5 | ## Logistic Regression Homomorphic Encryption Binary 6 | ######################################################## 7 | 8 | project(lr_test LANGUAGES CXX) 9 | 10 | cmake_minimum_required(VERSION 3.22 FATAL_ERROR) 11 | 12 | set(CMAKE_CXX_STANDARD 17) 13 | set(CMAKE_CXX_EXTENSIONS OFF) 14 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 15 | 16 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 17 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 18 | 19 | include(../../cmake/gflags.cmake) 20 | 21 | find_package(SEAL REQUIRED) 22 | 23 | set(THREADS_PREFER_PTHREAD_FLAG ON) 24 | find_package(Threads REQUIRED) 25 | 26 | find_package (OpenMP REQUIRED) 27 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 28 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") 29 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") 30 | 31 | file(GLOB CODE_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) 32 | add_executable(lr_test ${CODE_SRCS}) 33 | 34 | target_link_libraries(lr_test PRIVATE OpenMP::OpenMP_CXX) 35 | target_include_directories(lr_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) 36 | 37 | target_link_libraries(lr_test PRIVATE SEAL::seal) 38 | target_link_libraries(lr_test PRIVATE Threads::Threads) 39 | target_link_libraries(lr_test PRIVATE libgflags) 40 | 41 | add_subdirectory(datasets) 42 | add_dependencies(lr_test genSyntheticData) 43 | -------------------------------------------------------------------------------- /he-samples/examples/logistic-regression/README.md: -------------------------------------------------------------------------------- 1 | # Logistic/Linear Regression with Homomorphic Encryption 2 | The logistic regression example provides a fast and scalable implementation of 3 | SEAL CKKS HE scheme based logistic regression. It will be built whenever 4 | [SEAL](https://github.com/microsoft/SEAL) is enabled as part of the HE Toolkit 5 | build. 6 | 7 | This example is also capable of running linear regression instead of logistic 8 | regression. Logistic regression can be achieved by 9 | ["wrapping"](https://philippmuens.com/logistic-regression-from-scratch) a 10 | [multiple linear 11 | regression](https://en.wikipedia.org/wiki/Linear_regression#Simple_and_multiple_linear_regression) 12 | model with a [sigmoid 13 | function](https://en.wikipedia.org/wiki/Sigmoid_function). The sigmoid function 14 | can be skipped by using the `--linear_regression` option, effectively running a 15 | linear regression. 16 | 17 | ## Requirements 18 | ``` 19 | python3 >= 3.5 20 | virtualenv 21 | ``` 22 | Packages needed for generating the synthetic datasets are automatically 23 | installed in a virtual environment within 24 | `$HOME/.hekit/components/examples/logistic-regression/build/datasets/`. 25 | 26 | ## Usage 27 | Assuming the user used `hekit` to build the example program using the provided 28 | recipe file `recipes/examples.toml`, it can be run by executing 29 | ```bash 30 | cd $HOME/.hekit/components/examples/logistic-regression/build 31 | ./lr_test 32 | ``` 33 | 34 | ### Flags 35 | `--data`: Dataset name. Default is `lrtest_mid`. 36 | 37 | There are four different synthetic datasets available for testing, which are 38 | automatically generated during build time. 39 | 40 | | Name | Features | # Samples | 41 | | --- | --- | --- | 42 | | lrtest_small | 40 | 500 | 43 | | lrtest_mid | 80 | 2000 | 44 | | lrtest_large | 120 | 10000 | 45 | | lrtest_xlarge | 200 | 50000 | 46 | 47 | `--poly_modulus_degree`: Polynomial modulus degree. Determines the 48 | encoding slot count (half of the parameter) and encryption security level. 49 | Default is `8192`, and recommended size is `{4096, 8192, 16384}`, and 50 | must be a power of 2, with full range of `[1024, 32768]`. 51 | 52 | `--compare`: Compare the HE logistic regression inference result with non-HE 53 | inference for validation purposes. Default is `false`. 54 | 55 | `--data_plain`: Run with the data as plaintext. 56 | 57 | `--model_plain`: Run with the model as plaintext. 58 | 59 | `--linear_regression`: Calculate linear regression instead of logistic regression. 60 | 61 | `--security_level`: Security level. One of `[0, 128, 192, 256]`. 62 | 63 | `--coeff_modulus`: Coefficient modulus (list of primes). The bit-lengths of the primes to be generated. 64 | 65 | `--batch_size`: Batch size. 0 = automatic (poly_modulus_degree / 2). Max = poly_modulus_degree / 2. 66 | 67 | `--scale`: Scaling parameter defining precision. 68 | 69 | ## Data Preparation 70 | There are two example data preparation ipython notebooks in 71 | [datasets](datasets) folder. 72 | 73 | ## Acknowledgment 74 | [Kaggle home credit default risk](https://www.kaggle.com/c/home-credit-default-risk) 75 | -------------------------------------------------------------------------------- /he-samples/examples/logistic-regression/datasets/.gitignore: -------------------------------------------------------------------------------- 1 | *.csv 2 | -------------------------------------------------------------------------------- /he-samples/examples/logistic-regression/datasets/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | ################################################## 5 | ## Synthetic data generation for LR HE example 6 | ################################################## 7 | find_package(Python3 COMPONENTS Interpreter Development) 8 | if (Python3_VERSION VERSION_LESS 3.8) 9 | message(FATAL_ERROR "python >= 3.8 is required") 10 | endif() 11 | 12 | find_program(VIRTUALENV virtualenv) 13 | if(VIRTUALENV) 14 | set(VIRTUALENV_PREFIX ${VIRTUALENV} -p python3) 15 | else() 16 | set(VIRTUALENV_PREFIX ${Python3_EXECUTABLE} -m venv) 17 | endif() 18 | 19 | add_custom_command( 20 | OUTPUT venv 21 | COMMAND ${VIRTUALENV_PREFIX} venv 22 | ) 23 | 24 | add_custom_command( 25 | OUTPUT venv.stamp 26 | DEPENDS venv requirements.txt 27 | COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/requirements.txt ${CMAKE_CURRENT_BINARY_DIR} 28 | COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/*.py ${CMAKE_CURRENT_BINARY_DIR} 29 | COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/*.ipynb ${CMAKE_CURRENT_BINARY_DIR} 30 | COMMAND ${CMAKE_CURRENT_BINARY_DIR}/venv/bin/pip install -r requirements.txt --upgrade 31 | COMMAND touch venv.stamp 32 | ) 33 | 34 | add_custom_command( 35 | OUTPUT gendata.stamp 36 | DEPENDS venv.stamp 37 | COMMAND ${CMAKE_CURRENT_BINARY_DIR}/venv/bin/python generate_data.py 38 | COMMAND touch gendata.stamp 39 | ) 40 | 41 | add_custom_target(genSyntheticData 42 | DEPENDS gendata.stamp 43 | ) 44 | -------------------------------------------------------------------------------- /he-samples/examples/logistic-regression/datasets/lr_base.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | """Logistic Regression base functions 4 | 5 | Provides base functionality of logistic regression based on 'Homomorphic training of 30,000 logistic regression models' 6 | (https://eprint.iacr.org/2019/425) by Bergamaschi et al. 7 | """ 8 | 9 | import numpy as np 10 | 11 | 12 | # Sigmoid function 13 | def sigmoid(x): 14 | return 1.0 / (1 + np.exp(-x)) 15 | 16 | 17 | # 3-degree polynomial representation of sigmoid function, effective in range of [-5, 5] 18 | def sigmoid_poly3(x): 19 | return 0.5 - 1.20096 * (x / 8.0) + 0.81562 * (x / 8.0) ** 3 20 | 21 | 22 | # 4-degree polynomial representation of log(sigmoid(x)) function, effective in range of [-5, 5] 23 | def log_sig4(x): 24 | return 0.000527 * x**4 - 0.0822 * x**2 + 0.5 * x - 0.78 25 | 26 | 27 | # Realign target to -1, 1 and calculate X@y' 28 | def get_z(X, y, add1=True): 29 | if add1: 30 | X_ = np.concatenate([np.ones((X.shape[0], 1)), X], axis=1) 31 | else: 32 | X_ = np.array(X) 33 | y_ = 2 * y - 1 34 | z = X_ * y_[:, None] 35 | return np.array(z) 36 | 37 | 38 | # Compute initial weight for logistic regression 39 | def get_initweight(X, y, add1=True): 40 | n = X.shape[0] 41 | z = get_z(X, y, add1) 42 | return np.sum(z, axis=0) / n 43 | 44 | 45 | # get evaluation metrics (accuracy, f1 score, etc.) 46 | def get_eval_metrics(actual, predicted): 47 | tp, tn, fp, fn = 0, 0, 0, 0 48 | for a, p in zip(actual, predicted): 49 | if a == 1 and p == 1: 50 | tp += 1 51 | elif a == 1 and p == 0: 52 | fn += 1 53 | elif a == 0 and p == 1: 54 | fp += 1 55 | else: 56 | tn += 1 57 | 58 | acc = (tp + tn) / (tp + fp + tn + fn) 59 | precision = 0.0 60 | recall = 0.0 61 | 62 | if tp + fp > 0: 63 | precision = tp / (tp + fp) # correct 1s over predicted 1s 64 | 65 | if tp + fn > 0: 66 | recall = tp / (tp + fn) # correct 1s over actual 1s 67 | 68 | try: 69 | f1 = 2 * (precision * recall) / (precision + recall) 70 | except ZeroDivisionError: 71 | f1 = 0.0 72 | 73 | return acc, precision, recall, f1 74 | 75 | 76 | # loss/gradient descent with standard sigmoid 77 | def get_lgd(X, y, w): 78 | n = X.shape[0] 79 | z = get_z(X, y) 80 | zw = z @ w 81 | 82 | # calculate loss 83 | jw = np.sum(np.log(1 + np.exp(-zw))) / n 84 | 85 | # calculate gradient descent 86 | dw = 1.0 / (1 + np.exp(zw)) 87 | dzw = z * dw[:, None] 88 | djw = -np.sum(dzw, axis=0) / n 89 | 90 | return jw, djw 91 | 92 | 93 | # loss/gradient descent with poly3 sigmoid 94 | def get_lgd_poly3(X, y, w): 95 | n = X.shape[0] 96 | z = get_z(X, y) 97 | zw = z @ w 98 | 99 | # calculate loss 100 | jw = np.sum(-log_sig4(zw)) / n 101 | 102 | # calculate gradient descent 103 | dw = sigmoid_poly3(zw) 104 | dzw = z * dw[:, None] 105 | djw = -np.sum(dzw, axis=0) / n 106 | 107 | return jw, djw 108 | 109 | 110 | # test standard sigmoid 111 | def test(X, y, w, add1=True, sigmoid=sigmoid): 112 | if add1: 113 | X_ = np.concatenate([np.ones((X.shape[0], 1)), X], axis=1) 114 | else: 115 | X_ = np.array(X) 116 | 117 | xws = (np.inner(x, w) for x in X_) 118 | 119 | y_ = np.array([]) 120 | for xiw in xws: 121 | # 1./(1+np.exp(-xiw)) 122 | if sigmoid(xiw) > 0.5: 123 | y_ = np.append(y_, 1) 124 | else: 125 | y_ = np.append(y_, 0) 126 | 127 | acc, _, recall, f1 = get_eval_metrics(y, y_) 128 | return y_, acc, recall, f1 129 | 130 | 131 | # test poly3 sigmoid 132 | def test_poly3(X, y, w, add1=True): 133 | return test(X, y, w, add1, sigmoid=lambda x: sigmoid_poly3(-x)) 134 | -------------------------------------------------------------------------------- /he-samples/examples/logistic-regression/datasets/requirements.txt: -------------------------------------------------------------------------------- 1 | pip~=23.3.1 2 | numpy~=1.22.0 3 | urllib3~=1.26.6 4 | scikit-learn~=1.5.0 5 | -------------------------------------------------------------------------------- /he-samples/examples/logistic-regression/include/data_loader.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #ifndef HE_SAMPLES_EXAMPLES_LOGISTIC_REGRESSION_INCLUDE_DATA_LOADER_HPP_ 5 | #define HE_SAMPLES_EXAMPLES_LOGISTIC_REGRESSION_INCLUDE_DATA_LOADER_HPP_ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | enum class CSVState { UnquotedField, QuotedField, QuotedQuote }; 21 | enum class DataMode { TRAIN, TEST, EVAL }; 22 | enum class WeightType { W, V }; 23 | 24 | inline bool file_exists(const std::string& fn) { 25 | const std::filesystem::path p = fn; 26 | return std::filesystem::exists(p); 27 | } 28 | 29 | std::vector readCSVRow(const std::string& row); 30 | 31 | /// Read CSV file, Excel dialect. Accept "quoted fields ""with quotes""" 32 | std::vector> readCSV(std::istream& in); 33 | 34 | std::vector> dataLoader(std::string dataset_name, 35 | DataMode mode); 36 | 37 | std::vector weightsLoaderCSV(std::string dataset_name, 38 | WeightType wtype = WeightType::W); 39 | 40 | void splitWeights(const std::vector& rawweights, 41 | std::vector& out_weight, double& out_bias); 42 | void splitData(const std::vector>& rawdata, 43 | std::vector>& out_data, 44 | std::vector& out_target); 45 | 46 | std::vector> transpose( 47 | const std::vector>& data); 48 | 49 | #endif // HE_SAMPLES_EXAMPLES_LOGISTIC_REGRESSION_INCLUDE_DATA_LOADER_HPP_ 50 | -------------------------------------------------------------------------------- /he-samples/examples/logistic-regression/include/logger.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #ifndef HE_SAMPLES_EXAMPLES_LOGISTIC_REGRESSION_INCLUDE_LOGGER_HPP_ 5 | #define HE_SAMPLES_EXAMPLES_LOGISTIC_REGRESSION_INCLUDE_LOGGER_HPP_ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | struct LogLevel { 19 | const std::string name; 20 | 21 | LogLevel(const std::string& n) : name(n) {} 22 | virtual ~LogLevel(void) = default; 23 | }; 24 | 25 | struct Info : public LogLevel { 26 | Info(void) : LogLevel("INFO") {} 27 | }; 28 | 29 | struct Status : public LogLevel { 30 | Status(void) : LogLevel("STATUS") {} 31 | }; 32 | 33 | struct Debug : public LogLevel { 34 | Debug(void) : LogLevel("DEBUG") {} 35 | }; 36 | 37 | struct Result : public LogLevel { 38 | Result(void) : LogLevel("RESULT") {} 39 | }; 40 | 41 | template 42 | void _LOG(T only) { 43 | std::cout << only << std::endl; 44 | } 45 | 46 | template 47 | void _LOG(T current, args... next) { 48 | std::string sp = " "; 49 | if (Level().name == "STATUS") 50 | sp = ", "; 51 | else if (Level().name == "RESULT") 52 | sp = ": "; 53 | 54 | std::cout << current << sp; 55 | _LOG(next...); 56 | } 57 | 58 | template 59 | void LOG(args... to_print) { 60 | std::cout << Level().name << ": "; 61 | if (Level().name == "INFO") { 62 | std::time_t time = std::time(nullptr); 63 | std::string time_str = std::ctime(&time); 64 | time_str.pop_back(); 65 | std::cout << time_str << ": "; 66 | } 67 | 68 | _LOG(to_print...); 69 | } 70 | 71 | struct Point { 72 | int x; 73 | int y; 74 | friend std::ostream& operator<<(std::ostream& out, const Point& p) { 75 | return out << '{' << p.x << ',' << p.y << '}'; 76 | } 77 | }; 78 | 79 | template 80 | std::ostream& operator<<(std::ostream& out, const std::vector& v) { 81 | out << "{"; 82 | size_t last = v.size() - 1; 83 | for (size_t i = 0; i < v.size(); ++i) { 84 | out << v[i]; 85 | if (i != last) out << ", "; 86 | } 87 | out << "}"; 88 | return out; 89 | } 90 | 91 | #endif // HE_SAMPLES_EXAMPLES_LOGISTIC_REGRESSION_INCLUDE_LOGGER_HPP_ 92 | -------------------------------------------------------------------------------- /he-samples/examples/logistic-regression/include/lr_helper.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #ifndef HE_SAMPLES_EXAMPLES_LOGISTIC_REGRESSION_INCLUDE_LR_HELPER_HPP_ 5 | #define HE_SAMPLES_EXAMPLES_LOGISTIC_REGRESSION_INCLUDE_LR_HELPER_HPP_ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | // Base functionality for logistic regression based on 13 | // Efficient logistic regression training by Bergamaschi et. al 14 | // (https://eprint.iacr.org/2019/425) 15 | namespace lrhelper { 16 | 17 | // Compute initial weight with training set 18 | void get_init_weight(const std::vector>& data, 19 | const std::vector& target, 20 | std::vector& out_weight, double& out_bias); 21 | 22 | // Polynomial representation of sigmoid function 23 | template 24 | double sigmoid(const double x); 25 | 26 | // Polynomial representation of log(sigmoid()) function 27 | double minus_logsigmoid_poly4(const double x); 28 | 29 | // Calculates z. For more detail, check https://eprint.iacr.org/2019/425 30 | void get_z(const std::vector>& X, 31 | const std::vector& y, 32 | std::vector>& out_z_main, 33 | std::vector& out_z_bias); 34 | 35 | // Calculates z dot w. For more detail, check https://eprint.iacr.org/2019/425 36 | std::vector get_zw(const std::vector>& X, 37 | const std::vector& X_bias, 38 | const std::vector& W, const double bias); 39 | 40 | // Calculate loss and gradient descent 41 | std::map> get_lgd( 42 | const std::vector>& X, const std::vector& y, 43 | const std::vector& W, const double& bias, 44 | unsigned int sigmoid_degree = 3); 45 | 46 | // Get evaluation metric of model stored in following keys: 47 | // acc(accuracy), f1(f1 score), recall and precision 48 | std::map get_evalmetrics( 49 | const std::vector& expected, const std::vector& predicted); 50 | 51 | // Performs logistic regression inference in cleartext data 52 | std::vector test(const std::vector>& X, 53 | const std::vector& W, const double bias, 54 | bool clipResult = true, 55 | bool linear_regression = false); 56 | } // namespace lrhelper 57 | 58 | #endif // HE_SAMPLES_EXAMPLES_LOGISTIC_REGRESSION_INCLUDE_LR_HELPER_HPP_ 59 | -------------------------------------------------------------------------------- /he-samples/examples/logistic-regression/include/lrhe.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #ifndef HE_SAMPLES_EXAMPLES_LOGISTIC_REGRESSION_INCLUDE_LRHE_HPP_ 5 | #define HE_SAMPLES_EXAMPLES_LOGISTIC_REGRESSION_INCLUDE_LRHE_HPP_ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "lrhe_kernel.hpp" 17 | 18 | namespace lrhe { 19 | class LRHE { 20 | public: 21 | LRHE() {} 22 | LRHE(const kernel::LRHEKernel& lrheKernel, const bool encrypt_data = true, 23 | const bool encrypt_model = true, const bool linear_regression = false, 24 | const size_t batch_size = 0); 25 | 26 | ~LRHE() {} 27 | 28 | // Initialize LRHE with LRHEKernel 29 | // @param lrheKernel backend kernel class handling HE operations 30 | void init(const kernel::LRHEKernel& lrheKernel); 31 | 32 | // Load weight and bias, then encode/encrypt for LRHE inference 33 | // @param weights weight vector of LR model 34 | // @param bias bias of LR model 35 | void load_weight(const std::vector& weights, const double bias); 36 | 37 | std::vector get_weights() { return m_weights; } 38 | double get_bias() { return m_bias; } 39 | 40 | // Runs LR inference in SEAL CKKS HE 41 | std::vector inference( 42 | const std::vector>& inputData, 43 | bool clipResult = true); 44 | 45 | protected: 46 | bool m_linear_regression = false; 47 | 48 | private: 49 | bool m_encrypt_data = true; 50 | bool m_encrypt_model = true; 51 | 52 | std::unique_ptr m_kernel; 53 | bool m_isTrained = false; 54 | 55 | size_t m_slot_count = 0; 56 | size_t m_batch_size = 0; 57 | std::vector m_weights; 58 | double m_bias = 0.0; 59 | size_t m_n_weights = 0; 60 | std::vector m_ct_weights; 61 | seal::Ciphertext m_ct_bias; 62 | std::vector m_pt_weights; 63 | seal::Plaintext m_pt_bias; 64 | 65 | seal::Ciphertext encodeEncryptBias(const double bias); 66 | seal::Plaintext encodeBias(const double bias); 67 | std::vector decryptDecodeResult( 68 | const std::vector ct_result_batches, size_t n_samples); 69 | std::vector> encodeEncryptData( 70 | const std::vector>>& data_T); 71 | std::vector> encodeData( 72 | const std::vector>>& data_T); 73 | std::vector encodeEncryptWeights( 74 | const std::vector& weights); 75 | std::vector encodeWeights( 76 | const std::vector& weights); 77 | }; 78 | 79 | class LogisticRegressionHE : public LRHE { 80 | public: 81 | LogisticRegressionHE() {} 82 | LogisticRegressionHE(const kernel::LRHEKernel& lrheKernel, 83 | const bool encrypt_data = true, 84 | const bool encrypt_model = true, 85 | const size_t batch_size = 0) 86 | : LRHE(lrheKernel, encrypt_data, encrypt_model, false, batch_size) {} 87 | }; 88 | 89 | class LinearRegressionHE : public LRHE { 90 | public: 91 | LinearRegressionHE() { m_linear_regression = true; } 92 | LinearRegressionHE(const kernel::LRHEKernel& lrheKernel, 93 | const bool encrypt_data = true, 94 | const bool encrypt_model = true, 95 | const size_t batch_size = 0) 96 | : LRHE(lrheKernel, encrypt_data, encrypt_model, true, batch_size) {} 97 | }; 98 | } // namespace lrhe 99 | #endif // HE_SAMPLES_EXAMPLES_LOGISTIC_REGRESSION_INCLUDE_LRHE_HPP_ 100 | -------------------------------------------------------------------------------- /he-samples/examples/logistic-regression/include/omp_utils.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #ifndef EXAMPLES_LOGISTIC_REGRESSION_KERNELS_OMP_UTILS_H_ 5 | #define EXAMPLES_LOGISTIC_REGRESSION_KERNELS_OMP_UTILS_H_ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | class OMPUtilities { 12 | public: 13 | static int assignOMPThreads(int& remaining_threads, int requested_threads) { 14 | int retval = (requested_threads > 0 ? requested_threads : 1); 15 | if (retval > remaining_threads) retval = remaining_threads; 16 | if (retval > 1) 17 | remaining_threads -= retval; 18 | else 19 | retval = 1; 20 | return retval; 21 | } 22 | }; 23 | 24 | class OMPUtilitiesS : public OMPUtilities { 25 | public: 26 | static const int MaxThreads; 27 | 28 | /** 29 | * @brief Sets number of threads to assign at specified nesting level. 30 | * @param level[in] OpenMP nesting level. 31 | * @param threads[in] Number of threads to assign at specified nesting level. 32 | */ 33 | static void setThreadsAtLevel(int level, int threads); 34 | /** 35 | * @brief Retrieves threads at specified level. 36 | * @param level[in] OpenMP nesting level. 37 | * @return Number of threads to assign at specified nesting level. 38 | */ 39 | static int getThreadsAtLevel(int level); 40 | /** 41 | * @brief Retrieves threads to assign to current OpenMP nesting level. 42 | */ 43 | static int getThreadsAtLevel(); 44 | 45 | private: 46 | static std::vector m_threads_per_level; 47 | }; 48 | 49 | #endif // EXAMPLES_LOGISTIC_REGRESSION_KERNELS_OMP_UTILS_H_ 50 | -------------------------------------------------------------------------------- /he-samples/examples/logistic-regression/include/timer.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #ifndef HE_SAMPLES_EXAMPLES_LOGISTIC_REGRESSION_INCLUDE_UTILS_HPP_ 5 | #define HE_SAMPLES_EXAMPLES_LOGISTIC_REGRESSION_INCLUDE_UTILS_HPP_ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | namespace intel { 15 | 16 | namespace timer { 17 | // Get current time with chrono::high_resolution_clock 18 | std::chrono::steady_clock::time_point now(); 19 | 20 | // Get delta time since start to end in seconds 21 | double delta(std::chrono::steady_clock::time_point start, 22 | std::chrono::steady_clock::time_point end); 23 | 24 | // Get delta time since start in seconds 25 | double delta(std::chrono::steady_clock::time_point start); 26 | 27 | } // namespace timer 28 | } // namespace intel 29 | #endif // HE_SAMPLES_EXAMPLES_LOGISTIC_REGRESSION_INCLUDE_UTILS_HPP_ 30 | -------------------------------------------------------------------------------- /he-samples/examples/logistic-regression/src/omp_utils.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "omp_utils.h" 5 | 6 | #include 7 | 8 | const int OMPUtilitiesS::MaxThreads = omp_get_max_threads(); 9 | std::vector OMPUtilitiesS::m_threads_per_level(1, 10 | OMPUtilitiesS::MaxThreads); 11 | 12 | void OMPUtilitiesS::setThreadsAtLevel(int level, int threads) { 13 | if (threads < 1) threads = 1; 14 | std::size_t level_i = static_cast(level < 0 ? 0 : level); 15 | if (level_i >= m_threads_per_level.size()) 16 | m_threads_per_level.resize(level_i + 1, 1); 17 | m_threads_per_level[level_i] = threads; 18 | } 19 | 20 | int OMPUtilitiesS::getThreadsAtLevel() { 21 | return getThreadsAtLevel(omp_get_active_level()); 22 | } 23 | 24 | int OMPUtilitiesS::getThreadsAtLevel(int level) { 25 | int retval = 1; 26 | std::size_t level_i = static_cast(level < 0 ? 0 : level); 27 | if (level_i < m_threads_per_level.size()) { 28 | retval = m_threads_per_level[level_i]; 29 | } 30 | return retval; 31 | } 32 | -------------------------------------------------------------------------------- /he-samples/examples/logistic-regression/src/timer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "timer.hpp" 5 | 6 | namespace intel { 7 | namespace timer { 8 | std::chrono::steady_clock::time_point now() { 9 | return std::chrono::steady_clock::now(); 10 | } 11 | 12 | double delta(std::chrono::steady_clock::time_point start) { 13 | auto end = now(); 14 | return delta(start, end); 15 | } 16 | 17 | double delta(std::chrono::steady_clock::time_point start, 18 | std::chrono::steady_clock::time_point end) { 19 | return static_cast( 20 | std::chrono::duration_cast(end - start) 21 | .count()) / 22 | 1000.0; 23 | } 24 | } // namespace timer 25 | } // namespace intel 26 | -------------------------------------------------------------------------------- /he-samples/examples/psi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | ##################################################################### 5 | ## Private Set Intersection (PSI) 6 | ##################################################################### 7 | 8 | project(psi LANGUAGES CXX) 9 | 10 | cmake_minimum_required(VERSION 3.22 FATAL_ERROR) 11 | 12 | ## Use -std=c++17 as default. 13 | set(CMAKE_CXX_STANDARD 17) 14 | ## Disable C++ extensions 15 | set(CMAKE_CXX_EXTENSIONS OFF) 16 | ## Require full C++ standard 17 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 18 | 19 | find_package(helib REQUIRED) 20 | 21 | file(GLOB CODE_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") 22 | add_executable(psi ${CODE_SRCS}) 23 | 24 | target_link_libraries(psi PRIVATE helib) 25 | 26 | file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/datasets 27 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) 28 | -------------------------------------------------------------------------------- /he-samples/examples/psi/README.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | Private Set Intersection (PSI) allows two parties to compute the set 3 | intersection of their respective sets of encrypted data without revealing any 4 | information outside of the elements present in the intersecting set. This PSI 5 | example uses [HElib](https://github.com/homenc/HElib) to compute the 6 | intersection of two given sets encrypted under the BGV scheme. 7 | 8 | ## How It Works 9 | The PSI program reads words from the client and server sets and computes a hash 10 | value for each word. The hash value, an integer binary representation, is 11 | encoded as a polynomial with the coefficients in binary {0,1}. 12 | 13 | The encoded set for the client is encrypted, then the intersection is computed 14 | against the server set producing a resultant intersecting set that is 15 | decrypted. The program keeps a translation table for the client set of which 16 | hash corresponds to which word, so that the returned hashes can be translated 17 | back to words. 18 | 19 | The plaintext prime in this example is always 2 and the maximum size of these 20 | polynomials are the order of p in the Z\_{m}^{\*}/\ quotient group. 21 | 22 | The maximum number of entries that the client set supports is the number of 23 | slots in the plaintext (default 32). 24 | 25 | ## Usage 26 | The client set is a mandatory parameter, therefore before executing the 27 | example, the user must create this input file and write some items, one per 28 | line with no leading nor trailing whitespace. Be aware that the program is case 29 | sensitive, so the words in the client set must be defined with the same format 30 | as they are in the server set. 31 | 32 | For instance, if the program uses the default server set, 33 | [fruits.set](./datasets/fruits.set), create a file called `client.txt` under 34 | `/home/$USER/` and add some fruits in lower case, as shown in the following 35 | example: 36 | ``` 37 | apple 38 | tomato 39 | orange 40 | mango 41 | ``` 42 | 43 | Then, to run the example program execute 44 | ```bash 45 | ./psi /home/$USER/client.txt 46 | ``` 47 | 48 | ## Flags 49 | ``: Client set. 50 | 51 | `-n`: Number of threads. Default is `1`. 52 | 53 | `-m`: Order of the cyclotomic polynomial. Default is `771`. 54 | 55 | `--server`: Server set. Default is `./datasets/fruits.set`. 56 | 57 | `--bits`: Number of big Q bits. Default is `100`. 58 | 59 | `--ptxt`: Keep Client set in encoded plaintext. Default is `false`. 60 | 61 | ### Examples 62 | 63 | Although optional, it is a good idea to run the example with multiple threads 64 | using the `-n` flag and specifying the number of threads. 65 | ```bash 66 | ./psi /home/$USER/client.txt -n 64 67 | ``` 68 | 69 | There are three sets to choose from of differing sizes under the 70 | [datasets](./datasets) folder. The default is `fruits.set`, but it can be 71 | changed by executing 72 | ```bash 73 | ./psi /home/$USER/client.txt --server datasets/us_states.set 74 | ``` 75 | 76 | The default order of the cyclotomic polynomial used in the BGV scheme is 771 77 | which gives ord(p) = 16, where p = 2. You can change the default `m` by 78 | running: 79 | ```bash 80 | ./psi /home/$USER/client.txt -m 21845 --bits 100 81 | ``` 82 | Although, this usually coincides with needing to increase the number of bits 83 | for HElib to handle the increased noise level at larger m values. 84 | 85 | For more options see 86 | ```bash 87 | ./psi -h 88 | ``` 89 | -------------------------------------------------------------------------------- /he-samples/examples/psi/datasets/ancient_egyptian_gods.set: -------------------------------------------------------------------------------- 1 | Aah 2 | Aani 3 | Aati 4 | Abu 5 | Ahti 6 | Aker 7 | Amathaunta 8 | Amenhetep 9 | Amenhotep 10 | Amentet 11 | Am-heh 12 | Ammit 13 | Amn 14 | Amu-Aa 15 | Amun 16 | Amunet 17 | An-a-f 18 | Anat 19 | Andjety 20 | Anedjti 21 | Anezti 22 | Anhefta 23 | An-hetep-f 24 | Anhur 25 | Ani 26 | Anit 27 | An-mut-f 28 | Anput 29 | Anta 30 | An-tcher-f 31 | Anti 32 | Anubis 33 | Anuke 34 | Anuket 35 | Apedemak 36 | Apep 37 | Apepi 38 | Apesh 39 | Apet 40 | Apis 41 | Arensnuphis 42 | Asclepius 43 | Ash 44 | Astarte 45 | Astennu 46 | Aten 47 | Atum 48 | Ausaas 49 | Ba 50 | Baal 51 | Ba'alat Gebal 52 | Babi 53 | Banebdjedet 54 | Ba-Pef 55 | Ba-Ra 56 | Bastet 57 | Bat 58 | Bennu 59 | Bes 60 | Besna 61 | Buchis 62 | Dedun 63 | Dedwen 64 | Denwen 65 | Dionysus-Osiris 66 | Djebuty 67 | Djedefhor 68 | Djefa 69 | Dua 70 | Duamutef 71 | Esna 72 | Fa 73 | Female 74 | Fetket 75 | Geb 76 | Gengen Wer 77 | Ha 78 | Hapi 79 | Hapy 80 | Hapy-Wet 81 | Hardedef 82 | Harmachis 83 | Harsomtus 84 | Hathor 85 | Hatmehit 86 | Haurun 87 | Hedetet 88 | Heh 89 | Heka 90 | Heneb 91 | Henkhisesui 92 | Heptet 93 | Heqet 94 | Heret-Kau 95 | Hert-ketit-s 96 | Hert-Nemmat-Set 97 | Hert-sefu-s 98 | Heru-Khu 99 | Heru-pa-kaut 100 | Hery-sha-duat 101 | Heryshaf 102 | Hesat 103 | Heset 104 | Hetepes-Sekhus 105 | Hike 106 | Horea 107 | Hor-em-akhet 108 | Horon 109 | Horus 110 | Hu 111 | Iabet 112 | Iah 113 | Iat 114 | Ihu 115 | Ihy 116 | Imentet 117 | Imhotep 118 | Imset 119 | Ipy 120 | Ishtar 121 | Isis 122 | Iusaaset 123 | Iw 124 | Jupiter-Amun 125 | Kagemni 126 | Kebehut 127 | Kek 128 | Ken 129 | Khefthernebes 130 | Khentekhtai 131 | Khente-Khtai 132 | Khenti-Amenti 133 | Khenti-qerer 134 | Khepri 135 | Kherty 136 | Khesfu 137 | Khnemu 138 | Khnum 139 | Khonsu 140 | Kneph 141 | Maahes 142 | Maat 143 | Mafdet 144 | Mahes 145 | Mandulis 146 | Matit 147 | Mehen 148 | Mehet-Weret 149 | Mehit 150 | Menhit 151 | Menhyt 152 | Meret 153 | Meretseger 154 | Mesenet 155 | Meskhenet 156 | Mesta 157 | Mihos 158 | Min 159 | Mnevis 160 | Montu 161 | Mut 162 | Nakith 163 | Naunet 164 | Nebethetepet 165 | Nebt-Ankhiu 166 | Nebt-Khu 167 | Nebt-Mat 168 | Nebt-Setau 169 | Nebt-Shat 170 | Nebt-Shefshefet 171 | Nefer Hor 172 | Neferhotep 173 | Nefertari 174 | Nefertem 175 | Nefertum 176 | Nehebu-Kau 177 | Nehmetawy 178 | Neith 179 | Nekhbet 180 | Nekhebit 181 | Nemty 182 | Neper 183 | Nephthys 184 | Nepit 185 | Nu 186 | Nun 187 | Nut 188 | Osiris 189 | Pakhet 190 | Panebtawy 191 | Pelican 192 | Perit 193 | Pesi 194 | Petbe 195 | Peteese 196 | Pihor 197 | Ptah 198 | Ptah-hotep 199 | Qebehsenuef 200 | Qebehsenuf 201 | Qebui 202 | Qererti 203 | Qerhet 204 | Qetesh 205 | Qudshu 206 | Ra 207 | Ra-ateni 208 | Raet-Tawy 209 | Ra-Herakhty 210 | Re 211 | Rekhit 212 | Renenet 213 | Renenutet 214 | Renpet 215 | Reshep 216 | Sah 217 | Sait 218 | Satet 219 | Sebeg 220 | Sebiumeker 221 | Sed 222 | Sefkhet-Abwy 223 | Sehith 224 | Seker 225 | Sekhat-Hor 226 | Sekhemus 227 | Sekhet-Metu 228 | Sekhmet 229 | Sepa 230 | Sepes 231 | Septu 232 | Serapis 233 | Seret 234 | Serket 235 | Sesenet-Khu 236 | Seshat 237 | Set 238 | Seta-Ta 239 | Setcheh 240 | Setem 241 | Shed 242 | Shehbui 243 | Shemat-Khu 244 | Shentayet 245 | Shenty 246 | Shesmetet 247 | Shezmu 248 | Shu 249 | Sia 250 | Sobek 251 | Sopd 252 | Sopdet 253 | Sopdu 254 | Sothis 255 | Swenet 256 | Ta-Bitjet 257 | Tafner 258 | Ta-Sent-Nefert 259 | Tatenen 260 | Taweret 261 | Tayet 262 | Tayt 263 | Tefnut 264 | Temet 265 | Temtith 266 | Tenenet 267 | Themath 268 | Thermuthis 269 | Thmei 270 | Thoeris 271 | Thoth 272 | Tjenenyet 273 | Tjenmyt 274 | Tuamutef 275 | Tutu 276 | Uatchit 277 | Uneg 278 | Unut 279 | Usit 280 | Wadjet 281 | Wadj-wer 282 | Weneg 283 | Wenenu 284 | Wepset 285 | Wepwawet 286 | Werethekau 287 | Wosret 288 | Yah 289 | Yam 290 | -------------------------------------------------------------------------------- /he-samples/examples/psi/datasets/fruits.set: -------------------------------------------------------------------------------- 1 | abiu 2 | açaí 3 | acerola 4 | ackee 5 | apple 6 | apricot 7 | avocado 8 | banana 9 | bilberry 10 | blackberry 11 | blackcurrant 12 | blueberry 13 | boysenberry 14 | breadfruit 15 | canistel 16 | cempedak 17 | cherimoya 18 | cherry 19 | cloudberry 20 | coconut 21 | cranberry 22 | currant 23 | damson 24 | date 25 | pitaya 26 | durian 27 | elderberry 28 | feijoa 29 | fig 30 | gooseberry 31 | grape 32 | raisin 33 | grapefruit 34 | guava 35 | honeyberry 36 | huckleberry 37 | jabuticaba 38 | jackfruit 39 | jambul 40 | jostaberry 41 | jujube 42 | kiwano 43 | kiwifruit 44 | kumquat 45 | lemon 46 | lime 47 | loganberry 48 | longan 49 | loquat 50 | lulo 51 | lychee 52 | mango 53 | mangosteen 54 | marionberry 55 | melon 56 | cantaloupe 57 | honeydew 58 | watermelon 59 | mulberry 60 | nance 61 | nectarine 62 | orange 63 | clementine 64 | mandarine 65 | tangerine 66 | papaya 67 | passionfruit 68 | pawpaw 69 | peach 70 | pear 71 | persimmon 72 | plantain 73 | plum 74 | prune 75 | pineapple 76 | pineberry 77 | plumcot 78 | pomegranate 79 | pomelo 80 | quince 81 | raspberry 82 | salmonberry 83 | rambutan 84 | redcurrant 85 | salal 86 | salak 87 | satsuma 88 | strawberry 89 | tamarillo 90 | tamarind 91 | tangelo 92 | -------------------------------------------------------------------------------- /he-samples/examples/psi/datasets/us_states.set: -------------------------------------------------------------------------------- 1 | Alabama 2 | Alaska 3 | Arizona 4 | Arkansas 5 | California 6 | Colorado 7 | Connecticut 8 | Delaware 9 | Florida 10 | Georgia 11 | Hawaii 12 | Idaho 13 | Illinois 14 | Indiana 15 | Iowa 16 | Kansas 17 | Kentucky 18 | Louisiana 19 | Maine 20 | Maryland 21 | Massachusetts 22 | Michigan 23 | Minnesota 24 | Mississippi 25 | Missouri 26 | Montana 27 | Nebraska 28 | Nevada 29 | New Hampshire 30 | New Jersey 31 | New Mexico 32 | New York 33 | North Carolina 34 | North Dakota 35 | Ohio 36 | Oklahoma 37 | Oregon 38 | Pennsylvania 39 | Rhode Island 40 | South Carolina 41 | South Dakota 42 | Tennessee 43 | Texas 44 | Utah 45 | Vermont 46 | Virginia 47 | Washington 48 | West Virginia 49 | Wisconsin 50 | Wyoming 51 | -------------------------------------------------------------------------------- /he-samples/examples/secure-query/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | ##################################################################### 5 | ## Secure Query Binary: 6 | ##################################################################### 7 | 8 | project(secure-query LANGUAGES CXX) 9 | 10 | cmake_minimum_required(VERSION 3.22 FATAL_ERROR) 11 | 12 | ## Use -std=c++17 as default. 13 | set(CMAKE_CXX_STANDARD 17) 14 | ## Disable C++ extensions 15 | set(CMAKE_CXX_EXTENSIONS OFF) 16 | ## Require full C++ standard 17 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 18 | 19 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 20 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 21 | 22 | find_package(SEAL REQUIRED) 23 | 24 | set(THREADS_PREFER_PTHREAD_FLAG ON) 25 | find_package(Threads REQUIRED) 26 | 27 | find_package (OpenMP REQUIRED) 28 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 29 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") 30 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") 31 | 32 | file(GLOB CODE_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") 33 | add_executable(secure-query ${CODE_SRCS}) 34 | 35 | target_include_directories(secure-query PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) 36 | 37 | target_link_libraries(secure-query PRIVATE SEAL::seal) 38 | target_link_libraries(secure-query PRIVATE Threads::Threads) 39 | 40 | file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/datasets/us_state_capitals.csv 41 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) 42 | -------------------------------------------------------------------------------- /he-samples/examples/secure-query/datasets/us_state_capitals.csv: -------------------------------------------------------------------------------- 1 | Alabama,Montgomery 2 | Alaska,Juneau 3 | Arizona,Phoenix 4 | Arkansas,Little Rock 5 | California,Sacramento 6 | Colorado,Denver 7 | Connecticut,Hartford 8 | Delaware,Dover 9 | Florida,Tallahassee 10 | Georgia,Atlanta 11 | Hawaii,Honolulu 12 | Idaho,Boise 13 | Illinois,Springfield 14 | Indiana,Indianapolis 15 | Iowa,Des Moines 16 | Kansas,Topeka 17 | Kentucky,Frankfort 18 | Louisiana,Baton Rouge 19 | Maine,Augusta 20 | Maryland,Annapolis 21 | Massachusetts,Boston 22 | Michigan,Lansing 23 | Minnesota,St. Paul 24 | Mississippi,Jackson 25 | Missouri,Jefferson City 26 | Montana,Helena 27 | Nebraska,Lincoln 28 | Nevada,Carson City 29 | New Hampshire,Concord 30 | New Jersey,Trenton 31 | New Mexico,Santa Fe 32 | New York,Albany 33 | North Carolina,Raleigh 34 | North Dakota,Bismarck 35 | Ohio,Columbus 36 | Oklahoma,Oklahoma City 37 | Oregon,Salem 38 | Pennsylvania,Harrisburg 39 | Rhode Island,Providence 40 | South Carolina,Columbia 41 | South Dakota,Pierre 42 | Tennessee,Nashville 43 | Texas,Austin 44 | Utah,Salt Lake City 45 | Vermont,Montpelier 46 | Virginia,Richmond 47 | Washington,Olympia 48 | West Virginia,Charleston 49 | Wisconsin,Madison 50 | Wyoming,Cheyenne 51 | -------------------------------------------------------------------------------- /he-samples/examples/secure-query/images/encoding_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/he-samples/examples/secure-query/images/encoding_diagram.png -------------------------------------------------------------------------------- /he-samples/examples/secure-query/images/mask_comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/he-samples/examples/secure-query/images/mask_comparison.png -------------------------------------------------------------------------------- /he-samples/examples/secure-query/images/query_key_comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/he-samples/examples/secure-query/images/query_key_comparison.png -------------------------------------------------------------------------------- /he-samples/examples/secure-query/include/sq_types.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #ifndef HE_SAMPLES_EXAMPLES_SECURE_QUERY_INCLUDE_SQ_TYPES_H_ 5 | #define HE_SAMPLES_EXAMPLES_SECURE_QUERY_INCLUDE_SQ_TYPES_H_ 6 | #include 7 | #include 8 | 9 | #include "seal/seal.h" 10 | 11 | struct DatabaseEntry { 12 | DatabaseEntry(std::string _key, std::string _value) { 13 | key = _key; 14 | value = _value; 15 | } 16 | // int key; 17 | std::string key; 18 | std::string value; 19 | }; 20 | 21 | struct EncryptedDatabaseEntry { 22 | std::vector key; 23 | seal::Ciphertext value; 24 | }; 25 | 26 | #endif // HE_SAMPLES_EXAMPLES_SECURE_QUERY_INCLUDE_SQ_TYPES_H_ 27 | -------------------------------------------------------------------------------- /he-samples/examples/secure-query/include/sqclient.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "seal/seal.h" 11 | #include "sq_helper_functions.h" 12 | #include "sq_types.h" 13 | 14 | class SQClient { 15 | public: 16 | SQClient(); 17 | 18 | void initializeSealContext( 19 | size_t _polymodulus_degree, size_t _plaintext_modulus, 20 | std::vector coeff_modulus = std::vector()); 21 | 22 | /** 23 | * @brief `initializeSealContextInteractive` queries the user for desired 24 | * encryption parameters and then verifies that the parameter combination is 25 | * valid. If parameters are not valid then it will request a new set of 26 | * parameters. 27 | */ 28 | void initializeSealContextInteractive(); 29 | seal::EncryptionParameters sealParams() const; 30 | seal::PublicKey* publicKey() const; 31 | seal::RelinKeys* relinKeys() const; 32 | size_t keyLength() const; 33 | seal::Encryptor* encryptor() const; 34 | /** 35 | * @brief encodeStringQuery converts an `std::string` to a 4-bit 36 | * encoded/encrypted vector of ciphertexts. See the README for details on 37 | * this encoding. 38 | * @param query an std::string to be encoded/encrypted. 39 | * @return a vector of ciphertexts containing values in the range [0,15] 40 | * representing the value of each character in query as 2 ciphertexts. 41 | */ 42 | std::vector encodeStringQuery(const std::string& query); 43 | /** 44 | * @brief decodeToString converts a ciphertext containing a 4-bit encoded 45 | * string stored as coefficients into an `std::string`. This is the return 46 | * type from calls to `queryDatabaseForMatchingEntry`. 47 | * @param cipher1 A ciphertext for which the plaintext coefficient values are 48 | * used to store a 4-bit encoded character string. 49 | * @return an `std::string` containing the decoded string value contained in 50 | * `cipher1`. 51 | */ 52 | std::string decodeToString(seal::Ciphertext cipher1); 53 | 54 | private: 55 | size_t determineMaxKeyLengthForCurrentContext(); 56 | bool isKeyLengthValid(int key_length); 57 | size_t m_key_length; 58 | seal::EncryptionParameters m_seal_params; 59 | std::shared_ptr m_context; 60 | seal::KeyGenerator* m_keygen; 61 | seal::PublicKey* m_public_key; 62 | seal::SecretKey* m_secret_key; 63 | seal::RelinKeys* m_relin_keys; 64 | seal::Encryptor* m_encryptor; 65 | seal::Decryptor* m_decryptor; 66 | }; 67 | -------------------------------------------------------------------------------- /he-samples/examples/secure-query/include/sqserver.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | 9 | #include "seal/seal.h" 10 | #include "sq_helper_functions.h" 11 | #include "sq_types.h" 12 | 13 | class SQServer { 14 | public: 15 | SQServer(); 16 | void initializeSealContext(size_t _polymodulus_degree, 17 | size_t _plaintext_modulus); 18 | void createSealContextFromParameters(seal::EncryptionParameters _params); 19 | void setPublicKey(const seal::PublicKey& public_key); 20 | void setEncryptedDatabase( 21 | const std::vector& encrypted_database); 22 | void setDatabase(const std::vector& db); 23 | size_t keyLength() const; 24 | void setKeyLength(const size_t& keyLength); 25 | /** 26 | * @brief `queryDatabaseForMatchingEntry` compares the query key against the 27 | * key value of all database entries and returns a ciphertext containing the 28 | * entry's value if a match is found. 29 | * @param query_key is a vector of ciphertexts representing the encoded and 30 | * encrypted query key. 31 | * @param relin_keys used for relinearization during function execution 32 | * @return A ciphertext containing the encrypted value if a match is found or 33 | * zero if the query did not match any of the database keys. 34 | */ 35 | seal::Ciphertext queryDatabaseForMatchingEntry( 36 | std::vector query_key, seal::RelinKeys relin_keys); 37 | 38 | private: 39 | void createPlain1CT(); 40 | /** 41 | * @brief `generateComparisonMaskUsingFLT` performs a comparison between 42 | * `cipher1` and `cipher2` that evaluates to 1 if they are same value or 0 if 43 | * they differ. 44 | * 45 | * This comparison is achieved by combining the property that plaintext 46 | * values are defined modulo the plaintext modulus and the concept from 47 | * Fermat's little theorem which states that if `p` is a prime and `a` is any 48 | * integer not divisible by `p` then `a^(p-1) = 1 % p`, where `a` is an 49 | * integer and `p` is the plaintext modulus. To perform this comparison 50 | * `cipher1` is subtracted from `cipher2` to generate a mask ciphertext. The 51 | * value of this mask is 0 if `cipher1` and `cipher2` are the same value or 52 | * some value between `(-p,-1)` or `(1,p)`. We then raise this value to `p - 53 | * 1` which per FLT results in the value being `1 % p = 1 iff a != 0` or `0 54 | * iff a = 0`. We finally negate the value and add 1, resulting in 0 for all 55 | * non matches and 1 for any matching case. 56 | * @param cipher1 contains a value between `[0,p)`. 57 | * @param cipher2 contains a value between `[0,p)`. 58 | * @param relin_keys required for exponentiating in place. 59 | * @return A ciphertext encoding a value of 1 if the value of `cipher1 == 60 | * cipher2` or 0 if the value of `cipher1 != cipher2`. 61 | */ 62 | seal::Ciphertext generateComparisonMaskUsingFLT( 63 | const seal::Ciphertext& cipher1, const seal::Ciphertext& cipher2, 64 | seal::RelinKeys relin_keys); 65 | 66 | size_t m_key_length; 67 | bool m_encryption_enabled = false; 68 | seal::PublicKey m_public_key; 69 | seal::EncryptionParameters m_seal_params; 70 | std::shared_ptr m_context; 71 | seal::Encryptor* m_encryptor; 72 | seal::Evaluator* m_evaluator; 73 | std::vector m_encrypted_database; 74 | bool m_bplain1_created = false; 75 | seal::Ciphertext m_plain_1_ct; 76 | }; 77 | -------------------------------------------------------------------------------- /he-samples/examples/secure-query/include/timer.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace intel { 10 | namespace Common { 11 | 12 | /** 13 | * \class Timer 14 | * \details 15 | * 16 | * This class provides operations to track time. 17 | * 18 | * 19 | **/ 20 | class Timer { 21 | public: 22 | /** 23 | * @brief Constructor for the Timer Class 24 | * @param high_precision - flag to use high precision for time 25 | * @param start_active - flag to start timer instantly 26 | */ 27 | Timer(bool high_precision = false, bool start_active = false) { 28 | m_active = false; 29 | m_high_precision_mode = high_precision; 30 | if (start_active == true) { 31 | start(); 32 | } 33 | } 34 | 35 | /** 36 | * @brief start timer instantly 37 | */ 38 | void start() { 39 | if (m_high_precision_mode) 40 | m_high_start_time = std::chrono::high_resolution_clock::now(); 41 | else 42 | m_start_time = std::chrono::system_clock::now(); 43 | 44 | m_active = true; 45 | } 46 | 47 | /** 48 | * @brief stop timer instantly 49 | */ 50 | void stop() { 51 | if (m_high_precision_mode) 52 | m_high_end_time = std::chrono::high_resolution_clock::now(); 53 | else 54 | m_end_time = std::chrono::system_clock::now(); 55 | 56 | m_active = false; 57 | } 58 | 59 | /** 60 | * @brief indicate the timer is active or not 61 | * @return boolean - status of the timer 62 | */ 63 | bool isActive() { return m_active; } 64 | 65 | /** 66 | * @brief number of ellapse time that timer is holding 67 | * @param micro - indicates returning in ms or micro-second 68 | * @return number of elappsed milli-second or micro-second 69 | */ 70 | double elapsedMilliseconds(bool micro = false) { 71 | std::chrono::time_point endTime; 72 | std::chrono::time_point highEndTime; 73 | 74 | if (m_active) { 75 | if (m_high_precision_mode) 76 | highEndTime = std::chrono::high_resolution_clock::now(); 77 | else 78 | endTime = std::chrono::system_clock::now(); 79 | } else { 80 | if (m_high_precision_mode) 81 | highEndTime = m_high_end_time; 82 | else 83 | endTime = m_end_time; 84 | } 85 | 86 | if (micro == false) { 87 | if (m_high_precision_mode) 88 | return std::chrono::duration(highEndTime - 89 | m_high_start_time) 90 | .count(); 91 | else 92 | return std::chrono::duration(endTime - m_start_time) 93 | .count(); 94 | } else { 95 | if (m_high_precision_mode) 96 | return std::chrono::duration(highEndTime - 97 | m_high_start_time) 98 | .count(); 99 | else 100 | return std::chrono::duration(endTime - m_start_time) 101 | .count(); 102 | } 103 | } 104 | 105 | /** 106 | * @brief number of elapse time that timer is holding 107 | * @return number of elapsed second 108 | */ 109 | double elapsedSeconds() { return elapsedMilliseconds() / 1000.0; } 110 | 111 | /** 112 | * @brief number of elapse time that timer is holding 113 | * @return number of elappsed micro second 114 | */ 115 | double elapsedMicroSeconds() { return elapsedMilliseconds(true); } 116 | 117 | private: 118 | // Standard 119 | std::chrono::time_point m_start_time; 120 | std::chrono::time_point m_end_time; 121 | 122 | // High 123 | std::chrono::time_point m_high_start_time; 124 | std::chrono::time_point m_high_end_time; 125 | 126 | bool m_active; 127 | bool m_high_precision_mode; 128 | }; 129 | 130 | } // namespace Common 131 | } // namespace intel 132 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | project(sample-kernels LANGUAGES CXX) 5 | 6 | cmake_minimum_required(VERSION 3.22 FATAL_ERROR) 7 | 8 | ## Use -std=c++17 as default. 9 | set(CMAKE_CXX_STANDARD 17) 10 | ## Disable C++ extensions 11 | set(CMAKE_CXX_EXTENSIONS OFF) 12 | ## Require full C++ standard 13 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 14 | 15 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 16 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 17 | 18 | set(SAMPLE_KERNELS_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) 19 | 20 | find_package (OpenMP REQUIRED) 21 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 22 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") 23 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") 24 | 25 | set(THREADS_PREFER_PTHREAD_FLAG ON) 26 | find_package(Threads REQUIRED) 27 | 28 | # Download gbenchmark and gtest libraries 29 | include(../cmake/gbenchmark.cmake) 30 | include(../cmake/gtest.cmake) 31 | 32 | if (${ENABLE_SEAL}) 33 | # SEAL sample kernel 34 | 35 | find_package(SEAL REQUIRED) 36 | 37 | set(SAMPLE_KERNEL_SEAL_SRC main.cpp 38 | seal/seal_ckks_sample.cpp 39 | seal/seal_bfv_sample.cpp 40 | ) 41 | 42 | add_executable(sample-kernels-seal ${SAMPLE_KERNEL_SEAL_SRC}) 43 | 44 | target_include_directories(sample-kernels-seal PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) 45 | 46 | target_link_libraries(sample-kernels-seal PRIVATE SEAL::seal seal_kernel_executor) 47 | target_link_libraries(sample-kernels-seal PRIVATE libgbenchmark) 48 | target_link_libraries(sample-kernels-seal PRIVATE Threads::Threads) 49 | endif() 50 | 51 | if (${ENABLE_PALISADE}) 52 | # PALISADE sample kernel 53 | 54 | # Find HEXL (optional) 55 | find_package(HEXL) 56 | 57 | # Find package Palisade and create library libpalisade 58 | include(../cmake/palisade.cmake) 59 | 60 | set(SAMPLE_KERNEL_PALISADE_SRC main.cpp 61 | palisade/palisade_ckks_sample.cpp 62 | palisade/palisade_bfv_sample.cpp 63 | ) 64 | 65 | add_executable(sample-kernels-palisade ${SAMPLE_KERNEL_PALISADE_SRC}) 66 | 67 | target_include_directories(sample-kernels-palisade PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) 68 | 69 | target_link_libraries(sample-kernels-palisade PRIVATE libpalisade palisade_kernel_executor) 70 | target_link_libraries(sample-kernels-palisade PRIVATE libgbenchmark) 71 | target_link_libraries(sample-kernels-palisade PRIVATE Threads::Threads) 72 | 73 | endif() 74 | 75 | add_subdirectory(test) 76 | add_subdirectory(kernels) 77 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/kernel_util.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include 9 | 10 | namespace intel { 11 | namespace he { 12 | 13 | #define ADD_SAMPLE_HE_ARGS Args({8192, 3}) 14 | 15 | // Generates a vector of type T with size slots small entries 16 | template 17 | inline std::vector generateVector(size_t slots, size_t row_size = 0, 18 | size_t n_rows = 2, size_t n_slots = 4) { 19 | std::vector input(slots, static_cast(0)); 20 | if (row_size == 0) { 21 | for (size_t i = 0; i < slots; ++i) { 22 | input[i] = static_cast(i); 23 | } 24 | } else { 25 | for (size_t r = 0; r < n_rows; ++r) { 26 | for (size_t i = 0; i < n_slots; ++i) { 27 | input[i + r * row_size] = static_cast(i + r * n_slots); 28 | } 29 | } 30 | } 31 | return input; 32 | } 33 | } // namespace he 34 | } // namespace intel 35 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/kernels/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # PALISADE ops 5 | if (${ENABLE_PALISADE}) 6 | set(PALISADE_KERNEL_EXECUTOR_SRC 7 | palisade/palisade_ckks_kernel_executor.cpp 8 | palisade/palisade_bfv_kernel_executor.cpp 9 | palisade/palisade_omp_utils.cpp 10 | ) 11 | 12 | add_library(palisade_kernel_executor SHARED ${PALISADE_KERNEL_EXECUTOR_SRC}) 13 | target_include_directories(palisade_kernel_executor PRIVATE ${SAMPLE_KERNELS_SRC_DIR}) 14 | target_link_libraries(palisade_kernel_executor PRIVATE libpalisade) 15 | endif() 16 | 17 | # SEAL ops 18 | if (${ENABLE_SEAL}) 19 | set(SEAL_KERNEL_EXECUTOR_SRC 20 | seal/seal_ckks_kernel_executor.cpp 21 | seal/seal_ckks_context.cpp 22 | seal/seal_bfv_kernel_executor.cpp 23 | seal/seal_bfv_context.cpp 24 | seal/seal_omp_utils.cpp 25 | ) 26 | 27 | add_library(seal_kernel_executor SHARED ${SEAL_KERNEL_EXECUTOR_SRC}) 28 | target_include_directories(seal_kernel_executor PRIVATE ${SAMPLE_KERNELS_SRC_DIR}) 29 | target_link_libraries(seal_kernel_executor PRIVATE SEAL::seal) 30 | endif() 31 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/kernels/omp_utils.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | namespace intel { 7 | namespace he { 8 | 9 | class OMPUtilities { 10 | public: 11 | static int assignOMPThreads(int& remaining_threads, int requested_threads) { 12 | int retval = (requested_threads > 0 ? requested_threads : 1); 13 | if (retval > remaining_threads) retval = remaining_threads; 14 | if (retval > 1) 15 | remaining_threads -= retval; 16 | else 17 | retval = 1; 18 | return retval; 19 | } 20 | }; 21 | 22 | } // namespace he 23 | } // namespace intel 24 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/kernels/palisade/omp_utils_palisade.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "omp_utils_palisade.h" 5 | 6 | #include 7 | 8 | namespace intel { 9 | namespace he { 10 | namespace palisade { 11 | 12 | const int OMPUtilitiesP::MaxThreads = omp_get_max_threads(); 13 | 14 | } // namespace palisade 15 | } // namespace he 16 | } // namespace intel 17 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/kernels/palisade/omp_utils_palisade.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | #include "kernels/omp_utils.h" 7 | 8 | namespace intel { 9 | namespace he { 10 | namespace palisade { 11 | 12 | class OMPUtilitiesP : public OMPUtilities { 13 | public: 14 | static const int MaxThreads; 15 | }; 16 | 17 | } // namespace palisade 18 | } // namespace he 19 | } // namespace intel 20 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/kernels/palisade/palisade_bfv_kernel_executor.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace intel { 11 | namespace he { 12 | namespace palisade { 13 | 14 | class PalisadeBFVKernelExecutor { 15 | public: 16 | explicit PalisadeBFVKernelExecutor( 17 | lbcrypto::CryptoContext& cc, 18 | lbcrypto::LPPublicKey& public_key); 19 | 20 | // Performs a ciphertext-plaintext dot product. 21 | // @param arg1 Ciphertext of shape (dim1 x dim2) in column-major format 22 | // @param arg2 Plaintext of shape (dim2 x dim3) in column-major format 23 | // @param dim1 Input shape dimension 24 | // @param dim2 Input shape dimension 25 | // @param dim3 Input shape dimension 26 | // @return A ciphertext matrix of dimension (dim1 x dim3) in column-major 27 | // format 28 | std::vector> dotPlainBatchAxis( 29 | const std::vector>& A, 30 | const std::vector& B, size_t dim1, size_t dim2, 31 | size_t dim3); 32 | 33 | // Performs a ciphertext-ciphertext dot product. 34 | // @param arg1 Ciphertext of shape (dim1 x dim2) in column-major format 35 | // @param arg2 Ciphertext of shape (dim2 x dim3) in column-major format 36 | // @param dim1 Input shape dimension 37 | // @param dim2 Input shape dimension 38 | // @param dim3 Input shape dimension 39 | // @return A ciphertext matrix of dimension (dim1 x dim3) in column-major 40 | // format 41 | std::vector> dotCipherBatchAxis( 42 | const std::vector>& A, 43 | const std::vector>& B, 44 | size_t dim1, size_t dim2, size_t dim3); 45 | 46 | std::vector>> matMulEIP( 47 | const std::vector>& cipher_a, 48 | const std::vector>& cipher_b, 49 | size_t dim1, size_t dim2, size_t dim3, size_t batch_size); 50 | 51 | std::vector>> matMulVal( 52 | const std::vector>& cipher_a, 53 | const std::vector>& cipher_b, 54 | size_t dim1, size_t dim2, size_t dim3); 55 | 56 | std::vector> matMulRow( 57 | const std::vector>& vec_cipher_a, 58 | const lbcrypto::Ciphertext& cipher_b, size_t dim1, 59 | size_t dim2, size_t dim3, int64_t slots); 60 | 61 | // Returns index of coordinate [i,j] of [dim1 x dim2] matrix stored in 62 | // column-major format 63 | static size_t colMajorIndex(size_t dim1, size_t dim2, size_t i, size_t j) { 64 | if (i >= dim1) { 65 | std::stringstream ss; 66 | ss << i << " too large for dim1 (" << dim1 << ")"; 67 | throw std::runtime_error(ss.str()); 68 | } 69 | if (j >= dim2) { 70 | std::stringstream ss; 71 | ss << j << " too large for dim2 (" << dim2 << ")"; 72 | throw std::runtime_error(ss.str()); 73 | } 74 | return i + j * dim1; 75 | } 76 | 77 | private: 78 | lbcrypto::CryptoContext m_context; 79 | lbcrypto::LPPublicKey m_public_key; 80 | }; 81 | 82 | } // namespace palisade 83 | } // namespace he 84 | } // namespace intel 85 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/kernels/palisade/palisade_omp_utils.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "palisade_omp_utils.h" 5 | 6 | #include 7 | 8 | namespace intel { 9 | namespace he { 10 | namespace palisade { 11 | 12 | const int OMPUtilitiesP::MaxThreads = omp_get_max_threads(); 13 | 14 | } // namespace palisade 15 | } // namespace he 16 | } // namespace intel 17 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/kernels/palisade/palisade_omp_utils.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | #include "kernels/omp_utils.h" 7 | 8 | namespace intel { 9 | namespace he { 10 | namespace palisade { 11 | 12 | class OMPUtilitiesP : public OMPUtilities { 13 | public: 14 | static const int MaxThreads; 15 | }; 16 | 17 | } // namespace palisade 18 | } // namespace he 19 | } // namespace intel 20 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/kernels/seal/seal_bfv_context.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "kernels/seal/seal_bfv_context.h" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | namespace intel { 12 | namespace he { 13 | namespace heseal { 14 | 15 | std::vector SealBFVContext::encodeVector( 16 | const gsl::span& values, size_t batch_size) { 17 | size_t total_chunks = 18 | values.size() / batch_size + (values.size() % batch_size == 0 ? 0 : 1); 19 | size_t last_chunk_size = 20 | values.size() % batch_size == 0 ? batch_size : values.size() % batch_size; 21 | 22 | std::vector ret(total_chunks); 23 | #pragma omp parallel for 24 | for (size_t i = 0; i < total_chunks; ++i) { 25 | size_t actual_chunk_size = 26 | (i == total_chunks - 1) ? last_chunk_size : batch_size; 27 | gsl::span data_chunk(&values[i * batch_size], actual_chunk_size); 28 | m_batch_encoder->encode(data_chunk, ret[i]); 29 | } 30 | return ret; 31 | } 32 | 33 | std::vector SealBFVContext::decodeVector( 34 | const std::vector& plain, size_t batch_size) { 35 | std::vector ret(plain.size() * batch_size); 36 | #pragma omp parallel for 37 | for (std::size_t i = 0; i < plain.size(); ++i) { 38 | std::vector tmp; 39 | m_batch_encoder->decode(plain[i], tmp); 40 | for (size_t j = 0; j < batch_size; ++j) { 41 | ret[i * batch_size + j] = tmp[j]; 42 | } 43 | } 44 | return ret; 45 | } 46 | 47 | std::vector SealBFVContext::encryptVector( 48 | const std::vector& plain) { 49 | std::vector ret(plain.size()); 50 | #pragma omp parallel for 51 | for (std::size_t i = 0; i < plain.size(); ++i) 52 | m_encryptor->encrypt(plain[i], ret[i]); 53 | return ret; 54 | } 55 | 56 | std::vector SealBFVContext::decryptVector( 57 | const std::vector& cipher) { 58 | std::vector ret(cipher.size()); 59 | #pragma omp parallel for 60 | for (std::size_t i = 0; i < cipher.size(); ++i) 61 | m_decryptor->decrypt(cipher[i], ret[i]); 62 | return ret; 63 | } 64 | 65 | } // namespace heseal 66 | } // namespace he 67 | } // namespace intel 68 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/kernels/seal/seal_ckks_context.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "kernels/seal/seal_ckks_context.h" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace intel { 14 | namespace he { 15 | namespace heseal { 16 | 17 | std::vector SealCKKSContext::encodeVector( 18 | const gsl::span& values, size_t batch_size) { 19 | size_t total_chunks = 20 | values.size() / batch_size + (values.size() % batch_size == 0 ? 0 : 1); 21 | size_t last_chunk_size = 22 | values.size() % batch_size == 0 ? batch_size : values.size() % batch_size; 23 | 24 | std::vector ret(total_chunks); 25 | #pragma omp parallel for 26 | for (size_t i = 0; i < total_chunks; ++i) { 27 | size_t actual_chunk_size = 28 | (i == total_chunks - 1) ? last_chunk_size : batch_size; 29 | gsl::span data_chunk(&values[i * batch_size], actual_chunk_size); 30 | m_encoder->encode(data_chunk, m_scale, ret[i]); 31 | } 32 | return ret; 33 | } 34 | 35 | std::vector SealCKKSContext::encodeVector( 36 | const gsl::span& v) { 37 | std::size_t slot_count = m_encoder->slot_count(); 38 | std::size_t total_chunks = 39 | v.size() / slot_count + (v.size() % slot_count == 0 ? 0 : 1); 40 | gsl::span data = v; 41 | std::vector retval; 42 | retval.reserve(total_chunks); 43 | while (!data.empty()) { 44 | std::size_t actual_chunk_size = 45 | (data.size() > slot_count ? slot_count : data.size()); 46 | gsl::span data_chunk = data.first(actual_chunk_size); 47 | data = data.last(data.size() - actual_chunk_size); 48 | seal::Plaintext plain; 49 | m_encoder->encode(data_chunk, m_scale, plain); 50 | retval.emplace_back(std::move(plain)); 51 | } 52 | return retval; 53 | } 54 | 55 | std::vector SealCKKSContext::decodeVector( 56 | const std::vector& plain, size_t batch_size) { 57 | std::vector ret(plain.size() * batch_size); 58 | #pragma omp parallel for 59 | for (size_t i = 0; i < plain.size(); ++i) { 60 | std::vector tmp; 61 | m_encoder->decode(plain[i], tmp); 62 | std::copy(tmp.begin(), tmp.begin() + batch_size, 63 | ret.begin() + i * batch_size); 64 | } 65 | return ret; 66 | } 67 | 68 | std::vector SealCKKSContext::decodeVector( 69 | const std::vector& plain) { 70 | std::size_t slot_count = m_encoder->slot_count(); 71 | std::vector ret(plain.size() * slot_count); 72 | #pragma omp parallel for 73 | for (size_t i = 0; i < plain.size(); ++i) { 74 | std::vector tmp; 75 | m_encoder->decode(plain[i], tmp); 76 | std::size_t min_size = std::min(slot_count, tmp.size()); 77 | std::copy(tmp.begin(), tmp.begin() + min_size, 78 | ret.begin() + i * slot_count); 79 | } 80 | return ret; 81 | } 82 | 83 | std::vector SealCKKSContext::encryptVector( 84 | const std::vector& plain) { 85 | std::vector ret(plain.size()); 86 | #pragma omp parallel for 87 | for (size_t i = 0; i < plain.size(); ++i) 88 | m_encryptor->encrypt(plain[i], ret[i]); 89 | return ret; 90 | } 91 | 92 | std::vector SealCKKSContext::decryptVector( 93 | const std::vector& cipher) { 94 | std::vector ret(cipher.size()); 95 | #pragma omp parallel for 96 | for (size_t i = 0; i < cipher.size(); ++i) 97 | m_decryptor->decrypt(cipher[i], ret[i]); 98 | return ret; 99 | } 100 | 101 | } // namespace heseal 102 | } // namespace he 103 | } // namespace intel 104 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/kernels/seal/seal_omp_utils.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "seal_omp_utils.h" 5 | 6 | #include 7 | 8 | namespace intel { 9 | namespace he { 10 | namespace heseal { 11 | 12 | const int OMPUtilitiesS::MaxThreads = omp_get_max_threads(); 13 | std::vector OMPUtilitiesS::m_threads_per_level(1, 14 | OMPUtilitiesS::MaxThreads); 15 | 16 | void OMPUtilitiesS::setThreadsAtLevel(int level, int threads) { 17 | if (threads < 1) threads = 1; 18 | std::size_t level_i = static_cast(level < 0 ? 0 : level); 19 | if (level_i >= m_threads_per_level.size()) 20 | m_threads_per_level.resize(level_i + 1, 1); 21 | m_threads_per_level[level_i] = threads; 22 | } 23 | 24 | int OMPUtilitiesS::getThreadsAtLevel() { 25 | return getThreadsAtLevel(omp_get_active_level()); 26 | } 27 | 28 | int OMPUtilitiesS::getThreadsAtLevel(int level) { 29 | int retval = 1; 30 | std::size_t level_i = static_cast(level < 0 ? 0 : level); 31 | if (level_i < m_threads_per_level.size()) { 32 | retval = m_threads_per_level[level_i]; 33 | } 34 | return retval; 35 | } 36 | 37 | } // namespace heseal 38 | } // namespace he 39 | } // namespace intel 40 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/kernels/seal/seal_omp_utils.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include "kernels/omp_utils.h" 9 | 10 | namespace intel { 11 | namespace he { 12 | namespace heseal { 13 | 14 | class OMPUtilitiesS : public OMPUtilities { 15 | public: 16 | static const int MaxThreads; 17 | 18 | /** 19 | * @brief Sets number of threads to assign at specified nesting level. 20 | * @param level[in] OpenMP nesting level. 21 | * @param threads[in] Number of threads to assign at specified nesting level. 22 | */ 23 | static void setThreadsAtLevel(int level, int threads); 24 | /** 25 | * @brief Retrieves threads at specified level. 26 | * @param level[in] OpenMP nesting level. 27 | * @return Number of threads to assign at specified nesting level. 28 | */ 29 | static int getThreadsAtLevel(int level); 30 | /** 31 | * @brief Retrieves threads to assign to current OpenMP nesting level. 32 | */ 33 | static int getThreadsAtLevel(); 34 | 35 | private: 36 | static std::vector m_threads_per_level; 37 | }; 38 | 39 | } // namespace heseal 40 | } // namespace he 41 | } // namespace intel 42 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | 6 | int main(int argc, char** argv) { 7 | benchmark::Initialize(&argc, argv); 8 | benchmark::RunSpecifiedBenchmarks(); 9 | } 10 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/palisade/palisade_util.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #pragma once 14 | 15 | namespace intel { 16 | namespace he { 17 | namespace palisade { 18 | 19 | inline auto generatePalisadeCKKSContext(int poly_modulus_degree, 20 | std::vector coeff_modulus_bits) { 21 | uint64_t numPrimes = 22 | coeff_modulus_bits.size(); // count of primes for moduli chain 23 | uint64_t scaleExp = 50; // plaintext scaling factor 24 | 25 | // bits in the base of digits in key-switching/relinearization 26 | uint64_t relinWindow = 0; // 0 = RNS Decomposition 27 | int batch_size = poly_modulus_degree / 2; // Batch size == slot count 28 | int depth = 1; // Starting Depth of supported computation circuit? (unused?) 29 | int maxDepth = 5; // Max power of secret key for relin key generation 30 | int firstModSize = 60; // bit-length of the first modulus 31 | uint32_t numLargeDigits = 4; // number of big digits when ksTech == Hybrid 32 | 33 | // Get CKKS crypto context and generate encryption keys. 34 | auto palisade_context = lbcrypto::CryptoContextFactory:: 35 | genCryptoContextCKKSWithParamsGen( 36 | poly_modulus_degree * 2, numPrimes, scaleExp, relinWindow, batch_size, 37 | MODE::OPTIMIZED, depth, maxDepth, firstModSize, 38 | lbcrypto::KeySwitchTechnique::BV, 39 | lbcrypto::RescalingTechnique::APPROXRESCALE, numLargeDigits); 40 | 41 | palisade_context->Enable(PKESchemeFeature::ENCRYPTION); 42 | palisade_context->Enable(PKESchemeFeature::SHE); 43 | palisade_context->Enable(PKESchemeFeature::LEVELEDSHE); 44 | 45 | return palisade_context; 46 | } 47 | 48 | inline auto generatePalisadeBFVContext(int poly_modulus_degree, 49 | std::vector coeff_modulus_bits) { 50 | int plaintext_modulus = 65537; 51 | lbcrypto::SecurityLevel security_level = lbcrypto::HEStd_NotSet; 52 | float dist = 3.19; 53 | int num_adds = 0; 54 | int num_mults = coeff_modulus_bits.size(); 55 | int num_key_switches = 0; 56 | MODE mode = OPTIMIZED; 57 | // max size of relin key. 2 means we relin after every mult 58 | int max_depth = 2; 59 | 60 | // bits in the base of digits in key-switching/relinearization 61 | // (0 - means to use only CRT decomposition) 62 | uint64_t relin_window = 0; 63 | int dcrt_bits = 60; // size of "small" CRT moduli. 64 | 65 | auto palisade_context = lbcrypto::CryptoContextFactory:: 66 | genCryptoContextBFVrns(plaintext_modulus, security_level, dist, num_adds, 67 | num_mults, num_key_switches, mode, max_depth, 68 | relin_window, dcrt_bits, poly_modulus_degree); 69 | 70 | palisade_context->Enable(PKESchemeFeature::ENCRYPTION); 71 | palisade_context->Enable(PKESchemeFeature::SHE); 72 | 73 | return palisade_context; 74 | } 75 | 76 | inline auto generatePalisadeBFVBContext(int poly_modulus_degree, 77 | std::vector coeff_modulus_bits) { 78 | int plaintext_modulus = 65537; 79 | lbcrypto::SecurityLevel security_level = lbcrypto::HEStd_NotSet; 80 | float dist = 3.19; 81 | int num_adds = 0; 82 | int num_mults = coeff_modulus_bits.size(); 83 | int num_key_switches = 0; 84 | MODE mode = OPTIMIZED; 85 | // max size of relin key. 2 means we relin after every mult 86 | int max_depth = 2; 87 | 88 | // bits in the base of digits in key-switching/relinearization 89 | // (0 - means to use only CRT decomposition) 90 | uint64_t relin_window = 0; 91 | int dcrt_bits = 60; // size of "small" CRT moduli. 92 | 93 | auto palisade_context = lbcrypto::CryptoContextFactory:: 94 | genCryptoContextBFVrnsB(plaintext_modulus, security_level, dist, num_adds, 95 | num_mults, num_key_switches, mode, max_depth, 96 | relin_window, dcrt_bits, poly_modulus_degree); 97 | 98 | palisade_context->Enable(PKESchemeFeature::ENCRYPTION); 99 | palisade_context->Enable(PKESchemeFeature::SHE); 100 | 101 | return palisade_context; 102 | } 103 | 104 | } // namespace palisade 105 | } // namespace he 106 | } // namespace intel 107 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | set(SRC main.cpp) 5 | 6 | if (${ENABLE_PALISADE}) 7 | set(SRC ${SRC} 8 | test_palisade.cpp 9 | test_palisade_ckks_kernel_executor.cpp 10 | test_palisade_bfv_kernel_executor.cpp 11 | ) 12 | endif() 13 | 14 | if (${ENABLE_SEAL}) 15 | set(SRC ${SRC} 16 | test_seal.cpp 17 | test_seal_ckks_kernel_executor.cpp 18 | test_seal_bfv_kernel_executor.cpp 19 | ) 20 | endif() 21 | 22 | add_executable(unit-test ${SRC}) 23 | 24 | target_include_directories(unit-test 25 | PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} 26 | PRIVATE ${SAMPLE_KERNELS_SRC_DIR} 27 | ) 28 | 29 | target_link_libraries(unit-test PRIVATE libgtest) 30 | 31 | if (${ENABLE_PALISADE}) 32 | target_link_libraries(unit-test PRIVATE libpalisade palisade_kernel_executor) 33 | endif() 34 | 35 | if (${ENABLE_SEAL}) 36 | target_link_libraries(unit-test PRIVATE SEAL::seal seal_kernel_executor) 37 | endif() 38 | 39 | target_link_libraries(unit-test PRIVATE Threads::Threads) 40 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/test/README.md: -------------------------------------------------------------------------------- 1 | # Unit Tests 2 | This directory contains unit tests for the sample kernels built using both SEAL 3 | and PALISADE. These tests are used to verify the accuracy of the various 4 | [sample kernels](../) included in this project. 5 | 6 | Assuming the sample kernels were built via `hekit` using the provided recipe 7 | `sample-kernels.toml` they can run by calling 8 | ```bash 9 | $HOME/.hekit/components/sample-kernels/seal/build/test/unit-test 10 | $HOME/.hekit/components/sample-kernels/palisade/build/test/unit-test 11 | ``` 12 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/test/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | 6 | int main(int argc, char** argv) { 7 | ::testing::InitGoogleTest(&argc, argv); 8 | int rc = RUN_ALL_TESTS(); 9 | return rc; 10 | } 11 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/test/test_palisade.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace intel { 11 | namespace he { 12 | namespace palisade { 13 | 14 | TEST(palisade, simple_real_numbers) { 15 | uint32_t multDepth = 1; 16 | uint32_t scaleFactorBits = 50; 17 | uint32_t batchSize = 8; 18 | 19 | lbcrypto::SecurityLevel securityLevel = lbcrypto::HEStd_128_classic; 20 | 21 | // The following call creates a CKKS crypto context based on the 22 | // arguments defined above. 23 | lbcrypto::CryptoContext cc = 24 | lbcrypto::CryptoContextFactory::genCryptoContextCKKS( 25 | multDepth, scaleFactorBits, batchSize, securityLevel); 26 | // Enable the features that you wish to use 27 | cc->Enable(ENCRYPTION); 28 | cc->Enable(SHE); 29 | 30 | auto keys = cc->KeyGen(); 31 | cc->EvalMultKeyGen(keys.secretKey); 32 | cc->EvalAtIndexKeyGen(keys.secretKey, {1, -2}); 33 | 34 | // Inputs 35 | std::vector x1 = {0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0, 5.0}; 36 | std::vector x2 = {5.0, 4.0, 3.0, 2.0, 1.0, 0.75, 0.5, 0.25}; 37 | 38 | // Encoding as plaintexts 39 | lbcrypto::Plaintext ptxt1 = cc->MakeCKKSPackedPlaintext(x1); 40 | lbcrypto::Plaintext ptxt2 = cc->MakeCKKSPackedPlaintext(x2); 41 | 42 | // Encrypt the encoded vectors 43 | auto c1 = cc->Encrypt(keys.publicKey, ptxt1); 44 | auto c2 = cc->Encrypt(keys.publicKey, ptxt2); 45 | 46 | // Homomorphic addition 47 | auto cAdd = cc->EvalAdd(c1, c2); 48 | 49 | // Homomorphic subtraction 50 | auto cSub = cc->EvalSub(c1, c2); 51 | 52 | // Homomorphic scalar multiplication 53 | auto cScalar = cc->EvalMult(c1, 4.0); 54 | 55 | // Homomorphic multiplication 56 | auto cMul = cc->EvalMult(c1, c2); 57 | 58 | // Homomorphic rotations 59 | auto cRot1 = cc->EvalAtIndex(c1, 1); 60 | auto cRot2 = cc->EvalAtIndex(c1, -2); 61 | 62 | // Step 5: Decryption and output 63 | lbcrypto::Plaintext result; 64 | 65 | // Decrypt the result of addition 66 | cc->Decrypt(keys.secretKey, cAdd, &result); 67 | result->SetLength(batchSize); 68 | 69 | // Decrypt the result of subtraction 70 | cc->Decrypt(keys.secretKey, cSub, &result); 71 | result->SetLength(batchSize); 72 | 73 | // Decrypt the result of scalar multiplication 74 | cc->Decrypt(keys.secretKey, cScalar, &result); 75 | result->SetLength(batchSize); 76 | 77 | // Decrypt the result of multiplication 78 | cc->Decrypt(keys.secretKey, cMul, &result); 79 | result->SetLength(batchSize); 80 | 81 | // Decrypt the result of rotations 82 | cc->Decrypt(keys.secretKey, cRot1, &result); 83 | result->SetLength(batchSize); 84 | 85 | cc->Decrypt(keys.secretKey, cRot2, &result); 86 | result->SetLength(batchSize); 87 | 88 | ASSERT_TRUE(true); 89 | } 90 | 91 | } // namespace palisade 92 | } // namespace he 93 | } // namespace intel 94 | -------------------------------------------------------------------------------- /he-samples/sample-kernels/test/test_util.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Intel Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace intel { 10 | namespace he { 11 | 12 | // Generates a vector of type T with size slots small entries 13 | template 14 | inline std::vector generateVector(size_t slots, size_t row_size = 0, 15 | size_t n_rows = 2, size_t n_slots = 4) { 16 | std::vector input(slots, static_cast(0)); 17 | if (row_size == 0) { 18 | for (size_t i = 0; i < slots; ++i) { 19 | input[i] = static_cast(i); 20 | } 21 | } else { 22 | for (size_t r = 0; r < n_rows; ++r) { 23 | for (size_t i = 0; i < n_slots; ++i) { 24 | input[i + r * row_size] = static_cast(i + r * n_slots); 25 | } 26 | } 27 | } 28 | return input; 29 | } 30 | 31 | template 32 | void checkEqual(const std::vector& x, const std::vector& y, 33 | T abs_error = T(0.001)) { 34 | ASSERT_EQ(x.size(), y.size()); 35 | for (size_t i = 0; i < x.size(); ++i) { 36 | ASSERT_NEAR(x[i], y[i], 0.001); 37 | } 38 | } 39 | 40 | template 41 | void checkEqual(const std::vector>& x, 42 | const std::vector>& y, T abs_error = T(0.001)) { 43 | ASSERT_EQ(x.size(), y.size()); 44 | for (size_t i = 0; i < x.size(); ++i) { 45 | checkEqual(x[i], y[i], abs_error); 46 | } 47 | } 48 | 49 | template 50 | double evaluatePolygon_HornerMethod(double input, const CollectionT& coeff) { 51 | double retval; 52 | auto it = coeff.rbegin(); 53 | retval = *it; 54 | for (++it; it != coeff.rend(); ++it) retval = retval * input + *it; 55 | return retval; 56 | } 57 | 58 | template 59 | double approxSigmoid(double x); 60 | 61 | template <> 62 | inline double approxSigmoid<3>(double x) { 63 | // f3(x) ~= 0.5 + 1.20096(x/8) - 0.81562(x/8)^3 64 | std::array poly = {0.5, 0.15012, 0.0, -0.001593008}; 65 | double retval = evaluatePolygon_HornerMethod(x, poly); 66 | if (x < -5.0 || retval < 0.0) 67 | retval = 0.0; 68 | else if (x > 5.0 || retval > 1.0) 69 | retval = 1.0; 70 | return retval; 71 | } 72 | 73 | } // namespace he 74 | } // namespace intel 75 | -------------------------------------------------------------------------------- /hekit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | toolkit_root_dir="${HEKITPATH:-"."}" 4 | plugins_root_dir="$(realpath -q ~/.hekit/plugins)" 5 | PYTHONPATH="$toolkit_root_dir:$plugins_root_dir" python3 -m kit.hekit "$@" 6 | -------------------------------------------------------------------------------- /kit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/kit/__init__.py -------------------------------------------------------------------------------- /kit/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/kit/commands/__init__.py -------------------------------------------------------------------------------- /kit/commands/install.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """This module fetches, builds, or installs the requested libraries""" 5 | 6 | from argparse import HelpFormatter 7 | 8 | from kit.utils.component_builder import install_components_from_recipe_file 9 | from kit.utils.subparsers import validate_input 10 | from kit.utils.config import config_required 11 | 12 | 13 | @config_required 14 | def install_components(args): 15 | """Install command""" 16 | install_components_from_recipe_file( 17 | args.recipe_file, 18 | args.upto_stage, 19 | args.config.repo_location, 20 | args.force, 21 | args.recipe_arg, 22 | ) 23 | 24 | 25 | def get_recipe_arg_dict(recipe_arg: str) -> dict[str, str] | None: 26 | """Returns a dictionary filled with recipe_arg values""" 27 | 28 | pairs = [pair.split("=") for pair in recipe_arg.replace(" ", "").split(",")] 29 | try: 30 | return dict(pairs) 31 | except ValueError as e: 32 | for pair in pairs: 33 | if len(pair) != 2: 34 | raise ValueError(f"Wrong format for {pair}. Expected key=value") from e 35 | return None 36 | 37 | 38 | def set_install_subparser(subparsers) -> None: 39 | """create the parser for the 'install' command""" 40 | actions = ["install", "build", "fetch"] 41 | 42 | for action in actions: 43 | parser = subparsers.add_parser( 44 | action, 45 | description=f"{action} components", 46 | formatter_class=lambda prog: HelpFormatter(prog, max_help_position=30), 47 | ) 48 | parser.add_argument( 49 | "recipe_file", 50 | metavar="recipe-file", 51 | type=validate_input, 52 | help=f"TOML file for {action}", 53 | ) 54 | parser.add_argument( 55 | "--recipe_arg", 56 | default={}, 57 | type=get_recipe_arg_dict, 58 | help="Collection of key=value pairs separated by commas. The content of the TOML file will be replaced with this data.", 59 | ) 60 | 61 | if action == "fetch": 62 | parser.set_defaults(fn=install_components, upto_stage=action, force=False) 63 | return # Don't include the rest 64 | 65 | parser.add_argument( 66 | "-f", "--force", action="store_true", help=f"Re-execute {action}" 67 | ) 68 | parser.set_defaults(fn=install_components, upto_stage=action) 69 | -------------------------------------------------------------------------------- /kit/commands/remove.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """This module removes specific libraries""" 5 | 6 | from shutil import rmtree 7 | from os import listdir 8 | from kit.utils.tab_completion import components_completer, instances_completer 9 | from kit.utils.subparsers import validate_input 10 | from kit.utils.yes import is_yes 11 | from kit.utils.config import config_required 12 | 13 | 14 | @config_required 15 | def remove_components(args): 16 | """Remove component instances""" 17 | try: 18 | user_answer = "y" 19 | request_info = args.y 20 | component = args.component 21 | instance = args.instance 22 | repo_path = args.config.repo_location 23 | comp_path = f"{repo_path}/{component}" 24 | inst_path = f"{comp_path}/{instance}" 25 | 26 | if args.all: 27 | # Case: delete all components 28 | if component or instance: 29 | raise ValueError( 30 | "Flag '--all' cannot be used when specifying a component or instance" 31 | ) 32 | if request_info: 33 | user_answer = input( 34 | f"All components in {repo_path} will be deleted. Do you want to continue? (y/n) " 35 | ) 36 | if is_yes(user_answer): 37 | rmtree(repo_path) 38 | print("All components successfully removed") 39 | elif not component: 40 | raise ValueError( 41 | "A component or flag '--all' should be specified as argument" 42 | ) 43 | elif not instance: 44 | # Case: delete all instances of a component 45 | if request_info: 46 | user_answer = input( 47 | f"All instances of component '{component}' will be deleted. Do you want to continue? (y/n) " 48 | ) 49 | if is_yes(user_answer): 50 | rmtree(comp_path) 51 | print(f"All instances of component '{component}' successfully removed") 52 | else: 53 | # Case: delete a specific instances of a component 54 | rmtree(inst_path) 55 | print( 56 | f"Instance '{instance}' of component '{component}' successfully removed" 57 | ) 58 | 59 | # Delete the component directory if all its instances were deleted 60 | if len(listdir(comp_path)) == 0: 61 | rmtree(comp_path) 62 | 63 | except FileNotFoundError: 64 | print( 65 | "Nothing to remove", 66 | f"Instance '{instance}' of component '{component}' not found.", 67 | ) 68 | 69 | 70 | def set_remove_subparser(subparsers): 71 | """create the parser for the 'remove' command""" 72 | parser_remove = subparsers.add_parser( 73 | "remove", description="removes/uninstalls components" 74 | ) 75 | parser_remove.add_argument( 76 | "--all", action="store_true", help="remove all components" 77 | ) 78 | parser_remove.add_argument("-y", action="store_false", help="say yes to prompts") 79 | parser_remove.add_argument( 80 | "component", 81 | type=validate_input, 82 | help="component to be removed", 83 | nargs="?", 84 | default="", 85 | ).completer = components_completer 86 | parser_remove.add_argument( 87 | "instance", 88 | type=validate_input, 89 | help="instance to be removed", 90 | nargs="?", 91 | default="", 92 | ).completer = instances_completer 93 | parser_remove.set_defaults(fn=remove_components) 94 | -------------------------------------------------------------------------------- /kit/hekit.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """This module is the main entry point to the hekit command and subcommands for 5 | configuring the HE Toolkit environment 6 | """ 7 | 8 | import sys 9 | 10 | from os import geteuid 11 | from sys import stderr, exit as sys_exit 12 | from argparse import ArgumentParser, RawTextHelpFormatter 13 | 14 | from kit.utils.subparsers import ( 15 | get_options_description, 16 | register_subparser, 17 | validate_input, 18 | ) 19 | from kit.utils.constants import Constants 20 | from kit.utils.tab_completion import enable_tab_completion 21 | 22 | if sys.version_info < Constants.python_version_tuple: 23 | print( 24 | f"Intel HE Toolkit requires Python version {Constants.python_version_string} or above", 25 | file=sys.stderr, 26 | ) 27 | sys_exit(1) 28 | 29 | 30 | def parse_cmdline() -> tuple: 31 | """Parse commandline commands""" 32 | 33 | # create the top-level parser 34 | parser = ArgumentParser( 35 | prog="hekit", 36 | description="execute Intel HE Toolkit commands", 37 | formatter_class=RawTextHelpFormatter, 38 | ) 39 | parser.set_defaults(fn=None) 40 | parser.add_argument( 41 | "--debug", action="store_true", help="if exception occurs print out stacktrace" 42 | ) 43 | parser.add_argument( 44 | "--version", 45 | action="version", 46 | version=f"Intel HE Toolkit version {Constants.version}", 47 | help="display Intel HE toolkit version", 48 | ) 49 | parser.add_argument( 50 | "--config", 51 | type=validate_input, 52 | default="~/.hekit/default.config", 53 | help="use a non-default configuration file instead", 54 | ) 55 | 56 | # create subparsers for each command 57 | subparsers = parser.add_subparsers(metavar="sub-command") 58 | register_subparser(subparsers) 59 | 60 | # Get the name and description for each sub-command 61 | subparsers.help = get_options_description(subparsers.choices, width=20) 62 | 63 | # try to enable tab completion 64 | enable_tab_completion(parser) 65 | 66 | return parser.parse_args(), parser.print_help 67 | 68 | 69 | def main() -> None: 70 | """Starting point for program execution""" 71 | args, print_help = parse_cmdline() 72 | 73 | if args.fn is None: 74 | print("hekit requires a command", file=stderr) 75 | print_help(stderr) 76 | sys_exit(1) 77 | 78 | if args.debug is True: 79 | args.fn(args) 80 | sys_exit(0) 81 | 82 | try: 83 | args.fn(args) # Run the command 84 | except Exception as e: # pylint: disable=broad-except 85 | print("Error while running subcommand\n", f"{e!r}", file=stderr) 86 | sys_exit(1) 87 | 88 | 89 | if __name__ == "__main__": 90 | if geteuid() == 0: 91 | print("You cannot run hekit as root (a.k.a. superuser)") 92 | sys_exit(1) 93 | 94 | main() 95 | -------------------------------------------------------------------------------- /kit/tools/README.md: -------------------------------------------------------------------------------- 1 | # HE Tools 2 | 3 | The tools for `hekit` consist of useful commands for finding HE parameters 4 | based on user constraints. The following sections describe their details and usage. 5 | 6 | ## Dependencies 7 | - python >= 3.8 8 | 9 | ## gen-primes 10 | 11 | The command `hekit gen-primes` generates a list of sorted primes in range [n, 12 | m] where n and m are positive integers. 13 | 14 | ### Options 15 | 16 | The `gen-primes` command can be executed with the following options. 17 | 18 | | Option | Meaning | 19 | | --- | --- | 20 | | `-h, help` | Show the help message. | 21 | | `start` | start number. | 22 | | `stop number` | stop number. | 23 | 24 | ### Running 25 | 26 | To run the tool, simply provide start and stop arguments. For example: 27 | ```bash 28 | hekit gen-primes 1 100 29 | ``` 30 | 31 | ## algebras 32 | 33 | The command `hekit algebras` given the plaintext prime `p` and the required number of 34 | p-boxes `d` returns the available algebras. 35 | 36 | ### Options 37 | 38 | The `algebras` command can be executed with the following options. 39 | 40 | | Option | Meaning | 41 | | --- | --- | 42 | | `-h, help` | Show the help message. | 43 | | `-p` | Define plaintext prime. Default value is 2. | 44 | | `-d` | Define number of coefficients in a slot. Default value is 1. | 45 | | `--no-corrected` | Include corrected d orders. | 46 | | `--no-header` | Do not print headers. | 47 | 48 | The primes that can be used are only those provided in the `~/.hekit/primes.txt` file. If the file is not created by the user, it will be autogenerated with primes in range [2, 140 000] 49 | 50 | ### Running 51 | 52 | To run the tool, simply provide a single prime or a range where primes may be, 53 | or a combination of the two. For example, searching for algebras 54 | where the prime is 2, those between 11 and 25 inclusive, and 31. 55 | 56 | ```bash 57 | hekit algebras -p 2,11-25,31 58 | ``` 59 | 60 | Searching for algebras that give `d` larger than 1 simply pass the flag and 61 | argument in a similar manner to `p`. For example, searching algebras with 62 | the same `p`, but with `d` values of 2 and between 4 to 5, inclusive. 63 | 64 | ```bash 65 | hekit algebras -p 2,11-25,31 -d 2,4-5 66 | ``` 67 | 68 | For more information run 69 | ```bash 70 | hekit algebras -h 71 | ``` 72 | 73 | ### Result 74 | 75 | The table retuned by the searches have the following column headers shown below 76 | with their meanings, 77 | 78 | | Header | Meaning | 79 | | --- | --- | 80 | | `p` | plaintext prime | 81 | | `d` | number of coefficients in a slot (a.k.a. p-boxes) | 82 | | `m` | order of the cyclotomic polynomial | 83 | | `phi(m)` | the Euler totient of m which is the degree of the ciphertext and plaintext polynomial | 84 | | `nslots` | the number of slots in a ciphertext and plaintext polynomial (`phi(m)` / `d`) | 85 | -------------------------------------------------------------------------------- /kit/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/kit/tools/__init__.py -------------------------------------------------------------------------------- /kit/tools/gen_primes.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """This module generates a list of primes""" 5 | 6 | from kit.utils.primes import write_primes 7 | 8 | 9 | def set_gen_primes_subparser(subparsers): 10 | """Register subparser to generate primes""" 11 | 12 | parser = subparsers.add_parser( 13 | "gen-primes", 14 | description="generate primes in range [n, m] where n, m are positive integers", 15 | ) 16 | parser.add_argument("start", type=int, default=2, help="start number") 17 | parser.add_argument("stop", type=int, default=100, help="stop number") 18 | parser.set_defaults(fn=gen_primes) 19 | 20 | 21 | def gen_primes(args): 22 | """Generates a list of primes from start to stop values inclusive""" 23 | write_primes(args.start, args.stop) 24 | -------------------------------------------------------------------------------- /kit/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/kit/utils/__init__.py -------------------------------------------------------------------------------- /kit/utils/archive.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """This module provides a utility function to archive and compress directories and files""" 5 | 6 | import tarfile 7 | from pathlib import Path 8 | from typing import Iterable 9 | from kit.utils.typing import PathType 10 | 11 | 12 | def archive_and_compress( 13 | name: PathType, 14 | filepaths: Iterable[PathType], 15 | root: PathType, 16 | exist_ok: bool = False, 17 | ) -> None: 18 | """Archive and compress files and directories into tar.gz file""" 19 | root = Path(root if root else ".") 20 | # x:gz raises FileExistsError if file exists 21 | mode = "w:gz" if exist_ok else "x:gz" 22 | with tarfile.open(name, mode) as tar: 23 | for filepath in filepaths: 24 | tar.add(root / filepath, arcname=filepath) 25 | -------------------------------------------------------------------------------- /kit/utils/config.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """Module for dealing with the hekit configuration file""" 5 | 6 | from os import path 7 | from typing import NamedTuple 8 | from kit.utils.files import load_toml 9 | 10 | 11 | class Config(NamedTuple): 12 | """Represents a config""" 13 | 14 | config_filename: str 15 | repo_location: str 16 | 17 | 18 | class ConfigFileError(Exception): 19 | """Error for when config file is not constructed correctly""" 20 | 21 | 22 | def config_required(func): 23 | """Decorator that loads the config file before running the actual function""" 24 | 25 | def inner(args): 26 | # replace the filename with the actual config 27 | args.config = load_config(args.config) 28 | return func(args) 29 | 30 | return inner 31 | 32 | 33 | def load_config(filename: str) -> Config: 34 | """Load a config file in TOML format""" 35 | expand = path.expanduser # alias 36 | 37 | try: 38 | toml_dict = load_toml(filename) 39 | toml_dict = {k: expand(v) for k, v in toml_dict.items()} 40 | except Exception as e: 41 | raise ConfigFileError("Error while parsing config file\n", f" {e!r}") from e 42 | 43 | # deref kwargs this way, get exceptions unknown key for free 44 | return Config(expand(filename), **toml_dict) 45 | -------------------------------------------------------------------------------- /kit/utils/constants.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """This module defines constants that are used in the version, docker build and new commands""" 5 | 6 | from dataclasses import dataclass 7 | from getpass import getuser 8 | from pathlib import Path 9 | 10 | 11 | @dataclass(frozen=True, init=False) 12 | class Constants: # pylint: disable=too-many-instance-attributes 13 | """Defines constants for the he-toolkit""" 14 | 15 | # version and the docker's tags 16 | user: str = getuser() 17 | version: str = "2.1.0" 18 | base_label: str = f"{user}/ubuntu_he_base:{version}" 19 | toolkit_label: str = f"{user}/ubuntu_he_toolkit:{version}" 20 | vscode_label: str = f"{user}/ubuntu_he_vscode:{version}" 21 | custom_label: str = f"{user}/ubuntu_he_%s:{version}" 22 | 23 | # python version 24 | python_version_tuple = (3, 10) 25 | python_version_string = "3.10" 26 | 27 | # cmake properties 28 | cmake_min_version: str = "3.22" 29 | cmake_cxx_standard: str = "17" 30 | 31 | # Root directory 32 | HEKIT_ROOT_DIR: Path = Path(__file__).resolve().parent.parent.parent 33 | 34 | # Docker directory 35 | HEKIT_DOCKER_DIR: Path = Path(__file__).resolve().parent.parent.parent / "docker" 36 | 37 | # hekit core commands 38 | HEKIT_COMMANDS = { 39 | "check-dependencies", 40 | "docker-build", 41 | "init", 42 | "install", 43 | "build", 44 | "fetch", 45 | "list", 46 | "new", 47 | "plugins", 48 | "remove", 49 | "algebras", 50 | "gen-primes", 51 | } 52 | 53 | 54 | @dataclass(frozen=True, init=False) 55 | class PluginState: 56 | """Define the possible state of a plugin""" 57 | 58 | ENABLE: str = "enabled" 59 | DISABLE: str = "disabled" 60 | 61 | 62 | @dataclass(frozen=True, init=False) 63 | class PluginsConfig: 64 | """Define the attributes of the config file for plugins""" 65 | 66 | ROOT_DIR: Path = Path("~/.hekit/plugins/").expanduser() 67 | FILE: Path = ROOT_DIR / "plugins.toml" 68 | KEY: str = "plugins" 69 | -------------------------------------------------------------------------------- /kit/utils/docker_tools.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """This module provides helper functions to set up a docker container""" 5 | 6 | import json 7 | from sys import stderr, exit as sys_exit 8 | from pathlib import Path 9 | from docker import from_env as docker_from_env # type: ignore[attr-defined] 10 | from docker.errors import DockerException # pylint: disable=unused-import 11 | 12 | 13 | class DockerBuildError(Exception): 14 | """Exception for something wrong with the docker build.""" 15 | 16 | def __init__(self, message, error): 17 | super().__init__(message) 18 | self.message = message 19 | self.error = error 20 | 21 | def __repr__(self): 22 | return f"DockerBuildError({self.message}, {self.error})" 23 | 24 | 25 | def check_build(func): 26 | """For use as a decorator. 27 | Forward the build info, but check for error in build. 28 | If found raise exception""" 29 | 30 | def inner(*args, **kwargs): 31 | responses = func(*args, **kwargs) 32 | for response in map(json.loads, responses): 33 | if "stream" in response.keys(): 34 | yield response["stream"] 35 | elif "aux" in response.keys() and "ID" in response["aux"]: 36 | yield response["aux"]["ID"] 37 | elif "error" in response.keys(): 38 | raise DockerBuildError("Docker build failed", response) 39 | else: 40 | raise DockerBuildError("Unrecognized stream property", response) 41 | 42 | return inner 43 | 44 | 45 | def simple_container_logs(func): 46 | """Simplifies a container output to give logs and when exahsted 47 | provide the status code on exit""" 48 | 49 | def inner(*args, **kwargs): 50 | container = func(*args, **kwargs) 51 | for log in container.logs(stream=True): 52 | yield log, None 53 | yield b"\n", container.wait()["StatusCode"] 54 | 55 | return inner 56 | 57 | 58 | class DockerTools: 59 | """Defines helper functions to set up a docker container""" 60 | 61 | def __init__(self): 62 | self.client = docker_from_env() 63 | 64 | def image_exists(self, image_name: str) -> bool: 65 | """Returns True if image already exists otherwise False""" 66 | images = self.client.images.list(name=image_name) 67 | return len(images) > 0 68 | 69 | @check_build 70 | def build_image(self, dockerfile: str, tag: str, buildargs): 71 | """Build images as we like them built""" 72 | return self.client.api.build( 73 | dockerfile=dockerfile, 74 | rm=True, # remove intermediates 75 | buildargs=buildargs, 76 | tag=tag, 77 | path=".", 78 | ) 79 | 80 | @simple_container_logs 81 | def run_script_in_container( 82 | self, environment, scriptpath, image="ubuntu:20.04" 83 | ) -> int: 84 | """Executes a script in the container""" 85 | scriptsrc = Path(scriptpath).expanduser().resolve() 86 | return self.client.containers.run( 87 | volumes=[f"{scriptsrc}:/script"], 88 | detach=True, 89 | stream=True, 90 | environment=environment, 91 | image=image, 92 | command="/bin/bash /script", 93 | ) 94 | 95 | def pull_base_image(self, tag: str): 96 | """Pull base image using `docker pull`""" 97 | self.client.images.pull(tag) 98 | 99 | def try_build_new_image(self, dockerfile: str, tag: str, buildargs): 100 | """Builds an image if it does not exist""" 101 | if not self.image_exists(tag): 102 | response = self.build_image(dockerfile, tag, buildargs) 103 | for out in response: 104 | print(out) 105 | 106 | def test_connection(self, environment, scriptpath): 107 | """Tests docker connectivity""" 108 | # proxy checks 109 | check_conn = self.run_script_in_container(environment, scriptpath) 110 | # refactor for better output 111 | status_code = 0 112 | for log, status_code in check_conn: 113 | print("[CONTAINER]", log.decode("utf-8"), end="") 114 | if status_code != 0: 115 | print( 116 | "In-docker connectivity failing.", 117 | f"Return code was '{status_code}'", 118 | file=stderr, 119 | ) 120 | sys_exit(1) 121 | -------------------------------------------------------------------------------- /kit/utils/files.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """This module provides functions to list files and directories.""" 5 | 6 | from os import walk 7 | from enum import Enum 8 | from pathlib import Path 9 | from typing import Any, Callable, Optional 10 | 11 | from toml import load, dump 12 | 13 | from kit.utils.typing import PathType 14 | 15 | TomlDict = dict[str, Any] 16 | 17 | 18 | class FileType(Enum): 19 | """Categories of files""" 20 | 21 | DIRS = 1 22 | FILES = 2 23 | 24 | 25 | def file_exists(file: Path) -> bool: 26 | """Wrapper to check if file exists because Path.exists() cannot be mocked 27 | directly due to being used internally by pytest creating some clash""" 28 | return file.exists() 29 | 30 | 31 | def files_in_dir( 32 | path: PathType, cond: Optional[Callable] = None, ftype: FileType = FileType.FILES 33 | ) -> list[str]: 34 | """Returns a list of filenames in the directory given by path. Can be filtered by cond""" 35 | try: 36 | filenames = next(walk(path))[ftype.value] 37 | if cond is None: 38 | return sorted(filenames) 39 | return sorted(filter(cond, filenames)) 40 | except StopIteration: 41 | return [] 42 | 43 | 44 | def list_dirs(path: PathType) -> list[str]: 45 | """Return list of directories in path.""" 46 | return files_in_dir(path, ftype=FileType.DIRS) 47 | 48 | 49 | def create_default_workspace(dir_path: str = "~") -> Path: 50 | """Create the directory ~/.hekit""" 51 | workspace_path = Path(dir_path).expanduser() / ".hekit" 52 | workspace_path.mkdir(exist_ok=True) 53 | return workspace_path 54 | 55 | 56 | def load_toml(file_name: PathType) -> TomlDict: 57 | """Load a toml file and return its content as a dict""" 58 | file_path = Path(file_name).expanduser() 59 | 60 | if not file_exists(file_path): 61 | raise FileNotFoundError(f"File '{file_name}' not found") 62 | 63 | # Note: Path.resolve() cannot be used before checking Path.is_symlink() 64 | if file_path.is_symlink(): 65 | raise TypeError(f"File {file_path.name} cannot be a symlink") 66 | 67 | return load(file_path) 68 | 69 | 70 | def dump_toml(file_name: PathType, content: TomlDict) -> None: 71 | """Write a TOML file""" 72 | file_path = Path(file_name).expanduser() 73 | 74 | with file_path.open("w", encoding="utf-8") as f: 75 | dump(content, f) 76 | 77 | 78 | def dash_to_underscore(name: str) -> str: 79 | """Return string with dashes changed to underscores.""" 80 | return name.replace("-", "_") 81 | -------------------------------------------------------------------------------- /kit/utils/primes.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """Utils for computing things related to primes""" 5 | 6 | from sys import stdout 7 | from subprocess import CalledProcessError, run, PIPE # nosec B404 8 | from typing import Generator, Iterable 9 | 10 | 11 | def parse_factor_line(line: str) -> tuple[int, tuple[int, ...]]: 12 | """'num: f1 f2 f3' -> (num, (f1, f2, f3))""" 13 | head, *tail = line.split() # ['key:', 'v1', 'v2' , ...] 14 | if head[-1] != ":": 15 | raise ValueError(f"{line} does not have valid key format") 16 | key = int(head[:-1]) # exclude last char 17 | value = tuple(map(int, tail)) 18 | return key, value 19 | 20 | 21 | def compute_prime_factors( 22 | numbers: Iterable[int], factor_util: str = "factor" 23 | ) -> Generator: 24 | """Return generator. Keys m, Value prime factors. 25 | Process out to factor""" 26 | 27 | numbers = list(numbers) 28 | if len(numbers) == 0: 29 | raise ValueError("Input numbers is empty") 30 | 31 | command_and_args = [factor_util, *map(str, numbers)] 32 | try: 33 | out = run(command_and_args, stdout=PIPE, check=True) # nosec B603 34 | except CalledProcessError as error: 35 | # Was it a negative number on the input? 36 | if any(number < 0 for number in numbers): 37 | raise ValueError( 38 | f"A negative number was found in the input: {numbers}" 39 | ) from error 40 | raise error 41 | 42 | factor_lines = out.stdout.decode("ascii").strip().split("\n") 43 | 44 | # We only want the value a.k.a. the primes factors 45 | # A generator here does not save memory, but makes the parsing lazy 46 | return (parse_factor_line(line)[1] for line in factor_lines) 47 | 48 | 49 | def write_primes(start: int, stop: int, outfile=stdout) -> None: 50 | """Writes to outfile a list of primes from start to stop values inclusive""" 51 | if start > stop: 52 | raise ValueError(f"start '{start}' should not be larger than stop '{stop}'") 53 | numbers = range(start, stop + 1) 54 | prime_factors = compute_prime_factors(numbers) 55 | if prime_factors is None: 56 | raise ValueError( 57 | f"No prime factors were found for between '{start}' and '{stop}', inclusively" 58 | ) 59 | primes = [factors[0] for factors in prime_factors if len(factors) == 1] 60 | print("\n".join(map(str, primes)), file=outfile) 61 | -------------------------------------------------------------------------------- /kit/utils/tab_completion.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """This module provides tab completion if the dependencies are installed""" 5 | 6 | from pathlib import Path 7 | 8 | from kit.utils.constants import PluginsConfig, PluginState 9 | from kit.utils.config import load_config, load_toml 10 | from kit.utils.files import list_dirs 11 | 12 | 13 | try: 14 | # Tab completion is an optional feature, this means that 15 | # hekit can be executed without enabling this functionality. 16 | # But if the user installs argcomplete and registers the 17 | # hekit script, tab completion will be available 18 | from argcomplete import autocomplete 19 | except ImportError as e: 20 | 21 | def autocomplete(arg): # type: ignore[misc] # pylint: disable=unused-argument 22 | """Continue with the execution""" 23 | 24 | 25 | def enable_tab_completion(parser): 26 | """Enables tab completion feature if dependencies are fulfilled""" 27 | autocomplete(parser) 28 | 29 | 30 | def components_completer( 31 | prefix, parsed_args, **kwargs # pylint: disable=unused-argument 32 | ) -> list[str]: 33 | """Returns the components that were installed with the hekit""" 34 | config = load_config(parsed_args.config) 35 | return list_dirs(config.repo_location) 36 | 37 | 38 | def instances_completer( 39 | prefix, parsed_args, **kwargs # pylint: disable=unused-argument 40 | ) -> list[str]: 41 | """Returns the instances of a component that 42 | was installed with the hekit""" 43 | config = load_config(parsed_args.config) 44 | comp_name_path = f"{config.repo_location}/{parsed_args.component}" 45 | return list_dirs(comp_name_path) 46 | 47 | 48 | def get_plugins_by_state( 49 | state: str, source_file: Path = PluginsConfig.FILE 50 | ) -> list[str]: 51 | """Return a list of plugins with a specific state""" 52 | try: 53 | plugin_dict = load_toml(source_file)[PluginsConfig.KEY] 54 | return [k for k, v in plugin_dict.items() if v["state"] == state] 55 | 56 | except (FileNotFoundError, KeyError): 57 | return [] 58 | 59 | 60 | def plugins_enable_completer( 61 | prefix, parsed_args, **kwargs # pylint: disable=unused-argument 62 | ) -> list[str]: 63 | """Return a list of plugins that are enabled""" 64 | return get_plugins_by_state(PluginState.ENABLE) 65 | 66 | 67 | def plugins_disable_completer( 68 | prefix, parsed_args, **kwargs # pylint: disable=unused-argument 69 | ) -> list[str]: 70 | """Return a list of plugins that are disabled""" 71 | return get_plugins_by_state(PluginState.DISABLE) 72 | -------------------------------------------------------------------------------- /kit/utils/tsort.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """Module providing a topological sort""" 5 | 6 | from typing import Set, Any 7 | 8 | 9 | class CycleError(Exception): 10 | """Error for indicating a cycle found in an DAG""" 11 | 12 | 13 | def tsort(G: dict): 14 | """Topological sort of graph G""" 15 | # Following Python 3.9 TopologicalSorter.static_order(); 16 | # The dict values are the nodes pointing to the keys. 17 | 18 | def dfs(node, path): 19 | """Recursive generator. Depth first search.""" 20 | if len(path) > 1 and path[0] == path[-1]: 21 | raise CycleError(f"cycle:{path}") 22 | if node in visited: 23 | return 24 | visited.add(node) 25 | try: 26 | for next_node in G[node]: 27 | yield from dfs(next_node, [*path, next_node]) 28 | except KeyError: 29 | pass 30 | yield node 31 | 32 | visited: Set[Any] = set() 33 | 34 | for node in G.keys(): 35 | yield from dfs(node, [node]) 36 | -------------------------------------------------------------------------------- /kit/utils/typing.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """Some useful type aliases""" 5 | from pathlib import Path 6 | 7 | PathType = str | Path 8 | ListStr = list[str] 9 | -------------------------------------------------------------------------------- /kit/utils/yes.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """Tests for yes and no""" 5 | 6 | 7 | def is_yes(test: str) -> bool: 8 | """Test for yes""" 9 | return test.lower() in ("y", "ye", "yes") 10 | 11 | 12 | def is_no(test: str) -> bool: 13 | """Test for no""" 14 | return test.lower() in ("n", "no") 15 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | exclude = tests 3 | -------------------------------------------------------------------------------- /notes/.gitignore: -------------------------------------------------------------------------------- 1 | *.aux 2 | *.bbl 3 | *.dvi 4 | *.fls 5 | *.fdb_latexmk 6 | *.log 7 | *.nav 8 | *.pdf 9 | *.snm 10 | *.toc 11 | *.vrb 12 | _minted-* 13 | -------------------------------------------------------------------------------- /notes/README.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 3 | These are notes about the mathematical background of Homomoprhic Encryption. 4 | They include LATEX notes on, 5 | - "Number Theory" 6 | - "Groups" 7 | - "Rings" 8 | - "Fields" 9 | - "Cyclotomics" 10 | - "Galois Theory" 11 | - "Advanced Topics" 12 | They also include presentations about some of these topics. 13 | 14 | To build the latex files we recommend using `latexmk` program. 15 | Note, not to be confused with `latex-mk`. 16 | 17 | ```bash 18 | latexmk -pdf -shell-escape 19 | ``` 20 | -------------------------------------------------------------------------------- /notes/images/Gauss.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/notes/images/Gauss.pdf -------------------------------------------------------------------------------- /notes/images/HomomorphicData.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/notes/images/HomomorphicData.pdf -------------------------------------------------------------------------------- /notes/images/LatticeWithBasis.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/notes/images/LatticeWithBasis.pdf -------------------------------------------------------------------------------- /notes/images/Leonhard-Euler.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/notes/images/Leonhard-Euler.pdf -------------------------------------------------------------------------------- /notes/images/StandardData.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/notes/images/StandardData.pdf -------------------------------------------------------------------------------- /notes/images/clock.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/notes/images/clock.pdf -------------------------------------------------------------------------------- /notes/images/erat.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/notes/images/erat.pdf -------------------------------------------------------------------------------- /notes/images/fermat.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/notes/images/fermat.pdf -------------------------------------------------------------------------------- /notes/images/png2pdf.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/notes/images/png2pdf.pdf -------------------------------------------------------------------------------- /notes/images/poincare_henri1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/notes/images/poincare_henri1.pdf -------------------------------------------------------------------------------- /recipes/config-psi.toml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | [[hexl]] 5 | version = "1.2.5" 6 | name = "1.2.5" 7 | export_install_dir = "install" 8 | fetch = "git clone https://github.com/intel/hexl.git --branch v%version%" 9 | pre-build = """cmake -S %init_fetch_dir%/hexl -B %init_build_dir% 10 | -DCMAKE_INSTALL_PREFIX=%export_install_dir%""" 11 | build = "cmake --build %init_build_dir% -j" 12 | install = "cmake --install %init_install_dir%" 13 | export_cmake = "install/lib/cmake/hexl-%version%" 14 | 15 | 16 | [[ntl]] 17 | version = "11.5.1" 18 | name = "11.5.1" 19 | init_pre_build_dir = "build" 20 | init_build_dir = "build/ntl-%version%/src" 21 | init_install_dir = "build/ntl-%version%/src" 22 | export_install_dir = "install" 23 | fetch = "wget https://libntl.org/ntl-%version%.tar.gz" 24 | post-fetch = "bash -c 'tar -xvf %init_fetch_dir%/ntl-%version%.tar.gz -C %init_pre_build_dir%'" 25 | build = "bash -c './configure NTL_GMP_LIP=on NTL_THREADS=on NTL_THREAD_BOOST=on TUNE=generic PREFIX=%export_install_dir% && make -j'" 26 | install = "make install" 27 | 28 | 29 | [[helib]] 30 | version = "master" 31 | name = "psi-io" 32 | root = "%name%" 33 | export_install_dir = "install" 34 | fetch = "git clone https://github.com/helibproject/HElib.git --branch %version%" 35 | pre-build = """cmake -S %init_fetch_dir%/HElib -B %init_build_dir% 36 | -DCMAKE_INSTALL_PREFIX=%export_install_dir% 37 | -DNTL_DIR=$%ntl%/export_install_dir$ 38 | -DUSE_INTEL_HEXL=ON 39 | -DHEXL_DIR=$%hexl%/export_cmake$""" 40 | build = "cmake --build %init_build_dir% -j" 41 | install = "cmake --install %init_install_dir%" 42 | export_cmake = "install/share/cmake/helib" 43 | # Build the utils directory 44 | post-install = """bash -c 'cmake -S %init_fetch_dir%/HElib/utils -B %init_fetch_dir%/HElib/utils/build \ 45 | -Dhelib_DIR=%export_cmake% \ 46 | && cmake --build %init_fetch_dir%/HElib/utils/build -j'""" 47 | # Dependencies 48 | ntl = "ntl/11.5.1" 49 | hexl = "hexl/1.2.5" 50 | 51 | 52 | [[gtest]] 53 | name = "v1.12.1" 54 | version = "1.12.1" 55 | export_install_dir = "install" 56 | fetch = "git clone https://github.com/google/googletest.git --branch release-%version%" 57 | pre-build = """cmake -S %init_fetch_dir%/googletest -B %init_build_dir% 58 | -DCMAKE_INSTALL_PREFIX=%export_install_dir% 59 | -DBUILD_MOCK=OFF""" 60 | build = "cmake --build %init_build_dir% -j" 61 | install = "cmake --install %init_build_dir%" 62 | export_cmake = "install/lib/cmake/GTest" 63 | 64 | 65 | [[psi]] 66 | name = "configurable-psi" 67 | src_dir = "!toolkit-path!/deployments/config_psi/psi" 68 | pre-build = """cmake -S %src_dir% -B %init_build_dir% 69 | -Dhelib_DIR=$%helib%/export_cmake$ 70 | -DENABLE_TESTS=ON 71 | -DGTest_DIR=$%gtest%/export_cmake$""" 72 | build = "cmake --build %init_build_dir% -j" 73 | # Dependencies 74 | helib = "helib/psi-io" 75 | gtest = "gtest/v1.12.1" 76 | -------------------------------------------------------------------------------- /recipes/examples.toml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | [[examples]] 5 | name = "logistic-regression" 6 | src_dir = "!toolkit-path!/he-samples/examples/logistic-regression" 7 | pre-build = """cmake -S %src_dir% -B %init_build_dir% 8 | -DSEAL_DIR=$%seal%/export_cmake$ 9 | -DMicrosoft.GSL_DIR=$%GSL%/export_cmake$ 10 | -Dzstd_DIR=$%zstd%/export_cmake$ 11 | -DHEXL_DIR=$%hexl%/export_cmake$""" 12 | build = "cmake --build %init_build_dir% -j" 13 | # Dependencies 14 | seal = "seal/v3.7.2" 15 | hexl = "hexl/1.2.3" 16 | GSL = "gsl/v3.1.0" 17 | zstd = "zstd/v1.4.5" 18 | 19 | 20 | [[examples]] 21 | name = "secure-query" 22 | src_dir = "!toolkit-path!/he-samples/examples/secure-query" 23 | pre-build = """cmake -S %src_dir% -B %init_build_dir% 24 | -DSEAL_DIR=$%seal%/export_cmake$ 25 | -DMicrosoft.GSL_DIR=$%GSL%/export_cmake$ 26 | -Dzstd_DIR=$%zstd%/export_cmake$ 27 | -DHEXL_DIR=$%hexl%/export_cmake$""" 28 | build = "cmake --build %init_build_dir% -j" 29 | # Dependencies 30 | seal = "seal/v3.7.2" 31 | hexl = "hexl/1.2.3" 32 | GSL = "gsl/v3.1.0" 33 | zstd = "zstd/v1.4.5" 34 | 35 | 36 | [[examples]] 37 | name = "psi" 38 | src_dir = "!toolkit-path!/he-samples/examples/psi" 39 | pre-build = """cmake -S %src_dir% -B %init_build_dir% 40 | -Dhelib_DIR=$%helib%/export_cmake$""" 41 | build = "cmake --build %init_build_dir% -j" 42 | # Dependencies 43 | helib = "helib/v2.2.1" 44 | -------------------------------------------------------------------------------- /recipes/sample-kernels.toml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | [[sample-kernels]] 5 | name = "seal" 6 | src_dir = "!toolkit-path!/he-samples/sample-kernels" 7 | pre-build = """cmake -S %src_dir% -B %init_build_dir% 8 | -DENABLE_SEAL=ON 9 | -DSEAL_DIR=$%seal%/export_cmake$ 10 | -DMicrosoft.GSL_DIR=$%GSL%/export_cmake$ 11 | -Dzstd_DIR=$%zstd%/export_cmake$ 12 | -DHEXL_DIR=$%hexl%/export_cmake$""" 13 | build = "cmake --build %init_build_dir% -j" 14 | # Dependencies 15 | seal = "seal/v3.7.2" 16 | hexl = "hexl/1.2.3" 17 | GSL = "gsl/v3.1.0" 18 | zstd = "zstd/v1.4.5" 19 | 20 | 21 | [[sample-kernels]] 22 | name = "palisade" 23 | src_dir = "!toolkit-path!/he-samples/sample-kernels" 24 | pre-build = """cmake -S %src_dir% -B %init_build_dir% 25 | -DENABLE_PALISADE=ON 26 | -DPalisade_DIR=$%palisade%/export_cmake$ 27 | -DHEXL_DIR=$%hexl%/export_cmake$""" 28 | build = "cmake --build %init_build_dir% -j" 29 | # Dependencies 30 | palisade = "palisade/v1.11.6" 31 | hexl = "hexl/1.2.3" 32 | 33 | 34 | # Above two recipes combined into one instance (skipped) 35 | [[sample-kernels]] 36 | skip = true 37 | name = "all" 38 | src_dir = "!toolkit-path!/he-samples/sample-kernels" 39 | pre-build = """cmake -S %src_dir% -B %init_build_dir% 40 | -DENABLE_SEAL=ON 41 | -DENABLE_PALISADE=ON 42 | -DSEAL_DIR=$%seal%/export_cmake$ 43 | -DMicrosoft.GSL_DIR=$%GSL%/export_cmake$ 44 | -Dzstd_DIR=$%zstd%/export_cmake$ 45 | -DHEXL_DIR=$%hexl%/export_cmake$ 46 | -DPalisade_DIR=$%palisade%/export_cmake$""" 47 | build = "cmake --build %init_build_dir% -j" 48 | # Dependencies 49 | seal = "seal/v3.7.2" 50 | hexl = "hexl/1.2.3" 51 | GSL = "gsl/v3.1.0" 52 | zstd = "zstd/v1.4.5" 53 | palisade = "palisade/v1.11.6" 54 | -------------------------------------------------------------------------------- /recipes/test.toml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | [[hexl]] 5 | skip = false 6 | version = "1.2.1" 7 | name = "1.2.1" 8 | fetch = "git clone https://github.com/intel/hexl.git --branch %version%" 9 | init_fetch_dir = "fetch" 10 | init_build_dir = "build" 11 | init_install_dir = "build" 12 | pre-build = """cmake -S %init_fetch_dir%/hexl -B %init_build_dir% 13 | -DCMAKE_INSTALL_PREFIX=%export_install_dir%""" 14 | build = "cmake --build %init_build_dir% -j" 15 | install = "cmake --install %init_install_dir%" 16 | export_install_dir = "install" 17 | export_cmake = "install/lib/cmake/hexl-%version%" 18 | 19 | 20 | [[hexl]] 21 | skip = false 22 | version = "1.2.2" 23 | name = "1.2.2" 24 | fetch = "git clone https://github.com/intel/hexl.git --branch %version%" 25 | init_fetch_dir = "fetch" 26 | init_build_dir = "build" 27 | init_install_dir = "build" 28 | pre-build = """cmake -S %init_fetch_dir%/hexl -B %init_build_dir% 29 | -DCMAKE_INSTALL_PREFIX=%export_install_dir%""" 30 | build = "cmake --build %init_build_dir% -j" 31 | install = "cmake --install %init_install_dir%" 32 | export_install_dir = "install" 33 | export_cmake = "install/lib/cmake/hexl-%version%" 34 | 35 | 36 | [[ntl]] 37 | skip = false 38 | version = "11.5.1" 39 | name = "11.5.1" 40 | init_fetch_dir = "fetch" 41 | init_pre_build_dir = "build" 42 | init_build_dir = "build/ntl-%version%/src" 43 | init_install_dir = "build/ntl-%version%/src" 44 | export_install_dir = "install" 45 | fetch = "wget https://libntl.org/ntl-%version%.tar.gz" 46 | post-fetch = "bash -c 'tar -xvf %init_fetch_dir%/ntl-%version%.tar.gz -C %init_pre_build_dir%'" 47 | build = "bash -c './configure PREFIX=%export_install_dir% && make -j'" 48 | install = "make install" 49 | 50 | 51 | [[helib]] 52 | skip = false 53 | version = "2.2.0" 54 | name = "v2.2.0" 55 | root = "%name%" 56 | init_fetch_dir = "fetch" 57 | init_build_dir = "build" 58 | init_install_dir = "build" 59 | export_install_dir = "install" 60 | fetch = "git clone https://github.com/homenc/HElib.git --branch %name%" 61 | pre-build = """cmake -S %init_fetch_dir%/HElib -B %init_build_dir% \ 62 | -DCMAKE_INSTALL_PREFIX=%export_install_dir% \ 63 | -DUSE_INTEL_HEXL=ON \ 64 | -DHEXL_DIR=$%hexl%/export_cmake$""" 65 | build = "cmake --build %init_build_dir% -j" 66 | post-build = "" 67 | install = "cmake --install %init_install_dir%" 68 | # Dependencies 69 | hexl = "hexl/1.2.1" 70 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | # Recommended requirements 3 | toml 4 | argcomplete 5 | docker 6 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/tests/__init__.py -------------------------------------------------------------------------------- /tests/common_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """Utilities used by the tests""" 5 | 6 | import pytest 7 | from pathlib import Path 8 | from subprocess import run 9 | from shlex import split 10 | 11 | 12 | def create_config_file(path: Path) -> Path: 13 | config_file = f"{path}/default.config" 14 | 15 | with open(f"{config_file}", "w") as f: 16 | f.write(f'repo_location = "{path}"\n') 17 | 18 | return config_file 19 | 20 | 21 | def execute_process(cmd: str) -> str: 22 | return run(split(cmd), encoding="utf-8", capture_output=True) 23 | 24 | 25 | @pytest.fixture 26 | def input_files_path() -> Path: 27 | return Path(__file__).resolve().parent / "input_files" 28 | 29 | 30 | @pytest.fixture 31 | def hekit_path() -> Path: 32 | return Path(__file__).resolve().parent.parent / "hekit" 33 | -------------------------------------------------------------------------------- /tests/input_files/default.config: -------------------------------------------------------------------------------- 1 | repo_location = "~/.hekit_test/components" 2 | -------------------------------------------------------------------------------- /tests/input_files/default_symlink.config: -------------------------------------------------------------------------------- 1 | default.config -------------------------------------------------------------------------------- /tests/input_files/default_wrong.config: -------------------------------------------------------------------------------- 1 | repo_location 2 | -------------------------------------------------------------------------------- /tests/input_files/depend_wrong.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # Dependencies test file 5 | programA + 3.8 6 | -------------------------------------------------------------------------------- /tests/input_files/dependencies.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # Dependencies test file 5 | programA >= 3.8 6 | #comment 7 | programB 8 | #comment 9 | programC == 0.1 10 | -------------------------------------------------------------------------------- /tests/input_files/test.toml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | [[hexl]] 5 | skip = false 6 | version = "1.2.3" 7 | name = "1.2.3" 8 | fetch = "ls" 9 | build = "ls" 10 | install = "ls" 11 | -------------------------------------------------------------------------------- /tests/input_files/test_missing_quotes.toml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | [[hexl]] 5 | skip = false 6 | version = "!version!" 7 | name = "!name!" 8 | fetch = "git clone https://github.com/intel/hexl.git --branch %version%" 9 | init_fetch_dir = "fetch 10 | init_build_dir = "!build!" 11 | init_install_dir = "!build!" 12 | pre-build = "ls" 13 | build = "ls" 14 | install = "ls" 15 | export_install_dir = "install" 16 | export_cmake = "install/lib/cmake/hexl-%version%" 17 | -------------------------------------------------------------------------------- /tests/input_files/test_missing_value.toml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | [[hexl]] 5 | skip = false 6 | version = "!version!" 7 | name = 8 | fetch = "git clone https://github.com/intel/hexl.git --branch %version%" 9 | init_fetch_dir = "fetch" 10 | init_build_dir = "!build!" 11 | init_install_dir = "!build!" 12 | pre-build = "ls" 13 | build = "ls" 14 | install = "ls" 15 | export_install_dir = "install" 16 | export_cmake = "install/lib/cmake/hexl-%version%" 17 | -------------------------------------------------------------------------------- /tests/input_files/test_symlink.toml: -------------------------------------------------------------------------------- 1 | test.toml -------------------------------------------------------------------------------- /tests/input_files/test_two_instances.toml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | [[hexl]] 5 | #skip = false 6 | version = "1.2.3" 7 | name = "1.2.3" 8 | fetch = "git clone https://github.com/intel/hexl.git --branch %version%" 9 | init_fetch_dir = "fetch" 10 | init_build_dir = "build" 11 | init_install_dir = "build" 12 | pre-build = """cmake -S %init_fetch_dir%/hexl -B %init_build_dir% 13 | -DCMAKE_INSTALL_PREFIX=%export_install_dir%""" 14 | build = "cmake --build %init_build_dir% -j" 15 | install = "cmake --install %init_install_dir%" 16 | export_install_dir = "install" 17 | export_cmake = "install/lib/cmake/hexl-%version%" 18 | 19 | [[hexl]] 20 | skip = false 21 | version = "1.2.2" 22 | name = "1.2.2" 23 | fetch = "git clone https://github.com/intel/hexl.git --branch %version%" 24 | init_fetch_dir = "fetch" 25 | init_build_dir = "build" 26 | init_install_dir = "build" 27 | pre-build = "ls" 28 | build = "ls" 29 | install = "ls" 30 | export_install_dir = "install" 31 | export_cmake = "install/lib/cmake/hexl-%version%" 32 | -------------------------------------------------------------------------------- /tests/input_files/test_wrong_format.toml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | [[hexl]] 5 | skip = false 6 | version = 233.3 7 | name = "hexl" 8 | fetch = "git clone https://github.com/intel/hexl.git --branch %version%" 9 | pre-build = "ls" 10 | build = "ls" 11 | install = "ls" 12 | export_install_dir = "install" 13 | export_cmake = "install/lib/cmake/hexl-%version%" 14 | -------------------------------------------------------------------------------- /tests/test_integration_check_deps.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import pytest 5 | from pathlib import Path 6 | 7 | from kit.hekit import main 8 | from kit.commands.check_deps import check_dependencies 9 | 10 | 11 | def test_check_dependencies_not_found(mocker): 12 | args = MockArgs() 13 | mockers = Mockers(mocker) 14 | mockers.mock_parse_cmdline.return_value = args, "" 15 | mockers.mock_which.side_effect = [False, False, False] 16 | 17 | main() 18 | mockers.mock_print.assert_any_call( 19 | "'programA' was not found, a minimum 'programA 3.8' is required" 20 | ) 21 | mockers.mock_print.assert_any_call("'programB' was not found") 22 | mockers.mock_print.assert_any_call( 23 | "'programC' was not found, an exact 'programC 0.1' is required" 24 | ) 25 | 26 | 27 | def test_check_dependencies_found(mocker): 28 | args = MockArgs() 29 | mockers = Mockers(mocker) 30 | mockers.mock_parse_cmdline.return_value = args, "" 31 | mockers.mock_which.side_effect = [True, True, True] 32 | subp_1 = MockSubprocess("programA", 3.8) 33 | subp_2 = MockSubprocess("programC", 0.1) 34 | mockers.mock_run.side_effect = [subp_1, subp_2] 35 | 36 | main() 37 | mockers.mock_print.assert_any_call(f"'{subp_1.prog} {subp_1.vers}' found") 38 | mockers.mock_print.assert_any_call("'programB' found") 39 | mockers.mock_print.assert_any_call(f"'{subp_2.prog} {subp_2.vers}' found") 40 | 41 | 42 | def test_check_dependencies_found_wrong_version(mocker): 43 | args = MockArgs() 44 | mockers = Mockers(mocker) 45 | mockers.mock_parse_cmdline.return_value = args, "" 46 | mockers.mock_which.side_effect = [True, False, True] 47 | subp_1 = MockSubprocess("programA", 3.5) 48 | subp_2 = MockSubprocess("programC", 0.5) 49 | mockers.mock_run.side_effect = [subp_1, subp_2] 50 | 51 | main() 52 | mockers.mock_print.assert_any_call( 53 | f"'{subp_1.prog} {subp_1.vers}' found, but minimum version '3.8' is required" 54 | ) 55 | mockers.mock_print.assert_any_call("'programB' was not found") 56 | mockers.mock_print.assert_any_call( 57 | f"'{subp_2.prog} {subp_2.vers}' found, but exact version '0.1' is required" 58 | ) 59 | 60 | 61 | def test_check_dependencies_FileNotFoundError(mocker): 62 | args = MockArgs() 63 | args.dependencies_file = "/tests/input_files/no_a_file.txt" 64 | mockers = Mockers(mocker) 65 | mockers.mock_parse_cmdline.return_value = args, "" 66 | 67 | with pytest.raises(SystemExit) as exc_info: 68 | main() 69 | assert exc_info.value.code == 1 70 | 71 | 72 | """Utilities used by the tests""" 73 | 74 | 75 | class Mockers: 76 | def __init__(self, mocker): 77 | self.mock_parse_cmdline = mocker.patch("kit.hekit.parse_cmdline") 78 | self.mock_print = mocker.patch("kit.commands.check_deps.print") 79 | self.mock_which = mocker.patch("kit.commands.check_deps.which") 80 | self.mock_run = mocker.patch("kit.commands.check_deps.subprocess_run") 81 | 82 | 83 | class MockArgs: 84 | def __init__(self): 85 | self.tests_path = Path(__file__).resolve().parent 86 | self.dependencies_file = f"{self.tests_path}/input_files/dependencies.txt" 87 | self.config = f"{self.tests_path}/input_files/default.config" 88 | self.fn = check_dependencies 89 | self.version = False 90 | self.debug = False 91 | 92 | 93 | class MockSubprocess: 94 | def __init__(self, program, version): 95 | self.prog = program 96 | self.vers = version 97 | self.returncode = 0 98 | self.message = ( 99 | f"{program} (OS) {version}\nCopyright (C) 2022 \nThis is free software" 100 | ) 101 | self.stdout = self.message.encode("ascii") 102 | -------------------------------------------------------------------------------- /tests/test_integration_gen_primes.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import pytest 5 | import sys 6 | from tests.common_utils import execute_process, hekit_path 7 | 8 | 9 | def test_gen_primes_start_less_than_stop(hekit_path): 10 | """Verify that gen-primes cmd is executed correctly when 11 | start is less than stop""" 12 | cmd = f"{hekit_path} gen-primes 0 10" 13 | act_result = execute_process(cmd) 14 | assert "2\n3\n5\n7\n" in act_result.stdout 15 | assert not act_result.stderr 16 | assert 0 == act_result.returncode 17 | 18 | 19 | def test_gen_primes_start_equal_to_stop(hekit_path): 20 | """Verify that gen-primes cmd is executed correctly when 21 | start is equal to stop""" 22 | cmd = f"{hekit_path} gen-primes 10 10" 23 | act_result = execute_process(cmd) 24 | assert "\n" in act_result.stdout 25 | assert not act_result.stderr 26 | assert 0 == act_result.returncode 27 | 28 | 29 | def test_gen_primes_start_greater_than_stop(hekit_path): 30 | """Verify that gen-primes cmd triggers an error when 31 | start is greater than stop""" 32 | cmd = f"{hekit_path} gen-primes 100 10" 33 | act_result = execute_process(cmd) 34 | assert "Error while running subcommand" in act_result.stderr 35 | assert ( 36 | "ValueError(\"start '100' should not be larger than stop '10'\")" 37 | in act_result.stderr 38 | ) 39 | assert 0 != act_result.returncode 40 | 41 | 42 | def test_gen_primes_negative_start(hekit_path): 43 | """Verify that gen-primes cmd triggers an error when 44 | start is negative""" 45 | cmd = f"{hekit_path} gen-primes -1 10" 46 | act_result = execute_process(cmd) 47 | assert "Error while running subcommand" in act_result.stderr 48 | assert ( 49 | "ValueError('A negative number was found in the input: [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]')" 50 | in act_result.stderr 51 | ) 52 | assert 0 != act_result.returncode 53 | 54 | 55 | def test_gen_primes_negative_stop(hekit_path): 56 | """Verify that gen-primes cmd triggers an error when 57 | start and stop are negative""" 58 | cmd = f"{hekit_path} gen-primes -5 -1" 59 | act_result = execute_process(cmd) 60 | assert "Error while running subcommand" in act_result.stderr 61 | assert ( 62 | "ValueError('A negative number was found in the input: [-5, -4, -3, -2, -1]')" 63 | in act_result.stderr 64 | ) 65 | assert 0 != act_result.returncode 66 | 67 | 68 | def test_gen_primes_max_stop(hekit_path): 69 | """Verify that gen-primes cmd triggers an error when 70 | stop is equal to sys.maxsize""" 71 | cmd = f"{hekit_path} gen-primes -5 {sys.maxsize}" 72 | act_result = execute_process(cmd) 73 | assert "Error while running subcommand" in act_result.stderr 74 | assert ( 75 | "OverflowError('Python int too large to convert to C ssize_t')" 76 | in act_result.stderr 77 | ) 78 | assert 0 != act_result.returncode 79 | -------------------------------------------------------------------------------- /tests/test_integration_install.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import pytest 5 | from tests.common_utils import ( 6 | create_config_file, 7 | execute_process, 8 | hekit_path, 9 | input_files_path, 10 | ) 11 | 12 | 13 | def test_cmds_install_list_remove(tmp_path, hekit_path, input_files_path): 14 | """Verify that fetch, build, install, list and remove commands 15 | are executed without failures""" 16 | component, instance = "hexl", "1.2.3" 17 | config_file = create_config_file(tmp_path) 18 | 19 | # Test fetch command 20 | cmd = f"{hekit_path} --config {config_file} fetch {input_files_path}/test.toml" 21 | act_result = execute_process(cmd) 22 | assert f"fetch" in act_result.stdout 23 | assert not act_result.stderr 24 | assert 0 == act_result.returncode 25 | 26 | # Test list command after fetch 27 | cmd = f"{hekit_path} --config {config_file} list" 28 | act_result = execute_process(cmd) 29 | assert ( 30 | f"{component} {instance} success " 31 | in act_result.stdout 32 | ) 33 | assert not act_result.stderr 34 | assert 0 == act_result.returncode 35 | 36 | # Test build command 37 | cmd = f"{hekit_path} --config {config_file} build {input_files_path}/test.toml" 38 | act_result = execute_process(cmd) 39 | assert "build" in act_result.stdout 40 | assert not act_result.stderr 41 | assert 0 == act_result.returncode 42 | 43 | # Test list command after build 44 | cmd = f"{hekit_path} --config {config_file} list" 45 | act_result = execute_process(cmd) 46 | assert ( 47 | f"{component} {instance} success success " 48 | in act_result.stdout 49 | ) 50 | assert not act_result.stderr 51 | assert 0 == act_result.returncode 52 | 53 | # Test install command 54 | cmd = f"{hekit_path} --config {config_file} install {input_files_path}/test.toml" 55 | act_result = execute_process(cmd) 56 | assert "install" in act_result.stdout 57 | assert not act_result.stderr 58 | assert 0 == act_result.returncode 59 | 60 | # Test list command after install 61 | cmd = f"{hekit_path} --config {config_file} list" 62 | act_result = execute_process(cmd) 63 | assert ( 64 | f"{component} {instance} success success success " 65 | in act_result.stdout 66 | ) 67 | assert not act_result.stderr 68 | assert 0 == act_result.returncode 69 | 70 | # Test remove command 71 | cmd = f"{hekit_path} --config {config_file} remove {component} {instance}" 72 | act_result = execute_process(cmd) 73 | assert ( 74 | f"{component} {instance} success success success " 75 | not in act_result.stdout 76 | ) 77 | assert not act_result.stderr 78 | assert 0 == act_result.returncode 79 | -------------------------------------------------------------------------------- /tests/test_util_config.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import pytest 5 | from pathlib import Path 6 | from kit.utils.config import Config, config_required, load_config 7 | 8 | 9 | def test_config_required_without_error(args): 10 | @config_required 11 | def to_be_decorated(args): 12 | assert "/tests/input_files/default.config" in args.config.config_filename 13 | assert "/.hekit_test/components" in args.config.repo_location 14 | 15 | to_be_decorated(args) 16 | 17 | 18 | def test_config_required_file_error(args): 19 | args.config = "" 20 | 21 | @config_required 22 | def to_be_decorated(args): 23 | pass 24 | 25 | with pytest.raises(Exception) as exc_info: 26 | to_be_decorated(args) 27 | 28 | assert "Error while parsing config file" in str(exc_info.value) 29 | 30 | 31 | def test_config_required_value_error(args): 32 | args.config = f"{args.tests_path}/input_files/default_wrong.config" 33 | 34 | @config_required 35 | def to_be_decorated(args): 36 | pass 37 | 38 | with pytest.raises(Exception) as exc_info: 39 | to_be_decorated(args) 40 | 41 | assert "Error while parsing config file" in str(exc_info.value) 42 | 43 | 44 | """Utilities used by the tests""" 45 | 46 | 47 | class MockArgs: 48 | def __init__(self): 49 | self.tests_path = Path(__file__).resolve().parent 50 | self.config = f"{self.tests_path}/input_files/default.config" 51 | 52 | 53 | @pytest.fixture 54 | def args(): 55 | return MockArgs() 56 | -------------------------------------------------------------------------------- /tests/test_util_files.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import pytest 5 | 6 | from pathlib import Path 7 | from kit.utils.files import ( 8 | files_in_dir, 9 | list_dirs, 10 | create_default_workspace, 11 | load_toml, 12 | dump_toml, 13 | ) 14 | 15 | 16 | def test_files_in_dir_commands_filter_py(get_toolkit_path): 17 | exp_files = { 18 | "docker_build.py", 19 | "check_deps.py", 20 | "install.py", 21 | "init.py", 22 | "remove.py", 23 | "new.py", 24 | "list_cmd.py", 25 | "plugin.py", 26 | } 27 | module = get_toolkit_path / "kit" / "commands" 28 | filter = lambda f: f[0] != "_" and f.endswith(".py") 29 | 30 | filenames = files_in_dir(module, filter) 31 | assert exp_files == set(filenames) 32 | 33 | 34 | def test_files_in_dir_commands_filter_None(get_toolkit_path): 35 | exp_files = { 36 | "docker_build.py", 37 | "__init__.py", 38 | "check_deps.py", 39 | "install.py", 40 | "init.py", 41 | "remove.py", 42 | "new.py", 43 | "list_cmd.py", 44 | "plugin.py", 45 | } 46 | module = get_toolkit_path / "kit" / "commands" 47 | filter = None 48 | 49 | filenames = files_in_dir(module, filter) 50 | assert exp_files == set(filenames) 51 | 52 | 53 | def test_files_in_dir_tools_filter_py(get_toolkit_path): 54 | exp_files = {"algebras.py", "gen_primes.py"} 55 | module = get_toolkit_path / "kit" / "tools" 56 | filter = lambda f: f[0] != "_" and f.endswith(".py") 57 | 58 | filenames = files_in_dir(module, filter) 59 | assert exp_files == set(filenames) 60 | 61 | 62 | def test_list_dirs(get_toolkit_path): 63 | exp_files = ["utils", "tools", "commands"] 64 | module = get_toolkit_path / "kit" 65 | 66 | directories = list_dirs(module) 67 | assert exp_files[0] in set(directories) 68 | assert exp_files[1] in set(directories) 69 | assert exp_files[2] in set(directories) 70 | 71 | 72 | def test_create_default_config_dir_created(tmp_path): 73 | dir_path = create_default_workspace(tmp_path) 74 | assert dir_path.exists() 75 | 76 | 77 | def test_load_toml(get_toolkit_path): 78 | file = get_toolkit_path / "tests/input_files/default.config" 79 | act_dict = load_toml(file) 80 | assert "~/.hekit_test/components" == act_dict["repo_location"] 81 | 82 | 83 | def test_load_toml_symlink(get_toolkit_path): 84 | file = get_toolkit_path / "tests/input_files/default_symlink.config" 85 | with pytest.raises(Exception) as exc_info: 86 | load_toml(file) 87 | 88 | assert "default_symlink.config cannot be a symlink" in str(exc_info.value) 89 | 90 | 91 | def test_dump_toml(tmp_path): 92 | file_name = tmp_path / "test.toml" 93 | file_data = {"test": {"a": "b", "c": "d", "e": "g"}} 94 | 95 | dump_toml(file_name, file_data) 96 | assert file_name.exists() 97 | assert file_data == load_toml(file_name) 98 | 99 | 100 | @pytest.fixture 101 | def get_toolkit_path(): 102 | return Path(__file__).resolve().parent.parent 103 | -------------------------------------------------------------------------------- /tests/test_util_primes.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from kit.utils.primes import * 3 | 4 | 5 | def test_parse_factor_line(): 6 | # parse_factor_line doesn't check for correctness 7 | assert parse_factor_line("6: 2 3") == (6, (2, 3)) 8 | assert parse_factor_line("6: \t2 \t3") == (6, (2, 3)) 9 | assert parse_factor_line("6: 3 2") == (6, (3, 2)) # order matters 10 | assert parse_factor_line("36: 2 2 3 3") == (36, (2, 2, 3, 3)) 11 | 12 | with pytest.raises(ValueError): 13 | parse_factor_line("6 3 2") 14 | parse_factor_line("6.1: 3 2") 15 | -------------------------------------------------------------------------------- /tests/test_util_tab_comp.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import pytest 5 | from toml import dump 6 | from pathlib import Path 7 | from kit.utils.constants import PluginState, PluginsConfig 8 | from kit.utils.tab_completion import ( 9 | get_plugins_by_state, 10 | components_completer, 11 | instances_completer, 12 | ) 13 | 14 | 15 | def test_get_instances_two_instances(mocker, tmp_path): 16 | """Verify that the SW returns the lists of a component with two instances""" 17 | args = Mockargs() 18 | exp_comp = "hexl" 19 | exp_inst = ["1.0.1", "1.2.3"] 20 | mock_load_config = mocker.patch("kit.utils.tab_completion.load_config") 21 | mock_load_config.return_value = Config(tmp_path) 22 | create_plugin_file(tmp_path, exp_comp, exp_inst) 23 | 24 | act_comp = components_completer("", args) 25 | act_inst = instances_completer("", args) 26 | assert act_comp == [exp_comp] 27 | assert act_inst == exp_inst 28 | 29 | 30 | def test_get_instances_one_instance(mocker, tmp_path): 31 | """Verify that the SW returns the lists of a component with one instance""" 32 | args = Mockargs() 33 | exp_comp = "hexl" 34 | exp_inst = ["1.2.3"] 35 | mock_load_config = mocker.patch("kit.utils.tab_completion.load_config") 36 | mock_load_config.return_value = Config(tmp_path) 37 | create_plugin_file(tmp_path, exp_comp, exp_inst) 38 | 39 | act_comp = components_completer("", args) 40 | act_inst = instances_completer("", args) 41 | assert act_comp == [exp_comp] 42 | assert act_inst == exp_inst 43 | 44 | 45 | def test_get_instances_empty(mocker, tmp_path): 46 | """Verify that the SW returns an empty lists when there is no components""" 47 | args = Mockargs() 48 | mock_load_config = mocker.patch("kit.utils.tab_completion.load_config") 49 | mock_load_config.return_value = Config(tmp_path) 50 | 51 | act_comp = components_completer("", args) 52 | act_inst = instances_completer("", args) 53 | assert act_comp == [] 54 | assert act_inst == [] 55 | 56 | 57 | def test_get_plugins_by_state_enable(create_tmp_file): 58 | """Verify that the SW returns the enabled plugins""" 59 | act_data = get_plugins_by_state(PluginState.ENABLE, create_tmp_file) 60 | assert "plugin_a" in act_data 61 | assert "plugin_c" not in act_data 62 | assert "plugin_e" not in act_data 63 | 64 | 65 | def test_get_plugins_by_state_disable(create_tmp_file): 66 | """Verify that the SW returns the disabled plugins""" 67 | act_data = get_plugins_by_state(PluginState.DISABLE, create_tmp_file) 68 | assert "plugin_c" in act_data 69 | assert "plugin_a" not in act_data 70 | assert "plugin_e" not in act_data 71 | 72 | 73 | """Utilities used by the tests""" 74 | 75 | 76 | class Config: 77 | def __init__(self, repo_location): 78 | self.repo_location = repo_location 79 | 80 | 81 | class Mockargs: 82 | def __init__(self): 83 | self.tests_path = Path(__file__).resolve().parent 84 | self.component = "hexl" 85 | self.config = f"{self.tests_path}/input_files/default.config" 86 | 87 | 88 | def create_plugin_file(tmp_path, component, instances): 89 | component_path = tmp_path / component 90 | component_path.mkdir(exist_ok=True) 91 | 92 | for instance in instances: 93 | instance_path = component_path / instance 94 | instance_path.mkdir(exist_ok=True) 95 | 96 | 97 | @pytest.fixture 98 | def create_tmp_file(tmp_path): 99 | file_name = tmp_path / "test.toml" 100 | file_data = { 101 | PluginsConfig.KEY: { 102 | "plugin_a": {"version": "1.0.0", "state": PluginState.ENABLE}, 103 | "plugin_c": {"version": "1.0.0", "state": PluginState.DISABLE}, 104 | "plugin_e": {"version": "1.0.0", "state": "g"}, 105 | } 106 | } 107 | 108 | with file_name.open("w", encoding="utf-8") as f: 109 | dump(file_data, f) 110 | 111 | return file_name 112 | -------------------------------------------------------------------------------- /tests/test_util_tsort.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import pytest 5 | from kit.utils.tsort import tsort, CycleError 6 | 7 | 8 | def test_tsort_libraries(): 9 | G = { 10 | "example": ["helib"], 11 | "helib": ["ntl", "hexl"], 12 | "zstd": [], 13 | "seal": ["hexl", "gsl", "zstd"], 14 | "ntl": [], 15 | "gsl": [], 16 | "palisade": ["hexl"], 17 | "hexl": [], 18 | } 19 | 20 | result = tuple(tsort(G)) 21 | option1 = ("hexl", "ntl", "helib", "example", "zstd", "gsl", "seal", "palisade") 22 | option2 = ("ntl", "hexl", "helib", "example", "zstd", "gsl", "seal", "palisade") 23 | assert result == option1 or result == option2 24 | 25 | 26 | def test_tsort_of_DAG(): 27 | """Golden path tests. Can it sort given a DAG.""" 28 | # The dict values are the nodes pointing to the keys. 29 | G = {"D": {"B", "C"}, "C": {"A"}, "B": {"A"}} 30 | # Only two acceptable sorts here 31 | # ABCD or ACBD 32 | tsorted_nodes = "".join(tsort(G)) 33 | assert tsorted_nodes == "ABCD" or tsorted_nodes == "ACBD" 34 | 35 | # Graph from https://en.wikipedia.org/wiki/Topological_sorting 36 | # hash(int) == int in python unlike strings used above. 37 | G = {2: {11}, 9: {8, 11}, 10: {3, 11}, 11: {5, 7}, 8: {7, 3}} 38 | assert tuple(tsort(G)) == (5, 7, 11, 2, 3, 8, 9, 10) 39 | 40 | 41 | def test_tsort_handles_cycle(): 42 | """Should through an CycleError exception if a cycle is detected.""" 43 | # Graph with a cycle 44 | G = {15: {6}, 2: {11, 15}, 6: {2}} 45 | with pytest.raises(CycleError): 46 | tuple(tsort(G)) 47 | -------------------------------------------------------------------------------- /third-party/README.md: -------------------------------------------------------------------------------- 1 | # Third-party plugins 2 | 3 | This directory contains some third-party plugins that provide `hekit` with 4 | additional functionality. 5 | 6 | * `hello-world-plugin` - this plugin is a example or skeleton plugin to 7 | understand how to make third-party plugins. 8 | -------------------------------------------------------------------------------- /third-party/hello_world_plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelLabs/he-toolkit/14abd64a8ac930dac74007a05169f7f3285beff0/third-party/hello_world_plugin/__init__.py -------------------------------------------------------------------------------- /third-party/hello_world_plugin/hello.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | class HelloWorlds: 6 | """This class holds 'hello world' in different languages using the standard 2-letter codes for languages, 7 | https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes 8 | """ 9 | 10 | _hello_worlds: dict[str, str] = { 11 | "AF": "hello wêreld", # Afrikaans 12 | "SQ": "përshendetje botë", # Albanian 13 | "AR": "مرحبا بالعالم", # Arabic 14 | "HY": "բարեւ աշխարհ", # Armenian 15 | "AZ": "salam dunya", # Azerbaijani 16 | "EU": "kaixo mundua", # Basque 17 | "BE": "прывітанне сусвет", # Belarusian 18 | "BG": "здравей свят", # Bulgarian 19 | "CA": "hola món", # Catalan 20 | "ZH": "你好世界", # Chinese 21 | "CO": "salutu mondu", # Corsican 22 | "HR": "pozdrav svijete", # Croatian 23 | "CS": "ahoj světe", # Czech 24 | "DA": "hej verden", # Danish 25 | "NL": "hallo wereld", # Dutch 26 | "EN": "hello world", # English 27 | "FI": "hei maailma", # Finnish 28 | "FR": "bonjour le monde", # French 29 | "KA": "გამარჯობა მსოფლიო", # Georgian 30 | "DE": "hallo welt", # German 31 | "EL": "γειά σου κόσμε", # Greek 32 | "IW": "שלום עולם", # Hebrew 33 | "HU": "helló világ", # Hungarian 34 | "IS": "halló heimur", # Icelandic 35 | "GA": "dia duit ar domhan", # Irish 36 | "IT": "ciao mondo", # Italian 37 | "JA": "こんにちは世界", # Japanese 38 | "KK": "сәлем әлем", # Kazakh 39 | "KO": "안녕하세요 세계", # Korean 40 | "LA": "salve mundi", # Latin 41 | "LV": "sveika pasaule", # Latvian 42 | "LT": "labas pasauli", # Lithuanian 43 | "MS": "hai dunia", # Malay 44 | "MN": "сайн уу ертөнц", # Mongolian 45 | "NO": "hei verden", # Norwegian 46 | "FA": "سلام دنیا", # Persian 47 | "PL": "witaj świecie", # Polish 48 | "PT": "olá mundo", # Portuguese 49 | "RO": "salut lume", # Romanian 50 | "RU": "привет мир", # Russian 51 | "SM": "talofa le lalolagi", # Samoan 52 | "GD": "hàlo a shaoghail", # Scottish Gaelic 53 | "SR": "здраво свете", # Serbian 54 | "SK": "ahoj svet", # Slovak 55 | "ES": "hola mundo", # Spanish 56 | "SU": "halo dunya", # Sudanese 57 | "SV": "hej världen", # Swedish 58 | "TG": "салом ҷаҳон", # Tajik 59 | "TH": "สวัสดีชาวโลก", # Thai 60 | "TR": "selam dünya", # Turkish 61 | "TK": "salam dünýä", # Turkmen 62 | "UK": "привіт світ", # Ukrainian 63 | "UR": "ہیلو دنیا", # Urdu 64 | "VI": "Chào thế giới", # Vietnamese 65 | "CY": "helo byd", # Welsh 66 | } 67 | 68 | @classmethod 69 | def print_msg(cls, args) -> None: 70 | """Executes new functionality""" 71 | # Choices in `--lang` should block unknown 72 | msg = cls._hello_worlds[args.lang] 73 | print(msg) 74 | 75 | @classmethod 76 | def get_available_languages(cls) -> list[str]: 77 | """Returns as a list the available languages""" 78 | return sorted(cls._hello_worlds.keys()) 79 | -------------------------------------------------------------------------------- /third-party/hello_world_plugin/plugin.toml: -------------------------------------------------------------------------------- 1 | [plugin] 2 | name = "hello-world" 3 | version = "1.0.0" 4 | start = "start.py" 5 | -------------------------------------------------------------------------------- /third-party/hello_world_plugin/start.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | from hello_world.hello import HelloWorlds 5 | 6 | 7 | def set_hello_subparser(subparsers): 8 | """create the parser for the hello world plugin""" 9 | parser = subparsers.add_parser("hello-world", description="hello world") 10 | parser.add_argument( 11 | "--lang", 12 | metavar="language", 13 | type=str.upper, 14 | default="EN", 15 | choices=HelloWorlds.get_available_languages(), 16 | help="say hello world in this language", 17 | ) 18 | parser.set_defaults(fn=HelloWorlds.print_msg) 19 | -------------------------------------------------------------------------------- /tools/README.md: -------------------------------------------------------------------------------- 1 | # Development Tools 2 | 3 | The top level `tools` directory is to contain standalone programs and 4 | development libraries that are useful for HE development. 5 | 6 | All code in the top level `tools` directory should be considered currently 7 | experimental. As such, APIs are subject to change even between minor and 8 | patch version. 9 | --------------------------------------------------------------------------------