mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-10 23:28:11 -05:00
chore: initial commit
This commit is contained in:
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[alias]
|
||||
xtask = "run --manifest-path ./concrete-tasks/Cargo.toml --"
|
||||
17
.config/nextest.toml
Normal file
17
.config/nextest.toml
Normal 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
1
.gitbook.yaml
Normal file
@@ -0,0 +1 @@
|
||||
root: ./tfhe/docs
|
||||
30
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
30
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal 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.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal 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.
|
||||
28
.github/pull_request_template.md
vendored
Normal file
28
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# Description
|
||||
|
||||
<!--
|
||||
If your PR fixes an issue that is open, add the following line
|
||||
(where `link_your_issue_here` is in the form of `org-name/repo-name#issue-number):
|
||||
|
||||
fixes `<link_your_issue_here>`
|
||||
-->
|
||||
|
||||
|
||||
|
||||
# Checklist
|
||||
|
||||
<!--
|
||||
(Use '[x]' to check the checkboxes, or submit the PR and then click the checkboxes)
|
||||
You can remove items which do not apply or add strikethrough: `~~* [ ] list item~~`, .
|
||||
-->
|
||||
|
||||
|
||||
* [ ] Tests for the changes have been added (for bug fixes / features)
|
||||
* [ ] Docs have been added / updated (for bug fixes / features)
|
||||
* [ ] The PR description links to the related issue (to link an issue, use '#XXX'.)
|
||||
* [ ] The tests on AWS have been launched and are successful (apply the `aws_test` to the PR to launch the tests on AWS)
|
||||
* [ ] The draft release description has been updated
|
||||
* [ ] Check for breaking changes and add them to commit message following the conventional commit [specification][conventional-breaking]
|
||||
|
||||
|
||||
[conventional-breaking]: https://www.conventionalcommits.org/en/v1.0.0/#commit-message-with-description-and-breaking-change-footer
|
||||
67
.github/workflows/aws_slab_concrete.yml
vendored
Normal file
67
.github/workflows/aws_slab_concrete.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
name: Concrete AWS Tests
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
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:
|
||||
concrete-tests:
|
||||
runs-on: ${{ github.event.inputs.runner_name }}
|
||||
steps:
|
||||
- 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: 7200
|
||||
|
||||
# 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 nightly
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
default: true
|
||||
|
||||
- name: Install AWS CLI
|
||||
run: |
|
||||
apt update
|
||||
apt install -y awscli
|
||||
|
||||
- name: Download keys locally
|
||||
run: aws s3 cp --recursive --no-progress s3://concrete-libs-keycache ./keys
|
||||
|
||||
- name: Run concrete tests
|
||||
run: cargo test --release -p concrete --all-features
|
||||
env:
|
||||
RUSTFLAGS: "-C target-cpu=native"
|
||||
71
.github/workflows/aws_slab_shortint.yml
vendored
Normal file
71
.github/workflows/aws_slab_shortint.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
name: Shortint AWS Tests
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
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:
|
||||
runs-on: ${{ github.event.inputs.runner_name }}
|
||||
steps:
|
||||
- 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
|
||||
|
||||
# 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: Install cargo-nextest
|
||||
uses: taiki-e/install-action@nextest
|
||||
|
||||
- name: Install AWS CLI
|
||||
run: |
|
||||
apt update
|
||||
apt install -y awscli
|
||||
|
||||
- name: Download keys locally
|
||||
run: aws s3 cp --recursive --no-progress s3://concrete-libs-keycache ./keys
|
||||
|
||||
- name: Run shortint tests
|
||||
run: ./shortint-tests.sh
|
||||
|
||||
- name: Sync keys
|
||||
run: aws s3 sync ./keys s3://concrete-libs-keycache
|
||||
58
.github/workflows/cargo_build.yml
vendored
Normal file
58
.github/workflows/cargo_build.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
name: Cargo Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
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]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install latest nightly
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
default: true
|
||||
|
||||
- name: Build Release concrete-boolean
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --release -p concrete-boolean --all-targets
|
||||
|
||||
- name: Build Release concrete-shortint
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --release -p concrete-shortint --all-targets --all-features
|
||||
|
||||
- name: Build Release concrete features=booleans
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --release -p concrete --all-targets --features=booleans
|
||||
|
||||
- name: Build Release concrete features=shortints
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --release -p concrete --all-targets --features=shortints
|
||||
|
||||
- name: Build Release concrete features=all
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --release -p concrete --all-targets --all-features
|
||||
26
.github/workflows/cargo_test_boolean.yml
vendored
Normal file
26
.github/workflows/cargo_test_boolean.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Cargo test concrete-boolean
|
||||
|
||||
# We test concrete-boolean in github actions and not on AWS
|
||||
# as these tests are fast enough
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'concrete-boolean/**'
|
||||
|
||||
jobs:
|
||||
cargo-test-boolean:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Test concrete-boolean
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --release -p concrete-boolean --all-targets
|
||||
32
.github/workflows/check_commit.yml
vendored
Normal file
32
.github/workflows/check_commit.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# Check commit and PR compliance
|
||||
name: Check commit and PR compliance
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
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
|
||||
46
.github/workflows/lint.yml
vendored
Normal file
46
.github/workflows/lint.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
# Check formatting using rustfmt
|
||||
# and lint with clippy
|
||||
name: Rustfmt and Clippy check
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
rustfmt:
|
||||
name: rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly
|
||||
components: rustfmt
|
||||
override: true
|
||||
|
||||
- name: Run cargo fmt
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
clippy:
|
||||
name: clippy
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: 1.61
|
||||
components: clippy
|
||||
override: true
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-targets --features=booleans,shortints,internal-keycache -- --no-deps -D warnings
|
||||
35
.github/workflows/scheduled_lint.yml
vendored
Normal file
35
.github/workflows/scheduled_lint.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# Scheduled clipy check event that does not have a pinned version
|
||||
# of rust, and so will serve as a way to know when new lints are
|
||||
# available and that we trigger them.
|
||||
name: Scheduled Lints
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 9 * * MON' # Every Monday at 9
|
||||
|
||||
jobs:
|
||||
clippy:
|
||||
name: clippy
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
components: clippy
|
||||
override: true
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-targets --features=booleans,shortints,internal-keycache -- --no-deps -D warnings
|
||||
|
||||
audit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
target/
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
# Path we use for internal-keycache during tests
|
||||
keys/
|
||||
|
||||
**/Cargo.lock
|
||||
**/*.bin
|
||||
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"tfhe",
|
||||
"concrete-tasks",
|
||||
"concrete-utils",
|
||||
"tfhe-wasm-client-sdk",
|
||||
]
|
||||
|
||||
[profile.bench]
|
||||
lto = "fat"
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
31
INSTALL.md
Normal file
31
INSTALL.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Installation
|
||||
|
||||
The Rust compiler can be installed on __Linux__ and __macOS__ with the following command:
|
||||
|
||||
```bash
|
||||
curl --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
```
|
||||
|
||||
On __Windows__ you need to download the correct `rustup-init.exe` binary and run it, see [the rust documentation on that topic](https://forge.rust-lang.org/infra/other-installation-methods.html#other-ways-to-install-rustup).
|
||||
|
||||
Other rust installation methods are available on the
|
||||
[rust website](https://forge.rust-lang.org/infra/other-installation-methods.html).
|
||||
|
||||
## Apple Silicon
|
||||
|
||||
Building the project on Apple Silicon without an efficient CSPRNG is possible using the default stable rust toolchain.
|
||||
|
||||
To use the efficient CSPRNG, building the crate requires a nightly toolchain.
|
||||
|
||||
```shell
|
||||
rustup install nightly
|
||||
```
|
||||
|
||||
You then can override the toolchain for your local repository using:
|
||||
```shell
|
||||
rustup override set nightly
|
||||
```
|
||||
|
||||
## Windows
|
||||
|
||||
Can be built, indicate how (no default features, no unix seeder), requires a hardware seeder x86_64
|
||||
33
LICENSE
Normal file
33
LICENSE
Normal 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.
|
||||
161
README.md
Normal file
161
README.md
Normal file
@@ -0,0 +1,161 @@
|
||||
<p align="center">
|
||||
<!-- product name logo -->
|
||||
<img width=600 src="https://user-images.githubusercontent.com/5758427/177340641-f152edb7-1957-49a3-86ab-246774701aab.png">
|
||||
</p>
|
||||
<p align="center">
|
||||
<!-- Version badge using shields.io -->
|
||||
<a href="https://github.com/zama-ai/concrete/releases">
|
||||
<img src="https://img.shields.io/github/v/release/zama-ai/concrete-ml?style=flat-square">
|
||||
</a>
|
||||
<!-- Link to docs badge using shields.io -->
|
||||
<a href="https://docs.zama.ai/concrete">
|
||||
<img src="https://img.shields.io/badge/read-documentation-yellow?style=flat-square">
|
||||
</a>
|
||||
<!-- Link to tutorials badge using shields.io -->
|
||||
<a href="https://docs.zama.ai/concrete/tutorials">
|
||||
<img src="https://img.shields.io/badge/tutorials-and%20demos-orange?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/concrete/developer/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>
|
||||
|
||||
The `concrete` ecosystem is a set of crates that implements Zama's variant of
|
||||
[TFHE](https://eprint.iacr.org/2018/421.pdf). In a nutshell,
|
||||
[fully homomorphic encryption (FHE)](https://en.wikipedia.org/wiki/Homomorphic_encryption), allows
|
||||
you to perform computations over encrypted data, allowing you to implement Zero Trust services.
|
||||
|
||||
Concrete is based on the
|
||||
[Learning With Errors (LWE)](https://cims.nyu.edu/~regev/papers/lwesurvey.pdf) and the
|
||||
[Ring Learning With Errors (RLWE)](https://eprint.iacr.org/2012/230.pdf) problems, which are well
|
||||
studied cryptographic hardness assumptions believed to be secure even against quantum computers.
|
||||
|
||||
## Links
|
||||
|
||||
- [documentation](https://docs.zama.ai/concrete)
|
||||
- [whitepaper](http://whitepaper.zama.ai)
|
||||
- [community website](https://community.zama.ai)
|
||||
|
||||
## Concrete crates
|
||||
|
||||
Concrete is implemented using the [Rust Programming language](https://www.rust-lang.org/), which
|
||||
allows very fast, yet very secure implementations.
|
||||
|
||||
The ecosystem is composed of several crates (packages in the Rust language).
|
||||
The crates are split into 2 repositories:
|
||||
|
||||
- The `concrete` repository which contains crates intended to be more approachable by
|
||||
non-cryptographers.
|
||||
- The [concrete-core](https://github.com/zama-ai/concrete-core) repository which contains the crates
|
||||
implementing the low level cryptographic primitives.
|
||||
|
||||
The crates within this repository are:
|
||||
- [`concrete`](concrete): A high-level library, useful to cryptographers that want to quickly
|
||||
implement homomorphic applications, without having to understand the details of the
|
||||
jmplementation.
|
||||
- [`concrete-boolean`](concrete-boolean): A high-level library, implementing homomorphic Boolean gates, making it easy
|
||||
to run any kind of circuits over encrypted data.
|
||||
- [`concrete-shortint`](concrete-shortint): A high-level library, implementing operations on short integers (about 1 to 4 bits).
|
||||
|
||||
## Installation
|
||||
|
||||
As `concrete` relies on `concrete-core`, `concrete` is only supported on `x86_64 Linux` and `x86_64 macOS`.
|
||||
Windows users can use `concrete` through the `WSL`. For Installation instructions see [Install.md](INSTALL.md)
|
||||
or [documentation](https://docs.zama.ai/concrete).
|
||||
|
||||
## Getting Started
|
||||
|
||||
Here is a simple example of an encrypted addition between two encrypted 8-bit variables. For more
|
||||
information please read the [documentation](https://docs.zama.ai/concrete).
|
||||
|
||||
```rust
|
||||
use concrete::{ConfigBuilder, generate_keys, set_server_key, FheUint8};
|
||||
use concrete::prelude::*;
|
||||
|
||||
fn main() {
|
||||
let config = ConfigBuilder::all_disabled()
|
||||
.enable_default_uint8()
|
||||
.build();
|
||||
|
||||
let (client_key, server_key) = generate_keys(config);
|
||||
|
||||
set_server_key(server_key);
|
||||
|
||||
let clear_a = 27u8;
|
||||
let clear_b = 128u8;
|
||||
|
||||
let a = FheUint8::encrypt(clear_a, &client_key);
|
||||
let b = FheUint8::encrypt(clear_b, &client_key);
|
||||
|
||||
let result = a + b;
|
||||
|
||||
let decrypted_result: u8 = result.decrypt(&client_key);
|
||||
|
||||
let clear_result = clear_a + clear_b;
|
||||
|
||||
assert_eq!(decrypted_result, clear_result);
|
||||
}
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
There are two ways to contribute to Concrete:
|
||||
|
||||
- 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!
|
||||
|
||||
## Citing Concrete
|
||||
|
||||
To cite Concrete in academic papers, please use the following entry:
|
||||
|
||||
```text
|
||||
@inproceedings{WAHC:CJLOT20,
|
||||
title={CONCRETE: Concrete Operates oN Ciphertexts Rapidly by Extending TfhE},
|
||||
author={Chillotti, Ilaria and Joye, Marc and Ligier, Damien and Orfila, Jean-Baptiste and Tap, Samuel},
|
||||
booktitle={WAHC 2020--8th Workshop on Encrypted Computing \& Applied Homomorphic Cryptography},
|
||||
volume={15},
|
||||
year={2020}
|
||||
}
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
This library uses several dependencies and we would like to thank the contributors of those
|
||||
libraries.
|
||||
|
||||
We thank [Daniel May](https://gitlab.com/danieljrmay) for supporting this project and donating the
|
||||
`concrete` crate.
|
||||
|
||||
## 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 estimation, in this repository, used to be based on
|
||||
the [LWE Estimator](https://bitbucket.org/malb/lwe-estimator/src/master/),
|
||||
with `reduction_cost_model = BKZ.sieve`.
|
||||
We are currently moving to 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 Concrete,
|
||||
and will be released in upcoming versions.
|
||||
19
ci/slab.toml
Normal file
19
ci/slab.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[profile.cpu-big]
|
||||
region = "eu-west-3"
|
||||
image_id = "ami-04deffe45b5b236fd"
|
||||
instance_type = "c5a.8xlarge"
|
||||
|
||||
[profile.cpu]
|
||||
region = "eu-west-3"
|
||||
image_id = "ami-04deffe45b5b236fd"
|
||||
instance_type = "m5.2xlarge"
|
||||
|
||||
[command.shortint-test-cpu]
|
||||
workflow = "aws_slab_shortint.yml"
|
||||
profile = "cpu-big"
|
||||
check_run_name = "Shortint CPU AWS Tests"
|
||||
|
||||
[command.concrete-test-cpu]
|
||||
workflow = "aws_slab_concrete.yml"
|
||||
profile = "cpu"
|
||||
check_run_name = "Concrete CPU AWS Tests"
|
||||
11
concrete-tasks/Cargo.toml
Normal file
11
concrete-tasks/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "concrete-tasks"
|
||||
version = "0.0.0"
|
||||
edition = "2018"
|
||||
categories = ["do_not_publish"]
|
||||
|
||||
[dependencies]
|
||||
clap = "2.3"
|
||||
lazy_static = "1.4"
|
||||
log = "0.4"
|
||||
simplelog = "0.12.0"
|
||||
33
concrete-tasks/LICENSE
Normal file
33
concrete-tasks/LICENSE
Normal 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.
|
||||
119
concrete-tasks/src/build.rs
Normal file
119
concrete-tasks/src/build.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
use crate::utils::Environment;
|
||||
use crate::{cmd, ENV_TARGET_NATIVE};
|
||||
use std::collections::HashMap;
|
||||
use std::io::Error;
|
||||
|
||||
lazy_static! {
|
||||
static ref ENV_TARGET_SIMD: Environment = {
|
||||
let mut env = HashMap::new();
|
||||
env.insert(
|
||||
"RUSTFLAGS",
|
||||
"-Ctarget-feature=+aes,+rdseed,+sse2,+avx,+avx2",
|
||||
);
|
||||
env
|
||||
};
|
||||
static ref ENV_DOCTEST: Environment = {
|
||||
let mut env = HashMap::new();
|
||||
env.insert("RUSTDOCFLAGS", "-Zunstable-options --no-run");
|
||||
env
|
||||
};
|
||||
static ref ENV_DOCTEST_SIMD: Environment = {
|
||||
let mut env = HashMap::new();
|
||||
env.insert("RUSTDOCFLAGS", "-Zunstable-options --no-run");
|
||||
env.insert(
|
||||
"RUSTFLAGS",
|
||||
"-Ctarget-feature=+aes,+rdseed,+sse2,+avx,+avx2",
|
||||
);
|
||||
env
|
||||
};
|
||||
static ref ENV_DOCTEST_NATIVE: Environment = {
|
||||
let mut env = HashMap::new();
|
||||
env.insert("RUSTDOCFLAGS", "-Zunstable-options --no-run");
|
||||
env.insert("RUSTFLAGS", "-Ctarget-cpu=native");
|
||||
env
|
||||
};
|
||||
}
|
||||
|
||||
pub mod debug {
|
||||
use super::*;
|
||||
|
||||
pub fn crates() -> Result<(), Error> {
|
||||
cmd!("cargo build --all-features")
|
||||
}
|
||||
|
||||
pub fn benches() -> Result<(), Error> {
|
||||
cmd!("cargo build --benches")
|
||||
}
|
||||
|
||||
pub fn tests() -> Result<(), Error> {
|
||||
cmd!("cargo test --no-run --all-features")
|
||||
}
|
||||
|
||||
pub fn doctests() -> Result<(), Error> {
|
||||
cmd!(<ENV_DOCTEST> "cargo +nightly test --doc --all-features")
|
||||
}
|
||||
}
|
||||
|
||||
pub mod release {
|
||||
use super::*;
|
||||
|
||||
pub fn crates() -> Result<(), Error> {
|
||||
cmd!("cargo build --release --all-features")
|
||||
}
|
||||
|
||||
pub fn benches() -> Result<(), Error> {
|
||||
cmd!("cargo build --release --benches")
|
||||
}
|
||||
|
||||
pub fn tests() -> Result<(), Error> {
|
||||
cmd!("cargo test --release --no-run --all-features")
|
||||
}
|
||||
|
||||
pub fn doctests() -> Result<(), Error> {
|
||||
cmd!(<ENV_DOCTEST> "cargo +nightly test --release --doc --all-features")
|
||||
}
|
||||
}
|
||||
|
||||
pub mod simd {
|
||||
use super::*;
|
||||
|
||||
pub fn crates() -> Result<(), Error> {
|
||||
if cfg!(target_os = "linux") {
|
||||
cmd!(<ENV_TARGET_SIMD> "cargo build --release --all-features")
|
||||
} else if cfg!(target_os = "macos") {
|
||||
cmd!(<ENV_TARGET_NATIVE> "cargo build --release --all-features")
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn benches() -> Result<(), Error> {
|
||||
if cfg!(target_os = "linux") {
|
||||
cmd!(<ENV_TARGET_SIMD> "cargo build --release --benches")
|
||||
} else if cfg!(target_os = "macos") {
|
||||
cmd!(<ENV_TARGET_NATIVE> "cargo build --release --benches")
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tests() -> Result<(), Error> {
|
||||
if cfg!(target_os = "linux") {
|
||||
cmd!(<ENV_TARGET_SIMD> "cargo test --release --no-run --all-features")
|
||||
} else if cfg!(target_os = "macos") {
|
||||
cmd!(<ENV_TARGET_NATIVE> "cargo test --release --no-run --all-features")
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn doctests() -> Result<(), Error> {
|
||||
if cfg!(target_os = "linux") {
|
||||
cmd!(<ENV_DOCTEST_SIMD> "cargo +nightly test --release --doc --all-features")
|
||||
} else if cfg!(target_os = "macos") {
|
||||
cmd!(<ENV_DOCTEST_NATIVE> "cargo +nightly test --release --doc --all-features")
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
24
concrete-tasks/src/check.rs
Normal file
24
concrete-tasks/src/check.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use crate::cmd;
|
||||
use crate::utils::Environment;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Error;
|
||||
|
||||
lazy_static! {
|
||||
static ref ENV_DOC_KATEX: Environment = {
|
||||
let mut env = HashMap::new();
|
||||
env.insert("RUSTDOCFLAGS", "--html-in-header katex-header.html");
|
||||
env
|
||||
};
|
||||
}
|
||||
|
||||
pub fn doc() -> Result<(), Error> {
|
||||
cmd!(<ENV_DOC_KATEX> "cargo +nightly doc --features=doc --no-deps")
|
||||
}
|
||||
|
||||
pub fn clippy() -> Result<(), Error> {
|
||||
cmd!("cargo +nightly clippy --all-targets --all-features -- --no-deps")
|
||||
}
|
||||
|
||||
pub fn fmt() -> Result<(), Error> {
|
||||
cmd!("cargo +nightly fmt --all -- --check")
|
||||
}
|
||||
6
concrete-tasks/src/chore.rs
Normal file
6
concrete-tasks/src/chore.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
use crate::cmd;
|
||||
use std::io::Error;
|
||||
|
||||
pub fn format() -> Result<(), Error> {
|
||||
cmd!("cargo +nightly fmt")
|
||||
}
|
||||
12
concrete-tasks/src/doc.rs
Normal file
12
concrete-tasks/src/doc.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use crate::cmd;
|
||||
use std::io::Error;
|
||||
|
||||
pub fn test_book_boolean() -> Result<(), Error> {
|
||||
cmd!("cargo build -p concrete-boolean --release")?;
|
||||
cmd!("rustdoc --test concrete-boolean/docs/user/introduction.md -L target/release/deps")?;
|
||||
cmd!("rustdoc --test concrete-boolean/docs/user/how_does_it_work.md -L target/release/deps")?;
|
||||
cmd!("rustdoc --test concrete-boolean/docs/user/tutorial.md -L target/release/deps")?;
|
||||
cmd!("rustdoc --test concrete-boolean/docs/user/error.md -L target/release/deps")?;
|
||||
cmd!("rustdoc --test concrete-boolean/docs/user/advanced_topics/generate_keys.md -L target/release/deps")?;
|
||||
cmd!("rustdoc --test concrete-boolean/docs/user/advanced_topics/save_load.md -L target/release/deps")
|
||||
}
|
||||
175
concrete-tasks/src/main.rs
Normal file
175
concrete-tasks/src/main.rs
Normal file
@@ -0,0 +1,175 @@
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
use clap::{App, AppSettings, Arg};
|
||||
use log::LevelFilter;
|
||||
use simplelog::{ColorChoice, CombinedLogger, Config, TermLogger, TerminalMode};
|
||||
use std::collections::HashMap;
|
||||
use std::env::consts::OS;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
|
||||
mod build;
|
||||
mod check;
|
||||
mod chore;
|
||||
mod doc;
|
||||
mod test;
|
||||
mod utils;
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// CONSTANTS
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
lazy_static! {
|
||||
static ref DRY_RUN: AtomicBool = AtomicBool::new(false);
|
||||
static ref ROOT_DIR: PathBuf = utils::project_root();
|
||||
static ref ENV_TARGET_NATIVE: utils::Environment = {
|
||||
let mut env = HashMap::new();
|
||||
env.insert("RUSTFLAGS", "-Ctarget-cpu=native");
|
||||
env
|
||||
};
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// MACROS
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cmd {
|
||||
($cmd: literal) => {
|
||||
$crate::utils::execute($cmd, None, Some(&*$crate::ROOT_DIR))
|
||||
};
|
||||
(<$env: ident> $cmd: literal) => {
|
||||
$crate::utils::execute($cmd, Some(&*$env), Some(&*$crate::ROOT_DIR))
|
||||
};
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// MAIN
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
fn main() -> Result<(), std::io::Error> {
|
||||
// We check whether the current os is supported
|
||||
if !(OS == "linux" || OS == "macos") {
|
||||
panic!("Concrete tasks are only supported on linux and macos.")
|
||||
}
|
||||
|
||||
// We parse the input args
|
||||
let matches = App::new("concrete-tasks")
|
||||
.about("Performs concrete plumbing tasks")
|
||||
.arg(
|
||||
Arg::with_name("verbose")
|
||||
.short("v")
|
||||
.long("verbose")
|
||||
.help("Prints debug messages"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("dry-run")
|
||||
.long("dry-run")
|
||||
.help("Do not execute the commands"),
|
||||
)
|
||||
.subcommand(App::new("test").about("Executes all available tests in native mode"))
|
||||
.subcommand(App::new("cov").about("Computes test coverage in native mode"))
|
||||
.subcommand(App::new("build").about("Builds the crates in all available mode"))
|
||||
.subcommand(App::new("check").about("Performs all the available checks"))
|
||||
.subcommand(App::new("test_toplevel").about("Tests the `concrete` crate in native mode"))
|
||||
.subcommand(App::new("test_crates").about("Tests all the crates in native mode"))
|
||||
.subcommand(
|
||||
App::new("test_and_cov_crates")
|
||||
.about("Compute tests coverage of all crates in native mode"),
|
||||
)
|
||||
.subcommand(App::new("test_book_boolean").about("Test the book for concrete-boolean"))
|
||||
.subcommand(App::new("build_debug_crates").about("Build all the crates in debug mode"))
|
||||
.subcommand(App::new("build_release_crates").about("Build all the crates in release mode"))
|
||||
.subcommand(App::new("build_simd_crates").about("Build all the crates in simd mode"))
|
||||
.subcommand(App::new("build_benches").about("Build the benchmarks in release mode"))
|
||||
.subcommand(App::new("check_doc").about("Checks that the doc compiles without warnings"))
|
||||
.subcommand(App::new("check_clippy").about("Checks that clippy runs without warnings"))
|
||||
.subcommand(App::new("check_fmt").about("Checks that rustfmt runs without warnings"))
|
||||
.subcommand(App::new("chore_format").about("Format the codebase with rustfmt"))
|
||||
.setting(AppSettings::ArgRequiredElseHelp)
|
||||
.get_matches();
|
||||
|
||||
// We initialize the logger with proper verbosity
|
||||
let verb = if matches.is_present("verbose") {
|
||||
LevelFilter::Debug
|
||||
} else {
|
||||
LevelFilter::Info
|
||||
};
|
||||
CombinedLogger::init(vec![TermLogger::new(
|
||||
verb,
|
||||
Config::default(),
|
||||
TerminalMode::Mixed,
|
||||
ColorChoice::Auto,
|
||||
)])
|
||||
.unwrap();
|
||||
|
||||
// We set the dry-run mode if present
|
||||
if matches.is_present("dry-run") {
|
||||
DRY_RUN.store(true, Relaxed);
|
||||
}
|
||||
|
||||
// We execute the task.
|
||||
if matches.subcommand_matches("test").is_some() {
|
||||
test::crates()?;
|
||||
}
|
||||
if matches.subcommand_matches("cov").is_some() {
|
||||
test::cov_crates()?;
|
||||
}
|
||||
if matches.subcommand_matches("build").is_some() {
|
||||
build::debug::benches()?;
|
||||
build::debug::crates()?;
|
||||
build::debug::doctests()?;
|
||||
build::debug::tests()?;
|
||||
build::release::benches()?;
|
||||
build::release::crates()?;
|
||||
build::release::doctests()?;
|
||||
build::release::tests()?;
|
||||
build::simd::benches()?;
|
||||
build::simd::crates()?;
|
||||
build::simd::doctests()?;
|
||||
build::simd::tests()?;
|
||||
}
|
||||
if matches.subcommand_matches("check").is_some() {
|
||||
check::doc()?;
|
||||
check::clippy()?;
|
||||
check::fmt()?;
|
||||
}
|
||||
if matches.subcommand_matches("test_toplevel").is_some() {
|
||||
test::toplevel()?;
|
||||
}
|
||||
if matches.subcommand_matches("test_crates").is_some() {
|
||||
test::crates()?;
|
||||
}
|
||||
if matches.subcommand_matches("test_and_cov_crates").is_some() {
|
||||
test::cov_crates()?;
|
||||
}
|
||||
if matches.subcommand_matches("test_book_boolean").is_some() {
|
||||
doc::test_book_boolean()?;
|
||||
}
|
||||
if matches.subcommand_matches("build_debug_crates").is_some() {
|
||||
build::debug::crates()?;
|
||||
}
|
||||
if matches.subcommand_matches("build_release_crates").is_some() {
|
||||
build::release::crates()?;
|
||||
}
|
||||
if matches.subcommand_matches("build_simd_crates").is_some() {
|
||||
build::simd::crates()?;
|
||||
}
|
||||
if matches.subcommand_matches("build_benches").is_some() {
|
||||
build::release::benches()?;
|
||||
}
|
||||
if matches.subcommand_matches("check_doc").is_some() {
|
||||
check::doc()?;
|
||||
}
|
||||
if matches.subcommand_matches("check_clippy").is_some() {
|
||||
check::clippy()?;
|
||||
}
|
||||
if matches.subcommand_matches("check_fmt").is_some() {
|
||||
check::fmt()?;
|
||||
}
|
||||
if matches.subcommand_matches("chore_format").is_some() {
|
||||
chore::format()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
32
concrete-tasks/src/test.rs
Normal file
32
concrete-tasks/src/test.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use crate::utils::Environment;
|
||||
use crate::{cmd, ENV_TARGET_NATIVE};
|
||||
use std::collections::HashMap;
|
||||
use std::io::Error;
|
||||
|
||||
lazy_static! {
|
||||
static ref ENV_COVERAGE: Environment = {
|
||||
let mut env = HashMap::new();
|
||||
env.insert("CARGO_INCREMENTAL", "0");
|
||||
env.insert("RUSTFLAGS", "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests");
|
||||
env.insert("RUSTDOCFLAGS", "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests");
|
||||
env
|
||||
};
|
||||
}
|
||||
|
||||
pub fn toplevel() -> Result<(), Error> {
|
||||
cmd!(<ENV_TARGET_NATIVE> "cargo test --release --no-fail-fast --all-features -p concrete")
|
||||
}
|
||||
|
||||
pub fn boolean() -> Result<(), Error> {
|
||||
cmd!(<ENV_TARGET_NATIVE> "cargo test --release --no-fail-fast --all-features -p concrete-boolean")
|
||||
}
|
||||
|
||||
pub fn crates() -> Result<(), Error> {
|
||||
toplevel()?;
|
||||
boolean()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cov_crates() -> Result<(), Error> {
|
||||
cmd!(<ENV_COVERAGE> "cargo +nightly test --release --no-fail-fast --all-features")
|
||||
}
|
||||
49
concrete-tasks/src/utils.rs
Normal file
49
concrete-tasks/src/utils.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use log::{debug, info};
|
||||
use std::collections::HashMap;
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
|
||||
pub type Environment = HashMap<&'static str, &'static str>;
|
||||
|
||||
pub fn execute(cmd: &str, env: Option<&Environment>, cwd: Option<&PathBuf>) -> Result<(), Error> {
|
||||
info!("Executing {}", cmd);
|
||||
debug!("Env {:?}", env);
|
||||
debug!("Cwd {:?}", cwd);
|
||||
if crate::DRY_RUN.load(Relaxed) {
|
||||
info!("Skipping execution because of --dry-run mode");
|
||||
return Ok(());
|
||||
}
|
||||
let mut command = Command::new("sh");
|
||||
command
|
||||
.arg("-c")
|
||||
.arg(cmd)
|
||||
.stderr(Stdio::inherit())
|
||||
.stdout(Stdio::inherit());
|
||||
if let Some(env) = env {
|
||||
for (key, val) in env.iter() {
|
||||
command.env(key, val);
|
||||
}
|
||||
}
|
||||
if let Some(cwd) = cwd {
|
||||
command.current_dir(cwd);
|
||||
}
|
||||
let output = command.output()?;
|
||||
if !output.status.success() {
|
||||
Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
"Command exited with nonzero status.",
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn project_root() -> PathBuf {
|
||||
Path::new(&env!("CARGO_MANIFEST_DIR"))
|
||||
.ancestors()
|
||||
.nth(1)
|
||||
.unwrap()
|
||||
.to_path_buf()
|
||||
}
|
||||
12
concrete-utils/Cargo.toml
Normal file
12
concrete-utils/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "concrete-utils"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
authors = ["Zama team"]
|
||||
license = "BSD-3-Clause-Clear"
|
||||
description = "Internal utilities for concrete"
|
||||
|
||||
[dependencies]
|
||||
serde = "1.0.137"
|
||||
bincode = "1.3.3"
|
||||
once_cell = "1.13"
|
||||
33
concrete-utils/LICENSE
Normal file
33
concrete-utils/LICENSE
Normal 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.
|
||||
182
concrete-utils/src/keycache.rs
Normal file
182
concrete-utils/src/keycache.rs
Normal file
@@ -0,0 +1,182 @@
|
||||
use once_cell::sync::OnceCell;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, BufWriter};
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
pub trait PersistentStorage<P, K> {
|
||||
fn load(&self, param: P) -> Option<K>;
|
||||
fn store(&self, param: P, key: &K);
|
||||
}
|
||||
|
||||
pub trait NamedParam {
|
||||
fn name(&self) -> String;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! named_params_impl(
|
||||
( $thing:ident == ( $($const_param:ident),* $(,)? )) => {
|
||||
named_params_impl!({ *$thing } == ( $($const_param),* ))
|
||||
};
|
||||
|
||||
( { $thing:expr } == ( $($const_param:ident),* $(,)? )) => {
|
||||
$(
|
||||
if $thing == $const_param {
|
||||
return stringify!($const_param).to_string();
|
||||
}
|
||||
)*
|
||||
|
||||
panic!("Unnamed parameters");
|
||||
}
|
||||
);
|
||||
|
||||
pub struct FileStorage {
|
||||
prefix: String,
|
||||
}
|
||||
|
||||
impl FileStorage {
|
||||
pub fn new(prefix: String) -> Self {
|
||||
Self { prefix }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, K> PersistentStorage<P, K> for FileStorage
|
||||
where
|
||||
P: NamedParam + DeserializeOwned + Serialize + PartialEq,
|
||||
K: DeserializeOwned + Serialize,
|
||||
{
|
||||
fn load(&self, param: P) -> Option<K> {
|
||||
let mut path_buf = PathBuf::with_capacity(256);
|
||||
path_buf.push(&self.prefix);
|
||||
path_buf.push(param.name());
|
||||
path_buf.set_extension("bin");
|
||||
|
||||
if path_buf.exists() {
|
||||
let file = BufReader::new(File::open(&path_buf).unwrap());
|
||||
bincode::deserialize_from::<_, (P, K)>(file)
|
||||
.ok()
|
||||
.and_then(|(p, k)| if p == param { Some(k) } else { None })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn store(&self, param: P, key: &K) {
|
||||
let mut path_buf = PathBuf::with_capacity(256);
|
||||
path_buf.push(&self.prefix);
|
||||
std::fs::create_dir_all(&path_buf).unwrap();
|
||||
path_buf.push(param.name());
|
||||
path_buf.set_extension("bin");
|
||||
|
||||
let file = BufWriter::new(File::create(&path_buf).unwrap());
|
||||
bincode::serialize_into(file, &(param, key)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SharedKey<K> {
|
||||
inner: Arc<OnceCell<K>>,
|
||||
}
|
||||
|
||||
impl<K> Clone for SharedKey<K> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> Deref for SharedKey<K> {
|
||||
type Target = K;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.inner.get().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KeyCache<P, K, S> {
|
||||
// Where the keys will be stored persistently
|
||||
// So they are not generated between each run
|
||||
persistent_storage: S,
|
||||
// Temporary memory storage to avoid querying the persistent storage each time
|
||||
// the outer Arc makes it so that we don't clone the OnceCell contents when initializing it
|
||||
memory_storage: RwLock<Vec<(P, SharedKey<K>)>>,
|
||||
}
|
||||
|
||||
impl<P, K, S> KeyCache<P, K, S> {
|
||||
pub fn new(storage: S) -> Self {
|
||||
Self {
|
||||
persistent_storage: storage,
|
||||
memory_storage: RwLock::new(vec![]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, K, S> KeyCache<P, K, S>
|
||||
where
|
||||
P: Copy + PartialEq + NamedParam,
|
||||
S: PersistentStorage<P, K>,
|
||||
K: From<P> + Clone,
|
||||
{
|
||||
pub fn get(&self, param: P) -> SharedKey<K> {
|
||||
self.with_key(param, |k| k.clone())
|
||||
}
|
||||
|
||||
pub fn with_key<F, R>(&self, param: P, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&SharedKey<K>) -> R,
|
||||
{
|
||||
let load_from_persistent_storage = || {
|
||||
// we check if we can load the key from persistent storage
|
||||
let persistent_storage = &self.persistent_storage;
|
||||
let maybe_key = persistent_storage.load(param);
|
||||
match maybe_key {
|
||||
Some(key) => key,
|
||||
None => {
|
||||
let key = K::from(param);
|
||||
persistent_storage.store(param, &key);
|
||||
key
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let try_load_from_memory_and_init = || {
|
||||
// we only hold a read lock for a short duration to find the key
|
||||
let memory_storage = self.memory_storage.read().unwrap();
|
||||
let maybe_shared_cell = memory_storage
|
||||
.iter()
|
||||
.find(|(p, _)| *p == param)
|
||||
.map(|param_key| param_key.1.clone());
|
||||
drop(memory_storage);
|
||||
|
||||
if let Some(shared_cell) = maybe_shared_cell {
|
||||
shared_cell.inner.get_or_init(load_from_persistent_storage);
|
||||
Ok(shared_cell)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
};
|
||||
|
||||
match try_load_from_memory_and_init() {
|
||||
Ok(result) => f(&result),
|
||||
Err(()) => {
|
||||
{
|
||||
// we only hold a write lock for a short duration to push the lazily evaluated
|
||||
// key without actually evaluating the key
|
||||
let mut memory_storage = self.memory_storage.write().unwrap();
|
||||
if !memory_storage.iter().any(|(p, _)| *p == param) {
|
||||
memory_storage.push((
|
||||
param,
|
||||
SharedKey {
|
||||
inner: Arc::new(OnceCell::new()),
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
f(&try_load_from_memory_and_init().ok().unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
concrete-utils/src/lib.rs
Normal file
1
concrete-utils/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod keycache;
|
||||
15
katex-header.html
Normal file
15
katex-header.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/katex.min.css" integrity="sha384-9eLZqc9ds8eNjO3TmqPeYcDj8n+Qfa4nuSiGYa6DjLNcv9BtN69ZIulL9+8CqC9Y" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/katex.min.js" integrity="sha384-K3vbOmF2BtaVai+Qk37uypf7VrgBubhQreNQe9aGsz9lB63dIFiQVlJbr92dw2Lx" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/contrib/auto-render.min.js" integrity="sha384-kmZOZB5ObwgQnS/DuDg6TScgOiWWBiVt0plIRkZCmE6rDZGrEOQeHM5PcHi+nyqe" 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
5
rustfmt.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
unstable_features = true
|
||||
imports_granularity="Module"
|
||||
format_code_in_doc_comments = true
|
||||
wrap_comments = true
|
||||
comment_width = 100
|
||||
43
shortint-tests.sh
Executable file
43
shortint-tests.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
nproc_bin=nproc
|
||||
|
||||
# macOS detects CPUs differently
|
||||
if [[ $(uname) == "Darwin" ]]; then
|
||||
nproc_bin="sysctl -n hw.logicalcpu"
|
||||
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_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"
|
||||
|
||||
cargo nextest run \
|
||||
--release \
|
||||
--package tfhe \
|
||||
--profile ci \
|
||||
--features=x86_64-unix,shortints,internal-keycache \
|
||||
--test-threads "$(${nproc_bin})" \
|
||||
-E "${filter_expression}"
|
||||
|
||||
cargo test \
|
||||
--release \
|
||||
--package tfhe \
|
||||
--features=x86_64-unix,shortints,internal-keycache \
|
||||
--doc
|
||||
|
||||
echo "Test ran in $SECONDS seconds"
|
||||
32
tfhe-wasm-client-sdk/Cargo.toml
Normal file
32
tfhe-wasm-client-sdk/Cargo.toml
Normal file
@@ -0,0 +1,32 @@
|
||||
[package]
|
||||
name = "tfhe-wasm-client-sdk"
|
||||
version = "0.1.0"
|
||||
authors = ["Zama team"]
|
||||
edition = "2021"
|
||||
license = "BSD-3-Clause-Clear"
|
||||
description = "Wasm client interface for tfhe"
|
||||
homepage = "https://www.zama.ai/concrete-framework"
|
||||
readme = "README.md"
|
||||
keywords = ["fully", "homomorphic", "encryption", "fhe", "cryptography"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = { version = "0.2.63", features = ["serde-serialize"] }
|
||||
js-sys = "0.3"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde-wasm-bindgen = "0.4"
|
||||
paste = "1.0.7"
|
||||
|
||||
[dependencies.tfhe]
|
||||
path = "../tfhe"
|
||||
default-features = false
|
||||
features = [
|
||||
"backend_default",
|
||||
"backend_default_parallel",
|
||||
"backend_default_serialization",
|
||||
"backend_fft",
|
||||
"backend_fft_serialization",
|
||||
]
|
||||
33
tfhe-wasm-client-sdk/LICENSE
Normal file
33
tfhe-wasm-client-sdk/LICENSE
Normal 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.
|
||||
24
tfhe-wasm-client-sdk/README.md
Normal file
24
tfhe-wasm-client-sdk/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# WASM client SDK
|
||||
|
||||
This crate exposes a WASM interface for the so called "client-side" APIs of the project. Using these APIs, it is possible to run operations such as key generation, encryption and decryption.
|
||||
|
||||
## Building the client wasm SDK
|
||||
|
||||
To build the client wasm SDK, you will need a `rust` toolchain as well as `wasm-pack`. This tool will call the rust compiler to generate the `wasm` code and generate a javascript file containing boilerplate to ease the integration for your usecase (be it `nodejs`, or for the browser).
|
||||
|
||||
To install `wasm-pack` see [the project homepage](https://rustwasm.github.io/wasm-pack/installer/).
|
||||
|
||||
You can use `wasm-pack` to compile the client wasm SDK with the following command:
|
||||
```shell
|
||||
# For use with nodejs
|
||||
wasm-pack build --release --target=nodejs
|
||||
# For use with a Bundler-like web pack
|
||||
wasm-pack build --release --target=bundler
|
||||
# For use in the browser
|
||||
wasm-pack build --release --target=web
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This software is distributed under the BSD-3-Clause-Clear license. If you have any questions,
|
||||
please contact us at `hello@zama.ai`.
|
||||
1234
tfhe-wasm-client-sdk/src/lib.rs
Normal file
1234
tfhe-wasm-client-sdk/src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
139
tfhe/Cargo.toml
Normal file
139
tfhe/Cargo.toml
Normal file
@@ -0,0 +1,139 @@
|
||||
[package]
|
||||
name = "tfhe"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
readme = "README.md"
|
||||
keywords = ["fully", "homomorphic", "encryption", "fhe", "cryptography"]
|
||||
homepage = "https://zama.ai/"
|
||||
license = "BSD-3-Clause-Clear"
|
||||
description = "Concrete is a fully homomorphic encryption (FHE) library that implements Zama's variant of TFHE."
|
||||
|
||||
# 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"
|
||||
concrete-utils = { path = "../concrete-utils", version = "0.2.0"}
|
||||
lazy_static = { version = "1.4.0" }
|
||||
criterion = "0.3.5"
|
||||
doc-comment = "0.3.3"
|
||||
# Used in user documentation
|
||||
bincode = "1.3.3"
|
||||
|
||||
[dependencies]
|
||||
concrete-csprng = { version = "0.2.1" }
|
||||
concrete-cuda = { version = "0.1.1", optional = true }
|
||||
# both utils and lazy_static are used for the keycache
|
||||
# they both are deps to allow usage in bench in integer crate
|
||||
# and dev-deps to automatically enable them in tests
|
||||
concrete-utils = { path = "../concrete-utils", version = "0.2.0", 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 = "0.8"
|
||||
once_cell = "1.13"
|
||||
paste = "1.0.7"
|
||||
|
||||
[features]
|
||||
booleans = []
|
||||
shortints = []
|
||||
internal-keycache = ["concrete-utils", "lazy_static"]
|
||||
|
||||
cuda = ["backend_cuda"]
|
||||
nightly-avx512 = ["backend_fft_nightly_avx512"]
|
||||
|
||||
# A pure-rust backend. Included by default in the build.
|
||||
backend_default = ["concrete-csprng/generator_soft"]
|
||||
|
||||
# An accelerated backend, using the `concrete-fft` library.
|
||||
backend_fft = ["concrete-fft"]
|
||||
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",
|
||||
]
|
||||
|
||||
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"]
|
||||
|
||||
# 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 = [
|
||||
"backend_default",
|
||||
"backend_default_parallel",
|
||||
"backend_default_generator_x86_64_aesni",
|
||||
"backend_default_serialization",
|
||||
"backend_fft",
|
||||
"backend_fft_serialization",
|
||||
"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 = [
|
||||
"backend_default",
|
||||
"backend_default_parallel",
|
||||
"backend_default_generator_aarch64_aes",
|
||||
"backend_default_serialization",
|
||||
"backend_fft",
|
||||
"backend_fft_serialization",
|
||||
]
|
||||
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"]
|
||||
rustdoc-args = ["--html-in-header", "katex-header.html"]
|
||||
|
||||
###########
|
||||
# #
|
||||
# Benches #
|
||||
# #
|
||||
###########
|
||||
|
||||
[[bench]]
|
||||
name = "boolean-bench"
|
||||
path = "benches/boolean/bench.rs"
|
||||
harness = false
|
||||
required-features = ["booleans", "internal-keycache"]
|
||||
|
||||
[[bench]]
|
||||
name = "shortint-bench"
|
||||
path = "benches/shortint/bench.rs"
|
||||
harness = false
|
||||
required-features = ["shortints", "internal-keycache"]
|
||||
32
tfhe/LICENSE
Normal file
32
tfhe/LICENSE
Normal 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.
|
||||
62
tfhe/benches/boolean/bench.rs
Normal file
62
tfhe/benches/boolean/bench.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
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;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
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(¶ms);
|
||||
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");
|
||||
}
|
||||
232
tfhe/benches/shortint/bench.rs
Normal file
232
tfhe/benches/shortint/bench.rs
Normal file
@@ -0,0 +1,232 @@
|
||||
use tfhe::shortint::parameters::*;
|
||||
use tfhe::shortint::{Ciphertext, Parameters, ServerKey};
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
use tfhe::shortint::keycache::KEY_CACHE;
|
||||
use rand::Rng;
|
||||
|
||||
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,);
|
||||
77
tfhe/docs/boolean/user/advanced_topics/generate_keys.md
Normal file
77
tfhe/docs/boolean/user/advanced_topics/generate_keys.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Key Generation
|
||||
|
||||
There are two ways to generate keys with `concrete_boolean`:
|
||||
- use one of the provided parameter sets;
|
||||
- create a custom parameter set.
|
||||
|
||||
## Provided Parameter Sets
|
||||
|
||||
To generate a pair of client/server keys using one of the default parameter sets, you can use
|
||||
the helper function `gen_keys`:
|
||||
```rust
|
||||
extern crate concrete_boolean;
|
||||
use concrete_boolean::gen_keys;
|
||||
|
||||
// We generate a set of client/server keys, using the default parameters:
|
||||
let (client_key, server_key) = gen_keys();
|
||||
```
|
||||
|
||||
It will use `DEFAULT_PARAMETERS` to generate the two keys.
|
||||
|
||||
To generate a pair of keys with a different parameter set, such as `TFHE_LIB_PARAMETERS`, you can
|
||||
use the following function:
|
||||
|
||||
```rust
|
||||
extern crate concrete_boolean;
|
||||
use concrete_boolean::client_key::ClientKey;
|
||||
use concrete_boolean::server_key::ServerKey;
|
||||
use concrete_boolean::parameters::TFHE_LIB_PARAMETERS;
|
||||
|
||||
// generate the client key set
|
||||
let cks = ClientKey::new(TFHE_LIB_PARAMETERS);
|
||||
|
||||
// generate the server key set
|
||||
let sks = ServerKey::new(&cks);
|
||||
```
|
||||
|
||||
|
||||
## Custom Parameter Sets
|
||||
|
||||
If you are a cryptographer and know enough about FHE to tune the cryptographic parameters
|
||||
yourself, `concrete-boolean` offers the possibility to use your own parameter set.
|
||||
|
||||
Note that as soon as you do not use the default parameters, it is up to
|
||||
you to ensure the **correctness** and **security** of your program.
|
||||
|
||||
To generate a set of client/server keys using different parameters, you should use the
|
||||
constructor.
|
||||
|
||||
|
||||
```rust
|
||||
extern crate concrete_boolean;
|
||||
extern crate concrete_commons;
|
||||
use concrete_boolean::parameters::BooleanParameters;
|
||||
use concrete_boolean::client_key::ClientKey;
|
||||
use concrete_boolean::server_key::ServerKey;
|
||||
use concrete_commons::parameters::*;
|
||||
use concrete_commons::dispersion::*;
|
||||
|
||||
// You can create your own set of parameters, at your own risks
|
||||
let parameters = unsafe{
|
||||
BooleanParameters::new_insecure(
|
||||
LweDimension(586),
|
||||
GlweDimension(2),
|
||||
PolynomialSize(512),
|
||||
StandardDev(0.00008976167396834998),
|
||||
StandardDev(0.00000002989040792967434),
|
||||
DecompositionBaseLog(8),
|
||||
DecompositionLevelCount(2),
|
||||
DecompositionBaseLog(2),
|
||||
DecompositionLevelCount(5),
|
||||
)
|
||||
};
|
||||
|
||||
// We generate the client key from the parameters:
|
||||
let client_key = ClientKey::new(parameters);
|
||||
let server_key = ServerKey::new(&client_key);
|
||||
```
|
||||
8
tfhe/docs/boolean/user/advanced_topics/index.rst
Normal file
8
tfhe/docs/boolean/user/advanced_topics/index.rst
Normal file
@@ -0,0 +1,8 @@
|
||||
Advanced Topics
|
||||
===============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
generate_keys.md
|
||||
save_load.md
|
||||
54
tfhe/docs/boolean/user/advanced_topics/save_load.md
Normal file
54
tfhe/docs/boolean/user/advanced_topics/save_load.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# 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, ignore
|
||||
extern crate concrete_boolean;
|
||||
extern crate bincode;
|
||||
use concrete_boolean::gen_keys;
|
||||
use concrete_boolean::server_key::ServerKey;
|
||||
use concrete_boolean::client_key::ClientKey;
|
||||
use std::fs::File;
|
||||
use std::io::{Write, Read};
|
||||
|
||||
// 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();
|
||||
|
||||
// We write the keys to files:
|
||||
let mut file = File::create("/tmp/server_key.bin")
|
||||
.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("/tmp/client_key.bin")
|
||||
.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("/tmp/server_key.bin")
|
||||
.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("/tmp/client_key.bin")
|
||||
.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");
|
||||
|
||||
// We check for equality:
|
||||
assert_eq!(loaded_server_key, server_key);
|
||||
assert_eq!(loaded_client_key, client_key);
|
||||
```
|
||||
29
tfhe/docs/boolean/user/error.md
Normal file
29
tfhe/docs/boolean/user/error.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Probability of Error
|
||||
|
||||
|
||||
The security of homomorphic encryption schemes is based on a hard problem, requiring to add noise
|
||||
to ciphertexts in order to meet a fixed security level.
|
||||
Homomorphic computations are therefore noisy by design.
|
||||
|
||||
A too big amount of noise might render an encrypted message no longer decryptable.
|
||||
Then, it is really important to caliber the choice of the parameters depending on the error
|
||||
probability they provide.
|
||||
|
||||
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.
|
||||
|
||||
We propose two sets of parameters, both achieving 128 bits of security, which offer the
|
||||
possibility to make the more convenient choice according to the use-case.
|
||||
|
||||
|
||||
| Parameter set | Time per binary gate | Error probability |
|
||||
|:-------------------:|:--------------------:|:-----------------:|
|
||||
| DEFAULT_PARAMETERS | 11.3 ms | $ 2^{-25} $ |
|
||||
| TFHE_LIB_PARAMETERS | 18.0 ms | $ 2^{-165} $ |
|
||||
|
||||
|
||||
The previous table gives the error probability for a boolean gate according to the parameter set.
|
||||
|
||||
The measured time for a boolean gate computation was obtained on a regular laptop with a 2,6 GHz
|
||||
6-core Intel Core i7 processor equipped with AVX2 and on a single thread.
|
||||
61
tfhe/docs/boolean/user/how_does_it_work.md
Normal file
61
tfhe/docs/boolean/user/how_does_it_work.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# How Does It Work?
|
||||
|
||||
The `concrete-boolean` crate is a cryptographic library for [Fully
|
||||
Homomorphic Encryption](https://en.wikipedia.org/wiki/Homomorphic_encryption) for boolean circuits. It relies on `concrete-core`, the core library of the Concrete ecosystem developed at **Zama**.
|
||||
|
||||
In a nutshell, homomorphic encryption is a cryptographic paradigm enabling to **compute on
|
||||
encrypted data**.
|
||||
|
||||
FHE should be able to evaluate any function over encrypted inputs. In the case of Concrete-Boolean the function being evaluated is represented as a boolean circuit.
|
||||
|
||||
The `concrete-core` crate contains each homomorphic operator needed to build boolean gate
|
||||
evaluation in a
|
||||
stable manner.
|
||||
Built on top of `concrete-core`, `concrete-boolean` can execute **boolean circuits of any
|
||||
length** in an encrypted
|
||||
way.
|
||||
|
||||
You can find the description of the algorithms in the [TFHE](https://doi.org/10.1007/s00145-019-09319-x) paper (also available as [ePrint 2018/421](https://ia.cr/2018/421)).
|
||||
|
||||
## How secure is it?
|
||||
|
||||
The cryptographic scheme used in the `concrete-core`, is 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 ensuring correct computation for a
|
||||
certain probability with the standard security of 128 bits:
|
||||
|
||||
+ `concrete_boolean::parameters::DEFAULT_PARAMETERS`
|
||||
+ `concrete_boolean::parameters::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:
|
||||
|
||||
```rust
|
||||
extern crate concrete_boolean;
|
||||
extern crate concrete_commons;
|
||||
use concrete_boolean::parameters::BooleanParameters;
|
||||
use concrete_commons::parameters::*;
|
||||
use concrete_commons::dispersion::*;
|
||||
|
||||
// WARNING: might be insecure and/or incorrect
|
||||
// You can create your own set of parameters
|
||||
let parameters = unsafe{
|
||||
BooleanParameters::new_insecure(
|
||||
LweDimension(586),
|
||||
GlweDimension(2),
|
||||
PolynomialSize(512),
|
||||
StandardDev(0.00008976167396834998),
|
||||
StandardDev(0.00000002989040792967434),
|
||||
DecompositionBaseLog(8),
|
||||
DecompositionLevelCount(2),
|
||||
DecompositionBaseLog(2),
|
||||
DecompositionLevelCount(5),
|
||||
)
|
||||
};
|
||||
```
|
||||
14
tfhe/docs/boolean/user/index.rst
Normal file
14
tfhe/docs/boolean/user/index.rst
Normal file
@@ -0,0 +1,14 @@
|
||||
User Guide
|
||||
==========
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
introduction.md
|
||||
how_does_it_work.md
|
||||
tutorial.md
|
||||
Tutorial: Concrete Boolean and Conway’s Game of Life <https://medium.com/zama-ai/concrete-boolean-and-conways-game-of-life-a-tutorial-f2bcfd614131>
|
||||
Tutorial: Encrypted search using FHE <https://medium.com/zama-ai/encrypted-search-using-fully-homomorphic-encryption-99cd163b94>
|
||||
error.md
|
||||
Advanced Topics <advanced_topics/index>
|
||||
44
tfhe/docs/boolean/user/introduction.md
Normal file
44
tfhe/docs/boolean/user/introduction.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Introduction
|
||||
|
||||
Welcome to the `concrete-boolean` guide!
|
||||
|
||||
This library makes it possible to execute **boolean gates over encrypted bits**.
|
||||
It allows one 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.
|
||||
She can then decrypt it with her `secret key`.
|
||||
|
||||
The library is pretty simple to use, and can evaluate **homomorphic boolean circuits of arbitrary
|
||||
length**.
|
||||
|
||||
Here is a quick example of how the library can be used:
|
||||
|
||||
```rust
|
||||
extern crate concrete_boolean;
|
||||
use concrete_boolean::prelude::*;
|
||||
use concrete_boolean::gen_keys;
|
||||
|
||||
// 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);
|
||||
```
|
||||
|
||||
As simple as that! If you are hooked, jump to the next section!
|
||||
207
tfhe/docs/boolean/user/tutorial.md
Normal file
207
tfhe/docs/boolean/user/tutorial.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# Tutorial
|
||||
|
||||
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`.
|
||||
|
||||
## 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
|
||||
extern crate concrete_boolean;
|
||||
use concrete_boolean::gen_keys;
|
||||
use concrete_boolean::client_key::ClientKey;
|
||||
use concrete_boolean::server_key::ServerKey;
|
||||
|
||||
// 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, ignore
|
||||
extern crate concrete_boolean;
|
||||
extern crate bincode;
|
||||
use concrete_boolean::gen_keys;
|
||||
use concrete_boolean::server_key::ServerKey;
|
||||
use std::fs::File;
|
||||
use std::io::{Write, Read};
|
||||
|
||||
//---------------------------- 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();
|
||||
|
||||
// We write the server key to a file:
|
||||
let mut file = File::create("/tmp/server_key.bin")
|
||||
.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("/tmp/server_key.bin")
|
||||
.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");
|
||||
```
|
||||
|
||||
## 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, ignore
|
||||
# extern crate concrete_boolean;
|
||||
# extern crate bincode;
|
||||
# use concrete_boolean::gen_keys;
|
||||
# // 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
|
||||
// ...
|
||||
```
|
||||
|
||||
## 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, ignore
|
||||
# extern crate concrete_boolean;
|
||||
# extern crate bincode;
|
||||
# use concrete_boolean::gen_keys;
|
||||
# use concrete_boolean::prelude::*;
|
||||
# use concrete_boolean::ciphertext::Ciphertext;
|
||||
# // 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, ignore
|
||||
# extern crate concrete_boolean;
|
||||
# extern crate bincode;
|
||||
# use concrete_boolean::gen_keys;
|
||||
# use concrete_boolean::ciphertext::Ciphertext;
|
||||
# // 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);
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
20
tfhe/docs/shortint/SUMMARY.md
Normal file
20
tfhe/docs/shortint/SUMMARY.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Concrete-Shortint User Guide
|
||||
|
||||
[Introduction](introduction.md)
|
||||
|
||||
# Getting Started
|
||||
|
||||
[Installation](getting_started/installation.md)
|
||||
|
||||
[Writing Your First Circuit](getting_started/first_circuit.md)
|
||||
|
||||
[Types Of Operations](getting_started/operation_types.md)
|
||||
|
||||
[List of Operations](getting_started/operation_list.md)
|
||||
|
||||
[Cryptographic Parameters](getting_started/parameters.md)
|
||||
|
||||
|
||||
# How to
|
||||
|
||||
[Serialization / Deserialization](tutorials/serialization.md)
|
||||
@@ -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 |
83
tfhe/docs/shortint/getting_started/first_circuit.md
Normal file
83
tfhe/docs/shortint/getting_started/first_circuit.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Writing Your First Circuit
|
||||
|
||||
# 1. Key Generation
|
||||
|
||||
`concrete-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 concrete_shortint::{gen_keys, Parameters};
|
||||
|
||||
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 we have our keys we can encrypt values:
|
||||
|
||||
```rust
|
||||
use concrete_shortint::{gen_keys, Parameters};
|
||||
|
||||
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);
|
||||
}
|
||||
```
|
||||
|
||||
# 3. Computing and decrypting
|
||||
|
||||
With our `server_key`, and encrypted values, we can now do an addition
|
||||
and then decrypt the result.
|
||||
|
||||
```rust
|
||||
use concrete_shortint::{gen_keys, Parameters};
|
||||
|
||||
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);
|
||||
}
|
||||
```
|
||||
46
tfhe/docs/shortint/getting_started/installation.md
Normal file
46
tfhe/docs/shortint/getting_started/installation.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Installation
|
||||
|
||||
## Cargo.toml
|
||||
|
||||
To use `concrete-shortint`, you will need to add it to the list of dependencies
|
||||
of your project, by updating your `Cargo.toml` file.
|
||||
|
||||
```toml
|
||||
concrete-shortint = "0.1.0"
|
||||
```
|
||||
|
||||
### Supported platforms
|
||||
|
||||
As `concrete-shortint` relies on `concrete-core`, `concrete-shortint` is only supported on `x86_64 Linux`
|
||||
and `x86_64 macOS`.
|
||||
|
||||
Windows user can use `concrete-shortint` through the `WSL`.
|
||||
|
||||
macOS users which have the newer M1 (`arm64`) devices can use `concrete-shortint` by cross-compiling to
|
||||
`x86_64` and run their program with Rosetta.
|
||||
|
||||
First install the needed Rust toolchain:
|
||||
|
||||
```console
|
||||
# Install the macOS x86_64 toolchain (you only need to do this once)
|
||||
rustup toolchain install --force-non-host stable-x86_64-apple-darwin
|
||||
```
|
||||
|
||||
Then you can either:
|
||||
|
||||
- Manually specify the toolchain to use in each of the cargo commands:
|
||||
|
||||
For example:
|
||||
|
||||
```console
|
||||
cargo +stable-x86_64-apple-darwin build
|
||||
cargo +stable-x86_64-apple-darwin test
|
||||
```
|
||||
|
||||
- Or override the toolchain to use for the current project:
|
||||
|
||||
```console
|
||||
rustup override set stable-x86_64-apple-darwin
|
||||
# cargo will use the `stable-x86_64-apple-darwin` toolchain.
|
||||
cargo build
|
||||
```
|
||||
30
tfhe/docs/shortint/getting_started/operation_list.md
Normal file
30
tfhe/docs/shortint/getting_started/operation_list.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# List of available operations
|
||||
|
||||
`concrete-shortint` comes with a set of already implemented functions:
|
||||
|
||||
|
||||
- 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` (*)
|
||||
|
||||
|
||||
{% 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 %}
|
||||
|
||||
|
||||
52
tfhe/docs/shortint/getting_started/operation_types.md
Normal file
52
tfhe/docs/shortint/getting_started/operation_types.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# How Shortint are represented
|
||||
|
||||
In `concrete-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**.
|
||||
|
||||

|
||||
|
||||
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 `concrete-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.
|
||||
- `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's space capacity.
|
||||
29
tfhe/docs/shortint/getting_started/parameters.md
Normal file
29
tfhe/docs/shortint/getting_started/parameters.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Use of parameters
|
||||
|
||||
`concrete-shortint` comes with sets of parameters that permit to use the functionalities of the library
|
||||
securely and efficiently. The user is allowed to choose which set of parameters to use when creating the pair
|
||||
of keys.
|
||||
|
||||
|
||||
```rust
|
||||
use concrete_shortint::{gen_keys, Parameters};
|
||||
|
||||
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);
|
||||
}
|
||||
```
|
||||
|
||||
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`.
|
||||
27
tfhe/docs/shortint/how_to/pbs.md
Normal file
27
tfhe/docs/shortint/how_to/pbs.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# The programmable bootstrapping
|
||||
|
||||
In `concrete-shortint`, the user can evaluate any function on an encrypted ciphertext. To do so the user must first
|
||||
create a look-up table through the `generate_accumulator` method and use its output together with the ciphertext
|
||||
as parameters for the `programmable bootstrapping`.
|
||||
|
||||
```rust
|
||||
use concrete_shortint::gen_keys;
|
||||
use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
|
||||
|
||||
fn main() {
|
||||
// Generate the client key and the server key:
|
||||
let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
|
||||
|
||||
let msg: u64 = 1;
|
||||
let ct = cks.encrypt(msg);
|
||||
let modulus = cks.parameters.message_modulus.0 as u64;
|
||||
|
||||
// Generate the accumulator for the function f: x -> x^3 mod 2^2
|
||||
let acc = sks.generate_accumulator(|x| (x * x * x) % modulus);
|
||||
let ct_res = sks.programmable_bootstrap_keyswitch(&ct, &acc);
|
||||
|
||||
let dec = cks.decrypt(&ct_res);
|
||||
// 3^3 mod 4 = 3
|
||||
assert_eq!(dec, (msg * msg * msg) % modulus);
|
||||
}
|
||||
```
|
||||
9
tfhe/docs/shortint/introduction.md
Normal file
9
tfhe/docs/shortint/introduction.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Concrete-shortint
|
||||
|
||||
## Introduction
|
||||
|
||||
`concrete-shortint` is a Rust library based on `concrete-core` with the goal
|
||||
of providing an abstraction layer that provides "short integer" types meaning _unsigned_ integers with
|
||||
currently less than 8 bits and that fits on a single LWE ciphertext.
|
||||
|
||||
The intended target audience for this library is people who are somewhat familiar with cryptography.
|
||||
115
tfhe/docs/shortint/tutorials/circuit_evaluation.md
Normal file
115
tfhe/docs/shortint/tutorials/circuit_evaluation.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Circuit evaluation
|
||||
|
||||
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 concrete_shortint::gen_keys;
|
||||
use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
|
||||
|
||||
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 concrete_shortint::gen_keys;
|
||||
use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
|
||||
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 concrete_shortint::gen_keys;
|
||||
use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
|
||||
|
||||
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);
|
||||
}
|
||||
```
|
||||
75
tfhe/docs/shortint/tutorials/serialization.md
Normal file
75
tfhe/docs/shortint/tutorials/serialization.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# 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.
|
||||
concrete-shortint uses the serde framework, serde's Serialize and Deserialize are implemented on concrete-shortint's
|
||||
types.
|
||||
|
||||
To be able to serialize our data, we need to pick a [data format], for our use case,
|
||||
[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 concrete_shortint::{gen_keys, Parameters};
|
||||
use concrete_shortint::{ServerKey, Ciphertext};
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
```
|
||||
|
||||
[serde]: https://crates.io/crates/serde
|
||||
[data format]: https://serde.rs/#data-formats
|
||||
[bincode]: https://crates.io/crates/bincode
|
||||
24
tfhe/examples/generates_test_keys.rs
Normal file
24
tfhe/examples/generates_test_keys.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use concrete_utils::keycache::{FileStorage, NamedParam, PersistentStorage};
|
||||
|
||||
use tfhe::shortint::gen_keys;
|
||||
use tfhe::shortint::parameters::ALL_PARAMETER_VEC;
|
||||
|
||||
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,
|
||||
ALL_PARAMETER_VEC.len(),
|
||||
params.name()
|
||||
);
|
||||
let client_server_keys = gen_keys(params);
|
||||
file_storage.store(params, &client_server_keys);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
client_server_keys()
|
||||
}
|
||||
60
tfhe/src/boolean/ciphertext/mod.rs
Normal file
60
tfhe/src/boolean/ciphertext/mod.rs
Normal 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),
|
||||
})
|
||||
}
|
||||
}
|
||||
178
tfhe/src/boolean/client_key/mod.rs
Normal file
178
tfhe/src/boolean/client_key/mod.rs
Normal file
@@ -0,0 +1,178 @@
|
||||
//! 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
|
||||
/// 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);
|
||||
/// ```
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
316
tfhe/src/boolean/engine/bootstrapping/cpu.rs
Normal file
316
tfhe/src/boolean/engine/bootstrapping/cpu.rs
Normal file
@@ -0,0 +1,316 @@
|
||||
use crate::boolean::ciphertext::Ciphertext;
|
||||
use crate::boolean::{ClientKey, PLAINTEXT_TRUE};
|
||||
use crate::core_crypto::prelude::*;
|
||||
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(Box::new(UnixSeeder::new(0)))
|
||||
.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,
|
||||
})
|
||||
}
|
||||
}
|
||||
239
tfhe/src/boolean/engine/bootstrapping/cuda.rs
Normal file
239
tfhe/src/boolean/engine/bootstrapping/cuda.rs
Normal file
@@ -0,0 +1,239 @@
|
||||
use super::{BooleanServerKey, Bootstrapper, CpuBootstrapKey};
|
||||
use crate::boolean::PLAINTEXT_TRUE;
|
||||
use crate::core_crypto::prelude::*;
|
||||
|
||||
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(Box::new(UnixSeeder::new(0))).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))
|
||||
}
|
||||
}
|
||||
48
tfhe/src/boolean/engine/bootstrapping/mod.rs
Normal file
48
tfhe/src/boolean/engine/bootstrapping/mod.rs
Normal 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>>;
|
||||
}
|
||||
741
tfhe/src/boolean/engine/mod.rs
Normal file
741
tfhe/src/boolean/engine/mod.rs
Normal file
@@ -0,0 +1,741 @@
|
||||
use crate::boolean::ciphertext::Ciphertext;
|
||||
use crate::boolean::parameters::BooleanParameters;
|
||||
use crate::boolean::{ClientKey, 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;
|
||||
|
||||
#[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;
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
|
||||
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 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 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn new_seeder() -> Box<dyn Seeder> {
|
||||
let seeder: Box<dyn Seeder>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
if RdseedSeeder::is_available() {
|
||||
seeder = Box::new(RdseedSeeder);
|
||||
} else {
|
||||
seeder = Box::new(UnixSeeder::new(0));
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
{
|
||||
seeder = Box::new(UnixSeeder::new(0));
|
||||
}
|
||||
|
||||
seeder
|
||||
}
|
||||
|
||||
impl<B> BooleanEngine<B>
|
||||
where
|
||||
B: Bootstrapper,
|
||||
{
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn new() -> Self {
|
||||
let engine =
|
||||
DefaultEngine::new(new_seeder()).expect("Unexpectedly failed to create a core engine");
|
||||
|
||||
Self {
|
||||
engine,
|
||||
bootstrapper: Default::default(),
|
||||
}
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn new(seeder: Box<dyn Seeder>) -> Self {
|
||||
|
||||
}
|
||||
|
||||
/// 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> 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)
|
||||
}
|
||||
}
|
||||
134
tfhe/src/boolean/mod.rs
Normal file
134
tfhe/src/boolean/mod.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
//! Welcome the the `concrete-boolean` 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::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 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)
|
||||
}
|
||||
111
tfhe/src/boolean/parameters/mod.rs
Normal file
111
tfhe/src/boolean/parameters/mod.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
//! 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.
|
||||
// TODO: speak about the lattice estimator and give the explicit used commit for the parameters
|
||||
|
||||
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_insecure(
|
||||
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^{-25}$. 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(586),
|
||||
glwe_dimension: GlweDimension(2),
|
||||
polynomial_size: PolynomialSize(512),
|
||||
lwe_modular_std_dev: StandardDev(0.000_092_511_997_467_675_6), // 2^{-13.4}
|
||||
glwe_modular_std_dev: StandardDev(0.000_000_034_233_878_701_836_9), // 2^{-24.8}
|
||||
pbs_base_log: DecompositionBaseLog(8),
|
||||
pbs_level: DecompositionLevelCount(2),
|
||||
ks_base_log: DecompositionBaseLog(2),
|
||||
ks_level: DecompositionLevelCount(5),
|
||||
};
|
||||
|
||||
/// Parameter set used in [TFHE library](https://tfhe.github.io/tfhe/) for 128-bits of security.
|
||||
///
|
||||
/// Details about this set are provided
|
||||
/// [here](https://github.com/tfhe/tfhe/blob/master/src/libtfhe/tfhe_gate_bootstrapping.cpp).
|
||||
/// The secret keys generated with this parameter set are uniform binary.
|
||||
/// This parameter set ensures a probability of error is upper-bounded by $2^{-165}$.
|
||||
pub const TFHE_LIB_PARAMETERS: BooleanParameters = BooleanParameters {
|
||||
lwe_dimension: LweDimension(630),
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(1024),
|
||||
lwe_modular_std_dev: StandardDev(0.000_043_158_372_875_155_5), // 2^{-14.5}
|
||||
glwe_modular_std_dev: StandardDev(0.000_000_034_233_878_701_836_9), // 2^{-24.8}
|
||||
pbs_base_log: DecompositionBaseLog(7),
|
||||
pbs_level: DecompositionLevelCount(3),
|
||||
ks_base_log: DecompositionBaseLog(2),
|
||||
ks_level: DecompositionLevelCount(8),
|
||||
};
|
||||
5
tfhe/src/boolean/prelude.rs
Normal file
5
tfhe/src/boolean/prelude.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
#![doc(hidden)]
|
||||
pub use super::client_key::ClientKey;
|
||||
pub use super::gen_keys;
|
||||
pub use super::server_key::{BinaryBooleanGates, ServerKey};
|
||||
// pub use super::engine::BinaryGatesEngine;
|
||||
201
tfhe/src/boolean/server_key/mod.rs
Normal file
201
tfhe/src/boolean/server_key/mod.rs
Normal file
@@ -0,0 +1,201 @@
|
||||
//! 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::{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;
|
||||
}
|
||||
|
||||
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 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 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))
|
||||
}
|
||||
}
|
||||
640
tfhe/src/boolean/server_key/tests.rs
Normal file
640
tfhe/src/boolean/server_key/tests.rs
Normal file
@@ -0,0 +1,640 @@
|
||||
use crate::boolean::ciphertext::Ciphertext;
|
||||
use crate::boolean::client_key::ClientKey;
|
||||
use crate::boolean::parameters::BooleanParameters;
|
||||
use crate::boolean::server_key::{BinaryBooleanGates, 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(¶meters);
|
||||
|
||||
// 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(¶meters);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mux_gate(parameters: BooleanParameters) {
|
||||
// generate the client key set
|
||||
let cks = ClientKey::new(¶meters);
|
||||
|
||||
// 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(¶meters);
|
||||
|
||||
// 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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_nor_gate(parameters: BooleanParameters) {
|
||||
// generate the client key set
|
||||
let cks = ClientKey::new(¶meters);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_not_gate(parameters: BooleanParameters) {
|
||||
// generate the client key set
|
||||
let cks = ClientKey::new(¶meters);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_or_gate(parameters: BooleanParameters) {
|
||||
// generate the client key set
|
||||
let cks = ClientKey::new(¶meters);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xnor_gate(parameters: BooleanParameters) {
|
||||
// generate the client key set
|
||||
let cks = ClientKey::new(¶meters);
|
||||
|
||||
// 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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xor_gate(parameters: BooleanParameters) {
|
||||
// generate the client key set
|
||||
let cks = ClientKey::new(¶meters);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(¶meters);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
use crate::core_crypto::backends::cuda::engines::CudaError;
|
||||
use crate::core_crypto::backends::cuda::implementation::engines::{
|
||||
check_base_log, check_glwe_dim, AmortizedCudaEngine,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::{
|
||||
CudaFourierLweBootstrapKey32, CudaFourierLweBootstrapKey64, CudaGlweCiphertextVector32,
|
||||
CudaGlweCiphertextVector64, CudaLweCiphertextVector32, CudaLweCiphertextVector64,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::private::crypto::bootstrap::execute_lwe_ciphertext_vector_amortized_bootstrap_on_gpu;
|
||||
use crate::core_crypto::specification::engines::{
|
||||
LweCiphertextVectorDiscardingBootstrapEngine, LweCiphertextVectorDiscardingBootstrapError,
|
||||
};
|
||||
use crate::core_crypto::specification::entities::LweBootstrapKeyEntity;
|
||||
|
||||
/// # Description
|
||||
/// A discard bootstrap on a vector of input ciphertext vectors with 32 bits of precision.
|
||||
/// The bootstraps are all using one cuda bootstrap key in the Fourier domain, and as
|
||||
/// many lookup tables as there are input LWE ciphertexts.
|
||||
impl
|
||||
LweCiphertextVectorDiscardingBootstrapEngine<
|
||||
CudaFourierLweBootstrapKey32,
|
||||
CudaGlweCiphertextVector32,
|
||||
CudaLweCiphertextVector32,
|
||||
CudaLweCiphertextVector32,
|
||||
> for AmortizedCudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// DecompositionBaseLog, DecompositionLevelCount, GlweDimension, PolynomialSize,
|
||||
/// };
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let (lwe_dim, lwe_dim_output, glwe_dim, poly_size) = (
|
||||
/// LweDimension(130),
|
||||
/// LweDimension(512),
|
||||
/// GlweDimension(1),
|
||||
/// PolynomialSize(512),
|
||||
/// );
|
||||
/// let log_degree = f64::log2(poly_size.0 as f64) as i32;
|
||||
/// let val: u32 = ((poly_size.0 as f64 - (10. * f64::sqrt((lwe_dim.0 as f64) / 16.0)))
|
||||
/// * 2_f64.powi(32 - log_degree - 1)) as u32;
|
||||
/// let input = vec![val; 3];
|
||||
/// let noise = Variance(2_f64.powf(-29.));
|
||||
/// let (dec_lc, dec_bl) = (DecompositionLevelCount(3), DecompositionBaseLog(7));
|
||||
/// // An identity function is applied during the bootstrap
|
||||
/// let mut lut = vec![0u32; poly_size.0 * 3];
|
||||
/// for i in 0..poly_size.0 {
|
||||
/// let l = (i as f64 * 2_f64.powi(32 - log_degree - 1)) as u32;
|
||||
/// lut[i] = l;
|
||||
/// lut[i + poly_size.0] = l;
|
||||
/// lut[i + 2 * poly_size.0] = l;
|
||||
/// }
|
||||
///
|
||||
/// // 1. default engine
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// // create a vector of LWE ciphertexts
|
||||
/// let h_input_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dim)?;
|
||||
/// let h_input_plaintext_vector: PlaintextVector32 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_input_ciphertext_vector: LweCiphertextVector32 = default_engine
|
||||
/// .encrypt_lwe_ciphertext_vector(&h_input_key, &h_input_plaintext_vector, noise)?;
|
||||
/// // create a vector of GLWE ciphertexts containing the encryptions of the LUTs
|
||||
/// let h_lut_plaintext_vector = default_engine.create_plaintext_vector_from(&lut)?;
|
||||
/// let h_lut_key: GlweSecretKey32 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dim, poly_size)?;
|
||||
/// let h_lut_vector = default_engine.encrypt_glwe_ciphertext_vector(
|
||||
/// &h_lut_key,
|
||||
/// &h_lut_plaintext_vector,
|
||||
/// noise,
|
||||
/// )?;
|
||||
/// // create a BSK
|
||||
/// let h_bootstrap_key: LweBootstrapKey32 = default_engine.generate_new_lwe_bootstrap_key(
|
||||
/// &h_input_key,
|
||||
/// &h_lut_key,
|
||||
/// dec_bl,
|
||||
/// dec_lc,
|
||||
/// noise,
|
||||
/// )?;
|
||||
/// // initialize an output LWE ciphertext vector
|
||||
/// let h_dummy_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dim_output)?;
|
||||
///
|
||||
/// // 2. cuda engine
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let mut cuda_amortized_engine = AmortizedCudaEngine::new(())?;
|
||||
/// // convert input to GPU (split over the GPUs)
|
||||
/// let d_input_ciphertext_vector: CudaLweCiphertextVector32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_input_ciphertext_vector)?;
|
||||
/// // convert accumulators to GPU
|
||||
/// let d_input_lut_vector: CudaGlweCiphertextVector32 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&h_lut_vector)?;
|
||||
/// // convert BSK to GPU (and from Standard to Fourier representations)
|
||||
/// let d_fourier_bsk: CudaFourierLweBootstrapKey32 =
|
||||
/// cuda_engine.convert_lwe_bootstrap_key(&h_bootstrap_key)?;
|
||||
/// // launch bootstrap on GPU
|
||||
/// let h_zero_output_ciphertext_vector: LweCiphertextVector32 = default_engine
|
||||
/// .zero_encrypt_lwe_ciphertext_vector(&h_dummy_key, noise, LweCiphertextCount(3))?;
|
||||
/// let mut d_output_ciphertext_vector: CudaLweCiphertextVector32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_zero_output_ciphertext_vector)?;
|
||||
/// cuda_amortized_engine.discard_bootstrap_lwe_ciphertext_vector(
|
||||
/// &mut d_output_ciphertext_vector,
|
||||
/// &d_input_ciphertext_vector,
|
||||
/// &d_input_lut_vector,
|
||||
/// &d_fourier_bsk,
|
||||
/// )?;
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_bootstrap_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertextVector32,
|
||||
input: &CudaLweCiphertextVector32,
|
||||
acc: &CudaGlweCiphertextVector32,
|
||||
bsk: &CudaFourierLweBootstrapKey32,
|
||||
) -> Result<(), LweCiphertextVectorDiscardingBootstrapError<CudaError>> {
|
||||
LweCiphertextVectorDiscardingBootstrapError::perform_generic_checks(
|
||||
output, input, acc, bsk,
|
||||
)?;
|
||||
let poly_size = bsk.polynomial_size().0;
|
||||
check_poly_size!(poly_size);
|
||||
let glwe_dim = bsk.glwe_dimension();
|
||||
check_glwe_dim!(glwe_dim);
|
||||
let base_log = bsk.decomposition_base_log();
|
||||
check_base_log!(base_log);
|
||||
unsafe { self.discard_bootstrap_lwe_ciphertext_vector_unchecked(output, input, acc, bsk) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_bootstrap_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertextVector32,
|
||||
input: &CudaLweCiphertextVector32,
|
||||
acc: &CudaGlweCiphertextVector32,
|
||||
bsk: &CudaFourierLweBootstrapKey32,
|
||||
) {
|
||||
execute_lwe_ciphertext_vector_amortized_bootstrap_on_gpu::<u32>(
|
||||
self.get_cuda_streams(),
|
||||
&mut output.0,
|
||||
&input.0,
|
||||
&acc.0,
|
||||
&bsk.0,
|
||||
self.get_number_of_gpus(),
|
||||
self.get_cuda_shared_memory(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// A discard bootstrap on a vector of input ciphertext vectors with 64 bits of precision.
|
||||
/// The bootstraps are all using one cuda bootstrap key in the Fourier domain, and as
|
||||
/// many lookup tables as there are input LWE ciphertexts.
|
||||
impl
|
||||
LweCiphertextVectorDiscardingBootstrapEngine<
|
||||
CudaFourierLweBootstrapKey64,
|
||||
CudaGlweCiphertextVector64,
|
||||
CudaLweCiphertextVector64,
|
||||
CudaLweCiphertextVector64,
|
||||
> for AmortizedCudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// DecompositionBaseLog, DecompositionLevelCount, GlweDimension, PolynomialSize,
|
||||
/// };
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let (lwe_dim, lwe_dim_output, glwe_dim, poly_size) = (
|
||||
/// LweDimension(130),
|
||||
/// LweDimension(512),
|
||||
/// GlweDimension(1),
|
||||
/// PolynomialSize(512),
|
||||
/// );
|
||||
/// let log_degree = f64::log2(poly_size.0 as f64) as i32;
|
||||
/// let val: u64 = ((poly_size.0 as f64 - (10. * f64::sqrt((lwe_dim.0 as f64) / 16.0)))
|
||||
/// * 2_f64.powi(64 - log_degree - 1)) as u64;
|
||||
/// let input = vec![val; 3];
|
||||
/// let noise = Variance(2_f64.powf(-29.));
|
||||
/// let (dec_lc, dec_bl) = (DecompositionLevelCount(3), DecompositionBaseLog(7));
|
||||
/// // An identity function is applied during the bootstrap
|
||||
/// let mut lut = vec![0u64; poly_size.0 * 3];
|
||||
/// for i in 0..poly_size.0 {
|
||||
/// let l = (i as f64 * 2_f64.powi(64 - log_degree - 1)) as u64;
|
||||
/// lut[i] = l;
|
||||
/// lut[i + poly_size.0] = l;
|
||||
/// lut[i + 2 * poly_size.0] = l;
|
||||
/// }
|
||||
///
|
||||
/// // 1. default engine
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// // create a vector of LWE ciphertexts
|
||||
/// let h_input_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dim)?;
|
||||
/// let h_input_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_input_ciphertext_vector: LweCiphertextVector64 = default_engine
|
||||
/// .encrypt_lwe_ciphertext_vector(&h_input_key, &h_input_plaintext_vector, noise)?;
|
||||
/// // create a vector of GLWE ciphertexts containing the encryptions of the LUTs
|
||||
/// let h_lut_plaintext_vector = default_engine.create_plaintext_vector_from(&lut)?;
|
||||
/// let h_lut_key: GlweSecretKey64 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dim, poly_size)?;
|
||||
/// let h_lut_vector = default_engine.encrypt_glwe_ciphertext_vector(
|
||||
/// &h_lut_key,
|
||||
/// &h_lut_plaintext_vector,
|
||||
/// noise,
|
||||
/// )?;
|
||||
/// // create a BSK
|
||||
/// let h_bootstrap_key: LweBootstrapKey64 = default_engine.generate_new_lwe_bootstrap_key(
|
||||
/// &h_input_key,
|
||||
/// &h_lut_key,
|
||||
/// dec_bl,
|
||||
/// dec_lc,
|
||||
/// noise,
|
||||
/// )?;
|
||||
/// // initialize an output LWE ciphertext vector
|
||||
/// let h_dummy_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dim_output)?;
|
||||
///
|
||||
/// // 2. cuda engine
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let mut cuda_amortized_engine = AmortizedCudaEngine::new(())?;
|
||||
/// // convert input to GPU (split over the GPUs)
|
||||
/// let d_input_ciphertext_vector: CudaLweCiphertextVector64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_input_ciphertext_vector)?;
|
||||
/// // convert accumulators to GPU
|
||||
/// let d_input_lut_vector: CudaGlweCiphertextVector64 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&h_lut_vector)?;
|
||||
/// // convert BSK to GPU (and from Standard to Fourier representations)
|
||||
/// let d_fourier_bsk: CudaFourierLweBootstrapKey64 =
|
||||
/// cuda_engine.convert_lwe_bootstrap_key(&h_bootstrap_key)?;
|
||||
/// // launch bootstrap on GPU
|
||||
/// let h_zero_output_ciphertext_vector: LweCiphertextVector64 = default_engine
|
||||
/// .zero_encrypt_lwe_ciphertext_vector(&h_dummy_key, noise, LweCiphertextCount(3))?;
|
||||
/// let mut d_output_ciphertext_vector: CudaLweCiphertextVector64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_zero_output_ciphertext_vector)?;
|
||||
/// cuda_amortized_engine.discard_bootstrap_lwe_ciphertext_vector(
|
||||
/// &mut d_output_ciphertext_vector,
|
||||
/// &d_input_ciphertext_vector,
|
||||
/// &d_input_lut_vector,
|
||||
/// &d_fourier_bsk,
|
||||
/// )?;
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_bootstrap_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertextVector64,
|
||||
input: &CudaLweCiphertextVector64,
|
||||
acc: &CudaGlweCiphertextVector64,
|
||||
bsk: &CudaFourierLweBootstrapKey64,
|
||||
) -> Result<(), LweCiphertextVectorDiscardingBootstrapError<CudaError>> {
|
||||
LweCiphertextVectorDiscardingBootstrapError::perform_generic_checks(
|
||||
output, input, acc, bsk,
|
||||
)?;
|
||||
let poly_size = bsk.polynomial_size().0;
|
||||
check_poly_size!(poly_size);
|
||||
let glwe_dim = bsk.glwe_dimension();
|
||||
check_glwe_dim!(glwe_dim);
|
||||
let base_log = bsk.decomposition_base_log();
|
||||
check_base_log!(base_log);
|
||||
unsafe { self.discard_bootstrap_lwe_ciphertext_vector_unchecked(output, input, acc, bsk) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_bootstrap_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertextVector64,
|
||||
input: &CudaLweCiphertextVector64,
|
||||
acc: &CudaGlweCiphertextVector64,
|
||||
bsk: &CudaFourierLweBootstrapKey64,
|
||||
) {
|
||||
execute_lwe_ciphertext_vector_amortized_bootstrap_on_gpu::<u64>(
|
||||
self.get_cuda_streams(),
|
||||
&mut output.0,
|
||||
&input.0,
|
||||
&acc.0,
|
||||
&bsk.0,
|
||||
self.get_number_of_gpus(),
|
||||
self.get_cuda_shared_memory(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
use crate::core_crypto::backends::cuda::engines::CudaError;
|
||||
use crate::core_crypto::backends::cuda::private::device::{
|
||||
CudaStream, GpuIndex, NumberOfGpus,
|
||||
};
|
||||
use crate::core_crypto::prelude::sealed::AbstractEngineSeal;
|
||||
use crate::core_crypto::prelude::{AbstractEngine, SharedMemoryAmount};
|
||||
use concrete_cuda::cuda_bind::cuda_get_number_of_gpus;
|
||||
|
||||
/// A variant of CudaEngine exposed by the cuda backend.
|
||||
///
|
||||
/// This engine implements an amortized version of bootstrap on the GPU.
|
||||
/// It is dedicated to the execution of bootstraps over larger amounts of
|
||||
/// input ciphertexts than the CudaEngine's bootstrap implementation.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AmortizedCudaEngine {
|
||||
streams: Vec<CudaStream>,
|
||||
max_shared_memory: usize,
|
||||
}
|
||||
|
||||
impl AbstractEngineSeal for AmortizedCudaEngine {}
|
||||
|
||||
impl AbstractEngine for AmortizedCudaEngine {
|
||||
type EngineError = CudaError;
|
||||
|
||||
type Parameters = ();
|
||||
|
||||
fn new(_parameters: Self::Parameters) -> Result<Self, Self::EngineError> {
|
||||
let number_of_gpus = unsafe { cuda_get_number_of_gpus() as usize };
|
||||
if number_of_gpus == 0 {
|
||||
Err(CudaError::DeviceNotFound)
|
||||
} else {
|
||||
let mut streams: Vec<CudaStream> = Vec::new();
|
||||
for gpu_index in 0..number_of_gpus {
|
||||
streams.push(CudaStream::new(GpuIndex(gpu_index))?);
|
||||
}
|
||||
let max_shared_memory = streams[0].get_max_shared_memory()?;
|
||||
|
||||
Ok(AmortizedCudaEngine {
|
||||
streams,
|
||||
max_shared_memory: max_shared_memory as usize,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AmortizedCudaEngine {
|
||||
/// Get the number of available GPUs from the engine
|
||||
pub fn get_number_of_gpus(&self) -> NumberOfGpus {
|
||||
NumberOfGpus(self.streams.len())
|
||||
}
|
||||
/// Get the Cuda streams from the engine
|
||||
pub fn get_cuda_streams(&self) -> &Vec<CudaStream> {
|
||||
&self.streams
|
||||
}
|
||||
/// Get the size of the shared memory (on device 0)
|
||||
pub fn get_cuda_shared_memory(&self) -> SharedMemoryAmount {
|
||||
SharedMemoryAmount(self.max_shared_memory)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! check_poly_size {
|
||||
($poly_size: ident) => {
|
||||
if $poly_size != 512
|
||||
&& $poly_size != 1024
|
||||
&& $poly_size != 2048
|
||||
&& $poly_size != 4096
|
||||
&& $poly_size != 8192
|
||||
{
|
||||
return Err(CudaError::PolynomialSizeNotSupported.into());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mod lwe_ciphertext_vector_discarding_bootstrap;
|
||||
@@ -0,0 +1,340 @@
|
||||
use crate::core_crypto::backends::cuda::engines::{CudaEngine, CudaError};
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::{
|
||||
CudaGgswCiphertext32, CudaGgswCiphertext64,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::private::crypto::ggsw::ciphertext::CudaGgswCiphertext;
|
||||
use crate::core_crypto::commons::crypto::ggsw::StandardGgswCiphertext;
|
||||
use crate::core_crypto::commons::math::tensor::{AsRefSlice, AsRefTensor};
|
||||
use crate::core_crypto::prelude::{GgswCiphertext32, GgswCiphertext64};
|
||||
use crate::core_crypto::specification::engines::{
|
||||
GgswCiphertextConversionEngine, GgswCiphertextConversionError,
|
||||
};
|
||||
use crate::core_crypto::specification::entities::GgswCiphertextEntity;
|
||||
|
||||
impl From<CudaError> for GgswCiphertextConversionError<CudaError> {
|
||||
fn from(err: CudaError) -> Self {
|
||||
Self::Engine(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GGSW ciphertext with 32 bits of precision from CPU to GPU 0.
|
||||
/// Only this conversion is necessary to run the WopPBS on the GPU.
|
||||
impl GgswCiphertextConversionEngine<GgswCiphertext32, CudaGgswCiphertext32> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// use std::task::Poll;
|
||||
/// let glwe_dimension = GlweDimension(1);
|
||||
/// let polynomial_size = PolynomialSize(8);
|
||||
/// let level = DecompositionLevelCount(3);
|
||||
/// let base_log = DecompositionBaseLog(6);
|
||||
/// let std = LogStandardDev::from_log_standard_dev(-60.);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = 3_u32 << 20;
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey32 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_ciphertext: GgswCiphertext32 = default_engine.encrypt_scalar_ggsw_ciphertext(
|
||||
/// &h_key,
|
||||
/// &h_plaintext,
|
||||
/// noise,
|
||||
/// level,
|
||||
/// base_log,
|
||||
/// )?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaGgswCiphertext32 = cuda_engine.convert_ggsw_ciphertext(&h_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(d_ciphertext.decomposition_level_count(), level);
|
||||
/// assert_eq!(d_ciphertext.decomposition_base_log(), base_log);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_ggsw_ciphertext(
|
||||
&mut self,
|
||||
input: &GgswCiphertext32,
|
||||
) -> Result<CudaGgswCiphertext32, GgswCiphertextConversionError<CudaError>> {
|
||||
let stream = &self.streams[0];
|
||||
let data_per_gpu = input.polynomial_size().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.decomposition_level_count().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u32>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
Ok(unsafe { self.convert_ggsw_ciphertext_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_ggsw_ciphertext_unchecked(
|
||||
&mut self,
|
||||
input: &GgswCiphertext32,
|
||||
) -> CudaGgswCiphertext32 {
|
||||
// Copy the entire input vector over GPUs 0
|
||||
let data_per_gpu = input.polynomial_size().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.decomposition_level_count().0;
|
||||
let stream = &self.streams[0];
|
||||
let mut vec = stream.malloc::<u32>(data_per_gpu as u32);
|
||||
let input_slice = input.0.as_tensor().as_slice();
|
||||
stream.copy_to_gpu::<u32>(&mut vec, input_slice);
|
||||
CudaGgswCiphertext32(CudaGgswCiphertext::<u32> {
|
||||
d_vec: vec,
|
||||
glwe_dimension: input.glwe_dimension(),
|
||||
polynomial_size: input.polynomial_size(),
|
||||
decomposition_level_count: input.decomposition_level_count(),
|
||||
decomposition_base_log: input.decomposition_base_log(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GGSW ciphertext vector with 32 bits of precision from GPU 0 to CPU.
|
||||
/// This conversion is not necessary to run the bootstrap on the GPU.
|
||||
/// It is implemented for testing purposes only.
|
||||
impl GgswCiphertextConversionEngine<CudaGgswCiphertext32, GgswCiphertext32> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// use std::task::Poll;
|
||||
/// let glwe_dimension = GlweDimension(1);
|
||||
/// let polynomial_size = PolynomialSize(8);
|
||||
/// let level = DecompositionLevelCount(3);
|
||||
/// let base_log = DecompositionBaseLog(6);
|
||||
/// let std = LogStandardDev::from_log_standard_dev(-60.);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = 3_u32 << 20;
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey32 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_ciphertext: GgswCiphertext32 = default_engine.encrypt_scalar_ggsw_ciphertext(
|
||||
/// &h_key,
|
||||
/// &h_plaintext,
|
||||
/// noise,
|
||||
/// level,
|
||||
/// base_log,
|
||||
/// )?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaGgswCiphertext32 = cuda_engine.convert_ggsw_ciphertext(&h_ciphertext)?;
|
||||
/// let h_output_ciphertext: GgswCiphertext32 =
|
||||
/// cuda_engine.convert_ggsw_ciphertext(&d_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(d_ciphertext.decomposition_level_count(), level);
|
||||
/// assert_eq!(d_ciphertext.decomposition_base_log(), base_log);
|
||||
/// assert_eq!(h_ciphertext, h_output_ciphertext);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_ggsw_ciphertext(
|
||||
&mut self,
|
||||
input: &CudaGgswCiphertext32,
|
||||
) -> Result<GgswCiphertext32, GgswCiphertextConversionError<CudaError>> {
|
||||
Ok(unsafe { self.convert_ggsw_ciphertext_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_ggsw_ciphertext_unchecked(
|
||||
&mut self,
|
||||
input: &CudaGgswCiphertext32,
|
||||
) -> GgswCiphertext32 {
|
||||
// Copy the data from GPU 0 back to the CPU
|
||||
let data_per_gpu = input.polynomial_size().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.decomposition_level_count().0;
|
||||
let mut output = vec![0u32; data_per_gpu];
|
||||
let stream = &self.streams[0];
|
||||
stream.copy_to_cpu::<u32>(&mut output, &input.0.d_vec);
|
||||
GgswCiphertext32(StandardGgswCiphertext::from_container(
|
||||
output,
|
||||
input.glwe_dimension().to_glwe_size(),
|
||||
input.polynomial_size(),
|
||||
input.decomposition_base_log(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GGSW ciphertext with 64 bits of precision from CPU to GPU 0.
|
||||
/// Only this conversion is necessary to run the WopPBS on the GPU.
|
||||
impl GgswCiphertextConversionEngine<GgswCiphertext64, CudaGgswCiphertext64> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// use std::task::Poll;
|
||||
/// let glwe_dimension = GlweDimension(1);
|
||||
/// let polynomial_size = PolynomialSize(8);
|
||||
/// let level = DecompositionLevelCount(3);
|
||||
/// let base_log = DecompositionBaseLog(6);
|
||||
/// let std = LogStandardDev::from_log_standard_dev(-60.);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = 42_u64;
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey64 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext: Plaintext64 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_ciphertext: GgswCiphertext64 = default_engine.encrypt_scalar_ggsw_ciphertext(
|
||||
/// &h_key,
|
||||
/// &h_plaintext,
|
||||
/// noise,
|
||||
/// level,
|
||||
/// base_log,
|
||||
/// )?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaGgswCiphertext64 = cuda_engine.convert_ggsw_ciphertext(&h_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(d_ciphertext.decomposition_level_count(), level);
|
||||
/// assert_eq!(d_ciphertext.decomposition_base_log(), base_log);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_ggsw_ciphertext(
|
||||
&mut self,
|
||||
input: &GgswCiphertext64,
|
||||
) -> Result<CudaGgswCiphertext64, GgswCiphertextConversionError<CudaError>> {
|
||||
let stream = &self.streams[0];
|
||||
let data_per_gpu = input.polynomial_size().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.decomposition_level_count().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
Ok(unsafe { self.convert_ggsw_ciphertext_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_ggsw_ciphertext_unchecked(
|
||||
&mut self,
|
||||
input: &GgswCiphertext64,
|
||||
) -> CudaGgswCiphertext64 {
|
||||
// Copy the entire input vector over GPU 0
|
||||
let data_per_gpu = input.polynomial_size().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.decomposition_level_count().0;
|
||||
let stream = &self.streams[0];
|
||||
let mut vec = stream.malloc::<u64>(data_per_gpu as u32);
|
||||
let input_slice = input.0.as_tensor().as_slice();
|
||||
stream.copy_to_gpu::<u64>(&mut vec, input_slice);
|
||||
CudaGgswCiphertext64(CudaGgswCiphertext::<u64> {
|
||||
d_vec: vec,
|
||||
glwe_dimension: input.glwe_dimension(),
|
||||
polynomial_size: input.polynomial_size(),
|
||||
decomposition_level_count: input.decomposition_level_count(),
|
||||
decomposition_base_log: input.decomposition_base_log(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GGSW ciphertext vector with 64 bits of precision from GPU 0 to CPU.
|
||||
/// This conversion is not necessary to run the bootstrap on the GPU.
|
||||
/// It is implemented for testing purposes only.
|
||||
impl GgswCiphertextConversionEngine<CudaGgswCiphertext64, GgswCiphertext64> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// use std::task::Poll;
|
||||
/// let glwe_dimension = GlweDimension(1);
|
||||
/// let polynomial_size = PolynomialSize(8);
|
||||
/// let level = DecompositionLevelCount(3);
|
||||
/// let base_log = DecompositionBaseLog(6);
|
||||
/// let std = LogStandardDev::from_log_standard_dev(-60.);
|
||||
/// // Here a hard-set encoding is applied (shift by 50 bits)
|
||||
/// let input = 3_u64 << 50;
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey64 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext: Plaintext64 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_ciphertext: GgswCiphertext64 = default_engine.encrypt_scalar_ggsw_ciphertext(
|
||||
/// &h_key,
|
||||
/// &h_plaintext,
|
||||
/// noise,
|
||||
/// level,
|
||||
/// base_log,
|
||||
/// )?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaGgswCiphertext64 = cuda_engine.convert_ggsw_ciphertext(&h_ciphertext)?;
|
||||
/// let h_output_ciphertext: GgswCiphertext64 =
|
||||
/// cuda_engine.convert_ggsw_ciphertext(&d_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(d_ciphertext.decomposition_level_count(), level);
|
||||
/// assert_eq!(d_ciphertext.decomposition_base_log(), base_log);
|
||||
/// assert_eq!(h_ciphertext, h_output_ciphertext);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_ggsw_ciphertext(
|
||||
&mut self,
|
||||
input: &CudaGgswCiphertext64,
|
||||
) -> Result<GgswCiphertext64, GgswCiphertextConversionError<CudaError>> {
|
||||
Ok(unsafe { self.convert_ggsw_ciphertext_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_ggsw_ciphertext_unchecked(
|
||||
&mut self,
|
||||
input: &CudaGgswCiphertext64,
|
||||
) -> GgswCiphertext64 {
|
||||
// Copy the data from GPU 0 back to the CPU
|
||||
let data_per_gpu = input.polynomial_size().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.decomposition_level_count().0;
|
||||
let mut output = vec![0u64; data_per_gpu];
|
||||
let stream = &self.streams[0];
|
||||
stream.copy_to_cpu::<u64>(&mut output, &input.0.d_vec);
|
||||
GgswCiphertext64(StandardGgswCiphertext::from_container(
|
||||
output,
|
||||
input.glwe_dimension().to_glwe_size(),
|
||||
input.polynomial_size(),
|
||||
input.decomposition_base_log(),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
use crate::core_crypto::backends::cuda::engines::{CudaEngine, CudaError};
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::{
|
||||
CudaGlweCiphertext32, CudaGlweCiphertext64,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::private::crypto::glwe::ciphertext::CudaGlweCiphertext;
|
||||
use crate::core_crypto::commons::crypto::glwe::GlweCiphertext;
|
||||
use crate::core_crypto::commons::math::tensor::{AsRefSlice, AsRefTensor};
|
||||
use crate::core_crypto::prelude::{
|
||||
GlweCiphertext32, GlweCiphertext64, GlweCiphertextView64,
|
||||
};
|
||||
use crate::core_crypto::specification::engines::{
|
||||
GlweCiphertextConversionEngine, GlweCiphertextConversionError,
|
||||
};
|
||||
use crate::core_crypto::specification::entities::GlweCiphertextEntity;
|
||||
|
||||
impl From<CudaError> for GlweCiphertextConversionError<CudaError> {
|
||||
fn from(err: CudaError) -> Self {
|
||||
Self::Engine(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GLWE ciphertext with 32 bits of precision from CPU to GPU 0.
|
||||
/// Only this conversion is necessary to run the bootstrap on the GPU.
|
||||
impl GlweCiphertextConversionEngine<GlweCiphertext32, CudaGlweCiphertext32> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// GlweCiphertextCount, GlweDimension, PolynomialSize, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(3);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u32 << 20; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey32 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext_vector: PlaintextVector32 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext: GlweCiphertext32 =
|
||||
/// default_engine.encrypt_glwe_ciphertext(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaGlweCiphertext32 = cuda_engine.convert_glwe_ciphertext(&h_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext.polynomial_size(), polynomial_size);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_glwe_ciphertext(
|
||||
&mut self,
|
||||
input: &GlweCiphertext32,
|
||||
) -> Result<CudaGlweCiphertext32, GlweCiphertextConversionError<CudaError>> {
|
||||
let stream = &self.streams[0];
|
||||
let data_per_gpu = input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u32>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
Ok(unsafe { self.convert_glwe_ciphertext_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_glwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
input: &GlweCiphertext32,
|
||||
) -> CudaGlweCiphertext32 {
|
||||
// Copy the entire input vector over all GPUs
|
||||
let data_per_gpu = input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0;
|
||||
let stream = &self.streams[0];
|
||||
let mut vec = stream.malloc::<u32>(data_per_gpu as u32);
|
||||
let input_slice = input.0.as_tensor().as_slice();
|
||||
stream.copy_to_gpu::<u32>(&mut vec, input_slice);
|
||||
CudaGlweCiphertext32(CudaGlweCiphertext::<u32> {
|
||||
d_vec: vec,
|
||||
glwe_dimension: input.glwe_dimension(),
|
||||
polynomial_size: input.polynomial_size(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GLWE ciphertext vector with 32 bits of precision from GPU 0 to CPU.
|
||||
/// This conversion is not necessary to run the bootstrap on the GPU.
|
||||
/// It is implemented for testing purposes only.
|
||||
impl GlweCiphertextConversionEngine<CudaGlweCiphertext32, GlweCiphertext32> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// GlweCiphertextCount, GlweDimension, PolynomialSize, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(3);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u32 << 20; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey32 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext_vector: PlaintextVector32 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext: GlweCiphertext32 =
|
||||
/// default_engine.encrypt_glwe_ciphertext(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaGlweCiphertext32 = cuda_engine.convert_glwe_ciphertext(&h_ciphertext)?;
|
||||
/// let h_output_ciphertext: GlweCiphertext32 =
|
||||
/// cuda_engine.convert_glwe_ciphertext(&d_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(h_ciphertext, h_output_ciphertext);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_glwe_ciphertext(
|
||||
&mut self,
|
||||
input: &CudaGlweCiphertext32,
|
||||
) -> Result<GlweCiphertext32, GlweCiphertextConversionError<CudaError>> {
|
||||
Ok(unsafe { self.convert_glwe_ciphertext_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_glwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
input: &CudaGlweCiphertext32,
|
||||
) -> GlweCiphertext32 {
|
||||
// Copy the data from GPU 0 back to the CPU
|
||||
let mut output =
|
||||
vec![0u32; input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0];
|
||||
let stream = &self.streams[0];
|
||||
stream.copy_to_cpu::<u32>(&mut output, &input.0.d_vec);
|
||||
GlweCiphertext32(GlweCiphertext::from_container(
|
||||
output,
|
||||
input.polynomial_size(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GLWE ciphertext with 64 bits of precision from CPU to GPU 0.
|
||||
/// Only this conversion is necessary to run the bootstrap on the GPU.
|
||||
impl GlweCiphertextConversionEngine<GlweCiphertext64, CudaGlweCiphertext64> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// GlweCiphertextCount, GlweDimension, PolynomialSize, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(3);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u64 << 20; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey64 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext: GlweCiphertext64 =
|
||||
/// default_engine.encrypt_glwe_ciphertext(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaGlweCiphertext64 = cuda_engine.convert_glwe_ciphertext(&h_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext.polynomial_size(), polynomial_size);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_glwe_ciphertext(
|
||||
&mut self,
|
||||
input: &GlweCiphertext64,
|
||||
) -> Result<CudaGlweCiphertext64, GlweCiphertextConversionError<CudaError>> {
|
||||
let stream = &self.streams[0];
|
||||
let data_per_gpu = input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
Ok(unsafe { self.convert_glwe_ciphertext_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_glwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
input: &GlweCiphertext64,
|
||||
) -> CudaGlweCiphertext64 {
|
||||
// Copy the entire input vector over all GPUs
|
||||
let data_per_gpu = input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0;
|
||||
let stream = &self.streams[0];
|
||||
let mut vec = stream.malloc::<u64>(data_per_gpu as u32);
|
||||
let input_slice = input.0.as_tensor().as_slice();
|
||||
stream.copy_to_gpu::<u64>(&mut vec, input_slice);
|
||||
CudaGlweCiphertext64(CudaGlweCiphertext::<u64> {
|
||||
d_vec: vec,
|
||||
glwe_dimension: input.glwe_dimension(),
|
||||
polynomial_size: input.polynomial_size(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GLWE ciphertext vector with 64 bits of precision from GPU 0 to CPU.
|
||||
/// This conversion is not necessary to run the bootstrap on the GPU.
|
||||
/// It is implemented for testing purposes only.
|
||||
impl GlweCiphertextConversionEngine<CudaGlweCiphertext64, GlweCiphertext64> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// GlweCiphertextCount, GlweDimension, PolynomialSize, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(3);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u64 << 20; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey64 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext: GlweCiphertext64 =
|
||||
/// default_engine.encrypt_glwe_ciphertext(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaGlweCiphertext64 = cuda_engine.convert_glwe_ciphertext(&h_ciphertext)?;
|
||||
/// let h_output_ciphertext: GlweCiphertext64 =
|
||||
/// cuda_engine.convert_glwe_ciphertext(&d_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(h_ciphertext, h_output_ciphertext);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_glwe_ciphertext(
|
||||
&mut self,
|
||||
input: &CudaGlweCiphertext64,
|
||||
) -> Result<GlweCiphertext64, GlweCiphertextConversionError<CudaError>> {
|
||||
Ok(unsafe { self.convert_glwe_ciphertext_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_glwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
input: &CudaGlweCiphertext64,
|
||||
) -> GlweCiphertext64 {
|
||||
// Copy the data from GPU 0 back to the CPU
|
||||
let mut output =
|
||||
vec![0u64; input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0];
|
||||
let stream = &self.streams[0];
|
||||
stream.copy_to_cpu::<u64>(&mut output, &input.0.d_vec);
|
||||
GlweCiphertext64(GlweCiphertext::from_container(
|
||||
output,
|
||||
input.polynomial_size(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a view of a GLWE ciphertext with 64 bits of precision from CPU to GPU 0.
|
||||
impl GlweCiphertextConversionEngine<GlweCiphertextView64<'_>, CudaGlweCiphertext64> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// GlweCiphertextCount, GlweDimension, PolynomialSize, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(3);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u64 << 20; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey64 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext: GlweCiphertext64 =
|
||||
/// default_engine.encrypt_glwe_ciphertext(&h_key, &h_plaintext_vector, noise)?;
|
||||
/// let h_raw_ciphertext: Vec<u64> =
|
||||
/// default_engine.consume_retrieve_glwe_ciphertext(h_ciphertext)?;
|
||||
/// let mut h_view_ciphertext: GlweCiphertextView64 =
|
||||
/// default_engine.create_glwe_ciphertext_from(h_raw_ciphertext.as_slice(), polynomial_size)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaGlweCiphertext64 =
|
||||
/// cuda_engine.convert_glwe_ciphertext(&h_view_ciphertext)?;
|
||||
/// let h_output_ciphertext: GlweCiphertext64 =
|
||||
/// cuda_engine.convert_glwe_ciphertext(&d_ciphertext)?;
|
||||
///
|
||||
/// // Extracts the internal container
|
||||
/// let h_raw_output_ciphertext: Vec<u64> =
|
||||
/// default_engine.consume_retrieve_glwe_ciphertext(h_output_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(h_raw_ciphertext, h_raw_output_ciphertext);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_glwe_ciphertext(
|
||||
&mut self,
|
||||
input: &GlweCiphertextView64,
|
||||
) -> Result<CudaGlweCiphertext64, GlweCiphertextConversionError<CudaError>> {
|
||||
let stream = &self.streams[0];
|
||||
let data_per_gpu = input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
Ok(unsafe { self.convert_glwe_ciphertext_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_glwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
input: &GlweCiphertextView64,
|
||||
) -> CudaGlweCiphertext64 {
|
||||
// Copy the entire input vector over all GPUs
|
||||
let data_per_gpu = input.glwe_dimension().to_glwe_size().0 * input.polynomial_size().0;
|
||||
let stream = &self.streams[0];
|
||||
let mut vec = stream.malloc::<u64>(data_per_gpu as u32);
|
||||
let input_slice = input.0.as_tensor().as_slice();
|
||||
stream.copy_to_gpu::<u64>(&mut vec, input_slice);
|
||||
CudaGlweCiphertext64(CudaGlweCiphertext::<u64> {
|
||||
d_vec: vec,
|
||||
glwe_dimension: input.glwe_dimension(),
|
||||
polynomial_size: input.polynomial_size(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
use crate::core_crypto::backends::cuda::engines::{CudaEngine, CudaError};
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::CudaGlweCiphertext64;
|
||||
use crate::core_crypto::prelude::{
|
||||
GlweCiphertextDiscardingConversionError, GlweCiphertextMutView64,
|
||||
};
|
||||
use crate::core_crypto::specification::engines::GlweCiphertextDiscardingConversionEngine;
|
||||
|
||||
/// # Description
|
||||
/// Convert a GLWE ciphertext vector with 64 bits of precision from GPU 0 to a view on the CPU.
|
||||
impl GlweCiphertextDiscardingConversionEngine<CudaGlweCiphertext64, GlweCiphertextMutView64<'_>>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// GlweCiphertextCount, GlweDimension, PolynomialSize, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// use std::borrow::{Borrow, BorrowMut};
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// use tfhe::core_crypto::commons::numeric::CastInto;
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(3);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u64 << 20; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey64 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext: GlweCiphertext64 =
|
||||
/// default_engine.encrypt_glwe_ciphertext(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaGlweCiphertext64 = cuda_engine.convert_glwe_ciphertext(&h_ciphertext)?;
|
||||
///
|
||||
/// let mut h_raw_output_ciphertext =
|
||||
/// vec![0_u64; glwe_dimension.to_glwe_size().0 * polynomial_size.0];
|
||||
/// let mut h_view_output_ciphertext: GlweCiphertextMutView64 = default_engine
|
||||
/// .create_glwe_ciphertext_from(h_raw_output_ciphertext.as_mut_slice(), polynomial_size)?;
|
||||
///
|
||||
/// cuda_engine
|
||||
/// .discard_convert_glwe_ciphertext(h_view_output_ciphertext.borrow_mut(), &d_ciphertext)?;
|
||||
///
|
||||
/// // Extracts the internal container
|
||||
/// let h_raw_ciphertext: Vec<u64> =
|
||||
/// default_engine.consume_retrieve_glwe_ciphertext(h_ciphertext)?;
|
||||
/// let h_raw_output_ciphertext: &[u64] =
|
||||
/// default_engine.consume_retrieve_glwe_ciphertext(h_view_output_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext.polynomial_size(), polynomial_size);
|
||||
///
|
||||
/// assert_eq!(h_raw_ciphertext, h_raw_output_ciphertext.to_vec());
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_convert_glwe_ciphertext(
|
||||
&mut self,
|
||||
output: &mut GlweCiphertextMutView64,
|
||||
input: &CudaGlweCiphertext64,
|
||||
) -> Result<(), GlweCiphertextDiscardingConversionError<CudaError>> {
|
||||
GlweCiphertextDiscardingConversionError::perform_generic_checks(output, input)?;
|
||||
unsafe { self.discard_convert_glwe_ciphertext_unchecked(output, input) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_convert_glwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
output: &mut GlweCiphertextMutView64,
|
||||
input: &CudaGlweCiphertext64,
|
||||
) {
|
||||
// Copy the data from GPU 0 back to the CPU
|
||||
let stream = &self.streams[0];
|
||||
stream.copy_to_cpu::<u64>(output.0.tensor.as_mut_container(), &input.0.d_vec);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,741 @@
|
||||
use crate::core_crypto::backends::cuda::engines::{CudaEngine, CudaError};
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::{
|
||||
CudaGlweCiphertextVector32, CudaGlweCiphertextVector64,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::private::crypto::glwe::list::CudaGlweList;
|
||||
use crate::core_crypto::commons::crypto::glwe::GlweList;
|
||||
use crate::core_crypto::commons::math::tensor::{AsRefSlice, AsRefTensor};
|
||||
use crate::core_crypto::prelude::{
|
||||
GlweCiphertextVector32, GlweCiphertextVector64, GlweCiphertextVectorMutView32,
|
||||
GlweCiphertextVectorMutView64, GlweCiphertextVectorView32, GlweCiphertextVectorView64,
|
||||
};
|
||||
use crate::core_crypto::specification::engines::{
|
||||
GlweCiphertextVectorConversionEngine, GlweCiphertextVectorConversionError,
|
||||
};
|
||||
use crate::core_crypto::specification::entities::GlweCiphertextVectorEntity;
|
||||
|
||||
impl From<CudaError> for GlweCiphertextVectorConversionError<CudaError> {
|
||||
fn from(err: CudaError) -> Self {
|
||||
Self::Engine(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GLWE ciphertext vector with 32 bits of precision from CPU to GPU.
|
||||
/// Only this conversion is necessary to run the bootstrap on the GPU.
|
||||
/// The whole vector of GLWE ciphertexts is copied to all the GPUS: it corresponds
|
||||
/// to the input vector of lookup tables for the bootstrap.
|
||||
impl GlweCiphertextVectorConversionEngine<GlweCiphertextVector32, CudaGlweCiphertextVector32>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// GlweCiphertextCount, GlweDimension, PolynomialSize, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(3);
|
||||
/// let glwe_count = GlweCiphertextCount(2);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u32 << 20; polynomial_size.0 * glwe_count.0];
|
||||
/// let noise = Variance(2_f64.powf(-25.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey32 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext_vector: PlaintextVector32 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: GlweCiphertextVector32 =
|
||||
/// default_engine.encrypt_glwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaGlweCiphertextVector32 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&h_ciphertext_vector)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext_vector.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext_vector.glwe_ciphertext_count(), glwe_count,);
|
||||
/// assert_eq!(d_ciphertext_vector.polynomial_size(), polynomial_size);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_glwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &GlweCiphertextVector32,
|
||||
) -> Result<CudaGlweCiphertextVector32, GlweCiphertextVectorConversionError<CudaError>> {
|
||||
for gpu_index in 0..self.get_number_of_gpus().0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let data_per_gpu = input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_ciphertext_count().0
|
||||
* input.polynomial_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u32>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_glwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_glwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &GlweCiphertextVector32,
|
||||
) -> CudaGlweCiphertextVector32 {
|
||||
// Copy the entire input vector over all GPUs
|
||||
let mut vecs = Vec::with_capacity(self.get_number_of_gpus().0);
|
||||
let data_per_gpu = input.glwe_ciphertext_count().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.polynomial_size().0;
|
||||
for gpu_index in 0..self.get_number_of_gpus().0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let mut vec = stream.malloc::<u32>(data_per_gpu as u32);
|
||||
let input_slice = input.0.as_tensor().as_slice();
|
||||
stream.copy_to_gpu::<u32>(&mut vec, input_slice);
|
||||
vecs.push(vec);
|
||||
}
|
||||
CudaGlweCiphertextVector32(CudaGlweList::<u32> {
|
||||
d_vecs: vecs,
|
||||
glwe_ciphertext_count: input.glwe_ciphertext_count(),
|
||||
glwe_dimension: input.glwe_dimension(),
|
||||
polynomial_size: input.polynomial_size(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GLWE ciphertext vector with 32 bits of precision from GPU to CPU.
|
||||
/// This conversion is not necessary to run the bootstrap on the GPU.
|
||||
/// It is implemented for testing purposes only.
|
||||
impl GlweCiphertextVectorConversionEngine<CudaGlweCiphertextVector32, GlweCiphertextVector32>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// GlweCiphertextCount, GlweDimension, PolynomialSize, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(3);
|
||||
/// let glwe_count = GlweCiphertextCount(2);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u32 << 20; polynomial_size.0 * glwe_count.0];
|
||||
/// let noise = Variance(2_f64.powf(-25.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey32 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext_vector: PlaintextVector32 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: GlweCiphertextVector32 =
|
||||
/// default_engine.encrypt_glwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaGlweCiphertextVector32 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&h_ciphertext_vector)?;
|
||||
/// let h_output_ciphertext_vector: GlweCiphertextVector32 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&d_ciphertext_vector)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext_vector.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext_vector.glwe_ciphertext_count(), glwe_count,);
|
||||
/// assert_eq!(d_ciphertext_vector.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(h_ciphertext_vector, h_output_ciphertext_vector);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_glwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &CudaGlweCiphertextVector32,
|
||||
) -> Result<GlweCiphertextVector32, GlweCiphertextVectorConversionError<CudaError>> {
|
||||
Ok(unsafe { self.convert_glwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_glwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &CudaGlweCiphertextVector32,
|
||||
) -> GlweCiphertextVector32 {
|
||||
// Copy the data from GPU 0 back to the CPU
|
||||
let mut output = vec![
|
||||
0u32;
|
||||
input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_ciphertext_count().0
|
||||
* input.polynomial_size().0
|
||||
];
|
||||
let stream = &self.streams[0];
|
||||
stream.copy_to_cpu::<u32>(&mut output, input.0.d_vecs.first().unwrap());
|
||||
GlweCiphertextVector32(GlweList::from_container(
|
||||
output,
|
||||
input.glwe_dimension(),
|
||||
input.polynomial_size(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GLWE ciphertext vector with 64 bits of precision from CPU to GPU.
|
||||
/// Only this conversion is necessary to run the bootstrap on the GPU.
|
||||
/// The whole vector of GLWE ciphertexts is copied to all the GPUS: it corresponds
|
||||
/// to the input vector of lookup tables for the bootstrap.
|
||||
impl GlweCiphertextVectorConversionEngine<GlweCiphertextVector64, CudaGlweCiphertextVector64>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// GlweCiphertextCount, GlweDimension, PolynomialSize, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(3);
|
||||
/// let glwe_count = GlweCiphertextCount(2);
|
||||
/// // Here a hard-set encoding is applied (shift by 50 bits)
|
||||
/// let input = vec![3_u64 << 50; polynomial_size.0 * glwe_count.0];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey64 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: GlweCiphertextVector64 =
|
||||
/// default_engine.encrypt_glwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaGlweCiphertextVector64 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&h_ciphertext_vector)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext_vector.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext_vector.glwe_ciphertext_count(), glwe_count);
|
||||
/// assert_eq!(d_ciphertext_vector.polynomial_size(), polynomial_size);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_glwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &GlweCiphertextVector64,
|
||||
) -> Result<CudaGlweCiphertextVector64, GlweCiphertextVectorConversionError<CudaError>> {
|
||||
for gpu_index in 0..self.get_number_of_gpus().0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let data_per_gpu = input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_ciphertext_count().0
|
||||
* input.polynomial_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_glwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_glwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &GlweCiphertextVector64,
|
||||
) -> CudaGlweCiphertextVector64 {
|
||||
// Copy the entire input vector over all GPUs
|
||||
let mut vecs = Vec::with_capacity(self.get_number_of_gpus().0);
|
||||
let data_per_gpu = input.glwe_ciphertext_count().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.polynomial_size().0;
|
||||
for gpu_index in 0..self.get_number_of_gpus().0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let mut vec = stream.malloc::<u64>(data_per_gpu as u32);
|
||||
let input_slice = input.0.as_tensor().as_slice();
|
||||
stream.copy_to_gpu::<u64>(&mut vec, input_slice);
|
||||
vecs.push(vec);
|
||||
}
|
||||
CudaGlweCiphertextVector64(CudaGlweList::<u64> {
|
||||
d_vecs: vecs,
|
||||
glwe_ciphertext_count: input.glwe_ciphertext_count(),
|
||||
glwe_dimension: input.glwe_dimension(),
|
||||
polynomial_size: input.polynomial_size(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GLWE ciphertext vector with 64 bits of precision from GPU to CPU.
|
||||
/// This conversion is not necessary to run the bootstrap on the GPU.
|
||||
/// It is implemented for testing purposes only.
|
||||
impl GlweCiphertextVectorConversionEngine<CudaGlweCiphertextVector64, GlweCiphertextVector64>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// GlweCiphertextCount, GlweDimension, PolynomialSize, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(3);
|
||||
/// let glwe_count = GlweCiphertextCount(2);
|
||||
/// // Here a hard-set encoding is applied (shift by 50 bits)
|
||||
/// let input = vec![3_u64 << 50; polynomial_size.0 * glwe_count.0];
|
||||
/// let noise = Variance(2_f64.powf(-25.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey64 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: GlweCiphertextVector64 =
|
||||
/// default_engine.encrypt_glwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaGlweCiphertextVector64 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&h_ciphertext_vector)?;
|
||||
/// let h_output_ciphertext_vector: GlweCiphertextVector64 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&d_ciphertext_vector)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext_vector.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext_vector.glwe_ciphertext_count(), glwe_count);
|
||||
/// assert_eq!(d_ciphertext_vector.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(h_ciphertext_vector, h_output_ciphertext_vector);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_glwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &CudaGlweCiphertextVector64,
|
||||
) -> Result<GlweCiphertextVector64, GlweCiphertextVectorConversionError<CudaError>> {
|
||||
Ok(unsafe { self.convert_glwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_glwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &CudaGlweCiphertextVector64,
|
||||
) -> GlweCiphertextVector64 {
|
||||
// Copy the data from GPU 0 back to the CPU
|
||||
let mut output = vec![
|
||||
0u64;
|
||||
input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_ciphertext_count().0
|
||||
* input.polynomial_size().0
|
||||
];
|
||||
let stream = &self.streams[0];
|
||||
stream.copy_to_cpu::<u64>(&mut output, input.0.d_vecs.first().unwrap());
|
||||
GlweCiphertextVector64(GlweList::from_container(
|
||||
output,
|
||||
input.glwe_dimension(),
|
||||
input.polynomial_size(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GLWE ciphertext vector view with 32 bits of precision from CPU to GPU.
|
||||
/// The whole vector of GLWE ciphertexts is copied to all the GPUS: it corresponds
|
||||
/// to the input vector of lookup tables for the bootstrap.
|
||||
impl
|
||||
GlweCiphertextVectorConversionEngine<GlweCiphertextVectorView32<'_>, CudaGlweCiphertextVector32>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(3);
|
||||
/// let glwe_count = GlweCiphertextCount(2);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u32 << 20; polynomial_size.0 * glwe_count.0];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey32 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext_vector: PlaintextVector32 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: GlweCiphertextVector32 =
|
||||
/// default_engine.encrypt_glwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let h_raw_ciphertext_vector: Vec<u32> =
|
||||
/// default_engine.consume_retrieve_glwe_ciphertext_vector(h_ciphertext_vector)?;
|
||||
/// let mut h_view_ciphertext_vector: GlweCiphertextVectorView32 = default_engine
|
||||
/// .create_glwe_ciphertext_vector_from(
|
||||
/// h_raw_ciphertext_vector.as_slice(),
|
||||
/// glwe_dimension,
|
||||
/// polynomial_size,
|
||||
/// )?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaGlweCiphertextVector32 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&h_view_ciphertext_vector)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext_vector.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext_vector.glwe_ciphertext_count(), glwe_count,);
|
||||
/// assert_eq!(d_ciphertext_vector.polynomial_size(), polynomial_size);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_glwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &GlweCiphertextVectorView32,
|
||||
) -> Result<CudaGlweCiphertextVector32, GlweCiphertextVectorConversionError<CudaError>> {
|
||||
for gpu_index in 0..self.get_number_of_gpus().0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let data_per_gpu = input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_ciphertext_count().0
|
||||
* input.polynomial_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u32>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_glwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_glwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &GlweCiphertextVectorView32,
|
||||
) -> CudaGlweCiphertextVector32 {
|
||||
// Copy the entire input vector over all GPUs
|
||||
let mut vecs = Vec::with_capacity(self.get_number_of_gpus().0);
|
||||
let data_per_gpu = input.glwe_ciphertext_count().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.polynomial_size().0;
|
||||
for gpu_index in 0..self.get_number_of_gpus().0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let mut vec = stream.malloc::<u32>(data_per_gpu as u32);
|
||||
let input_slice = input.0.as_tensor().as_slice();
|
||||
stream.copy_to_gpu::<u32>(&mut vec, input_slice);
|
||||
vecs.push(vec);
|
||||
}
|
||||
CudaGlweCiphertextVector32(CudaGlweList::<u32> {
|
||||
d_vecs: vecs,
|
||||
glwe_ciphertext_count: input.glwe_ciphertext_count(),
|
||||
glwe_dimension: input.glwe_dimension(),
|
||||
polynomial_size: input.polynomial_size(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GLWE ciphertext vector view with 64 bits of precision from CPU to GPU.
|
||||
/// The whole vector of GLWE ciphertexts is copied to all the GPUS: it corresponds
|
||||
/// to the input vector of lookup tables for the bootstrap.
|
||||
impl
|
||||
GlweCiphertextVectorConversionEngine<GlweCiphertextVectorView64<'_>, CudaGlweCiphertextVector64>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(3);
|
||||
/// let glwe_count = GlweCiphertextCount(2);
|
||||
/// // Here a hard-set encoding is applied (shift by 50 bits)
|
||||
/// let input = vec![3_u64 << 50; polynomial_size.0 * glwe_count.0];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey64 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: GlweCiphertextVector64 =
|
||||
/// default_engine.encrypt_glwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let h_raw_ciphertext_vector: Vec<u64> =
|
||||
/// default_engine.consume_retrieve_glwe_ciphertext_vector(h_ciphertext_vector)?;
|
||||
/// let mut h_view_ciphertext_vector: GlweCiphertextVectorView64 = default_engine
|
||||
/// .create_glwe_ciphertext_vector_from(
|
||||
/// h_raw_ciphertext_vector.as_slice(),
|
||||
/// glwe_dimension,
|
||||
/// polynomial_size,
|
||||
/// )?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaGlweCiphertextVector64 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&h_view_ciphertext_vector)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext_vector.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(d_ciphertext_vector.glwe_ciphertext_count(), glwe_count,);
|
||||
/// assert_eq!(d_ciphertext_vector.polynomial_size(), polynomial_size);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_glwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &GlweCiphertextVectorView64,
|
||||
) -> Result<CudaGlweCiphertextVector64, GlweCiphertextVectorConversionError<CudaError>> {
|
||||
for gpu_index in 0..self.get_number_of_gpus().0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let data_per_gpu = input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_ciphertext_count().0
|
||||
* input.polynomial_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_glwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_glwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &GlweCiphertextVectorView64,
|
||||
) -> CudaGlweCiphertextVector64 {
|
||||
// Copy the entire input vector over all GPUs
|
||||
let mut vecs = Vec::with_capacity(self.get_number_of_gpus().0);
|
||||
let data_per_gpu = input.glwe_ciphertext_count().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.polynomial_size().0;
|
||||
for gpu_index in 0..self.get_number_of_gpus().0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let mut vec = stream.malloc::<u64>(data_per_gpu as u32);
|
||||
let input_slice = input.0.as_tensor().as_slice();
|
||||
stream.copy_to_gpu::<u64>(&mut vec, input_slice);
|
||||
vecs.push(vec);
|
||||
}
|
||||
CudaGlweCiphertextVector64(CudaGlweList::<u64> {
|
||||
d_vecs: vecs,
|
||||
glwe_ciphertext_count: input.glwe_ciphertext_count(),
|
||||
glwe_dimension: input.glwe_dimension(),
|
||||
polynomial_size: input.polynomial_size(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GLWE ciphertext vector view with 32 bits of precision from CPU to GPU.
|
||||
/// The whole vector of GLWE ciphertexts is copied to all the GPUS: it corresponds
|
||||
/// to the input vector of lookup tables for the bootstrap.
|
||||
impl
|
||||
GlweCiphertextVectorConversionEngine<
|
||||
GlweCiphertextVectorMutView32<'_>,
|
||||
CudaGlweCiphertextVector32,
|
||||
> for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(3);
|
||||
/// let glwe_count = GlweCiphertextCount(2);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u32 << 20; polynomial_size.0 * glwe_count.0];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey32 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext_vector: PlaintextVector32 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: GlweCiphertextVector32 =
|
||||
/// default_engine.encrypt_glwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
/// let glwe_ciphertext_count = h_ciphertext_vector.glwe_ciphertext_count();
|
||||
///
|
||||
/// let mut h_raw_ciphertext_vector: Vec<u32> =
|
||||
/// default_engine.consume_retrieve_glwe_ciphertext_vector(h_ciphertext_vector)?;
|
||||
/// let h_view_ciphertext_vector: GlweCiphertextVectorMutView32 = default_engine
|
||||
/// .create_glwe_ciphertext_vector_from(
|
||||
/// h_raw_ciphertext_vector.as_mut_slice(),
|
||||
/// glwe_dimension,
|
||||
/// polynomial_size,
|
||||
/// )?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaGlweCiphertextVector32 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&h_view_ciphertext_vector)?;
|
||||
/// let h_output_ciphertext_vector: GlweCiphertextVector32 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&d_ciphertext_vector)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext_vector.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(
|
||||
/// d_ciphertext_vector.glwe_ciphertext_count(),
|
||||
/// glwe_ciphertext_count
|
||||
/// );
|
||||
/// assert_eq!(d_ciphertext_vector.polynomial_size(), polynomial_size);
|
||||
///
|
||||
/// // Extracts the internal container
|
||||
/// let h_raw_output_ciphertext_vector: Vec<u32> =
|
||||
/// default_engine.consume_retrieve_glwe_ciphertext_vector(h_output_ciphertext_vector)?;
|
||||
/// assert_eq!(
|
||||
/// h_raw_ciphertext_vector,
|
||||
/// h_raw_output_ciphertext_vector.to_vec()
|
||||
/// );
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_glwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &GlweCiphertextVectorMutView32,
|
||||
) -> Result<CudaGlweCiphertextVector32, GlweCiphertextVectorConversionError<CudaError>> {
|
||||
for gpu_index in 0..self.get_number_of_gpus().0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let data_per_gpu = input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_ciphertext_count().0
|
||||
* input.polynomial_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u32>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_glwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_glwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &GlweCiphertextVectorMutView32,
|
||||
) -> CudaGlweCiphertextVector32 {
|
||||
// Copy the entire input vector over all GPUs
|
||||
let mut vecs = Vec::with_capacity(self.get_number_of_gpus().0 as usize);
|
||||
let data_per_gpu = input.glwe_ciphertext_count().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.polynomial_size().0;
|
||||
for gpu_index in 0..self.get_number_of_gpus().0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let mut vec = stream.malloc::<u32>(data_per_gpu as u32);
|
||||
let input_slice = input.0.as_tensor().as_slice();
|
||||
stream.copy_to_gpu::<u32>(&mut vec, input_slice);
|
||||
vecs.push(vec);
|
||||
}
|
||||
CudaGlweCiphertextVector32(CudaGlweList::<u32> {
|
||||
d_vecs: vecs,
|
||||
glwe_ciphertext_count: input.glwe_ciphertext_count(),
|
||||
glwe_dimension: input.glwe_dimension(),
|
||||
polynomial_size: input.polynomial_size(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GLWE ciphertext vector view with 64 bits of precision from CPU to GPU.
|
||||
/// The whole vector of GLWE ciphertexts is copied to all the GPUS: it corresponds
|
||||
/// to the input vector of lookup tables for the bootstrap.
|
||||
impl
|
||||
GlweCiphertextVectorConversionEngine<
|
||||
GlweCiphertextVectorMutView64<'_>,
|
||||
CudaGlweCiphertextVector64,
|
||||
> for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(3);
|
||||
/// let glwe_count = GlweCiphertextCount(2);
|
||||
/// // Here a hard-set encoding is applied (shift by 50 bits)
|
||||
/// let input = vec![3_u64 << 50; polynomial_size.0 * glwe_count.0];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey64 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: GlweCiphertextVector64 =
|
||||
/// default_engine.encrypt_glwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
/// let glwe_ciphertext_count = h_ciphertext_vector.glwe_ciphertext_count();
|
||||
///
|
||||
/// let mut h_raw_ciphertext_vector: Vec<u64> =
|
||||
/// default_engine.consume_retrieve_glwe_ciphertext_vector(h_ciphertext_vector)?;
|
||||
/// let h_view_ciphertext_vector: GlweCiphertextVectorMutView64 = default_engine
|
||||
/// .create_glwe_ciphertext_vector_from(
|
||||
/// h_raw_ciphertext_vector.as_mut_slice(),
|
||||
/// glwe_dimension,
|
||||
/// polynomial_size,
|
||||
/// )?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaGlweCiphertextVector64 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&h_view_ciphertext_vector)?;
|
||||
/// let h_output_ciphertext_vector: GlweCiphertextVector64 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&d_ciphertext_vector)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext_vector.glwe_dimension(), glwe_dimension);
|
||||
/// assert_eq!(
|
||||
/// d_ciphertext_vector.glwe_ciphertext_count(),
|
||||
/// glwe_ciphertext_count
|
||||
/// );
|
||||
/// assert_eq!(d_ciphertext_vector.polynomial_size(), polynomial_size);
|
||||
///
|
||||
/// // Extracts the internal container
|
||||
/// let h_raw_output_ciphertext_vector: Vec<u64> =
|
||||
/// default_engine.consume_retrieve_glwe_ciphertext_vector(h_output_ciphertext_vector)?;
|
||||
/// assert_eq!(
|
||||
/// h_raw_ciphertext_vector,
|
||||
/// h_raw_output_ciphertext_vector.to_vec()
|
||||
/// );
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_glwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &GlweCiphertextVectorMutView64,
|
||||
) -> Result<CudaGlweCiphertextVector64, GlweCiphertextVectorConversionError<CudaError>> {
|
||||
for gpu_index in 0..self.get_number_of_gpus().0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let data_per_gpu = input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_ciphertext_count().0
|
||||
* input.polynomial_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_glwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_glwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &GlweCiphertextVectorMutView64,
|
||||
) -> CudaGlweCiphertextVector64 {
|
||||
// Copy the entire input vector over all GPUs
|
||||
let mut vecs = Vec::with_capacity(self.get_number_of_gpus().0 as usize);
|
||||
let data_per_gpu = input.glwe_ciphertext_count().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.polynomial_size().0;
|
||||
for gpu_index in 0..self.get_number_of_gpus().0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let mut vec = stream.malloc::<u64>(data_per_gpu as u32);
|
||||
let input_slice = input.0.as_tensor().as_slice();
|
||||
stream.copy_to_gpu::<u64>(&mut vec, input_slice);
|
||||
vecs.push(vec);
|
||||
}
|
||||
CudaGlweCiphertextVector64(CudaGlweList::<u64> {
|
||||
d_vecs: vecs,
|
||||
glwe_ciphertext_count: input.glwe_ciphertext_count(),
|
||||
glwe_dimension: input.glwe_dimension(),
|
||||
polynomial_size: input.polynomial_size(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
use crate::core_crypto::backends::cuda::engines::{CudaEngine, CudaError};
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::CudaGlweCiphertextVector64;
|
||||
use crate::core_crypto::prelude::GlweCiphertextVectorMutView64;
|
||||
use crate::core_crypto::specification::engines::{
|
||||
GlweCiphertextVectorDiscardingConversionEngine, GlweCiphertextVectorDiscardingConversionError,
|
||||
};
|
||||
|
||||
impl From<CudaError> for GlweCiphertextVectorDiscardingConversionError<CudaError> {
|
||||
fn from(err: CudaError) -> Self {
|
||||
Self::Engine(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a GLWE ciphertext vector with 64 bits of precision from GPU to CPU.
|
||||
/// This conversion is not necessary to run the bootstrap on the GPU.
|
||||
/// It is implemented for testing purposes only.
|
||||
impl
|
||||
GlweCiphertextVectorDiscardingConversionEngine<
|
||||
CudaGlweCiphertextVector64,
|
||||
GlweCiphertextVectorMutView64<'_>,
|
||||
> for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(3);
|
||||
/// // Here a hard-set encoding is applied (shift by 50 bits)
|
||||
/// let input = vec![3_u64 << 50; 6];
|
||||
/// let noise = Variance(2_f64.powf(-25.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: GlweSecretKey64 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dimension, polynomial_size)?;
|
||||
/// let h_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: GlweCiphertextVector64 =
|
||||
/// default_engine.encrypt_glwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
/// let glwe_ciphertext_count = h_ciphertext_vector.glwe_ciphertext_count();
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaGlweCiphertextVector64 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&h_ciphertext_vector)?;
|
||||
/// ///
|
||||
/// // Prepares the output container
|
||||
/// let mut h_raw_output_ciphertext_vector =
|
||||
/// vec![0_u64; (glwe_dimension.0 + 1) * polynomial_size.0 * glwe_ciphertext_count.0];
|
||||
/// let mut h_view_output_ciphertext_vector: GlweCiphertextVectorMutView64 = default_engine
|
||||
/// .create_glwe_ciphertext_vector_from(
|
||||
/// h_raw_output_ciphertext_vector.as_mut_slice(),
|
||||
/// glwe_dimension,
|
||||
/// polynomial_size,
|
||||
/// )?;
|
||||
///
|
||||
/// cuda_engine.discard_convert_glwe_ciphertext_vector(
|
||||
/// &mut h_view_output_ciphertext_vector,
|
||||
/// &d_ciphertext_vector,
|
||||
/// )?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// h_view_output_ciphertext_vector.glwe_dimension(),
|
||||
/// glwe_dimension
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// h_view_output_ciphertext_vector.glwe_ciphertext_count(),
|
||||
/// glwe_ciphertext_count
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// h_view_output_ciphertext_vector.polynomial_size(),
|
||||
/// polynomial_size
|
||||
/// );
|
||||
/// ///
|
||||
/// // Extracts the internal container
|
||||
/// let h_raw_input_ciphertext_vector: Vec<u64> =
|
||||
/// default_engine.consume_retrieve_glwe_ciphertext_vector(h_ciphertext_vector)?;
|
||||
/// let h_raw_output_ciphertext_vector: &[u64] =
|
||||
/// default_engine.consume_retrieve_glwe_ciphertext_vector(h_view_output_ciphertext_vector)?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// h_raw_input_ciphertext_vector,
|
||||
/// h_raw_output_ciphertext_vector.to_vec()
|
||||
/// );
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_convert_glwe_ciphertext_vector(
|
||||
&mut self,
|
||||
output: &mut GlweCiphertextVectorMutView64,
|
||||
input: &CudaGlweCiphertextVector64,
|
||||
) -> Result<(), GlweCiphertextVectorDiscardingConversionError<CudaError>> {
|
||||
unsafe { self.discard_convert_glwe_ciphertext_vector_unchecked(output, input) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_convert_glwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
output: &mut GlweCiphertextVectorMutView64,
|
||||
input: &CudaGlweCiphertextVector64,
|
||||
) {
|
||||
// Copy the data from GPU 0 back to the CPU
|
||||
let stream = &self.streams[0];
|
||||
let output_container = output.0.tensor.as_mut_container();
|
||||
stream.copy_to_cpu::<u64>(output_container, input.0.d_vecs.first().unwrap());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
use crate::core_crypto::backends::cuda::engines::CudaError;
|
||||
use crate::core_crypto::backends::cuda::implementation::engines::{
|
||||
check_base_log, check_glwe_dim, CudaEngine,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::{
|
||||
CudaFourierLweBootstrapKey32, CudaFourierLweBootstrapKey64,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::private::crypto::bootstrap::{
|
||||
convert_lwe_bootstrap_key_from_cpu_to_gpu, CudaBootstrapKey,
|
||||
};
|
||||
use crate::core_crypto::prelude::{LweBootstrapKey32, LweBootstrapKey64};
|
||||
use crate::core_crypto::specification::engines::{
|
||||
LweBootstrapKeyConversionEngine, LweBootstrapKeyConversionError,
|
||||
};
|
||||
use crate::core_crypto::specification::entities::LweBootstrapKeyEntity;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
impl From<CudaError> for LweBootstrapKeyConversionError<CudaError> {
|
||||
fn from(err: CudaError) -> Self {
|
||||
Self::Engine(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE bootstrap key corresponding to 32 bits of precision from the CPU to the GPU.
|
||||
/// The bootstrap key is copied entirely to all the GPUs and converted from the standard to the
|
||||
/// Fourier domain.
|
||||
|
||||
impl LweBootstrapKeyConversionEngine<LweBootstrapKey32, CudaFourierLweBootstrapKey32>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::backends::cuda::private::device::GpuIndex;
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
|
||||
/// Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let (lwe_dim, glwe_dim, poly_size) = (LweDimension(4), GlweDimension(1), PolynomialSize(512));
|
||||
/// let (dec_lc, dec_bl) = (DecompositionLevelCount(3), DecompositionBaseLog(5));
|
||||
/// let noise = Variance(2_f64.powf(-25.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let lwe_sk: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dim)?;
|
||||
/// let glwe_sk: GlweSecretKey32 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dim, poly_size)?;
|
||||
/// let bsk: LweBootstrapKey32 =
|
||||
/// default_engine.generate_new_lwe_bootstrap_key(&lwe_sk, &glwe_sk, dec_bl, dec_lc, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_fourier_bsk: CudaFourierLweBootstrapKey32 =
|
||||
/// cuda_engine.convert_lwe_bootstrap_key(&bsk)?;
|
||||
///
|
||||
/// assert_eq!(d_fourier_bsk.glwe_dimension(), glwe_dim);
|
||||
/// assert_eq!(d_fourier_bsk.polynomial_size(), poly_size);
|
||||
/// assert_eq!(d_fourier_bsk.input_lwe_dimension(), lwe_dim);
|
||||
/// assert_eq!(d_fourier_bsk.decomposition_base_log(), dec_bl);
|
||||
/// assert_eq!(d_fourier_bsk.decomposition_level_count(), dec_lc);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_bootstrap_key(
|
||||
&mut self,
|
||||
input: &LweBootstrapKey32,
|
||||
) -> Result<CudaFourierLweBootstrapKey32, LweBootstrapKeyConversionError<CudaError>> {
|
||||
let poly_size = input.0.polynomial_size();
|
||||
check_poly_size!(poly_size);
|
||||
let glwe_dim = input.0.glwe_size().to_glwe_dimension();
|
||||
check_glwe_dim!(glwe_dim);
|
||||
let base_log = input.0.base_log();
|
||||
check_base_log!(base_log);
|
||||
let data_per_gpu = input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.input_lwe_dimension().0
|
||||
* input.decomposition_level_count().0
|
||||
* input.polynomial_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u32>() as u64;
|
||||
for stream in self.streams.iter() {
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_lwe_bootstrap_key_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_bootstrap_key_unchecked(
|
||||
&mut self,
|
||||
input: &LweBootstrapKey32,
|
||||
) -> CudaFourierLweBootstrapKey32 {
|
||||
let vecs = convert_lwe_bootstrap_key_from_cpu_to_gpu::<u32, _>(
|
||||
self.get_cuda_streams(),
|
||||
&input.0,
|
||||
self.get_number_of_gpus(),
|
||||
);
|
||||
CudaFourierLweBootstrapKey32(CudaBootstrapKey::<u32> {
|
||||
d_vecs: vecs,
|
||||
polynomial_size: input.polynomial_size(),
|
||||
input_lwe_dimension: input.input_lwe_dimension(),
|
||||
glwe_dimension: input.glwe_dimension(),
|
||||
decomp_level: input.decomposition_level_count(),
|
||||
decomp_base_log: input.decomposition_base_log(),
|
||||
_phantom: PhantomData::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE bootstrap key corresponding to 64 bits of precision from the CPU to the GPU.
|
||||
/// The bootstrap key is copied entirely to all the GPUs and converted from the standard to the
|
||||
/// Fourier domain.
|
||||
|
||||
impl LweBootstrapKeyConversionEngine<LweBootstrapKey64, CudaFourierLweBootstrapKey64>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::backends::cuda::private::device::GpuIndex;
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
|
||||
/// Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let (lwe_dim, glwe_dim, poly_size) = (LweDimension(4), GlweDimension(1), PolynomialSize(512));
|
||||
/// let (dec_lc, dec_bl) = (DecompositionLevelCount(3), DecompositionBaseLog(5));
|
||||
/// let noise = Variance(2_f64.powf(-25.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let lwe_sk: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dim)?;
|
||||
/// let glwe_sk: GlweSecretKey64 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dim, poly_size)?;
|
||||
/// let bsk: LweBootstrapKey64 =
|
||||
/// default_engine.generate_new_lwe_bootstrap_key(&lwe_sk, &glwe_sk, dec_bl, dec_lc, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_fourier_bsk: CudaFourierLweBootstrapKey64 =
|
||||
/// cuda_engine.convert_lwe_bootstrap_key(&bsk)?;
|
||||
///
|
||||
/// assert_eq!(d_fourier_bsk.glwe_dimension(), glwe_dim);
|
||||
/// assert_eq!(d_fourier_bsk.polynomial_size(), poly_size);
|
||||
/// assert_eq!(d_fourier_bsk.input_lwe_dimension(), lwe_dim);
|
||||
/// assert_eq!(d_fourier_bsk.decomposition_base_log(), dec_bl);
|
||||
/// assert_eq!(d_fourier_bsk.decomposition_level_count(), dec_lc);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_bootstrap_key(
|
||||
&mut self,
|
||||
input: &LweBootstrapKey64,
|
||||
) -> Result<CudaFourierLweBootstrapKey64, LweBootstrapKeyConversionError<CudaError>> {
|
||||
let poly_size = input.0.polynomial_size();
|
||||
check_poly_size!(poly_size);
|
||||
let glwe_dim = input.0.glwe_size().to_glwe_dimension();
|
||||
check_glwe_dim!(glwe_dim);
|
||||
let data_per_gpu = input.glwe_dimension().to_glwe_size().0
|
||||
* input.glwe_dimension().to_glwe_size().0
|
||||
* input.input_lwe_dimension().0
|
||||
* input.decomposition_level_count().0
|
||||
* input.polynomial_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
|
||||
for stream in self.streams.iter() {
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_lwe_bootstrap_key_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_bootstrap_key_unchecked(
|
||||
&mut self,
|
||||
input: &LweBootstrapKey64,
|
||||
) -> CudaFourierLweBootstrapKey64 {
|
||||
let vecs = convert_lwe_bootstrap_key_from_cpu_to_gpu::<u64, _>(
|
||||
self.get_cuda_streams(),
|
||||
&input.0,
|
||||
self.get_number_of_gpus(),
|
||||
);
|
||||
CudaFourierLweBootstrapKey64(CudaBootstrapKey::<u64> {
|
||||
d_vecs: vecs,
|
||||
polynomial_size: input.polynomial_size(),
|
||||
input_lwe_dimension: input.input_lwe_dimension(),
|
||||
glwe_dimension: input.glwe_dimension(),
|
||||
decomp_level: input.decomposition_level_count(),
|
||||
decomp_base_log: input.decomposition_base_log(),
|
||||
_phantom: PhantomData::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
use crate::core_crypto::backends::cuda::implementation::engines::{CudaEngine, CudaError};
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::{
|
||||
CudaLweCiphertext32, CudaLweCiphertext64,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::private::crypto::lwe::ciphertext::CudaLweCiphertext;
|
||||
use crate::core_crypto::commons::crypto::lwe::LweCiphertext;
|
||||
use crate::core_crypto::commons::math::tensor::{AsRefSlice, AsRefTensor};
|
||||
use crate::core_crypto::prelude::{LweCiphertext32, LweCiphertext64, LweCiphertextView64};
|
||||
use crate::core_crypto::specification::engines::{
|
||||
LweCiphertextConversionEngine, LweCiphertextConversionError,
|
||||
};
|
||||
use crate::core_crypto::specification::entities::LweCiphertextEntity;
|
||||
|
||||
impl From<CudaError> for LweCiphertextConversionError<CudaError> {
|
||||
fn from(err: CudaError) -> Self {
|
||||
Self::Engine(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE ciphertext with 32 bits of precision from CPU to GPU 0.
|
||||
impl LweCiphertextConversionEngine<LweCiphertext32, CudaLweCiphertext32> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = 3_u32 << 20;
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_ciphertext: LweCiphertext32 =
|
||||
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaLweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext.lwe_dimension(), lwe_dimension);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_ciphertext(
|
||||
&mut self,
|
||||
input: &LweCiphertext32,
|
||||
) -> Result<CudaLweCiphertext32, LweCiphertextConversionError<CudaError>> {
|
||||
let stream = self.streams.first().unwrap();
|
||||
let data_per_gpu = input.lwe_dimension().to_lwe_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u32>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
Ok(unsafe { self.convert_lwe_ciphertext_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
input: &LweCiphertext32,
|
||||
) -> CudaLweCiphertext32 {
|
||||
let alloc_size = input.lwe_dimension().to_lwe_size().0 as u32;
|
||||
let input_slice = input.0.as_tensor().as_slice();
|
||||
let stream = self.streams.first().unwrap();
|
||||
let mut vec = stream.malloc::<u32>(alloc_size);
|
||||
stream.copy_to_gpu::<u32>(&mut vec, input_slice);
|
||||
CudaLweCiphertext32(CudaLweCiphertext::<u32> {
|
||||
d_vec: vec,
|
||||
lwe_dimension: input.lwe_dimension(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE ciphertext with 32 bits of precision from GPU 0 to CPU.
|
||||
impl LweCiphertextConversionEngine<CudaLweCiphertext32, LweCiphertext32> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = 3_u32 << 20;
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_ciphertext: LweCiphertext32 =
|
||||
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaLweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
|
||||
///
|
||||
/// let h_ciphertext_output: LweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&d_ciphertext)?;
|
||||
/// assert_eq!(h_ciphertext_output.lwe_dimension(), lwe_dimension);
|
||||
/// assert_eq!(h_ciphertext, h_ciphertext_output);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_ciphertext(
|
||||
&mut self,
|
||||
input: &CudaLweCiphertext32,
|
||||
) -> Result<LweCiphertext32, LweCiphertextConversionError<CudaError>> {
|
||||
Ok(unsafe { self.convert_lwe_ciphertext_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
input: &CudaLweCiphertext32,
|
||||
) -> LweCiphertext32 {
|
||||
let mut output = vec![0_u32; input.lwe_dimension().to_lwe_size().0];
|
||||
let stream = self.streams.first().unwrap();
|
||||
stream.copy_to_cpu::<u32>(&mut output, &input.0.d_vec);
|
||||
LweCiphertext32(LweCiphertext::from_container(output))
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE ciphertext with 64 bits of precision from CPU to GPU 0.
|
||||
impl LweCiphertextConversionEngine<LweCiphertext64, CudaLweCiphertext64> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = 3_u64 << 20;
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext: Plaintext64 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_ciphertext: LweCiphertext64 =
|
||||
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaLweCiphertext64 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext.lwe_dimension(), lwe_dimension);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_ciphertext(
|
||||
&mut self,
|
||||
input: &LweCiphertext64,
|
||||
) -> Result<CudaLweCiphertext64, LweCiphertextConversionError<CudaError>> {
|
||||
let stream = self.streams.first().unwrap();
|
||||
let data_per_gpu = input.lwe_dimension().to_lwe_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
Ok(unsafe { self.convert_lwe_ciphertext_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
input: &LweCiphertext64,
|
||||
) -> CudaLweCiphertext64 {
|
||||
let alloc_size = input.lwe_dimension().to_lwe_size().0 as u32;
|
||||
let input_slice = input.0.as_tensor().as_slice();
|
||||
let stream = self.streams.first().unwrap();
|
||||
let mut vec = stream.malloc::<u64>(alloc_size);
|
||||
stream.copy_to_gpu::<u64>(&mut vec, input_slice);
|
||||
CudaLweCiphertext64(CudaLweCiphertext::<u64> {
|
||||
d_vec: vec,
|
||||
lwe_dimension: input.lwe_dimension(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE ciphertext with 64 bits of precision from GPU 0 to CPU.
|
||||
impl LweCiphertextConversionEngine<CudaLweCiphertext64, LweCiphertext64> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 50 bits)
|
||||
/// let input = 3_u64 << 50;
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext: Plaintext64 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_ciphertext: LweCiphertext64 =
|
||||
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaLweCiphertext64 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
|
||||
///
|
||||
/// let h_ciphertext_output: LweCiphertext64 = cuda_engine.convert_lwe_ciphertext(&d_ciphertext)?;
|
||||
/// assert_eq!(h_ciphertext_output.lwe_dimension(), lwe_dimension);
|
||||
/// assert_eq!(h_ciphertext, h_ciphertext_output);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_ciphertext(
|
||||
&mut self,
|
||||
input: &CudaLweCiphertext64,
|
||||
) -> Result<LweCiphertext64, LweCiphertextConversionError<CudaError>> {
|
||||
Ok(unsafe { self.convert_lwe_ciphertext_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
input: &CudaLweCiphertext64,
|
||||
) -> LweCiphertext64 {
|
||||
let mut output = vec![0_u64; input.lwe_dimension().to_lwe_size().0];
|
||||
let stream = self.streams.first().unwrap();
|
||||
stream.copy_to_cpu::<u64>(&mut output, &input.0.d_vec);
|
||||
LweCiphertext64(LweCiphertext::from_container(output))
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a view of an LWE ciphertext with 64 bits of precision from CPU to GPU 0.
|
||||
impl LweCiphertextConversionEngine<LweCiphertextView64<'_>, CudaLweCiphertext64> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = 3_u64 << 20;
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext: Plaintext64 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_ciphertext: LweCiphertext64 =
|
||||
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
|
||||
///
|
||||
/// // Creates a LweCiphertextView64 object from LweCiphertext64
|
||||
/// let h_raw_ciphertext: Vec<u64> =
|
||||
/// default_engine.consume_retrieve_lwe_ciphertext(h_ciphertext)?;
|
||||
/// let mut h_view_ciphertext: LweCiphertextView64 =
|
||||
/// default_engine.create_lwe_ciphertext_from(h_raw_ciphertext.as_slice())?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaLweCiphertext64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext(&h_view_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext.lwe_dimension(), lwe_dimension);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_ciphertext(
|
||||
&mut self,
|
||||
input: &LweCiphertextView64,
|
||||
) -> Result<CudaLweCiphertext64, LweCiphertextConversionError<CudaError>> {
|
||||
let stream = &self.streams[0];
|
||||
let data_per_gpu = input.lwe_dimension().to_lwe_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
Ok(unsafe { self.convert_lwe_ciphertext_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
input: &LweCiphertextView64,
|
||||
) -> CudaLweCiphertext64 {
|
||||
let alloc_size = input.lwe_dimension().to_lwe_size().0 as u32;
|
||||
let input_slice = input.0.as_tensor().as_slice();
|
||||
let stream = &self.streams[0];
|
||||
let mut d_vec = stream.malloc::<u64>(alloc_size);
|
||||
stream.copy_to_gpu::<u64>(&mut d_vec, input_slice);
|
||||
CudaLweCiphertext64(CudaLweCiphertext::<u64> {
|
||||
d_vec,
|
||||
lwe_dimension: input.lwe_dimension(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
use crate::core_crypto::backends::cuda::engines::CudaError;
|
||||
use crate::core_crypto::backends::cuda::implementation::engines::{
|
||||
check_base_log, check_glwe_dim, CudaEngine,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::{
|
||||
CudaFourierLweBootstrapKey32, CudaFourierLweBootstrapKey64, CudaGlweCiphertext32,
|
||||
CudaGlweCiphertext64, CudaLweCiphertext32, CudaLweCiphertext64,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::private::device::NumberOfSamples;
|
||||
use crate::core_crypto::prelude::LweCiphertextIndex;
|
||||
use crate::core_crypto::specification::engines::{
|
||||
LweCiphertextDiscardingBootstrapEngine, LweCiphertextDiscardingBootstrapError,
|
||||
};
|
||||
use crate::core_crypto::specification::entities::LweBootstrapKeyEntity;
|
||||
|
||||
impl From<CudaError> for LweCiphertextDiscardingBootstrapError<CudaError> {
|
||||
fn from(err: CudaError) -> Self {
|
||||
Self::Engine(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// A discard bootstrap on an input ciphertext with 32 bits of precision.
|
||||
/// The input bootstrap key is in the Fourier domain.
|
||||
impl
|
||||
LweCiphertextDiscardingBootstrapEngine<
|
||||
CudaFourierLweBootstrapKey32,
|
||||
CudaGlweCiphertext32,
|
||||
CudaLweCiphertext32,
|
||||
CudaLweCiphertext32,
|
||||
> for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// DecompositionBaseLog, DecompositionLevelCount, GlweDimension, PolynomialSize,
|
||||
/// };
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let (lwe_dim, lwe_dim_output, glwe_dim, poly_size) = (
|
||||
/// LweDimension(130),
|
||||
/// LweDimension(512),
|
||||
/// GlweDimension(1),
|
||||
/// PolynomialSize(512),
|
||||
/// );
|
||||
/// let log_degree = f64::log2(poly_size.0 as f64) as i32;
|
||||
/// let val: u32 = ((poly_size.0 as f64 - (10. * f64::sqrt((lwe_dim.0 as f64) / 16.0)))
|
||||
/// * 2_f64.powi(32 - log_degree - 1)) as u32;
|
||||
/// let input = val;
|
||||
/// let noise = Variance(2_f64.powf(-29.));
|
||||
/// let (dec_lc, dec_bl) = (DecompositionLevelCount(3), DecompositionBaseLog(7));
|
||||
/// // An identity function is applied during the bootstrap
|
||||
/// let mut lut = vec![0u32; poly_size.0];
|
||||
/// for i in 0..poly_size.0 {
|
||||
/// let l = (i as f64 * 2_f64.powi(32 - log_degree - 1)) as u32;
|
||||
/// lut[i] = l;
|
||||
/// }
|
||||
///
|
||||
/// // 1. default engine
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// // create a vector of LWE ciphertexts
|
||||
/// let h_input_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dim)?;
|
||||
/// let h_input_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_input_ciphertext: LweCiphertext32 =
|
||||
/// default_engine.encrypt_lwe_ciphertext(&h_input_key, &h_input_plaintext, noise)?;
|
||||
/// // create a GLWE ciphertext containing an encryption of the LUT
|
||||
/// let h_lut_plaintext_vector = default_engine.create_plaintext_vector_from(&lut)?;
|
||||
/// let h_lut_key: GlweSecretKey32 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dim, poly_size)?;
|
||||
/// let h_lut =
|
||||
/// default_engine.encrypt_glwe_ciphertext(&h_lut_key, &h_lut_plaintext_vector, noise)?;
|
||||
/// // create a BSK
|
||||
/// let h_bootstrap_key: LweBootstrapKey32 = default_engine.generate_new_lwe_bootstrap_key(
|
||||
/// &h_input_key,
|
||||
/// &h_lut_key,
|
||||
/// dec_bl,
|
||||
/// dec_lc,
|
||||
/// noise,
|
||||
/// )?;
|
||||
/// // initialize an output LWE ciphertext vector
|
||||
/// let h_dummy_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dim_output)?;
|
||||
///
|
||||
/// // 2. cuda engine
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// // convert input to GPU 0
|
||||
/// let d_input_ciphertext: CudaLweCiphertext32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext(&h_input_ciphertext)?;
|
||||
/// // convert accumulator to GPU
|
||||
/// let d_input_lut: CudaGlweCiphertext32 = cuda_engine.convert_glwe_ciphertext(&h_lut)?;
|
||||
/// // convert BSK to GPU (and from Standard to Fourier representations)
|
||||
/// let d_fourier_bsk: CudaFourierLweBootstrapKey32 =
|
||||
/// cuda_engine.convert_lwe_bootstrap_key(&h_bootstrap_key)?;
|
||||
/// // launch bootstrap on GPU
|
||||
/// let h_zero_output_ciphertext: LweCiphertext32 =
|
||||
/// default_engine.zero_encrypt_lwe_ciphertext(&h_dummy_key, noise)?;
|
||||
/// let mut d_output_ciphertext: CudaLweCiphertext32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext(&h_zero_output_ciphertext)?;
|
||||
/// cuda_engine.discard_bootstrap_lwe_ciphertext(
|
||||
/// &mut d_output_ciphertext,
|
||||
/// &d_input_ciphertext,
|
||||
/// &d_input_lut,
|
||||
/// &d_fourier_bsk,
|
||||
/// )?;
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_bootstrap_lwe_ciphertext(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertext32,
|
||||
input: &CudaLweCiphertext32,
|
||||
acc: &CudaGlweCiphertext32,
|
||||
bsk: &CudaFourierLweBootstrapKey32,
|
||||
) -> Result<(), LweCiphertextDiscardingBootstrapError<CudaError>> {
|
||||
LweCiphertextDiscardingBootstrapError::perform_generic_checks(output, input, acc, bsk)?;
|
||||
let poly_size = bsk.polynomial_size();
|
||||
check_poly_size!(poly_size);
|
||||
let glwe_dim = bsk.glwe_dimension();
|
||||
check_glwe_dim!(glwe_dim);
|
||||
let base_log = bsk.decomposition_base_log();
|
||||
check_base_log!(base_log);
|
||||
unsafe { self.discard_bootstrap_lwe_ciphertext_unchecked(output, input, acc, bsk) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_bootstrap_lwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertext32,
|
||||
input: &CudaLweCiphertext32,
|
||||
acc: &CudaGlweCiphertext32,
|
||||
bsk: &CudaFourierLweBootstrapKey32,
|
||||
) {
|
||||
let stream = self.streams.first().unwrap();
|
||||
let mut test_vector_indexes = stream.malloc::<u32>(1);
|
||||
stream.copy_to_gpu(&mut test_vector_indexes, &[0]);
|
||||
|
||||
stream.discard_bootstrap_low_latency_lwe_ciphertext_vector::<u32>(
|
||||
&mut output.0.d_vec,
|
||||
&acc.0.d_vec,
|
||||
&test_vector_indexes,
|
||||
&input.0.d_vec,
|
||||
bsk.0.d_vecs.first().unwrap(),
|
||||
input.0.lwe_dimension,
|
||||
bsk.glwe_dimension(),
|
||||
bsk.polynomial_size(),
|
||||
bsk.decomposition_base_log(),
|
||||
bsk.decomposition_level_count(),
|
||||
NumberOfSamples(1),
|
||||
LweCiphertextIndex(0),
|
||||
self.get_cuda_shared_memory(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// A discard bootstrap on an input ciphertext with 64 bits of precision.
|
||||
/// The input bootstrap key is in the Fourier domain.
|
||||
impl
|
||||
LweCiphertextDiscardingBootstrapEngine<
|
||||
CudaFourierLweBootstrapKey64,
|
||||
CudaGlweCiphertext64,
|
||||
CudaLweCiphertext64,
|
||||
CudaLweCiphertext64,
|
||||
> for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// DecompositionBaseLog, DecompositionLevelCount, GlweDimension, PolynomialSize,
|
||||
/// };
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let (lwe_dim, lwe_dim_output, glwe_dim, poly_size) = (
|
||||
/// LweDimension(130),
|
||||
/// LweDimension(512),
|
||||
/// GlweDimension(1),
|
||||
/// PolynomialSize(512),
|
||||
/// );
|
||||
/// let log_degree = f64::log2(poly_size.0 as f64) as i64;
|
||||
/// let val: u64 = ((poly_size.0 as f64 - (10. * f64::sqrt((lwe_dim.0 as f64) / 16.0)))
|
||||
/// * 2_f64.powi((64 - log_degree - 1) as i32)) as u64;
|
||||
/// let input = val;
|
||||
/// let noise = Variance(2_f64.powf(-29.));
|
||||
/// let (dec_lc, dec_bl) = (DecompositionLevelCount(3), DecompositionBaseLog(7));
|
||||
/// // An identity function is applied during the bootstrap
|
||||
/// let mut lut = vec![0u64; poly_size.0];
|
||||
/// for i in 0..poly_size.0 {
|
||||
/// let l = (i as f64 * 2_f64.powi((64 - log_degree - 1) as i32)) as u64;
|
||||
/// lut[i] = l;
|
||||
/// }
|
||||
///
|
||||
/// // 1. default engine
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// // create a vector of LWE ciphertexts
|
||||
/// let h_input_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dim)?;
|
||||
/// let h_input_plaintext: Plaintext64 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_input_ciphertext: LweCiphertext64 =
|
||||
/// default_engine.encrypt_lwe_ciphertext(&h_input_key, &h_input_plaintext, noise)?;
|
||||
/// // create a GLWE ciphertext containing an encryption of the LUT
|
||||
/// let h_lut_plaintext_vector = default_engine.create_plaintext_vector_from(&lut)?;
|
||||
/// let h_lut_key: GlweSecretKey64 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dim, poly_size)?;
|
||||
/// let h_lut =
|
||||
/// default_engine.encrypt_glwe_ciphertext(&h_lut_key, &h_lut_plaintext_vector, noise)?;
|
||||
/// // create a BSK
|
||||
/// let h_bootstrap_key: LweBootstrapKey64 = default_engine.generate_new_lwe_bootstrap_key(
|
||||
/// &h_input_key,
|
||||
/// &h_lut_key,
|
||||
/// dec_bl,
|
||||
/// dec_lc,
|
||||
/// noise,
|
||||
/// )?;
|
||||
/// // initialize an output LWE ciphertext vector
|
||||
/// let h_dummy_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dim_output)?;
|
||||
///
|
||||
/// // 2. cuda engine
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// // convert input to GPU 0
|
||||
/// let d_input_ciphertext: CudaLweCiphertext64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext(&h_input_ciphertext)?;
|
||||
/// // convert accumulator to GPU
|
||||
/// let d_input_lut: CudaGlweCiphertext64 = cuda_engine.convert_glwe_ciphertext(&h_lut)?;
|
||||
/// // convert BSK to GPU (and from Standard to Fourier representations)
|
||||
/// let d_fourier_bsk: CudaFourierLweBootstrapKey64 =
|
||||
/// cuda_engine.convert_lwe_bootstrap_key(&h_bootstrap_key)?;
|
||||
/// // launch bootstrap on GPU
|
||||
/// let h_zero_output_ciphertext: LweCiphertext64 =
|
||||
/// default_engine.zero_encrypt_lwe_ciphertext(&h_dummy_key, noise)?;
|
||||
/// let mut d_output_ciphertext: CudaLweCiphertext64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext(&h_zero_output_ciphertext)?;
|
||||
/// cuda_engine.discard_bootstrap_lwe_ciphertext(
|
||||
/// &mut d_output_ciphertext,
|
||||
/// &d_input_ciphertext,
|
||||
/// &d_input_lut,
|
||||
/// &d_fourier_bsk,
|
||||
/// )?;
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_bootstrap_lwe_ciphertext(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertext64,
|
||||
input: &CudaLweCiphertext64,
|
||||
acc: &CudaGlweCiphertext64,
|
||||
bsk: &CudaFourierLweBootstrapKey64,
|
||||
) -> Result<(), LweCiphertextDiscardingBootstrapError<CudaError>> {
|
||||
LweCiphertextDiscardingBootstrapError::perform_generic_checks(output, input, acc, bsk)?;
|
||||
let poly_size = bsk.polynomial_size();
|
||||
check_poly_size!(poly_size);
|
||||
let glwe_dim = bsk.glwe_dimension();
|
||||
check_glwe_dim!(glwe_dim);
|
||||
let base_log = bsk.decomposition_base_log();
|
||||
check_base_log!(base_log);
|
||||
unsafe { self.discard_bootstrap_lwe_ciphertext_unchecked(output, input, acc, bsk) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_bootstrap_lwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertext64,
|
||||
input: &CudaLweCiphertext64,
|
||||
acc: &CudaGlweCiphertext64,
|
||||
bsk: &CudaFourierLweBootstrapKey64,
|
||||
) {
|
||||
let stream = self.streams.first().unwrap();
|
||||
let mut test_vector_indexes = stream.malloc::<u32>(1);
|
||||
stream.copy_to_gpu(&mut test_vector_indexes, &[0]);
|
||||
|
||||
stream.discard_bootstrap_low_latency_lwe_ciphertext_vector::<u64>(
|
||||
&mut output.0.d_vec,
|
||||
&acc.0.d_vec,
|
||||
&test_vector_indexes,
|
||||
&input.0.d_vec,
|
||||
bsk.0.d_vecs.first().unwrap(),
|
||||
input.0.lwe_dimension,
|
||||
bsk.glwe_dimension(),
|
||||
bsk.polynomial_size(),
|
||||
bsk.decomposition_base_log(),
|
||||
bsk.decomposition_level_count(),
|
||||
NumberOfSamples(1),
|
||||
LweCiphertextIndex(0),
|
||||
self.get_cuda_shared_memory(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
use crate::core_crypto::backends::cuda::implementation::engines::{CudaEngine, CudaError};
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::{
|
||||
CudaLweCiphertext32, CudaLweCiphertext64,
|
||||
};
|
||||
use crate::core_crypto::commons::math::tensor::{AsMutSlice, AsRefSlice};
|
||||
use crate::core_crypto::prelude::{
|
||||
LweCiphertext32, LweCiphertextMutView32, LweCiphertextMutView64,
|
||||
};
|
||||
use crate::core_crypto::specification::engines::{
|
||||
LweCiphertextDiscardingConversionEngine, LweCiphertextDiscardingConversionError,
|
||||
};
|
||||
|
||||
/// # Description
|
||||
///
|
||||
/// Convert an LWE ciphertext with 32 bits of precision from GPU 0 to a view on the CPU.
|
||||
impl LweCiphertextDiscardingConversionEngine<CudaLweCiphertext32, LweCiphertextMutView32<'_>>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// use std::borrow::BorrowMut;
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 25 bits)
|
||||
/// let input = 3_u32 << 25;
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_ciphertext: LweCiphertext32 =
|
||||
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaLweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
|
||||
///
|
||||
/// // Prepares the output container
|
||||
/// let mut h_raw_output_ciphertext = vec![0_u32; lwe_dimension.0 + 1];
|
||||
/// let mut h_output_view_ciphertext: LweCiphertextMutView32 =
|
||||
/// default_engine.create_lwe_ciphertext_from(h_raw_output_ciphertext.as_mut_slice())?;
|
||||
///
|
||||
/// cuda_engine.discard_convert_lwe_ciphertext(&mut h_output_view_ciphertext, &d_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(h_output_view_ciphertext.lwe_dimension(), lwe_dimension);
|
||||
/// // Extracts the internal container
|
||||
/// let h_raw_input_ciphertext: Vec<u32> =
|
||||
/// default_engine.consume_retrieve_lwe_ciphertext(h_ciphertext)?;
|
||||
/// let h_raw_output_ciphertext: &[u32] =
|
||||
/// default_engine.consume_retrieve_lwe_ciphertext(h_output_view_ciphertext)?;
|
||||
/// assert_eq!(h_raw_input_ciphertext.as_slice(), h_raw_output_ciphertext);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_convert_lwe_ciphertext(
|
||||
&mut self,
|
||||
output: &mut LweCiphertextMutView32,
|
||||
input: &CudaLweCiphertext32,
|
||||
) -> Result<(), LweCiphertextDiscardingConversionError<CudaError>> {
|
||||
unsafe { self.discard_convert_lwe_ciphertext_unchecked(output, input) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_convert_lwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
output: &mut LweCiphertextMutView32,
|
||||
input: &CudaLweCiphertext32,
|
||||
) {
|
||||
let stream = &self.streams[0];
|
||||
stream.copy_to_cpu::<u32>(output.0.tensor.as_mut_slice(), &input.0.d_vec);
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
///
|
||||
/// Convert an LWE ciphertext with 32 bits of precision from GPU 0 to a ciphertext on the CPU.
|
||||
impl LweCiphertextDiscardingConversionEngine<CudaLweCiphertext32, LweCiphertext32> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// use std::borrow::BorrowMut;
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 25 bits)
|
||||
/// let input = 3_u32 << 25;
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_ciphertext: LweCiphertext32 =
|
||||
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaLweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
|
||||
///
|
||||
/// // Prepares the output container
|
||||
/// let h_raw_output_ciphertext = vec![0_u32; lwe_dimension.0 + 1];
|
||||
/// let mut h_output_ciphertext: LweCiphertext32 =
|
||||
/// default_engine.create_lwe_ciphertext_from(h_raw_output_ciphertext)?;
|
||||
///
|
||||
/// cuda_engine.discard_convert_lwe_ciphertext(&mut h_output_ciphertext, &d_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(h_output_ciphertext.lwe_dimension(), lwe_dimension);
|
||||
/// // Extracts the internal container
|
||||
/// let h_raw_input_ciphertext: Vec<u32> =
|
||||
/// default_engine.consume_retrieve_lwe_ciphertext(h_ciphertext)?;
|
||||
/// let h_raw_output_ciphertext: Vec<u32> =
|
||||
/// default_engine.consume_retrieve_lwe_ciphertext(h_output_ciphertext)?;
|
||||
/// assert_eq!(h_raw_input_ciphertext, h_raw_output_ciphertext);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_convert_lwe_ciphertext(
|
||||
&mut self,
|
||||
output: &mut LweCiphertext32,
|
||||
input: &CudaLweCiphertext32,
|
||||
) -> Result<(), LweCiphertextDiscardingConversionError<CudaError>> {
|
||||
unsafe { self.discard_convert_lwe_ciphertext_unchecked(output, input) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_convert_lwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
output: &mut LweCiphertext32,
|
||||
input: &CudaLweCiphertext32,
|
||||
) {
|
||||
let stream = &self.streams[0];
|
||||
stream.copy_to_cpu::<u32>(output.0.tensor.as_mut_slice(), &input.0.d_vec);
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
///
|
||||
/// Convert an LWE ciphertext with 32 bits of precision from CPU to a ciphertext on the GPU 0.
|
||||
impl LweCiphertextDiscardingConversionEngine<LweCiphertext32, CudaLweCiphertext32> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// use std::borrow::BorrowMut;
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 25 bits)
|
||||
/// let input = 3_u32 << 25;
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_ciphertext: LweCiphertext32 =
|
||||
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let mut d_ciphertext: CudaLweCiphertext32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
|
||||
///
|
||||
/// let h_ciphertext_out: LweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&d_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(h_ciphertext, h_ciphertext_out);
|
||||
///
|
||||
/// // Prepare input for discarding convert
|
||||
/// let input_2 = 5_u32 << 25;
|
||||
/// let h_plaintext_2: Plaintext32 = default_engine.create_plaintext_from(&input_2)?;
|
||||
/// let mut h_ciphertext_2: LweCiphertext32 = default_engine
|
||||
/// .trivially_encrypt_lwe_ciphertext(lwe_dimension.to_lwe_size(), &h_plaintext_2)?;
|
||||
///
|
||||
/// cuda_engine.discard_convert_lwe_ciphertext(&mut d_ciphertext, &h_ciphertext_2)?;
|
||||
///
|
||||
/// let h_ciphertext_out_2: LweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&d_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(h_ciphertext_2, h_ciphertext_out_2);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_convert_lwe_ciphertext(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertext32,
|
||||
input: &LweCiphertext32,
|
||||
) -> Result<(), LweCiphertextDiscardingConversionError<CudaError>> {
|
||||
unsafe { self.discard_convert_lwe_ciphertext_unchecked(output, input) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_convert_lwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertext32,
|
||||
input: &LweCiphertext32,
|
||||
) {
|
||||
let stream = &self.streams[0];
|
||||
stream.copy_to_gpu::<u32>(&mut output.0.d_vec, input.0.tensor.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
///
|
||||
/// Convert an LWE ciphertext with 64 bits of precision from GPU 0 to a view on the CPU.
|
||||
impl LweCiphertextDiscardingConversionEngine<CudaLweCiphertext64, LweCiphertextMutView64<'_>>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// use std::borrow::BorrowMut;
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 25 bits)
|
||||
/// let input = 3_u64 << 50;
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext: Plaintext64 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_ciphertext: LweCiphertext64 =
|
||||
/// default_engine.encrypt_lwe_ciphertext(&h_key, &h_plaintext, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaLweCiphertext64 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
|
||||
///
|
||||
/// // Prepares the output container
|
||||
/// let mut h_raw_output_ciphertext = vec![0_u64; lwe_dimension.0 + 1];
|
||||
/// let mut h_view_output_ciphertext: LweCiphertextMutView64 =
|
||||
/// default_engine.create_lwe_ciphertext_from(h_raw_output_ciphertext.as_mut_slice())?;
|
||||
///
|
||||
/// cuda_engine
|
||||
/// .discard_convert_lwe_ciphertext(h_view_output_ciphertext.borrow_mut(), &d_ciphertext)?;
|
||||
///
|
||||
/// assert_eq!(h_view_output_ciphertext.lwe_dimension(), lwe_dimension);
|
||||
/// // Extracts the internal container
|
||||
/// let h_raw_input_ciphertext: Vec<u64> =
|
||||
/// default_engine.consume_retrieve_lwe_ciphertext(h_ciphertext)?;
|
||||
/// let h_raw_output_ciphertext: &[u64] =
|
||||
/// default_engine.consume_retrieve_lwe_ciphertext(h_view_output_ciphertext)?;
|
||||
/// assert_eq!(h_raw_input_ciphertext, h_raw_output_ciphertext.to_vec());
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_convert_lwe_ciphertext(
|
||||
&mut self,
|
||||
output: &mut LweCiphertextMutView64,
|
||||
input: &CudaLweCiphertext64,
|
||||
) -> Result<(), LweCiphertextDiscardingConversionError<CudaError>> {
|
||||
unsafe { self.discard_convert_lwe_ciphertext_unchecked(output, input) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_convert_lwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
output: &mut LweCiphertextMutView64,
|
||||
input: &CudaLweCiphertext64,
|
||||
) {
|
||||
let stream = &self.streams[0];
|
||||
stream.copy_to_cpu::<u64>(output.0.tensor.as_mut_slice(), &input.0.d_vec);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
use crate::core_crypto::backends::cuda::engines::CudaError;
|
||||
use crate::core_crypto::backends::cuda::implementation::engines::CudaEngine;
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::{
|
||||
CudaLweCiphertext32, CudaLweCiphertext64, CudaLweKeyswitchKey32, CudaLweKeyswitchKey64,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::private::device::NumberOfSamples;
|
||||
use crate::core_crypto::specification::engines::{
|
||||
LweCiphertextDiscardingKeyswitchEngine, LweCiphertextDiscardingKeyswitchError,
|
||||
};
|
||||
use crate::core_crypto::specification::entities::LweKeyswitchKeyEntity;
|
||||
|
||||
impl From<CudaError> for LweCiphertextDiscardingKeyswitchError<CudaError> {
|
||||
fn from(err: CudaError) -> Self {
|
||||
Self::Engine(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// A discard keyswitch on a vector of input ciphertext vectors with 32 bits of precision.
|
||||
impl
|
||||
LweCiphertextDiscardingKeyswitchEngine<
|
||||
CudaLweKeyswitchKey32,
|
||||
CudaLweCiphertext32,
|
||||
CudaLweCiphertext32,
|
||||
> for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let input_lwe_dimension = LweDimension(6);
|
||||
/// let output_lwe_dimension = LweDimension(3);
|
||||
/// let decomposition_level_count = DecompositionLevelCount(2);
|
||||
/// let decomposition_base_log = DecompositionBaseLog(8);
|
||||
///
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = 3_u32 << 20;
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// // Generate two secret keys
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let input_key: LweSecretKey32 =
|
||||
/// default_engine.generate_new_lwe_secret_key(input_lwe_dimension)?;
|
||||
/// let output_key: LweSecretKey32 =
|
||||
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
|
||||
///
|
||||
/// // Generate keyswitch keys to switch between first_key and second_key
|
||||
/// let h_ksk = default_engine.generate_new_lwe_keyswitch_key(
|
||||
/// &input_key,
|
||||
/// &output_key,
|
||||
/// decomposition_level_count,
|
||||
/// decomposition_base_log,
|
||||
/// noise,
|
||||
/// )?;
|
||||
///
|
||||
/// // Encrypt something
|
||||
/// let h_plaintext: Plaintext32 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_ciphertext: LweCiphertext32 =
|
||||
/// default_engine.encrypt_lwe_ciphertext(&input_key, &h_plaintext, noise)?;
|
||||
///
|
||||
/// // Copy to the GPU
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaLweCiphertext32 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
|
||||
/// let d_ksk: CudaLweKeyswitchKey32 = cuda_engine.convert_lwe_keyswitch_key(&h_ksk)?;
|
||||
///
|
||||
/// // launch keyswitch on GPU
|
||||
/// let h_dummy_key: LweSecretKey32 =
|
||||
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
|
||||
/// let h_zero_ciphertext: LweCiphertext32 =
|
||||
/// default_engine.zero_encrypt_lwe_ciphertext(&h_dummy_key, noise)?;
|
||||
///
|
||||
/// let mut d_keyswitched_ciphertext: CudaLweCiphertext32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext(&h_zero_ciphertext)?;
|
||||
/// cuda_engine.discard_keyswitch_lwe_ciphertext(
|
||||
/// &mut d_keyswitched_ciphertext,
|
||||
/// &d_ciphertext,
|
||||
/// &d_ksk,
|
||||
/// )?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// d_keyswitched_ciphertext.lwe_dimension(),
|
||||
/// output_lwe_dimension
|
||||
/// );
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_keyswitch_lwe_ciphertext(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertext32,
|
||||
input: &CudaLweCiphertext32,
|
||||
ksk: &CudaLweKeyswitchKey32,
|
||||
) -> Result<(), LweCiphertextDiscardingKeyswitchError<CudaError>> {
|
||||
LweCiphertextDiscardingKeyswitchError::perform_generic_checks(output, input, ksk)?;
|
||||
unsafe { self.discard_keyswitch_lwe_ciphertext_unchecked(output, input, ksk) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_keyswitch_lwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertext32,
|
||||
input: &CudaLweCiphertext32,
|
||||
ksk: &CudaLweKeyswitchKey32,
|
||||
) {
|
||||
let stream = &self.streams[0];
|
||||
|
||||
stream.discard_keyswitch_lwe_ciphertext_vector::<u32>(
|
||||
&mut output.0.d_vec,
|
||||
&input.0.d_vec,
|
||||
input.0.lwe_dimension,
|
||||
output.0.lwe_dimension,
|
||||
ksk.0.d_vecs.first().unwrap(),
|
||||
ksk.decomposition_base_log(),
|
||||
ksk.decomposition_level_count(),
|
||||
NumberOfSamples(1),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// A discard keyswitch on a vector of input ciphertext vectors with 64 bits of precision.
|
||||
impl
|
||||
LweCiphertextDiscardingKeyswitchEngine<
|
||||
CudaLweKeyswitchKey64,
|
||||
CudaLweCiphertext64,
|
||||
CudaLweCiphertext64,
|
||||
> for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let input_lwe_dimension = LweDimension(6);
|
||||
/// let output_lwe_dimension = LweDimension(3);
|
||||
/// let decomposition_level_count = DecompositionLevelCount(2);
|
||||
/// let decomposition_base_log = DecompositionBaseLog(8);
|
||||
///
|
||||
/// // Here a hard-set encoding is applied (shift by 50 bits)
|
||||
/// let input = 3_u64 << 50;
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// // Generate two secret keys
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let input_key: LweSecretKey64 =
|
||||
/// default_engine.generate_new_lwe_secret_key(input_lwe_dimension)?;
|
||||
/// let output_key: LweSecretKey64 =
|
||||
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
|
||||
///
|
||||
/// // Generate keyswitch keys to switch between first_key and second_key
|
||||
/// let h_ksk = default_engine.generate_new_lwe_keyswitch_key(
|
||||
/// &input_key,
|
||||
/// &output_key,
|
||||
/// decomposition_level_count,
|
||||
/// decomposition_base_log,
|
||||
/// noise,
|
||||
/// )?;
|
||||
///
|
||||
/// // Encrypt something
|
||||
/// let h_plaintext: Plaintext64 = default_engine.create_plaintext_from(&input)?;
|
||||
/// let mut h_ciphertext: LweCiphertext64 =
|
||||
/// default_engine.encrypt_lwe_ciphertext(&input_key, &h_plaintext, noise)?;
|
||||
///
|
||||
/// // Copy to the GPU
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext: CudaLweCiphertext64 = cuda_engine.convert_lwe_ciphertext(&h_ciphertext)?;
|
||||
/// let d_ksk: CudaLweKeyswitchKey64 = cuda_engine.convert_lwe_keyswitch_key(&h_ksk)?;
|
||||
///
|
||||
/// // launch keyswitch on GPU
|
||||
/// let h_dummy_key: LweSecretKey64 =
|
||||
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
|
||||
/// let h_zero_ciphertext: LweCiphertext64 =
|
||||
/// default_engine.zero_encrypt_lwe_ciphertext(&h_dummy_key, noise)?;
|
||||
///
|
||||
/// let mut d_keyswitched_ciphertext: CudaLweCiphertext64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext(&h_zero_ciphertext)?;
|
||||
/// cuda_engine.discard_keyswitch_lwe_ciphertext(
|
||||
/// &mut d_keyswitched_ciphertext,
|
||||
/// &d_ciphertext,
|
||||
/// &d_ksk,
|
||||
/// )?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// d_keyswitched_ciphertext.lwe_dimension(),
|
||||
/// output_lwe_dimension
|
||||
/// );
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_keyswitch_lwe_ciphertext(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertext64,
|
||||
input: &CudaLweCiphertext64,
|
||||
ksk: &CudaLweKeyswitchKey64,
|
||||
) -> Result<(), LweCiphertextDiscardingKeyswitchError<CudaError>> {
|
||||
LweCiphertextDiscardingKeyswitchError::perform_generic_checks(output, input, ksk)?;
|
||||
unsafe { self.discard_keyswitch_lwe_ciphertext_unchecked(output, input, ksk) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_keyswitch_lwe_ciphertext_unchecked(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertext64,
|
||||
input: &CudaLweCiphertext64,
|
||||
ksk: &CudaLweKeyswitchKey64,
|
||||
) {
|
||||
let stream = &self.streams[0];
|
||||
|
||||
stream.discard_keyswitch_lwe_ciphertext_vector::<u64>(
|
||||
&mut output.0.d_vec,
|
||||
&input.0.d_vec,
|
||||
input.0.lwe_dimension,
|
||||
output.0.lwe_dimension,
|
||||
ksk.0.d_vecs.first().unwrap(),
|
||||
ksk.decomposition_base_log(),
|
||||
ksk.decomposition_level_count(),
|
||||
NumberOfSamples(1),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,688 @@
|
||||
use crate::core_crypto::backends::cuda::implementation::engines::{CudaEngine, CudaError};
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::{
|
||||
CudaLweCiphertextVector32, CudaLweCiphertextVector64,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::private::crypto::lwe::list::{
|
||||
copy_lwe_ciphertext_vector_from_cpu_to_gpu, copy_lwe_ciphertext_vector_from_gpu_to_cpu,
|
||||
CudaLweList,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::private::device::GpuIndex;
|
||||
use crate::core_crypto::backends::cuda::private::{
|
||||
compute_number_of_samples_on_gpu, number_of_active_gpus,
|
||||
};
|
||||
use crate::core_crypto::commons::crypto::lwe::LweList;
|
||||
use crate::core_crypto::prelude::{
|
||||
CiphertextCount, LweCiphertextVector32, LweCiphertextVector64, LweCiphertextVectorMutView32,
|
||||
LweCiphertextVectorMutView64, LweCiphertextVectorView32, LweCiphertextVectorView64,
|
||||
};
|
||||
use crate::core_crypto::specification::engines::{
|
||||
LweCiphertextVectorConversionEngine, LweCiphertextVectorConversionError,
|
||||
};
|
||||
use crate::core_crypto::specification::entities::LweCiphertextVectorEntity;
|
||||
|
||||
impl From<CudaError> for LweCiphertextVectorConversionError<CudaError> {
|
||||
fn from(err: CudaError) -> Self {
|
||||
Self::Engine(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE ciphertext vector with 32 bits of precision from CPU to GPU.
|
||||
///
|
||||
/// The input ciphertext vector is split over GPUs, so that each GPU contains
|
||||
/// the total amount of ciphertexts divided by the number of GPUs on the machine.
|
||||
/// The last GPU takes the remainder of the division if there is any.
|
||||
impl LweCiphertextVectorConversionEngine<LweCiphertextVector32, CudaLweCiphertextVector32>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u32 << 20; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext_vector: PlaintextVector32 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: LweCiphertextVector32 =
|
||||
/// default_engine.encrypt_lwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaLweCiphertextVector32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_ciphertext_vector)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext_vector.lwe_dimension(), lwe_dimension);
|
||||
/// assert_eq!(
|
||||
/// d_ciphertext_vector.lwe_ciphertext_count(),
|
||||
/// LweCiphertextCount(3)
|
||||
/// );
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &LweCiphertextVector32,
|
||||
) -> Result<CudaLweCiphertextVector32, LweCiphertextVectorConversionError<CudaError>> {
|
||||
let number_of_gpus = number_of_active_gpus(
|
||||
self.get_number_of_gpus(),
|
||||
CiphertextCount(input.lwe_ciphertext_count().0),
|
||||
);
|
||||
for gpu_index in 0..number_of_gpus.0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let samples = compute_number_of_samples_on_gpu(
|
||||
self.get_number_of_gpus(),
|
||||
CiphertextCount(input.lwe_ciphertext_count().0),
|
||||
GpuIndex(gpu_index),
|
||||
);
|
||||
let data_per_gpu = samples.0 * input.lwe_dimension().to_lwe_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u32>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_lwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &LweCiphertextVector32,
|
||||
) -> CudaLweCiphertextVector32 {
|
||||
let vecs = copy_lwe_ciphertext_vector_from_cpu_to_gpu::<u32, _>(
|
||||
self.get_cuda_streams(),
|
||||
&input.0,
|
||||
self.get_number_of_gpus(),
|
||||
);
|
||||
CudaLweCiphertextVector32(CudaLweList::<u32> {
|
||||
d_vecs: vecs,
|
||||
lwe_ciphertext_count: input.lwe_ciphertext_count(),
|
||||
lwe_dimension: input.lwe_dimension(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE ciphertext vector with 32 bits of precision from GPU to CPU.
|
||||
/// The data from each GPU is copied into a part of an LweCiphertextVector32 on the CPU.
|
||||
impl LweCiphertextVectorConversionEngine<CudaLweCiphertextVector32, LweCiphertextVector32>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u32 << 20; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext_vector: PlaintextVector32 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: LweCiphertextVector32 =
|
||||
/// default_engine.encrypt_lwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaLweCiphertextVector32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_ciphertext_vector)?;
|
||||
///
|
||||
/// let h_ciphertext_vector_output: LweCiphertextVector32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&d_ciphertext_vector)?;
|
||||
/// assert_eq!(h_ciphertext_vector_output.lwe_dimension(), lwe_dimension);
|
||||
/// assert_eq!(
|
||||
/// h_ciphertext_vector_output.lwe_ciphertext_count(),
|
||||
/// LweCiphertextCount(3)
|
||||
/// );
|
||||
/// assert_eq!(h_ciphertext_vector, h_ciphertext_vector_output);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &CudaLweCiphertextVector32,
|
||||
) -> Result<LweCiphertextVector32, LweCiphertextVectorConversionError<CudaError>> {
|
||||
Ok(unsafe { self.convert_lwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &CudaLweCiphertextVector32,
|
||||
) -> LweCiphertextVector32 {
|
||||
let output = copy_lwe_ciphertext_vector_from_gpu_to_cpu::<u32>(
|
||||
self.get_cuda_streams(),
|
||||
&input.0,
|
||||
self.get_number_of_gpus(),
|
||||
);
|
||||
LweCiphertextVector32(LweList::from_container(
|
||||
output,
|
||||
input.lwe_dimension().to_lwe_size(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE ciphertext vector with 64 bits of precision from CPU to GPU.
|
||||
///
|
||||
/// The input ciphertext vector is split over GPUs, so that each GPU contains
|
||||
/// the total amount of ciphertexts divided by the number of GPUs on the machine.
|
||||
/// The last GPU takes the remainder of the division if there is any.
|
||||
impl LweCiphertextVectorConversionEngine<LweCiphertextVector64, CudaLweCiphertextVector64>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u64 << 20; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: LweCiphertextVector64 =
|
||||
/// default_engine.encrypt_lwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaLweCiphertextVector64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_ciphertext_vector)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext_vector.lwe_dimension(), lwe_dimension);
|
||||
/// assert_eq!(
|
||||
/// d_ciphertext_vector.lwe_ciphertext_count(),
|
||||
/// LweCiphertextCount(3)
|
||||
/// );
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &LweCiphertextVector64,
|
||||
) -> Result<CudaLweCiphertextVector64, LweCiphertextVectorConversionError<CudaError>> {
|
||||
let number_of_gpus = number_of_active_gpus(
|
||||
self.get_number_of_gpus(),
|
||||
CiphertextCount(input.lwe_ciphertext_count().0),
|
||||
);
|
||||
for gpu_index in 0..number_of_gpus.0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let samples = compute_number_of_samples_on_gpu(
|
||||
self.get_number_of_gpus(),
|
||||
CiphertextCount(input.lwe_ciphertext_count().0),
|
||||
GpuIndex(gpu_index),
|
||||
);
|
||||
let data_per_gpu = samples.0 * input.lwe_dimension().to_lwe_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_lwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &LweCiphertextVector64,
|
||||
) -> CudaLweCiphertextVector64 {
|
||||
let vecs = copy_lwe_ciphertext_vector_from_cpu_to_gpu::<u64, _>(
|
||||
self.get_cuda_streams(),
|
||||
&input.0,
|
||||
self.get_number_of_gpus(),
|
||||
);
|
||||
CudaLweCiphertextVector64(CudaLweList::<u64> {
|
||||
d_vecs: vecs,
|
||||
lwe_ciphertext_count: input.lwe_ciphertext_count(),
|
||||
lwe_dimension: input.lwe_dimension(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE ciphertext vector with 64 bits of precision from GPU to CPU.
|
||||
/// The data from each GPU is copied into a part of an LweCiphertextVector64 on the CPU.
|
||||
impl LweCiphertextVectorConversionEngine<CudaLweCiphertextVector64, LweCiphertextVector64>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u64 << 20; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: LweCiphertextVector64 =
|
||||
/// default_engine.encrypt_lwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaLweCiphertextVector64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_ciphertext_vector)?;
|
||||
///
|
||||
/// let h_ciphertext_vector_output: LweCiphertextVector64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&d_ciphertext_vector)?;
|
||||
/// assert_eq!(h_ciphertext_vector_output.lwe_dimension(), lwe_dimension);
|
||||
/// assert_eq!(
|
||||
/// h_ciphertext_vector_output.lwe_ciphertext_count(),
|
||||
/// LweCiphertextCount(3)
|
||||
/// );
|
||||
/// assert_eq!(h_ciphertext_vector, h_ciphertext_vector_output);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &CudaLweCiphertextVector64,
|
||||
) -> Result<LweCiphertextVector64, LweCiphertextVectorConversionError<CudaError>> {
|
||||
Ok(unsafe { self.convert_lwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &CudaLweCiphertextVector64,
|
||||
) -> LweCiphertextVector64 {
|
||||
let output = copy_lwe_ciphertext_vector_from_gpu_to_cpu::<u64>(
|
||||
self.get_cuda_streams(),
|
||||
&input.0,
|
||||
self.get_number_of_gpus(),
|
||||
);
|
||||
LweCiphertextVector64(LweList::from_container(
|
||||
output,
|
||||
input.lwe_dimension().to_lwe_size(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE ciphertext vector view with 32 bits of precision from CPU to GPU.
|
||||
///
|
||||
/// The input ciphertext vector view is split over GPUs, so that each GPU contains
|
||||
/// the total amount of ciphertexts divided by the number of GPUs on the machine.
|
||||
/// The last GPU takes the remainder of the division if there is any.
|
||||
impl LweCiphertextVectorConversionEngine<LweCiphertextVectorView32<'_>, CudaLweCiphertextVector32>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u32 << 20; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext_vector: PlaintextVector32 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: LweCiphertextVector32 =
|
||||
/// default_engine.encrypt_lwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
/// let lwe_ciphertext_count = h_ciphertext_vector.lwe_ciphertext_count();
|
||||
/// let lwe_size = h_ciphertext_vector.lwe_dimension().to_lwe_size();
|
||||
///
|
||||
/// let h_raw_ciphertext_vector: Vec<u32> =
|
||||
/// default_engine.consume_retrieve_lwe_ciphertext_vector(h_ciphertext_vector)?;
|
||||
/// let mut h_view_ciphertext_vector: LweCiphertextVectorView32 = default_engine
|
||||
/// .create_lwe_ciphertext_vector_from(h_raw_ciphertext_vector.as_slice(), lwe_size)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaLweCiphertextVector32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_view_ciphertext_vector)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext_vector.lwe_dimension(), lwe_dimension);
|
||||
/// assert_eq!(
|
||||
/// d_ciphertext_vector.lwe_ciphertext_count(),
|
||||
/// lwe_ciphertext_count
|
||||
/// );
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &LweCiphertextVectorView32,
|
||||
) -> Result<CudaLweCiphertextVector32, LweCiphertextVectorConversionError<CudaError>> {
|
||||
let number_of_gpus = number_of_active_gpus(
|
||||
self.get_number_of_gpus(),
|
||||
CiphertextCount(input.lwe_ciphertext_count().0),
|
||||
);
|
||||
for gpu_index in 0..number_of_gpus.0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let samples = compute_number_of_samples_on_gpu(
|
||||
self.get_number_of_gpus(),
|
||||
CiphertextCount(input.lwe_ciphertext_count().0),
|
||||
GpuIndex(gpu_index),
|
||||
);
|
||||
let data_per_gpu = samples.0 * input.lwe_dimension().to_lwe_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u32>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_lwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &LweCiphertextVectorView32,
|
||||
) -> CudaLweCiphertextVector32 {
|
||||
let vecs = copy_lwe_ciphertext_vector_from_cpu_to_gpu::<u32, _>(
|
||||
self.get_cuda_streams(),
|
||||
&input.0,
|
||||
self.get_number_of_gpus(),
|
||||
);
|
||||
CudaLweCiphertextVector32(CudaLweList::<u32> {
|
||||
d_vecs: vecs,
|
||||
lwe_ciphertext_count: input.lwe_ciphertext_count(),
|
||||
lwe_dimension: input.lwe_dimension(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE ciphertext vector view with 64 bits of precision from CPU to GPU.
|
||||
///
|
||||
/// The input ciphertext vector view is split over GPUs, so that each GPU contains
|
||||
/// the total amount of ciphertexts divided by the number of GPUs on the machine.
|
||||
/// The last GPU takes the remainder of the division if there is any.
|
||||
impl LweCiphertextVectorConversionEngine<LweCiphertextVectorView64<'_>, CudaLweCiphertextVector64>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 50 bits)
|
||||
/// let input = vec![3_u64 << 50; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: LweCiphertextVector64 =
|
||||
/// default_engine.encrypt_lwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
/// let lwe_ciphertext_count = h_ciphertext_vector.lwe_ciphertext_count();
|
||||
/// let lwe_size = h_ciphertext_vector.lwe_dimension().to_lwe_size();
|
||||
///
|
||||
/// let h_raw_ciphertext_vector: Vec<u64> =
|
||||
/// default_engine.consume_retrieve_lwe_ciphertext_vector(h_ciphertext_vector)?;
|
||||
/// let mut h_view_ciphertext_vector: LweCiphertextVectorView64 = default_engine
|
||||
/// .create_lwe_ciphertext_vector_from(h_raw_ciphertext_vector.as_slice(), lwe_size)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaLweCiphertextVector64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_view_ciphertext_vector)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext_vector.lwe_dimension(), lwe_dimension);
|
||||
/// assert_eq!(
|
||||
/// d_ciphertext_vector.lwe_ciphertext_count(),
|
||||
/// lwe_ciphertext_count
|
||||
/// );
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &LweCiphertextVectorView64,
|
||||
) -> Result<CudaLweCiphertextVector64, LweCiphertextVectorConversionError<CudaError>> {
|
||||
let number_of_gpus = number_of_active_gpus(
|
||||
self.get_number_of_gpus(),
|
||||
CiphertextCount(input.lwe_ciphertext_count().0),
|
||||
);
|
||||
for gpu_index in 0..number_of_gpus.0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let samples = compute_number_of_samples_on_gpu(
|
||||
self.get_number_of_gpus(),
|
||||
CiphertextCount(input.lwe_ciphertext_count().0),
|
||||
GpuIndex(gpu_index),
|
||||
);
|
||||
let data_per_gpu = samples.0 * input.lwe_dimension().to_lwe_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_lwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &LweCiphertextVectorView64,
|
||||
) -> CudaLweCiphertextVector64 {
|
||||
let vecs = copy_lwe_ciphertext_vector_from_cpu_to_gpu::<u64, _>(
|
||||
self.get_cuda_streams(),
|
||||
&input.0,
|
||||
self.get_number_of_gpus(),
|
||||
);
|
||||
CudaLweCiphertextVector64(CudaLweList::<u64> {
|
||||
d_vecs: vecs,
|
||||
lwe_ciphertext_count: input.lwe_ciphertext_count(),
|
||||
lwe_dimension: input.lwe_dimension(),
|
||||
})
|
||||
}
|
||||
}
|
||||
/// # Description
|
||||
/// Convert a mutable LWE ciphertext vector view with 32 bits of precision from CPU to GPU.
|
||||
///
|
||||
/// The input ciphertext vector view is split over GPUs, so that each GPU contains
|
||||
/// the total amount of ciphertexts divided by the number of GPUs on the machine.
|
||||
/// The last GPU takes the remainder of the division if there is any.
|
||||
impl
|
||||
LweCiphertextVectorConversionEngine<LweCiphertextVectorMutView32<'_>, CudaLweCiphertextVector32>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u32 << 20; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext_vector: PlaintextVector32 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: LweCiphertextVector32 =
|
||||
/// default_engine.encrypt_lwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
/// let lwe_ciphertext_count = h_ciphertext_vector.lwe_ciphertext_count();
|
||||
/// let lwe_size = h_ciphertext_vector.lwe_dimension().to_lwe_size();
|
||||
///
|
||||
/// let mut h_raw_ciphertext_vector: Vec<u32> =
|
||||
/// default_engine.consume_retrieve_lwe_ciphertext_vector(h_ciphertext_vector)?;
|
||||
/// let mut h_view_ciphertext_vector: LweCiphertextVectorMutView32 = default_engine
|
||||
/// .create_lwe_ciphertext_vector_from(h_raw_ciphertext_vector.as_mut_slice(), lwe_size)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaLweCiphertextVector32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_view_ciphertext_vector)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext_vector.lwe_dimension(), lwe_dimension);
|
||||
/// assert_eq!(
|
||||
/// d_ciphertext_vector.lwe_ciphertext_count(),
|
||||
/// lwe_ciphertext_count
|
||||
/// );
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &LweCiphertextVectorMutView32,
|
||||
) -> Result<CudaLweCiphertextVector32, LweCiphertextVectorConversionError<CudaError>> {
|
||||
let number_of_gpus = number_of_active_gpus(
|
||||
self.get_number_of_gpus(),
|
||||
CiphertextCount(input.lwe_ciphertext_count().0),
|
||||
);
|
||||
for gpu_index in 0..number_of_gpus.0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let samples = compute_number_of_samples_on_gpu(
|
||||
self.get_number_of_gpus(),
|
||||
CiphertextCount(input.lwe_ciphertext_count().0),
|
||||
GpuIndex(gpu_index),
|
||||
);
|
||||
let data_per_gpu = samples.0 * input.lwe_dimension().to_lwe_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u32>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_lwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &LweCiphertextVectorMutView32,
|
||||
) -> CudaLweCiphertextVector32 {
|
||||
let vecs = copy_lwe_ciphertext_vector_from_cpu_to_gpu::<u32, _>(
|
||||
self.get_cuda_streams(),
|
||||
&input.0,
|
||||
self.get_number_of_gpus(),
|
||||
);
|
||||
CudaLweCiphertextVector32(CudaLweList::<u32> {
|
||||
d_vecs: vecs,
|
||||
lwe_ciphertext_count: input.lwe_ciphertext_count(),
|
||||
lwe_dimension: input.lwe_dimension(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert a mutable LWE ciphertext vector view with 64 bits of precision from CPU to GPU.
|
||||
///
|
||||
/// The input ciphertext vector view is split over GPUs, so that each GPU contains
|
||||
/// the total amount of ciphertexts divided by the number of GPUs on the machine.
|
||||
/// The last GPU takes the remainder of the division if there is any.
|
||||
impl
|
||||
LweCiphertextVectorConversionEngine<LweCiphertextVectorMutView64<'_>, CudaLweCiphertextVector64>
|
||||
for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 50 bits)
|
||||
/// let input = vec![3_u64 << 50; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: LweCiphertextVector64 =
|
||||
/// default_engine.encrypt_lwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
/// let lwe_ciphertext_count = h_ciphertext_vector.lwe_ciphertext_count();
|
||||
/// let lwe_size = h_ciphertext_vector.lwe_dimension().to_lwe_size();
|
||||
///
|
||||
/// let mut h_raw_ciphertext_vector: Vec<u64> =
|
||||
/// default_engine.consume_retrieve_lwe_ciphertext_vector(h_ciphertext_vector)?;
|
||||
/// let mut h_view_ciphertext_vector: LweCiphertextVectorMutView64 = default_engine
|
||||
/// .create_lwe_ciphertext_vector_from(h_raw_ciphertext_vector.as_mut_slice(), lwe_size)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaLweCiphertextVector64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_view_ciphertext_vector)?;
|
||||
///
|
||||
/// assert_eq!(d_ciphertext_vector.lwe_dimension(), lwe_dimension);
|
||||
/// assert_eq!(
|
||||
/// d_ciphertext_vector.lwe_ciphertext_count(),
|
||||
/// lwe_ciphertext_count
|
||||
/// );
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
input: &LweCiphertextVectorMutView64,
|
||||
) -> Result<CudaLweCiphertextVector64, LweCiphertextVectorConversionError<CudaError>> {
|
||||
let number_of_gpus = number_of_active_gpus(
|
||||
self.get_number_of_gpus(),
|
||||
CiphertextCount(input.lwe_ciphertext_count().0),
|
||||
);
|
||||
for gpu_index in 0..number_of_gpus.0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let samples = compute_number_of_samples_on_gpu(
|
||||
self.get_number_of_gpus(),
|
||||
CiphertextCount(input.lwe_ciphertext_count().0),
|
||||
GpuIndex(gpu_index),
|
||||
);
|
||||
let data_per_gpu = samples.0 * input.lwe_dimension().to_lwe_size().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_lwe_ciphertext_vector_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
input: &LweCiphertextVectorMutView64,
|
||||
) -> CudaLweCiphertextVector64 {
|
||||
let vecs = copy_lwe_ciphertext_vector_from_cpu_to_gpu::<u64, _>(
|
||||
self.get_cuda_streams(),
|
||||
&input.0,
|
||||
self.get_number_of_gpus(),
|
||||
);
|
||||
CudaLweCiphertextVector64(CudaLweList::<u64> {
|
||||
d_vecs: vecs,
|
||||
lwe_ciphertext_count: input.lwe_ciphertext_count(),
|
||||
lwe_dimension: input.lwe_dimension(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
use crate::core_crypto::backends::cuda::engines::CudaError;
|
||||
use crate::core_crypto::backends::cuda::implementation::engines::{
|
||||
check_base_log, check_glwe_dim, CudaEngine,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::{
|
||||
CudaFourierLweBootstrapKey32, CudaFourierLweBootstrapKey64, CudaGlweCiphertextVector32,
|
||||
CudaGlweCiphertextVector64, CudaLweCiphertextVector32, CudaLweCiphertextVector64,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::private::crypto::bootstrap::execute_lwe_ciphertext_vector_low_latency_bootstrap_on_gpu;
|
||||
use crate::core_crypto::specification::engines::{
|
||||
LweCiphertextVectorDiscardingBootstrapEngine, LweCiphertextVectorDiscardingBootstrapError,
|
||||
};
|
||||
use crate::core_crypto::specification::entities::LweBootstrapKeyEntity;
|
||||
|
||||
/// # Description
|
||||
/// A discard bootstrap on a vector of input ciphertext vectors with 32 bits of precision.
|
||||
/// The bootstraps are all using one cuda bootstrap key in the Fourier domain, and as
|
||||
/// many lookup tables as there are input LWE ciphertexts.
|
||||
impl
|
||||
LweCiphertextVectorDiscardingBootstrapEngine<
|
||||
CudaFourierLweBootstrapKey32,
|
||||
CudaGlweCiphertextVector32,
|
||||
CudaLweCiphertextVector32,
|
||||
CudaLweCiphertextVector32,
|
||||
> for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// DecompositionBaseLog, DecompositionLevelCount, GlweDimension, PolynomialSize,
|
||||
/// };
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let (lwe_dim, lwe_dim_output, glwe_dim, poly_size) = (
|
||||
/// LweDimension(130),
|
||||
/// LweDimension(512),
|
||||
/// GlweDimension(1),
|
||||
/// PolynomialSize(512),
|
||||
/// );
|
||||
/// let log_degree = f64::log2(poly_size.0 as f64) as i32;
|
||||
/// let val: u32 = ((poly_size.0 as f64 - (10. * f64::sqrt((lwe_dim.0 as f64) / 16.0)))
|
||||
/// * 2_f64.powi(32 - log_degree - 1)) as u32;
|
||||
/// let input = vec![val; 3];
|
||||
/// let noise = Variance(2_f64.powf(-29.));
|
||||
/// let (dec_lc, dec_bl) = (DecompositionLevelCount(3), DecompositionBaseLog(7));
|
||||
/// // An identity function is applied during the bootstrap
|
||||
/// let mut lut = vec![0u32; poly_size.0 * 3];
|
||||
/// for i in 0..poly_size.0 {
|
||||
/// let l = (i as f64 * 2_f64.powi(32 - log_degree - 1)) as u32;
|
||||
/// lut[i] = l;
|
||||
/// lut[i + poly_size.0] = l;
|
||||
/// lut[i + 2 * poly_size.0] = l;
|
||||
/// }
|
||||
///
|
||||
/// // 1. default engine
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// // create a vector of LWE ciphertexts
|
||||
/// let h_input_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dim)?;
|
||||
/// let h_input_plaintext_vector: PlaintextVector32 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_input_ciphertext_vector: LweCiphertextVector32 = default_engine
|
||||
/// .encrypt_lwe_ciphertext_vector(&h_input_key, &h_input_plaintext_vector, noise)?;
|
||||
/// // create a vector of GLWE ciphertexts containing the encryptions of the LUTs
|
||||
/// let h_lut_plaintext_vector = default_engine.create_plaintext_vector_from(&lut)?;
|
||||
/// let h_lut_key: GlweSecretKey32 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dim, poly_size)?;
|
||||
/// let h_lut_vector = default_engine.encrypt_glwe_ciphertext_vector(
|
||||
/// &h_lut_key,
|
||||
/// &h_lut_plaintext_vector,
|
||||
/// noise,
|
||||
/// )?;
|
||||
/// // create a BSK
|
||||
/// let h_bootstrap_key: LweBootstrapKey32 = default_engine.generate_new_lwe_bootstrap_key(
|
||||
/// &h_input_key,
|
||||
/// &h_lut_key,
|
||||
/// dec_bl,
|
||||
/// dec_lc,
|
||||
/// noise,
|
||||
/// )?;
|
||||
/// // initialize an output LWE ciphertext vector
|
||||
/// let h_dummy_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dim_output)?;
|
||||
///
|
||||
/// // 2. cuda engine
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// // convert input to GPU (split over the GPUs)
|
||||
/// let d_input_ciphertext_vector: CudaLweCiphertextVector32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_input_ciphertext_vector)?;
|
||||
/// // convert accumulators to GPU
|
||||
/// let d_input_lut_vector: CudaGlweCiphertextVector32 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&h_lut_vector)?;
|
||||
/// // convert BSK to GPU (and from Standard to Fourier representations)
|
||||
/// let d_fourier_bsk: CudaFourierLweBootstrapKey32 =
|
||||
/// cuda_engine.convert_lwe_bootstrap_key(&h_bootstrap_key)?;
|
||||
/// // launch bootstrap on GPU
|
||||
/// let h_zero_output_ciphertext_vector: LweCiphertextVector32 = default_engine
|
||||
/// .zero_encrypt_lwe_ciphertext_vector(&h_dummy_key, noise, LweCiphertextCount(3))?;
|
||||
/// let mut d_output_ciphertext_vector: CudaLweCiphertextVector32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_zero_output_ciphertext_vector)?;
|
||||
/// cuda_engine.discard_bootstrap_lwe_ciphertext_vector(
|
||||
/// &mut d_output_ciphertext_vector,
|
||||
/// &d_input_ciphertext_vector,
|
||||
/// &d_input_lut_vector,
|
||||
/// &d_fourier_bsk,
|
||||
/// )?;
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_bootstrap_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertextVector32,
|
||||
input: &CudaLweCiphertextVector32,
|
||||
acc: &CudaGlweCiphertextVector32,
|
||||
bsk: &CudaFourierLweBootstrapKey32,
|
||||
) -> Result<(), LweCiphertextVectorDiscardingBootstrapError<CudaError>> {
|
||||
LweCiphertextVectorDiscardingBootstrapError::perform_generic_checks(
|
||||
output, input, acc, bsk,
|
||||
)?;
|
||||
let poly_size = bsk.polynomial_size();
|
||||
check_poly_size!(poly_size);
|
||||
let glwe_dim = bsk.glwe_dimension();
|
||||
check_glwe_dim!(glwe_dim);
|
||||
let base_log = bsk.decomposition_base_log();
|
||||
check_base_log!(base_log);
|
||||
unsafe { self.discard_bootstrap_lwe_ciphertext_vector_unchecked(output, input, acc, bsk) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_bootstrap_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertextVector32,
|
||||
input: &CudaLweCiphertextVector32,
|
||||
acc: &CudaGlweCiphertextVector32,
|
||||
bsk: &CudaFourierLweBootstrapKey32,
|
||||
) {
|
||||
execute_lwe_ciphertext_vector_low_latency_bootstrap_on_gpu::<u32>(
|
||||
self.get_cuda_streams(),
|
||||
&mut output.0,
|
||||
&input.0,
|
||||
&acc.0,
|
||||
&bsk.0,
|
||||
self.get_number_of_gpus(),
|
||||
self.get_cuda_shared_memory(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// A discard bootstrap on a vector of input ciphertext vectors with 64 bits of precision.
|
||||
/// The bootstraps are all using one cuda bootstrap key in the Fourier domain, and as
|
||||
/// many lookup tables as there are input LWE ciphertexts.
|
||||
impl
|
||||
LweCiphertextVectorDiscardingBootstrapEngine<
|
||||
CudaFourierLweBootstrapKey64,
|
||||
CudaGlweCiphertextVector64,
|
||||
CudaLweCiphertextVector64,
|
||||
CudaLweCiphertextVector64,
|
||||
> for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// DecompositionBaseLog, DecompositionLevelCount, GlweDimension, PolynomialSize,
|
||||
/// };
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let (lwe_dim, lwe_dim_output, glwe_dim, poly_size) = (
|
||||
/// LweDimension(130),
|
||||
/// LweDimension(512),
|
||||
/// GlweDimension(1),
|
||||
/// PolynomialSize(512),
|
||||
/// );
|
||||
/// let log_degree = f64::log2(poly_size.0 as f64) as i32;
|
||||
/// let val: u64 = ((poly_size.0 as f64 - (10. * f64::sqrt((lwe_dim.0 as f64) / 16.0)))
|
||||
/// * 2_f64.powi(64 - log_degree - 1)) as u64;
|
||||
/// let input = vec![val; 3];
|
||||
/// let noise = Variance(2_f64.powf(-29.));
|
||||
/// let (dec_lc, dec_bl) = (DecompositionLevelCount(3), DecompositionBaseLog(7));
|
||||
/// // An identity function is applied during the bootstrap
|
||||
/// let mut lut = vec![0u64; poly_size.0 * 3];
|
||||
/// for i in 0..poly_size.0 {
|
||||
/// let l = (i as f64 * 2_f64.powi(64 - log_degree - 1)) as u64;
|
||||
/// lut[i] = l;
|
||||
/// lut[i + poly_size.0] = l;
|
||||
/// lut[i + 2 * poly_size.0] = l;
|
||||
/// }
|
||||
///
|
||||
/// // 1. default engine
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// // create a vector of LWE ciphertexts
|
||||
/// let h_input_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dim)?;
|
||||
/// let h_input_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_input_ciphertext_vector: LweCiphertextVector64 = default_engine
|
||||
/// .encrypt_lwe_ciphertext_vector(&h_input_key, &h_input_plaintext_vector, noise)?;
|
||||
/// // create a vector of GLWE ciphertexts containing the encryptions of the LUTs
|
||||
/// let h_lut_plaintext_vector = default_engine.create_plaintext_vector_from(&lut)?;
|
||||
/// let h_lut_key: GlweSecretKey64 =
|
||||
/// default_engine.generate_new_glwe_secret_key(glwe_dim, poly_size)?;
|
||||
/// let h_lut_vector = default_engine.encrypt_glwe_ciphertext_vector(
|
||||
/// &h_lut_key,
|
||||
/// &h_lut_plaintext_vector,
|
||||
/// noise,
|
||||
/// )?;
|
||||
/// // create a BSK
|
||||
/// let h_bootstrap_key: LweBootstrapKey64 = default_engine.generate_new_lwe_bootstrap_key(
|
||||
/// &h_input_key,
|
||||
/// &h_lut_key,
|
||||
/// dec_bl,
|
||||
/// dec_lc,
|
||||
/// noise,
|
||||
/// )?;
|
||||
/// // initialize an output LWE ciphertext vector
|
||||
/// let h_dummy_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dim_output)?;
|
||||
///
|
||||
/// // 2. cuda engine
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// // convert input to GPU (split over the GPUs)
|
||||
/// let d_input_ciphertext_vector: CudaLweCiphertextVector64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_input_ciphertext_vector)?;
|
||||
/// // convert accumulators to GPU
|
||||
/// let d_input_lut_vector: CudaGlweCiphertextVector64 =
|
||||
/// cuda_engine.convert_glwe_ciphertext_vector(&h_lut_vector)?;
|
||||
/// // convert BSK to GPU (and from Standard to Fourier representations)
|
||||
/// let d_fourier_bsk: CudaFourierLweBootstrapKey64 =
|
||||
/// cuda_engine.convert_lwe_bootstrap_key(&h_bootstrap_key)?;
|
||||
/// // launch bootstrap on GPU
|
||||
/// let h_zero_output_ciphertext_vector: LweCiphertextVector64 = default_engine
|
||||
/// .zero_encrypt_lwe_ciphertext_vector(&h_dummy_key, noise, LweCiphertextCount(3))?;
|
||||
/// let mut d_output_ciphertext_vector: CudaLweCiphertextVector64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_zero_output_ciphertext_vector)?;
|
||||
/// cuda_engine.discard_bootstrap_lwe_ciphertext_vector(
|
||||
/// &mut d_output_ciphertext_vector,
|
||||
/// &d_input_ciphertext_vector,
|
||||
/// &d_input_lut_vector,
|
||||
/// &d_fourier_bsk,
|
||||
/// )?;
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_bootstrap_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertextVector64,
|
||||
input: &CudaLweCiphertextVector64,
|
||||
acc: &CudaGlweCiphertextVector64,
|
||||
bsk: &CudaFourierLweBootstrapKey64,
|
||||
) -> Result<(), LweCiphertextVectorDiscardingBootstrapError<CudaError>> {
|
||||
LweCiphertextVectorDiscardingBootstrapError::perform_generic_checks(
|
||||
output, input, acc, bsk,
|
||||
)?;
|
||||
let poly_size = bsk.polynomial_size();
|
||||
check_poly_size!(poly_size);
|
||||
let glwe_dim = bsk.glwe_dimension();
|
||||
check_glwe_dim!(glwe_dim);
|
||||
let base_log = bsk.decomposition_base_log();
|
||||
check_base_log!(base_log);
|
||||
unsafe { self.discard_bootstrap_lwe_ciphertext_vector_unchecked(output, input, acc, bsk) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_bootstrap_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertextVector64,
|
||||
input: &CudaLweCiphertextVector64,
|
||||
acc: &CudaGlweCiphertextVector64,
|
||||
bsk: &CudaFourierLweBootstrapKey64,
|
||||
) {
|
||||
execute_lwe_ciphertext_vector_low_latency_bootstrap_on_gpu::<u64>(
|
||||
self.get_cuda_streams(),
|
||||
&mut output.0,
|
||||
&input.0,
|
||||
&acc.0,
|
||||
&bsk.0,
|
||||
self.get_number_of_gpus(),
|
||||
self.get_cuda_shared_memory(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
use crate::core_crypto::backends::cuda::implementation::engines::{CudaEngine, CudaError};
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::{
|
||||
CudaLweCiphertextVector32, CudaLweCiphertextVector64,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::private::crypto::lwe::list::discard_copy_lwe_ciphertext_vector_from_gpu_to_cpu;
|
||||
use crate::core_crypto::prelude::{
|
||||
LweCiphertextVectorMutView32, LweCiphertextVectorMutView64,
|
||||
};
|
||||
use crate::core_crypto::specification::engines::{
|
||||
LweCiphertextVectorDiscardingConversionEngine, LweCiphertextVectorDiscardingConversionError,
|
||||
};
|
||||
|
||||
impl From<CudaError> for LweCiphertextVectorDiscardingConversionError<CudaError> {
|
||||
fn from(err: CudaError) -> Self {
|
||||
Self::Engine(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE ciphertext vector with 32 bits of precision from GPU to a view on CPU.
|
||||
/// The data from each GPU is copied into a part of an LweCiphertextVectorMutView32 on the CPU.
|
||||
impl
|
||||
LweCiphertextVectorDiscardingConversionEngine<
|
||||
CudaLweCiphertextVector32,
|
||||
LweCiphertextVectorMutView32<'_>,
|
||||
> for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// use std::borrow::BorrowMut;
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u32 << 20; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey32 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext_vector: PlaintextVector32 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: LweCiphertextVector32 =
|
||||
/// default_engine.encrypt_lwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaLweCiphertextVector32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_ciphertext_vector)?;
|
||||
/// let lwe_ciphertext_count = d_ciphertext_vector.lwe_ciphertext_count();
|
||||
/// let lwe_size = d_ciphertext_vector.lwe_dimension().to_lwe_size();
|
||||
///
|
||||
/// // Prepares the output container
|
||||
/// let mut h_raw_output_ciphertext_vector = vec![0_u32; lwe_size.0 * lwe_ciphertext_count.0];
|
||||
/// let mut h_view_output_ciphertext_vector: LweCiphertextVectorMutView32 = default_engine
|
||||
/// .create_lwe_ciphertext_vector_from(
|
||||
/// h_raw_output_ciphertext_vector.as_mut_slice(),
|
||||
/// lwe_size,
|
||||
/// )?;
|
||||
///
|
||||
/// cuda_engine.discard_convert_lwe_ciphertext_vector(
|
||||
/// h_view_output_ciphertext_vector.borrow_mut(),
|
||||
/// &d_ciphertext_vector,
|
||||
/// )?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// h_view_output_ciphertext_vector.lwe_dimension(),
|
||||
/// lwe_dimension
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// h_view_output_ciphertext_vector.lwe_ciphertext_count(),
|
||||
/// lwe_ciphertext_count
|
||||
/// );
|
||||
///
|
||||
/// // Extracts the internal container
|
||||
/// let h_raw_input_ciphertext_vector: Vec<u32> =
|
||||
/// default_engine.consume_retrieve_lwe_ciphertext_vector(h_ciphertext_vector)?;
|
||||
/// let h_raw_output_ciphertext_vector: &[u32] =
|
||||
/// default_engine.consume_retrieve_lwe_ciphertext_vector(h_view_output_ciphertext_vector)?;
|
||||
/// assert_eq!(
|
||||
/// h_raw_input_ciphertext_vector,
|
||||
/// h_raw_output_ciphertext_vector.to_vec()
|
||||
/// );
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_convert_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
output: &mut LweCiphertextVectorMutView32,
|
||||
input: &CudaLweCiphertextVector32,
|
||||
) -> Result<(), LweCiphertextVectorDiscardingConversionError<CudaError>> {
|
||||
unsafe { self.discard_convert_lwe_ciphertext_vector_unchecked(output, input) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_convert_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
output: &mut LweCiphertextVectorMutView32,
|
||||
input: &CudaLweCiphertextVector32,
|
||||
) {
|
||||
discard_copy_lwe_ciphertext_vector_from_gpu_to_cpu::<u32>(
|
||||
&mut output.0,
|
||||
self.get_cuda_streams(),
|
||||
&input.0,
|
||||
self.get_number_of_gpus(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE ciphertext vector with 64 bits of precision from GPU to a view on CPU.
|
||||
/// The data from each GPU is copied into a part of an LweCiphertextVectorMutView64 on the CPU.
|
||||
impl
|
||||
LweCiphertextVectorDiscardingConversionEngine<
|
||||
CudaLweCiphertextVector64,
|
||||
LweCiphertextVectorMutView64<'_>,
|
||||
> for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// use std::borrow::BorrowMut;
|
||||
/// let lwe_dimension = LweDimension(6);
|
||||
/// // Here a hard-set encoding is applied (shift by 50 bits)
|
||||
/// let input = vec![3_u64 << 50; 3];
|
||||
/// let noise = Variance(2_f64.powf(-50.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let h_key: LweSecretKey64 = default_engine.generate_new_lwe_secret_key(lwe_dimension)?;
|
||||
/// let h_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: LweCiphertextVector64 =
|
||||
/// default_engine.encrypt_lwe_ciphertext_vector(&h_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaLweCiphertextVector64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_ciphertext_vector)?;
|
||||
/// let lwe_ciphertext_count = d_ciphertext_vector.lwe_ciphertext_count();
|
||||
/// let lwe_size = d_ciphertext_vector.lwe_dimension().to_lwe_size();
|
||||
///
|
||||
/// // Prepares the output container
|
||||
/// let mut h_raw_output_ciphertext_vector = vec![0_u64; lwe_size.0 * lwe_ciphertext_count.0];
|
||||
/// let mut h_view_output_ciphertext_vector: LweCiphertextVectorMutView64 = default_engine
|
||||
/// .create_lwe_ciphertext_vector_from(
|
||||
/// h_raw_output_ciphertext_vector.as_mut_slice(),
|
||||
/// lwe_size,
|
||||
/// )?;
|
||||
///
|
||||
/// cuda_engine.discard_convert_lwe_ciphertext_vector(
|
||||
/// h_view_output_ciphertext_vector.borrow_mut(),
|
||||
/// &d_ciphertext_vector,
|
||||
/// )?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// h_view_output_ciphertext_vector.lwe_dimension(),
|
||||
/// lwe_dimension
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// h_view_output_ciphertext_vector.lwe_ciphertext_count(),
|
||||
/// lwe_ciphertext_count
|
||||
/// );
|
||||
///
|
||||
/// // Extracts the internal container
|
||||
/// let h_raw_input_ciphertext_vector: Vec<u64> =
|
||||
/// default_engine.consume_retrieve_lwe_ciphertext_vector(h_ciphertext_vector)?;
|
||||
/// let h_raw_output_ciphertext_vector: &[u64] =
|
||||
/// default_engine.consume_retrieve_lwe_ciphertext_vector(h_view_output_ciphertext_vector)?;
|
||||
/// assert_eq!(
|
||||
/// h_raw_input_ciphertext_vector,
|
||||
/// h_raw_output_ciphertext_vector.to_vec()
|
||||
/// );
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_convert_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
output: &mut LweCiphertextVectorMutView64,
|
||||
input: &CudaLweCiphertextVector64,
|
||||
) -> Result<(), LweCiphertextVectorDiscardingConversionError<CudaError>> {
|
||||
unsafe { self.discard_convert_lwe_ciphertext_vector_unchecked(output, input) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_convert_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
output: &mut LweCiphertextVectorMutView64,
|
||||
input: &CudaLweCiphertextVector64,
|
||||
) {
|
||||
discard_copy_lwe_ciphertext_vector_from_gpu_to_cpu::<u64>(
|
||||
&mut output.0,
|
||||
self.get_cuda_streams(),
|
||||
&input.0,
|
||||
self.get_number_of_gpus(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
use crate::core_crypto::backends::cuda::engines::CudaError;
|
||||
use crate::core_crypto::backends::cuda::implementation::engines::CudaEngine;
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::{
|
||||
CudaLweCiphertextVector32, CudaLweCiphertextVector64, CudaLweKeyswitchKey32,
|
||||
CudaLweKeyswitchKey64,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::private::crypto::keyswitch::execute_lwe_ciphertext_vector_keyswitch_on_gpu;
|
||||
use crate::core_crypto::specification::engines::{
|
||||
LweCiphertextVectorDiscardingKeyswitchEngine, LweCiphertextVectorDiscardingKeyswitchError,
|
||||
};
|
||||
|
||||
impl From<CudaError> for LweCiphertextVectorDiscardingKeyswitchError<CudaError> {
|
||||
fn from(err: CudaError) -> Self {
|
||||
Self::Engine(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// A discard keyswitch on a vector of input ciphertext vectors with 32 bits of precision.
|
||||
impl
|
||||
LweCiphertextVectorDiscardingKeyswitchEngine<
|
||||
CudaLweKeyswitchKey32,
|
||||
CudaLweCiphertextVector32,
|
||||
CudaLweCiphertextVector32,
|
||||
> for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let input_lwe_dimension = LweDimension(6);
|
||||
/// let output_lwe_dimension = LweDimension(3);
|
||||
/// let decomposition_level_count = DecompositionLevelCount(2);
|
||||
/// let decomposition_base_log = DecompositionBaseLog(8);
|
||||
///
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u32 << 20; 3];
|
||||
/// let noise = Variance(2_f64.powf(-25.));
|
||||
///
|
||||
/// // Generate two secret keys
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let input_key: LweSecretKey32 =
|
||||
/// default_engine.generate_new_lwe_secret_key(input_lwe_dimension)?;
|
||||
/// let output_key: LweSecretKey32 =
|
||||
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
|
||||
///
|
||||
/// // Generate keyswitch keys to switch between first_key and second_key
|
||||
/// let h_ksk = default_engine.generate_new_lwe_keyswitch_key(
|
||||
/// &input_key,
|
||||
/// &output_key,
|
||||
/// decomposition_level_count,
|
||||
/// decomposition_base_log,
|
||||
/// noise,
|
||||
/// )?;
|
||||
///
|
||||
/// // Encrypt something
|
||||
/// let h_plaintext_vector: PlaintextVector32 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: LweCiphertextVector32 =
|
||||
/// default_engine.encrypt_lwe_ciphertext_vector(&input_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// // Copy to the GPU
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaLweCiphertextVector32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_ciphertext_vector)?;
|
||||
/// let d_ksk: CudaLweKeyswitchKey32 = cuda_engine.convert_lwe_keyswitch_key(&h_ksk)?;
|
||||
///
|
||||
/// // launch keyswitch on GPU
|
||||
/// let h_dummy_key: LweSecretKey32 =
|
||||
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
|
||||
/// let h_zero_ciphertext_vector: LweCiphertextVector32 = default_engine
|
||||
/// .zero_encrypt_lwe_ciphertext_vector(
|
||||
/// &h_dummy_key,
|
||||
/// noise,
|
||||
/// h_ciphertext_vector.lwe_ciphertext_count(),
|
||||
/// )?;
|
||||
///
|
||||
/// let mut d_keyswitched_ciphertext_vector: CudaLweCiphertextVector32 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_zero_ciphertext_vector)?;
|
||||
/// cuda_engine.discard_keyswitch_lwe_ciphertext_vector(
|
||||
/// &mut d_keyswitched_ciphertext_vector,
|
||||
/// &d_ciphertext_vector,
|
||||
/// &d_ksk,
|
||||
/// )?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// d_keyswitched_ciphertext_vector.lwe_dimension(),
|
||||
/// output_lwe_dimension
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// d_keyswitched_ciphertext_vector.lwe_ciphertext_count(),
|
||||
/// LweCiphertextCount(3)
|
||||
/// );
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_keyswitch_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertextVector32,
|
||||
input: &CudaLweCiphertextVector32,
|
||||
ksk: &CudaLweKeyswitchKey32,
|
||||
) -> Result<(), LweCiphertextVectorDiscardingKeyswitchError<CudaError>> {
|
||||
LweCiphertextVectorDiscardingKeyswitchError::perform_generic_checks(output, input, ksk)?;
|
||||
unsafe { self.discard_keyswitch_lwe_ciphertext_vector_unchecked(output, input, ksk) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_keyswitch_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertextVector32,
|
||||
input: &CudaLweCiphertextVector32,
|
||||
ksk: &CudaLweKeyswitchKey32,
|
||||
) {
|
||||
execute_lwe_ciphertext_vector_keyswitch_on_gpu::<u32>(
|
||||
self.get_cuda_streams(),
|
||||
&mut output.0,
|
||||
&input.0,
|
||||
&ksk.0,
|
||||
self.get_number_of_gpus(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// A discard keyswitch on a vector of input ciphertext vectors with 64 bits of precision.
|
||||
impl
|
||||
LweCiphertextVectorDiscardingKeyswitchEngine<
|
||||
CudaLweKeyswitchKey64,
|
||||
CudaLweCiphertextVector64,
|
||||
CudaLweCiphertextVector64,
|
||||
> for CudaEngine
|
||||
{
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// LweCiphertextCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let input_lwe_dimension = LweDimension(6);
|
||||
/// let output_lwe_dimension = LweDimension(3);
|
||||
/// let decomposition_level_count = DecompositionLevelCount(2);
|
||||
/// let decomposition_base_log = DecompositionBaseLog(8);
|
||||
///
|
||||
/// // Here a hard-set encoding is applied (shift by 20 bits)
|
||||
/// let input = vec![3_u64 << 20; 3];
|
||||
/// let noise = Variance(2_f64.powf(-25.));
|
||||
///
|
||||
/// // Generate two secret keys
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let input_key: LweSecretKey64 =
|
||||
/// default_engine.generate_new_lwe_secret_key(input_lwe_dimension)?;
|
||||
/// let output_key: LweSecretKey64 =
|
||||
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
|
||||
///
|
||||
/// // Generate keyswitch keys to switch between first_key and second_key
|
||||
/// let h_ksk = default_engine.generate_new_lwe_keyswitch_key(
|
||||
/// &input_key,
|
||||
/// &output_key,
|
||||
/// decomposition_level_count,
|
||||
/// decomposition_base_log,
|
||||
/// noise,
|
||||
/// )?;
|
||||
///
|
||||
/// // Encrypt something
|
||||
/// let h_plaintext_vector: PlaintextVector64 =
|
||||
/// default_engine.create_plaintext_vector_from(&input)?;
|
||||
/// let mut h_ciphertext_vector: LweCiphertextVector64 =
|
||||
/// default_engine.encrypt_lwe_ciphertext_vector(&input_key, &h_plaintext_vector, noise)?;
|
||||
///
|
||||
/// // Copy to the GPU
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ciphertext_vector: CudaLweCiphertextVector64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_ciphertext_vector)?;
|
||||
/// let d_ksk: CudaLweKeyswitchKey64 = cuda_engine.convert_lwe_keyswitch_key(&h_ksk)?;
|
||||
///
|
||||
/// // launch keyswitch on GPU
|
||||
/// let h_dummy_key: LweSecretKey64 =
|
||||
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
|
||||
/// let h_zero_ciphertext_vector: LweCiphertextVector64 = default_engine
|
||||
/// .zero_encrypt_lwe_ciphertext_vector(
|
||||
/// &h_dummy_key,
|
||||
/// noise,
|
||||
/// h_ciphertext_vector.lwe_ciphertext_count(),
|
||||
/// )?;
|
||||
///
|
||||
/// let mut d_keyswitched_ciphertext_vector: CudaLweCiphertextVector64 =
|
||||
/// cuda_engine.convert_lwe_ciphertext_vector(&h_zero_ciphertext_vector)?;
|
||||
/// cuda_engine.discard_keyswitch_lwe_ciphertext_vector(
|
||||
/// &mut d_keyswitched_ciphertext_vector,
|
||||
/// &d_ciphertext_vector,
|
||||
/// &d_ksk,
|
||||
/// )?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// d_keyswitched_ciphertext_vector.lwe_dimension(),
|
||||
/// output_lwe_dimension
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// d_keyswitched_ciphertext_vector.lwe_ciphertext_count(),
|
||||
/// LweCiphertextCount(3)
|
||||
/// );
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn discard_keyswitch_lwe_ciphertext_vector(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertextVector64,
|
||||
input: &CudaLweCiphertextVector64,
|
||||
ksk: &CudaLweKeyswitchKey64,
|
||||
) -> Result<(), LweCiphertextVectorDiscardingKeyswitchError<CudaError>> {
|
||||
LweCiphertextVectorDiscardingKeyswitchError::perform_generic_checks(output, input, ksk)?;
|
||||
unsafe { self.discard_keyswitch_lwe_ciphertext_vector_unchecked(output, input, ksk) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn discard_keyswitch_lwe_ciphertext_vector_unchecked(
|
||||
&mut self,
|
||||
output: &mut CudaLweCiphertextVector64,
|
||||
input: &CudaLweCiphertextVector64,
|
||||
ksk: &CudaLweKeyswitchKey64,
|
||||
) {
|
||||
execute_lwe_ciphertext_vector_keyswitch_on_gpu::<u64>(
|
||||
self.get_cuda_streams(),
|
||||
&mut output.0,
|
||||
&input.0,
|
||||
&ksk.0,
|
||||
self.get_number_of_gpus(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
use crate::core_crypto::backends::cuda::engines::CudaError;
|
||||
use crate::core_crypto::backends::cuda::implementation::engines::CudaEngine;
|
||||
use crate::core_crypto::backends::cuda::implementation::entities::{
|
||||
CudaLweKeyswitchKey32, CudaLweKeyswitchKey64,
|
||||
};
|
||||
use crate::core_crypto::backends::cuda::private::crypto::keyswitch::CudaLweKeyswitchKey;
|
||||
use crate::core_crypto::commons::crypto::lwe::LweKeyswitchKey;
|
||||
use crate::core_crypto::commons::math::tensor::{AsRefSlice, AsRefTensor};
|
||||
use crate::core_crypto::prelude::{LweKeyswitchKey32, LweKeyswitchKey64};
|
||||
use crate::core_crypto::specification::engines::{
|
||||
LweKeyswitchKeyConversionEngine, LweKeyswitchKeyConversionError,
|
||||
};
|
||||
use crate::core_crypto::specification::entities::LweKeyswitchKeyEntity;
|
||||
|
||||
impl From<CudaError> for LweKeyswitchKeyConversionError<CudaError> {
|
||||
fn from(err: CudaError) -> Self {
|
||||
Self::Engine(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE keyswitch key corresponding to 32 bits of precision from the CPU to the GPU.
|
||||
/// We only support the conversion from CPU to GPU: the conversion from GPU to CPU is not
|
||||
/// necessary at this stage to support the keyswitch. The keyswitch key is copied entirely to all
|
||||
/// the GPUs.
|
||||
impl LweKeyswitchKeyConversionEngine<LweKeyswitchKey32, CudaLweKeyswitchKey32> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::backends::cuda::private::device::GpuIndex;
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// DecompositionBaseLog, DecompositionLevelCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let input_lwe_dimension = LweDimension(6);
|
||||
/// let output_lwe_dimension = LweDimension(3);
|
||||
/// let decomposition_level_count = DecompositionLevelCount(2);
|
||||
/// let decomposition_base_log = DecompositionBaseLog(8);
|
||||
/// let noise = Variance(2_f64.powf(-25.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let input_key: LweSecretKey32 =
|
||||
/// default_engine.generate_new_lwe_secret_key(input_lwe_dimension)?;
|
||||
/// let output_key: LweSecretKey32 =
|
||||
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
|
||||
/// let ksk = default_engine.generate_new_lwe_keyswitch_key(
|
||||
/// &input_key,
|
||||
/// &output_key,
|
||||
/// decomposition_level_count,
|
||||
/// decomposition_base_log,
|
||||
/// noise,
|
||||
/// )?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ksk: CudaLweKeyswitchKey32 = cuda_engine.convert_lwe_keyswitch_key(&ksk)?;
|
||||
///
|
||||
/// assert_eq!(d_ksk.input_lwe_dimension(), input_lwe_dimension);
|
||||
/// assert_eq!(d_ksk.output_lwe_dimension(), output_lwe_dimension);
|
||||
/// assert_eq!(d_ksk.decomposition_level_count(), decomposition_level_count);
|
||||
/// assert_eq!(d_ksk.decomposition_base_log(), decomposition_base_log);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_keyswitch_key(
|
||||
&mut self,
|
||||
input: &LweKeyswitchKey32,
|
||||
) -> Result<CudaLweKeyswitchKey32, LweKeyswitchKeyConversionError<CudaError>> {
|
||||
for gpu_index in 0..self.get_number_of_gpus().0 {
|
||||
let stream = &self.streams[gpu_index];
|
||||
let data_per_gpu = input.decomposition_level_count().0
|
||||
* (input.output_lwe_dimension().0 + 1)
|
||||
* input.input_lwe_dimension().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u32>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_lwe_keyswitch_key_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_keyswitch_key_unchecked(
|
||||
&mut self,
|
||||
input: &LweKeyswitchKey32,
|
||||
) -> CudaLweKeyswitchKey32 {
|
||||
// Copy the entire input vector over all GPUs
|
||||
let mut d_vecs = Vec::with_capacity(self.get_number_of_gpus().0);
|
||||
|
||||
let data_per_gpu = input.decomposition_level_count().0
|
||||
* input.output_lwe_dimension().to_lwe_size().0
|
||||
* input.input_lwe_dimension().0;
|
||||
for stream in self.streams.iter() {
|
||||
let mut d_vec = stream.malloc::<u32>(data_per_gpu as u32);
|
||||
stream.copy_to_gpu(&mut d_vec, input.0.as_tensor().as_slice());
|
||||
d_vecs.push(d_vec);
|
||||
}
|
||||
CudaLweKeyswitchKey32(CudaLweKeyswitchKey::<u32> {
|
||||
d_vecs,
|
||||
input_lwe_dimension: input.input_lwe_dimension(),
|
||||
output_lwe_dimension: input.output_lwe_dimension(),
|
||||
decomp_level: input.decomposition_level_count(),
|
||||
decomp_base_log: input.decomposition_base_log(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE keyswitch key corresponding to 32 bits of precision from the GPU to the CPU.
|
||||
/// We assume consistency between all the available GPUs and simply copy what is in the one with
|
||||
/// index 0.
|
||||
impl LweKeyswitchKeyConversionEngine<CudaLweKeyswitchKey32, LweKeyswitchKey32> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::backends::cuda::private::device::GpuIndex;
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// DecompositionBaseLog, DecompositionLevelCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let input_lwe_dimension = LweDimension(6);
|
||||
/// let output_lwe_dimension = LweDimension(3);
|
||||
/// let decomposition_level_count = DecompositionLevelCount(2);
|
||||
/// let decomposition_base_log = DecompositionBaseLog(8);
|
||||
/// let noise = Variance(2_f64.powf(-25.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let input_key: LweSecretKey32 =
|
||||
/// default_engine.generate_new_lwe_secret_key(input_lwe_dimension)?;
|
||||
/// let output_key: LweSecretKey32 =
|
||||
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
|
||||
/// let h_ksk = default_engine.generate_new_lwe_keyswitch_key(
|
||||
/// &input_key,
|
||||
/// &output_key,
|
||||
/// decomposition_level_count,
|
||||
/// decomposition_base_log,
|
||||
/// noise,
|
||||
/// )?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ksk: CudaLweKeyswitchKey32 = cuda_engine.convert_lwe_keyswitch_key(&h_ksk)?;
|
||||
/// let h_output_ksk: LweKeyswitchKey32 = cuda_engine.convert_lwe_keyswitch_key(&d_ksk)?;
|
||||
///
|
||||
/// assert_eq!(d_ksk.input_lwe_dimension(), input_lwe_dimension);
|
||||
/// assert_eq!(d_ksk.output_lwe_dimension(), output_lwe_dimension);
|
||||
/// assert_eq!(d_ksk.decomposition_level_count(), decomposition_level_count);
|
||||
/// assert_eq!(d_ksk.decomposition_base_log(), decomposition_base_log);
|
||||
/// assert_eq!(h_output_ksk, h_ksk);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_keyswitch_key(
|
||||
&mut self,
|
||||
input: &CudaLweKeyswitchKey32,
|
||||
) -> Result<LweKeyswitchKey32, LweKeyswitchKeyConversionError<CudaError>> {
|
||||
Ok(unsafe { self.convert_lwe_keyswitch_key_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_keyswitch_key_unchecked(
|
||||
&mut self,
|
||||
input: &CudaLweKeyswitchKey32,
|
||||
) -> LweKeyswitchKey32 {
|
||||
let data_per_gpu = input.decomposition_level_count().0
|
||||
* input.output_lwe_dimension().to_lwe_size().0
|
||||
* input.input_lwe_dimension().0;
|
||||
|
||||
// Copy the data from GPU 0 back to the CPU
|
||||
let mut output = vec![0u32; data_per_gpu];
|
||||
let stream = self.streams.first().unwrap();
|
||||
stream.copy_to_cpu::<u32>(&mut output, input.0.d_vecs.first().unwrap());
|
||||
|
||||
LweKeyswitchKey32(LweKeyswitchKey::from_container(
|
||||
output,
|
||||
input.decomposition_base_log(),
|
||||
input.decomposition_level_count(),
|
||||
input.output_lwe_dimension(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// # Description
|
||||
/// Convert an LWE keyswitch key corresponding to 64 bits of precision from the CPU to the GPU.
|
||||
/// We only support the conversion from CPU to GPU: the conversion from GPU to CPU is not
|
||||
/// necessary at this stage to support the keyswitch. The keyswitch key is copied entirely to all
|
||||
/// the GPUs.
|
||||
impl LweKeyswitchKeyConversionEngine<LweKeyswitchKey64, CudaLweKeyswitchKey64> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::backends::cuda::private::device::GpuIndex;
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// DecompositionBaseLog, DecompositionLevelCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let input_lwe_dimension = LweDimension(6);
|
||||
/// let output_lwe_dimension = LweDimension(3);
|
||||
/// let decomposition_level_count = DecompositionLevelCount(2);
|
||||
/// let decomposition_base_log = DecompositionBaseLog(8);
|
||||
/// let noise = Variance(2_f64.powf(-25.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let input_key: LweSecretKey64 =
|
||||
/// default_engine.generate_new_lwe_secret_key(input_lwe_dimension)?;
|
||||
/// let output_key: LweSecretKey64 =
|
||||
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
|
||||
/// let ksk = default_engine.generate_new_lwe_keyswitch_key(
|
||||
/// &input_key,
|
||||
/// &output_key,
|
||||
/// decomposition_level_count,
|
||||
/// decomposition_base_log,
|
||||
/// noise,
|
||||
/// )?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ksk: CudaLweKeyswitchKey64 = cuda_engine.convert_lwe_keyswitch_key(&ksk)?;
|
||||
///
|
||||
/// assert_eq!(d_ksk.input_lwe_dimension(), input_lwe_dimension);
|
||||
/// assert_eq!(d_ksk.output_lwe_dimension(), output_lwe_dimension);
|
||||
/// assert_eq!(d_ksk.decomposition_level_count(), decomposition_level_count);
|
||||
/// assert_eq!(d_ksk.decomposition_base_log(), decomposition_base_log);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_keyswitch_key(
|
||||
&mut self,
|
||||
input: &LweKeyswitchKey64,
|
||||
) -> Result<CudaLweKeyswitchKey64, LweKeyswitchKeyConversionError<CudaError>> {
|
||||
for stream in self.streams.iter() {
|
||||
let data_per_gpu = input.decomposition_level_count().0
|
||||
* input.output_lwe_dimension().to_lwe_size().0
|
||||
* input.input_lwe_dimension().0;
|
||||
let size = data_per_gpu as u64 * std::mem::size_of::<u64>() as u64;
|
||||
stream.check_device_memory(size)?;
|
||||
}
|
||||
Ok(unsafe { self.convert_lwe_keyswitch_key_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_keyswitch_key_unchecked(
|
||||
&mut self,
|
||||
input: &LweKeyswitchKey64,
|
||||
) -> CudaLweKeyswitchKey64 {
|
||||
// Copy the entire input vector over all GPUs
|
||||
let mut d_vecs = Vec::with_capacity(self.get_number_of_gpus().0);
|
||||
|
||||
let data_per_gpu = input.decomposition_level_count().0
|
||||
* input.output_lwe_dimension().to_lwe_size().0
|
||||
* input.input_lwe_dimension().0;
|
||||
for stream in self.streams.iter() {
|
||||
let mut d_vec = stream.malloc::<u64>(data_per_gpu as u32);
|
||||
stream.copy_to_gpu(&mut d_vec, input.0.as_tensor().as_slice());
|
||||
d_vecs.push(d_vec);
|
||||
}
|
||||
CudaLweKeyswitchKey64(CudaLweKeyswitchKey::<u64> {
|
||||
d_vecs,
|
||||
input_lwe_dimension: input.input_lwe_dimension(),
|
||||
output_lwe_dimension: input.output_lwe_dimension(),
|
||||
decomp_level: input.decomposition_level_count(),
|
||||
decomp_base_log: input.decomposition_base_log(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl LweKeyswitchKeyConversionEngine<CudaLweKeyswitchKey64, LweKeyswitchKey64> for CudaEngine {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::backends::cuda::private::device::GpuIndex;
|
||||
/// use tfhe::core_crypto::prelude::{
|
||||
/// DecompositionBaseLog, DecompositionLevelCount, LweDimension, Variance, *,
|
||||
/// };
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // DISCLAIMER: the parameters used here are only for test purpose, and are not secure.
|
||||
/// let input_lwe_dimension = LweDimension(6);
|
||||
/// let output_lwe_dimension = LweDimension(3);
|
||||
/// let decomposition_level_count = DecompositionLevelCount(2);
|
||||
/// let decomposition_base_log = DecompositionBaseLog(8);
|
||||
/// let noise = Variance(2_f64.powf(-25.));
|
||||
///
|
||||
/// const UNSAFE_SECRET: u128 = 0;
|
||||
/// let mut default_engine = DefaultEngine::new(Box::new(UnixSeeder::new(UNSAFE_SECRET)))?;
|
||||
/// let input_key: LweSecretKey64 =
|
||||
/// default_engine.generate_new_lwe_secret_key(input_lwe_dimension)?;
|
||||
/// let output_key: LweSecretKey64 =
|
||||
/// default_engine.generate_new_lwe_secret_key(output_lwe_dimension)?;
|
||||
/// let h_ksk = default_engine.generate_new_lwe_keyswitch_key(
|
||||
/// &input_key,
|
||||
/// &output_key,
|
||||
/// decomposition_level_count,
|
||||
/// decomposition_base_log,
|
||||
/// noise,
|
||||
/// )?;
|
||||
///
|
||||
/// let mut cuda_engine = CudaEngine::new(())?;
|
||||
/// let d_ksk: CudaLweKeyswitchKey64 = cuda_engine.convert_lwe_keyswitch_key(&h_ksk)?;
|
||||
/// let h_output_ksk: LweKeyswitchKey64 = cuda_engine.convert_lwe_keyswitch_key(&d_ksk)?;
|
||||
///
|
||||
/// assert_eq!(d_ksk.input_lwe_dimension(), input_lwe_dimension);
|
||||
/// assert_eq!(d_ksk.output_lwe_dimension(), output_lwe_dimension);
|
||||
/// assert_eq!(d_ksk.decomposition_level_count(), decomposition_level_count);
|
||||
/// assert_eq!(d_ksk.decomposition_base_log(), decomposition_base_log);
|
||||
/// assert_eq!(h_output_ksk, h_ksk);
|
||||
///
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn convert_lwe_keyswitch_key(
|
||||
&mut self,
|
||||
input: &CudaLweKeyswitchKey64,
|
||||
) -> Result<LweKeyswitchKey64, LweKeyswitchKeyConversionError<CudaError>> {
|
||||
Ok(unsafe { self.convert_lwe_keyswitch_key_unchecked(input) })
|
||||
}
|
||||
|
||||
unsafe fn convert_lwe_keyswitch_key_unchecked(
|
||||
&mut self,
|
||||
input: &CudaLweKeyswitchKey64,
|
||||
) -> LweKeyswitchKey64 {
|
||||
let data_per_gpu = input.decomposition_level_count().0
|
||||
* input.output_lwe_dimension().to_lwe_size().0
|
||||
* input.input_lwe_dimension().0;
|
||||
|
||||
// Copy the data from GPU 0 back to the CPU
|
||||
let mut output = vec![0u64; data_per_gpu];
|
||||
let stream = self.streams.first().unwrap();
|
||||
stream.copy_to_cpu::<u64>(&mut output, input.0.d_vecs.first().unwrap());
|
||||
|
||||
LweKeyswitchKey64(LweKeyswitchKey::from_container(
|
||||
output,
|
||||
input.decomposition_base_log(),
|
||||
input.decomposition_level_count(),
|
||||
input.output_lwe_dimension(),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
use crate::core_crypto::backends::cuda::private::device::{
|
||||
CudaStream, GpuIndex, NumberOfGpus,
|
||||
};
|
||||
use crate::core_crypto::prelude::sealed::AbstractEngineSeal;
|
||||
use crate::core_crypto::prelude::{AbstractEngine, CudaError, SharedMemoryAmount};
|
||||
use concrete_cuda::cuda_bind::cuda_get_number_of_gpus;
|
||||
/// The main engine exposed by the cuda backend.
|
||||
///
|
||||
/// This engine handles single-GPU and multi-GPU computations for the user. It always associates
|
||||
/// one Cuda stream to each available Nvidia GPU, and splits the input ciphertexts evenly over
|
||||
/// the GPUs (the last GPU may be a bit more loaded if the number of GPUs does not divide the
|
||||
/// number of input ciphertexts). This engine does not give control over the streams, nor the GPU
|
||||
/// load balancing. In this way, we can overlap computations done on different GPUs, but not
|
||||
/// computations done on a given GPU, which are executed in a sequence.
|
||||
// A finer access to streams could allow for more overlapping of computations
|
||||
// on a given device. We'll probably want to support it in the future, in an AdvancedCudaEngine
|
||||
// for example.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CudaEngine {
|
||||
streams: Vec<CudaStream>,
|
||||
max_shared_memory: usize,
|
||||
}
|
||||
|
||||
impl AbstractEngineSeal for CudaEngine {}
|
||||
|
||||
impl AbstractEngine for CudaEngine {
|
||||
type EngineError = CudaError;
|
||||
|
||||
type Parameters = ();
|
||||
|
||||
fn new(_parameters: Self::Parameters) -> Result<Self, Self::EngineError> {
|
||||
let number_of_gpus = unsafe { cuda_get_number_of_gpus() as usize };
|
||||
if number_of_gpus == 0 {
|
||||
Err(CudaError::DeviceNotFound)
|
||||
} else {
|
||||
let mut streams: Vec<CudaStream> = Vec::new();
|
||||
for gpu_index in 0..number_of_gpus {
|
||||
streams.push(CudaStream::new(GpuIndex(gpu_index))?);
|
||||
}
|
||||
let max_shared_memory = streams[0].get_max_shared_memory()?;
|
||||
|
||||
Ok(CudaEngine {
|
||||
streams,
|
||||
max_shared_memory: max_shared_memory as usize,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CudaEngine {
|
||||
/// Get the number of available GPUs from the engine
|
||||
pub fn get_number_of_gpus(&self) -> NumberOfGpus {
|
||||
NumberOfGpus(self.streams.len())
|
||||
}
|
||||
/// Get the Cuda streams from the engine
|
||||
pub fn get_cuda_streams(&self) -> &Vec<CudaStream> {
|
||||
&self.streams
|
||||
}
|
||||
/// Get the size of the shared memory (on device 0)
|
||||
pub fn get_cuda_shared_memory(&self) -> SharedMemoryAmount {
|
||||
SharedMemoryAmount(self.max_shared_memory)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! check_poly_size {
|
||||
($poly_size: ident) => {
|
||||
if $poly_size.0 != 512 && $poly_size.0 != 1024 && $poly_size.0 != 2048 {
|
||||
return Err(CudaError::PolynomialSizeNotSupported.into());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mod ggsw_ciphertext_conversion;
|
||||
mod glwe_ciphertext_conversion;
|
||||
mod glwe_ciphertext_discarding_conversion;
|
||||
mod glwe_ciphertext_vector_conversion;
|
||||
mod glwe_ciphertext_vector_discarding_conversion;
|
||||
mod lwe_bootstrap_key_conversion;
|
||||
mod lwe_ciphertext_conversion;
|
||||
mod lwe_ciphertext_discarding_bootstrap;
|
||||
mod lwe_ciphertext_discarding_conversion;
|
||||
mod lwe_ciphertext_discarding_keyswitch;
|
||||
mod lwe_ciphertext_vector_conversion;
|
||||
mod lwe_ciphertext_vector_discarding_bootstrap;
|
||||
mod lwe_ciphertext_vector_discarding_conversion;
|
||||
mod lwe_ciphertext_vector_discarding_keyswitch;
|
||||
mod lwe_keyswitch_key_conversion;
|
||||
@@ -0,0 +1,97 @@
|
||||
//! A module containing the [engines](crate::core_crypto::specification::engines) exposed by
|
||||
//! the cuda backend.
|
||||
|
||||
use crate::core_crypto::backends::cuda::private::device::GpuIndex;
|
||||
use crate::core_crypto::specification::engines::LweCiphertextVectorDiscardingBootstrapError;
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
mod cuda_engine;
|
||||
pub use cuda_engine::*;
|
||||
|
||||
mod cuda_amortized_engine;
|
||||
pub use cuda_amortized_engine::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct SharedMemoryAmount(pub usize);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CudaError {
|
||||
DeviceNotFound,
|
||||
SharedMemoryNotFound(GpuIndex),
|
||||
NotEnoughDeviceMemory(GpuIndex),
|
||||
InvalidDeviceIndex(GpuIndex),
|
||||
UnspecifiedDeviceError(GpuIndex),
|
||||
PolynomialSizeNotSupported,
|
||||
GlweDimensionNotSupported,
|
||||
BaseLogNotSupported,
|
||||
}
|
||||
impl Display for CudaError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
CudaError::DeviceNotFound => {
|
||||
write!(f, "No GPU detected on the machine.")
|
||||
}
|
||||
CudaError::SharedMemoryNotFound(gpu_index) => {
|
||||
write!(f, "No shared memory detected on the GPU #{}.", gpu_index.0)
|
||||
}
|
||||
CudaError::NotEnoughDeviceMemory(gpu_index) => {
|
||||
write!(
|
||||
f,
|
||||
"The GPU #{} does not have enough global memory to hold all the data.",
|
||||
gpu_index.0
|
||||
)
|
||||
}
|
||||
CudaError::InvalidDeviceIndex(gpu_index) => {
|
||||
write!(
|
||||
f,
|
||||
"The specified GPU index, {}, does not exist.",
|
||||
gpu_index.0
|
||||
)
|
||||
}
|
||||
CudaError::PolynomialSizeNotSupported => {
|
||||
write!(
|
||||
f,
|
||||
"The polynomial size should be a power of 2. Values strictly lower than \
|
||||
512, and strictly greater than 8192, are not supported."
|
||||
)
|
||||
}
|
||||
CudaError::GlweDimensionNotSupported => {
|
||||
write!(f, "The only supported GLWE dimension is 1.")
|
||||
}
|
||||
CudaError::BaseLogNotSupported => {
|
||||
write!(f, "The base log has to be lower or equal to 16.")
|
||||
}
|
||||
CudaError::UnspecifiedDeviceError(gpu_index) => {
|
||||
write!(f, "Unspecified device error on GPU #{}.", gpu_index.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for CudaError {}
|
||||
|
||||
impl From<CudaError> for LweCiphertextVectorDiscardingBootstrapError<CudaError> {
|
||||
fn from(err: CudaError) -> Self {
|
||||
Self::Engine(err)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! check_glwe_dim {
|
||||
($glwe_dimension: ident) => {
|
||||
if $glwe_dimension.0 != 1 {
|
||||
return Err(CudaError::GlweDimensionNotSupported.into());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! check_base_log {
|
||||
($base_log: ident) => {
|
||||
if $base_log.0 > 16 {
|
||||
return Err(CudaError::BaseLogNotSupported.into());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use {check_base_log, check_glwe_dim};
|
||||
@@ -0,0 +1,63 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::core_crypto::prelude::{
|
||||
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, PolynomialSize,
|
||||
};
|
||||
|
||||
use crate::core_crypto::backends::cuda::private::crypto::ggsw::ciphertext::CudaGgswCiphertext;
|
||||
use crate::core_crypto::specification::entities::markers::GgswCiphertextKind;
|
||||
use crate::core_crypto::specification::entities::{AbstractEntity, GgswCiphertextEntity};
|
||||
|
||||
/// A structure representing a vector of GGSW ciphertexts with 32 bits of precision on the GPU.
|
||||
/// It is used as input to the Cuda WopPBS.
|
||||
#[derive(Debug)]
|
||||
pub struct CudaGgswCiphertext32(pub(crate) CudaGgswCiphertext<u32>);
|
||||
|
||||
impl AbstractEntity for CudaGgswCiphertext32 {
|
||||
type Kind = GgswCiphertextKind;
|
||||
}
|
||||
|
||||
impl GgswCiphertextEntity for CudaGgswCiphertext32 {
|
||||
fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.0.glwe_dimension
|
||||
}
|
||||
|
||||
fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.0.polynomial_size
|
||||
}
|
||||
|
||||
fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.0.decomposition_level_count
|
||||
}
|
||||
|
||||
fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.0.decomposition_base_log
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure representing a vector of GGSW ciphertexts with 64 bits of precision on the GPU.
|
||||
/// It is used as input to the Cuda WopPBS.
|
||||
#[derive(Debug)]
|
||||
pub struct CudaGgswCiphertext64(pub(crate) CudaGgswCiphertext<u64>);
|
||||
|
||||
impl AbstractEntity for CudaGgswCiphertext64 {
|
||||
type Kind = GgswCiphertextKind;
|
||||
}
|
||||
|
||||
impl GgswCiphertextEntity for CudaGgswCiphertext64 {
|
||||
fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.0.glwe_dimension
|
||||
}
|
||||
|
||||
fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.0.polynomial_size
|
||||
}
|
||||
|
||||
fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.0.decomposition_level_count
|
||||
}
|
||||
|
||||
fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.0.decomposition_base_log
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::core_crypto::prelude::{GlweDimension, PolynomialSize};
|
||||
|
||||
use crate::core_crypto::backends::cuda::private::crypto::glwe::ciphertext::CudaGlweCiphertext;
|
||||
use crate::core_crypto::specification::entities::markers::GlweCiphertextKind;
|
||||
use crate::core_crypto::specification::entities::{AbstractEntity, GlweCiphertextEntity};
|
||||
|
||||
/// A structure representing a vector of GLWE ciphertexts with 32 bits of precision on the GPU.
|
||||
/// It is used as input to the Cuda bootstrap for the array of lookup tables.
|
||||
#[derive(Debug)]
|
||||
pub struct CudaGlweCiphertext32(pub(crate) CudaGlweCiphertext<u32>);
|
||||
|
||||
impl AbstractEntity for CudaGlweCiphertext32 {
|
||||
type Kind = GlweCiphertextKind;
|
||||
}
|
||||
|
||||
impl GlweCiphertextEntity for CudaGlweCiphertext32 {
|
||||
fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.0.glwe_dimension
|
||||
}
|
||||
|
||||
fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.0.polynomial_size
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure representing a vector of GLWE ciphertexts with 64 bits of precision on the GPU.
|
||||
/// It is used as input to the Cuda bootstrap for the array of lookup tables.
|
||||
#[derive(Debug)]
|
||||
pub struct CudaGlweCiphertext64(pub(crate) CudaGlweCiphertext<u64>);
|
||||
|
||||
impl AbstractEntity for CudaGlweCiphertext64 {
|
||||
type Kind = GlweCiphertextKind;
|
||||
}
|
||||
|
||||
impl GlweCiphertextEntity for CudaGlweCiphertext64 {
|
||||
fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.0.glwe_dimension
|
||||
}
|
||||
|
||||
fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.0.polynomial_size
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::core_crypto::prelude::{GlweCiphertextCount, GlweDimension, PolynomialSize};
|
||||
|
||||
use crate::core_crypto::backends::cuda::private::crypto::glwe::list::CudaGlweList;
|
||||
use crate::core_crypto::specification::entities::markers::GlweCiphertextVectorKind;
|
||||
use crate::core_crypto::specification::entities::{
|
||||
AbstractEntity, GlweCiphertextVectorEntity,
|
||||
};
|
||||
|
||||
/// A structure representing a vector of GLWE ciphertexts with 32 bits of precision on the GPU.
|
||||
/// It is used as input to the Cuda bootstrap for the array of lookup tables.
|
||||
#[derive(Debug)]
|
||||
pub struct CudaGlweCiphertextVector32(pub(crate) CudaGlweList<u32>);
|
||||
|
||||
impl AbstractEntity for CudaGlweCiphertextVector32 {
|
||||
type Kind = GlweCiphertextVectorKind;
|
||||
}
|
||||
|
||||
impl GlweCiphertextVectorEntity for CudaGlweCiphertextVector32 {
|
||||
fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.0.glwe_dimension
|
||||
}
|
||||
|
||||
fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.0.polynomial_size
|
||||
}
|
||||
|
||||
fn glwe_ciphertext_count(&self) -> GlweCiphertextCount {
|
||||
self.0.glwe_ciphertext_count
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure representing a vector of GLWE ciphertexts with 64 bits of precision on the GPU.
|
||||
/// It is used as input to the Cuda bootstrap for the array of lookup tables.
|
||||
#[derive(Debug)]
|
||||
pub struct CudaGlweCiphertextVector64(pub(crate) CudaGlweList<u64>);
|
||||
|
||||
impl AbstractEntity for CudaGlweCiphertextVector64 {
|
||||
type Kind = GlweCiphertextVectorKind;
|
||||
}
|
||||
|
||||
impl GlweCiphertextVectorEntity for CudaGlweCiphertextVector64 {
|
||||
fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.0.glwe_dimension
|
||||
}
|
||||
|
||||
fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.0.polynomial_size
|
||||
}
|
||||
|
||||
fn glwe_ciphertext_count(&self) -> GlweCiphertextCount {
|
||||
self.0.glwe_ciphertext_count
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
use crate::core_crypto::prelude::{
|
||||
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
|
||||
};
|
||||
|
||||
use crate::core_crypto::backends::cuda::private::crypto::bootstrap::CudaBootstrapKey;
|
||||
use crate::core_crypto::specification::entities::markers::LweBootstrapKeyKind;
|
||||
use crate::core_crypto::specification::entities::{AbstractEntity, LweBootstrapKeyEntity};
|
||||
|
||||
/// A structure representing a Fourier bootstrap key for 32 bits precision ciphertexts on the GPU.
|
||||
#[derive(Debug)]
|
||||
pub struct CudaFourierLweBootstrapKey32(pub(crate) CudaBootstrapKey<u32>);
|
||||
|
||||
impl AbstractEntity for CudaFourierLweBootstrapKey32 {
|
||||
type Kind = LweBootstrapKeyKind;
|
||||
}
|
||||
|
||||
impl LweBootstrapKeyEntity for CudaFourierLweBootstrapKey32 {
|
||||
fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.0.glwe_dimension
|
||||
}
|
||||
|
||||
fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.0.polynomial_size
|
||||
}
|
||||
|
||||
fn input_lwe_dimension(&self) -> LweDimension {
|
||||
self.0.input_lwe_dimension
|
||||
}
|
||||
|
||||
fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.0.decomp_base_log
|
||||
}
|
||||
|
||||
fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.0.decomp_level
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure representing a Fourier bootstrap key for 64 bits precision ciphertexts on the GPU.
|
||||
#[derive(Debug)]
|
||||
pub struct CudaFourierLweBootstrapKey64(pub(crate) CudaBootstrapKey<u64>);
|
||||
|
||||
impl AbstractEntity for CudaFourierLweBootstrapKey64 {
|
||||
type Kind = LweBootstrapKeyKind;
|
||||
}
|
||||
|
||||
impl LweBootstrapKeyEntity for CudaFourierLweBootstrapKey64 {
|
||||
fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.0.glwe_dimension
|
||||
}
|
||||
|
||||
fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.0.polynomial_size
|
||||
}
|
||||
|
||||
fn input_lwe_dimension(&self) -> LweDimension {
|
||||
self.0.input_lwe_dimension
|
||||
}
|
||||
|
||||
fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.0.decomp_base_log
|
||||
}
|
||||
|
||||
fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.0.decomp_level
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::core_crypto::prelude::LweDimension;
|
||||
|
||||
use crate::core_crypto::backends::cuda::private::crypto::lwe::ciphertext::CudaLweCiphertext;
|
||||
use crate::core_crypto::specification::entities::markers::LweCiphertextKind;
|
||||
use crate::core_crypto::specification::entities::{AbstractEntity, LweCiphertextEntity};
|
||||
|
||||
/// A structure representing a vector of LWE ciphertexts with 32 bits of precision on the GPU.
|
||||
#[derive(Debug)]
|
||||
pub struct CudaLweCiphertext32(pub(crate) CudaLweCiphertext<u32>);
|
||||
|
||||
impl AbstractEntity for CudaLweCiphertext32 {
|
||||
type Kind = LweCiphertextKind;
|
||||
}
|
||||
|
||||
impl LweCiphertextEntity for CudaLweCiphertext32 {
|
||||
fn lwe_dimension(&self) -> LweDimension {
|
||||
self.0.lwe_dimension
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure representing a vector of LWE ciphertexts with 64 bits of precision on the GPU.
|
||||
#[derive(Debug)]
|
||||
pub struct CudaLweCiphertext64(pub(crate) CudaLweCiphertext<u64>);
|
||||
|
||||
impl AbstractEntity for CudaLweCiphertext64 {
|
||||
type Kind = LweCiphertextKind;
|
||||
}
|
||||
|
||||
impl LweCiphertextEntity for CudaLweCiphertext64 {
|
||||
fn lwe_dimension(&self) -> LweDimension {
|
||||
self.0.lwe_dimension
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user