Compare commits

...

92 Commits

Author SHA1 Message Date
Daniel Demmler
4cd3d2d75a return a reference to the LWE public key 2022-12-21 18:02:51 +01:00
Dragos Rotaru
b3fc2378cc added lwe public key return function 2022-12-15 15:01:25 +02:00
Dragos Rotaru
05de72602c added rc encrypt 2022-12-15 10:21:33 +02:00
Arthur Meyre
bcc791a88d chore(docs): fix a clippy lint for docstrings 2022-12-12 17:44:13 +01:00
Arthur Meyre
4d34f93621 chore(tfhe): rename lwe_linear_algebra algorithms 2022-12-12 14:53:19 +01:00
Arthur Meyre
61459fd3d0 chore(ci): fix shellcheck lints in workflows 2022-12-12 14:38:55 +01:00
Arthur Meyre
589238267b chore(ci): update m1 workflow 2022-12-12 14:32:32 +01:00
Arthur Meyre
d35c595d89 chore(ci): target to check all targets (bench, test, etc.) for clippy lints 2022-12-12 12:40:18 +01:00
Arthur Meyre
5642880a1e docs(tfhe): add various docstrings
- add docstring for lwe_keyswitch
- add docstring for lwe_keyswitch_key_generation
- add docstring for lwe_secret_key_generation
2022-12-12 11:19:50 +01:00
Arthur Meyre
d8a0067aff chore(ci): restore boolean tests on CPU machine
- fix exit code of toolchain installation in case of failure
2022-12-09 20:22:09 +01:00
Arthur Meyre
983f94d64f docs(tfhe): add docstrings for lwe_encryption 2022-12-09 20:14:43 +01:00
Arthur Meyre
5f14bf1cca fix(tfhe): fix various docstring content and LweMask creation bug 2022-12-09 20:14:29 +01:00
Arthur Meyre
5589572efc chore(tfhe): rename some buffers to avoid confusion about their usage 2022-12-09 14:55:30 +01:00
Arthur Meyre
fc1f322f5a docs(tfhe): add docstring for glwe_sample_extraction 2022-12-09 14:34:32 +01:00
Arthur Meyre
55ae47bed6 docs(tfhe): add PolynomialList docstrings 2022-12-09 14:13:44 +01:00
Arthur Meyre
db96573595 docs(tfhe): add docstring for Polynomial 2022-12-09 13:38:44 +01:00
Arthur Meyre
d763131b41 fix(tfhe): make seeders module public 2022-12-09 13:38:16 +01:00
Arthur Meyre
d0650ad6ac docs(tfhe): add docstring for glwe_secret_key_generation module 2022-12-09 12:23:38 +01:00
Arthur Meyre
43f47806d2 docs(tfhe): add glwe encryption formal definitions and docstrings
- correct some an -> a
2022-12-09 11:32:32 +01:00
Arthur Meyre
45505cb198 chore(docs): fix GGSW docstring to have actual GlweSecretKey generation 2022-12-09 11:31:46 +01:00
Arthur Meyre
5190aee8ab docs(tfhe): add link for GGSW encryption algorithm definition
- document helper function for ggsw encryption
2022-12-09 10:20:57 +01:00
Arthur Meyre
6568d7f807 docs(tfhe): docstring for Plaintext
- add more sensible bounds for Plaintext and add PlaintextRef and
PlaintextRefMut for a more homogeneous and less confusing dev experience
2022-12-09 09:46:45 +01:00
J-B Orfila
7559c22b7e docs(crypto_api): add ggsw encryption doctest 2022-12-09 09:46:38 +01:00
Arthur Meyre
cc4587d010 docs(tfhe): docstring for LweSecretKey 2022-12-08 17:17:24 +01:00
Arthur Meyre
b4c321835c docs(tfhe): correct a -> an 2022-12-08 17:14:47 +01:00
Arthur Meyre
a87849789d docs(tfhe): add disclaimer about parameters being toy example parameters 2022-12-08 17:08:56 +01:00
Arthur Meyre
bfc111a2db docs(tfhe): add docstrings for LwePublicKey 2022-12-08 17:06:15 +01:00
Arthur Meyre
7cec0d40fb docs(tfhe): docstring for LwePrivateFunctionalPackingKeyswitchKey 2022-12-08 16:52:30 +01:00
Arthur Meyre
0bdb72466c docs(tfhe): docstring for LwePrivateFunctionalPackingKeyswitchKeyList 2022-12-08 16:35:22 +01:00
Arthur Meyre
6053ee5610 docs(tfhe): add LweKeyswitchKey docstring
- fix method naming
2022-12-08 15:46:54 +01:00
Arthur Meyre
9233b4a7c3 chore(tools): add tasks tools to escape latex equations in docs
- add all checks to pcc and run that in CI
2022-12-08 15:12:15 +01:00
Arthur Meyre
60ff266bc1 docs(tfhe): add GswCiphertext for formal definitions 2022-12-08 15:12:15 +01:00
Arthur Meyre
5f284a7c55 docs(tfhe): add docstrings for LweCiphertext 2022-12-08 15:12:06 +01:00
Arthur Meyre
27d54d19ba docs(tfhe): add LweCiphertextList docstring 2022-12-08 12:05:25 +01:00
Arthur Meyre
315f5b68ab docs(tfhe): add LweBootstrapKey docstrings
- update wording for `new` functions, the allocated vector is not empty.
2022-12-08 11:34:00 +01:00
Arthur Meyre
2d73edc5a3 docs(tfhe): add docstring for GlweSecretKey
- update docstring to indicate useful functions to fill structs
- fix GlweMask docstring
2022-12-08 11:07:34 +01:00
Arthur Meyre
2de0ce4d36 chore(tfhe): update wording to use imperative form in docstrings 2022-12-08 10:00:13 +01:00
Arthur Meyre
45eb5ababd refactor(core): use from_le_bytes for gaussian RNG (see uniform RNG)
- avoids small allocations, uses std::mem::size_of for size
2022-12-07 20:40:54 +01:00
Arthur Meyre
869eace723 docs(tfhe): add GlweCiphertext documentation 2022-12-07 17:18:58 +01:00
Arthur Meyre
97d536b810 chore(tfhe): finish GlweSize/PolynomialSize ordering consistency 2022-12-07 16:40:59 +01:00
Arthur Meyre
2ba22da596 chore(ci): add test compilation checks 2022-12-07 16:30:51 +01:00
Arthur Meyre
b57b6acd8a docs(tfhe): add docstring for GlweCiphertextList
- uniformize orders of GlweSize and PolynomialSize arguments for GLWE-like
entities
2022-12-07 16:11:18 +01:00
Arthur Meyre
2790b08d39 chore(tfhe): change update wording for in place random noise addition 2022-12-07 15:58:32 +01:00
Arthur Meyre
7e73b8ea78 chore(tfhe): change "in place" naming for "assign" following rust style 2022-12-07 15:53:27 +01:00
Arthur Meyre
bc3fe8e0db docs(tfhe): add docstrings for GgswCiphertext, import formal definition 2022-12-07 15:43:02 +01:00
Arthur Meyre
07e567f279 chore(tfhe): misc fixes 2022-12-07 14:52:59 +01:00
Arthur Meyre
9abe5c01fe docs(core): bring back some doc strings for random generators 2022-12-07 14:44:11 +01:00
Arthur Meyre
c8af9c096b feat(tfhe): add karatsuba multiplication for polynomials 2022-12-07 14:04:55 +01:00
Arthur Meyre
9db65bc3b7 docs(tfhe): update polynomial and slice algorithms naming
- update docstrings to be better rendered in html.
2022-12-07 11:40:08 +01:00
Arthur Meyre
0e4c37ed71 docs(tfhe): update name in module documentation 2022-12-07 10:51:56 +01:00
Arthur Meyre
4564182894 docs(tfhe): update entities documentation 2022-12-07 10:35:41 +01:00
Arthur Meyre
1311b0d18b docs(tfhe): update common traits docs 2022-12-07 10:34:46 +01:00
Arthur Meyre
94c023d810 docs(core): add docstring and tests for GgswCiphertextList 2022-12-06 17:31:35 +01:00
Arthur Meyre
57060aa509 feat(core): add prelude 2022-12-06 17:31:13 +01:00
Arthur Meyre
8eb5b26edb chore(core): update Plaintext docstring 2022-12-06 17:31:01 +01:00
J-B Orfila
03e52562ed docs(crypto): doctests slice algorithms 2022-12-06 17:23:40 +01:00
Arthur Meyre
2384a00696 refactor(tfhe): rename polynomial primitives and add docstrings + tests 2022-12-06 16:07:52 +01:00
Arthur Meyre
22699d2c2a chore(tfhe): derive PartialEq and Eq for all entities by default 2022-12-06 16:03:52 +01:00
Arthur Meyre
deb4909f0d chore(tfhe): update rand to avoid deprecation warnings 2022-12-06 16:02:36 +01:00
Arthur Meyre
8094067759 refactor(thfe): remove deprecation on MonomialDegree 2022-12-06 15:05:16 +01:00
Arthur Meyre
c170602f3a refactor(tfhe): move parameters and dispersion modules 2022-12-06 14:05:19 +01:00
Arthur Meyre
533e37e6ee refactor(tfhe): only one instance of FftBuffers, use for simple PBS algo 2022-12-06 11:26:41 +01:00
Arthur Meyre
ce771791b6 chore(doc): deny doc broken links crate-wide 2022-12-06 11:25:57 +01:00
Arthur Meyre
e9e5e75954 chore(tfhe): add convenience traits to commons::traits for glob import 2022-12-06 10:46:24 +01:00
Arthur Meyre
402cd62fda chore(tools): add convenience pcc and conformance targets 2022-12-06 10:38:33 +01:00
Arthur Meyre
9b7559b2dc chore(tfhe): fix refactor TODOs 2022-12-06 10:29:20 +01:00
Arthur Meyre
c7e1cae9d9 refactor(tfhe): unplug core and remove unused parts 2022-12-06 09:44:42 +01:00
Arthur Meyre
e6a2e2e6e5 refactor(boolean): unplug core engines 2022-12-06 09:44:42 +01:00
Arthur Meyre
e94d84bed1 refactor(tfhe): unplug CUDA from boolean and remove the CUDA backend 2022-12-06 09:44:41 +01:00
Arthur Meyre
2225eb712a refactor(tfhe): refactor serizalization, unplug core_crypto::prelude 2022-12-06 09:44:41 +01:00
Arthur Meyre
28370cd7c3 refactor(tfhe): entities Clone + Debug and default parallel + serialization 2022-12-06 09:44:41 +01:00
J-B Orfila
61052df95d feat(core): blind rotate binding 2022-12-06 09:44:40 +01:00
Arthur Meyre
e123524397 refactor(tfhe): Change Base naming scheme 2022-12-06 09:44:40 +01:00
Arthur Meyre
75348e273a refactor(tfhe): remove core engines from ShortintEngine 2022-12-06 09:44:39 +01:00
Arthur Meyre
e6d8130c65 refactor(tfhe): migrate PFPKSK 2022-12-06 09:44:39 +01:00
Arthur Meyre
c23f65dd8f refactor(tfhe): plug woPBS primitives 2022-12-06 09:44:39 +01:00
Arthur Meyre
7265dbc78b refactor(tfhe): plug fft backend with new primitives
- uniformize fft caches to avoid serialization problems
2022-12-06 09:44:38 +01:00
Arthur Meyre
bd25256efb chore(tfhe): remove binary naming 2022-12-06 09:44:38 +01:00
Arthur Meyre
1a2ed8c3f7 refactor(tfhe): add allocate and encrypt for BSK
- use new generation when creating ServerKey in shortint
- next step requires taking parts of the FFT backend for the refactor
2022-12-06 09:44:38 +01:00
Arthur Meyre
6ba6ddddc5 refactor(tfhe): add parallel bootstrap key generation
- add equivalence test between refactored sequential and parallel BSK
generation
2022-12-06 09:44:37 +01:00
Arthur Meyre
a2b6cab69e chore(tfhe): update associated types name for contiguous container traits 2022-12-06 09:44:37 +01:00
Arthur Meyre
4adef7844d refactor(tfhe): reproduce sequential BSK generation 2022-12-06 09:44:36 +01:00
Arthur Meyre
d407bb0213 refactor(tfhe): add GGSW encryption with coherency test between old and new 2022-12-06 09:44:36 +01:00
Arthur Meyre
1fd1c3516e chore(tfhe): minor fixes 2022-12-06 09:44:36 +01:00
Arthur Meyre
a0932f979c refactor(tfhe): rewrite lwe keyswitch algorithm with new system 2022-12-06 09:44:35 +01:00
Arthur Meyre
9ab75b646f chore(tfhe): make imports globs for ease of use 2022-12-06 09:44:35 +01:00
Arthur Meyre
bc54f357a5 chore(ci): fix tooling with minimum version for GATs requirements 2022-12-06 09:44:35 +01:00
Arthur Meyre
5aa1b1ebf2 refactor(tfhe): add refactored LweKeyswitchKey generation algorithm 2022-12-06 09:44:34 +01:00
Arthur Meyre
c92f2bd837 refactor(tfhe): transition GlweSecretKey
- serialization work still pending
2022-12-06 09:44:34 +01:00
Arthur Meyre
fe2263e67d refactor(shortint): change the LweCiphertext type 2022-12-06 09:44:33 +01:00
Arthur Meyre
31be933571 refactor(tfhe): first step of progressive refactor
- provide new structs and compatibility layers (as much as possible) to
convert between types as much as possible
- we are missing key view types in public APIs making this a bit tricky in
that particular case
2022-12-06 09:44:33 +01:00
Arthur Meyre
86b348f0c3 refactor(core): introduce new modules for progressive rework
- strategy is to have new entities for which required algorithms will be
implemented re-using existing private implementations
- when algorithms are missing at first conversion functions will be used to
be able to switch back to the old system and use existing primitives
2022-12-06 09:44:27 +01:00
365 changed files with 12849 additions and 42510 deletions

2
.cargo/config.toml Normal file
View File

@@ -0,0 +1,2 @@
[alias]
xtask = "run --manifest-path ./tasks/Cargo.toml --"

View File

@@ -68,6 +68,10 @@ jobs:
run: |
make test_user_doc
- name: Run boolean tests
run: |
make test_boolean
- name: Install AWS CLI
run: |
apt update

View File

@@ -1,120 +0,0 @@
# Compile and test project on an AWS instance
name: AWS tests on GPU
# This workflow is meant to be run via Zama CI bot Slab.
on:
workflow_dispatch:
inputs:
instance_id:
description: "AWS instance ID"
type: string
instance_image_id:
description: "AWS instance AMI ID"
type: string
instance_type:
description: "AWS EC2 instance product type"
type: string
runner_name:
description: "Action runner name"
type: string
request_id:
description: 'Slab request ID'
type: string
matrix_item:
description: 'Build matrix item'
type: string
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-C target-cpu=native"
ACTION_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
jobs:
run-tests-linux:
concurrency:
group: ${{ github.ref }}_${{ github.event.inputs.instance_image_id }}_${{ github.event.inputs.instance_type }}
cancel-in-progress: true
name: Test code in EC2
runs-on: ${{ github.event.inputs.runner_name }}
strategy:
fail-fast: false
# explicit include-based build matrix, of known valid options
matrix:
include:
- os: ubuntu-20.04
cuda: "11.8"
old_cuda: "11.1"
cuda_arch: "70"
gcc: 8
env:
CUDA_PATH: /usr/local/cuda-${{ matrix.cuda }}
OLD_CUDA_PATH: /usr/local/cuda-${{ matrix.old_cuda }}
steps:
- name: EC2 instance configuration used
run: |
echo "IDs: ${{ github.event.inputs.instance_id }}"
echo "AMI: ${{ github.event.inputs.instance_image_id }}"
echo "Type: ${{ github.event.inputs.instance_type }}"
echo "Request ID: ${{ github.event.inputs.request_id }}"
- uses: actions/checkout@v2
- name: Set up home
run: |
echo "HOME=/home/ubuntu" >> "${GITHUB_ENV}"
- name: Export CUDA variables
run: |
echo "CUDA_PATH=$CUDA_PATH" >> "${GITHUB_ENV}"
echo "$CUDA_PATH/bin" >> "${GITHUB_PATH}"
echo "LD_LIBRARY_PATH=$CUDA_PATH/lib:$LD_LIBRARY_PATH" >> "${GITHUB_ENV}"
# Specify the correct host compilers
- name: Export gcc and g++ variables
run: |
echo "CC=/usr/bin/gcc-${{ matrix.gcc }}" >> "${GITHUB_ENV}"
echo "CXX=/usr/bin/g++-${{ matrix.gcc }}" >> "${GITHUB_ENV}"
echo "CUDAHOSTCXX=/usr/bin/g++-${{ matrix.gcc }}" >> "${GITHUB_ENV}"
echo "CUDACXX=$CUDA_PATH/bin/nvcc" >> "${GITHUB_ENV}"
echo "HOME=/home/ubuntu" >> "${GITHUB_ENV}"
- name: Install latest stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
default: true
- name: Cuda clippy
run: |
make clippy_cuda
- name: Run core cuda tests
run: |
make test_core_crypto_cuda
- name: Test tfhe-rs/boolean with cpu
run: |
make test_boolean
- name: Test tfhe-rs/boolean with cuda backend with CUDA 11.8
run: |
make test_boolean_cuda
- name: Export variables for CUDA 11.1
run: |
echo "CUDA_PATH=$OLD_CUDA_PATH" >> "${GITHUB_ENV}"
echo "LD_LIBRARY_PATH=$OLD_CUDA_PATH/lib:$LD_LIBRARY_PATH" >> "${GITHUB_ENV}"
echo "CUDACXX=$OLD_CUDA_PATH/bin/nvcc" >> "${GITHUB_ENV}"
- name: Test tfhe-rs/boolean with cuda backend with CUDA 11.1
run: |
cargo clean
make test_boolean_cuda
- name: Slack Notification
if: ${{ always() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
SLACK_ICON: https://pbs.twimg.com/profile_images/1274014582265298945/OjBKP9kn_400x400.png
SLACK_MESSAGE: "(Slab ci-bot beta) AWS tests GPU finished with status ${{ job.status }}. (${{ env.ACTION_RUN_URL }})"
SLACK_USERNAME: ${{ secrets.BOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

View File

@@ -73,9 +73,9 @@ jobs:
python3 ./ci/benchmark_parser.py target/criterion ${{ env.RESULTS_FILENAME }} \
--database tfhe_rs_benchmarks \
--hardware ${{ inputs.instance_type }} \
--project-version ${COMMIT_HASH} \
--project-version "${COMMIT_HASH}" \
--branch ${{ github.ref_name }} \
--commit-date ${COMMIT_DATE} \
--commit-date "${COMMIT_DATE}" \
--bench-date "${{ env.BENCH_DATE }}"
- name: Remove previous raw results

View File

@@ -28,49 +28,25 @@ jobs:
run: |
echo "rs-toolchain=$(make rs_toolchain)" >> "${GITHUB_OUTPUT}"
- name: Check format
- name: Run pcc checks
run: |
make check_fmt
- name: Build doc
run: |
make doc
- name: Clippy boolean
run: |
make clippy_boolean
make pcc
- name: Build Release boolean
run: |
make build_boolean
- name: Clippy shortint
run: |
make clippy_shortint
- name: Build Release shortint
run: |
make build_shortint
- name: Clippy shortint and boolean
run: |
make clippy
- name: Build Release shortint and boolean
run: |
make build_boolean_and_shortint
- name: C API Clippy
run: |
make clippy_c_api
- name: Build Release c_api
run: |
make build_c_api
- name: wasm API Clippy
run: |
make clippy_js_wasm_api
# The wasm build check is a bit annoying to set-up here and is done during the tests in
# aws_tfhe_tests.yml

View File

@@ -28,38 +28,22 @@ jobs:
toolchain: stable
default: true
- name: Build doc
- name: Run pcc checks
run: |
make doc
- name: Clippy boolean
run: |
make clippy_boolean
make pcc
- name: Build Release boolean
run: |
make build_boolean
- name: Clippy shortint
run: |
make clippy_shortint
- name: Build Release shortint
run: |
make build_shortint
- name: Clippy shortint and boolean
run: |
make clippy
- name: Build Release shortint and boolean
run: |
make build_boolean_and_shortint
- name: C API Clippy
run: |
make clippy_c_api
- name: Build Release c_api
run: |
make build_c_api

View File

@@ -73,9 +73,9 @@ jobs:
python3 ./ci/benchmark_parser.py target/criterion ${{ env.RESULTS_FILENAME }} \
--database tfhe_rs_benchmarks \
--hardware ${{ inputs.instance_type }} \
--project-version ${COMMIT_HASH} \
--project-version "${COMMIT_HASH}" \
--branch ${{ github.ref_name }} \
--commit-date ${COMMIT_DATE} \
--commit-date "${COMMIT_DATE}" \
--bench-date "${{ env.BENCH_DATE }}" \
--walk-subdirs

View File

@@ -1,6 +1,6 @@
[workspace]
resolver = "2"
members = ["tfhe"]
members = ["tfhe", "tasks"]
[profile.bench]
lto = "fat"

View File

@@ -5,6 +5,7 @@ TARGET_ARCH_FEATURE:=$(shell ./scripts/get_arch_feature.sh)
RS_BUILD_TOOLCHAIN:=$(shell \
( (echo $(TARGET_ARCH_FEATURE) | grep -q x86) && echo stable) || echo $(RS_CHECK_TOOLCHAIN))
CARGO_RS_BUILD_TOOLCHAIN:=+$(RS_BUILD_TOOLCHAIN)
MIN_RUST_VERSION:=1.65
# This is done to avoid forgetting it, we still precise the RUSTFLAGS in the commands to be able to
# copy paste the command in the termianl and change them if required without forgetting the flags
export RUSTFLAGS:=-C target-cpu=native
@@ -21,21 +22,24 @@ rs_build_toolchain:
install_rs_check_toolchain:
@rustup toolchain list | grep -q "$(RS_CHECK_TOOLCHAIN)" || \
rustup toolchain install --profile default "$(RS_CHECK_TOOLCHAIN)" || \
echo "Unable to install $(RS_CHECK_TOOLCHAIN) toolchain, check your rustup installation. \
Rustup can be downloaded at https://rustup.rs/"
( echo "Unable to install $(RS_CHECK_TOOLCHAIN) toolchain, check your rustup installation. \
Rustup can be downloaded at https://rustup.rs/" && exit 1 )
.PHONY: install_rs_build_toolchain # Install the toolchain used for builds
install_rs_build_toolchain:
@rustup toolchain list | grep -q "$(RS_BUILD_TOOLCHAIN)" || \
@( rustup toolchain list | grep -q "$(RS_BUILD_TOOLCHAIN)" && \
./scripts/check_cargo_min_ver.sh \
--rust-toolchain "$(CARGO_RS_BUILD_TOOLCHAIN)" \
--min-rust-version "$(MIN_RUST_VERSION)" ) || \
rustup toolchain install --profile default "$(RS_BUILD_TOOLCHAIN)" || \
echo "Unable to install $(RS_BUILD_TOOLCHAIN) toolchain, check your rustup installation. \
Rustup can be downloaded at https://rustup.rs/"
( echo "Unable to install $(RS_BUILD_TOOLCHAIN) toolchain, check your rustup installation. \
Rustup can be downloaded at https://rustup.rs/" && exit 1 )
.PHONY: install_cargo_nextest # Install cargo nextest used for shortint tests
install_cargo_nextest: install_rs_build_toolchain
@cargo nextest --version > /dev/null 2>&1 || \
cargo $(CARGO_RS_BUILD_TOOLCHAIN) install cargo-nextest --locked || \
echo "Unable to install cargo nextest, unknown error."
( echo "Unable to install cargo nextest, unknown error." && exit 1 )
.PHONY: fmt # Format rust code
fmt: install_rs_check_toolchain
@@ -69,20 +73,26 @@ clippy_c_api: install_rs_check_toolchain
--features=$(TARGET_ARCH_FEATURE),boolean-c-api,shortint-c-api \
-p tfhe -- --no-deps -D warnings
.PHONY: clippy_cuda # Run clippy lints enabling the boolean, shortint, cuda and c API features
clippy_cuda: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy \
--features=$(TARGET_ARCH_FEATURE),cuda,boolean-c-api,shortint-c-api \
-p tfhe -- --no-deps -D warnings
.PHONY: clippy_js_wasm_api # Run clippy lints enabling the boolean, shortint and the js wasm API
clippy_js_wasm_api: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy \
--features=boolean-client-js-wasm-api,shortint-client-js-wasm-api \
-p tfhe -- --no-deps -D warnings
.PHONY: clippy_all # Run all non-CUDA clippy targets
clippy_all: clippy clippy_c_api clippy_js_wasm_api
.PHONY: clippy_tasks # Run clippy lints on helper tasks crate.
clippy_tasks:
RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy \
-p tasks -- --no-deps -D warnings
.PHONY: clippy_all_targets # Run clippy lints on all targets (benches, examples, etc.)
clippy_all_targets:
RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy --all-targets \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,internal-keycache \
-p tfhe -- --no-deps -D warnings
.PHONY: clippy_all # Run all clippy targets
clippy_all: clippy clippy_boolean clippy_shortint clippy_all_targets clippy_c_api \
clippy_js_wasm_api clippy_tasks
.PHONY: gen_key_cache # Run the script to generate keys and cache them for shortint tests
gen_key_cache: install_rs_build_toolchain
@@ -115,21 +125,11 @@ test_core_crypto: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --release \
--features=$(TARGET_ARCH_FEATURE) -p tfhe -- core_crypto::
.PHONY: test_core_crypto_cuda # Run the tests of the core_crypto module with cuda enabled
test_core_crypto_cuda: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --release \
--features=$(TARGET_ARCH_FEATURE),cuda -p tfhe -- core_crypto::backends::cuda::
.PHONY: test_boolean # Run the tests of the boolean module
test_boolean: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --release \
--features=$(TARGET_ARCH_FEATURE),boolean -p tfhe -- boolean::
.PHONY: test_boolean_cuda # Run the tests of the boolean module with cuda enabled
test_boolean_cuda: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --release \
--features=$(TARGET_ARCH_FEATURE),boolean,cuda -p tfhe -- boolean::
.PHONY: test_c_api # Run the tests for the C API
test_c_api: install_rs_build_toolchain
./scripts/c_api_tests.sh $(CARGO_RS_BUILD_TOOLCHAIN)
@@ -155,6 +155,26 @@ doc: install_rs_check_toolchain
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" doc \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint --no-deps
.PHONY: format_doc_latex # Format the documentation latex equations to avoid broken rendering.
format_doc_latex:
cargo xtask format_latex_doc
@"$(MAKE)" --no-print-directory fmt
@printf "\n===============================\n\n"
@printf "Please manually inspect changes made by format_latex_doc, rustfmt can break equations \
if the line length is exceeded\n"
@printf "\n===============================\n"
.PHONY: check_compile_tests # Build tests in debug without running them
check_compile_tests:
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --no-run \
--features=$(TARGET_ARCH_FEATURE),shortint,boolean,internal-keycache -p tfhe
.PHONY: pcc # pcc stands for pre commit checks
pcc: check_fmt doc clippy_all check_compile_tests
.PHONY: conformance # Automatically fix problems that can be fixed
conformance: fmt
.PHONY: help # Generate list of targets with descriptions
help:
@grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1\t\2/' | expand -t30 | sort
@grep '^\.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1\t\2/' | expand -t30 | sort

View File

@@ -3,13 +3,6 @@ region = "eu-west-3"
image_id = "ami-04deffe45b5b236fd"
instance_type = "c6i.8xlarge"
[profile.gpu]
region = "us-east-1"
image_id = "ami-0ae662beb44082155"
instance_type = "p3.2xlarge"
subnet_id = "subnet-8123c9e7"
security_group = "sg-0466d33ced960ba35"
[profile.bench]
region = "eu-west-3"
image_id = "ami-04deffe45b5b236fd"
@@ -18,12 +11,7 @@ instance_type = "m6i.metal"
[command.cpu_test]
workflow = "aws_tfhe_tests.yml"
profile = "cpu-big"
check_run_name = "Shortint CPU AWS Tests"
[command.gpu_test]
workflow = "aws_tfhe_tests_w_gpu.yml"
profile = "gpu"
check_run_name = "AWS tests GPU (Slab)"
check_run_name = "CPU AWS Tests"
[command.shortint_bench]
workflow = "shortint_benchmark.yml"

60
scripts/check_cargo_min_ver.sh Executable file
View File

@@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -e
function usage() {
echo "$0: check minimum cargo version"
echo
echo "--help Print this message"
echo "--rust-toolchain The toolchain to check the version for with leading"
echo "--min-rust-version Check toolchain version is >= to this version, default is 1.65"
echo
}
RUST_TOOLCHAIN=""
# We set the default rust version 1.65 which is the minimum version required for stable GATs
MIN_RUST_VERSION="1.65"
while [ -n "$1" ]
do
case "$1" in
"--help" | "-h" )
usage
exit 0
;;
"--rust-toolchain" )
shift
RUST_TOOLCHAIN="$1"
;;
"--min-rust-version" )
shift
MIN_RUST_VERSION="$1"
;;
*)
echo "Unknown param : $1"
exit 1
;;
esac
shift
done
if [[ "${RUST_TOOLCHAIN::1}" != "+" ]]; then
RUST_TOOLCHAIN="+${RUST_TOOLCHAIN}"
fi
ver_string="$(cargo ${RUST_TOOLCHAIN:+"${RUST_TOOLCHAIN}"} --version | \
cut -d ' ' -f 2 | cut -d '-' -f 1)"
ver_major="$(echo "${ver_string}" | cut -d '.' -f 1)"
ver_minor="$(echo "${ver_string}" | cut -d '.' -f 2)"
min_ver_major="$(echo "${MIN_RUST_VERSION}" | cut -d '.' -f 1)"
min_ver_minor="$(echo "${MIN_RUST_VERSION}" | cut -d '.' -f 2)"
if [[ "${ver_major}" -ge "${min_ver_major}" ]] && [[ "${ver_minor}" -ge "${min_ver_minor}" ]]; then
exit 0
fi
exit 1

View File

@@ -50,6 +50,18 @@ cargo ${1:+"${1}"} nextest run \
--test-threads "${n_threads}" \
-E "${filter_expression}"
filter_expression_wopbs='test(/^shortint::wopbs::.*$/)'
# Run tests only no examples or benches for wopbs
cargo ${1:+"${1}"} nextest run \
--tests \
--release \
--package tfhe \
--profile ci \
--features="${ARCH_FEATURE}",shortint,internal-keycache \
--test-threads "${n_threads}" \
-E "${filter_expression_wopbs}"
cargo ${1:+"${1}"} test \
--release \
--package tfhe \

12
tasks/Cargo.toml Normal file
View File

@@ -0,0 +1,12 @@
[package]
name = "tasks"
version = "0.0.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = "3.1"
lazy_static = "1.4"
log = "0.4"
simplelog = "0.12"

View File

@@ -0,0 +1,453 @@
use crate::utils::project_root;
use std::io::{Error, ErrorKind};
use std::{fmt, fs};
fn recurse_find_rs_files(
root_dir: std::path::PathBuf,
rs_files: &mut Vec<std::path::PathBuf>,
at_root: bool,
) {
for curr_entry in root_dir.read_dir().unwrap() {
let curr_path = curr_entry.unwrap().path().canonicalize().unwrap();
if curr_path.is_file() {
if let Some(extension) = curr_path.extension() {
if extension == "rs" {
rs_files.push(curr_path);
}
}
} else if curr_path.is_dir() {
if at_root {
// Hardcoded ignores for root .git and target
match curr_path.file_name().unwrap().to_str().unwrap() {
".git" => continue,
"target" => continue,
_ => recurse_find_rs_files(curr_path.to_path_buf(), rs_files, false),
};
} else {
recurse_find_rs_files(curr_path.to_path_buf(), rs_files, false);
}
}
}
}
#[derive(Debug)]
struct LatexEscapeToolError {
details: String,
}
impl LatexEscapeToolError {
fn new(msg: &str) -> LatexEscapeToolError {
LatexEscapeToolError {
details: msg.to_string(),
}
}
}
impl fmt::Display for LatexEscapeToolError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.details)
}
}
impl std::error::Error for LatexEscapeToolError {}
const DOC_TEST_START: &str = "///";
const DOC_COMMENT_START: &str = "//!";
const BACKSLASH_UTF8_LEN: usize = '\\'.len_utf8();
enum LineType {
DocTest { code_block_limit: bool },
DocComment { code_block_limit: bool },
EmptyLine,
Other,
}
fn get_line_type_and_trimmed_line(line: &str) -> (LineType, &str) {
let mut trimmed_line = line.trim_start();
let line_type = if trimmed_line.starts_with(DOC_COMMENT_START) {
trimmed_line = trimmed_line
.strip_prefix(DOC_COMMENT_START)
.unwrap()
.trim_start();
let has_code_block_limit = trimmed_line.starts_with("```");
LineType::DocComment {
code_block_limit: has_code_block_limit,
}
} else if trimmed_line.starts_with(DOC_TEST_START) {
trimmed_line = trimmed_line
.strip_prefix(DOC_TEST_START)
.unwrap()
.trim_start();
let has_code_block_limit = trimmed_line.starts_with("```");
LineType::DocTest {
code_block_limit: has_code_block_limit,
}
} else if trimmed_line.is_empty() {
LineType::EmptyLine
} else {
LineType::Other
};
(line_type, trimmed_line)
}
struct CommentContent<'a> {
is_in_code_block: bool,
line_start: &'a str,
line_content: &'a str,
}
fn find_contiguous_doc_comment<'a>(
lines: &[&'a str],
start_line_idx: usize,
) -> (Vec<CommentContent<'a>>, usize) {
let mut doc_comment_end_line_idx = start_line_idx + 1;
let mut is_in_code_block = false;
let mut contiguous_doc_comment = Vec::<CommentContent>::new();
for (line_idx, line) in lines.iter().enumerate().skip(start_line_idx) {
let (line_type, line_content) = get_line_type_and_trimmed_line(line);
let line_start = &line[..line.len() - line_content.len()];
// If there is an empty line we are still in the DocComment
let line_type = if let LineType::EmptyLine = line_type {
LineType::DocComment {
code_block_limit: false,
}
} else {
line_type
};
match line_type {
LineType::DocComment { code_block_limit } => {
if code_block_limit {
// We have found a code block limit, either starting or ending, toggle the
// flag
is_in_code_block = !is_in_code_block;
};
contiguous_doc_comment.push(CommentContent {
is_in_code_block,
line_start,
line_content,
});
// For now the only thing we know is that the next line is potentially the end of
// the comment block, required if a file is a giant comment block to have the proper
// bound
doc_comment_end_line_idx = line_idx + 1;
}
_ => {
// We are sure that the current line is the end of the comment block
doc_comment_end_line_idx = line_idx;
break;
}
};
}
(contiguous_doc_comment, doc_comment_end_line_idx)
}
fn find_contiguous_doc_test<'a>(
lines: &[&'a str],
start_line_idx: usize,
) -> (Vec<CommentContent<'a>>, usize) {
let mut doc_test_end_line_idx = start_line_idx + 1;
let mut is_in_code_block = false;
let mut contiguous_doc_test = Vec::<CommentContent>::new();
for (line_idx, line) in lines.iter().enumerate().skip(start_line_idx) {
let (line_type, line_content) = get_line_type_and_trimmed_line(line);
let line_start = &line[..line.len() - line_content.len()];
// If there is an empty line we are still in the DocTest
let line_type = if let LineType::EmptyLine = line_type {
LineType::DocTest {
code_block_limit: false,
}
} else {
line_type
};
match line_type {
LineType::DocTest { code_block_limit } => {
if code_block_limit {
// We have found a code block limit, either starting or ending, toggle the
// flag
is_in_code_block = !is_in_code_block;
};
contiguous_doc_test.push(CommentContent {
is_in_code_block,
line_start,
line_content,
});
// For now the only thing we know is that the next line is potentially the end of
// the comment block, required if a file is a giant comment block to have the proper
// bound
doc_test_end_line_idx = line_idx + 1;
}
_ => {
// We are sure that the current line is the end of the comment block
doc_test_end_line_idx = line_idx;
break;
}
};
}
(contiguous_doc_test, doc_test_end_line_idx)
}
fn find_contiguous_part_in_doc_test_or_comment(
part_is_code_block: bool,
full_doc_comment_content: &Vec<CommentContent>,
part_start_idx: usize,
) -> (usize, usize) {
let mut next_line_idx = part_start_idx + 1;
loop {
// We have exhausted the doc comment content, break
if next_line_idx == full_doc_comment_content.len() {
break;
}
let CommentContent {
is_in_code_block: next_line_is_in_code_block,
line_start: _,
line_content: _,
} = full_doc_comment_content[next_line_idx];
// We check if the next line is in a different part, if so we break
if next_line_is_in_code_block != part_is_code_block {
break;
}
next_line_idx += 1;
}
// next_line_idx points to the end of the part and is therefore returned as the part_stop_idx
(part_start_idx, next_line_idx)
}
enum LatexEquationKind {
Inline,
Multiline,
NotAnEquation,
}
fn escape_underscores_rewrite_equations(
comment_to_rewrite: &[CommentContent],
rewritten_content: &mut String,
) -> Result<(), LatexEscapeToolError> {
let mut latex_equation_kind = LatexEquationKind::NotAnEquation;
for CommentContent {
is_in_code_block: _,
line_start,
line_content,
} in comment_to_rewrite.iter()
{
rewritten_content.push_str(line_start);
let mut previous_char = '\0';
let mut chars = line_content.chars().peekable();
while let Some(current_char) = chars.next() {
match (previous_char, current_char) {
('$', '$') => {
match latex_equation_kind {
LatexEquationKind::Inline => {
// Problem we find an opening $$ after an opening $, return an error
return Err(LatexEscapeToolError::new(
"Found an opening '$' without a corresponding closing '$'",
));
}
LatexEquationKind::Multiline => {
// Closing $$, no more in a latex equation
latex_equation_kind = LatexEquationKind::NotAnEquation
}
LatexEquationKind::NotAnEquation => {
// Opening $$, in a multiline latex equation
latex_equation_kind = LatexEquationKind::Multiline
}
};
}
(_, '$') => {
let is_inline_marker = chars.peek() != Some(&'$');
if is_inline_marker {
match latex_equation_kind {
LatexEquationKind::Multiline => {
// Problem we find an opening $ after an opening $$, return an error
return Err(LatexEscapeToolError::new(
"Found an opening '$$' without a corresponding closing '$$'",
));
}
LatexEquationKind::Inline => {
// Closing $, no more in a latex equation
latex_equation_kind = LatexEquationKind::NotAnEquation
}
LatexEquationKind::NotAnEquation => {
// Opening $, in an inline latex equation
latex_equation_kind = LatexEquationKind::Inline
}
};
}
// If the marker is not an inline marker but a multiline marker let the other
// case manage it at the next iteration
}
// If the _ is not escaped and we are in an equation we need to escape it
(prev, '_') if prev != '\\' => match latex_equation_kind {
LatexEquationKind::NotAnEquation => (),
_ => rewritten_content.push('\\'),
},
_ => (),
}
rewritten_content.push(current_char);
previous_char = current_char;
}
}
Ok(())
}
fn process_doc_lines_until_impossible<'a>(
lines: &[&'a str],
rewritten_content: &'a mut String,
comment_search_fn: fn(&[&'a str], usize) -> (Vec<CommentContent<'a>>, usize),
start_line_idx: usize,
) -> Result<usize, LatexEscapeToolError> {
let (full_doc_content, doc_end_line_idx) = comment_search_fn(lines, start_line_idx);
// Now we find code blocks parts OR pure comments parts
let mut current_line_in_doc_idx = 0;
while current_line_in_doc_idx < full_doc_content.len() {
let CommentContent {
is_in_code_block,
line_start: _,
line_content: _,
} = full_doc_content[current_line_in_doc_idx];
let (current_part_start_idx, current_part_stop_idx) =
find_contiguous_part_in_doc_test_or_comment(
is_in_code_block,
&full_doc_content,
current_line_in_doc_idx,
);
let current_part_content = &full_doc_content[current_part_start_idx..current_part_stop_idx];
// The current part is a code block
if is_in_code_block {
for CommentContent {
is_in_code_block: _,
line_start,
line_content,
} in current_part_content.iter()
{
// We can just push the content unmodified
rewritten_content.push_str(line_start);
rewritten_content.push_str(line_content);
}
} else {
// The part is a pure comment, we need to rewrite equations
escape_underscores_rewrite_equations(current_part_content, rewritten_content)?;
}
current_line_in_doc_idx += current_part_content.len();
}
Ok(doc_end_line_idx)
}
fn process_non_doc_lines_until_impossible(
lines: &Vec<&str>,
rewritten_content: &mut String,
mut line_idx: usize,
) -> usize {
while line_idx < lines.len() {
let line = lines[line_idx];
match get_line_type_and_trimmed_line(line) {
(LineType::Other, _) => {
rewritten_content.push_str(line);
line_idx += 1;
}
_ => break,
};
}
line_idx
}
fn escape_underscore_in_latex_doc_in_file(
file_path: &std::path::Path,
) -> Result<(), LatexEscapeToolError> {
let file_name = file_path.to_str().unwrap();
let content = std::fs::read_to_string(file_name).unwrap();
let number_of_underscores = content.matches('_').count();
let potential_additional_capacity_required = number_of_underscores * BACKSLASH_UTF8_LEN;
// Enough for the length of the original string + the length if we had to escape *all* `_`
// which won't happen but avoids reallocations
let mut rewritten_content =
String::with_capacity(content.len() + potential_additional_capacity_required);
let content_by_lines: Vec<&str> = content.split_inclusive('\n').collect();
let mut line_idx = 0_usize;
while line_idx < content_by_lines.len() {
let line = content_by_lines[line_idx];
let (line_type, _) = get_line_type_and_trimmed_line(line);
line_idx = match line_type {
LineType::DocComment {
code_block_limit: _,
} => process_doc_lines_until_impossible(
&content_by_lines,
&mut rewritten_content,
find_contiguous_doc_comment,
line_idx,
)?,
LineType::DocTest {
code_block_limit: _,
} => process_doc_lines_until_impossible(
&content_by_lines,
&mut rewritten_content,
find_contiguous_doc_test,
line_idx,
)?,
LineType::Other => process_non_doc_lines_until_impossible(
&content_by_lines,
&mut rewritten_content,
line_idx,
),
LineType::EmptyLine => {
rewritten_content.push_str(line);
line_idx + 1
}
};
}
fs::write(file_name, rewritten_content).unwrap();
Ok(())
}
pub fn escape_underscore_in_latex_doc() -> Result<(), Error> {
let project_root = project_root();
let mut src_files: Vec<std::path::PathBuf> = Vec::new();
recurse_find_rs_files(project_root, &mut src_files, true);
println!("Found {} files to process.", src_files.len());
let mut files_with_problems: Vec<(std::path::PathBuf, LatexEscapeToolError)> = Vec::new();
println!("Processing...");
for file in src_files.into_iter() {
if let Err(err) = escape_underscore_in_latex_doc_in_file(&file) {
files_with_problems.push((file, err));
}
}
println!("Done!");
if !files_with_problems.is_empty() {
for (file_with_problem, error) in files_with_problems.iter() {
println!(
"File: {}, has error: {}",
file_with_problem.display(),
error
);
}
return Err(Error::new(
ErrorKind::InvalidInput,
"Issues while processing files, check log.",
));
}
Ok(())
}

88
tasks/src/main.rs Normal file
View File

@@ -0,0 +1,88 @@
#[macro_use]
extern crate lazy_static;
use clap::{Arg, Command};
use log::LevelFilter;
use simplelog::{ColorChoice, CombinedLogger, Config, TermLogger, TerminalMode};
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::Relaxed;
mod format_latex_doc;
mod utils;
// -------------------------------------------------------------------------------------------------
// CONSTANTS
// -------------------------------------------------------------------------------------------------
lazy_static! {
static ref DRY_RUN: AtomicBool = AtomicBool::new(false);
static ref ROOT_DIR: PathBuf = utils::project_root();
static ref ENV_TARGET_NATIVE: utils::Environment = {
let mut env = HashMap::new();
env.insert("RUSTFLAGS", "-Ctarget-cpu=native");
env
};
}
// -------------------------------------------------------------------------------------------------
// MACROS
// -------------------------------------------------------------------------------------------------
#[macro_export]
macro_rules! cmd {
(<$env: ident> $cmd: expr) => {
$crate::utils::execute($cmd, Some(&*$env), Some(&*$crate::ROOT_DIR))
};
($cmd: expr) => {
$crate::utils::execute($cmd, None, Some(&*$crate::ROOT_DIR))
};
}
// -------------------------------------------------------------------------------------------------
// MAIN
// -------------------------------------------------------------------------------------------------
fn main() -> Result<(), std::io::Error> {
// We parse the input args
let matches = Command::new("tasks")
.about("Rust scripts runner")
.arg(
Arg::new("verbose")
.short('v')
.long("verbose")
.help("Prints debug messages"),
)
.arg(
Arg::new("dry-run")
.long("dry-run")
.help("Do not execute the commands"),
)
.subcommand(Command::new("format_latex_doc").about("Escape underscores in latex equations"))
.arg_required_else_help(true)
.get_matches();
// We initialize the logger with proper verbosity
let verb = if matches.contains_id("verbose") {
LevelFilter::Debug
} else {
LevelFilter::Info
};
CombinedLogger::init(vec![TermLogger::new(
verb,
Config::default(),
TerminalMode::Mixed,
ColorChoice::Auto,
)])
.unwrap();
// We set the dry-run mode if present
if matches.contains_id("dry-run") {
DRY_RUN.store(true, Relaxed);
}
if matches.subcommand_matches("format_latex_doc").is_some() {
format_latex_doc::escape_underscore_in_latex_doc()?;
}
Ok(())
}

50
tasks/src/utils.rs Normal file
View File

@@ -0,0 +1,50 @@
use log::{debug, info};
use std::collections::HashMap;
use std::io::{Error, ErrorKind};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::atomic::Ordering::Relaxed;
pub type Environment = HashMap<&'static str, &'static str>;
#[allow(dead_code)]
pub fn execute(cmd: &str, env: Option<&Environment>, cwd: Option<&PathBuf>) -> Result<(), Error> {
info!("Executing {}", cmd);
debug!("Env {:?}", env);
debug!("Cwd {:?}", cwd);
if crate::DRY_RUN.load(Relaxed) {
info!("Skipping execution because of --dry-run mode");
return Ok(());
}
let mut command = Command::new("sh");
command
.arg("-c")
.arg(cmd)
.stderr(Stdio::inherit())
.stdout(Stdio::inherit());
if let Some(env) = env {
for (key, val) in env.iter() {
command.env(key, val);
}
}
if let Some(cwd) = cwd {
command.current_dir(cwd);
}
let output = command.output()?;
if !output.status.success() {
Err(Error::new(
ErrorKind::Other,
"Command exited with nonzero status.",
))
} else {
Ok(())
}
}
pub fn project_root() -> PathBuf {
Path::new(&env!("CARGO_MANIFEST_DIR"))
.ancestors()
.nth(1)
.unwrap()
.to_path_buf()
}

View File

@@ -15,7 +15,8 @@ exclude = ["/docs/", "/c_api_tests/", "/CMakeLists.txt"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dev-dependencies]
rand = "0.7"
rand = "0.8.5"
rand_distr = "0.4.3"
kolmogorov_smirnov = "1.1.0"
paste = "1.0.7"
lazy_static = { version = "1.4.0" }
@@ -23,24 +24,28 @@ criterion = "0.3.5"
doc-comment = "0.3.3"
# Used in user documentation
bincode = "1.3.3"
fs2 = { version = "0.4.3"}
fs2 = { version = "0.4.3" }
[build-dependencies]
cbindgen = { version = "0.24.3", optional = true }
[dependencies]
concrete-csprng = { version = "0.2.1" }
concrete-cuda = { version = "0.1.1", optional = true }
concrete-csprng = { version = "0.2.1", features = [
"generator_soft",
"parallel",
] }
lazy_static = { version = "1.4.0", optional = true }
serde = { version = "1.0", optional = true }
rayon = { version = "1.5.0", optional = true }
bincode = { version = "1.3.3", optional = true }
concrete-fft = { version = "0.1", optional = true }
aligned-vec = "0.5"
dyn-stack = { version = "0.8", optional = true }
serde = { version = "1.0", features = ["derive"] }
rayon = { version = "1.5.0" }
bincode = { version = "1.3.3" }
concrete-fft = { version = "0.1", features = ["serde"] }
aligned-vec = { version = "0.5", features = ["serde"] }
dyn-stack = { version = "0.8" }
once_cell = "1.13"
paste = "1.0.7"
fs2 = { version = "0.4.3", optional = true }
# While we wait for repeat_n in rust standard library
itertools = "0.10.5"
# wasm deps
wasm-bindgen = { version = "0.2.63", features = [
@@ -52,11 +57,11 @@ serde-wasm-bindgen = { version = "0.4", optional = true }
getrandom = { version = "0.2.8", optional = true }
[features]
boolean = ["minimal_core_crypto_features"]
shortint = ["minimal_core_crypto_features"]
boolean = []
shortint = []
internal-keycache = ["lazy_static", "fs2"]
__c_api = ["cbindgen", "minimal_core_crypto_features"]
__c_api = ["cbindgen"]
boolean-c-api = ["boolean", "__c_api"]
shortint-c-api = ["shortint", "__c_api"]
@@ -71,78 +76,30 @@ __wasm_api = [
boolean-client-js-wasm-api = ["boolean", "__wasm_api"]
shortint-client-js-wasm-api = ["shortint", "__wasm_api"]
cuda = ["backend_cuda"]
nightly-avx512 = ["backend_fft_nightly_avx512"]
# A pure-rust CPU backend.
backend_default = ["concrete-csprng/generator_soft"]
# An accelerated backend, using the `concrete-fft` library.
backend_fft = ["concrete-fft", "dyn-stack"]
backend_fft_serialization = [
"bincode",
"concrete-fft/serde",
"aligned-vec/serde",
"__commons_serialization",
]
backend_fft_nightly_avx512 = ["concrete-fft/nightly"]
# Enables the parallel engine in default backend.
backend_default_parallel = ["__commons_parallel"]
nightly-avx512 = ["concrete-fft/nightly"]
# Enable the x86_64 specific accelerated implementation of the random generator for the default
# backend
backend_default_generator_x86_64_aesni = [
"concrete-csprng/generator_x86_64_aesni",
]
generator_x86_64_aesni = ["concrete-csprng/generator_x86_64_aesni"]
# Enable the aarch64 specific accelerated implementation of the random generator for the default
# backend
backend_default_generator_aarch64_aes = [
"concrete-csprng/generator_aarch64_aes",
]
# Enable the serialization engine in the default backend.
backend_default_serialization = ["bincode", "__commons_serialization"]
# A GPU backend, relying on Cuda acceleration
backend_cuda = ["concrete-cuda"]
generator_aarch64_aes = ["concrete-csprng/generator_aarch64_aes"]
# Private features
__profiling = []
__private_docs = []
__commons_parallel = ["rayon", "concrete-csprng/parallel"]
__commons_serialization = ["serde", "serde/derive"]
seeder_unix = ["concrete-csprng/seeder_unix"]
seeder_x86_64_rdseed = ["concrete-csprng/seeder_x86_64_rdseed"]
minimal_core_crypto_features = [
"backend_default",
"backend_default_parallel",
"backend_default_serialization",
"backend_fft",
"backend_fft_serialization",
]
# These target_arch features enable a set of public features for concrete-core if users want a known
# good/working configuration for concrete-core.
# For a target_arch that does not yet have such a feature, one can still enable features manually or
# create a feature for said target_arch to make its use simpler.
x86_64 = [
"minimal_core_crypto_features",
"backend_default_generator_x86_64_aesni",
"seeder_x86_64_rdseed",
]
x86_64 = ["generator_x86_64_aesni", "seeder_x86_64_rdseed"]
x86_64-unix = ["x86_64", "seeder_unix"]
# CUDA builds are Unix only at the moment
x86_64-unix-cuda = ["x86_64-unix", "cuda"]
aarch64 = [
"minimal_core_crypto_features",
"backend_default_generator_aarch64_aes",
]
aarch64 = ["generator_aarch64_aes"]
aarch64-unix = ["aarch64", "seeder_unix"]
[package.metadata.docs.rs]

View File

@@ -44,17 +44,10 @@ fn bench_gates(c: &mut Criterion, params: BooleanParameters, parameter_name: &st
c.bench_function(&id, |b| b.iter(|| black_box(sks.mux(&ct1, &ct2, &ct3))));
}
#[cfg(not(feature = "cuda"))]
fn bench_default_parameters(c: &mut Criterion) {
bench_gates(c, DEFAULT_PARAMETERS, "DEFAULT_PARAMETERS");
}
#[cfg(feature = "cuda")]
fn bench_default_parameters(_: &mut Criterion) {
let _ = DEFAULT_PARAMETERS; // to avoid unused import warnings
println!("DEFAULT_PARAMETERS not benched as they are not compatible with the cuda feature.");
}
fn bench_tfhe_lib_parameters(c: &mut Criterion) {
bench_gates(c, TFHE_LIB_PARAMETERS, "TFHE_LIB_PARAMETERS");
}

View File

@@ -16,6 +16,12 @@ This library exposes a C binding to the `TFHE-rs` primitives to implement _Fully
RUSTFLAGS="-C target-cpu=native" cargo build --release --features=x86_64-unix,boolean-c-api,shortint-c-api -p tfhe
```
or on a Unix aarch64 machine using the following command
```shell
RUSTFLAGS="-C target-cpu=native" cargo build --release --features=aarch64-unix,boolean-c-api,shortint-c-api -p tfhe
```
All features are opt-in, but for simplicity here, the C API is enabled for boolean and shortint.
The `tfhe.h` header as well as the static (.a) and dynamic (.so) `libtfhe` binaries can then be found in "${REPO\_ROOT}/target/release/"

View File

@@ -1,12 +1,15 @@
use tfhe::shortint::keycache::{FileStorage, NamedParam, PersistentStorage};
use tfhe::shortint::parameters::ALL_PARAMETER_VEC;
use tfhe::shortint::{gen_keys, ClientKey, ServerKey};
use tfhe::shortint::keycache::{NamedParam, KEY_CACHE, KEY_CACHE_WOPBS};
use tfhe::shortint::parameters::parameters_wopbs_message_carry::{
WOPBS_PARAM_MESSAGE_1_CARRY_1, WOPBS_PARAM_MESSAGE_2_CARRY_2, WOPBS_PARAM_MESSAGE_3_CARRY_3,
WOPBS_PARAM_MESSAGE_4_CARRY_4,
};
use tfhe::shortint::parameters::{
Parameters, ALL_PARAMETER_VEC, PARAM_MESSAGE_1_CARRY_1, PARAM_MESSAGE_2_CARRY_2,
PARAM_MESSAGE_3_CARRY_3, PARAM_MESSAGE_4_CARRY_4,
};
fn client_server_keys() {
let file_storage = FileStorage::new("keys/shortint/client_server".to_string());
println!("Generating (ClientKey, ServerKey)");
println!("Generating shortint (ClientKey, ServerKey)");
for (i, params) in ALL_PARAMETER_VEC.iter().copied().enumerate() {
println!(
"Generating [{} / {}] : {}",
@@ -15,17 +18,41 @@ fn client_server_keys() {
params.name()
);
let keys: Option<(ClientKey, ServerKey)> = file_storage.load(params);
let _ = KEY_CACHE.get_from_param(params);
// Clear keys as we go to avoid filling the RAM
KEY_CACHE.clear_in_memory_cache()
}
if keys.is_some() {
continue;
}
const WOPBS_PARAMS: [(Parameters, Parameters); 4] = [
(PARAM_MESSAGE_1_CARRY_1, WOPBS_PARAM_MESSAGE_1_CARRY_1),
(PARAM_MESSAGE_2_CARRY_2, WOPBS_PARAM_MESSAGE_2_CARRY_2),
(PARAM_MESSAGE_3_CARRY_3, WOPBS_PARAM_MESSAGE_3_CARRY_3),
(PARAM_MESSAGE_4_CARRY_4, WOPBS_PARAM_MESSAGE_4_CARRY_4),
];
let client_server_keys = gen_keys(params);
file_storage.store(params, &client_server_keys);
println!("Generating woPBS keys");
for (i, (params_shortint, params_wopbs)) in WOPBS_PARAMS.iter().copied().enumerate() {
println!(
"Generating [{} / {}] : {}, {}",
i + 1,
WOPBS_PARAMS.len(),
params_shortint.name(),
params_wopbs.name(),
);
let _ = KEY_CACHE_WOPBS.get_from_param((params_shortint, params_wopbs));
// Clear keys as we go to avoid filling the RAM
KEY_CACHE_WOPBS.clear_in_memory_cache()
}
}
fn main() {
let work_dir = std::env::current_dir().unwrap();
println!("work_dir: {}", std::env::current_dir().unwrap().display());
// Change workdir so that the location of the keycache matches the one for tests
let mut new_work_dir = work_dir;
new_work_dir.push("tfhe");
std::env::set_current_dir(new_work_dir).unwrap();
client_server_keys()
}

View File

@@ -2,7 +2,7 @@
//!
//! This module implements the ciphertext structure containing an encryption of a Boolean message.
use crate::core_crypto::prelude::*;
use crate::core_crypto::entities::*;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// A structure containing a ciphertext, meant to encrypt a Boolean message.
@@ -10,7 +10,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// It is used to evaluate a Boolean circuits homomorphically.
#[derive(Clone, Debug)]
pub enum Ciphertext {
Encrypted(LweCiphertext32),
Encrypted(LweCiphertextOwned<u32>),
Trivial(bool),
}
@@ -25,11 +25,9 @@ impl Serialize for Ciphertext {
where
S: Serializer,
{
let mut ser_eng = DefaultSerializationEngine::new(()).map_err(serde::ser::Error::custom)?;
match self {
Ciphertext::Encrypted(lwe) => {
let ciphertext = ser_eng.serialize(lwe).map_err(serde::ser::Error::custom)?;
let ciphertext = bincode::serialize(lwe).map_err(serde::ser::Error::custom)?;
SerializableCiphertext::Encrypted(ciphertext)
}
Ciphertext::Trivial(b) => SerializableCiphertext::Trivial(*b),
@@ -45,13 +43,10 @@ impl<'de> Deserialize<'de> for Ciphertext {
{
let thing = SerializableCiphertext::deserialize(deserializer)?;
let mut de_eng = DefaultSerializationEngine::new(()).map_err(serde::de::Error::custom)?;
Ok(match thing {
SerializableCiphertext::Encrypted(data) => {
let lwe = de_eng
.deserialize(data.as_slice())
.map_err(serde::de::Error::custom)?;
let lwe =
bincode::deserialize(data.as_slice()).map_err(serde::de::Error::custom)?;
Self::Encrypted(lwe)
}
SerializableCiphertext::Trivial(b) => Self::Trivial(b),

View File

@@ -4,9 +4,9 @@
//! encryption and decryption methods.
use crate::boolean::ciphertext::Ciphertext;
use crate::boolean::engine::{CpuBooleanEngine, WithThreadLocalEngine};
use crate::boolean::engine::{BooleanEngine, WithThreadLocalEngine};
use crate::boolean::parameters::BooleanParameters;
use crate::core_crypto::prelude::*;
use crate::core_crypto::entities::*;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{Debug, Formatter};
@@ -20,8 +20,8 @@ use std::fmt::{Debug, Formatter};
/// * `parameters` - the cryptographic parameter set.
#[derive(Clone)]
pub struct ClientKey {
pub(crate) lwe_secret_key: LweSecretKey32,
pub(crate) glwe_secret_key: GlweSecretKey32,
pub(crate) lwe_secret_key: LweSecretKeyOwned<u32>,
pub(crate) glwe_secret_key: GlweSecretKeyOwned<u32>,
pub(crate) parameters: BooleanParameters,
}
@@ -46,12 +46,11 @@ impl Debug for ClientKey {
}
impl ClientKey {
/// Encrypts a Boolean message using the client key.
/// Encrypt a Boolean message using the client key.
///
/// # Example
///
/// ```rust
/// # #[cfg(not(feature = "cuda"))]
/// # fn main() {
/// use tfhe::boolean::prelude::*;
///
@@ -65,19 +64,16 @@ impl ClientKey {
/// let dec = cks.decrypt(&ct);
/// assert_eq!(true, dec);
/// # }
/// # #[cfg(feature = "cuda")]
/// # fn main() {}
/// ```
pub fn encrypt(&self, message: bool) -> Ciphertext {
CpuBooleanEngine::with_thread_local_mut(|engine| engine.encrypt(message, self))
BooleanEngine::with_thread_local_mut(|engine| engine.encrypt(message, self))
}
/// Decrypts a ciphertext encrypting a Boolean message using the client key.
/// Decrypt a ciphertext encrypting a Boolean message using the client key.
///
/// # Example
///
/// ```rust
/// # #[cfg(not(feature = "cuda"))]
/// # fn main() {
/// use tfhe::boolean::prelude::*;
///
@@ -91,24 +87,16 @@ impl ClientKey {
/// let dec = cks.decrypt(&ct);
/// assert_eq!(true, dec);
/// # }
/// # #[cfg(feature = "cuda")]
/// # fn main() {}
/// ```
pub fn decrypt(&self, ct: &Ciphertext) -> bool {
CpuBooleanEngine::with_thread_local_mut(|engine| engine.decrypt(ct, self))
BooleanEngine::with_thread_local_mut(|engine| engine.decrypt(ct, self))
}
/// Allocates and generates a client key.
///
/// # Panic
///
/// This will panic when the "cuda" feature is enabled and the parameters
/// uses a GlweDimension > 1 (as it is not yet supported by the cuda backend).
/// Allocate and generate a client key.
///
/// # Example
///
/// ```rust
/// # #[cfg(not(feature = "cuda"))]
/// # fn main() {
/// use tfhe::boolean::client_key::ClientKey;
/// use tfhe::boolean::parameters::TFHE_LIB_PARAMETERS;
@@ -117,23 +105,9 @@ impl ClientKey {
/// // Generate the client key:
/// let cks = ClientKey::new(&TFHE_LIB_PARAMETERS);
/// # }
/// # #[cfg(feature = "cuda")]
/// # fn main() {
/// use tfhe::boolean::client_key::ClientKey;
/// use tfhe::boolean::parameters::GPU_DEFAULT_PARAMETERS;
/// use tfhe::boolean::prelude::*;
///
/// // Generate the client key:
/// let cks = ClientKey::new(&GPU_DEFAULT_PARAMETERS);}
/// ```
pub fn new(parameter_set: &BooleanParameters) -> ClientKey {
#[cfg(feature = "cuda")]
{
if parameter_set.glwe_dimension.0 > 1 {
panic!("the cuda backend does not support support GlweSize greater than one");
}
}
CpuBooleanEngine::with_thread_local_mut(|engine| engine.create_client_key(*parameter_set))
BooleanEngine::with_thread_local_mut(|engine| engine.create_client_key(*parameter_set))
}
}
@@ -149,14 +123,10 @@ impl Serialize for ClientKey {
where
S: Serializer,
{
let mut ser_eng = DefaultSerializationEngine::new(()).map_err(serde::ser::Error::custom)?;
let lwe_secret_key = ser_eng
.serialize(&self.lwe_secret_key)
.map_err(serde::ser::Error::custom)?;
let glwe_secret_key = ser_eng
.serialize(&self.glwe_secret_key)
.map_err(serde::ser::Error::custom)?;
let lwe_secret_key =
bincode::serialize(&self.lwe_secret_key).map_err(serde::ser::Error::custom)?;
let glwe_secret_key =
bincode::serialize(&self.glwe_secret_key).map_err(serde::ser::Error::custom)?;
SerializableClientKey {
lwe_secret_key,
@@ -174,14 +144,11 @@ impl<'de> Deserialize<'de> for ClientKey {
{
let thing =
SerializableClientKey::deserialize(deserializer).map_err(serde::de::Error::custom)?;
let mut de_eng = DefaultSerializationEngine::new(()).map_err(serde::de::Error::custom)?;
Ok(Self {
lwe_secret_key: de_eng
.deserialize(thing.lwe_secret_key.as_slice())
lwe_secret_key: bincode::deserialize(thing.lwe_secret_key.as_slice())
.map_err(serde::de::Error::custom)?,
glwe_secret_key: de_eng
.deserialize(thing.glwe_secret_key.as_slice())
glwe_secret_key: bincode::deserialize(thing.glwe_secret_key.as_slice())
.map_err(serde::de::Error::custom)?,
parameters: thing.parameters,
})

View File

@@ -0,0 +1,310 @@
use crate::boolean::ciphertext::Ciphertext;
use crate::boolean::{ClientKey, PLAINTEXT_TRUE};
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::computation_buffers::ComputationBuffers;
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
use crate::core_crypto::commons::math::random::{ActivatedRandomGenerator, Seeder};
use crate::core_crypto::entities::*;
use crate::core_crypto::fft_impl::math::fft::Fft;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::error::Error;
/// Memory used as buffer for the bootstrap
///
/// It contains contiguous chunk which is then sliced and converted
/// into core's View types.
#[derive(Default)]
struct Memory {
buffer: Vec<u32>,
}
impl Memory {
/// Return a tuple with buffers that matches the server key.
///
/// - The first element is the accumulator for bootstrap step.
/// - The second element is a lwe buffer where the result of the of the bootstrap should be
/// written
fn as_buffers(
&mut self,
server_key: &ServerKey,
) -> (GlweCiphertextView<'_, u32>, LweCiphertextMutView<'_, u32>) {
let num_elem_in_accumulator = server_key.bootstrapping_key.glwe_size().0
* server_key.bootstrapping_key.polynomial_size().0;
let num_elem_in_lwe = server_key
.bootstrapping_key
.output_lwe_dimension()
.to_lwe_size()
.0;
let total_elem_needed = num_elem_in_accumulator + num_elem_in_lwe;
let all_elements = if self.buffer.len() < total_elem_needed {
self.buffer.resize(total_elem_needed, 0u32);
self.buffer.as_mut_slice()
} else {
&mut self.buffer[..total_elem_needed]
};
let (accumulator_elements, lwe_elements) =
all_elements.split_at_mut(num_elem_in_accumulator);
{
let mut accumulator = GlweCiphertextMutView::from_container(
accumulator_elements,
server_key.bootstrapping_key.polynomial_size(),
);
accumulator.get_mut_mask().as_mut().fill(0u32);
accumulator.get_mut_body().as_mut().fill(PLAINTEXT_TRUE);
}
let accumulator = GlweCiphertextView::from_container(
accumulator_elements,
server_key.bootstrapping_key.polynomial_size(),
);
let lwe = LweCiphertextMutView::from_container(lwe_elements);
(accumulator, lwe)
}
}
/// A structure containing the server public key.
///
/// This server key data lives on the CPU.
///
/// The server key is generated by the client and is meant to be published: the client
/// sends it to the server so it can compute homomorphic Boolean circuits.
///
/// In more details, it contains:
/// * `bootstrapping_key` - a public key, used to perform the bootstrapping operation.
/// * `key_switching_key` - a public key, used to perform the key-switching operation.
#[derive(Clone)]
pub struct ServerKey {
pub(crate) bootstrapping_key: FourierLweBootstrapKeyOwned,
pub(crate) key_switching_key: LweKeyswitchKeyOwned<u32>,
}
/// Perform ciphertext bootstraps on the CPU
pub(crate) struct Bootstrapper {
memory: Memory,
/// A structure containing two CSPRNGs to generate material for encryption like public masks
/// and secret errors.
///
/// The [`EncryptionRandomGenerator`] contains two CSPRNGs, one publicly seeded used to
/// generate mask coefficients and one privately seeded used to generate errors during
/// encryption.
pub(crate) encryption_generator: EncryptionRandomGenerator<ActivatedRandomGenerator>,
pub(crate) computation_buffers: ComputationBuffers,
}
impl Bootstrapper {
pub fn new(seeder: &mut dyn Seeder) -> Self {
Bootstrapper {
memory: Default::default(),
encryption_generator: EncryptionRandomGenerator::<_>::new(seeder.seed(), seeder),
computation_buffers: Default::default(),
}
}
pub(crate) fn new_server_key(
&mut self,
cks: &ClientKey,
) -> Result<ServerKey, Box<dyn std::error::Error>> {
let standard_bootstraping_key: LweBootstrapKeyOwned<u32> =
par_allocate_and_generate_new_lwe_bootstrap_key(
&cks.lwe_secret_key,
&cks.glwe_secret_key,
cks.parameters.pbs_base_log,
cks.parameters.pbs_level,
cks.parameters.glwe_modular_std_dev,
&mut self.encryption_generator,
);
// creation of the bootstrapping key in the Fourier domain
let mut fourier_bsk = FourierLweBootstrapKey::new(
standard_bootstraping_key.input_lwe_dimension(),
standard_bootstraping_key.glwe_size(),
standard_bootstraping_key.polynomial_size(),
standard_bootstraping_key.decomposition_base_log(),
standard_bootstraping_key.decomposition_level_count(),
);
let fft = Fft::new(standard_bootstraping_key.polynomial_size());
let fft = fft.as_view();
self.computation_buffers.resize(
convert_standard_lwe_bootstrap_key_to_fourier_mem_optimized_scratch(fft)
.unwrap()
.unaligned_bytes_required(),
);
let stack = self.computation_buffers.stack();
// Conversion to fourier domain
convert_standard_lwe_bootstrap_key_to_fourier_mem_optimized(
&standard_bootstraping_key,
&mut fourier_bsk,
fft,
stack,
);
// Convert the GLWE secret key into an LWE secret key:
let big_lwe_secret_key = cks.glwe_secret_key.clone().into_lwe_secret_key();
// creation of the key switching key
let ksk = allocate_and_generate_new_lwe_keyswitch_key(
&big_lwe_secret_key,
&cks.lwe_secret_key,
cks.parameters.ks_base_log,
cks.parameters.ks_level,
cks.parameters.lwe_modular_std_dev,
&mut self.encryption_generator,
);
Ok(ServerKey {
bootstrapping_key: fourier_bsk,
key_switching_key: ksk,
})
}
pub(crate) fn bootstrap(
&mut self,
input: &LweCiphertextOwned<u32>,
server_key: &ServerKey,
) -> Result<LweCiphertextOwned<u32>, Box<dyn Error>> {
let (accumulator, mut buffer_after_pbs) = self.memory.as_buffers(server_key);
let fourier_bsk = &server_key.bootstrapping_key;
let fft = Fft::new(fourier_bsk.polynomial_size());
let fft = fft.as_view();
self.computation_buffers.resize(
programmable_bootstrap_lwe_ciphertext_mem_optimized_scratch::<u64>(
fourier_bsk.glwe_size(),
fourier_bsk.polynomial_size(),
fft,
)
.unwrap()
.unaligned_bytes_required(),
);
let stack = self.computation_buffers.stack();
programmable_bootstrap_lwe_ciphertext_mem_optimized(
input,
&mut buffer_after_pbs,
&accumulator,
fourier_bsk,
fft,
stack,
);
Ok(LweCiphertext::from_container(
buffer_after_pbs.as_ref().to_owned(),
))
}
pub(crate) fn keyswitch(
&mut self,
input: &LweCiphertextOwned<u32>,
server_key: &ServerKey,
) -> Result<LweCiphertextOwned<u32>, Box<dyn Error>> {
// Allocate the output of the KS
let mut output = LweCiphertext::new(
0u32,
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
);
keyswitch_lwe_ciphertext(&server_key.key_switching_key, input, &mut output);
Ok(output)
}
pub(crate) fn bootstrap_keyswitch(
&mut self,
mut ciphertext: LweCiphertextOwned<u32>,
server_key: &ServerKey,
) -> Result<Ciphertext, Box<dyn Error>> {
let (accumulator, mut buffer_lwe_after_pbs) = self.memory.as_buffers(server_key);
let fourier_bsk = &server_key.bootstrapping_key;
let fft = Fft::new(fourier_bsk.polynomial_size());
let fft = fft.as_view();
self.computation_buffers.resize(
programmable_bootstrap_lwe_ciphertext_mem_optimized_scratch::<u64>(
fourier_bsk.glwe_size(),
fourier_bsk.polynomial_size(),
fft,
)
.unwrap()
.unaligned_bytes_required(),
);
let stack = self.computation_buffers.stack();
// Compute a bootstrap
programmable_bootstrap_lwe_ciphertext_mem_optimized(
&ciphertext,
&mut buffer_lwe_after_pbs,
&accumulator,
fourier_bsk,
fft,
stack,
);
// Compute a key switch to get back to input key
keyswitch_lwe_ciphertext(
&server_key.key_switching_key,
&buffer_lwe_after_pbs,
&mut ciphertext,
);
Ok(Ciphertext::Encrypted(ciphertext))
}
}
#[derive(Serialize, Deserialize)]
struct SerializableServerKey {
pub bootstrapping_key: Vec<u8>,
pub key_switching_key: Vec<u8>,
}
impl Serialize for ServerKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let key_switching_key =
bincode::serialize(&self.key_switching_key).map_err(serde::ser::Error::custom)?;
let bootstrapping_key =
bincode::serialize(&self.bootstrapping_key).map_err(serde::ser::Error::custom)?;
SerializableServerKey {
key_switching_key,
bootstrapping_key,
}
.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for ServerKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let thing =
SerializableServerKey::deserialize(deserializer).map_err(serde::de::Error::custom)?;
let key_switching_key = bincode::deserialize(thing.key_switching_key.as_slice())
.map_err(serde::de::Error::custom)?;
let bootstrapping_key = bincode::deserialize(thing.bootstrapping_key.as_slice())
.map_err(serde::de::Error::custom)?;
Ok(Self {
bootstrapping_key,
key_switching_key,
})
}
}

View File

@@ -1,317 +0,0 @@
use crate::boolean::ciphertext::Ciphertext;
use crate::boolean::{ClientKey, PLAINTEXT_TRUE};
use crate::core_crypto::prelude::*;
use crate::seeders::new_seeder;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::error::Error;
use super::{BooleanServerKey, Bootstrapper};
/// Memory used as buffer for the bootstrap
///
/// It contains contiguous chunk which is then sliced and converted
/// into core's View types.
#[derive(Default)]
struct Memory {
elements: Vec<u32>,
}
impl Memory {
/// Returns a tuple with buffers that matches the server key.
///
/// - The first element is the accumulator for bootstrap step.
/// - The second element is a lwe buffer where the result of the of the bootstrap should be
/// written
fn as_buffers(
&mut self,
engine: &mut DefaultEngine,
server_key: &CpuBootstrapKey,
) -> (GlweCiphertextView32, LweCiphertextMutView32) {
let num_elem_in_accumulator = server_key
.bootstrapping_key
.glwe_dimension()
.to_glwe_size()
.0
* server_key.bootstrapping_key.polynomial_size().0;
let num_elem_in_lwe = server_key
.bootstrapping_key
.output_lwe_dimension()
.to_lwe_size()
.0;
let total_elem_needed = num_elem_in_accumulator + num_elem_in_lwe;
let all_elements = if self.elements.len() < total_elem_needed {
self.elements.resize(total_elem_needed, 0u32);
self.elements.as_mut_slice()
} else {
&mut self.elements[..total_elem_needed]
};
let (accumulator_elements, lwe_elements) =
all_elements.split_at_mut(num_elem_in_accumulator);
accumulator_elements
[num_elem_in_accumulator - server_key.bootstrapping_key.polynomial_size().0..]
.fill(PLAINTEXT_TRUE);
let accumulator = engine
.create_glwe_ciphertext_from(
&*accumulator_elements,
server_key.bootstrapping_key.polynomial_size(),
)
.unwrap();
let lwe = engine.create_lwe_ciphertext_from(lwe_elements).unwrap();
(accumulator, lwe)
}
}
/// A structure containing the server public key.
///
/// This server key data lives on the CPU.
///
/// The server key is generated by the client and is meant to be published: the client
/// sends it to the server so it can compute homomorphic Boolean circuits.
///
/// In more details, it contains:
/// * `bootstrapping_key` - a public key, used to perform the bootstrapping operation.
/// * `key_switching_key` - a public key, used to perform the key-switching operation.
#[derive(Clone)]
pub struct CpuBootstrapKey {
pub(super) standard_bootstraping_key: LweBootstrapKey32,
pub(super) bootstrapping_key: FftFourierLweBootstrapKey32,
pub(super) key_switching_key: LweKeyswitchKey32,
}
impl CpuBootstrapKey {}
impl BooleanServerKey for CpuBootstrapKey {
fn lwe_size(&self) -> LweSize {
self.bootstrapping_key.input_lwe_dimension().to_lwe_size()
}
}
/// Performs ciphertext bootstraps on the CPU
pub(crate) struct CpuBootstrapper {
memory: Memory,
engine: DefaultEngine,
fourier_engine: FftEngine,
}
impl CpuBootstrapper {
pub(crate) fn new_server_key(
&mut self,
cks: &ClientKey,
) -> Result<CpuBootstrapKey, Box<dyn std::error::Error>> {
// convert into a variance for rlwe context
let var_rlwe = Variance(cks.parameters.glwe_modular_std_dev.get_variance());
// creation of the bootstrapping key in the Fourier domain
let standard_bootstraping_key: LweBootstrapKey32 =
self.engine.generate_new_lwe_bootstrap_key(
&cks.lwe_secret_key,
&cks.glwe_secret_key,
cks.parameters.pbs_base_log,
cks.parameters.pbs_level,
var_rlwe,
)?;
let fourier_bsk = self
.fourier_engine
.convert_lwe_bootstrap_key(&standard_bootstraping_key)?;
// Convert the GLWE secret key into an LWE secret key:
let big_lwe_secret_key = self
.engine
.transform_glwe_secret_key_to_lwe_secret_key(cks.glwe_secret_key.clone())?;
// convert into a variance for lwe context
let var_lwe = Variance(cks.parameters.lwe_modular_std_dev.get_variance());
// creation of the key switching key
let ksk = self.engine.generate_new_lwe_keyswitch_key(
&big_lwe_secret_key,
&cks.lwe_secret_key,
cks.parameters.ks_level,
cks.parameters.ks_base_log,
var_lwe,
)?;
Ok(CpuBootstrapKey {
standard_bootstraping_key,
bootstrapping_key: fourier_bsk,
key_switching_key: ksk,
})
}
}
impl Default for CpuBootstrapper {
fn default() -> Self {
let engine =
DefaultEngine::new(new_seeder()).expect("Unexpectedly failed to create a core engine");
let fourier_engine = FftEngine::new(()).unwrap();
Self {
memory: Default::default(),
engine,
fourier_engine,
}
}
}
impl Bootstrapper for CpuBootstrapper {
type ServerKey = CpuBootstrapKey;
fn bootstrap(
&mut self,
input: &LweCiphertext32,
server_key: &Self::ServerKey,
) -> Result<LweCiphertext32, Box<dyn Error>> {
let (accumulator, mut buffer_lwe_after_pbs) =
self.memory.as_buffers(&mut self.engine, server_key);
// Need to be able to get view from &Lwe
let inner_data = self
.engine
.consume_retrieve_lwe_ciphertext(input.clone())
.unwrap();
let input = self
.engine
.create_lwe_ciphertext_from(inner_data.as_slice())
.unwrap();
self.fourier_engine.discard_bootstrap_lwe_ciphertext(
&mut buffer_lwe_after_pbs,
&input,
&accumulator,
&server_key.bootstrapping_key,
)?;
let data = self
.engine
.consume_retrieve_lwe_ciphertext(buffer_lwe_after_pbs)
.unwrap()
.to_vec();
let ct = self.engine.create_lwe_ciphertext_from(data)?;
Ok(ct)
}
fn keyswitch(
&mut self,
input: &LweCiphertext32,
server_key: &Self::ServerKey,
) -> Result<LweCiphertext32, Box<dyn Error>> {
// Allocate the output of the KS
let mut ct_ks = self
.engine
.create_lwe_ciphertext_from(vec![0u32; server_key.lwe_size().0])?;
self.engine.discard_keyswitch_lwe_ciphertext(
&mut ct_ks,
input,
&server_key.key_switching_key,
)?;
Ok(ct_ks)
}
fn bootstrap_keyswitch(
&mut self,
ciphertext: LweCiphertext32,
server_key: &Self::ServerKey,
) -> Result<Ciphertext, Box<dyn Error>> {
let (accumulator, mut buffer_lwe_after_pbs) =
self.memory.as_buffers(&mut self.engine, server_key);
let mut inner_data = self
.engine
.consume_retrieve_lwe_ciphertext(ciphertext)
.unwrap();
let input_view = self
.engine
.create_lwe_ciphertext_from(inner_data.as_slice())?;
self.fourier_engine.discard_bootstrap_lwe_ciphertext(
&mut buffer_lwe_after_pbs,
&input_view,
&accumulator,
&server_key.bootstrapping_key,
)?;
// Convert from a mut view to a view
let slice = self
.engine
.consume_retrieve_lwe_ciphertext(buffer_lwe_after_pbs)
.unwrap();
let buffer_lwe_after_pbs = self.engine.create_lwe_ciphertext_from(&*slice)?;
let mut output_view = self
.engine
.create_lwe_ciphertext_from(inner_data.as_mut_slice())?;
// Compute a key switch to get back to input key
self.engine.discard_keyswitch_lwe_ciphertext(
&mut output_view,
&buffer_lwe_after_pbs,
&server_key.key_switching_key,
)?;
let ciphertext = self.engine.create_lwe_ciphertext_from(inner_data)?;
Ok(Ciphertext::Encrypted(ciphertext))
}
}
#[derive(Serialize, Deserialize)]
struct SerializableCpuServerKey {
pub standard_bootstraping_key: Vec<u8>,
pub key_switching_key: Vec<u8>,
}
impl Serialize for CpuBootstrapKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut ser_eng = DefaultSerializationEngine::new(()).map_err(serde::ser::Error::custom)?;
let key_switching_key = ser_eng
.serialize(&self.key_switching_key)
.map_err(serde::ser::Error::custom)?;
let standard_bootstraping_key = ser_eng
.serialize(&self.standard_bootstraping_key)
.map_err(serde::ser::Error::custom)?;
SerializableCpuServerKey {
key_switching_key,
standard_bootstraping_key,
}
.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for CpuBootstrapKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let thing = SerializableCpuServerKey::deserialize(deserializer)
.map_err(serde::de::Error::custom)?;
let mut ser_eng = DefaultSerializationEngine::new(()).map_err(serde::de::Error::custom)?;
let key_switching_key = ser_eng
.deserialize(thing.key_switching_key.as_slice())
.map_err(serde::de::Error::custom)?;
let standard_bootstraping_key = ser_eng
.deserialize(thing.standard_bootstraping_key.as_slice())
.map_err(serde::de::Error::custom)?;
let bootstrapping_key = FftEngine::new(())
.unwrap()
.convert_lwe_bootstrap_key(&standard_bootstraping_key)
.map_err(serde::de::Error::custom)?;
Ok(Self {
standard_bootstraping_key,
key_switching_key,
bootstrapping_key,
})
}
}

View File

@@ -1,240 +0,0 @@
use super::{BooleanServerKey, Bootstrapper, CpuBootstrapKey};
use crate::boolean::PLAINTEXT_TRUE;
use crate::core_crypto::prelude::*;
use crate::seeders::new_seeder;
use std::collections::BTreeMap;
use crate::boolean::ciphertext::Ciphertext;
pub(crate) struct CudaBootstrapKey {
bootstrapping_key: CudaFourierLweBootstrapKey32,
key_switching_key: CudaLweKeyswitchKey32,
}
impl BooleanServerKey for CudaBootstrapKey {
fn lwe_size(&self) -> LweSize {
self.bootstrapping_key.input_lwe_dimension().to_lwe_size()
}
}
#[derive(PartialOrd, PartialEq, Ord, Eq)]
struct KeyId {
// Both of these are for the accumulator
glwe_dimension: GlweDimension,
polynomial_size: PolynomialSize,
lwe_dimension_after_bootstrap: LweDimension,
}
#[derive(Default)]
struct CudaMemory {
cuda_buffers: BTreeMap<KeyId, CudaBuffers>,
}
/// All the buffers needed to do a bootstrap or a keyswitch or bootstrap + keyswitch
struct CudaBuffers {
accumulator: CudaGlweCiphertext32,
// Its size is the one of a ciphertext after pbs
lwe_after_bootstrap: CudaLweCiphertext32,
// Its size is the one of a ciphertext after a keyswitch
// ie the size of a ciphertext before the bootstrap
lwe_after_keyswitch: CudaLweCiphertext32,
}
impl CudaMemory {
/// Returns the buffers that matches the given key.
fn as_buffers_for_key(
&mut self,
cpu_engine: &mut DefaultEngine,
cuda_engine: &mut CudaEngine,
server_key: &CudaBootstrapKey,
) -> &mut CudaBuffers {
let key_id = KeyId {
glwe_dimension: server_key.bootstrapping_key.glwe_dimension(),
polynomial_size: server_key.bootstrapping_key.polynomial_size(),
lwe_dimension_after_bootstrap: server_key.bootstrapping_key.output_lwe_dimension(),
};
self.cuda_buffers.entry(key_id).or_insert_with(|| {
let output_lwe_size = server_key
.bootstrapping_key
.output_lwe_dimension()
.to_lwe_size();
let output_ciphertext = cpu_engine
.create_lwe_ciphertext_from(vec![0u32; output_lwe_size.0])
.unwrap();
let cuda_lwe_after_bootstrap = cuda_engine
.convert_lwe_ciphertext(&output_ciphertext)
.unwrap();
let num_elements = server_key
.bootstrapping_key
.glwe_dimension()
.to_glwe_size()
.0
* server_key.bootstrapping_key.polynomial_size().0;
let mut acc = vec![0u32; num_elements];
acc[num_elements - server_key.bootstrapping_key.polynomial_size().0..]
.fill(PLAINTEXT_TRUE);
let accumulator = cpu_engine
.create_glwe_ciphertext_from(acc, server_key.bootstrapping_key.polynomial_size())
.unwrap();
let cuda_accumulator = cuda_engine.convert_glwe_ciphertext(&accumulator).unwrap();
let lwe_size_after_keyswitch = server_key
.key_switching_key
.output_lwe_dimension()
.to_lwe_size();
let output_ciphertext = cpu_engine
.create_lwe_ciphertext_from(vec![0u32; lwe_size_after_keyswitch.0])
.unwrap();
let cuda_lwe_after_keyswitch = cuda_engine
.convert_lwe_ciphertext(&output_ciphertext)
.unwrap();
CudaBuffers {
accumulator: cuda_accumulator,
lwe_after_bootstrap: cuda_lwe_after_bootstrap,
lwe_after_keyswitch: cuda_lwe_after_keyswitch,
}
})
}
}
pub(crate) struct CudaBootstrapper {
cuda_engine: CudaEngine,
cpu_engine: DefaultEngine,
memory: CudaMemory,
}
impl Default for CudaBootstrapper {
fn default() -> Self {
Self {
cuda_engine: CudaEngine::new(()).unwrap(),
// Secret does not matter, we won't generate keys or ciphertext.
cpu_engine: DefaultEngine::new(new_seeder()).unwrap(),
memory: Default::default(),
}
}
}
impl CudaBootstrapper {
pub(crate) fn new_serverk_key(
&mut self,
server_key: &CpuBootstrapKey,
) -> Result<CudaBootstrapKey, Box<dyn std::error::Error>> {
let bootstrapping_key = self
.cuda_engine
.convert_lwe_bootstrap_key(&server_key.standard_bootstraping_key)?;
let key_switching_key = self
.cuda_engine
.convert_lwe_keyswitch_key(&server_key.key_switching_key)?;
Ok(CudaBootstrapKey {
bootstrapping_key,
key_switching_key,
})
}
}
impl Bootstrapper for CudaBootstrapper {
type ServerKey = CudaBootstrapKey;
fn bootstrap(
&mut self,
input: &LweCiphertext32,
server_key: &Self::ServerKey,
) -> Result<LweCiphertext32, Box<dyn std::error::Error>> {
let cuda_buffers =
self.memory
.as_buffers_for_key(&mut self.cpu_engine, &mut self.cuda_engine, server_key);
// The output size of keyswitch is the one of regular boolean ciphertext
// so we can use lwe_after_keyswitch
self.cuda_engine
.discard_convert_lwe_ciphertext(&mut cuda_buffers.lwe_after_keyswitch, input)?;
self.cuda_engine.discard_bootstrap_lwe_ciphertext(
&mut cuda_buffers.lwe_after_bootstrap,
&cuda_buffers.lwe_after_keyswitch,
&cuda_buffers.accumulator,
&server_key.bootstrapping_key,
)?;
let output_ciphertext = self
.cuda_engine
.convert_lwe_ciphertext(&cuda_buffers.lwe_after_bootstrap)?;
Ok(output_ciphertext)
}
fn keyswitch(
&mut self,
input: &LweCiphertext32,
server_key: &Self::ServerKey,
) -> Result<LweCiphertext32, Box<dyn std::error::Error>> {
let cuda_buffers =
self.memory
.as_buffers_for_key(&mut self.cpu_engine, &mut self.cuda_engine, server_key);
// The input of the function we implement must be a ciphertext that result of a bootstrap
// so we can discard convert in the lwe ciphertext after bootstrap
self.cuda_engine
.discard_convert_lwe_ciphertext(&mut cuda_buffers.lwe_after_bootstrap, input)?;
self.cuda_engine.discard_keyswitch_lwe_ciphertext(
&mut cuda_buffers.lwe_after_keyswitch,
&cuda_buffers.lwe_after_bootstrap,
&server_key.key_switching_key,
)?;
let output_ciphertext = self
.cuda_engine
.convert_lwe_ciphertext(&cuda_buffers.lwe_after_keyswitch)?;
Ok(output_ciphertext)
}
fn bootstrap_keyswitch(
&mut self,
ciphertext: LweCiphertext32,
server_key: &Self::ServerKey,
) -> Result<Ciphertext, Box<dyn std::error::Error>> {
// We re-implement instead of calling our bootstrap and then keyswitch fn
// to avoid one extra conversion / copy cpu <-> gpu
let cuda_buffers =
self.memory
.as_buffers_for_key(&mut self.cpu_engine, &mut self.cuda_engine, server_key);
// The output size of keyswitch is the one of regular boolean ciphertext
// so we can use it
self.cuda_engine
.discard_convert_lwe_ciphertext(&mut cuda_buffers.lwe_after_keyswitch, &ciphertext)?;
self.cuda_engine.discard_bootstrap_lwe_ciphertext(
&mut cuda_buffers.lwe_after_bootstrap,
&cuda_buffers.lwe_after_keyswitch,
&cuda_buffers.accumulator,
&server_key.bootstrapping_key,
)?;
self.cuda_engine.discard_keyswitch_lwe_ciphertext(
&mut cuda_buffers.lwe_after_keyswitch,
&cuda_buffers.lwe_after_bootstrap,
&server_key.key_switching_key,
)?;
// We write the result from gpu to cpu avoiding an extra allocation
let mut data = self
.cpu_engine
.consume_retrieve_lwe_ciphertext(ciphertext)?;
let mut view = self
.cpu_engine
.create_lwe_ciphertext_from(data.as_mut_slice())?;
self.cuda_engine
.discard_convert_lwe_ciphertext(&mut view, &cuda_buffers.lwe_after_keyswitch)?;
let output_ciphertext = self.cpu_engine.create_lwe_ciphertext_from(data)?;
Ok(Ciphertext::Encrypted(output_ciphertext))
}
}

View File

@@ -1,48 +0,0 @@
use crate::boolean::ciphertext::Ciphertext;
use crate::core_crypto::prelude::{LweCiphertext32, LweSize};
mod cpu;
#[cfg(feature = "cuda")]
mod cuda;
#[cfg(feature = "cuda")]
pub(crate) use cuda::{CudaBootstrapKey, CudaBootstrapper};
pub(crate) use cpu::{CpuBootstrapKey, CpuBootstrapper};
pub trait BooleanServerKey {
/// The LweSize of the Ciphertexts that this key can bootstrap
fn lwe_size(&self) -> LweSize;
}
/// Trait for types which implement the bootstrapping + key switching
/// of a ciphertext.
///
/// Meant to be implemented for different hardware (CPU, GPU) or for other bootstrapping
/// technics.
pub(crate) trait Bootstrapper: Default {
type ServerKey: BooleanServerKey;
/// Shall return the result of the bootstrapping of the
/// input ciphertext or an error if any
fn bootstrap(
&mut self,
input: &LweCiphertext32,
server_key: &Self::ServerKey,
) -> Result<LweCiphertext32, Box<dyn std::error::Error>>;
/// Shall return the result of the key switching of the
/// input ciphertext or an error if any
fn keyswitch(
&mut self,
input: &LweCiphertext32,
server_key: &Self::ServerKey,
) -> Result<LweCiphertext32, Box<dyn std::error::Error>>;
/// Shall do the bootstrapping and key switching of the ciphertext.
/// The result is returned as a new value.
fn bootstrap_keyswitch(
&mut self,
ciphertext: LweCiphertext32,
server_key: &Self::ServerKey,
) -> Result<Ciphertext, Box<dyn std::error::Error>>;
}

View File

@@ -1,18 +1,18 @@
use crate::boolean::ciphertext::Ciphertext;
use crate::boolean::parameters::BooleanParameters;
use crate::boolean::{ClientKey, PublicKey, PLAINTEXT_FALSE, PLAINTEXT_TRUE};
use crate::core_crypto::prelude::*;
use bootstrapping::{BooleanServerKey, Bootstrapper, CpuBootstrapper};
use crate::core_crypto::algorithms::*;
use crate::core_crypto::entities::*;
use std::cell::RefCell;
pub mod bootstrapping;
use crate::boolean::engine::bootstrapping::CpuBootstrapKey;
use crate::core_crypto::backends::default::engines::ActivatedRandomGenerator;
use crate::core_crypto::commons::crypto::secret::generators::DeterministicSeeder;
use crate::boolean::engine::bootstrapping::{Bootstrapper, ServerKey};
use crate::core_crypto::commons::generators::{
DeterministicSeeder, EncryptionRandomGenerator, SecretRandomGenerator,
};
use crate::core_crypto::commons::math::random::{ActivatedRandomGenerator, Seeder};
use crate::core_crypto::commons::parameters::*;
use crate::seeders::new_seeder;
#[cfg(feature = "cuda")]
use bootstrapping::{CudaBootstrapKey, CudaBootstrapper};
pub(crate) trait BinaryGatesEngine<L, R, K> {
fn and(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
fn nand(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
@@ -39,75 +39,51 @@ pub(crate) trait WithThreadLocalEngine {
F: FnOnce(&mut Self) -> R;
}
pub(crate) type CpuBooleanEngine = BooleanEngine<CpuBootstrapper>;
#[cfg(feature = "cuda")]
pub(crate) type CudaBooleanEngine = BooleanEngine<CudaBootstrapper>;
// All our thread local engines
// that our exposed types will use internally to implement their methods
thread_local! {
static CPU_ENGINE: RefCell<BooleanEngine<CpuBootstrapper>> = RefCell::new(BooleanEngine::<_>::new());
#[cfg(feature = "cuda")]
static CUDA_ENGINE: RefCell<BooleanEngine<CudaBootstrapper>> = RefCell::new(BooleanEngine::<_>::new());
static BOOLEAN_ENGINE: RefCell<BooleanEngine> = RefCell::new(BooleanEngine::new());
}
impl WithThreadLocalEngine for CpuBooleanEngine {
pub struct BooleanEngine {
/// A structure containing a single CSPRNG to generate secret key coefficients.
secret_generator: SecretRandomGenerator<ActivatedRandomGenerator>,
/// A structure containing two CSPRNGs to generate material for encryption like public masks
/// and secret errors.
///
/// The [`EncryptionRandomGenerator`] contains two CSPRNGs, one publicly seeded used to
/// generate mask coefficients and one privately seeded used to generate errors during
/// encryption.
encryption_generator: EncryptionRandomGenerator<ActivatedRandomGenerator>,
bootstrapper: Bootstrapper,
}
impl WithThreadLocalEngine for BooleanEngine {
fn with_thread_local_mut<R, F>(func: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
CPU_ENGINE.with(|engine_cell| func(&mut engine_cell.borrow_mut()))
}
}
#[cfg(feature = "cuda")]
impl WithThreadLocalEngine for CudaBooleanEngine {
fn with_thread_local_mut<R, F>(func: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
CUDA_ENGINE.with(|engine_cell| func(&mut engine_cell.borrow_mut()))
}
}
pub(crate) struct BooleanEngine<B> {
pub(crate) engine: DefaultEngine,
bootstrapper: B,
}
impl BooleanEngine<CpuBootstrapper> {
pub fn create_server_key(&mut self, cks: &ClientKey) -> CpuBootstrapKey {
let server_key = self.bootstrapper.new_server_key(cks).unwrap();
server_key
}
}
#[cfg(feature = "cuda")]
impl BooleanEngine<CudaBootstrapper> {
pub fn create_server_key(&mut self, cpu_key: &CpuBootstrapKey) -> CudaBootstrapKey {
let server_key = self.bootstrapper.new_serverk_key(cpu_key).unwrap();
server_key
BOOLEAN_ENGINE.with(|engine_cell| func(&mut engine_cell.borrow_mut()))
}
}
// We have q = 2^32 so log2q = 32
const LOG2_Q_32: usize = 32;
impl<B> BooleanEngine<B> {
impl BooleanEngine {
pub fn create_client_key(&mut self, parameters: BooleanParameters) -> ClientKey {
// generate the lwe secret key
let lwe_secret_key: LweSecretKey32 = self
.engine
.generate_new_lwe_secret_key(parameters.lwe_dimension)
.unwrap();
let lwe_secret_key = allocate_and_generate_new_binary_lwe_secret_key(
parameters.lwe_dimension,
&mut self.secret_generator,
);
// generate the rlwe secret key
let glwe_secret_key: GlweSecretKey32 = self
.engine
.generate_new_glwe_secret_key(parameters.glwe_dimension, parameters.polynomial_size)
.unwrap();
// generate the glwe secret key
let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
parameters.glwe_dimension,
parameters.polynomial_size,
&mut self.secret_generator,
);
ClientKey {
lwe_secret_key,
@@ -116,6 +92,12 @@ impl<B> BooleanEngine<B> {
}
}
pub fn create_server_key(&mut self, cks: &ClientKey) -> ServerKey {
let server_key = self.bootstrapper.new_server_key(cks).unwrap();
server_key
}
pub fn create_public_key(&mut self, client_key: &ClientKey) -> PublicKey {
let client_parameters = client_key.parameters;
@@ -124,15 +106,15 @@ impl<B> BooleanEngine<B> {
client_parameters.lwe_dimension.to_lwe_size().0 * LOG2_Q_32 + 128,
);
let lwe_public_key: LwePublicKeyOwned<u32> = par_allocate_and_generate_new_lwe_public_key(
&client_key.lwe_secret_key,
zero_encryption_count,
client_key.parameters.lwe_modular_std_dev,
&mut self.encryption_generator,
);
PublicKey {
lwe_public_key: self
.engine
.generate_new_lwe_public_key(
&client_key.lwe_secret_key,
Variance(client_key.parameters.lwe_modular_std_dev.get_variance()),
zero_encryption_count,
)
.unwrap(),
lwe_public_key,
parameters: client_key.parameters.to_owned(),
}
}
@@ -143,47 +125,42 @@ impl<B> BooleanEngine<B> {
pub fn encrypt(&mut self, message: bool, cks: &ClientKey) -> Ciphertext {
// encode the boolean message
let plain: Plaintext32 = if message {
self.engine.create_plaintext_from(&PLAINTEXT_TRUE).unwrap()
let plain: Plaintext<u32> = if message {
Plaintext(PLAINTEXT_TRUE)
} else {
self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap()
Plaintext(PLAINTEXT_FALSE)
};
// convert into a variance
let var = Variance(cks.parameters.lwe_modular_std_dev.get_variance());
// encryption
let ct = self
.engine
.encrypt_lwe_ciphertext(&cks.lwe_secret_key, &plain, var)
.unwrap();
let ct = allocate_and_encrypt_new_lwe_ciphertext(
&cks.lwe_secret_key,
plain,
cks.parameters.lwe_modular_std_dev,
&mut self.encryption_generator,
);
Ciphertext::Encrypted(ct)
}
pub fn encrypt_with_public_key(&mut self, message: bool, pks: &PublicKey) -> Ciphertext {
// encode the boolean message
let plain: Plaintext32 = if message {
self.engine.create_plaintext_from(&PLAINTEXT_TRUE).unwrap()
let plain: Plaintext<u32> = if message {
Plaintext(PLAINTEXT_TRUE)
} else {
self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap()
Plaintext(PLAINTEXT_FALSE)
};
let mut underlying_ciphertext = self
.engine
.create_lwe_ciphertext_from(vec![0u32; pks.parameters.lwe_dimension.to_lwe_size().0])
.unwrap();
let mut output = LweCiphertext::new(0u32, pks.parameters.lwe_dimension.to_lwe_size());
// encryption
self.engine
.discard_encrypt_lwe_ciphertext_with_public_key(
&pks.lwe_public_key,
&mut underlying_ciphertext,
&plain,
)
.unwrap();
encrypt_lwe_ciphertext_with_public_key(
&pks.lwe_public_key,
&mut output,
plain,
&mut self.secret_generator,
);
Ciphertext::Encrypted(underlying_ciphertext)
Ciphertext::Encrypted(output)
}
pub fn decrypt(&mut self, ct: &Ciphertext, cks: &ClientKey) -> bool {
@@ -191,16 +168,10 @@ impl<B> BooleanEngine<B> {
Ciphertext::Trivial(b) => *b,
Ciphertext::Encrypted(ciphertext) => {
// decryption
let decrypted = self
.engine
.decrypt_lwe_ciphertext(&cks.lwe_secret_key, ciphertext)
.unwrap();
let decrypted = decrypt_lwe_ciphertext(&cks.lwe_secret_key, ciphertext);
// cast as a u32
let mut decrypted_u32: u32 = 0;
self.engine
.discard_retrieve_plaintext(&mut decrypted_u32, &decrypted)
.unwrap();
let decrypted_u32 = decrypted.0;
// return
decrypted_u32 < (1 << 31)
@@ -214,7 +185,7 @@ impl<B> BooleanEngine<B> {
Ciphertext::Encrypted(ct_ct) => {
// Compute the linear combination for NOT: -ct
let mut ct_res = ct_ct.clone();
self.engine.fuse_opp_lwe_ciphertext(&mut ct_res).unwrap(); // compute the negation
lwe_ciphertext_opposite_assign(&mut ct_res);
// Output the result:
Ciphertext::Encrypted(ct_res)
@@ -226,35 +197,38 @@ impl<B> BooleanEngine<B> {
match ct {
Ciphertext::Trivial(message) => *message = !*message,
Ciphertext::Encrypted(ct_ct) => {
self.engine.fuse_opp_lwe_ciphertext(ct_ct).unwrap(); // compute the negation
lwe_ciphertext_opposite_assign(ct_ct); // compute the negation
}
}
}
}
impl<B> BooleanEngine<B>
where
B: Bootstrapper,
{
pub fn new() -> Self {
let root_seeder = new_seeder();
impl Default for BooleanEngine {
fn default() -> Self {
Self::new()
}
}
Self::new_from_seeder(root_seeder)
impl BooleanEngine {
pub fn new() -> Self {
let mut root_seeder = new_seeder();
Self::new_from_seeder(root_seeder.as_mut())
}
pub fn new_from_seeder(mut root_seeder: Box<dyn Seeder>) -> Self {
pub fn new_from_seeder(root_seeder: &mut dyn Seeder) -> Self {
let mut deterministic_seeder =
DeterministicSeeder::<ActivatedRandomGenerator>::new(root_seeder.seed());
let default_engine_seeder = Box::new(DeterministicSeeder::<ActivatedRandomGenerator>::new(
deterministic_seeder.seed(),
));
let engine =
DefaultEngine::new(default_engine_seeder).expect("Failed to create a DefaultEngine");
// Note that the operands are evaluated from left to right for Rust Struct expressions
// See: https://doc.rust-lang.org/stable/reference/expressions.html?highlight=left#evaluation-order-of-operands
Self {
engine,
bootstrapper: Default::default(),
secret_generator: SecretRandomGenerator::<_>::new(deterministic_seeder.seed()),
encryption_generator: EncryptionRandomGenerator::<_>::new(
deterministic_seeder.seed(),
&mut deterministic_seeder,
),
bootstrapper: Bootstrapper::new(&mut deterministic_seeder),
}
}
@@ -262,20 +236,24 @@ where
fn convert_into_lwe_ciphertext_32(
&mut self,
ct: &Ciphertext,
server_key: &B::ServerKey,
) -> LweCiphertext32 {
server_key: &ServerKey,
) -> LweCiphertextOwned<u32> {
match ct {
Ciphertext::Encrypted(ct_ct) => ct_ct.clone(),
Ciphertext::Trivial(message) => {
// encode the boolean message
let plain: Plaintext32 = if *message {
self.engine.create_plaintext_from(&PLAINTEXT_TRUE).unwrap()
let plain: Plaintext<u32> = if *message {
Plaintext(PLAINTEXT_TRUE)
} else {
self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap()
Plaintext(PLAINTEXT_FALSE)
};
self.engine
.trivially_encrypt_lwe_ciphertext(server_key.lwe_size(), &plain)
.unwrap()
allocate_and_trivially_encrypt_new_lwe_ciphertext(
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
plain,
)
}
}
}
@@ -285,7 +263,7 @@ where
ct_condition: &Ciphertext,
ct_then: &Ciphertext,
ct_else: &Ciphertext,
server_key: &B::ServerKey,
server_key: &ServerKey,
) -> Ciphertext {
// In theory MUX gate = (ct_condition AND ct_then) + (!ct_condition AND ct_else)
@@ -325,36 +303,30 @@ where
let ct_then_ct = self.convert_into_lwe_ciphertext_32(ct_then, server_key);
let ct_else_ct = self.convert_into_lwe_ciphertext_32(ct_else, server_key);
let mut buffer_lwe_before_pbs_o = self
.engine
.create_lwe_ciphertext_from(vec![0u32; server_key.lwe_size().0])
.unwrap();
let mut buffer_lwe_before_pbs_o = LweCiphertext::new(
0u32,
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
);
let buffer_lwe_before_pbs = &mut buffer_lwe_before_pbs_o;
let bootstrapper = &mut self.bootstrapper;
// Compute the linear combination for first AND: ct_condition + ct_then +
// (0,...,0,-1/8)
self.engine
.discard_add_lwe_ciphertext(buffer_lwe_before_pbs, ct_condition_ct, &ct_then_ct)
.unwrap(); // ct_condition + ct_then
let cst = self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(buffer_lwe_before_pbs, &cst)
.unwrap(); //
// - 1/8
lwe_ciphertext_add(buffer_lwe_before_pbs, ct_condition_ct, &ct_then_ct);
let cst = Plaintext(PLAINTEXT_FALSE);
lwe_ciphertext_plaintext_add_assign(buffer_lwe_before_pbs, cst); // - 1/8
// Compute the linear combination for second AND: - ct_condition + ct_else +
// (0,...,0,-1/8)
let mut ct_temp_2 = ct_condition_ct.clone(); // ct_condition
self.engine.fuse_opp_lwe_ciphertext(&mut ct_temp_2).unwrap(); // compute the negation
self.engine
.fuse_add_lwe_ciphertext(&mut ct_temp_2, &ct_else_ct)
.unwrap(); // + ct_else
let cst = self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut ct_temp_2, &cst)
.unwrap(); //
// - 1/8
lwe_ciphertext_opposite_assign(&mut ct_temp_2); // compute the negation
lwe_ciphertext_add_assign(&mut ct_temp_2, &ct_else_ct); // + ct_else
let cst = Plaintext(PLAINTEXT_FALSE);
lwe_ciphertext_plaintext_add_assign(&mut ct_temp_2, cst); // - 1/8
// Compute the first programmable bootstrapping with fixed test polynomial:
let mut ct_pbs_1 = bootstrapper
@@ -365,13 +337,9 @@ where
// Compute the linear combination to add the two results:
// buffer_lwe_pbs + ct_pbs_2 + (0,...,0, +1/8)
self.engine
.fuse_add_lwe_ciphertext(&mut ct_pbs_1, &ct_pbs_2)
.unwrap(); // + buffer_lwe_pbs
let cst = self.engine.create_plaintext_from(&PLAINTEXT_TRUE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut ct_pbs_1, &cst)
.unwrap(); // + 1/8
lwe_ciphertext_add_assign(&mut ct_pbs_1, &ct_pbs_2); // + buffer_lwe_pbs
let cst = Plaintext(PLAINTEXT_TRUE);
lwe_ciphertext_plaintext_add_assign(&mut ct_pbs_1, cst); // + 1/8
let ct_ks = bootstrapper.keyswitch(&ct_pbs_1, server_key).unwrap();
@@ -382,15 +350,12 @@ where
}
}
impl<B> BinaryGatesEngine<&Ciphertext, &Ciphertext, B::ServerKey> for BooleanEngine<B>
where
B: Bootstrapper,
{
impl BinaryGatesEngine<&Ciphertext, &Ciphertext, ServerKey> for BooleanEngine {
fn and(
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
server_key: &ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
@@ -403,21 +368,22 @@ where
self.and(*message_left, ct_right, server_key)
}
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = self
.engine
.create_lwe_ciphertext_from(vec![0u32; server_key.lwe_size().0])
.unwrap();
let mut buffer_lwe_before_pbs = LweCiphertext::new(
0u32,
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
);
let bootstrapper = &mut self.bootstrapper;
// compute the linear combination for AND: ct_left + ct_right + (0,...,0,-1/8)
self.engine
.discard_add_lwe_ciphertext(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct)
.unwrap(); // ct_left + ct_right
let cst = self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut buffer_lwe_before_pbs, &cst)
.unwrap(); //
// - 1/8
// ct_left + ct_right
lwe_ciphertext_add(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct);
let cst = Plaintext(PLAINTEXT_FALSE);
// - 1/8
lwe_ciphertext_plaintext_add_assign(&mut buffer_lwe_before_pbs, cst);
// compute the bootstrap and the key switch
bootstrapper
@@ -431,7 +397,7 @@ where
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
server_key: &ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
@@ -444,23 +410,22 @@ where
self.nand(*message_left, ct_right, server_key)
}
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = self
.engine
.create_lwe_ciphertext_from(vec![0u32; server_key.lwe_size().0])
.unwrap();
let mut buffer_lwe_before_pbs = LweCiphertext::new(
0u32,
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
);
let bootstrapper = &mut self.bootstrapper;
// Compute the linear combination for NAND: - ct_left - ct_right + (0,...,0,1/8)
self.engine
.discard_add_lwe_ciphertext(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct)
.unwrap(); // ct_left + ct_right
self.engine
.fuse_opp_lwe_ciphertext(&mut buffer_lwe_before_pbs)
.unwrap(); // compute the negation
let cst = self.engine.create_plaintext_from(&PLAINTEXT_TRUE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut buffer_lwe_before_pbs, &cst)
.unwrap(); // + 1/8
// ct_left + ct_right
lwe_ciphertext_add(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct);
lwe_ciphertext_opposite_assign(&mut buffer_lwe_before_pbs);
let cst = Plaintext(PLAINTEXT_TRUE);
// + 1/8
lwe_ciphertext_plaintext_add_assign(&mut buffer_lwe_before_pbs, cst);
// compute the bootstrap and the key switch
bootstrapper
@@ -474,7 +439,7 @@ where
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
server_key: &ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
@@ -487,24 +452,23 @@ where
self.nor(*message_left, ct_right, server_key)
}
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = self
.engine
.create_lwe_ciphertext_from(vec![0u32; server_key.lwe_size().0])
.unwrap();
let mut buffer_lwe_before_pbs = LweCiphertext::new(
0u32,
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
);
let bootstrapper = &mut self.bootstrapper;
// Compute the linear combination for NOR: - ct_left - ct_right + (0,...,0,-1/8)
self.engine
.discard_add_lwe_ciphertext(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct)
.unwrap(); // ct_left + ct_right
self.engine
.fuse_opp_lwe_ciphertext(&mut buffer_lwe_before_pbs)
.unwrap(); // compute the negation
let cst = self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut buffer_lwe_before_pbs, &cst)
.unwrap(); //
// - 1/8
// ct_left + ct_right
lwe_ciphertext_add(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct);
// compute the negation
lwe_ciphertext_opposite_assign(&mut buffer_lwe_before_pbs);
let cst = Plaintext(PLAINTEXT_FALSE);
// - 1/8
lwe_ciphertext_plaintext_add_assign(&mut buffer_lwe_before_pbs, cst);
// compute the bootstrap and the key switch
bootstrapper
@@ -518,7 +482,7 @@ where
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
server_key: &ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
@@ -531,20 +495,21 @@ where
self.or(*message_left, ct_right, server_key)
}
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = self
.engine
.create_lwe_ciphertext_from(vec![0u32; server_key.lwe_size().0])
.unwrap();
let mut buffer_lwe_before_pbs = LweCiphertext::new(
0u32,
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
);
let bootstrapper = &mut self.bootstrapper;
// Compute the linear combination for OR: ct_left + ct_right + (0,...,0,+1/8)
self.engine
.discard_add_lwe_ciphertext(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct)
.unwrap(); // ct_left + ct_right
let cst = self.engine.create_plaintext_from(&PLAINTEXT_TRUE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut buffer_lwe_before_pbs, &cst)
.unwrap(); // + 1/8
// ct_left + ct_right
lwe_ciphertext_add(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct);
let cst = Plaintext(PLAINTEXT_TRUE);
// + 1/8
lwe_ciphertext_plaintext_add_assign(&mut buffer_lwe_before_pbs, cst);
// compute the bootstrap and the key switch
bootstrapper
@@ -558,7 +523,7 @@ where
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
server_key: &ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
@@ -571,24 +536,24 @@ where
self.xor(*message_left, ct_right, server_key)
}
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = self
.engine
.create_lwe_ciphertext_from(vec![0u32; server_key.lwe_size().0])
.unwrap();
let mut buffer_lwe_before_pbs = LweCiphertext::new(
0u32,
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
);
let bootstrapper = &mut self.bootstrapper;
// Compute the linear combination for XOR: 2*(ct_left + ct_right) + (0,...,0,1/4)
self.engine
.discard_add_lwe_ciphertext(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct)
.unwrap(); // ct_left + ct_right
let cst_add = self.engine.create_plaintext_from(&PLAINTEXT_TRUE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut buffer_lwe_before_pbs, &cst_add)
.unwrap(); // + 1/8
let cst_mul = self.engine.create_cleartext_from(&2u32).unwrap();
self.engine
.fuse_mul_lwe_ciphertext_cleartext(&mut buffer_lwe_before_pbs, &cst_mul)
.unwrap(); //* 2
// ct_left + ct_right
lwe_ciphertext_add(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct);
let cst_add = Plaintext(PLAINTEXT_TRUE);
// + 1/8
lwe_ciphertext_plaintext_add_assign(&mut buffer_lwe_before_pbs, cst_add);
let cst_mul = Cleartext(2u32);
//* 2
lwe_ciphertext_cleartext_mul_assign(&mut buffer_lwe_before_pbs, cst_mul);
// compute the bootstrap and the key switch
bootstrapper
@@ -602,7 +567,7 @@ where
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
server_key: &ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
@@ -615,27 +580,26 @@ where
self.xnor(*message_left, ct_right, server_key)
}
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = self
.engine
.create_lwe_ciphertext_from(vec![0u32; server_key.lwe_size().0])
.unwrap();
let mut buffer_lwe_before_pbs = LweCiphertext::new(
0u32,
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
);
let bootstrapper = &mut self.bootstrapper;
// Compute the linear combination for XNOR: 2*(-ct_left - ct_right + (0,...,0,-1/8))
self.engine
.discard_add_lwe_ciphertext(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct)
.unwrap(); // ct_left + ct_right
let cst_add = self.engine.create_plaintext_from(&PLAINTEXT_TRUE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut buffer_lwe_before_pbs, &cst_add)
.unwrap(); // + 1/8
self.engine
.fuse_opp_lwe_ciphertext(&mut buffer_lwe_before_pbs)
.unwrap(); // compute the negation
let cst_mul = self.engine.create_cleartext_from(&2u32).unwrap();
self.engine
.fuse_mul_lwe_ciphertext_cleartext(&mut buffer_lwe_before_pbs, &cst_mul)
.unwrap(); //* 2
// ct_left + ct_right
lwe_ciphertext_add(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct);
let cst_add = Plaintext(PLAINTEXT_TRUE);
// + 1/8
lwe_ciphertext_plaintext_add_assign(&mut buffer_lwe_before_pbs, cst_add);
// compute the negation
lwe_ciphertext_opposite_assign(&mut buffer_lwe_before_pbs);
let cst_mul = Cleartext(2u32);
//* 2
lwe_ciphertext_cleartext_mul_assign(&mut buffer_lwe_before_pbs, cst_mul);
// compute the bootstrap and the key switch
bootstrapper
@@ -646,15 +610,12 @@ where
}
}
impl<B> BinaryGatesAssignEngine<&mut Ciphertext, &Ciphertext, B::ServerKey> for BooleanEngine<B>
where
B: Bootstrapper,
{
impl BinaryGatesAssignEngine<&mut Ciphertext, &Ciphertext, ServerKey> for BooleanEngine {
fn and_assign(
&mut self,
ct_left: &mut Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
server_key: &ServerKey,
) {
let ct_left_clone = ct_left.clone();
*ct_left = self.and(&ct_left_clone, ct_right, server_key);
@@ -664,7 +625,7 @@ where
&mut self,
ct_left: &mut Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
server_key: &ServerKey,
) {
let ct_left_clone = ct_left.clone();
*ct_left = self.nand(&ct_left_clone, ct_right, server_key);
@@ -674,7 +635,7 @@ where
&mut self,
ct_left: &mut Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
server_key: &ServerKey,
) {
let ct_left_clone = ct_left.clone();
*ct_left = self.nor(&ct_left_clone, ct_right, server_key);
@@ -684,7 +645,7 @@ where
&mut self,
ct_left: &mut Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
server_key: &ServerKey,
) {
let ct_left_clone = ct_left.clone();
*ct_left = self.or(&ct_left_clone, ct_right, server_key);
@@ -694,7 +655,7 @@ where
&mut self,
ct_left: &mut Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
server_key: &ServerKey,
) {
let ct_left_clone = ct_left.clone();
*ct_left = self.xor(&ct_left_clone, ct_right, server_key);
@@ -704,93 +665,79 @@ where
&mut self,
ct_left: &mut Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
server_key: &ServerKey,
) {
let ct_left_clone = ct_left.clone();
*ct_left = self.xnor(&ct_left_clone, ct_right, server_key);
}
}
impl<B> BinaryGatesAssignEngine<&mut Ciphertext, bool, B::ServerKey> for BooleanEngine<B>
where
B: Bootstrapper,
{
fn and_assign(&mut self, ct_left: &mut Ciphertext, ct_right: bool, server_key: &B::ServerKey) {
impl BinaryGatesAssignEngine<&mut Ciphertext, bool, ServerKey> for BooleanEngine {
fn and_assign(&mut self, ct_left: &mut Ciphertext, ct_right: bool, server_key: &ServerKey) {
let ct_left_clone = ct_left.clone();
*ct_left = self.and(&ct_left_clone, ct_right, server_key);
}
fn nand_assign(&mut self, ct_left: &mut Ciphertext, ct_right: bool, server_key: &B::ServerKey) {
fn nand_assign(&mut self, ct_left: &mut Ciphertext, ct_right: bool, server_key: &ServerKey) {
let ct_left_clone = ct_left.clone();
*ct_left = self.nand(&ct_left_clone, ct_right, server_key);
}
fn nor_assign(&mut self, ct_left: &mut Ciphertext, ct_right: bool, server_key: &B::ServerKey) {
fn nor_assign(&mut self, ct_left: &mut Ciphertext, ct_right: bool, server_key: &ServerKey) {
let ct_left_clone = ct_left.clone();
*ct_left = self.nor(&ct_left_clone, ct_right, server_key);
}
fn or_assign(&mut self, ct_left: &mut Ciphertext, ct_right: bool, server_key: &B::ServerKey) {
fn or_assign(&mut self, ct_left: &mut Ciphertext, ct_right: bool, server_key: &ServerKey) {
let ct_left_clone = ct_left.clone();
*ct_left = self.or(&ct_left_clone, ct_right, server_key);
}
fn xor_assign(&mut self, ct_left: &mut Ciphertext, ct_right: bool, server_key: &B::ServerKey) {
fn xor_assign(&mut self, ct_left: &mut Ciphertext, ct_right: bool, server_key: &ServerKey) {
let ct_left_clone = ct_left.clone();
*ct_left = self.xor(&ct_left_clone, ct_right, server_key);
}
fn xnor_assign(&mut self, ct_left: &mut Ciphertext, ct_right: bool, server_key: &B::ServerKey) {
fn xnor_assign(&mut self, ct_left: &mut Ciphertext, ct_right: bool, server_key: &ServerKey) {
let ct_left_clone = ct_left.clone();
*ct_left = self.xnor(&ct_left_clone, ct_right, server_key);
}
}
impl<B> BinaryGatesAssignEngine<bool, &mut Ciphertext, B::ServerKey> for BooleanEngine<B>
where
B: Bootstrapper,
{
fn and_assign(&mut self, ct_left: bool, ct_right: &mut Ciphertext, server_key: &B::ServerKey) {
impl BinaryGatesAssignEngine<bool, &mut Ciphertext, ServerKey> for BooleanEngine {
fn and_assign(&mut self, ct_left: bool, ct_right: &mut Ciphertext, server_key: &ServerKey) {
let ct_right_clone = ct_right.clone();
*ct_right = self.and(ct_left, &ct_right_clone, server_key);
}
fn nand_assign(&mut self, ct_left: bool, ct_right: &mut Ciphertext, server_key: &B::ServerKey) {
fn nand_assign(&mut self, ct_left: bool, ct_right: &mut Ciphertext, server_key: &ServerKey) {
let ct_right_clone = ct_right.clone();
*ct_right = self.nand(ct_left, &ct_right_clone, server_key);
}
fn nor_assign(&mut self, ct_left: bool, ct_right: &mut Ciphertext, server_key: &B::ServerKey) {
fn nor_assign(&mut self, ct_left: bool, ct_right: &mut Ciphertext, server_key: &ServerKey) {
let ct_right_clone = ct_right.clone();
*ct_right = self.nor(ct_left, &ct_right_clone, server_key);
}
fn or_assign(&mut self, ct_left: bool, ct_right: &mut Ciphertext, server_key: &B::ServerKey) {
fn or_assign(&mut self, ct_left: bool, ct_right: &mut Ciphertext, server_key: &ServerKey) {
let ct_right_clone = ct_right.clone();
*ct_right = self.or(ct_left, &ct_right_clone, server_key);
}
fn xor_assign(&mut self, ct_left: bool, ct_right: &mut Ciphertext, server_key: &B::ServerKey) {
fn xor_assign(&mut self, ct_left: bool, ct_right: &mut Ciphertext, server_key: &ServerKey) {
let ct_right_clone = ct_right.clone();
*ct_right = self.xor(ct_left, &ct_right_clone, server_key);
}
fn xnor_assign(&mut self, ct_left: bool, ct_right: &mut Ciphertext, server_key: &B::ServerKey) {
fn xnor_assign(&mut self, ct_left: bool, ct_right: &mut Ciphertext, server_key: &ServerKey) {
let ct_right_clone = ct_right.clone();
*ct_right = self.xnor(ct_left, &ct_right_clone, server_key);
}
}
impl<B> BinaryGatesEngine<&Ciphertext, bool, B::ServerKey> for BooleanEngine<B>
where
B: Bootstrapper,
{
fn and(
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
) -> Ciphertext {
impl BinaryGatesEngine<&Ciphertext, bool, ServerKey> for BooleanEngine {
fn and(&mut self, ct_left: &Ciphertext, ct_right: bool, _server_key: &ServerKey) -> Ciphertext {
if ct_right {
// ct AND true = ct
ct_left.clone()
@@ -804,7 +751,7 @@ where
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
_server_key: &ServerKey,
) -> Ciphertext {
if ct_right {
// NOT (ct AND true) = NOT(ct)
@@ -815,12 +762,7 @@ where
}
}
fn nor(
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
) -> Ciphertext {
fn nor(&mut self, ct_left: &Ciphertext, ct_right: bool, _server_key: &ServerKey) -> Ciphertext {
if ct_right {
// NOT (ct OR true) = NOT(true) = false
self.trivial_encrypt(false)
@@ -830,12 +772,7 @@ where
}
}
fn or(
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
) -> Ciphertext {
fn or(&mut self, ct_left: &Ciphertext, ct_right: bool, _server_key: &ServerKey) -> Ciphertext {
if ct_right {
// ct OR true = true
self.trivial_encrypt(true)
@@ -845,12 +782,7 @@ where
}
}
fn xor(
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
) -> Ciphertext {
fn xor(&mut self, ct_left: &Ciphertext, ct_right: bool, _server_key: &ServerKey) -> Ciphertext {
if ct_right {
// ct XOR true = NOT(ct)
self.not(ct_left)
@@ -864,7 +796,7 @@ where
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
_server_key: &ServerKey,
) -> Ciphertext {
if ct_right {
// NOT(ct XOR true) = NOT(NOT(ct)) = ct
@@ -876,61 +808,28 @@ where
}
}
impl<B> BinaryGatesEngine<bool, &Ciphertext, B::ServerKey> for BooleanEngine<B>
where
B: Bootstrapper,
{
fn and(
&mut self,
ct_left: bool,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
impl BinaryGatesEngine<bool, &Ciphertext, ServerKey> for BooleanEngine {
fn and(&mut self, ct_left: bool, ct_right: &Ciphertext, server_key: &ServerKey) -> Ciphertext {
self.and(ct_right, ct_left, server_key)
}
fn nand(
&mut self,
ct_left: bool,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
fn nand(&mut self, ct_left: bool, ct_right: &Ciphertext, server_key: &ServerKey) -> Ciphertext {
self.nand(ct_right, ct_left, server_key)
}
fn nor(
&mut self,
ct_left: bool,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
fn nor(&mut self, ct_left: bool, ct_right: &Ciphertext, server_key: &ServerKey) -> Ciphertext {
self.nor(ct_right, ct_left, server_key)
}
fn or(
&mut self,
ct_left: bool,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
fn or(&mut self, ct_left: bool, ct_right: &Ciphertext, server_key: &ServerKey) -> Ciphertext {
self.or(ct_right, ct_left, server_key)
}
fn xor(
&mut self,
ct_left: bool,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
fn xor(&mut self, ct_left: bool, ct_right: &Ciphertext, server_key: &ServerKey) -> Ciphertext {
self.xor(ct_right, ct_left, server_key)
}
fn xnor(
&mut self,
ct_left: bool,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
fn xnor(&mut self, ct_left: bool, ct_right: &Ciphertext, server_key: &ServerKey) -> Ciphertext {
self.xnor(ct_right, ct_left, server_key)
}
}

View File

@@ -1,5 +1,4 @@
#![deny(rustdoc::broken_intra_doc_links)]
//! Welcome the the tfhe.rs `boolean` module documentation!
//! Welcome to the TFHE-rs `boolean` module documentation!
//!
//! # Description
//! This library makes it possible to execute boolean gates over encrypted bits.
@@ -18,7 +17,6 @@
//! homomorphically.
//!
//! ```rust
//! # #[cfg(not(feature = "cuda"))]
//! # fn main() {
//!
//! use tfhe::boolean::gen_keys;
@@ -54,9 +52,6 @@
//! let output_3 = client_key.decrypt(&ct_9);
//! assert_eq!(output_3, true);
//! # }
//!
//! # #[cfg(feature = "cuda")]
//! # fn main() {}
//! ```
use crate::boolean::client_key::ClientKey;
@@ -114,15 +109,12 @@ pub(crate) fn random_integer() -> u32 {
/// meant to be published (the client sends it to the server).
///
/// ```rust
/// # #[cfg(not(feature = "cuda"))]
/// # fn main() {
/// use tfhe::boolean::gen_keys;
/// use tfhe::boolean::prelude::*;
/// // generate the client key and the server key:
/// let (cks, sks) = gen_keys();
/// # }
/// # #[cfg(feature = "cuda")]
/// # fn main() {}
/// ```
pub fn gen_keys() -> (ClientKey, ServerKey) {
// generate the client key

View File

@@ -18,9 +18,9 @@
//! This is an unsafe operation as failing to properly fix the parameters will potentially result
//! with an incorrect and/or insecure computation.
pub use crate::core_crypto::prelude::{
pub use crate::core_crypto::commons::dispersion::StandardDev;
pub use crate::core_crypto::commons::parameters::{
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
StandardDev,
};
use serde::{Deserialize, Serialize};
@@ -107,18 +107,3 @@ pub const TFHE_LIB_PARAMETERS: BooleanParameters = BooleanParameters {
ks_base_log: DecompositionBaseLog(5),
ks_level: DecompositionLevelCount(3),
};
/// This parameter set ensures a probability of error upper-bounded by $2^{-40}$ and 128-bit
/// security.
/// They are GPU-compliant.
pub const GPU_DEFAULT_PARAMETERS: BooleanParameters = BooleanParameters {
lwe_dimension: LweDimension(686),
glwe_dimension: GlweDimension(1),
polynomial_size: PolynomialSize(1024),
lwe_modular_std_dev: StandardDev(0.000019703241702943194),
glwe_modular_std_dev: StandardDev(0.00000004053919869756513),
pbs_base_log: DecompositionBaseLog(6),
pbs_level: DecompositionLevelCount(3),
ks_base_log: DecompositionBaseLog(2),
ks_level: DecompositionLevelCount(6),
};

View File

@@ -1,24 +1,23 @@
use crate::boolean::ciphertext::Ciphertext;
use crate::boolean::client_key::ClientKey;
use crate::boolean::engine::{CpuBooleanEngine, WithThreadLocalEngine};
use crate::boolean::engine::{BooleanEngine, WithThreadLocalEngine};
use crate::boolean::parameters::BooleanParameters;
use crate::core_crypto::prelude::*;
use crate::core_crypto::entities::*;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// A structure containing a public key.
#[derive(Clone)]
pub struct PublicKey {
pub(crate) lwe_public_key: LwePublicKey32,
pub(crate) lwe_public_key: LwePublicKeyOwned<u32>,
pub(crate) parameters: BooleanParameters,
}
impl PublicKey {
/// Encrypts a Boolean message using the client key.
/// Encrypt a Boolean message using the client key.
///
/// # Example
///
/// ```rust
/// # #[cfg(not(feature = "cuda"))]
/// # fn main() {
/// use tfhe::boolean::prelude::*;
///
@@ -36,26 +35,16 @@ impl PublicKey {
/// let dec = cks.decrypt(&ct_res);
/// assert_eq!(false, dec);
/// # }
/// # #[cfg(feature = "cuda")]
/// # fn main() {}
/// ```
pub fn encrypt(&self, message: bool) -> Ciphertext {
CpuBooleanEngine::with_thread_local_mut(|engine| {
engine.encrypt_with_public_key(message, self)
})
BooleanEngine::with_thread_local_mut(|engine| engine.encrypt_with_public_key(message, self))
}
/// Allocates and generates a client key.
///
/// # Panic
///
/// This will panic when the "cuda" feature is enabled and the parameters
/// uses a GlweDimension > 1 (as it is not yet supported by the cuda backend).
/// Allocate and generate a client key.
///
/// # Example
///
/// ```rust
/// # #[cfg(not(feature = "cuda"))]
/// # fn main() {
/// use tfhe::boolean::prelude::*;
///
@@ -64,11 +53,9 @@ impl PublicKey {
///
/// let pks = PublicKey::new(&cks);
/// # }
/// # #[cfg(feature = "cuda")]
/// # fn main() {}
/// ```
pub fn new(client_key: &ClientKey) -> PublicKey {
CpuBooleanEngine::with_thread_local_mut(|engine| engine.create_public_key(client_key))
BooleanEngine::with_thread_local_mut(|engine| engine.create_public_key(client_key))
}
}
@@ -83,11 +70,8 @@ impl Serialize for PublicKey {
where
S: Serializer,
{
let mut ser_eng = DefaultSerializationEngine::new(()).map_err(serde::ser::Error::custom)?;
let lwe_public_key = ser_eng
.serialize(&self.lwe_public_key)
.map_err(serde::ser::Error::custom)?;
let lwe_public_key =
bincode::serialize(&self.lwe_public_key).map_err(serde::ser::Error::custom)?;
SerializablePublicKey {
lwe_public_key,
@@ -104,11 +88,9 @@ impl<'de> Deserialize<'de> for PublicKey {
{
let thing =
SerializablePublicKey::deserialize(deserializer).map_err(serde::de::Error::custom)?;
let mut de_eng = DefaultSerializationEngine::new(()).map_err(serde::de::Error::custom)?;
Ok(Self {
lwe_public_key: de_eng
.deserialize(thing.lwe_public_key.as_slice())
lwe_public_key: bincode::deserialize(thing.lwe_public_key.as_slice())
.map_err(serde::de::Error::custom)?,
parameters: thing.parameters,
})

View File

@@ -8,18 +8,12 @@
#[cfg(test)]
mod tests;
use serde::{Deserialize, Serialize};
use crate::boolean::ciphertext::Ciphertext;
use crate::boolean::client_key::ClientKey;
use crate::boolean::engine::bootstrapping::CpuBootstrapKey;
#[cfg(feature = "cuda")]
use crate::boolean::engine::{bootstrapping::CudaBootstrapKey, CudaBooleanEngine};
pub use crate::boolean::engine::bootstrapping::ServerKey;
use crate::boolean::engine::{
BinaryGatesAssignEngine, BinaryGatesEngine, CpuBooleanEngine, WithThreadLocalEngine,
BinaryGatesAssignEngine, BinaryGatesEngine, BooleanEngine, WithThreadLocalEngine,
};
#[cfg(feature = "cuda")]
use std::sync::Arc;
pub trait BinaryBooleanGates<L, R> {
fn and(&self, ct_left: L, ct_right: R) -> Ciphertext;
@@ -39,156 +33,103 @@ pub trait BinaryBooleanGatesAssign<L, R> {
fn xnor_assign(&self, ct_left: L, ct_right: R);
}
trait RefFromServerKey {
fn get_ref(server_key: &ServerKey) -> &Self;
}
trait DefaultImplementation {
type Engine: WithThreadLocalEngine;
type BootsrapKey: RefFromServerKey;
}
#[derive(Clone)]
pub struct ServerKey {
cpu_key: CpuBootstrapKey,
#[cfg(feature = "cuda")]
cuda_key: Arc<CudaBootstrapKey>,
}
#[cfg(not(feature = "cuda"))]
mod implementation {
use super::*;
impl RefFromServerKey for CpuBootstrapKey {
fn get_ref(server_key: &ServerKey) -> &Self {
&server_key.cpu_key
}
}
impl DefaultImplementation for ServerKey {
type Engine = CpuBooleanEngine;
type BootsrapKey = CpuBootstrapKey;
}
}
#[cfg(feature = "cuda")]
mod implementation {
use super::*;
impl RefFromServerKey for CudaBootstrapKey {
fn get_ref(server_key: &ServerKey) -> &Self {
&server_key.cuda_key
}
}
impl DefaultImplementation for ServerKey {
type Engine = CudaBooleanEngine;
type BootsrapKey = CudaBootstrapKey;
type Engine = BooleanEngine;
}
}
impl<Lhs, Rhs> BinaryBooleanGates<Lhs, Rhs> for ServerKey
where
<ServerKey as DefaultImplementation>::Engine:
BinaryGatesEngine<Lhs, Rhs, <ServerKey as DefaultImplementation>::BootsrapKey>,
<ServerKey as DefaultImplementation>::Engine: BinaryGatesEngine<Lhs, Rhs, ServerKey>,
{
fn and(&self, ct_left: Lhs, ct_right: Rhs) -> Ciphertext {
let bootstrap_key = <ServerKey as DefaultImplementation>::BootsrapKey::get_ref(self);
<ServerKey as DefaultImplementation>::Engine::with_thread_local_mut(|engine| {
engine.and(ct_left, ct_right, bootstrap_key)
engine.and(ct_left, ct_right, self)
})
}
fn nand(&self, ct_left: Lhs, ct_right: Rhs) -> Ciphertext {
let bootstrap_key = <ServerKey as DefaultImplementation>::BootsrapKey::get_ref(self);
<ServerKey as DefaultImplementation>::Engine::with_thread_local_mut(|engine| {
engine.nand(ct_left, ct_right, bootstrap_key)
engine.nand(ct_left, ct_right, self)
})
}
fn nor(&self, ct_left: Lhs, ct_right: Rhs) -> Ciphertext {
let bootstrap_key = <ServerKey as DefaultImplementation>::BootsrapKey::get_ref(self);
<ServerKey as DefaultImplementation>::Engine::with_thread_local_mut(|engine| {
engine.nor(ct_left, ct_right, bootstrap_key)
engine.nor(ct_left, ct_right, self)
})
}
fn or(&self, ct_left: Lhs, ct_right: Rhs) -> Ciphertext {
let bootstrap_key = <ServerKey as DefaultImplementation>::BootsrapKey::get_ref(self);
<ServerKey as DefaultImplementation>::Engine::with_thread_local_mut(|engine| {
engine.or(ct_left, ct_right, bootstrap_key)
engine.or(ct_left, ct_right, self)
})
}
fn xor(&self, ct_left: Lhs, ct_right: Rhs) -> Ciphertext {
let bootstrap_key = <ServerKey as DefaultImplementation>::BootsrapKey::get_ref(self);
<ServerKey as DefaultImplementation>::Engine::with_thread_local_mut(|engine| {
engine.xor(ct_left, ct_right, bootstrap_key)
engine.xor(ct_left, ct_right, self)
})
}
fn xnor(&self, ct_left: Lhs, ct_right: Rhs) -> Ciphertext {
let bootstrap_key = <ServerKey as DefaultImplementation>::BootsrapKey::get_ref(self);
<ServerKey as DefaultImplementation>::Engine::with_thread_local_mut(|engine| {
engine.xnor(ct_left, ct_right, bootstrap_key)
engine.xnor(ct_left, ct_right, self)
})
}
}
impl<Lhs, Rhs> BinaryBooleanGatesAssign<Lhs, Rhs> for ServerKey
where
<ServerKey as DefaultImplementation>::Engine:
BinaryGatesAssignEngine<Lhs, Rhs, <ServerKey as DefaultImplementation>::BootsrapKey>,
<ServerKey as DefaultImplementation>::Engine: BinaryGatesAssignEngine<Lhs, Rhs, ServerKey>,
{
fn and_assign(&self, ct_left: Lhs, ct_right: Rhs) {
let bootstrap_key = <ServerKey as DefaultImplementation>::BootsrapKey::get_ref(self);
<ServerKey as DefaultImplementation>::Engine::with_thread_local_mut(|engine| {
engine.and_assign(ct_left, ct_right, bootstrap_key)
engine.and_assign(ct_left, ct_right, self)
})
}
fn nand_assign(&self, ct_left: Lhs, ct_right: Rhs) {
let bootstrap_key = <ServerKey as DefaultImplementation>::BootsrapKey::get_ref(self);
<ServerKey as DefaultImplementation>::Engine::with_thread_local_mut(|engine| {
engine.nand_assign(ct_left, ct_right, bootstrap_key)
engine.nand_assign(ct_left, ct_right, self)
})
}
fn nor_assign(&self, ct_left: Lhs, ct_right: Rhs) {
let bootstrap_key = <ServerKey as DefaultImplementation>::BootsrapKey::get_ref(self);
<ServerKey as DefaultImplementation>::Engine::with_thread_local_mut(|engine| {
engine.nor_assign(ct_left, ct_right, bootstrap_key)
engine.nor_assign(ct_left, ct_right, self)
})
}
fn or_assign(&self, ct_left: Lhs, ct_right: Rhs) {
let bootstrap_key = <ServerKey as DefaultImplementation>::BootsrapKey::get_ref(self);
<ServerKey as DefaultImplementation>::Engine::with_thread_local_mut(|engine| {
engine.or_assign(ct_left, ct_right, bootstrap_key)
engine.or_assign(ct_left, ct_right, self)
})
}
fn xor_assign(&self, ct_left: Lhs, ct_right: Rhs) {
let bootstrap_key = <ServerKey as DefaultImplementation>::BootsrapKey::get_ref(self);
<ServerKey as DefaultImplementation>::Engine::with_thread_local_mut(|engine| {
engine.xor_assign(ct_left, ct_right, bootstrap_key)
engine.xor_assign(ct_left, ct_right, self)
})
}
fn xnor_assign(&self, ct_left: Lhs, ct_right: Rhs) {
let bootstrap_key = <ServerKey as DefaultImplementation>::BootsrapKey::get_ref(self);
<ServerKey as DefaultImplementation>::Engine::with_thread_local_mut(|engine| {
engine.xnor_assign(ct_left, ct_right, bootstrap_key)
engine.xnor_assign(ct_left, ct_right, self)
})
}
}
impl ServerKey {
pub fn new(cks: &ClientKey) -> Self {
let cpu_key =
CpuBooleanEngine::with_thread_local_mut(|engine| engine.create_server_key(cks));
Self::from(cpu_key)
BooleanEngine::with_thread_local_mut(|engine| engine.create_server_key(cks))
}
pub fn trivial_encrypt(&self, message: bool) -> Ciphertext {
@@ -196,11 +137,11 @@ impl ServerKey {
}
pub fn not(&self, ct: &Ciphertext) -> Ciphertext {
CpuBooleanEngine::with_thread_local_mut(|engine| engine.not(ct))
BooleanEngine::with_thread_local_mut(|engine| engine.not(ct))
}
pub fn not_assign(&self, ct: &mut Ciphertext) {
CpuBooleanEngine::with_thread_local_mut(|engine| engine.not_assign(ct))
BooleanEngine::with_thread_local_mut(|engine| engine.not_assign(ct))
}
pub fn mux(
@@ -209,56 +150,8 @@ impl ServerKey {
ct_then: &Ciphertext,
ct_else: &Ciphertext,
) -> Ciphertext {
#[cfg(feature = "cuda")]
{
CudaBooleanEngine::with_thread_local_mut(|engine| {
engine.mux(ct_condition, ct_then, ct_else, &self.cuda_key)
})
}
#[cfg(not(feature = "cuda"))]
{
CpuBooleanEngine::with_thread_local_mut(|engine| {
engine.mux(ct_condition, ct_then, ct_else, &self.cpu_key)
})
}
}
}
impl From<CpuBootstrapKey> for ServerKey {
fn from(cpu_key: CpuBootstrapKey) -> Self {
#[cfg(feature = "cuda")]
{
let cuda_key = CudaBooleanEngine::with_thread_local_mut(|engine| {
engine.create_server_key(&cpu_key)
});
let cuda_key = Arc::new(cuda_key);
Self { cpu_key, cuda_key }
}
#[cfg(not(feature = "cuda"))]
{
Self { cpu_key }
}
}
}
impl Serialize for ServerKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.cpu_key.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for ServerKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let cpu_key = CpuBootstrapKey::deserialize(deserializer)?;
Ok(Self::from(cpu_key))
BooleanEngine::with_thread_local_mut(|engine| {
engine.mux(ct_condition, ct_then, ct_else, self)
})
}
}

View File

@@ -13,7 +13,6 @@ const NB_CT: usize = 8;
/// Number of gates computed in the deep circuit test
const NB_GATE: usize = 1 << 11;
#[cfg(not(feature = "cuda"))]
mod default_parameters_tests {
use super::*;
use crate::boolean::parameters::DEFAULT_PARAMETERS;
@@ -60,7 +59,6 @@ mod default_parameters_tests {
}
}
#[cfg(not(feature = "cuda"))]
mod tfhe_lib_parameters_tests {
use super::*;
use crate::boolean::parameters::TFHE_LIB_PARAMETERS;
@@ -107,52 +105,6 @@ mod tfhe_lib_parameters_tests {
}
}
mod gpu_default_parameters_tests {
use super::*;
use crate::boolean::parameters::GPU_DEFAULT_PARAMETERS;
#[test]
fn test_encrypt_decrypt_lwe_secret_key_default_parameters() {
test_encrypt_decrypt_lwe_secret_key(GPU_DEFAULT_PARAMETERS);
}
#[test]
fn test_and_gate_default_parameters() {
test_and_gate(GPU_DEFAULT_PARAMETERS);
}
#[test]
fn test_nand_gate_default_parameters() {
test_nand_gate(GPU_DEFAULT_PARAMETERS);
}
#[test]
fn test_or_gate_default_parameters() {
test_or_gate(GPU_DEFAULT_PARAMETERS);
}
#[test]
fn test_nor_gate_default_parameters() {
test_nor_gate(GPU_DEFAULT_PARAMETERS);
}
#[test]
fn test_xor_gate_default_parameters() {
test_xor_gate(GPU_DEFAULT_PARAMETERS);
}
#[test]
fn test_xnor_gate_default_parameters() {
test_xnor_gate(GPU_DEFAULT_PARAMETERS);
}
#[test]
fn test_not_gate_default_parameters() {
test_not_gate(GPU_DEFAULT_PARAMETERS);
}
#[test]
fn test_mux_gate_default_parameters() {
test_mux_gate(GPU_DEFAULT_PARAMETERS);
}
#[test]
fn test_deep_circuit_default_parameters() {
test_deep_circuit(GPU_DEFAULT_PARAMETERS);
}
}
/// test encryption and decryption with the LWE secret key
fn test_encrypt_decrypt_lwe_secret_key(parameters: BooleanParameters) {
// generate the client key set

View File

@@ -109,7 +109,7 @@ pub unsafe extern "C" fn boolean_trivial_encrypt(
check_ptr_is_non_null_and_aligned(result).unwrap();
let heap_allocated_result = Box::new(BooleanCiphertext(
boolean::engine::CpuBooleanEngine::with_thread_local_mut(|engine| {
boolean::engine::BooleanEngine::with_thread_local_mut(|engine| {
engine.trivial_encrypt(message)
}),
));

View File

@@ -1,7 +1,7 @@
use crate::c_api::utils::*;
use crate::core_crypto::prelude::{
use crate::core_crypto::commons::dispersion::StandardDev;
use crate::core_crypto::commons::parameters::{
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
StandardDev,
};
use std::os::raw::c_int;

View File

@@ -1,4 +1,3 @@
#![deny(rustdoc::broken_intra_doc_links)]
#![allow(clippy::missing_safety_doc)]
#[cfg(feature = "boolean-c-api")]
pub mod boolean;

View File

@@ -1,7 +1,7 @@
use crate::c_api::utils::*;
use crate::core_crypto::prelude::{
pub use crate::core_crypto::commons::dispersion::StandardDev;
pub use crate::core_crypto::commons::parameters::{
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
StandardDev,
};
use std::os::raw::c_int;

View File

@@ -8,10 +8,10 @@ pub type AccumulatorCallback = Option<extern "C" fn(u64) -> u64>;
pub type BivariateAccumulatorCallback = Option<extern "C" fn(u64, u64) -> u64>;
pub struct ShortintPBSAccumulator(
pub(in crate::c_api) crate::core_crypto::prelude::GlweCiphertext64,
pub(in crate::c_api) crate::core_crypto::entities::GlweCiphertextOwned<u64>,
);
pub struct ShortintBivariatePBSAccumulator(
pub(in crate::c_api) crate::core_crypto::prelude::GlweCiphertext64,
pub(in crate::c_api) crate::core_crypto::entities::GlweCiphertextOwned<u64>,
);
#[no_mangle]

View File

@@ -0,0 +1,317 @@
use crate::core_crypto::algorithms::slice_algorithms::*;
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::dispersion::DispersionParameter;
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
use crate::core_crypto::commons::math::decomposition::DecompositionLevel;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use rayon::prelude::*;
/// Encrypt a plaintext in a [`GGSW ciphertext`](`GgswCiphertext`).
///
/// See the [`formal definition`](`GgswCiphertext#ggsw-encryption`) for the definition of the
/// encryption algorithm.
///
/// ```
/// use tfhe::core_crypto::commons::generators::{
/// EncryptionRandomGenerator, SecretRandomGenerator,
/// };
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for GgswCiphertext creation
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
/// let decomp_base_log = DecompositionBaseLog(8);
/// let decomp_level_count = DecompositionLevelCount(3);
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the GlweSecretKey
/// let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
/// glwe_size.to_glwe_dimension(),
/// polynomial_size,
/// &mut secret_generator,
/// );
///
/// // Create the plaintext
/// let encoded_msg = 3u64 << 60;
/// let plaintext = Plaintext(encoded_msg);
///
/// // Create a new GgswCiphertext
/// let mut ggsw = GgswCiphertext::new(
/// 0u64,
/// glwe_size,
/// polynomial_size,
/// decomp_base_log,
/// decomp_level_count,
/// );
///
/// encrypt_ggsw_ciphertext(
/// &glwe_secret_key,
/// &mut ggsw,
/// plaintext,
/// glwe_modular_std_dev,
/// &mut encryption_generator,
/// );
/// ```
pub fn encrypt_ggsw_ciphertext<Scalar, KeyCont, OutputCont, Gen>(
glwe_secret_key: &GlweSecretKey<KeyCont>,
output: &mut GgswCiphertext<OutputCont>,
encoded: Plaintext<Scalar>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
output.polynomial_size() == glwe_secret_key.polynomial_size(),
"Mismatch between polynomial sizes of output cipertexts and input secret key. \
Got {:?} in output, and {:?} in secret key.",
output.polynomial_size(),
glwe_secret_key.polynomial_size()
);
assert!(
output.glwe_size().to_glwe_dimension() == glwe_secret_key.glwe_dimension(),
"Mismatch between GlweDimension of output cipertexts and input secret key. \
Got {:?} in output, and {:?} in secret key.",
output.glwe_size().to_glwe_dimension(),
glwe_secret_key.glwe_dimension()
);
// Generators used to have same sequential and parallel key generation
let gen_iter = generator
.fork_ggsw_to_ggsw_levels::<Scalar>(
output.decomposition_level_count(),
output.glwe_size(),
output.polynomial_size(),
)
.expect("Failed to split generator into ggsw levels");
let output_glwe_size = output.glwe_size();
let output_polynomial_size = output.polynomial_size();
let decomp_base_log = output.decomposition_base_log();
for (level_index, (mut level_matrix, mut generator)) in
output.iter_mut().zip(gen_iter).enumerate()
{
let decomp_level = DecompositionLevel(level_index + 1);
let factor = encoded
.0
.wrapping_neg()
.wrapping_mul(Scalar::ONE << (Scalar::BITS - (decomp_base_log.0 * decomp_level.0)));
// We iterate over the rows of the level matrix, the last row needs special treatment
let gen_iter = generator
.fork_ggsw_level_to_glwe::<Scalar>(output_glwe_size, output_polynomial_size)
.expect("Failed to split generator into glwe");
let last_row_index = level_matrix.glwe_size().0 - 1;
for ((row_index, mut row_as_glwe), mut generator) in level_matrix
.as_mut_glwe_list()
.iter_mut()
.enumerate()
.zip(gen_iter)
{
encrypt_ggsw_level_matrix_row(
glwe_secret_key,
(row_index, last_row_index),
factor,
&mut row_as_glwe,
noise_parameters,
&mut generator,
);
}
}
}
/// Parallel variant of [`encrypt_ggsw_ciphertext`].
///
/// See the [`formal definition`](`GgswCiphertext#ggsw-encryption`) for the definition of the
/// encryption algorithm.
///
/// New tasks are created per level matrix and per row of each level matrix.
///
/// ```
/// use tfhe::core_crypto::commons::generators::{
/// EncryptionRandomGenerator, SecretRandomGenerator,
/// };
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for GgswCiphertext creation
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
/// let decomp_base_log = DecompositionBaseLog(8);
/// let decomp_level_count = DecompositionLevelCount(3);
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the GlweSecretKey
/// let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
/// glwe_size.to_glwe_dimension(),
/// polynomial_size,
/// &mut secret_generator,
/// );
///
/// // Create the plaintext
/// let encoded_msg = 3u64 << 60;
/// let plaintext = Plaintext(encoded_msg);
///
/// // Create a new GgswCiphertext
/// let mut ggsw = GgswCiphertext::new(
/// 0u64,
/// glwe_size,
/// polynomial_size,
/// decomp_base_log,
/// decomp_level_count,
/// );
///
/// par_encrypt_ggsw_ciphertext(
/// &glwe_secret_key,
/// &mut ggsw,
/// plaintext,
/// glwe_modular_std_dev,
/// &mut encryption_generator,
/// );
/// ```
pub fn par_encrypt_ggsw_ciphertext<Scalar, KeyCont, OutputCont, Gen>(
glwe_secret_key: &GlweSecretKey<KeyCont>,
output: &mut GgswCiphertext<OutputCont>,
encoded: Plaintext<Scalar>,
noise_parameters: impl DispersionParameter + Sync,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus + Sync + Send,
KeyCont: Container<Element = Scalar> + Sync,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ParallelByteRandomGenerator,
{
assert!(
output.polynomial_size() == glwe_secret_key.polynomial_size(),
"Mismatch between polynomial sizes of output cipertexts and input secret key. \
Got {:?} in output, and {:?} in secret key.",
output.polynomial_size(),
glwe_secret_key.polynomial_size()
);
assert!(
output.glwe_size().to_glwe_dimension() == glwe_secret_key.glwe_dimension(),
"Mismatch between GlweDimension of output cipertexts and input secret key. \
Got {:?} in output, and {:?} in secret key.",
output.glwe_size().to_glwe_dimension(),
glwe_secret_key.glwe_dimension()
);
// Generators used to have same sequential and parallel key generation
let gen_iter = generator
.par_fork_ggsw_to_ggsw_levels::<Scalar>(
output.decomposition_level_count(),
output.glwe_size(),
output.polynomial_size(),
)
.expect("Failed to split generator into ggsw levels");
let output_glwe_size = output.glwe_size();
let output_polynomial_size = output.polynomial_size();
let decomp_base_log = output.decomposition_base_log();
output.par_iter_mut().zip(gen_iter).enumerate().for_each(
|(level_index, (mut level_matrix, mut generator))| {
let decomp_level = DecompositionLevel(level_index + 1);
let factor = encoded
.0
.wrapping_neg()
.wrapping_mul(Scalar::ONE << (Scalar::BITS - (decomp_base_log.0 * decomp_level.0)));
// We iterate over the rows of the level matrix, the last row needs special treatment
let gen_iter = generator
.par_fork_ggsw_level_to_glwe::<Scalar>(output_glwe_size, output_polynomial_size)
.expect("Failed to split generator into glwe");
let last_row_index = level_matrix.glwe_size().0 - 1;
level_matrix
.as_mut_glwe_list()
.par_iter_mut()
.enumerate()
.zip(gen_iter)
.for_each(|((row_index, mut row_as_glwe), mut generator)| {
encrypt_ggsw_level_matrix_row(
glwe_secret_key,
(row_index, last_row_index),
factor,
&mut row_as_glwe,
noise_parameters,
&mut generator,
);
});
},
);
}
/// Convenience function to encrypt a row of a [`GgswLevelMatrix`] irrespective of the current row
/// being encrypted. Allows to share code between sequential ([`encrypt_ggsw_ciphertext`]) and
/// parallel ([`par_encrypt_ggsw_ciphertext`]) variants of the GGSW ciphertext encryption.
///
/// You probably don't want to use this function directly.
fn encrypt_ggsw_level_matrix_row<Scalar, KeyCont, OutputCont, Gen>(
glwe_secret_key: &GlweSecretKey<KeyCont>,
(row_index, last_row_index): (usize, usize),
factor: Scalar,
row_as_glwe: &mut GlweCiphertext<OutputCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
if row_index < last_row_index {
// Not the last row
let sk_poly_list = glwe_secret_key.as_polynomial_list();
let sk_poly = sk_poly_list.get(row_index);
// Copy the key polynomial to the output body, to avoid allocating a temporary buffer
let mut body = row_as_glwe.get_mut_body();
body.as_mut().copy_from_slice(sk_poly.as_ref());
slice_wrapping_scalar_mul_assign(body.as_mut(), factor);
encrypt_glwe_ciphertext_assign(glwe_secret_key, row_as_glwe, noise_parameters, generator);
} else {
// The last row needs a slightly different treatment
let mut body = row_as_glwe.get_mut_body();
body.as_mut().fill(Scalar::ZERO);
body.as_mut()[0] = factor.wrapping_neg();
encrypt_glwe_ciphertext_assign(glwe_secret_key, row_as_glwe, noise_parameters, generator);
}
}

View File

@@ -0,0 +1,681 @@
use crate::core_crypto::algorithms::polynomial_algorithms::*;
use crate::core_crypto::commons::dispersion::DispersionParameter;
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// Variant of [`encrypt_glwe_ciphertext`] which assumes that the plaintexts to encrypt are already
/// loaded in the body of the output [`GLWE ciphertext`](`GlweCiphertext`), this is sometimes useful
/// to avoid allocating a [`PlaintextList`] in situ.
///
/// See this [`formal definition`](`encrypt_glwe_ciphertext#formal-definition`) for the definition
/// of the GLWE encryption algorithm.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::commons::generators::{
/// EncryptionRandomGenerator, SecretRandomGenerator,
/// };
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for GlweCiphertext creation
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the GlweSecretKey
/// let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
/// glwe_size.to_glwe_dimension(),
/// polynomial_size,
/// &mut secret_generator,
/// );
///
/// // Create the plaintext
/// let msg = 3u64;
/// let encoded_msg = msg << 60;
///
/// // Create a new GlweCiphertext
/// let mut glwe = GlweCiphertext::new(0u64, glwe_size, polynomial_size);
///
/// // Manually fill the body with the encoded message
/// glwe.get_mut_body().as_mut().fill(encoded_msg);
///
/// encrypt_glwe_ciphertext_assign(
/// &glwe_secret_key,
/// &mut glwe,
/// glwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// let mut output_plaintext_list = PlaintextList::new(0u64, PlaintextCount(polynomial_size.0));
///
/// decrypt_glwe_ciphertext(&glwe_secret_key, &glwe, &mut output_plaintext_list);
///
/// // Round and remove encoding
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
///
/// output_plaintext_list
/// .iter_mut()
/// .for_each(|elt| *elt.0 = decomposer.closest_representable(*elt.0));
///
/// // Get the raw vector
/// let mut cleartext_list = output_plaintext_list.into_container();
/// // Remove the encoding
/// cleartext_list.iter_mut().for_each(|elt| *elt = *elt >> 60);
/// // Get the list immutably
/// let cleartext_list = cleartext_list;
///
/// // Check we recovered the original message for each plaintext we encrypted
/// cleartext_list.iter().for_each(|&elt| assert_eq!(elt, msg));
/// ```
pub fn encrypt_glwe_ciphertext_assign<Scalar, KeyCont, OutputCont, Gen>(
glwe_secret_key: &GlweSecretKey<KeyCont>,
output: &mut GlweCiphertext<OutputCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
output.glwe_size().to_glwe_dimension() == glwe_secret_key.glwe_dimension(),
"Mismatch between GlweDimension of output cipertext and input secret key. \
Got {:?} in output, and {:?} in secret key.",
output.glwe_size().to_glwe_dimension(),
glwe_secret_key.glwe_dimension()
);
assert!(
output.polynomial_size() == glwe_secret_key.polynomial_size(),
"Mismatch between PolynomialSize of output cipertext and input secret key. \
Got {:?} in output, and {:?} in secret key.",
output.polynomial_size(),
glwe_secret_key.polynomial_size()
);
let (mut mask, mut body) = output.get_mut_mask_and_body();
generator.fill_slice_with_random_mask(mask.as_mut());
generator
.unsigned_torus_slice_wrapping_add_random_noise_assign(body.as_mut(), noise_parameters);
polynomial_wrapping_add_multisum_assign(
&mut body.as_mut_polynomial(),
&mask.as_polynomial_list(),
&glwe_secret_key.as_polynomial_list(),
);
}
/// Encrypt a (scalar) plaintext list in a [`GLWE ciphertext`](`GlweCiphertext`).
///
/// # Formal Definition
///
/// ## GLWE Encryption
/// ###### inputs:
/// - $\mathsf{PT}\in\mathcal{R}\_q$: a plaintext
/// - $\vec{S} \in\mathcal{R}\_q^k$: a secret key
/// - $\mathcal{D\_{\sigma^2,\mu}}$: a normal distribution of variance $\sigma^2$ and mean $\mu$
///
/// ###### outputs:
/// - $\mathsf{CT} = \left( \vec{A} , B \right) \in \mathsf{GLWE}\_{\vec{S}}( \mathsf{PT} )\subseteq
/// \mathcal{R}\_q^{k+1}$: a GLWE ciphertext
///
/// ###### algorithm:
/// 1. uniformly sample each coefficient of the polynomial vector $\vec{A}\in\mathcal{R}^k\_q$
/// 2. sample each integer error coefficient of an error polynomial $E\in\mathcal{R}\_q$ from
/// $\mathcal{D\_{\sigma^2,\mu}}$ 3. compute $B = \left\langle \vec{A} , \vec{S} \right\rangle +
/// \mathsf{PT} + E \in\mathcal{R}\_q$ 4. output $\left( \vec{A} , B \right)$
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::commons::generators::{
/// EncryptionRandomGenerator, SecretRandomGenerator,
/// };
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for GlweCiphertext creation
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the GlweSecretKey
/// let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
/// glwe_size.to_glwe_dimension(),
/// polynomial_size,
/// &mut secret_generator,
/// );
///
/// // Create the plaintext
/// let msg = 3u64;
/// let encoded_msg = msg << 60;
/// let plaintext_list = PlaintextList::new(encoded_msg, PlaintextCount(polynomial_size.0));
///
/// // Create a new GlweCiphertext
/// let mut glwe = GlweCiphertext::new(0u64, glwe_size, polynomial_size);
///
/// encrypt_glwe_ciphertext(
/// &glwe_secret_key,
/// &mut glwe,
/// &plaintext_list,
/// glwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// let mut output_plaintext_list = PlaintextList::new(0u64, plaintext_list.plaintext_count());
///
/// decrypt_glwe_ciphertext(&glwe_secret_key, &glwe, &mut output_plaintext_list);
///
/// // Round and remove encoding
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
///
/// output_plaintext_list
/// .iter_mut()
/// .for_each(|elt| *elt.0 = decomposer.closest_representable(*elt.0));
///
/// // Get the raw vector
/// let mut cleartext_list = output_plaintext_list.into_container();
/// // Remove the encoding
/// cleartext_list.iter_mut().for_each(|elt| *elt = *elt >> 60);
/// // Get the list immutably
/// let cleartext_list = cleartext_list;
///
/// // Check we recovered the original message for each plaintext we encrypted
/// cleartext_list.iter().for_each(|&elt| assert_eq!(elt, msg));
/// ```
pub fn encrypt_glwe_ciphertext<Scalar, KeyCont, InputCont, OutputCont, Gen>(
glwe_secret_key: &GlweSecretKey<KeyCont>,
output_glwe_ciphertext: &mut GlweCiphertext<OutputCont>,
input_plaintext_list: &PlaintextList<InputCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
output_glwe_ciphertext.polynomial_size().0 == input_plaintext_list.plaintext_count().0,
"Mismatch between PolynomialSize of output cipertext PlaintextCount of input. \
Got {:?} in output, and {:?} in input.",
output_glwe_ciphertext.polynomial_size(),
input_plaintext_list.plaintext_count()
);
assert!(
output_glwe_ciphertext.glwe_size().to_glwe_dimension() == glwe_secret_key.glwe_dimension(),
"Mismatch between GlweDimension of output cipertext and input secret key. \
Got {:?} in output, and {:?} in secret key.",
output_glwe_ciphertext.glwe_size().to_glwe_dimension(),
glwe_secret_key.glwe_dimension()
);
assert!(
output_glwe_ciphertext.polynomial_size() == glwe_secret_key.polynomial_size(),
"Mismatch between PolynomialSize of output cipertext and input secret key. \
Got {:?} in output, and {:?} in secret key.",
output_glwe_ciphertext.polynomial_size(),
glwe_secret_key.polynomial_size()
);
let (mut mask, mut body) = output_glwe_ciphertext.get_mut_mask_and_body();
generator.fill_slice_with_random_mask(mask.as_mut());
generator.fill_slice_with_random_noise(body.as_mut(), noise_parameters);
polynomial_wrapping_add_assign(
&mut body.as_mut_polynomial(),
&input_plaintext_list.as_polynomial(),
);
polynomial_wrapping_add_multisum_assign(
&mut body.as_mut_polynomial(),
&mask.as_polynomial_list(),
&glwe_secret_key.as_polynomial_list(),
);
}
/// Encrypt a (scalar) plaintext list in [`GLWE ciphertexts`](`GlweCiphertext`) of the output
/// [`GLWE ciphertext list`](`GlweCiphertextList`).
///
/// See this [`formal definition`](`encrypt_glwe_ciphertext#formal-definition`) for the definition
/// of the GLWE encryption algorithm.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::commons::generators::{
/// EncryptionRandomGenerator, SecretRandomGenerator,
/// };
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for GlweCiphertext creation
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
/// let glwe_count = GlweCiphertextCount(2);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the GlweSecretKey
/// let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
/// glwe_size.to_glwe_dimension(),
/// polynomial_size,
/// &mut secret_generator,
/// );
///
/// // Create the plaintext
/// let msg = 3u64;
/// let encoded_msg = msg << 60;
/// let plaintext_list = PlaintextList::new(
/// encoded_msg,
/// PlaintextCount(polynomial_size.0 * glwe_count.0),
/// );
///
/// // Create a new GlweCiphertextList
/// let mut glwe_list = GlweCiphertextList::new(0u64, glwe_size, polynomial_size, glwe_count);
///
/// encrypt_glwe_ciphertext_list(
/// &glwe_secret_key,
/// &plaintext_list,
/// &mut glwe_list,
/// glwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// let mut output_plaintext_list = PlaintextList::new(0u64, plaintext_list.plaintext_count());
///
/// decrypt_glwe_ciphertext_list(&glwe_secret_key, &glwe_list, &mut output_plaintext_list);
///
/// // Round and remove encoding
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
///
/// output_plaintext_list
/// .iter_mut()
/// .for_each(|elt| *elt.0 = decomposer.closest_representable(*elt.0));
///
/// // Get the raw vector
/// let mut cleartext_list = output_plaintext_list.into_container();
/// // Remove the encoding
/// cleartext_list.iter_mut().for_each(|elt| *elt = *elt >> 60);
/// // Get the list immutably
/// let cleartext_list = cleartext_list;
///
/// // Check we recovered the original message for each plaintext we encrypted
/// cleartext_list.iter().for_each(|&elt| assert_eq!(elt, msg));
/// ```
pub fn encrypt_glwe_ciphertext_list<Scalar, KeyCont, InputCont, OutputCont, Gen>(
glwe_secret_key: &GlweSecretKey<KeyCont>,
input_plaintext_list: &PlaintextList<InputCont>,
output_glwe_ciphertext_list: &mut GlweCiphertextList<OutputCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
output_glwe_ciphertext_list.polynomial_size().0
* output_glwe_ciphertext_list.glwe_ciphertext_count().0
== input_plaintext_list.plaintext_count().0,
"Mismatch between required number of plaintexts: {} ({:?} * {:?}) and input \
PlaintextCount: {:?}",
output_glwe_ciphertext_list.polynomial_size().0
* output_glwe_ciphertext_list.glwe_ciphertext_count().0,
output_glwe_ciphertext_list.polynomial_size(),
output_glwe_ciphertext_list.glwe_ciphertext_count(),
input_plaintext_list.plaintext_count()
);
assert!(
output_glwe_ciphertext_list.glwe_size().to_glwe_dimension()
== glwe_secret_key.glwe_dimension(),
"Mismatch between GlweDimension of output cipertext and input secret key. \
Got {:?} in output, and {:?} in secret key.",
output_glwe_ciphertext_list.glwe_size().to_glwe_dimension(),
glwe_secret_key.glwe_dimension()
);
assert!(
output_glwe_ciphertext_list.polynomial_size() == glwe_secret_key.polynomial_size(),
"Mismatch between PolynomialSize of output cipertext and input secret key. \
Got {:?} in output, and {:?} in secret key.",
output_glwe_ciphertext_list.polynomial_size(),
glwe_secret_key.polynomial_size()
);
let polynomial_size = output_glwe_ciphertext_list.polynomial_size();
for (mut ciphertext, encoded) in output_glwe_ciphertext_list
.iter_mut()
.zip(input_plaintext_list.chunks_exact(polynomial_size.0))
{
encrypt_glwe_ciphertext(
glwe_secret_key,
&mut ciphertext,
&encoded,
noise_parameters,
generator,
);
}
}
/// Decrypt a [`GLWE ciphertext`](`GlweCiphertext`) in a (scalar) plaintext list.
///
/// See [`encrypt_glwe_ciphertext`] for usage.
///
/// # Formal Definition
///
/// ## GLWE Decryption
/// ###### inputs:
/// - $\mathsf{CT} = \left( \vec{A} , B \right) \in \mathsf{GLWE}\_{\vec{S}}( \mathsf{PT} )\subseteq
/// \mathcal{R}\_q^{k+1}$: an GLWE ciphertext
/// - $\vec{S} \in\mathcal{R}\_q^k$: a secret key
///
/// ###### outputs:
/// - $\mathsf{PT}\in\mathcal{R}\_q$: a plaintext
///
/// ###### algorithm:
///
/// 1. compute $\mathsf{PT} = B - \left\langle \vec{A} , \vec{S} \right\rangle \in\mathcal{R}\_q$
/// 2. output $\mathsf{PT}$
///
/// **Remark:** Observe that the decryption is followed by a decoding phase that will contain a
/// rounding.
pub fn decrypt_glwe_ciphertext<Scalar, KeyCont, InputCont, OutputCont>(
glwe_secret_key: &GlweSecretKey<KeyCont>,
input_glwe_ciphertext: &GlweCiphertext<InputCont>,
output_plaintext_list: &mut PlaintextList<OutputCont>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
{
assert!(
output_plaintext_list.plaintext_count().0 == input_glwe_ciphertext.polynomial_size().0,
"Mismatched output PlaintextCount {:?} and input PolynomialSize {:?}",
output_plaintext_list.plaintext_count(),
input_glwe_ciphertext.polynomial_size()
);
assert!(
glwe_secret_key.glwe_dimension() == input_glwe_ciphertext.glwe_size().to_glwe_dimension(),
"Mismatched GlweDimension between glwe_secret_key {:?} and input_glwe_ciphertext {:?}",
glwe_secret_key.glwe_dimension(),
input_glwe_ciphertext.glwe_size().to_glwe_dimension()
);
assert!(
glwe_secret_key.polynomial_size() == input_glwe_ciphertext.polynomial_size(),
"Mismatched PolynomialSize between glwe_secret_key {:?} and input_glwe_ciphertext {:?}",
glwe_secret_key.polynomial_size(),
input_glwe_ciphertext.polynomial_size()
);
let (mask, body) = input_glwe_ciphertext.get_mask_and_body();
output_plaintext_list
.as_mut()
.copy_from_slice(body.as_ref());
polynomial_wrapping_sub_multisum_assign(
&mut output_plaintext_list.as_mut_polynomial(),
&mask.as_polynomial_list(),
&glwe_secret_key.as_polynomial_list(),
);
}
/// Decrypt a [`GLWE ciphertext list`](`GlweCiphertextList`) in a (scalar) plaintext list.
///
/// See [`encrypt_glwe_ciphertext_list`] for usage.
pub fn decrypt_glwe_ciphertext_list<Scalar, KeyCont, InputCont, OutputCont>(
glwe_secret_key: &GlweSecretKey<KeyCont>,
input_glwe_ciphertext_list: &GlweCiphertextList<InputCont>,
output_plaintext_list: &mut PlaintextList<OutputCont>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
{
assert!(
output_plaintext_list.plaintext_count().0
== input_glwe_ciphertext_list.polynomial_size().0
* input_glwe_ciphertext_list.glwe_ciphertext_count().0,
"Mismatched output PlaintextCount {:?} and input PolynomialSize ({:?}) * \
GlweCiphertextCount ({:?}) = {:?}",
output_plaintext_list.plaintext_count(),
input_glwe_ciphertext_list.polynomial_size(),
input_glwe_ciphertext_list.glwe_ciphertext_count(),
input_glwe_ciphertext_list.polynomial_size().0
* input_glwe_ciphertext_list.glwe_ciphertext_count().0
);
assert!(
glwe_secret_key.glwe_dimension()
== input_glwe_ciphertext_list.glwe_size().to_glwe_dimension(),
"Mismatched GlweDimension between glwe_secret_key {:?} and input_glwe_ciphertext_list {:?}",
glwe_secret_key.glwe_dimension(),
input_glwe_ciphertext_list.glwe_size().to_glwe_dimension()
);
assert!(
glwe_secret_key.polynomial_size() == input_glwe_ciphertext_list.polynomial_size(),
"Mismatched PolynomialSize between glwe_secret_key {:?} and input_glwe_ciphertext_list {:?}",
glwe_secret_key.polynomial_size(),
input_glwe_ciphertext_list.polynomial_size()
);
for (ciphertext, mut output_sublist) in input_glwe_ciphertext_list
.iter()
.zip(output_plaintext_list.chunks_exact_mut(input_glwe_ciphertext_list.polynomial_size().0))
{
decrypt_glwe_ciphertext(glwe_secret_key, &ciphertext, &mut output_sublist);
}
}
/// A trivial encryption uses a zero mask and no noise.
///
/// It is absolutely not secure, as the body contains a direct copy of the plaintext.
/// However, it is useful for some FHE algorithms taking public information as input. For
/// example, a trivial GLWE encryption of a public lookup table is used in the programmable
/// bootstrap.
///
/// By definition a trivial encryption can be decrypted by any [`GLWE secret key`](`GlweSecretKey`).
///
/// Trivially encrypt an input (scalar) plaintext list in a [`GLWE ciphertext`](`GlweCiphertext`).
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::commons::generators::SecretRandomGenerator;
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for GlweCiphertext creation
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the plaintext
/// let msg = 3u64;
/// let encoded_msg = msg << 60;
/// let plaintext_list = PlaintextList::new(encoded_msg, PlaintextCount(polynomial_size.0));
///
/// // Create a new GlweCiphertext
/// let mut glwe = GlweCiphertext::new(0u64, glwe_size, polynomial_size);
///
/// trivially_encrypt_glwe_ciphertext(&mut glwe, &plaintext_list);
///
/// // Here we show the content of the trivial encryption is actually the input data in clear and
/// // that the mask is full of 0s
/// assert_eq!(glwe.get_body().as_ref(), plaintext_list.as_ref());
/// glwe.get_mask()
/// .as_ref()
/// .iter()
/// .for_each(|&elt| assert_eq!(elt, 0));
///
/// // Now we demonstrate that any random GlweSecretKey can be used to decrypt it.
/// let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
/// glwe_size.to_glwe_dimension(),
/// polynomial_size,
/// &mut secret_generator,
/// );
///
/// let mut output_plaintext_list = PlaintextList::new(0u64, plaintext_list.plaintext_count());
///
/// decrypt_glwe_ciphertext(&glwe_secret_key, &glwe, &mut output_plaintext_list);
///
/// // Again the trivial encryption encrypts _nothing_
/// assert_eq!(output_plaintext_list.as_ref(), glwe.get_body().as_ref());
/// ```
pub fn trivially_encrypt_glwe_ciphertext<Scalar, InputCont, OutputCont>(
output: &mut GlweCiphertext<OutputCont>,
encoded: &PlaintextList<InputCont>,
) where
Scalar: UnsignedTorus,
OutputCont: ContainerMut<Element = Scalar>,
InputCont: Container<Element = Scalar>,
{
assert!(
encoded.plaintext_count().0 == output.polynomial_size().0,
"Mismatched input PlaintextCount {:?} and output PolynomialSize {:?}",
encoded.plaintext_count(),
output.polynomial_size()
);
let (mut mask, mut body) = output.get_mut_mask_and_body();
mask.as_mut().fill(Scalar::ZERO);
body.as_mut().copy_from_slice(encoded.as_ref());
}
/// A trivial encryption uses a zero mask and no noise.
///
/// It is absolutely not secure, as the body contains a direct copy of the plaintext.
/// However, it is useful for some FHE algorithms taking public information as input. For
/// example, a trivial GLWE encryption of a public lookup table is used in the programmable
/// bootstrap.
///
/// By definition a trivial encryption can be decrypted by any [`GLWE secret key`](`GlweSecretKey`).
///
/// Allocate a new [`GLWE ciphertext`](`GlweCiphertext`) and trivially encrypt an input (scalar)
/// plaintext list in it.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::commons::generators::SecretRandomGenerator;
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for GlweCiphertext creation
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the plaintext
/// let msg = 3u64;
/// let encoded_msg = msg << 60;
/// let plaintext_list = PlaintextList::new(encoded_msg, PlaintextCount(polynomial_size.0));
///
/// // Create a new GlweCiphertext
/// let mut glwe = allocate_and_trivially_encrypt_new_glwe_ciphertext(glwe_size, &plaintext_list);
///
/// // Here we show the content of the trivial encryption is actually the input data in clear and
/// // that the mask is full of 0s
/// assert_eq!(glwe.get_body().as_ref(), plaintext_list.as_ref());
/// glwe.get_mask()
/// .as_ref()
/// .iter()
/// .for_each(|&elt| assert_eq!(elt, 0));
///
/// // Now we demonstrate that any random GlweSecretKey can be used to decrypt it.
/// let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
/// glwe_size.to_glwe_dimension(),
/// polynomial_size,
/// &mut secret_generator,
/// );
///
/// let mut output_plaintext_list = PlaintextList::new(0u64, plaintext_list.plaintext_count());
///
/// decrypt_glwe_ciphertext(&glwe_secret_key, &glwe, &mut output_plaintext_list);
///
/// // Again the trivial encryption encrypts _nothing_
/// assert_eq!(output_plaintext_list.as_ref(), glwe.get_body().as_ref());
/// ```
pub fn allocate_and_trivially_encrypt_new_glwe_ciphertext<Scalar, InputCont>(
glwe_size: GlweSize,
encoded: &PlaintextList<InputCont>,
) -> GlweCiphertextOwned<Scalar>
where
Scalar: UnsignedTorus,
InputCont: Container<Element = Scalar>,
{
let polynomial_size = PolynomialSize(encoded.plaintext_count().0);
let mut new_ct = GlweCiphertextOwned::new(Scalar::ZERO, glwe_size, polynomial_size);
let mut body = new_ct.get_mut_body();
body.as_mut().copy_from_slice(encoded.as_ref());
new_ct
}

View File

@@ -0,0 +1,128 @@
use crate::core_crypto::algorithms::slice_algorithms::*;
use crate::core_crypto::commons::numeric::UnsignedInteger;
use crate::core_crypto::commons::parameters::{MonomialDegree, *};
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// Extract the nth coefficient from the body of a [`GLWE Ciphertext`](`GlweCiphertext`) as an
/// [`LWE ciphertext`](`LweCiphertext`).
///
/// # Formal definition
///
/// This operation is usually referred to as a _sample extract_ in the literature.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::commons::generators::{
/// EncryptionRandomGenerator, SecretRandomGenerator,
/// };
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for GlweCiphertext creation
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the GlweSecretKey
/// let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
/// glwe_size.to_glwe_dimension(),
/// polynomial_size,
/// &mut secret_generator,
/// );
///
/// // Create the plaintext
/// let msg = 3u64;
/// let encoded_msg = msg << 60;
/// let mut plaintext_list = PlaintextList::new(encoded_msg, PlaintextCount(polynomial_size.0));
///
/// let special_value = 15;
/// *plaintext_list.get_mut(42).0 = 15 << 60;
///
/// // Create a new GlweCiphertext
/// let mut glwe = GlweCiphertext::new(0u64, glwe_size, polynomial_size);
///
/// encrypt_glwe_ciphertext(
/// &glwe_secret_key,
/// &mut glwe,
/// &plaintext_list,
/// glwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// // Now we get the equivalent LweSecretKey from the GlweSecretKey
/// let equivalent_lwe_sk = glwe_secret_key.clone().into_lwe_secret_key();
///
/// let mut extracted_sample =
/// LweCiphertext::new(0u64, equivalent_lwe_sk.lwe_dimension().to_lwe_size());
///
/// // Here we chose to extract sample at index 42 (corresponding to the MonomialDegree(42))
/// extract_lwe_sample_from_glwe_ciphertext(&glwe, &mut extracted_sample, MonomialDegree(42));
///
/// let decrypted_plaintext = decrypt_lwe_ciphertext(&equivalent_lwe_sk, &extracted_sample);
///
/// // Round and remove encoding
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
///
/// let recovered_message = decomposer.closest_representable(decrypted_plaintext.0) >> 60;
///
/// // We check we recover our special value instead of the 3 stored in all other slots of the
/// // GlweCiphertext
/// assert_eq!(special_value, recovered_message);
/// ```
pub fn extract_lwe_sample_from_glwe_ciphertext<Scalar, InputCont, OutputCont>(
input_glwe: &GlweCiphertext<InputCont>,
output_lwe: &mut LweCiphertext<OutputCont>,
nth: MonomialDegree,
) where
Scalar: UnsignedInteger,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
{
assert!(
input_glwe.glwe_size().to_glwe_dimension().0 * input_glwe.polynomial_size().0
== output_lwe.lwe_size().to_lwe_dimension().0,
"Mismatch between equivalent LweDimension of input ciphertext and output ciphertext. \
Got {:?} for input and {:?} for output.",
LweDimension(input_glwe.glwe_size().to_glwe_dimension().0 * input_glwe.polynomial_size().0),
output_lwe.lwe_size().to_lwe_dimension(),
);
// We retrieve the bodies and masks of the two ciphertexts.
let (mut lwe_mask, lwe_body) = output_lwe.get_mut_mask_and_body();
let (glwe_mask, glwe_body) = input_glwe.get_mask_and_body();
// We copy the body
*lwe_body.0 = glwe_body.as_ref()[nth.0];
// We copy the mask (each polynomial is in the wrong order)
lwe_mask.as_mut().copy_from_slice(glwe_mask.as_ref());
// We compute the number of elements which must be
// turned into their opposite
let opposite_count = input_glwe.polynomial_size().0 - nth.0 - 1;
// We loop through the polynomials
for lwe_mask_poly in lwe_mask.as_mut().chunks_mut(input_glwe.polynomial_size().0) {
// We reverse the polynomial
lwe_mask_poly.reverse();
// We compute the opposite of the proper coefficients
slice_wrapping_opposite_assign(&mut lwe_mask_poly[0..opposite_count]);
// We rotate the polynomial properly
lwe_mask_poly.rotate_left(opposite_count);
}
}

View File

@@ -0,0 +1,71 @@
use crate::core_crypto::commons::generators::SecretRandomGenerator;
use crate::core_crypto::commons::math::random::{RandomGenerable, UniformBinary};
use crate::core_crypto::commons::numeric::Numeric;
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// Allocate a new [`GLWE secret key`](`GlweSecretKey`) and fill it with uniformly random binary
/// coefficients.
///
/// See [`encrypt_glwe_ciphertext`](`super::glwe_encryption::encrypt_glwe_ciphertext`)
/// for usage.
pub fn allocate_and_generate_new_binary_glwe_secret_key<Scalar, Gen>(
glwe_dimension: GlweDimension,
polynomial_size: PolynomialSize,
generator: &mut SecretRandomGenerator<Gen>,
) -> GlweSecretKeyOwned<Scalar>
where
Scalar: RandomGenerable<UniformBinary> + Numeric,
Gen: ByteRandomGenerator,
{
let mut glwe_secret_key =
GlweSecretKeyOwned::new(Scalar::ZERO, glwe_dimension, polynomial_size);
generate_binary_glwe_secret_key(&mut glwe_secret_key, generator);
glwe_secret_key
}
/// Fill a [`GLWE secret key`](`GlweSecretKey`) with uniformly random binary coefficients.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::commons::generators::SecretRandomGenerator;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for GlweCiphertext creation
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// let mut glwe_secret_key =
/// GlweSecretKey::new(0u64, glwe_size.to_glwe_dimension(), polynomial_size);
///
/// generate_binary_glwe_secret_key(&mut glwe_secret_key, &mut secret_generator);
///
/// // Check all coefficients are not zero as we just generated a new key
/// // Note probability of this assert failing is (1/2)^polynomial_size or ~5.6 * 10^-309 for a
/// // polynomial size of 1024.
/// assert!(glwe_secret_key.as_ref().iter().all(|&elt| elt == 0) == false);
/// ```
pub fn generate_binary_glwe_secret_key<Scalar, InCont, Gen>(
glwe_secret_key: &mut GlweSecretKey<InCont>,
generator: &mut SecretRandomGenerator<Gen>,
) where
Scalar: RandomGenerable<UniformBinary>,
InCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
generator.fill_slice_with_random_uniform_binary(glwe_secret_key.as_mut())
}

View File

@@ -0,0 +1,59 @@
use crate::core_crypto::commons::computation_buffers::ComputationBuffers;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use crate::core_crypto::fft_impl::crypto::bootstrap::FourierLweBootstrapKey;
use crate::core_crypto::fft_impl::crypto::ggsw::fill_with_forward_fourier_scratch;
use crate::core_crypto::fft_impl::math::fft::{Fft, FftView};
use concrete_fft::c64;
use dyn_stack::{DynStack, SizeOverflow, StackReq};
/// Convert an [`LWE bootstrap key`](`LweBootstrapKey`) with standard coefficients to the Fourier
/// domain.
pub fn convert_standard_lwe_bootstrap_key_to_fourier<Scalar, InputCont, OutputCont>(
input_bsk: &LweBootstrapKey<InputCont>,
output_bsk: &mut FourierLweBootstrapKey<OutputCont>,
) where
Scalar: UnsignedTorus,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = c64>,
{
let mut buffers = ComputationBuffers::new();
let fft = Fft::new(input_bsk.polynomial_size());
let fft = fft.as_view();
buffers.resize(
convert_standard_lwe_bootstrap_key_to_fourier_mem_optimized_scratch(fft)
.unwrap()
.unaligned_bytes_required(),
);
let stack = buffers.stack();
output_bsk
.as_mut_view()
.fill_with_forward_fourier(input_bsk.as_view(), fft, stack);
}
/// Memory optimized version of [`convert_standard_lwe_bootstrap_key_to_fourier`].
pub fn convert_standard_lwe_bootstrap_key_to_fourier_mem_optimized<Scalar, InputCont, OutputCont>(
input_bsk: &LweBootstrapKey<InputCont>,
output_bsk: &mut FourierLweBootstrapKey<OutputCont>,
fft: FftView<'_>,
stack: DynStack<'_>,
) where
Scalar: UnsignedTorus,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = c64>,
{
output_bsk
.as_mut_view()
.fill_with_forward_fourier(input_bsk.as_view(), fft, stack);
}
/// Return the required memory for [`convert_standard_lwe_bootstrap_key_to_fourier_mem_optimized`].
pub fn convert_standard_lwe_bootstrap_key_to_fourier_mem_optimized_scratch(
fft: FftView<'_>,
) -> Result<StackReq, SizeOverflow> {
fill_with_forward_fourier_scratch(fft)
}

View File

@@ -0,0 +1,310 @@
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::dispersion::DispersionParameter;
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use rayon::prelude::*;
pub fn generate_lwe_bootstrap_key<Scalar, InputKeyCont, OutputKeyCont, OutputCont, Gen>(
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
output: &mut LweBootstrapKey<OutputCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
output.input_lwe_dimension() == input_lwe_secret_key.lwe_dimension(),
"Mismatched LweDimension between input LWE secret key and LWE bootstrap key. \
Input LWE secret key LweDimension: {:?}, LWE bootstrap key input LweDimension {:?}.",
input_lwe_secret_key.lwe_dimension(),
output.input_lwe_dimension()
);
assert!(
output.glwe_size() == output_glwe_secret_key.glwe_dimension().to_glwe_size(),
"Mismatched GlweSize between output GLWE secret key and LWE bootstrap key. \
Output GLWE secret key GlweSize: {:?}, LWE bootstrap key GlweSize {:?}.",
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
output.glwe_size()
);
assert!(
output.polynomial_size() == output_glwe_secret_key.polynomial_size(),
"Mismatched PolynomialSize between output GLWE secret key and LWE bootstrap key. \
Output GLWE secret key PolynomialSize: {:?}, LWE bootstrap key PolynomialSize {:?}.",
output_glwe_secret_key.polynomial_size(),
output.polynomial_size()
);
let gen_iter = generator
.fork_bsk_to_ggsw::<Scalar>(
output.input_lwe_dimension(),
output.decomposition_level_count(),
output.glwe_size(),
output.polynomial_size(),
)
.unwrap();
for ((mut ggsw, &input_key_element), mut generator) in output
.iter_mut()
.zip(input_lwe_secret_key.as_ref())
.zip(gen_iter)
{
encrypt_ggsw_ciphertext(
output_glwe_secret_key,
&mut ggsw,
Plaintext(input_key_element),
noise_parameters,
&mut generator,
);
}
}
pub fn allocate_and_generate_new_lwe_bootstrap_key<Scalar, InputKeyCont, OutputKeyCont, Gen>(
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) -> LweBootstrapKeyOwned<Scalar>
where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: Container<Element = Scalar>,
Gen: ByteRandomGenerator,
{
let mut bsk = LweBootstrapKeyOwned::new(
Scalar::ZERO,
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
output_glwe_secret_key.polynomial_size(),
decomp_base_log,
decomp_level_count,
input_lwe_secret_key.lwe_dimension(),
);
generate_lwe_bootstrap_key(
input_lwe_secret_key,
output_glwe_secret_key,
&mut bsk,
noise_parameters,
generator,
);
bsk
}
/// Parallel variant of [`generate_lwe_bootstrap_key`], it is recommended to use this function for
/// better key generation times as LWE bootstrapping keys can be quite large.
pub fn par_generate_lwe_bootstrap_key<Scalar, InputKeyCont, OutputKeyCont, OutputCont, Gen>(
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
output: &mut LweBootstrapKey<OutputCont>,
noise_parameters: impl DispersionParameter + Sync,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus + Sync + Send,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: Container<Element = Scalar> + Sync,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ParallelByteRandomGenerator,
{
assert!(
output.input_lwe_dimension() == input_lwe_secret_key.lwe_dimension(),
"Mismatched LweDimension between input LWE secret key and LWE bootstrap key. \
Input LWE secret key LweDimension: {:?}, LWE bootstrap key input LweDimension {:?}.",
input_lwe_secret_key.lwe_dimension(),
output.input_lwe_dimension()
);
assert!(
output.glwe_size() == output_glwe_secret_key.glwe_dimension().to_glwe_size(),
"Mismatched GlweSize between output GLWE secret key and LWE bootstrap key. \
Output GLWE secret key GlweSize: {:?}, LWE bootstrap key GlweSize {:?}.",
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
output.glwe_size()
);
assert!(
output.polynomial_size() == output_glwe_secret_key.polynomial_size(),
"Mismatched PolynomialSize between output GLWE secret key and LWE bootstrap key. \
Output GLWE secret key PolynomialSize: {:?}, LWE bootstrap key PolynomialSize {:?}.",
output_glwe_secret_key.polynomial_size(),
output.polynomial_size()
);
let gen_iter = generator
.par_fork_bsk_to_ggsw::<Scalar>(
output.input_lwe_dimension(),
output.decomposition_level_count(),
output.glwe_size(),
output.polynomial_size(),
)
.unwrap();
output
.par_iter_mut()
.zip(input_lwe_secret_key.as_ref().par_iter())
.zip(gen_iter)
.for_each(|((mut ggsw, &input_key_element), mut generator)| {
par_encrypt_ggsw_ciphertext(
output_glwe_secret_key,
&mut ggsw,
Plaintext(input_key_element),
noise_parameters,
&mut generator,
);
})
}
pub fn par_allocate_and_generate_new_lwe_bootstrap_key<Scalar, InputKeyCont, OutputKeyCont, Gen>(
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
noise_parameters: impl DispersionParameter + Sync,
generator: &mut EncryptionRandomGenerator<Gen>,
) -> LweBootstrapKeyOwned<Scalar>
where
Scalar: UnsignedTorus + Sync + Send,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: Container<Element = Scalar> + Sync,
Gen: ParallelByteRandomGenerator,
{
let mut bsk = LweBootstrapKeyOwned::new(
Scalar::ZERO,
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
output_glwe_secret_key.polynomial_size(),
decomp_base_log,
decomp_level_count,
input_lwe_secret_key.lwe_dimension(),
);
par_generate_lwe_bootstrap_key(
input_lwe_secret_key,
output_glwe_secret_key,
&mut bsk,
noise_parameters,
generator,
);
bsk
}
#[cfg(test)]
mod parallel_test {
use crate::core_crypto::algorithms::{
allocate_and_generate_new_binary_glwe_secret_key,
allocate_and_generate_new_binary_lwe_secret_key, generate_lwe_bootstrap_key,
par_generate_lwe_bootstrap_key,
};
use crate::core_crypto::commons::dispersion::StandardDev;
use crate::core_crypto::commons::generators::{DeterministicSeeder, EncryptionRandomGenerator};
use crate::core_crypto::commons::math::random::Seed;
use crate::core_crypto::commons::math::torus::UnsignedTorus;
use crate::core_crypto::commons::parameters::{
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
};
use crate::core_crypto::commons::test_tools::new_secret_random_generator;
use crate::core_crypto::entities::LweBootstrapKeyOwned;
use concrete_csprng::generators::SoftwareRandomGenerator;
fn test_refactored_bsk_parallel_gen_equivalence<T: UnsignedTorus + Send + Sync>() {
for _ in 0..10 {
let lwe_dim =
LweDimension(crate::core_crypto::commons::test_tools::random_usize_between(5..10));
let glwe_dim =
GlweDimension(crate::core_crypto::commons::test_tools::random_usize_between(5..10));
let poly_size = PolynomialSize(
crate::core_crypto::commons::test_tools::random_usize_between(5..10),
);
let level = DecompositionLevelCount(
crate::core_crypto::commons::test_tools::random_usize_between(2..5),
);
let base_log = DecompositionBaseLog(
crate::core_crypto::commons::test_tools::random_usize_between(2..5),
);
let mask_seed = Seed(crate::core_crypto::commons::test_tools::any_usize() as u128);
let deterministic_seeder_seed =
Seed(crate::core_crypto::commons::test_tools::any_usize() as u128);
let mut secret_generator = new_secret_random_generator();
let lwe_sk =
allocate_and_generate_new_binary_lwe_secret_key(lwe_dim, &mut secret_generator);
let glwe_sk = allocate_and_generate_new_binary_glwe_secret_key(
glwe_dim,
poly_size,
&mut secret_generator,
);
let mut parallel_bsk = LweBootstrapKeyOwned::new(
T::ZERO,
glwe_dim.to_glwe_size(),
poly_size,
base_log,
level,
lwe_dim,
);
let mut encryption_generator =
EncryptionRandomGenerator::<SoftwareRandomGenerator>::new(
mask_seed,
&mut DeterministicSeeder::<SoftwareRandomGenerator>::new(
deterministic_seeder_seed,
),
);
par_generate_lwe_bootstrap_key(
&lwe_sk,
&glwe_sk,
&mut parallel_bsk,
StandardDev::from_standard_dev(10.),
&mut encryption_generator,
);
let mut sequential_bsk = LweBootstrapKeyOwned::new(
T::ZERO,
glwe_dim.to_glwe_size(),
poly_size,
base_log,
level,
lwe_dim,
);
let mut encryption_generator =
EncryptionRandomGenerator::<SoftwareRandomGenerator>::new(
mask_seed,
&mut DeterministicSeeder::<SoftwareRandomGenerator>::new(
deterministic_seeder_seed,
),
);
generate_lwe_bootstrap_key(
&lwe_sk,
&glwe_sk,
&mut sequential_bsk,
StandardDev::from_standard_dev(10.),
&mut encryption_generator,
);
assert_eq!(parallel_bsk.as_ref(), sequential_bsk.as_ref());
}
}
#[test]
fn test_refactored_bsk_parallel_gen_equivalence_u32() {
test_refactored_bsk_parallel_gen_equivalence::<u32>()
}
#[test]
fn test_refactored_bsk_parallel_gen_equivalence_u64() {
test_refactored_bsk_parallel_gen_equivalence::<u64>()
}
}

View File

@@ -0,0 +1,791 @@
//! Module containing functions related to LWE ciphertext encryption and decryption
use crate::core_crypto::algorithms::slice_algorithms::*;
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::dispersion::DispersionParameter;
use crate::core_crypto::commons::generators::{EncryptionRandomGenerator, SecretRandomGenerator};
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use rayon::prelude::*;
/// Encrypt an input plaintext in an output [`LWE ciphertext`](`LweCiphertext`).
///
/// # Formal Definition
///
/// ## LWE Encryption
/// ###### inputs:
/// - $\mathsf{pt}\in\mathbb{Z}\_q$: a plaintext
/// - $\vec{s}\in\mathbb{Z}\_q^n$: a secret key
/// - $\mathcal{D\_{\sigma^2,\mu}}$: a normal distribution of variance $\sigma^2$ and a mean $\mu$
///
/// ###### outputs:
/// - $\mathsf{ct} = \left( \vec{a} , b\right) \in \mathsf{LWE}^n\_{\vec{s}}( \mathsf{pt} )\subseteq
/// \mathbb{Z}\_q^{(n+1)}$: an LWE ciphertext
///
/// ###### algorithm:
/// 1. uniformly sample a vector $\vec{a}\in\mathbb{Z}\_q^n$
/// 2. sample an integer error term $e \hookleftarrow \mathcal{D\_{\sigma^2,\mu}}$
/// 3. compute $b = \left\langle \vec{a} , \vec{s} \right\rangle + \mathsf{pt} + e \in\mathbb{Z}\_q$
/// 4. output $\left( \vec{a} , b\right)$
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::commons::generators::{
/// EncryptionRandomGenerator, SecretRandomGenerator,
/// };
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweCiphertext creation
/// let lwe_dimension = LweDimension(742);
/// let lwe_modular_std_dev = StandardDev(0.000007069849454709433);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the LweSecretKey
/// let lwe_secret_key =
/// allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
///
/// // Create the plaintext
/// let msg = 3u64;
/// let plaintext = Plaintext(msg << 60);
///
/// // Create a new LweCiphertext
/// let mut lwe = LweCiphertext::new(0u64, lwe_dimension.to_lwe_size());
///
/// encrypt_lwe_ciphertext(
/// &lwe_secret_key,
/// &mut lwe,
/// plaintext,
/// lwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// let decrypted_plaintext = decrypt_lwe_ciphertext(&lwe_secret_key, &lwe);
///
/// // Round and remove encoding
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
///
/// let rounded = decomposer.closest_representable(decrypted_plaintext.0);
///
/// // Remove the encoding
/// let cleartext = rounded >> 60;
///
/// // Check we recovered the original message
/// assert_eq!(cleartext, msg);
/// ```
pub fn encrypt_lwe_ciphertext<Scalar, KeyCont, OutputCont, Gen>(
lwe_secret_key: &LweSecretKey<KeyCont>,
output: &mut LweCiphertext<OutputCont>,
encoded: Plaintext<Scalar>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
output.lwe_size().to_lwe_dimension() == lwe_secret_key.lwe_dimension(),
"Mismatch between LweDimension of output cipertext and input secret key. \
Got {:?} in output, and {:?} in secret key.",
output.lwe_size().to_lwe_dimension(),
lwe_secret_key.lwe_dimension()
);
let (mut mask, body) = output.get_mut_mask_and_body();
generator.fill_slice_with_random_mask(mask.as_mut());
// generate an error from the normal distribution described by std_dev
*body.0 = generator.random_noise(noise_parameters);
// compute the multisum between the secret key and the mask
*body.0 = (*body.0).wrapping_add(slice_wrapping_dot_product(
mask.as_ref(),
lwe_secret_key.as_ref(),
));
*body.0 = (*body.0).wrapping_add(encoded.0);
}
/// Allocate a new [`LWE ciphertext`](`LweCiphertext`) and encrypt an input plaintext in it.
///
/// See this [`formal definition`](`encrypt_lwe_ciphertext#formal-definition`) for the definition
/// of the LWE encryption algorithm.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::commons::generators::{
/// EncryptionRandomGenerator, SecretRandomGenerator,
/// };
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweCiphertext creation
/// let lwe_dimension = LweDimension(742);
/// let lwe_modular_std_dev = StandardDev(0.000007069849454709433);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the LweSecretKey
/// let lwe_secret_key =
/// allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
///
/// // Create the plaintext
/// let msg = 3u64;
/// let plaintext = Plaintext(msg << 60);
///
/// // Create a new LweCiphertext
/// let lwe = allocate_and_encrypt_new_lwe_ciphertext(
/// &lwe_secret_key,
/// plaintext,
/// lwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// let decrypted_plaintext = decrypt_lwe_ciphertext(&lwe_secret_key, &lwe);
///
/// // Round and remove encoding
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
///
/// let rounded = decomposer.closest_representable(decrypted_plaintext.0);
///
/// // Remove the encoding
/// let cleartext = rounded >> 60;
///
/// // Check we recovered the original message
/// assert_eq!(cleartext, msg);
/// ```
pub fn allocate_and_encrypt_new_lwe_ciphertext<Scalar, KeyCont, Gen>(
lwe_secret_key: &LweSecretKey<KeyCont>,
encoded: Plaintext<Scalar>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) -> LweCiphertextOwned<Scalar>
where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
Gen: ByteRandomGenerator,
{
let mut new_ct =
LweCiphertextOwned::new(Scalar::ZERO, lwe_secret_key.lwe_dimension().to_lwe_size());
encrypt_lwe_ciphertext(
lwe_secret_key,
&mut new_ct,
encoded,
noise_parameters,
generator,
);
new_ct
}
/// A trivial encryption uses a zero mask and no noise.
///
/// It is absolutely not secure, as the body contains a direct copy of the plaintext.
/// However, it is useful for some FHE algorithms taking public information as input.
///
/// By definition a trivial encryption can be decrypted by any [`LWE secret key`](`LweSecretKey`).
///
/// Trivially encrypt an input plaintext in an [`LWE ciphertext`](`LweCiphertext`).
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::commons::generators::SecretRandomGenerator;
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweCiphertext creation
/// let lwe_dimension = LweDimension(742);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the plaintext
/// let msg = 3u64;
/// let plaintext = Plaintext(msg << 60);
///
/// // Create a new LweCiphertext
/// let mut lwe = LweCiphertext::new(0u64, lwe_dimension.to_lwe_size());
///
/// trivially_encrypt_lwe_ciphertext(&mut lwe, plaintext);
///
/// // Here we show the content of the trivial encryption is actually the input data in clear and
/// // that the mask is full of 0s
/// assert_eq!(*lwe.get_body().0, plaintext.0);
/// lwe.get_mask()
/// .as_ref()
/// .iter()
/// .for_each(|&elt| assert_eq!(elt, 0));
///
/// // Now we demonstrate that any random LweSecretKey can be used to decrypt it.
/// let lwe_secret_key =
/// allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
///
/// let decrypted_plaintext = decrypt_lwe_ciphertext(&lwe_secret_key, &lwe);
///
/// // Again the trivial encryption encrypts _nothing_
/// assert_eq!(decrypted_plaintext.0, *lwe.get_body().0);
/// ```
pub fn trivially_encrypt_lwe_ciphertext<Scalar, OutputCont>(
output: &mut LweCiphertext<OutputCont>,
encoded: Plaintext<Scalar>,
) where
Scalar: UnsignedTorus,
OutputCont: ContainerMut<Element = Scalar>,
{
output
.get_mut_mask()
.as_mut()
.iter_mut()
.for_each(|elt| *elt = Scalar::ZERO);
*output.get_mut_body().0 = encoded.0
}
/// A trivial encryption uses a zero mask and no noise.
///
/// It is absolutely not secure, as the body contains a direct copy of the plaintext.
/// However, it is useful for some FHE algorithms taking public information as input.
///
/// By definition a trivial encryption can be decrypted by any [`LWE secret key`](`LweSecretKey`).
///
/// Allocate a new [`LWE ciphertext`](`LweCiphertext`) and trivially encrypt an input plaintext in
/// it.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::commons::generators::SecretRandomGenerator;
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweCiphertext creation
/// let lwe_dimension = LweDimension(742);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the plaintext
/// let msg = 3u64;
/// let plaintext = Plaintext(msg << 60);
///
/// // Create a new LweCiphertext
/// let mut lwe =
/// allocate_and_trivially_encrypt_new_lwe_ciphertext(lwe_dimension.to_lwe_size(), plaintext);
///
/// // Here we show the content of the trivial encryption is actually the input data in clear and
/// // that the mask is full of 0s
/// assert_eq!(*lwe.get_body().0, plaintext.0);
/// lwe.get_mask()
/// .as_ref()
/// .iter()
/// .for_each(|&elt| assert_eq!(elt, 0));
///
/// // Now we demonstrate that any random LweSecretKey can be used to decrypt it.
/// let lwe_secret_key =
/// allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
///
/// let decrypted_plaintext = decrypt_lwe_ciphertext(&lwe_secret_key, &lwe);
///
/// // Again the trivial encryption encrypts _nothing_
/// assert_eq!(decrypted_plaintext.0, *lwe.get_body().0);
/// ```
pub fn allocate_and_trivially_encrypt_new_lwe_ciphertext<Scalar>(
lwe_size: LweSize,
encoded: Plaintext<Scalar>,
) -> LweCiphertextOwned<Scalar>
where
Scalar: UnsignedTorus,
{
let mut new_ct = LweCiphertextOwned::new(Scalar::ZERO, lwe_size);
*new_ct.get_mut_body().0 = encoded.0;
new_ct
}
/// Decrypt an [`LWE ciphertext`](`LweCiphertext`) and return a noisy plaintext.
///
/// See [`encrypt_lwe_ciphertext`] for usage.
///
/// # Formal Definition
///
/// ## LWE Decryption
/// ###### inputs:
/// - $\mathsf{ct} = \left( \vec{a} , b\right) \in \mathsf{LWE}^n\_{\vec{s}}( \mathsf{pt} )\subseteq
/// \mathbb{Z}\_q^{(n+1)}$: an LWE ciphertext
/// - $\vec{s}\in\mathbb{Z}\_q^n$: a secret key
///
/// ###### outputs:
/// - $\mathsf{pt}\in\mathbb{Z}\_q$: a plaintext
///
/// ###### algorithm:
/// 1. compute $\mathsf{pt} = b - \left\langle \vec{a} , \vec{s} \right\rangle \in\mathbb{Z}\_q$
/// 2. output $\mathsf{pt}$
///
/// **Remark:** Observe that the decryption is followed by a decoding phase that will contain a
/// rounding.
pub fn decrypt_lwe_ciphertext<Scalar, KeyCont, InputCont>(
lwe_secret_key: &LweSecretKey<KeyCont>,
lwe_ciphertext: &LweCiphertext<InputCont>,
) -> Plaintext<Scalar>
where
Scalar: UnsignedInteger,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
{
assert!(
lwe_ciphertext.lwe_size().to_lwe_dimension() == lwe_secret_key.lwe_dimension(),
"Mismatch between LweDimension of output cipertext and input secret key. \
Got {:?} in output, and {:?} in secret key.",
lwe_ciphertext.lwe_size().to_lwe_dimension(),
lwe_secret_key.lwe_dimension()
);
let (mask, body) = lwe_ciphertext.get_mask_and_body();
Plaintext(body.0.wrapping_sub(slice_wrapping_dot_product(
mask.as_ref(),
lwe_secret_key.as_ref(),
)))
}
/// Encrypt an input plaintext list in an output [`LWE ciphertext list`](`LweCiphertextList`).
///
/// See this [`formal definition`](`encrypt_lwe_ciphertext#formal-definition`) for the definition
/// of the LWE encryption algorithm.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::commons::generators::{
/// EncryptionRandomGenerator, SecretRandomGenerator,
/// };
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweCiphertext creation
/// let lwe_dimension = LweDimension(742);
/// let lwe_ciphertext_count = LweCiphertextCount(2);
/// let lwe_modular_std_dev = StandardDev(0.000007069849454709433);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the LweSecretKey
/// let lwe_secret_key =
/// allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
///
/// // Create the plaintext
/// let msg = 3u64;
/// let encoded_msg = msg << 60;
/// let plaintext_list = PlaintextList::new(encoded_msg, PlaintextCount(lwe_ciphertext_count.0));
///
/// // Create a new LweCiphertext
/// let mut lwe_list =
/// LweCiphertextList::new(0u64, lwe_dimension.to_lwe_size(), lwe_ciphertext_count);
///
/// encrypt_lwe_ciphertext_list(
/// &lwe_secret_key,
/// &mut lwe_list,
/// &plaintext_list,
/// lwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// let mut output_plaintext_list =
/// PlaintextList::new(0u64, PlaintextCount(lwe_list.lwe_ciphertext_count().0));
/// decrypt_lwe_ciphertext_list(&lwe_secret_key, &lwe_list, &mut output_plaintext_list);
///
/// // Round and remove encoding
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
///
/// output_plaintext_list
/// .iter_mut()
/// .for_each(|elt| *elt.0 = decomposer.closest_representable(*elt.0));
///
/// // Get the raw vector
/// let mut cleartext_list = output_plaintext_list.into_container();
/// // Remove the encoding
/// cleartext_list.iter_mut().for_each(|elt| *elt = *elt >> 60);
/// // Get the list immutably
/// let cleartext_list = cleartext_list;
///
/// // Check we recovered the original message for each plaintext we encrypted
/// cleartext_list.iter().for_each(|&elt| assert_eq!(elt, msg));
/// ```
pub fn encrypt_lwe_ciphertext_list<Scalar, KeyCont, OutputCont, InputCont, Gen>(
lwe_secret_key: &LweSecretKey<KeyCont>,
output: &mut LweCiphertextList<OutputCont>,
encoded: &PlaintextList<InputCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
InputCont: Container<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
output.lwe_ciphertext_count().0 == encoded.plaintext_count().0,
"Mismatch between number of output cipertexts and input plaintexts. \
Got {:?} plaintexts, and {:?} ciphertext.",
encoded.plaintext_count(),
output.lwe_ciphertext_count()
);
for (encoded_plaintext_ref, mut ciphertext) in encoded.iter().zip(output.iter_mut()) {
encrypt_lwe_ciphertext(
lwe_secret_key,
&mut ciphertext,
encoded_plaintext_ref.into(),
noise_parameters,
generator,
)
}
}
/// Parallel variant of [`encrypt_lwe_ciphertext_list`].
///
/// See this [`formal definition`](`encrypt_lwe_ciphertext#formal-definition`) for the definition
/// of the LWE encryption algorithm.
///
/// ```
/// use tfhe::core_crypto::commons::generators::{
/// EncryptionRandomGenerator, SecretRandomGenerator,
/// };
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweCiphertext creation
/// let lwe_dimension = LweDimension(742);
/// let lwe_ciphertext_count = LweCiphertextCount(2);
/// let lwe_modular_std_dev = StandardDev(0.000007069849454709433);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the LweSecretKey
/// let lwe_secret_key =
/// allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
///
/// // Create the plaintext
/// let msg = 3u64;
/// let encoded_msg = msg << 60;
/// let plaintext_list = PlaintextList::new(encoded_msg, PlaintextCount(lwe_ciphertext_count.0));
///
/// // Create a new LweCiphertext
/// let mut lwe_list =
/// LweCiphertextList::new(0u64, lwe_dimension.to_lwe_size(), lwe_ciphertext_count);
///
/// par_encrypt_lwe_ciphertext_list(
/// &lwe_secret_key,
/// &mut lwe_list,
/// &plaintext_list,
/// lwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// let mut output_plaintext_list =
/// PlaintextList::new(0u64, PlaintextCount(lwe_list.lwe_ciphertext_count().0));
/// decrypt_lwe_ciphertext_list(&lwe_secret_key, &lwe_list, &mut output_plaintext_list);
///
/// // Round and remove encoding
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
///
/// output_plaintext_list
/// .iter_mut()
/// .for_each(|elt| *elt.0 = decomposer.closest_representable(*elt.0));
///
/// // Get the raw vector
/// let mut cleartext_list = output_plaintext_list.into_container();
/// // Remove the encoding
/// cleartext_list.iter_mut().for_each(|elt| *elt = *elt >> 60);
/// // Get the list immutably
/// let cleartext_list = cleartext_list;
///
/// // Check we recovered the original message for each plaintext we encrypted
/// cleartext_list.iter().for_each(|&elt| assert_eq!(elt, msg));
/// ```
pub fn par_encrypt_lwe_ciphertext_list<Scalar, KeyCont, OutputCont, InputCont, Gen>(
lwe_secret_key: &LweSecretKey<KeyCont>,
output: &mut LweCiphertextList<OutputCont>,
encoded: &PlaintextList<InputCont>,
noise_parameters: impl DispersionParameter + Sync,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus + Sync + Send,
KeyCont: Container<Element = Scalar> + Sync,
OutputCont: ContainerMut<Element = Scalar>,
InputCont: Container<Element = Scalar>,
Gen: ParallelByteRandomGenerator,
{
assert!(
output.lwe_ciphertext_count().0 == encoded.plaintext_count().0,
"Mismatch between number of output cipertexts and input plaintexts. \
Got {:?} plaintexts, and {:?} ciphertext.",
encoded.plaintext_count(),
output.lwe_ciphertext_count()
);
let gen_iter = generator
.par_fork_lwe_list_to_lwe::<Scalar>(output.lwe_ciphertext_count(), output.lwe_size())
.unwrap();
encoded
.par_iter()
.zip(output.par_iter_mut())
.zip(gen_iter)
.for_each(|((encoded_plaintext_ref, mut ciphertext), mut generator)| {
encrypt_lwe_ciphertext(
lwe_secret_key,
&mut ciphertext,
encoded_plaintext_ref.into(),
noise_parameters,
&mut generator,
)
});
}
/// Decrypt an [`LWE ciphertext list`](`LweCiphertextList`) in a plaintext list.
///
/// See [`encrypt_lwe_ciphertext_list`] for usage.
///
/// See this [`formal definition`](`decrypt_lwe_ciphertext#formal-definition`) for the definition
/// of the LWE decryption algorithm.
pub fn decrypt_lwe_ciphertext_list<Scalar, KeyCont, InputCont, OutputCont>(
lwe_secret_key: &LweSecretKey<KeyCont>,
input_lwe_ciphertext_list: &LweCiphertextList<InputCont>,
output_plaintext_list: &mut PlaintextList<OutputCont>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
{
assert!(
output_plaintext_list.plaintext_count().0
== input_lwe_ciphertext_list.lwe_ciphertext_count().0,
"Mismatched output PlaintextCount {:?} and input LweCiphertextCount ({:?}).",
output_plaintext_list.plaintext_count(),
input_lwe_ciphertext_list.lwe_ciphertext_count(),
);
for (ciphertext, output_plaintext) in input_lwe_ciphertext_list
.iter()
.zip(output_plaintext_list.iter_mut())
{
*output_plaintext.0 = decrypt_lwe_ciphertext(lwe_secret_key, &ciphertext).0;
}
}
/// Encrypt an input plaintext in an output [`LWE ciphertext`](`LweCiphertext`) using an
/// [`LWE public key`](`LwePublicKey`). The ciphertext can be decrypted using the
/// [`LWE secret key`](`LweSecretKey`) that was used to generate the public key.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::commons::generators::{
/// EncryptionRandomGenerator, SecretRandomGenerator,
/// };
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweCiphertext creation
/// let lwe_dimension = LweDimension(742);
/// let lwe_modular_std_dev = StandardDev(0.000007069849454709433);
/// let zero_encryption_count =
/// LwePublicKeyZeroEncryptionCount(lwe_dimension.to_lwe_size().0 * 64 + 128);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the LweSecretKey
/// let lwe_secret_key =
/// allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
///
/// let lwe_public_key = allocate_and_generate_new_lwe_public_key(
/// &lwe_secret_key,
/// zero_encryption_count,
/// lwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// // Create the plaintext
/// let msg = 3u64;
/// let plaintext = Plaintext(msg << 60);
///
/// // Create a new LweCiphertext
/// let mut lwe = LweCiphertext::new(0u64, lwe_dimension.to_lwe_size());
///
/// encrypt_lwe_ciphertext_with_public_key(
/// &lwe_public_key,
/// &mut lwe,
/// plaintext,
/// &mut secret_generator,
/// );
///
/// let decrypted_plaintext = decrypt_lwe_ciphertext(&lwe_secret_key, &lwe);
///
/// // Round and remove encoding
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
///
/// let rounded = decomposer.closest_representable(decrypted_plaintext.0);
///
/// // Remove the encoding
/// let cleartext = rounded >> 60;
///
/// // Check we recovered the original message
/// assert_eq!(cleartext, msg);
/// ```
pub fn encrypt_lwe_ciphertext_with_public_key<Scalar, KeyCont, OutputCont, Gen>(
lwe_public_key: &LwePublicKey<KeyCont>,
output: &mut LweCiphertext<OutputCont>,
encoded: Plaintext<Scalar>,
generator: &mut SecretRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
output.lwe_size().to_lwe_dimension() == lwe_public_key.lwe_size().to_lwe_dimension(),
"Mismatch between LweDimension of output cipertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.lwe_size().to_lwe_dimension(),
lwe_public_key.lwe_size().to_lwe_dimension()
);
output.as_mut().fill(Scalar::ZERO);
let mut ct_choice = vec![Scalar::ZERO; lwe_public_key.zero_encryption_count().0];
generator.fill_slice_with_random_uniform_binary(&mut ct_choice);
// Add the public encryption of zeros to get the zero encryption
for (&chosen, public_encryption_of_zero) in ct_choice.iter().zip(lwe_public_key.iter()) {
if chosen == Scalar::ONE {
lwe_ciphertext_add_assign(output, &public_encryption_of_zero);
}
}
// Add encoded plaintext
let body = output.get_mut_body();
*body.0 = (*body.0).wrapping_add(encoded.0);
}
pub fn rc_encrypt_lwe_ciphertext_with_public_key<Scalar, KeyCont, OutputCont, Gen>(
lwe_public_key: &LwePublicKey<KeyCont>,
output: &mut LweCiphertext<OutputCont>,
encoded: Plaintext<Scalar>,
generator: &mut SecretRandomGenerator<Gen>,
) -> Vec<Scalar>
where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
output.lwe_size().to_lwe_dimension() == lwe_public_key.lwe_size().to_lwe_dimension(),
"Mismatch between LweDimension of output cipertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.lwe_size().to_lwe_dimension(),
lwe_public_key.lwe_size().to_lwe_dimension()
);
output.as_mut().fill(Scalar::ZERO);
let mut ct_choice = vec![Scalar::ZERO; lwe_public_key.zero_encryption_count().0];
generator.fill_slice_with_random_uniform_binary(&mut ct_choice);
// Add the public encryption of zeros to get the zero encryption
for (&chosen, public_encryption_of_zero) in ct_choice.iter().zip(lwe_public_key.iter()) {
if chosen == Scalar::ONE {
lwe_ciphertext_add_assign(output, &public_encryption_of_zero);
}
}
// Add encoded plaintext
let body = output.get_mut_body();
*body.0 = (*body.0).wrapping_add(encoded.0);
ct_choice
}

View File

@@ -0,0 +1,190 @@
use crate::core_crypto::algorithms::slice_algorithms::*;
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
use crate::core_crypto::commons::numeric::UnsignedInteger;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// Keyswitch an [`LWE ciphertext`](`LweCiphertext`) encrytped under an
/// [`LWE secret key`](`LweSecretKey`) to another [`LWE secret key`](`LweSecretKey`).
///
/// # Formal Definition
///
/// ## LWE Keyswitch
///
/// This homomorphic procedure transforms an input
/// [`LWE ciphertext`](`crate::core_crypto::entities::LweCiphertext`)
/// $\mathsf{ct}\_{\mathsf{in}} =
/// \left( \vec{a}\_{\mathsf{in}} , b\_{\mathsf{in}}\right) \in \mathsf{LWE}^{n\_{\mathsf{in}}}\_
/// {\vec{s}\_{\mathsf{in}}}( \mathsf{pt} ) \subseteq \mathbb{Z}\_q^{(n\_{\mathsf{in}}+1)}$ into an
/// output [`LWE ciphertext`](`crate::core_crypto::entities::LweCiphertext`)
/// $\mathsf{ct}\_{\mathsf{out}} =
/// \left( \vec{a}\_{\mathsf{out}} , b\_{\mathsf{out}}\right) \in
/// \mathsf{LWE}^{n\_{\mathsf{out}}}\_{\vec{s}\_{\mathsf{out}}}( \mathsf{pt} )\subseteq
/// \mathbb{Z}\_q^{(n\_{\mathsf{out}}+1)}$ where $n\_{\mathsf{in}} = |\vec{s}\_{\mathsf{in}}|$ and
/// $n\_{\mathsf{out}} = |\vec{s}\_{\mathsf{out}}|$. It requires a
/// [`key switching key`](`crate::core_crypto::entities::LweKeyswitchKey`).
/// The input ciphertext is encrypted under the
/// [`LWE secret key`](`crate::core_crypto::entities::LweSecretKey`)
/// $\vec{s}\_{\mathsf{in}}$ and the output ciphertext is
/// encrypted under the [`LWE secret key`](`crate::core_crypto::entities::LweSecretKey`)
/// $\vec{s}\_{\mathsf{out}}$.
///
/// $$\mathsf{ct}\_{\mathsf{in}} \in \mathsf{LWE}^{n\_{\mathsf{in}}}\_{\vec{s}\_{\mathsf{in}}}(
/// \mathsf{pt} ) ~~~~~~~~~~\mathsf{KSK}\_{\vec{s}\_{\mathsf{in}}\rightarrow
/// \vec{s}\_{\mathsf{out}}}$$ $$ \mathsf{keyswitch}\left(\mathsf{ct}\_{\mathsf{in}} , \mathsf{KSK}
/// \right) \rightarrow \mathsf{ct}\_{\mathsf{out}} \in
/// \mathsf{LWE}^{n\_{\mathsf{out}}}\_{\vec{s}\_{\mathsf{out}}} \left( \mathsf{pt} \right)$$
///
/// ## Algorithm
/// ###### inputs:
/// - $\mathsf{ct}\_{\mathsf{in}} = \left( \vec{a}\_{\mathsf{in}} , b\_{\mathsf{in}}\right) \in
/// \mathsf{LWE}^{n\_{\mathsf{in}}}\_{\vec{s}\_{\mathsf{in}}}( \mathsf{pt} )$: an [`LWE
/// ciphertext`](`LweCiphertext`) with $\vec{a}\_{\mathsf{in}}=\left(a\_0, \cdots
/// a\_{n\_{\mathsf{in}}-1}\right)$
/// - $\mathsf{KSK}\_{\vec{s}\_{\mathsf{in}}\rightarrow \vec{s}\_{\mathsf{out}}}$: a
/// [`key switching key`](`crate::core_crypto::entities::LweKeyswitchKey`)
///
/// ###### outputs:
/// - $\mathsf{ct}\_{\mathsf{out}} \in \mathsf{LWE}^{n\_{\mathsf{out}}}\_{\vec{s}\_{\mathsf{out}}}
/// \left( \mathsf{pt} \right)$: an
/// [`LWE ciphertext`](`crate::core_crypto::entities::LweCiphertext`)
///
/// ###### algorithm:
/// 1. set $\mathsf{ct}=\left( 0 , \cdots , 0 , b\_{\mathsf{in}} \right) \in
/// \mathbb{Z}\_q^{(n\_{\mathsf{out}}+1)}$
/// 2. compute $\mathsf{ct}\_{\mathsf{out}} = \mathsf{ct} -
/// \sum\_{i=0}^{n\_{\mathsf{in}}-1} \mathsf{decompProduct}\left( a\_i , \overline{\mathsf{ct}\_i}
/// \right)$
/// 3. output $\mathsf{ct}\_{\mathsf{out}}$
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::commons::generators::{
/// EncryptionRandomGenerator, SecretRandomGenerator,
/// };
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweCiphertext creation
/// let input_lwe_dimension = LweDimension(742);
/// let lwe_modular_std_dev = StandardDev(0.000007069849454709433);
/// let output_lwe_dimension = LweDimension(2048);
/// let decomp_base_log = DecompositionBaseLog(3);
/// let decomp_level_count = DecompositionLevelCount(5);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the LweSecretKey
/// let input_lwe_secret_key =
/// allocate_and_generate_new_binary_lwe_secret_key(input_lwe_dimension, &mut secret_generator);
/// let output_lwe_secret_key = allocate_and_generate_new_binary_lwe_secret_key(
/// output_lwe_dimension,
/// &mut secret_generator,
/// );
///
/// let ksk = allocate_and_generate_new_lwe_keyswitch_key(
/// &input_lwe_secret_key,
/// &output_lwe_secret_key,
/// decomp_base_log,
/// decomp_level_count,
/// lwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// // Create the plaintext
/// let msg = 3u64;
/// let plaintext = Plaintext(msg << 60);
///
/// // Create a new LweCiphertext
/// let input_lwe = allocate_and_encrypt_new_lwe_ciphertext(
/// &input_lwe_secret_key,
/// plaintext,
/// lwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// let mut output_lwe = LweCiphertext::new(0, output_lwe_secret_key.lwe_dimension().to_lwe_size());
///
/// keyswitch_lwe_ciphertext(&ksk, &input_lwe, &mut output_lwe);
///
/// let decrypted_plaintext = decrypt_lwe_ciphertext(&output_lwe_secret_key, &output_lwe);
///
/// // Round and remove encoding
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
///
/// let rounded = decomposer.closest_representable(decrypted_plaintext.0);
///
/// // Remove the encoding
/// let cleartext = rounded >> 60;
///
/// // Check we recovered the original message
/// assert_eq!(cleartext, msg);
/// ```
pub fn keyswitch_lwe_ciphertext<Scalar, KSKCont, InputCont, OutputCont>(
lwe_keyswitch_key: &LweKeyswitchKey<KSKCont>,
input_lwe_ciphertext: &LweCiphertext<InputCont>,
output_lwe_ciphertext: &mut LweCiphertext<OutputCont>,
) where
Scalar: UnsignedInteger,
KSKCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
{
assert!(
lwe_keyswitch_key.input_key_lwe_dimension()
== input_lwe_ciphertext.lwe_size().to_lwe_dimension(),
"Mismatched input LweDimension. \
LweKeyswitchKey input LweDimension: {:?}, input LweCiphertext LweDimension {:?}.",
lwe_keyswitch_key.input_key_lwe_dimension(),
input_lwe_ciphertext.lwe_size().to_lwe_dimension(),
);
assert!(
lwe_keyswitch_key.output_key_lwe_dimension()
== output_lwe_ciphertext.lwe_size().to_lwe_dimension(),
"Mismatched output LweDimension. \
LweKeyswitchKey output LweDimension: {:?}, output LweCiphertext LweDimension {:?}.",
lwe_keyswitch_key.output_key_lwe_dimension(),
output_lwe_ciphertext.lwe_size().to_lwe_dimension(),
);
// Clear the output ciphertext, as it will get updated gradually
output_lwe_ciphertext.as_mut().fill(Scalar::ZERO);
// Copy the input body to the output ciphertext
*output_lwe_ciphertext.get_mut_body().0 = *input_lwe_ciphertext.get_body().0;
// We instantiate a decomposer
let decomposer = SignedDecomposer::new(
lwe_keyswitch_key.decomposition_base_log(),
lwe_keyswitch_key.decomposition_level_count(),
);
for (keyswitch_key_block, &input_mask_element) in lwe_keyswitch_key
.iter()
.zip(input_lwe_ciphertext.get_mask().as_ref())
{
let decomposition_iter = decomposer.decompose(input_mask_element);
// loop over the number of levels in reverse (from highest to lowest)
for (level_key_ciphertext, decomposed) in
keyswitch_key_block.iter().rev().zip(decomposition_iter)
{
slice_wrapping_sub_scalar_mul_assign(
output_lwe_ciphertext.as_mut(),
level_key_ciphertext.as_ref(),
decomposed.value(),
);
}
}
}

View File

@@ -0,0 +1,159 @@
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::dispersion::DispersionParameter;
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
use crate::core_crypto::commons::math::decomposition::{DecompositionLevel, DecompositionTerm};
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// Fill an [`LWE keyswitch key`](`LweKeyswitchKey`) with an actual keyswitching key constructed
/// from an input and an output key [`LWE secret key`](`LweSecretKey`).
///
/// ```
/// use tfhe::core_crypto::commons::generators::{
/// EncryptionRandomGenerator, SecretRandomGenerator,
/// };
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweCiphertext creation
/// let input_lwe_dimension = LweDimension(742);
/// let lwe_modular_std_dev = StandardDev(0.000007069849454709433);
/// let output_lwe_dimension = LweDimension(2048);
/// let decomp_base_log = DecompositionBaseLog(3);
/// let decomp_level_count = DecompositionLevelCount(5);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the LweSecretKey
/// let input_lwe_secret_key =
/// allocate_and_generate_new_binary_lwe_secret_key(input_lwe_dimension, &mut secret_generator);
/// let output_lwe_secret_key = allocate_and_generate_new_binary_lwe_secret_key(
/// output_lwe_dimension,
/// &mut secret_generator,
/// );
///
/// let mut ksk = LweKeyswitchKey::new(
/// 0u64,
/// decomp_base_log,
/// decomp_level_count,
/// input_lwe_dimension,
/// output_lwe_dimension,
/// );
///
/// generate_lwe_keyswitch_key(
/// &input_lwe_secret_key,
/// &output_lwe_secret_key,
/// &mut ksk,
/// lwe_modular_std_dev,
/// &mut encryption_generator,
/// );
///
/// assert!(ksk.as_ref().iter().all(|&x| x == 0) == false);
/// ```
pub fn generate_lwe_keyswitch_key<Scalar, InputKeyCont, OutputKeyCont, KSKeyCont, Gen>(
input_lwe_sk: &LweSecretKey<InputKeyCont>,
output_lwe_sk: &LweSecretKey<OutputKeyCont>,
lwe_keyswitch_key: &mut LweKeyswitchKey<KSKeyCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: Container<Element = Scalar>,
KSKeyCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
lwe_keyswitch_key.input_key_lwe_dimension() == input_lwe_sk.lwe_dimension(),
"The destination LweKeyswitchKey input LweDimension is not equal \
to the input LweSecretKey LweDimension. Destination: {:?}, input: {:?}",
lwe_keyswitch_key.input_key_lwe_dimension(),
input_lwe_sk.lwe_dimension()
);
assert!(
lwe_keyswitch_key.output_key_lwe_dimension() == output_lwe_sk.lwe_dimension(),
"The destination LweKeyswitchKey output LweDimension is not equal \
to the output LweSecretKey LweDimension. Destination: {:?}, output: {:?}",
lwe_keyswitch_key.output_key_lwe_dimension(),
input_lwe_sk.lwe_dimension()
);
let decomp_base_log = lwe_keyswitch_key.decomposition_base_log();
let decomp_level_count = lwe_keyswitch_key.decomposition_level_count();
// The plaintexts used to encrypt a key element will be stored in this buffer
let mut decomposition_plaintexts_buffer =
PlaintextListOwned::new(Scalar::ZERO, PlaintextCount(decomp_level_count.0));
// Iterate over the input key elements and the destination lwe_keyswitch_key memory
for (input_key_element, mut keyswitch_key_block) in input_lwe_sk
.as_ref()
.iter()
.zip(lwe_keyswitch_key.iter_mut())
{
// We fill the buffer with the powers of the key elmements
for (level, message) in (1..=decomp_level_count.0)
.map(DecompositionLevel)
.zip(decomposition_plaintexts_buffer.iter_mut())
{
*message.0 = DecompositionTerm::new(level, decomp_base_log, *input_key_element)
.to_recomposition_summand();
}
encrypt_lwe_ciphertext_list(
output_lwe_sk,
&mut keyswitch_key_block,
&decomposition_plaintexts_buffer,
noise_parameters,
generator,
);
}
}
/// Allocate a new [`LWE keyswitch key`](`LweKeyswitchKey`) and fill it with an actual keyswitching
/// key constructed from an input and an output key [`LWE secret key`](`LweSecretKey`).
///
/// See [`keyswitch_lwe_ciphertext`] for usage.
pub fn allocate_and_generate_new_lwe_keyswitch_key<Scalar, InputKeyCont, OutputKeyCont, Gen>(
input_lwe_sk: &LweSecretKey<InputKeyCont>,
output_lwe_sk: &LweSecretKey<OutputKeyCont>,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) -> LweKeyswitchKeyOwned<Scalar>
where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: Container<Element = Scalar>,
Gen: ByteRandomGenerator,
{
let mut new_lwe_keyswitch_key = LweKeyswitchKeyOwned::new(
Scalar::ZERO,
decomp_base_log,
decomp_level_count,
input_lwe_sk.lwe_dimension(),
output_lwe_sk.lwe_dimension(),
);
generate_lwe_keyswitch_key(
input_lwe_sk,
output_lwe_sk,
&mut new_lwe_keyswitch_key,
noise_parameters,
generator,
);
new_lwe_keyswitch_key
}

View File

@@ -0,0 +1,85 @@
//! Module containing functions related to LWE ciphertext linear algebra, like addition,
//! multiplication, etc.
use crate::core_crypto::algorithms::slice_algorithms::*;
use crate::core_crypto::commons::numeric::UnsignedInteger;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
pub fn lwe_ciphertext_add_assign<Scalar, LhsCont, RhsCont>(
lhs: &mut LweCiphertext<LhsCont>,
rhs: &LweCiphertext<RhsCont>,
) where
Scalar: UnsignedInteger,
LhsCont: ContainerMut<Element = Scalar>,
RhsCont: Container<Element = Scalar>,
{
slice_wrapping_add_assign(lhs.as_mut(), rhs.as_ref());
}
pub fn lwe_ciphertext_add<Scalar, OutputCont, LhsCont, RhsCont>(
output: &mut LweCiphertext<OutputCont>,
lhs: &LweCiphertext<LhsCont>,
rhs: &LweCiphertext<RhsCont>,
) where
Scalar: UnsignedInteger,
OutputCont: ContainerMut<Element = Scalar>,
LhsCont: Container<Element = Scalar>,
RhsCont: Container<Element = Scalar>,
{
slice_wrapping_add(output.as_mut(), lhs.as_ref(), rhs.as_ref());
}
pub fn lwe_ciphertext_plaintext_add_assign<Scalar, InCont>(
lhs: &mut LweCiphertext<InCont>,
rhs: Plaintext<Scalar>,
) where
Scalar: UnsignedInteger,
InCont: ContainerMut<Element = Scalar>,
{
let body = lhs.get_mut_body();
*body.0 = (*body.0).wrapping_add(rhs.0);
}
pub fn lwe_ciphertext_opposite_assign<Scalar, InCont>(ct: &mut LweCiphertext<InCont>)
where
Scalar: UnsignedInteger,
InCont: ContainerMut<Element = Scalar>,
{
slice_wrapping_opposite_assign(ct.as_mut());
}
pub fn lwe_ciphertext_cleartext_mul_assign<Scalar, InCont>(
lhs: &mut LweCiphertext<InCont>,
rhs: Cleartext<Scalar>,
) where
Scalar: UnsignedInteger,
InCont: ContainerMut<Element = Scalar>,
{
slice_wrapping_scalar_mul_assign(lhs.as_mut(), rhs.0);
}
pub fn lwe_ciphertext_sub_assign<Scalar, LhsCont, RhsCont>(
lhs: &mut LweCiphertext<LhsCont>,
rhs: &LweCiphertext<RhsCont>,
) where
Scalar: UnsignedInteger,
LhsCont: ContainerMut<Element = Scalar>,
RhsCont: Container<Element = Scalar>,
{
slice_wrapping_sub_assign(lhs.as_mut(), rhs.as_ref());
}
pub fn lwe_ciphertext_cleartext_mul<Scalar, InputCont, OutputCont>(
output: &mut LweCiphertext<OutputCont>,
lhs: &LweCiphertext<InputCont>,
rhs: Cleartext<Scalar>,
) where
Scalar: UnsignedInteger,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
{
output.as_mut().copy_from_slice(lhs.as_ref());
lwe_ciphertext_cleartext_mul_assign(output, rhs);
}

View File

@@ -0,0 +1,94 @@
use crate::core_crypto::algorithms::polynomial_algorithms::*;
use crate::core_crypto::algorithms::slice_algorithms::*;
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
pub fn private_functional_keyswitch_lwe_ciphertext_into_glwe_ciphertext<
Scalar,
KeyCont,
InputCont,
OutputCont,
>(
lwe_pfpksk: &LwePrivateFunctionalPackingKeyswitchKey<KeyCont>,
output_glwe_ciphertext: &mut GlweCiphertext<OutputCont>,
input_lwe_ciphertext: &LweCiphertext<InputCont>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
{
assert!(
lwe_pfpksk.input_lwe_key_dimension().0
== input_lwe_ciphertext.lwe_size().to_lwe_dimension().0
);
assert!(
lwe_pfpksk.output_glwe_key_dimension().0
== output_glwe_ciphertext.glwe_size().to_glwe_dimension().0
);
// We reset the output
output_glwe_ciphertext.as_mut().fill(Scalar::ZERO);
// We instantiate a decomposer
let decomposer = SignedDecomposer::new(
lwe_pfpksk.decomposition_base_log(),
lwe_pfpksk.decomposition_level_count(),
);
for (keyswitch_key_block, &input_lwe_element) in
lwe_pfpksk.iter().zip(input_lwe_ciphertext.as_ref().iter())
{
// We decompose
let rounded = decomposer.closest_representable(input_lwe_element);
let decomp = decomposer.decompose(rounded);
// Loop over the number of levels:
// We compute the multiplication of a ciphertext from the private functional
// keyswitching key with a piece of the decomposition and subtract it to the buffer
for (level_key_cipher, decomposed) in keyswitch_key_block.iter().rev().zip(decomp) {
slice_wrapping_sub_scalar_mul_assign(
output_glwe_ciphertext.as_mut(),
level_key_cipher.as_ref(),
decomposed.value(),
);
}
}
}
pub fn private_functional_keyswitch_lwe_ciphertext_list_and_pack_in_glwe_cipheretext<
Scalar,
KeyCont,
InputCont,
OutputCont,
>(
lwe_pfpksk: &LwePrivateFunctionalPackingKeyswitchKey<KeyCont>,
output: &mut GlweCiphertext<OutputCont>,
input: &LweCiphertextList<InputCont>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar> + Clone,
{
assert!(input.lwe_ciphertext_count().0 <= output.polynomial_size().0);
output.as_mut().fill(Scalar::ZERO);
let mut buffer = output.clone();
// for each ciphertext, call mono_key_switch
for (degree, input_ciphertext) in input.iter().enumerate() {
private_functional_keyswitch_lwe_ciphertext_into_glwe_ciphertext(
lwe_pfpksk,
&mut buffer,
&input_ciphertext,
);
buffer
.as_mut_polynomial_list()
.iter_mut()
.for_each(|mut poly| {
polynomial_wrapping_monic_monomial_mul_assign(&mut poly, MonomialDegree(degree))
});
slice_wrapping_add_assign(output.as_mut(), buffer.as_ref());
}
}

View File

@@ -0,0 +1,231 @@
use crate::core_crypto::algorithms::slice_algorithms::*;
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::dispersion::DispersionParameter;
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
use crate::core_crypto::commons::math::decomposition::{DecompositionLevel, DecompositionTerm};
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use rayon::prelude::*;
pub fn generate_lwe_private_functional_packing_keyswitch_key<
Scalar,
InputKeyCont,
OutputKeyCont,
KSKeyCont,
Gen,
ScalarFunc,
PolyCont,
>(
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
lwe_pfpksk: &mut LwePrivateFunctionalPackingKeyswitchKey<KSKeyCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
f: ScalarFunc,
polynomial: &Polynomial<PolyCont>,
) where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: Container<Element = Scalar>,
KSKeyCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
ScalarFunc: Fn(Scalar) -> Scalar,
PolyCont: Container<Element = Scalar>,
{
assert!(
input_lwe_secret_key.lwe_dimension() == lwe_pfpksk.input_lwe_key_dimension(),
"Mismatched LweDimension between input_lwe_secret_key {:?} and lwe_pfpksk input dimension \
{:?}.",
input_lwe_secret_key.lwe_dimension(),
lwe_pfpksk.input_lwe_key_dimension()
);
assert!(
output_glwe_secret_key.glwe_dimension() == lwe_pfpksk.output_glwe_key_dimension(),
"Mismatched GlweDimension between output_glwe_secret_key {:?} and lwe_pfpksk output \
dimension {:?}.",
output_glwe_secret_key.glwe_dimension(),
lwe_pfpksk.output_glwe_key_dimension()
);
assert!(
output_glwe_secret_key.polynomial_size() == lwe_pfpksk.output_polynomial_size(),
"Mismatched PolynomialSize between output_glwe_secret_key {:?} and lwe_pfpksk output \
polynomial size {:?}.",
output_glwe_secret_key.polynomial_size(),
lwe_pfpksk.output_polynomial_size()
);
// We instantiate a buffer
let mut messages = PlaintextListOwned::new(
Scalar::ZERO,
PlaintextCount(
lwe_pfpksk.decomposition_level_count().0 * lwe_pfpksk.output_polynomial_size().0,
),
);
// We retrieve decomposition arguments
let decomp_level_count = lwe_pfpksk.decomposition_level_count();
let decomp_base_log = lwe_pfpksk.decomposition_base_log();
let polynomial_size = lwe_pfpksk.output_polynomial_size();
let last_key_iter_bit = [Scalar::MAX];
// add minus one for the function which will be applied to the decomposed body
// ( Scalar::MAX = -Scalar::ONE )
let input_key_bit_iter = input_lwe_secret_key
.as_ref()
.iter()
.chain(last_key_iter_bit.iter());
let gen_iter = generator
.fork_pfpksk_to_pfpksk_chunks::<Scalar>(
decomp_level_count,
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
output_glwe_secret_key.polynomial_size(),
input_lwe_secret_key.lwe_dimension().to_lwe_size(),
)
.unwrap();
// loop over the before key blocks
for ((&input_key_bit, mut keyswitch_key_block), mut loop_generator) in
input_key_bit_iter.zip(lwe_pfpksk.iter_mut()).zip(gen_iter)
{
// We fill the buffer with the powers of the key bits
for (level, mut message) in (1..=decomp_level_count.0)
.map(DecompositionLevel)
.zip(messages.chunks_exact_mut(polynomial_size.0))
{
slice_wrapping_add_scalar_mul_assign(
message.as_mut(),
polynomial.as_ref(),
DecompositionTerm::new(
level,
decomp_base_log,
f(Scalar::ONE).wrapping_mul(input_key_bit),
)
.to_recomposition_summand(),
);
}
// We encrypt the buffer
encrypt_glwe_ciphertext_list(
output_glwe_secret_key,
&messages,
&mut keyswitch_key_block,
noise_parameters,
&mut loop_generator,
)
}
}
/// Parallel variant of [`generate_lwe_private_functional_packing_keyswitch_key`]. You may want to
/// use this variant for bette key generation times.
pub fn par_generate_lwe_private_functional_packing_keyswitch_key<
Scalar,
InputKeyCont,
OutputKeyCont,
KSKeyCont,
Gen,
ScalarFunc,
PolyCont,
>(
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
lwe_pfpksk: &mut LwePrivateFunctionalPackingKeyswitchKey<KSKeyCont>,
noise_parameters: impl DispersionParameter + Sync,
generator: &mut EncryptionRandomGenerator<Gen>,
f: ScalarFunc,
polynomial: &Polynomial<PolyCont>,
) where
Scalar: UnsignedTorus + Sync + Send,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: Container<Element = Scalar> + Sync,
KSKeyCont: ContainerMut<Element = Scalar> + Sync,
Gen: ParallelByteRandomGenerator,
ScalarFunc: Fn(Scalar) -> Scalar + Sync,
PolyCont: Container<Element = Scalar> + Sync,
{
assert!(
input_lwe_secret_key.lwe_dimension() == lwe_pfpksk.input_lwe_key_dimension(),
"Mismatched LweDimension between input_lwe_secret_key {:?} and lwe_pfpksk input dimension \
{:?}.",
input_lwe_secret_key.lwe_dimension(),
lwe_pfpksk.input_lwe_key_dimension()
);
assert!(
output_glwe_secret_key.glwe_dimension() == lwe_pfpksk.output_glwe_key_dimension(),
"Mismatched GlweDimension between output_glwe_secret_key {:?} and lwe_pfpksk output \
dimension {:?}.",
output_glwe_secret_key.glwe_dimension(),
lwe_pfpksk.output_glwe_key_dimension()
);
assert!(
output_glwe_secret_key.polynomial_size() == lwe_pfpksk.output_polynomial_size(),
"Mismatched PolynomialSize between output_glwe_secret_key {:?} and lwe_pfpksk output \
polynomial size {:?}.",
output_glwe_secret_key.polynomial_size(),
lwe_pfpksk.output_polynomial_size()
);
// We retrieve decomposition arguments
let decomp_level_count = lwe_pfpksk.decomposition_level_count();
let decomp_base_log = lwe_pfpksk.decomposition_base_log();
let polynomial_size = lwe_pfpksk.output_polynomial_size();
let last_key_iter_bit = [Scalar::MAX];
// add minus one for the function which will be applied to the decomposed body
// ( Scalar::MAX = -Scalar::ONE )
let input_key_bit_iter = input_lwe_secret_key
.as_ref()
.par_iter()
.chain(last_key_iter_bit.par_iter());
let gen_iter = generator
.par_fork_pfpksk_to_pfpksk_chunks::<Scalar>(
decomp_level_count,
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
output_glwe_secret_key.polynomial_size(),
input_lwe_secret_key.lwe_dimension().to_lwe_size(),
)
.unwrap();
let plaintext_count = PlaintextCount(
lwe_pfpksk.decomposition_level_count().0 * lwe_pfpksk.output_polynomial_size().0,
);
// loop over the before key blocks
input_key_bit_iter
.zip(lwe_pfpksk.par_iter_mut())
.zip(gen_iter)
.for_each(
|((&input_key_bit, mut keyswitch_key_block), mut loop_generator)| {
// We instantiate a buffer
let mut messages = PlaintextListOwned::new(Scalar::ZERO, plaintext_count);
// We fill the buffer with the powers of the key bits
for (level, mut message) in (1..=decomp_level_count.0)
.map(DecompositionLevel)
.zip(messages.chunks_exact_mut(polynomial_size.0))
{
slice_wrapping_add_scalar_mul_assign(
message.as_mut(),
polynomial.as_ref(),
DecompositionTerm::new(
level,
decomp_base_log,
f(Scalar::ONE).wrapping_mul(input_key_bit),
)
.to_recomposition_summand(),
);
}
// We encrypt the buffer
encrypt_glwe_ciphertext_list(
output_glwe_secret_key,
&messages,
&mut keyswitch_key_block,
noise_parameters,
&mut loop_generator,
)
},
);
}

View File

@@ -0,0 +1,146 @@
use crate::core_crypto::commons::computation_buffers::ComputationBuffers;
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use crate::core_crypto::fft_impl::crypto::bootstrap::{bootstrap_scratch, FourierLweBootstrapKey};
use crate::core_crypto::fft_impl::crypto::wop_pbs::blind_rotate_assign_scratch;
use crate::core_crypto::fft_impl::math::fft::{Fft, FftView};
use concrete_fft::c64;
use dyn_stack::{DynStack, SizeOverflow, StackReq};
pub fn blind_rotate_assign<Scalar, InputCont, OutputCont, KeyCont>(
input: &LweCiphertext<InputCont>,
lut: &mut GlweCiphertext<OutputCont>,
fourier_bsk: &FourierLweBootstrapKey<KeyCont>,
) where
// CastInto required for PBS modulus switch which returns a usize
Scalar: UnsignedTorus + CastInto<usize>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
KeyCont: Container<Element = c64>,
{
let mut buffers = ComputationBuffers::new();
let fft = Fft::new(fourier_bsk.polynomial_size());
let fft = fft.as_view();
buffers.resize(
blind_rotate_assign_mem_optimized_scratch::<Scalar>(
fourier_bsk.glwe_size(),
fourier_bsk.polynomial_size(),
fft,
)
.unwrap()
.unaligned_bytes_required(),
);
let stack = buffers.stack();
blind_rotate_assign_mem_optimized(input, lut, fourier_bsk, fft, stack);
}
pub fn blind_rotate_assign_mem_optimized<Scalar, InputCont, OutputCont, KeyCont>(
input: &LweCiphertext<InputCont>,
lut: &mut GlweCiphertext<OutputCont>,
fourier_bsk: &FourierLweBootstrapKey<KeyCont>,
fft: FftView<'_>,
stack: DynStack<'_>,
) where
// CastInto required for PBS modulus switch which returns a usize
Scalar: UnsignedTorus + CastInto<usize>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
KeyCont: Container<Element = c64>,
{
fourier_bsk
.as_view()
.blind_rotate_assign(lut.as_mut_view(), input.as_ref(), fft, stack);
}
/// Return the required memory for [`blind_rotate_assign_mem_optimized`].
pub fn blind_rotate_assign_mem_optimized_scratch<Scalar>(
glwe_size: GlweSize,
polynomial_size: PolynomialSize,
fft: FftView<'_>,
) -> Result<StackReq, SizeOverflow> {
blind_rotate_assign_scratch::<Scalar>(glwe_size, polynomial_size, fft)
}
pub fn programmable_bootstrap_lwe_ciphertext<Scalar, InputCont, OutputCont, AccCont, KeyCont>(
input: &LweCiphertext<InputCont>,
output: &mut LweCiphertext<OutputCont>,
accumulator: &GlweCiphertext<AccCont>,
fourier_bsk: &FourierLweBootstrapKey<KeyCont>,
) where
// CastInto required for PBS modulus switch which returns a usize
Scalar: UnsignedTorus + CastInto<usize>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
AccCont: Container<Element = Scalar>,
KeyCont: Container<Element = c64>,
{
let mut buffers = ComputationBuffers::new();
let fft = Fft::new(fourier_bsk.polynomial_size());
let fft = fft.as_view();
buffers.resize(
programmable_bootstrap_lwe_ciphertext_mem_optimized_scratch::<Scalar>(
fourier_bsk.glwe_size(),
fourier_bsk.polynomial_size(),
fft,
)
.unwrap()
.unaligned_bytes_required(),
);
let stack = buffers.stack();
programmable_bootstrap_lwe_ciphertext_mem_optimized(
input,
output,
accumulator,
fourier_bsk,
fft,
stack,
)
}
pub fn programmable_bootstrap_lwe_ciphertext_mem_optimized<
Scalar,
InputCont,
OutputCont,
AccCont,
KeyCont,
>(
input: &LweCiphertext<InputCont>,
output: &mut LweCiphertext<OutputCont>,
accumulator: &GlweCiphertext<AccCont>,
fourier_bsk: &FourierLweBootstrapKey<KeyCont>,
fft: FftView<'_>,
stack: DynStack<'_>,
) where
// CastInto required for PBS modulus switch which returns a usize
Scalar: UnsignedTorus + CastInto<usize>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
AccCont: Container<Element = Scalar>,
KeyCont: Container<Element = c64>,
{
fourier_bsk.as_view().bootstrap(
output.as_mut(),
input.as_ref(),
accumulator.as_view(),
fft,
stack,
);
}
/// Return the required memory for [`programmable_bootstrap_lwe_ciphertext_mem_optimized`].
pub fn programmable_bootstrap_lwe_ciphertext_mem_optimized_scratch<Scalar>(
glwe_size: GlweSize,
polynomial_size: PolynomialSize,
fft: FftView<'_>,
) -> Result<StackReq, SizeOverflow> {
bootstrap_scratch::<Scalar>(glwe_size, polynomial_size, fft)
}

View File

@@ -0,0 +1,102 @@
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::dispersion::DispersionParameter;
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
pub fn generate_lwe_public_key<Scalar, InputKeyCont, OutputKeyCont, Gen>(
lwe_secret_key: &LweSecretKey<InputKeyCont>,
output: &mut LwePublicKey<OutputKeyCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
lwe_secret_key.lwe_dimension() == output.lwe_size().to_lwe_dimension(),
"Mismatched LweDimension between input LweSecretKey {:?} and output LwePublicKey {:?}",
lwe_secret_key.lwe_dimension(),
output.lwe_size().to_lwe_dimension(),
);
let zeros = PlaintextListOwned::new(
Scalar::ZERO,
PlaintextCount(output.zero_encryption_count().0),
);
encrypt_lwe_ciphertext_list(lwe_secret_key, output, &zeros, noise_parameters, generator)
}
pub fn allocate_and_generate_new_lwe_public_key<Scalar, InputKeyCont, Gen>(
lwe_secret_key: &LweSecretKey<InputKeyCont>,
zero_encryption_count: LwePublicKeyZeroEncryptionCount,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) -> LwePublicKeyOwned<Scalar>
where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
Gen: ByteRandomGenerator,
{
let mut pk = LwePublicKeyOwned::new(
Scalar::ZERO,
lwe_secret_key.lwe_dimension().to_lwe_size(),
zero_encryption_count,
);
generate_lwe_public_key(lwe_secret_key, &mut pk, noise_parameters, generator);
pk
}
pub fn par_generate_lwe_public_key<Scalar, InputKeyCont, OutputKeyCont, Gen>(
lwe_secret_key: &LweSecretKey<InputKeyCont>,
output: &mut LwePublicKey<OutputKeyCont>,
noise_parameters: impl DispersionParameter + Sync,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus + Sync + Send,
InputKeyCont: Container<Element = Scalar> + Sync,
OutputKeyCont: ContainerMut<Element = Scalar>,
Gen: ParallelByteRandomGenerator,
{
assert!(
lwe_secret_key.lwe_dimension() == output.lwe_size().to_lwe_dimension(),
"Mismatch LweDimension between lwe_secret_key {:?} and public key {:?}",
lwe_secret_key.lwe_dimension(),
output.lwe_size().to_lwe_dimension()
);
let zeros = PlaintextListOwned::new(
Scalar::ZERO,
PlaintextCount(output.zero_encryption_count().0),
);
par_encrypt_lwe_ciphertext_list(lwe_secret_key, output, &zeros, noise_parameters, generator)
}
pub fn par_allocate_and_generate_new_lwe_public_key<Scalar, InputKeyCont, Gen>(
lwe_secret_key: &LweSecretKey<InputKeyCont>,
zero_encryption_count: LwePublicKeyZeroEncryptionCount,
noise_parameters: impl DispersionParameter + Sync,
generator: &mut EncryptionRandomGenerator<Gen>,
) -> LwePublicKeyOwned<Scalar>
where
Scalar: UnsignedTorus + Sync + Send,
InputKeyCont: Container<Element = Scalar> + Sync,
Gen: ParallelByteRandomGenerator,
{
let mut pk = LwePublicKeyOwned::new(
Scalar::ZERO,
lwe_secret_key.lwe_dimension().to_lwe_size(),
zero_encryption_count,
);
generate_lwe_public_key(lwe_secret_key, &mut pk, noise_parameters, generator);
pk
}

View File

@@ -0,0 +1,67 @@
use crate::core_crypto::commons::generators::SecretRandomGenerator;
use crate::core_crypto::commons::math::random::{RandomGenerable, UniformBinary};
use crate::core_crypto::commons::numeric::Numeric;
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// Allocate a new [`LWE secret key`](`LweSecretKey`) and fill it with uniformly random binary
/// coefficients.
///
/// See [`encrypt_lwe_ciphertext`](`super::lwe_encryption::encrypt_lwe_ciphertext`) for usage.
pub fn allocate_and_generate_new_binary_lwe_secret_key<Scalar, Gen>(
lwe_dimension: LweDimension,
generator: &mut SecretRandomGenerator<Gen>,
) -> LweSecretKeyOwned<Scalar>
where
Scalar: RandomGenerable<UniformBinary> + Numeric,
Gen: ByteRandomGenerator,
{
let mut lwe_secret_key = LweSecretKeyOwned::new(Scalar::ZERO, lwe_dimension);
generate_binary_lwe_secret_key(&mut lwe_secret_key, generator);
lwe_secret_key
}
/// Fill an [`LWE secret key`](`LweSecretKey`) with uniformly random binary coefficients.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::commons::generators::SecretRandomGenerator;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for GlweCiphertext creation
/// let lwe_dimension = LweDimension(742);
//
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// let mut lwe_secret_key =
/// LweSecretKey::new(0u64, lwe_dimension);
///
/// generate_binary_lwe_secret_key(&mut lwe_secret_key, &mut secret_generator);
///
/// // Check all coefficients are not zero as we just generated a new key
/// // Note probability of this assert failing is (1/2)^lwe_dimension or ~4.3 * 10^-224 for an LWE
/// // dimension of 742.
/// assert!(lwe_secret_key.as_ref().iter().all(|&elt| elt == 0) == false);
/// ```
pub fn generate_binary_lwe_secret_key<Scalar, InCont, Gen>(
lwe_secret_key: &mut LweSecretKey<InCont>,
generator: &mut SecretRandomGenerator<Gen>,
) where
Scalar: RandomGenerable<UniformBinary>,
InCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
generator.fill_slice_with_random_uniform_binary(lwe_secret_key.as_mut())
}

View File

@@ -0,0 +1,356 @@
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::dispersion::DispersionParameter;
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use crate::core_crypto::fft_impl::crypto::bootstrap::FourierLweBootstrapKey;
use crate::core_crypto::fft_impl::crypto::wop_pbs::{
circuit_bootstrap_boolean_vertical_packing, circuit_bootstrap_boolean_vertical_packing_scratch,
extract_bits, extract_bits_scratch,
};
use crate::core_crypto::fft_impl::math::fft::FftView;
use concrete_fft::c64;
use dyn_stack::{DynStack, SizeOverflow, StackReq};
use rayon::prelude::*;
pub fn allocate_and_generate_new_circuit_bootstrap_lwe_pfpksk_list<
Scalar,
LweKeyCont,
GlweKeyCont,
Gen,
>(
input_lwe_secret_key: &LweSecretKey<LweKeyCont>,
output_glwe_secret_key: &GlweSecretKey<GlweKeyCont>,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) -> LwePrivateFunctionalPackingKeyswitchKeyListOwned<Scalar>
where
Scalar: UnsignedTorus,
LweKeyCont: Container<Element = Scalar>,
GlweKeyCont: Container<Element = Scalar>,
Gen: ByteRandomGenerator,
{
let mut cbs_pfpksk_list = LwePrivateFunctionalPackingKeyswitchKeyListOwned::new(
Scalar::ZERO,
decomp_base_log,
decomp_level_count,
input_lwe_secret_key.lwe_dimension(),
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
output_glwe_secret_key.polynomial_size(),
FunctionalPackingKeyswitchKeyCount(
output_glwe_secret_key.glwe_dimension().to_glwe_size().0,
),
);
generate_circuit_bootstrap_lwe_pfpksk_list(
&mut cbs_pfpksk_list,
input_lwe_secret_key,
output_glwe_secret_key,
noise_parameters,
generator,
);
cbs_pfpksk_list
}
pub fn generate_circuit_bootstrap_lwe_pfpksk_list<
Scalar,
OutputCont,
LweKeyCont,
GlweKeyCont,
Gen,
>(
output_cbs_pfpksk_list: &mut LwePrivateFunctionalPackingKeyswitchKeyList<OutputCont>,
input_lwe_secret_key: &LweSecretKey<LweKeyCont>,
output_glwe_secret_key: &GlweSecretKey<GlweKeyCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
OutputCont: ContainerMut<Element = Scalar>,
LweKeyCont: Container<Element = Scalar>,
GlweKeyCont: Container<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
output_cbs_pfpksk_list.lwe_pfpksk_count().0
== output_glwe_secret_key.glwe_dimension().to_glwe_size().0,
"Current list has {} pfpksk, need to have {} \
(output_glwe_key.glwe_dimension().to_glwe_size())",
output_cbs_pfpksk_list.lwe_pfpksk_count().0,
output_glwe_secret_key.glwe_dimension().to_glwe_size().0
);
let decomp_level_count = output_cbs_pfpksk_list.decomposition_level_count();
let gen_iter = generator
.fork_cbs_pfpksk_to_pfpksk::<Scalar>(
decomp_level_count,
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
output_glwe_secret_key.polynomial_size(),
input_lwe_secret_key.lwe_dimension().to_lwe_size(),
output_cbs_pfpksk_list.lwe_pfpksk_count(),
)
.unwrap();
let mut last_polynomial_as_list = PolynomialListOwned::new(
Scalar::ZERO,
output_glwe_secret_key.polynomial_size(),
PolynomialCount(1),
);
// We apply the x -> -x function so instead of putting one in the first coeff of the
// polynomial, we put Scalar::MAX == - Sclar::One so that we can use a single function in
// the loop avoiding branching
last_polynomial_as_list.get_mut(0)[0] = Scalar::MAX;
for ((mut lwe_pfpksk, polynomial_to_encrypt), mut loop_generator) in output_cbs_pfpksk_list
.iter_mut()
.zip(
output_glwe_secret_key
.as_polynomial_list()
.iter()
.chain(last_polynomial_as_list.iter()),
)
.zip(gen_iter)
{
generate_lwe_private_functional_packing_keyswitch_key(
input_lwe_secret_key,
output_glwe_secret_key,
&mut lwe_pfpksk,
noise_parameters,
&mut loop_generator,
|x| Scalar::ZERO.wrapping_sub(x),
&polynomial_to_encrypt,
);
}
}
pub fn par_allocate_and_generate_new_circuit_bootstrap_lwe_pfpksk_list<
Scalar,
LweKeyCont,
GlweKeyCont,
Gen,
>(
input_lwe_secret_key: &LweSecretKey<LweKeyCont>,
output_glwe_secret_key: &GlweSecretKey<GlweKeyCont>,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
noise_parameters: impl DispersionParameter + Sync,
generator: &mut EncryptionRandomGenerator<Gen>,
) -> LwePrivateFunctionalPackingKeyswitchKeyListOwned<Scalar>
where
Scalar: UnsignedTorus + Sync + Send,
LweKeyCont: Container<Element = Scalar> + Sync,
GlweKeyCont: Container<Element = Scalar> + Sync,
Gen: ParallelByteRandomGenerator,
{
let mut cbs_pfpksk_list = LwePrivateFunctionalPackingKeyswitchKeyListOwned::new(
Scalar::ZERO,
decomp_base_log,
decomp_level_count,
input_lwe_secret_key.lwe_dimension(),
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
output_glwe_secret_key.polynomial_size(),
FunctionalPackingKeyswitchKeyCount(
output_glwe_secret_key.glwe_dimension().to_glwe_size().0,
),
);
par_generate_circuit_bootstrap_lwe_pfpksk_list(
&mut cbs_pfpksk_list,
input_lwe_secret_key,
output_glwe_secret_key,
noise_parameters,
generator,
);
cbs_pfpksk_list
}
pub fn par_generate_circuit_bootstrap_lwe_pfpksk_list<
Scalar,
OutputCont,
LweKeyCont,
GlweKeyCont,
Gen,
>(
output_cbs_pfpksk_list: &mut LwePrivateFunctionalPackingKeyswitchKeyList<OutputCont>,
input_lwe_secret_key: &LweSecretKey<LweKeyCont>,
output_glwe_secret_key: &GlweSecretKey<GlweKeyCont>,
noise_parameters: impl DispersionParameter + Sync,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus + Sync + Send,
OutputCont: ContainerMut<Element = Scalar>,
LweKeyCont: Container<Element = Scalar> + Sync,
GlweKeyCont: Container<Element = Scalar> + Sync,
Gen: ParallelByteRandomGenerator,
{
assert!(
output_cbs_pfpksk_list.lwe_pfpksk_count().0
== output_glwe_secret_key.glwe_dimension().to_glwe_size().0,
"Current list has {} pfpksk, need to have {} \
(output_glwe_key.glwe_dimension().to_glwe_size())",
output_cbs_pfpksk_list.lwe_pfpksk_count().0,
output_glwe_secret_key.glwe_dimension().to_glwe_size().0
);
let decomp_level_count = output_cbs_pfpksk_list.decomposition_level_count();
let gen_iter = generator
.par_fork_cbs_pfpksk_to_pfpksk::<Scalar>(
decomp_level_count,
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
output_glwe_secret_key.polynomial_size(),
input_lwe_secret_key.lwe_dimension().to_lwe_size(),
output_cbs_pfpksk_list.lwe_pfpksk_count(),
)
.unwrap();
let mut last_polynomial_as_list = PolynomialListOwned::new(
Scalar::ZERO,
output_glwe_secret_key.polynomial_size(),
PolynomialCount(1),
);
// We apply the x -> -x function so instead of putting one in the first coeff of the
// polynomial, we put Scalar::MAX == - Sclar::One so that we can use a single function in
// the loop avoiding branching
last_polynomial_as_list.get_mut(0)[0] = Scalar::MAX;
output_cbs_pfpksk_list
.par_iter_mut()
.zip(
output_glwe_secret_key
.as_polynomial_list()
.par_iter()
.chain(last_polynomial_as_list.par_iter()),
)
.zip(gen_iter)
.for_each(
|((mut lwe_pfpksk, polynomial_to_encrypt), mut loop_generator)| {
par_generate_lwe_private_functional_packing_keyswitch_key(
input_lwe_secret_key,
output_glwe_secret_key,
&mut lwe_pfpksk,
noise_parameters,
&mut loop_generator,
|x| Scalar::ZERO.wrapping_sub(x),
&polynomial_to_encrypt,
);
},
);
}
#[allow(clippy::too_many_arguments)]
pub fn extract_bits_from_lwe_ciphertext<Scalar, InputCont, OutputCont, BskCont, KSKCont>(
lwe_in: &LweCiphertext<InputCont>,
lwe_list_out: &mut LweCiphertextList<OutputCont>,
fourier_bsk: &FourierLweBootstrapKey<BskCont>,
ksk: &LweKeyswitchKey<KSKCont>,
delta_log: DeltaLog,
number_of_bits_to_extract: ExtractedBitsCount,
fft: FftView<'_>,
stack: DynStack<'_>,
) where
// CastInto required for PBS modulus switch which returns a usize
Scalar: UnsignedTorus + CastInto<usize>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
BskCont: Container<Element = c64>,
KSKCont: Container<Element = Scalar>,
{
extract_bits(
lwe_list_out.as_mut_view(),
lwe_in.as_view(),
ksk.as_view(),
fourier_bsk.as_view(),
delta_log,
number_of_bits_to_extract,
fft,
stack,
)
}
pub fn extract_bits_from_lwe_ciphertext_scratch<Scalar>(
lwe_dimension: LweDimension,
ksk_output_key_lwe_dimension: LweDimension,
glwe_size: GlweSize,
polynomial_size: PolynomialSize,
fft: FftView<'_>,
) -> Result<StackReq, SizeOverflow> {
extract_bits_scratch::<Scalar>(
lwe_dimension,
ksk_output_key_lwe_dimension,
glwe_size,
polynomial_size,
fft,
)
}
#[allow(clippy::too_many_arguments)]
pub fn circuit_bootstrap_boolean_vertical_packing_lwe_ciphertext_list<
Scalar,
InputCont,
OutputCont,
LutCont,
BskCont,
PFPKSKCont,
>(
lwe_list_in: &LweCiphertextList<InputCont>,
lwe_list_out: &mut LweCiphertextList<OutputCont>,
big_lut_as_polynomial_list: &PolynomialList<LutCont>,
fourier_bsk: &FourierLweBootstrapKey<BskCont>,
pfpksk_list: &LwePrivateFunctionalPackingKeyswitchKeyList<PFPKSKCont>,
level_cbs: DecompositionLevelCount,
base_log_cbs: DecompositionBaseLog,
fft: FftView<'_>,
stack: DynStack<'_>,
) where
// CastInto required for PBS modulus switch which returns a usize
Scalar: UnsignedTorus + CastInto<usize>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
LutCont: Container<Element = Scalar>,
BskCont: Container<Element = c64>,
PFPKSKCont: Container<Element = Scalar>,
{
circuit_bootstrap_boolean_vertical_packing(
big_lut_as_polynomial_list.as_view(),
fourier_bsk.as_view(),
lwe_list_out.as_mut_view(),
lwe_list_in.as_view(),
pfpksk_list.as_view(),
level_cbs,
base_log_cbs,
fft,
stack,
)
}
#[allow(clippy::too_many_arguments)]
pub fn circuit_bootstrap_boolean_vertical_packing_lwe_ciphertext_list_scracth<Scalar>(
lwe_list_in_count: LweCiphertextCount,
lwe_list_out_count: LweCiphertextCount,
lwe_in_size: LweSize,
big_lut_polynomial_count: PolynomialCount,
bsk_output_lwe_size: LweSize,
glwe_size: GlweSize,
fpksk_output_polynomial_size: PolynomialSize,
level_cbs: DecompositionLevelCount,
fft: FftView<'_>,
) -> Result<StackReq, SizeOverflow> {
circuit_bootstrap_boolean_vertical_packing_scratch::<Scalar>(
lwe_list_in_count,
lwe_list_out_count,
lwe_in_size,
big_lut_polynomial_count,
bsk_output_lwe_size,
glwe_size,
fpksk_output_polynomial_size,
level_cbs,
fft,
)
}

View File

@@ -0,0 +1,9 @@
use crate::core_crypto::prelude::*;
/// Convenience function using a bit trick to determine whether a scalar is a power of 2.
pub fn is_power_of_two<Scalar>(scalar: Scalar) -> bool
where
Scalar: UnsignedInteger,
{
(scalar != Scalar::ZERO) && ((scalar & (scalar - Scalar::ONE)) == Scalar::ZERO)
}

View File

@@ -0,0 +1,39 @@
pub mod ggsw_encryption;
pub mod glwe_encryption;
pub mod glwe_sample_extraction;
pub mod glwe_secret_key_generation;
pub mod lwe_bootstrap_key_conversion;
pub mod lwe_bootstrap_key_generation;
pub mod lwe_encryption;
pub mod lwe_keyswitch;
pub mod lwe_keyswitch_key_generation;
pub mod lwe_linear_algebra;
pub mod lwe_private_functional_packing_keyswitch;
pub mod lwe_private_functional_packing_keyswitch_key_generation;
pub mod lwe_programmable_bootstrapping;
pub mod lwe_public_key_generation;
pub mod lwe_secret_key_generation;
pub mod lwe_wopbs;
pub mod misc;
pub mod polynomial_algorithms;
pub mod slice_algorithms;
// No pub use for slice and polynomial algorithms which would not interest higher level users
// They can still be used via `use crate::core_crypto::algorithms::slice_algorithms::*;`
pub use ggsw_encryption::*;
pub use glwe_encryption::*;
pub use glwe_sample_extraction::*;
pub use glwe_secret_key_generation::*;
pub use lwe_bootstrap_key_conversion::*;
pub use lwe_bootstrap_key_generation::*;
pub use lwe_encryption::*;
pub use lwe_keyswitch::*;
pub use lwe_keyswitch_key_generation::*;
pub use lwe_linear_algebra::*;
pub use lwe_private_functional_packing_keyswitch::*;
pub use lwe_private_functional_packing_keyswitch_key_generation::*;
pub use lwe_programmable_bootstrapping::*;
pub use lwe_public_key_generation::*;
pub use lwe_secret_key_generation::*;
pub use lwe_wopbs::*;
pub use misc::*;

View File

@@ -0,0 +1,600 @@
//! Module providing algorithms to perform computations on polynomials modulo $X^{N} + 1$.
use crate::core_crypto::algorithms::misc::*;
use crate::core_crypto::algorithms::slice_algorithms::*;
use crate::core_crypto::commons::numeric::UnsignedInteger;
use crate::core_crypto::commons::parameters::MonomialDegree;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// Add a polynomial to the output polynomial.
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::polynomial_algorithms::*;
/// use tfhe::core_crypto::entities::*;
/// let mut first = Polynomial::from_container(vec![1u8, 2, 3, 4, 5, 6]);
/// let second = Polynomial::from_container(vec![255u8, 255, 255, 1, 2, 3]);
/// polynomial_wrapping_add_assign(&mut first, &second);
/// assert_eq!(first.as_ref(), &[0u8, 1, 2, 5, 7, 9]);
/// ```
pub fn polynomial_wrapping_add_assign<Scalar, OutputCont, InputCont>(
lhs: &mut Polynomial<OutputCont>,
rhs: &Polynomial<InputCont>,
) where
Scalar: UnsignedInteger,
OutputCont: ContainerMut<Element = Scalar>,
InputCont: Container<Element = Scalar>,
{
assert_eq!(lhs.polynomial_size(), rhs.polynomial_size());
slice_wrapping_add_assign(lhs.as_mut(), rhs.as_ref())
}
/// Add the sum of the element-wise product between two lists of polynomials to the output
/// polynomial.
///
/// I.e., if the output polynomial is $C(X)$, for a collection of polynomials $(P\_i(X)))\_i$
/// and another collection of polynomials $(B\_i(X))\_i$ we perform the operation:
/// $$
/// C(X) := C(X) + \sum\_i P\_i(X) \times B\_i(X) mod (X^{N} + 1)
/// $$
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::polynomial_algorithms::*;
/// use tfhe::core_crypto::commons::parameters::*;
/// use tfhe::core_crypto::entities::*;
/// let poly_list = PolynomialList::from_container(vec![100_u8, 20, 3, 4, 5, 6], PolynomialSize(3));
/// let bin_poly_list = PolynomialList::from_container(vec![0, 1, 1, 1, 0, 0], PolynomialSize(3));
/// let mut output = Polynomial::new(250, PolynomialSize(3));
/// polynomial_wrapping_add_multisum_assign(&mut output, &poly_list, &bin_poly_list);
/// assert_eq!(output.as_ref(), &[231, 96, 120]);
/// ```
pub fn polynomial_wrapping_add_multisum_assign<Scalar, OutputCont, InputCont1, InputCont2>(
output: &mut Polynomial<OutputCont>,
poly_list_1: &PolynomialList<InputCont1>,
poly_list_2: &PolynomialList<InputCont2>,
) where
Scalar: UnsignedInteger,
OutputCont: ContainerMut<Element = Scalar>,
InputCont1: Container<Element = Scalar>,
InputCont2: Container<Element = Scalar>,
{
for (poly_1, poly_2) in poly_list_1.iter().zip(poly_list_2.iter()) {
polynomial_wrapping_add_mul_assign(output, &poly_1, &poly_2);
}
}
/// Add the result of the product between two polynomials, reduced modulo $(X^{N}+1)$, to the
/// output polynomial.
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::polynomial_algorithms::*;
/// use tfhe::core_crypto::entities::*;
/// let poly_1 = Polynomial::from_container(vec![1_u8, 2, 3]);
/// let poly_2 = Polynomial::from_container(vec![0, 1, 1]);
/// let mut res = Polynomial::from_container(vec![1, 0, 253]);
/// polynomial_wrapping_add_mul_assign(&mut res, &poly_1, &poly_2);
/// assert_eq!(res.as_ref(), &[252, 254, 0]);
/// ```
pub fn polynomial_wrapping_add_mul_assign<Scalar, OutputCont, InputCont1, InputCont2>(
output: &mut Polynomial<OutputCont>,
lhs: &Polynomial<InputCont1>,
rhs: &Polynomial<InputCont2>,
) where
Scalar: UnsignedInteger,
OutputCont: ContainerMut<Element = Scalar>,
InputCont1: Container<Element = Scalar>,
InputCont2: Container<Element = Scalar>,
{
assert!(
output.polynomial_size() == lhs.polynomial_size(),
"Output polynomial size {:?} is not the same as input lhs polynomial {:?}.",
output.polynomial_size(),
lhs.polynomial_size(),
);
assert!(
output.polynomial_size() == rhs.polynomial_size(),
"Output polynomial size {:?} is not the same as input rhs polynomial {:?}.",
output.polynomial_size(),
rhs.polynomial_size(),
);
let degree = output.degree();
let polynomial_size = output.polynomial_size();
for (lhs_degree, &lhs_coeff) in lhs.iter().enumerate() {
for (rhs_degree, &rhs_coeff) in rhs.iter().enumerate() {
let target_degree = lhs_degree + rhs_degree;
if target_degree <= degree {
let output_coefficient = &mut output.as_mut()[target_degree];
*output_coefficient =
(*output_coefficient).wrapping_add(lhs_coeff.wrapping_mul(rhs_coeff));
} else {
let target_degree = target_degree % polynomial_size.0;
let output_coefficient = &mut output.as_mut()[target_degree];
*output_coefficient =
(*output_coefficient).wrapping_sub(lhs_coeff.wrapping_mul(rhs_coeff));
}
}
}
}
/// Divides (mod $(X^{N}+1)$), the output polynomial with a monic monomial of a given degree i.e.
/// $X^{degree}$.
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Examples
///
/// ```
/// use tfhe::core_crypto::algorithms::polynomial_algorithms::*;
/// use tfhe::core_crypto::commons::parameters::*;
/// use tfhe::core_crypto::entities::*;
/// let mut poly = Polynomial::from_container(vec![1u8, 2, 3]);
/// polynomial_wrapping_monic_monomial_div_assign(&mut poly, MonomialDegree(2));
/// assert_eq!(poly.as_ref(), &[3, 255, 254]);
/// ```
pub fn polynomial_wrapping_monic_monomial_div_assign<Scalar, OutputCont>(
output: &mut Polynomial<OutputCont>,
monomial_degree: MonomialDegree,
) where
Scalar: UnsignedInteger,
OutputCont: ContainerMut<Element = Scalar>,
{
let full_cycles_count = monomial_degree.0 / output.as_ref().container_len();
if full_cycles_count % 2 != 0 {
output
.as_mut()
.iter_mut()
.for_each(|a| *a = a.wrapping_neg());
}
let remaining_degree = monomial_degree.0 % output.as_ref().container_len();
output.as_mut().rotate_left(remaining_degree);
output
.as_mut()
.iter_mut()
.rev()
.take(remaining_degree)
.for_each(|a| *a = a.wrapping_neg());
}
/// Multiply (mod $(X^{N}+1)$), the output polynomial with a monic monomial of a given degree i.e.
/// $X^{degree}$.
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Examples
///
/// ```
/// use tfhe::core_crypto::algorithms::polynomial_algorithms::*;
/// use tfhe::core_crypto::commons::parameters::*;
/// use tfhe::core_crypto::entities::*;
/// let mut poly = Polynomial::from_container(vec![1u8, 2, 3]);
/// polynomial_wrapping_monic_monomial_mul_assign(&mut poly, MonomialDegree(2));
/// assert_eq!(poly.as_ref(), &[254, 253, 1]);
/// ```
pub fn polynomial_wrapping_monic_monomial_mul_assign<Scalar, OutputCont>(
output: &mut Polynomial<OutputCont>,
monomial_degree: MonomialDegree,
) where
Scalar: UnsignedInteger,
OutputCont: ContainerMut<Element = Scalar>,
{
let full_cycles_count = monomial_degree.0 / output.as_ref().container_len();
if full_cycles_count % 2 != 0 {
output
.as_mut()
.iter_mut()
.for_each(|a| *a = a.wrapping_neg());
}
let remaining_degree = monomial_degree.0 % output.as_ref().container_len();
output.as_mut().rotate_right(remaining_degree);
output
.as_mut()
.iter_mut()
.take(remaining_degree)
.for_each(|a| *a = a.wrapping_neg());
}
/// Subtract the sum of the element-wise product between two lists of polynomials, to the output
/// polynomial.
///
/// I.e., if the output polynomial is $C(X)$, for two lists of polynomials $(P\_i(X)))\_i$ and
/// $(B\_i(X))\_i$ we perform the operation:
/// $$
/// C(X) := C(X) + \sum\_i P\_i(X) \times B\_i(X) mod (X^{N} + 1)
/// $$
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::polynomial_algorithms::*;
/// use tfhe::core_crypto::commons::parameters::*;
/// use tfhe::core_crypto::entities::*;
/// let poly_list =
/// PolynomialList::from_container(vec![100 as u8, 20, 3, 4, 5, 6], PolynomialSize(3));
/// let bin_poly_list = PolynomialList::from_container(vec![0, 1, 1, 1, 0, 0], PolynomialSize(3));
/// let mut output = Polynomial::new(250 as u8, PolynomialSize(3));
/// polynomial_wrapping_sub_multisum_assign(&mut output, &poly_list, &bin_poly_list);
/// assert_eq!(output.as_ref(), &[13, 148, 124]);
/// ```
pub fn polynomial_wrapping_sub_multisum_assign<Scalar, OutputCont, InputCont1, InputCont2>(
output: &mut Polynomial<OutputCont>,
poly_list_1: &PolynomialList<InputCont1>,
poly_list_2: &PolynomialList<InputCont2>,
) where
Scalar: UnsignedInteger,
OutputCont: ContainerMut<Element = Scalar>,
InputCont1: Container<Element = Scalar>,
InputCont2: Container<Element = Scalar>,
{
for (poly_1, poly_2) in poly_list_1.iter().zip(poly_list_2.iter()) {
polynomial_wrapping_sub_mul_assign(output, &poly_1, &poly_2);
}
}
/// Subtract the result of the product between two polynomials, reduced modulo $(X^{N}+1)$, to the
/// output polynomial.
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::polynomial_algorithms::*;
/// use tfhe::core_crypto::entities::*;
/// let poly_1 = Polynomial::from_container(vec![1_u8, 2, 3]);
/// let poly_2 = Polynomial::from_container(vec![0, 1, 1]);
/// let mut res = Polynomial::from_container(vec![255, 255, 1]);
/// polynomial_wrapping_sub_mul_assign(&mut res, &poly_1, &poly_2);
/// assert_eq!(res.as_ref(), &[4, 1, 254]);
/// ```
pub fn polynomial_wrapping_sub_mul_assign<Scalar, OutputCont, InputCont1, InputCont2>(
output: &mut Polynomial<OutputCont>,
lhs: &Polynomial<InputCont1>,
rhs: &Polynomial<InputCont2>,
) where
Scalar: UnsignedInteger,
OutputCont: ContainerMut<Element = Scalar>,
InputCont1: Container<Element = Scalar>,
InputCont2: Container<Element = Scalar>,
{
assert!(
output.polynomial_size() == lhs.polynomial_size(),
"Output polynomial size {:?} is not the same as input lhs polynomial {:?}.",
output.polynomial_size(),
lhs.polynomial_size(),
);
assert!(
output.polynomial_size() == rhs.polynomial_size(),
"Output polynomial size {:?} is not the same as input rhs polynomial {:?}.",
output.polynomial_size(),
rhs.polynomial_size(),
);
let degree = output.degree();
let polynomial_size = output.polynomial_size();
for (lhs_degree, &lhs_coeff) in lhs.iter().enumerate() {
for (rhs_degree, &rhs_coeff) in rhs.iter().enumerate() {
let target_degree = lhs_degree + rhs_degree;
if target_degree <= degree {
let output_coefficient = &mut output.as_mut()[target_degree];
*output_coefficient =
(*output_coefficient).wrapping_sub(lhs_coeff.wrapping_mul(rhs_coeff));
} else {
let target_degree = target_degree % polynomial_size.0;
let output_coefficient = &mut output.as_mut()[target_degree];
*output_coefficient =
(*output_coefficient).wrapping_add(lhs_coeff.wrapping_mul(rhs_coeff));
}
}
}
}
/// Fill the ouptut polynomial, with the result of the product of two polynomials, reduced modulo
/// $(X^{N} + 1)$ with the schoolbook algorithm Complexity: $O(N^{2})$
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::polynomial_algorithms::*;
/// use tfhe::core_crypto::commons::parameters::*;
/// use tfhe::core_crypto::entities::*;
/// let lhs = Polynomial::from_container(vec![4_u8, 5, 0]);
/// let rhs = Polynomial::from_container(vec![7_u8, 9, 0]);
/// let mut output = Polynomial::new(2u8, PolynomialSize(3));
/// polynomial_wrapping_mul(&mut output, &lhs, &rhs);
/// assert_eq!(output.as_ref(), &[28, 71, 45]);
/// ```
pub fn polynomial_wrapping_mul<Scalar, OutputCont, LhsCont, RhsCont>(
output: &mut Polynomial<OutputCont>,
lhs: &Polynomial<LhsCont>,
rhs: &Polynomial<RhsCont>,
) where
Scalar: UnsignedInteger,
OutputCont: ContainerMut<Element = Scalar>,
LhsCont: Container<Element = Scalar>,
RhsCont: Container<Element = Scalar>,
{
output.as_mut().fill(Scalar::ZERO);
polynomial_wrapping_add_mul_assign(output, lhs, rhs);
}
/// Fill the output polynomial, with the result of the product of two polynomials, reduced modulo
/// $(X^{N} + 1)$ with the Karatsuba algorithm Complexity: $O(N^{1.58})$
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::polynomial_algorithms::*;
/// use tfhe::core_crypto::commons::parameters::*;
/// use tfhe::core_crypto::entities::*;
/// let lhs = Polynomial::from_container(vec![1_u32; 128]);
/// let rhs = Polynomial::from_container(vec![2_u32; 128]);
/// let mut res_kara = Polynomial::new(0u32, PolynomialSize(128));
/// let mut res_mul = Polynomial::new(0u32, PolynomialSize(128));
/// polynomial_karatsuba_wrapping_mul(&mut res_kara, &lhs, &rhs);
/// polynomial_wrapping_mul(&mut res_mul, &lhs, &rhs);
/// assert_eq!(res_kara, res_mul);
/// ```
pub fn polynomial_karatsuba_wrapping_mul<Scalar, OutputCont, LhsCont, RhsCont>(
output: &mut Polynomial<OutputCont>,
p: &Polynomial<LhsCont>,
q: &Polynomial<RhsCont>,
) where
Scalar: UnsignedInteger,
OutputCont: ContainerMut<Element = Scalar>,
LhsCont: Container<Element = Scalar>,
RhsCont: Container<Element = Scalar>,
{
// check same dimensions
assert!(
output.polynomial_size() == p.polynomial_size(),
"Output polynomial size {:?} is not the same as input lhs polynomial {:?}.",
output.polynomial_size(),
p.polynomial_size(),
);
assert!(
output.polynomial_size() == q.polynomial_size(),
"Output polynomial size {:?} is not the same as input rhs polynomial {:?}.",
output.polynomial_size(),
q.polynomial_size(),
);
let poly_size = output.polynomial_size().0;
// check dimensions are a power of 2
assert!(is_power_of_two::<u32>(poly_size.try_into().unwrap()));
// allocate slices for the rec
let mut a0 = vec![Scalar::ZERO; poly_size];
let mut a1 = vec![Scalar::ZERO; poly_size];
let mut a2 = vec![Scalar::ZERO; poly_size];
let mut input_a2_p = vec![Scalar::ZERO; poly_size / 2];
let mut input_a2_q = vec![Scalar::ZERO; poly_size / 2];
// prepare for splitting
let bottom = 0..(poly_size / 2);
let top = (poly_size / 2)..poly_size;
// induction
induction_karatsuba(&mut a0, &p[bottom.clone()], &q[bottom.clone()]);
induction_karatsuba(&mut a1, &p[top.clone()], &q[top.clone()]);
slice_wrapping_add(&mut input_a2_p, &p[bottom.clone()], &p[top.clone()]);
slice_wrapping_add(&mut input_a2_q, &q[bottom.clone()], &q[top.clone()]);
induction_karatsuba(&mut a2, &input_a2_p, &input_a2_q);
// rebuild the result
let output: &mut [Scalar] = output.as_mut();
slice_wrapping_sub(output, &a0, &a1);
slice_wrapping_sub_assign(&mut output[bottom.clone()], &a2[top.clone()]);
slice_wrapping_add_assign(&mut output[bottom.clone()], &a0[top.clone()]);
slice_wrapping_add_assign(&mut output[bottom.clone()], &a1[top.clone()]);
slice_wrapping_add_assign(&mut output[top.clone()], &a2[bottom.clone()]);
slice_wrapping_sub_assign(&mut output[top.clone()], &a0[bottom.clone()]);
slice_wrapping_sub_assign(&mut output[top], &a1[bottom]);
}
/// Compute the induction for the karatsuba algorithm.
fn induction_karatsuba<Scalar>(res: &mut [Scalar], p: &[Scalar], q: &[Scalar])
where
Scalar: UnsignedInteger,
{
// stop the induction when polynomials have KARATUSBA_STOP elements
const KARATUSBA_STOP: usize = 32;
if p.len() == KARATUSBA_STOP {
// schoolbook algorithm
for (lhs_degree, &lhs_elt) in p.iter().enumerate() {
for (rhs_degree, &rhs_elt) in q.iter().enumerate() {
res[lhs_degree + rhs_degree] =
res[lhs_degree + rhs_degree].wrapping_add(lhs_elt.wrapping_mul(rhs_elt))
}
}
} else {
let poly_size = res.len();
// allocate slices for the rec
let mut a0 = vec![Scalar::ZERO; poly_size / 2];
let mut a1 = vec![Scalar::ZERO; poly_size / 2];
let mut a2 = vec![Scalar::ZERO; poly_size / 2];
let mut input_a2_p = vec![Scalar::ZERO; poly_size / 4];
let mut input_a2_q = vec![Scalar::ZERO; poly_size / 4];
// prepare for splitting
let bottom = 0..(poly_size / 4);
let top = (poly_size / 4)..(poly_size / 2);
// rec
induction_karatsuba(&mut a0, &p[bottom.clone()], &q[bottom.clone()]);
induction_karatsuba(&mut a1, &p[top.clone()], &q[top.clone()]);
slice_wrapping_add(&mut input_a2_p, &p[bottom.clone()], &p[top.clone()]);
slice_wrapping_add(&mut input_a2_q, &q[bottom], &q[top]);
induction_karatsuba(&mut a2, &input_a2_p, &input_a2_q);
// rebuild the result
slice_wrapping_sub(&mut res[(poly_size / 4)..(3 * poly_size / 4)], &a2, &a0);
slice_wrapping_sub_assign(&mut res[(poly_size / 4)..(3 * poly_size / 4)], &a1);
slice_wrapping_add_assign(&mut res[0..(poly_size / 2)], &a0);
slice_wrapping_add_assign(&mut res[(poly_size / 2)..poly_size], &a1);
}
}
#[cfg(test)]
mod test {
use rand::Rng;
use crate::core_crypto::algorithms::polynomial_algorithms::*;
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::test_tools::*;
fn test_multiply_divide_unit_monomial<T: UnsignedTorus>() {
//! tests if multiply_by_monomial and divide_by_monomial cancel each other
let mut rng = rand::thread_rng();
let mut generator = new_random_generator();
// settings
let polynomial_size = random_polynomial_size(2048);
// generate a random Torus polynomial
let mut poly = Polynomial::new(T::ZERO, polynomial_size);
generator.fill_slice_with_random_uniform(poly.as_mut());
let polynomial_size = polynomial_size.0;
// copy this polynomial
let ground_truth = poly.clone();
// generate a random r
let mut r: usize = rng.gen();
r %= polynomial_size;
// multiply by X^r and then divides by X^r
polynomial_wrapping_monic_monomial_mul_assign(&mut poly, MonomialDegree(r));
polynomial_wrapping_monic_monomial_div_assign(&mut poly, MonomialDegree(r));
// test
assert_eq!(&poly, &ground_truth);
// generate a random r_big
let mut r_big: usize = rng.gen();
r_big = r_big % polynomial_size + 2048;
// multiply by X^r_big and then divides by X^r_big
polynomial_wrapping_monic_monomial_mul_assign(&mut poly, MonomialDegree(r_big));
polynomial_wrapping_monic_monomial_div_assign(&mut poly, MonomialDegree(r_big));
// test
assert_eq!(&poly, &ground_truth);
// divides by X^r_big and then multiply by X^r_big
polynomial_wrapping_monic_monomial_mul_assign(&mut poly, MonomialDegree(r_big));
polynomial_wrapping_monic_monomial_div_assign(&mut poly, MonomialDegree(r_big));
// test
assert_eq!(&poly, &ground_truth);
}
/// test if we have the same result when using schoolbook or karatsuba
/// for random polynomial multiplication
fn test_multiply_karatsuba<T: UnsignedTorus>() {
// 50 times the test
for _i in 0..50 {
// random source
let mut rng = rand::thread_rng();
// random settings settings
let polynomial_log = (rng.gen::<usize>() % 7) + 6;
let polynomial_size = PolynomialSize(1 << polynomial_log);
let mut generator = new_random_generator();
// generate two random Torus polynomials
let mut poly_1 = Polynomial::new(T::ZERO, polynomial_size);
generator.fill_slice_with_random_uniform::<T>(poly_1.as_mut());
let poly_1 = poly_1;
let mut poly_2 = Polynomial::new(T::ZERO, polynomial_size);
generator.fill_slice_with_random_uniform::<T>(poly_2.as_mut());
let poly_2 = poly_2;
// copy this polynomial
let mut sb_mul = Polynomial::new(T::ZERO, polynomial_size);
let mut ka_mul = Polynomial::new(T::ZERO, polynomial_size);
// compute the schoolbook
polynomial_wrapping_mul(&mut sb_mul, &poly_1, &poly_2);
// compute the karatsuba
polynomial_karatsuba_wrapping_mul(&mut ka_mul, &poly_1, &poly_2);
// test
assert_eq!(&sb_mul, &ka_mul);
}
}
#[test]
pub fn test_multiply_divide_unit_monomial_u32() {
test_multiply_divide_unit_monomial::<u32>()
}
#[test]
pub fn test_multiply_divide_unit_monomial_u64() {
test_multiply_divide_unit_monomial::<u64>()
}
#[test]
pub fn test_multiply_karatsuba_u32() {
test_multiply_karatsuba::<u32>()
}
#[test]
pub fn test_multiply_karatsuba_u64() {
test_multiply_karatsuba::<u64>()
}
}

View File

@@ -0,0 +1,303 @@
//! Module providing algorithms to perform computations on raw slices.
use crate::core_crypto::commons::numeric::UnsignedInteger;
/// Compute a dot product between two slices containing unsigned integers.
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::slice_algorithms::*;
/// let mut first = vec![1u8, 2, 3, 4, 5, 6];
/// let second = vec![255u8, 255, 255, 1, 2, 3];
/// let dot_product = slice_wrapping_dot_product(&first, &second);
/// assert_eq!(dot_product, 26);
/// ```
pub fn slice_wrapping_dot_product<Scalar>(lhs: &[Scalar], rhs: &[Scalar]) -> Scalar
where
Scalar: UnsignedInteger,
{
assert!(
lhs.len() == rhs.len(),
"lhs (len: {}) and rhs (len: {}) must have the same length",
lhs.len(),
rhs.len()
);
lhs.iter()
.zip(rhs.iter())
.fold(Scalar::ZERO, |acc, (&left, &right)| {
acc.wrapping_add(left.wrapping_mul(right))
})
}
/// Add a slice containing unsigned integers to another one element-wise.
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::slice_algorithms::*;
/// let mut first = vec![1u8, 2, 3, 4, 5, 6];
/// let second = vec![255u8, 255, 255, 1, 2, 3];
/// let mut add = vec![0_u8; 6];
/// slice_wrapping_add(&mut add, &first, &second);
/// assert_eq!(&add, &[0u8, 1, 2, 5, 7, 9]);
/// ```
pub fn slice_wrapping_add<Scalar>(output: &mut [Scalar], lhs: &[Scalar], rhs: &[Scalar])
where
Scalar: UnsignedInteger,
{
assert!(
lhs.len() == rhs.len(),
"lhs (len: {}) and rhs (len: {}) must have the same length",
lhs.len(),
rhs.len()
);
assert!(
output.len() == lhs.len(),
"output (len: {}) and rhs (lhs: {}) must have the same length",
output.len(),
lhs.len()
);
output
.iter_mut()
.zip(lhs.iter().zip(rhs.iter()))
.for_each(|(out, (&lhs, &rhs))| *out = lhs.wrapping_add(rhs));
}
/// Add a slice containing unsigned integers to another one element-wise and in place.
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::slice_algorithms::*;
/// let mut first = vec![1u8, 2, 3, 4, 5, 6];
/// let second = vec![255u8, 255, 255, 1, 2, 3];
/// slice_wrapping_add_assign(&mut first, &second);
/// assert_eq!(&first, &[0u8, 1, 2, 5, 7, 9]);
/// ```
pub fn slice_wrapping_add_assign<Scalar>(lhs: &mut [Scalar], rhs: &[Scalar])
where
Scalar: UnsignedInteger,
{
assert!(
lhs.len() == rhs.len(),
"lhs (len: {}) and rhs (len: {}) must have the same length",
lhs.len(),
rhs.len()
);
lhs.iter_mut()
.zip(rhs.iter())
.for_each(|(lhs, &rhs)| *lhs = (*lhs).wrapping_add(rhs));
}
/// Add a slice containing unsigned integers to another one mutiplied by a scalar.
///
/// Let *a*,*b* be two slices, let *c* be a scalar, this computes: *a <- a+bc*
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::slice_algorithms::*;
/// let mut first = vec![1u8, 2, 3, 4, 5, 6];
/// let second = vec![255u8, 255, 255, 1, 2, 3];
/// let scalar = 4u8;
/// slice_wrapping_add_scalar_mul_assign(&mut first, &second, scalar);
/// assert_eq!(&first, &[253u8, 254, 255, 8, 13, 18]);
/// ```
pub fn slice_wrapping_add_scalar_mul_assign<Scalar>(
lhs: &mut [Scalar],
rhs: &[Scalar],
scalar: Scalar,
) where
Scalar: UnsignedInteger,
{
assert!(
lhs.len() == rhs.len(),
"lhs (len: {}) and rhs (len: {}) must have the same length",
lhs.len(),
rhs.len()
);
lhs.iter_mut()
.zip(rhs.iter())
.for_each(|(lhs, &rhs)| *lhs = (*lhs).wrapping_add(rhs.wrapping_mul(scalar)));
}
/// Subtract a slice containing unsigned integers to another one element-wise.
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::slice_algorithms::*;
/// let mut first = vec![1u8, 2, 3, 4, 5, 6];
/// let second = vec![255u8, 255, 255, 1, 2, 3];
/// let mut add = vec![0_u8; 6];
/// slice_wrapping_sub(&mut add, &first, &second);
/// assert_eq!(&add, &[2, 3, 4, 3, 3, 3]);
/// ```
pub fn slice_wrapping_sub<Scalar>(output: &mut [Scalar], lhs: &[Scalar], rhs: &[Scalar])
where
Scalar: UnsignedInteger,
{
assert!(
lhs.len() == rhs.len(),
"lhs (len: {}) and rhs (len: {}) must have the same length",
lhs.len(),
rhs.len()
);
assert!(
output.len() == lhs.len(),
"output (len: {}) and rhs (lhs: {}) must have the same length",
output.len(),
lhs.len()
);
output
.iter_mut()
.zip(lhs.iter().zip(rhs.iter()))
.for_each(|(out, (&lhs, &rhs))| *out = lhs.wrapping_sub(rhs));
}
/// Subtract a slice containing unsigned integers to another one, element-wise and in place.
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::slice_algorithms::*;
/// let mut first = vec![1u8, 2, 3, 4, 5, 6];
/// let second = vec![255u8, 255, 255, 1, 2, 3];
/// slice_wrapping_sub_assign(&mut first, &second);
/// assert_eq!(&first, &[2u8, 3, 4, 3, 3, 3]);
/// ```
pub fn slice_wrapping_sub_assign<Scalar>(lhs: &mut [Scalar], rhs: &[Scalar])
where
Scalar: UnsignedInteger,
{
assert!(
lhs.len() == rhs.len(),
"lhs (len: {}) and rhs (len: {}) must have the same length",
lhs.len(),
rhs.len()
);
lhs.iter_mut()
.zip(rhs.iter())
.for_each(|(lhs, &rhs)| *lhs = (*lhs).wrapping_sub(rhs));
}
/// Subtract a slice containing unsigned integers to another one mutiplied by a scalar,
/// element-wise and in place.
///
/// Let *a*,*b* be two slices, let *c* be a scalar, this computes: *a <- a-bc*
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::slice_algorithms::*;
/// let mut first = vec![1u8, 2, 3, 4, 5, 6];
/// let second = vec![255u8, 255, 255, 1, 2, 3];
/// let scalar = 4u8;
/// slice_wrapping_sub_scalar_mul_assign(&mut first, &second, scalar);
/// assert_eq!(&first, &[5u8, 6, 7, 0, 253, 250]);
pub fn slice_wrapping_sub_scalar_mul_assign<Scalar>(
lhs: &mut [Scalar],
rhs: &[Scalar],
scalar: Scalar,
) where
Scalar: UnsignedInteger,
{
assert!(
lhs.len() == rhs.len(),
"lhs (len: {}) and rhs (len: {}) must have the same length",
lhs.len(),
rhs.len()
);
lhs.iter_mut()
.zip(rhs.iter())
.for_each(|(lhs, &rhs)| *lhs = (*lhs).wrapping_sub(rhs.wrapping_mul(scalar)));
}
/// Compute the opposite of a slice containing unsigned integers, element-wise and in place.
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::slice_algorithms::*;
/// let mut first = vec![1u8, 2, 3, 4, 5, 6];
/// slice_wrapping_opposite_assign(&mut first);
/// assert_eq!(&first, &[255u8, 254, 253, 252, 251, 250]);
/// ```
pub fn slice_wrapping_opposite_assign<Scalar>(slice: &mut [Scalar])
where
Scalar: UnsignedInteger,
{
slice
.iter_mut()
.for_each(|elt| *elt = (*elt).wrapping_neg());
}
/// Multiply a slice containing unsigned integers by a scalar, element-wise and in place.
///
/// # Note
///
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
/// unsigned integer capacity.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::algorithms::slice_algorithms::*;
/// let mut first = vec![1u8, 2, 3, 4, 5, 6];
/// let scalar = 252;
/// slice_wrapping_scalar_mul_assign(&mut first, scalar);
/// assert_eq!(&first, &[252, 248, 244, 240, 236, 232]);
/// ```
pub fn slice_wrapping_scalar_mul_assign<Scalar>(lhs: &mut [Scalar], rhs: Scalar)
where
Scalar: UnsignedInteger,
{
lhs.iter_mut()
.for_each(|lhs| *lhs = (*lhs).wrapping_mul(rhs));
}

View File

@@ -1,340 +0,0 @@
use crate::core_crypto::backends::cuda::engines::{CudaEngine, CudaError};
use crate::core_crypto::backends::cuda::implementation::entities::{
CudaGlweCiphertext32, CudaGlweCiphertext64,
};
use crate::core_crypto::backends::cuda::private::crypto::glwe::ciphertext::CudaGlweCiphertext;
use crate::core_crypto::commons::crypto::glwe::GlweCiphertext;
use crate::core_crypto::commons::math::tensor::{AsRefSlice, AsRefTensor};
use crate::core_crypto::prelude::{GlweCiphertext32, GlweCiphertext64, GlweCiphertextView64};
use crate::core_crypto::specification::engines::{
GlweCiphertextConversionEngine, GlweCiphertextConversionError,
};
use crate::core_crypto::specification::entities::GlweCiphertextEntity;
impl From<CudaError> for GlweCiphertextConversionError<CudaError> {
fn from(err: CudaError) -> Self {
Self::Engine(err)
}
}
/// # Description
/// Convert a GLWE ciphertext with 32 bits of precision from CPU to GPU 0.
/// Only this conversion is necessary to run the bootstrap on the GPU.
impl GlweCiphertextConversionEngine<GlweCiphertext32, CudaGlweCiphertext32> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::*;
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let glwe_dimension = GlweDimension(2);
/// let polynomial_size = PolynomialSize(4);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = vec![3_u32 << 20; polynomial_size.0];
/// let noise = Variance(2_f64.powf(-50.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let h_plaintext_vector: PlaintextVector32 =
/// default_engine.create_plaintext_vector_from(&input)?;
/// let mut h_ciphertext: GlweCiphertext32 = default_engine
/// .trivially_encrypt_glwe_ciphertext(glwe_dimension.to_glwe_size(), &h_plaintext_vector)?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ciphertext: CudaGlweCiphertext32 = cuda_engine.convert_glwe_ciphertext(&h_ciphertext)?;
///
/// assert_eq!(d_ciphertext.glwe_dimension(), glwe_dimension);
/// assert_eq!(d_ciphertext.polynomial_size(), polynomial_size);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_glwe_ciphertext(
&mut self,
input: &GlweCiphertext32,
) -> Result<CudaGlweCiphertext32, GlweCiphertextConversionError<CudaError>> {
let stream = &self.streams[0];
let data_per_gpu = input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0;
let size = data_per_gpu as u64 * std::mem::size_of::<u32>() as u64;
stream.check_device_memory(size)?;
Ok(unsafe { self.convert_glwe_ciphertext_unchecked(input) })
}
unsafe fn convert_glwe_ciphertext_unchecked(
&mut self,
input: &GlweCiphertext32,
) -> CudaGlweCiphertext32 {
// Copy the entire input vector over all GPUs
let data_per_gpu = input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0;
let stream = &self.streams[0];
let mut vec = stream.malloc::<u32>(data_per_gpu as u32);
let input_slice = input.0.as_tensor().as_slice();
stream.copy_to_gpu::<u32>(&mut vec, input_slice);
CudaGlweCiphertext32(CudaGlweCiphertext::<u32> {
d_vec: vec,
glwe_dimension: input.glwe_dimension(),
polynomial_size: input.polynomial_size(),
})
}
}
/// # Description
/// Convert a GLWE ciphertext vector with 32 bits of precision from GPU 0 to CPU.
/// This conversion is not necessary to run the bootstrap on the GPU.
/// It is implemented for testing purposes only.
impl GlweCiphertextConversionEngine<CudaGlweCiphertext32, GlweCiphertext32> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::*;
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let glwe_dimension = GlweDimension(2);
/// let polynomial_size = PolynomialSize(4);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = vec![3_u32 << 20; polynomial_size.0];
/// let noise = Variance(2_f64.powf(-50.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let h_plaintext_vector: PlaintextVector32 =
/// default_engine.create_plaintext_vector_from(&input)?;
/// let mut h_ciphertext: GlweCiphertext32 = default_engine
/// .trivially_encrypt_glwe_ciphertext(glwe_dimension.to_glwe_size(), &h_plaintext_vector)?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ciphertext: CudaGlweCiphertext32 = cuda_engine.convert_glwe_ciphertext(&h_ciphertext)?;
/// let h_output_ciphertext: GlweCiphertext32 =
/// cuda_engine.convert_glwe_ciphertext(&d_ciphertext)?;
///
/// assert_eq!(d_ciphertext.glwe_dimension(), glwe_dimension);
/// assert_eq!(d_ciphertext.polynomial_size(), polynomial_size);
/// assert_eq!(h_ciphertext, h_output_ciphertext);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_glwe_ciphertext(
&mut self,
input: &CudaGlweCiphertext32,
) -> Result<GlweCiphertext32, GlweCiphertextConversionError<CudaError>> {
Ok(unsafe { self.convert_glwe_ciphertext_unchecked(input) })
}
unsafe fn convert_glwe_ciphertext_unchecked(
&mut self,
input: &CudaGlweCiphertext32,
) -> GlweCiphertext32 {
// Copy the data from GPU 0 back to the CPU
let mut output =
vec![0u32; input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0];
let stream = &self.streams[0];
stream.copy_to_cpu::<u32>(&mut output, &input.0.d_vec);
GlweCiphertext32(GlweCiphertext::from_container(
output,
input.polynomial_size(),
))
}
}
/// # Description
/// Convert a GLWE ciphertext with 64 bits of precision from CPU to GPU 0.
/// Only this conversion is necessary to run the bootstrap on the GPU.
impl GlweCiphertextConversionEngine<GlweCiphertext64, CudaGlweCiphertext64> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::*;
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let glwe_dimension = GlweDimension(2);
/// let polynomial_size = PolynomialSize(4);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = vec![3_u64 << 20; polynomial_size.0];
/// let noise = Variance(2_f64.powf(-50.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let h_plaintext_vector: PlaintextVector64 =
/// default_engine.create_plaintext_vector_from(&input)?;
/// let mut h_ciphertext: GlweCiphertext64 = default_engine
/// .trivially_encrypt_glwe_ciphertext(glwe_dimension.to_glwe_size(), &h_plaintext_vector)?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ciphertext: CudaGlweCiphertext64 = cuda_engine.convert_glwe_ciphertext(&h_ciphertext)?;
///
/// assert_eq!(d_ciphertext.glwe_dimension(), glwe_dimension);
/// assert_eq!(d_ciphertext.polynomial_size(), polynomial_size);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_glwe_ciphertext(
&mut self,
input: &GlweCiphertext64,
) -> Result<CudaGlweCiphertext64, GlweCiphertextConversionError<CudaError>> {
let stream = &self.streams[0];
let data_per_gpu = input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0;
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
stream.check_device_memory(size)?;
Ok(unsafe { self.convert_glwe_ciphertext_unchecked(input) })
}
unsafe fn convert_glwe_ciphertext_unchecked(
&mut self,
input: &GlweCiphertext64,
) -> CudaGlweCiphertext64 {
// Copy the entire input vector over all GPUs
let data_per_gpu = input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0;
let stream = &self.streams[0];
let mut vec = stream.malloc::<u64>(data_per_gpu as u32);
let input_slice = input.0.as_tensor().as_slice();
stream.copy_to_gpu::<u64>(&mut vec, input_slice);
CudaGlweCiphertext64(CudaGlweCiphertext::<u64> {
d_vec: vec,
glwe_dimension: input.glwe_dimension(),
polynomial_size: input.polynomial_size(),
})
}
}
/// # Description
/// Convert a GLWE ciphertext vector with 64 bits of precision from GPU 0 to CPU.
/// This conversion is not necessary to run the bootstrap on the GPU.
/// It is implemented for testing purposes only.
impl GlweCiphertextConversionEngine<CudaGlweCiphertext64, GlweCiphertext64> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::*;
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let glwe_dimension = GlweDimension(2);
/// let polynomial_size = PolynomialSize(4);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = vec![3_u64 << 20; polynomial_size.0];
/// let noise = Variance(2_f64.powf(-50.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let h_plaintext_vector: PlaintextVector64 =
/// default_engine.create_plaintext_vector_from(&input)?;
/// let mut h_ciphertext: GlweCiphertext64 = default_engine
/// .trivially_encrypt_glwe_ciphertext(glwe_dimension.to_glwe_size(), &h_plaintext_vector)?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ciphertext: CudaGlweCiphertext64 = cuda_engine.convert_glwe_ciphertext(&h_ciphertext)?;
/// let h_output_ciphertext: GlweCiphertext64 =
/// cuda_engine.convert_glwe_ciphertext(&d_ciphertext)?;
///
/// assert_eq!(d_ciphertext.glwe_dimension(), glwe_dimension);
/// assert_eq!(d_ciphertext.polynomial_size(), polynomial_size);
/// assert_eq!(h_ciphertext, h_output_ciphertext);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_glwe_ciphertext(
&mut self,
input: &CudaGlweCiphertext64,
) -> Result<GlweCiphertext64, GlweCiphertextConversionError<CudaError>> {
Ok(unsafe { self.convert_glwe_ciphertext_unchecked(input) })
}
unsafe fn convert_glwe_ciphertext_unchecked(
&mut self,
input: &CudaGlweCiphertext64,
) -> GlweCiphertext64 {
// Copy the data from GPU 0 back to the CPU
let mut output =
vec![0u64; input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0];
let stream = &self.streams[0];
stream.copy_to_cpu::<u64>(&mut output, &input.0.d_vec);
GlweCiphertext64(GlweCiphertext::from_container(
output,
input.polynomial_size(),
))
}
}
/// # Description
/// Convert a view of a GLWE ciphertext with 64 bits of precision from CPU to GPU 0.
impl GlweCiphertextConversionEngine<GlweCiphertextView64<'_>, CudaGlweCiphertext64> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::*;
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let glwe_dimension = GlweDimension(2);
/// let polynomial_size = PolynomialSize(4);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = vec![3_u64 << 20; polynomial_size.0];
/// let noise = Variance(2_f64.powf(-50.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let h_plaintext_vector: PlaintextVector64 =
/// default_engine.create_plaintext_vector_from(&input)?;
/// let mut h_ciphertext: GlweCiphertext64 = default_engine
/// .trivially_encrypt_glwe_ciphertext(glwe_dimension.to_glwe_size(), &h_plaintext_vector)?;
/// let h_raw_ciphertext: Vec<u64> =
/// default_engine.consume_retrieve_glwe_ciphertext(h_ciphertext)?;
/// let mut h_view_ciphertext: GlweCiphertextView64 =
/// default_engine.create_glwe_ciphertext_from(h_raw_ciphertext.as_slice(), polynomial_size)?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ciphertext: CudaGlweCiphertext64 =
/// cuda_engine.convert_glwe_ciphertext(&h_view_ciphertext)?;
/// let h_output_ciphertext: GlweCiphertext64 =
/// cuda_engine.convert_glwe_ciphertext(&d_ciphertext)?;
///
/// // Extracts the internal container
/// let h_raw_output_ciphertext: Vec<u64> =
/// default_engine.consume_retrieve_glwe_ciphertext(h_output_ciphertext)?;
///
/// assert_eq!(d_ciphertext.glwe_dimension(), glwe_dimension);
/// assert_eq!(d_ciphertext.polynomial_size(), polynomial_size);
/// assert_eq!(h_raw_ciphertext, h_raw_output_ciphertext);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_glwe_ciphertext(
&mut self,
input: &GlweCiphertextView64,
) -> Result<CudaGlweCiphertext64, GlweCiphertextConversionError<CudaError>> {
let stream = &self.streams[0];
let data_per_gpu = input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0;
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
stream.check_device_memory(size)?;
Ok(unsafe { self.convert_glwe_ciphertext_unchecked(input) })
}
unsafe fn convert_glwe_ciphertext_unchecked(
&mut self,
input: &GlweCiphertextView64,
) -> CudaGlweCiphertext64 {
// Copy the entire input vector over all GPUs
let data_per_gpu = input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0;
let stream = &self.streams[0];
let mut vec = stream.malloc::<u64>(data_per_gpu as u32);
let input_slice = input.0.as_tensor().as_slice();
stream.copy_to_gpu::<u64>(&mut vec, input_slice);
CudaGlweCiphertext64(CudaGlweCiphertext::<u64> {
d_vec: vec,
glwe_dimension: input.glwe_dimension(),
polynomial_size: input.polynomial_size(),
})
}
}

View File

@@ -1,196 +0,0 @@
use crate::core_crypto::backends::cuda::engines::CudaError;
use crate::core_crypto::backends::cuda::implementation::engines::{
check_base_log, check_glwe_dim, CudaEngine,
};
use crate::core_crypto::backends::cuda::implementation::entities::{
CudaFourierLweBootstrapKey32, CudaFourierLweBootstrapKey64,
};
use crate::core_crypto::backends::cuda::private::crypto::bootstrap::{
convert_lwe_bootstrap_key_from_cpu_to_gpu, CudaBootstrapKey,
};
use crate::core_crypto::prelude::{LweBootstrapKey32, LweBootstrapKey64};
use crate::core_crypto::specification::engines::{
LweBootstrapKeyConversionEngine, LweBootstrapKeyConversionError,
};
use crate::core_crypto::specification::entities::LweBootstrapKeyEntity;
use std::marker::PhantomData;
impl From<CudaError> for LweBootstrapKeyConversionError<CudaError> {
fn from(err: CudaError) -> Self {
Self::Engine(err)
}
}
/// # Description
/// Convert an LWE bootstrap key corresponding to 32 bits of precision from the CPU to the GPU.
/// The bootstrap key is copied entirely to all the GPUs and converted from the standard to the
/// Fourier domain.
impl LweBootstrapKeyConversionEngine<LweBootstrapKey32, CudaFourierLweBootstrapKey32>
for CudaEngine
{
/// # Example
/// ```
/// use tfhe::core_crypto::backends::cuda::private::device::GpuIndex;
/// use tfhe::core_crypto::prelude::{
/// DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
/// Variance, *,
/// };
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let (lwe_dim, glwe_dim, poly_size) = (LweDimension(4), GlweDimension(1), PolynomialSize(512));
/// let (dec_lc, dec_bl) = (DecompositionLevelCount(3), DecompositionBaseLog(5));
/// let noise = Variance(2_f64.powf(-25.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let lwe_sk: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dim)?;
/// let glwe_sk: GlweSecretKey32 =
/// default_engine.generate_new_glwe_secret_key(glwe_dim, poly_size)?;
/// let bsk: LweBootstrapKey32 =
/// default_engine.generate_new_lwe_bootstrap_key(&lwe_sk, &glwe_sk, dec_bl, dec_lc, noise)?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_fourier_bsk: CudaFourierLweBootstrapKey32 =
/// cuda_engine.convert_lwe_bootstrap_key(&bsk)?;
///
/// assert_eq!(d_fourier_bsk.glwe_dimension(), glwe_dim);
/// assert_eq!(d_fourier_bsk.polynomial_size(), poly_size);
/// assert_eq!(d_fourier_bsk.input_lwe_dimension(), lwe_dim);
/// assert_eq!(d_fourier_bsk.decomposition_base_log(), dec_bl);
/// assert_eq!(d_fourier_bsk.decomposition_level_count(), dec_lc);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_lwe_bootstrap_key(
&mut self,
input: &LweBootstrapKey32,
) -> Result<CudaFourierLweBootstrapKey32, LweBootstrapKeyConversionError<CudaError>> {
let poly_size = input.0.polynomial_size();
check_poly_size!(poly_size);
let glwe_dim = input.0.glwe_size().to_glwe_dimension();
check_glwe_dim!(glwe_dim);
let base_log = input.0.base_log();
check_base_log!(base_log);
let data_per_gpu = input.glwe_dimension().to_glwe_size().0
* input.glwe_dimension().to_glwe_size().0
* input.input_lwe_dimension().0
* input.decomposition_level_count().0
* input.polynomial_size().0;
let size = data_per_gpu as u64 * std::mem::size_of::<u32>() as u64;
for stream in self.streams.iter() {
stream.check_device_memory(size)?;
}
Ok(unsafe { self.convert_lwe_bootstrap_key_unchecked(input) })
}
unsafe fn convert_lwe_bootstrap_key_unchecked(
&mut self,
input: &LweBootstrapKey32,
) -> CudaFourierLweBootstrapKey32 {
let vecs = convert_lwe_bootstrap_key_from_cpu_to_gpu::<u32, _>(
self.get_cuda_streams(),
&input.0,
self.get_number_of_gpus(),
);
CudaFourierLweBootstrapKey32(CudaBootstrapKey::<u32> {
d_vecs: vecs,
polynomial_size: input.polynomial_size(),
input_lwe_dimension: input.input_lwe_dimension(),
glwe_dimension: input.glwe_dimension(),
decomp_level: input.decomposition_level_count(),
decomp_base_log: input.decomposition_base_log(),
_phantom: PhantomData::default(),
})
}
}
/// # Description
/// Convert an LWE bootstrap key corresponding to 64 bits of precision from the CPU to the GPU.
/// The bootstrap key is copied entirely to all the GPUs and converted from the standard to the
/// Fourier domain.
impl LweBootstrapKeyConversionEngine<LweBootstrapKey64, CudaFourierLweBootstrapKey64>
for CudaEngine
{
/// # Example
/// ```
/// use tfhe::core_crypto::backends::cuda::private::device::GpuIndex;
/// use tfhe::core_crypto::prelude::{
/// DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
/// Variance, *,
/// };
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let (lwe_dim, glwe_dim, poly_size) = (LweDimension(4), GlweDimension(1), PolynomialSize(512));
/// let (dec_lc, dec_bl) = (DecompositionLevelCount(3), DecompositionBaseLog(5));
/// let noise = Variance(2_f64.powf(-25.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let lwe_sk: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dim)?;
/// let glwe_sk: GlweSecretKey64 =
/// default_engine.generate_new_glwe_secret_key(glwe_dim, poly_size)?;
/// let bsk: LweBootstrapKey64 =
/// default_engine.generate_new_lwe_bootstrap_key(&lwe_sk, &glwe_sk, dec_bl, dec_lc, noise)?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_fourier_bsk: CudaFourierLweBootstrapKey64 =
/// cuda_engine.convert_lwe_bootstrap_key(&bsk)?;
///
/// assert_eq!(d_fourier_bsk.glwe_dimension(), glwe_dim);
/// assert_eq!(d_fourier_bsk.polynomial_size(), poly_size);
/// assert_eq!(d_fourier_bsk.input_lwe_dimension(), lwe_dim);
/// assert_eq!(d_fourier_bsk.decomposition_base_log(), dec_bl);
/// assert_eq!(d_fourier_bsk.decomposition_level_count(), dec_lc);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_lwe_bootstrap_key(
&mut self,
input: &LweBootstrapKey64,
) -> Result<CudaFourierLweBootstrapKey64, LweBootstrapKeyConversionError<CudaError>> {
let poly_size = input.0.polynomial_size();
check_poly_size!(poly_size);
let glwe_dim = input.0.glwe_size().to_glwe_dimension();
check_glwe_dim!(glwe_dim);
let data_per_gpu = input.glwe_dimension().to_glwe_size().0
* input.glwe_dimension().to_glwe_size().0
* input.input_lwe_dimension().0
* input.decomposition_level_count().0
* input.polynomial_size().0;
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
for stream in self.streams.iter() {
stream.check_device_memory(size)?;
}
Ok(unsafe { self.convert_lwe_bootstrap_key_unchecked(input) })
}
unsafe fn convert_lwe_bootstrap_key_unchecked(
&mut self,
input: &LweBootstrapKey64,
) -> CudaFourierLweBootstrapKey64 {
let vecs = convert_lwe_bootstrap_key_from_cpu_to_gpu::<u64, _>(
self.get_cuda_streams(),
&input.0,
self.get_number_of_gpus(),
);
CudaFourierLweBootstrapKey64(CudaBootstrapKey::<u64> {
d_vecs: vecs,
polynomial_size: input.polynomial_size(),
input_lwe_dimension: input.input_lwe_dimension(),
glwe_dimension: input.glwe_dimension(),
decomp_level: input.decomposition_level_count(),
decomp_base_log: input.decomposition_base_log(),
_phantom: PhantomData::default(),
})
}
}

View File

@@ -1,301 +0,0 @@
use crate::core_crypto::backends::cuda::implementation::engines::{CudaEngine, CudaError};
use crate::core_crypto::backends::cuda::implementation::entities::{
CudaLweCiphertext32, CudaLweCiphertext64,
};
use crate::core_crypto::backends::cuda::private::crypto::lwe::ciphertext::CudaLweCiphertext;
use crate::core_crypto::commons::crypto::lwe::LweCiphertext;
use crate::core_crypto::commons::math::tensor::{AsRefSlice, AsRefTensor};
use crate::core_crypto::prelude::{LweCiphertext32, LweCiphertext64, LweCiphertextView64};
use crate::core_crypto::specification::engines::{
LweCiphertextConversionEngine, LweCiphertextConversionError,
};
use crate::core_crypto::specification::entities::LweCiphertextEntity;
impl From<CudaError> for LweCiphertextConversionError<CudaError> {
fn from(err: CudaError) -> Self {
Self::Engine(err)
}
}
/// # Description
/// Convert an LWE ciphertext with 32 bits of precision from CPU to GPU 0.
impl LweCiphertextConversionEngine<LweCiphertext32, CudaLweCiphertext32> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::{LweCiphertextCount, LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(6);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = 3_u32 << 20;
/// let noise = Variance(2_f64.powf(-50.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let h_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let h_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
/// let mut h_ciphertext: LweCiphertext32 =
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ciphertext: CudaLweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
///
/// assert_eq!(d_ciphertext.lwe_dimension(), lwe_dimension);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_lwe_ciphertext(
&mut self,
input: &LweCiphertext32,
) -> Result<CudaLweCiphertext32, LweCiphertextConversionError<CudaError>> {
let stream = self.streams.first().unwrap();
let data_per_gpu = input.lwe_dimension().to_lwe_size().0;
let size = data_per_gpu as u64 * std::mem::size_of::<u32>() as u64;
stream.check_device_memory(size)?;
Ok(unsafe { self.convert_lwe_ciphertext_unchecked(input) })
}
unsafe fn convert_lwe_ciphertext_unchecked(
&mut self,
input: &LweCiphertext32,
) -> CudaLweCiphertext32 {
let alloc_size = input.lwe_dimension().to_lwe_size().0 as u32;
let input_slice = input.0.as_tensor().as_slice();
let stream = self.streams.first().unwrap();
let mut vec = stream.malloc::<u32>(alloc_size);
stream.copy_to_gpu::<u32>(&mut vec, input_slice);
CudaLweCiphertext32(CudaLweCiphertext::<u32> {
d_vec: vec,
lwe_dimension: input.lwe_dimension(),
})
}
}
/// # Description
/// Convert an LWE ciphertext with 32 bits of precision from GPU 0 to CPU.
impl LweCiphertextConversionEngine<CudaLweCiphertext32, LweCiphertext32> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::{LweCiphertextCount, LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(6);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = 3_u32 << 20;
/// let noise = Variance(2_f64.powf(-50.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let h_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let h_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
/// let mut h_ciphertext: LweCiphertext32 =
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ciphertext: CudaLweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
///
/// let h_ciphertext_output: LweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&d_ciphertext)?;
/// assert_eq!(h_ciphertext_output.lwe_dimension(), lwe_dimension);
/// assert_eq!(h_ciphertext, h_ciphertext_output);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_lwe_ciphertext(
&mut self,
input: &CudaLweCiphertext32,
) -> Result<LweCiphertext32, LweCiphertextConversionError<CudaError>> {
Ok(unsafe { self.convert_lwe_ciphertext_unchecked(input) })
}
unsafe fn convert_lwe_ciphertext_unchecked(
&mut self,
input: &CudaLweCiphertext32,
) -> LweCiphertext32 {
let mut output = vec![0_u32; input.lwe_dimension().to_lwe_size().0];
let stream = self.streams.first().unwrap();
stream.copy_to_cpu::<u32>(&mut output, &input.0.d_vec);
LweCiphertext32(LweCiphertext::from_container(output))
}
}
/// # Description
/// Convert an LWE ciphertext with 64 bits of precision from CPU to GPU 0.
impl LweCiphertextConversionEngine<LweCiphertext64, CudaLweCiphertext64> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::{LweCiphertextCount, LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(6);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = 3_u64 << 20;
/// let noise = Variance(2_f64.powf(-50.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let h_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let h_plaintext: Plaintext64 = default_engine.create_plaintext_from(&input)?;
/// let mut h_ciphertext: LweCiphertext64 =
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ciphertext: CudaLweCiphertext64 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
///
/// assert_eq!(d_ciphertext.lwe_dimension(), lwe_dimension);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_lwe_ciphertext(
&mut self,
input: &LweCiphertext64,
) -> Result<CudaLweCiphertext64, LweCiphertextConversionError<CudaError>> {
let stream = self.streams.first().unwrap();
let data_per_gpu = input.lwe_dimension().to_lwe_size().0;
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
stream.check_device_memory(size)?;
Ok(unsafe { self.convert_lwe_ciphertext_unchecked(input) })
}
unsafe fn convert_lwe_ciphertext_unchecked(
&mut self,
input: &LweCiphertext64,
) -> CudaLweCiphertext64 {
let alloc_size = input.lwe_dimension().to_lwe_size().0 as u32;
let input_slice = input.0.as_tensor().as_slice();
let stream = self.streams.first().unwrap();
let mut vec = stream.malloc::<u64>(alloc_size);
stream.copy_to_gpu::<u64>(&mut vec, input_slice);
CudaLweCiphertext64(CudaLweCiphertext::<u64> {
d_vec: vec,
lwe_dimension: input.lwe_dimension(),
})
}
}
/// # Description
/// Convert an LWE ciphertext with 64 bits of precision from GPU 0 to CPU.
impl LweCiphertextConversionEngine<CudaLweCiphertext64, LweCiphertext64> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::{LweCiphertextCount, LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(6);
/// // Here a hard-set encoding is applied (shift by 50 bits)
/// let input = 3_u64 << 50;
/// let noise = Variance(2_f64.powf(-50.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let h_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let h_plaintext: Plaintext64 = default_engine.create_plaintext_from(&input)?;
/// let mut h_ciphertext: LweCiphertext64 =
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ciphertext: CudaLweCiphertext64 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
///
/// let h_ciphertext_output: LweCiphertext64 = cuda_engine.convert_lwe_ciphertext(&d_ciphertext)?;
/// assert_eq!(h_ciphertext_output.lwe_dimension(), lwe_dimension);
/// assert_eq!(h_ciphertext, h_ciphertext_output);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_lwe_ciphertext(
&mut self,
input: &CudaLweCiphertext64,
) -> Result<LweCiphertext64, LweCiphertextConversionError<CudaError>> {
Ok(unsafe { self.convert_lwe_ciphertext_unchecked(input) })
}
unsafe fn convert_lwe_ciphertext_unchecked(
&mut self,
input: &CudaLweCiphertext64,
) -> LweCiphertext64 {
let mut output = vec![0_u64; input.lwe_dimension().to_lwe_size().0];
let stream = self.streams.first().unwrap();
stream.copy_to_cpu::<u64>(&mut output, &input.0.d_vec);
LweCiphertext64(LweCiphertext::from_container(output))
}
}
/// # Description
/// Convert a view of an LWE ciphertext with 64 bits of precision from CPU to GPU 0.
impl LweCiphertextConversionEngine<LweCiphertextView64<'_>, CudaLweCiphertext64> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::{LweCiphertextCount, LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(6);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = 3_u64 << 20;
/// let noise = Variance(2_f64.powf(-50.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let h_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let h_plaintext: Plaintext64 = default_engine.create_plaintext_from(&input)?;
/// let mut h_ciphertext: LweCiphertext64 =
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
///
/// // Creates a LweCiphertextView64 object from LweCiphertext64
/// let h_raw_ciphertext: Vec<u64> =
/// default_engine.consume_retrieve_lwe_ciphertext(h_ciphertext)?;
/// let mut h_view_ciphertext: LweCiphertextView64 =
/// default_engine.create_lwe_ciphertext_from(h_raw_ciphertext.as_slice())?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ciphertext: CudaLweCiphertext64 =
/// cuda_engine.convert_lwe_ciphertext(&h_view_ciphertext)?;
///
/// assert_eq!(d_ciphertext.lwe_dimension(), lwe_dimension);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_lwe_ciphertext(
&mut self,
input: &LweCiphertextView64,
) -> Result<CudaLweCiphertext64, LweCiphertextConversionError<CudaError>> {
let stream = &self.streams[0];
let data_per_gpu = input.lwe_dimension().to_lwe_size().0;
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
stream.check_device_memory(size)?;
Ok(unsafe { self.convert_lwe_ciphertext_unchecked(input) })
}
unsafe fn convert_lwe_ciphertext_unchecked(
&mut self,
input: &LweCiphertextView64,
) -> CudaLweCiphertext64 {
let alloc_size = input.lwe_dimension().to_lwe_size().0 as u32;
let input_slice = input.0.as_tensor().as_slice();
let stream = &self.streams[0];
let mut d_vec = stream.malloc::<u64>(alloc_size);
stream.copy_to_gpu::<u64>(&mut d_vec, input_slice);
CudaLweCiphertext64(CudaLweCiphertext::<u64> {
d_vec,
lwe_dimension: input.lwe_dimension(),
})
}
}

View File

@@ -1,298 +0,0 @@
use crate::core_crypto::backends::cuda::engines::CudaError;
use crate::core_crypto::backends::cuda::implementation::engines::{
check_base_log, check_glwe_dim, CudaEngine,
};
use crate::core_crypto::backends::cuda::implementation::entities::{
CudaFourierLweBootstrapKey32, CudaFourierLweBootstrapKey64, CudaGlweCiphertext32,
CudaGlweCiphertext64, CudaLweCiphertext32, CudaLweCiphertext64,
};
use crate::core_crypto::backends::cuda::private::device::NumberOfSamples;
use crate::core_crypto::prelude::LweCiphertextIndex;
use crate::core_crypto::specification::engines::{
LweCiphertextDiscardingBootstrapEngine, LweCiphertextDiscardingBootstrapError,
};
use crate::core_crypto::specification::entities::LweBootstrapKeyEntity;
impl From<CudaError> for LweCiphertextDiscardingBootstrapError<CudaError> {
fn from(err: CudaError) -> Self {
Self::Engine(err)
}
}
/// # Description
/// A discard bootstrap on an input ciphertext with 32 bits of precision.
/// The input bootstrap key is in the Fourier domain.
impl
LweCiphertextDiscardingBootstrapEngine<
CudaFourierLweBootstrapKey32,
CudaGlweCiphertext32,
CudaLweCiphertext32,
CudaLweCiphertext32,
> for CudaEngine
{
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::{LweCiphertextCount, LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use tfhe::core_crypto::prelude::{
/// DecompositionBaseLog, DecompositionLevelCount, GlweDimension, PolynomialSize,
/// };
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let (lwe_dim, lwe_dim_output, glwe_dim, poly_size) = (
/// LweDimension(130),
/// LweDimension(512),
/// GlweDimension(1),
/// PolynomialSize(512),
/// );
/// let log_degree = f64::log2(poly_size.0 as f64) as i32;
/// let val: u32 = ((poly_size.0 as f64 - (10. * f64::sqrt((lwe_dim.0 as f64) / 16.0)))
/// * 2_f64.powi(32 - log_degree - 1)) as u32;
/// let input = val;
/// let noise = Variance(2_f64.powf(-29.));
/// let (dec_lc, dec_bl) = (DecompositionLevelCount(3), DecompositionBaseLog(7));
/// // An identity function is applied during the bootstrap
/// let mut lut = vec![0u32; poly_size.0];
/// for i in 0..poly_size.0 {
/// let l = (i as f64 * 2_f64.powi(32 - log_degree - 1)) as u32;
/// lut[i] = l;
/// }
///
/// // 1. default engine
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// // create a vector of LWE ciphertexts
/// let h_input_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dim)?;
/// let h_input_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
/// let mut h_input_ciphertext: LweCiphertext32 =
/// default_engine.encrypt_lwe_ciphertext(&h_input_key, &h_input_plaintext, noise)?;
/// // create a GLWE ciphertext containing an encryption of the LUT
/// let h_lut_plaintext_vector = default_engine.create_plaintext_vector_from(&lut)?;
/// let h_lut_key: GlweSecretKey32 =
/// default_engine.generate_new_glwe_secret_key(glwe_dim, poly_size)?;
/// let mut h_lut: GlweCiphertext32 = default_engine
/// .trivially_encrypt_glwe_ciphertext(glwe_dim.to_glwe_size(), &h_lut_plaintext_vector)?;
/// // create a BSK
/// let h_bootstrap_key: LweBootstrapKey32 = default_engine.generate_new_lwe_bootstrap_key(
/// &h_input_key,
/// &h_lut_key,
/// dec_bl,
/// dec_lc,
/// noise,
/// )?;
/// // initialize an output LWE ciphertext vector
/// let h_dummy_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dim_output)?;
///
/// // 2. cuda engine
/// let mut cuda_engine = CudaEngine::new(())?;
/// // convert input to GPU 0
/// let d_input_ciphertext: CudaLweCiphertext32 =
/// cuda_engine.convert_lwe_ciphertext(&h_input_ciphertext)?;
/// // convert accumulator to GPU
/// let d_input_lut: CudaGlweCiphertext32 = cuda_engine.convert_glwe_ciphertext(&h_lut)?;
/// // convert BSK to GPU (and from Standard to Fourier representations)
/// let d_fourier_bsk: CudaFourierLweBootstrapKey32 =
/// cuda_engine.convert_lwe_bootstrap_key(&h_bootstrap_key)?;
/// // launch bootstrap on GPU
/// let h_zero_output_ciphertext: LweCiphertext32 =
/// default_engine.zero_encrypt_lwe_ciphertext(&h_dummy_key, noise)?;
/// let mut d_output_ciphertext: CudaLweCiphertext32 =
/// cuda_engine.convert_lwe_ciphertext(&h_zero_output_ciphertext)?;
/// cuda_engine.discard_bootstrap_lwe_ciphertext(
/// &mut d_output_ciphertext,
/// &d_input_ciphertext,
/// &d_input_lut,
/// &d_fourier_bsk,
/// )?;
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_bootstrap_lwe_ciphertext(
&mut self,
output: &mut CudaLweCiphertext32,
input: &CudaLweCiphertext32,
acc: &CudaGlweCiphertext32,
bsk: &CudaFourierLweBootstrapKey32,
) -> Result<(), LweCiphertextDiscardingBootstrapError<CudaError>> {
LweCiphertextDiscardingBootstrapError::perform_generic_checks(output, input, acc, bsk)?;
let poly_size = bsk.polynomial_size();
check_poly_size!(poly_size);
let glwe_dim = bsk.glwe_dimension();
check_glwe_dim!(glwe_dim);
let base_log = bsk.decomposition_base_log();
check_base_log!(base_log);
unsafe { self.discard_bootstrap_lwe_ciphertext_unchecked(output, input, acc, bsk) };
Ok(())
}
unsafe fn discard_bootstrap_lwe_ciphertext_unchecked(
&mut self,
output: &mut CudaLweCiphertext32,
input: &CudaLweCiphertext32,
acc: &CudaGlweCiphertext32,
bsk: &CudaFourierLweBootstrapKey32,
) {
let stream = self.streams.first().unwrap();
let mut test_vector_indexes = stream.malloc::<u32>(1);
stream.copy_to_gpu(&mut test_vector_indexes, &[0]);
stream.discard_bootstrap_low_latency_lwe_ciphertext_vector::<u32>(
&mut output.0.d_vec,
&acc.0.d_vec,
&test_vector_indexes,
&input.0.d_vec,
bsk.0.d_vecs.first().unwrap(),
input.0.lwe_dimension,
bsk.glwe_dimension(),
bsk.polynomial_size(),
bsk.decomposition_base_log(),
bsk.decomposition_level_count(),
NumberOfSamples(1),
LweCiphertextIndex(0),
self.get_cuda_shared_memory(),
);
}
}
/// # Description
/// A discard bootstrap on an input ciphertext with 64 bits of precision.
/// The input bootstrap key is in the Fourier domain.
impl
LweCiphertextDiscardingBootstrapEngine<
CudaFourierLweBootstrapKey64,
CudaGlweCiphertext64,
CudaLweCiphertext64,
CudaLweCiphertext64,
> for CudaEngine
{
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::{LweCiphertextCount, LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use tfhe::core_crypto::prelude::{
/// DecompositionBaseLog, DecompositionLevelCount, GlweDimension, PolynomialSize,
/// };
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let (lwe_dim, lwe_dim_output, glwe_dim, poly_size) = (
/// LweDimension(130),
/// LweDimension(512),
/// GlweDimension(1),
/// PolynomialSize(512),
/// );
/// let log_degree = f64::log2(poly_size.0 as f64) as i64;
/// let val: u64 = ((poly_size.0 as f64 - (10. * f64::sqrt((lwe_dim.0 as f64) / 16.0)))
/// * 2_f64.powi((64 - log_degree - 1) as i32)) as u64;
/// let input = val;
/// let noise = Variance(2_f64.powf(-29.));
/// let (dec_lc, dec_bl) = (DecompositionLevelCount(3), DecompositionBaseLog(7));
/// // An identity function is applied during the bootstrap
/// let mut lut = vec![0u64; poly_size.0];
/// for i in 0..poly_size.0 {
/// let l = (i as f64 * 2_f64.powi((64 - log_degree - 1) as i32)) as u64;
/// lut[i] = l;
/// }
///
/// // 1. default engine
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// // create a vector of LWE ciphertexts
/// let h_input_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dim)?;
/// let h_input_plaintext: Plaintext64 = default_engine.create_plaintext_from(&input)?;
/// let mut h_input_ciphertext: LweCiphertext64 =
/// default_engine.encrypt_lwe_ciphertext(&h_input_key, &h_input_plaintext, noise)?;
/// // create a GLWE ciphertext containing an encryption of the LUT
/// let h_lut_plaintext_vector = default_engine.create_plaintext_vector_from(&lut)?;
/// let h_lut_key: GlweSecretKey64 =
/// default_engine.generate_new_glwe_secret_key(glwe_dim, poly_size)?;
/// let mut h_lut: GlweCiphertext64 = default_engine
/// .trivially_encrypt_glwe_ciphertext(glwe_dim.to_glwe_size(), &h_lut_plaintext_vector)?;
/// // create a BSK
/// let h_bootstrap_key: LweBootstrapKey64 = default_engine.generate_new_lwe_bootstrap_key(
/// &h_input_key,
/// &h_lut_key,
/// dec_bl,
/// dec_lc,
/// noise,
/// )?;
/// // initialize an output LWE ciphertext vector
/// let h_dummy_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dim_output)?;
///
/// // 2. cuda engine
/// let mut cuda_engine = CudaEngine::new(())?;
/// // convert input to GPU 0
/// let d_input_ciphertext: CudaLweCiphertext64 =
/// cuda_engine.convert_lwe_ciphertext(&h_input_ciphertext)?;
/// // convert accumulator to GPU
/// let d_input_lut: CudaGlweCiphertext64 = cuda_engine.convert_glwe_ciphertext(&h_lut)?;
/// // convert BSK to GPU (and from Standard to Fourier representations)
/// let d_fourier_bsk: CudaFourierLweBootstrapKey64 =
/// cuda_engine.convert_lwe_bootstrap_key(&h_bootstrap_key)?;
/// // launch bootstrap on GPU
/// let h_zero_output_ciphertext: LweCiphertext64 =
/// default_engine.zero_encrypt_lwe_ciphertext(&h_dummy_key, noise)?;
/// let mut d_output_ciphertext: CudaLweCiphertext64 =
/// cuda_engine.convert_lwe_ciphertext(&h_zero_output_ciphertext)?;
/// cuda_engine.discard_bootstrap_lwe_ciphertext(
/// &mut d_output_ciphertext,
/// &d_input_ciphertext,
/// &d_input_lut,
/// &d_fourier_bsk,
/// )?;
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_bootstrap_lwe_ciphertext(
&mut self,
output: &mut CudaLweCiphertext64,
input: &CudaLweCiphertext64,
acc: &CudaGlweCiphertext64,
bsk: &CudaFourierLweBootstrapKey64,
) -> Result<(), LweCiphertextDiscardingBootstrapError<CudaError>> {
LweCiphertextDiscardingBootstrapError::perform_generic_checks(output, input, acc, bsk)?;
let poly_size = bsk.polynomial_size();
check_poly_size!(poly_size);
let glwe_dim = bsk.glwe_dimension();
check_glwe_dim!(glwe_dim);
let base_log = bsk.decomposition_base_log();
check_base_log!(base_log);
unsafe { self.discard_bootstrap_lwe_ciphertext_unchecked(output, input, acc, bsk) };
Ok(())
}
unsafe fn discard_bootstrap_lwe_ciphertext_unchecked(
&mut self,
output: &mut CudaLweCiphertext64,
input: &CudaLweCiphertext64,
acc: &CudaGlweCiphertext64,
bsk: &CudaFourierLweBootstrapKey64,
) {
let stream = self.streams.first().unwrap();
let mut test_vector_indexes = stream.malloc::<u32>(1);
stream.copy_to_gpu(&mut test_vector_indexes, &[0]);
stream.discard_bootstrap_low_latency_lwe_ciphertext_vector::<u64>(
&mut output.0.d_vec,
&acc.0.d_vec,
&test_vector_indexes,
&input.0.d_vec,
bsk.0.d_vecs.first().unwrap(),
input.0.lwe_dimension,
bsk.glwe_dimension(),
bsk.polynomial_size(),
bsk.decomposition_base_log(),
bsk.decomposition_level_count(),
NumberOfSamples(1),
LweCiphertextIndex(0),
self.get_cuda_shared_memory(),
);
}
}

View File

@@ -1,278 +0,0 @@
use crate::core_crypto::backends::cuda::implementation::engines::{CudaEngine, CudaError};
use crate::core_crypto::backends::cuda::implementation::entities::{
CudaLweCiphertext32, CudaLweCiphertext64,
};
use crate::core_crypto::commons::math::tensor::{AsMutSlice, AsRefSlice};
use crate::core_crypto::prelude::{
LweCiphertext32, LweCiphertextMutView32, LweCiphertextMutView64,
};
use crate::core_crypto::specification::engines::{
LweCiphertextDiscardingConversionEngine, LweCiphertextDiscardingConversionError,
};
/// # Description
///
/// Convert an LWE ciphertext with 32 bits of precision from GPU 0 to a view on the CPU.
impl LweCiphertextDiscardingConversionEngine<CudaLweCiphertext32, LweCiphertextMutView32<'_>>
for CudaEngine
{
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::{LweCiphertextCount, LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// use std::borrow::BorrowMut;
/// let lwe_dimension = LweDimension(6);
/// // Here a hard-set encoding is applied (shift by 25 bits)
/// let input = 3_u32 << 25;
/// let noise = Variance(2_f64.powf(-50.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let h_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let h_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
/// let mut h_ciphertext: LweCiphertext32 =
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ciphertext: CudaLweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
///
/// // Prepares the output container
/// let mut h_raw_output_ciphertext = vec![0_u32; lwe_dimension.0 + 1];
/// let mut h_output_view_ciphertext: LweCiphertextMutView32 =
/// default_engine.create_lwe_ciphertext_from(h_raw_output_ciphertext.as_mut_slice())?;
///
/// cuda_engine.discard_convert_lwe_ciphertext(&mut h_output_view_ciphertext, &d_ciphertext)?;
///
/// assert_eq!(h_output_view_ciphertext.lwe_dimension(), lwe_dimension);
/// // Extracts the internal container
/// let h_raw_input_ciphertext: Vec<u32> =
/// default_engine.consume_retrieve_lwe_ciphertext(h_ciphertext)?;
/// let h_raw_output_ciphertext: &[u32] =
/// default_engine.consume_retrieve_lwe_ciphertext(h_output_view_ciphertext)?;
/// assert_eq!(h_raw_input_ciphertext.as_slice(), h_raw_output_ciphertext);
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_convert_lwe_ciphertext(
&mut self,
output: &mut LweCiphertextMutView32,
input: &CudaLweCiphertext32,
) -> Result<(), LweCiphertextDiscardingConversionError<CudaError>> {
unsafe { self.discard_convert_lwe_ciphertext_unchecked(output, input) };
Ok(())
}
unsafe fn discard_convert_lwe_ciphertext_unchecked(
&mut self,
output: &mut LweCiphertextMutView32,
input: &CudaLweCiphertext32,
) {
let stream = &self.streams[0];
stream.copy_to_cpu::<u32>(output.0.tensor.as_mut_slice(), &input.0.d_vec);
}
}
/// # Description
///
/// Convert an LWE ciphertext with 32 bits of precision from GPU 0 to a ciphertext on the CPU.
impl LweCiphertextDiscardingConversionEngine<CudaLweCiphertext32, LweCiphertext32> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::{LweCiphertextCount, LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// use std::borrow::BorrowMut;
/// let lwe_dimension = LweDimension(6);
/// // Here a hard-set encoding is applied (shift by 25 bits)
/// let input = 3_u32 << 25;
/// let noise = Variance(2_f64.powf(-50.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let h_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let h_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
/// let mut h_ciphertext: LweCiphertext32 =
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ciphertext: CudaLweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
///
/// // Prepares the output container
/// let h_raw_output_ciphertext = vec![0_u32; lwe_dimension.0 + 1];
/// let mut h_output_ciphertext: LweCiphertext32 =
/// default_engine.create_lwe_ciphertext_from(h_raw_output_ciphertext)?;
///
/// cuda_engine.discard_convert_lwe_ciphertext(&mut h_output_ciphertext, &d_ciphertext)?;
///
/// assert_eq!(h_output_ciphertext.lwe_dimension(), lwe_dimension);
/// // Extracts the internal container
/// let h_raw_input_ciphertext: Vec<u32> =
/// default_engine.consume_retrieve_lwe_ciphertext(h_ciphertext)?;
/// let h_raw_output_ciphertext: Vec<u32> =
/// default_engine.consume_retrieve_lwe_ciphertext(h_output_ciphertext)?;
/// assert_eq!(h_raw_input_ciphertext, h_raw_output_ciphertext);
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_convert_lwe_ciphertext(
&mut self,
output: &mut LweCiphertext32,
input: &CudaLweCiphertext32,
) -> Result<(), LweCiphertextDiscardingConversionError<CudaError>> {
unsafe { self.discard_convert_lwe_ciphertext_unchecked(output, input) };
Ok(())
}
unsafe fn discard_convert_lwe_ciphertext_unchecked(
&mut self,
output: &mut LweCiphertext32,
input: &CudaLweCiphertext32,
) {
let stream = &self.streams[0];
stream.copy_to_cpu::<u32>(output.0.tensor.as_mut_slice(), &input.0.d_vec);
}
}
/// # Description
///
/// Convert an LWE ciphertext with 32 bits of precision from CPU to a ciphertext on the GPU 0.
impl LweCiphertextDiscardingConversionEngine<LweCiphertext32, CudaLweCiphertext32> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::{LweCiphertextCount, LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// use std::borrow::BorrowMut;
/// let lwe_dimension = LweDimension(6);
/// // Here a hard-set encoding is applied (shift by 25 bits)
/// let input = 3_u32 << 25;
/// let noise = Variance(2_f64.powf(-50.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let h_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let h_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
/// let mut h_ciphertext: LweCiphertext32 =
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let mut d_ciphertext: CudaLweCiphertext32 =
/// cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
///
/// let h_ciphertext_out: LweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&d_ciphertext)?;
///
/// assert_eq!(h_ciphertext, h_ciphertext_out);
///
/// // Prepare input for discarding convert
/// let input_2 = 5_u32 << 25;
/// let h_plaintext_2: Plaintext32 = default_engine.create_plaintext_from(&input_2)?;
/// let mut h_ciphertext_2: LweCiphertext32 = default_engine
/// .trivially_encrypt_lwe_ciphertext(lwe_dimension.to_lwe_size(), &h_plaintext_2)?;
///
/// cuda_engine.discard_convert_lwe_ciphertext(&mut d_ciphertext, &h_ciphertext_2)?;
///
/// let h_ciphertext_out_2: LweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&d_ciphertext)?;
///
/// assert_eq!(h_ciphertext_2, h_ciphertext_out_2);
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_convert_lwe_ciphertext(
&mut self,
output: &mut CudaLweCiphertext32,
input: &LweCiphertext32,
) -> Result<(), LweCiphertextDiscardingConversionError<CudaError>> {
unsafe { self.discard_convert_lwe_ciphertext_unchecked(output, input) };
Ok(())
}
unsafe fn discard_convert_lwe_ciphertext_unchecked(
&mut self,
output: &mut CudaLweCiphertext32,
input: &LweCiphertext32,
) {
let stream = &self.streams[0];
stream.copy_to_gpu::<u32>(&mut output.0.d_vec, input.0.tensor.as_slice());
}
}
/// # Description
///
/// Convert an LWE ciphertext with 64 bits of precision from GPU 0 to a view on the CPU.
impl LweCiphertextDiscardingConversionEngine<CudaLweCiphertext64, LweCiphertextMutView64<'_>>
for CudaEngine
{
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::{LweCiphertextCount, LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// use std::borrow::BorrowMut;
/// let lwe_dimension = LweDimension(6);
/// // Here a hard-set encoding is applied (shift by 25 bits)
/// let input = 3_u64 << 50;
/// let noise = Variance(2_f64.powf(-50.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let h_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let h_plaintext: Plaintext64 = default_engine.create_plaintext_from(&input)?;
/// let mut h_ciphertext: LweCiphertext64 =
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ciphertext: CudaLweCiphertext64 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
///
/// // Prepares the output container
/// let mut h_raw_output_ciphertext = vec![0_u64; lwe_dimension.0 + 1];
/// let mut h_view_output_ciphertext: LweCiphertextMutView64 =
/// default_engine.create_lwe_ciphertext_from(h_raw_output_ciphertext.as_mut_slice())?;
///
/// cuda_engine
/// .discard_convert_lwe_ciphertext(h_view_output_ciphertext.borrow_mut(), &d_ciphertext)?;
///
/// assert_eq!(h_view_output_ciphertext.lwe_dimension(), lwe_dimension);
/// // Extracts the internal container
/// let h_raw_input_ciphertext: Vec<u64> =
/// default_engine.consume_retrieve_lwe_ciphertext(h_ciphertext)?;
/// let h_raw_output_ciphertext: &[u64] =
/// default_engine.consume_retrieve_lwe_ciphertext(h_view_output_ciphertext)?;
/// assert_eq!(h_raw_input_ciphertext, h_raw_output_ciphertext.to_vec());
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_convert_lwe_ciphertext(
&mut self,
output: &mut LweCiphertextMutView64,
input: &CudaLweCiphertext64,
) -> Result<(), LweCiphertextDiscardingConversionError<CudaError>> {
unsafe { self.discard_convert_lwe_ciphertext_unchecked(output, input) };
Ok(())
}
unsafe fn discard_convert_lwe_ciphertext_unchecked(
&mut self,
output: &mut LweCiphertextMutView64,
input: &CudaLweCiphertext64,
) {
let stream = &self.streams[0];
stream.copy_to_cpu::<u64>(output.0.tensor.as_mut_slice(), &input.0.d_vec);
}
}

View File

@@ -1,230 +0,0 @@
use crate::core_crypto::backends::cuda::engines::CudaError;
use crate::core_crypto::backends::cuda::implementation::engines::CudaEngine;
use crate::core_crypto::backends::cuda::implementation::entities::{
CudaLweCiphertext32, CudaLweCiphertext64, CudaLweKeyswitchKey32, CudaLweKeyswitchKey64,
};
use crate::core_crypto::backends::cuda::private::device::NumberOfSamples;
use crate::core_crypto::specification::engines::{
LweCiphertextDiscardingKeyswitchEngine, LweCiphertextDiscardingKeyswitchError,
};
use crate::core_crypto::specification::entities::LweKeyswitchKeyEntity;
impl From<CudaError> for LweCiphertextDiscardingKeyswitchError<CudaError> {
fn from(err: CudaError) -> Self {
Self::Engine(err)
}
}
/// # Description
/// A discard keyswitch on a vector of input ciphertext vectors with 32 bits of precision.
impl
LweCiphertextDiscardingKeyswitchEngine<
CudaLweKeyswitchKey32,
CudaLweCiphertext32,
CudaLweCiphertext32,
> for CudaEngine
{
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::{LweCiphertextCount, LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let input_lwe_dimension = LweDimension(6);
/// let output_lwe_dimension = LweDimension(3);
/// let decomposition_level_count = DecompositionLevelCount(2);
/// let decomposition_base_log = DecompositionBaseLog(8);
///
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = 3_u32 << 20;
/// let noise = Variance(2_f64.powf(-50.));
///
/// // Generate two secret keys
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let input_key: LweSecretKey32 =
/// default_engine.generate_new_lwe_secret_key(input_lwe_dimension)?;
/// let output_key: LweSecretKey32 =
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
///
/// // Generate keyswitch keys to switch between first_key and second_key
/// let h_ksk = default_engine.generate_new_lwe_keyswitch_key(
/// &input_key,
/// &output_key,
/// decomposition_level_count,
/// decomposition_base_log,
/// noise,
/// )?;
///
/// // Encrypt something
/// let h_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
/// let mut h_ciphertext: LweCiphertext32 =
/// default_engine.encrypt_lwe_ciphertext(&input_key, &h_plaintext, noise)?;
///
/// // Copy to the GPU
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ciphertext: CudaLweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
/// let d_ksk: CudaLweKeyswitchKey32 = cuda_engine.convert_lwe_keyswitch_key(&h_ksk)?;
///
/// // launch keyswitch on GPU
/// let h_dummy_key: LweSecretKey32 =
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
/// let h_zero_ciphertext: LweCiphertext32 =
/// default_engine.zero_encrypt_lwe_ciphertext(&h_dummy_key, noise)?;
///
/// let mut d_keyswitched_ciphertext: CudaLweCiphertext32 =
/// cuda_engine.convert_lwe_ciphertext(&h_zero_ciphertext)?;
/// cuda_engine.discard_keyswitch_lwe_ciphertext(
/// &mut d_keyswitched_ciphertext,
/// &d_ciphertext,
/// &d_ksk,
/// )?;
///
/// assert_eq!(
/// d_keyswitched_ciphertext.lwe_dimension(),
/// output_lwe_dimension
/// );
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_keyswitch_lwe_ciphertext(
&mut self,
output: &mut CudaLweCiphertext32,
input: &CudaLweCiphertext32,
ksk: &CudaLweKeyswitchKey32,
) -> Result<(), LweCiphertextDiscardingKeyswitchError<CudaError>> {
LweCiphertextDiscardingKeyswitchError::perform_generic_checks(output, input, ksk)?;
unsafe { self.discard_keyswitch_lwe_ciphertext_unchecked(output, input, ksk) };
Ok(())
}
unsafe fn discard_keyswitch_lwe_ciphertext_unchecked(
&mut self,
output: &mut CudaLweCiphertext32,
input: &CudaLweCiphertext32,
ksk: &CudaLweKeyswitchKey32,
) {
let stream = &self.streams[0];
stream.discard_keyswitch_lwe_ciphertext_vector::<u32>(
&mut output.0.d_vec,
&input.0.d_vec,
input.0.lwe_dimension,
output.0.lwe_dimension,
ksk.0.d_vecs.first().unwrap(),
ksk.decomposition_base_log(),
ksk.decomposition_level_count(),
NumberOfSamples(1),
);
}
}
/// # Description
/// A discard keyswitch on a vector of input ciphertext vectors with 64 bits of precision.
impl
LweCiphertextDiscardingKeyswitchEngine<
CudaLweKeyswitchKey64,
CudaLweCiphertext64,
CudaLweCiphertext64,
> for CudaEngine
{
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::{LweCiphertextCount, LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let input_lwe_dimension = LweDimension(6);
/// let output_lwe_dimension = LweDimension(3);
/// let decomposition_level_count = DecompositionLevelCount(2);
/// let decomposition_base_log = DecompositionBaseLog(8);
///
/// // Here a hard-set encoding is applied (shift by 50 bits)
/// let input = 3_u64 << 50;
/// let noise = Variance(2_f64.powf(-50.));
///
/// // Generate two secret keys
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let input_key: LweSecretKey64 =
/// default_engine.generate_new_lwe_secret_key(input_lwe_dimension)?;
/// let output_key: LweSecretKey64 =
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
///
/// // Generate keyswitch keys to switch between first_key and second_key
/// let h_ksk = default_engine.generate_new_lwe_keyswitch_key(
/// &input_key,
/// &output_key,
/// decomposition_level_count,
/// decomposition_base_log,
/// noise,
/// )?;
///
/// // Encrypt something
/// let h_plaintext: Plaintext64 = default_engine.create_plaintext_from(&input)?;
/// let mut h_ciphertext: LweCiphertext64 =
/// default_engine.encrypt_lwe_ciphertext(&input_key, &h_plaintext, noise)?;
///
/// // Copy to the GPU
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ciphertext: CudaLweCiphertext64 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
/// let d_ksk: CudaLweKeyswitchKey64 = cuda_engine.convert_lwe_keyswitch_key(&h_ksk)?;
///
/// // launch keyswitch on GPU
/// let h_dummy_key: LweSecretKey64 =
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
/// let h_zero_ciphertext: LweCiphertext64 =
/// default_engine.zero_encrypt_lwe_ciphertext(&h_dummy_key, noise)?;
///
/// let mut d_keyswitched_ciphertext: CudaLweCiphertext64 =
/// cuda_engine.convert_lwe_ciphertext(&h_zero_ciphertext)?;
/// cuda_engine.discard_keyswitch_lwe_ciphertext(
/// &mut d_keyswitched_ciphertext,
/// &d_ciphertext,
/// &d_ksk,
/// )?;
///
/// assert_eq!(
/// d_keyswitched_ciphertext.lwe_dimension(),
/// output_lwe_dimension
/// );
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_keyswitch_lwe_ciphertext(
&mut self,
output: &mut CudaLweCiphertext64,
input: &CudaLweCiphertext64,
ksk: &CudaLweKeyswitchKey64,
) -> Result<(), LweCiphertextDiscardingKeyswitchError<CudaError>> {
LweCiphertextDiscardingKeyswitchError::perform_generic_checks(output, input, ksk)?;
unsafe { self.discard_keyswitch_lwe_ciphertext_unchecked(output, input, ksk) };
Ok(())
}
unsafe fn discard_keyswitch_lwe_ciphertext_unchecked(
&mut self,
output: &mut CudaLweCiphertext64,
input: &CudaLweCiphertext64,
ksk: &CudaLweKeyswitchKey64,
) {
let stream = &self.streams[0];
stream.discard_keyswitch_lwe_ciphertext_vector::<u64>(
&mut output.0.d_vec,
&input.0.d_vec,
input.0.lwe_dimension,
output.0.lwe_dimension,
ksk.0.d_vecs.first().unwrap(),
ksk.decomposition_base_log(),
ksk.decomposition_level_count(),
NumberOfSamples(1),
);
}
}

View File

@@ -1,346 +0,0 @@
use crate::core_crypto::backends::cuda::engines::CudaError;
use crate::core_crypto::backends::cuda::implementation::engines::CudaEngine;
use crate::core_crypto::backends::cuda::implementation::entities::{
CudaLweKeyswitchKey32, CudaLweKeyswitchKey64,
};
use crate::core_crypto::backends::cuda::private::crypto::keyswitch::CudaLweKeyswitchKey;
use crate::core_crypto::commons::crypto::lwe::LweKeyswitchKey;
use crate::core_crypto::commons::math::tensor::{AsRefSlice, AsRefTensor};
use crate::core_crypto::prelude::{LweKeyswitchKey32, LweKeyswitchKey64};
use crate::core_crypto::specification::engines::{
LweKeyswitchKeyConversionEngine, LweKeyswitchKeyConversionError,
};
use crate::core_crypto::specification::entities::LweKeyswitchKeyEntity;
impl From<CudaError> for LweKeyswitchKeyConversionError<CudaError> {
fn from(err: CudaError) -> Self {
Self::Engine(err)
}
}
/// # Description
/// Convert an LWE keyswitch key corresponding to 32 bits of precision from the CPU to the GPU.
/// We only support the conversion from CPU to GPU: the conversion from GPU to CPU is not
/// necessary at this stage to support the keyswitch. The keyswitch key is copied entirely to all
/// the GPUs.
impl LweKeyswitchKeyConversionEngine<LweKeyswitchKey32, CudaLweKeyswitchKey32> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::backends::cuda::private::device::GpuIndex;
/// use tfhe::core_crypto::prelude::{
/// DecompositionBaseLog, DecompositionLevelCount, LweDimension, Variance, *,
/// };
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let input_lwe_dimension = LweDimension(6);
/// let output_lwe_dimension = LweDimension(3);
/// let decomposition_level_count = DecompositionLevelCount(2);
/// let decomposition_base_log = DecompositionBaseLog(8);
/// let noise = Variance(2_f64.powf(-25.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let input_key: LweSecretKey32 =
/// default_engine.generate_new_lwe_secret_key(input_lwe_dimension)?;
/// let output_key: LweSecretKey32 =
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
/// let ksk = default_engine.generate_new_lwe_keyswitch_key(
/// &input_key,
/// &output_key,
/// decomposition_level_count,
/// decomposition_base_log,
/// noise,
/// )?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ksk: CudaLweKeyswitchKey32 = cuda_engine.convert_lwe_keyswitch_key(&ksk)?;
///
/// assert_eq!(d_ksk.input_lwe_dimension(), input_lwe_dimension);
/// assert_eq!(d_ksk.output_lwe_dimension(), output_lwe_dimension);
/// assert_eq!(d_ksk.decomposition_level_count(), decomposition_level_count);
/// assert_eq!(d_ksk.decomposition_base_log(), decomposition_base_log);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_lwe_keyswitch_key(
&mut self,
input: &LweKeyswitchKey32,
) -> Result<CudaLweKeyswitchKey32, LweKeyswitchKeyConversionError<CudaError>> {
for gpu_index in 0..self.get_number_of_gpus().0 {
let stream = &self.streams[gpu_index];
let data_per_gpu = input.decomposition_level_count().0
* (input.output_lwe_dimension().0 + 1)
* input.input_lwe_dimension().0;
let size = data_per_gpu as u64 * std::mem::size_of::<u32>() as u64;
stream.check_device_memory(size)?;
}
Ok(unsafe { self.convert_lwe_keyswitch_key_unchecked(input) })
}
unsafe fn convert_lwe_keyswitch_key_unchecked(
&mut self,
input: &LweKeyswitchKey32,
) -> CudaLweKeyswitchKey32 {
// Copy the entire input vector over all GPUs
let mut d_vecs = Vec::with_capacity(self.get_number_of_gpus().0);
let data_per_gpu = input.decomposition_level_count().0
* input.output_lwe_dimension().to_lwe_size().0
* input.input_lwe_dimension().0;
for stream in self.streams.iter() {
let mut d_vec = stream.malloc::<u32>(data_per_gpu as u32);
stream.copy_to_gpu(&mut d_vec, input.0.as_tensor().as_slice());
d_vecs.push(d_vec);
}
CudaLweKeyswitchKey32(CudaLweKeyswitchKey::<u32> {
d_vecs,
input_lwe_dimension: input.input_lwe_dimension(),
output_lwe_dimension: input.output_lwe_dimension(),
decomp_level: input.decomposition_level_count(),
decomp_base_log: input.decomposition_base_log(),
})
}
}
/// # Description
/// Convert an LWE keyswitch key corresponding to 32 bits of precision from the GPU to the CPU.
/// We assume consistency between all the available GPUs and simply copy what is in the one with
/// index 0.
impl LweKeyswitchKeyConversionEngine<CudaLweKeyswitchKey32, LweKeyswitchKey32> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::backends::cuda::private::device::GpuIndex;
/// use tfhe::core_crypto::prelude::{
/// DecompositionBaseLog, DecompositionLevelCount, LweDimension, Variance, *,
/// };
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let input_lwe_dimension = LweDimension(6);
/// let output_lwe_dimension = LweDimension(3);
/// let decomposition_level_count = DecompositionLevelCount(2);
/// let decomposition_base_log = DecompositionBaseLog(8);
/// let noise = Variance(2_f64.powf(-25.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let input_key: LweSecretKey32 =
/// default_engine.generate_new_lwe_secret_key(input_lwe_dimension)?;
/// let output_key: LweSecretKey32 =
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
/// let h_ksk = default_engine.generate_new_lwe_keyswitch_key(
/// &input_key,
/// &output_key,
/// decomposition_level_count,
/// decomposition_base_log,
/// noise,
/// )?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ksk: CudaLweKeyswitchKey32 = cuda_engine.convert_lwe_keyswitch_key(&h_ksk)?;
/// let h_output_ksk: LweKeyswitchKey32 = cuda_engine.convert_lwe_keyswitch_key(&d_ksk)?;
///
/// assert_eq!(d_ksk.input_lwe_dimension(), input_lwe_dimension);
/// assert_eq!(d_ksk.output_lwe_dimension(), output_lwe_dimension);
/// assert_eq!(d_ksk.decomposition_level_count(), decomposition_level_count);
/// assert_eq!(d_ksk.decomposition_base_log(), decomposition_base_log);
/// assert_eq!(h_output_ksk, h_ksk);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_lwe_keyswitch_key(
&mut self,
input: &CudaLweKeyswitchKey32,
) -> Result<LweKeyswitchKey32, LweKeyswitchKeyConversionError<CudaError>> {
Ok(unsafe { self.convert_lwe_keyswitch_key_unchecked(input) })
}
unsafe fn convert_lwe_keyswitch_key_unchecked(
&mut self,
input: &CudaLweKeyswitchKey32,
) -> LweKeyswitchKey32 {
let data_per_gpu = input.decomposition_level_count().0
* input.output_lwe_dimension().to_lwe_size().0
* input.input_lwe_dimension().0;
// Copy the data from GPU 0 back to the CPU
let mut output = vec![0u32; data_per_gpu];
let stream = self.streams.first().unwrap();
stream.copy_to_cpu::<u32>(&mut output, input.0.d_vecs.first().unwrap());
LweKeyswitchKey32(LweKeyswitchKey::from_container(
output,
input.decomposition_base_log(),
input.decomposition_level_count(),
input.output_lwe_dimension(),
))
}
}
/// # Description
/// Convert an LWE keyswitch key corresponding to 64 bits of precision from the CPU to the GPU.
/// We only support the conversion from CPU to GPU: the conversion from GPU to CPU is not
/// necessary at this stage to support the keyswitch. The keyswitch key is copied entirely to all
/// the GPUs.
impl LweKeyswitchKeyConversionEngine<LweKeyswitchKey64, CudaLweKeyswitchKey64> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::backends::cuda::private::device::GpuIndex;
/// use tfhe::core_crypto::prelude::{
/// DecompositionBaseLog, DecompositionLevelCount, LweDimension, Variance, *,
/// };
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let input_lwe_dimension = LweDimension(6);
/// let output_lwe_dimension = LweDimension(3);
/// let decomposition_level_count = DecompositionLevelCount(2);
/// let decomposition_base_log = DecompositionBaseLog(8);
/// let noise = Variance(2_f64.powf(-25.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let input_key: LweSecretKey64 =
/// default_engine.generate_new_lwe_secret_key(input_lwe_dimension)?;
/// let output_key: LweSecretKey64 =
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
/// let ksk = default_engine.generate_new_lwe_keyswitch_key(
/// &input_key,
/// &output_key,
/// decomposition_level_count,
/// decomposition_base_log,
/// noise,
/// )?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ksk: CudaLweKeyswitchKey64 = cuda_engine.convert_lwe_keyswitch_key(&ksk)?;
///
/// assert_eq!(d_ksk.input_lwe_dimension(), input_lwe_dimension);
/// assert_eq!(d_ksk.output_lwe_dimension(), output_lwe_dimension);
/// assert_eq!(d_ksk.decomposition_level_count(), decomposition_level_count);
/// assert_eq!(d_ksk.decomposition_base_log(), decomposition_base_log);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_lwe_keyswitch_key(
&mut self,
input: &LweKeyswitchKey64,
) -> Result<CudaLweKeyswitchKey64, LweKeyswitchKeyConversionError<CudaError>> {
for stream in self.streams.iter() {
let data_per_gpu = input.decomposition_level_count().0
* input.output_lwe_dimension().to_lwe_size().0
* input.input_lwe_dimension().0;
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
stream.check_device_memory(size)?;
}
Ok(unsafe { self.convert_lwe_keyswitch_key_unchecked(input) })
}
unsafe fn convert_lwe_keyswitch_key_unchecked(
&mut self,
input: &LweKeyswitchKey64,
) -> CudaLweKeyswitchKey64 {
// Copy the entire input vector over all GPUs
let mut d_vecs = Vec::with_capacity(self.get_number_of_gpus().0);
let data_per_gpu = input.decomposition_level_count().0
* input.output_lwe_dimension().to_lwe_size().0
* input.input_lwe_dimension().0;
for stream in self.streams.iter() {
let mut d_vec = stream.malloc::<u64>(data_per_gpu as u32);
stream.copy_to_gpu(&mut d_vec, input.0.as_tensor().as_slice());
d_vecs.push(d_vec);
}
CudaLweKeyswitchKey64(CudaLweKeyswitchKey::<u64> {
d_vecs,
input_lwe_dimension: input.input_lwe_dimension(),
output_lwe_dimension: input.output_lwe_dimension(),
decomp_level: input.decomposition_level_count(),
decomp_base_log: input.decomposition_base_log(),
})
}
}
impl LweKeyswitchKeyConversionEngine<CudaLweKeyswitchKey64, LweKeyswitchKey64> for CudaEngine {
/// # Example
/// ```
/// use tfhe::core_crypto::backends::cuda::private::device::GpuIndex;
/// use tfhe::core_crypto::prelude::{
/// DecompositionBaseLog, DecompositionLevelCount, LweDimension, Variance, *,
/// };
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let input_lwe_dimension = LweDimension(6);
/// let output_lwe_dimension = LweDimension(3);
/// let decomposition_level_count = DecompositionLevelCount(2);
/// let decomposition_base_log = DecompositionBaseLog(8);
/// let noise = Variance(2_f64.powf(-25.));
///
/// const UNSAFE_SECRET: u128 = 0;
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let input_key: LweSecretKey64 =
/// default_engine.generate_new_lwe_secret_key(input_lwe_dimension)?;
/// let output_key: LweSecretKey64 =
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
/// let h_ksk = default_engine.generate_new_lwe_keyswitch_key(
/// &input_key,
/// &output_key,
/// decomposition_level_count,
/// decomposition_base_log,
/// noise,
/// )?;
///
/// let mut cuda_engine = CudaEngine::new(())?;
/// let d_ksk: CudaLweKeyswitchKey64 = cuda_engine.convert_lwe_keyswitch_key(&h_ksk)?;
/// let h_output_ksk: LweKeyswitchKey64 = cuda_engine.convert_lwe_keyswitch_key(&d_ksk)?;
///
/// assert_eq!(d_ksk.input_lwe_dimension(), input_lwe_dimension);
/// assert_eq!(d_ksk.output_lwe_dimension(), output_lwe_dimension);
/// assert_eq!(d_ksk.decomposition_level_count(), decomposition_level_count);
/// assert_eq!(d_ksk.decomposition_base_log(), decomposition_base_log);
/// assert_eq!(h_output_ksk, h_ksk);
///
/// #
/// # Ok(())
/// # }
/// ```
fn convert_lwe_keyswitch_key(
&mut self,
input: &CudaLweKeyswitchKey64,
) -> Result<LweKeyswitchKey64, LweKeyswitchKeyConversionError<CudaError>> {
Ok(unsafe { self.convert_lwe_keyswitch_key_unchecked(input) })
}
unsafe fn convert_lwe_keyswitch_key_unchecked(
&mut self,
input: &CudaLweKeyswitchKey64,
) -> LweKeyswitchKey64 {
let data_per_gpu = input.decomposition_level_count().0
* input.output_lwe_dimension().to_lwe_size().0
* input.input_lwe_dimension().0;
// Copy the data from GPU 0 back to the CPU
let mut output = vec![0u64; data_per_gpu];
let stream = self.streams.first().unwrap();
stream.copy_to_cpu::<u64>(&mut output, input.0.d_vecs.first().unwrap());
LweKeyswitchKey64(LweKeyswitchKey::from_container(
output,
input.decomposition_base_log(),
input.decomposition_level_count(),
input.output_lwe_dimension(),
))
}
}

View File

@@ -1,77 +0,0 @@
use crate::core_crypto::backends::cuda::private::device::{CudaStream, GpuIndex, NumberOfGpus};
use crate::core_crypto::prelude::sealed::AbstractEngineSeal;
use crate::core_crypto::prelude::{AbstractEngine, CudaError, SharedMemoryAmount};
use concrete_cuda::cuda_bind::cuda_get_number_of_gpus;
/// The main engine exposed by the cuda backend.
///
/// This engine handles single-GPU and multi-GPU computations for the user. It always associates
/// one Cuda stream to each available Nvidia GPU, and splits the input ciphertexts evenly over
/// the GPUs (the last GPU may be a bit more loaded if the number of GPUs does not divide the
/// number of input ciphertexts). This engine does not give control over the streams, nor the GPU
/// load balancing. In this way, we can overlap computations done on different GPUs, but not
/// computations done on a given GPU, which are executed in a sequence.
// A finer access to streams could allow for more overlapping of computations
// on a given device. We'll probably want to support it in the future, in an AdvancedCudaEngine
// for example.
#[derive(Debug, Clone)]
pub struct CudaEngine {
streams: Vec<CudaStream>,
max_shared_memory: usize,
}
impl AbstractEngineSeal for CudaEngine {}
impl AbstractEngine for CudaEngine {
type EngineError = CudaError;
type Parameters = ();
fn new(_parameters: Self::Parameters) -> Result<Self, Self::EngineError> {
let number_of_gpus = unsafe { cuda_get_number_of_gpus() as usize };
if number_of_gpus == 0 {
Err(CudaError::DeviceNotFound)
} else {
let mut streams: Vec<CudaStream> = Vec::new();
for gpu_index in 0..number_of_gpus {
streams.push(CudaStream::new(GpuIndex(gpu_index))?);
}
let max_shared_memory = streams[0].get_max_shared_memory()?;
Ok(CudaEngine {
streams,
max_shared_memory: max_shared_memory as usize,
})
}
}
}
impl CudaEngine {
/// Get the number of available GPUs from the engine
pub fn get_number_of_gpus(&self) -> NumberOfGpus {
NumberOfGpus(self.streams.len())
}
/// Get the Cuda streams from the engine
pub fn get_cuda_streams(&self) -> &Vec<CudaStream> {
&self.streams
}
/// Get the size of the shared memory (on device 0)
pub fn get_cuda_shared_memory(&self) -> SharedMemoryAmount {
SharedMemoryAmount(self.max_shared_memory)
}
}
macro_rules! check_poly_size {
($poly_size: ident) => {
if $poly_size.0 != 512 && $poly_size.0 != 1024 && $poly_size.0 != 2048 {
return Err(CudaError::PolynomialSizeNotSupported.into());
}
};
}
mod glwe_ciphertext_conversion;
mod lwe_bootstrap_key_conversion;
mod lwe_ciphertext_conversion;
mod lwe_ciphertext_discarding_bootstrap;
mod lwe_ciphertext_discarding_conversion;
mod lwe_ciphertext_discarding_keyswitch;
mod lwe_keyswitch_key_conversion;

View File

@@ -1,87 +0,0 @@
//! A module containing the [engines](crate::core_crypto::specification::engines) exposed by
//! the cuda backend.
use crate::core_crypto::backends::cuda::private::device::GpuIndex;
use std::error::Error;
use std::fmt::{Display, Formatter};
mod cuda_engine;
pub use cuda_engine::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SharedMemoryAmount(pub usize);
#[derive(Debug)]
pub enum CudaError {
DeviceNotFound,
SharedMemoryNotFound(GpuIndex),
NotEnoughDeviceMemory(GpuIndex),
InvalidDeviceIndex(GpuIndex),
UnspecifiedDeviceError(GpuIndex),
PolynomialSizeNotSupported,
GlweDimensionNotSupported,
BaseLogNotSupported,
}
impl Display for CudaError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
CudaError::DeviceNotFound => {
write!(f, "No GPU detected on the machine.")
}
CudaError::SharedMemoryNotFound(gpu_index) => {
write!(f, "No shared memory detected on the GPU #{}.", gpu_index.0)
}
CudaError::NotEnoughDeviceMemory(gpu_index) => {
write!(
f,
"The GPU #{} does not have enough global memory to hold all the data.",
gpu_index.0
)
}
CudaError::InvalidDeviceIndex(gpu_index) => {
write!(
f,
"The specified GPU index, {}, does not exist.",
gpu_index.0
)
}
CudaError::PolynomialSizeNotSupported => {
write!(
f,
"The polynomial size should be a power of 2. Values strictly lower than \
512, and strictly greater than 8192, are not supported."
)
}
CudaError::GlweDimensionNotSupported => {
write!(f, "The only supported GLWE dimension is 1.")
}
CudaError::BaseLogNotSupported => {
write!(f, "The base log has to be lower or equal to 16.")
}
CudaError::UnspecifiedDeviceError(gpu_index) => {
write!(f, "Unspecified device error on GPU #{}.", gpu_index.0)
}
}
}
}
impl Error for CudaError {}
macro_rules! check_glwe_dim {
($glwe_dimension: ident) => {
if $glwe_dimension.0 != 1 {
return Err(CudaError::GlweDimensionNotSupported.into());
}
};
}
macro_rules! check_base_log {
($base_log: ident) => {
if $base_log.0 > 16 {
return Err(CudaError::BaseLogNotSupported.into());
}
};
}
pub(crate) use {check_base_log, check_glwe_dim};

View File

@@ -1,45 +0,0 @@
use std::fmt::Debug;
use crate::core_crypto::prelude::{GlweDimension, PolynomialSize};
use crate::core_crypto::backends::cuda::private::crypto::glwe::ciphertext::CudaGlweCiphertext;
use crate::core_crypto::specification::entities::markers::GlweCiphertextKind;
use crate::core_crypto::specification::entities::{AbstractEntity, GlweCiphertextEntity};
/// A structure representing a vector of GLWE ciphertexts with 32 bits of precision on the GPU.
/// It is used as input to the Cuda bootstrap for the array of lookup tables.
#[derive(Debug)]
pub struct CudaGlweCiphertext32(pub(crate) CudaGlweCiphertext<u32>);
impl AbstractEntity for CudaGlweCiphertext32 {
type Kind = GlweCiphertextKind;
}
impl GlweCiphertextEntity for CudaGlweCiphertext32 {
fn glwe_dimension(&self) -> GlweDimension {
self.0.glwe_dimension
}
fn polynomial_size(&self) -> PolynomialSize {
self.0.polynomial_size
}
}
/// A structure representing a vector of GLWE ciphertexts with 64 bits of precision on the GPU.
/// It is used as input to the Cuda bootstrap for the array of lookup tables.
#[derive(Debug)]
pub struct CudaGlweCiphertext64(pub(crate) CudaGlweCiphertext<u64>);
impl AbstractEntity for CudaGlweCiphertext64 {
type Kind = GlweCiphertextKind;
}
impl GlweCiphertextEntity for CudaGlweCiphertext64 {
fn glwe_dimension(&self) -> GlweDimension {
self.0.glwe_dimension
}
fn polynomial_size(&self) -> PolynomialSize {
self.0.polynomial_size
}
}

View File

@@ -1,67 +0,0 @@
use crate::core_crypto::prelude::{
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
};
use crate::core_crypto::backends::cuda::private::crypto::bootstrap::CudaBootstrapKey;
use crate::core_crypto::specification::entities::markers::LweBootstrapKeyKind;
use crate::core_crypto::specification::entities::{AbstractEntity, LweBootstrapKeyEntity};
/// A structure representing a Fourier bootstrap key for 32 bits precision ciphertexts on the GPU.
#[derive(Debug)]
pub struct CudaFourierLweBootstrapKey32(pub(crate) CudaBootstrapKey<u32>);
impl AbstractEntity for CudaFourierLweBootstrapKey32 {
type Kind = LweBootstrapKeyKind;
}
impl LweBootstrapKeyEntity for CudaFourierLweBootstrapKey32 {
fn glwe_dimension(&self) -> GlweDimension {
self.0.glwe_dimension
}
fn polynomial_size(&self) -> PolynomialSize {
self.0.polynomial_size
}
fn input_lwe_dimension(&self) -> LweDimension {
self.0.input_lwe_dimension
}
fn decomposition_base_log(&self) -> DecompositionBaseLog {
self.0.decomp_base_log
}
fn decomposition_level_count(&self) -> DecompositionLevelCount {
self.0.decomp_level
}
}
/// A structure representing a Fourier bootstrap key for 64 bits precision ciphertexts on the GPU.
#[derive(Debug)]
pub struct CudaFourierLweBootstrapKey64(pub(crate) CudaBootstrapKey<u64>);
impl AbstractEntity for CudaFourierLweBootstrapKey64 {
type Kind = LweBootstrapKeyKind;
}
impl LweBootstrapKeyEntity for CudaFourierLweBootstrapKey64 {
fn glwe_dimension(&self) -> GlweDimension {
self.0.glwe_dimension
}
fn polynomial_size(&self) -> PolynomialSize {
self.0.polynomial_size
}
fn input_lwe_dimension(&self) -> LweDimension {
self.0.input_lwe_dimension
}
fn decomposition_base_log(&self) -> DecompositionBaseLog {
self.0.decomp_base_log
}
fn decomposition_level_count(&self) -> DecompositionLevelCount {
self.0.decomp_level
}
}

View File

@@ -1,35 +0,0 @@
use std::fmt::Debug;
use crate::core_crypto::prelude::LweDimension;
use crate::core_crypto::backends::cuda::private::crypto::lwe::ciphertext::CudaLweCiphertext;
use crate::core_crypto::specification::entities::markers::LweCiphertextKind;
use crate::core_crypto::specification::entities::{AbstractEntity, LweCiphertextEntity};
/// A structure representing a vector of LWE ciphertexts with 32 bits of precision on the GPU.
#[derive(Debug)]
pub struct CudaLweCiphertext32(pub(crate) CudaLweCiphertext<u32>);
impl AbstractEntity for CudaLweCiphertext32 {
type Kind = LweCiphertextKind;
}
impl LweCiphertextEntity for CudaLweCiphertext32 {
fn lwe_dimension(&self) -> LweDimension {
self.0.lwe_dimension
}
}
/// A structure representing a vector of LWE ciphertexts with 64 bits of precision on the GPU.
#[derive(Debug)]
pub struct CudaLweCiphertext64(pub(crate) CudaLweCiphertext<u64>);
impl AbstractEntity for CudaLweCiphertext64 {
type Kind = LweCiphertextKind;
}
impl LweCiphertextEntity for CudaLweCiphertext64 {
fn lwe_dimension(&self) -> LweDimension {
self.0.lwe_dimension
}
}

View File

@@ -1,57 +0,0 @@
use crate::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount, LweDimension};
use crate::core_crypto::backends::cuda::private::crypto::keyswitch::CudaLweKeyswitchKey;
use crate::core_crypto::specification::entities::markers::LweKeyswitchKeyKind;
use crate::core_crypto::specification::entities::{AbstractEntity, LweKeyswitchKeyEntity};
/// A structure representing a keyswitch key for 32 bits precision ciphertexts on the GPU.
#[derive(Debug)]
pub struct CudaLweKeyswitchKey32(pub(crate) CudaLweKeyswitchKey<u32>);
impl AbstractEntity for CudaLweKeyswitchKey32 {
type Kind = LweKeyswitchKeyKind;
}
impl LweKeyswitchKeyEntity for CudaLweKeyswitchKey32 {
fn input_lwe_dimension(&self) -> LweDimension {
self.0.input_lwe_dimension
}
fn output_lwe_dimension(&self) -> LweDimension {
self.0.output_lwe_dimension
}
fn decomposition_level_count(&self) -> DecompositionLevelCount {
self.0.decomp_level
}
fn decomposition_base_log(&self) -> DecompositionBaseLog {
self.0.decomp_base_log
}
}
/// A structure representing a keyswitch key for 64 bits precision ciphertexts on the GPU.
#[derive(Debug)]
pub struct CudaLweKeyswitchKey64(pub(crate) CudaLweKeyswitchKey<u64>);
impl AbstractEntity for CudaLweKeyswitchKey64 {
type Kind = LweKeyswitchKeyKind;
}
impl LweKeyswitchKeyEntity for CudaLweKeyswitchKey64 {
fn input_lwe_dimension(&self) -> LweDimension {
self.0.input_lwe_dimension
}
fn output_lwe_dimension(&self) -> LweDimension {
self.0.output_lwe_dimension
}
fn decomposition_level_count(&self) -> DecompositionLevelCount {
self.0.decomp_level
}
fn decomposition_base_log(&self) -> DecompositionBaseLog {
self.0.decomp_base_log
}
}

View File

@@ -1,12 +0,0 @@
//! A module containing all the [entities](crate::core_crypto::specification::entities)
//! exposed by the cuda backend.
mod glwe_ciphertext;
mod lwe_bootstrap_key;
mod lwe_ciphertext;
mod lwe_keyswitch_key;
pub use glwe_ciphertext::*;
pub use lwe_bootstrap_key::*;
pub use lwe_ciphertext::*;
pub use lwe_keyswitch_key::*;

View File

@@ -1,2 +0,0 @@
pub mod engines;
pub mod entities;

View File

@@ -1,13 +0,0 @@
//! A module containing the cuda backend implementation.
//!
//! This module contains CUDA GPU implementations of some FHE cryptographic primitives. In
//! particular, it makes it possible to execute bootstraps on a vector of ciphertext vectors, with a
//! vector of LUT and a bootstrap key as other inputs. To do so, the backend also exposes functions
//! to transfer data to and from the GPU, via conversion functions.
#[doc(hidden)]
pub mod private;
pub(crate) mod implementation;
pub use implementation::{engines, entities};

View File

@@ -1,67 +0,0 @@
//! Bootstrap key with Cuda.
use crate::core_crypto::backends::cuda::private::device::{CudaStream, NumberOfGpus};
use crate::core_crypto::backends::cuda::private::vec::CudaVec;
use crate::core_crypto::commons::crypto::bootstrap::StandardBootstrapKey;
use crate::core_crypto::commons::math::tensor::{AsRefSlice, AsRefTensor};
use crate::core_crypto::commons::numeric::UnsignedInteger;
use crate::core_crypto::prelude::{
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
};
use std::marker::PhantomData;
#[derive(Debug)]
pub(crate) struct CudaBootstrapKey<T: UnsignedInteger> {
// Pointers to GPU data: one cuda vec per GPU
pub(crate) d_vecs: Vec<CudaVec<f64>>,
// Input LWE dimension
pub(crate) input_lwe_dimension: LweDimension,
// Size of polynomials in the key
pub(crate) polynomial_size: PolynomialSize,
// GLWE dimension
pub(crate) glwe_dimension: GlweDimension,
// Number of decomposition levels
pub(crate) decomp_level: DecompositionLevelCount,
// Value of the base log for the decomposition
pub(crate) decomp_base_log: DecompositionBaseLog,
// Field to hold type T
pub(crate) _phantom: PhantomData<T>,
}
unsafe impl<T> Send for CudaBootstrapKey<T> where T: Send + UnsignedInteger {}
unsafe impl<T> Sync for CudaBootstrapKey<T> where T: Sync + UnsignedInteger {}
pub(crate) unsafe fn convert_lwe_bootstrap_key_from_cpu_to_gpu<T: UnsignedInteger, Cont>(
streams: &[CudaStream],
input: &StandardBootstrapKey<Cont>,
number_of_gpus: NumberOfGpus,
) -> Vec<CudaVec<f64>>
where
Cont: AsRefSlice<Element = T>,
{
// Copy the entire input vector over all GPUs
let mut vecs = Vec::with_capacity(number_of_gpus.0);
// TODO
// Check if it would be better to have GPU 0 compute the BSK and copy it back to the
// CPU, then copy the BSK to the other GPUs. The order of instructions varies depending on
// the Cuda warp scheduling, which we cannot assume is deterministic, so we'll end up with
// slightly different BSKs on the GPUs. It is unclear how significantly this affects the
// noise after the bootstrap.
let total_polynomials =
input.key_size().0 * input.glwe_size().0 * input.glwe_size().0 * input.level_count().0;
let alloc_size = total_polynomials * input.polynomial_size().0;
for stream in streams.iter() {
let mut d_vec = stream.malloc::<f64>(alloc_size as u32);
let input_slice = input.as_tensor().as_slice();
stream.initialize_twiddles(input.polynomial_size());
stream.convert_lwe_bootstrap_key::<T>(
&mut d_vec,
input_slice,
input.key_size(),
input.glwe_size().to_glwe_dimension(),
input.level_count(),
input.polynomial_size(),
);
vecs.push(d_vec);
}
vecs
}

View File

@@ -1,18 +0,0 @@
use crate::core_crypto::backends::cuda::private::vec::CudaVec;
use crate::core_crypto::commons::numeric::UnsignedInteger;
use crate::core_crypto::prelude::{GlweDimension, PolynomialSize};
/// One GLWE ciphertext on GPU 0.
///
/// There is no multi GPU support at this stage since the user cannot
/// specify on which GPU to convert the data.
// Fields with `d_` are data in the GPU
#[derive(Debug)]
pub(crate) struct CudaGlweCiphertext<T: UnsignedInteger> {
// Pointer to GPU data: one cuda vec on GPU 0
pub(crate) d_vec: CudaVec<T>,
// Glwe dimension
pub(crate) glwe_dimension: GlweDimension,
// Polynomial size
pub(crate) polynomial_size: PolynomialSize,
}

View File

@@ -1,3 +0,0 @@
//! GLWE ciphertexts and ciphertext vectors with Cuda.
pub(crate) mod ciphertext;

View File

@@ -1,21 +0,0 @@
//! Keyswitch key with Cuda.
use crate::core_crypto::backends::cuda::private::vec::CudaVec;
use crate::core_crypto::commons::numeric::UnsignedInteger;
use crate::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount, LweDimension};
#[derive(Debug)]
pub(crate) struct CudaLweKeyswitchKey<T: UnsignedInteger> {
// Pointers to GPU data: one cuda vec per GPU
pub(crate) d_vecs: Vec<CudaVec<T>>,
// Input LWE dimension
pub(crate) input_lwe_dimension: LweDimension,
// Output LWE dimension
pub(crate) output_lwe_dimension: LweDimension,
// Number of decomposition levels
pub(crate) decomp_level: DecompositionLevelCount,
// Value of the base log for the decomposition
pub(crate) decomp_base_log: DecompositionBaseLog,
}
unsafe impl<T> Send for CudaLweKeyswitchKey<T> where T: Send + UnsignedInteger {}
unsafe impl<T> Sync for CudaLweKeyswitchKey<T> where T: Sync + UnsignedInteger {}

View File

@@ -1,17 +0,0 @@
use crate::core_crypto::backends::cuda::private::vec::CudaVec;
use crate::core_crypto::commons::numeric::UnsignedInteger;
use crate::core_crypto::prelude::LweDimension;
/// An LWE ciphertext on the GPU 0.
///
/// There is no multi GPU support at this stage since the user cannot
/// specify on which GPU to convert the data.
// Fields with `d_` are data in the GPU
#[derive(Debug)]
pub(crate) struct CudaLweCiphertext<T: UnsignedInteger> {
// Pointers to GPU data: one cuda vec on GPU 0
pub(crate) d_vec: CudaVec<T>,
// Lwe dimension
pub(crate) lwe_dimension: LweDimension,
}

View File

@@ -1,3 +0,0 @@
//! LWE ciphertexts and ciphertext vectors with Cuda.
pub(crate) mod ciphertext;

View File

@@ -1,8 +0,0 @@
//! Low-overhead homomorphic primitives.
//!
//! This module implements low-overhead fully homomorphic operations.
pub mod bootstrap;
pub mod glwe;
pub mod keyswitch;
pub mod lwe;

View File

@@ -1,398 +0,0 @@
use crate::core_crypto::backends::cuda::engines::CudaError;
use crate::core_crypto::backends::cuda::private::pointers::StreamPointer;
use crate::core_crypto::backends::cuda::private::vec::CudaVec;
use crate::core_crypto::commons::numeric::{Numeric, UnsignedInteger};
use crate::core_crypto::prelude::{
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweCiphertextIndex, LweDimension,
PolynomialSize, SharedMemoryAmount,
};
use concrete_cuda::cuda_bind::*;
use std::ffi::c_void;
use std::marker::PhantomData;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct GpuIndex(pub usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NumberOfSamples(pub usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NumberOfGpus(pub usize);
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CudaStream {
gpu_index: GpuIndex,
stream: StreamPointer,
}
impl CudaStream {
/// Creates a new stream attached to GPU at gpu_index
pub(crate) fn new(gpu_index: GpuIndex) -> Result<Self, CudaError> {
if gpu_index.0 >= unsafe { cuda_get_number_of_gpus() } as usize {
Err(CudaError::InvalidDeviceIndex(gpu_index))
} else {
let stream = StreamPointer(unsafe { cuda_create_stream(gpu_index.0 as u32) });
Ok(CudaStream { gpu_index, stream })
}
}
/// Gets the GPU index the stream is associated to
pub(crate) fn gpu_index(&self) -> GpuIndex {
self.gpu_index
}
/// Gets the stream handle
pub(crate) fn stream_handle(&self) -> StreamPointer {
self.stream
}
/// Check that the GPU has enough global memory
pub(crate) fn check_device_memory(&self, size: u64) -> Result<(), CudaError> {
let valid = unsafe { cuda_check_valid_malloc(size, self.gpu_index().0 as u32) };
match valid {
0 => Ok(()),
-1 => Err(CudaError::NotEnoughDeviceMemory(self.gpu_index())),
-2 => Err(CudaError::InvalidDeviceIndex(self.gpu_index())),
_ => Err(CudaError::UnspecifiedDeviceError(self.gpu_index())),
}
}
/// Allocates `elements` on the GPU
pub(crate) fn malloc<T>(&self, elements: u32) -> CudaVec<T>
where
T: Numeric,
{
let size = elements as u64 * std::mem::size_of::<T>() as u64;
let ptr = unsafe { cuda_malloc(size, self.gpu_index().0 as u32) };
CudaVec {
ptr,
idx: self.gpu_index.0 as u32,
len: elements as usize,
_phantom: PhantomData::default(),
}
}
/// Copies data from slice into GPU pointer
///
/// # Safety
///
/// - `dest` __must__ be a valid pointer
/// - [CudaStream::cuda_synchronize_device] __must__ have been called before
/// - [CudaStream::cuda_synchronize_device] __must__ be called after the copy
/// as soon as synchronization is required
pub(crate) unsafe fn copy_to_gpu_async<T>(&self, dest: &mut CudaVec<T>, src: &[T])
where
T: Numeric,
{
let size = (src.len() * std::mem::size_of::<T>()) as u64;
cuda_memcpy_async_to_gpu(
dest.as_mut_c_ptr(),
src.as_ptr() as *const c_void,
size,
self.stream_handle().0,
self.gpu_index().0 as u32,
);
}
/// Copies data from slice into GPU pointer
///
/// # Safety
///
/// - `dest` __must__ be a valid pointer
/// - [CudaStream::cuda_synchronize_device] __must__ have been called before
pub(crate) unsafe fn copy_to_gpu<T>(&self, dest: &mut CudaVec<T>, src: &[T])
where
T: Numeric,
{
self.copy_to_gpu_async(dest, src);
self.synchronize_device();
}
/// Copies data from GPU pointer into slice
///
/// # Safety
///
/// - `dest` __must__ be a valid pointer
/// - [CudaStream::cuda_synchronize_device] __must__ have been called before
/// - [CudaStream::cuda_synchronize_device] __must__ be called as soon as synchronization is
/// required
pub(crate) unsafe fn copy_to_cpu_async<T>(&self, dest: &mut [T], src: &CudaVec<T>)
where
T: Numeric,
{
let size = (dest.len() * std::mem::size_of::<T>()) as u64;
cuda_memcpy_async_to_cpu(
dest.as_mut_ptr() as *mut c_void,
src.as_c_ptr(),
size,
self.stream_handle().0,
self.gpu_index().0 as u32,
);
}
/// Copies data from GPU pointer into slice
///
/// # Safety
///
/// - `dest` __must__ be a valid pointer
/// - [CudaStream::cuda_synchronize_device] __must__ have been called before
pub(crate) unsafe fn copy_to_cpu<T>(&self, dest: &mut [T], src: &CudaVec<T>)
where
T: Numeric,
{
self.copy_to_cpu_async(dest, src);
self.synchronize_device();
}
/// Synchronizes the device
#[allow(dead_code)]
pub(crate) fn synchronize_device(&self) {
unsafe { cuda_synchronize_device(self.gpu_index().0 as u32) };
}
/// Get the maximum amount of shared memory
pub(crate) fn get_max_shared_memory(&self) -> Result<i32, CudaError> {
let max_shared_memory = unsafe { cuda_get_max_shared_memory(self.gpu_index().0 as u32) };
match max_shared_memory {
0 => Err(CudaError::SharedMemoryNotFound(self.gpu_index())),
-2 => Err(CudaError::InvalidDeviceIndex(self.gpu_index())),
_ => Ok(max_shared_memory),
}
}
/// Initialize twiddles
#[allow(dead_code)]
pub fn initialize_twiddles(&self, polynomial_size: PolynomialSize) {
unsafe { cuda_initialize_twiddles(polynomial_size.0 as u32, self.gpu_index.0 as u32) };
}
/// Convert bootstrap key
#[allow(dead_code)]
pub unsafe fn convert_lwe_bootstrap_key<T: UnsignedInteger>(
&self,
dest: &mut CudaVec<f64>,
src: &[T],
input_lwe_dim: LweDimension,
glwe_dim: GlweDimension,
l_gadget: DecompositionLevelCount,
polynomial_size: PolynomialSize,
) {
if T::BITS == 32 {
cuda_convert_lwe_bootstrap_key_32(
dest.as_mut_c_ptr(),
src.as_ptr() as *mut c_void,
self.stream.0,
self.gpu_index.0 as u32,
input_lwe_dim.0 as u32,
glwe_dim.0 as u32,
l_gadget.0 as u32,
polynomial_size.0 as u32,
)
} else if T::BITS == 64 {
cuda_convert_lwe_bootstrap_key_64(
dest.as_mut_c_ptr(),
src.as_ptr() as *mut c_void,
self.stream.0,
self.gpu_index.0 as u32,
input_lwe_dim.0 as u32,
glwe_dim.0 as u32,
l_gadget.0 as u32,
polynomial_size.0 as u32,
)
}
}
/// Discarding bootstrap on a vector of LWE ciphertexts
#[allow(dead_code, clippy::too_many_arguments)]
pub unsafe fn discard_bootstrap_amortized_lwe_ciphertext_vector<T: UnsignedInteger>(
&self,
lwe_array_out: &mut CudaVec<T>,
test_vector: &CudaVec<T>,
test_vector_indexes: &CudaVec<u32>,
lwe_array_in: &CudaVec<T>,
bootstrapping_key: &CudaVec<f64>,
lwe_dimension: LweDimension,
glwe_dimension: GlweDimension,
polynomial_size: PolynomialSize,
base_log: DecompositionBaseLog,
level: DecompositionLevelCount,
num_samples: NumberOfSamples,
lwe_idx: LweCiphertextIndex,
max_shared_memory: SharedMemoryAmount,
) {
if T::BITS == 32 {
cuda_bootstrap_amortized_lwe_ciphertext_vector_32(
self.stream.0,
lwe_array_out.as_mut_c_ptr(),
test_vector.as_c_ptr(),
test_vector_indexes.as_c_ptr(),
lwe_array_in.as_c_ptr(),
bootstrapping_key.as_c_ptr(),
lwe_dimension.0 as u32,
glwe_dimension.0 as u32,
polynomial_size.0 as u32,
base_log.0 as u32,
level.0 as u32,
num_samples.0 as u32,
num_samples.0 as u32,
lwe_idx.0 as u32,
max_shared_memory.0 as u32,
)
} else if T::BITS == 64 {
cuda_bootstrap_amortized_lwe_ciphertext_vector_64(
self.stream.0,
lwe_array_out.as_mut_c_ptr(),
test_vector.as_c_ptr(),
test_vector_indexes.as_c_ptr(),
lwe_array_in.as_c_ptr(),
bootstrapping_key.as_c_ptr(),
lwe_dimension.0 as u32,
glwe_dimension.0 as u32,
polynomial_size.0 as u32,
base_log.0 as u32,
level.0 as u32,
num_samples.0 as u32,
num_samples.0 as u32,
lwe_idx.0 as u32,
max_shared_memory.0 as u32,
)
}
}
/// Discarding bootstrap on a vector of LWE ciphertexts
#[allow(dead_code, clippy::too_many_arguments)]
pub unsafe fn discard_bootstrap_low_latency_lwe_ciphertext_vector<T: UnsignedInteger>(
&self,
lwe_array_out: &mut CudaVec<T>,
test_vector: &CudaVec<T>,
test_vector_indexes: &CudaVec<u32>,
lwe_array_in: &CudaVec<T>,
bootstrapping_key: &CudaVec<f64>,
lwe_dimension: LweDimension,
glwe_dimension: GlweDimension,
polynomial_size: PolynomialSize,
base_log: DecompositionBaseLog,
level: DecompositionLevelCount,
num_samples: NumberOfSamples,
lwe_idx: LweCiphertextIndex,
max_shared_memory: SharedMemoryAmount,
) {
if T::BITS == 32 {
cuda_bootstrap_low_latency_lwe_ciphertext_vector_32(
self.stream.0,
lwe_array_out.as_mut_c_ptr(),
test_vector.as_c_ptr(),
test_vector_indexes.as_c_ptr(),
lwe_array_in.as_c_ptr(),
bootstrapping_key.as_c_ptr(),
lwe_dimension.0 as u32,
glwe_dimension.0 as u32,
polynomial_size.0 as u32,
base_log.0 as u32,
level.0 as u32,
num_samples.0 as u32,
num_samples.0 as u32,
lwe_idx.0 as u32,
max_shared_memory.0 as u32,
)
} else if T::BITS == 64 {
cuda_bootstrap_low_latency_lwe_ciphertext_vector_64(
self.stream.0,
lwe_array_out.as_mut_c_ptr(),
test_vector.as_c_ptr(),
test_vector_indexes.as_c_ptr(),
lwe_array_in.as_c_ptr(),
bootstrapping_key.as_c_ptr(),
lwe_dimension.0 as u32,
glwe_dimension.0 as u32,
polynomial_size.0 as u32,
base_log.0 as u32,
level.0 as u32,
num_samples.0 as u32,
num_samples.0 as u32,
lwe_idx.0 as u32,
max_shared_memory.0 as u32,
)
}
}
/// Discarding keyswitch on a vector of LWE ciphertexts
#[allow(dead_code, clippy::too_many_arguments)]
pub unsafe fn discard_keyswitch_lwe_ciphertext_vector<T: UnsignedInteger>(
&self,
lwe_array_out: &mut CudaVec<T>,
lwe_array_in: &CudaVec<T>,
input_lwe_dimension: LweDimension,
output_lwe_dimension: LweDimension,
keyswitch_key: &CudaVec<T>,
base_log: DecompositionBaseLog,
l_gadget: DecompositionLevelCount,
num_samples: NumberOfSamples,
) {
if T::BITS == 32 {
cuda_keyswitch_lwe_ciphertext_vector_32(
self.stream.0,
lwe_array_out.as_mut_c_ptr(),
lwe_array_in.as_c_ptr(),
keyswitch_key.as_c_ptr(),
input_lwe_dimension.0 as u32,
output_lwe_dimension.0 as u32,
base_log.0 as u32,
l_gadget.0 as u32,
num_samples.0 as u32,
)
} else if T::BITS == 64 {
cuda_keyswitch_lwe_ciphertext_vector_64(
self.stream.0,
lwe_array_out.as_mut_c_ptr(),
lwe_array_in.as_c_ptr(),
keyswitch_key.as_c_ptr(),
input_lwe_dimension.0 as u32,
output_lwe_dimension.0 as u32,
base_log.0 as u32,
l_gadget.0 as u32,
num_samples.0 as u32,
)
}
}
}
impl Drop for CudaStream {
fn drop(&mut self) {
unsafe {
cuda_destroy_stream(self.stream_handle().0, self.gpu_index().0 as u32);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn print_gpu_info() {
println!("Number of GPUs: {}", unsafe { cuda_get_number_of_gpus() });
let gpu_index = GpuIndex(0);
let stream = CudaStream::new(gpu_index).unwrap();
println!(
"Max shared memory: {}",
stream.get_max_shared_memory().unwrap()
)
}
#[test]
fn allocate_and_copy() {
let vec = vec![1_u64, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
let gpu_index = GpuIndex(0);
let stream = CudaStream::new(gpu_index).unwrap();
stream.check_device_memory(vec.len() as u64).unwrap();
let mut d_vec: CudaVec<u64> = stream.malloc::<u64>(vec.len() as u32);
unsafe {
stream.copy_to_gpu(&mut d_vec, &vec);
}
let mut empty = vec![0_u64; vec.len()];
unsafe {
stream.copy_to_cpu(&mut empty, &d_vec);
}
assert_eq!(vec, empty);
}
}

View File

@@ -1,5 +0,0 @@
pub mod crypto;
pub mod device;
pub mod pointers;
pub mod vec;
pub mod wopbs;

View File

@@ -1,5 +0,0 @@
use std::ffi::c_void;
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct StreamPointer(pub *mut c_void);

View File

@@ -1,52 +0,0 @@
use crate::core_crypto::commons::numeric::Numeric;
use concrete_cuda::cuda_bind::cuda_drop;
use std::ffi::c_void;
use std::marker::PhantomData;
/// A contiguous array type stored in the gpu memory.
///
/// Note:
/// -----
///
/// Such a structure:
/// + can be created via the `CudaStream::malloc` function
/// + can not be copied or cloned but can be (mutably) borrowed
/// + frees the gpu memory on drop.
///
/// Put differently, it owns a region of the gpu memory at a given time. For this reason, regarding
/// memory, it is pretty close to a `Vec`. That being said, it only present a very very limited api.
#[derive(Debug)]
pub struct CudaVec<T: Numeric> {
pub(super) ptr: *mut c_void,
pub(super) idx: u32,
pub(super) len: usize,
pub(super) _phantom: PhantomData<T>,
}
impl<T: Numeric> CudaVec<T> {
/// Returns a raw pointer to the vectors buffer.
pub fn as_c_ptr(&self) -> *const c_void {
self.ptr as *const c_void
}
/// Returns an unsafe mutable pointer to the vectors buffer.
pub fn as_mut_c_ptr(&mut self) -> *mut c_void {
self.ptr
}
/// Returns the number of elements in the vector, also referred to as its length.
pub fn len(&self) -> usize {
self.len
}
/// Returns `true` if the CudaVec contains no elements.
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
impl<T: Numeric> Drop for CudaVec<T> {
fn drop(&mut self) {
unsafe { cuda_drop(self.ptr, self.idx) };
}
}

View File

@@ -1,2 +0,0 @@
#[cfg(test)]
mod test;

View File

@@ -1,407 +0,0 @@
use crate::core_crypto::backends::cuda::private::device::{CudaStream, GpuIndex};
use crate::core_crypto::commons::crypto::bootstrap::StandardBootstrapKey;
use crate::core_crypto::commons::crypto::encoding::{Plaintext, PlaintextList};
use crate::core_crypto::commons::crypto::ggsw::StandardGgswCiphertext;
use crate::core_crypto::commons::crypto::glwe::GlweCiphertext;
use crate::core_crypto::commons::crypto::lwe::{LweCiphertext, LweKeyswitchKey, LweList};
use crate::core_crypto::commons::crypto::secret::generators::{
EncryptionRandomGenerator, SecretRandomGenerator,
};
use crate::core_crypto::commons::crypto::secret::{GlweSecretKey, LweSecretKey};
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
use crate::core_crypto::commons::math::polynomial::PolynomialList;
use crate::core_crypto::commons::math::tensor::{AsMutTensor, AsRefSlice, AsRefTensor};
use crate::core_crypto::commons::test_tools;
use crate::core_crypto::prelude::*;
use concrete_csprng::generators::SoftwareRandomGenerator;
use concrete_csprng::seeders::UnixSeeder;
use concrete_cuda::cuda_bind::{
cuda_cmux_tree_64, cuda_convert_lwe_bootstrap_key_64, cuda_extract_bits_64,
cuda_initialize_twiddles, cuda_synchronize_device,
};
use std::os::raw::c_void;
#[test]
pub fn test_cuda_cmux_tree() {
let polynomial_size = PolynomialSize(512);
let glwe_dimension = GlweDimension(1);
let level = DecompositionLevelCount(3);
let base_log = DecompositionBaseLog(6);
let delta_log = 60;
let std = LogStandardDev::from_log_standard_dev(-60.);
println!(
"polynomial_size: {}, glwe_dimension: {}, level: {}, base_log: {}",
polynomial_size.0, glwe_dimension.0, level.0, base_log.0
);
let r = 10; // Depth of the tree
let num_lut = 1 << r;
// Size of a GGSW ciphertext
// N * (k+1) * (k+1) * ell
let ggsw_size = polynomial_size.0
* glwe_dimension.to_glwe_size().0
* glwe_dimension.to_glwe_size().0
* level.0;
// Size of a GLWE ciphertext
// (k+1) * N
let glwe_size = glwe_dimension.to_glwe_size().0 * polynomial_size.0;
println!("r: {}", r);
println!("glwe_size: {}, ggsw_size: {}", glwe_size, ggsw_size);
// Engines
const UNSAFE_SECRET: u128 = 0;
let mut seeder = UnixSeeder::new(UNSAFE_SECRET);
// Key
let mut secret_generator = SecretRandomGenerator::<SoftwareRandomGenerator>::new(seeder.seed());
let mut encryption_generator =
EncryptionRandomGenerator::<SoftwareRandomGenerator>::new(seeder.seed(), &mut seeder);
let rlwe_sk: GlweSecretKey<_, Vec<u64>> =
GlweSecretKey::generate_binary(glwe_dimension, polynomial_size, &mut secret_generator);
// Instantiate the LUTs
// We need 2^r GLWEs
let mut h_concatenated_luts = vec![];
let mut h_luts = PolynomialList::allocate(0u64, PolynomialCount(num_lut), polynomial_size);
for (i, mut polynomial) in h_luts.polynomial_iter_mut().enumerate() {
polynomial
.as_mut_tensor()
.fill_with_element((i as u64 % (1 << (64 - delta_log))) << delta_log);
let mut h_lut = polynomial.as_tensor().as_slice().to_vec();
let mut h_zeroes = vec![0_u64; polynomial_size.0];
// println!("lut {}) {}", i, h_lut[0]);
// Mask is zero
h_concatenated_luts.append(&mut h_zeroes);
// Body is something else
h_concatenated_luts.append(&mut h_lut);
}
// Now we have (2**r GLWE ciphertexts)
assert_eq!(h_concatenated_luts.len(), num_lut * glwe_size);
println!("\nWe have {} LUTs", num_lut);
// Copy to Device
let gpu_index = GpuIndex(0);
let stream = CudaStream::new(gpu_index).unwrap();
let mut d_concatenated_luts = stream.malloc::<u64>(h_concatenated_luts.len() as u32);
unsafe {
stream.copy_to_gpu::<u64>(&mut d_concatenated_luts, h_concatenated_luts.as_slice());
}
// Instantiate the GGSW m^tree ciphertexts
// We need r GGSW ciphertexts
// Bit decomposition of the value from MSB to LSB
let mut value = 0b111101;
let witness = value;
//bit decomposition of the value
let mut vec_message = vec![Plaintext(0); r as usize];
for i in 0..r {
vec_message[i as usize] = Plaintext(value & 1);
value >>= 1;
}
// bit decomposition are stored in ggsw
let mut h_concatenated_ggsw = vec![];
for vec_msg in vec_message.iter().take(r as usize) {
println!("vec_msg: {}", vec_msg.0);
let mut ggsw = StandardGgswCiphertext::allocate(
0 as u64,
polynomial_size,
glwe_dimension.to_glwe_size(),
level,
base_log,
);
rlwe_sk.encrypt_constant_ggsw(&mut ggsw, vec_msg, std, &mut encryption_generator);
let ggsw_slice = ggsw.as_tensor().as_slice();
h_concatenated_ggsw.append(&mut ggsw_slice.to_vec());
}
assert_eq!(h_concatenated_ggsw.len(), (r as usize) * ggsw_size);
println!("We have {} ggsw", r);
// Copy to Device
let mut d_concatenated_mtree = stream.malloc::<u64>(h_concatenated_ggsw.len() as u32);
unsafe {
stream.copy_to_gpu::<u64>(&mut d_concatenated_mtree, h_concatenated_ggsw.as_slice());
}
let mut d_result = stream.malloc::<u64>(glwe_size as u32);
unsafe {
cuda_cmux_tree_64(
stream.stream_handle().0,
d_result.as_mut_c_ptr(),
d_concatenated_mtree.as_c_ptr(),
d_concatenated_luts.as_c_ptr(),
glwe_dimension.0 as u32,
polynomial_size.0 as u32,
base_log.0 as u32,
level.0 as u32,
r as u32,
stream.get_max_shared_memory().unwrap() as u32,
);
}
let mut h_result = vec![49u64; glwe_size];
unsafe {
stream.copy_to_cpu::<u64>(&mut h_result, &d_result);
}
assert_eq!(h_result.len(), glwe_size);
let glwe_result = GlweCiphertext::from_container(h_result, polynomial_size);
let mut decrypted_result =
PlaintextList::from_container(vec![0_u64; rlwe_sk.polynomial_size().0]);
rlwe_sk.decrypt_glwe(&mut decrypted_result, &glwe_result);
let lut_number =
((*decrypted_result.tensor.first() as f64) / (1u64 << delta_log) as f64).round();
println!("\nresult: {:?}", decrypted_result.tensor.first());
// println!("\nresult: {:?}", decrypted_result.tensor.as_container());
println!("witness : {:?}", witness % (1 << (64 - delta_log)));
println!("lut_number: {}", lut_number);
// println!(
// "lut value : {:?}",
// h_luts[witness as usize]
// );
println!("Done!");
assert_eq!(lut_number as u64, witness % (1 << (64 - delta_log)))
}
#[test]
pub fn test_cuda_extract_bits() {
// Define settings for an insecure toy example
let polynomial_size = PolynomialSize(1024);
let glwe_dimension = GlweDimension(1);
let lwe_dimension = LweDimension(585);
let level_bsk = DecompositionLevelCount(2);
let base_log_bsk = DecompositionBaseLog(7);
let level_ksk = DecompositionLevelCount(2);
let base_log_ksk = DecompositionBaseLog(11);
let std = LogStandardDev::from_log_standard_dev(-60.);
let number_of_bits_of_message_including_padding = 5_usize;
// Tests take about 2-3 seconds on a laptop with this number
let nos: u32 = 1;
let number_of_test_runs = 10;
const UNSAFE_SECRET: u128 = 0;
let mut seeder = UnixSeeder::new(UNSAFE_SECRET);
let mut secret_generator = SecretRandomGenerator::<SoftwareRandomGenerator>::new(seeder.seed());
let mut encryption_generator =
EncryptionRandomGenerator::<SoftwareRandomGenerator>::new(seeder.seed(), &mut seeder);
// allocation and generation of the key in coef domain:
let rlwe_sk: GlweSecretKey<_, Vec<u64>> =
GlweSecretKey::generate_binary(glwe_dimension, polynomial_size, &mut secret_generator);
let lwe_small_sk: LweSecretKey<_, Vec<u64>> =
LweSecretKey::generate_binary(lwe_dimension, &mut secret_generator);
let mut coef_bsk = StandardBootstrapKey::allocate(
0_u64,
glwe_dimension.to_glwe_size(),
polynomial_size,
level_bsk,
base_log_bsk,
lwe_dimension,
);
coef_bsk.fill_with_new_key(&lwe_small_sk, &rlwe_sk, std, &mut encryption_generator);
/*
// allocation for the bootstrapping key
let mut fourier_bsk: FourierBootstrapKey<_, u64> = FourierBootstrapKey::allocate(
Complex64::new(0., 0.),
rlwe_dimension.to_glwe_size(),
polynomial_size,
level_bsk,
base_log_bsk,
lwe_dimension,
);
*/
let mut h_coef_bsk: Vec<u64> = vec![];
let mut h_ksk: Vec<u64> = vec![];
h_coef_bsk.append(&mut coef_bsk.tensor.as_slice().to_vec());
let gpu_index = GpuIndex(0);
let stream = CudaStream::new(gpu_index).unwrap();
let bsk_size = (glwe_dimension.0 + 1)
* (glwe_dimension.0 + 1)
* polynomial_size.0
* level_bsk.0
* lwe_dimension.0;
let ksksize = level_ksk.0 * polynomial_size.0 * (lwe_dimension.0 + 1);
let mut h_lut_vector_indexes = vec![0 as u32; 1];
let mut d_lwe_array_out = stream.malloc::<u64>(
nos * (lwe_dimension.0 as u32 + 1) * (number_of_bits_of_message_including_padding) as u32,
);
let mut d_lwe_array_in = stream.malloc::<u64>(nos * (polynomial_size.0 + 1) as u32);
let mut d_lwe_array_in_buffer = stream.malloc::<u64>(nos * (polynomial_size.0 + 1) as u32);
let mut d_lwe_array_in_shifted_buffer =
stream.malloc::<u64>(nos * (polynomial_size.0 + 1) as u32);
let mut d_lwe_array_out_ks_buffer = stream.malloc::<u64>(nos * (lwe_dimension.0 + 1) as u32);
let mut d_lwe_array_out_pbs_buffer = stream.malloc::<u64>(nos * (polynomial_size.0 + 1) as u32);
let mut d_lut_pbs = stream.malloc::<u64>((2 * polynomial_size.0) as u32);
let mut d_lut_vector_indexes = stream.malloc::<u32>(1);
let mut d_ksk = stream.malloc::<u64>(ksksize as u32);
let mut d_bsk_fourier = stream.malloc::<f64>(bsk_size as u32);
//decomp_size.0 * (output_size.0 + 1) * input_size.0
unsafe {
cuda_initialize_twiddles(polynomial_size.0 as u32, gpu_index.0 as u32);
cuda_convert_lwe_bootstrap_key_64(
d_bsk_fourier.as_mut_c_ptr(),
h_coef_bsk.as_ptr() as *mut c_void,
stream.stream_handle().0,
gpu_index.0 as u32,
lwe_dimension.0 as u32,
glwe_dimension.0 as u32,
level_bsk.0 as u32,
polynomial_size.0 as u32,
);
stream.copy_to_gpu::<u32>(&mut d_lut_vector_indexes, &mut h_lut_vector_indexes);
}
//let mut buffers = FourierBuffers::new(fourier_bsk.polynomial_size(),
// fourier_bsk.glwe_size()); fourier_bsk.fill_with_forward_fourier(&coef_bsk, &mut buffers);
let lwe_big_sk = LweSecretKey::binary_from_container(rlwe_sk.as_tensor().as_slice());
let mut ksk_lwe_big_to_small = LweKeyswitchKey::allocate(
0_u64,
level_ksk,
base_log_ksk,
lwe_big_sk.key_size(),
lwe_small_sk.key_size(),
);
ksk_lwe_big_to_small.fill_with_keyswitch_key(
&lwe_big_sk,
&lwe_small_sk,
std,
&mut encryption_generator,
);
let delta_log = DeltaLog(64 - number_of_bits_of_message_including_padding);
// Decomposer to manage the rounding after decrypting the extracted bit
let decomposer = SignedDecomposer::new(DecompositionBaseLog(1), DecompositionLevelCount(1));
h_ksk.clone_from(&ksk_lwe_big_to_small.into_container());
////////////////////////////////////////////////////////////////////////////////////////////////
use std::time::Instant;
let mut now = Instant::now();
let mut elapsed = now.elapsed();
for _ in 0..number_of_test_runs {
// Generate a random plaintext in [0; 2^{number_of_bits_of_message_including_padding}[
let val = test_tools::random_uint_between(
0..2u64.pow(number_of_bits_of_message_including_padding as u32),
);
// Encryption
let message = Plaintext(val << delta_log.0);
println!("{:?}", message);
let mut lwe_array_in = LweCiphertext::allocate(0u64, LweSize(polynomial_size.0 + 1));
lwe_big_sk.encrypt_lwe(&mut lwe_array_in, &message, std, &mut encryption_generator);
// Bit extraction
// Extract all the bits
let number_values_to_extract = ExtractedBitsCount(64 - delta_log.0);
let mut _lwe_array_out_list = LweList::allocate(
0u64,
lwe_dimension.to_lwe_size(),
CiphertextCount(number_values_to_extract.0),
);
/*
extract_bits(
delta_log,
&mut lwe_array_out_list,
&lwe_array_in,
&ksk_lwe_big_to_small,
&fourier_bsk,
&mut buffers,
number_values_to_extract,
);
*/
unsafe {
stream.copy_to_gpu::<u64>(&mut d_ksk, &mut h_ksk);
//println!("rust_lwe_array_in: {:?}", lwe_array_in);
stream.copy_to_gpu::<u64>(&mut d_lwe_array_in, &mut lwe_array_in.tensor.as_slice());
now = Instant::now();
cuda_extract_bits_64(
stream.stream_handle().0,
d_lwe_array_out.as_mut_c_ptr(),
d_lwe_array_in.as_c_ptr(),
d_lwe_array_in_buffer.as_mut_c_ptr(),
d_lwe_array_in_shifted_buffer.as_mut_c_ptr(),
d_lwe_array_out_ks_buffer.as_mut_c_ptr(),
d_lwe_array_out_pbs_buffer.as_mut_c_ptr(),
d_lut_pbs.as_mut_c_ptr(),
d_lut_vector_indexes.as_mut_c_ptr(),
d_ksk.as_c_ptr(),
d_bsk_fourier.as_c_ptr(),
number_values_to_extract.0 as u32,
delta_log.0 as u32,
polynomial_size.0 as u32,
lwe_dimension.0 as u32,
glwe_dimension.0 as u32,
base_log_bsk.0 as u32,
level_bsk.0 as u32,
base_log_ksk.0 as u32,
level_ksk.0 as u32,
nos,
);
elapsed += now.elapsed();
println!("elapsed: {:?}", elapsed);
let mut h_result = vec![0u64; (lwe_dimension.0 + 1) * number_values_to_extract.0];
stream.copy_to_cpu::<u64>(&mut h_result, &d_lwe_array_out);
cuda_synchronize_device(gpu_index.0 as u32);
let mut i = 0;
for result_h in h_result.chunks(lwe_dimension.0 + 1).rev() {
let result_ct = LweCiphertext::from_container(result_h);
let mut decrypted_message = Plaintext(0_u64);
lwe_small_sk.decrypt_lwe(&mut decrypted_message, &result_ct);
// Round after decryption using decomposer
let decrypted_rounded = decomposer.closest_representable(decrypted_message.0);
// Bring back the extracted bit found in the MSB in the LSB
let decrypted_extract_bit = decrypted_rounded >> 63;
println!("extracted bit : {:?}", decrypted_extract_bit);
println!("{:?}", decrypted_message);
// TODO decomposition algorithm should be changed for keyswitch and amortized pbs.
assert_eq!(
((message.0 >> delta_log.0) >> i) & 1,
decrypted_extract_bit,
"Bit #{}, for plaintext {:#066b}",
delta_log.0 + i,
message.0
);
i += 1;
}
}
}
println!("number of tests: {}", number_of_test_runs);
println!("total_time: {:?}", elapsed);
println!("average time {:?}", elapsed / number_of_test_runs);
}

View File

@@ -1,19 +0,0 @@
#[cfg(feature = "backend_default_generator_x86_64_aesni")]
use concrete_csprng::generators::AesniRandomGenerator;
#[cfg(feature = "backend_default_generator_aarch64_aes")]
use concrete_csprng::generators::NeonAesRandomGenerator;
#[cfg(all(
not(feature = "backend_default_generator_x86_64_aesni"),
not(feature = "backend_default_generator_aarch64_aes")
))]
use concrete_csprng::generators::SoftwareRandomGenerator;
#[cfg(feature = "backend_default_generator_x86_64_aesni")]
pub type ActivatedRandomGenerator = AesniRandomGenerator;
#[cfg(feature = "backend_default_generator_aarch64_aes")]
pub type ActivatedRandomGenerator = NeonAesRandomGenerator;
#[cfg(all(
not(feature = "backend_default_generator_x86_64_aesni"),
not(feature = "backend_default_generator_aarch64_aes")
))]
pub type ActivatedRandomGenerator = SoftwareRandomGenerator;

View File

@@ -1,104 +0,0 @@
use crate::core_crypto::backends::default::implementation::engines::DefaultEngine;
use crate::core_crypto::backends::default::implementation::entities::{Cleartext32, Cleartext64};
use crate::core_crypto::commons::crypto::encoding::Cleartext as ImplCleartext;
use crate::core_crypto::prelude::CleartextF64;
use crate::core_crypto::specification::engines::{CleartextCreationEngine, CleartextCreationError};
/// # Description:
/// Implementation of [`CleartextCreationEngine`] for [`DefaultEngine`] that operates on 32 bits
/// integers.
impl CleartextCreationEngine<u32, Cleartext32> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::*;
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let input: u32 = 3;
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let cleartext: Cleartext32 = engine.create_cleartext_from(&input)?;
/// #
/// # Ok(())
/// # }
/// ```
fn create_cleartext_from(
&mut self,
input: &u32,
) -> Result<Cleartext32, CleartextCreationError<Self::EngineError>> {
Ok(unsafe { self.create_cleartext_from_unchecked(input) })
}
unsafe fn create_cleartext_from_unchecked(&mut self, input: &u32) -> Cleartext32 {
Cleartext32(ImplCleartext(*input))
}
}
/// # Description:
/// Implementation of [`CleartextCreationEngine`] for [`DefaultEngine`] that operates on 64 bits
/// integers.
impl CleartextCreationEngine<u64, Cleartext64> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::*;
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let input: u64 = 3;
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let cleartext: Cleartext64 = engine.create_cleartext_from(&input)?;
/// #
/// # Ok(())
/// # }
/// ```
fn create_cleartext_from(
&mut self,
input: &u64,
) -> Result<Cleartext64, CleartextCreationError<Self::EngineError>> {
Ok(unsafe { self.create_cleartext_from_unchecked(input) })
}
unsafe fn create_cleartext_from_unchecked(&mut self, input: &u64) -> Cleartext64 {
Cleartext64(ImplCleartext(*input))
}
}
/// # Description:
/// Implementation of [`CleartextCreationEngine`] for [`DefaultEngine`] that operates on 64 bits
/// floating point numbers.
impl CleartextCreationEngine<f64, CleartextF64> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::*;
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let input: f64 = 3.;
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let cleartext: CleartextF64 = engine.create_cleartext_from(&input)?;
/// #
/// # Ok(())
/// # }
/// ```
fn create_cleartext_from(
&mut self,
value: &f64,
) -> Result<CleartextF64, CleartextCreationError<Self::EngineError>> {
Ok(unsafe { self.create_cleartext_from_unchecked(value) })
}
unsafe fn create_cleartext_from_unchecked(&mut self, value: &f64) -> CleartextF64 {
CleartextF64(ImplCleartext(*value))
}
}

View File

@@ -1,301 +0,0 @@
use crate::core_crypto::backends::default::implementation::engines::DefaultEngine;
use crate::core_crypto::backends::default::implementation::entities::{
GlweCiphertext32, GlweCiphertext64, GlweCiphertextMutView32, GlweCiphertextMutView64,
GlweCiphertextView32, GlweCiphertextView64,
};
use crate::core_crypto::commons::math::tensor::IntoTensor;
use crate::core_crypto::specification::engines::{
GlweCiphertextConsumingRetrievalEngine, GlweCiphertextConsumingRetrievalError,
};
/// # Description:
/// Implementation of [`GlweCiphertextConsumingRetrievalEngine`] for [`DefaultEngine`] that returns
/// the underlying vec of a [`GlweCiphertext32`] consuming it in the process
impl GlweCiphertextConsumingRetrievalEngine<GlweCiphertext32, Vec<u32>> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{GlweSize, PolynomialSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let glwe_size = GlweSize(600);
/// let polynomial_size = PolynomialSize(1024);
///
/// // You have to make sure you size the container properly
/// let mut owned_container = vec![0_u32; glwe_size.0 * polynomial_size.0];
/// let original_vec_ptr = owned_container.as_ptr();
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext: GlweCiphertext32 =
/// engine.create_glwe_ciphertext_from(owned_container, polynomial_size)?;
/// let retrieved_container = engine.consume_retrieve_glwe_ciphertext(ciphertext)?;
/// assert_eq!(original_vec_ptr, retrieved_container.as_ptr());
/// #
/// # Ok(())
/// # }
/// ```
fn consume_retrieve_glwe_ciphertext(
&mut self,
ciphertext: GlweCiphertext32,
) -> Result<Vec<u32>, GlweCiphertextConsumingRetrievalError<Self::EngineError>> {
Ok(unsafe { self.consume_retrieve_glwe_ciphertext_unchecked(ciphertext) })
}
unsafe fn consume_retrieve_glwe_ciphertext_unchecked(
&mut self,
ciphertext: GlweCiphertext32,
) -> Vec<u32> {
ciphertext.0.into_tensor().into_container()
}
}
/// # Description:
/// Implementation of [`GlweCiphertextConsumingRetrievalEngine`] for [`DefaultEngine`] that returns
/// the underlying vec of a [`GlweCiphertext64`] consuming it in the process
impl GlweCiphertextConsumingRetrievalEngine<GlweCiphertext64, Vec<u64>> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{GlweSize, PolynomialSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let glwe_size = GlweSize(600);
/// let polynomial_size = PolynomialSize(1024);
///
/// // You have to make sure you size the container properly
/// let mut owned_container = vec![0_u64; glwe_size.0 * polynomial_size.0];
/// let original_vec_ptr = owned_container.as_ptr();
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext: GlweCiphertext64 =
/// engine.create_glwe_ciphertext_from(owned_container, polynomial_size)?;
/// let retrieved_container = engine.consume_retrieve_glwe_ciphertext(ciphertext)?;
/// assert_eq!(original_vec_ptr, retrieved_container.as_ptr());
/// #
/// # Ok(())
/// # }
/// ```
fn consume_retrieve_glwe_ciphertext(
&mut self,
ciphertext: GlweCiphertext64,
) -> Result<Vec<u64>, GlweCiphertextConsumingRetrievalError<Self::EngineError>> {
Ok(unsafe { self.consume_retrieve_glwe_ciphertext_unchecked(ciphertext) })
}
unsafe fn consume_retrieve_glwe_ciphertext_unchecked(
&mut self,
ciphertext: GlweCiphertext64,
) -> Vec<u64> {
ciphertext.0.into_tensor().into_container()
}
}
/// # Description:
/// Implementation of [`GlweCiphertextConsumingRetrievalEngine`] for [`DefaultEngine`] that returns
/// the underlying slice of a [`GlweCiphertextView32`] consuming it in the process
impl<'data> GlweCiphertextConsumingRetrievalEngine<GlweCiphertextView32<'data>, &'data [u32]>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{GlweSize, PolynomialSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let glwe_size = GlweSize(600);
/// let polynomial_size = PolynomialSize(1024);
///
/// // You have to make sure you size the container properly
/// let mut owned_container = vec![0_u32; glwe_size.0 * polynomial_size.0];
///
/// let slice = &owned_container[..];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: GlweCiphertextView32 =
/// engine.create_glwe_ciphertext_from(slice, polynomial_size)?;
/// let retrieved_slice = engine.consume_retrieve_glwe_ciphertext(ciphertext_view)?;
/// assert_eq!(slice, retrieved_slice);
/// #
/// # Ok(())
/// # }
/// ```
fn consume_retrieve_glwe_ciphertext(
&mut self,
ciphertext: GlweCiphertextView32<'data>,
) -> Result<&'data [u32], GlweCiphertextConsumingRetrievalError<Self::EngineError>> {
Ok(unsafe { self.consume_retrieve_glwe_ciphertext_unchecked(ciphertext) })
}
unsafe fn consume_retrieve_glwe_ciphertext_unchecked(
&mut self,
ciphertext: GlweCiphertextView32<'data>,
) -> &'data [u32] {
ciphertext.0.into_tensor().into_container()
}
}
/// # Description:
/// Implementation of [`GlweCiphertextConsumingRetrievalEngine`] for [`DefaultEngine`] that returns
/// the underlying slice of a [`GlweCiphertextMutView32`] consuming it in the process
impl<'data> GlweCiphertextConsumingRetrievalEngine<GlweCiphertextMutView32<'data>, &'data mut [u32]>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{GlweSize, PolynomialSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let glwe_size = GlweSize(600);
/// let polynomial_size = PolynomialSize(1024);
///
/// // You have to make sure you size the container properly
/// let mut owned_container = vec![0_u32; glwe_size.0 * polynomial_size.0];
///
/// let slice = &mut owned_container[..];
/// // Required as we can't borrow a mut slice more than once
/// let underlying_ptr = slice.as_ptr();
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: GlweCiphertextMutView32 =
/// engine.create_glwe_ciphertext_from(slice, polynomial_size)?;
/// let retrieved_slice = engine.consume_retrieve_glwe_ciphertext(ciphertext_view)?;
/// assert_eq!(underlying_ptr, retrieved_slice.as_ptr());
/// #
/// # Ok(())
/// # }
/// ```
fn consume_retrieve_glwe_ciphertext(
&mut self,
ciphertext: GlweCiphertextMutView32<'data>,
) -> Result<&'data mut [u32], GlweCiphertextConsumingRetrievalError<Self::EngineError>> {
Ok(unsafe { self.consume_retrieve_glwe_ciphertext_unchecked(ciphertext) })
}
unsafe fn consume_retrieve_glwe_ciphertext_unchecked(
&mut self,
ciphertext: GlweCiphertextMutView32<'data>,
) -> &'data mut [u32] {
ciphertext.0.into_tensor().into_container()
}
}
/// # Description:
/// Implementation of [`GlweCiphertextConsumingRetrievalEngine`] for [`DefaultEngine`] that returns
/// the underlying slice of a [`GlweCiphertextView64`] consuming it in the process
impl<'data> GlweCiphertextConsumingRetrievalEngine<GlweCiphertextView64<'data>, &'data [u64]>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{GlweSize, PolynomialSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let glwe_size = GlweSize(600);
/// let polynomial_size = PolynomialSize(1024);
///
/// // You have to make sure you size the container properly
/// let mut owned_container = vec![0_u64; glwe_size.0 * polynomial_size.0];
///
/// let slice = &owned_container[..];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: GlweCiphertextView64 =
/// engine.create_glwe_ciphertext_from(slice, polynomial_size)?;
/// let retrieved_slice = engine.consume_retrieve_glwe_ciphertext(ciphertext_view)?;
/// assert_eq!(slice, retrieved_slice);
/// #
/// # Ok(())
/// # }
/// ```
fn consume_retrieve_glwe_ciphertext(
&mut self,
ciphertext: GlweCiphertextView64<'data>,
) -> Result<&'data [u64], GlweCiphertextConsumingRetrievalError<Self::EngineError>> {
Ok(unsafe { self.consume_retrieve_glwe_ciphertext_unchecked(ciphertext) })
}
unsafe fn consume_retrieve_glwe_ciphertext_unchecked(
&mut self,
ciphertext: GlweCiphertextView64<'data>,
) -> &'data [u64] {
ciphertext.0.into_tensor().into_container()
}
}
/// # Description:
/// Implementation of [`GlweCiphertextConsumingRetrievalEngine`] for [`DefaultEngine`] that returns
/// the underlying slice of a [`GlweCiphertextMutView64`] consuming it in the process
impl<'data> GlweCiphertextConsumingRetrievalEngine<GlweCiphertextMutView64<'data>, &'data mut [u64]>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{GlweSize, PolynomialSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let glwe_size = GlweSize(600);
/// let polynomial_size = PolynomialSize(1024);
///
/// // You have to make sure you size the container properly
/// let mut owned_container = vec![0_u64; glwe_size.0 * polynomial_size.0];
///
/// let slice = &mut owned_container[..];
/// // Required as we can't borrow a mut slice more than once
/// let underlying_ptr = slice.as_ptr();
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: GlweCiphertextMutView64 =
/// engine.create_glwe_ciphertext_from(slice, polynomial_size)?;
/// let retrieved_slice = engine.consume_retrieve_glwe_ciphertext(ciphertext_view)?;
/// assert_eq!(underlying_ptr, retrieved_slice.as_ptr());
/// #
/// # Ok(())
/// # }
/// ```
fn consume_retrieve_glwe_ciphertext(
&mut self,
ciphertext: GlweCiphertextMutView64<'data>,
) -> Result<&'data mut [u64], GlweCiphertextConsumingRetrievalError<Self::EngineError>> {
Ok(unsafe { self.consume_retrieve_glwe_ciphertext_unchecked(ciphertext) })
}
unsafe fn consume_retrieve_glwe_ciphertext_unchecked(
&mut self,
ciphertext: GlweCiphertextMutView64<'data>,
) -> &'data mut [u64] {
ciphertext.0.into_tensor().into_container()
}
}

View File

@@ -1,340 +0,0 @@
use crate::core_crypto::backends::default::implementation::engines::DefaultEngine;
use crate::core_crypto::backends::default::implementation::entities::{
GlweCiphertext32, GlweCiphertext64, GlweCiphertextMutView32, GlweCiphertextMutView64,
GlweCiphertextView32, GlweCiphertextView64,
};
use crate::core_crypto::commons::crypto::glwe::GlweCiphertext as ImplGlweCiphertext;
use crate::core_crypto::prelude::PolynomialSize;
use crate::core_crypto::specification::engines::{
GlweCiphertextCreationEngine, GlweCiphertextCreationError,
};
/// # Description:
/// Implementation of [`GlweCiphertextCreationEngine`] for [`DefaultEngine`] which returns a
/// [`GlweCiphertext32`].
impl GlweCiphertextCreationEngine<Vec<u32>, GlweCiphertext32> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{GlweSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let glwe_size = GlweSize(600);
/// let polynomial_size = PolynomialSize(1024);
///
/// // You have to make sure you size the container properly
/// let owned_container = vec![0_u32; glwe_size.0 * polynomial_size.0];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext: GlweCiphertext32 =
/// engine.create_glwe_ciphertext_from(owned_container, polynomial_size)?;
/// #
/// # Ok(())
/// # }
/// ```
fn create_glwe_ciphertext_from(
&mut self,
container: Vec<u32>,
polynomial_size: PolynomialSize,
) -> Result<GlweCiphertext32, GlweCiphertextCreationError<Self::EngineError>> {
GlweCiphertextCreationError::<Self::EngineError>::perform_generic_checks(
container.len(),
polynomial_size,
)?;
Ok(unsafe { self.create_glwe_ciphertext_from_unchecked(container, polynomial_size) })
}
unsafe fn create_glwe_ciphertext_from_unchecked(
&mut self,
container: Vec<u32>,
polynomial_size: PolynomialSize,
) -> GlweCiphertext32 {
GlweCiphertext32(ImplGlweCiphertext::from_container(
container,
polynomial_size,
))
}
}
/// # Description:
/// Implementation of [`GlweCiphertextCreationEngine`] for [`DefaultEngine`] which returns a
/// [`GlweCiphertext64`].
impl GlweCiphertextCreationEngine<Vec<u64>, GlweCiphertext64> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{GlweSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let glwe_size = GlweSize(600);
/// let polynomial_size = PolynomialSize(1024);
///
/// // You have to make sure you size the container properly
/// let owned_container = vec![0_u64; glwe_size.0 * polynomial_size.0];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext: GlweCiphertext64 =
/// engine.create_glwe_ciphertext_from(owned_container, polynomial_size)?;
/// #
/// # Ok(())
/// # }
/// ```
fn create_glwe_ciphertext_from(
&mut self,
container: Vec<u64>,
polynomial_size: PolynomialSize,
) -> Result<GlweCiphertext64, GlweCiphertextCreationError<Self::EngineError>> {
GlweCiphertextCreationError::<Self::EngineError>::perform_generic_checks(
container.len(),
polynomial_size,
)?;
Ok(unsafe { self.create_glwe_ciphertext_from_unchecked(container, polynomial_size) })
}
unsafe fn create_glwe_ciphertext_from_unchecked(
&mut self,
container: Vec<u64>,
polynomial_size: PolynomialSize,
) -> GlweCiphertext64 {
GlweCiphertext64(ImplGlweCiphertext::from_container(
container,
polynomial_size,
))
}
}
/// # Description:
/// Implementation of [`GlweCiphertextCreationEngine`] for [`DefaultEngine`] which returns an
/// immutable [`GlweCiphertextView32`] that does not own its memory.
impl<'data> GlweCiphertextCreationEngine<&'data [u32], GlweCiphertextView32<'data>>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{GlweSize, PolynomialSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let glwe_size = GlweSize(600);
/// let polynomial_size = PolynomialSize(1024);
///
/// // You have to make sure you size the container properly
/// let mut owned_container = vec![0_u32; glwe_size.0 * polynomial_size.0];
///
/// let slice = &owned_container[..];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: GlweCiphertextView32 =
/// engine.create_glwe_ciphertext_from(slice, polynomial_size)?;
/// #
/// # Ok(())
/// # }
/// ```
fn create_glwe_ciphertext_from(
&mut self,
container: &'data [u32],
polynomial_size: PolynomialSize,
) -> Result<GlweCiphertextView32<'data>, GlweCiphertextCreationError<Self::EngineError>> {
GlweCiphertextCreationError::<Self::EngineError>::perform_generic_checks(
container.len(),
polynomial_size,
)?;
Ok(unsafe { self.create_glwe_ciphertext_from_unchecked(container, polynomial_size) })
}
unsafe fn create_glwe_ciphertext_from_unchecked(
&mut self,
container: &'data [u32],
polynomial_size: PolynomialSize,
) -> GlweCiphertextView32<'data> {
GlweCiphertextView32(ImplGlweCiphertext::from_container(
container,
polynomial_size,
))
}
}
/// # Description:
/// Implementation of [`GlweCiphertextCreationEngine`] for [`DefaultEngine`] which returns a mutable
/// [`GlweCiphertextMutView32`] that does not own its memory.
impl<'data> GlweCiphertextCreationEngine<&'data mut [u32], GlweCiphertextMutView32<'data>>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{GlweSize, PolynomialSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let glwe_size = GlweSize(600);
/// let polynomial_size = PolynomialSize(1024);
///
/// // You have to make sure you size the container properly
/// let mut owned_container = vec![0_u32; glwe_size.0 * polynomial_size.0];
///
/// let slice = &mut owned_container[..];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: GlweCiphertextMutView32 =
/// engine.create_glwe_ciphertext_from(slice, polynomial_size)?;
/// #
/// # Ok(())
/// # }
/// ```
fn create_glwe_ciphertext_from(
&mut self,
container: &'data mut [u32],
polynomial_size: PolynomialSize,
) -> Result<GlweCiphertextMutView32<'data>, GlweCiphertextCreationError<Self::EngineError>>
{
GlweCiphertextCreationError::<Self::EngineError>::perform_generic_checks(
container.len(),
polynomial_size,
)?;
Ok(unsafe { self.create_glwe_ciphertext_from_unchecked(container, polynomial_size) })
}
unsafe fn create_glwe_ciphertext_from_unchecked(
&mut self,
container: &'data mut [u32],
polynomial_size: PolynomialSize,
) -> GlweCiphertextMutView32<'data> {
GlweCiphertextMutView32(ImplGlweCiphertext::from_container(
container,
polynomial_size,
))
}
}
/// # Description:
/// Implementation of [`GlweCiphertextCreationEngine`] for [`DefaultEngine`] which returns an
/// immutable [`GlweCiphertextView64`] that does not own its memory.
impl<'data> GlweCiphertextCreationEngine<&'data [u64], GlweCiphertextView64<'data>>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{PolynomialSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let glwe_size = 600_usize;
/// let polynomial_size = PolynomialSize(1024);
///
/// // You have to make sure you size the container properly
/// let mut owned_container = vec![0_u64; (glwe_size + 1) * polynomial_size.0];
///
/// let slice = &owned_container[..];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: GlweCiphertextView64 =
/// engine.create_glwe_ciphertext_from(slice, polynomial_size)?;
/// #
/// # Ok(())
/// # }
/// ```
fn create_glwe_ciphertext_from(
&mut self,
container: &'data [u64],
polynomial_size: PolynomialSize,
) -> Result<GlweCiphertextView64<'data>, GlweCiphertextCreationError<Self::EngineError>> {
GlweCiphertextCreationError::<Self::EngineError>::perform_generic_checks(
container.len(),
polynomial_size,
)?;
Ok(unsafe { self.create_glwe_ciphertext_from_unchecked(container, polynomial_size) })
}
unsafe fn create_glwe_ciphertext_from_unchecked(
&mut self,
container: &'data [u64],
polynomial_size: PolynomialSize,
) -> GlweCiphertextView64<'data> {
GlweCiphertextView64(ImplGlweCiphertext::from_container(
container,
polynomial_size,
))
}
}
/// # Description:
/// Implementation of [`GlweCiphertextCreationEngine`] for [`DefaultEngine`] which returns a mutable
/// [`GlweCiphertextMutView64`] that does not own its memory.
impl<'data> GlweCiphertextCreationEngine<&'data mut [u64], GlweCiphertextMutView64<'data>>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{GlweSize, PolynomialSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let glwe_size = GlweSize(600);
/// let polynomial_size = PolynomialSize(1024);
///
/// // You have to make sure you size the container properly
/// let mut owned_container = vec![0_u64; glwe_size.0 * polynomial_size.0];
///
/// let slice = &mut owned_container[..];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: GlweCiphertextMutView64 =
/// engine.create_glwe_ciphertext_from(slice, polynomial_size)?;
/// #
/// # Ok(())
/// # }
/// ```
fn create_glwe_ciphertext_from(
&mut self,
container: &'data mut [u64],
polynomial_size: PolynomialSize,
) -> Result<GlweCiphertextMutView64<'data>, GlweCiphertextCreationError<Self::EngineError>>
{
GlweCiphertextCreationError::<Self::EngineError>::perform_generic_checks(
container.len(),
polynomial_size,
)?;
Ok(unsafe { self.create_glwe_ciphertext_from_unchecked(container, polynomial_size) })
}
unsafe fn create_glwe_ciphertext_from_unchecked(
&mut self,
container: &'data mut [u64],
polynomial_size: PolynomialSize,
) -> GlweCiphertextMutView64<'data> {
GlweCiphertextMutView64(ImplGlweCiphertext::from_container(
container,
polynomial_size,
))
}
}

View File

@@ -1,105 +0,0 @@
use crate::core_crypto::prelude::GlweSize;
use crate::core_crypto::backends::default::entities::{
GlweCiphertext32, GlweCiphertext64, PlaintextVector32, PlaintextVector64,
};
use crate::core_crypto::commons::crypto::glwe::GlweCiphertext as ImplGlweCiphertext;
use crate::core_crypto::specification::engines::{
GlweCiphertextTrivialEncryptionEngine, GlweCiphertextTrivialEncryptionError,
};
use crate::core_crypto::backends::default::engines::DefaultEngine;
impl GlweCiphertextTrivialEncryptionEngine<PlaintextVector32, GlweCiphertext32> for DefaultEngine {
/// # Example:
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
///
/// use tfhe::core_crypto::prelude::{GlweDimension, PolynomialSize, Variance, *};
///
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let glwe_dimension = GlweDimension(2);
/// let polynomial_size = PolynomialSize(4);
/// let input = vec![3_u32 << 20; polynomial_size.0];
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let plaintext_vector: PlaintextVector32 = engine.create_plaintext_vector_from(&input)?;
/// // DISCLAIMER: trivial encryption is NOT secure, and DOES NOT hide the message at all.
/// let ciphertext: GlweCiphertext32 = engine
/// .trivially_encrypt_glwe_ciphertext(glwe_dimension.to_glwe_size(), &plaintext_vector)?;
///
/// assert_eq!(ciphertext.glwe_dimension(), glwe_dimension);
/// assert_eq!(ciphertext.polynomial_size(), polynomial_size);
///
/// # Ok(())
/// # }
/// ```
fn trivially_encrypt_glwe_ciphertext(
&mut self,
glwe_size: GlweSize,
input: &PlaintextVector32,
) -> Result<GlweCiphertext32, GlweCiphertextTrivialEncryptionError<Self::EngineError>> {
unsafe { Ok(self.trivially_encrypt_glwe_ciphertext_unchecked(glwe_size, input)) }
}
unsafe fn trivially_encrypt_glwe_ciphertext_unchecked(
&mut self,
glwe_size: GlweSize,
input: &PlaintextVector32,
) -> GlweCiphertext32 {
let ciphertext: ImplGlweCiphertext<Vec<u32>> =
ImplGlweCiphertext::new_trivial_encryption(glwe_size, &input.0);
GlweCiphertext32(ciphertext)
}
}
impl GlweCiphertextTrivialEncryptionEngine<PlaintextVector64, GlweCiphertext64> for DefaultEngine {
/// # Example:
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
///
/// use tfhe::core_crypto::prelude::{GlweDimension, PolynomialSize, Variance, *};
///
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let glwe_dimension = GlweDimension(2);
/// let polynomial_size = PolynomialSize(4);
/// let input = vec![3_u64 << 20; polynomial_size.0];
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let plaintext_vector: PlaintextVector64 = engine.create_plaintext_vector_from(&input)?;
/// // DISCLAIMER: trivial encryption is NOT secure, and DOES NOT hide the message at all.
/// let ciphertext: GlweCiphertext64 = engine
/// .trivially_encrypt_glwe_ciphertext(glwe_dimension.to_glwe_size(), &plaintext_vector)?;
///
/// assert_eq!(ciphertext.glwe_dimension(), glwe_dimension);
/// assert_eq!(ciphertext.polynomial_size(), polynomial_size);
///
/// # Ok(())
/// # }
/// ```
fn trivially_encrypt_glwe_ciphertext(
&mut self,
glwe_size: GlweSize,
input: &PlaintextVector64,
) -> Result<GlweCiphertext64, GlweCiphertextTrivialEncryptionError<Self::EngineError>> {
unsafe { Ok(self.trivially_encrypt_glwe_ciphertext_unchecked(glwe_size, input)) }
}
unsafe fn trivially_encrypt_glwe_ciphertext_unchecked(
&mut self,
glwe_size: GlweSize,
input: &PlaintextVector64,
) -> GlweCiphertext64 {
let ciphertext: ImplGlweCiphertext<Vec<u64>> =
ImplGlweCiphertext::new_trivial_encryption(glwe_size, &input.0);
GlweCiphertext64(ciphertext)
}
}

View File

@@ -1,110 +0,0 @@
use crate::core_crypto::prelude::{GlweDimension, PolynomialSize};
use crate::core_crypto::backends::default::implementation::engines::DefaultEngine;
use crate::core_crypto::backends::default::implementation::entities::{
GlweSecretKey32, GlweSecretKey64,
};
use crate::core_crypto::commons::crypto::secret::GlweSecretKey as ImplGlweSecretKey;
use crate::core_crypto::specification::engines::{
GlweSecretKeyGenerationEngine, GlweSecretKeyGenerationError,
};
/// # Description:
/// Implementation of [`GlweSecretKeyGenerationEngine`] for [`DefaultEngine`] that operates on
/// 32 bits integers.
impl GlweSecretKeyGenerationEngine<GlweSecretKey32> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{GlweDimension, PolynomialSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let glwe_dimension = GlweDimension(2);
/// let polynomial_size = PolynomialSize(4);
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let glwe_secret_key: GlweSecretKey32 =
/// engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
/// #
/// assert_eq!(glwe_secret_key.glwe_dimension(), glwe_dimension);
/// assert_eq!(glwe_secret_key.polynomial_size(), polynomial_size);
///
/// #
/// # Ok(())
/// # }
/// ```
fn generate_new_glwe_secret_key(
&mut self,
glwe_dimension: GlweDimension,
polynomial_size: PolynomialSize,
) -> Result<GlweSecretKey32, GlweSecretKeyGenerationError<Self::EngineError>> {
GlweSecretKeyGenerationError::perform_generic_checks(glwe_dimension, polynomial_size)?;
Ok(unsafe { self.generate_new_glwe_secret_key_unchecked(glwe_dimension, polynomial_size) })
}
unsafe fn generate_new_glwe_secret_key_unchecked(
&mut self,
glwe_dimension: GlweDimension,
polynomial_size: PolynomialSize,
) -> GlweSecretKey32 {
GlweSecretKey32(ImplGlweSecretKey::generate_binary(
glwe_dimension,
polynomial_size,
&mut self.secret_generator,
))
}
}
/// # Description:
/// Implementation of [`GlweSecretKeyGenerationEngine`] for [`DefaultEngine`] that operates on
/// 64 bits integers.
impl GlweSecretKeyGenerationEngine<GlweSecretKey64> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{GlweDimension, PolynomialSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let glwe_dimension = GlweDimension(2);
/// let polynomial_size = PolynomialSize(4);
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let glwe_secret_key: GlweSecretKey64 =
/// engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
/// #
/// assert_eq!(glwe_secret_key.glwe_dimension(), glwe_dimension);
/// assert_eq!(glwe_secret_key.polynomial_size(), polynomial_size);
///
/// #
/// # Ok(())
/// # }
/// ```
fn generate_new_glwe_secret_key(
&mut self,
glwe_dimension: GlweDimension,
polynomial_size: PolynomialSize,
) -> Result<GlweSecretKey64, GlweSecretKeyGenerationError<Self::EngineError>> {
GlweSecretKeyGenerationError::perform_generic_checks(glwe_dimension, polynomial_size)?;
Ok(unsafe { self.generate_new_glwe_secret_key_unchecked(glwe_dimension, polynomial_size) })
}
unsafe fn generate_new_glwe_secret_key_unchecked(
&mut self,
glwe_dimension: GlweDimension,
polynomial_size: PolynomialSize,
) -> GlweSecretKey64 {
GlweSecretKey64(ImplGlweSecretKey::generate_binary(
glwe_dimension,
polynomial_size,
&mut self.secret_generator,
))
}
}

View File

@@ -1,91 +0,0 @@
use crate::core_crypto::backends::default::engines::DefaultEngine;
use crate::core_crypto::backends::default::entities::{
GlweSecretKey32, GlweSecretKey64, LweSecretKey32, LweSecretKey64,
};
use crate::core_crypto::specification::engines::{
GlweToLweSecretKeyTransformationEngine, GlweToLweSecretKeyTransformationError,
};
impl GlweToLweSecretKeyTransformationEngine<GlweSecretKey32, LweSecretKey32> for DefaultEngine {
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use tfhe::core_crypto::prelude::{GlweDimension, LweDimension, PolynomialSize, *};
///
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let glwe_dimension = GlweDimension(2);
/// let polynomial_size = PolynomialSize(4);
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
///
/// let glwe_secret_key: GlweSecretKey32 =
/// engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
/// assert_eq!(glwe_secret_key.glwe_dimension(), glwe_dimension);
/// assert_eq!(glwe_secret_key.polynomial_size(), polynomial_size);
///
/// let lwe_secret_key = engine.transform_glwe_secret_key_to_lwe_secret_key(glwe_secret_key)?;
/// assert_eq!(lwe_secret_key.lwe_dimension(), LweDimension(8));
///
/// # Ok(())
/// # }
/// ```
fn transform_glwe_secret_key_to_lwe_secret_key(
&mut self,
glwe_secret_key: GlweSecretKey32,
) -> Result<LweSecretKey32, GlweToLweSecretKeyTransformationError<Self::EngineError>> {
Ok(unsafe { self.transform_glwe_secret_key_to_lwe_secret_key_unchecked(glwe_secret_key) })
}
unsafe fn transform_glwe_secret_key_to_lwe_secret_key_unchecked(
&mut self,
glwe_secret_key: GlweSecretKey32,
) -> LweSecretKey32 {
LweSecretKey32(glwe_secret_key.0.into_lwe_secret_key())
}
}
impl GlweToLweSecretKeyTransformationEngine<GlweSecretKey64, LweSecretKey64> for DefaultEngine {
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use tfhe::core_crypto::prelude::{GlweDimension, LweDimension, PolynomialSize, *};
///
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let glwe_dimension = GlweDimension(2);
/// let polynomial_size = PolynomialSize(4);
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
///
/// let glwe_secret_key: GlweSecretKey64 =
/// engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
/// assert_eq!(glwe_secret_key.glwe_dimension(), glwe_dimension);
/// assert_eq!(glwe_secret_key.polynomial_size(), polynomial_size);
///
/// let lwe_secret_key = engine.transform_glwe_secret_key_to_lwe_secret_key(glwe_secret_key)?;
/// assert_eq!(lwe_secret_key.lwe_dimension(), LweDimension(8));
///
/// # Ok(())
/// # }
/// ```
fn transform_glwe_secret_key_to_lwe_secret_key(
&mut self,
glwe_secret_key: GlweSecretKey64,
) -> Result<LweSecretKey64, GlweToLweSecretKeyTransformationError<Self::EngineError>> {
Ok(unsafe { self.transform_glwe_secret_key_to_lwe_secret_key_unchecked(glwe_secret_key) })
}
unsafe fn transform_glwe_secret_key_to_lwe_secret_key_unchecked(
&mut self,
glwe_secret_key: GlweSecretKey64,
) -> LweSecretKey64 {
LweSecretKey64(glwe_secret_key.0.into_lwe_secret_key())
}
}

View File

@@ -1,192 +0,0 @@
use crate::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount, Variance};
use crate::core_crypto::backends::default::implementation::engines::DefaultEngine;
use crate::core_crypto::backends::default::implementation::entities::{
GlweSecretKey32, GlweSecretKey64, LweBootstrapKey32, LweBootstrapKey64, LweSecretKey32,
LweSecretKey64,
};
use crate::core_crypto::commons::crypto::bootstrap::StandardBootstrapKey as ImplStandardBootstrapKey;
use crate::core_crypto::prelude::{GlweSecretKeyEntity, LweSecretKeyEntity};
use crate::core_crypto::specification::engines::{
LweBootstrapKeyGenerationEngine, LweBootstrapKeyGenerationError,
};
/// # Description:
/// Implementation of [`LweBootstrapKeyGenerationEngine`] for [`DefaultEngine`] that operates on
/// 32 bits integers. It outputs a bootstrap key in the standard domain.
impl LweBootstrapKeyGenerationEngine<LweSecretKey32, GlweSecretKey32, LweBootstrapKey32>
for DefaultEngine
{
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::{
/// DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
/// Variance, *,
/// };
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let (lwe_dim, glwe_dim, poly_size) = (LweDimension(4), GlweDimension(6), PolynomialSize(256));
/// let (dec_lc, dec_bl) = (DecompositionLevelCount(3), DecompositionBaseLog(5));
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let lwe_sk: LweSecretKey32 = engine.generate_new_lwe_secret_key(lwe_dim)?;
/// let glwe_sk: GlweSecretKey32 = engine.generate_new_glwe_secret_key(glwe_dim, poly_size)?;
///
/// let bsk: LweBootstrapKey32 =
/// engine.generate_new_lwe_bootstrap_key(&lwe_sk, &glwe_sk, dec_bl, dec_lc, noise)?;
/// #
/// assert_eq!(bsk.glwe_dimension(), glwe_dim);
/// assert_eq!(bsk.polynomial_size(), poly_size);
/// assert_eq!(bsk.input_lwe_dimension(), lwe_dim);
/// assert_eq!(bsk.decomposition_base_log(), dec_bl);
/// assert_eq!(bsk.decomposition_level_count(), dec_lc);
///
/// #
/// # Ok(())
/// # }
/// ```
fn generate_new_lwe_bootstrap_key(
&mut self,
input_key: &LweSecretKey32,
output_key: &GlweSecretKey32,
decomposition_base_log: DecompositionBaseLog,
decomposition_level_count: DecompositionLevelCount,
noise: Variance,
) -> Result<LweBootstrapKey32, LweBootstrapKeyGenerationError<Self::EngineError>> {
LweBootstrapKeyGenerationError::perform_generic_checks(
decomposition_base_log,
decomposition_level_count,
32,
)?;
Ok(unsafe {
self.generate_new_lwe_bootstrap_key_unchecked(
input_key,
output_key,
decomposition_base_log,
decomposition_level_count,
noise,
)
})
}
unsafe fn generate_new_lwe_bootstrap_key_unchecked(
&mut self,
input_key: &LweSecretKey32,
output_key: &GlweSecretKey32,
decomposition_base_log: DecompositionBaseLog,
decomposition_level_count: DecompositionLevelCount,
noise: Variance,
) -> LweBootstrapKey32 {
let mut key = ImplStandardBootstrapKey::allocate(
0,
output_key.glwe_dimension().to_glwe_size(),
output_key.polynomial_size(),
decomposition_level_count,
decomposition_base_log,
input_key.lwe_dimension(),
);
key.fill_with_new_key(
&input_key.0,
&output_key.0,
noise,
&mut self.encryption_generator,
);
LweBootstrapKey32(key)
}
}
/// # Description:
/// Implementation of [`LweBootstrapKeyGenerationEngine`] for [`DefaultEngine`] that operates on
/// 64 bits integers. It outputs a bootstrap key in the standard domain.
impl LweBootstrapKeyGenerationEngine<LweSecretKey64, GlweSecretKey64, LweBootstrapKey64>
for DefaultEngine
{
/// # Example
/// ```
/// use tfhe::core_crypto::prelude::{
/// DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
/// Variance, *,
/// };
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let (lwe_dim, glwe_dim, poly_size) = (LweDimension(4), GlweDimension(6), PolynomialSize(256));
/// let (dec_lc, dec_bl) = (DecompositionLevelCount(3), DecompositionBaseLog(5));
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let lwe_sk: LweSecretKey64 = engine.generate_new_lwe_secret_key(lwe_dim)?;
/// let glwe_sk: GlweSecretKey64 = engine.generate_new_glwe_secret_key(glwe_dim, poly_size)?;
///
/// let bsk: LweBootstrapKey64 =
/// engine.generate_new_lwe_bootstrap_key(&lwe_sk, &glwe_sk, dec_bl, dec_lc, noise)?;
/// #
/// assert_eq!(bsk.glwe_dimension(), glwe_dim);
/// assert_eq!(bsk.polynomial_size(), poly_size);
/// assert_eq!(bsk.input_lwe_dimension(), lwe_dim);
/// assert_eq!(bsk.decomposition_base_log(), dec_bl);
/// assert_eq!(bsk.decomposition_level_count(), dec_lc);
///
/// #
/// # Ok(())
/// # }
/// ```
fn generate_new_lwe_bootstrap_key(
&mut self,
input_key: &LweSecretKey64,
output_key: &GlweSecretKey64,
decomposition_base_log: DecompositionBaseLog,
decomposition_level_count: DecompositionLevelCount,
noise: Variance,
) -> Result<LweBootstrapKey64, LweBootstrapKeyGenerationError<Self::EngineError>> {
LweBootstrapKeyGenerationError::perform_generic_checks(
decomposition_base_log,
decomposition_level_count,
64,
)?;
Ok(unsafe {
self.generate_new_lwe_bootstrap_key_unchecked(
input_key,
output_key,
decomposition_base_log,
decomposition_level_count,
noise,
)
})
}
unsafe fn generate_new_lwe_bootstrap_key_unchecked(
&mut self,
input_key: &LweSecretKey64,
output_key: &GlweSecretKey64,
decomposition_base_log: DecompositionBaseLog,
decomposition_level_count: DecompositionLevelCount,
noise: Variance,
) -> LweBootstrapKey64 {
let mut key = ImplStandardBootstrapKey::allocate(
0,
output_key.glwe_dimension().to_glwe_size(),
output_key.polynomial_size(),
decomposition_level_count,
decomposition_base_log,
input_key.lwe_dimension(),
);
key.fill_with_new_key(
&input_key.0,
&output_key.0,
noise,
&mut self.encryption_generator,
);
LweBootstrapKey64(key)
}
}

View File

@@ -1,116 +0,0 @@
use crate::core_crypto::backends::default::implementation::engines::DefaultEngine;
use crate::core_crypto::backends::default::implementation::entities::{
Cleartext32, Cleartext64, LweCiphertext32, LweCiphertext64,
};
use crate::core_crypto::specification::engines::{
LweCiphertextCleartextFusingMultiplicationEngine,
LweCiphertextCleartextFusingMultiplicationError,
};
/// # Description:
/// Implementation of [`LweCiphertextCleartextFusingMultiplicationEngine`] for [`DefaultEngine`]
/// that operates on 32 bits integers.
impl LweCiphertextCleartextFusingMultiplicationEngine<LweCiphertext32, Cleartext32>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(2);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = 3_u32 << 20;
/// let cleartext_input = 12_u32;
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let cleartext: Cleartext32 = engine.create_cleartext_from(&cleartext_input)?;
/// let key: LweSecretKey32 = engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let plaintext = engine.create_plaintext_from(&input)?;
/// let mut ciphertext = engine.encrypt_lwe_ciphertext(&key, &plaintext, noise)?;
///
/// engine.fuse_mul_lwe_ciphertext_cleartext(&mut ciphertext, &cleartext)?;
/// #
/// assert_eq!(ciphertext.lwe_dimension(), lwe_dimension);
///
/// #
/// # Ok(())
/// # }
/// ```
fn fuse_mul_lwe_ciphertext_cleartext(
&mut self,
output: &mut LweCiphertext32,
input: &Cleartext32,
) -> Result<(), LweCiphertextCleartextFusingMultiplicationError<Self::EngineError>> {
unsafe { self.fuse_mul_lwe_ciphertext_cleartext_unchecked(output, input) };
Ok(())
}
unsafe fn fuse_mul_lwe_ciphertext_cleartext_unchecked(
&mut self,
output: &mut LweCiphertext32,
input: &Cleartext32,
) {
output.0.update_with_scalar_mul(input.0);
}
}
/// # Description:
/// Implementation of [`LweCiphertextCleartextFusingMultiplicationEngine`] for [`DefaultEngine`]
/// that operates on 64 bits integers.
impl LweCiphertextCleartextFusingMultiplicationEngine<LweCiphertext64, Cleartext64>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(2);
/// // Here a hard-set encoding is applied (shift by 50 bits)
/// let input = 3_u64 << 50;
/// let cleartext_input = 12_u64;
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let cleartext: Cleartext64 = engine.create_cleartext_from(&cleartext_input)?;
/// let key: LweSecretKey64 = engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let plaintext = engine.create_plaintext_from(&input)?;
/// let mut ciphertext = engine.encrypt_lwe_ciphertext(&key, &plaintext, noise)?;
///
/// engine.fuse_mul_lwe_ciphertext_cleartext(&mut ciphertext, &cleartext)?;
/// #
/// assert_eq!(ciphertext.lwe_dimension(), lwe_dimension);
///
/// #
/// # Ok(())
/// # }
/// ```
fn fuse_mul_lwe_ciphertext_cleartext(
&mut self,
output: &mut LweCiphertext64,
input: &Cleartext64,
) -> Result<(), LweCiphertextCleartextFusingMultiplicationError<Self::EngineError>> {
unsafe { self.fuse_mul_lwe_ciphertext_cleartext_unchecked(output, input) };
Ok(())
}
unsafe fn fuse_mul_lwe_ciphertext_cleartext_unchecked(
&mut self,
output: &mut LweCiphertext64,
input: &Cleartext64,
) {
output.0.update_with_scalar_mul(input.0);
}
}

View File

@@ -1,277 +0,0 @@
use crate::core_crypto::backends::default::implementation::engines::DefaultEngine;
use crate::core_crypto::backends::default::implementation::entities::{
LweCiphertext32, LweCiphertext64, LweCiphertextMutView32, LweCiphertextMutView64,
LweCiphertextView32, LweCiphertextView64,
};
use crate::core_crypto::commons::math::tensor::IntoTensor;
use crate::core_crypto::specification::engines::{
LweCiphertextConsumingRetrievalEngine, LweCiphertextConsumingRetrievalError,
};
/// # Description:
/// Implementation of [`LweCiphertextConsumingRetrievalEngine`] for [`DefaultEngine`] that returns
/// the underlying vec of a [`LweCiphertext32`] consuming it in the process
impl LweCiphertextConsumingRetrievalEngine<LweCiphertext32, Vec<u32>> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let lwe_size = LweSize(128);
/// let mut owned_container = vec![0_u32; lwe_size.0];
/// let original_vec_ptr = owned_container.as_ptr();
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext: LweCiphertext32 = engine.create_lwe_ciphertext_from(owned_container)?;
/// let retrieved_container = engine.consume_retrieve_lwe_ciphertext(ciphertext)?;
/// assert_eq!(original_vec_ptr, retrieved_container.as_ptr());
/// #
/// # Ok(())
/// # }
/// ```
fn consume_retrieve_lwe_ciphertext(
&mut self,
ciphertext: LweCiphertext32,
) -> Result<Vec<u32>, LweCiphertextConsumingRetrievalError<Self::EngineError>> {
Ok(unsafe { self.consume_retrieve_lwe_ciphertext_unchecked(ciphertext) })
}
unsafe fn consume_retrieve_lwe_ciphertext_unchecked(
&mut self,
ciphertext: LweCiphertext32,
) -> Vec<u32> {
ciphertext.0.into_tensor().into_container()
}
}
/// # Description:
/// Implementation of [`LweCiphertextConsumingRetrievalEngine`] for [`DefaultEngine`] that returns
/// the underlying vec of a [`LweCiphertext64`] consuming it in the process
impl LweCiphertextConsumingRetrievalEngine<LweCiphertext64, Vec<u64>> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let lwe_size = LweSize(128);
/// let mut owned_container = vec![0_u64; lwe_size.0];
/// let original_vec_ptr = owned_container.as_ptr();
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext: LweCiphertext64 = engine.create_lwe_ciphertext_from(owned_container)?;
/// let retrieved_container = engine.consume_retrieve_lwe_ciphertext(ciphertext)?;
/// assert_eq!(original_vec_ptr, retrieved_container.as_ptr());
/// #
/// # Ok(())
/// # }
/// ```
fn consume_retrieve_lwe_ciphertext(
&mut self,
ciphertext: LweCiphertext64,
) -> Result<Vec<u64>, LweCiphertextConsumingRetrievalError<Self::EngineError>> {
Ok(unsafe { self.consume_retrieve_lwe_ciphertext_unchecked(ciphertext) })
}
unsafe fn consume_retrieve_lwe_ciphertext_unchecked(
&mut self,
ciphertext: LweCiphertext64,
) -> Vec<u64> {
ciphertext.0.into_tensor().into_container()
}
}
/// # Description:
/// Implementation of [`LweCiphertextConsumingRetrievalEngine`] for [`DefaultEngine`] that returns
/// the underlying container of a [`LweCiphertextView32`] consuming it in the process
impl<'data> LweCiphertextConsumingRetrievalEngine<LweCiphertextView32<'data>, &'data [u32]>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let lwe_size = LweSize(128);
/// let mut owned_container = vec![0_u32; lwe_size.0];
///
/// let slice = &owned_container[..];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: LweCiphertextView32 = engine.create_lwe_ciphertext_from(slice)?;
/// let retrieved_slice = engine.consume_retrieve_lwe_ciphertext(ciphertext_view)?;
/// assert_eq!(slice, retrieved_slice);
/// #
/// # Ok(())
/// # }
/// ```
fn consume_retrieve_lwe_ciphertext(
&mut self,
ciphertext: LweCiphertextView32<'data>,
) -> Result<&'data [u32], LweCiphertextConsumingRetrievalError<Self::EngineError>> {
Ok(unsafe { self.consume_retrieve_lwe_ciphertext_unchecked(ciphertext) })
}
unsafe fn consume_retrieve_lwe_ciphertext_unchecked(
&mut self,
ciphertext: LweCiphertextView32<'data>,
) -> &'data [u32] {
ciphertext.0.into_tensor().into_container()
}
}
/// # Description:
/// Implementation of [`LweCiphertextConsumingRetrievalEngine`] for [`DefaultEngine`] that returns
/// the underlying container of a [`LweCiphertextMutView32`] consuming it in the process
impl<'data> LweCiphertextConsumingRetrievalEngine<LweCiphertextMutView32<'data>, &'data mut [u32]>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let lwe_size = LweSize(128);
/// let mut owned_container = vec![0_u32; lwe_size.0];
///
/// let slice = &mut owned_container[..];
/// // Required as we can't borrow a mut slice more than once
/// let underlying_ptr = slice.as_ptr();
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: LweCiphertextMutView32 = engine.create_lwe_ciphertext_from(slice)?;
/// let retrieved_slice = engine.consume_retrieve_lwe_ciphertext(ciphertext_view)?;
/// assert_eq!(underlying_ptr, retrieved_slice.as_ptr());
/// #
/// # Ok(())
/// # }
/// ```
fn consume_retrieve_lwe_ciphertext(
&mut self,
ciphertext: LweCiphertextMutView32<'data>,
) -> Result<&'data mut [u32], LweCiphertextConsumingRetrievalError<Self::EngineError>> {
Ok(unsafe { self.consume_retrieve_lwe_ciphertext_unchecked(ciphertext) })
}
unsafe fn consume_retrieve_lwe_ciphertext_unchecked(
&mut self,
ciphertext: LweCiphertextMutView32<'data>,
) -> &'data mut [u32] {
ciphertext.0.into_tensor().into_container()
}
}
/// # Description:
/// Implementation of [`LweCiphertextConsumingRetrievalEngine`] for [`DefaultEngine`] that returns
/// the underlying container of a [`LweCiphertextView64`] consuming it in the process
impl<'data> LweCiphertextConsumingRetrievalEngine<LweCiphertextView64<'data>, &'data [u64]>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let lwe_size = LweSize(128);
/// let mut owned_container = vec![0_u64; lwe_size.0];
///
/// let slice = &owned_container[..];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: LweCiphertextView64 = engine.create_lwe_ciphertext_from(slice)?;
/// let retrieved_slice = engine.consume_retrieve_lwe_ciphertext(ciphertext_view)?;
/// assert_eq!(slice, retrieved_slice);
/// #
/// # Ok(())
/// # }
/// ```
fn consume_retrieve_lwe_ciphertext(
&mut self,
ciphertext: LweCiphertextView64<'data>,
) -> Result<&'data [u64], LweCiphertextConsumingRetrievalError<Self::EngineError>> {
Ok(unsafe { self.consume_retrieve_lwe_ciphertext_unchecked(ciphertext) })
}
unsafe fn consume_retrieve_lwe_ciphertext_unchecked(
&mut self,
ciphertext: LweCiphertextView64<'data>,
) -> &'data [u64] {
ciphertext.0.into_tensor().into_container()
}
}
/// # Description:
/// Implementation of [`LweCiphertextConsumingRetrievalEngine`] for [`DefaultEngine`] that returns
/// the underlying container of a [`LweCiphertextMutView64`] consuming it in the process
impl<'data> LweCiphertextConsumingRetrievalEngine<LweCiphertextMutView64<'data>, &'data mut [u64]>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let lwe_size = LweSize(128);
/// let mut owned_container = vec![0_u64; lwe_size.0];
///
/// let slice = &mut owned_container[..];
/// // Required as we can't borrow a mut slice more than once
/// let underlying_ptr = slice.as_ptr();
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: LweCiphertextMutView64 = engine.create_lwe_ciphertext_from(slice)?;
/// let retrieved_slice = engine.consume_retrieve_lwe_ciphertext(ciphertext_view)?;
/// assert_eq!(underlying_ptr, retrieved_slice.as_ptr());
/// #
/// # Ok(())
/// # }
/// ```
fn consume_retrieve_lwe_ciphertext(
&mut self,
ciphertext: LweCiphertextMutView64<'data>,
) -> Result<&'data mut [u64], LweCiphertextConsumingRetrievalError<Self::EngineError>> {
Ok(unsafe { self.consume_retrieve_lwe_ciphertext_unchecked(ciphertext) })
}
unsafe fn consume_retrieve_lwe_ciphertext_unchecked(
&mut self,
ciphertext: LweCiphertextMutView64<'data>,
) -> &'data mut [u64] {
ciphertext.0.into_tensor().into_container()
}
}

View File

@@ -1,264 +0,0 @@
use crate::core_crypto::backends::default::implementation::engines::DefaultEngine;
use crate::core_crypto::backends::default::implementation::entities::{
LweCiphertext32, LweCiphertext64, LweCiphertextMutView32, LweCiphertextMutView64,
LweCiphertextView32, LweCiphertextView64,
};
use crate::core_crypto::commons::crypto::lwe::LweCiphertext as ImplLweCiphertext;
use crate::core_crypto::specification::engines::{
LweCiphertextCreationEngine, LweCiphertextCreationError,
};
/// # Description:
/// Implementation of [`LweCiphertextCreationEngine`] for [`DefaultEngine`] which returns an
/// [`LweCiphertext32`].
impl LweCiphertextCreationEngine<Vec<u32>, LweCiphertext32> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let lwe_size = LweSize(128);
/// let owned_container = vec![0_u32; lwe_size.0];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext: LweCiphertext32 = engine.create_lwe_ciphertext_from(owned_container)?;
/// #
/// # Ok(())
/// # }
/// ```
fn create_lwe_ciphertext_from(
&mut self,
container: Vec<u32>,
) -> Result<LweCiphertext32, LweCiphertextCreationError<Self::EngineError>> {
LweCiphertextCreationError::<Self::EngineError>::perform_generic_checks(container.len())?;
Ok(unsafe { self.create_lwe_ciphertext_from_unchecked(container) })
}
unsafe fn create_lwe_ciphertext_from_unchecked(
&mut self,
container: Vec<u32>,
) -> LweCiphertext32 {
LweCiphertext32(ImplLweCiphertext::from_container(container))
}
}
/// # Description:
/// Implementation of [`LweCiphertextCreationEngine`] for [`DefaultEngine`] which returns an
/// [`LweCiphertext64`].
impl LweCiphertextCreationEngine<Vec<u64>, LweCiphertext64> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let lwe_size = LweSize(128);
/// let owned_container = vec![0_u64; lwe_size.0];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext: LweCiphertext64 = engine.create_lwe_ciphertext_from(owned_container)?;
/// #
/// # Ok(())
/// # }
/// ```
fn create_lwe_ciphertext_from(
&mut self,
container: Vec<u64>,
) -> Result<LweCiphertext64, LweCiphertextCreationError<Self::EngineError>> {
LweCiphertextCreationError::<Self::EngineError>::perform_generic_checks(container.len())?;
Ok(unsafe { self.create_lwe_ciphertext_from_unchecked(container) })
}
unsafe fn create_lwe_ciphertext_from_unchecked(
&mut self,
container: Vec<u64>,
) -> LweCiphertext64 {
LweCiphertext64(ImplLweCiphertext::from_container(container))
}
}
/// # Description:
/// Implementation of [`LweCiphertextCreationEngine`] for [`DefaultEngine`] which returns an
/// immutable [`LweCiphertextView32`] that does not own its memory.
impl<'data> LweCiphertextCreationEngine<&'data [u32], LweCiphertextView32<'data>>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let lwe_size = LweSize(128);
/// let mut owned_container = vec![0_u32; lwe_size.0];
///
/// let slice = &owned_container[..];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: LweCiphertextView32 = engine.create_lwe_ciphertext_from(slice)?;
/// #
/// # Ok(())
/// # }
/// ```
fn create_lwe_ciphertext_from(
&mut self,
container: &'data [u32],
) -> Result<LweCiphertextView32<'data>, LweCiphertextCreationError<Self::EngineError>> {
LweCiphertextCreationError::<Self::EngineError>::perform_generic_checks(container.len())?;
Ok(unsafe { self.create_lwe_ciphertext_from_unchecked(container) })
}
unsafe fn create_lwe_ciphertext_from_unchecked(
&mut self,
container: &'data [u32],
) -> LweCiphertextView32<'data> {
LweCiphertextView32(ImplLweCiphertext::from_container(container))
}
}
/// # Description:
/// Implementation of [`LweCiphertextCreationEngine`] for [`DefaultEngine`] which returns a mutable
/// [`LweCiphertextMutView32`] that does not own its memory.
impl<'data> LweCiphertextCreationEngine<&'data mut [u32], LweCiphertextMutView32<'data>>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let lwe_size = LweSize(128);
/// let mut owned_container = vec![0_u32; lwe_size.0];
///
/// let slice = &mut owned_container[..];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: LweCiphertextMutView32 = engine.create_lwe_ciphertext_from(slice)?;
/// #
/// # Ok(())
/// # }
/// ```
fn create_lwe_ciphertext_from(
&mut self,
container: &'data mut [u32],
) -> Result<LweCiphertextMutView32<'data>, LweCiphertextCreationError<Self::EngineError>> {
LweCiphertextCreationError::<Self::EngineError>::perform_generic_checks(container.len())?;
Ok(unsafe { self.create_lwe_ciphertext_from_unchecked(container) })
}
unsafe fn create_lwe_ciphertext_from_unchecked(
&mut self,
container: &'data mut [u32],
) -> LweCiphertextMutView32<'data> {
LweCiphertextMutView32(ImplLweCiphertext::from_container(container))
}
}
/// # Description:
/// Implementation of [`LweCiphertextCreationEngine`] for [`DefaultEngine`] which returns an
/// immutable [`LweCiphertextView64`] that does not own its memory.
impl<'data> LweCiphertextCreationEngine<&'data [u64], LweCiphertextView64<'data>>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::*;
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let mut owned_container = vec![0_u64; 128];
///
/// let slice = &owned_container[..];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: LweCiphertextView64 = engine.create_lwe_ciphertext_from(slice)?;
/// #
/// # Ok(())
/// # }
/// ```
fn create_lwe_ciphertext_from(
&mut self,
container: &'data [u64],
) -> Result<LweCiphertextView64<'data>, LweCiphertextCreationError<Self::EngineError>> {
LweCiphertextCreationError::<Self::EngineError>::perform_generic_checks(container.len())?;
Ok(unsafe { self.create_lwe_ciphertext_from_unchecked(container) })
}
unsafe fn create_lwe_ciphertext_from_unchecked(
&mut self,
container: &'data [u64],
) -> LweCiphertextView64<'data> {
LweCiphertextView64(ImplLweCiphertext::from_container(container))
}
}
/// # Description:
/// Implementation of [`LweCiphertextCreationEngine`] for [`DefaultEngine`] which returns a mutable
/// [`LweCiphertextMutView64`] that does not own its memory.
impl<'data> LweCiphertextCreationEngine<&'data mut [u64], LweCiphertextMutView64<'data>>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweSize, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // Here we create a container outside of the engine
/// // Note that the size here is just for demonstration purposes and should not be chosen
/// // without proper security analysis for production
/// let lwe_size = LweSize(128);
/// let mut owned_container = vec![0_u64; lwe_size.0];
///
/// let slice = &mut owned_container[..];
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let ciphertext_view: LweCiphertextMutView64 = engine.create_lwe_ciphertext_from(slice)?;
/// #
/// # Ok(())
/// # }
/// ```
fn create_lwe_ciphertext_from(
&mut self,
container: &'data mut [u64],
) -> Result<LweCiphertextMutView64<'data>, LweCiphertextCreationError<Self::EngineError>> {
LweCiphertextCreationError::<Self::EngineError>::perform_generic_checks(container.len())?;
Ok(unsafe { self.create_lwe_ciphertext_from_unchecked(container) })
}
unsafe fn create_lwe_ciphertext_from_unchecked(
&mut self,
container: &'data mut [u64],
) -> LweCiphertextMutView64<'data> {
LweCiphertextMutView64(ImplLweCiphertext::from_container(container))
}
}

View File

@@ -1,227 +0,0 @@
use crate::core_crypto::backends::default::implementation::engines::DefaultEngine;
use crate::core_crypto::backends::default::implementation::entities::{
LweCiphertext32, LweCiphertext64, LweCiphertextView32, LweCiphertextView64, LweSecretKey32,
LweSecretKey64, Plaintext32, Plaintext64,
};
use crate::core_crypto::commons::crypto::encoding::Plaintext as ImplPlaintext;
use crate::core_crypto::specification::engines::{
LweCiphertextDecryptionEngine, LweCiphertextDecryptionError,
};
/// # Description:
/// Implementation of [`LweCiphertextDecryptionEngine`] for [`DefaultEngine`] that operates on
/// 32 bits integers.
impl LweCiphertextDecryptionEngine<LweSecretKey32, LweCiphertext32, Plaintext32> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(2);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = 3_u32 << 20;
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let key: LweSecretKey32 = engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let plaintext = engine.create_plaintext_from(&input)?;
/// let ciphertext = engine.encrypt_lwe_ciphertext(&key, &plaintext, noise)?;
///
/// let decrypted_plaintext = engine.decrypt_lwe_ciphertext(&key, &ciphertext)?;
///
/// #
/// # Ok(())
/// # }
/// ```
fn decrypt_lwe_ciphertext(
&mut self,
key: &LweSecretKey32,
input: &LweCiphertext32,
) -> Result<Plaintext32, LweCiphertextDecryptionError<Self::EngineError>> {
Ok(unsafe { self.decrypt_lwe_ciphertext_unchecked(key, input) })
}
unsafe fn decrypt_lwe_ciphertext_unchecked(
&mut self,
key: &LweSecretKey32,
input: &LweCiphertext32,
) -> Plaintext32 {
let mut plaintext = ImplPlaintext(0u32);
key.0.decrypt_lwe(&mut plaintext, &input.0);
Plaintext32(plaintext)
}
}
/// # Description:
/// Implementation of [`LweCiphertextDecryptionEngine`] for [`DefaultEngine`] that operates on
/// 64 bits integers.
impl LweCiphertextDecryptionEngine<LweSecretKey64, LweCiphertext64, Plaintext64> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(2);
/// // Here a hard-set encoding is applied (shift by 50 bits)
/// let input = 3_u64 << 50;
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let key: LweSecretKey64 = engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let plaintext = engine.create_plaintext_from(&input)?;
/// let ciphertext = engine.encrypt_lwe_ciphertext(&key, &plaintext, noise)?;
///
/// let decrypted_plaintext = engine.decrypt_lwe_ciphertext(&key, &ciphertext)?;
///
/// #
/// # Ok(())
/// # }
/// ```
fn decrypt_lwe_ciphertext(
&mut self,
key: &LweSecretKey64,
input: &LweCiphertext64,
) -> Result<Plaintext64, LweCiphertextDecryptionError<Self::EngineError>> {
Ok(unsafe { self.decrypt_lwe_ciphertext_unchecked(key, input) })
}
unsafe fn decrypt_lwe_ciphertext_unchecked(
&mut self,
key: &LweSecretKey64,
input: &LweCiphertext64,
) -> Plaintext64 {
let mut plaintext = ImplPlaintext(0u64);
key.0.decrypt_lwe(&mut plaintext, &input.0);
Plaintext64(plaintext)
}
}
/// # Description:
/// Implementation of [`LweCiphertextDecryptionEngine`] for [`DefaultEngine`] that operates on
/// an [`LweCiphertextView32`] containing 32 bits integers.
impl LweCiphertextDecryptionEngine<LweSecretKey32, LweCiphertextView32<'_>, Plaintext32>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(2);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = 3_u32 << 20;
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let key: LweSecretKey32 = engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let plaintext = engine.create_plaintext_from(&input)?;
///
/// let mut raw_ciphertext = vec![0_u32; key.lwe_dimension().to_lwe_size().0];
/// let mut ciphertext_view: LweCiphertextMutView32 =
/// engine.create_lwe_ciphertext_from(&mut raw_ciphertext[..])?;
/// engine.discard_encrypt_lwe_ciphertext(&key, &mut ciphertext_view, &plaintext, noise)?;
///
/// // Convert MutView to View
/// let raw_ciphertext = engine.consume_retrieve_lwe_ciphertext(ciphertext_view)?;
/// let ciphertext_view: LweCiphertextView32 =
/// engine.create_lwe_ciphertext_from(&raw_ciphertext[..])?;
///
/// let decrypted_plaintext = engine.decrypt_lwe_ciphertext(&key, &ciphertext_view)?;
///
/// #
/// # Ok(())
/// # }
/// ```
fn decrypt_lwe_ciphertext(
&mut self,
key: &LweSecretKey32,
input: &LweCiphertextView32<'_>,
) -> Result<Plaintext32, LweCiphertextDecryptionError<Self::EngineError>> {
Ok(unsafe { self.decrypt_lwe_ciphertext_unchecked(key, input) })
}
unsafe fn decrypt_lwe_ciphertext_unchecked(
&mut self,
key: &LweSecretKey32,
input: &LweCiphertextView32<'_>,
) -> Plaintext32 {
let mut plaintext = ImplPlaintext(0u32);
key.0.decrypt_lwe(&mut plaintext, &input.0);
Plaintext32(plaintext)
}
}
/// # Description:
/// Implementation of [`LweCiphertextDecryptionEngine`] for [`DefaultEngine`] that operates on
/// an [`LweCiphertextView64`] containing 64 bits integers.
impl LweCiphertextDecryptionEngine<LweSecretKey64, LweCiphertextView64<'_>, Plaintext64>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(2);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = 3_u64 << 20;
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let key: LweSecretKey64 = engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let plaintext = engine.create_plaintext_from(&input)?;
///
/// let mut raw_ciphertext = vec![0_u64; key.lwe_dimension().to_lwe_size().0];
/// let mut ciphertext_view: LweCiphertextMutView64 =
/// engine.create_lwe_ciphertext_from(&mut raw_ciphertext[..])?;
/// engine.discard_encrypt_lwe_ciphertext(&key, &mut ciphertext_view, &plaintext, noise)?;
///
/// // Convert MutView to View
/// let raw_ciphertext = engine.consume_retrieve_lwe_ciphertext(ciphertext_view)?;
/// let ciphertext_view: LweCiphertextView64 =
/// engine.create_lwe_ciphertext_from(&raw_ciphertext[..])?;
///
/// let decrypted_plaintext = engine.decrypt_lwe_ciphertext(&key, &ciphertext_view)?;
///
/// #
/// # Ok(())
/// # }
/// ```
fn decrypt_lwe_ciphertext(
&mut self,
key: &LweSecretKey64,
input: &LweCiphertextView64<'_>,
) -> Result<Plaintext64, LweCiphertextDecryptionError<Self::EngineError>> {
Ok(unsafe { self.decrypt_lwe_ciphertext_unchecked(key, input) })
}
unsafe fn decrypt_lwe_ciphertext_unchecked(
&mut self,
key: &LweSecretKey64,
input: &LweCiphertextView64<'_>,
) -> Plaintext64 {
let mut plaintext = ImplPlaintext(0u64);
key.0.decrypt_lwe(&mut plaintext, &input.0);
Plaintext64(plaintext)
}
}

View File

@@ -1,293 +0,0 @@
use crate::core_crypto::backends::default::implementation::engines::DefaultEngine;
use crate::core_crypto::backends::default::implementation::entities::{
LweCiphertext32, LweCiphertext64, LweCiphertextMutView32, LweCiphertextMutView64,
LweCiphertextView32, LweCiphertextView64,
};
use crate::core_crypto::commons::math::tensor::{AsMutTensor, AsRefTensor};
use crate::core_crypto::specification::engines::{
LweCiphertextDiscardingAdditionEngine, LweCiphertextDiscardingAdditionError,
};
/// # Description:
/// Implementation of [`LweCiphertextDiscardingAdditionEngine`] for [`DefaultEngine`] that operates
/// on 32 bits integers.
impl LweCiphertextDiscardingAdditionEngine<LweCiphertext32, LweCiphertext32> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(2);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input_1 = 3_u32 << 20;
/// let input_2 = 7_u32 << 20;
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let key: LweSecretKey32 = engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let plaintext_1 = engine.create_plaintext_from(&input_1)?;
/// let plaintext_2 = engine.create_plaintext_from(&input_2)?;
/// let ciphertext_1 = engine.encrypt_lwe_ciphertext(&key, &plaintext_1, noise)?;
/// let ciphertext_2 = engine.encrypt_lwe_ciphertext(&key, &plaintext_2, noise)?;
/// let mut ciphertext_3 = engine.zero_encrypt_lwe_ciphertext(&key, noise)?;
///
/// engine.discard_add_lwe_ciphertext(&mut ciphertext_3, &ciphertext_1, &ciphertext_2)?;
/// #
/// assert_eq!(ciphertext_3.lwe_dimension(), lwe_dimension);
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_add_lwe_ciphertext(
&mut self,
output: &mut LweCiphertext32,
input_1: &LweCiphertext32,
input_2: &LweCiphertext32,
) -> Result<(), LweCiphertextDiscardingAdditionError<Self::EngineError>> {
LweCiphertextDiscardingAdditionError::perform_generic_checks(output, input_1, input_2)?;
unsafe { self.discard_add_lwe_ciphertext_unchecked(output, input_1, input_2) };
Ok(())
}
unsafe fn discard_add_lwe_ciphertext_unchecked(
&mut self,
output: &mut LweCiphertext32,
input_1: &LweCiphertext32,
input_2: &LweCiphertext32,
) {
output
.0
.as_mut_tensor()
.fill_with_copy(input_1.0.as_tensor());
output.0.update_with_add(&input_2.0);
}
}
/// # Description:
/// Implementation of [`LweCiphertextDiscardingAdditionEngine`] for [`DefaultEngine`] that operates
/// on 64 bits integers.
impl LweCiphertextDiscardingAdditionEngine<LweCiphertext64, LweCiphertext64> for DefaultEngine {
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(2);
/// // Here a hard-set encoding is applied (shift by 50 bits)
/// let input_1 = 3_u64 << 50;
/// let input_2 = 7_u64 << 50;
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let key: LweSecretKey64 = engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let plaintext_1 = engine.create_plaintext_from(&input_1)?;
/// let plaintext_2 = engine.create_plaintext_from(&input_2)?;
/// let ciphertext_1 = engine.encrypt_lwe_ciphertext(&key, &plaintext_1, noise)?;
/// let ciphertext_2 = engine.encrypt_lwe_ciphertext(&key, &plaintext_2, noise)?;
/// let mut ciphertext_3 = engine.zero_encrypt_lwe_ciphertext(&key, noise)?;
///
/// engine.discard_add_lwe_ciphertext(&mut ciphertext_3, &ciphertext_1, &ciphertext_2)?;
/// #
/// assert_eq!(ciphertext_3.lwe_dimension(), lwe_dimension);
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_add_lwe_ciphertext(
&mut self,
output: &mut LweCiphertext64,
input_1: &LweCiphertext64,
input_2: &LweCiphertext64,
) -> Result<(), LweCiphertextDiscardingAdditionError<Self::EngineError>> {
LweCiphertextDiscardingAdditionError::perform_generic_checks(output, input_1, input_2)?;
unsafe { self.discard_add_lwe_ciphertext_unchecked(output, input_1, input_2) };
Ok(())
}
unsafe fn discard_add_lwe_ciphertext_unchecked(
&mut self,
output: &mut LweCiphertext64,
input_1: &LweCiphertext64,
input_2: &LweCiphertext64,
) {
output
.0
.as_mut_tensor()
.fill_with_copy(input_1.0.as_tensor());
output.0.update_with_add(&input_2.0);
}
}
/// # Description:
/// Implementation of [`LweCiphertextDiscardingAdditionEngine`] for [`DefaultEngine`] that operates
/// on views containing 32 bits integers.
impl LweCiphertextDiscardingAdditionEngine<LweCiphertextView32<'_>, LweCiphertextMutView32<'_>>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(2);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input_1 = 3_u32 << 20;
/// let input_2 = 7_u32 << 20;
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let key: LweSecretKey32 = engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let plaintext_1 = engine.create_plaintext_from(&input_1)?;
/// let plaintext_2 = engine.create_plaintext_from(&input_2)?;
///
/// let mut ciphertext_1_container = vec![0_u32; key.lwe_dimension().to_lwe_size().0];
/// let mut ciphertext_1: LweCiphertextMutView32 =
/// engine.create_lwe_ciphertext_from(&mut ciphertext_1_container[..])?;
/// engine.discard_encrypt_lwe_ciphertext(&key, &mut ciphertext_1, &plaintext_1, noise)?;
/// let mut ciphertext_2_container = vec![0_u32; key.lwe_dimension().to_lwe_size().0];
/// let mut ciphertext_2: LweCiphertextMutView32 =
/// engine.create_lwe_ciphertext_from(&mut ciphertext_2_container[..])?;
/// engine.discard_encrypt_lwe_ciphertext(&key, &mut ciphertext_2, &plaintext_2, noise)?;
///
/// // Convert MutView to View
/// let raw_ciphertext_1 = engine.consume_retrieve_lwe_ciphertext(ciphertext_1)?;
/// let ciphertext_1: LweCiphertextView32 =
/// engine.create_lwe_ciphertext_from(&raw_ciphertext_1[..])?;
/// let raw_ciphertext_2 = engine.consume_retrieve_lwe_ciphertext(ciphertext_2)?;
/// let ciphertext_2: LweCiphertextView32 =
/// engine.create_lwe_ciphertext_from(&raw_ciphertext_2[..])?;
///
/// let mut ciphertext_3_container = vec![0_u32; key.lwe_dimension().to_lwe_size().0];
/// let mut ciphertext_3: LweCiphertextMutView32 =
/// engine.create_lwe_ciphertext_from(&mut ciphertext_3_container[..])?;
///
/// engine.discard_add_lwe_ciphertext(&mut ciphertext_3, &ciphertext_1, &ciphertext_2)?;
/// #
/// assert_eq!(ciphertext_3.lwe_dimension(), lwe_dimension);
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_add_lwe_ciphertext(
&mut self,
output: &mut LweCiphertextMutView32,
input_1: &LweCiphertextView32,
input_2: &LweCiphertextView32,
) -> Result<(), LweCiphertextDiscardingAdditionError<Self::EngineError>> {
LweCiphertextDiscardingAdditionError::perform_generic_checks(output, input_1, input_2)?;
unsafe { self.discard_add_lwe_ciphertext_unchecked(output, input_1, input_2) };
Ok(())
}
unsafe fn discard_add_lwe_ciphertext_unchecked(
&mut self,
output: &mut LweCiphertextMutView32,
input_1: &LweCiphertextView32,
input_2: &LweCiphertextView32,
) {
output
.0
.as_mut_tensor()
.fill_with_copy(input_1.0.as_tensor());
output.0.update_with_add(&input_2.0);
}
}
/// # Description:
/// Implementation of [`LweCiphertextDiscardingAdditionEngine`] for [`DefaultEngine`] that operates
/// on on views containing 64 bits integers.
impl LweCiphertextDiscardingAdditionEngine<LweCiphertextView64<'_>, LweCiphertextMutView64<'_>>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(2);
/// // Here a hard-set encoding is applied (shift by 50 bits)
/// let input_1 = 3_u64 << 50;
/// let input_2 = 7_u64 << 50;
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let key: LweSecretKey64 = engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let plaintext_1 = engine.create_plaintext_from(&input_1)?;
/// let plaintext_2 = engine.create_plaintext_from(&input_2)?;
///
/// let mut ciphertext_1_container = vec![0_u64; key.lwe_dimension().to_lwe_size().0];
/// let mut ciphertext_1: LweCiphertextMutView64 =
/// engine.create_lwe_ciphertext_from(&mut ciphertext_1_container[..])?;
/// engine.discard_encrypt_lwe_ciphertext(&key, &mut ciphertext_1, &plaintext_1, noise)?;
/// let mut ciphertext_2_container = vec![0_u64; key.lwe_dimension().to_lwe_size().0];
/// let mut ciphertext_2: LweCiphertextMutView64 =
/// engine.create_lwe_ciphertext_from(&mut ciphertext_2_container[..])?;
/// engine.discard_encrypt_lwe_ciphertext(&key, &mut ciphertext_2, &plaintext_2, noise)?;
///
/// // Convert MutView to View
/// let raw_ciphertext_1 = engine.consume_retrieve_lwe_ciphertext(ciphertext_1)?;
/// let ciphertext_1: LweCiphertextView64 =
/// engine.create_lwe_ciphertext_from(&raw_ciphertext_1[..])?;
/// let raw_ciphertext_2 = engine.consume_retrieve_lwe_ciphertext(ciphertext_2)?;
/// let ciphertext_2: LweCiphertextView64 =
/// engine.create_lwe_ciphertext_from(&raw_ciphertext_2[..])?;
///
/// let mut ciphertext_3_container = vec![0_u64; key.lwe_dimension().to_lwe_size().0];
/// let mut ciphertext_3: LweCiphertextMutView64 =
/// engine.create_lwe_ciphertext_from(&mut ciphertext_3_container[..])?;
///
/// engine.discard_add_lwe_ciphertext(&mut ciphertext_3, &ciphertext_1, &ciphertext_2)?;
/// #
/// assert_eq!(ciphertext_3.lwe_dimension(), lwe_dimension);
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_add_lwe_ciphertext(
&mut self,
output: &mut LweCiphertextMutView64,
input_1: &LweCiphertextView64,
input_2: &LweCiphertextView64,
) -> Result<(), LweCiphertextDiscardingAdditionError<Self::EngineError>> {
LweCiphertextDiscardingAdditionError::perform_generic_checks(output, input_1, input_2)?;
unsafe { self.discard_add_lwe_ciphertext_unchecked(output, input_1, input_2) };
Ok(())
}
unsafe fn discard_add_lwe_ciphertext_unchecked(
&mut self,
output: &mut LweCiphertextMutView64,
input_1: &LweCiphertextView64,
input_2: &LweCiphertextView64,
) {
output
.0
.as_mut_tensor()
.fill_with_copy(input_1.0.as_tensor());
output.0.update_with_add(&input_2.0);
}
}

View File

@@ -1,264 +0,0 @@
use crate::core_crypto::prelude::Variance;
use crate::core_crypto::backends::default::implementation::engines::DefaultEngine;
use crate::core_crypto::backends::default::implementation::entities::{
LweCiphertext32, LweCiphertext64, LweCiphertextMutView32, LweCiphertextMutView64,
LweSecretKey32, LweSecretKey64, Plaintext32, Plaintext64,
};
use crate::core_crypto::specification::engines::{
LweCiphertextDiscardingEncryptionEngine, LweCiphertextDiscardingEncryptionError,
};
/// # Description:
/// Implementation of [`LweCiphertextDiscardingEncryptionEngine`] for [`DefaultEngine`] that
/// operates on 32 bits integers.
impl LweCiphertextDiscardingEncryptionEngine<LweSecretKey32, Plaintext32, LweCiphertext32>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(2);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = 3_u32 << 20;
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let key: LweSecretKey32 = engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let plaintext = engine.create_plaintext_from(&input)?;
/// let mut ciphertext = engine.encrypt_lwe_ciphertext(&key, &plaintext, noise)?;
///
/// engine.discard_encrypt_lwe_ciphertext(&key, &mut ciphertext, &plaintext, noise)?;
/// #
/// assert_eq!(ciphertext.lwe_dimension(), lwe_dimension);
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_encrypt_lwe_ciphertext(
&mut self,
key: &LweSecretKey32,
output: &mut LweCiphertext32,
input: &Plaintext32,
noise: Variance,
) -> Result<(), LweCiphertextDiscardingEncryptionError<Self::EngineError>> {
LweCiphertextDiscardingEncryptionError::perform_generic_checks(key, output)?;
unsafe { self.discard_encrypt_lwe_ciphertext_unchecked(key, output, input, noise) };
Ok(())
}
unsafe fn discard_encrypt_lwe_ciphertext_unchecked(
&mut self,
key: &LweSecretKey32,
output: &mut LweCiphertext32,
input: &Plaintext32,
noise: Variance,
) {
key.0.encrypt_lwe(
&mut output.0,
&input.0,
noise,
&mut self.encryption_generator,
);
}
}
/// # Description:
/// Implementation of [`LweCiphertextDiscardingEncryptionEngine`] for [`DefaultEngine`] that
/// operates on 64 bits integers.
impl LweCiphertextDiscardingEncryptionEngine<LweSecretKey64, Plaintext64, LweCiphertext64>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(2);
/// // Here a hard-set encoding is applied (shift by 50 bits)
/// let input = 3_u64 << 50;
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Unix seeder must be given a secret input.
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let key: LweSecretKey64 = engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let plaintext = engine.create_plaintext_from(&input)?;
/// let mut ciphertext = engine.encrypt_lwe_ciphertext(&key, &plaintext, noise)?;
///
/// engine.discard_encrypt_lwe_ciphertext(&key, &mut ciphertext, &plaintext, noise)?;
/// #
/// assert_eq!(ciphertext.lwe_dimension(), lwe_dimension);
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_encrypt_lwe_ciphertext(
&mut self,
key: &LweSecretKey64,
output: &mut LweCiphertext64,
input: &Plaintext64,
noise: Variance,
) -> Result<(), LweCiphertextDiscardingEncryptionError<Self::EngineError>> {
LweCiphertextDiscardingEncryptionError::perform_generic_checks(key, output)?;
unsafe { self.discard_encrypt_lwe_ciphertext_unchecked(key, output, input, noise) };
Ok(())
}
unsafe fn discard_encrypt_lwe_ciphertext_unchecked(
&mut self,
key: &LweSecretKey64,
output: &mut LweCiphertext64,
input: &Plaintext64,
noise: Variance,
) {
key.0.encrypt_lwe(
&mut output.0,
&input.0,
noise,
&mut self.encryption_generator,
);
}
}
/// # Description:
/// Implementation of [`LweCiphertextDiscardingEncryptionEngine`] for [`DefaultEngine`] that
/// operates on 32 bits integers.
impl
LweCiphertextDiscardingEncryptionEngine<LweSecretKey32, Plaintext32, LweCiphertextMutView32<'_>>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(2);
/// // Here a hard-set encoding is applied (shift by 20 bits)
/// let input = 3_u32 << 20;
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let key: LweSecretKey32 = engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let plaintext = engine.create_plaintext_from(&input)?;
///
/// let mut output_cipertext_container = vec![0_32; lwe_dimension.to_lwe_size().0];
/// let mut output_ciphertext: LweCiphertextMutView32 =
/// engine.create_lwe_ciphertext_from(&mut output_cipertext_container[..])?;
///
/// engine.discard_encrypt_lwe_ciphertext(&key, &mut output_ciphertext, &plaintext, noise)?;
/// #
/// assert_eq!(output_ciphertext.lwe_dimension(), lwe_dimension);
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_encrypt_lwe_ciphertext(
&mut self,
key: &LweSecretKey32,
output: &mut LweCiphertextMutView32,
input: &Plaintext32,
noise: Variance,
) -> Result<(), LweCiphertextDiscardingEncryptionError<Self::EngineError>> {
LweCiphertextDiscardingEncryptionError::perform_generic_checks(key, output)?;
unsafe { self.discard_encrypt_lwe_ciphertext_unchecked(key, output, input, noise) };
Ok(())
}
unsafe fn discard_encrypt_lwe_ciphertext_unchecked(
&mut self,
key: &LweSecretKey32,
output: &mut LweCiphertextMutView32,
input: &Plaintext32,
noise: Variance,
) {
key.0.encrypt_lwe(
&mut output.0,
&input.0,
noise,
&mut self.encryption_generator,
);
}
}
/// # Description:
/// Implementation of [`LweCiphertextDiscardingEncryptionEngine`] for [`DefaultEngine`] that
/// operates on 64 bits integers.
impl
LweCiphertextDiscardingEncryptionEngine<LweSecretKey64, Plaintext64, LweCiphertextMutView64<'_>>
for DefaultEngine
{
/// # Example:
/// ```
/// use tfhe::core_crypto::prelude::{LweDimension, Variance, *};
/// # use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
/// let lwe_dimension = LweDimension(2);
/// // Here a hard-set encoding is applied (shift by 50 bits)
/// let input = 3_u64 << 50;
/// let noise = Variance(2_f64.powf(-25.));
///
/// // Here we just give it 0, which is totally unsafe.
/// const UNSAFE_SECRET: u128 = 0;
/// let mut engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
/// let key: LweSecretKey64 = engine.generate_new_lwe_secret_key(lwe_dimension)?;
/// let plaintext = engine.create_plaintext_from(&input)?;
///
/// let mut output_cipertext_container = vec![0_64; lwe_dimension.to_lwe_size().0];
/// let mut output_ciphertext: LweCiphertextMutView64 =
/// engine.create_lwe_ciphertext_from(&mut output_cipertext_container[..])?;
///
/// engine.discard_encrypt_lwe_ciphertext(&key, &mut output_ciphertext, &plaintext, noise)?;
/// #
/// assert_eq!(output_ciphertext.lwe_dimension(), lwe_dimension);
///
/// #
/// # Ok(())
/// # }
/// ```
fn discard_encrypt_lwe_ciphertext(
&mut self,
key: &LweSecretKey64,
output: &mut LweCiphertextMutView64,
input: &Plaintext64,
noise: Variance,
) -> Result<(), LweCiphertextDiscardingEncryptionError<Self::EngineError>> {
LweCiphertextDiscardingEncryptionError::perform_generic_checks(key, output)?;
unsafe { self.discard_encrypt_lwe_ciphertext_unchecked(key, output, input, noise) };
Ok(())
}
unsafe fn discard_encrypt_lwe_ciphertext_unchecked(
&mut self,
key: &LweSecretKey64,
output: &mut LweCiphertextMutView64,
input: &Plaintext64,
noise: Variance,
) {
key.0.encrypt_lwe(
&mut output.0,
&input.0,
noise,
&mut self.encryption_generator,
);
}
}

Some files were not shown because too many files have changed in this diff Show More