mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-11 07:38:08 -05:00
Compare commits
92 Commits
al/doc_erc
...
blockchain
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cd3d2d75a | ||
|
|
b3fc2378cc | ||
|
|
05de72602c | ||
|
|
bcc791a88d | ||
|
|
4d34f93621 | ||
|
|
61459fd3d0 | ||
|
|
589238267b | ||
|
|
d35c595d89 | ||
|
|
5642880a1e | ||
|
|
d8a0067aff | ||
|
|
983f94d64f | ||
|
|
5f14bf1cca | ||
|
|
5589572efc | ||
|
|
fc1f322f5a | ||
|
|
55ae47bed6 | ||
|
|
db96573595 | ||
|
|
d763131b41 | ||
|
|
d0650ad6ac | ||
|
|
43f47806d2 | ||
|
|
45505cb198 | ||
|
|
5190aee8ab | ||
|
|
6568d7f807 | ||
|
|
7559c22b7e | ||
|
|
cc4587d010 | ||
|
|
b4c321835c | ||
|
|
a87849789d | ||
|
|
bfc111a2db | ||
|
|
7cec0d40fb | ||
|
|
0bdb72466c | ||
|
|
6053ee5610 | ||
|
|
9233b4a7c3 | ||
|
|
60ff266bc1 | ||
|
|
5f284a7c55 | ||
|
|
27d54d19ba | ||
|
|
315f5b68ab | ||
|
|
2d73edc5a3 | ||
|
|
2de0ce4d36 | ||
|
|
45eb5ababd | ||
|
|
869eace723 | ||
|
|
97d536b810 | ||
|
|
2ba22da596 | ||
|
|
b57b6acd8a | ||
|
|
2790b08d39 | ||
|
|
7e73b8ea78 | ||
|
|
bc3fe8e0db | ||
|
|
07e567f279 | ||
|
|
9abe5c01fe | ||
|
|
c8af9c096b | ||
|
|
9db65bc3b7 | ||
|
|
0e4c37ed71 | ||
|
|
4564182894 | ||
|
|
1311b0d18b | ||
|
|
94c023d810 | ||
|
|
57060aa509 | ||
|
|
8eb5b26edb | ||
|
|
03e52562ed | ||
|
|
2384a00696 | ||
|
|
22699d2c2a | ||
|
|
deb4909f0d | ||
|
|
8094067759 | ||
|
|
c170602f3a | ||
|
|
533e37e6ee | ||
|
|
ce771791b6 | ||
|
|
e9e5e75954 | ||
|
|
402cd62fda | ||
|
|
9b7559b2dc | ||
|
|
c7e1cae9d9 | ||
|
|
e6a2e2e6e5 | ||
|
|
e94d84bed1 | ||
|
|
2225eb712a | ||
|
|
28370cd7c3 | ||
|
|
61052df95d | ||
|
|
e123524397 | ||
|
|
75348e273a | ||
|
|
e6d8130c65 | ||
|
|
c23f65dd8f | ||
|
|
7265dbc78b | ||
|
|
bd25256efb | ||
|
|
1a2ed8c3f7 | ||
|
|
6ba6ddddc5 | ||
|
|
a2b6cab69e | ||
|
|
4adef7844d | ||
|
|
d407bb0213 | ||
|
|
1fd1c3516e | ||
|
|
a0932f979c | ||
|
|
9ab75b646f | ||
|
|
bc54f357a5 | ||
|
|
5aa1b1ebf2 | ||
|
|
c92f2bd837 | ||
|
|
fe2263e67d | ||
|
|
31be933571 | ||
|
|
86b348f0c3 |
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[alias]
|
||||
xtask = "run --manifest-path ./tasks/Cargo.toml --"
|
||||
4
.github/workflows/aws_tfhe_tests.yml
vendored
4
.github/workflows/aws_tfhe_tests.yml
vendored
@@ -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
|
||||
|
||||
120
.github/workflows/aws_tfhe_tests_w_gpu.yml
vendored
120
.github/workflows/aws_tfhe_tests_w_gpu.yml
vendored
@@ -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 }}
|
||||
4
.github/workflows/boolean_benchmark.yml
vendored
4
.github/workflows/boolean_benchmark.yml
vendored
@@ -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
|
||||
|
||||
28
.github/workflows/cargo_build.yml
vendored
28
.github/workflows/cargo_build.yml
vendored
@@ -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
|
||||
|
||||
20
.github/workflows/m1_tests.yml
vendored
20
.github/workflows/m1_tests.yml
vendored
@@ -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
|
||||
|
||||
4
.github/workflows/shortint_benchmark.yml
vendored
4
.github/workflows/shortint_benchmark.yml
vendored
@@ -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
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["tfhe"]
|
||||
members = ["tfhe", "tasks"]
|
||||
|
||||
[profile.bench]
|
||||
lto = "fat"
|
||||
|
||||
70
Makefile
70
Makefile
@@ -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
|
||||
|
||||
14
ci/slab.toml
14
ci/slab.toml
@@ -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
60
scripts/check_cargo_min_ver.sh
Executable 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
|
||||
@@ -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
12
tasks/Cargo.toml
Normal 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"
|
||||
453
tasks/src/format_latex_doc.rs
Normal file
453
tasks/src/format_latex_doc.rs
Normal 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
88
tasks/src/main.rs
Normal 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
50
tasks/src/utils.rs
Normal 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()
|
||||
}
|
||||
@@ -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]
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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/"
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
310
tfhe/src/boolean/engine/bootstrapping.rs
Normal file
310
tfhe/src/boolean/engine/bootstrapping.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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>>;
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}),
|
||||
));
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
#[cfg(feature = "boolean-c-api")]
|
||||
pub mod boolean;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
317
tfhe/src/core_crypto/algorithms/ggsw_encryption.rs
Normal file
317
tfhe/src/core_crypto/algorithms/ggsw_encryption.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
681
tfhe/src/core_crypto/algorithms/glwe_encryption.rs
Normal file
681
tfhe/src/core_crypto/algorithms/glwe_encryption.rs
Normal 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
|
||||
}
|
||||
128
tfhe/src/core_crypto/algorithms/glwe_sample_extraction.rs
Normal file
128
tfhe/src/core_crypto/algorithms/glwe_sample_extraction.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
310
tfhe/src/core_crypto/algorithms/lwe_bootstrap_key_generation.rs
Normal file
310
tfhe/src/core_crypto/algorithms/lwe_bootstrap_key_generation.rs
Normal 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>()
|
||||
}
|
||||
}
|
||||
791
tfhe/src/core_crypto/algorithms/lwe_encryption.rs
Normal file
791
tfhe/src/core_crypto/algorithms/lwe_encryption.rs
Normal 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
|
||||
}
|
||||
190
tfhe/src/core_crypto/algorithms/lwe_keyswitch.rs
Normal file
190
tfhe/src/core_crypto/algorithms/lwe_keyswitch.rs
Normal 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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
159
tfhe/src/core_crypto/algorithms/lwe_keyswitch_key_generation.rs
Normal file
159
tfhe/src/core_crypto/algorithms/lwe_keyswitch_key_generation.rs
Normal 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
|
||||
}
|
||||
85
tfhe/src/core_crypto/algorithms/lwe_linear_algebra.rs
Normal file
85
tfhe/src/core_crypto/algorithms/lwe_linear_algebra.rs
Normal 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);
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
102
tfhe/src/core_crypto/algorithms/lwe_public_key_generation.rs
Normal file
102
tfhe/src/core_crypto/algorithms/lwe_public_key_generation.rs
Normal 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
|
||||
}
|
||||
67
tfhe/src/core_crypto/algorithms/lwe_secret_key_generation.rs
Normal file
67
tfhe/src/core_crypto/algorithms/lwe_secret_key_generation.rs
Normal 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())
|
||||
}
|
||||
356
tfhe/src/core_crypto/algorithms/lwe_wopbs.rs
Normal file
356
tfhe/src/core_crypto/algorithms/lwe_wopbs.rs
Normal 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,
|
||||
)
|
||||
}
|
||||
9
tfhe/src/core_crypto/algorithms/misc.rs
Normal file
9
tfhe/src/core_crypto/algorithms/misc.rs
Normal 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)
|
||||
}
|
||||
39
tfhe/src/core_crypto/algorithms/mod.rs
Normal file
39
tfhe/src/core_crypto/algorithms/mod.rs
Normal 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::*;
|
||||
600
tfhe/src/core_crypto/algorithms/polynomial_algorithms.rs
Normal file
600
tfhe/src/core_crypto/algorithms/polynomial_algorithms.rs
Normal 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>()
|
||||
}
|
||||
}
|
||||
303
tfhe/src/core_crypto/algorithms/slice_algorithms.rs
Normal file
303
tfhe/src/core_crypto/algorithms/slice_algorithms.rs
Normal 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));
|
||||
}
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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};
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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::*;
|
||||
@@ -1,2 +0,0 @@
|
||||
pub mod engines;
|
||||
pub mod entities;
|
||||
@@ -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};
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
//! GLWE ciphertexts and ciphertext vectors with Cuda.
|
||||
|
||||
pub(crate) mod ciphertext;
|
||||
@@ -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 {}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
//! LWE ciphertexts and ciphertext vectors with Cuda.
|
||||
|
||||
pub(crate) mod ciphertext;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
pub mod crypto;
|
||||
pub mod device;
|
||||
pub mod pointers;
|
||||
pub mod vec;
|
||||
pub mod wopbs;
|
||||
@@ -1,5 +0,0 @@
|
||||
use std::ffi::c_void;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub struct StreamPointer(pub *mut c_void);
|
||||
@@ -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 vector’s buffer.
|
||||
pub fn as_c_ptr(&self) -> *const c_void {
|
||||
self.ptr as *const c_void
|
||||
}
|
||||
|
||||
/// Returns an unsafe mutable pointer to the vector’s 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) };
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user