chore: initial commit

This commit is contained in:
Arthur Meyre
2022-10-21 09:46:35 +02:00
commit 6bcceb80d6
602 changed files with 102188 additions and 0 deletions

2
.cargo/config.toml Normal file
View File

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

17
.config/nextest.toml Normal file
View File

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

1
.gitbook.yaml Normal file
View File

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

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

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

View File

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

28
.github/pull_request_template.md vendored Normal file
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

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

161
README.md Normal file
View 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
View 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
View 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
View File

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

119
concrete-tasks/src/build.rs Normal file
View 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!()
}
}
}

View 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")
}

View 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
View 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
View 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(())
}

View 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")
}

View 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
View 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
View File

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

View 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())
}
}
}
}

View File

@@ -0,0 +1 @@
pub mod keycache;

1
grcov.yml Normal file
View File

@@ -0,0 +1 @@
output-path: ./lcov.info

15
katex-header.html Normal file
View 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>

BIN
logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

5
rustfmt.toml Normal file
View File

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

43
shortint-tests.sh Executable file
View 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"

View 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",
]

View File

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

View 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`.

File diff suppressed because it is too large Load Diff

139
tfhe/Cargo.toml Normal file
View 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
View File

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

View File

@@ -0,0 +1,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(&params);
let sks = ServerKey::new(&cks);
let ct1 = cks.encrypt(true);
let ct2 = cks.encrypt(false);
let ct3 = cks.encrypt(true);
let id = format!("AND gate {}", parameter_name);
c.bench_function(&id, |b| b.iter(|| black_box(sks.and(&ct1, &ct2))));
let id = format!("NAND gate {}", parameter_name);
c.bench_function(&id, |b| b.iter(|| black_box(sks.nand(&ct1, &ct2))));
let id = format!("OR gate {}", parameter_name);
c.bench_function(&id, |b| b.iter(|| black_box(sks.or(&ct1, &ct2))));
let id = format!("XOR gate {}", parameter_name);
c.bench_function(&id, |b| b.iter(|| black_box(sks.xor(&ct1, &ct2))));
let id = format!("XNOR gate {}", parameter_name);
c.bench_function(&id, |b| b.iter(|| black_box(sks.xnor(&ct1, &ct2))));
let id = format!("NOT gate {}", parameter_name);
c.bench_function(&id, |b| b.iter(|| black_box(sks.not(&ct1))));
let id = format!("MUX gate {}", parameter_name);
c.bench_function(&id, |b| b.iter(|| black_box(sks.mux(&ct1, &ct2, &ct3))));
}
#[cfg(not(feature = "cuda"))]
fn bench_default_parameters(c: &mut Criterion) {
bench_gates(c, DEFAULT_PARAMETERS, "DEFAULT_PARAMETERS");
}
#[cfg(feature = "cuda")]
fn bench_default_parameters(_: &mut Criterion) {
let _ = DEFAULT_PARAMETERS; // to avoid unused import warnings
println!("DEFAULT_PARAMETERS not benched as they are not compatible with the cuda feature.");
}
fn bench_tfhe_lib_parameters(c: &mut Criterion) {
bench_gates(c, TFHE_LIB_PARAMETERS, "TFHE_LIB_PARAMETERS");
}

View File

@@ -0,0 +1,232 @@
use 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,);

View 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);
```

View File

@@ -0,0 +1,8 @@
Advanced Topics
===============
.. toctree::
:maxdepth: 2
generate_keys.md
save_load.md

View 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);
```

View 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.

View 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),
)
};
```

View File

@@ -0,0 +1,14 @@
User Guide
==========
.. toctree::
:maxdepth: 2
introduction.md
how_does_it_work.md
tutorial.md
Tutorial: Concrete Boolean and Conways 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>

View 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!

View 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);
```

View 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)

View File

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

After

Width:  |  Height:  |  Size: 4.8 KiB

View 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);
}
```

View 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
```

View 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 %}

View 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**.
![](ciphertext-representation.svg)
The message buffer is the space where the actual message is
stored. This represents the modulus of the input messages
(denoted by `MessageModulus` in the code)
When doing computations on a ciphertext, the encrypted message can overflow the message
modulus: the exceeding information is stored in
the carry buffer. The size of the carry buffer is defined by another modulus, called
`CarryModulus`.
Together, the message modulus and the carry modulus form the plaintext space that is
available in a ciphertext. This space cannot be overflowed, otherwise the computation may result
in incorrect outputs.
In order to ensure the computation correctness, we keep track of the maximum value encrypted in a
ciphertext
via an associated attribute called the **degree**. When the degree reaches a defined
threshold, the carry buffer may be emptied to resume safely the computations.
Therefore, in `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.

View 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`.

View 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);
}
```

View 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.

View 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);
}
```

View 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

View 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()
}

View File

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

View File

@@ -0,0 +1,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,
})
}
}

View 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,
})
}
}

View 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))
}
}

View File

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

View File

@@ -0,0 +1,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
View 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)
}

View 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),
};

View 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;

View 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))
}
}

View 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(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// encryption of false
let ct_false = cks.encrypt(false);
// encryption of true
let ct_true = cks.encrypt(true);
// decryption of false
let dec_false = cks.decrypt(&ct_false);
// decryption of true
let dec_true = cks.decrypt(&ct_true);
// assert
assert!(!dec_false);
assert!(dec_true);
// encryption of false
let ct_false = sks.trivial_encrypt(false);
// encryption of true
let ct_true = sks.trivial_encrypt(true);
// decryption of false
let dec_false = cks.decrypt(&ct_false);
// decryption of true
let dec_true = cks.decrypt(&ct_true);
// assert
assert!(!dec_false);
assert!(dec_true);
}
}
/// This function randomly either computes a regular encryption of the message or a trivial
/// encryption of the message
fn random_enum_encryption(cks: &ClientKey, sks: &ServerKey, message: bool) -> Ciphertext {
if random_boolean() {
cks.encrypt(message)
} else {
sks.trivial_encrypt(message)
}
}
fn test_and_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of two random booleans
let b1 = random_boolean();
let b2 = random_boolean();
let expected_result = b1 && b2;
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
// AND gate -> "left: {:?}, right: {:?}",ct1, ct2
let ct_res = sks.and(&ct1, &ct2);
// decryption
let dec_and = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_and,
"left: {:?}, right: {:?}",
ct1, ct2
);
// AND gate -> left: Ciphertext, right: bool
let ct_res = sks.and(&ct1, b2);
// decryption
let dec_and = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_and, "left: {:?}, right: {:?}", ct1, b2);
// AND gate -> left: bool, right: Ciphertext
let ct_res = sks.and(b1, &ct2);
// decryption
let dec_and = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_and, "left: {:?}, right: {:?}", b1, ct2);
}
}
fn test_mux_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of three random booleans
let b1 = random_boolean();
let b2 = random_boolean();
let b3 = random_boolean();
let expected_result = if b1 { b2 } else { b3 };
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
// encryption of b3
let ct3 = random_enum_encryption(&cks, &sks, b3);
// MUX gate
let ct_res = sks.mux(&ct1, &ct2, &ct3);
// decryption
let dec_mux = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_mux,
"cond: {:?}, then: {:?}, else: {:?}",
ct1, ct2, ct3
);
}
}
fn test_nand_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of two random booleans
let b1 = random_boolean();
let b2 = random_boolean();
let expected_result = !(b1 && b2);
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
// NAND gate -> left: Ciphertext, right: Ciphertext
let ct_res = sks.nand(&ct1, &ct2);
// decryption
let dec_nand = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_nand,
"left: {:?}, right: {:?}",
ct1, ct2
);
// NAND gate -> left: Ciphertext, right: bool
let ct_res = sks.nand(&ct1, b2);
// decryption
let dec_nand = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_nand,
"left: {:?}, right: {:?}",
ct1, b2
);
// NAND gate -> left: bool, right: Ciphertext
let ct_res = sks.nand(b1, &ct2);
// decryption
let dec_nand = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_nand,
"left: {:?}, right: {:?}",
b1, ct2
);
}
}
fn test_nor_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of two random booleans
let b1 = random_boolean();
let b2 = random_boolean();
let expected_result = !(b1 || b2);
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
// NOR gate -> left: Ciphertext, right: Ciphertext
let ct_res = sks.nor(&ct1, &ct2);
// decryption
let dec_nor = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_nor,
"left: {:?}, right: {:?}",
ct1, ct2
);
// NOR gate -> left: Ciphertext, right: bool
let ct_res = sks.nor(&ct1, b2);
// decryption
let dec_nor = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_nor, "left: {:?}, right: {:?}", ct1, b2);
// NOR gate -> left: bool, right: Ciphertext
let ct_res = sks.nor(b1, &ct2);
// decryption
let dec_nor = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_nor, "left: {:?}, right: {:?}", b1, ct2);
}
}
fn test_not_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of one random booleans
let b1 = random_boolean();
let expected_result = !b1;
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// NOT gate
let ct_res = sks.not(&ct1);
// decryption
let dec_not = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_not);
}
}
fn test_or_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of two random booleans
let b1 = random_boolean();
let b2 = random_boolean();
let expected_result = b1 || b2;
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
// OR gate -> left: Ciphertext, right: Ciphertext
let ct_res = sks.or(&ct1, &ct2);
// decryption
let dec_or = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_or, "left: {:?}, right: {:?}", ct1, ct2);
// OR gate -> left: Ciphertext, right: bool
let ct_res = sks.or(&ct1, b2);
// decryption
let dec_or = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_or, "left: {:?}, right: {:?}", ct1, b2);
// OR gate -> left: bool, right: Ciphertext
let ct_res = sks.or(b1, &ct2);
// decryption
let dec_or = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_or, "left: {:?}, right: {:?}", b1, ct2);
}
}
fn test_xnor_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of two random booleans
let b1 = random_boolean();
let b2 = random_boolean();
let expected_result = b1 == b2;
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
// XNOR gate -> left: Ciphertext, right: Ciphertext
let ct_res = sks.xnor(&ct1, &ct2);
// decryption
let dec_xnor = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_xnor,
"left: {:?}, right: {:?}",
ct1, ct2
);
// XNOR gate -> left: Ciphertext, right: bool
let ct_res = sks.xnor(&ct1, b2);
// decryption
let dec_xnor = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_xnor,
"left: {:?}, right: {:?}",
ct1, b2
);
// XNOR gate -> left: bool, right: Ciphertext
let ct_res = sks.xnor(b1, &ct2);
// decryption
let dec_xnor = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_xnor,
"left: {:?}, right: {:?}",
b1, ct2
);
}
}
fn test_xor_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
for _ in 0..NB_TEST {
// generation of two random booleans
let b1 = random_boolean();
let b2 = random_boolean();
let expected_result = b1 ^ b2;
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
// XOR gate -> left: Ciphertext, right: Ciphertext
let ct_res = sks.xor(&ct1, &ct2);
// decryption
let dec_xor = cks.decrypt(&ct_res);
// assert
assert_eq!(
expected_result, dec_xor,
"left: {:?}, right: {:?}",
ct1, ct2
);
// XOR gate -> left: Ciphertext, right: bool
let ct_res = sks.xor(&ct1, b2);
// decryption
let dec_xor = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_xor, "left: {:?}, right: {:?}", ct1, b2);
// XOR gate -> left: bool, right: Ciphertext
let ct_res = sks.xor(b1, &ct2);
// decryption
let dec_xor = cks.decrypt(&ct_res);
// assert
assert_eq!(expected_result, dec_xor, "left: {:?}, right: {:?}", b1, ct2);
}
}
/// generate a random index for the table in the long run tests
fn random_index() -> usize {
(random_integer() % (NB_CT as u32)) as usize
}
/// randomly select a gate, randomly select inputs and the output,
/// compute the selected gate with the selected inputs
/// and write in the selected output
fn random_gate_all(ct_tab: &mut [Ciphertext], bool_tab: &mut [bool], sks: &ServerKey) {
// select a random gate in the array [NOT,CMUX,AND,NAND,NOR,OR,XOR,XNOR]
let gate_id = random_integer() % 8;
let index_1: usize = random_index();
let index_2: usize = random_index();
if gate_id == 0 {
// NOT gate
bool_tab[index_2] = !bool_tab[index_1];
ct_tab[index_2] = sks.not(&ct_tab[index_1]);
} else if gate_id == 1 {
// MUX gate
let index_3: usize = random_index();
let index_4: usize = random_index();
bool_tab[index_4] = if bool_tab[index_1] {
bool_tab[index_2]
} else {
bool_tab[index_3]
};
ct_tab[index_4] = sks.mux(&ct_tab[index_1], &ct_tab[index_2], &ct_tab[index_3]);
} else {
// 2-input gate
let index_3: usize = random_index();
if gate_id == 2 {
// AND gate
bool_tab[index_3] = bool_tab[index_1] && bool_tab[index_2];
ct_tab[index_3] = sks.and(&ct_tab[index_1], &ct_tab[index_2]);
} else if gate_id == 3 {
// NAND gate
bool_tab[index_3] = !(bool_tab[index_1] && bool_tab[index_2]);
ct_tab[index_3] = sks.nand(&ct_tab[index_1], &ct_tab[index_2]);
} else if gate_id == 4 {
// NOR gate
bool_tab[index_3] = !(bool_tab[index_1] || bool_tab[index_2]);
ct_tab[index_3] = sks.nor(&ct_tab[index_1], &ct_tab[index_2]);
} else if gate_id == 5 {
// OR gate
bool_tab[index_3] = bool_tab[index_1] || bool_tab[index_2];
ct_tab[index_3] = sks.or(&ct_tab[index_1], &ct_tab[index_2]);
} else if gate_id == 6 {
// XOR gate
bool_tab[index_3] = bool_tab[index_1] ^ bool_tab[index_2];
ct_tab[index_3] = sks.xor(&ct_tab[index_1], &ct_tab[index_2]);
} else {
// XNOR gate
bool_tab[index_3] = !(bool_tab[index_1] ^ bool_tab[index_2]);
ct_tab[index_3] = sks.xnor(&ct_tab[index_1], &ct_tab[index_2]);
}
}
}
fn test_deep_circuit(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
// create an array of ciphertexts
let mut ct_tab: Vec<Ciphertext> = vec![cks.encrypt(true); NB_CT];
// create an array of booleans
let mut bool_tab: Vec<bool> = vec![true; NB_CT];
// randomly fill both arrays
for (ct, boolean) in ct_tab.iter_mut().zip(bool_tab.iter_mut()) {
*boolean = random_boolean();
*ct = cks.encrypt(*boolean);
}
// compute NB_GATE gates
for _ in 0..NB_GATE {
random_gate_all(&mut ct_tab, &mut bool_tab, &sks);
}
// decrypt and assert equality
for (ct, boolean) in ct_tab.iter().zip(bool_tab.iter()) {
let dec = cks.decrypt(ct);
assert_eq!(*boolean, dec);
}
}

View File

@@ -0,0 +1,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(),
);
}
}

View File

@@ -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;

View File

@@ -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(),
))
}
}

View File

@@ -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(),
})
}
}

View File

@@ -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);
}
}

View File

@@ -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(),
})
}
}

View File

@@ -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());
}
}

View File

@@ -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(),
})
}
}

View File

@@ -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(),
})
}
}

View File

@@ -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(),
);
}
}

View File

@@ -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);
}
}

View File

@@ -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),
);
}
}

View File

@@ -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(),
})
}
}

View File

@@ -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(),
);
}
}

View File

@@ -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(),
);
}
}

View File

@@ -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(),
);
}
}

View File

@@ -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(),
))
}
}

View File

@@ -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;

View File

@@ -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};

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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