Compare commits

...

17 Commits

Author SHA1 Message Date
Arthur Meyre
cdc25d6c60 chore(core): add more sanity checks on RNG 2023-04-21 13:16:18 +02:00
Arthur Meyre
ab59514b0d fix(core): fix rng 2023-04-21 13:16:18 +02:00
tmontaigu
7c2fa7529c feat(boolean): add BooleanEngine::replace_thread_local
This new associated function allows to replace
the engine used in the thread.
2023-04-20 17:22:37 +02:00
Arthur Meyre
945ce4617f chore(tfhe): bump version to 0.2.2 2023-04-20 09:21:53 +02:00
Arthur Meyre
ff84e70ca9 chore(core): disable split pbs128 2023-04-20 09:21:53 +02:00
tmontaigu
956c4080f5 feat(hlapi): add trivial encryptions 2023-04-19 11:12:45 +02:00
tmontaigu
fea1b4db92 feat(integer): add trivial encryption 2023-04-19 11:12:45 +02:00
Arthur Meyre
13c1fcb6e7 chore(tfhe): bump version to 0.2.1 2023-04-19 10:18:51 +02:00
tmontaigu
69b9bd3860 fix(hlapi): use correct number of blocks for FheUint32
The FheUint32 was wrongly defined as being 32 blocks of 2 bits
when it should have been 16 blocks.
2023-04-19 10:18:30 +02:00
Arthur Meyre
747693e889 chore(doc): updated benchmarks for min to reflect the fix done to min/max 2023-04-19 10:16:44 +02:00
Arthur Meyre
e452d5d6d2 chore(bench): only run avx512 benches 2023-04-18 16:37:38 +02:00
Arthur Meyre
5425ba5199 fix(integer): fix mul correctness
- update benches accordingly
2023-04-18 16:37:38 +02:00
dependabot[bot]
aeda381f12 chore(deps): bump actions/checkout from 3.5.0 to 3.5.2
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.0 to 3.5.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](8f4b7f8486...8e5e7e5ab8)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-18 10:46:04 +02:00
Arthur Meyre
9b2cccfee6 chore(integer): restore empty carry check for default comparator tests
- only extract assign message instead of doing a full propagate as carries
are not supposed to be non zero (though the degree will have grown)
2023-04-18 10:35:20 +02:00
J-B Orfila
b534f6a406 chore(doc): fix typo 2023-04-14 15:28:29 +02:00
J-B Orfila
7a72dd2619 chore(doc): fix TOML 2023-04-14 14:50:50 +02:00
J-B Orfila
343f31e070 chore(doc): fix dead links 2023-04-13 17:56:01 +02:00
40 changed files with 782 additions and 173 deletions

View File

@@ -44,7 +44,7 @@ jobs:
echo "Type: ${{ github.event.inputs.instance_type }}"
echo "Request ID: ${{ github.event.inputs.request_id }}"
- uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
- name: Set up home
run: |

View File

@@ -44,7 +44,7 @@ jobs:
echo "Type: ${{ github.event.inputs.instance_type }}"
echo "Request ID: ${{ github.event.inputs.request_id }}"
- uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
- name: Set up home
run: |

View File

@@ -5,19 +5,19 @@ on:
workflow_dispatch:
inputs:
instance_id:
description: 'Instance ID'
description: "Instance ID"
type: string
instance_image_id:
description: 'Instance AMI ID'
description: "Instance AMI ID"
type: string
instance_type:
description: 'Instance product type'
description: "Instance product type"
type: string
runner_name:
description: 'Action runner name'
description: "Action runner name"
type: string
request_id:
description: 'Slab request ID'
description: "Slab request ID"
type: string
env:
@@ -42,7 +42,7 @@ jobs:
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
- name: Checkout tfhe-rs repo with tags
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
with:
fetch-depth: 0
@@ -57,9 +57,9 @@ jobs:
toolchain: nightly
override: true
- name: Run benchmarks
- name: Run benchmarks with AVX512
run: |
make bench_boolean
make AVX512_SUPPORT=ON bench_boolean
- name: Parse results
run: |
@@ -73,24 +73,8 @@ jobs:
--commit-date "${COMMIT_DATE}" \
--bench-date "${{ env.BENCH_DATE }}" \
--walk-subdirs \
--throughput
- name: Remove previous raw results
run: |
rm -rf target/criterion
- name: Run benchmarks with AVX512
run: |
make AVX512_SUPPORT=ON bench_boolean
- name: Parse AVX512 results
run: |
python3 ./ci/benchmark_parser.py target/criterion ${{ env.RESULTS_FILENAME }} \
--hardware ${{ inputs.instance_type }} \
--name-suffix avx512 \
--walk-subdirs \
--throughput \
--append-results
--throughput
- name: Measure key sizes
run: |
@@ -101,7 +85,7 @@ jobs:
python3 ./ci/benchmark_parser.py tfhe/boolean_key_sizes.csv ${{ env.RESULTS_FILENAME }} \
--key-sizes \
--append-results
- name: Upload parsed results artifact
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
with:
@@ -109,7 +93,7 @@ jobs:
path: ${{ env.RESULTS_FILENAME }}
- name: Checkout Slab repo
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
with:
repository: zama-ai/slab
path: slab
@@ -118,7 +102,7 @@ jobs:
- name: Send data to Slab
shell: bash
env:
COMPRESSED_RESULTS : ${{ env.RESULTS_FILENAME }}.gz
COMPRESSED_RESULTS: ${{ env.RESULTS_FILENAME }}.gz
run: |
echo "Computing HMac on results file"
SIGNATURE="$(slab/scripts/hmac_calculator.sh ${{ env.RESULTS_FILENAME }} '${{ secrets.JOB_SECRET }}')"

View File

@@ -21,7 +21,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
- name: Run pcc checks
run: |

View File

@@ -5,19 +5,19 @@ on:
workflow_dispatch:
inputs:
instance_id:
description: 'Instance ID'
description: "Instance ID"
type: string
instance_image_id:
description: 'Instance AMI ID'
description: "Instance AMI ID"
type: string
instance_type:
description: 'Instance product type'
description: "Instance product type"
type: string
runner_name:
description: 'Action runner name'
description: "Action runner name"
type: string
request_id:
description: 'Slab request ID'
description: "Slab request ID"
type: string
env:
@@ -42,7 +42,7 @@ jobs:
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
- name: Checkout tfhe-rs repo with tags
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
with:
fetch-depth: 0
@@ -57,9 +57,9 @@ jobs:
toolchain: nightly
override: true
- name: Run benchmarks
- name: Run benchmarks with AVX512
run: |
make bench_integer
make AVX512_SUPPORT=ON bench_integer
- name: Parse results
run: |
@@ -73,24 +73,8 @@ jobs:
--commit-date "${COMMIT_DATE}" \
--bench-date "${{ env.BENCH_DATE }}" \
--walk-subdirs \
--throughput
- name: Remove previous raw results
run: |
rm -rf target/criterion
- name: Run benchmarks with AVX512
run: |
make AVX512_SUPPORT=ON bench_integer
- name: Parse AVX512 results
run: |
python3 ./ci/benchmark_parser.py target/criterion ${{ env.RESULTS_FILENAME }} \
--hardware ${{ inputs.instance_type }} \
--walk-subdirs \
--name-suffix avx512 \
--throughput \
--append-results
--throughput
- name: Upload parsed results artifact
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
@@ -99,7 +83,7 @@ jobs:
path: ${{ env.RESULTS_FILENAME }}
- name: Checkout Slab repo
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
with:
repository: zama-ai/slab
path: slab

View File

@@ -20,7 +20,7 @@ jobs:
runs-on: ["self-hosted", "m1mac"]
steps:
- uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
- name: Install latest stable
uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af

View File

@@ -5,22 +5,21 @@ on:
workflow_dispatch:
inputs:
instance_id:
description: 'Instance ID'
description: "Instance ID"
type: string
instance_image_id:
description: 'Instance AMI ID'
description: "Instance AMI ID"
type: string
instance_type:
description: 'Instance product type'
description: "Instance product type"
type: string
runner_name:
description: 'Action runner name'
description: "Action runner name"
type: string
request_id:
description: 'Slab request ID'
description: "Slab request ID"
type: string
env:
CARGO_TERM_COLOR: always
RESULTS_FILENAME: parsed_benchmark_results_${{ github.sha }}.json
@@ -43,7 +42,7 @@ jobs:
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
- name: Checkout tfhe-rs repo with tags
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
with:
fetch-depth: 0
@@ -84,7 +83,7 @@ jobs:
path: ${{ env.RESULTS_FILENAME }}
- name: Checkout Slab repo
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
with:
repository: zama-ai/slab
path: slab

View File

@@ -5,22 +5,21 @@ on:
workflow_dispatch:
inputs:
instance_id:
description: 'Instance ID'
description: "Instance ID"
type: string
instance_image_id:
description: 'Instance AMI ID'
description: "Instance AMI ID"
type: string
instance_type:
description: 'Instance product type'
description: "Instance product type"
type: string
runner_name:
description: 'Action runner name'
description: "Action runner name"
type: string
request_id:
description: 'Slab request ID'
description: "Slab request ID"
type: string
env:
CARGO_TERM_COLOR: always
RESULTS_FILENAME: parsed_benchmark_results_${{ github.sha }}.json
@@ -43,7 +42,7 @@ jobs:
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
- name: Checkout tfhe-rs repo with tags
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
with:
fetch-depth: 0
@@ -58,9 +57,9 @@ jobs:
toolchain: nightly
override: true
- name: Run benchmarks
- name: Run benchmarks with AVX512
run: |
make bench_shortint
make AVX512_SUPPORT=ON bench_shortint
- name: Parse results
run: |
@@ -74,24 +73,8 @@ jobs:
--commit-date "${COMMIT_DATE}" \
--bench-date "${{ env.BENCH_DATE }}" \
--walk-subdirs \
--throughput
- name: Remove previous raw results
run: |
rm -rf target/criterion
- name: Run benchmarks with AVX512
run: |
make AVX512_SUPPORT=ON bench_shortint
- name: Parse AVX512 results
run: |
python3 ./ci/benchmark_parser.py target/criterion ${{ env.RESULTS_FILENAME }} \
--hardware ${{ inputs.instance_type }} \
--walk-subdirs \
--name-suffix avx512 \
--throughput \
--append-results
--throughput
- name: Measure key sizes
run: |
@@ -110,7 +93,7 @@ jobs:
path: ${{ env.RESULTS_FILENAME }}
- name: Checkout Slab repo
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
with:
repository: zama-ai/slab
path: slab

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Slab repo
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
with:
repository: zama-ai/slab
path: slab

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
with:
fetch-depth: 0
- name: Save repo

View File

@@ -58,7 +58,7 @@ tfhe = { version = "*", features = ["boolean", "shortint", "integer", "x86_64"]
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
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,
to run in release mode with cargo's `--release` flag to have the best performances possible,
eg: `cargo run --release`.
Here is a full example evaluating a Boolean circuit:

View File

@@ -1,6 +1,6 @@
[package]
name = "tfhe"
version = "0.2.0"
version = "0.2.2"
edition = "2021"
readme = "../README.md"
keywords = ["fully", "homomorphic", "encryption", "fhe", "cryptography"]

View File

@@ -32,6 +32,34 @@ int uint128_client_key(const ClientKey *client_key) {
return ok;
}
int uint128_encrypt_trivial(const ClientKey *client_key) {
int ok;
FheUint128 *lhs = NULL;
FheUint128 *rhs = NULL;
FheUint128 *result = NULL;
ok = fhe_uint128_try_encrypt_trivial_u128(10, 20, &lhs);
assert(ok == 0);
ok = fhe_uint128_try_encrypt_trivial_u128(1, 2, &rhs);
assert(ok == 0);
ok = fhe_uint128_sub(lhs, rhs, &result);
assert(ok == 0);
uint64_t w0, w1;
ok = fhe_uint128_decrypt(result, client_key, &w0, &w1);
assert(ok == 0);
assert(w0 == 9);
assert(w1 == 18);
fhe_uint128_destroy(lhs);
fhe_uint128_destroy(rhs);
fhe_uint128_destroy(result);
return ok;
}
int uint128_public_key(const ClientKey *client_key, const PublicKey *public_key) {
int ok;
FheUint128 *lhs = NULL;
@@ -79,6 +107,7 @@ int main(void) {
set_server_key(server_key);
uint128_client_key(client_key);
uint128_encrypt_trivial(client_key);
uint128_public_key(client_key, public_key);
client_key_destroy(client_key);

View File

@@ -4,7 +4,6 @@
#include <inttypes.h>
#include <stdio.h>
int uint256_client_key(const ClientKey *client_key) {
int ok;
FheUint256 *lhs = NULL;
@@ -49,6 +48,50 @@ int uint256_client_key(const ClientKey *client_key) {
return ok;
}
int uint256_encrypt_trivial(const ClientKey *client_key) {
int ok;
FheUint256 *lhs = NULL;
FheUint256 *rhs = NULL;
FheUint256 *result = NULL;
U256 *lhs_clear = NULL;
U256 *rhs_clear = NULL;
U256 *result_clear = NULL;
ok = u256_from_u64_words(1, 2, 3, 4, &lhs_clear);
assert(ok == 0);
ok = u256_from_u64_words(5, 6, 7, 8, &rhs_clear);
assert(ok == 0);
ok = fhe_uint256_try_encrypt_trivial_u256(lhs_clear, &lhs);
assert(ok == 0);
ok = fhe_uint256_try_encrypt_trivial_u256(rhs_clear, &rhs);
assert(ok == 0);
ok = fhe_uint256_add(lhs, rhs, &result);
assert(ok == 0);
ok = fhe_uint256_decrypt(result, client_key, &result_clear);
assert(ok == 0);
uint64_t w0, w1, w2, w3;
ok = u256_to_u64_words(result_clear, &w0, &w1, &w2, &w3);
assert(ok == 0);
assert(w0 == 6);
assert(w1 == 8);
assert(w2 == 10);
assert(w3 == 12);
u256_destroy(lhs_clear);
u256_destroy(rhs_clear);
u256_destroy(result_clear);
fhe_uint256_destroy(lhs);
fhe_uint256_destroy(rhs);
fhe_uint256_destroy(result);
return ok;
}
int uint256_public_key(const ClientKey *client_key, const PublicKey *public_key) {
int ok;
FheUint256 *lhs = NULL;
@@ -112,6 +155,7 @@ int main(void) {
set_server_key(server_key);
uint256_client_key(client_key);
uint256_encrypt_trivial(client_key);
uint256_public_key(client_key, public_key);
client_key_destroy(client_key);

View File

@@ -67,6 +67,37 @@ int public_key_test(const ClientKey *client_key, const PublicKey *public_key) {
return ok;
}
int trivial_encrypt_test(const ClientKey *client_key) {
int ok;
FheBool *lhs = NULL;
FheBool *rhs = NULL;
FheBool *result = NULL;
bool lhs_clear = 0;
bool rhs_clear = 1;
ok = fhe_bool_try_encrypt_trivial_bool(lhs_clear, &lhs);
assert(ok == 0);
ok = fhe_bool_try_encrypt_trivial_bool(rhs_clear, &rhs);
assert(ok == 0);
ok = fhe_bool_bitand(lhs, rhs, &result);
assert(ok == 0);
bool clear;
ok = fhe_bool_decrypt(result, client_key, &clear);
assert(ok == 0);
assert(clear == (lhs_clear & rhs_clear));
fhe_bool_destroy(lhs);
fhe_bool_destroy(rhs);
fhe_bool_destroy(result);
return ok;
}
int main(void)
{
@@ -88,6 +119,7 @@ int main(void)
client_key_test(client_key);
public_key_test(client_key, public_key);
trivial_encrypt_test(client_key);
client_key_destroy(client_key);
public_key_destroy(public_key);

View File

@@ -9,7 +9,7 @@ Welcome to this tutorial about TFHE-rs `core_crypto` module.
To use `TFHE-rs`, first it has to be added as a dependency in the `Cargo.toml`:
```toml
tfhe = { version = "0.2.0", features = [ "x86_64-unix" ] }
tfhe = { version = "0.2.2", features = [ "x86_64-unix" ] }
```
This enables the `x86_64-unix` feature to have efficient implementations of various algorithms for `x86_64` CPUs on a Unix-like system. The 'unix' suffix indicates that the `UnixSeeder`, which uses `/dev/random` to generate random numbers, is activated as a fallback if no hardware number generator is available, like `rdseed` on `x86_64` or if the [`Randomization Services`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc) on Apple platforms are not available. To avoid having the `UnixSeeder` as a potential fallback or to run on non-Unix systems (e.g., Windows), the `x86_64` feature is sufficient.
@@ -19,19 +19,19 @@ For Apple Silicon, the `aarch64-unix` or `aarch64` feature should be enabled. `a
In short: For x86\_64-based machines running Unix-like OSes:
```toml
tfhe = { version = "0.2.0", features = ["x86_64-unix"] }
tfhe = { version = "0.2.2", features = ["x86_64-unix"] }
```
For Apple Silicon or aarch64-based machines running Unix-like OSes:
```toml
tfhe = { version = "0.2.0", features = ["aarch64-unix"] }
tfhe = { version = "0.2.2", features = ["aarch64-unix"] }
```
For x86\_64-based machines with the [`rdseed instruction`](https://en.wikipedia.org/wiki/RDRAND) running Windows:
```toml
tfhe = { version = "0.2.0", features = ["x86_64"] }
tfhe = { version = "0.2.2", features = ["x86_64"] }
```
### Commented code to double a 2-bits message in a leveled fashion and using a PBS with the `core_crypto` module.

View File

@@ -59,10 +59,10 @@ To ensure predictable timings, the operation flavor is the `default` one: a carr
| Plaintext size | add | mul | greater\_than (gt) | min |
| -------------------| -------------- | ------------------- | --------- | ------- |
| 8 bits | 129.0 ms | 178.2 ms | 111.9 ms | 287.7 ms |
| 16 bits | 256.3 ms | 328.0 ms | 145.3 ms | 437.4 ms |
| 32 bits | 469.4 ms | 645.5 ms | 192.0 ms | 776.4 ms |
| 40 bits | 608.0 ms | 849.3 ms | 228.4 ms | 953.5 ms |
| 64 bits | 959.9 ms | 1.49 s | 249.0 ms | 1.36 s |
| 128 bits | 1.88 s | 3.25 s | 294.7 ms | 2.37 s |
| 256 bits | 3.66 s | 8.38 s | 361.8 ms | 4.51 s |
| 8 bits | 129.0 ms | 227.2 ms | 111.9 ms | 186.8 ms |
| 16 bits | 256.3 ms | 756.0 ms | 145.3 ms | 233.1 ms |
| 32 bits | 469.4 ms | 2.10 s | 192.0 ms | 282.9 ms |
| 40 bits | 608.0 ms | 3.37 s | 228.4 ms | 318.6 ms |
| 64 bits | 959.9 ms | 5.53 s | 249.0 ms | 336.5 ms |
| 128 bits | 1.88 s | 14.1 s | 294.7 ms | 398.6 ms |
| 256 bits | 3.66 s | 29.2 s | 361.8 ms | 509.1 ms |

View File

@@ -5,7 +5,7 @@
To use `TFHE-rs` in your project, you first need to add it as a dependency in your `Cargo.toml`:
```toml
tfhe = { version = "0.2.0", features = [ "boolean", "shortint", "integer", "x86_64-unix" ] }
tfhe = { version = "0.2.2", features = [ "boolean", "shortint", "integer", "x86_64-unix" ] }
```
{% hint style="info" %}

View File

@@ -11,7 +11,7 @@ To serialize our data, a [data format](https://serde.rs/#data-formats) should be
[dependencies]
# ...
tfhe = { version = "0.2.0", features = ["integer"]}
tfhe = { version = "0.2.2", features = ["integer","x86_64-unix"]}
bincode = "1.3.3"
```

View File

@@ -9,7 +9,7 @@ The basic steps for using the high-level API of TFHE-rs are:
3. Client-side: Encrypting data;
4. Server-side: Setting the server key;
5. Server-side: Computing over encrypted data;
6. Client-side: Encrypting data.
6. Client-side: Decrypting data.
Here is the full example (mixing client and server parts):
@@ -44,10 +44,13 @@ fn main() {
}
```
Default configuration for x86 Unix machines:
```toml
tfhe = { version = "0.2.0", features = ["integer"]}
tfhe = { version = "0.2.2", features = ["integer", "x86_64-unix"]}
```
Other configurations can be found [here](../getting_started/installation.md).
### Imports.
`tfhe` uses `traits` to have a consistent API for creating FHE types and enable users to write generic functions. To be able to use associated functions and methods of a trait, the trait has to be in scope.
@@ -64,13 +67,8 @@ The first step is the creation of the configuration. The configuration is used t
Creating a configuration is done using the ConfigBuilder type.
In this example, 8-bit unsigned integers with default parameters are used. The `integers` feature must also be enabled, as per the table on the Getting Started page.
{% hint style="info" %}
```toml
tfhe = { version = "0.2.0", features = ["integer"]}
```
{% endhint %}
In this example, 8-bit unsigned integers with default parameters are used. The `integers`
feature must also be enabled, as per the table on the [Getting Started page](../getting_started/installation.md).
The config is done by first creating a builder with all types deactivated. Then, the `uint8` type with default parameters is activated.
@@ -191,10 +189,13 @@ To use the `FheUint8` type, the `integer` feature must be activated:
# Cargo.toml
[dependencies]
# ...
tfhe = { version = "0.2.0", features = ["integer"]}
# Default configuration for x86 Unix machines:
tfhe = { version = "0.2.2", features = ["integer", "x86_64-unix"]}
```
Other configurations can be found [here](../getting_started/installation.md).
```rust
use tfhe::{FheUint8, ConfigBuilder, generate_keys, set_server_key, ClientKey};
use tfhe::prelude::*;
@@ -317,11 +318,13 @@ To use Booleans, the `booleans` feature in our Cargo.toml must be enabled:
```toml
# Cargo.toml
[dependencies]
# ...
tfhe = { version = "0.2.0", features = ["booleans"]}
# Default configuration for x86 Unix machines:
tfhe = { version = "0.2.2", features = ["boolean", "x86_64-unix"]}
```
Other configurations can be found [here](../getting_started/installation.md).
#### function definition
First, the verification function is defined.
@@ -461,7 +464,7 @@ To make the `compute_parity_bit` function compatible with both `FheBool` and `bo
Writing a generic function that accepts `FHE` types as well as clear types can help test the function to see if it is correct. If the function is generic, it can run with clear data, allowing the use of print-debugging or a debugger to spot errors.
Writing generic functions that use operator overloading for our FHE types can be trickier than normal, since, as explained in [Generic Bounds How To](../how\_to/generic\_bounds.md), `FHE` types are not copy. So using the reference `&` is mandatory, even though this is not the case when using native types, which are all `Copy`.
Writing generic functions that use operator overloading for our FHE types can be trickier than normal, since `FHE` types are not copy. So using the reference `&` is mandatory, even though this is not the case when using native types, which are all `Copy`.
This will make the generic bounds trickier at first.
@@ -549,7 +552,7 @@ help: consider adding an explicit lifetime bound...
|
```
The way to fix this is to use `Higher-Rank Trait Bounds`, as shown in the [Generic Bounds How To](../how\_to/generic\_bounds.md):
The way to fix this is to use `Higher-Rank Trait Bounds`:
```Rust
where

View File

@@ -18,6 +18,9 @@ use crate::core_crypto::commons::math::random::{ActivatedRandomGenerator, Seeder
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::seeders::new_seeder;
#[cfg(test)]
mod tests;
pub(crate) trait BinaryGatesEngine<L, R, K> {
fn and(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
fn nand(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
@@ -257,6 +260,37 @@ impl Default for BooleanEngine {
}
impl BooleanEngine {
/// Replace the thread_local BooleanEngine
///
/// `new_engine` will replace the already_existing
/// `thread_local` engine.
///
/// # Example
///
/// ```rust
/// use tfhe::boolean::engine::BooleanEngine;
/// use tfhe::core_crypto::commons::generators::DeterministicSeeder;
/// use tfhe::core_crypto::commons::math::random::Seed;
/// use tfhe::core_crypto::prelude::ActivatedRandomGenerator;
///
/// // WARNING: Using a deterministic seed is not recommended
/// // as it renders the random generation insecure
///
/// let deterministic_seed = Seed(0);
///
/// let mut seeder = DeterministicSeeder::<ActivatedRandomGenerator>::new(deterministic_seed);
/// let boolean_engine = BooleanEngine::new_from_seeder(&mut seeder);
/// BooleanEngine::replace_thread_local(boolean_engine);
///
/// // This uses the engine create earlier
/// let (cks, sks) = tfhe::boolean::gen_keys();
/// ```
pub fn replace_thread_local(new_engine: Self) {
Self::with_thread_local_mut(|local_engine| {
let _ = std::mem::replace(local_engine, new_engine);
})
}
pub fn new() -> Self {
let mut root_seeder = new_seeder();

View File

@@ -0,0 +1,54 @@
#[test]
fn test_replacing_thread_local_engine() {
use crate::boolean::engine::BooleanEngine;
use crate::core_crypto::commons::generators::DeterministicSeeder;
use crate::core_crypto::commons::math::random::Seed;
use crate::core_crypto::prelude::ActivatedRandomGenerator;
let deterministic_seed = Seed(0);
// We change the engine in the main thread
// then generate a client key, and then encrypt
// a boolean value and serialize it to compare
// it with other ciphertext
let mut seeder = DeterministicSeeder::<ActivatedRandomGenerator>::new(deterministic_seed);
let boolean_engine = BooleanEngine::new_from_seeder(&mut seeder);
BooleanEngine::replace_thread_local(boolean_engine);
let (cks, _) = crate::boolean::gen_keys();
let ct = cks.encrypt(false);
let main_thread_data = bincode::serialize(&ct).unwrap();
// In this thread, we don't change the engine
// and so we expect the encrypted value to be
// different compared with the one from the main thread
//
// This also "proves" that a thread is not affected
// by engine changes from other thread as engines are
// thread_local
let second_thread_data = std::thread::spawn(|| {
let (cks, _) = crate::boolean::gen_keys();
let ct = cks.encrypt(false);
bincode::serialize(&ct).unwrap()
})
.join()
.unwrap();
assert_ne!(second_thread_data, main_thread_data);
// In this thread, we change the engine,
// with a new engine that has the same seed
// as the one in the main thread
// So we expect the encrypted value to be the same
// compared with the one from the main thread
let third_thread_data = std::thread::spawn(move || {
let mut seeder = DeterministicSeeder::<ActivatedRandomGenerator>::new(deterministic_seed);
let boolean_engine = BooleanEngine::new_from_seeder(&mut seeder);
BooleanEngine::replace_thread_local(boolean_engine);
let (cks, _) = crate::boolean::gen_keys();
let ct = cks.encrypt(false);
bincode::serialize(&ct).unwrap()
})
.join()
.unwrap();
assert_eq!(third_thread_data, main_thread_data);
}

View File

@@ -11,6 +11,7 @@ impl_binary_fn_on_type!(FheBool => bitand, bitor, bitxor);
impl_unary_fn_on_type!(FheBool => not);
impl_decrypt_on_type!(FheBool, bool);
impl_try_encrypt_trivial_on_type!(FheBool{crate::high_level_api::FheBool}, bool);
impl_try_encrypt_with_client_key_on_type!(FheBool{crate::high_level_api::FheBool}, bool);
impl_try_encrypt_with_public_key_on_type!(FheBool{crate::high_level_api::FheBool}, bool);

View File

@@ -81,40 +81,62 @@ create_integer_wrapper_type!(name: FheUint128, clear_scalar_type: u64);
create_integer_wrapper_type!(name: FheUint256, clear_scalar_type: u64);
impl_decrypt_on_type!(FheUint8, u8);
impl_try_encrypt_trivial_on_type!(FheUint8{crate::high_level_api::FheUint8}, u8);
impl_try_encrypt_with_client_key_on_type!(FheUint8{crate::high_level_api::FheUint8}, u8);
impl_try_encrypt_with_public_key_on_type!(FheUint8{crate::high_level_api::FheUint8}, u8);
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint8{crate::high_level_api::CompressedFheUint8}, u8);
impl_decrypt_on_type!(FheUint10, u16);
impl_try_encrypt_trivial_on_type!(FheUint10{crate::high_level_api::FheUint10}, u16);
impl_try_encrypt_with_client_key_on_type!(FheUint10{crate::high_level_api::FheUint10}, u16);
impl_try_encrypt_with_public_key_on_type!(FheUint10{crate::high_level_api::FheUint10}, u16);
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint10{crate::high_level_api::CompressedFheUint10}, u16);
impl_decrypt_on_type!(FheUint12, u16);
impl_try_encrypt_trivial_on_type!(FheUint12{crate::high_level_api::FheUint12}, u16);
impl_try_encrypt_with_client_key_on_type!(FheUint12{crate::high_level_api::FheUint12}, u16);
impl_try_encrypt_with_public_key_on_type!(FheUint12{crate::high_level_api::FheUint12}, u16);
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint12{crate::high_level_api::CompressedFheUint12}, u16);
impl_decrypt_on_type!(FheUint14, u16);
impl_try_encrypt_trivial_on_type!(FheUint14{crate::high_level_api::FheUint14}, u16);
impl_try_encrypt_with_client_key_on_type!(FheUint14{crate::high_level_api::FheUint14}, u16);
impl_try_encrypt_with_public_key_on_type!(FheUint14{crate::high_level_api::FheUint14}, u16);
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint14{crate::high_level_api::CompressedFheUint14}, u16);
impl_decrypt_on_type!(FheUint16, u16);
impl_try_encrypt_trivial_on_type!(FheUint16{crate::high_level_api::FheUint16}, u16);
impl_try_encrypt_with_client_key_on_type!(FheUint16{crate::high_level_api::FheUint16}, u16);
impl_try_encrypt_with_public_key_on_type!(FheUint16{crate::high_level_api::FheUint16}, u16);
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint16{crate::high_level_api::CompressedFheUint16}, u16);
impl_decrypt_on_type!(FheUint32, u32);
impl_try_encrypt_trivial_on_type!(FheUint32{crate::high_level_api::FheUint32}, u32);
impl_try_encrypt_with_client_key_on_type!(FheUint32{crate::high_level_api::FheUint32}, u32);
impl_try_encrypt_with_public_key_on_type!(FheUint32{crate::high_level_api::FheUint32}, u32);
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint32{crate::high_level_api::CompressedFheUint32}, u32);
impl_decrypt_on_type!(FheUint64, u64);
impl_try_encrypt_trivial_on_type!(FheUint64{crate::high_level_api::FheUint64}, u64);
impl_try_encrypt_with_client_key_on_type!(FheUint64{crate::high_level_api::FheUint64}, u64);
impl_try_encrypt_with_public_key_on_type!(FheUint64{crate::high_level_api::FheUint64}, u64);
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint64{crate::high_level_api::CompressedFheUint64}, u64);
#[no_mangle]
pub unsafe extern "C" fn fhe_uint128_try_encrypt_trivial_u128(
low_word: u64,
high_word: u64,
result: *mut *mut FheUint128,
) -> c_int {
catch_panic(|| {
let value = ((high_word as u128) << 64u128) | low_word as u128;
let inner = <crate::high_level_api::FheUint128>::try_encrypt_trivial(value).unwrap();
*result = Box::into_raw(Box::new(FheUint128(inner)));
})
}
#[no_mangle]
pub unsafe extern "C" fn fhe_uint128_try_encrypt_with_client_key_u128(
low_word: u64,
@@ -189,6 +211,18 @@ pub unsafe extern "C" fn fhe_uint128_decrypt(
})
}
#[no_mangle]
pub unsafe extern "C" fn fhe_uint256_try_encrypt_trivial_u256(
value: *const U256,
result: *mut *mut FheUint256,
) -> c_int {
catch_panic(|| {
let inner = <crate::high_level_api::FheUint256>::try_encrypt_trivial((*value).0).unwrap();
*result = Box::into_raw(Box::new(FheUint256(inner)));
})
}
#[no_mangle]
pub unsafe extern "C" fn fhe_uint256_try_encrypt_with_client_key_u256(
value: *const U256,

View File

@@ -59,6 +59,23 @@ macro_rules! impl_try_encrypt_with_public_key_on_type {
};
}
macro_rules! impl_try_encrypt_trivial_on_type {
($wrapper_type:ty{$wrapped_type:ty}, $input_type:ty) => {
::paste::paste! {
#[no_mangle]
pub unsafe extern "C" fn [<$wrapper_type:snake _try_encrypt_trivial_ $input_type:snake>](
value: $input_type,
result: *mut *mut $wrapper_type,
) -> ::std::os::raw::c_int {
$crate::c_api::utils::catch_panic(|| {
let inner = <$wrapped_type>::try_encrypt_trivial(value).unwrap();
*result = Box::into_raw(Box::new($wrapper_type(inner)));
})
}
}
};
}
macro_rules! impl_decrypt_on_type {
($wrapper_type:ty, $output_type:ty) => {
::paste::paste! {

View File

@@ -229,6 +229,10 @@ impl<G: ByteRandomGenerator> EncryptionRandomGenerator<G> {
where
Scalar: UnsignedInteger + RandomGenerable<Gaussian<f64>, CustomModulus = f64>,
{
if custom_modulus.is_native_modulus() {
return self.random_noise(std);
}
let custom_modulus_f64: f64 = custom_modulus.get().cast_into();
Scalar::generate_one_custom_modulus(
&mut self.noise,
@@ -552,7 +556,7 @@ fn noise_bytes_per_pfpksk(
#[cfg(test)]
mod test {
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::dispersion::Variance;
use crate::core_crypto::commons::dispersion::{StandardDev, Variance};
use crate::core_crypto::commons::parameters::{
CiphertextModulus, DecompositionBaseLog, DecompositionLevelCount, GlweSize, LweDimension,
PolynomialSize,
@@ -560,6 +564,7 @@ mod test {
use crate::core_crypto::commons::test_tools::{
new_encryption_random_generator, new_secret_random_generator,
};
use crate::core_crypto::commons::traits::UnsignedTorus;
#[test]
fn test_gaussian_sampling_margin_factor_does_not_panic() {
@@ -598,4 +603,206 @@ mod test {
&mut enc_generator,
);
}
fn noise_gen_native<Scalar: UnsignedTorus>() {
let mut gen = new_encryption_random_generator();
let bits = (Scalar::BITS / 2) as i32;
for _ in 0..1000 {
let val: Scalar = gen.random_noise(StandardDev(2.0f64.powi(-bits)));
assert!(val != Scalar::ZERO);
}
}
#[test]
fn noise_gen_native_u32() {
noise_gen_native::<u32>();
}
#[test]
fn noise_gen_native_u64() {
noise_gen_native::<u64>();
}
#[test]
fn noise_gen_native_u128() {
noise_gen_native::<u128>();
}
fn noise_gen_custom_mod<Scalar: UnsignedTorus>(ciphertext_modulus: CiphertextModulus<Scalar>) {
let mut gen = new_encryption_random_generator();
let bits = (Scalar::BITS / 2) as i32;
for _ in 0..1000 {
let val: Scalar =
gen.random_noise_custom_mod(StandardDev(2.0f64.powi(-bits)), ciphertext_modulus);
assert!(val != Scalar::ZERO);
}
}
#[test]
fn noise_gen_custom_mod_u32() {
noise_gen_custom_mod::<u32>(CiphertextModulus::try_new_power_of_2(31).unwrap());
}
#[test]
fn noise_gen_custom_mod_u64() {
noise_gen_custom_mod::<u64>(CiphertextModulus::try_new_power_of_2(63).unwrap());
}
#[test]
fn noise_gen_custom_mod_u128() {
noise_gen_custom_mod::<u128>(CiphertextModulus::try_new_power_of_2(127).unwrap());
}
#[test]
fn noise_gen_native_custom_mod_u32() {
noise_gen_custom_mod::<u32>(CiphertextModulus::new_native());
}
#[test]
fn noise_gen_native_custom_mod_u64() {
noise_gen_custom_mod::<u64>(CiphertextModulus::new_native());
}
#[test]
fn noise_gen_native_custom_mod_u128() {
noise_gen_custom_mod::<u128>(CiphertextModulus::new_native());
}
fn noise_gen_slice_native<Scalar: UnsignedTorus>() {
let mut gen = new_encryption_random_generator();
let bits = (Scalar::BITS / 2) as i32;
let mut slice = vec![Scalar::ZERO; 1000];
gen.fill_slice_with_random_noise(&mut slice, StandardDev(2.0f64.powi(-bits)));
assert!(slice.iter().all(|&x| x != Scalar::ZERO))
}
#[test]
fn noise_gen_slice_native_u32() {
noise_gen_slice_native::<u32>();
}
#[test]
fn noise_gen_slice_native_u64() {
noise_gen_slice_native::<u64>();
}
#[test]
fn noise_gen_slice_native_u128() {
noise_gen_slice_native::<u128>();
}
fn noise_gen_slice_custom_mod<Scalar: UnsignedTorus>(
ciphertext_modulus: CiphertextModulus<Scalar>,
) {
let mut gen = new_encryption_random_generator();
let bits = (Scalar::BITS / 2) as i32;
let mut slice = vec![Scalar::ZERO; 1000];
gen.fill_slice_with_random_noise_custom_mod(
&mut slice,
StandardDev(2.0f64.powi(-bits)),
ciphertext_modulus,
);
assert!(slice.iter().all(|&x| x != Scalar::ZERO))
}
#[test]
fn noise_gen_slice_custom_mod_u32() {
noise_gen_slice_custom_mod::<u32>(CiphertextModulus::try_new_power_of_2(31).unwrap());
}
#[test]
fn noise_gen_slice_custom_mod_u64() {
noise_gen_slice_custom_mod::<u64>(CiphertextModulus::try_new_power_of_2(63).unwrap());
}
#[test]
fn noise_gen_slice_custom_mod_u128() {
noise_gen_slice_custom_mod::<u128>(CiphertextModulus::try_new_power_of_2(127).unwrap());
}
#[test]
fn noise_gen_slice_native_custom_mod_u32() {
noise_gen_slice_custom_mod::<u32>(CiphertextModulus::new_native());
}
#[test]
fn noise_gen_slice_native_custom_mod_u64() {
noise_gen_slice_custom_mod::<u64>(CiphertextModulus::new_native());
}
#[test]
fn noise_gen_slice_native_custom_mod_u128() {
noise_gen_slice_custom_mod::<u128>(CiphertextModulus::new_native());
}
fn mask_gen_slice_native<Scalar: UnsignedTorus>() {
let mut gen = new_encryption_random_generator();
let mut slice = vec![Scalar::ZERO; 1000];
gen.fill_slice_with_random_mask(&mut slice);
assert!(slice.iter().all(|&x| x != Scalar::ZERO))
}
#[test]
fn mask_gen_native_u32() {
mask_gen_slice_native::<u32>();
}
#[test]
fn mask_gen_native_u64() {
mask_gen_slice_native::<u64>();
}
#[test]
fn mask_gen_native_u128() {
mask_gen_slice_native::<u128>();
}
fn mask_gen_slice_custom_mod<Scalar: UnsignedTorus>(
ciphertext_modulus: CiphertextModulus<Scalar>,
) {
let mut gen = new_encryption_random_generator();
let mut slice = vec![Scalar::ZERO; 1000];
gen.fill_slice_with_random_mask_custom_mod(&mut slice, ciphertext_modulus);
assert!(slice.iter().all(|&x| x != Scalar::ZERO))
}
#[test]
fn mask_gen_slice_custom_mod_u32() {
mask_gen_slice_custom_mod::<u32>(CiphertextModulus::try_new_power_of_2(31).unwrap());
}
#[test]
fn mask_gen_slice_custom_mod_u64() {
mask_gen_slice_custom_mod::<u64>(CiphertextModulus::try_new_power_of_2(63).unwrap());
}
#[test]
fn mask_gen_slice_custom_mod_u128() {
mask_gen_slice_custom_mod::<u128>(CiphertextModulus::try_new_power_of_2(127).unwrap());
}
#[test]
fn mask_gen_slice_native_custom_mod_u32() {
mask_gen_slice_custom_mod::<u32>(CiphertextModulus::new_native());
}
#[test]
fn mask_gen_slice_native_custom_mod_u64() {
mask_gen_slice_custom_mod::<u64>(CiphertextModulus::new_native());
}
#[test]
fn mask_gen_slice_native_custom_mod_u128() {
mask_gen_slice_custom_mod::<u128>(CiphertextModulus::new_native());
}
}

View File

@@ -175,8 +175,9 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
/// use concrete_csprng::seeders::Seed;
/// use tfhe::core_crypto::commons::math::random::RandomGenerator;
/// let mut generator = RandomGenerator::<SoftwareRandomGenerator>::new(Seed(0));
/// let mut vec = vec![1u32; 100];
/// let mut vec = vec![0u32; 1000];
/// generator.fill_slice_with_random_uniform(&mut vec);
/// assert!(vec.iter().all(|&x| x != 0));
/// ```
pub fn fill_slice_with_random_uniform<Scalar>(&mut self, output: &mut [Scalar])
where
@@ -196,11 +197,12 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
/// use tfhe::core_crypto::commons::math::random::RandomGenerator;
/// use tfhe::core_crypto::commons::parameters::CiphertextModulus;
/// let mut generator = RandomGenerator::<SoftwareRandomGenerator>::new(Seed(0));
/// let mut vec = vec![1u32; 100];
/// let mut vec = vec![0u32; 1000];
/// generator.fill_slice_with_random_uniform_custom_mod(
/// &mut vec,
/// CiphertextModulus::try_new_power_of_2(31).unwrap(),
/// );
/// assert!(vec.iter().all(|&x| x != 0));
/// ```
pub fn fill_slice_with_random_uniform_custom_mod<Scalar>(
&mut self,
@@ -243,8 +245,9 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
/// use concrete_csprng::seeders::Seed;
/// use tfhe::core_crypto::commons::math::random::RandomGenerator;
/// let mut generator = RandomGenerator::<SoftwareRandomGenerator>::new(Seed(0));
/// let mut vec = vec![2u32; 100];
/// let mut vec = vec![0u32; 1000];
/// generator.fill_slice_with_random_uniform_binary(&mut vec);
/// assert!(vec.iter().any(|&x| x != 0));
/// ```
pub fn fill_slice_with_random_uniform_binary<Scalar>(&mut self, output: &mut [Scalar])
where
@@ -348,11 +351,15 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
/// // check that both samples are in 6 sigmas.
/// assert!(g1.abs() <= 6.);
/// assert!(g2.abs() <= 6.);
/// assert!(g1 != 0.);
/// assert!(g2 != 0.);
/// // for f64
/// let (g1, g2): (f64, f64) = generator.random_gaussian(0. as f64, 1. as f64);
/// // check that both samples are in 6 sigmas.
/// assert!(g1.abs() <= 6.);
/// assert!(g2.abs() <= 6.);
/// assert!(g1 != 0.);
/// assert!(g2 != 0.);
/// ```
pub fn random_gaussian<Float, Scalar>(&mut self, mean: Float, std: Float) -> (Scalar, Scalar)
where
@@ -371,8 +378,9 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
/// use concrete_csprng::seeders::Seed;
/// use tfhe::core_crypto::commons::math::random::RandomGenerator;
/// let mut generator = RandomGenerator::<SoftwareRandomGenerator>::new(Seed(0));
/// let mut vec = vec![1000f32; 100];
/// let mut vec = vec![0f32; 1000];
/// generator.fill_slice_with_random_gaussian(&mut vec, 0., 1.);
/// assert!(vec.iter().all(|&x| x != 0.));
/// ```
pub fn fill_slice_with_random_gaussian<Float, Scalar>(
&mut self,
@@ -404,13 +412,14 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
/// use tfhe::core_crypto::commons::math::random::RandomGenerator;
/// use tfhe::core_crypto::commons::parameters::CiphertextModulus;
/// let mut generator = RandomGenerator::<SoftwareRandomGenerator>::new(Seed(0));
/// let mut vec = vec![1000u64; 100];
/// let mut vec = vec![0u64; 1000];
/// generator.fill_slice_with_random_gaussian_custom_mod(
/// &mut vec,
/// 0.,
/// 1.,
/// CiphertextModulus::try_new_power_of_2(63).unwrap(),
/// );
/// assert!(vec.iter().all(|&x| x != 0));
/// ```
pub fn fill_slice_with_random_gaussian_custom_mod<Float, Scalar>(
&mut self,
@@ -423,6 +432,11 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
Scalar: UnsignedInteger,
(Scalar, Scalar): RandomGenerable<Gaussian<Float>, CustomModulus = Float>,
{
if custom_modulus.is_native_modulus() {
self.fill_slice_with_random_gaussian(output, mean, std);
return;
}
let custom_modulus_float: Float = custom_modulus.get().cast_into();
output.chunks_mut(2).for_each(|s| {
let (g1, g2) = <(Scalar, Scalar)>::generate_one_custom_modulus(
@@ -448,8 +462,9 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
/// use concrete_csprng::seeders::Seed;
/// use tfhe::core_crypto::commons::math::random::RandomGenerator;
/// let mut generator = RandomGenerator::<SoftwareRandomGenerator>::new(Seed(0));
/// let mut vec = vec![1000u32; 100];
/// let mut vec = vec![0u32; 1000];
/// generator.unsigned_torus_slice_wrapping_add_random_gaussian_assign(&mut vec, 0., 1.);
/// assert!(vec.iter().all(|&x| x != 0));
/// ```
pub fn unsigned_torus_slice_wrapping_add_random_gaussian_assign<Float, Scalar>(
&mut self,
@@ -479,9 +494,16 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
/// use concrete_csprng::generators::SoftwareRandomGenerator;
/// use concrete_csprng::seeders::Seed;
/// use tfhe::core_crypto::commons::math::random::RandomGenerator;
/// use tfhe::core_crypto::commons::parameters::CiphertextModulus;
/// let mut generator = RandomGenerator::<SoftwareRandomGenerator>::new(Seed(0));
/// let mut vec = vec![1000u32; 100];
/// generator.unsigned_torus_slice_wrapping_add_random_gaussian_assign(&mut vec, 0., 1.);
/// let mut vec = vec![0u32; 1000];
/// generator.unsigned_torus_slice_wrapping_add_random_gaussian_custom_mod_assign(
/// &mut vec,
/// 0.,
/// 1.,
/// CiphertextModulus::try_new_power_of_2(31).unwrap(),
/// );
/// assert!(vec.iter().all(|&x| x != 0));
/// ```
pub fn unsigned_torus_slice_wrapping_add_random_gaussian_custom_mod_assign<Float, Scalar>(
&mut self,
@@ -494,6 +516,11 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
Float: FloatingPoint + CastFrom<u128>,
(Scalar, Scalar): RandomGenerable<Gaussian<Float>, CustomModulus = Float>,
{
if custom_modulus.is_native_modulus() {
self.unsigned_torus_slice_wrapping_add_random_gaussian_assign(output, mean, std);
return;
}
let custom_modulus_float: Float = custom_modulus.get().cast_into();
output.chunks_mut(2).for_each(|s| {
let (g1, g2) = <(Scalar, Scalar)>::generate_one_custom_modulus(

View File

@@ -17,8 +17,6 @@ use crate::core_crypto::entities::*;
use crate::core_crypto::fft_impl::common::{pbs_modulus_switch, FourierBootstrapKey};
use crate::core_crypto::prelude::ContainerMut;
use aligned_vec::{avec, ABox, CACHELINE_ALIGN};
use core::any::TypeId;
use core::mem::transmute;
use dyn_stack::{PodStack, ReborrowMut, SizeOverflow, StackReq};
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
@@ -357,14 +355,6 @@ where
fft: Fft128View<'_>,
stack: PodStack<'_>,
) {
if TypeId::of::<Scalar>() == TypeId::of::<u128>() {
let mut lwe_out: LweCiphertext<&mut [u128]> = unsafe { transmute(lwe_out) };
let lwe_in: LweCiphertext<&[u128]> = unsafe { transmute(lwe_in) };
let accumulator: GlweCiphertext<&[u128]> = unsafe { transmute(accumulator) };
return this.bootstrap_u128(&mut lwe_out, &lwe_in, &accumulator, fft, stack);
}
let (mut local_accumulator_data, stack) =
stack.collect_aligned(CACHELINE_ALIGN, accumulator.as_ref().iter().copied());
let mut local_accumulator = GlweCiphertextMutView::from_container(

View File

@@ -166,6 +166,8 @@ where
)
}
// TODO: investigate split version noise
#[allow(dead_code)]
pub(crate) fn bootstrap_u128<ContLweOut, ContLweIn, ContAcc>(
&self,
lwe_out: &mut LweCiphertext<ContLweOut>,

View File

@@ -190,3 +190,14 @@ fn test_compressed_bool() {
assert_eq!(a.decrypt(&keys), true);
assert_eq!(b.decrypt(&keys), false);
}
#[test]
fn test_trivial_bool() {
let keys = setup_static_default();
let a = FheBool::encrypt_trivial(true);
let b = FheBool::encrypt_trivial(false);
assert_eq!(a.decrypt(&keys), true);
assert_eq!(b.decrypt(&keys), false);
}

View File

@@ -2,7 +2,7 @@ use std::marker::PhantomData;
use crate::high_level_api::integers::parameters::EvaluationIntegerKey;
use super::client_key::GenericIntegerClientKey;
use super::client_key::{GenericIntegerClientKey, RadixClientKey};
use super::parameters::IntegerParameter;
use crate::integer::wopbs::WopbsKey;
@@ -11,12 +11,16 @@ use crate::integer::wopbs::WopbsKey;
pub struct GenericIntegerServerKey<P: IntegerParameter> {
pub(in crate::high_level_api::integers) inner: P::InnerServerKey,
pub(in crate::high_level_api::integers) wopbs_key: WopbsKey,
// To know if we have to encrypt into a big or small when trivial encrypting
pub(in crate::high_level_api::integers) pbs_order: crate::shortint::PBSOrder,
// To know the num block when trivial encrypting
pub(in crate::high_level_api::integers) num_block: usize,
_marker: PhantomData<P>,
}
impl<P> GenericIntegerServerKey<P>
where
P: IntegerParameter,
P: IntegerParameter<InnerClientKey = RadixClientKey>,
P::InnerServerKey: EvaluationIntegerKey<P::InnerClientKey>,
{
pub(super) fn new(client_key: &GenericIntegerClientKey<P>) -> Self {
@@ -29,6 +33,8 @@ where
Self {
inner,
wopbs_key,
pbs_order: client_key.inner.pbs_order,
num_block: client_key.inner.inner.num_blocks(),
_marker: Default::default(),
}
}

View File

@@ -201,3 +201,39 @@ fn test_integer_compressed_public_key() {
let clear: u8 = a.decrypt(&client_key);
assert_eq!(clear, 213u8);
}
#[test]
fn test_trivial_fhe_uint8() {
let config = ConfigBuilder::all_disabled().enable_default_uint8().build();
let (client_key, sks) = generate_keys(config);
set_server_key(sks);
let a = FheUint8::try_encrypt_trivial(234u8).unwrap();
assert!(matches!(
&*a.ciphertext.borrow(),
crate::high_level_api::integers::server_key::RadixCiphertextDyn::Big(_)
));
let clear: u8 = a.decrypt(&client_key);
assert_eq!(clear, 234);
}
#[test]
fn test_trivial_fhe_uint256_small() {
let config = ConfigBuilder::all_disabled()
.enable_default_uint256_small()
.build();
let (client_key, sks) = generate_keys(config);
set_server_key(sks);
let clear_a = U256::from(u128::MAX);
let a = FheUint256::try_encrypt_trivial(clear_a).unwrap();
assert!(matches!(
&*a.ciphertext.borrow(),
crate::high_level_api::integers::server_key::RadixCiphertextDyn::Small(_)
));
let clear: U256 = a.decrypt(&client_key);
assert_eq!(clear, clear_a);
}

View File

@@ -21,7 +21,9 @@ use crate::high_level_api::keys::{
CompressedPublicKey, RefKeyFromCompressedPublicKeyChain, RefKeyFromKeyChain,
RefKeyFromPublicKeyChain,
};
use crate::high_level_api::traits::{FheBootstrap, FheDecrypt, FheEq, FheOrd, FheTryEncrypt};
use crate::high_level_api::traits::{
FheBootstrap, FheDecrypt, FheEq, FheOrd, FheTrivialEncrypt, FheTryEncrypt, FheTryTrivialEncrypt,
};
use crate::high_level_api::{ClientKey, PublicKey};
use crate::integer::U256;
@@ -166,6 +168,47 @@ where
}
}
impl<P, T> FheTryTrivialEncrypt<T> for GenericInteger<P>
where
T: Into<U256>,
P: IntegerParameter<
InnerCiphertext = RadixCiphertextDyn,
InnerServerKey = crate::integer::ServerKey,
>,
P::Id: WithGlobalKey<Key = GenericIntegerServerKey<P>> + Default,
{
type Error = crate::high_level_api::errors::Error;
fn try_encrypt_trivial(value: T) -> Result<Self, Self::Error> {
let value = value.into();
let id = P::Id::default();
let ciphertext = id.with_global(|key| match key.pbs_order {
crate::shortint::PBSOrder::KeyswitchBootstrap => {
RadixCiphertextDyn::Big(key.inner.create_trivial_radix(value, key.num_block))
}
crate::shortint::PBSOrder::BootstrapKeyswitch => {
RadixCiphertextDyn::Small(key.inner.create_trivial_radix(value, key.num_block))
}
})?;
Ok(Self::new(ciphertext, id))
}
}
impl<P, T> FheTrivialEncrypt<T> for GenericInteger<P>
where
T: Into<U256>,
P: IntegerParameter<
InnerCiphertext = RadixCiphertextDyn,
InnerServerKey = crate::integer::ServerKey,
>,
P::Id: WithGlobalKey<Key = GenericIntegerServerKey<P>> + Default,
{
#[track_caller]
fn encrypt_trivial(value: T) -> Self {
Self::try_encrypt_trivial(value).unwrap()
}
}
impl<P> GenericInteger<P>
where
P: IntegerParameter,

View File

@@ -413,7 +413,7 @@ static_int_type! {
parameters: Radix {
big_block_parameters: crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2,
small_block_parameters: crate::shortint::parameters::PARAM_SMALL_MESSAGE_2_CARRY_2,
num_block: 32,
num_block: 16,
wopbs_block_parameters: crate::shortint::parameters::parameters_wopbs_message_carry::WOPBS_PARAM_MESSAGE_2_CARRY_2,
},
}

View File

@@ -1,5 +1,7 @@
use crate::high_level_api::prelude::*;
use crate::high_level_api::{generate_keys, CompressedFheUint2, ConfigBuilder, FheUint2};
use crate::high_level_api::{
generate_keys, set_server_key, CompressedFheUint2, ConfigBuilder, FheUint2,
};
use crate::CompressedPublicKey;
#[test]
@@ -24,3 +26,15 @@ fn test_shortint_compressed_public_key() {
let clear = a.decrypt(&client_key);
assert_eq!(clear, 2);
}
#[test]
fn test_trivial_shortint() {
let config = ConfigBuilder::all_disabled().enable_default_uint2().build();
let (client_key, sks) = generate_keys(config);
set_server_key(sks);
let a = FheUint2::try_encrypt_trivial(2).unwrap();
let clear = a.decrypt(&client_key);
assert_eq!(clear, 2);
}

View File

@@ -17,7 +17,8 @@ use crate::high_level_api::keys::{
};
use crate::high_level_api::shortints::public_key::compressed::GenericShortIntCompressedPublicKey;
use crate::high_level_api::traits::{
FheBootstrap, FheDecrypt, FheEq, FheNumberConstant, FheOrd, FheTryEncrypt, FheTryTrivialEncrypt,
FheBootstrap, FheDecrypt, FheEq, FheNumberConstant, FheOrd, FheTrivialEncrypt, FheTryEncrypt,
FheTryTrivialEncrypt,
};
use crate::high_level_api::PublicKey;
@@ -311,6 +312,18 @@ where
}
}
impl<Clear, P> FheTrivialEncrypt<Clear> for GenericShortInt<P>
where
Clear: TryInto<u8>,
P: StaticShortIntegerParameter,
P::Id: Default + WithGlobalKey<Key = GenericShortIntServerKey<P>>,
{
#[track_caller]
fn encrypt_trivial(value: Clear) -> Self {
Self::try_encrypt_trivial(value).unwrap()
}
}
impl<P> GenericShortInt<P>
where
P: ShortIntegerParameter,

View File

@@ -109,29 +109,35 @@ impl AsLittleEndianWords for U256 {
}
}
pub(crate) trait BlockEncryptionKey {
fn parameters(&self) -> &crate::shortint::Parameters;
pub(crate) trait KnowsMessageModulus {
fn message_modulus(&self) -> MessageModulus;
}
impl BlockEncryptionKey for crate::shortint::ClientKey {
fn parameters(&self) -> &crate::shortint::Parameters {
&self.parameters
impl KnowsMessageModulus for crate::shortint::ClientKey {
fn message_modulus(&self) -> MessageModulus {
self.parameters.message_modulus
}
}
impl<OpOrder: crate::shortint::PBSOrderMarker> BlockEncryptionKey
impl<OpOrder: crate::shortint::PBSOrderMarker> KnowsMessageModulus
for crate::shortint::PublicKeyBase<OpOrder>
{
fn parameters(&self) -> &crate::shortint::Parameters {
&self.parameters
fn message_modulus(&self) -> MessageModulus {
self.parameters.message_modulus
}
}
impl<OpOrder: crate::shortint::PBSOrderMarker> BlockEncryptionKey
impl<OpOrder: crate::shortint::PBSOrderMarker> KnowsMessageModulus
for crate::shortint::CompressedPublicKeyBase<OpOrder>
{
fn parameters(&self) -> &crate::shortint::Parameters {
&self.parameters
fn message_modulus(&self) -> MessageModulus {
self.parameters.message_modulus
}
}
impl KnowsMessageModulus for crate::shortint::ServerKey {
fn message_modulus(&self) -> MessageModulus {
self.message_modulus
}
}
@@ -151,7 +157,7 @@ pub(crate) fn encrypt_words_radix_impl<BlockKey, Block, RadixCiphertextType, T,
) -> RadixCiphertextType
where
T: AsLittleEndianWords,
BlockKey: BlockEncryptionKey,
BlockKey: KnowsMessageModulus,
F: Fn(&BlockKey, u64) -> Block,
RadixCiphertextType: From<Vec<Block>>,
{
@@ -165,8 +171,8 @@ where
// | bit values are not valid and should not be encrypted)
// |-> current_power (start of next block of bits to encrypt (inclusive))
let mask = (encrypting_key.parameters().message_modulus.0 - 1) as u128;
let block_modulus = encrypting_key.parameters().message_modulus.0 as u128;
let mask = (encrypting_key.message_modulus().0 - 1) as u128;
let block_modulus = encrypting_key.message_modulus().0 as u128;
let mut blocks = Vec::with_capacity(num_blocks);

View File

@@ -1107,7 +1107,9 @@ impl<'a> Comparator<'a> {
};
let mut res = self.unchecked_max_parallelized(lhs, rhs);
self.server_key.full_propagate_parallelized(&mut res);
res.blocks
.par_iter_mut()
.for_each(|block| self.server_key.key.message_extract_assign(block));
res
}
@@ -1143,7 +1145,9 @@ impl<'a> Comparator<'a> {
};
let mut res = self.unchecked_min_parallelized(lhs, rhs);
self.server_key.full_propagate_parallelized(&mut res);
res.blocks
.par_iter_mut()
.for_each(|block| self.server_key.key.message_extract_assign(block));
res
}
}
@@ -1333,7 +1337,7 @@ mod tests {
assert!(super::has_non_zero_carries(&ct_0));
assert!(super::has_non_zero_carries(&ct_1));
let encrypted_result = default_comparator_method(&comparator, &ct_0, &ct_1);
// assert!(!super::has_non_zero_carries(&encrypted_result));
assert!(!super::has_non_zero_carries(&encrypted_result));
// Sanity decryption checks
{

View File

@@ -12,6 +12,7 @@ mod sub;
use super::ServerKey;
use crate::integer::ciphertext::RadixCiphertext;
use crate::integer::encryption::{encrypt_words_radix_impl, AsLittleEndianWords};
use crate::shortint::PBSOrderMarker;
#[cfg(test)]
@@ -49,6 +50,44 @@ impl ServerKey {
RadixCiphertext::from(vec_res)
}
/// Create a trivial radix ciphertext
///
/// Trivial means that the value is not encrypted
///
/// # Example
///
/// ```rust
/// use tfhe::integer::{gen_keys_radix, RadixCiphertextBig};
/// use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
///
/// let num_blocks = 4;
///
/// // Generate the client key and the server key:
/// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_blocks);
///
/// let ctxt: RadixCiphertextBig = sks.create_trivial_radix(212u64, num_blocks);
///
/// // Decrypt:
/// let dec: u64 = cks.decrypt(&ctxt);
/// assert_eq!(212, dec);
/// ```
pub fn create_trivial_radix<T, PBSOrder>(
&self,
value: T,
num_blocks: usize,
) -> RadixCiphertext<PBSOrder>
where
PBSOrder: PBSOrderMarker,
T: AsLittleEndianWords,
{
encrypt_words_radix_impl(
&self.key,
value,
num_blocks,
crate::shortint::ServerKey::create_trivial,
)
}
/// Propagate the carry of the 'index' block to the next one.
///
/// # Example

View File

@@ -342,10 +342,10 @@ impl ServerKey {
terms.lock().unwrap().push(term);
});
let terms = terms.into_inner().unwrap();
let mut terms = terms.into_inner().unwrap();
for term in terms {
self.unchecked_add_assign(&mut result, &term);
for term in terms.iter_mut() {
self.smart_add_assign(&mut result, term);
}
result
@@ -495,7 +495,20 @@ impl ServerKey {
(ct1, &tmp_rhs)
}
};
self.unchecked_mul_assign_parallelized(lhs, rhs);
let num_blocks = lhs.blocks.len();
let mut terms = vec![self.create_trivial_zero_radix(num_blocks); num_blocks];
terms
.par_iter_mut()
.zip(rhs.blocks.par_iter().enumerate())
.for_each(|(term, (i, rhs_i))| {
*term = self.unchecked_block_mul_parallelized(lhs, rhs_i, i);
});
*lhs = self
.smart_binary_op_seq_parallelized(&mut terms, ServerKey::smart_add_parallelized)
.unwrap_or_else(|| self.create_trivial_zero_radix(num_blocks));
self.full_propagate_parallelized(lhs);
}
}