Compare commits

...

18 Commits

Author SHA1 Message Date
Mayeul@Zama
98d31d8f17 chore(tfhe): prepare release 0.11.2 2025-01-30 10:32:10 +01:00
Mayeul@Zama
721ca2fed6 feat(integer): impl Named for compression keys 2025-01-30 10:32:10 +01:00
Nicolas Sarlin
e95399306e chore(doc): add strings feature to doctests 2025-01-20 10:59:27 +01:00
Nicolas Sarlin
f3e3c2d499 chore(tfhe): prepare release 0.11.1 2025-01-20 10:59:27 +01:00
Nicolas Sarlin
626074a16b fix(safe_ser): aliases in named for renamed types deserialization 2025-01-17 13:32:38 +01:00
Pedro Alves
b4fa072d2c chore(docs): Remove mention to NVLink
NVLink is not needed anymore in the CUDA backend.
2025-01-16 09:34:54 +01:00
Arthur Meyre
0377a74103 chore(docs): fix various issues with the docs 2025-01-15 11:37:00 +01:00
Arthur Meyre
8690c625fc docs: indicate PBS benchmarks have Gaussian parameters 2025-01-13 16:57:55 +01:00
Arthur Meyre
ce81c4983e chore(docs): replace tabs by spaces 2025-01-13 16:57:55 +01:00
Arthur Meyre
c271265749 docs: add TUniform distribution and link in benchmarks 2025-01-13 16:57:55 +01:00
Agnes Leroy
f0e5e9fce9 chore(doc): update links to the benchmark tables 2025-01-13 16:57:55 +01:00
Nicolas Sarlin
65dd420ed8 doc(zk): explain how to use zkv1 2025-01-13 13:41:08 +01:00
tmontaigu
478716db7e chore(docs): add strings guides 2025-01-13 13:24:50 +01:00
Arthur Meyre
5392070c2e chore: enable strings for docs.rs generation 2025-01-10 14:33:14 +01:00
Agnes Leroy
1bb7526157 chore(test): modify cpu multi-bit parameters for noise test 2025-01-08 15:16:50 +01:00
David Testé
e99676f294 chore(ci): relocate permission checking after should-run step
This induces a failure if the job has to run AND if the triggering actor isn't a member of the zama-ai organization. That would help tfhe-rs maintainers to re-run only workflows that are supposed to run.

The reference is selected based on the event emitted.

We also now use token with restricted permission to check out the repository.
2025-01-08 09:30:19 +01:00
David Testé
ebc186e7f5 chore(ci): remove pull_request which duplicate pull_request_target
Previously pull_request and pull_request_target events were both
emitted thus leading one cancelling the other because of
concurrency group name format.
Since external contribution needs to be allowed we only need
pull_request_target event.
2025-01-08 09:30:19 +01:00
yuxizama
5652d417c8 chore(docs): update discord link 2025-01-07 18:46:22 +01:00
32 changed files with 433 additions and 138 deletions

View File

@@ -11,26 +11,17 @@ env:
SLACK_ICON: https://pbs.twimg.com/profile_images/1274014582265298945/OjBKP9kn_400x400.png
SLACK_USERNAME: ${{ secrets.BOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
IS_PULL_REQUEST: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
IS_PULL_REQUEST: ${{ github.event_name == 'pull_request_target' }}
REF: ${{ github.event.pull_request.head.sha || github.sha }}
on:
# Allows you to run this workflow manually from the Actions tab as an alternative.
workflow_dispatch:
pull_request:
pull_request_target:
jobs:
check-user-permission:
if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target'
uses: ./.github/workflows/check_triggering_actor.yml
secrets:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
should-run:
runs-on: ubuntu-latest
needs: check-user-permission
if: github.event_name != 'pull_request_target' ||
needs.check-user-permission.result == 'success'
permissions:
pull-requests: write
outputs:
@@ -64,8 +55,8 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
fetch-depth: 0
token: ${{ secrets.FHE_ACTIONS_TOKEN }}
ref: ${{ github.event.pull_request.head.sha }}
token: ${{ secrets.REPO_CHECKOUT_TOKEN }}
ref: ${{ env.REF }}
- name: Check for file changes
id: changed-files
@@ -133,11 +124,17 @@ jobs:
run: |
echo "any_changed=true" >> "$GITHUB_OUTPUT"
check-user-permission:
needs: should-run
uses: ./.github/workflows/check_triggering_actor.yml
secrets:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
setup-instance:
name: Setup instance (fast-tests)
if: github.event_name != 'pull_request' ||
if: github.event_name != 'pull_request_target' ||
needs.should-run.outputs.any_file_changed == 'true'
needs: should-run
needs: [ should-run, check-user-permission ]
runs-on: ubuntu-latest
outputs:
runner-name: ${{ steps.start-instance.outputs.label }}
@@ -155,8 +152,8 @@ jobs:
fast-tests:
name: Fast CPU tests
if: github.event_name != 'pull_request' ||
(github.event_name == 'pull_request' && needs.setup-instance.result != 'skipped')
if: github.event_name != 'pull_request_target' ||
(github.event_name == 'pull_request_target' && needs.setup-instance.result != 'skipped')
needs: [ should-run, setup-instance ]
concurrency:
group: ${{ github.workflow }}_${{ github.head_ref || github.ref }}
@@ -167,8 +164,8 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: 'false'
token: ${{ secrets.FHE_ACTIONS_TOKEN }}
ref: ${{ github.event.pull_request.head.sha }}
token: ${{ secrets.REPO_CHECKOUT_TOKEN }}
ref: ${{ env.REF }}
- name: Install latest stable
uses: dtolnay/rust-toolchain@a54c7afa936fefeb4456b2dd8068152669aa8203

View File

@@ -824,7 +824,7 @@ test_strings: install_rs_build_toolchain
.PHONY: test_user_doc # Run tests from the .md documentation
test_user_doc: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) --doc \
--features=boolean,shortint,integer,internal-keycache,pbs-stats,zk-pok \
--features=boolean,shortint,integer,internal-keycache,pbs-stats,zk-pok,strings \
-p $(TFHE_SPEC) \
-- test_user_docs::

View File

@@ -1,6 +1,6 @@
[package]
name = "tfhe"
version = "0.11.0"
version = "0.11.2"
edition = "2021"
readme = "../README.md"
keywords = ["fully", "homomorphic", "encryption", "fhe", "cryptography"]
@@ -53,7 +53,9 @@ strum = { version = "0.26", features = ["derive"] }
cbindgen = { version = "0.26.0", optional = true }
[dependencies]
tfhe-csprng = { version = "0.5.0", path = "../tfhe-csprng", features = ["parallel"] }
tfhe-csprng = { version = "0.5.0", path = "../tfhe-csprng", features = [
"parallel",
] }
serde = { workspace = true, features = ["default", "derive"] }
rayon = { workspace = true }
bincode = "1.3.3"
@@ -140,7 +142,15 @@ split-linked-modules = true
[package.metadata.docs.rs]
# TODO: manage builds for docs.rs based on their documentation https://docs.rs/about
features = ["boolean", "shortint", "integer", "gpu", "zk-pok", "software-prng"]
features = [
"boolean",
"shortint",
"integer",
"gpu",
"zk-pok",
"software-prng",
"strings",
]
rustdoc-args = ["--html-in-header", "katex-header.html"]
###########

View File

@@ -48,7 +48,7 @@ Take a deep dive into TFHE-rs, exploring APIs from the highest to the lowest lev
Ask technical questions and discuss with the community. Our team of experts usually answers within 24 hours during working days.
* [Community forum](https://community.zama.ai/)
* [Discord channel](https://discord.com/invite/fhe-org)
* [Discord channel](https://discord.com/invite/zama)
### Developers

View File

@@ -42,6 +42,7 @@
* [Trivial ciphertexts](guides/trivial\_ciphertext.md)
* [PBS statistics](guides/pbs-stats.md)
* [Array](guides/array.md)
* [Strings](guides/strings.md)
## Tutorials

View File

@@ -24,7 +24,7 @@ fn main() {
let mut buffer = vec![];
// The last argument is the max allowed size for the serialized buffer
// The last argument is the max allowed size for the serialized buffer
safe_serialize(&server_key, &mut buffer, 1 << 30).unwrap();
let _server_key_deser: ServerKey =
@@ -158,7 +158,7 @@ In the following example, we use [bincode](https://crates.io/crates/bincode) for
[dependencies]
# ...
tfhe = { version = "0.11.0", features = ["integer"] }
tfhe = { version = "0.11.2", features = ["integer"] }
bincode = "1.3.3"
```

View File

@@ -14,11 +14,11 @@ The following tables benchmark the execution time of some operation sets using `
The next table shows the operation timings on CPU when all inputs are encrypted
{% embed url="https://docs.google.com/spreadsheets/d/1Z2NZvWEkDnbHPYE4Su0Oh2Zz1VBnT9dWbo3E29-LcDg/edit?usp=sharing" %}
{% embed url="https://docs.google.com/spreadsheets/d/1b_-72ArnSdaqfr-gJOnMmVdcBokYZohnylO4LUj2PMw/edit?usp=sharing" %}
The next table shows the operation timings on CPU when the left input is encrypted and the right is a clear scalar of the same size:
{% embed url="https://docs.google.com/spreadsheets/d/1NGPnuBhRasES9Ghaij4ixJJTpXVMqDzbqMniX-qIMGc/edit?usp=sharing" %}
{% embed url="https://docs.google.com/spreadsheets/d/1m3tjCi_2GSIHop2zZLAtVbhdDn5wqTGd2lOA3CcJe-U/edit?usp=sharing" %}
All timings are based on parallelized Radix-based integer operations where each block is encrypted using the default parameters `PARAM_MESSAGE_2_CARRY_2_KS_PBS`. To ensure predictable timings, we perform operations in the `default` mode, which ensures that the input and output encoding are similar (i.e., the carries are always emptied).
@@ -28,7 +28,9 @@ You can minimize operational costs by selecting from 'unchecked', 'checked', or
The next table shows the execution time of a keyswitch followed by a programmable bootstrapping depending on the precision of the input message. The associated parameter set is given. The configuration is Concrete FFT + AVX-512.
{% embed url="https://docs.google.com/spreadsheets/d/1OdZrsk0dHTWSLLvstkpiv0u5G5tE0mCqItTb7WixGdg/edit?usp=sharing" %}
Note that these benchmarks use Gaussian parameters.
{% embed url="https://docs.google.com/spreadsheets/d/1o6MWpbzbYhDs3Pnoq-2hlNEgO9G8wGR5niW-OOZ6c_4/edit?usp=sharing" %}
## Reproducing TFHE-rs benchmarks

View File

@@ -8,26 +8,28 @@ All GPU benchmarks presented here were obtained on H100 GPUs, and rely on the mu
Below come the results for the execution on a single H100.
The following table shows the performance when the inputs of the benchmarked operation are encrypted:
{% embed url="https://docs.google.com/spreadsheets/d/1dhNYXm7oY0l2qjX3dNpSZKjIBJElkEZtPDIWHZ4FA_A/edit?usp=sharing" %}
{% embed url="https://docs.google.com/spreadsheets/d/1xGWykMa8fZ7RWUjkCl-52FJ-BNge8cB-5CSHrVZ6XRo/edit?usp=sharing" %}
The following table shows the performance when the left input of the benchmarked operation is encrypted and the other is a clear scalar of the same size:
{% embed url="https://docs.google.com/spreadsheets/d/1wtnFnOwHrSOvfTWluUEaDoTULyveseVl1ZsYo3AOFKk/edit?usp=sharing" %}
{% embed url="https://docs.google.com/spreadsheets/d/1MZfE9c-cQw3yAP55tu0i8uLl4lTAiH9zW3gRFp0ve7s/edit?usp=sharing" %}
## 2xH100
Below come the results for the execution on two H100's.
The following table shows the performance when the inputs of the benchmarked operation are encrypted:
{% embed url="https://docs.google.com/spreadsheets/d/1_2AUeu3ua8_PXxMfeJCh-pp6b9e529PGVEYUuZRAThg/edit?usp=sharing" %}
{% embed url="https://docs.google.com/spreadsheets/d/1bcL0wgFk-cfR4asGSCWDFt7JaqDYJT-l4pH58A-yBkc/edit?usp=sharing" %}
The following table shows the performance when the left input of the benchmarked operation is encrypted and the other is a clear scalar of the same size:
{% embed url="https://docs.google.com/spreadsheets/d/1nLPt_m1MbkSdhMop0iKDnSN_c605l_JdMpK5JC90N_Q/edit?usp=sharing" %}
{% embed url="https://docs.google.com/spreadsheets/d/1_8VIoStixns22lQq_RBSjVm-0iFHjJpntQTrvEHZpSg/edit?usp=sharing" %}
## Programmable bootstrapping
The next table shows the execution time of a keyswitch followed by a programmable bootstrapping depending on the precision of the input message. The associated parameter set is given.
{% embed url="https://docs.google.com/spreadsheets/d/11JfbPxJ8XMMfob4AZIWhDSglTO9X_YX8R7dNQK73uuk/edit?usp=sharing" %}
Note that these benchmarks use Gaussian parameters.
{% embed url="https://docs.google.com/spreadsheets/d/1KhElQ7sIsShUSVQw5bKFoP-x5BgMaWh1pZtrVAdC3T4/edit?usp=sharing" %}

View File

@@ -2,6 +2,8 @@
This document summarizes the timings of some homomorphic operations over 64-bit encrypted integers, depending on the hardware. More details are given for [the CPU](cpu\_benchmarks.md), [the GPU](gpu\_benchmarks.md), or [zeros-knowledge proofs](zk\_proof\_benchmarks.md).
The cryptographic parameters used for benchmarking follow a tweaked uniform (TUniform) noise distribution instead of a Gaussian. The main advantage of this distribution is to be bounded, whereas the usual Gaussian one is not. In some practical cases, this can simplify the use of homomorphic computation. See the [noise section](../security\_and\_cryptography.md#noise) of the Security and cryptography documentation page for more information on the noise distributions.
You can get the parameters used for benchmarks by cloning the repository and checking out the commit you want to use (starting with the v0.11.0 release) and run the following make command:
```console
@@ -10,4 +12,4 @@ make print_doc_bench_parameters
### Operation time (ms) over FheUint 64
{% embed url="https://docs.google.com/spreadsheets/d/1ZbgsKnFH8eKrFjy9khFeaLYnUhbSV8Xu4H6rwulo0o8/edit?usp=sharing" %}
{% embed url="https://docs.google.com/spreadsheets/d/1OMdGSakEUbIFSEQKhAinTolJjvmPBbafi3DEe3UfzsQ/edit?usp=sharing" %}

View File

@@ -4,4 +4,4 @@ This document details the performance benchmarks of [zero-knowledge proofs](../.
Benchmarks for the zero-knowledge proofs have been run on a `m6i.4xlarge` with 16 cores to simulate an usual client configuration. The verification are done on a `hpc7a.96xlarge` AWS instances to mimic a powerful server.
{% embed url="https://docs.google.com/spreadsheets/d/1llCYHCz2CyLdTwXkiqhjVzJLzxW_RqdjHxmk72m1jm4/edit?usp=sharing" %}
{% embed url="https://docs.google.com/spreadsheets/d/1x12I7Tkdx63Q6sNllygg6urSd5KC1sj1wj4L9jWiET4/edit?usp=sharing" %}

View File

@@ -7,7 +7,7 @@ This document provides instructions to set up **TFHE-rs** in your project.
First, add **TFHE-rs** as a dependency in your `Cargo.toml`.
```toml
tfhe = { version = "0.11.0", features = ["boolean", "shortint", "integer"] }
tfhe = { version = "0.11.2", features = ["boolean", "shortint", "integer"] }
```
{% hint style="info" %}
@@ -35,5 +35,5 @@ By default, **TFHE-rs** makes the assumption that hardware AES features are enab
To add support for older CPU, import **TFHE-rs** with the `software-prng` feature in your `Cargo.toml`:
```toml
tfhe = { version = "0.11.0", features = ["boolean", "shortint", "integer", "software-prng"] }
tfhe = { version = "0.11.2", features = ["boolean", "shortint", "integer", "software-prng"] }
```

View File

@@ -274,39 +274,39 @@ use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheInt32};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Basic configuration to use homomorphic integers
// Basic configuration to use homomorphic integers
let config = ConfigBuilder::default().build();
// Key generation
let (client_key, server_keys) = generate_keys(config);
let clear_a = 32i32;
let clear_b = -45i32;
// Encrypting the input data using the (private) client_key
// FheInt32: Encrypted equivalent to i32
let encrypted_a = FheInt32::try_encrypt(clear_a, &client_key)?;
let encrypted_b = FheInt32::try_encrypt(clear_b, &client_key)?;
// On the server side:
set_server_key(server_keys);
// Clear equivalent computations: 32 > -45
let encrypted_comp = &encrypted_a.gt(&encrypted_b);
let clear_res = encrypted_comp.decrypt(&client_key);
assert_eq!(clear_res, clear_a > clear_b);
// `encrypted_comp` is a FheBool, thus it encrypts a boolean value.
// Key generation
let (client_key, server_keys) = generate_keys(config);
let clear_a = 32i32;
let clear_b = -45i32;
// Encrypting the input data using the (private) client_key
// FheInt32: Encrypted equivalent to i32
let encrypted_a = FheInt32::try_encrypt(clear_a, &client_key)?;
let encrypted_b = FheInt32::try_encrypt(clear_b, &client_key)?;
// On the server side:
set_server_key(server_keys);
// Clear equivalent computations: 32 > -45
let encrypted_comp = &encrypted_a.gt(&encrypted_b);
let clear_res = encrypted_comp.decrypt(&client_key);
assert_eq!(clear_res, clear_a > clear_b);
// `encrypted_comp` is a FheBool, thus it encrypts a boolean value.
// This acts as a condition on which the
// `select` function can be applied on.
// Clear equivalent computations:
// if 32 > -45 {result = 32} else {result = -45}
let encrypted_res = &encrypted_comp.select(&encrypted_a, &encrypted_b);
let clear_res: i32 = encrypted_res.decrypt(&client_key);
assert_eq!(clear_res, clear_a);
Ok(())
// `select` function can be applied on.
// Clear equivalent computations:
// if 32 > -45 {result = 32} else {result = -45}
let encrypted_res = &encrypted_comp.select(&encrypted_a, &encrypted_b);
let clear_res: i32 = encrypted_res.decrypt(&client_key);
assert_eq!(clear_res, clear_a);
Ok(())
}
```

View File

@@ -59,7 +59,7 @@ edition = "2021"
Then add the following configuration to include **TFHE-rs**:
```toml
tfhe = { version = "0.11.0", features = ["integer"] }
tfhe = { version = "0.11.2", features = ["integer"] }
```
Your updated `Cargo.toml` file should look like this:
@@ -71,7 +71,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
tfhe = { version = "0.11.0", features = ["integer"] }
tfhe = { version = "0.11.2", features = ["integer"] }
```
If you are on a different platform please refer to the [installation documentation](installation.md) for configuration options of other supported platforms.

View File

@@ -40,7 +40,7 @@ $$b = (\sum_{i = 0}^{n-1}{a_i * s_i}) + plaintext$$
Now that the encryption scheme is defined, let's review the example of the addition between ciphertexts to illustrate why it is slower to compute over encrypted data.
To add two ciphertexts, we must add their $mask$ and $body$:
To add two ciphertexts, we must add their $$mask$$ and $$body$$:
$$
ct_0 = (a_{0}, ..., a_{n-1}, b) \\ ct_1 = (a_{0}^{\prime}, ..., a_{n-1}^{\prime}, b^{\prime}) \\ ct_{2} = ct_0 + ct_1 \\ ct_{2} = (a_{0} + a_{0}^{\prime}, ..., a_{n-1} + a_{n-1}^{\prime}, b + b^{\prime})\\ b + b^{\prime} = (\sum_{i = 0}^{n-1}{a_i * s_i}) + plaintext + (\sum_{i = 0}^{n-1}{a_i^{\prime} * s_i}) + plaintext^{\prime}\\ b + b^{\prime} = (\sum_{i = 0}^{n-1}{(a_i + a_i^{\prime})* s_i}) + \Delta m + \Delta m^{\prime} + e + e^{\prime}\\
@@ -63,7 +63,9 @@ The following sections explain the concept of noise and padding in ciphertexts.
To ensure security, LWE requires random noise to be added to the message during encryption.
TFHE scheme draws this random noise from a Centered Normal Distribution with a standard deviation parameter. The choice of standard deviation impacts the security level: increasing the standard deviation enhances security while keeping other factors constant.
TFHE scheme draws this random noise either from:
- A Centered Normal Distribution with a standard deviation parameter. The choice of standard deviation impacts the security level: increasing the standard deviation enhances security while keeping other factors constant.
- A Tweaked Uniform (TUniform) Distribution with a bound parameter $$2^b$$ defined as follows: any value in the interval $$(2^b, ... , 2^b)$$ is selected with probability $$1/2^{b+1}$$, with the two end points $$2^b$$ and $$2^b$$ being selected with probability $$1/2^{b+2}$$. The main advantage of this distribution is to be bounded, whereas the usual Central Normal Distribution one is not. In some practical cases, this can simplify the use of homomorphic computation. The choice of the bound impacts the security level: increasing the bound enhances security while keeping other factors constant.
**TFHE-rs** encodes the noise in the least significant bits of each plaintext. Each leveled computation increases the value of the noise. If too many computations are performed, the noise will eventually overflow into the message bits and lead to an incorrect result.
@@ -93,7 +95,7 @@ For example, when adding two ciphertexts, the sum could exceed the range of eith
By default, the cryptographic parameters provided by **TFHE-rs** ensure at least 128 bits of security. The security has been evaluated using the latest versions of the Lattice Estimator ([repository](https://github.com/malb/lattice-estimator)) with `red_cost_model = reduction.RC.BDGL16`.
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^{-40}$. 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].
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)

View File

@@ -19,7 +19,7 @@ The following example shows a complete workflow of working with encrypted arrays
# Cargo.toml
[dependencies]
tfhe = { version = "0.11.0", features = ["integer"] }
tfhe = { version = "0.11.2", features = ["integer"] }
```
```rust

View File

@@ -16,7 +16,7 @@ You can load serialized data with the `unversionize` function, even in newer ver
[dependencies]
# ...
tfhe = { version = "0.11.0", features = ["integer"] }
tfhe = { version = "0.11.2", features = ["integer"] }
tfhe-versionable = "0.4.0"
bincode = "1.3.3"
```

View File

@@ -35,8 +35,8 @@ let (result, overflowed) = (&a).overflowing_add(&b);
let result: u16 = result.decrypt(&client_key);
assert_eq!(result, u16::MAX.wrapping_add(1u16));
assert_eq!(
overflowed.decrypt(&client_key),
u16::MAX.overflowing_add(1u16).1
overflowed.decrypt(&client_key),
u16::MAX.overflowing_add(1u16).1
);
assert!(overflowed.decrypt(&client_key));
```

View File

@@ -19,7 +19,7 @@ To use the **TFHE-rs** GPU backend in your project, add the following dependency
```toml
tfhe = { version = "0.11.0", features = ["boolean", "shortint", "integer", "gpu"] }
tfhe = { version = "0.11.2", features = ["boolean", "shortint", "integer", "gpu"] }
```
{% hint style="success" %}
@@ -164,13 +164,7 @@ All operations follow the same syntax than the one described in [here](../gettin
## Multi-GPU support
TFHE-rs supports platforms with multiple GPUs with some restrictions at the moment:
the platform should have NVLink support, and only GPUs that have peer access to GPU 0 via NVLink
will be used for the computation.
Depending on the platform, this can restrict the number of GPUs used to perform the computation.
There is **nothing to change in the code to execute on multiple GPUs**, when
they are available and have peer access to GPU 0 via NVLink. To keep the API as user-friendly as possible, the configuration is automatically set, i.e., the user has no fine-grained control over the number of GPUs to be used.
TFHE-rs supports platforms with multiple GPUs. There is **nothing to change in the code to execute on such platforms**. To keep the API as user-friendly as possible, the configuration is automatically set, i.e., the user has no fine-grained control over the number of GPUs to be used.
## Benchmark
Please refer to the [GPU benchmarks](../getting_started/benchmarks/gpu_benchmarks.md) for detailed performance benchmark results.

View File

@@ -0,0 +1,97 @@
# Strings
This document explains the FheAsciiString type for handling encrypted strings in TFHE-rs.
TFHE-rs has supports for ASCII strings with the type FheAsciiString.
You can enable this feature using the flag: --features=strings
{% hint style="warning" %}
Strings are not yet compatible with `CompactCiphertextList` and `CompressedCiphertextList`
{% endhint %}
## Supported Operations
A variety of common operations are supported for `FheAsciiString`. These include:
- **Comparisons**: `eq`, `ne`, `lt`, `le`, `gt`, `ge`, `eq_ignore_case`
- **Case conversion**: `to_lowercase` / `to_uppercase`
- **String checks**: `starts_with` / `ends_with` / `contains`
- **Trimming**: `trim_start` / `trim_end` / `trim`
- **Prefix/suffix operations**: `strip_prefix` / `strip_suffix`
- **Search**: `find` / `rfind`
When encrypting strings, you can add padding to hide the actual length of strings.
The null character (b'\0') is used as the padding.
Here is an example:
```toml
# Cargo.toml
[dependencies]
tfhe = { version = "0.11.2", features = ["integer", "strings"] }
```
```rust
use tfhe::{ConfigBuilder, generate_keys, set_server_key, FheAsciiString, FheStringLen, ClearString};
use tfhe::prelude::*;
use tfhe::safe_serialization::safe_serialize;
fn main() {
let config = ConfigBuilder::default().build();
let (cks, sks) = generate_keys(config);
set_server_key(sks);
let r = FheAsciiString::try_encrypt("café is french for coffee", &cks);
// As the input string is not strictly ASCII, it is not compatible
assert!(r.is_err());
let string = FheAsciiString::try_encrypt("tfhe-rs", &cks).unwrap();
// This adds 3 chars of padding to the chars of the input string
let padded_string = FheAsciiString::try_encrypt_with_padding("tfhe-rs", 3, &cks).unwrap();
// This makes it so the string has 10 chars (adds padding or truncates input as necessary)
let other_string = FheAsciiString::try_encrypt_with_fixed_sized("tfhe", 10, &cks).unwrap();
let mut buffer1 = vec![];
safe_serialize(&padded_string, &mut buffer1, 1 << 30).unwrap();
let mut buffer2 = vec![];
safe_serialize(&other_string, &mut buffer2, 1 << 30).unwrap();
// The two strings created with padding, have the same
// memory/disk footprint, even though the lengths are not the same
assert_eq!(buffer1.len(), buffer2.len());
// When a string has no padding, its length is known in clear
let len = string.len();
assert!(matches!(len, FheStringLen::NoPadding(7)));
// When a string has padding, its length is only known as an encrypted value
let FheStringLen::Padding(encrypted_len) = padded_string.len() else {
panic!("Expected len to be encrypted");
};
let padded_string_len: u16 = encrypted_len.decrypt(&cks);
assert_eq!(padded_string_len, 7); // Note padding chars are not counted
// The enum resulting of a len() / is_empty() call can be transformed
// to a FheUint16 using `into_ciphertext`
assert!(string.len().into_ciphertext().is_trivial());
assert!(!padded_string.len().into_ciphertext().is_trivial());
let other_string_len: u16 = other_string.len().into_ciphertext().decrypt(&cks);
assert_eq!(other_string_len, 4);
// Padded and un-padded strings are equal if the content is
assert!(padded_string.eq(&string).decrypt(&cks));
let prefix = ClearString::new("tfhe".to_string());
let (stripped_string, has_been_stripped) = string.strip_prefix(&prefix);
// Notice that stripping, makes the string as being considered as padded
// as it is not possible to homomorphically remove chars
let FheStringLen::Padding(encrypted_len) = stripped_string.len() else {
panic!("Expected len to be encrypted");
};
let stripped_string_len: u16 = encrypted_len.decrypt(&cks);
assert_eq!(stripped_string_len, 3);
let decrypted = stripped_string.decrypt(&cks);
assert_eq!(decrypted, "-rs");
assert!(has_been_stripped.decrypt(&cks));
}
```

View File

@@ -8,10 +8,14 @@ This document explains how to implement the zero-knowledge proofs function for c
You can enable this feature using the flag: `--features=zk-pok` when building **TFHE-rs**.
{% endhint %}
Using this feature is straightforward: during encryption, the client generates the proof, and the server validates it before conducting any homomorphic computations. The following example demonstrates how a client can encrypt and prove a ciphertext, and how a server can verify the ciphertext and compute it:
To use this feature, you must first generate a **CRS** (Common Reference String). The CRS is a piece of cryptographic data that is necessary to ensure the security of zero-knowledge proofs. The CRS should be generated in advance and shared between all the clients and the server. A CRS can be reused for multiple encryptions with the same parameters.
Once the CRS is generated, using zero-knowledge proofs is straightforward: during encryption, the client generates the proof, and the server validates it before performing any homomorphic computations.
Note that you need to use dedicated parameters for the compact public key encryption. This helps to reduce the size of encrypted data and speed up the zero-knowledge proof computation.
The following example shows how a client can encrypt and prove a ciphertext, and how a server can verify and compute the ciphertext:
```rust
use rand::prelude::*;
use tfhe::prelude::*;
@@ -28,12 +32,13 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
let casting_params = tfhe::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
// Enable the dedicated parameters on the config
let config = tfhe::ConfigBuilder::with_custom_parameters(params)
.use_dedicated_compact_public_key_parameters((cpk_params, casting_params));
.use_dedicated_compact_public_key_parameters((cpk_params, casting_params)).build();
// The CRS should be generated in an offline phase then shared to all clients and the server
let crs = CompactPkeCrs::from_config(config, 64).unwrap();
// Then use TFHE-rs as usual
let client_key = tfhe::ClientKey::generate(config.clone());
// This is done in an offline phase and the CRS is shared to all clients and the server
let crs = CompactPkeCrs::from_config(config.into(), 64).unwrap();
let client_key = tfhe::ClientKey::generate(config);
let server_key = tfhe::ServerKey::new(&client_key);
let public_key = tfhe::CompactPublicKey::try_new(&client_key).unwrap();
// This can be left empty, but if provided allows to tie the proof to arbitrary data
@@ -79,5 +84,71 @@ and by building the code for the native CPU architecture and in release mode, e.
You can choose a more costly proof with `ZkComputeLoad::Proof`, which has a faster verification time. Alternatively, you can select `ZkComputeLoad::Verify` for a faster proof and slower verification.
{% endhint %}
## Scheme version
The ZK scheme used to generate and verify proofs is available in two versions:
- ZKV1: This version is close to the original paper from [Libert](https://eprint.iacr.org/2023/800).
- ZKV2: Differing from the paper, this version provides better performance for provers and verifiers.
**TFHE-rs** selects automatically the scheme to use based on the encryption parameters during the CRS generation. With default parameters, ZKV2 is selected.
The following example shows how to generate a CRS and proofs for ZKV1. Compared to the previous example, only the parameters are changed:
```rust
use rand::prelude::*;
use tfhe::prelude::*;
use tfhe::set_server_key;
use tfhe::zk::{CompactPkeCrs, ZkComputeLoad};
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut rng = thread_rng();
let params = tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
// Indicate which parameters to use for the Compact Public Key encryption
let cpk_params = tfhe::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_ZKV1;
// And parameters allowing to keyswitch/cast to the computation parameters.
let casting_params = tfhe::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_ZKV1;
// Enable the dedicated parameters on the config
let config = tfhe::ConfigBuilder::with_custom_parameters(params)
.use_dedicated_compact_public_key_parameters((cpk_params, casting_params)).build();
// The CRS should be generated in an offline phase then shared to all clients and the server
let crs = CompactPkeCrs::from_config(config, 64).unwrap();
// Then use TFHE-rs as usual
let client_key = tfhe::ClientKey::generate(config);
let server_key = tfhe::ServerKey::new(&client_key);
let public_key = tfhe::CompactPublicKey::try_new(&client_key).unwrap();
// This can be left empty, but if provided allows to tie the proof to arbitrary data
let metadata = [b'T', b'F', b'H', b'E', b'-', b'r', b's'];
let clear_a = rng.gen::<u64>();
let clear_b = rng.gen::<u64>();
let proven_compact_list = tfhe::ProvenCompactCiphertextList::builder(&public_key)
.push(clear_a)
.push(clear_b)
.build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Verify)?;
// Server side
let result = {
set_server_key(server_key);
// Verify the ciphertexts
let expander =
proven_compact_list.verify_and_expand(&crs, &public_key, &metadata)?;
let a: tfhe::FheUint64 = expander.get(0)?.unwrap();
let b: tfhe::FheUint64 = expander.get(1)?.unwrap();
a + b
};
// Back on the client side
let a_plus_b: u64 = result.decrypt(&client_key);
assert_eq!(a_plus_b, clear_a.wrapping_add(clear_b));
Ok(())
}
```
## Benchmark
Please refer to the [Zero-knowledge proof benchmarks](../getting_started/benchmarks/zk_proof_benchmarks.md) for detailed performance benchmark results.

View File

@@ -9,7 +9,7 @@ Welcome to this tutorial about `TFHE-rs` `core_crypto` module.
To use `TFHE-rs`, it first has to be added as a dependency in the `Cargo.toml`:
```toml
tfhe = { version = "0.11.0" }
tfhe = { version = "0.11.2" }
```
### Commented code to double a 2-bit message in a leveled fashion and using a PBS with the `core_crypto` module.

View File

@@ -8,13 +8,13 @@ Some cryptographic parameters will require tuning to ensure both the correctness
To make it simpler, **we've provided two sets of parameters**, which ensure correct computations for a certain probability with the standard security of 128 bits. There exists an error probability due to the probabilistic nature of the encryption, which requires adding randomness (noise) following a Gaussian distribution. If this noise is too large, the decryption will not give a correct result. There is a trade-off between efficiency and correctness: generally, using a less efficient parameter set (in terms of computation time) leads to a smaller risk of having an error during homomorphic evaluation.
In the two proposed sets of parameters, the only difference lies in this error probability. The default parameter set ensures an error probability of at most $$2^{-40}$$ when computing a programmable bootstrapping (i.e., any gates but the `not`). The other one is closer to the error probability claimed in the original [TFHE paper](https://eprint.iacr.org/2018/421), namely $$2^{-165}$$, but it is up-to-date regarding security requirements.
In the two proposed sets of parameters, the only difference lies in this error probability. The default parameter set ensures an error probability of at most $$2^{-64}$$ when computing a programmable bootstrapping (i.e., any gates but the `not`). The other one is closer to the error probability claimed in the original [TFHE paper](https://eprint.iacr.org/2018/421), namely $$2^{-165}$$, but it is up-to-date regarding security requirements.
The following array summarizes this:
| Parameter set | Error probability |
| :-------------------: | :---------------: |
| DEFAULT\_PARAMETERS | $$2^{-40}$$ |
| DEFAULT\_PARAMETERS | $$2^{-64}$$ |
| TFHE\_LIB\_PARAMETERS | $$2^{-165}$$ |
## User-defined parameters

View File

@@ -1,6 +1,6 @@
# Cryptographic Parameters
All parameter sets provide at least 128-bits of security according to the [Lattice-Estimator](https://github.com/malb/lattice-estimator), with an error probability equal to $$2^{-40}$$ when using programmable bootstrapping. This error probability is due to the randomness added at each encryption (see [here](../../../getting\_started/security\_and\_cryptography.md) for more details about the encryption process).
All parameter sets provide at least 128-bits of security according to the [Lattice-Estimator](https://github.com/malb/lattice-estimator), with an error probability equal to $$2^{-64}$$ when using programmable bootstrapping. This error probability is due to the randomness added at each encryption (see [here](../../../getting\_started/security\_and\_cryptography.md) for more details about the encryption process).
## Parameters and message precision
@@ -57,26 +57,22 @@ use tfhe::shortint::parameters::DynamicDistribution;
fn main() {
// WARNING: might be insecure and/or incorrect
// You can create your own set of parameters
let param = ClassicPBSParameters::new(
LweDimension(656),
GlweDimension(2),
PolynomialSize(512),
DynamicDistribution::new_gaussian_from_std_dev(
StandardDev(0.000034119201269311964),
),
DynamicDistribution::new_gaussian_from_std_dev(
StandardDev(0.00000004053919869756513),
),
DecompositionBaseLog(8),
DecompositionLevelCount(2),
DecompositionBaseLog(3),
DecompositionLevelCount(4),
MessageModulus(4),
CarryModulus(1),
MaxNoiseLevel::new(2),
2.0f64.powi(-40),
CiphertextModulus::new_native(),
EncryptionKeyChoice::Big,
);
let param = ClassicPBSParameters {
lwe_dimension: LweDimension(879),
glwe_dimension: GlweDimension(1),
polynomial_size: PolynomialSize(2048),
lwe_noise_distribution: DynamicDistribution::new_t_uniform(46),
glwe_noise_distribution: DynamicDistribution::new_t_uniform(17),
pbs_base_log: DecompositionBaseLog(23),
pbs_level: DecompositionLevelCount(1),
ks_base_log: DecompositionBaseLog(3),
ks_level: DecompositionLevelCount(5),
message_modulus: MessageModulus(4),
carry_modulus: CarryModulus(4),
max_noise_level: MaxNoiseLevel::new(5),
log2_p_fail: -71.625,
ciphertext_modulus: CiphertextModulus::new_native(),
encryption_key_choice: EncryptionKeyChoice::Big,
};
}
```

View File

@@ -1,8 +1,13 @@
# Homomorphic case changing on Ascii string
This tutorial demonstrates how to build a data type that represents an ASCII string in Fully Homomorphic Encryption (FHE) by implementing to\_lower and to\_upper functions.
This tutorial demonstrates how to build your own data type that represents an ASCII string in Fully Homomorphic Encryption (FHE) by implementing to\_lower and to\_upper functions.
An ASCII character is stored in 7 bits. To store an encrypted ASCII, we use the `FheUint8`:
{% hint style="info" %}
Since version 0.11, **TFHE-rs** has introduced the `strings` feature, which provides an easy to use FHE strings API. See the [fhe strings guide](../guides/strings.md) for more information.
{% endhint %}
An ASCII character is stored in 7 bits. In this tutorial, we use the `FheUint8` to store an encrypted ASCII:
* The uppercase letters are in the range \[65, 90]
* The lowercase letters are in the range \[97, 122]
@@ -24,13 +29,10 @@ To use the `FheUint8` type, enable the `integer` feature:
# Cargo.toml
[dependencies]
# Default configuration for x86 Unix machines:
tfhe = { version = "0.11.0", features = ["integer"] }
tfhe = { version = "0.11.2", features = ["integer"] }
```
Refer to the [installation guide](../getting\_started/installation.md) for other configurations.
The `FheAsciiString::encrypt` function performs data validation to ensure the input string contains only ASCII characters.
The `MyFheString::encrypt` function performs data validation to ensure the input string contains only ASCII characters.
In FHE operations, direct branching on encrypted values is not possible. However, you can evaluate a boolean condition to obtain the desired outcome. Here is an example to check and convert the 'char' to a lowercase without using a branch:
@@ -83,7 +85,7 @@ use tfhe::{generate_keys, set_server_key, ClientKey, ConfigBuilder, FheUint8};
const UP_LOW_DISTANCE: u8 = 32;
struct FheAsciiString {
struct MyFheString {
bytes: Vec<FheUint8>,
}
@@ -95,7 +97,7 @@ fn to_lower(c: &FheUint8) -> FheUint8 {
c + FheUint8::cast_from(c.gt(64) & c.lt(91)) * UP_LOW_DISTANCE
}
impl FheAsciiString {
impl MyFheString {
fn encrypt(string: &str, client_key: &ClientKey) -> Self {
assert!(
string.is_ascii(),
@@ -140,7 +142,7 @@ fn main() {
set_server_key(server_key);
let my_string = FheAsciiString::encrypt("Hello Zama, how is it going?", &client_key);
let my_string = MyFheString::encrypt("Hello Zama, how is it going?", &client_key);
let verif_string = my_string.decrypt(&client_key);
println!("Start string: {verif_string}");
@@ -155,3 +157,45 @@ fn main() {
assert_eq!(verif_string, "hello zama, how is it going?");
}
```
## Using **TFHE-rs** strings feature
This code can be greatly simplified by using the `strings` feature from **TFHE-rs**.
First, add the feature in your `Cargo.toml`
```toml
# Cargo.toml
[dependencies]
tfhe = { version = "0.11.2", features = ["strings"] }
```
The `FheAsciiString` type allows to simply do homomorphic case changing of encrypted strings (and much more!):
```rust
use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheAsciiString};
fn main() {
let config = ConfigBuilder::default().build();
let (client_key, server_key) = generate_keys(config);
set_server_key(server_key);
let my_string =
FheAsciiString::try_encrypt("Hello Zama, how is it going?", &client_key).unwrap();
let verif_string = my_string.decrypt(&client_key);
println!("Start string: {verif_string}");
let my_string_upper = my_string.to_uppercase();
let verif_string = my_string_upper.decrypt(&client_key);
println!("Upper string: {verif_string}");
assert_eq!(verif_string, "HELLO ZAMA, HOW IS IT GOING?");
let my_string_lower = my_string_upper.to_lowercase();
let verif_string = my_string_lower.decrypt(&client_key);
println!("Lower string: {verif_string}");
assert_eq!(verif_string, "hello zama, how is it going?");
}
```
You can read more about this in the [FHE strings documentation](../guides/strings.md)

View File

@@ -17,7 +17,7 @@ This function returns a Boolean (`true` or `false`) so that the total count of `
```toml
# Cargo.toml
tfhe = { version = "0.11.0", features = ["integer"] }
tfhe = { version = "0.11.2", features = ["integer"] }
```
First, define the verification function.

View File

@@ -36,16 +36,16 @@ pub const NOISE_TEST_PARAMS_4_BITS_NATIVE_U64_132_BITS_GAUSSIAN: ClassicTestPara
#[allow(dead_code)]
pub const NOISE_TEST_PARAMS_MULTI_BIT_GROUP_3_2_BITS_NATIVE_U64_132_BITS_GAUSSIAN:
MultiBitTestParams<u64> = MultiBitTestParams {
input_lwe_dimension: LweDimension(256 * 3),
input_lwe_dimension: LweDimension(759),
lwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
1.1098369627275701e-05,
1.296274149494132e-05,
)),
decomp_base_log: DecompositionBaseLog(17),
decomp_base_log: DecompositionBaseLog(22),
decomp_level_count: DecompositionLevelCount(1),
glwe_dimension: GlweDimension(3),
polynomial_size: PolynomialSize(512),
glwe_dimension: GlweDimension(2),
polynomial_size: PolynomialSize(1024),
glwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
1.9524392655548086e-11,
2.845267479601915e-15,
)),
message_modulus_log: MessageModulusLog(2),
ciphertext_modulus: CiphertextModulus::new_native(),
@@ -56,9 +56,9 @@ pub const NOISE_TEST_PARAMS_MULTI_BIT_GROUP_3_2_BITS_NATIVE_U64_132_BITS_GAUSSIA
#[allow(clippy::excessive_precision)]
pub const NOISE_TEST_PARAMS_MULTI_BIT_GROUP_3_4_BITS_NATIVE_U64_132_BITS_GAUSSIAN:
MultiBitTestParams<u64> = MultiBitTestParams {
input_lwe_dimension: LweDimension(279 * 3),
input_lwe_dimension: LweDimension(912),
lwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
3.3747142481837397e-06,
9.252442079345288e-07,
)),
decomp_base_log: DecompositionBaseLog(22),
decomp_level_count: DecompositionLevelCount(1),
@@ -77,7 +77,7 @@ pub const NOISE_TEST_PARAMS_MULTI_BIT_GROUP_3_4_BITS_NATIVE_U64_132_BITS_GAUSSIA
#[allow(dead_code)]
pub const NOISE_TEST_PARAMS_MULTI_BIT_GROUP_3_6_BITS_NATIVE_U64_132_BITS_GAUSSIAN:
MultiBitTestParams<u64> = MultiBitTestParams {
input_lwe_dimension: LweDimension(326 * 3),
input_lwe_dimension: LweDimension(978),
lwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
2.962875621642539e-07,
)),

View File

@@ -1,6 +1,7 @@
use super::ClientKey;
use crate::conformance::ParameterSetConformant;
use crate::integer::backward_compatibility::list_compression::*;
use crate::named::Named;
use serde::{Deserialize, Serialize};
use tfhe_versionable::Versionize;
@@ -34,6 +35,26 @@ pub struct CompressedDecompressionKey {
pub(crate) key: crate::shortint::list_compression::CompressedDecompressionKey,
}
impl Named for CompressionPrivateKeys {
const NAME: &'static str = "high_level_api::CompressionPrivateKeys";
}
impl Named for CompressionKey {
const NAME: &'static str = "high_level_api::CompressionKey";
}
impl Named for DecompressionKey {
const NAME: &'static str = "high_level_api::DecompressionKey";
}
impl Named for CompressedCompressionKey {
const NAME: &'static str = "high_level_api::CompressedCompressionKey";
}
impl Named for CompressedDecompressionKey {
const NAME: &'static str = "high_level_api::CompressedDecompressionKey";
}
impl CompressedCompressionKey {
pub fn decompress(&self) -> CompressionKey {
CompressionKey {

View File

@@ -120,7 +120,8 @@ mod js_on_wasm_api;
feature = "shortint",
feature = "boolean",
feature = "integer",
feature = "zk-pok"
feature = "zk-pok",
feature = "strings"
))]
mod test_user_docs;

View File

@@ -1,3 +1,7 @@
pub trait Named {
/// Default name for the type
const NAME: &'static str;
/// Aliases that should also be accepted for backward compatibility when checking the name of
/// values of this type
const BACKWARD_COMPATIBILITY_ALIASES: &'static [&'static str] = &[];
}

View File

@@ -122,7 +122,11 @@ Please use the versioned serialization mode for backward compatibility.",
}
}
if self.name != T::NAME {
if self.name != T::NAME
&& T::BACKWARD_COMPATIBILITY_ALIASES
.iter()
.all(|alias| self.name != *alias)
{
return Err(format!(
"On deserialization, expected type {}, got type {}",
T::NAME,
@@ -494,13 +498,17 @@ pub fn safe_deserialize_conformant<
#[cfg(all(test, feature = "shortint"))]
mod test_shortint {
use crate::safe_serialization::{DeserializationConfig, SerializationConfig};
use tfhe_versionable::Versionize;
use crate::named::Named;
use crate::shortint::parameters::{
PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
V0_11_PARAM_MESSAGE_3_CARRY_3_KS_PBS_GAUSSIAN_2M64,
};
use crate::shortint::{gen_keys, Ciphertext};
use super::*;
#[test]
fn safe_deserialization_ct_unversioned() {
let (ck, _sk) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64);
@@ -626,6 +634,46 @@ mod test_shortint {
let dec = ck.decrypt(&ct2);
assert_eq!(msg, dec);
}
#[test]
fn safe_deserialization_named() {
#[derive(Serialize, Deserialize, Versionize)]
#[repr(transparent)]
struct Foo(u64);
impl Named for Foo {
const NAME: &'static str = "Foo";
}
#[derive(Deserialize, Versionize)]
#[repr(transparent)]
struct Bar(u64);
impl Named for Bar {
const NAME: &'static str = "Bar";
const BACKWARD_COMPATIBILITY_ALIASES: &'static [&'static str] = &["Foo"];
}
#[derive(Deserialize, Versionize)]
#[repr(transparent)]
struct Baz(u64);
impl Named for Baz {
const NAME: &'static str = "Baz";
}
let foo = Foo(3);
let mut foo_ser = Vec::new();
safe_serialize(&foo, &mut foo_ser, 0x1000).unwrap();
let foo_deser: Foo = safe_deserialize(foo_ser.as_slice(), 0x1000).unwrap();
let bar_deser: Bar = safe_deserialize(foo_ser.as_slice(), 0x1000).unwrap();
assert_eq!(foo_deser.0, bar_deser.0);
assert!(safe_deserialize::<Baz>(foo_ser.as_slice(), 0x1000).is_err());
}
}
#[cfg(all(test, feature = "integer"))]

View File

@@ -57,6 +57,7 @@ mod test_cpu_doc {
doctest!("../docs/guides/pbs-stats.md", guides_pbs_stats);
doctest!("../docs/guides/public_key.md", guides_public_key);
doctest!("../docs/guides/rayon_crate.md", guides_rayon_crate);
doctest!("../docs/guides/strings.md", guides_strings);
doctest!("../docs/guides/trait_bounds.md", guides_trait_bounds);
doctest!(
"../docs/guides/trivial_ciphertext.md",

View File

@@ -196,6 +196,8 @@ pub enum CompactPkeCrs {
impl Named for CompactPkeCrs {
const NAME: &'static str = "zk::CompactPkeCrs";
const BACKWARD_COMPATIBILITY_ALIASES: &'static [&'static str] = &["zk::CompactPkePublicParams"];
}
impl From<ZkCompactPkeV1PublicParams> for CompactPkeCrs {