Artifact AsiaCrypt 2026: Accelerating TFHE with Sorted-Bootstrapping Techniques.

Co-authored-by: Loris Bergerat <loris.bergerat@zama.ai>
Co-authored-by: Arthur Meyre <arthur.meyre@zama.ai>
Co-authored-by: Jean-Baptiste Orfila <jb.orfila@zama.ai>
Co-authored-by: Adeline Roux Langlois <adeline.roux-langlois@cnrs.fr>
Co-authored-by: Samuel Tap <samuel.tap@zama.ai>
This commit is contained in:
Arthur Meyre
2023-11-20 16:33:17 +01:00
committed by Loris
parent 6f1a9bdaa5
commit 42626ba937
20 changed files with 7790 additions and 272 deletions

View File

@@ -967,7 +967,45 @@ bench_pbs128: install_rs_check_toolchain
bench_pbs_gpu: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" __TFHE_RS_FAST_BENCH=$(FAST_BENCH) cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
--bench pbs-bench \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,gpu,internal-keycache,nightly-avx512 -p $(TFHE_SPEC)
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,gpu,internal-keycache,nightly-avx512 -p $(TFHE_SPEC)\
.PHONY: bench_pbs_asiacrypt # Run benchmarks for PBS for asiacrypt artifact
bench_pbs_asiacrypt: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS) -A dead_code" cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
--bench pbs-bench \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,internal-keycache,nightly-avx512,pbs_asiacrypt -p $(TFHE_SPEC)
.PHONY: bench_ly # Run benchmarks for LY23 for asiacrypt artifact
bench_ly: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS) -A dead_code" cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
--bench pbs-bench \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,internal-keycache,nightly-avx512,ly -p $(TFHE_SPEC)
.PHONY: bench_sorted # Run benchmarks for Sorted PBS for asiacrypt artifact
bench_sorted: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS) -A dead_code" cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
--bench pbs-bench \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,internal-keycache,nightly-avx512,sorted -p $(TFHE_SPEC)
.PHONY: bench_cms # Run benchmarks for Sorted PBS with CMS for asiacrypt artifact
bench_cms: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS) -A dead_code" cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
--bench pbs-bench \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,internal-keycache,nightly-avx512,cms -p $(TFHE_SPEC)
.PHONY: bench_ly23_parallelized # Run benchmarks for PBS
bench_ly23_parallelized: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS) -A dead_code" cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
--bench pbs-bench \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,internal-keycache,nightly-avx512,ly23_parallelized -p $(TFHE_SPEC)
.PHONY: bench_sorted_parallelized # Run benchmarks for PBS
bench_sorted_parallelized: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS) -A dead_code" cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
--bench pbs-bench \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,internal-keycache,nightly-avx512,sorted_parallelized -p $(TFHE_SPEC)
.PHONY: bench_ks # Run benchmarks for keyswitch
bench_ks: install_rs_check_toolchain

280
README-TFHE-rs.md Normal file
View File

@@ -0,0 +1,280 @@
<p align="center">
<!-- product name logo -->
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/zama-ai/tfhe-rs/assets/157474013/5283e0ba-da1e-43af-9f2a-c5221367a12b">
<source media="(prefers-color-scheme: light)" srcset="https://github.com/zama-ai/tfhe-rs/assets/157474013/b94a8c96-7595-400b-9311-70765c706955">
<img width=600 alt="Zama TFHE-rs">
</picture>
</p>
<hr/>
<p align="center">
<a href="https://docs.zama.ai/tfhe-rs"> 📒 Documentation</a> | <a href="https://zama.ai/community"> 💛 Community support</a> | <a href="https://github.com/zama-ai/awesome-zama"> 📚 FHE resources by Zama</a>
</p>
<p align="center">
<a href="https://github.com/zama-ai/tfhe-rs/releases"><img src="https://img.shields.io/github/v/release/zama-ai/tfhe-rs?style=flat-square"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/License-BSD--3--Clause--Clear-%23ffb243?style=flat-square"></a>
<a href="https://github.com/zama-ai/bounty-program"><img src="https://img.shields.io/badge/Contribute-Zama%20Bounty%20Program-%23ffd208?style=flat-square"></a>
</p>
## About
### What is TFHE-rs
**TFHE-rs** is a pure Rust implementation of TFHE for boolean and integer arithmetics over encrypted data.
It includes:
- a **Rust** API
- a **C** API
- and a **client-side WASM** API
TFHE-rs is designed for developers and researchers who want full control over
what they can do with TFHE, while not having to worry about the low-level
implementation. The goal is to have a stable, simple, high-performance, and
production-ready library for all the advanced features of TFHE.
<br></br>
### Main features
- **Low-level cryptographic library** that implements Zamas variant of TFHE, including programmable bootstrapping
- **Implementation of the original TFHE boolean API** that can be used as a drop-in replacement for other TFHE libraries
- **Short integer API** that enables exact, unbounded FHE integer arithmetics with up to 8 bits of message space
- **Size-efficient public key encryption**
- **Ciphertext and server key compression** for efficient data transfer
- **Full Rust API, C bindings to the Rust High-Level API, and client-side Javascript API using WASM**.
*Learn more about TFHE-rs features in the [documentation](https://docs.zama.ai/tfhe-rs/readme).*
<br></br>
## Table of Contents
- **[Getting started](#getting-started)**
- [Cargo.toml configuration](#cargotoml-configuration)
- [A simple example](#a-simple-example)
- **[Resources](#resources)**
- [TFHE deep dive](#tfhe-deep-dive)
- [Tutorials](#tutorials)
- [Documentation](#documentation)
- **[Working with TFHE-rs](#working-with-tfhe-rs)**
- [Disclaimers](#disclaimers)
- [Citations](#citations)
- [Contributing](#contributing)
- [License](#license)
- **[Support](#support)**
<br></br>
## Getting started
### Cargo.toml configuration
To use the latest version of `TFHE-rs` in your project, you first need to add it as a dependency in your `Cargo.toml`:
+ For x86_64-based machines running Unix-like OSes:
```toml
tfhe = { version = "*", features = ["boolean", "shortint", "integer", "x86_64-unix"] }
```
+ For Apple Silicon or aarch64-based machines running Unix-like OSes:
```toml
tfhe = { version = "*", features = ["boolean", "shortint", "integer", "aarch64-unix"] }
```
+ For x86_64-based machines with the [`rdseed instruction`](https://en.wikipedia.org/wiki/RDRAND) running Windows:
```toml
tfhe = { version = "*", features = ["boolean", "shortint", "integer", "x86_64"] }
```
> [!Note]
> Note: You need to use a Rust version >= 1.73 to compile TFHE-rs.
> [!Note]
> Note: aarch64-based machines are not yet supported for Windows as it's currently missing an entropy source to be able to seed the [CSPRNGs](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator) used in TFHE-rs.
<p align="right">
<a href="#about" > ↑ Back to top </a>
</p>
### A simple example
Here is a full example:
``` rust
use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint32, FheUint8};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Basic configuration to use homomorphic integers
let config = ConfigBuilder::default().build();
// Key generation
let (client_key, server_keys) = generate_keys(config);
let clear_a = 1344u32;
let clear_b = 5u32;
let clear_c = 7u8;
// Encrypting the input data using the (private) client_key
// FheUint32: Encrypted equivalent to u32
let mut encrypted_a = FheUint32::try_encrypt(clear_a, &client_key)?;
let encrypted_b = FheUint32::try_encrypt(clear_b, &client_key)?;
// FheUint8: Encrypted equivalent to u8
let encrypted_c = FheUint8::try_encrypt(clear_c, &client_key)?;
// On the server side:
set_server_key(server_keys);
// Clear equivalent computations: 1344 * 5 = 6720
let encrypted_res_mul = &encrypted_a * &encrypted_b;
// Clear equivalent computations: 6720 >> 5 = 210
encrypted_a = &encrypted_res_mul >> &encrypted_b;
// Clear equivalent computations: let casted_a = a as u8;
let casted_a: FheUint8 = encrypted_a.cast_into();
// Clear equivalent computations: min(210, 7) = 7
let encrypted_res_min = &casted_a.min(&encrypted_c);
// Operation between clear and encrypted data:
// Clear equivalent computations: 7 & 1 = 1
let encrypted_res = encrypted_res_min & 1_u8;
// Decrypting on the client side:
let clear_res: u8 = encrypted_res.decrypt(&client_key);
assert_eq!(clear_res, 1_u8);
Ok(())
}
```
To run this code, use the following command:
<p align="center"> <code> cargo run --release </code> </p>
> [!Note]
> Note that when running code that uses `TFHE-rs`, it is highly recommended
to run in release mode with cargo's `--release` flag to have the best performances possible.
*Find an example with more explanations in [this part of the documentation](https://docs.zama.ai/tfhe-rs/getting-started/quick_start)*
<p align="right">
<a href="#about" > ↑ Back to top </a>
</p>
## Resources
### TFHE deep dive
- [TFHE Deep Dive - Part I - Ciphertext types](https://www.zama.ai/post/tfhe-deep-dive-part-1)
- [TFHE Deep Dive - Part II - Encodings and linear leveled operations](https://www.zama.ai/post/tfhe-deep-dive-part-2)
- [TFHE Deep Dive - Part III - Key switching and leveled multiplications](https://www.zama.ai/post/tfhe-deep-dive-part-3)
- [TFHE Deep Dive - Part IV - Programmable Bootstrapping](https://www.zama.ai/post/tfhe-deep-dive-part-4)
<br></br>
### Tutorials
- [[Video tutorial] Implement signed integers using TFHE-rs ](https://www.zama.ai/post/video-tutorial-implement-signed-integers-ssing-tfhe-rs)
- [Homomorphic parity bit](https://docs.zama.ai/tfhe-rs/tutorials/parity_bit)
- [Homomorphic case changing on Ascii string](https://docs.zama.ai/tfhe-rs/tutorials/ascii_fhe_string)
- [Boolean SHA256 with TFHE-rs](https://www.zama.ai/post/boolean-sha256-tfhe-rs)
- [Dark market with TFHE-rs](https://www.zama.ai/post/dark-market-tfhe-rs)
- [Regular expression engine with TFHE-rs](https://www.zama.ai/post/regex-engine-tfhe-rs)
*Explore more useful resources in [TFHE-rs tutorials](https://docs.zama.ai/tfhe-rs/tutorials) and [Awesome Zama repo](https://github.com/zama-ai/awesome-zama)*
<br></br>
### Documentation
Full, comprehensive documentation is available here: [https://docs.zama.ai/tfhe-rs](https://docs.zama.ai/tfhe-rs).
<p align="right">
<a href="#about" > ↑ Back to top </a>
</p>
## Working with TFHE-rs
### Disclaimers
#### Security estimation
Security estimations are done using the
[Lattice Estimator](https://github.com/malb/lattice-estimator)
with `red_cost_model = reduction.RC.BDGL16`.
When a new update is published in the Lattice Estimator, we update parameters accordingly.
### Security model
The default parameters for the TFHE-rs library are chosen considering the IND-CPA security model, and are selected with a bootstrapping failure probability fixed at p_error = $2^{-64}$. In particular, it is assumed that the results of decrypted computations are not shared by the secret key owner with any third parties, as such an action can lead to leakage of the secret encryption key. If you are designing an application where decryptions must be shared, you will need to craft custom encryption parameters which are chosen in consideration of the IND-CPA^D security model [1].
[1] Li, Baiyu, et al. "Securing approximate homomorphic encryption using differential privacy." Annual International Cryptology Conference. Cham: Springer Nature Switzerland, 2022. https://eprint.iacr.org/2022/816.pdf
#### Side-channel attacks
Mitigation for side-channel attacks has not yet been implemented in TFHE-rs,
and will be released in upcoming versions.
<br></br>
### Citations
To cite TFHE-rs in academic papers, please use the following entry:
```text
@Misc{TFHE-rs,
title={{TFHE-rs: A Pure Rust Implementation of the TFHE Scheme for Boolean and Integer Arithmetics Over Encrypted Data}},
author={Zama},
year={2022},
note={\url{https://github.com/zama-ai/tfhe-rs}},
}
```
### Contributing
There are two ways to contribute to TFHE-rs:
- [Open issues](https://github.com/zama-ai/tfhe-rs/issues/new/choose) to report bugs and typos, or to suggest new ideas
- Request 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!
<br></br>
### License
This software is distributed under the **BSD-3-Clause-Clear** license. Read [this](LICENSE) for more details.
#### FAQ
**Is Zamas technology free to use?**
>Zamas libraries are free to use under the BSD 3-Clause Clear license only for development, research, prototyping, and experimentation purposes. However, for any commercial use of Zama's open source code, companies must purchase Zamas commercial patent license.
>
>Everything we do is open source and we are very transparent on what it means for our users, you can read more about how we monetize our open source products at Zama in [this blogpost](https://www.zama.ai/post/open-source).
**What do I need to do if I want to use Zamas technology for commercial purposes?**
>To commercially use Zamas technology you need to be granted Zamas patent license. Please contact us hello@zama.ai for more information.
**Do you file IP on your technology?**
>Yes, all Zamas technologies are patented.
**Can you customize a solution for my specific use case?**
>We are open to collaborating and advancing the FHE space with our partners. If you have specific needs, please email us at hello@zama.ai.
<p align="right">
<a href="#about" > ↑ Back to top </a>
</p>
## Support
<a target="_blank" href="https://community.zama.ai">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/zama-ai/tfhe-rs/assets/157474013/08656d0a-3f44-4126-b8b6-8c601dff5380">
<source media="(prefers-color-scheme: light)" srcset="https://github.com/zama-ai/tfhe-rs/assets/157474013/1c9c9308-50ac-4aab-a4b9-469bb8c536a4">
<img alt="Support">
</picture>
</a>
🌟 If you find this project helpful or interesting, please consider giving it a star on GitHub! Your support helps to grow the community and motivates further development.
<p align="right">
<a href="#about" > ↑ Back to top </a>
</p>

330
README.md
View File

@@ -1,280 +1,114 @@
<p align="center">
<!-- product name logo -->
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/zama-ai/tfhe-rs/assets/157474013/5283e0ba-da1e-43af-9f2a-c5221367a12b">
<source media="(prefers-color-scheme: light)" srcset="https://github.com/zama-ai/tfhe-rs/assets/157474013/b94a8c96-7595-400b-9311-70765c706955">
<img width=600 alt="Zama TFHE-rs">
</picture>
</p>
<hr/>
<p align="center">
<a href="https://docs.zama.ai/tfhe-rs"> 📒 Documentation</a> | <a href="https://zama.ai/community"> 💛 Community support</a> | <a href="https://github.com/zama-ai/awesome-zama"> 📚 FHE resources by Zama</a>
</p>
# Artifact: Accelerating TFHE with Sorted-Bootstrapping Techniques
<p align="center">
<a href="https://github.com/zama-ai/tfhe-rs/releases"><img src="https://img.shields.io/github/v/release/zama-ai/tfhe-rs?style=flat-square"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/License-BSD--3--Clause--Clear-%23ffb243?style=flat-square"></a>
<a href="https://github.com/zama-ai/bounty-program"><img src="https://img.shields.io/badge/Contribute-Zama%20Bounty%20Program-%23ffd208?style=flat-square"></a>
</p>
## Description
## About
In what follows, we provide instructions on how to run the benchmarks given in **Table 3** (page 29), from the paper titled **Accelerating TFHE with Sorted Bootstrapping Techniques**.
The implementation of the techniques described in the aforementioned paper has been integrated into the **TFHE-rs** library, version `0.8.0-alpha.2`.
The code related to the extended PBS (EBS), the sorted PBS (SBS) and the version with the companion modulus switch are located in ```tfhe/src/core_crypto/fft_impl/fft64/crypto/bootstrap.rs```.
All the benchmarks can be found in ```tfhe/benches/core_crypto/pbs_bench.rs```.
### What is TFHE-rs
**TFHE-rs** is a pure Rust implementation of TFHE for boolean and integer arithmetics over encrypted data.
## Setup and Dependencies
It includes:
- a **Rust** API
- a **C** API
- and a **client-side WASM** API
Tested on Linux and macOS with Rust version ≥ 1.85 (we recommend installing Rust via [rustup](https://www.rust-lang.org/tools/install), as rustup is required later by the provided Makefile).
The complete list of dependencies and a guide on how to install TFHE-rs can be found in the online documentation [here](https://docs.zama.ai/tfhe-rs/0.8) or in the local file [here](./README-TFHE-rs.md).
TFHE-rs is designed for developers and researchers who want full control over
what they can do with TFHE, while not having to worry about the low-level
implementation. The goal is to have a stable, simple, high-performance, and
production-ready library for all the advanced features of TFHE.
<br></br>
## How to run benchmarks
The benchmarks run across all precision and all failure probabilities, progressing from lower precision (4 bits) to higher precision (9 bits) and from higher failure probabilities ($2^{-64}$) to the lower failure probabilities ($2^{-128}$).
All the following benchmarks are **sequential**.
At the root of the project (i.e., in the TFHE-rs folder), enter the following commands to run the different benchmarks:
### Main features
- ```make bench_ly```: returns the timings associated with the extended PBS (EBS[LY23] in **Table 3**).
- ```make bench_sorted```: returns the timings associated with the sorted PBS (SBS in **Table 3**).
- ```make bench_cms```: returns the timings associated with the SBS with the companion modulus switch (cms) (SBS + CMS in **Table 3**).
- **Low-level cryptographic library** that implements Zamas variant of TFHE, including programmable bootstrapping
- **Implementation of the original TFHE boolean API** that can be used as a drop-in replacement for other TFHE libraries
- **Short integer API** that enables exact, unbounded FHE integer arithmetics with up to 8 bits of message space
- **Size-efficient public key encryption**
- **Ciphertext and server key compression** for efficient data transfer
- **Full Rust API, C bindings to the Rust High-Level API, and client-side Javascript API using WASM**.
*Learn more about TFHE-rs features in the [documentation](https://docs.zama.ai/tfhe-rs/readme).*
<br></br>
To modify the performed benchmarks, only a few lines need to be commented and modified in ```tfhe/benches/core_crypto/pbs_bench.rs```.
To focus only on specific benchmarks, you only need to keep the wanted precision and failure probabilities and adjust the number of tests with ```[ParametersLY23; X]```, where `X` must be equal to the number of uncommented parameters.
## Table of Contents
- **[Getting started](#getting-started)**
- [Cargo.toml configuration](#cargotoml-configuration)
- [A simple example](#a-simple-example)
- **[Resources](#resources)**
- [TFHE deep dive](#tfhe-deep-dive)
- [Tutorials](#tutorials)
- [Documentation](#documentation)
- **[Working with TFHE-rs](#working-with-tfhe-rs)**
- [Disclaimers](#disclaimers)
- [Citations](#citations)
- [Contributing](#contributing)
- [License](#license)
- **[Support](#support)**
<br></br>
All parameter sets follow a syntax of the form: ```{Name}_{Precision}_{-Log2(pfail)}```.
For instance, ```LY_5_64``` means that the parameter set is associated to the algorithm referred as *LY* in the paper, with *5* bits of precision for input message and $2^{-64}$ as failure probability.
## Getting started
For the EBS and the SBS, you need to modify lines 1502 to 1507 and lines 1612 to 1616 for the SBS with CMS.
For instance, changing lines 1502 to 1507:
```rust
const PARAM_BENCHES_LY23: [ParametersLY23; 16] = [
//LY_5_40, LY_6_40, LY_7_40, LY_8_40, LY_9_40
LY_5_64, LY_6_64, LY_7_64, LY_8_64, LY_9_64,
LY_5_80, LY_6_80, LY_7_81, LY_8_81, LY_9_81,
LY_4_128, LY_5_128, LY_6_129, LY_7_128, LY_8_128, LY_9_129,
];
```
into:
```rust
const PARAM_BENCHES_LY23: [ParametersLY23; 6] = [
//LY_5_40, LY_6_40, LY_7_40, LY_8_40, LY_9_40,
//LY_5_64, LY_6_64, LY_7_64, LY_8_64, LY_9_64,
//LY_5_80, LY_6_80, LY_7_81, LY_8_81, LY_9_81,
LY_4_128, LY_5_128, LY_6_129, LY_7_128, LY_8_128, LY_9_129,
];
```
and using the command ```make bench_ly``` launches only benchmarks for the EBS experiment with a failure probability of $2^{-128}$.
### Cargo.toml configuration
To use the latest version of `TFHE-rs` in your project, you first need to add it as a dependency in your `Cargo.toml`:
+ For x86_64-based machines running Unix-like OSes:
### Sample Output Structure
```toml
tfhe = { version = "*", features = ["boolean", "shortint", "integer", "x86_64-unix"] }
A typical benchmark result looks like this:
```
KS_Extended_PBS_LY23/PRECISION_6_BITS__EXTENDED_FACTOR_2^2__PFAIL_2^-64
time: [123.38 ms 123.46 ms 123.55 ms]
Found 19 outliers among 500 measurements (3.80%)
```
+ For Apple Silicon or aarch64-based machines running Unix-like OSes:
The first line indicates the operation whose latency is measured.
```toml
tfhe = { version = "*", features = ["boolean", "shortint", "integer", "aarch64-unix"] }
**Examples:**
```
+ For x86_64-based machines with the [`rdseed instruction`](https://en.wikipedia.org/wiki/RDRAND) running Windows:
```toml
tfhe = { version = "*", features = ["boolean", "shortint", "integer", "x86_64"] }
KS_Extended_PBS_LY23/PRECISION_6_BITS__EXTENDED_FACTOR_2^2__PFAIL_2^-64
```
means that this benchmark is measuring the latency of a Keyswitch (KS) followed by an Extended Bootstrapping (EBS [LY23]) with 6 bits of message, an extended factor equal to $2^2$, and a failure probability pfail $=2^{-64}$.
> [!Note]
> Note: You need to use a Rust version >= 1.73 to compile TFHE-rs.
#### Understanding Benchmark Output (Criterion.rs)
This project uses Criterion.rs for benchmarking. Criterion is a powerful and statistically robust benchmarking framework for Rust, and it may produce outputs that are unfamiliar at first glance. Here is a short explanation:
> [!Note]
> Note: aarch64-based machines are not yet supported for Windows as it's currently missing an entropy source to be able to seed the [CSPRNGs](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator) used in TFHE-rs.
<p align="right">
<a href="#about" > ↑ Back to top </a>
</p>
### A simple example
Here is a full example:
``` rust
use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint32, FheUint8};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Basic configuration to use homomorphic integers
let config = ConfigBuilder::default().build();
// Key generation
let (client_key, server_keys) = generate_keys(config);
let clear_a = 1344u32;
let clear_b = 5u32;
let clear_c = 7u8;
// Encrypting the input data using the (private) client_key
// FheUint32: Encrypted equivalent to u32
let mut encrypted_a = FheUint32::try_encrypt(clear_a, &client_key)?;
let encrypted_b = FheUint32::try_encrypt(clear_b, &client_key)?;
// FheUint8: Encrypted equivalent to u8
let encrypted_c = FheUint8::try_encrypt(clear_c, &client_key)?;
// On the server side:
set_server_key(server_keys);
// Clear equivalent computations: 1344 * 5 = 6720
let encrypted_res_mul = &encrypted_a * &encrypted_b;
// Clear equivalent computations: 6720 >> 5 = 210
encrypted_a = &encrypted_res_mul >> &encrypted_b;
// Clear equivalent computations: let casted_a = a as u8;
let casted_a: FheUint8 = encrypted_a.cast_into();
// Clear equivalent computations: min(210, 7) = 7
let encrypted_res_min = &casted_a.min(&encrypted_c);
// Operation between clear and encrypted data:
// Clear equivalent computations: 7 & 1 = 1
let encrypted_res = encrypted_res_min & 1_u8;
// Decrypting on the client side:
let clear_res: u8 = encrypted_res.decrypt(&client_key);
assert_eq!(clear_res, 1_u8);
Ok(())
}
```
To run this code, use the following command:
<p align="center"> <code> cargo run --release </code> </p>
> [!Note]
> Note that when running code that uses `TFHE-rs`, it is highly recommended
to run in release mode with cargo's `--release` flag to have the best performances possible.
*Find an example with more explanations in [this part of the documentation](https://docs.zama.ai/tfhe-rs/getting-started/quick_start)*
<p align="right">
<a href="#about" > ↑ Back to top </a>
</p>
time: [low est. median high est.]: The estimated execution time of the function.
change: The performance change compared to a previous run (if available).
outliers: Some runs deviated from the typical time. Criterion detects and accounts for these using statistical methods.
#### Common Warnings and What They Mean
##### `Found X outliers among Y measurements`
Criterion runs each benchmark many times (default: 100) to get statistically significant results.
An *outlier* is a run that was significantly faster or slower than the others.
## Resources
- **Why does this happen?** Often, it's due to **other processes on the machine** (e.g., background services, OS interrupts, or CPU scheduling) affecting performance temporarily.
- **Why it doesn't invalidate results:** Criterion uses statistical techniques to minimize the impact of these outliers when estimating performance.
- **Best practice to reduce outliers:** Run the benchmarks on a **freshly rebooted machine**, with as few background processes as possible. Ideally, let the system idle for a minute after boot to stabilize before running benchmarks.
### TFHE deep dive
- [TFHE Deep Dive - Part I - Ciphertext types](https://www.zama.ai/post/tfhe-deep-dive-part-1)
- [TFHE Deep Dive - Part II - Encodings and linear leveled operations](https://www.zama.ai/post/tfhe-deep-dive-part-2)
- [TFHE Deep Dive - Part III - Key switching and leveled multiplications](https://www.zama.ai/post/tfhe-deep-dive-part-3)
- [TFHE Deep Dive - Part IV - Programmable Bootstrapping](https://www.zama.ai/post/tfhe-deep-dive-part-4)
<br></br>
##### `Unable to complete 100 samples in 5.0s.`
The benchmark took longer than the expected 5 seconds.
This is merely a warning indicating that the full set of 100 samples could not be collected within the default 5-second measurement window.
### Tutorials
- [[Video tutorial] Implement signed integers using TFHE-rs ](https://www.zama.ai/post/video-tutorial-implement-signed-integers-ssing-tfhe-rs)
- [Homomorphic parity bit](https://docs.zama.ai/tfhe-rs/tutorials/parity_bit)
- [Homomorphic case changing on Ascii string](https://docs.zama.ai/tfhe-rs/tutorials/ascii_fhe_string)
- [Boolean SHA256 with TFHE-rs](https://www.zama.ai/post/boolean-sha256-tfhe-rs)
- [Dark market with TFHE-rs](https://www.zama.ai/post/dark-market-tfhe-rs)
- [Regular expression engine with TFHE-rs](https://www.zama.ai/post/regex-engine-tfhe-rs)
- **No action is required**: Criterion will still proceed to run all 100 samples, and the results remain statistically valid.
- **Why the warning appears**: It's there to inform you that benchmarking is taking longer than expected and to help you tune settings if needed.
- **Optional**: If you're constrained by time (e.g., running in CI), you can:
- Reduce the sample size (e.g., to 10 or 20 samples).
- Or increase the measurement time using:
```bash
cargo bench -- --measurement-time 30
```
*Explore more useful resources in [TFHE-rs tutorials](https://docs.zama.ai/tfhe-rs/tutorials) and [Awesome Zama repo](https://github.com/zama-ai/awesome-zama)*
<br></br>
### Documentation
## Other Experiments (See long-paper version)
This section explains how to run more benchmarks related to the paper results appearing in the long version.
At the root of the project, run:
Full, comprehensive documentation is available here: [https://docs.zama.ai/tfhe-rs](https://docs.zama.ai/tfhe-rs).
<p align="right">
<a href="#about" > ↑ Back to top </a>
</p>
- ```make bench_pbs_asiacrypt```: returns the timings associated to the vanilla PBS. Can be used as another baseline comparison to see the impact of the EBS, SBS and SBS+CMS.
- ```make bench_ly23_parallelized```: returns the timings associated to the parallelized version of the extended PBS (EBS) from LY.
- ```make bench_sorted_parallelized```: returns the timings associated to the parallelized sorted PBS (SBS).
## Working with TFHE-rs
These benchmarks can be used to see the impact of the sorted PBS in a parallelized context.
### Disclaimers
#### Security estimation
Security estimations are done using the
[Lattice Estimator](https://github.com/malb/lattice-estimator)
with `red_cost_model = reduction.RC.BDGL16`.
When a new update is published in the Lattice Estimator, we update parameters accordingly.
### Security model
The default parameters for the TFHE-rs library are chosen considering the IND-CPA security model, and are selected with a bootstrapping failure probability fixed at p_error = $2^{-64}$. In particular, it is assumed that the results of decrypted computations are not shared by the secret key owner with any third parties, as such an action can lead to leakage of the secret encryption key. If you are designing an application where decryptions must be shared, you will need to craft custom encryption parameters which are chosen in consideration of the IND-CPA^D security model [1].
[1] Li, Baiyu, et al. "Securing approximate homomorphic encryption using differential privacy." Annual International Cryptology Conference. Cham: Springer Nature Switzerland, 2022. https://eprint.iacr.org/2022/816.pdf
#### Side-channel attacks
Mitigation for side-channel attacks has not yet been implemented in TFHE-rs,
and will be released in upcoming versions.
<br></br>
### Citations
To cite TFHE-rs in academic papers, please use the following entry:
```text
@Misc{TFHE-rs,
title={{TFHE-rs: A Pure Rust Implementation of the TFHE Scheme for Boolean and Integer Arithmetics Over Encrypted Data}},
author={Zama},
year={2022},
note={\url{https://github.com/zama-ai/tfhe-rs}},
}
```
### Contributing
There are two ways to contribute to TFHE-rs:
- [Open issues](https://github.com/zama-ai/tfhe-rs/issues/new/choose) to report bugs and typos, or to suggest new ideas
- Request 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!
<br></br>
### License
This software is distributed under the **BSD-3-Clause-Clear** license. Read [this](LICENSE) for more details.
#### FAQ
**Is Zamas technology free to use?**
>Zamas libraries are free to use under the BSD 3-Clause Clear license only for development, research, prototyping, and experimentation purposes. However, for any commercial use of Zama's open source code, companies must purchase Zamas commercial patent license.
>
>Everything we do is open source and we are very transparent on what it means for our users, you can read more about how we monetize our open source products at Zama in [this blogpost](https://www.zama.ai/post/open-source).
**What do I need to do if I want to use Zamas technology for commercial purposes?**
>To commercially use Zamas technology you need to be granted Zamas patent license. Please contact us hello@zama.ai for more information.
**Do you file IP on your technology?**
>Yes, all Zamas technologies are patented.
**Can you customize a solution for my specific use case?**
>We are open to collaborating and advancing the FHE space with our partners. If you have specific needs, please email us at hello@zama.ai.
<p align="right">
<a href="#about" > ↑ Back to top </a>
</p>
## Support
<a target="_blank" href="https://community.zama.ai">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/zama-ai/tfhe-rs/assets/157474013/08656d0a-3f44-4126-b8b6-8c601dff5380">
<source media="(prefers-color-scheme: light)" srcset="https://github.com/zama-ai/tfhe-rs/assets/157474013/1c9c9308-50ac-4aab-a4b9-469bb8c536a4">
<img alt="Support">
</picture>
</a>
🌟 If you find this project helpful or interesting, please consider giving it a star on GitHub! Your support helps to grow the community and motivates further development.
<p align="right">
<a href="#about" > ↑ Back to top </a>
</p>
As with the previous benchmarks, the launched experiment can be modified to only be performed on the wanted precision and failure probabilities by changing lines 1619 to 1628 for the PBS and lines 1630 to 1663 for the parallelized version.

View File

@@ -1,4 +1,5 @@
#![deny(rustdoc::broken_intra_doc_links)]
#![allow(deprecated)]
//! Cryptographically secure pseudo random number generator.
//!
//! Welcome to the `concrete-csprng` documentation.

View File

@@ -64,7 +64,7 @@ rayon = { version = "1.5.0" }
bincode = "1.3.3"
concrete-fft = { version = "0.4.1", features = ["serde", "fft128"] }
concrete-ntt = { version = "0.1.2" }
pulp = "0.18.8"
pulp = "=0.18.21"
tfhe-cuda-backend = { version = "0.4.0-alpha.0", path = "../backends/tfhe-cuda-backend", optional = true }
aligned-vec = { version = "0.5", features = ["serde"] }
dyn-stack = { version = "0.9" }
@@ -97,6 +97,14 @@ internal-keycache = ["dep:lazy_static", "dep:fs2"]
gpu = ["dep:tfhe-cuda-backend"]
zk-pok = ["dep:tfhe-zk-pok"]
ly = []
sorted = []
cms = []
ly23_parallelized = []
sorted_parallelized = []
pbs_asiacrypt = []
pbs-stats = []
# Experimental section

File diff suppressed because it is too large Load Diff

266
tfhe/examples/ly23_perf.rs Normal file
View File

@@ -0,0 +1,266 @@
#![allow(dead_code)]
use tfhe::core_crypto::prelude::*;
use tfhe::shortint::parameters::*;
////////LY23///////////
struct ParametersLY23 {
param: ClassicPBSParameters,
log_extension_factor: u64,
}
const LY23_1: ParametersLY23 = ParametersLY23 {
param: PARAM_MESSAGE_1_CARRY_0_LY23_EXT_FACT_0_64,
log_extension_factor: 0,
};
const LY23_2: ParametersLY23 = ParametersLY23 {
param: PARAM_MESSAGE_1_CARRY_1_LY23_EXT_FACT_0_64,
log_extension_factor: 0,
};
const LY23_3: ParametersLY23 = ParametersLY23 {
param: PARAM_MESSAGE_1_CARRY_2_LY23_EXT_FACT_0_64,
log_extension_factor: 0,
};
const LY23_4: ParametersLY23 = ParametersLY23 {
param: PARAM_MESSAGE_2_CARRY_2_LY23_EXT_FACT_0_64,
log_extension_factor: 0,
};
const LY23_5: ParametersLY23 = ParametersLY23 {
param: PARAM_MESSAGE_2_CARRY_3_LY23_EXT_FACT_1_64,
log_extension_factor: 1,
};
const LY23_6: ParametersLY23 = ParametersLY23 {
param: PARAM_MESSAGE_3_CARRY_3_LY23_EXT_FACT_2_64,
log_extension_factor: 2,
};
const LY23_7: ParametersLY23 = ParametersLY23 {
param: PARAM_MESSAGE_3_CARRY_4_LY23_EXT_FACT_3_64,
log_extension_factor: 3,
};
const LY23_8: ParametersLY23 = ParametersLY23 {
param: PARAM_MESSAGE_4_CARRY_4_LY23_EXT_FACT_4_64,
log_extension_factor: 4,
};
const PARAM_BENCHES_LY23: [ParametersLY23; 8] = [
LY23_1, LY23_2, LY23_3, LY23_4, LY23_5, LY23_6, LY23_7, LY23_8,
];
pub fn generate_programmable_bootstrap_glwe_lut<F, Scalar: UnsignedTorus + CastFrom<usize>>(
polynomial_size: PolynomialSize,
glwe_size: GlweSize,
message_modulus: usize,
ciphertext_modulus: tfhe::core_crypto::prelude::CiphertextModulus<Scalar>,
delta: Scalar,
f: F,
) -> GlweCiphertextOwned<Scalar>
where
F: Fn(Scalar) -> Scalar,
{
// N/(p/2) = size of each block, to correct noise from the input we introduce the
// notion of box, which manages redundancy to yield a denoised value
// for several noisy values around a true input value.
let box_size = polynomial_size.0 / message_modulus;
// Create the accumulator
let mut accumulator_scalar = vec![Scalar::ZERO; polynomial_size.0];
// Fill each box with the encoded denoised value
for i in 0..message_modulus {
let index = i * box_size;
accumulator_scalar[index..index + box_size]
.iter_mut()
.for_each(|a| *a = f(Scalar::cast_from(i)) * delta);
}
let half_box_size = box_size / 2;
if ciphertext_modulus.is_compatible_with_native_modulus() {
// Negate the first half_box_size coefficients to manage negacyclicity and rotate
for a_i in accumulator_scalar[0..half_box_size].iter_mut() {
*a_i = (*a_i).wrapping_neg();
}
} else {
let modulus: Scalar = ciphertext_modulus.get_custom_modulus().cast_into();
for a_i in accumulator_scalar[0..half_box_size].iter_mut() {
*a_i = (*a_i).wrapping_neg_custom_mod(modulus);
}
}
// Rotate the accumulator
accumulator_scalar.rotate_left(half_box_size);
let accumulator_plaintext = PlaintextList::from_container(accumulator_scalar);
allocate_and_trivially_encrypt_new_glwe_ciphertext(
glwe_size,
&accumulator_plaintext,
ciphertext_modulus,
)
}
#[inline(never)]
fn keygen(
param: ParametersLY23,
) -> (
LweSecretKeyOwned<u64>,
GlweSecretKeyOwned<u64>,
FourierLweBootstrapKeyOwned,
) {
// Create the PRNG
let mut seeder = new_seeder();
let seeder = seeder.as_mut();
let mut encryption_generator =
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
let mut secret_generator =
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
let params = param.param;
// Create the LweSecretKey
let input_lwe_secret_key = allocate_and_generate_new_binary_lwe_secret_key(
params.lwe_dimension,
&mut secret_generator,
);
let output_glwe_secret_key: GlweSecretKeyOwned<u64> =
allocate_and_generate_new_binary_glwe_secret_key(
params.glwe_dimension,
params.polynomial_size,
&mut secret_generator,
);
// Create the empty bootstrapping key in the Fourier domain
let mut fourier_bsk = FourierLweBootstrapKey::new(
params.lwe_dimension,
params.glwe_dimension.to_glwe_size(),
params.polynomial_size,
params.pbs_base_log,
params.pbs_level,
);
let bsk = par_allocate_and_generate_new_lwe_bootstrap_key(
&input_lwe_secret_key,
&output_glwe_secret_key,
params.pbs_base_log,
params.pbs_level,
params.glwe_noise_distribution,
params.ciphertext_modulus,
&mut encryption_generator,
);
par_convert_standard_lwe_bootstrap_key_to_fourier(&bsk, &mut fourier_bsk);
(input_lwe_secret_key, output_glwe_secret_key, fourier_bsk)
}
#[inline(never)]
fn run() {
let mut seeder = new_seeder();
let seeder = seeder.as_mut();
let mut encryption_generator =
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
let param = LY23_6;
let params = param.param;
let glwe_dimension = params.glwe_dimension;
let polynomial_size = params.polynomial_size;
let extension_factor = Ly23ExtensionFactor(1 << param.log_extension_factor);
let extended_polynomial_size = PolynomialSize(polynomial_size.0 * extension_factor.0);
let (input_lwe_secret_key, output_glwe_secret_key, fourier_bsk) = keygen(param);
let output_lwe_secret_key = output_glwe_secret_key.as_lwe_secret_key();
// for param in PARAM_BENCHES_LY23.iter() {
// }
// Allocate a new LweCiphertext and encrypt our plaintext
let lwe_ciphertext_in: LweCiphertextOwned<u64> = allocate_and_encrypt_new_lwe_ciphertext(
&input_lwe_secret_key,
Plaintext(0u64),
params.lwe_noise_distribution,
params.ciphertext_modulus,
&mut encryption_generator,
);
let total_modulus = params.message_modulus.0 * params.carry_modulus.0;
let accumulator = generate_programmable_bootstrap_glwe_lut(
extended_polynomial_size,
glwe_dimension.to_glwe_size(),
total_modulus,
params.ciphertext_modulus,
(1u64 << 63) >> total_modulus.ilog2(),
|x| x,
);
// Allocate the LweCiphertext to store the result of the PBS
let mut out_pbs_ct = LweCiphertext::new(
0u64,
output_lwe_secret_key.lwe_dimension().to_lwe_size(),
params.ciphertext_modulus,
);
let fft = Fft::new(fourier_bsk.polynomial_size());
let fft = fft.as_view();
let mut buffers = ComputationBuffers::new();
buffers.resize(
programmable_bootstrap_lwe_ciphertext_mem_optimized_requirement_ly23::<u64>(
glwe_dimension.to_glwe_size(),
polynomial_size,
extension_factor,
fft,
)
.unwrap()
.unaligned_bytes_required(),
);
// let mut thread_buffers = Vec::with_capacity(extension_factor.0);
// for _ in 0..extension_factor.0 {
// let mut buffer = ComputationBuffers::new();
// buffer.resize(
// add_external_product_assign_mem_optimized_requirement::<u64>(
// glwe_dimension.to_glwe_size(),
// params.polynomial_size,
// fft,
// )
// .unwrap()
// .unaligned_bytes_required(),
// );
// thread_buffers.push(buffer);
// }
// let mut thread_stacks: Vec<_> = thread_buffers.iter_mut().map(|x| x.stack()).collect();
let start = std::time::Instant::now();
const LOOPS: u32 = 2000;
for _ in 0..LOOPS {
fourier_bsk.as_view().bootstrap_ly23(
out_pbs_ct.as_mut_view(),
lwe_ciphertext_in.as_view(),
accumulator.as_view(),
extension_factor,
fft,
buffers.stack(),
);
}
let elapsed = start.elapsed();
let elapsed_per_pbs = elapsed / LOOPS;
println!("Elapsed: {elapsed:?}");
println!("Runtime per PBS: {elapsed_per_pbs:?}");
}
pub fn main() {
run()
}

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,9 @@ use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use crate::core_crypto::fft_impl::fft64::crypto::bootstrap::bootstrap_scratch;
use crate::core_crypto::fft_impl::fft64::crypto::bootstrap::{
bootstrap_ly23_scratch, bootstrap_scratch, FourierLweBootstrapKey,
};
use crate::core_crypto::fft_impl::fft64::crypto::ggsw::{
add_external_product_assign as impl_add_external_product_assign,
add_external_product_assign_scratch as impl_add_external_product_assign_scratch, cmux,
@@ -1071,3 +1073,12 @@ pub fn programmable_bootstrap_lwe_ciphertext_mem_optimized_requirement<OutputSca
) -> Result<StackReq, SizeOverflow> {
bootstrap_scratch::<OutputScalar>(glwe_size, polynomial_size, fft)
}
pub fn programmable_bootstrap_lwe_ciphertext_mem_optimized_requirement_ly23<Scalar>(
glwe_size: GlweSize,
small_polynomial_size: PolynomialSize,
extension_factor: Ly23ExtensionFactor,
fft: FftView<'_>,
) -> Result<StackReq, SizeOverflow> {
bootstrap_ly23_scratch::<Scalar>(glwe_size, small_polynomial_size, extension_factor, fft)
}

View File

@@ -13,6 +13,7 @@ pub mod lwe_bootstrap_key_generation;
pub mod lwe_compact_ciphertext_list_expansion;
pub mod lwe_compact_public_key_generation;
pub mod lwe_encryption;
pub mod lwe_extended_programmable_bootstrapping;
pub mod lwe_keyswitch;
pub mod lwe_keyswitch_key_generation;
pub mod lwe_linear_algebra;
@@ -61,6 +62,7 @@ pub use lwe_bootstrap_key_generation::*;
pub use lwe_compact_ciphertext_list_expansion::*;
pub use lwe_compact_public_key_generation::*;
pub use lwe_encryption::*;
pub use lwe_extended_programmable_bootstrapping::*;
pub use lwe_keyswitch::*;
pub use lwe_keyswitch_key_generation::*;
pub use lwe_linear_algebra::*;

View File

@@ -270,7 +270,11 @@ pub struct LweBskGroupingFactor(pub usize);
impl LweBskGroupingFactor {
pub fn ggsw_per_multi_bit_element(&self) -> GgswPerLweMultiBitBskElement {
GgswPerLweMultiBitBskElement(1 << self.0)
GgswPerLweMultiBitBskElement(self.group_power_set_size())
}
pub fn group_power_set_size(&self) -> usize {
1 << self.0
}
}
@@ -377,3 +381,9 @@ impl std::ops::Mul<EncryptionNoiseSampleCount> for usize {
/// A quantity representing a number of bytes used for noise generation during encryption.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct EncryptionNoiseByteCount(pub usize);
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub struct Ly23ExtensionFactor(pub usize);
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub struct Ly23ShortcutCoeffCount(pub usize);

View File

@@ -52,6 +52,12 @@ impl<T> Container for aligned_vec::AVec<T> {
impl<T> ContainerMut for aligned_vec::AVec<T> {}
impl<'data, T> Container for dyn_stack::DynArray<'data, T> {
type Element = T;
}
impl<'data, T> ContainerMut for dyn_stack::DynArray<'data, T> {}
pub trait IntoContainerOwned: Container + AsMut<[Self::Element]> {
fn collect<I: Iterator<Item = Self::Element>>(iter: I) -> Self;
}

View File

@@ -500,4 +500,16 @@ pub trait ContiguousEntityContainerMut: ContiguousEntityContainer + AsMut<[Self:
.zip(rayon::iter::repeatn(meta, entity_count))
.map(|(elt, meta)| Self::SelfMutView::<'_>::create_from(elt, meta))
}
fn rotate_left(&mut self, count: usize) {
let entity_view_pod_size = self.get_entity_view_pod_size();
let pod_rotation = entity_view_pod_size * count;
self.as_mut().rotate_left(pod_rotation);
}
fn rotate_right(&mut self, count: usize) {
let entity_view_pod_size = self.get_entity_view_pod_size();
let pod_rotation = entity_view_pod_size * count;
self.as_mut().rotate_right(pod_rotation);
}
}

View File

@@ -164,6 +164,29 @@ impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> GlweCiphertextList
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
self.ciphertext_modulus
}
pub fn as_view(&self) -> GlweCiphertextListView<C::Element> {
GlweCiphertextListView::from_container(
self.as_ref(),
self.glwe_size(),
self.polynomial_size(),
self.ciphertext_modulus(),
)
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> GlweCiphertextList<C> {
pub fn as_mut_view(&mut self) -> GlweCiphertextListMutView<C::Element> {
let glwe_size = self.glwe_size();
let polynomial_size = self.polynomial_size();
let ciphertext_modulus = self.ciphertext_modulus();
GlweCiphertextListMutView::from_container(
self.as_mut(),
glwe_size,
polynomial_size,
ciphertext_modulus,
)
}
}
/// A [`GlweCiphertextList`] owning the memory for its own storage.

View File

@@ -1,4 +1,5 @@
use super::*;
use std::io::ErrorKind::Interrupted;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct StairKSParam<Scalar: UnsignedInteger> {
@@ -298,3 +299,335 @@ fn lwe_encrypt_stair_keyswitch_pbs_decrypt_custom_mod<
fn lwe_encrypt_stair_keyswitch_pbs_decrypt_custom_mod_params_stair_4() {
lwe_encrypt_stair_keyswitch_pbs_decrypt_custom_mod(PRECISION_4_STAIR)
}
pub struct StairKSParamOneStepLY<Scalar: UnsignedInteger> {
pub log_precision: MessageModulusLog,
/// This value is unused but allows to identify the parameter optimization that was done
pub _log_mu: usize,
pub glwe_dimension: GlweDimension,
pub polynomial_size: PolynomialSize,
pub partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount,
pub bsk_glwe_noise_distribution: DynamicDistribution<Scalar>,
pub lwe_dimension: LweDimension,
pub ks_lwe_noise_distribution: DynamicDistribution<Scalar>,
pub pbs_level: DecompositionLevelCount,
pub pbs_base_log: DecompositionBaseLog,
pub ks_level: DecompositionLevelCount,
pub ks_base_log: DecompositionBaseLog,
/// The number of elements being dropped when going from the large key to the inter key
pub ks_unshared_coeff_count: LweSecretKeyUnsharedCoefCount,
pub ciphertext_modulus: CiphertextModulus<Scalar>,
pub log_extension_factor: u64,
}
pub const PRECISION_2_STAIR_ONE_STEP: StairKSParamOneStepLY<u64> = StairKSParamOneStepLY {
log_precision: MessageModulusLog(2),
_log_mu: 2,
glwe_dimension: GlweDimension(3),
polynomial_size: PolynomialSize(512),
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(3 * 512),
bsk_glwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
8.390846778442423e-12,
)),
lwe_dimension: LweDimension(709),
ks_lwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
2.0944272028597803e-05,
)),
pbs_level: DecompositionLevelCount(1),
pbs_base_log: DecompositionBaseLog(18),
ks_level: DecompositionLevelCount(4),
ks_base_log: DecompositionBaseLog(3),
ks_unshared_coeff_count: LweSecretKeyUnsharedCoefCount(827),
ciphertext_modulus: CiphertextModulus::new_native(),
log_extension_factor: 1,
};
pub const PRECISION_4_STAIR_ONE_STEP: StairKSParamOneStepLY<u64> = StairKSParamOneStepLY {
log_precision: MessageModulusLog(4),
_log_mu: 4,
glwe_dimension: GlweDimension(1),
polynomial_size: PolynomialSize(2048),
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2048),
bsk_glwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
9.188173694010523e-16,
)),
lwe_dimension: LweDimension(790),
ks_lwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
4.948683445911403e-06,
)),
pbs_level: DecompositionLevelCount(1),
pbs_base_log: DecompositionBaseLog(23),
ks_level: DecompositionLevelCount(5),
ks_base_log: DecompositionBaseLog(3),
ks_unshared_coeff_count: LweSecretKeyUnsharedCoefCount(1258),
ciphertext_modulus: CiphertextModulus::new_native(),
log_extension_factor: 1,
};
pub const PRECISION_6_STAIR_ONE_STEP: StairKSParamOneStepLY<u64> = StairKSParamOneStepLY {
log_precision: MessageModulusLog(6),
_log_mu: 6,
glwe_dimension: GlweDimension(1),
polynomial_size: PolynomialSize(2048),
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2048),
bsk_glwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
9.188173694010523e-16,
)),
lwe_dimension: LweDimension(853),
ks_lwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
1.6112135273032647e-06,
)),
pbs_level: DecompositionLevelCount(2),
pbs_base_log: DecompositionBaseLog(15),
ks_level: DecompositionLevelCount(8),
ks_base_log: DecompositionBaseLog(2),
ks_unshared_coeff_count: LweSecretKeyUnsharedCoefCount(1195),
ciphertext_modulus: CiphertextModulus::new_native(),
log_extension_factor: 2,
};
pub const PRECISION_8_STAIR_ONE_STEP: StairKSParamOneStepLY<u64> = StairKSParamOneStepLY {
log_precision: MessageModulusLog(8),
_log_mu: 8,
glwe_dimension: GlweDimension(1),
polynomial_size: PolynomialSize(2048),
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2048),
bsk_glwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
9.188173694010523e-16,
)),
lwe_dimension: LweDimension(925),
ks_lwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
4.468862725257972e-07,
)),
pbs_level: DecompositionLevelCount(2),
pbs_base_log: DecompositionBaseLog(16),
ks_level: DecompositionLevelCount(19),
ks_base_log: DecompositionBaseLog(1),
ks_unshared_coeff_count: LweSecretKeyUnsharedCoefCount(1123),
ciphertext_modulus: CiphertextModulus::new_native(),
log_extension_factor: 4,
};
fn lwe_encrypt_stair_keyswitch_pbs_decrypt_custom_mod_one_step<
Scalar: UnsignedTorus + Send + Sync + CastFrom<usize> + CastInto<usize>,
>(
params: StairKSParamOneStepLY<Scalar>,
) {
let StairKSParamOneStepLY {
log_precision,
_log_mu,
glwe_dimension,
polynomial_size,
partial_glwe_secret_key_fill,
bsk_glwe_noise_distribution,
lwe_dimension,
ks_lwe_noise_distribution,
pbs_level,
pbs_base_log,
ks_level,
ks_base_log,
ks_unshared_coeff_count,
ciphertext_modulus,
log_extension_factor,
} = params;
let shortcut_coeff_count = Ly23ShortcutCoeffCount(1);
let extension_factor = Ly23ExtensionFactor(1 << log_extension_factor);
let extended_polynomial_size = PolynomialSize(polynomial_size.0 * extension_factor.0);
let msg_modulus = Scalar::ONE.shl(log_precision.0);
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
let mut rsc = TestResources::new();
const NB_TESTS: usize = 10;
let mut msg = msg_modulus;
let delta: Scalar = encoding_with_padding / msg_modulus;
let large_lwe_dimension = glwe_dimension.to_equivalent_lwe_dimension(polynomial_size);
// Our algorithm is set up so that this equality holds, but in practice it could be more generic
// if we had an intermediate LweDimension being defined
assert_eq!(
partial_glwe_secret_key_fill.0,
lwe_dimension.0 + ks_unshared_coeff_count.0
);
while msg != Scalar::ZERO {
msg = msg.wrapping_sub(Scalar::ONE);
let glwe_secret_key = allocate_and_generate_new_partial_binary_glwe_secret_key(
glwe_dimension,
polynomial_size,
partial_glwe_secret_key_fill,
&mut rsc.secret_random_generator,
);
let large_lwe_secret_key = glwe_secret_key.as_lwe_secret_key();
let large_lwe_dimension_without_zeros = LweDimension(partial_glwe_secret_key_fill.0);
let inter_lwe_dimension = LweDimension(
large_lwe_dimension_without_zeros
.shared_coef_count_from(ks_unshared_coeff_count)
.0,
);
let inter_lwe_secret_key = allocate_and_generate_fully_shared_binary_lwe_secret_key(
&large_lwe_secret_key,
inter_lwe_dimension,
);
//Shrinking KSK generations
let ksk_large_to_inter = allocate_and_generate_new_lwe_shrinking_keyswitch_key(
&large_lwe_secret_key,
LweSecretKeySharedCoefCount(inter_lwe_dimension.0),
ks_base_log,
ks_level,
ks_lwe_noise_distribution,
ciphertext_modulus,
&mut rsc.encryption_random_generator,
);
assert!(check_encrypted_content_respects_mod(
&ksk_large_to_inter,
ciphertext_modulus
));
//PBS PART
let mut bsk = LweBootstrapKey::new(
Scalar::ZERO,
glwe_dimension.to_glwe_size(),
polynomial_size,
pbs_base_log,
pbs_level,
lwe_dimension,
ciphertext_modulus,
);
par_generate_lwe_bootstrap_key(
&inter_lwe_secret_key,
&glwe_secret_key,
&mut bsk,
bsk_glwe_noise_distribution,
&mut rsc.encryption_random_generator,
);
assert!(check_encrypted_content_respects_mod(
&*bsk,
ciphertext_modulus
));
let mut fbsk = FourierLweBootstrapKey::new(
inter_lwe_secret_key.lwe_dimension(),
glwe_dimension.to_glwe_size(),
polynomial_size,
pbs_base_log,
pbs_level,
);
convert_standard_lwe_bootstrap_key_to_fourier(&bsk, &mut fbsk);
drop(bsk);
let accumulator = generate_programmable_bootstrap_glwe_lut(
extended_polynomial_size,
glwe_dimension.to_glwe_size(),
msg_modulus.cast_into(),
ciphertext_modulus,
delta,
|x| x,
);
let mut buffers = ComputationBuffers::new();
let fft = Fft::new(polynomial_size);
let fft = fft.as_view();
buffers.resize(
programmable_bootstrap_lwe_ciphertext_mem_optimized_requirement_ly23::<u64>(
glwe_dimension.to_glwe_size(),
polynomial_size,
extension_factor,
fft,
)
.unwrap()
.unaligned_bytes_required(),
);
for _ in 0..NB_TESTS {
let plaintext = Plaintext(msg * delta);
//Encryption
let mut large_lwe = LweCiphertext::new(
Scalar::ZERO,
large_lwe_dimension.to_lwe_size(),
ciphertext_modulus,
);
encrypt_lwe_ciphertext(
&large_lwe_secret_key,
&mut large_lwe,
plaintext,
bsk_glwe_noise_distribution,
&mut rsc.encryption_random_generator,
);
assert!(check_encrypted_content_respects_mod(
&large_lwe,
ciphertext_modulus
));
//Shrinking KS
let mut inter_lwe = LweCiphertext::new(
Scalar::ZERO,
inter_lwe_secret_key.lwe_dimension().to_lwe_size(),
ciphertext_modulus,
);
let mut out_pbs_ct = LweCiphertext::new(
Scalar::ZERO,
large_lwe_secret_key.lwe_dimension().to_lwe_size(),
ciphertext_modulus,
);
// Check the AP works for several iterations
for _ in 0..NB_TESTS {
shrinking_keyswitch_lwe_ciphertext(&ksk_large_to_inter, &large_lwe, &mut inter_lwe);
assert!(check_encrypted_content_respects_mod(
&inter_lwe,
ciphertext_modulus
));
let dec_inter = decrypt_lwe_ciphertext(&inter_lwe_secret_key, &inter_lwe);
let decoded = round_decode(dec_inter.0, delta) % msg_modulus;
assert_eq!(decoded, msg, "Err after first shrinking KS");
fbsk.as_view().bootstrap_bergerat24(
out_pbs_ct.as_mut_view(),
inter_lwe.as_view(),
accumulator.as_view(),
extension_factor,
shortcut_coeff_count,
fft,
buffers.stack(),
);
assert!(check_encrypted_content_respects_mod(
&out_pbs_ct,
ciphertext_modulus
));
let dec_large = decrypt_lwe_ciphertext(&large_lwe_secret_key, &out_pbs_ct);
let decoded = round_decode(dec_large.0, delta) % msg_modulus;
assert_eq!(decoded, msg, "Err after PBS");
}
}
}
}
#[test]
fn lwe_encrypt_stair_keyswitch_pbs_decrypt_custom_mod_params_stair_4_one_step() {
lwe_encrypt_stair_keyswitch_pbs_decrypt_custom_mod_one_step(PRECISION_2_STAIR_ONE_STEP);
println!("2ok");
lwe_encrypt_stair_keyswitch_pbs_decrypt_custom_mod_one_step(PRECISION_4_STAIR_ONE_STEP);
println!("4ok");
lwe_encrypt_stair_keyswitch_pbs_decrypt_custom_mod_one_step(PRECISION_6_STAIR_ONE_STEP);
println!("6ok");
lwe_encrypt_stair_keyswitch_pbs_decrypt_custom_mod_one_step(PRECISION_8_STAIR_ONE_STEP);
println!("8ok");
}

File diff suppressed because it is too large Load Diff

View File

@@ -32,8 +32,8 @@
#![allow(clippy::bool_to_int_with_if)] // 6
#![allow(clippy::unsafe_derive_deserialize)] // 1
#![allow(clippy::cast_possible_wrap)]
#![allow(deprecated)]
// 1
// These pedantic lints are deemed to bring too little value therefore they are allowed (which are
// their natural state anyways, being pedantic lints)

View File

@@ -242,6 +242,123 @@ named_params_impl!( ShortintParameterSet =>
COVERAGE_PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS,
#[cfg(tarpaulin)]
COVERAGE_PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS,
//
//new params Bench LY
PARAM_MESSAGE_2_PBS_MS_0_EF_0_40,
PARAM_MESSAGE_3_PBS_MS_0_EF_0_40,
PARAM_MESSAGE_4_PBS_MS_0_EF_0_40,
PARAM_MESSAGE_5_PBS_MS_0_EF_0_40,
PARAM_MESSAGE_6_PBS_MS_0_EF_0_40,
PARAM_MESSAGE_7_PBS_MS_0_EF_0_40,
PARAM_MESSAGE_8_PBS_MS_0_EF_0_40,
PARAM_MESSAGE_9_PBS_MS_0_EF_0_40,
//
PARAM_MESSAGE_5_SORTED_PBS_MS_0_EF_1_40,
PARAM_MESSAGE_6_SORTED_PBS_MS_0_EF_3_40,
PARAM_MESSAGE_7_SORTED_PBS_MS_0_EF_3_40,
PARAM_MESSAGE_8_SORTED_PBS_MS_0_EF_4_40,
PARAM_MESSAGE_9_SORTED_PBS_MS_0_EF_5_40,
//
PARAM_MESSAGE_5_BEST_PBS_MS_20_EF_1_40,
PARAM_MESSAGE_6_BEST_PBS_MS_1_EF_3_40,
PARAM_MESSAGE_7_BEST_PBS_MS_94_EF_3_40,
PARAM_MESSAGE_8_BEST_PBS_MS_90_EF_4_40,
PARAM_MESSAGE_9_BEST_PBS_MS_76_EF_5_40,
//
PARAM_MESSAGE_2_PBS_MS_0_EF_0_64,
PARAM_MESSAGE_3_PBS_MS_0_EF_0_64,
PARAM_MESSAGE_4_PBS_MS_0_EF_0_64,
PARAM_MESSAGE_5_PBS_MS_0_EF_0_64,
PARAM_MESSAGE_6_PBS_MS_0_EF_0_64,
PARAM_MESSAGE_7_PBS_MS_0_EF_0_64,
PARAM_MESSAGE_8_PBS_MS_0_EF_0_64,
PARAM_MESSAGE_9_PBS_MS_0_EF_0_64,
//
PARAM_MESSAGE_5_SORTED_PBS_MS_0_EF_2_64,
PARAM_MESSAGE_6_SORTED_PBS_MS_0_EF_2_64,
PARAM_MESSAGE_7_SORTED_PBS_MS_0_EF_3_64,
PARAM_MESSAGE_8_SORTED_PBS_MS_0_EF_4_64,
PARAM_MESSAGE_9_SORTED_PBS_MS_0_EF_5_64,
//
PARAM_MESSAGE_5_BEST_PBS_MS_151_EF_2_80,
PARAM_MESSAGE_6_BEST_PBS_MS_255_EF_3_80,
PARAM_MESSAGE_7_BEST_PBS_MS_256_EF_4_80,
PARAM_MESSAGE_8_BEST_PBS_MS_256_EF_5_80,
PARAM_MESSAGE_9_BEST_PBS_MS_255_EF_6_80,
//
PARAM_MESSAGE_2_PBS_MS_0_EF_0_80,
PARAM_MESSAGE_3_PBS_MS_0_EF_0_80,
PARAM_MESSAGE_4_PBS_MS_0_EF_0_80,
PARAM_MESSAGE_5_PBS_MS_0_EF_0_81,
PARAM_MESSAGE_6_PBS_MS_0_EF_0_81,
PARAM_MESSAGE_7_PBS_MS_0_EF_0_80,
PARAM_MESSAGE_8_PBS_MS_0_EF_0_81,
PARAM_MESSAGE_9_PBS_MS_0_EF_0_80,
//
PARAM_MESSAGE_5_SORTED_PBS_MS_0_EF_2_80,
PARAM_MESSAGE_6_SORTED_PBS_MS_0_EF_4_80,
PARAM_MESSAGE_7_SORTED_PBS_MS_0_EF_4_81,
PARAM_MESSAGE_8_SORTED_PBS_MS_0_EF_5_81,
PARAM_MESSAGE_9_SORTED_PBS_MS_0_EF_6_81,
//
PARAM_MESSAGE_2_PBS_MS_0_EF_0_129,
PARAM_MESSAGE_3_PBS_MS_0_EF_0_130,
PARAM_MESSAGE_4_PBS_MS_0_EF_0_129,
PARAM_MESSAGE_5_PBS_MS_0_EF_0_130,
PARAM_MESSAGE_6_PBS_MS_0_EF_0_129,
PARAM_MESSAGE_7_PBS_MS_0_EF_0_129,
PARAM_MESSAGE_8_PBS_MS_0_EF_0_129,
PARAM_MESSAGE_9_PBS_MS_0_EF_0_129,
//
PARAM_MESSAGE_4_SORTED_PBS_MS_0_EF_1_128,
PARAM_MESSAGE_5_SORTED_PBS_MS_0_EF_2_128,
PARAM_MESSAGE_6_SORTED_PBS_MS_0_EF_3_129,
PARAM_MESSAGE_7_SORTED_PBS_MS_0_EF_4_128,
PARAM_MESSAGE_8_SORTED_PBS_MS_0_EF_5_128,
PARAM_MESSAGE_9_SORTED_PBS_MS_0_EF_6_129,
//
PARAM_MESSAGE_4_BEST_PBS_MS_123_EF_1_128,
PARAM_MESSAGE_5_BEST_PBS_MS_0_EF_2_128,
PARAM_MESSAGE_6_BEST_PBS_MS_150_EF_3_128,
PARAM_MESSAGE_7_BEST_PBS_MS_148_EF_4_128,
PARAM_MESSAGE_8_BEST_PBS_MS_137_EF_5_128,
PARAM_MESSAGE_9_BEST_PBS_MS_96_EF_6_128,
//
PARAM_MESSAGE_1_CARRY_1_PARALLEL_PBS_MS_0_EF_4_128,
PARAM_MESSAGE_1_CARRY_2_PARALLEL_PBS_MS_0_EF_4_128,
PARAM_MESSAGE_2_CARRY_2_PARALLEL_PBS_MS_0_EF_4_128,
PARAM_MESSAGE_2_CARRY_3_PARALLEL_PBS_MS_0_EF_4_128,
PARAM_MESSAGE_3_CARRY_3_PARALLEL_PBS_MS_0_EF_4_128,
PARAM_MESSAGE_3_CARRY_4_PARALLEL_PBS_MS_0_EF_4_128,
PARAM_MESSAGE_4_CARRY_4_PARALLEL_PBS_MS_0_EF_4_128,
PARAM_MESSAGE_4_CARRY_5_PARALLEL_PBS_MS_0_EF_4_128,
//
PARAM_MESSAGE_1_CARRY_1_PARALLEL_PBS_MS_0_EF_4_80,
PARAM_MESSAGE_1_CARRY_2_PARALLEL_PBS_MS_0_EF_4_80,
PARAM_MESSAGE_2_CARRY_2_PARALLEL_PBS_MS_0_EF_4_80,
PARAM_MESSAGE_2_CARRY_3_PARALLEL_PBS_MS_0_EF_4_80,
PARAM_MESSAGE_3_CARRY_3_PARALLEL_PBS_MS_0_EF_4_80,
PARAM_MESSAGE_3_CARRY_4_PARALLEL_PBS_MS_0_EF_4_80,
PARAM_MESSAGE_4_CARRY_4_PARALLEL_PBS_MS_0_EF_4_80,
PARAM_MESSAGE_4_CARRY_5_PARALLEL_PBS_MS_0_EF_4_80,
//
PARAM_MESSAGE_1_CARRY_1_PARALLEL_PBS_MS_0_EF_4_64,
PARAM_MESSAGE_1_CARRY_2_PARALLEL_PBS_MS_0_EF_4_64,
PARAM_MESSAGE_2_CARRY_2_PARALLEL_PBS_MS_0_EF_4_64,
PARAM_MESSAGE_2_CARRY_3_PARALLEL_PBS_MS_0_EF_4_64,
PARAM_MESSAGE_3_CARRY_3_PARALLEL_PBS_MS_0_EF_4_64,
PARAM_MESSAGE_3_CARRY_4_PARALLEL_PBS_MS_0_EF_4_64,
PARAM_MESSAGE_4_CARRY_4_PARALLEL_PBS_MS_0_EF_4_64,
PARAM_MESSAGE_4_CARRY_5_PARALLEL_PBS_MS_0_EF_4_64,
//
PARAM_MESSAGE_1_CARRY_1_PARALLEL_PBS_MS_0_EF_4_40,
PARAM_MESSAGE_1_CARRY_2_PARALLEL_PBS_MS_0_EF_4_40,
PARAM_MESSAGE_2_CARRY_2_PARALLEL_PBS_MS_0_EF_4_40,
PARAM_MESSAGE_2_CARRY_3_PARALLEL_PBS_MS_0_EF_4_40,
PARAM_MESSAGE_3_CARRY_3_PARALLEL_PBS_MS_0_EF_4_40,
PARAM_MESSAGE_3_CARRY_4_PARALLEL_PBS_MS_0_EF_4_40,
PARAM_MESSAGE_4_CARRY_4_PARALLEL_PBS_MS_0_EF_4_40,
PARAM_MESSAGE_4_CARRY_5_PARALLEL_PBS_MS_0_EF_4_40,
);
impl NamedParam for ClassicPBSParameters {

File diff suppressed because it is too large Load Diff