feat(tfhe): new tfhe-rs package, initial commit

This commit is contained in:
Arthur Meyre
2022-10-21 09:46:35 +02:00
commit 74c4dbf781
399 changed files with 74872 additions and 0 deletions

17
.config/nextest.toml Normal file
View File

@@ -0,0 +1,17 @@
[profile.ci]
# Print out output for failing tests as soon as they fail, and also at the end
# of the run (for easy scrollability).
failure-output = "final"
fail-fast = false
retries = 0
slow-timeout = "5m"
[[profile.ci.overrides]]
filter = 'test(/^.*param_message_1_carry_[567]$/) or test(/^.*param_message_4_carry_4$/)'
retries = 3
[[profile.ci.overrides]]
filter = 'test(/^.*param_message_[23]_carry_[23]$/)'
retries = 1

1
.gitbook.yaml Normal file
View File

@@ -0,0 +1 @@
root: ./tfhe/docs

30
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,30 @@
---
name: Bug report
about: Report a problem with concrete
title: ''
labels: triage_required
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behaviour
1. first step
2. second step
3. etc
**Expected behaviour**
A clear and concise description of what you expected to happen.
**Evidence**
If applicable, add material to help explain your problem (e.g. screenshots, logs, artifacts, etc.).
**Configuration(please complete the following information):**
- OS: [e.g. Ubuntu 20.04]
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for concrete
title: ''
labels: feature_request
assignees: ''
---
**What is the problem you want to solve and can not with the current version?**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

102
.github/workflows/aws_tfhe_tests.yml vendored Normal file
View File

@@ -0,0 +1,102 @@
name: AWS Tests on CPU
env:
CARGO_TERM_COLOR: always
ACTION_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
RUSTFLAGS: "-C target-cpu=native"
on:
# Allows you to run this workflow manually from the Actions tab as an alternative.
workflow_dispatch:
# All the inputs are provided by Slab
inputs:
instance_id:
description: "AWS instance ID"
type: string
instance_image_id:
description: "AWS instance AMI ID"
type: string
instance_type:
description: "AWS instance product type"
type: string
runner_name:
description: "Action runner name"
type: string
jobs:
shortint-tests:
concurrency:
group: ${{ github.ref }}_${{ github.event.inputs.instance_image_id }}_${{ github.event.inputs.instance_type }}
cancel-in-progress: true
runs-on: ${{ github.event.inputs.runner_name }}
steps:
# Step used for log purpose.
- name: Instance configuration used
run: |
echo "ID: ${{ github.event.inputs.instance_id }}"
echo "AMI: ${{ github.event.inputs.instance_image_id }}"
echo "Type: ${{ github.event.inputs.instance_type }}"
- uses: actions/checkout@v2
- name: Set up home
run: |
echo "HOME=/home/ubuntu" >> "${GITHUB_ENV}"
- name: Install latest stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
default: true
- name: Run core tests
run: |
make test_core_crypto
- name: Run C API tests
run: |
make test_c_api
- name: Run user docs tests
run: |
make test_user_doc
- name: Install AWS CLI
run: |
apt update
apt install -y awscli
- name: Configure AWS credentials from Test account
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_IAM_ID }}
aws-secret-access-key: ${{ secrets.AWS_IAM_KEY }}
role-to-assume: concrete-lib-ci
aws-region: eu-west-3
role-duration-seconds: 10800
- name: Download keys locally
run: aws s3 cp --recursive --no-progress s3://concrete-libs-keycache ./keys
- name: Gen Keys if required
run: |
make gen_key_cache
- name: Sync keys
run: aws s3 sync ./keys s3://concrete-libs-keycache
- name: Run shortint tests
run: |
make test_shortint_ci
- 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: "Shortint tests finished with status: ${{ job.status }}. (${{ env.ACTION_RUN_URL }})"
SLACK_USERNAME: ${{ secrets.BOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

View File

@@ -0,0 +1,113 @@
# 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
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 }}"
- 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 }}

69
.github/workflows/cargo_build.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: Cargo Build
on:
pull_request:
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-C target-cpu=native"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true
jobs:
cargo-builds:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
fail-fast: false
steps:
- uses: actions/checkout@v2
- name: Get rust toolchain to use for checks and lints
id: toolchain
run: |
echo "rs-toolchain=$(make rs_toolchain)" >> "${GITHUB_OUTPUT}"
- name: Check format
run: |
make check_fmt
- name: Build doc
run: |
make doc
- name: Clippy boolean
run: |
make clippy_boolean
- 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

33
.github/workflows/check_commit.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
# Check commit and PR compliance
name: Check commit and PR compliance
on:
pull_request:
branches:
- main
- dev
jobs:
check-commit-pr:
name: Check commit and PR
runs-on: ubuntu-latest
steps:
- name: Check first line
uses: gsactions/commit-message-checker@v1
with:
pattern: '^((feat|fix|chore|refactor|style|test|docs|doc)\(\w+\)\:) .+$'
flags: "gs"
error: 'Your first line has to contain a commit type and scope like "feat(my_feature): msg".'
excludeDescription: "true" # optional: this excludes the description body of a pull request
excludeTitle: "true" # optional: this excludes the title of a pull request
checkAllCommitMessages: "true" # optional: this checks all commits associated with a pull request
accessToken: ${{ secrets.GITHUB_TOKEN }} # github access token is only required if checkAllCommitMessages is true
- name: Check line length
uses: gsactions/commit-message-checker@v1
with:
pattern: '(^.{0,74}$\r?\n?){0,20}'
flags: "gm"
error: "The maximum line length of 74 characters is exceeded."
excludeDescription: "true" # optional: this excludes the description body of a pull request
excludeTitle: "true" # optional: this excludes the title of a pull request
checkAllCommitMessages: "true" # optional: this checks all commits associated with a pull request
accessToken: ${{ secrets.GITHUB_TOKEN }} # github access token is only required if checkAllCommitMessages is true

128
.github/workflows/m1_tests.yml vendored Normal file
View File

@@ -0,0 +1,128 @@
name: Tests on M1 CPU
on:
workflow_dispatch:
pull_request:
types: [labeled]
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-C target-cpu=native"
ACTION_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true
jobs:
cargo-builds:
if: "github.event_name != 'pull_request' || contains(github.event.label.name, 'm1_test')"
runs-on: ["self-hosted", "m1mac"]
steps:
- uses: actions/checkout@v2
- name: Install latest stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
default: true
- name: Build doc
run: |
make doc
- name: Clippy boolean
run: |
make clippy_boolean
- 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: Test tfhe-rs/boolean with cpu
run: |
make test_boolean
- name: Run core tests
run: |
make test_core_crypto
- name: Run C API tests
run: |
make test_c_api
- name: Run user docs tests
run: |
make test_user_doc
- name: Configure AWS credentials from Test account
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_IAM_ID }}
aws-secret-access-key: ${{ secrets.AWS_IAM_KEY }}
role-to-assume: concrete-lib-ci
aws-region: eu-west-3
role-duration-seconds: 10800
- name: Download keys locally
run: aws s3 cp --recursive --no-progress s3://concrete-libs-keycache ./keys
- name: Gen Keys if required
run: |
make gen_key_cache
- name: Sync keys
run: aws s3 sync ./keys s3://concrete-libs-keycache
- name: Run shortint tests
run: |
make test_shortint_ci
remove_label:
name: Remove m1_test label
runs-on: ubuntu-latest
needs:
- cargo-builds
if: ${{ always() }}
steps:
- uses: actions-ecosystem/action-remove-labels@v1
with:
labels: m1_test
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Slack Notification
if: ${{ always() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
env:
SLACK_COLOR: ${{ needs.cargo-builds.result }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
SLACK_ICON: https://pbs.twimg.com/profile_images/1274014582265298945/OjBKP9kn_400x400.png
SLACK_MESSAGE: "M1 tests finished with status: ${{ needs.cargo-builds.result }}. (${{ env.ACTION_RUN_URL }})"
SLACK_USERNAME: ${{ secrets.BOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

9
.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
target/
.idea/
.vscode/
# Path we use for internal-keycache during tests
keys/
**/Cargo.lock
**/*.bin

9
Cargo.toml Normal file
View File

@@ -0,0 +1,9 @@
[workspace]
resolver = "2"
members = ["tfhe"]
[profile.bench]
lto = "fat"
[profile.release]
lto = "fat"

33
LICENSE Normal file
View File

@@ -0,0 +1,33 @@
BSD 3-Clause Clear License
Copyright © 2022 ZAMA.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
3. Neither the name of ZAMA nor the names of its contributors may be used to endorse
or promote products derived from this software without specific prior written permission.
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE*.
THIS SOFTWARE IS PROVIDED BY THE ZAMA AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
ZAMA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*In addition to the rights carried by this license, ZAMA grants to the user a non-exclusive,
free and non-commercial license on all patents filed in its name relating to the open-source
code (the "Patents") for the sole purpose of evaluation, development, research, prototyping
and experimentation.

151
Makefile Normal file
View File

@@ -0,0 +1,151 @@
SHELL:=$(shell /usr/bin/env which bash)
RS_CHECK_TOOLCHAIN:=$(shell cat toolchain.txt)
CARGO_RS_CHECK_TOOLCHAIN:=+$(RS_CHECK_TOOLCHAIN)
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)
# 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
.PHONY: rs_check_toolchain # Echo the rust toolchain used for checks
rs_check_toolchain:
@echo $(RS_CHECK_TOOLCHAIN)
.PHONY: rs_build_toolchain # Echo the rust toolchain used for builds
rs_build_toolchain:
@echo $(RS_BUILD_TOOLCHAIN)
.PHONY: install_rs_check_toolchain # Install the toolchain used for checks
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/"
.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 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/"
.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."
.PHONY: fmt # Format rust code
fmt: install_rs_check_toolchain
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt
.PHONT: check_fmt # Check rust code format
check_fmt: install_rs_check_toolchain
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt --check
.PHONY: clippy_boolean # Run clippy lints enabling the boolean features
clippy_boolean: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy \
--features=$(TARGET_ARCH_FEATURE),boolean \
-p tfhe -- --no-deps -D warnings
.PHONY: clippy_shortint # Run clippy lints enabling the shortint features
clippy_shortint: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy \
--features=$(TARGET_ARCH_FEATURE),shortint \
-p tfhe -- --no-deps -D warnings
.PHONY: clippy # Run clippy lints enabling the boolean, shortint
clippy: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint \
-p tfhe -- --no-deps -D warnings
.PHONY: clippy_c_api # Run clippy lints enabling the boolean, shortint and the C API
clippy_c_api: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy \
--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: gen_key_cache # Run the script to generate keys and cache them for shortint tests
gen_key_cache: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) run --release \
--example generates_test_keys \
--features=$(TARGET_ARCH_FEATURE),shortint,internal-keycache -p tfhe
.PHONY: build_boolean # Build with boolean enabled
build_boolean: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) build --release \
--features=$(TARGET_ARCH_FEATURE),boolean -p tfhe
.PHONY: build_shortint # Build with shortint enabled
build_shortint: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) build --release \
--features=$(TARGET_ARCH_FEATURE),shortint -p tfhe
.PHONY: build_boolean_and_shortint # Build with boolean and shortint enabled
build_boolean_and_shortint: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) build --release \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint -p tfhe
.PHONY: build_c_api # Build the C API for boolean and shortint
build_c_api: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) build --release
--features=$(TARGET_ARCH_FEATURE),boolean-c-api,shortint-c-api -p tfhe
.PHONY: test_core_crypto # Run the tests of the core_crypto module
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)
.PHONY: test_shortint_ci # Run the tests for shortint ci
test_shortint_ci: install_rs_build_toolchain install_cargo_nextest
./scripts/shortint-tests.sh $(CARGO_RS_BUILD_TOOLCHAIN)
.PHONY: test_shortint # Run all the tests for shortint
test_shortint: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --release \
--features=$(TARGET_ARCH_FEATURE),shortint,internal-keycache -p tfhe -- shortint::
.PHONY: test_user_doc # Run tests from the .md documentation
test_user_doc: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --release --doc \
--features=$(TARGET_ARCH_FEATURE),shortint,boolean,internal-keycache -p tfhe \
-- test_user_docs::
.PHONY: doc # Build rust doc
doc: install_rs_check_toolchain
RUSTDOCFLAGS="--html-in-header katex-header.html" \
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" doc \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint --no-deps
.PHONY: help # Generate list of targets with descriptions
help:
@grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1\t\2/' | expand -t30 | sort

133
README.md Normal file
View File

@@ -0,0 +1,133 @@
<p align="center">
<!-- product name logo -->
<img width=600 src="https://user-images.githubusercontent.com/86411313/201107820-b1b861be-6b3f-46cc-bccd-ed051201781a.png">
</p>
<p align="center">
<!-- Version badge using shields.io -->
<a href="https://github.com/zama-ai/tfhe-rs/releases">
<img src="https://img.shields.io/github/v/release/zama-ai/tfhe-rs?style=flat-square">
</a>
<!-- Link to docs badge using shields.io -->
<a href="https://docs.zama.ai/tfhe-rs">
<img src="https://img.shields.io/badge/read-documentation-yellow?style=flat-square">
</a>
<!-- Community forum badge using shields.io -->
<a href="https://community.zama.ai">
<img src="https://img.shields.io/badge/community%20forum-online-brightgreen?style=flat-square">
</a>
<!-- Open source badge using shields.io -->
<a href="https://docs.zama.ai/tfhe-rs/developers/contributing">
<img src="https://img.shields.io/badge/we're%20open%20source-contributing.md-blue?style=flat-square">
</a>
<!-- Follow on twitter badge using shields.io -->
<a href="https://twitter.com/zama_fhe">
<img src="https://img.shields.io/twitter/follow/zama_fhe?color=blue&style=flat-square">
</a>
</p>
**TFHE-rs** is a pure Rust implementation of TFHE for boolean and small integer
arithmetics over encrypted data. It includes:
- a **Rust** API
- a **C** API
- and a **client-side WASM** API
**TFHE-rs** is meant for developers and researchers who want full control over
what they can do with TFHE, while not having to worry about the low level
implementation. The goal is to have a stable, simple, high-performance and
production-ready library for all the advanced features of TFHE.
## Getting Started
To use `TFHE-rs` in your project, you first need to add it as a dependency in your `Cargo.toml`:
```toml
tfhe = { version = "0.1.0", features = [ "boolean","shortint","x86_64-unix" ] }
```
Here is a full example evaluating a Boolean circuit:
```rust
use tfhe::boolean::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (mut client_key, mut server_key) = gen_keys();
// We use the client secret key to encrypt two messages:
let ct_1 = client_key.encrypt(true);
let ct_2 = client_key.encrypt(false);
// We use the server public key to execute a boolean circuit:
// if ((NOT ct_2) NAND (ct_1 AND ct_2)) then (NOT ct_2) else (ct_1 AND ct_2)
let ct_3 = server_key.not(&ct_2);
let ct_4 = server_key.and(&ct_1, &ct_2);
let ct_5 = server_key.nand(&ct_3, &ct_4);
let ct_6 = server_key.mux(&ct_5, &ct_3, &ct_4);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_6);
assert_eq!(output, true);
}
```
Another example of how the library can be used with shortints:
```rust
use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys(Parameters::default());
let msg1 = 1;
let msg2 = 0;
let modulus = client_key.parameters.message_modulus.0;
// We use the client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
// We use the server public key to execute an integer circuit:
let ct_3 = server_key.unchecked_add(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_3);
assert_eq!(output, (msg1 + msg2) % modulus as u64);
}
```
## Contributing
There are two ways to contribute to TFHE-rs:
- you can open issues to report bugs or typos and to suggest new ideas
- you can ask to become an official contributor by emailing [hello@zama.ai](mailto:hello@zama.ai).
(becoming an approved contributor involves signing our Contributor License Agreement (CLA))
Only approved contributors can send pull requests, so please make sure to get in touch before you do!
## Credits
This library uses several dependencies and we would like to thank the contributors of those
libraries.
## License
This software is distributed under the BSD-3-Clause-Clear license. If you have any questions,
please contact us at `hello@zama.ai`.
## Disclaimers
### Security Estimation
Security estimations are done using the
[Lattice Estimator](https://github.com/malb/lattice-estimator)
with `red_cost_model = reduction.RC.BDGL16`.
When a new update is published in the Lattice Estimator, we update parameters accordingly.
### Side-Channel Attacks
Mitigation for side channel attacks have not yet been implemented in TFHE-rs,
and will be released in upcoming versions.

21
ci/slab.toml Normal file
View File

@@ -0,0 +1,21 @@
[profile.cpu-big]
region = "eu-west-3"
image_id = "ami-04deffe45b5b236fd"
instance_type = "c5a.8xlarge"
[profile.gpu]
region = "us-east-1"
image_id = "ami-0ae662beb44082155"
instance_type = "p3.2xlarge"
subnet_id = "subnet-8123c9e7"
security_group = "sg-0466d33ced960ba35"
[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)"

15
katex-header.html Normal file
View File

@@ -0,0 +1,15 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.15.3/dist/katex.min.css" integrity="sha384-KiWOvVjnN8qwAZbuQyWDIbfCLFhLXNETzBQjA/92pIowpC0d2O3nppDGQVgwd2nB" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/katex@0.15.3/dist/katex.min.js" integrity="sha384-0fdwu/T/EQMsQlrHCCHoH10pkPLlKA1jL5dFyUOvB3lfeT2540/2g6YgSi2BL14p" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/katex@0.15.3/dist/contrib/auto-render.min.js" integrity="sha384-+XBljXPPiv+OzfbB3cVmLHf4hdUFHlWNZN5spNQ7rmHTXpd7WvJum6fIACpNNfIR" crossorigin="anonymous"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
renderMathInElement(document.body, {
delimiters: [
{left: "$$", right: "$$", display: true},
{left: "\\(", right: "\\)", display: false},
{left: "$", right: "$", display: false},
{left: "\\[", right: "\\]", display: true}
]
});
});
</script>

5
rustfmt.toml Normal file
View File

@@ -0,0 +1,5 @@
unstable_features = true
imports_granularity="Module"
format_code_in_doc_comments = true
wrap_comments = true
comment_width = 100

20
scripts/c_api_tests.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -e
CURR_DIR="$(dirname "$0")"
ARCH_FEATURE="$("${CURR_DIR}/get_arch_feature.sh")"
REPO_ROOT="${CURR_DIR}/.."
TFHE_BUILD_DIR="${REPO_ROOT}/tfhe/build/"
mkdir -p "${TFHE_BUILD_DIR}"
cd "${TFHE_BUILD_DIR}"
cmake .. -DCMAKE_BUILD_TYPE=RELEASE
RUSTFLAGS="-C target-cpu=native" cargo ${1:+"${1}"} build \
--release --features="${ARCH_FEATURE}",boolean-c-api,shortint-c-api -p tfhe
make -j
make "test"

19
scripts/get_arch_feature.sh Executable file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -e
ARCH_FEATURE=x86_64
IS_AARCH64="$( (uname -a | grep -c arm64) || true)"
if [[ "${IS_AARCH64}" != "0" ]]; then
ARCH_FEATURE=aarch64
fi
UNAME="$(uname)"
if [[ "${UNAME}" == "Linux" || "${UNAME}" == "Darwin" ]]; then
ARCH_FEATURE="${ARCH_FEATURE}-unix"
fi
echo "${ARCH_FEATURE}"

60
scripts/shortint-tests.sh Executable file
View File

@@ -0,0 +1,60 @@
#!/bin/bash
set -e
CURR_DIR="$(dirname "$0")"
ARCH_FEATURE="$("${CURR_DIR}/get_arch_feature.sh")"
nproc_bin=nproc
# macOS detects CPUs differently
if [[ $(uname) == "Darwin" ]]; then
nproc_bin="sysctl -n hw.logicalcpu"
fi
n_threads="$(${nproc_bin})"
if uname -a | grep "arm64"; then
if [[ $(uname) == "Darwin" ]]; then
# Keys are 4.7 gigs at max, CI M1 macs only has 8 gigs of RAM
n_threads=1
fi
else
# Keys are 4.7 gigs at max, test machine has 32 gigs of RAM
n_threads=6
fi
filter_expression=''\
'('\
' test(/^shortint::server_key::.*_param_message_1_carry_1$/)'\
'or test(/^shortint::server_key::.*_param_message_1_carry_2$/)'\
'or test(/^shortint::server_key::.*_param_message_1_carry_3$/)'\
'or test(/^shortint::server_key::.*_param_message_1_carry_4$/)'\
'or test(/^shortint::server_key::.*_param_message_1_carry_5$/)'\
'or test(/^shortint::server_key::.*_param_message_1_carry_6$/)'\
'or test(/^shortint::server_key::.*_param_message_2_carry_2$/)'\
'or test(/^shortint::server_key::.*_param_message_3_carry_3$/)'\
'or test(/^shortint::server_key::.*_param_message_4_carry_4$/)'\
')'\
'and not test(~smart_add_and_mul)' # This test is too slow
export RUSTFLAGS="-C target-cpu=native"
# Run tests only no examples or benches
cargo ${1:+"${1}"} nextest run \
--tests \
--release \
--package tfhe \
--profile ci \
--features="${ARCH_FEATURE}",shortint,internal-keycache \
--test-threads "${n_threads}" \
-E "${filter_expression}"
cargo ${1:+"${1}"} test \
--release \
--package tfhe \
--features="${ARCH_FEATURE}",shortint,internal-keycache \
--doc \
shortint::
echo "Test ran in $SECONDS seconds"

1
tfhe/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
build/

6
tfhe/CMakeLists.txt Normal file
View File

@@ -0,0 +1,6 @@
# tfhe/CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(tfhe-c-api C)
set(SOURCE c_api_tests/*.c)
enable_testing()
add_subdirectory(c_api_tests)

180
tfhe/Cargo.toml Normal file
View File

@@ -0,0 +1,180 @@
[package]
name = "tfhe"
version = "0.1.0"
edition = "2021"
readme = "../README.md"
keywords = ["fully", "homomorphic", "encryption", "fhe", "cryptography"]
homepage = "https://zama.ai/"
documentation = "https://docs.zama.ai/tfhe-rs"
repository = "https://github.com/zama-ai/tfhe-rs"
license = "BSD-3-Clause-Clear"
description = "Concrete is a fully homomorphic encryption (FHE) library that implements Zama's variant of TFHE."
build = "build.rs"
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"
kolmogorov_smirnov = "1.1.0"
paste = "1.0.7"
lazy_static = { version = "1.4.0" }
criterion = "0.3.5"
doc-comment = "0.3.3"
# Used in user documentation
bincode = "1.3.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 }
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 }
once_cell = "1.13"
paste = "1.0.7"
fs2 = { version = "0.4.3", optional = true }
# wasm deps
wasm-bindgen = { version = "0.2.63", features = [
"serde-serialize",
], optional = true }
js-sys = { version = "0.3", optional = true }
console_error_panic_hook = { version = "0.1.7", optional = true }
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"]
internal-keycache = ["lazy_static", "fs2"]
__c_api = ["cbindgen", "minimal_core_crypto_features"]
boolean-c-api = ["boolean", "__c_api"]
shortint-c-api = ["shortint", "__c_api"]
__wasm_api = [
"wasm-bindgen",
"js-sys",
"console_error_panic_hook",
"serde-wasm-bindgen",
"getrandom",
"getrandom/js",
]
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"]
# 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",
]
# 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"]
# 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-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-unix = ["aarch64", "seeder_unix"]
[package.metadata.docs.rs]
# TODO: manage builds for docs.rs based on their documentation https://docs.rs/about
features = ["x86_64-unix", "boolean", "shortint"]
rustdoc-args = ["--html-in-header", "katex-header.html"]
###########
# #
# Benches #
# #
###########
[[bench]]
name = "boolean-bench"
path = "benches/boolean/bench.rs"
harness = false
required-features = ["boolean", "internal-keycache"]
[[bench]]
name = "shortint-bench"
path = "benches/shortint/bench.rs"
harness = false
required-features = ["shortint", "internal-keycache"]
[[example]]
name = "generates_test_keys"
required-features = ["shortint", "internal-keycache"]
[[example]]
name = "micro_bench_and"
required-features = ["boolean"]
[lib]
crate-type = ["lib", "staticlib", "cdylib"]

32
tfhe/LICENSE Normal file
View File

@@ -0,0 +1,32 @@
BSD 3-Clause Clear License
Copyright © 2022 ZAMA.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
3. Neither the name of ZAMA nor the names of its contributors may be used to endorse
or promote products derived from this software without specific prior written permission.
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE*.
THIS SOFTWARE IS PROVIDED BY THE ZAMA AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
ZAMA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*In addition to the rights carried by this license, ZAMA grants to the user a non-exclusive,
free and non-commercial license on all patents filed in its name relating to the open-source
code (the "Patents") for the sole purpose of evaluation, development, research, prototyping
and experimentation.

View File

@@ -0,0 +1,60 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use tfhe::boolean::client_key::ClientKey;
use tfhe::boolean::parameters::{BooleanParameters, DEFAULT_PARAMETERS, TFHE_LIB_PARAMETERS};
use tfhe::boolean::prelude::BinaryBooleanGates;
use tfhe::boolean::server_key::ServerKey;
criterion_group!(
gates_benches,
bench_default_parameters,
bench_tfhe_lib_parameters
);
criterion_main!(gates_benches);
// Put all `bench_function` in one place
// so the keygen is only run once per parameters saving time.
fn bench_gates(c: &mut Criterion, params: BooleanParameters, parameter_name: &str) {
let cks = ClientKey::new(&params);
let sks = ServerKey::new(&cks);
let ct1 = cks.encrypt(true);
let ct2 = cks.encrypt(false);
let ct3 = cks.encrypt(true);
let id = format!("AND gate {}", parameter_name);
c.bench_function(&id, |b| b.iter(|| black_box(sks.and(&ct1, &ct2))));
let id = format!("NAND gate {}", parameter_name);
c.bench_function(&id, |b| b.iter(|| black_box(sks.nand(&ct1, &ct2))));
let id = format!("OR gate {}", parameter_name);
c.bench_function(&id, |b| b.iter(|| black_box(sks.or(&ct1, &ct2))));
let id = format!("XOR gate {}", parameter_name);
c.bench_function(&id, |b| b.iter(|| black_box(sks.xor(&ct1, &ct2))));
let id = format!("XNOR gate {}", parameter_name);
c.bench_function(&id, |b| b.iter(|| black_box(sks.xnor(&ct1, &ct2))));
let id = format!("NOT gate {}", parameter_name);
c.bench_function(&id, |b| b.iter(|| black_box(sks.not(&ct1))));
let id = format!("MUX gate {}", parameter_name);
c.bench_function(&id, |b| b.iter(|| black_box(sks.mux(&ct1, &ct2, &ct3))));
}
#[cfg(not(feature = "cuda"))]
fn bench_default_parameters(c: &mut Criterion) {
bench_gates(c, DEFAULT_PARAMETERS, "DEFAULT_PARAMETERS");
}
#[cfg(feature = "cuda")]
fn bench_default_parameters(_: &mut Criterion) {
let _ = DEFAULT_PARAMETERS; // to avoid unused import warnings
println!("DEFAULT_PARAMETERS not benched as they are not compatible with the cuda feature.");
}
fn bench_tfhe_lib_parameters(c: &mut Criterion) {
bench_gates(c, TFHE_LIB_PARAMETERS, "TFHE_LIB_PARAMETERS");
}

View File

@@ -0,0 +1,232 @@
use criterion::{criterion_group, criterion_main, Criterion};
use tfhe::shortint::parameters::*;
use tfhe::shortint::{Ciphertext, Parameters, ServerKey};
use rand::Rng;
use tfhe::shortint::keycache::KEY_CACHE;
use tfhe::shortint::keycache::KEY_CACHE_WOPBS;
use tfhe::shortint::parameters::parameters_wopbs::WOPBS_PARAM_MESSAGE_4_NORM2_6;
macro_rules! named_param {
($param:ident) => {
(stringify!($param), $param)
};
}
const SERVER_KEY_BENCH_PARAMS: [(&str, Parameters); 4] = [
named_param!(PARAM_MESSAGE_1_CARRY_1),
named_param!(PARAM_MESSAGE_2_CARRY_2),
named_param!(PARAM_MESSAGE_3_CARRY_3),
named_param!(PARAM_MESSAGE_4_CARRY_4),
];
fn bench_server_key_binary_function<F>(c: &mut Criterion, bench_name: &str, binary_op: F)
where
F: Fn(&ServerKey, &mut Ciphertext, &mut Ciphertext),
{
let mut bench_group = c.benchmark_group(bench_name);
for (param_name, param) in SERVER_KEY_BENCH_PARAMS {
let keys = KEY_CACHE.get_from_param(param);
let (cks, sks) = (keys.client_key(), keys.server_key());
let mut rng = rand::thread_rng();
let modulus = 1_u64 << cks.parameters.message_modulus.0;
let clear_0 = rng.gen::<u64>() % modulus;
let clear_1 = rng.gen::<u64>() % modulus;
let mut ct_0 = cks.encrypt(clear_0);
let mut ct_1 = cks.encrypt(clear_1);
let bench_id = format!("{}::{}", bench_name, param_name);
bench_group.bench_function(&bench_id, |b| {
b.iter(|| {
binary_op(sks, &mut ct_0, &mut ct_1);
})
});
}
bench_group.finish()
}
fn bench_server_key_binary_scalar_function<F>(c: &mut Criterion, bench_name: &str, binary_op: F)
where
F: Fn(&ServerKey, &mut Ciphertext, u8),
{
let mut bench_group = c.benchmark_group(bench_name);
for (param_name, param) in SERVER_KEY_BENCH_PARAMS {
let keys = KEY_CACHE.get_from_param(param);
let (cks, sks) = (keys.client_key(), keys.server_key());
let mut rng = rand::thread_rng();
let modulus = 1_u64 << cks.parameters.message_modulus.0;
let clear_0 = rng.gen::<u64>() % modulus;
let clear_1 = rng.gen::<u64>() % modulus;
let mut ct_0 = cks.encrypt(clear_0);
let bench_id = format!("{}::{}", bench_name, param_name);
bench_group.bench_function(&bench_id, |b| {
b.iter(|| {
binary_op(sks, &mut ct_0, clear_1 as u8);
})
});
}
bench_group.finish()
}
fn carry_extract(c: &mut Criterion) {
let mut bench_group = c.benchmark_group("carry_extract");
for (param_name, param) in SERVER_KEY_BENCH_PARAMS {
let keys = KEY_CACHE.get_from_param(param);
let (cks, sks) = (keys.client_key(), keys.server_key());
let mut rng = rand::thread_rng();
let modulus = 1_u64 << cks.parameters.message_modulus.0;
let clear_0 = rng.gen::<u64>() % modulus;
let ct_0 = cks.encrypt(clear_0);
let bench_id = format!("ServerKey::carry_extract::{}", param_name);
bench_group.bench_function(&bench_id, |b| {
b.iter(|| {
sks.carry_extract(&ct_0);
})
});
}
bench_group.finish()
}
fn programmable_bootstrapping(c: &mut Criterion) {
let mut bench_group = c.benchmark_group("programmable_bootstrap");
for (param_name, param) in SERVER_KEY_BENCH_PARAMS {
let keys = KEY_CACHE.get_from_param(param);
let (cks, sks) = (keys.client_key(), keys.server_key());
let mut rng = rand::thread_rng();
let modulus = cks.parameters.message_modulus.0 as u64;
let acc = sks.generate_accumulator(|x| x);
let clear_0 = rng.gen::<u64>() % modulus;
let ctxt = cks.encrypt(clear_0);
let id = format!("ServerKey::programmable_bootstrap::{}", param_name);
bench_group.bench_function(&id, |b| {
b.iter(|| {
sks.keyswitch_programmable_bootstrap(&ctxt, &acc);
})
});
}
bench_group.finish();
}
fn bench_wopbs_param_message_8_norm2_5(c: &mut Criterion) {
let mut bench_group = c.benchmark_group("programmable_bootstrap");
let param = WOPBS_PARAM_MESSAGE_4_NORM2_6;
let keys = KEY_CACHE_WOPBS.get_from_param((param, param));
let (cks, _, wopbs_key) = (keys.client_key(), keys.server_key(), keys.wopbs_key());
let mut rng = rand::thread_rng();
let clear = rng.gen::<usize>() % param.message_modulus.0;
let mut ct = cks.encrypt_without_padding(clear as u64);
let vec_lut = wopbs_key.generate_lut_native_crt(&ct, |x| x);
let id = format!("Shortint WOPBS: {:?}", param);
bench_group.bench_function(&id, |b| {
b.iter(|| {
wopbs_key.programmable_bootstrapping_native_crt(&mut ct, &vec_lut);
})
});
bench_group.finish();
}
macro_rules! define_server_key_bench_fn (
($server_key_method:ident) => {
fn $server_key_method(c: &mut Criterion) {
bench_server_key_binary_function(
c,
concat!("ServerKey::", stringify!($server_key_method)),
|server_key, lhs, rhs| {
server_key.$server_key_method(lhs, rhs);
})
}
}
);
macro_rules! define_server_key_scalar_bench_fn (
($server_key_method:ident) => {
fn $server_key_method(c: &mut Criterion) {
bench_server_key_binary_scalar_function(
c,
concat!("ServerKey::", stringify!($server_key_method)),
|server_key, lhs, rhs| {
server_key.$server_key_method(lhs, rhs);
})
}
}
);
define_server_key_bench_fn!(unchecked_add);
define_server_key_bench_fn!(unchecked_sub);
define_server_key_bench_fn!(unchecked_mul_lsb);
define_server_key_bench_fn!(unchecked_mul_msb);
define_server_key_bench_fn!(smart_bitand);
define_server_key_bench_fn!(smart_bitor);
define_server_key_bench_fn!(smart_bitxor);
define_server_key_bench_fn!(smart_add);
define_server_key_bench_fn!(smart_sub);
define_server_key_bench_fn!(smart_mul_lsb);
define_server_key_scalar_bench_fn!(unchecked_scalar_add);
define_server_key_scalar_bench_fn!(unchecked_scalar_mul);
criterion_group!(
arithmetic_operation,
unchecked_add,
unchecked_sub,
unchecked_mul_lsb,
unchecked_mul_msb,
smart_bitand,
smart_bitor,
smart_bitxor,
smart_add,
smart_sub,
smart_mul_lsb,
carry_extract,
// programmable_bootstrapping,
// multivalue_programmable_bootstrapping
//bench_two_block_pbs
//wopbs_v0_norm2_2,
bench_wopbs_param_message_8_norm2_5,
programmable_bootstrapping
);
criterion_group!(
arithmetic_scalar_operation,
unchecked_scalar_add,
unchecked_scalar_mul,
);
criterion_main!(arithmetic_operation,); // arithmetic_scalar_operation,);

35
tfhe/build.rs Normal file
View File

@@ -0,0 +1,35 @@
// tfhe/build.rs
#[cfg(feature = "__c_api")]
fn gen_c_api() {
use std::env;
use std::path::PathBuf;
/// Find the location of the `target/` directory. Note that this may be
/// overridden by `cmake`, so we also need to check the `CARGO_TARGET_DIR`
/// variable.
fn target_dir() -> PathBuf {
if let Ok(target) = env::var("CARGO_TARGET_DIR") {
PathBuf::from(target)
} else {
PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("../target/release")
}
}
extern crate cbindgen;
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let package_name = env::var("CARGO_PKG_NAME").unwrap();
let output_file = target_dir()
.join(format!("{package_name}.h"))
.display()
.to_string();
cbindgen::generate(crate_dir)
.unwrap()
.write_to_file(output_file);
}
fn main() {
#[cfg(feature = "__c_api")]
gen_c_api()
}

View File

@@ -0,0 +1 @@
ColumnLimit: 100

View File

@@ -0,0 +1,37 @@
project(tfhe-c-api-tests)
cmake_minimum_required(VERSION 3.16)
set(TFHE_C_API_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../target/release/")
include_directories(${TFHE_C_API_RELEASE})
add_library(Tfhe STATIC IMPORTED)
set_target_properties(Tfhe PROPERTIES IMPORTED_LOCATION ${TFHE_C_API_RELEASE}/libtfhe.a)
if(APPLE)
find_library(SECURITY_FRAMEWORK Security)
if (NOT SECURITY_FRAMEWORK)
message(FATAL_ERROR "Security framework not found")
endif()
endif()
file(GLOB TEST_CASES test_*.c)
foreach (testsourcefile ${TEST_CASES})
get_filename_component(testname ${testsourcefile} NAME_WLE)
get_filename_component(groupname ${testsourcefile} DIRECTORY)
add_executable(${testname} ${testsourcefile})
add_test(
NAME ${testname}
COMMAND ${testname}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Testing
)
target_include_directories(${testname} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${testname} LINK_PUBLIC Tfhe m pthread dl)
if(APPLE)
target_link_libraries(${testname} LINK_PUBLIC ${SECURITY_FRAMEWORK})
endif()
target_compile_options(${testname} PRIVATE -Werror)
# Enabled asserts even in release mode
add_definitions(-UNDEBUG)
endforeach (testsourcefile ${TEST_CASES})

View File

@@ -0,0 +1,123 @@
#include "tfhe.h"
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <tgmath.h>
void test_default_keygen_w_serde(void) {
BooleanClientKey *cks = NULL;
BooleanServerKey *sks = NULL;
BooleanCiphertext *ct = NULL;
Buffer ct_ser_buffer = {.pointer = NULL, .length = 0};
BooleanCiphertext *deser_ct = NULL;
int gen_keys_ok = boolean_gen_keys_with_default_parameters(&cks, &sks);
assert(gen_keys_ok == 0);
int encrypt_ok = boolean_client_key_encrypt(cks, true, &ct);
assert(encrypt_ok == 0);
int ser_ok = boolean_serialize_ciphertext(ct, &ct_ser_buffer);
assert(ser_ok == 0);
BufferView deser_view = {.pointer = ct_ser_buffer.pointer, .length = ct_ser_buffer.length};
int deser_ok = boolean_deserialize_ciphertext(deser_view, &deser_ct);
assert(deser_ok == 0);
assert(deser_view.length == ct_ser_buffer.length);
for (size_t idx = 0; idx < deser_view.length; ++idx) {
assert(deser_view.pointer[idx] == ct_ser_buffer.pointer[idx]);
}
bool result = false;
int decrypt_ok = boolean_client_key_decrypt(cks, deser_ct, &result);
assert(decrypt_ok == 0);
assert(result == true);
destroy_boolean_client_key(cks);
destroy_boolean_server_key(sks);
destroy_boolean_ciphertext(ct);
destroy_boolean_ciphertext(deser_ct);
destroy_buffer(&ct_ser_buffer);
}
void test_predefined_keygen_w_serde(void) {
BooleanClientKey *cks = NULL;
BooleanServerKey *sks = NULL;
int gen_keys_ok = boolean_gen_keys_with_predefined_parameters_set(
BOOLEAN_PARAMETERS_SET_DEFAULT_PARAMETERS, &cks, &sks);
assert(gen_keys_ok == 0);
destroy_boolean_client_key(cks);
destroy_boolean_server_key(sks);
gen_keys_ok = boolean_gen_keys_with_predefined_parameters_set(
BOOLEAN_PARAMETERS_SET_THFE_LIB_PARAMETERS, &cks, &sks);
assert(gen_keys_ok == 0);
destroy_boolean_client_key(cks);
destroy_boolean_server_key(sks);
}
void test_custom_keygen(void) {
BooleanClientKey *cks = NULL;
BooleanServerKey *sks = NULL;
BooleanParameters *params = NULL;
int params_ok = boolean_create_parameters(10, 1, 1024, 10e-100, 10e-100, 3, 1, 4, 2, &params);
assert(params_ok == 0);
int gen_keys_ok = boolean_gen_keys_with_parameters(params, &cks, &sks);
assert(gen_keys_ok == 0);
destroy_boolean_parameters(params);
destroy_boolean_client_key(cks);
destroy_boolean_server_key(sks);
}
void test_public_keygen(void) {
BooleanClientKey *cks = NULL;
BooleanPublicKey *pks = NULL;
BooleanParameters *params = NULL;
BooleanCiphertext *ct = NULL;
int get_params_ok = boolean_get_parameters(BOOLEAN_PARAMETERS_SET_DEFAULT_PARAMETERS, &params);
assert(get_params_ok == 0);
int gen_keys_ok = boolean_gen_client_key(params, &cks);
assert(gen_keys_ok == 0);
int gen_pks = boolean_gen_public_key(cks, &pks);
assert(gen_pks == 0);
bool msg = true;
int encrypt_ok = boolean_public_key_encrypt(pks, msg, &ct);
assert(encrypt_ok == 0);
bool result = false;
int decrypt_ok = boolean_client_key_decrypt(cks, ct, &result);
assert(decrypt_ok == 0);
assert(result == true);
destroy_boolean_parameters(params);
destroy_boolean_client_key(cks);
destroy_boolean_public_key(pks);
destroy_boolean_ciphertext(ct);
}
int main(void) {
test_default_keygen_w_serde();
test_predefined_keygen_w_serde();
test_custom_keygen();
test_public_keygen();
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,403 @@
#include "tfhe.h"
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <tgmath.h>
void test_binary_boolean_function(BooleanClientKey *cks, BooleanServerKey *sks,
bool (*c_fun)(bool, bool),
int (*api_fun)(const BooleanServerKey *,
const BooleanCiphertext *,
const BooleanCiphertext *, BooleanCiphertext **)) {
for (int idx_left_trivial = 0; idx_left_trivial < 2; ++idx_left_trivial) {
for (int idx_right_trivial = 0; idx_right_trivial < 2; ++idx_right_trivial) {
for (int idx_left = 0; idx_left < 2; ++idx_left) {
for (int idx_right = 0; idx_right < 2; ++idx_right) {
BooleanCiphertext *ct_left = NULL;
BooleanCiphertext *ct_right = NULL;
BooleanCiphertext *ct_result = NULL;
bool left = (bool)idx_left;
bool right = (bool)idx_right;
bool left_trivial = (bool)idx_left_trivial;
bool right_trivial = (bool)idx_right_trivial;
bool expected = c_fun(left, right);
if (left_trivial) {
int encrypt_left_ok = boolean_trivial_encrypt(left, &ct_left);
assert(encrypt_left_ok == 0);
} else {
int encrypt_left_ok = boolean_client_key_encrypt(cks, left, &ct_left);
assert(encrypt_left_ok == 0);
}
if (right_trivial) {
int encrypt_left_ok = boolean_trivial_encrypt(right, &ct_right);
assert(encrypt_left_ok == 0);
} else {
int encrypt_right_ok = boolean_client_key_encrypt(cks, right, &ct_right);
assert(encrypt_right_ok == 0);
}
int api_call_ok = api_fun(sks, ct_left, ct_right, &ct_result);
assert(api_call_ok == 0);
bool decrypted_result = false;
int decrypt_ok = boolean_client_key_decrypt(cks, ct_result, &decrypted_result);
assert(decrypt_ok == 0);
assert(decrypted_result == expected);
destroy_boolean_ciphertext(ct_left);
destroy_boolean_ciphertext(ct_right);
destroy_boolean_ciphertext(ct_result);
}
}
}
}
}
void test_binary_boolean_function_assign(
BooleanClientKey *cks, BooleanServerKey *sks, bool (*c_fun)(bool, bool),
int (*api_fun)(const BooleanServerKey *, BooleanCiphertext *, const BooleanCiphertext *)) {
for (int idx_left_trivial = 0; idx_left_trivial < 2; ++idx_left_trivial) {
for (int idx_right_trivial = 0; idx_right_trivial < 2; ++idx_right_trivial) {
for (int idx_left = 0; idx_left < 2; ++idx_left) {
for (int idx_right = 0; idx_right < 2; ++idx_right) {
BooleanCiphertext *ct_left_and_result = NULL;
BooleanCiphertext *ct_right = NULL;
bool left = (bool)idx_left;
bool right = (bool)idx_right;
bool left_trivial = (bool)idx_left_trivial;
bool right_trivial = (bool)idx_right_trivial;
bool expected = c_fun(left, right);
if (left_trivial) {
int encrypt_left_ok = boolean_trivial_encrypt(left, &ct_left_and_result);
assert(encrypt_left_ok == 0);
} else {
int encrypt_left_ok = boolean_client_key_encrypt(cks, left, &ct_left_and_result);
assert(encrypt_left_ok == 0);
}
if (right_trivial) {
int encrypt_left_ok = boolean_trivial_encrypt(right, &ct_right);
assert(encrypt_left_ok == 0);
} else {
int encrypt_right_ok = boolean_client_key_encrypt(cks, right, &ct_right);
assert(encrypt_right_ok == 0);
}
int api_call_ok = api_fun(sks, ct_left_and_result, ct_right);
assert(api_call_ok == 0);
bool decrypted_result = false;
int decrypt_ok = boolean_client_key_decrypt(cks, ct_left_and_result, &decrypted_result);
assert(decrypt_ok == 0);
assert(decrypted_result == expected);
destroy_boolean_ciphertext(ct_left_and_result);
destroy_boolean_ciphertext(ct_right);
}
}
}
}
}
void test_binary_boolean_function_scalar(BooleanClientKey *cks, BooleanServerKey *sks,
bool (*c_fun)(bool, bool),
int (*api_fun)(const BooleanServerKey *,
const BooleanCiphertext *, bool,
BooleanCiphertext **)) {
for (int idx_left = 0; idx_left < 2; ++idx_left) {
for (int idx_right = 0; idx_right < 2; ++idx_right) {
BooleanCiphertext *ct_left = NULL;
BooleanCiphertext *ct_result = NULL;
bool left = (bool)idx_left;
bool right = (bool)idx_right;
bool expected = c_fun(left, right);
int encrypt_left_ok = boolean_client_key_encrypt(cks, left, &ct_left);
assert(encrypt_left_ok == 0);
int api_call_ok = api_fun(sks, ct_left, right, &ct_result);
assert(api_call_ok == 0);
bool decrypted_result = false;
int decrypt_ok = boolean_client_key_decrypt(cks, ct_result, &decrypted_result);
assert(decrypt_ok == 0);
assert(decrypted_result == expected);
destroy_boolean_ciphertext(ct_left);
destroy_boolean_ciphertext(ct_result);
}
}
}
void test_binary_boolean_function_scalar_assign(BooleanClientKey *cks, BooleanServerKey *sks,
bool (*c_fun)(bool, bool),
int (*api_fun)(const BooleanServerKey *,
BooleanCiphertext *, bool)) {
for (int idx_left = 0; idx_left < 2; ++idx_left) {
for (int idx_right = 0; idx_right < 2; ++idx_right) {
BooleanCiphertext *ct_left_and_result = NULL;
bool left = (bool)idx_left;
bool right = (bool)idx_right;
bool expected = c_fun(left, right);
int encrypt_left_ok = boolean_client_key_encrypt(cks, left, &ct_left_and_result);
assert(encrypt_left_ok == 0);
int api_call_ok = api_fun(sks, ct_left_and_result, right);
assert(api_call_ok == 0);
bool decrypted_result = false;
int decrypt_ok = boolean_client_key_decrypt(cks, ct_left_and_result, &decrypted_result);
assert(decrypt_ok == 0);
assert(decrypted_result == expected);
destroy_boolean_ciphertext(ct_left_and_result);
}
}
}
void test_not(BooleanClientKey *cks, BooleanServerKey *sks) {
for (int idx_in_trivial = 0; idx_in_trivial < 2; ++idx_in_trivial) {
for (int idx_in = 0; idx_in < 2; ++idx_in) {
BooleanCiphertext *ct_in = NULL;
BooleanCiphertext *ct_result = NULL;
bool in = (bool)idx_in;
bool in_trivial = (bool)idx_in_trivial;
bool expected = !in;
if (in_trivial) {
int encrypt_in_ok = boolean_trivial_encrypt(in, &ct_in);
assert(encrypt_in_ok == 0);
} else {
int encrypt_in_ok = boolean_client_key_encrypt(cks, in, &ct_in);
assert(encrypt_in_ok == 0);
}
int api_call_ok = boolean_server_key_not(sks, ct_in, &ct_result);
assert(api_call_ok == 0);
bool decrypted_result = false;
int decrypt_ok = boolean_client_key_decrypt(cks, ct_result, &decrypted_result);
assert(decrypt_ok == 0);
assert(decrypted_result == expected);
destroy_boolean_ciphertext(ct_in);
destroy_boolean_ciphertext(ct_result);
}
}
}
void test_not_assign(BooleanClientKey *cks, BooleanServerKey *sks) {
for (int idx_in_trivial = 0; idx_in_trivial < 2; ++idx_in_trivial) {
for (int idx_in = 0; idx_in < 2; ++idx_in) {
BooleanCiphertext *ct_in_and_result = NULL;
bool in = (bool)idx_in;
bool in_trivial = (bool)idx_in_trivial;
bool expected = !in;
if (in_trivial) {
int encrypt_in_ok = boolean_trivial_encrypt(in, &ct_in_and_result);
assert(encrypt_in_ok == 0);
} else {
int encrypt_in_ok = boolean_client_key_encrypt(cks, in, &ct_in_and_result);
assert(encrypt_in_ok == 0);
}
int api_call_ok = boolean_server_key_not_assign(sks, ct_in_and_result);
assert(api_call_ok == 0);
bool decrypted_result = false;
int decrypt_ok = boolean_client_key_decrypt(cks, ct_in_and_result, &decrypted_result);
assert(decrypt_ok == 0);
assert(decrypted_result == expected);
destroy_boolean_ciphertext(ct_in_and_result);
}
}
}
void test_mux(BooleanClientKey *cks, BooleanServerKey *sks) {
for (int idx_cond_trivial = 0; idx_cond_trivial < 2; ++idx_cond_trivial) {
for (int idx_then_trivial = 0; idx_then_trivial < 2; ++idx_then_trivial) {
for (int idx_else_trivial = 0; idx_else_trivial < 2; ++idx_else_trivial) {
for (int idx_condition = 0; idx_condition < 2; ++idx_condition) {
for (int idx_then = 0; idx_then < 2; ++idx_then) {
for (int idx_else = 0; idx_else < 2; ++idx_else) {
BooleanCiphertext *ct_cond = NULL;
BooleanCiphertext *ct_then = NULL;
BooleanCiphertext *ct_else = NULL;
BooleanCiphertext *ct_result = NULL;
bool cond = (bool)idx_else;
bool then = (bool)idx_then;
bool else_ = (bool)idx_else;
bool cond_trivial = (bool)idx_cond_trivial;
bool then_trivial = (bool)idx_then_trivial;
bool else_trivial = (bool)idx_else_trivial;
bool expected = else_;
if (cond) {
expected = then;
}
if (cond_trivial) {
int encrypt_cond_ok = boolean_trivial_encrypt(cond, &ct_cond);
assert(encrypt_cond_ok == 0);
} else {
int encrypt_cond_ok = boolean_client_key_encrypt(cks, cond, &ct_cond);
assert(encrypt_cond_ok == 0);
}
if (then_trivial) {
int encrypt_then_ok = boolean_trivial_encrypt(then, &ct_then);
assert(encrypt_then_ok == 0);
} else {
int encrypt_then_ok = boolean_client_key_encrypt(cks, then, &ct_then);
assert(encrypt_then_ok == 0);
}
if (else_trivial) {
int encrypt_else_ok = boolean_trivial_encrypt(else_, &ct_else);
assert(encrypt_else_ok == 0);
} else {
int encrypt_else_ok = boolean_client_key_encrypt(cks, else_, &ct_else);
assert(encrypt_else_ok == 0);
}
int api_call_ok = boolean_server_key_mux(sks, ct_cond, ct_then, ct_else, &ct_result);
assert(api_call_ok == 0);
bool decrypted_result = false;
int decrypt_ok = boolean_client_key_decrypt(cks, ct_result, &decrypted_result);
assert(decrypt_ok == 0);
assert(decrypted_result == expected);
destroy_boolean_ciphertext(ct_cond);
destroy_boolean_ciphertext(ct_then);
destroy_boolean_ciphertext(ct_else);
destroy_boolean_ciphertext(ct_result);
}
}
}
}
}
}
}
bool c_and(bool left, bool right) { return left && right; }
bool c_nand(bool left, bool right) { return !c_and(left, right); }
bool c_or(bool left, bool right) { return left || right; }
bool c_nor(bool left, bool right) { return !c_or(left, right); }
bool c_xor(bool left, bool right) { return left != right; }
bool c_xnor(bool left, bool right) { return !c_xor(left, right); }
void test_server_key(void) {
BooleanClientKey *cks = NULL;
BooleanServerKey *sks = NULL;
Buffer cks_ser_buffer = {.pointer = NULL, .length = 0};
BooleanClientKey *deser_cks = NULL;
Buffer sks_ser_buffer = {.pointer = NULL, .length = 0};
BooleanServerKey *deser_sks = NULL;
int gen_keys_ok = boolean_gen_keys_with_default_parameters(&cks, &sks);
assert(gen_keys_ok == 0);
int ser_cks_ok = boolean_serialize_client_key(cks, &cks_ser_buffer);
assert(ser_cks_ok == 0);
BufferView deser_view = {.pointer = cks_ser_buffer.pointer, .length = cks_ser_buffer.length};
int deser_cks_ok = boolean_deserialize_client_key(deser_view, &deser_cks);
assert(deser_cks_ok == 0);
int ser_sks_ok = boolean_serialize_server_key(sks, &sks_ser_buffer);
assert(ser_sks_ok == 0);
deser_view.pointer = sks_ser_buffer.pointer;
deser_view.length = sks_ser_buffer.length;
int deser_sks_ok = boolean_deserialize_server_key(deser_view, &deser_sks);
assert(deser_sks_ok == 0);
test_binary_boolean_function(deser_cks, deser_sks, c_and, boolean_server_key_and);
test_binary_boolean_function(deser_cks, deser_sks, c_nand, boolean_server_key_nand);
test_binary_boolean_function(deser_cks, deser_sks, c_or, boolean_server_key_or);
test_binary_boolean_function(deser_cks, deser_sks, c_nor, boolean_server_key_nor);
test_binary_boolean_function(deser_cks, deser_sks, c_xor, boolean_server_key_xor);
test_binary_boolean_function(deser_cks, deser_sks, c_xnor, boolean_server_key_xnor);
test_not(deser_cks, deser_sks);
test_mux(deser_cks, deser_sks);
test_binary_boolean_function_assign(deser_cks, deser_sks, c_and, boolean_server_key_and_assign);
test_binary_boolean_function_assign(deser_cks, deser_sks, c_nand, boolean_server_key_nand_assign);
test_binary_boolean_function_assign(deser_cks, deser_sks, c_or, boolean_server_key_or_assign);
test_binary_boolean_function_assign(deser_cks, deser_sks, c_nor, boolean_server_key_nor_assign);
test_binary_boolean_function_assign(deser_cks, deser_sks, c_xor, boolean_server_key_xor_assign);
test_binary_boolean_function_assign(deser_cks, deser_sks, c_xnor, boolean_server_key_xnor_assign);
test_not_assign(deser_cks, deser_sks);
test_binary_boolean_function_scalar(deser_cks, deser_sks, c_and, boolean_server_key_and_scalar);
test_binary_boolean_function_scalar(deser_cks, deser_sks, c_nand, boolean_server_key_nand_scalar);
test_binary_boolean_function_scalar(deser_cks, deser_sks, c_or, boolean_server_key_or_scalar);
test_binary_boolean_function_scalar(deser_cks, deser_sks, c_nor, boolean_server_key_nor_scalar);
test_binary_boolean_function_scalar(deser_cks, deser_sks, c_xor, boolean_server_key_xor_scalar);
test_binary_boolean_function_scalar(deser_cks, deser_sks, c_xnor, boolean_server_key_xnor_scalar);
test_binary_boolean_function_scalar_assign(deser_cks, deser_sks, c_and,
boolean_server_key_and_scalar_assign);
test_binary_boolean_function_scalar_assign(deser_cks, deser_sks, c_nand,
boolean_server_key_nand_scalar_assign);
test_binary_boolean_function_scalar_assign(deser_cks, deser_sks, c_or,
boolean_server_key_or_scalar_assign);
test_binary_boolean_function_scalar_assign(deser_cks, deser_sks, c_nor,
boolean_server_key_nor_scalar_assign);
test_binary_boolean_function_scalar_assign(deser_cks, deser_sks, c_xor,
boolean_server_key_xor_scalar_assign);
test_binary_boolean_function_scalar_assign(deser_cks, deser_sks, c_xnor,
boolean_server_key_xnor_scalar_assign);
destroy_boolean_client_key(cks);
destroy_boolean_server_key(sks);
destroy_boolean_client_key(deser_cks);
destroy_boolean_server_key(deser_sks);
destroy_buffer(&cks_ser_buffer);
destroy_buffer(&sks_ser_buffer);
}
int main(void) {
test_server_key();
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,51 @@
#include "tfhe.h"
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <tgmath.h>
#include <time.h>
void micro_bench_and() {
BooleanClientKey *cks = NULL;
BooleanServerKey *sks = NULL;
// int gen_keys_ok = boolean_gen_keys_with_default_parameters(&cks, &sks);
// assert(gen_keys_ok == 0);
int gen_keys_ok = boolean_gen_keys_with_predefined_parameters_set(
BOOLEAN_PARAMETERS_SET_THFE_LIB_PARAMETERS, &cks, &sks);
assert(gen_keys_ok == 0);
int num_loops = 10000;
BooleanCiphertext *ct_left = NULL;
BooleanCiphertext *ct_right = NULL;
int encrypt_left_ok = boolean_client_key_encrypt(cks, false, &ct_left);
assert(encrypt_left_ok == 0);
int encrypt_right_ok = boolean_client_key_encrypt(cks, true, &ct_right);
assert(encrypt_right_ok == 0);
clock_t start = clock();
for (int idx_loops = 0; idx_loops < num_loops; ++idx_loops) {
BooleanCiphertext *ct_result = NULL;
boolean_server_key_and(sks, ct_left, ct_right, &ct_result);
destroy_boolean_ciphertext(ct_result);
}
clock_t stop = clock();
double elapsed_ms = (double)((stop - start) * 1000) / CLOCKS_PER_SEC;
double mean_ms = elapsed_ms / num_loops;
printf("%g ms, mean %g ms\n", elapsed_ms, mean_ms);
destroy_boolean_client_key(cks);
destroy_boolean_server_key(sks);
}
int main(void) {
micro_bench_and();
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,112 @@
#include "tfhe.h"
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <tgmath.h>
void test_predefined_keygen_w_serde(void) {
ShortintClientKey *cks = NULL;
ShortintServerKey *sks = NULL;
ShortintParameters *params = NULL;
ShortintCiphertext *ct = NULL;
Buffer ct_ser_buffer = {.pointer = NULL, .length = 0};
ShortintCiphertext *deser_ct = NULL;
int get_params_ok = shortint_get_parameters(2, 2, &params);
assert(get_params_ok == 0);
int gen_keys_ok = shortint_gen_keys_with_parameters(params, &cks, &sks);
assert(gen_keys_ok == 0);
int encrypt_ok = shortint_client_key_encrypt(cks, 3, &ct);
assert(encrypt_ok == 0);
int ser_ok = shortint_serialize_ciphertext(ct, &ct_ser_buffer);
assert(ser_ok == 0);
BufferView deser_view = {.pointer = ct_ser_buffer.pointer, .length = ct_ser_buffer.length};
int deser_ok = shortint_deserialize_ciphertext(deser_view, &deser_ct);
assert(deser_ok == 0);
assert(deser_view.length == ct_ser_buffer.length);
for (size_t idx = 0; idx < deser_view.length; ++idx) {
assert(deser_view.pointer[idx] == ct_ser_buffer.pointer[idx]);
}
uint64_t result = -1;
int decrypt_ok = shortint_client_key_decrypt(cks, deser_ct, &result);
assert(decrypt_ok == 0);
assert(result == 3);
destroy_shortint_client_key(cks);
destroy_shortint_server_key(sks);
destroy_shortint_parameters(params);
destroy_shortint_ciphertext(ct);
destroy_shortint_ciphertext(deser_ct);
destroy_buffer(&ct_ser_buffer);
}
void test_custom_keygen(void) {
ShortintClientKey *cks = NULL;
ShortintServerKey *sks = NULL;
ShortintParameters *params = NULL;
int params_ok = shortint_create_parameters(10, 1, 1024, 10e-100, 10e-100, 2, 3, 2, 3, 2, 3,
10e-100, 2, 3, 2, 2, &params);
assert(params_ok == 0);
int gen_keys_ok = shortint_gen_keys_with_parameters(params, &cks, &sks);
assert(gen_keys_ok == 0);
destroy_shortint_parameters(params);
destroy_shortint_client_key(cks);
destroy_shortint_server_key(sks);
}
void test_public_keygen(void) {
ShortintClientKey *cks = NULL;
ShortintServerKey *sks = NULL;
ShortintPublicKey *pks = NULL;
ShortintParameters *params = NULL;
ShortintCiphertext *ct = NULL;
int get_params_ok = shortint_get_parameters(2, 2, &params);
assert(get_params_ok == 0);
int gen_keys_ok = shortint_gen_client_key(params, &cks);
assert(gen_keys_ok == 0);
int gen_pks = shortint_gen_public_key(cks, &pks);
assert(gen_pks == 0);
int gen_sks = shortint_gen_server_key(cks, &sks);
assert(gen_sks == 0);
uint64_t msg = 2;
int encrypt_ok = shortint_public_key_encrypt(pks, sks, msg, &ct);
assert(encrypt_ok == 0);
uint64_t result = -1;
int decrypt_ok = shortint_client_key_decrypt(cks, ct, &result);
assert(decrypt_ok == 0);
assert(result == 2);
destroy_shortint_parameters(params);
destroy_shortint_client_key(cks);
destroy_shortint_server_key(sks);
destroy_shortint_public_key(pks);
destroy_shortint_ciphertext(ct);
}
int main(void) {
test_predefined_keygen_w_serde();
test_custom_keygen();
test_public_keygen();
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,197 @@
#include "tfhe.h"
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <tgmath.h>
uint64_t double_accumulator_2_bits_message(uint64_t in) { return (in * 2) % 4; }
uint64_t get_max_value_of_accumulator_generator(uint64_t (*accumulator_func)(uint64_t),
size_t message_bits) {
uint64_t max_value = 0;
for (size_t idx = 0; idx < (1 << message_bits); ++idx) {
uint64_t acc_value = accumulator_func((uint64_t)idx);
max_value = acc_value > max_value ? acc_value : max_value;
}
return max_value;
}
uint64_t product_accumulator_2_bits_encrypted_mul(uint64_t left, uint64_t right) {
return (left * right) % 4;
}
uint64_t get_max_value_of_bivariate_accumulator_generator(uint64_t (*accumulator_func)(uint64_t,
uint64_t),
size_t message_bits_left,
size_t message_bits_right) {
uint64_t max_value = 0;
for (size_t idx_left = 0; idx_left < (1 << message_bits_left); ++idx_left) {
for (size_t idx_right = 0; idx_right < (1 << message_bits_right); ++idx_right) {
uint64_t acc_value = accumulator_func((uint64_t)idx_left, (uint64_t)idx_right);
max_value = acc_value > max_value ? acc_value : max_value;
}
}
return max_value;
}
void test_shortint_pbs_2_bits_message(void) {
ShortintPBSAccumulator *accumulator = NULL;
ShortintClientKey *cks = NULL;
ShortintServerKey *sks = NULL;
ShortintParameters *params = NULL;
int get_params_ok = shortint_get_parameters(2, 2, &params);
assert(get_params_ok == 0);
int gen_keys_ok = shortint_gen_keys_with_parameters(params, &cks, &sks);
assert(gen_keys_ok == 0);
int gen_acc_ok = shortint_server_key_generate_pbs_accumulator(
sks, double_accumulator_2_bits_message, &accumulator);
assert(gen_acc_ok == 0);
for (int in_idx = 0; in_idx < 4; ++in_idx) {
ShortintCiphertext *ct = NULL;
ShortintCiphertext *ct_out = NULL;
uint64_t in_val = (uint64_t)in_idx;
int encrypt_ok = shortint_client_key_encrypt(cks, in_val, &ct);
assert(encrypt_ok == 0);
size_t degree = -1;
int get_degree_ok = shortint_ciphertext_get_degree(ct, &degree);
assert(get_degree_ok == 0);
assert(degree == 3);
int pbs_ok = shortint_server_key_programmable_bootstrap(sks, accumulator, ct, &ct_out);
assert(pbs_ok == 0);
size_t degree_to_set =
(size_t)get_max_value_of_accumulator_generator(double_accumulator_2_bits_message, 2);
int set_degree_ok = shortint_ciphertext_set_degree(ct_out, degree_to_set);
assert(set_degree_ok == 0);
degree = -1;
get_degree_ok = shortint_ciphertext_get_degree(ct_out, &degree);
assert(get_degree_ok == 0);
assert(degree == degree_to_set);
uint64_t result_non_assign = -1;
int decrypt_non_assign_ok = shortint_client_key_decrypt(cks, ct_out, &result_non_assign);
assert(decrypt_non_assign_ok == 0);
assert(result_non_assign == double_accumulator_2_bits_message(in_val));
int pbs_assign_ok = shortint_server_key_programmable_bootstrap_assign(sks, accumulator, ct_out);
assert(pbs_assign_ok == 0);
degree_to_set =
(size_t)get_max_value_of_accumulator_generator(double_accumulator_2_bits_message, 2);
set_degree_ok = shortint_ciphertext_set_degree(ct_out, degree_to_set);
assert(set_degree_ok == 0);
uint64_t result_assign = -1;
int decrypt_assign_ok = shortint_client_key_decrypt(cks, ct_out, &result_assign);
assert(decrypt_assign_ok == 0);
assert(result_assign == double_accumulator_2_bits_message(result_non_assign));
destroy_shortint_ciphertext(ct);
destroy_shortint_ciphertext(ct_out);
}
destroy_shortint_pbs_accumulator(accumulator);
destroy_shortint_client_key(cks);
destroy_shortint_server_key(sks);
destroy_shortint_parameters(params);
}
void test_shortint_bivariate_pbs_2_bits_message(void) {
ShortintBivariatePBSAccumulator *accumulator = NULL;
ShortintClientKey *cks = NULL;
ShortintServerKey *sks = NULL;
ShortintParameters *params = NULL;
int get_params_ok = shortint_get_parameters(2, 2, &params);
assert(get_params_ok == 0);
int gen_keys_ok = shortint_gen_keys_with_parameters(params, &cks, &sks);
assert(gen_keys_ok == 0);
int gen_acc_ok = shortint_server_key_generate_bivariate_pbs_accumulator(
sks, product_accumulator_2_bits_encrypted_mul, &accumulator);
assert(gen_acc_ok == 0);
for (int left_idx = 0; left_idx < 4; ++left_idx) {
for (int right_idx = 0; right_idx < 4; ++right_idx) {
ShortintCiphertext *ct_left = NULL;
ShortintCiphertext *ct_right = NULL;
ShortintCiphertext *ct_out = NULL;
uint64_t left_val = (uint64_t)left_idx;
uint64_t right_val = (uint64_t)right_idx;
int encrypt_left_ok = shortint_client_key_encrypt(cks, left_val, &ct_left);
assert(encrypt_left_ok == 0);
int encrypt_right_ok = shortint_client_key_encrypt(cks, right_val, &ct_right);
assert(encrypt_right_ok == 0);
int pbs_ok = shortint_server_key_bivariate_programmable_bootstrap(sks, accumulator, ct_left,
ct_right, &ct_out);
assert(pbs_ok == 0);
size_t degree_to_set = (size_t)get_max_value_of_bivariate_accumulator_generator(
product_accumulator_2_bits_encrypted_mul, 2, 2);
int set_degree_ok = shortint_ciphertext_set_degree(ct_right, degree_to_set);
assert(set_degree_ok == 0);
uint64_t result_non_assign = -1;
int decrypt_non_assign_ok = shortint_client_key_decrypt(cks, ct_out, &result_non_assign);
assert(decrypt_non_assign_ok == 0);
assert(result_non_assign == product_accumulator_2_bits_encrypted_mul(left_val, right_val));
int pbs_assign_ok = shortint_server_key_bivariate_programmable_bootstrap_assign(
sks, accumulator, ct_out, ct_right);
assert(pbs_assign_ok == 0);
degree_to_set =
(size_t)get_max_value_of_accumulator_generator(double_accumulator_2_bits_message, 2);
set_degree_ok = shortint_ciphertext_set_degree(ct_out, degree_to_set);
assert(set_degree_ok == 0);
uint64_t result_assign = -1;
int decrypt_assign_ok = shortint_client_key_decrypt(cks, ct_out, &result_assign);
assert(decrypt_assign_ok == 0);
assert(result_assign ==
product_accumulator_2_bits_encrypted_mul(result_non_assign, right_val));
destroy_shortint_ciphertext(ct_left);
destroy_shortint_ciphertext(ct_right);
destroy_shortint_ciphertext(ct_out);
}
}
destroy_shortint_bivariate_pbs_accumulator(accumulator);
destroy_shortint_client_key(cks);
destroy_shortint_server_key(sks);
destroy_shortint_parameters(params);
}
int main(void) {
test_shortint_pbs_2_bits_message();
test_shortint_bivariate_pbs_2_bits_message();
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,557 @@
#include "tfhe.h"
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <tgmath.h>
void test_shortint_unary_op(const ShortintClientKey *cks, const ShortintServerKey *sks,
const uint32_t message_bits, const uint32_t carry_bits,
uint64_t (*c_fun)(uint64_t),
int (*api_fun)(const ShortintServerKey *, ShortintCiphertext *,
ShortintCiphertext **)) {
int message_max = 1 << message_bits;
for (int val_in = 0; val_in < message_max; ++val_in) {
ShortintCiphertext *ct_in = NULL;
ShortintCiphertext *ct_result = NULL;
uint64_t in = (uint64_t)val_in;
uint64_t expected = c_fun(in) % message_max;
int encrypt_left_ok = shortint_client_key_encrypt(cks, in, &ct_in);
assert(encrypt_left_ok == 0);
int api_call_ok = api_fun(sks, ct_in, &ct_result);
assert(api_call_ok == 0);
uint64_t decrypted_result = -1;
int decrypt_ok = shortint_client_key_decrypt(cks, ct_result, &decrypted_result);
assert(decrypt_ok == 0);
assert(decrypted_result == expected);
destroy_shortint_ciphertext(ct_in);
destroy_shortint_ciphertext(ct_result);
}
}
void test_shortint_unary_op_assign(const ShortintClientKey *cks, const ShortintServerKey *sks,
const uint32_t message_bits, const uint32_t carry_bits,
uint64_t (*c_fun)(uint64_t),
int (*api_fun)(const ShortintServerKey *,
ShortintCiphertext *)) {
int message_max = 1 << message_bits;
for (int in = 0; in < message_max; ++in) {
ShortintCiphertext *ct_in_and_result = NULL;
uint64_t in = (uint64_t)in;
uint64_t expected = c_fun(in) % message_max;
int encrypt_left_ok = shortint_client_key_encrypt(cks, in, &ct_in_and_result);
assert(encrypt_left_ok == 0);
int api_call_ok = api_fun(sks, ct_in_and_result);
assert(api_call_ok == 0);
uint64_t decrypted_result = -1;
int decrypt_ok = shortint_client_key_decrypt(cks, ct_in_and_result, &decrypted_result);
assert(decrypt_ok == 0);
assert(decrypted_result == expected);
destroy_shortint_ciphertext(ct_in_and_result);
}
}
void test_shortint_binary_op(const ShortintClientKey *cks, const ShortintServerKey *sks,
const uint32_t message_bits, const uint32_t carry_bits,
uint64_t (*c_fun)(uint64_t, uint64_t),
int (*api_fun)(const ShortintServerKey *, ShortintCiphertext *,
ShortintCiphertext *, ShortintCiphertext **)) {
int message_max = 1 << message_bits;
for (int val_left = 0; val_left < message_max; ++val_left) {
for (int val_right = 0; val_right < message_max; ++val_right) {
ShortintCiphertext *ct_left = NULL;
ShortintCiphertext *ct_right = NULL;
ShortintCiphertext *ct_result = NULL;
uint64_t left = (uint64_t)val_left;
uint64_t right = (uint64_t)val_right;
uint64_t expected = c_fun(left, right) % message_max;
int encrypt_left_ok = shortint_client_key_encrypt(cks, left, &ct_left);
assert(encrypt_left_ok == 0);
int encrypt_right_ok = shortint_client_key_encrypt(cks, right, &ct_right);
assert(encrypt_right_ok == 0);
int api_call_ok = api_fun(sks, ct_left, ct_right, &ct_result);
assert(api_call_ok == 0);
uint64_t decrypted_result = -1;
int decrypt_ok = shortint_client_key_decrypt(cks, ct_result, &decrypted_result);
assert(decrypt_ok == 0);
assert(decrypted_result == expected);
destroy_shortint_ciphertext(ct_left);
destroy_shortint_ciphertext(ct_right);
destroy_shortint_ciphertext(ct_result);
}
}
}
void test_shortint_binary_op_assign(const ShortintClientKey *cks, const ShortintServerKey *sks,
const uint32_t message_bits, const uint32_t carry_bits,
uint64_t (*c_fun)(uint64_t, uint64_t),
int (*api_fun)(const ShortintServerKey *, ShortintCiphertext *,
ShortintCiphertext *)) {
int message_max = 1 << message_bits;
for (int val_left = 0; val_left < message_max; ++val_left) {
for (int val_right = 0; val_right < message_max; ++val_right) {
ShortintCiphertext *ct_left_and_result = NULL;
ShortintCiphertext *ct_right = NULL;
uint64_t left = (uint64_t)val_left;
uint64_t right = (uint64_t)val_right;
uint64_t expected = c_fun(left, right) % message_max;
int encrypt_left_ok = shortint_client_key_encrypt(cks, left, &ct_left_and_result);
assert(encrypt_left_ok == 0);
int encrypt_right_ok = shortint_client_key_encrypt(cks, right, &ct_right);
assert(encrypt_right_ok == 0);
int api_call_ok = api_fun(sks, ct_left_and_result, ct_right);
assert(api_call_ok == 0);
uint64_t decrypted_result = -1;
int decrypt_ok = shortint_client_key_decrypt(cks, ct_left_and_result, &decrypted_result);
assert(decrypt_ok == 0);
assert(decrypted_result == expected);
destroy_shortint_ciphertext(ct_left_and_result);
destroy_shortint_ciphertext(ct_right);
}
}
}
void test_shortint_binary_scalar_op(
const ShortintClientKey *cks, const ShortintServerKey *sks, const uint32_t message_bits,
const uint32_t carry_bits, uint64_t (*c_fun)(uint64_t, uint8_t),
int (*api_fun)(const ShortintServerKey *, ShortintCiphertext *, uint8_t, ShortintCiphertext **),
uint8_t forbidden_scalar_values[], size_t forbidden_scalar_values_len) {
int message_max = 1 << message_bits;
for (int val_left = 0; val_left < message_max; ++val_left) {
for (int val_right = 0; val_right < message_max; ++val_right) {
ShortintCiphertext *ct_left = NULL;
ShortintCiphertext *ct_result = NULL;
uint64_t left = (uint64_t)val_left;
uint8_t scalar_right = (uint8_t)val_right;
if (forbidden_scalar_values != NULL) {
bool found_forbidden_value = false;
for (int idx = 0; idx < forbidden_scalar_values_len; ++idx) {
if (forbidden_scalar_values[idx] == scalar_right) {
found_forbidden_value = true;
break;
}
}
if (found_forbidden_value) {
continue;
}
}
uint64_t expected = c_fun(left, scalar_right) % message_max;
int encrypt_left_ok = shortint_client_key_encrypt(cks, left, &ct_left);
assert(encrypt_left_ok == 0);
int api_call_ok = api_fun(sks, ct_left, scalar_right, &ct_result);
assert(api_call_ok == 0);
uint64_t decrypted_result = -1;
int decrypt_ok = shortint_client_key_decrypt(cks, ct_result, &decrypted_result);
assert(decrypt_ok == 0);
assert(decrypted_result == expected);
destroy_shortint_ciphertext(ct_left);
destroy_shortint_ciphertext(ct_result);
}
}
}
void test_shortint_binary_scalar_op_assign(
const ShortintClientKey *cks, const ShortintServerKey *sks, const uint32_t message_bits,
const uint32_t carry_bits, uint64_t (*c_fun)(uint64_t, uint8_t),
int (*api_fun)(const ShortintServerKey *, ShortintCiphertext *, uint8_t),
uint8_t forbidden_scalar_values[], size_t forbidden_scalar_values_len) {
int message_max = 1 << message_bits;
for (int val_left = 0; val_left < message_max; ++val_left) {
for (int val_right = 0; val_right < message_max; ++val_right) {
ShortintCiphertext *ct_left_and_result = NULL;
uint64_t left = (uint64_t)val_left;
uint8_t scalar_right = (uint8_t)val_right;
if (forbidden_scalar_values != NULL) {
bool found_forbidden_value = false;
for (int idx = 0; idx < forbidden_scalar_values_len; ++idx) {
if (forbidden_scalar_values[idx] == scalar_right) {
found_forbidden_value = true;
break;
}
}
if (found_forbidden_value) {
continue;
}
}
uint64_t expected = c_fun(left, scalar_right) % message_max;
int encrypt_left_ok = shortint_client_key_encrypt(cks, left, &ct_left_and_result);
assert(encrypt_left_ok == 0);
int api_call_ok = api_fun(sks, ct_left_and_result, scalar_right);
assert(api_call_ok == 0);
uint64_t decrypted_result = -1;
int decrypt_ok = shortint_client_key_decrypt(cks, ct_left_and_result, &decrypted_result);
assert(decrypt_ok == 0);
assert(decrypted_result == expected);
destroy_shortint_ciphertext(ct_left_and_result);
}
}
}
uint64_t add(uint64_t left, uint64_t right) { return left + right; }
uint64_t sub(uint64_t left, uint64_t right) { return left - right; }
uint64_t mul(uint64_t left, uint64_t right) { return left * right; }
uint64_t neg(uint64_t in) { return -in; }
uint64_t homomorphic_div(uint64_t left, uint64_t right) {
if (right != 0) {
return left / right;
} else {
// Special value chosen in the shortint implementation in case of a division by 0
return 0;
}
}
uint64_t bitand(uint64_t left, uint64_t right) { return left & right; }
uint64_t bitxor(uint64_t left, uint64_t right) { return left ^ right; }
uint64_t bitor (uint64_t left, uint64_t right) { return left | right; }
uint64_t greater(uint64_t left, uint64_t right) { return (uint64_t)(left > right); }
uint64_t greater_or_equal(uint64_t left, uint64_t right) { return (uint64_t)(left >= right); }
uint64_t less(uint64_t left, uint64_t right) { return (uint64_t)(left < right); }
uint64_t less_or_equal(uint64_t left, uint64_t right) { return (uint64_t)(left <= right); }
uint64_t equal(uint64_t left, uint64_t right) { return (uint64_t)(left == right); }
uint64_t not_equal(uint64_t left, uint64_t right) { return (uint64_t)(left != right); }
uint64_t scalar_greater(uint64_t left, uint8_t right) { return (uint64_t)(left > right); }
uint64_t scalar_greater_or_equal(uint64_t left, uint8_t right) { return (uint64_t)(left >= right); }
uint64_t scalar_less(uint64_t left, uint8_t right) { return (uint64_t)(left < right); }
uint64_t scalar_less_or_equal(uint64_t left, uint8_t right) { return (uint64_t)(left <= right); }
uint64_t scalar_equal(uint64_t left, uint8_t right) { return (uint64_t)(left == right); }
uint64_t scalar_not_equal(uint64_t left, uint8_t right) { return (uint64_t)(left != right); }
uint64_t scalar_add(uint64_t left, uint8_t right) { return left + right; }
uint64_t scalar_sub(uint64_t left, uint8_t right) { return left - right; }
uint64_t scalar_mul(uint64_t left, uint8_t right) { return left * right; }
uint64_t scalar_div(uint64_t left, uint8_t right) { return left / right; }
uint64_t scalar_mod(uint64_t left, uint8_t right) { return left % right; }
uint64_t left_shift(uint64_t left, uint8_t right) { return left << right; }
uint64_t right_shift(uint64_t left, uint8_t right) { return left >> right; }
void test_server_key(void) {
ShortintClientKey *cks = NULL;
ShortintServerKey *sks = NULL;
Buffer cks_ser_buffer = {.pointer = NULL, .length = 0};
ShortintClientKey *deser_cks = NULL;
Buffer sks_ser_buffer = {.pointer = NULL, .length = 0};
ShortintServerKey *deser_sks = NULL;
ShortintParameters *params = NULL;
const uint32_t message_bits = 2;
const uint32_t carry_bits = 2;
int get_params_ok = shortint_get_parameters(message_bits, carry_bits, &params);
assert(get_params_ok == 0);
int gen_keys_ok = shortint_gen_keys_with_parameters(params, &cks, &sks);
assert(gen_keys_ok == 0);
int ser_cks_ok = shortint_serialize_client_key(cks, &cks_ser_buffer);
assert(ser_cks_ok == 0);
BufferView deser_view = {.pointer = cks_ser_buffer.pointer, .length = cks_ser_buffer.length};
int deser_cks_ok = shortint_deserialize_client_key(deser_view, &deser_cks);
assert(deser_cks_ok == 0);
int ser_sks_ok = shortint_serialize_server_key(sks, &sks_ser_buffer);
assert(ser_sks_ok == 0);
deser_view.pointer = sks_ser_buffer.pointer;
deser_view.length = sks_ser_buffer.length;
int deser_sks_ok = shortint_deserialize_server_key(deser_view, &deser_sks);
assert(deser_sks_ok == 0);
printf("add\n");
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, add,
shortint_server_key_smart_add);
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, add,
shortint_server_key_unchecked_add);
test_shortint_binary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, add,
shortint_server_key_smart_add_assign);
test_shortint_binary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, add,
shortint_server_key_unchecked_add_assign);
printf("sub\n");
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, sub,
shortint_server_key_smart_sub);
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, sub,
shortint_server_key_unchecked_sub);
test_shortint_binary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, sub,
shortint_server_key_smart_sub_assign);
test_shortint_binary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, sub,
shortint_server_key_unchecked_sub_assign);
printf("mul\n");
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, mul,
shortint_server_key_smart_mul);
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, mul,
shortint_server_key_unchecked_mul);
test_shortint_binary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, mul,
shortint_server_key_smart_mul_assign);
test_shortint_binary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, mul,
shortint_server_key_unchecked_mul_assign);
printf("left_shift\n");
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, left_shift,
shortint_server_key_smart_scalar_left_shift, NULL, 0);
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, left_shift,
shortint_server_key_unchecked_scalar_left_shift, NULL, 0);
test_shortint_binary_scalar_op_assign(deser_cks, deser_sks, message_bits, carry_bits, left_shift,
shortint_server_key_smart_scalar_left_shift_assign, NULL,
0);
test_shortint_binary_scalar_op_assign(deser_cks, deser_sks, message_bits, carry_bits, left_shift,
shortint_server_key_unchecked_scalar_left_shift_assign,
NULL, 0);
printf("right_shift\n");
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, right_shift,
shortint_server_key_smart_scalar_right_shift, NULL, 0);
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, right_shift,
shortint_server_key_unchecked_scalar_right_shift, NULL, 0);
test_shortint_binary_scalar_op_assign(deser_cks, deser_sks, message_bits, carry_bits, right_shift,
shortint_server_key_smart_scalar_right_shift_assign, NULL,
0);
test_shortint_binary_scalar_op_assign(deser_cks, deser_sks, message_bits, carry_bits, right_shift,
shortint_server_key_unchecked_scalar_right_shift_assign,
NULL, 0);
printf("scalar_add\n");
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, scalar_add,
shortint_server_key_smart_scalar_add, NULL, 0);
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, scalar_add,
shortint_server_key_unchecked_scalar_add, NULL, 0);
test_shortint_binary_scalar_op_assign(deser_cks, deser_sks, message_bits, carry_bits, scalar_add,
shortint_server_key_smart_scalar_add_assign, NULL, 0);
test_shortint_binary_scalar_op_assign(deser_cks, deser_sks, message_bits, carry_bits, scalar_add,
shortint_server_key_unchecked_scalar_add_assign, NULL, 0);
printf("scalar_sub\n");
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, scalar_sub,
shortint_server_key_smart_scalar_sub, NULL, 0);
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, scalar_sub,
shortint_server_key_unchecked_scalar_sub, NULL, 0);
test_shortint_binary_scalar_op_assign(deser_cks, deser_sks, message_bits, carry_bits, scalar_sub,
shortint_server_key_smart_scalar_sub_assign, NULL, 0);
test_shortint_binary_scalar_op_assign(deser_cks, deser_sks, message_bits, carry_bits, scalar_sub,
shortint_server_key_unchecked_scalar_sub_assign, NULL, 0);
printf("scalar_mul\n");
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, scalar_mul,
shortint_server_key_smart_scalar_mul, NULL, 0);
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, scalar_mul,
shortint_server_key_unchecked_scalar_mul, NULL, 0);
test_shortint_binary_scalar_op_assign(deser_cks, deser_sks, message_bits, carry_bits, scalar_mul,
shortint_server_key_smart_scalar_mul_assign, NULL, 0);
test_shortint_binary_scalar_op_assign(deser_cks, deser_sks, message_bits, carry_bits, scalar_mul,
shortint_server_key_unchecked_scalar_mul_assign, NULL, 0);
printf("bitand\n");
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, bitand,
shortint_server_key_smart_bitand);
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, bitand,
shortint_server_key_unchecked_bitand);
test_shortint_binary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, bitand,
shortint_server_key_smart_bitand_assign);
test_shortint_binary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, bitand,
shortint_server_key_unchecked_bitand_assign);
printf("bitxor\n");
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, bitxor,
shortint_server_key_smart_bitxor);
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, bitxor,
shortint_server_key_unchecked_bitxor);
test_shortint_binary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, bitxor,
shortint_server_key_smart_bitxor_assign);
test_shortint_binary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, bitxor,
shortint_server_key_unchecked_bitxor_assign);
printf("bitor\n");
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, bitor,
shortint_server_key_smart_bitor);
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, bitor,
shortint_server_key_unchecked_bitor);
test_shortint_binary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, bitor,
shortint_server_key_smart_bitor_assign);
test_shortint_binary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, bitor,
shortint_server_key_unchecked_bitor_assign);
printf("greater\n");
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, greater,
shortint_server_key_smart_greater);
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, greater,
shortint_server_key_unchecked_greater);
printf("greater_or_equal\n");
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, greater_or_equal,
shortint_server_key_smart_greater_or_equal);
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, greater_or_equal,
shortint_server_key_unchecked_greater_or_equal);
printf("less\n");
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, less,
shortint_server_key_smart_less);
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, less,
shortint_server_key_unchecked_less);
printf("less_or_equal\n");
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, less_or_equal,
shortint_server_key_smart_less_or_equal);
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, less_or_equal,
shortint_server_key_unchecked_less_or_equal);
printf("equal\n");
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, equal,
shortint_server_key_smart_equal);
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, equal,
shortint_server_key_unchecked_equal);
printf("not_equal\n");
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, not_equal,
shortint_server_key_smart_not_equal);
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, not_equal,
shortint_server_key_unchecked_not_equal);
printf("scalar_greater\n");
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, scalar_greater,
shortint_server_key_smart_scalar_greater, NULL, 0);
printf("scalar_greater_or_equal\n");
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits,
scalar_greater_or_equal,
shortint_server_key_smart_scalar_greater_or_equal, NULL, 0);
printf("scalar_less\n");
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, scalar_less,
shortint_server_key_smart_scalar_less, NULL, 0);
printf("scalar_less_or_equal\n");
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits,
scalar_less_or_equal,
shortint_server_key_smart_scalar_less_or_equal, NULL, 0);
printf("scalar_equal\n");
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, scalar_equal,
shortint_server_key_smart_scalar_equal, NULL, 0);
printf("scalar_not_equal\n");
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, scalar_not_equal,
shortint_server_key_smart_scalar_not_equal, NULL, 0);
printf("neg\n");
test_shortint_unary_op(deser_cks, deser_sks, message_bits, carry_bits, neg,
shortint_server_key_smart_neg);
test_shortint_unary_op(deser_cks, deser_sks, message_bits, carry_bits, neg,
shortint_server_key_unchecked_neg);
test_shortint_unary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, neg,
shortint_server_key_smart_neg_assign);
test_shortint_unary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, neg,
shortint_server_key_unchecked_neg_assign);
printf("div\n");
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, homomorphic_div,
shortint_server_key_smart_div);
test_shortint_binary_op(deser_cks, deser_sks, message_bits, carry_bits, homomorphic_div,
shortint_server_key_unchecked_div);
test_shortint_binary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, homomorphic_div,
shortint_server_key_smart_div_assign);
test_shortint_binary_op_assign(deser_cks, deser_sks, message_bits, carry_bits, homomorphic_div,
shortint_server_key_unchecked_div_assign);
printf("scalar_div\n");
uint8_t forbidden_scalar_div_values[1] = {0};
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, scalar_div,
shortint_server_key_unchecked_scalar_div,
forbidden_scalar_div_values, 1);
test_shortint_binary_scalar_op_assign(deser_cks, deser_sks, message_bits, carry_bits, scalar_div,
shortint_server_key_unchecked_scalar_div_assign,
forbidden_scalar_div_values, 1);
printf("scalar_mod\n");
uint8_t forbidden_scalar_mod_values[1] = {0};
test_shortint_binary_scalar_op(deser_cks, deser_sks, message_bits, carry_bits, scalar_mod,
shortint_server_key_unchecked_scalar_mod,
forbidden_scalar_mod_values, 1);
test_shortint_binary_scalar_op_assign(deser_cks, deser_sks, message_bits, carry_bits, scalar_mod,
shortint_server_key_unchecked_scalar_mod_assign,
forbidden_scalar_mod_values, 1);
destroy_shortint_client_key(cks);
destroy_shortint_server_key(sks);
destroy_shortint_client_key(deser_cks);
destroy_shortint_server_key(deser_sks);
destroy_shortint_parameters(params);
destroy_buffer(&cks_ser_buffer);
destroy_buffer(&sks_ser_buffer);
}
int main(void) {
test_server_key();
return EXIT_SUCCESS;
}

129
tfhe/cbindgen.toml Normal file
View File

@@ -0,0 +1,129 @@
# This is a template cbindgen.toml file with all of the default values.
# Some values are commented out because their absence is the real default.
#
# See https://github.com/eqrion/cbindgen/blob/master/docs.md#cbindgentoml
# for detailed documentation of every option here.
language = "C"
############## Options for Wrapping the Contents of the Header #################
header = "// Copyright © 2022 ZAMA.\n// All rights reserved."
# trailer = "/* Text to put at the end of the generated file */"
include_guard = "TFHE_RS_C_API_H"
# pragma_once = true
autogen_warning = "// Warning, this file is autogenerated by cbindgen. Do not modify this manually."
include_version = false
#namespace = "tfhe_rs_c_api"
namespaces = []
using_namespaces = []
sys_includes = []
includes = []
no_includes = false
cpp_compat = true
after_includes = ""
############################ Code Style Options ################################
braces = "SameLine"
line_length = 100
tab_width = 2
documentation = false
documentation_style = "auto"
line_endings = "LF" # also "CR", "CRLF", "Native"
############################# Codegen Options ##################################
style = "both"
sort_by = "Name" # default for `fn.sort_by` and `const.sort_by`
usize_is_size_t = true
[defines]
# "target_os = freebsd" = "DEFINE_FREEBSD"
# "feature = serde" = "DEFINE_SERDE"
[export]
include = ["BooleanParametersSet"]
exclude = []
#prefix = "CAPI_"
item_types = []
renaming_overrides_prefixing = false
[export.rename]
[export.body]
[export.mangle]
[fn]
rename_args = "None"
# must_use = "MUST_USE_FUNC"
# no_return = "NO_RETURN"
# prefix = "START_FUNC"
# postfix = "END_FUNC"
args = "auto"
sort_by = "Name"
[struct]
rename_fields = "None"
# must_use = "MUST_USE_STRUCT"
derive_constructor = false
derive_eq = false
derive_neq = false
derive_lt = false
derive_lte = false
derive_gt = false
derive_gte = false
[enum]
rename_variants = "None"
# must_use = "MUST_USE_ENUM"
add_sentinel = false
prefix_with_name = false
derive_helper_methods = false
derive_const_casts = false
derive_mut_casts = false
# cast_assert_name = "ASSERT"
derive_tagged_enum_destructor = false
derive_tagged_enum_copy_constructor = false
enum_class = true
private_default_tagged_enum_constructor = false
[const]
allow_static_const = true
allow_constexpr = false
sort_by = "Name"
[macro_expansion]
bitflags = false
############## Options for How Your Rust library Should Be Parsed ##############
[parse]
parse_deps = true
include = ["tfhe"]
exclude = []
clean = false
extra_bindings = []
[parse.expand]
crates = []
all_features = false
default_features = true
features = []

View File

@@ -0,0 +1,90 @@
# Operations and Examples
In thfe::boolean, the available operations are mainly related to their equivalent Boolean gates,
i.e., AND, OR,... In what follows, an example of a unary gate (NOT) and one about a binary gate
(XOR). The last one is about the ternary MUX gate are detailed, which gives the possibility to
homomorphically compute conditional statements of the form ``If..Then..Else``.
## The NOT unary gate
```rust
use tfhe::boolean::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (mut client_key, mut server_key) = gen_keys();
// We use the client secret key to encrypt a message:
let ct_1 = client_key.encrypt(true);
// We use the server public key to execute the NOT gate:
let ct_not = server_key.not(&ct_1);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_not);
assert_eq!(output, false);
}
```
## Binary gates
```rust
use tfhe::boolean::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (mut client_key, mut server_key) = gen_keys();
// We use the client secret key to encrypt a message:
let ct_1 = client_key.encrypt(true);
let ct_2 = client_key.encrypt(false);
// We use the server public key to execute the XOR gate:
let ct_xor = server_key.xor(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_xor);
assert_eq!(output, true^false);
}
```
## The MUX ternary gate
Let ``ct_1, ct_2, ct_3`` be three Boolean
ciphertexts. Then, the MUX gate (abbreviation of MUtipleXer) is equivalent to the operation:
```r
if ct_1 {
return ct_2
} else {
return ct_3
}
```
This example show how to use the MUX ternary gate.
```rust
use tfhe::boolean::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (mut client_key, mut server_key) = gen_keys();
let bool1 = true;
let bool2 = false;
let bool3 = true;
// We use the client secret key to encrypt a message:
let ct_1 = client_key.encrypt(true);
let ct_2 = client_key.encrypt(false);
let ct_3 = client_key.encrypt(false);
// We use the server public key to execute the NOT gate:
let ct_xor = server_key.mux(&ct_1, &ct_2, &ct_3);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_xor);
assert_eq!(output, if bool1 {bool2} else {bool3});
}
```

View File

@@ -0,0 +1,54 @@
# Cryptographic parameters
## Default parameters
The TFHE cryptographic scheme relies on a variant of [Regev cryptosystem](https://cims.nyu.edu/~regev/papers/lwesurvey.pdf), and is based on a problem so hard to solve, that is even post-quantum resistant.
In practice, you need to tune some cryptographic parameters, in order to ensure the correctness of the result, and the security of the computation.
To make it simpler, **we provide two sets of parameters**, which ensure correct computations for a certain probability with the standard security of 128 bits. There exists an error probability due the probabilistic nature of the encryption, which requires adding randomness (called noise) following a Gaussian distribution. If this noise is too large, the decryption will not give a correct result. There is a trade-off between efficiency and correctness: generally, using a less efficient parameter set (in terms of computation time) leads to a smaller risk of having an error during homomorphic evaluation.
In the two proposed sets of parameters, the only difference lies into this probability error.
The default parameter set ensures a probability error of at most $$2^{-40}$$ when computing a
programmable bootstrapping (i.e., any gates but the `not`). The other one is closer to the error
probability claimed into the original [TFHE paper](https://eprint.iacr.org/2018/421),
namely $$2^{-165}$$, but up to date regarding security requirements.
The following array summarizes this:
| Parameter set | Error probability |
|:-------------------:|:-----------------:|
| DEFAULT_PARAMETERS | $$ 2^{-40} $$ |
| TFHE_LIB_PARAMETERS | $$ 2^{-165} $$ |
## User-defined parameters
Note that if you desire, you can also create your own set of parameters.
This is an `unsafe` operation as failing to properly fix the parameters will potentially result with an incorrect and/or insecure computation:
```rust
use tfhe::boolean::prelude::*;
fn main() {
// WARNING: might be insecure and/or incorrect
// You can create your own set of parameters
let parameters = unsafe {
BooleanParameters::new(
LweDimension(586),
GlweDimension(2),
PolynomialSize(512),
StandardDev(0.00008976167396834998),
StandardDev(0.00000002989040792967434),
DecompositionBaseLog(8),
DecompositionLevelCount(2),
DecompositionBaseLog(2),
DecompositionLevelCount(5),
)
};
}
```

View File

@@ -0,0 +1,57 @@
# Save and Load Keys From Files
Since the `ServerKey` and `ClientKey` types both implement the `Serialize` and
`Deserialize` traits, you are free to use any serializer that suits you to save and load the
keys to disk.
Here is an example using the `bincode` serialization library, which serializes to a
binary format:
```rust
use std::fs::File;
use std::io::{Write, Read};
use tfhe::boolean::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys();
// We serialize the keys to bytes:
let encoded_server_key: Vec<u8> = bincode::serialize(&server_key).unwrap();
let encoded_client_key: Vec<u8> = bincode::serialize(&client_key).unwrap();
let server_key_file = "/tmp/ser_example_server_key.bin";
let client_key_file = "/tmp/ser_example_client_key.bin";
// We write the keys to files:
let mut file = File::create(server_key_file)
.expect("failed to create server key file");
file.write_all(encoded_server_key.as_slice()).expect("failed to write key to file");
let mut file = File::create(client_key_file)
.expect("failed to create client key file");
file.write_all(encoded_client_key.as_slice()).expect("failed to write key to file");
// We retrieve the keys:
let mut file = File::open(server_key_file)
.expect("failed to open server key file");
let mut encoded_server_key: Vec<u8> = Vec::new();
file.read_to_end(&mut encoded_server_key).expect("failed to read the key");
let mut file = File::open(client_key_file)
.expect("failed to open client key file");
let mut encoded_client_key: Vec<u8> = Vec::new();
file.read_to_end(&mut encoded_client_key).expect("failed to read the key");
// We deserialize the keys:
let loaded_server_key: ServerKey = bincode::deserialize(&encoded_server_key[..])
.expect("failed to deserialize");
let loaded_client_key: ClientKey = bincode::deserialize(&encoded_client_key[..])
.expect("failed to deserialize");
let ct_1 = client_key.encrypt(false);
// We check for equality:
assert_eq!(false, loaded_client_key.decrypt(&ct_1));
}
```

View File

@@ -0,0 +1,246 @@
# Tutorial: a first boolean circuit
This library is meant to be used both on the **server side** and on the **client side**.
The usual use case would follow those steps:
1. On the **client side**, generate the `client` and `server keys`.
2. Send the `server key` to the **server**.
3. Then any number of times:
+ On the **client side**, *encryption* of the input data with the `client key`.
+ Transmit the encrypted input to the **server**.
+ On the **server side**, *homomorphic computation* with the `server key`.
+ Transmit the encrypted output to the **client**.
+ On the **client side**, *decryption* of the output data with `client key`.
## 1. Setup
In the first step, the client creates two keys: the `client key` and the `server key`,
with the
`concrete_boolean::gen_keys` function:
```rust
use tfhe::boolean::prelude::*;
fn main() {
// We generate the client key and the server key,
// using the default parameters:
let (client_key, server_key): (ClientKey, ServerKey) = gen_keys();
}
```
In more details:
+ The `client_key` is of type `ClientKey`. It is **secret**, and must **never** be transmitted.
This key will only be used to encrypt and decrypt data.
+ The `server_key` is of type `ServerKey`. It is a **public key**, and can be shared with any
party.
This key has to be sent to the server because it is required for the homomorphic computation.
Note that both the `client_key` and `server_key` implement the `Serialize` and `Deserialize` traits.
This way you can use any compatible serializer to store/send the data. For instance, to store
the `server_key` in a binary file, you can use the `bincode` library:
```rust
use std::fs::File;
use std::io::{Write, Read};
use tfhe::boolean::prelude::*;
fn main() {
//---------------------------- CLIENT SIDE ----------------------------
// We generate a client key and a server key, using the default parameters:
let (client_key, server_key) = gen_keys();
// We serialize the server key to bytes, and store them in a file:
let encoded: Vec<u8> = bincode::serialize(&server_key).unwrap();
let server_key_file = "/tmp/tutorial_server_key.bin";
// We write the server key to a file:
let mut file = File::create(server_key_file)
.expect("failed to create server key file");
file.write_all(encoded.as_slice()).expect("failed to write key to file");
// ...
// We send the key to server side
// ...
//---------------------------- SERVER SIDE ----------------------------
// We read the file:
let mut file = File::open(server_key_file)
.expect("failed to open server key file");
let mut encoded: Vec<u8> = Vec::new();
file.read_to_end(&mut encoded).expect("failed to read key");
// We deserialize the server key:
let key: ServerKey = bincode::deserialize(&encoded[..])
.expect("failed to deserialize");
}
```
## 2. Encrypting Inputs
Once the server key is available on the **server side**, it is possible to perform some
homomorphic computations.
The client simply needs to encrypt some data and send it to the server.
Again, the `Ciphertext` type implements the `Serialize` and
the `Deserialize` traits, so that any serializer and communication tool suiting your use case
can be
used:
```rust
use tfhe::boolean::prelude::*;
fn main() {
// Don't consider the following line; you should follow the procedure above.
let (mut client_key, _) = gen_keys();
//---------------------------- SERVER SIDE
// We use the client key to encrypt the messages:
let ct_1 = client_key.encrypt(true);
let ct_2 = client_key.encrypt(false);
// We serialize the ciphertexts:
let encoded_1: Vec<u8> = bincode::serialize(&ct_1).unwrap();
let encoded_2: Vec<u8> = bincode::serialize(&ct_2).unwrap();
// ...
// And we send them to the server somehow
// ...
}
```
## 2bis. Encrypting Inputs using public key
Once the server key is available on the **server side**, it is possible to perform some
homomorphic computations.
The client simply needs to encrypt some data and send it to the server.
Again, the `Ciphertext` type implements the `Serialize` and
the `Deserialize` traits, so that any serializer and communication tool suiting your use case
can be
used:
```rust
use tfhe::boolean::prelude::*;
fn main() {
// Don't consider the following line; you should follow the procedure above.
let (client_key, _) = gen_keys();
let public_key = PublicKey::new(&client_key);
//---------------------------- SERVER SIDE
// We use the public key to encrypt the messages:
let ct_1 = public_key.encrypt(true);
let ct_2 = public_key.encrypt(false);
// We serialize the ciphertexts:
let encoded_1: Vec<u8> = bincode::serialize(&ct_1).unwrap();
let encoded_2: Vec<u8> = bincode::serialize(&ct_2).unwrap();
// ...
// And we send them to the server somehow
// ...
}
```
## Executing a Boolean Circuit
Once the encrypted inputs are on the **server side**, the `server_key` can be used to
homomorphically execute the desired boolean circuit:
```rust
use std::fs::File;
use std::io::{Write, Read};
use tfhe::boolean::prelude::*;
fn main() {
// Don't consider the following lines; you should follow the procedure above.
let (mut client_key, mut server_key) = gen_keys();
let ct_1 = client_key.encrypt(true);
let ct_2 = client_key.encrypt(false);
let encoded_1: Vec<u8> = bincode::serialize(&ct_1).unwrap();
let encoded_2: Vec<u8> = bincode::serialize(&ct_2).unwrap();
//---------------------------- ON SERVER SIDE ----------------------------
// We deserialize the ciphertexts:
let ct_1: Ciphertext = bincode::deserialize(&encoded_1[..])
.expect("failed to deserialize");
let ct_2: Ciphertext = bincode::deserialize(&encoded_2[..])
.expect("failed to deserialize");
// We use the server key to execute the boolean circuit:
// if ((NOT ct_2) NAND (ct_1 AND ct_2)) then (NOT ct_2) else (ct_1 AND ct_2)
let ct_3 = server_key.not(&ct_2);
let ct_4 = server_key.and(&ct_1, &ct_2);
let ct_5 = server_key.nand(&ct_3, &ct_4);
let ct_6 = server_key.mux(&ct_5, &ct_3, &ct_4);
// Then we serialize the output of the circuit:
let encoded_output: Vec<u8> = bincode::serialize(&ct_6)
.expect("failed to serialize output");
// ...
// And we send the output to the client
// ...
}
```
## Decrypting the output
Once the encrypted output is on the client side, the `client_key` can be used to
decrypt it:
```rust
use std::fs::File;
use std::io::{Write, Read};
use tfhe::boolean::prelude::*;
fn main() {
// Don't consider the following lines; you should follow the procedure above.
let (mut client_key, mut server_key) = gen_keys();
let ct_6 = client_key.encrypt(true);
let encoded_output: Vec<u8> = bincode::serialize(&ct_6).unwrap();
//---------------------------- ON CLIENT SIDE
// We deserialize the output ciphertext:
let output: Ciphertext = bincode::deserialize(&encoded_output[..])
.expect("failed to deserialize");
// Finally, we decrypt the output:
let output = client_key.decrypt(&output);
// And check that the result is the expected one:
assert_eq!(output, true);
}
```

38
tfhe/docs/README.md Normal file
View File

@@ -0,0 +1,38 @@
# What is TFHE-rs?
<mark style="background-color:yellow;">⭐️</mark> [<mark style="background-color:yellow;">Star the repo on Github</mark>](https://github.com/zama-ai/tfhe-rs) <mark style="background-color:yellow;">| 🗣</mark> [<mark style="background-color:yellow;">Community support forum</mark> ](https://community.zama.ai)<mark style="background-color:yellow;">| 📁</mark> [<mark style="background-color:yellow;">Contribute to the project</mark>](https://docs.zama.ai/tfhe-rs/developers/contributing)<mark style="background-color:yellow;"></mark>
![](_static/docs\_home.jpg)
TFHE-rs is a pure Rust implementation of TFHE for boolean and small integer arithmetics over encrypted data. It includes a Rust and C API, as well as a client-side WASM API.
TFHE-rs is meant for developers and researchers who want full control over what they can do with TFHE, while not having to worry about the low level implementation.
The goal is to have a stable, simple, high-performance, and production-ready library for all the advanced features of TFHE.
### Key Cryptographic concepts
TFHE-rs library implements Zamas variant of Fully Homomorphic Encryption over the Torus (TFHE). TFHE is based on Learning With Errors (LWE), a well studied cryptographic primitive believed to be secure even against quantum computers.
In cryptography, a raw value is called a message (also sometimes called a cleartext), an encoded message is called a plaintext and an encrypted plaintext is called a ciphertext.
The idea of homomorphic encryption is that you can compute on ciphertexts while not knowing messages encrypted in them. A scheme is said to be _fully homomorphic_, meaning any program can be evaluated with it, if at least two of the following operations are supported \($$x$$is a plaintext and $$E[x]$$ is the
corresponding ciphertext\):
* homomorphic univariate function evaluation: $$f(E[x]) = E[f(x)]$$
* homomorphic addition: $$E[x] + E[y] = E[x + y]$$
* homomorphic multiplication: $$E[x] * E[y] = E[x * y]$$
Zama's variant of TFHE is fully homomorphic and deals with fixed-precision numbers as messages. It implements all needed homomorphic operations, such as addition and function evaluation via **Programmable Bootstrapping**. You can read more about Zama's TFHE variant in the [preliminary whitepaper](https://whitepaper.zama.ai/).
Using FHE in a Rust program with TFHE-rs consists in:
* generating a client key and a server key using secure parameters:
* client key encrypts/decrypts data and must be kept secret
* server key is used to perform operations on encrypted data and could be
public (also called evaluation key)
* encrypting plaintexts using the client key to produce ciphertexts
* operating homomorphically on ciphertexts with the server key
* decrypting the resulting ciphertexts into plaintexts using the client key
If you would like to know more about the problems that FHE solves, we suggest you review our [6 minute introduction to homomorphic encryption](https://6min.zama.ai/).

33
tfhe/docs/SUMMARY.md Normal file
View File

@@ -0,0 +1,33 @@
# Table of contents
* [What is TFHE-rs?](README.md)
## Getting Started
* [Installation](getting\_started/installation.md)
* [Quick Start](getting\_started/quick\_start.md)
* [Supported Operations](getting\_started/operations.md)
* [Benchmarks](getting\_started/benchmarks.md)
* [Security and Cryptography](getting\_started/security\_and\_cryptography.md)
## Booleans
* [Tutorial](Booleans/tutorial.md)
* [Operations](Booleans/operations.md)
* [Cryptographic Parameters](Booleans/parameters.md)
* [Serialization/Deserialization](Booleans/serialization.md)
## Shortint
* [Tutorial](shortint/tutorial.md)
* [Operations](shortint/operations.md)
* [Cryptographic Parameters](shortint/parameters.md)
* [Serialization/Deserialization](shortint/serialization.md)
## C API
* [Tutorial](c_api/tutorial.md)
## Developers
* [Contributing](dev/contributing.md)
## API references
* [docs.rs](https://docs.rs/tfhe/)

View File

@@ -0,0 +1,16 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 424 173" width="424" height="173">
<!-- svg-source:excalidraw -->
<defs>
<style>
@font-face {
font-family: "Virgil";
src: url("https://excalidraw.com/Virgil.woff2");
}
@font-face {
font-family: "Cascadia";
src: url("https://excalidraw.com/Cascadia.woff2");
}
</style>
</defs>
<g stroke-linecap="round" transform="translate(26 44) rotate(0 194 38.60598503740641)"><path d="M-0.72 -1.78 C148.22 -1.79, 298.89 -0.4, 389.35 -0.66 M0.64 -0.29 C96.57 -1.4, 195.57 -0.99, 387.19 -1.07 M388.14 2.03 C392.43 23.61, 391.39 49, 385.98 75.21 M389.42 -0.67 C389.81 14.73, 390.33 33.43, 388.78 76.49 M387.8 79.39 C277.29 77.61, 166.8 75.17, -1.18 76.59 M387.01 76.05 C246.87 81.15, 103.96 81.92, 0.98 78.37 M-3.84 75.76 C-2.18 57.07, 4.75 34.74, -0.59 -2.81 M1 77.36 C-0.79 48.54, -1.41 22.74, 1.4 1.83" stroke="#364fc7" stroke-width="1" fill="none"></path></g><g stroke-linecap="round" transform="translate(43.27283572239912 63.29950568870447) rotate(0 35.31670822942641 19.35162094763092)"><path d="M0.11 -2.85 C28.93 -4.68, 48.62 2.97, 73.79 0.71 M0.92 1.49 C21.03 0.46, 41.01 -1.23, 70.4 0.64 M70.88 3.15 C68.43 11.93, 69.14 26.71, 68.06 39.17 M69.56 -1.68 C71.59 8.67, 68.48 17.57, 70.35 40.23 M68.74 39.32 C48.28 40.85, 30.28 35.73, -0.92 40.56 M69.59 37.89 C48.04 38.14, 23.44 38.86, -0.31 39.89 M2.24 36.95 C-1.92 26.09, -1.01 13.79, -3.25 -2.11 M0.55 39.03 C1.24 29.93, -1.56 22.42, 0.25 1.64" stroke="#0b7285" stroke-width="1" fill="none"></path></g><g stroke-linecap="round" transform="translate(244.47880299251847 58.546134663341206) rotate(0 77.40648379052368 25.15710723192018)"><path d="M0.5 3.27 C41.25 1.83, 85.12 4.03, 158.28 2.64 M1.74 -0.75 C60.33 -2.64, 122.13 -1.19, 153.85 -0.7 M155.49 -2.28 C158.86 11.5, 154.4 20.06, 153.49 51.17 M153.03 -0.55 C152.94 16.04, 156.21 35.59, 154.41 49.32 M157.56 51.63 C96.68 46.86, 29.51 52.23, -2.5 49.76 M155.17 52.19 C121.59 51.66, 87.99 54.57, -0.92 50.35 M-1.24 51.37 C-0.7 37.8, 0.93 29.69, -2.96 2.02 M-1.48 52.03 C0.44 36.29, -0.71 20.9, 1.27 0.34" stroke="#a61e4d" stroke-width="1" fill="none"></path></g><g transform="translate(249.47880299251847 71.70324189526139) rotate(0 72.40648379052368 12)"><text x="72.40648379052374" y="17" font-family="Virgil, Segoe UI Emoji" font-size="19.30839567747301px" fill="#a61e4d" text-anchor="middle" style="white-space: pre;" direction="ltr">noise</text></g><g stroke-linecap="round" transform="translate(35.610972568578745 52.67581047381532) rotate(0 89.501246882793 30.962593516209466)"><path d="M3.48 2.12 C42.75 -4.16, 86.77 -1.56, 177.5 3.84 M-1.92 1.02 C66.55 2.87, 130.16 1.88, 179.45 -1.63 M179.35 -3.41 C181.95 11.53, 178.24 25.72, 176.46 63.54 M180.52 0.51 C178.89 22.76, 177.57 49.34, 177.3 60.97 M175.68 59.6 C140.66 62.47, 97.79 56.72, 3.94 58.21 M177.14 61.73 C134.25 62.89, 91.18 61.47, 1.89 60.4 M-0.84 60.36 C-0.67 48.08, 3.19 30.78, -2.25 -3.82 M0.14 61.15 C-2.52 41.13, 0.16 19.4, -0.06 -0.75" stroke="#087f5b" stroke-width="1" fill="none"></path></g><g transform="translate(48.27283572239912 70.65112663633539) rotate(0 30.31670822942641 12)"><text x="30.316708229426418" y="17" font-family="Virgil, Segoe UI Emoji" font-size="19.248703637731044px" fill="#087f5b" text-anchor="middle" style="white-space: pre;" direction="ltr">carry</text></g><g stroke-linecap="round" transform="translate(123.17705735660911 63.319201995012975) rotate(0 43.5411471321695 21.286783042394035)"><path d="M3.78 -1.95 C21.85 2.27, 33.36 -1.31, 85.7 0.15 M0.95 -1.99 C30.47 0.73, 63.7 1.15, 87.61 0.81 M83.5 1.56 C89.47 15.22, 83.3 24.69, 84.55 46.54 M85.31 -0.87 C84.54 12.96, 85.46 26.61, 85.69 43.87 M85.54 45.92 C71.62 40.51, 50.01 40.74, -2.16 41.74 M85.45 44.45 C55.35 43.91, 24.36 42.43, 0.99 42.77 M-3.26 44.49 C1.26 31.11, -1.14 19.41, -1.89 2.33 M-1.27 43.89 C-1.22 30.47, 1.35 21.91, -0.8 -1.4" stroke="#0b7285" stroke-width="1" fill="none"></path></g><g transform="translate(128.1770573566091 72.60598503740698) rotate(0 38.5411471321695 12)"><text x="38.54114713216951" y="17" font-family="Virgil, Segoe UI Emoji" font-size="19.27057356608477px" fill="#087f5b" text-anchor="middle" style="white-space: pre;" direction="ltr">message</text></g><g transform="translate(371 138) rotate(0 20 12.5)"><text x="0" y="18" font-family="Virgil, Segoe UI Emoji" font-size="20px" fill="#364fc7" text-anchor="start" style="white-space: pre;" direction="ltr">LSB</text></g><g transform="translate(10 135) rotate(0 21.5 12.5)"><text x="0" y="18" font-family="Virgil, Segoe UI Emoji" font-size="20px" fill="#364fc7" text-anchor="start" style="white-space: pre;" direction="ltr">MSB</text></g><g transform="translate(162 10) rotate(0 51 12.5)"><text x="0" y="18" font-family="Virgil, Segoe UI Emoji" font-size="20px" fill="#364fc7" text-anchor="start" style="white-space: pre;" direction="ltr">Ciphertext</text></g></svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
tfhe/docs/_static/docs_home.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
tfhe/docs/_static/fig6.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
tfhe/docs/_static/fig7.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
tfhe/docs/_static/fig8.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
tfhe/docs/_static/lwe.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

174
tfhe/docs/c_api/tutorial.md Normal file
View File

@@ -0,0 +1,174 @@
# Tutorial: using the C API
Welcome to this `TFHE-rs` C API tutorial!
This library exposes a C binding to the `TFHE-rs` primitives to implement _Fully Homomorphic Encryption_ (FHE) programs.
# First steps using `TFHE-rs` C API
## Setting-up `TFHE-rs` C API for use in a C program.
`TFHE-rs` C API can be built on a Unix x86_64 machine using the following command:
```shell
RUSTFLAGS="-C target-cpu=native" cargo build --release --features=x86_64-unix,boolean-c-api,shortint-c-api -p tfhe
```
All features are opt-in, but for simplicity here, the C API is enabled for booleans and shortints.
The `tfhe.h` header as well as the static (.a) and dynamic (.so) `libtfhe` binaries can then be found in "${REPO_ROOT}/target/release/"
The build system needs to be set-up so that the C or C++ program links against `TFHE-rs` C API
binaries.
Here is a minimal CMakeLists.txt allowing to do just that:
```cmake
project(my-project)
cmake_minimum_required(VERSION 3.16)
set(TFHE_C_API "/path/to/tfhe-rs/binaries/and/header")
include_directories(${TFHE_C_API})
add_library(tfhe STATIC IMPORTED)
set_target_properties(tfhe PROPERTIES IMPORTED_LOCATION ${TFHE_C_API}/libtfhe.a)
if(APPLE)
find_library(SECURITY_FRAMEWORK Security)
if (NOT SECURITY_FRAMEWORK)
message(FATAL_ERROR "Security framework not found")
endif()
endif()
set(EXECUTABLE_NAME my-executable)
add_executable(${EXECUTABLE_NAME} main.c)
target_include_directories(${EXECUTABLE_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${EXECUTABLE_NAME} LINK_PUBLIC tfhe m pthread dl)
if(APPLE)
target_link_libraries(${EXECUTABLE_NAME} LINK_PUBLIC ${SECURITY_FRAMEWORK})
endif()
target_compile_options(${EXECUTABLE_NAME} PRIVATE -Werror)
```
## Commented code of a PBS doubling a 2 bits encrypted message using `TFHE-rs C API`
The steps required to perform the mutiplication by 2 of a 2 bits ciphertext
using a PBS are detailed.
This is NOT the most efficient way of doing this operation,
but it allows to show the management required to run a PBS manually using the C API.
WARNING: The following example does not have proper memory management in the error case to make it easier to fit the code on this page.
To run the example below, the above CMakeLists.txt and main.c files need to be in the same
directory. The commands to run are:
```shell
# /!\ Be sure to update CMakeLists.txt to give the absolute path to the compiled tfhe library
$ ls
CMakeLists.txt main.c
$ mkdir build && cd build
$ cmake .. -DCMAKE_BUILD_TYPE=RELEASE
...
$ make
...
$ ./my-executable
Result: 2
$
```
```c
#include "tfhe.h"
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
uint64_t double_accumulator_2_bits_message(uint64_t in) { return (in * 2) % 4; }
uint64_t get_max_value_of_accumulator_generator(uint64_t (*accumulator_func)(uint64_t),
size_t message_bits)
{
uint64_t max_value = 0;
for (size_t idx = 0; idx < (1 << message_bits); ++idx)
{
uint64_t acc_value = accumulator_func((uint64_t)idx);
max_value = acc_value > max_value ? acc_value : max_value;
}
return max_value;
}
int main(void)
{
ShortintPBSAccumulator *accumulator = NULL;
ShortintClientKey *cks = NULL;
ShortintServerKey *sks = NULL;
ShortintParameters *params = NULL;
// Get the parameters for 2 bits messages with 2 bits of carry
int get_params_ok = shortint_get_parameters(2, 2, &params);
assert(get_params_ok == 0);
// Generate the keys with the parameters
int gen_keys_ok = shortint_gen_keys_with_parameters(params, &cks, &sks);
assert(gen_keys_ok == 0);
// Generate the accumulator for the PBS
int gen_acc_ok = shortint_server_key_generate_pbs_accumulator(
sks, double_accumulator_2_bits_message, &accumulator);
assert(gen_acc_ok == 0);
ShortintCiphertext *ct = NULL;
ShortintCiphertext *ct_out = NULL;
// We will compute 1 * 2 using a PBS, it's not the recommended way to perform a multiplication,
// but it shows how to manage a PBS manually in the C API
uint64_t in_val = 1;
// Encrypt the input value
int encrypt_ok = shortint_client_key_encrypt(cks, in_val, &ct);
assert(encrypt_ok == 0);
// Check the degree is set to the maximum value that can be encrypted on 2 bits, i.e. 3
// This check is not required and is just added to show, the degree information can be retrieved
// in the C APi
size_t degree = -1;
int get_degree_ok = shortint_ciphertext_get_degree(ct, &degree);
assert(get_degree_ok == 0);
assert(degree == 3);
// Apply the PBS on our encrypted input
int pbs_ok = shortint_server_key_programmable_bootstrap(sks, accumulator, ct, &ct_out);
assert(pbs_ok == 0);
// Set the degree to keep consistency for potential further computations
// Note: This is only required for the PBS
size_t degree_to_set =
(size_t)get_max_value_of_accumulator_generator(double_accumulator_2_bits_message, 2);
int set_degree_ok = shortint_ciphertext_set_degree(ct_out, degree_to_set);
assert(set_degree_ok == 0);
// Decrypt the result
uint64_t result = -1;
int decrypt_non_assign_ok = shortint_client_key_decrypt(cks, ct_out, &result);
assert(decrypt_non_assign_ok == 0);
// Check the result is what we expect i.e. 2
assert(result == double_accumulator_2_bits_message(in_val));
printf("Result: %ld\n", result);
// Destroy entities from the C API
destroy_shortint_ciphertext(ct);
destroy_shortint_ciphertext(ct_out);
destroy_shortint_pbs_accumulator(accumulator);
destroy_shortint_client_key(cks);
destroy_shortint_server_key(sks);
destroy_shortint_parameters(params);
return EXIT_SUCCESS;
}
```
# Audience
Programmers wishing to use `TFHE-rs` but unable to use Rust (for various reasons) can use these bindings in their language of choice as long as it can interface with C code to bring `TFHE-rs` functionalities to said language.

View File

@@ -0,0 +1,6 @@
# Contribute
There are two ways to contribute to **TFHE-rs**:
* you can open issues to report bugs and typos and to suggest ideas
* you can ask to become an official contributor by emailing hello@zama.ai. Only approved contributors can end pull requests, so please make sure to get in touch before you do!

View File

@@ -0,0 +1,44 @@
# Benchmarks
Due to their nature, homomorphic operations are obviously slower than their clear equivalent. In what follows, some timings are exposed for the basic operations. For completeness, some benchmarks of other libraries are also given.
All the benchmarks had been launched on an AWS m6i.metal with the following specifications:
Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz and 512GB of RAM.
## Booleans
This measures the execution time of a single binary boolean gate.
### thfe.rs::booleans
| Parameter set | concrete-fft | concrete-fft + avx512 |
| --- | --- | --- |
| DEFAULT_PARAMETERS | 8.8ms | 6.8ms |
| TFHE_LIB_PARAMETERS | 13.6ms | 10.9ms |
### tfhe-lib
| Parameter set | fftw | spqlios-fma|
| --- | --- | --- |
| default_128bit_gate_bootstrapping_parameters | 28.9ms | 15.7ms |
### OpenFHE
| Parameter set | GINX | GINX (Intel HEXL) |
| --- | --- | --- |
| STD_128 | 172ms | 78ms |
| MEDIUM | 113ms | 50.2ms |
## Shortints
This measures the execution time for some operations and some parameter sets of shortints.
### thfe.rs::shortint
This uses the concrete-fft + avx512 configuration.
| Parameter set | unchecked_add | unchecked_mul_lsb | keyswitch_programmable_bootstrap |
| --- | --- | --- | --- |
| PARAM_MESSAGE_1_CARRY_1 | 337 ns | 10.1 ms | 9.91 ms |
| PARAM_MESSAGE_2_CARRY_2 | 407 ns | 21.7 ms | 21.4 ms |
| PARAM_MESSAGE_3_CARRY_3 | 3.06 µs | 161 ms | 159 ms |
| PARAM_MESSAGE_4_CARRY_4 | 11.7 µs | 1.03 s | 956 ms |

View File

@@ -0,0 +1,80 @@
# Installation
## Importing into your project
To use `TFHE-rs` in your project, you first need to add it as a dependency in your `Cargo.toml`:
```toml
tfhe = { version = "0.1.0", features = [ "boolean", "shortint", "x86_64-unix" ] }
```
## Choosing your features
`TFHE-rs` exposes different `cargo features` to customize the types and features used.
### Kinds.
This crate exposes two kinds of data types. Each kind is enabled by activating its corresponding feature in the TOML line. Each kind may have multiple types:
| Kind | Features | Type(s) |
| --------- | ------------- |------------------------------------------|
| Booleans | `boolean` | Booleans |
| ShortInts | `shortint` | Short unsigned integers |
### Serialization.
The different data types and keys exposed by the crate can be serialized / deserialized.
More information can be found [here](../Booleans/serialization.md) for Booleans and [here](../shortint/serialization.md) for shortint.
## Supported platforms
TFHE-rs is supported on Linux (x86, aarch64), macOS (x86, aarch64) and Windows (x86 with `RDSEED`
instruction).
| OS | x86 | aarch64 |
| --------- | ------------- |------------------|
| Linux | `x86_64-unix` | `aarch64-unix`* |
| macOS | `x86_64-unix` | `aarch64-unix`* |
| Windows | `x86_64` | Unsupported |
{% hint style="info" %}
Users who have ARM devices can use `TFHE-rs` by compiling using the
`nightly` toolchain.
{% endhint %}
### Using TFHE-rs with nightly toolchain
First, install the needed Rust toolchain:
```shell
rustup toolchain install nightly
```
Then, you can either:
* Manually specify the toolchain to use in each of the cargo commands:
For example:
```shell
cargo +nightly build
cargo +nightly test
```
* Or override the toolchain to use for the current project:
```shell
rustup override set nightly
# cargo will use the `nightly` toolchain.
cargo build
```
To check the toolchain that Cargo will use by default, you can use the following command:
```shell
rustup show
```

View File

@@ -0,0 +1,53 @@
# Supported Operations
## Boolean
The list of supported operations by the homomorphic booleans is:
|Operation Name | type |
| ------ | ------ |
| `not` | Unary |
| `and` | Binary |
| `or` | Binary |
| `xor` | Binary |
| `nor` | Binary |
| `xnor` | Binary |
| `cmux` | Ternary |
A walk-through using homomorphic Booleans can be found [here](../Booleans/tutorial.md).
## ShortInt
In TFHE-rs, the shortints represent short unsigned integers encoded over 8 bits maximum. A complete homomorphic arithmetic is provided, along with the possibility to compute univariate and bi-variate functions. Some operations are only available for integers up to 4 bits. More technical details can be found [here](../shortint/operations.md).
The list of supported operations is:
| Operation name | Type |
|--------------- | ------ |
| Negation | Unary |
| Addition | Binary |
| Subtraction | Binary |
| Multiplication | Binary |
| Division* | Binary |
| Modular reduction | Binary |
| Comparisons | Binary |
| Left/Right Shift | Binary |
| And | Binary |
| Or | Binary |
| Xor | Binary |
| Exact Function Evaluation | Unary/Binary |
{% hint style="info" %}
\* The division operation implements a subtlety: since data is encrypted, it might be possible to compute a division by 0. In this case, the division is tweaked so that dividing by 0 returns 0.
{% endhint %}
A walk-through example can be found [here](../shortint/tutorial.md) and more examples and
explanations can be found [here](../shortint/operations.md)

View File

@@ -0,0 +1,80 @@
# Quick start
This library makes it possible to execute **homomorphic operations over encrypted data**, where the data are either Booleans or short integers (named shortints in the rest of this documentation).
It allows one to execute a circuit on an **untrusted server** because both circuit inputs and outputs are kept **private**.
Data are indeed encrypted on the client side, before being sent to the server. On the server side every computation is performed on ciphertexts.
The server however has to know the circuit to be evaluated. At the end of the computation, the server returns the encryption of the result to the user. She can then decrypt it with her `secret key`.
## General method to write homomorphic circuit program
The overall process to write an homomorphic program is the same for both Boolean and short integers types.
In a nutshell, the basic steps for using the TFHE-rs library are the following:
- Choose a data type (Boolean or shortint)
- Import the library
- Create client and server keys
- Encrypt data with the client key
- Compute over encrypted data using the server key
- Decrypt data with the client key
### Boolean example
Here is an example to illustrate how the library can be used to evaluate a Boolean circuit:
```rust
use tfhe::boolean::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (mut client_key, mut server_key) = gen_keys();
// We use the client secret key to encrypt two messages:
let ct_1 = client_key.encrypt(true);
let ct_2 = client_key.encrypt(false);
// We use the server public key to execute a boolean circuit:
// if ((NOT ct_2) NAND (ct_1 AND ct_2)) then (NOT ct_2) else (ct_1 AND ct_2)
let ct_3 = server_key.not(&ct_2);
let ct_4 = server_key.and(&ct_1, &ct_2);
let ct_5 = server_key.nand(&ct_3, &ct_4);
let ct_6 = server_key.mux(&ct_5, &ct_3, &ct_4);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_6);
assert_eq!(output, true);
}
```
### Shortint example
and here is a full example using shortints:
```rust
use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys(Parameters::default());
let msg1 = 1;
let msg2 = 0;
let modulus = client_key.parameters.message_modulus.0;
// We use the client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
// We use the server public key to execute an integer circuit:
let ct_3 = server_key.unchecked_add(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_3);
assert_eq!(output, (msg1 + msg2) % modulus as u64);
}
```
The library is pretty simple to use, and can evaluate **homomorphic circuits of arbitrary length**. The description of the algorithms can be found in the [TFHE](https://doi.org/10.1007/s00145-019-09319-x) paper (also available as [ePrint 2018/421](https://ia.cr/2018/421)).

View File

@@ -0,0 +1,121 @@
# Cryptography & Security
# TFHE
TFHE-rs is a cryptographic library dedicated to Fully Homomorphic Encryption. As its name
suggests, it is based on the TFHE scheme.
It is interesting to understand some basics about TFHE,
in order to apprehend where the limitations are coming from both
in terms of precision (number of bits used to represent the plaintext values)
and execution time (why TFHE operations are slower than native operations).
# LWE Ciphertexts
Although there are many kinds of ciphertexts in TFHE,
all the encrypted values in TFHE-rs are mainly stored as LWE ciphertexts.
The security of TFHE relies on the LWE problem which stands for Learning With Errors.
The problem is believed to be secure against quantum attacks.
An LWE Ciphertext is a collection of 32-bits or 64-bits unsigned integers.
Before encrypting a message in an LWE ciphertext, one needs to first encode it as a plaintext.
This is done by shifting the message to the most significant bits of the unsigned integer type used.
Then, a little random value called noise is added to the least significant bits.
This noise (also called error for Learning With Errors) is crucial to the security of the ciphertext.
$$ plaintext = (\Delta * m) + e $$
![](../_static/lwe.png)
To go from a **plaintext** to a **ciphertext** one needs to encrypt the plaintext using a secret key.
An LWE secret key is a list of `n` random integers: $$S = (s_0, ..., s_n)$$.
$$n$$ is called the $$LweDimension$$
A LWE ciphertext, is composed of two parts:
- The mask $$(a_0, ..., a_{n-1})$$
- The body $$b$$
The mask of a _fresh_ ciphertext (one that is the result of an encryption
and not an operation such as ciphertext addition) is a list of `n` uniformly random values.
The body is computed as follows:
$$ b = (\sum_{i = 0}^{n-1}{a_i * s_i}) + plaintext $$
Now that the encryption scheme is defined, to illustrate why it is slower to compute over encrypted data,
let us show the example of the addition between ciphertexts.
To add two ciphertexts, we must add their $mask$ and $body$ as done below.
$$
ct_0 = (a_{0}, ..., a_{n}, b) \\
ct_1 = (a_{1}^{'}, ..., a_{n}^{'}, b^{'}) \\
ct_{2} = ct_0 + ct_1 \\
ct_{2} = (a_{0} + a_{0}^{'}, ..., a_{n} + a_{n}^{'}, b + b^{'})\\
b + b^{'} = (\sum_{i = 0}^{n-1}{a_i * s_i}) + plaintext + (\sum_{i = 0}^{n-1}{a_i^{'} * s_i}) + plaintext^{'}\\
b + b^{'} = (\sum_{i = 0}^{n-1}{(a_i + a_i^{'})* s_i}) + \Delta m + \Delta m^{'} + e + e^{'}\\
$$
To add ciphertexts, it is sufficient to add their masks and bodies.
Instead of just adding 2 integers, one needs to add $$n + 1$$ elements.
The addition is an intuitive example to show the slowdown of FHE computation compared to plaintext
computation but other operations are far more expensive
(e.g., the computation of a lookup table using the Programmable Bootstrapping)
# Ciphertexts Operations
## Understanding noise and padding
In FHE, there are two types of operations that can be applied to ciphertexts:
* **leveled operations**, which increase the noise in the ciphertext
* **bootstrapped operations**, which reduce the noise in the ciphertext
In FHE, the noise must be tracked and managed in order to guarantee the correctness of the computation.
Bootstrapping operations are used across the computation to decrease the noise in the ciphertexts, preventing it from tampering the message. The rest of the operations are called leveled because they do not need bootstrapping operations and thus are most of the time really fast.
The following sections explain the concept of noise and padding in ciphertexts.
### Noise
For it to be secure, LWE requires random noise to be added to the message at encryption time.
In TFHE, this random noise is drawn from a Centered Normal Distribution parameterized by a standard deviation. This standard deviation is a security parameter.
With all other security parameters set, the larger the standard deviation is, the more secure the encryption is.
In `TFHE-rs`, the noise is encoded in the least significant bits of the plaintexts. Each leveled computation will increase the noise, and thus if too many computations are done, the noise will eventually overflow onto the significant data bits of the message and lead to an incorrect result.
The figure below illustrates this problem in case of an addition, where an extra bit of noise is incurred as a result.
![Noise overtaking on the plaintexts after homomorphic addition. Most Significant bits are on the left.](../_static/fig7.png)
TFHE-rs offers the possibility to automatically manage the noise, by performing bootstrapping operations to reset the noise when needed.
### Padding
Since encoded values have a fixed precision, operating on them can sometime produce results that are outside the original interval. To avoid losing precision or wrapping around the interval, TFHE-rs uses additional bits by defining bits of **padding** on the most significant bits.
As an example, consider adding two ciphertexts. Adding two values could en up outside the range of either ciphertexts, and thus necessitate a carry, which would then be carried onto the first padding bit. In the figure below, each plaintext over 32 bits has one bit of padding on its left \(i.e., the most significant bit\). After the addition, the padding bit is no longer available, as it has been used in order for the carry. This is referred to as **consuming** bits of padding. Since no padding is left, there is no guarantee that additional additions would yield correct results.
![](../_static/fig6.png)
If you would like to know more about TFHE, you can find more information in our [TFHE Deep Dive](https://www.zama.ai/post/tfhe-deep-dive-part-1).
## Security
By default, the cryptographic parameters provided by `TFHE-rs` ensure at least 128 bits of security.
The security has been evaluated using the latest versions of the Lattice Estimator ([repository](https://github.com/malb/lattice-estimator)) with `red_cost_model = reduction.RC.BDGL16`.
For all sets of parameters, the error probability when computing a univariate function over one ciphertext is $$2^{-40}$$. Note that univariate functions might be performed when arithmetic functions are computed (for instance, the multiplication of two ciphertexts).
## Public key encryption
In public key encryption, the public key consists in providing a given number of message encrypting the value 0. By setting the number of encryptions of 0 in the public key at $$m = \lceil (n+1) \log(q) \rceil + \lambda$$, where $$n$$ is the LWE dimension, $$q$$ is the ciphertext modulus and $$\lambda$$ is the number of security bits. In a nutshell, this construction is secure due to the left-over-hash lemma, which is essentially related to the impossibility of breaking the underlying multiple subset sum problem. By using this formula, this guarantees both a high density subset sum and an exponentially large number of possible associated random vectors per LWE sample (a,b)

View File

@@ -0,0 +1,378 @@
# How Shortint are represented
In `shortint`, the encrypted data is stored in an LWE ciphertext.
Conceptually, the message stored in an LWE ciphertext, is divided into
a **carry buffer** and a **message buffer**.
![](../\_static/ciphertext-representation.svg)
The message buffer is the space where the actual message is stored. This represents the modulus of the input messages (denoted by `MessageModulus` in the code). When doing computations on a ciphertext, the encrypted message can overflow the message modulus: the exceeding information is stored in the carry buffer. The size of the carry buffer is defined by another modulus, called `CarryModulus`.
Together, the message modulus and the carry modulus form the plaintext space that is available in a ciphertext. This space cannot be overflowed, otherwise the computation may result in incorrect outputs.
In order to ensure the computation correctness, we keep track of the maximum value encrypted in a
ciphertext via an associated attribute called the **degree**. When the degree reaches a defined threshold, the carry buffer may be emptied to resume safely the computations. Therefore, in `shortint` the carry modulus is mainly considered as a means to do more computations.
# Types of operations
The operations available via a `ServerKey` may come in different variants:
- operations that take their inputs as encrypted values.
- scalar operations take at least one non-encrypted value as input.
For example, the addition has both variants:
- `ServerKey::unchecked_add` which takes two encrypted values and adds them.
- `ServerKey::unchecked_scalar_add` which takes an encrypted value and a clear value (the
so-called scalar) and adds them.
Each operation may come in different 'flavors':
- `unchecked`: Always does the operation, without checking if the result may exceed the capacity of
the plaintext space. Using this operations might have an impact on the correctness of the
following operations;
- `checked`: Checks are done before computing the operation, returning an error if operation
cannot be done safely;
- `smart`: Always does the operation, if the operation cannot be computed safely, the smart operation
will clear the carry modulus to make the operation possible.
Not all operations have these 3 flavors, as some of them are implemented in a way
that the operation is always possible without ever exceeding the plaintext space capacity.
# How to use operation types
Let's try to do a circuit evaluation using the different flavours of operations we already introduced.
For a very small circuit, the `unchecked` flavour may be enough to do the computation correctly.
Otherwise, the `checked` and `smart` are the best options.
As an example, let's do a scalar multiplication, a subtraction and a multiplication.
```rust
use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
let msg1 = 3;
let msg2 = 3;
let scalar = 4;
let modulus = client_key.parameters.message_modulus.0;
// We use the client key to encrypt two messages:
let mut ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
server_key.unchecked_scalar_mul_assign(&mut ct_1, scalar);
server_key.unchecked_sub_assign(&mut ct_1, &ct_2);
server_key.unchecked_mul_lsb_assign(&mut ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_1);
println!("expected {}, found {}", ((msg1 * scalar as u64 - msg2) * msg2) % modulus as u64, output);
}
```
During this computation the carry buffer has been overflowed and as all the operations were `unchecked` the output
may be incorrect.
If we redo this same circuit but using the `checked` flavour, a panic will occur.
```rust
use tfhe::shortint::prelude::*;
use std::error::Error;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
let msg1 = 3;
let msg2 = 3;
let scalar = 4;
let modulus = client_key.parameters.message_modulus.0;
// We use the client key to encrypt two messages:
let mut ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
let mut ops = || -> Result<(), Box<dyn Error>> {
server_key.checked_scalar_mul_assign(&mut ct_1, scalar)?;
server_key.checked_sub_assign(&mut ct_1, &ct_2)?;
server_key.checked_mul_lsb_assign(&mut ct_1, &ct_2)?;
Ok(())
};
match ops() {
Ok(_) => (),
Err(e) => {
println!("correctness of operations is not guaranteed due to error: {}", e);
return;
},
}
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_1);
assert_eq!(output, ((msg1 * scalar as u64 - msg2) * msg2) % modulus as u64);
}
```
Therefore, the `checked` flavour permits to manually manage the overflow of the carry buffer
by raising an error if the correctness is not guaranteed.
Lastly, using the `smart` flavour will output the correct result all the time. However, the
computation may be slower
as the carry buffer may be cleaned during the computations.
```rust
use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
let msg1 = 3;
let msg2 = 3;
let scalar = 4;
let modulus = client_key.parameters.message_modulus.0;
// We use the client key to encrypt two messages:
let mut ct_1 = client_key.encrypt(msg1);
let mut ct_2 = client_key.encrypt(msg2);
server_key.smart_scalar_mul_assign(&mut ct_1, scalar);
server_key.smart_sub_assign(&mut ct_1, &mut ct_2);
server_key.smart_mul_lsb_assign(&mut ct_1, &mut ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_1);
assert_eq!(output, ((msg1 * scalar as u64 - msg2) * msg2) % modulus as u64);
}
```
#List of available operations
{% hint style="warning" %}
Currently, certain operations can only be used if the parameter set chosen is compatible with the
bivariate programmable bootstrapping, meaning the carry buffer is larger or equal than the
message buffer. These operations are marked with a star (*).
{% endhint %}
The list of implemented operations for shortints is:
- addition between two ciphertexts
- addition between a ciphertext and an unencrypted scalar
- comparisons `<`, `<=`, `>`, `>=`, `==` between a ciphertext and an unencrypted scalar
- division of a ciphertext by an unencrypted scalar
- LSB multiplication between two ciphertexts returning the result truncated to fit in the `message buffer`
- multiplication of a ciphertext by an unencrypted scalar
- bitwise shift `<<`, `>>`
- subtraction of a ciphertext by another ciphertext
- subtraction of a ciphertext by an unencrypted scalar
- negation of a ciphertext
- bitwise and, or and xor (*)
- comparisons `<`, `<=`, `>`, `>=`, `==` between two ciphertexts (*)
- division between two ciphertexts (*)
- MSB multiplication between two ciphertexts returning the part overflowing the `message buffer` (*)
In what follows, some simple code examples are given.
## Public key encryption
TFHE-rs supports both private and public key encryption methods. Note that the only difference
between both lies into the encryption step: in this case, the encryption method is called using
`public_key` instead of `client_key`.
Here a small example on how to use public encryption:
```rust
use tfhe::boolean::prelude::*;
fn main() {
// Generate the client key and the server key:
let (cks, mut sks) = gen_keys();
let pks = PublicKey::new(&cks);
// Encryption of one message:
let ct = pks.encrypt(true);
// Decryption:
let dec = cks.decrypt(&ct);
assert_eq!(true, dec);
}
```
In what follows, all examples are related to private key encryption.
## Arithmetic operations
Classical arithmetic operations are supported by shortints:
```rust
use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys to compute over Z/2^2Z, with 2 carry bits
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
let msg1 = 2;
let msg2 = 1;
let modulus = client_key.parameters.message_modulus.0;
// We use the private client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
// We use the server public key to execute an integer circuit:
let ct_3 = server_key.unchecked_add(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_3);
assert_eq!(output, (msg1 + msg2) % modulus as u64);
}
```
### Bitwise operations
Short homomorphic integer types support some bitwise operations.
A simple example on how to use these operations:
```rust
use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys to compute over Z/2^2Z, with 2 carry bits
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
let msg1 = 2;
let msg2 = 1;
let modulus = client_key.parameters.message_modulus.0;
// We use the private client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
// We use the server public key to homomorphically compute a bitwise AND:
let ct_3 = server_key.unchecked_bitand(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_3);
assert_eq!(output, (msg1 & msg2) % modulus as u64);
}
```
### Comparisons
Short homomorphic integer types support comparison operations.
A simple example on how to use these operations:
```rust
use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys to compute over Z/2^2Z, with 2 carry bits
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
let msg1 = 2;
let msg2 = 1;
let modulus = client_key.parameters.message_modulus.0;
// We use the private client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
// We use the server public key to execute an integer circuit:
let ct_3 = server_key.unchecked_greater_or_equal(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_3);
assert_eq!(output, (msg1 >= msg2) as u64 % modulus as u64);
}
```
### Univariate function evaluations
A simple example on how to use this operation to homomorphically compute
the hamming weight (i.e., the number of bit equals to one) of an encrypted
number.
```rust
use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys to compute over Z/2^2Z, with 2 carry bits
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
let msg1 = 3;
let modulus = client_key.parameters.message_modulus.0;
// We use the private client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
//define the accumulator as the
let acc = server_key.generate_accumulator(|n| n.count_ones().into());
// add the two ciphertexts
let ct_res = server_key.keyswitch_programmable_bootstrap(&ct_1, &acc);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_res);
assert_eq!(output, msg1.count_ones() as u64);
}
```
### Bi-variate function evaluations
Using the shortint types offers the possibility to evaluate bi-variate functions, i.e.,
functions that takes two ciphertexts as input. This requires to choose a parameter set
such that the carry buffer size is at least as large as the message one i.e.,
PARAM_MESSAGE_X_CARRY_Y with X <= Y.
In what follows, a simple code example:
```rust
use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys to compute over Z/2^2Z, with 2 carry bits
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
let msg1 = 3;
let msg2 = 2;
let modulus = client_key.parameters.message_modulus.0 as u64;
// We use the private client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
// Compute the accumulator for the bivariate functions
let acc = server_key.generate_accumulator_bivariate(|x,y| (x.count_ones()
+ y.count_ones()) as u64 % modulus );
let ct_res = server_key.keyswitch_programmable_bootstrap_bivariate(&ct_1, &ct_2, &acc);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_res);
assert_eq!(output, (msg1.count_ones() as u64 + msg2.count_ones() as u64) % modulus);
}
```

View File

@@ -0,0 +1,88 @@
# Cryptographic parameters
All parameter sets provides at least 128-bits of security according to the [Lattice-Estimator](https://github.com/malb/lattice-estimator), with an error probability equals to $$2^{-40}$$ when computing a programmable bootstrapping. This error probability is due to the randomness added at each encryption (see [here](../getting_started/security_and_cryptography.md) for more details about the encryption process).
## Parameters and message precision
`shortint` comes with sets of parameters that permit to use the functionalities of the library securely and efficiently. Each parameter sets is associated to the message and carry precisions. Thus, each key pair is entangled to precision.
The user is allowed to choose which set of parameters to use when creating the pair of keys.
The difference between the parameter sets is the total amount of space dedicated to the plaintext and how it is split between the message buffer and the carry buffer. The syntax chosen for the name of a parameter is:
`PARAM_MESSAGE_{number of message bits}_CARRY_{number of carry bits}`. For example, the set of parameters for a message buffer of 5 bits and a carry buffer of 2 bits is `PARAM_MESSAGE_5_CARRY_2`.
In what follows, there is an example where keys are generated to have messages encoded over 3 bits i.e., computations are done modulus $$2^3 = 8$$), with 3 bits of carry.
```rust
use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
let msg1 = 3;
let msg2 = 7;
// We use the client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
}
```
## Impact of parameters on the operations
As shown [here](../getting_started/benchmarks.md), the choice of the parameter set impacts the operations available and their efficiency.
### Generic bi-variate functions
The computations of bi-variate functions is based on a trick *concatenating* two ciphertexts into one. In the case where the carry buffer is not at least as large as the message one, this trick is not working anymore. Then, many bi-variate operations, such as comparisons cannot be correctly computed anymore. The only exception concerns the multiplication.
### Multiplication
In the case of the multiplication, two algorithms are implemented: the first one relies on the bi-variate function trick, where the other one is based on the [quarter square method](https://en.wikipedia.org/wiki/Multiplication_algorithm#Quarter_square_multiplication). In order to correctly compute a multiplication, the only requirement is to have at least one bit of carry (i.e., using parameter sets PARAM_MESSAGE_X_CARRY_Y with Y>=1). This method is in general slower than using the other one. Note that using the `smart` version of the multiplication automatically chooses which algorithm is used depending on the chosen parameters.
## User-defined parameter sets
Beyond the predefined parameter sets, this is possible to define new parameter sets.
To do so, it is sufficient to use the function `unsecure_parameters()` or to manually fulfill the
`Parameter` structure fields.
For instance:
```rust
use tfhe::shortint::prelude::*;
fn main() {
let param = unsafe {
Parameters::new(
LweDimension(656),
GlweDimension(2),
PolynomialSize(512),
StandardDev(0.000034119201269311964),
StandardDev(0.00000004053919869756513),
DecompositionBaseLog(8),
DecompositionLevelCount(2),
DecompositionBaseLog(3),
DecompositionLevelCount(4),
StandardDev(0.00000000037411618952047216),
DecompositionBaseLog(15),
DecompositionLevelCount(1),
DecompositionLevelCount(0),
DecompositionBaseLog(0),
MessageModulus(4),
CarryModulus(1),
)
};
}
```

View File

@@ -0,0 +1,64 @@
# Serialization / Deserialization
As explained in the introduction, some types (`Serverkey`, `Ciphertext`) are meant to be shared with the server that does the computations.
The easiest way to send these data to a server is to use the serialization and deserialization features. tfhe::shortint uses the [serde](https://crates.io/crates/serde) framework, serde's Serialize and Deserialize are implemented on tfhe::shortint's types.
To be able to serialize our data, we need to pick a [data format](https://serde.rs/#data-formats), for our use case, [bincode](https://crates.io/crates/bincode) is a good choice, mainly because it is binary format.
```toml
# Cargo.toml
[dependencies]
# ...
bincode = "1.3.3"
```
```rust
// main.rs
use bincode;
use std::io::Cursor;
use tfhe::shortint::prelude::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let (client_key, server_key) = gen_keys(Parameters::default());
let msg1 = 1;
let msg2 = 0;
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
let mut serialized_data = Vec::new();
bincode::serialize_into(&mut serialized_data, &server_key)?;
bincode::serialize_into(&mut serialized_data, &ct_1)?;
bincode::serialize_into(&mut serialized_data, &ct_2)?;
// Simulate sending serialized data to a server and getting
// back the serialized result
let serialized_result = server_function(&serialized_data)?;
let result: Ciphertext = bincode::deserialize(&serialized_result)?;
let output = client_key.decrypt(&result);
assert_eq!(output, msg1 + msg2);
Ok(())
}
fn server_function(serialized_data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let mut serialized_data = Cursor::new(serialized_data);
let server_key: ServerKey = bincode::deserialize_from(&mut serialized_data)?;
let ct_1: Ciphertext = bincode::deserialize_from(&mut serialized_data)?;
let ct_2: Ciphertext = bincode::deserialize_from(&mut serialized_data)?;
let result = server_key.unchecked_add(&ct_1, &ct_2);
let serialized_result = bincode::serialize(&result)?;
Ok(serialized_result)
}
```

View File

@@ -0,0 +1,96 @@
# Tutorial: Writing an homomorphic circuit using shortints
# 1. Key Generation
`tfhe::shortint` provides 2 key types:
- `ClientKey`
- `ServerKey`
The `ClientKey` is the key that encrypts and decrypts messages (integer values up to 8 bits here), thus this key is meant to be kept private and should never be shared. This key is created from parameter values that will dictate both the security and efficiency of computations. The parameters also set the maximum number of bits of message encrypted in a ciphertext.
The `ServerKey` is the key that is used to actually do the FHE computations. It contains (among other things) a bootstrapping key and a keyswitching key. This key is created from a `ClientKey` that needs to be shared to the server, therefore it is not meant to be kept private. A user with a `ServerKey` can compute on the encrypted data sent by the owner of the associated `ClientKey`.
To reflect that, computation/operation methods are tied to the `ServerKey` type.
```rust
use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys(Parameters::default());
}
```
# 2. Encrypting values
Once the keys have been generated, the client key is used to encrypt data:
```rust
use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys(Parameters::default());
let msg1 = 1;
let msg2 = 0;
// We use the client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
}
```
# 2 bis. Encrypting values using a public key
Once the keys have been generated, the client key is used to encrypt data:
```rust
use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys(Parameters::default());
let public_key = PublicKey::new(&client_key);
let msg1 = 1;
let msg2 = 0;
// We use the client key to encrypt two messages:
let ct_1 = public_key.encrypt(&server_key, msg1);
let ct_2 = public_key.encrypt(&server_key, msg2);
}
```
# 3. Computing and decrypting
With our `server_key`, and encrypted values, we can now do an addition
and then decrypt the result.
```rust
use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys(Parameters::default());
let msg1 = 1;
let msg2 = 0;
let modulus = client_key.parameters.message_modulus.0;
// We use the client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
// We use the server public key to execute an integer circuit:
let ct_3 = server_key.unchecked_add(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_3);
assert_eq!(output, (msg1 + msg2) % modulus as u64);
}
```

View File

@@ -0,0 +1,31 @@
use tfhe::shortint::keycache::{FileStorage, NamedParam, PersistentStorage};
use tfhe::shortint::parameters::ALL_PARAMETER_VEC;
use tfhe::shortint::{gen_keys, ClientKey, ServerKey};
fn client_server_keys() {
let file_storage = FileStorage::new("keys/shortint/client_server".to_string());
println!("Generating (ClientKey, ServerKey)");
for (i, params) in ALL_PARAMETER_VEC.iter().copied().enumerate() {
println!(
"Generating [{} / {}] : {}",
i + 1,
ALL_PARAMETER_VEC.len(),
params.name()
);
let keys: Option<(ClientKey, ServerKey)> = file_storage.load(params);
if keys.is_some() {
continue;
}
let client_server_keys = gen_keys(params);
file_storage.store(params, &client_server_keys);
}
}
fn main() {
client_server_keys()
}

View File

@@ -0,0 +1,28 @@
use tfhe::boolean::client_key::ClientKey;
use tfhe::boolean::parameters::TFHE_LIB_PARAMETERS;
use tfhe::boolean::prelude::BinaryBooleanGates;
use tfhe::boolean::server_key::ServerKey;
fn main() {
// let (cks, sks) = gen_keys();
let cks = ClientKey::new(&TFHE_LIB_PARAMETERS);
let sks = ServerKey::new(&cks);
let left = false;
let right = true;
let ct_left = cks.encrypt(left);
let ct_right = cks.encrypt(right);
let start = std::time::Instant::now();
let num_loops: usize = 10000;
for _ in 0..num_loops {
let _ = sks.and(&ct_left, &ct_right);
}
let elapsed = start.elapsed().as_millis() as f64;
let mean: f64 = elapsed / num_loops as f64;
println!("{elapsed:?} ms, mean {mean:?} ms");
}

View File

@@ -0,0 +1,60 @@
//! An encryption of a boolean message.
//!
//! This module implements the ciphertext structure containing an encryption of a Boolean message.
use crate::core_crypto::prelude::*;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// A structure containing a ciphertext, meant to encrypt a Boolean message.
///
/// It is used to evaluate a Boolean circuits homomorphically.
#[derive(Clone, Debug)]
pub enum Ciphertext {
Encrypted(LweCiphertext32),
Trivial(bool),
}
#[derive(Serialize, Deserialize)]
enum SerializableCiphertext {
Encrypted(Vec<u8>),
Trivial(bool),
}
impl Serialize for Ciphertext {
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)?;
match self {
Ciphertext::Encrypted(lwe) => {
let ciphertext = ser_eng.serialize(lwe).map_err(serde::ser::Error::custom)?;
SerializableCiphertext::Encrypted(ciphertext)
}
Ciphertext::Trivial(b) => SerializableCiphertext::Trivial(*b),
}
.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Ciphertext {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
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)?;
Self::Encrypted(lwe)
}
SerializableCiphertext::Trivial(b) => Self::Trivial(b),
})
}
}

View File

@@ -0,0 +1,189 @@
//! The secret key of the client.
//!
//! This module implements the generation of the client' secret keys, together with the
//! encryption and decryption methods.
use crate::boolean::ciphertext::Ciphertext;
use crate::boolean::engine::{CpuBooleanEngine, WithThreadLocalEngine};
use crate::boolean::parameters::BooleanParameters;
use crate::core_crypto::prelude::*;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{Debug, Formatter};
/// A structure containing the client key, which must be kept secret.
///
/// In more details, it contains:
/// * `lwe_secret_key` - an LWE secret key, used to encrypt the inputs and decrypt the outputs.
/// This secret key is also used in the generation of bootstrapping and key switching keys.
/// * `glwe_secret_key` - a GLWE secret key, used to generate the bootstrapping keys and key
/// switching keys.
/// * `parameters` - the cryptographic parameter set.
#[derive(Clone)]
pub struct ClientKey {
pub(crate) lwe_secret_key: LweSecretKey32,
pub(crate) glwe_secret_key: GlweSecretKey32,
pub(crate) parameters: BooleanParameters,
}
impl PartialEq for ClientKey {
fn eq(&self, other: &Self) -> bool {
self.parameters == other.parameters
&& self.lwe_secret_key == other.lwe_secret_key
&& self.glwe_secret_key == other.glwe_secret_key
}
}
impl Debug for ClientKey {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "ClientKey {{ ")?;
write!(f, "lwe_secret_key: {:?}, ", self.lwe_secret_key)?;
write!(f, "glwe_secret_key: {:?}, ", self.glwe_secret_key)?;
write!(f, "parameters: {:?}, ", self.parameters)?;
write!(f, "engine: CoreEngine, ")?;
write!(f, "}}")?;
Ok(())
}
}
impl ClientKey {
/// Encrypts a Boolean message using the client key.
///
/// # Example
///
/// ```rust
/// # #[cfg(not(feature = "cuda"))]
/// # fn main() {
/// use tfhe::boolean::prelude::*;
///
/// // Generate the client key and the server key:
/// let (cks, mut sks) = gen_keys();
///
/// // Encryption of one message:
/// let ct = cks.encrypt(true);
///
/// // Decryption:
/// 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))
}
/// Decrypts a ciphertext encrypting a Boolean message using the client key.
///
/// # Example
///
/// ```rust
/// # #[cfg(not(feature = "cuda"))]
/// # fn main() {
/// use tfhe::boolean::prelude::*;
///
/// // Generate the client key and the server key:
/// let (cks, mut sks) = gen_keys();
///
/// // Encryption of one message:
/// let ct = cks.encrypt(true);
///
/// // Decryption:
/// 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))
}
/// 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).
///
/// # Example
///
/// ```rust
/// # #[cfg(not(feature = "cuda"))]
/// # fn main() {
/// use tfhe::boolean::client_key::ClientKey;
/// use tfhe::boolean::parameters::TFHE_LIB_PARAMETERS;
/// use tfhe::boolean::prelude::*;
///
/// // 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))
}
}
#[derive(Serialize, Deserialize)]
struct SerializableClientKey {
lwe_secret_key: Vec<u8>,
glwe_secret_key: Vec<u8>,
parameters: BooleanParameters,
}
impl Serialize for ClientKey {
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 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)?;
SerializableClientKey {
lwe_secret_key,
glwe_secret_key,
parameters: self.parameters,
}
.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for ClientKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
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())
.map_err(serde::de::Error::custom)?,
glwe_secret_key: de_eng
.deserialize(thing.glwe_secret_key.as_slice())
.map_err(serde::de::Error::custom)?,
parameters: thing.parameters,
})
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,936 @@
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 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::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;
fn nor(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
fn or(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
fn xor(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
fn xnor(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
}
pub(crate) trait BinaryGatesAssignEngine<L, R, K> {
fn and_assign(&mut self, ct_left: L, ct_right: R, server_key: &K);
fn nand_assign(&mut self, ct_left: L, ct_right: R, server_key: &K);
fn nor_assign(&mut self, ct_left: L, ct_right: R, server_key: &K);
fn or_assign(&mut self, ct_left: L, ct_right: R, server_key: &K);
fn xor_assign(&mut self, ct_left: L, ct_right: R, server_key: &K);
fn xnor_assign(&mut self, ct_left: L, ct_right: R, server_key: &K);
}
/// Trait to be able to acces thread_local
/// engines in a generic way
pub(crate) trait WithThreadLocalEngine {
fn with_thread_local_mut<R, F>(func: F) -> R
where
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());
}
impl WithThreadLocalEngine for CpuBooleanEngine {
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
}
}
// We have q = 32 so log2q = 5
const LOG2_Q_32: usize = 5;
impl<B> BooleanEngine<B> {
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();
// generate the rlwe secret key
let glwe_secret_key: GlweSecretKey32 = self
.engine
.generate_new_glwe_secret_key(parameters.glwe_dimension, parameters.polynomial_size)
.unwrap();
ClientKey {
lwe_secret_key,
glwe_secret_key,
parameters,
}
}
pub fn create_public_key(&mut self, client_key: &ClientKey) -> PublicKey {
let client_parameters = client_key.parameters;
// Formula is (n + 1) * log2(q) + 128
let zero_encryption_count = LwePublicKeyZeroEncryptionCount(
client_parameters.lwe_dimension.to_lwe_size().0 * LOG2_Q_32 + 128,
);
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(),
parameters: client_key.parameters.to_owned(),
}
}
pub fn trivial_encrypt(&mut self, message: bool) -> Ciphertext {
Ciphertext::Trivial(message)
}
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()
} else {
self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap()
};
// 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();
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()
} else {
self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap()
};
let mut underlying_ciphertext = self
.engine
.create_lwe_ciphertext_from(vec![0u32; pks.parameters.lwe_dimension.to_lwe_size().0])
.unwrap();
// encryption
self.engine
.discard_encrypt_lwe_ciphertext_with_public_key(
&pks.lwe_public_key,
&mut underlying_ciphertext,
&plain,
)
.unwrap();
Ciphertext::Encrypted(underlying_ciphertext)
}
pub fn decrypt(&mut self, ct: &Ciphertext, cks: &ClientKey) -> bool {
match ct {
Ciphertext::Trivial(b) => *b,
Ciphertext::Encrypted(ciphertext) => {
// decryption
let decrypted = self
.engine
.decrypt_lwe_ciphertext(&cks.lwe_secret_key, ciphertext)
.unwrap();
// cast as a u32
let mut decrypted_u32: u32 = 0;
self.engine
.discard_retrieve_plaintext(&mut decrypted_u32, &decrypted)
.unwrap();
// return
decrypted_u32 < (1 << 31)
}
}
}
pub fn not(&mut self, ct: &Ciphertext) -> Ciphertext {
match ct {
Ciphertext::Trivial(message) => Ciphertext::Trivial(!*message),
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
// Output the result:
Ciphertext::Encrypted(ct_res)
}
}
}
pub fn not_assign(&mut self, ct: &mut Ciphertext) {
match ct {
Ciphertext::Trivial(message) => *message = !*message,
Ciphertext::Encrypted(ct_ct) => {
self.engine.fuse_opp_lwe_ciphertext(ct_ct).unwrap(); // compute the negation
}
}
}
}
impl<B> BooleanEngine<B>
where
B: Bootstrapper,
{
pub fn new() -> Self {
let root_seeder = new_seeder();
Self::new_from_seeder(root_seeder)
}
pub fn new_from_seeder(mut root_seeder: Box<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");
Self {
engine,
bootstrapper: Default::default(),
}
}
/// convert into an actual LWE ciphertext even when trivial
fn convert_into_lwe_ciphertext_32(
&mut self,
ct: &Ciphertext,
server_key: &B::ServerKey,
) -> LweCiphertext32 {
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()
} else {
self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap()
};
self.engine
.trivially_encrypt_lwe_ciphertext(server_key.lwe_size(), &plain)
.unwrap()
}
}
}
pub fn mux(
&mut self,
ct_condition: &Ciphertext,
ct_then: &Ciphertext,
ct_else: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
// In theory MUX gate = (ct_condition AND ct_then) + (!ct_condition AND ct_else)
match ct_condition {
// in the case of the condition is trivially encrypted
Ciphertext::Trivial(message_condition) => {
if *message_condition {
ct_then.clone()
} else {
ct_else.clone()
}
}
Ciphertext::Encrypted(ct_condition_ct) => {
// condition is actually encrypted
// take a shortcut if ct_then is trivially encrypted
if let Ciphertext::Trivial(message_then) = ct_then {
return if *message_then {
self.or(ct_condition, ct_else, server_key)
} else {
let ct_not_condition = self.not(ct_condition);
self.and(&ct_not_condition, ct_else, server_key)
};
}
// take a shortcut if ct_else is trivially encrypted
if let Ciphertext::Trivial(message_else) = ct_else {
return if *message_else {
let ct_not_condition = self.not(ct_condition);
self.or(ct_then, &ct_not_condition, server_key)
} else {
self.and(ct_condition, ct_then, server_key)
};
}
// convert inputs into LweCiphertext32
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 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
// 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
// Compute the first programmable bootstrapping with fixed test polynomial:
let mut ct_pbs_1 = bootstrapper
.bootstrap(buffer_lwe_before_pbs, server_key)
.unwrap();
let ct_pbs_2 = bootstrapper.bootstrap(&ct_temp_2, server_key).unwrap();
// 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
let ct_ks = bootstrapper.keyswitch(&ct_pbs_1, server_key).unwrap();
// Output the result:
Ciphertext::Encrypted(ct_ks)
}
}
}
}
impl<B> BinaryGatesEngine<&Ciphertext, &Ciphertext, B::ServerKey> for BooleanEngine<B>
where
B: Bootstrapper,
{
fn and(
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
Ciphertext::Trivial(*message_left && *message_right)
}
(Ciphertext::Encrypted(_), Ciphertext::Trivial(message_right)) => {
self.and(ct_left, *message_right, server_key)
}
(Ciphertext::Trivial(message_left), Ciphertext::Encrypted(_)) => {
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 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
// compute the bootstrap and the key switch
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
}
fn nand(
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
Ciphertext::Trivial(!(*message_left && *message_right))
}
(Ciphertext::Encrypted(_), Ciphertext::Trivial(message_right)) => {
self.nand(ct_left, *message_right, server_key)
}
(Ciphertext::Trivial(message_left), Ciphertext::Encrypted(_)) => {
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 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
// compute the bootstrap and the key switch
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
}
fn nor(
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
Ciphertext::Trivial(!(*message_left || *message_right))
}
(Ciphertext::Encrypted(_), Ciphertext::Trivial(message_right)) => {
self.nor(ct_left, *message_right, server_key)
}
(Ciphertext::Trivial(message_left), Ciphertext::Encrypted(_)) => {
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 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
// compute the bootstrap and the key switch
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
}
fn or(
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
Ciphertext::Trivial(*message_left || *message_right)
}
(Ciphertext::Encrypted(_), Ciphertext::Trivial(message_right)) => {
self.or(ct_left, *message_right, server_key)
}
(Ciphertext::Trivial(message_left), Ciphertext::Encrypted(_)) => {
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 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
// compute the bootstrap and the key switch
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
}
fn xor(
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
Ciphertext::Trivial(*message_left ^ *message_right)
}
(Ciphertext::Encrypted(_), Ciphertext::Trivial(message_right)) => {
self.xor(ct_left, *message_right, server_key)
}
(Ciphertext::Trivial(message_left), Ciphertext::Encrypted(_)) => {
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 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
// compute the bootstrap and the key switch
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
}
fn xnor(
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
Ciphertext::Trivial(!(*message_left ^ *message_right))
}
(Ciphertext::Encrypted(_), Ciphertext::Trivial(message_right)) => {
self.xnor(ct_left, *message_right, server_key)
}
(Ciphertext::Trivial(message_left), Ciphertext::Encrypted(_)) => {
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 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
// compute the bootstrap and the key switch
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
}
}
impl<B> BinaryGatesAssignEngine<&mut Ciphertext, &Ciphertext, B::ServerKey> for BooleanEngine<B>
where
B: Bootstrapper,
{
fn and_assign(
&mut self,
ct_left: &mut Ciphertext,
ct_right: &Ciphertext,
server_key: &B::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: &Ciphertext,
server_key: &B::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: &Ciphertext,
server_key: &B::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: &Ciphertext,
server_key: &B::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: &Ciphertext,
server_key: &B::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: &Ciphertext,
server_key: &B::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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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 {
if ct_right {
// ct AND true = ct
ct_left.clone()
} else {
// ct AND false = false
self.trivial_encrypt(false)
}
}
fn nand(
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
) -> Ciphertext {
if ct_right {
// NOT (ct AND true) = NOT(ct)
self.not(ct_left)
} else {
// NOT (ct AND false) = NOT(false) = true
self.trivial_encrypt(true)
}
}
fn nor(
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
) -> Ciphertext {
if ct_right {
// NOT (ct OR true) = NOT(true) = false
self.trivial_encrypt(false)
} else {
// NOT (ct OR false) = NOT(ct)
self.not(ct_left)
}
}
fn or(
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
) -> Ciphertext {
if ct_right {
// ct OR true = true
self.trivial_encrypt(true)
} else {
// ct OR false = ct
ct_left.clone()
}
}
fn xor(
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
) -> Ciphertext {
if ct_right {
// ct XOR true = NOT(ct)
self.not(ct_left)
} else {
// ct XOR false = ct
ct_left.clone()
}
}
fn xnor(
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
) -> Ciphertext {
if ct_right {
// NOT(ct XOR true) = NOT(NOT(ct)) = ct
ct_left.clone()
} else {
// NOT(ct XOR false) = NOT(ct)
self.not(ct_left)
}
}
}
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 {
self.and(ct_right, ct_left, server_key)
}
fn nand(
&mut self,
ct_left: bool,
ct_right: &Ciphertext,
server_key: &B::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 {
self.nor(ct_right, ct_left, server_key)
}
fn or(
&mut self,
ct_left: bool,
ct_right: &Ciphertext,
server_key: &B::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 {
self.xor(ct_right, ct_left, server_key)
}
fn xnor(
&mut self,
ct_left: bool,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
self.xnor(ct_right, ct_left, server_key)
}
}

136
tfhe/src/boolean/mod.rs Normal file
View File

@@ -0,0 +1,136 @@
#![deny(rustdoc::broken_intra_doc_links)]
//! Welcome the the tfhe.rs `boolean` module documentation!
//!
//! # Description
//! This library makes it possible to execute boolean gates over encrypted bits.
//! It allows to execute a boolean circuit on an untrusted server because both circuit inputs and
//! outputs are kept private.
//! Data are indeed encrypted on the client side, before being sent to the server.
//! On the server side every computation is performed on ciphertexts.
//! The server however has to know the boolean circuit to be evaluated.
//! At the end of the computation, the server returns the encryption of the result to the user.
//!
//!
//!
//! # Quick Example
//!
//! The following piece of code shows how to generate keys and run a small Boolean circuit
//! homomorphically.
//!
//! ```rust
//! # #[cfg(not(feature = "cuda"))]
//! # fn main() {
//!
//! use tfhe::boolean::gen_keys;
//! use tfhe::boolean::prelude::*;
//!
//! // We generate a set of client/server keys, using the default parameters:
//! let (mut client_key, mut server_key) = gen_keys();
//!
//! // We use the client secret key to encrypt two messages:
//! let ct_1 = client_key.encrypt(true);
//! let ct_2 = client_key.encrypt(false);
//!
//! // We use the server public key to execute a boolean circuit:
//! // if ((NOT ct_2) NAND (ct_1 AND ct_2)) then (NOT ct_2) else (ct_1 AND ct_2)
//! let ct_3 = server_key.not(&ct_2);
//! let ct_4 = server_key.and(&ct_1, &ct_2);
//! let ct_5 = server_key.nand(&ct_3, &ct_4);
//! let ct_6 = server_key.mux(&ct_5, &ct_3, &ct_4);
//!
//! // We use the client key to decrypt the output of the circuit:
//! let output_1 = client_key.decrypt(&ct_6);
//! assert_eq!(output_1, true);
//!
//! // It is possible to compute gates with one input unencrypted
//! let ct_7 = server_key.and(&ct_6, true);
//! let output_2 = client_key.decrypt(&ct_7);
//! assert_eq!(output_2, true);
//!
//! // It is possible to trivially encrypt on the server side
//! // i.e. to not encrypt but still generate a compatible Ciphertext
//! let ct_8 = server_key.trivial_encrypt(false);
//! let ct_9 = server_key.mux(&ct_7, &ct_3, &ct_8);
//! let output_3 = client_key.decrypt(&ct_9);
//! assert_eq!(output_3, true);
//! # }
//!
//! # #[cfg(feature = "cuda")]
//! # fn main() {}
//! ```
use crate::boolean::client_key::ClientKey;
use crate::boolean::parameters::DEFAULT_PARAMETERS;
use crate::boolean::public_key::PublicKey;
use crate::boolean::server_key::ServerKey;
#[cfg(test)]
use rand::Rng;
pub mod ciphertext;
pub mod client_key;
pub mod engine;
pub mod parameters;
pub mod prelude;
pub mod public_key;
pub mod server_key;
/// The scaling factor used for the plaintext
pub(crate) const PLAINTEXT_LOG_SCALING_FACTOR: usize = 3;
/// The plaintext associated with true: 1/8
pub(crate) const PLAINTEXT_TRUE: u32 = 1 << (32 - PLAINTEXT_LOG_SCALING_FACTOR);
/// The plaintext associated with false: -1/8
pub(crate) const PLAINTEXT_FALSE: u32 = 7 << (32 - PLAINTEXT_LOG_SCALING_FACTOR);
/// tool to generate random booleans
#[cfg(test)]
pub(crate) fn random_boolean() -> bool {
// create a random generator
let mut rng = rand::thread_rng();
// generate a random bit
let n: u32 = (rng.gen::<u32>()) % 2;
// convert it to boolean and return
n != 0
}
/// tool to generate random integers
#[cfg(test)]
pub(crate) fn random_integer() -> u32 {
// create a random generator
let mut rng = rand::thread_rng();
// generate a random u32
rng.gen::<u32>()
}
/// Generate a couple of client and server keys with the default cryptographic parameters:
/// `DEFAULT_PARAMETERS`.
/// The client is the one generating both keys.
/// * the client key is used to encrypt and decrypt and has to be kept secret;
/// * the server key is used to perform homomorphic operations on the server side and it is
/// 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
let cks = ClientKey::new(&DEFAULT_PARAMETERS);
// generate the server key
let sks = ServerKey::new(&cks);
// return
(cks, sks)
}

View File

@@ -0,0 +1,124 @@
//! The cryptographic parameter set.
//!
//! This module provides the structure containing the cryptographic parameters required for the
//! homomorphic evaluation of Boolean circuit as well as a list of secure cryptographic parameter
//! sets.
//!
//! Two parameter sets are provided:
//! * `tfhe::boolean::parameters::DEFAULT_PARAMETERS`
//! * `tfhe::boolean::parameters::TFHE_LIB_PARAMETERS`
//!
//! They ensure the correctness of the Boolean circuit evaluation result (up to a certain
//! probability) along with 128-bits of security.
//!
//! The two parameter sets offer a trade-off in terms of execution time versus error probability.
//! The `DEFAULT_PARAMETERS` set offers better performances on homomorphic circuit evaluation
//! with an higher probability error in comparison with the `TFHE_LIB_PARAMETERS`.
//! Note that if you desire, you can also create your own set of parameters.
//! 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::{
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
StandardDev,
};
use serde::{Deserialize, Serialize};
/// A set of cryptographic parameters for homomorphic Boolean circuit evaluation.
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct BooleanParameters {
pub lwe_dimension: LweDimension,
pub glwe_dimension: GlweDimension,
pub polynomial_size: PolynomialSize,
pub lwe_modular_std_dev: StandardDev,
pub glwe_modular_std_dev: StandardDev,
pub pbs_base_log: DecompositionBaseLog,
pub pbs_level: DecompositionLevelCount,
pub ks_base_log: DecompositionBaseLog,
pub ks_level: DecompositionLevelCount,
}
impl BooleanParameters {
/// Constructs a new set of parameters for boolean circuit evaluation.
///
/// # Safety
///
/// This function is unsafe, as failing to fix the parameters properly would yield incorrect
/// and insecure computation. Unless you are a cryptographer who really knows the impact of each
/// of those parameters, you __must__ stick with the provided parameters [`DEFAULT_PARAMETERS`]
/// and [`TFHE_LIB_PARAMETERS`], which both offer correct results with 128 bits of security.
#[allow(clippy::too_many_arguments)]
pub unsafe fn new(
lwe_dimension: LweDimension,
glwe_dimension: GlweDimension,
polynomial_size: PolynomialSize,
lwe_modular_std_dev: StandardDev,
glwe_modular_std_dev: StandardDev,
pbs_base_log: DecompositionBaseLog,
pbs_level: DecompositionLevelCount,
ks_base_log: DecompositionBaseLog,
ks_level: DecompositionLevelCount,
) -> BooleanParameters {
BooleanParameters {
lwe_dimension,
glwe_dimension,
polynomial_size,
lwe_modular_std_dev,
glwe_modular_std_dev,
pbs_base_log,
pbs_level,
ks_level,
ks_base_log,
}
}
}
/// Default parameter set.
///
/// This parameter set ensures 128-bits of security, and a probability of error is upper-bounded by
/// $2^{-40}$. The secret keys generated with this parameter set are uniform binary.
/// This parameter set allows to evaluate faster Boolean circuits than the `TFHE_LIB_PARAMETERS`
/// one.
pub const DEFAULT_PARAMETERS: BooleanParameters = BooleanParameters {
lwe_dimension: LweDimension(777),
glwe_dimension: GlweDimension(3),
polynomial_size: PolynomialSize(512),
lwe_modular_std_dev: StandardDev(0.000003725679281679651),
glwe_modular_std_dev: StandardDev(0.0000000000034525330484572114),
pbs_base_log: DecompositionBaseLog(18),
pbs_level: DecompositionLevelCount(1),
ks_base_log: DecompositionBaseLog(4),
ks_level: DecompositionLevelCount(3),
};
/// The secret keys generated with this parameter set are uniform binary.
/// This parameter set ensures a probability of error upper-bounded by $2^{-165}$ as the ones
/// proposed into [TFHE library](https://tfhe.github.io/tfhe/) for for 128-bits of security.
/// They are updated to the last security standards, so they differ from the original
/// publication.
pub const TFHE_LIB_PARAMETERS: BooleanParameters = BooleanParameters {
lwe_dimension: LweDimension(830),
glwe_dimension: GlweDimension(2),
polynomial_size: PolynomialSize(1024),
lwe_modular_std_dev: StandardDev(0.000001412290588219445),
glwe_modular_std_dev: StandardDev(0.00000000000000029403601535432533),
pbs_base_log: DecompositionBaseLog(23),
pbs_level: DecompositionLevelCount(1),
ks_base_log: DecompositionBaseLog(5),
ks_level: DecompositionLevelCount(3),
};
/// This parameter set ensures a probability of error upper-bounded by $2^{-40}$ and 128-bit
/// security.
/// They are GPU-compliant.
pub const GPU_DEFAULT_PARAMETERS: BooleanParameters = BooleanParameters {
lwe_dimension: LweDimension(686),
glwe_dimension: GlweDimension(1),
polynomial_size: PolynomialSize(1024),
lwe_modular_std_dev: StandardDev(0.000019703241702943194),
glwe_modular_std_dev: StandardDev(0.00000004053919869756513),
pbs_base_log: DecompositionBaseLog(6),
pbs_level: DecompositionLevelCount(3),
ks_base_log: DecompositionBaseLog(2),
ks_level: DecompositionLevelCount(6),
};

View File

@@ -0,0 +1,7 @@
#![doc(hidden)]
pub use super::ciphertext::Ciphertext;
pub use super::client_key::ClientKey;
pub use super::gen_keys;
pub use super::parameters::*;
pub use super::public_key::PublicKey;
pub use super::server_key::{BinaryBooleanGates, ServerKey};

View File

@@ -0,0 +1,116 @@
use crate::boolean::ciphertext::Ciphertext;
use crate::boolean::client_key::ClientKey;
use crate::boolean::engine::{CpuBooleanEngine, WithThreadLocalEngine};
use crate::boolean::parameters::BooleanParameters;
use crate::core_crypto::prelude::*;
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) parameters: BooleanParameters,
}
impl PublicKey {
/// Encrypts a Boolean message using the client key.
///
/// # Example
///
/// ```rust
/// # #[cfg(not(feature = "cuda"))]
/// # fn main() {
/// use tfhe::boolean::prelude::*;
///
/// // Generate the client key and the server key:
/// let (cks, sks) = gen_keys();
///
/// let pks = PublicKey::new(&cks);
///
/// // Encryption of one message:
/// let ct1 = pks.encrypt(true);
/// let ct2 = pks.encrypt(false);
/// let ct_res = sks.and(&ct1, &ct2);
///
/// // Decryption:
/// 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)
})
}
/// 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).
///
/// # Example
///
/// ```rust
/// # #[cfg(not(feature = "cuda"))]
/// # fn main() {
/// use tfhe::boolean::prelude::*;
///
/// // Generate the client key and the server key:
/// let (cks, mut sks) = gen_keys();
///
/// 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))
}
}
#[derive(Serialize, Deserialize)]
struct SerializablePublicKey {
lwe_public_key: Vec<u8>,
parameters: BooleanParameters,
}
impl Serialize for PublicKey {
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 lwe_public_key = ser_eng
.serialize(&self.lwe_public_key)
.map_err(serde::ser::Error::custom)?;
SerializablePublicKey {
lwe_public_key,
parameters: self.parameters,
}
.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for PublicKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
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())
.map_err(serde::de::Error::custom)?,
parameters: thing.parameters,
})
}
}

View File

@@ -0,0 +1,264 @@
//! The public key for homomorphic computation.
//!
//! This module implements the generation of the server's public key, together with all the
//! available homomorphic Boolean gates ($\mathrm{AND}$, $\mathrm{MUX}$, $\mathrm{NAND}$,
//! $\mathrm{NOR}$,
//! $\mathrm{NOT}$, $\mathrm{OR}$, $\mathrm{XNOR}$, $\mathrm{XOR}$).
#[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};
use crate::boolean::engine::{
BinaryGatesAssignEngine, BinaryGatesEngine, CpuBooleanEngine, WithThreadLocalEngine,
};
#[cfg(feature = "cuda")]
use std::sync::Arc;
pub trait BinaryBooleanGates<L, R> {
fn and(&self, ct_left: L, ct_right: R) -> Ciphertext;
fn nand(&self, ct_left: L, ct_right: R) -> Ciphertext;
fn nor(&self, ct_left: L, ct_right: R) -> Ciphertext;
fn or(&self, ct_left: L, ct_right: R) -> Ciphertext;
fn xor(&self, ct_left: L, ct_right: R) -> Ciphertext;
fn xnor(&self, ct_left: L, ct_right: R) -> Ciphertext;
}
pub trait BinaryBooleanGatesAssign<L, R> {
fn and_assign(&self, ct_left: L, ct_right: R);
fn nand_assign(&self, ct_left: L, ct_right: R);
fn nor_assign(&self, ct_left: L, ct_right: R);
fn or_assign(&self, ct_left: L, ct_right: R);
fn xor_assign(&self, ct_left: L, ct_right: 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;
}
}
impl<Lhs, Rhs> BinaryBooleanGates<Lhs, Rhs> for ServerKey
where
<ServerKey as DefaultImplementation>::Engine:
BinaryGatesEngine<Lhs, Rhs, <ServerKey as DefaultImplementation>::BootsrapKey>,
{
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)
})
}
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)
})
}
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)
})
}
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)
})
}
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)
})
}
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)
})
}
}
impl<Lhs, Rhs> BinaryBooleanGatesAssign<Lhs, Rhs> for ServerKey
where
<ServerKey as DefaultImplementation>::Engine:
BinaryGatesAssignEngine<Lhs, Rhs, <ServerKey as DefaultImplementation>::BootsrapKey>,
{
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)
})
}
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)
})
}
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)
})
}
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)
})
}
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)
})
}
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)
})
}
}
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)
}
pub fn trivial_encrypt(&self, message: bool) -> Ciphertext {
Ciphertext::Trivial(message)
}
pub fn not(&self, ct: &Ciphertext) -> Ciphertext {
CpuBooleanEngine::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))
}
pub fn mux(
&self,
ct_condition: &Ciphertext,
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))
}
}

View File

@@ -0,0 +1,913 @@
use crate::boolean::ciphertext::Ciphertext;
use crate::boolean::client_key::ClientKey;
use crate::boolean::parameters::BooleanParameters;
use crate::boolean::server_key::{BinaryBooleanGates, BinaryBooleanGatesAssign, ServerKey};
use crate::boolean::{random_boolean, random_integer};
/// Number of assert in randomized tests
const NB_TEST: usize = 128;
/// Number of ciphertext in the deep circuit test
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;
#[test]
fn test_encrypt_decrypt_lwe_secret_key_default_parameters() {
test_encrypt_decrypt_lwe_secret_key(DEFAULT_PARAMETERS);
}
#[test]
fn test_and_gate_default_parameters() {
test_and_gate(DEFAULT_PARAMETERS);
}
#[test]
fn test_nand_gate_default_parameters() {
test_nand_gate(DEFAULT_PARAMETERS);
}
#[test]
fn test_or_gate_default_parameters() {
test_or_gate(DEFAULT_PARAMETERS);
}
#[test]
fn test_nor_gate_default_parameters() {
test_nor_gate(DEFAULT_PARAMETERS);
}
#[test]
fn test_xor_gate_default_parameters() {
test_xor_gate(DEFAULT_PARAMETERS);
}
#[test]
fn test_xnor_gate_default_parameters() {
test_xnor_gate(DEFAULT_PARAMETERS);
}
#[test]
fn test_not_gate_default_parameters() {
test_not_gate(DEFAULT_PARAMETERS);
}
#[test]
fn test_mux_gate_default_parameters() {
test_mux_gate(DEFAULT_PARAMETERS);
}
#[test]
fn test_deep_circuit_default_parameters() {
test_deep_circuit(DEFAULT_PARAMETERS);
}
}
#[cfg(not(feature = "cuda"))]
mod tfhe_lib_parameters_tests {
use super::*;
use crate::boolean::parameters::TFHE_LIB_PARAMETERS;
#[test]
fn test_encrypt_decrypt_lwe_secret_key_tfhe_lib_parameters() {
test_encrypt_decrypt_lwe_secret_key(TFHE_LIB_PARAMETERS);
}
#[test]
fn test_and_gate_tfhe_lib_parameters() {
test_and_gate(TFHE_LIB_PARAMETERS);
}
#[test]
fn test_nand_gate_tfhe_lib_parameters() {
test_nand_gate(TFHE_LIB_PARAMETERS);
}
#[test]
fn test_or_gate_tfhe_lib_parameters() {
test_or_gate(TFHE_LIB_PARAMETERS);
}
#[test]
fn test_nor_gate_tfhe_lib_parameters() {
test_nor_gate(TFHE_LIB_PARAMETERS);
}
#[test]
fn test_xor_gate_tfhe_lib_parameters() {
test_xor_gate(TFHE_LIB_PARAMETERS);
}
#[test]
fn test_xnor_gate_tfhe_lib_parameters() {
test_xnor_gate(TFHE_LIB_PARAMETERS);
}
#[test]
fn test_not_gate_tfhe_lib_parameters() {
test_not_gate(TFHE_LIB_PARAMETERS);
}
#[test]
fn test_mux_gate_tfhe_lib_parameters() {
test_mux_gate(TFHE_LIB_PARAMETERS);
}
#[test]
fn test_deep_circuit_tfhe_lib_parameters() {
test_deep_circuit(TFHE_LIB_PARAMETERS);
}
}
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
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// encryption of false
let ct_false = cks.encrypt(false);
// encryption of true
let ct_true = cks.encrypt(true);
// decryption of false
let dec_false = cks.decrypt(&ct_false);
// decryption of true
let dec_true = cks.decrypt(&ct_true);
// assert
assert!(!dec_false);
assert!(dec_true);
// encryption of false
let ct_false = sks.trivial_encrypt(false);
// encryption of true
let ct_true = sks.trivial_encrypt(true);
// decryption of false
let dec_false = cks.decrypt(&ct_false);
// decryption of true
let dec_true = cks.decrypt(&ct_true);
// assert
assert!(!dec_false);
assert!(dec_true);
}
}
/// This function randomly either computes a regular encryption of the message or a trivial
/// encryption of the message
fn random_enum_encryption(cks: &ClientKey, sks: &ServerKey, message: bool) -> Ciphertext {
if random_boolean() {
cks.encrypt(message)
} else {
sks.trivial_encrypt(message)
}
}
fn test_and_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of two random booleans
let b1 = random_boolean();
let b2 = random_boolean();
let expected_result = b1 && b2;
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
// AND gate -> "left: {:?}, right: {:?}",ct1, ct2
let ct_res = sks.and(&ct1, &ct2);
// decryption
let dec_and = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_and,
"left: {:?}, right: {:?}",
ct1, ct2
);
// AND gate -> left: Ciphertext, right: bool
let ct_res = sks.and(&ct1, b2);
// decryption
let dec_and = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_and, "left: {:?}, right: {:?}", ct1, b2);
// AND gate -> left: bool, right: Ciphertext
let ct_res = sks.and(b1, &ct2);
// decryption
let dec_and = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_and, "left: {:?}, right: {:?}", b1, ct2);
// AND gate -> "left: {:?}, right: {:?}",ct1, ct2
let mut ct_res = ct1.clone();
sks.and_assign(&mut ct_res, &ct2);
// decryption
let dec_and = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_and,
"left: {:?}, right: {:?}",
ct1, ct2
);
// AND gate -> left: Ciphertext, right: bool
let mut ct_res = ct1.clone();
sks.and_assign(&mut ct_res, b2);
// decryption
let dec_and = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_and, "left: {:?}, right: {:?}", ct1, b2);
// AND gate -> left: bool, right: Ciphertext
let mut ct_res = ct2.clone();
sks.and_assign(b1, &mut ct_res);
// decryption
let dec_and = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_and, "left: {:?}, right: {:?}", b1, ct2);
}
}
fn test_mux_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of three random booleans
let b1 = random_boolean();
let b2 = random_boolean();
let b3 = random_boolean();
let expected_result = if b1 { b2 } else { b3 };
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
// encryption of b3
let ct3 = random_enum_encryption(&cks, &sks, b3);
// MUX gate
let ct_res = sks.mux(&ct1, &ct2, &ct3);
// decryption
let dec_mux = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_mux,
"cond: {:?}, then: {:?}, else: {:?}",
ct1, ct2, ct3
);
}
}
fn test_nand_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of two random booleans
let b1 = random_boolean();
let b2 = random_boolean();
let expected_result = !(b1 && b2);
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
// NAND gate -> left: Ciphertext, right: Ciphertext
let ct_res = sks.nand(&ct1, &ct2);
// decryption
let dec_nand = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_nand,
"left: {:?}, right: {:?}",
ct1, ct2
);
// NAND gate -> left: Ciphertext, right: bool
let ct_res = sks.nand(&ct1, b2);
// decryption
let dec_nand = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_nand,
"left: {:?}, right: {:?}",
ct1, b2
);
// NAND gate -> left: bool, right: Ciphertext
let ct_res = sks.nand(b1, &ct2);
// decryption
let dec_nand = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_nand,
"left: {:?}, right: {:?}",
b1, ct2
);
// NAND gate -> "left: {:?}, right: {:?}",ct1, ct2
let mut ct_res = ct1.clone();
sks.nand_assign(&mut ct_res, &ct2);
// decryption
let dec_nand = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_nand,
"left: {:?}, right: {:?}",
ct1, ct2
);
// NAND gate -> left: Ciphertext, right: bool
let mut ct_res = ct1.clone();
sks.nand_assign(&mut ct_res, b2);
// decryption
let dec_nand = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_nand,
"left: {:?}, right: {:?}",
ct1, b2
);
// NAND gate -> left: bool, right: Ciphertext
let mut ct_res = ct2.clone();
sks.nand_assign(b1, &mut ct_res);
// decryption
let dec_nand = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_nand,
"left: {:?}, right: {:?}",
b1, ct2
);
}
}
fn test_nor_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of two random booleans
let b1 = random_boolean();
let b2 = random_boolean();
let expected_result = !(b1 || b2);
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
// NOR gate -> left: Ciphertext, right: Ciphertext
let ct_res = sks.nor(&ct1, &ct2);
// decryption
let dec_nor = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_nor,
"left: {:?}, right: {:?}",
ct1, ct2
);
// NOR gate -> left: Ciphertext, right: bool
let ct_res = sks.nor(&ct1, b2);
// decryption
let dec_nor = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_nor, "left: {:?}, right: {:?}", ct1, b2);
// NOR gate -> left: bool, right: Ciphertext
let ct_res = sks.nor(b1, &ct2);
// decryption
let dec_nor = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_nor, "left: {:?}, right: {:?}", b1, ct2);
// NOR gate -> "left: {:?}, right: {:?}",ct1, ct2
let mut ct_res = ct1.clone();
sks.nor_assign(&mut ct_res, &ct2);
// decryption
let dec_nor = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_nor,
"left: {:?}, right: {:?}",
ct1, ct2
);
// NOR gate -> left: Ciphertext, right: bool
let mut ct_res = ct1.clone();
sks.nor_assign(&mut ct_res, b2);
// decryption
let dec_nor = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_nor, "left: {:?}, right: {:?}", ct1, b2);
// NOR gate -> left: bool, right: Ciphertext
let mut ct_res = ct2.clone();
sks.nor_assign(b1, &mut ct_res);
// decryption
let dec_nor = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_nor, "left: {:?}, right: {:?}", b1, ct2);
}
}
fn test_not_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of one random booleans
let b1 = random_boolean();
let expected_result = !b1;
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// NOT gate
let ct_res = sks.not(&ct1);
// decryption
let dec_not = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_not);
// NOT gate
let mut ct_res = ct1.clone();
sks.not_assign(&mut ct_res);
// decryption
let dec_not = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_not);
}
}
fn test_or_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of two random booleans
let b1 = random_boolean();
let b2 = random_boolean();
let expected_result = b1 || b2;
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
// OR gate -> left: Ciphertext, right: Ciphertext
let ct_res = sks.or(&ct1, &ct2);
// decryption
let dec_or = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_or, "left: {:?}, right: {:?}", ct1, ct2);
// OR gate -> left: Ciphertext, right: bool
let ct_res = sks.or(&ct1, b2);
// decryption
let dec_or = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_or, "left: {:?}, right: {:?}", ct1, b2);
// OR gate -> left: bool, right: Ciphertext
let ct_res = sks.or(b1, &ct2);
// decryption
let dec_or = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_or, "left: {:?}, right: {:?}", b1, ct2);
// OR gate -> "left: {:?}, right: {:?}",ct1, ct2
let mut ct_res = ct1.clone();
sks.or_assign(&mut ct_res, &ct2);
// decryption
let dec_or = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_or, "left: {:?}, right: {:?}", ct1, ct2);
// OR gate -> left: Ciphertext, right: bool
let mut ct_res = ct1.clone();
sks.or_assign(&mut ct_res, b2);
// decryption
let dec_or = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_or, "left: {:?}, right: {:?}", ct1, b2);
// OR gate -> left: bool, right: Ciphertext
let mut ct_res = ct2.clone();
sks.or_assign(b1, &mut ct_res);
// decryption
let dec_or = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_or, "left: {:?}, right: {:?}", b1, ct2);
}
}
fn test_xnor_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of two random booleans
let b1 = random_boolean();
let b2 = random_boolean();
let expected_result = b1 == b2;
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
// XNOR gate -> left: Ciphertext, right: Ciphertext
let ct_res = sks.xnor(&ct1, &ct2);
// decryption
let dec_xnor = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_xnor,
"left: {:?}, right: {:?}",
ct1, ct2
);
// XNOR gate -> left: Ciphertext, right: bool
let ct_res = sks.xnor(&ct1, b2);
// decryption
let dec_xnor = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_xnor,
"left: {:?}, right: {:?}",
ct1, b2
);
// XNOR gate -> left: bool, right: Ciphertext
let ct_res = sks.xnor(b1, &ct2);
// decryption
let dec_xnor = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_xnor,
"left: {:?}, right: {:?}",
b1, ct2
);
// XNOR gate -> "left: {:?}, right: {:?}",ct1, ct2
let mut ct_res = ct1.clone();
sks.xnor_assign(&mut ct_res, &ct2);
// decryption
let dec_xnor = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_xnor,
"left: {:?}, right: {:?}",
ct1, ct2
);
// XNOR gate -> left: Ciphertext, right: bool
let mut ct_res = ct1.clone();
sks.xnor_assign(&mut ct_res, b2);
// decryption
let dec_xnor = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_xnor,
"left: {:?}, right: {:?}",
ct1, b2
);
// XNOR gate -> left: bool, right: Ciphertext
let mut ct_res = ct2.clone();
sks.xnor_assign(b1, &mut ct_res);
// decryption
let dec_xnor = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_xnor,
"left: {:?}, right: {:?}",
b1, ct2
);
}
}
fn test_xor_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of two random booleans
let b1 = random_boolean();
let b2 = random_boolean();
let expected_result = b1 ^ b2;
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
// XOR gate -> left: Ciphertext, right: Ciphertext
let ct_res = sks.xor(&ct1, &ct2);
// decryption
let dec_xor = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_xor,
"left: {:?}, right: {:?}",
ct1, ct2
);
// XOR gate -> left: Ciphertext, right: bool
let ct_res = sks.xor(&ct1, b2);
// decryption
let dec_xor = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_xor, "left: {:?}, right: {:?}", ct1, b2);
// XOR gate -> left: bool, right: Ciphertext
let ct_res = sks.xor(b1, &ct2);
// decryption
let dec_xor = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_xor, "left: {:?}, right: {:?}", b1, ct2);
// XOR gate -> "left: {:?}, right: {:?}",ct1, ct2
let mut ct_res = ct1.clone();
sks.xor_assign(&mut ct_res, &ct2);
// decryption
let dec_xor = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_xor,
"left: {:?}, right: {:?}",
ct1, ct2
);
// XOR gate -> left: Ciphertext, right: bool
let mut ct_res = ct1.clone();
sks.xor_assign(&mut ct_res, b2);
// decryption
let dec_xor = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_xor, "left: {:?}, right: {:?}", ct1, b2);
// XOR gate -> left: bool, right: Ciphertext
let mut ct_res = ct2.clone();
sks.xor_assign(b1, &mut ct_res);
// decryption
let dec_xor = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_xor, "left: {:?}, right: {:?}", b1, ct2);
}
}
/// generate a random index for the table in the long run tests
fn random_index() -> usize {
(random_integer() % (NB_CT as u32)) as usize
}
/// randomly select a gate, randomly select inputs and the output,
/// compute the selected gate with the selected inputs
/// and write in the selected output
fn random_gate_all(ct_tab: &mut [Ciphertext], bool_tab: &mut [bool], sks: &ServerKey) {
// select a random gate in the array [NOT,CMUX,AND,NAND,NOR,OR,XOR,XNOR]
let gate_id = random_integer() % 8;
let index_1: usize = random_index();
let index_2: usize = random_index();
if gate_id == 0 {
// NOT gate
bool_tab[index_2] = !bool_tab[index_1];
ct_tab[index_2] = sks.not(&ct_tab[index_1]);
} else if gate_id == 1 {
// MUX gate
let index_3: usize = random_index();
let index_4: usize = random_index();
bool_tab[index_4] = if bool_tab[index_1] {
bool_tab[index_2]
} else {
bool_tab[index_3]
};
ct_tab[index_4] = sks.mux(&ct_tab[index_1], &ct_tab[index_2], &ct_tab[index_3]);
} else {
// 2-input gate
let index_3: usize = random_index();
if gate_id == 2 {
// AND gate
bool_tab[index_3] = bool_tab[index_1] && bool_tab[index_2];
ct_tab[index_3] = sks.and(&ct_tab[index_1], &ct_tab[index_2]);
} else if gate_id == 3 {
// NAND gate
bool_tab[index_3] = !(bool_tab[index_1] && bool_tab[index_2]);
ct_tab[index_3] = sks.nand(&ct_tab[index_1], &ct_tab[index_2]);
} else if gate_id == 4 {
// NOR gate
bool_tab[index_3] = !(bool_tab[index_1] || bool_tab[index_2]);
ct_tab[index_3] = sks.nor(&ct_tab[index_1], &ct_tab[index_2]);
} else if gate_id == 5 {
// OR gate
bool_tab[index_3] = bool_tab[index_1] || bool_tab[index_2];
ct_tab[index_3] = sks.or(&ct_tab[index_1], &ct_tab[index_2]);
} else if gate_id == 6 {
// XOR gate
bool_tab[index_3] = bool_tab[index_1] ^ bool_tab[index_2];
ct_tab[index_3] = sks.xor(&ct_tab[index_1], &ct_tab[index_2]);
} else {
// XNOR gate
bool_tab[index_3] = !(bool_tab[index_1] ^ bool_tab[index_2]);
ct_tab[index_3] = sks.xnor(&ct_tab[index_1], &ct_tab[index_2]);
}
}
}
fn test_deep_circuit(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
// create an array of ciphertexts
let mut ct_tab: Vec<Ciphertext> = vec![cks.encrypt(true); NB_CT];
// create an array of booleans
let mut bool_tab: Vec<bool> = vec![true; NB_CT];
// randomly fill both arrays
for (ct, boolean) in ct_tab.iter_mut().zip(bool_tab.iter_mut()) {
*boolean = random_boolean();
*ct = cks.encrypt(*boolean);
}
// compute NB_GATE gates
for _ in 0..NB_GATE {
random_gate_all(&mut ct_tab, &mut bool_tab, &sks);
}
// decrypt and assert equality
for (ct, boolean) in ct_tab.iter().zip(bool_tab.iter()) {
let dec = cks.decrypt(ct);
assert_eq!(*boolean, dec);
}
}

View File

@@ -0,0 +1,44 @@
use crate::c_api::buffer::*;
use crate::c_api::utils::*;
use std::os::raw::c_int;
use crate::boolean;
pub struct BooleanCiphertext(pub(in crate::c_api) boolean::ciphertext::Ciphertext);
#[no_mangle]
pub unsafe extern "C" fn boolean_serialize_ciphertext(
ciphertext: *const BooleanCiphertext,
result: *mut Buffer,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let ciphertext = get_ref_checked(ciphertext).unwrap();
let buffer: Buffer = bincode::serialize(&ciphertext.0).unwrap().into();
*result = buffer;
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_deserialize_ciphertext(
buffer_view: BufferView,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let ciphertext: boolean::ciphertext::Ciphertext =
bincode::deserialize(buffer_view.into()).unwrap();
let heap_allocated_ciphertext = Box::new(BooleanCiphertext(ciphertext));
*result = Box::into_raw(heap_allocated_ciphertext);
})
}

View File

@@ -0,0 +1,106 @@
use crate::c_api::buffer::*;
use crate::c_api::utils::*;
use bincode;
use std::os::raw::c_int;
use crate::boolean;
use super::BooleanCiphertext;
pub struct BooleanClientKey(pub(in crate::c_api) boolean::client_key::ClientKey);
#[no_mangle]
pub unsafe extern "C" fn boolean_gen_client_key(
boolean_parameters: *const super::parameters::BooleanParameters,
result_client_key: *mut *mut BooleanClientKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result_client_key).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result_client_key = std::ptr::null_mut();
let boolean_parameters = get_ref_checked(boolean_parameters).unwrap();
let client_key = boolean::client_key::ClientKey::new(&boolean_parameters.0);
let heap_allocated_client_key = Box::new(BooleanClientKey(client_key));
*result_client_key = Box::into_raw(heap_allocated_client_key);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_client_key_encrypt(
client_key: *const BooleanClientKey,
value_to_encrypt: bool,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let client_key = get_ref_checked(client_key).unwrap();
let heap_allocated_ciphertext =
Box::new(BooleanCiphertext(client_key.0.encrypt(value_to_encrypt)));
*result = Box::into_raw(heap_allocated_ciphertext);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_client_key_decrypt(
client_key: *const BooleanClientKey,
ciphertext_to_decrypt: *const BooleanCiphertext,
result: *mut bool,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let client_key = get_ref_checked(client_key).unwrap();
let ciphertext_to_decrypt = get_ref_checked(ciphertext_to_decrypt).unwrap();
*result = client_key.0.decrypt(&ciphertext_to_decrypt.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_serialize_client_key(
client_key: *const BooleanClientKey,
result: *mut Buffer,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let client_key = get_ref_checked(client_key).unwrap();
let buffer: Buffer = bincode::serialize(&client_key.0).unwrap().into();
*result = buffer;
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_deserialize_client_key(
buffer_view: BufferView,
result: *mut *mut BooleanClientKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let client_key: boolean::client_key::ClientKey =
bincode::deserialize(buffer_view.into()).unwrap();
let heap_allocated_client_key = Box::new(BooleanClientKey(client_key));
*result = Box::into_raw(heap_allocated_client_key);
})
}

View File

@@ -0,0 +1,54 @@
use crate::c_api::utils::*;
use std::os::raw::c_int;
use super::parameters::BooleanParameters;
use super::{BooleanCiphertext, BooleanClientKey, BooleanPublicKey, BooleanServerKey};
#[no_mangle]
pub unsafe extern "C" fn destroy_boolean_client_key(client_key: *mut BooleanClientKey) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(client_key).unwrap();
drop(Box::from_raw(client_key));
})
}
#[no_mangle]
pub unsafe extern "C" fn destroy_boolean_server_key(server_key: *mut BooleanServerKey) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(server_key).unwrap();
drop(Box::from_raw(server_key));
})
}
#[no_mangle]
pub unsafe extern "C" fn destroy_boolean_public_key(public_key: *mut BooleanPublicKey) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(public_key).unwrap();
drop(Box::from_raw(public_key));
})
}
#[no_mangle]
pub unsafe extern "C" fn destroy_boolean_parameters(
boolean_parameters: *mut BooleanParameters,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(boolean_parameters).unwrap();
drop(Box::from_raw(boolean_parameters));
})
}
#[no_mangle]
pub unsafe extern "C" fn destroy_boolean_ciphertext(
boolean_ciphertext: *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(boolean_ciphertext).unwrap();
drop(Box::from_raw(boolean_ciphertext));
})
}

View File

@@ -0,0 +1,119 @@
pub mod ciphertext;
pub mod client_key;
pub mod destroy;
pub mod parameters;
pub mod public_key;
pub mod server_key;
use crate::c_api::utils::*;
use std::os::raw::c_int;
use crate::boolean;
pub use ciphertext::BooleanCiphertext;
pub use client_key::BooleanClientKey;
pub use public_key::BooleanPublicKey;
pub use server_key::BooleanServerKey;
#[no_mangle]
pub unsafe extern "C" fn boolean_gen_keys_with_default_parameters(
result_client_key: *mut *mut BooleanClientKey,
result_server_key: *mut *mut BooleanServerKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result_client_key).unwrap();
check_ptr_is_non_null_and_aligned(result_server_key).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result_client_key = std::ptr::null_mut();
*result_server_key = std::ptr::null_mut();
let (client_key, server_key) = boolean::gen_keys();
let heap_allocated_client_key = Box::new(BooleanClientKey(client_key));
let heap_allocated_server_key = Box::new(BooleanServerKey(server_key));
*result_client_key = Box::into_raw(heap_allocated_client_key);
*result_server_key = Box::into_raw(heap_allocated_server_key);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_gen_keys_with_parameters(
boolean_parameters: *const parameters::BooleanParameters,
result_client_key: *mut *mut BooleanClientKey,
result_server_key: *mut *mut BooleanServerKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result_client_key).unwrap();
check_ptr_is_non_null_and_aligned(result_server_key).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result_client_key = std::ptr::null_mut();
*result_server_key = std::ptr::null_mut();
let boolean_parameters = get_ref_checked(boolean_parameters).unwrap();
let client_key = boolean::client_key::ClientKey::new(&boolean_parameters.0);
let server_key = boolean::server_key::ServerKey::new(&client_key);
let heap_allocated_client_key = Box::new(BooleanClientKey(client_key));
let heap_allocated_server_key = Box::new(BooleanServerKey(server_key));
*result_client_key = Box::into_raw(heap_allocated_client_key);
*result_server_key = Box::into_raw(heap_allocated_server_key);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_gen_keys_with_predefined_parameters_set(
boolean_parameters_set: c_int,
result_client_key: *mut *mut BooleanClientKey,
result_server_key: *mut *mut BooleanServerKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result_client_key).unwrap();
check_ptr_is_non_null_and_aligned(result_server_key).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result_client_key = std::ptr::null_mut();
*result_server_key = std::ptr::null_mut();
let boolean_parameters_set_as_enum =
parameters::BooleanParametersSet::try_from(boolean_parameters_set).unwrap();
let boolean_parameters =
parameters::BooleanParameters::from(boolean_parameters_set_as_enum);
let client_key = boolean::client_key::ClientKey::new(&boolean_parameters.0);
let server_key = boolean::server_key::ServerKey::new(&client_key);
let heap_allocated_client_key = Box::new(BooleanClientKey(client_key));
let heap_allocated_server_key = Box::new(BooleanServerKey(server_key));
*result_client_key = Box::into_raw(heap_allocated_client_key);
*result_server_key = Box::into_raw(heap_allocated_server_key);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_trivial_encrypt(
message: bool,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
use boolean::engine::WithThreadLocalEngine;
check_ptr_is_non_null_and_aligned(result).unwrap();
let heap_allocated_result = Box::new(BooleanCiphertext(
boolean::engine::CpuBooleanEngine::with_thread_local_mut(|engine| {
engine.trivial_encrypt(message)
}),
));
*result = Box::into_raw(heap_allocated_result);
})
}

View File

@@ -0,0 +1,104 @@
use crate::c_api::utils::*;
use crate::core_crypto::prelude::{
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
StandardDev,
};
use std::os::raw::c_int;
use crate::boolean;
pub struct BooleanParameters(pub(in crate::c_api) boolean::parameters::BooleanParameters);
#[no_mangle]
pub unsafe extern "C" fn boolean_get_parameters(
boolean_parameters_set: c_int,
result: *mut *mut BooleanParameters,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let boolean_parameters_set_as_enum =
BooleanParametersSet::try_from(boolean_parameters_set).unwrap();
let boolean_parameters = Box::new(BooleanParameters::from(boolean_parameters_set_as_enum));
*result = Box::into_raw(boolean_parameters);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_create_parameters(
lwe_dimension: usize,
glwe_dimension: usize,
polynomial_size: usize,
lwe_modular_std_dev: f64,
glwe_modular_std_dev: f64,
pbs_base_log: usize,
pbs_level: usize,
ks_base_log: usize,
ks_level: usize,
result_parameters: *mut *mut BooleanParameters,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result_parameters).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result_parameters = std::ptr::null_mut();
let heap_allocated_parameters =
Box::new(BooleanParameters(boolean::parameters::BooleanParameters {
lwe_dimension: LweDimension(lwe_dimension),
glwe_dimension: GlweDimension(glwe_dimension),
polynomial_size: PolynomialSize(polynomial_size),
lwe_modular_std_dev: StandardDev(lwe_modular_std_dev),
glwe_modular_std_dev: StandardDev(glwe_modular_std_dev),
pbs_base_log: DecompositionBaseLog(pbs_base_log),
pbs_level: DecompositionLevelCount(pbs_level),
ks_base_log: DecompositionBaseLog(ks_base_log),
ks_level: DecompositionLevelCount(ks_level),
}));
*result_parameters = Box::into_raw(heap_allocated_parameters);
})
}
pub(in crate::c_api) enum BooleanParametersSet {
DefaultParameters,
TfheLibParameters,
}
pub const BOOLEAN_PARAMETERS_SET_DEFAULT_PARAMETERS: c_int = 0;
pub const BOOLEAN_PARAMETERS_SET_THFE_LIB_PARAMETERS: c_int = 1;
impl TryFrom<c_int> for BooleanParametersSet {
type Error = String;
fn try_from(value: c_int) -> Result<Self, Self::Error> {
match value {
BOOLEAN_PARAMETERS_SET_DEFAULT_PARAMETERS => {
Ok(BooleanParametersSet::DefaultParameters)
}
BOOLEAN_PARAMETERS_SET_THFE_LIB_PARAMETERS => {
Ok(BooleanParametersSet::TfheLibParameters)
}
_ => Err(format!(
"Invalid value '{value}' for BooleansParametersSet, use \
BOOLEAN_PARAMETERS_SET constants"
)),
}
}
}
impl From<BooleanParametersSet> for BooleanParameters {
fn from(boolean_parameters_set: BooleanParametersSet) -> Self {
match boolean_parameters_set {
BooleanParametersSet::DefaultParameters => {
BooleanParameters(boolean::parameters::DEFAULT_PARAMETERS)
}
BooleanParametersSet::TfheLibParameters => {
BooleanParameters(boolean::parameters::TFHE_LIB_PARAMETERS)
}
}
}
}

View File

@@ -0,0 +1,91 @@
use crate::c_api::buffer::*;
use crate::c_api::utils::*;
use bincode;
use std::os::raw::c_int;
use crate::boolean;
use super::{BooleanCiphertext, BooleanClientKey};
pub struct BooleanPublicKey(pub(in crate::c_api) boolean::public_key::PublicKey);
#[no_mangle]
pub unsafe extern "C" fn boolean_gen_public_key(
client_key: *const BooleanClientKey,
result: *mut *mut BooleanPublicKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let client_key = get_ref_checked(client_key).unwrap();
let heap_allocated_public_key = Box::new(BooleanPublicKey(
boolean::public_key::PublicKey::new(&client_key.0),
));
*result = Box::into_raw(heap_allocated_public_key);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_public_key_encrypt(
public_key: *const BooleanPublicKey,
value_to_encrypt: bool,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let public_key = get_ref_checked(public_key).unwrap();
let heap_allocated_ciphertext =
Box::new(BooleanCiphertext(public_key.0.encrypt(value_to_encrypt)));
*result = Box::into_raw(heap_allocated_ciphertext);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_serialize_public_key(
public_key: *const BooleanPublicKey,
result: *mut Buffer,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let public_key = get_ref_checked(public_key).unwrap();
let buffer: Buffer = bincode::serialize(&public_key.0).unwrap().into();
*result = buffer;
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_deserialize_public_key(
buffer_view: BufferView,
result: *mut *mut BooleanPublicKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let public_key: boolean::public_key::PublicKey =
bincode::deserialize(buffer_view.into()).unwrap();
let heap_allocated_public_key = Box::new(BooleanPublicKey(public_key));
*result = Box::into_raw(heap_allocated_public_key);
})
}

View File

@@ -0,0 +1,604 @@
use crate::c_api::buffer::*;
use crate::c_api::utils::*;
use std::os::raw::c_int;
use crate::boolean;
use crate::boolean::server_key::{BinaryBooleanGates, BinaryBooleanGatesAssign};
use super::BooleanCiphertext;
pub struct BooleanServerKey(pub(in crate::c_api) boolean::server_key::ServerKey);
#[no_mangle]
pub unsafe extern "C" fn boolean_gen_server_key(
client_key: *const super::BooleanClientKey,
result_server_key: *mut *mut BooleanServerKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result_server_key).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result_server_key = std::ptr::null_mut();
let client_key = get_ref_checked(client_key).unwrap();
let server_key = boolean::server_key::ServerKey::new(&client_key.0);
let heap_allocated_server_key = Box::new(BooleanServerKey(server_key));
*result_server_key = Box::into_raw(heap_allocated_server_key);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_and(
server_key: *const BooleanServerKey,
ct_left: *const BooleanCiphertext,
ct_right: *const BooleanCiphertext,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let ct_right = get_ref_checked(ct_right).unwrap();
let heap_allocated_result =
Box::new(BooleanCiphertext(server_key.0.and(&ct_left.0, &ct_right.0)));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_nand(
server_key: *const BooleanServerKey,
ct_left: *const BooleanCiphertext,
ct_right: *const BooleanCiphertext,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let ct_right = get_ref_checked(ct_right).unwrap();
let heap_allocated_result = Box::new(BooleanCiphertext(
server_key.0.nand(&ct_left.0, &ct_right.0),
));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_nor(
server_key: *const BooleanServerKey,
ct_left: *const BooleanCiphertext,
ct_right: *const BooleanCiphertext,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let ct_right = get_ref_checked(ct_right).unwrap();
let heap_allocated_result =
Box::new(BooleanCiphertext(server_key.0.nor(&ct_left.0, &ct_right.0)));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_or(
server_key: *const BooleanServerKey,
ct_left: *const BooleanCiphertext,
ct_right: *const BooleanCiphertext,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let ct_right = get_ref_checked(ct_right).unwrap();
let heap_allocated_result =
Box::new(BooleanCiphertext(server_key.0.or(&ct_left.0, &ct_right.0)));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_xor(
server_key: *const BooleanServerKey,
ct_left: *const BooleanCiphertext,
ct_right: *const BooleanCiphertext,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let ct_right = get_ref_checked(ct_right).unwrap();
let heap_allocated_result =
Box::new(BooleanCiphertext(server_key.0.xor(&ct_left.0, &ct_right.0)));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_xnor(
server_key: *const BooleanServerKey,
ct_left: *const BooleanCiphertext,
ct_right: *const BooleanCiphertext,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let ct_right = get_ref_checked(ct_right).unwrap();
let heap_allocated_result = Box::new(BooleanCiphertext(
server_key.0.xnor(&ct_left.0, &ct_right.0),
));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_not(
server_key: *const BooleanServerKey,
ct_input: *const BooleanCiphertext,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let ct_input = get_ref_checked(ct_input).unwrap();
let heap_allocated_result = Box::new(BooleanCiphertext(server_key.0.not(&ct_input.0)));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_and_assign(
server_key: *const BooleanServerKey,
ct_left: *mut BooleanCiphertext,
ct_right: *const BooleanCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_ref_checked(ct_right).unwrap();
server_key.0.and_assign(&mut ct_left.0, &ct_right.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_nand_assign(
server_key: *const BooleanServerKey,
ct_left: *mut BooleanCiphertext,
ct_right: *const BooleanCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_ref_checked(ct_right).unwrap();
server_key.0.nand_assign(&mut ct_left.0, &ct_right.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_nor_assign(
server_key: *const BooleanServerKey,
ct_left: *mut BooleanCiphertext,
ct_right: *const BooleanCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_ref_checked(ct_right).unwrap();
server_key.0.nor_assign(&mut ct_left.0, &ct_right.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_or_assign(
server_key: *const BooleanServerKey,
ct_left: *mut BooleanCiphertext,
ct_right: *const BooleanCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_ref_checked(ct_right).unwrap();
server_key.0.or_assign(&mut ct_left.0, &ct_right.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_xor_assign(
server_key: *const BooleanServerKey,
ct_left: *mut BooleanCiphertext,
ct_right: *const BooleanCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_ref_checked(ct_right).unwrap();
server_key.0.xor_assign(&mut ct_left.0, &ct_right.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_xnor_assign(
server_key: *const BooleanServerKey,
ct_left: *mut BooleanCiphertext,
ct_right: *const BooleanCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_ref_checked(ct_right).unwrap();
server_key.0.xnor_assign(&mut ct_left.0, &ct_right.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_not_assign(
server_key: *const BooleanServerKey,
ct_input: *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_input = get_mut_checked(ct_input).unwrap();
server_key.0.not_assign(&mut ct_input.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_mux(
server_key: *const BooleanServerKey,
ct_condition: *const BooleanCiphertext,
ct_then: *const BooleanCiphertext,
ct_else: *const BooleanCiphertext,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let ct_condition = get_ref_checked(ct_condition).unwrap();
let ct_then = get_ref_checked(ct_then).unwrap();
let ct_else = get_ref_checked(ct_else).unwrap();
let heap_allocated_result = Box::new(BooleanCiphertext(server_key.0.mux(
&ct_condition.0,
&ct_then.0,
&ct_else.0,
)));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_and_scalar(
server_key: *const BooleanServerKey,
ct_left: *const BooleanCiphertext,
scalar: bool,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let heap_allocated_result =
Box::new(BooleanCiphertext(server_key.0.and(&ct_left.0, scalar)));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_nand_scalar(
server_key: *const BooleanServerKey,
ct_left: *const BooleanCiphertext,
scalar: bool,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let heap_allocated_result =
Box::new(BooleanCiphertext(server_key.0.nand(&ct_left.0, scalar)));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_nor_scalar(
server_key: *const BooleanServerKey,
ct_left: *const BooleanCiphertext,
scalar: bool,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let heap_allocated_result =
Box::new(BooleanCiphertext(server_key.0.nor(&ct_left.0, scalar)));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_or_scalar(
server_key: *const BooleanServerKey,
ct_left: *const BooleanCiphertext,
scalar: bool,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let heap_allocated_result =
Box::new(BooleanCiphertext(server_key.0.or(&ct_left.0, scalar)));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_xor_scalar(
server_key: *const BooleanServerKey,
ct_left: *const BooleanCiphertext,
scalar: bool,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let heap_allocated_result =
Box::new(BooleanCiphertext(server_key.0.xor(&ct_left.0, scalar)));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_xnor_scalar(
server_key: *const BooleanServerKey,
ct_left: *const BooleanCiphertext,
scalar: bool,
result: *mut *mut BooleanCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let heap_allocated_result =
Box::new(BooleanCiphertext(server_key.0.xnor(&ct_left.0, scalar)));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_and_scalar_assign(
server_key: *const BooleanServerKey,
ct_left: *mut BooleanCiphertext,
scalar: bool,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
server_key.0.and_assign(&mut ct_left.0, scalar);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_nand_scalar_assign(
server_key: *const BooleanServerKey,
ct_left: *mut BooleanCiphertext,
scalar: bool,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
server_key.0.nand_assign(&mut ct_left.0, scalar);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_nor_scalar_assign(
server_key: *const BooleanServerKey,
ct_left: *mut BooleanCiphertext,
scalar: bool,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
server_key.0.nor_assign(&mut ct_left.0, scalar);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_or_scalar_assign(
server_key: *const BooleanServerKey,
ct_left: *mut BooleanCiphertext,
scalar: bool,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
server_key.0.or_assign(&mut ct_left.0, scalar);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_xor_scalar_assign(
server_key: *const BooleanServerKey,
ct_left: *mut BooleanCiphertext,
scalar: bool,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
server_key.0.xor_assign(&mut ct_left.0, scalar);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_server_key_xnor_scalar_assign(
server_key: *const BooleanServerKey,
ct_left: *mut BooleanCiphertext,
scalar: bool,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
server_key.0.xnor_assign(&mut ct_left.0, scalar);
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_serialize_server_key(
server_key: *const BooleanServerKey,
result: *mut Buffer,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let buffer: Buffer = bincode::serialize(&server_key.0).unwrap().into();
*result = buffer;
})
}
#[no_mangle]
pub unsafe extern "C" fn boolean_deserialize_server_key(
buffer_view: BufferView,
result: *mut *mut BooleanServerKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key: boolean::server_key::ServerKey =
bincode::deserialize(buffer_view.into()).unwrap();
let heap_allocated_server_key = Box::new(BooleanServerKey(server_key));
*result = Box::into_raw(heap_allocated_server_key);
})
}

81
tfhe/src/c_api/buffer.rs Normal file
View File

@@ -0,0 +1,81 @@
//! Module providing some common `C` FFI utilities for key serialization and deserialization.
use crate::c_api::utils::*;
use std::os::raw::c_int;
#[repr(C)]
pub struct Buffer {
pointer: *mut u8,
length: usize,
}
#[repr(C)]
pub struct BufferView {
pointer: *const u8,
length: usize,
}
impl From<Vec<u8>> for Buffer {
fn from(a: Vec<u8>) -> Self {
let a = a.leak();
Self {
pointer: a.as_mut_ptr(),
length: a.len(),
}
}
}
impl From<BufferView> for &[u8] {
fn from(bf: BufferView) -> &'static [u8] {
unsafe { std::slice::from_raw_parts(bf.pointer, bf.length) }
}
}
impl From<&[u8]> for BufferView {
fn from(a: &[u8]) -> Self {
Self {
pointer: a.as_ptr(),
length: a.len(),
}
}
}
/// Deallocate the memory pointed to by a [`Buffer`].
///
/// The [`Buffer`] `pointer` is set to `NULL` and `length` is set to `0` to signal it was freed in
/// addition to the function's return code.
///
/// This function is [checked](crate#safety-checked-and-unchecked-functions).
#[no_mangle]
pub unsafe extern "C" fn destroy_buffer(buffer: *mut Buffer) -> c_int {
catch_panic(|| {
let buffer = get_mut_checked(buffer).unwrap();
let pointer = get_mut_checked(buffer.pointer).unwrap();
let length = buffer.length;
// Reconstruct a vector that will be dropped so that the memory gets freed
Vec::from_raw_parts(pointer, length, length);
buffer.length = 0;
buffer.pointer = std::ptr::null_mut();
})
}
/// [Unchecked](crate#safety-checked-and-unchecked-functions) version of [`destroy_buffer`].
#[no_mangle]
pub unsafe extern "C" fn destroy_buffer_unchecked(buffer: *mut Buffer) -> c_int {
catch_panic(|| {
let buffer = &mut (*buffer);
let pointer = &mut (*buffer.pointer);
let length = buffer.length;
// Reconstruct a vector that will be dropped so that the memory gets freed
Vec::from_raw_parts(pointer, length, length);
buffer.length = 0;
buffer.pointer = std::ptr::null_mut();
})
}

8
tfhe/src/c_api/mod.rs Normal file
View File

@@ -0,0 +1,8 @@
#![deny(rustdoc::broken_intra_doc_links)]
#![allow(clippy::missing_safety_doc)]
#[cfg(feature = "boolean-c-api")]
pub mod boolean;
pub mod buffer;
#[cfg(feature = "shortint-c-api")]
pub mod shortint;
pub(crate) mod utils;

View File

@@ -0,0 +1,70 @@
use crate::c_api::buffer::*;
use crate::c_api::utils::*;
use std::os::raw::c_int;
use crate::shortint;
pub struct ShortintCiphertext(pub(in crate::c_api) shortint::ciphertext::Ciphertext);
#[no_mangle]
pub unsafe extern "C" fn shortint_ciphertext_set_degree(
ciphertext: *mut ShortintCiphertext,
degree: usize,
) -> c_int {
catch_panic(|| {
let ciphertext = get_mut_checked(ciphertext).unwrap();
ciphertext.0.degree.0 = degree;
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_ciphertext_get_degree(
ciphertext: *const ShortintCiphertext,
result: *mut usize,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let ciphertext = get_ref_checked(ciphertext).unwrap();
*result = ciphertext.0.degree.0;
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_serialize_ciphertext(
ciphertext: *const ShortintCiphertext,
result: *mut Buffer,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let ciphertext = get_ref_checked(ciphertext).unwrap();
let buffer: Buffer = bincode::serialize(&ciphertext.0).unwrap().into();
*result = buffer;
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_deserialize_ciphertext(
buffer_view: BufferView,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let ciphertext: shortint::ciphertext::Ciphertext =
bincode::deserialize(buffer_view.into()).unwrap();
let heap_allocated_ciphertext = Box::new(ShortintCiphertext(ciphertext));
*result = Box::into_raw(heap_allocated_ciphertext);
})
}

View File

@@ -0,0 +1,106 @@
use crate::c_api::buffer::*;
use crate::c_api::utils::*;
use bincode;
use std::os::raw::c_int;
use crate::shortint;
use super::ShortintCiphertext;
pub struct ShortintClientKey(pub(in crate::c_api) shortint::client_key::ClientKey);
#[no_mangle]
pub unsafe extern "C" fn shortint_gen_client_key(
shortint_parameters: *const super::parameters::ShortintParameters,
result_client_key: *mut *mut ShortintClientKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result_client_key).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result_client_key = std::ptr::null_mut();
let shortint_parameters = get_ref_checked(shortint_parameters).unwrap();
let client_key = shortint::client_key::ClientKey::new(shortint_parameters.0.to_owned());
let heap_allocated_client_key = Box::new(ShortintClientKey(client_key));
*result_client_key = Box::into_raw(heap_allocated_client_key);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_client_key_encrypt(
client_key: *const ShortintClientKey,
value_to_encrypt: u64,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let client_key = get_ref_checked(client_key).unwrap();
let heap_allocated_ciphertext =
Box::new(ShortintCiphertext(client_key.0.encrypt(value_to_encrypt)));
*result = Box::into_raw(heap_allocated_ciphertext);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_client_key_decrypt(
client_key: *const ShortintClientKey,
ciphertext_to_decrypt: *const ShortintCiphertext,
result: *mut u64,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let client_key = get_ref_checked(client_key).unwrap();
let ciphertext_to_decrypt = get_ref_checked(ciphertext_to_decrypt).unwrap();
*result = client_key.0.decrypt(&ciphertext_to_decrypt.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_serialize_client_key(
client_key: *const ShortintClientKey,
result: *mut Buffer,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let client_key = get_ref_checked(client_key).unwrap();
let buffer: Buffer = bincode::serialize(&client_key.0).unwrap().into();
*result = buffer;
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_deserialize_client_key(
buffer_view: BufferView,
result: *mut *mut ShortintClientKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let client_key: shortint::client_key::ClientKey =
bincode::deserialize(buffer_view.into()).unwrap();
let heap_allocated_client_key = Box::new(ShortintClientKey(client_key));
*result = Box::into_raw(heap_allocated_client_key);
})
}

View File

@@ -0,0 +1,79 @@
use crate::c_api::utils::*;
use std::os::raw::c_int;
use super::parameters::ShortintParameters;
use super::{
ShortintBivariatePBSAccumulator, ShortintCiphertext, ShortintClientKey, ShortintPBSAccumulator,
ShortintPublicKey, ShortintServerKey,
};
#[no_mangle]
pub unsafe extern "C" fn destroy_shortint_client_key(client_key: *mut ShortintClientKey) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(client_key).unwrap();
drop(Box::from_raw(client_key));
})
}
#[no_mangle]
pub unsafe extern "C" fn destroy_shortint_server_key(server_key: *mut ShortintServerKey) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(server_key).unwrap();
drop(Box::from_raw(server_key));
})
}
#[no_mangle]
pub unsafe extern "C" fn destroy_shortint_public_key(public_key: *mut ShortintPublicKey) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(public_key).unwrap();
drop(Box::from_raw(public_key));
})
}
#[no_mangle]
pub unsafe extern "C" fn destroy_shortint_parameters(
shortint_parameters: *mut ShortintParameters,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(shortint_parameters).unwrap();
drop(Box::from_raw(shortint_parameters));
})
}
#[no_mangle]
pub unsafe extern "C" fn destroy_shortint_ciphertext(
shortint_ciphertext: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(shortint_ciphertext).unwrap();
drop(Box::from_raw(shortint_ciphertext));
})
}
#[no_mangle]
pub unsafe extern "C" fn destroy_shortint_pbs_accumulator(
pbs_accumulator: *mut ShortintPBSAccumulator,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(pbs_accumulator).unwrap();
drop(Box::from_raw(pbs_accumulator));
})
}
#[no_mangle]
pub unsafe extern "C" fn destroy_shortint_bivariate_pbs_accumulator(
pbs_accumulator: *mut ShortintBivariatePBSAccumulator,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(pbs_accumulator).unwrap();
drop(Box::from_raw(pbs_accumulator));
})
}

View File

@@ -0,0 +1,45 @@
pub mod ciphertext;
pub mod client_key;
pub mod destroy;
pub mod parameters;
pub mod public_key;
pub mod server_key;
use crate::c_api::utils::*;
use std::os::raw::c_int;
use crate::shortint;
pub use ciphertext::ShortintCiphertext;
pub use client_key::ShortintClientKey;
pub use public_key::ShortintPublicKey;
pub use server_key::pbs::{ShortintBivariatePBSAccumulator, ShortintPBSAccumulator};
pub use server_key::ShortintServerKey;
#[no_mangle]
pub unsafe extern "C" fn shortint_gen_keys_with_parameters(
shortint_parameters: *const parameters::ShortintParameters,
result_client_key: *mut *mut ShortintClientKey,
result_server_key: *mut *mut ShortintServerKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result_client_key).unwrap();
check_ptr_is_non_null_and_aligned(result_server_key).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result_client_key = std::ptr::null_mut();
*result_server_key = std::ptr::null_mut();
let shortint_parameters = get_ref_checked(shortint_parameters).unwrap();
let client_key = shortint::client_key::ClientKey::new(shortint_parameters.0.to_owned());
let server_key = shortint::server_key::ServerKey::new(&client_key);
let heap_allocated_client_key = Box::new(ShortintClientKey(client_key));
let heap_allocated_server_key = Box::new(ShortintServerKey(server_key));
*result_client_key = Box::into_raw(heap_allocated_client_key);
*result_server_key = Box::into_raw(heap_allocated_server_key);
})
}

View File

@@ -0,0 +1,119 @@
use crate::c_api::utils::*;
use crate::core_crypto::prelude::{
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
StandardDev,
};
use std::os::raw::c_int;
use crate::shortint;
pub struct ShortintParameters(pub(in crate::c_api) shortint::parameters::Parameters);
#[no_mangle]
pub unsafe extern "C" fn shortint_get_parameters(
message_bits: u32,
carry_bits: u32,
result: *mut *mut ShortintParameters,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let params: Option<_> = match (message_bits, carry_bits) {
(1, 0) => Some(crate::shortint::parameters::PARAM_MESSAGE_1_CARRY_0),
(1, 1) => Some(crate::shortint::parameters::PARAM_MESSAGE_1_CARRY_1),
(2, 0) => Some(crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_0),
(1, 2) => Some(crate::shortint::parameters::PARAM_MESSAGE_1_CARRY_2),
(2, 1) => Some(crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_1),
(3, 0) => Some(crate::shortint::parameters::PARAM_MESSAGE_3_CARRY_0),
(1, 3) => Some(crate::shortint::parameters::PARAM_MESSAGE_1_CARRY_3),
(2, 2) => Some(crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2),
(3, 1) => Some(crate::shortint::parameters::PARAM_MESSAGE_3_CARRY_1),
(4, 0) => Some(crate::shortint::parameters::PARAM_MESSAGE_4_CARRY_0),
(1, 4) => Some(crate::shortint::parameters::PARAM_MESSAGE_1_CARRY_4),
(2, 3) => Some(crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_3),
(3, 2) => Some(crate::shortint::parameters::PARAM_MESSAGE_3_CARRY_2),
(4, 1) => Some(crate::shortint::parameters::PARAM_MESSAGE_4_CARRY_1),
(5, 0) => Some(crate::shortint::parameters::PARAM_MESSAGE_5_CARRY_0),
(1, 5) => Some(crate::shortint::parameters::PARAM_MESSAGE_1_CARRY_5),
(2, 4) => Some(crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_4),
(3, 3) => Some(crate::shortint::parameters::PARAM_MESSAGE_3_CARRY_3),
(4, 2) => Some(crate::shortint::parameters::PARAM_MESSAGE_4_CARRY_2),
(5, 1) => Some(crate::shortint::parameters::PARAM_MESSAGE_5_CARRY_1),
(6, 0) => Some(crate::shortint::parameters::PARAM_MESSAGE_6_CARRY_0),
(1, 6) => Some(crate::shortint::parameters::PARAM_MESSAGE_1_CARRY_6),
(2, 5) => Some(crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_5),
(3, 4) => Some(crate::shortint::parameters::PARAM_MESSAGE_3_CARRY_4),
(4, 3) => Some(crate::shortint::parameters::PARAM_MESSAGE_4_CARRY_3),
(5, 2) => Some(crate::shortint::parameters::PARAM_MESSAGE_5_CARRY_2),
(6, 1) => Some(crate::shortint::parameters::PARAM_MESSAGE_6_CARRY_1),
(7, 0) => Some(crate::shortint::parameters::PARAM_MESSAGE_7_CARRY_0),
(1, 7) => Some(crate::shortint::parameters::PARAM_MESSAGE_1_CARRY_7),
(2, 6) => Some(crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_6),
(3, 5) => Some(crate::shortint::parameters::PARAM_MESSAGE_3_CARRY_5),
(4, 4) => Some(crate::shortint::parameters::PARAM_MESSAGE_4_CARRY_4),
(5, 3) => Some(crate::shortint::parameters::PARAM_MESSAGE_5_CARRY_3),
(6, 2) => Some(crate::shortint::parameters::PARAM_MESSAGE_6_CARRY_2),
(7, 1) => Some(crate::shortint::parameters::PARAM_MESSAGE_7_CARRY_1),
(8, 0) => Some(crate::shortint::parameters::PARAM_MESSAGE_8_CARRY_0),
_ => None,
};
match params {
Some(params) => {
let params = Box::new(ShortintParameters(params));
*result = Box::into_raw(params);
}
None => *result = std::ptr::null_mut(),
}
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_create_parameters(
lwe_dimension: usize,
glwe_dimension: usize,
polynomial_size: usize,
lwe_modular_std_dev: f64,
glwe_modular_std_dev: f64,
pbs_base_log: usize,
pbs_level: usize,
ks_base_log: usize,
ks_level: usize,
pfks_level: usize,
pfks_base_log: usize,
pfks_modular_std_dev: f64,
cbs_level: usize,
cbs_base_log: usize,
message_modulus: usize,
carry_modulus: usize,
result: *mut *mut ShortintParameters,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let heap_allocated_parameters =
Box::new(ShortintParameters(shortint::parameters::Parameters {
lwe_dimension: LweDimension(lwe_dimension),
glwe_dimension: GlweDimension(glwe_dimension),
polynomial_size: PolynomialSize(polynomial_size),
lwe_modular_std_dev: StandardDev(lwe_modular_std_dev),
glwe_modular_std_dev: StandardDev(glwe_modular_std_dev),
pbs_base_log: DecompositionBaseLog(pbs_base_log),
pbs_level: DecompositionLevelCount(pbs_level),
ks_base_log: DecompositionBaseLog(ks_base_log),
ks_level: DecompositionLevelCount(ks_level),
pfks_level: DecompositionLevelCount(pfks_level),
pfks_base_log: DecompositionBaseLog(pfks_base_log),
pfks_modular_std_dev: StandardDev(pfks_modular_std_dev),
cbs_level: DecompositionLevelCount(cbs_level),
cbs_base_log: DecompositionBaseLog(cbs_base_log),
message_modulus: crate::shortint::parameters::MessageModulus(message_modulus),
carry_modulus: crate::shortint::parameters::CarryModulus(carry_modulus),
}));
*result = Box::into_raw(heap_allocated_parameters);
})
}

View File

@@ -0,0 +1,94 @@
use crate::c_api::buffer::*;
use crate::c_api::utils::*;
use bincode;
use std::os::raw::c_int;
use crate::shortint;
use super::{ShortintCiphertext, ShortintClientKey, ShortintServerKey};
pub struct ShortintPublicKey(pub(in crate::c_api) shortint::public_key::PublicKey);
#[no_mangle]
pub unsafe extern "C" fn shortint_gen_public_key(
client_key: *const ShortintClientKey,
result: *mut *mut ShortintPublicKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let client_key = get_ref_checked(client_key).unwrap();
let heap_allocated_public_key = Box::new(ShortintPublicKey(
shortint::public_key::PublicKey::new(&client_key.0),
));
*result = Box::into_raw(heap_allocated_public_key);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_public_key_encrypt(
public_key: *const ShortintPublicKey,
server_key: *const ShortintServerKey,
value_to_encrypt: u64,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let public_key = get_ref_checked(public_key).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let heap_allocated_ciphertext = Box::new(ShortintCiphertext(
public_key.0.encrypt(&server_key.0, value_to_encrypt),
));
*result = Box::into_raw(heap_allocated_ciphertext);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_serialize_public_key(
public_key: *const ShortintPublicKey,
result: *mut Buffer,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let public_key = get_ref_checked(public_key).unwrap();
let buffer: Buffer = bincode::serialize(&public_key.0).unwrap().into();
*result = buffer;
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_deserialize_public_key(
buffer_view: BufferView,
result: *mut *mut ShortintPublicKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let public_key: shortint::public_key::PublicKey =
bincode::deserialize(buffer_view.into()).unwrap();
let heap_allocated_public_key = Box::new(ShortintPublicKey(public_key));
*result = Box::into_raw(heap_allocated_public_key);
})
}

View File

@@ -0,0 +1,82 @@
use crate::c_api::utils::*;
use std::os::raw::c_int;
use super::{ShortintCiphertext, ShortintServerKey};
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_add(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_add(&mut ct_left.0, &mut ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_add(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_add(&ct_left.0, &ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_add_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
server_key
.0
.smart_add_assign(&mut ct_left_and_result.0, &mut ct_right.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_add_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
server_key
.0
.unchecked_add_assign(&mut ct_left_and_result.0, &ct_right.0);
})
}

View File

@@ -0,0 +1,238 @@
use crate::c_api::utils::*;
use std::os::raw::c_int;
use super::{ShortintCiphertext, ShortintServerKey};
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_bitand(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_bitand(&mut ct_left.0, &mut ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_bitand(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_bitand(&ct_left.0, &ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_bitand_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
server_key
.0
.smart_bitand_assign(&mut ct_left_and_result.0, &mut ct_right.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_bitand_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
server_key
.0
.unchecked_bitand_assign(&mut ct_left_and_result.0, &ct_right.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_bitxor(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_bitxor(&mut ct_left.0, &mut ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_bitxor(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_bitxor(&ct_left.0, &ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_bitxor_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
server_key
.0
.smart_bitxor_assign(&mut ct_left_and_result.0, &mut ct_right.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_bitxor_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
server_key
.0
.unchecked_bitxor_assign(&mut ct_left_and_result.0, &ct_right.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_bitor(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_bitor(&mut ct_left.0, &mut ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_bitor(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_bitor(&ct_left.0, &ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_bitor_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
server_key
.0
.smart_bitor_assign(&mut ct_left_and_result.0, &mut ct_right.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_bitor_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
server_key
.0
.unchecked_bitor_assign(&mut ct_left_and_result.0, &ct_right.0);
})
}

View File

@@ -0,0 +1,406 @@
use crate::c_api::utils::*;
use std::os::raw::c_int;
use super::{ShortintCiphertext, ShortintServerKey};
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_greater(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_greater(&mut ct_left.0, &mut ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_greater(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_greater(&ct_left.0, &ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_greater_or_equal(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key
.0
.smart_greater_or_equal(&mut ct_left.0, &mut ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_greater_or_equal(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key
.0
.unchecked_greater_or_equal(&ct_left.0, &ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_less(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_less(&mut ct_left.0, &mut ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_less(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_less(&ct_left.0, &ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_less_or_equal(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key
.0
.smart_less_or_equal(&mut ct_left.0, &mut ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_less_or_equal(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key
.0
.unchecked_less_or_equal(&ct_left.0, &ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_equal(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_equal(&mut ct_left.0, &mut ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_equal(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_equal(&ct_left.0, &ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_not_equal(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key
.0
.smart_not_equal(&mut ct_left.0, &mut ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_not_equal(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_not_equal(&ct_left.0, &ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_greater(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_scalar_greater(&ct_left.0, right),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_greater_or_equal(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key
.0
.smart_scalar_greater_or_equal(&ct_left.0, right),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_less(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_scalar_less(&ct_left.0, right),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_less_or_equal(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_scalar_less_or_equal(&ct_left.0, right),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_equal(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_scalar_equal(&ct_left.0, right),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_not_equal(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_scalar_not_equal(&ct_left.0, right),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}

View File

@@ -0,0 +1,156 @@
use crate::c_api::utils::*;
use std::os::raw::c_int;
use super::{ShortintCiphertext, ShortintServerKey};
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_div(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_div(&mut ct_left.0, &mut ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_div(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_div(&ct_left.0, &ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_div_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
server_key
.0
.smart_div_assign(&mut ct_left_and_result.0, &mut ct_right.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_div_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
server_key
.0
.unchecked_div_assign(&mut ct_left_and_result.0, &ct_right.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_scalar_div(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_scalar_div(&ct_left.0, right),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_scalar_div_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
right: u8,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
server_key
.0
.unchecked_scalar_div_assign(&mut ct_left_and_result.0, right);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_scalar_mod(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_scalar_mod(&ct_left.0, right),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_scalar_mod_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
right: u8,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
server_key
.0
.unchecked_scalar_mod_assign(&mut ct_left_and_result.0, right);
})
}

View File

@@ -0,0 +1,81 @@
use crate::c_api::buffer::*;
use crate::c_api::utils::*;
use std::os::raw::c_int;
use crate::shortint;
use super::ShortintCiphertext;
pub mod add;
pub mod bitwise_op;
pub mod comp_op;
pub mod div_mod;
pub mod mul;
pub mod neg;
pub mod pbs;
pub mod scalar_add;
pub mod scalar_mul;
pub mod scalar_sub;
pub mod shift;
pub mod sub;
pub struct ShortintServerKey(pub(in crate::c_api) shortint::server_key::ServerKey);
#[no_mangle]
pub unsafe extern "C" fn shortint_gen_server_key(
client_key: *const super::ShortintClientKey,
result_server_key: *mut *mut ShortintServerKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result_server_key).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result_server_key = std::ptr::null_mut();
let client_key = get_ref_checked(client_key).unwrap();
let server_key = shortint::server_key::ServerKey::new(&client_key.0);
let heap_allocated_server_key = Box::new(ShortintServerKey(server_key));
*result_server_key = Box::into_raw(heap_allocated_server_key);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_serialize_server_key(
server_key: *const ShortintServerKey,
result: *mut Buffer,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let buffer: Buffer = bincode::serialize(&server_key.0).unwrap().into();
*result = buffer;
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_deserialize_server_key(
buffer_view: BufferView,
result: *mut *mut ShortintServerKey,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key: shortint::server_key::ServerKey =
bincode::deserialize(buffer_view.into()).unwrap();
let heap_allocated_server_key = Box::new(ShortintServerKey(server_key));
*result = Box::into_raw(heap_allocated_server_key);
})
}

View File

@@ -0,0 +1,82 @@
use crate::c_api::utils::*;
use std::os::raw::c_int;
use super::{ShortintCiphertext, ShortintServerKey};
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_mul(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_mul_lsb(&mut ct_left.0, &mut ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_mul(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_mul_lsb(&ct_left.0, &ct_right.0),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_mul_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
server_key
.0
.smart_mul_lsb_assign(&mut ct_left_and_result.0, &mut ct_right.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_mul_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
server_key
.0
.unchecked_mul_lsb_assign(&mut ct_left_and_result.0, &ct_right.0);
})
}

View File

@@ -0,0 +1,68 @@
use crate::c_api::utils::*;
use std::os::raw::c_int;
use super::{ShortintCiphertext, ShortintServerKey};
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_neg(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result =
Box::new(ShortintCiphertext(server_key.0.smart_neg(&mut ct_left.0)));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_neg(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result =
Box::new(ShortintCiphertext(server_key.0.unchecked_neg(&ct_left.0)));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_neg_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
server_key.0.smart_neg_assign(&mut ct_left_and_result.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_neg_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
server_key.0.unchecked_neg_assign(&mut ct_left_and_result.0);
})
}

View File

@@ -0,0 +1,176 @@
use crate::c_api::utils::*;
use std::os::raw::c_int;
use super::{ShortintCiphertext, ShortintServerKey};
// This is the accepted way to declare a pointer to a C function/callback in cbindgen
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 struct ShortintBivariatePBSAccumulator(
pub(in crate::c_api) crate::core_crypto::prelude::GlweCiphertext64,
);
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_generate_pbs_accumulator(
server_key: *const ShortintServerKey,
accumulator_callback: AccumulatorCallback,
result: *mut *mut ShortintPBSAccumulator,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let accumulator_callback = accumulator_callback.unwrap();
let server_key = get_ref_checked(server_key).unwrap();
// Closure is required as extern "C" fn does not implement the Fn trait
#[allow(clippy::redundant_closure)]
let heap_allocated_accumulator = Box::new(ShortintPBSAccumulator(
server_key
.0
.generate_accumulator(|x: u64| accumulator_callback(x)),
));
*result = Box::into_raw(heap_allocated_accumulator);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_programmable_bootstrap(
server_key: *const ShortintServerKey,
accumulator: *const ShortintPBSAccumulator,
ct_in: *const ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let accumulator = get_ref_checked(accumulator).unwrap();
let ct_in = get_ref_checked(ct_in).unwrap();
let heap_allocated_result = Box::new(ShortintCiphertext(
server_key
.0
.keyswitch_programmable_bootstrap(&ct_in.0, &accumulator.0),
));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_programmable_bootstrap_assign(
server_key: *const ShortintServerKey,
accumulator: *const ShortintPBSAccumulator,
ct_in_and_result: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let accumulator = get_ref_checked(accumulator).unwrap();
let ct_in_and_result = get_mut_checked(ct_in_and_result).unwrap();
server_key
.0
.keyswitch_programmable_bootstrap_assign(&mut ct_in_and_result.0, &accumulator.0);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_generate_bivariate_pbs_accumulator(
server_key: *const ShortintServerKey,
accumulator_callback: BivariateAccumulatorCallback,
result: *mut *mut ShortintBivariatePBSAccumulator,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let accumulator_callback = accumulator_callback.unwrap();
let server_key = get_ref_checked(server_key).unwrap();
// Closure is required as extern "C" fn does not implement the Fn trait
#[allow(clippy::redundant_closure)]
let heap_allocated_accumulator = Box::new(ShortintBivariatePBSAccumulator(
server_key
.0
.generate_accumulator_bivariate(|x: u64, y: u64| accumulator_callback(x, y)),
));
*result = Box::into_raw(heap_allocated_accumulator);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_bivariate_programmable_bootstrap(
server_key: *const ShortintServerKey,
accumulator: *const ShortintBivariatePBSAccumulator,
ct_left: *const ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let server_key = get_ref_checked(server_key).unwrap();
let accumulator = get_ref_checked(accumulator).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let heap_allocated_result = Box::new(ShortintCiphertext(
crate::shortint::engine::ShortintEngine::with_thread_local_mut(|engine| {
engine
.smart_bivariate_pbs(&server_key.0, &ct_left.0, &mut ct_right.0, &accumulator.0)
.unwrap()
}),
));
*result = Box::into_raw(heap_allocated_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_bivariate_programmable_bootstrap_assign(
server_key: *const ShortintServerKey,
accumulator: *const ShortintBivariatePBSAccumulator,
ct_left_and_result: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let accumulator = get_ref_checked(accumulator).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
crate::shortint::engine::ShortintEngine::with_thread_local_mut(|engine| {
engine
.smart_bivariate_pbs_assign(
&server_key.0,
&mut ct_left_and_result.0,
&mut ct_right.0,
&accumulator.0,
)
.unwrap()
});
})
}

View File

@@ -0,0 +1,78 @@
use crate::c_api::utils::*;
use std::os::raw::c_int;
use super::{ShortintCiphertext, ShortintServerKey};
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_add(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
scalar_right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_scalar_add(&mut ct_left.0, scalar_right),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_scalar_add(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
scalar_right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_scalar_add(&ct_left.0, scalar_right),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_add_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
scalar_right: u8,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
server_key
.0
.smart_scalar_add_assign(&mut ct_left_and_result.0, scalar_right);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_scalar_add_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
scalar_right: u8,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
server_key
.0
.unchecked_scalar_add_assign(&mut ct_left_and_result.0, scalar_right);
})
}

View File

@@ -0,0 +1,78 @@
use crate::c_api::utils::*;
use std::os::raw::c_int;
use super::{ShortintCiphertext, ShortintServerKey};
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_mul(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
scalar_right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_scalar_mul(&mut ct_left.0, scalar_right),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_scalar_mul(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
scalar_right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_scalar_mul(&ct_left.0, scalar_right),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_mul_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
scalar_right: u8,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
server_key
.0
.smart_scalar_mul_assign(&mut ct_left_and_result.0, scalar_right);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_scalar_mul_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
scalar_right: u8,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
server_key
.0
.unchecked_scalar_mul_assign(&mut ct_left_and_result.0, scalar_right);
})
}

View File

@@ -0,0 +1,78 @@
use crate::c_api::utils::*;
use std::os::raw::c_int;
use super::{ShortintCiphertext, ShortintServerKey};
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_sub(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
scalar_right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_scalar_sub(&mut ct_left.0, scalar_right),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_scalar_sub(
server_key: *const ShortintServerKey,
ct_left: *mut ShortintCiphertext,
scalar_right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_scalar_sub(&ct_left.0, scalar_right),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_sub_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
scalar_right: u8,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
server_key
.0
.smart_scalar_sub_assign(&mut ct_left_and_result.0, scalar_right);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_scalar_sub_assign(
server_key: *const ShortintServerKey,
ct_left_and_result: *mut ShortintCiphertext,
scalar_right: u8,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct_left_and_result = get_mut_checked(ct_left_and_result).unwrap();
server_key
.0
.unchecked_scalar_sub_assign(&mut ct_left_and_result.0, scalar_right);
})
}

View File

@@ -0,0 +1,134 @@
use crate::c_api::utils::*;
use std::os::raw::c_int;
use super::{ShortintCiphertext, ShortintServerKey};
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_left_shift(
server_key: *const ShortintServerKey,
ct: *mut ShortintCiphertext,
shift: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct = get_mut_checked(ct).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.smart_scalar_left_shift(&mut ct.0, shift),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_scalar_left_shift(
server_key: *const ShortintServerKey,
ct: *mut ShortintCiphertext,
shift: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct = get_mut_checked(ct).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_scalar_left_shift(&ct.0, shift),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_right_shift(
server_key: *const ShortintServerKey,
ct: *mut ShortintCiphertext,
shift: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
shortint_server_key_unchecked_scalar_right_shift(server_key, ct, shift, result)
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_scalar_right_shift(
server_key: *const ShortintServerKey,
ct: *mut ShortintCiphertext,
shift: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct = get_mut_checked(ct).unwrap();
let heap_allocated_ct_result = Box::new(ShortintCiphertext(
server_key.0.unchecked_scalar_right_shift(&ct.0, shift),
));
*result = Box::into_raw(heap_allocated_ct_result);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_left_shift_assign(
server_key: *const ShortintServerKey,
ct: *mut ShortintCiphertext,
shift: u8,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct = get_mut_checked(ct).unwrap();
server_key
.0
.smart_scalar_left_shift_assign(&mut ct.0, shift);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_scalar_left_shift_assign(
server_key: *const ShortintServerKey,
ct: *mut ShortintCiphertext,
shift: u8,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct = get_mut_checked(ct).unwrap();
server_key
.0
.unchecked_scalar_left_shift_assign(&mut ct.0, shift);
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_right_shift_assign(
server_key: *const ShortintServerKey,
ct: *mut ShortintCiphertext,
shift: u8,
) -> c_int {
shortint_server_key_unchecked_scalar_right_shift_assign(server_key, ct, shift)
}
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_unchecked_scalar_right_shift_assign(
server_key: *const ShortintServerKey,
ct: *mut ShortintCiphertext,
shift: u8,
) -> c_int {
catch_panic(|| {
let server_key = get_ref_checked(server_key).unwrap();
let ct = get_mut_checked(ct).unwrap();
server_key
.0
.unchecked_scalar_right_shift_assign(&mut ct.0, shift);
})
}

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