Compare commits

...

74 Commits

Author SHA1 Message Date
Mayeul@Zama
ce625df1f6 needed here too ? 2023-09-08 16:00:25 +02:00
Mayeul@Zama
5f16704fa8 fix(shortint): add LUT generation without carry 2023-09-08 16:00:25 +02:00
Mayeul@Zama
d686cd86b5 remove now invalid asserts 2023-09-08 16:00:15 +02:00
Mayeul@Zama
a2a3f10341 fix(shortint): fix degree after lookup_table 2023-09-08 16:00:15 +02:00
Mayeul@Zama
d7ac3b6d15 feat(shortint): add asserts in smart ops 2023-09-08 16:00:15 +02:00
Mayeul@Zama
44b9241c83 feat(shortint): cleanup input if necessary in ops 2023-09-08 16:00:15 +02:00
Mayeul@Zama
9df1725efc feat(shortint): cleanup input if necessary in apply_lookup_table_bivariate 2023-09-08 16:00:15 +02:00
Mayeul@Zama
55d97ed794 feat(shortint): no PBS in message/carry extract if useless 2023-09-08 16:00:15 +02:00
Mayeul@Zama
d10c11720e style(shortint): smart always take mut references 2023-09-08 16:00:15 +02:00
Mayeul@Zama
f1e0742b9f feat(shortint): replace apply_msg_identity_lut_assign by message_extract 2023-09-08 16:00:15 +02:00
Mayeul@Zama
e6136ca418 feat(shortint): replace clear_carry by message_extract 2023-09-08 16:00:15 +02:00
Mayeul@Zama
0805cd69e0 chore(shortint): remove comp_assign operations 2023-09-08 16:00:15 +02:00
David Testé
ae3c261d1e chore(bench): add 128 and 256 bit sizes for multi-bit benchmarks 2023-09-08 13:25:28 +02:00
tmontaigu
95dcf95e88 fix(integer): make trim_radix_msb handle dirty inputs 2023-09-08 10:29:25 +02:00
J-B Orfila
b92d6400f4 chore(bench): add new parameter sets to bench 2023-09-07 14:49:20 +02:00
David Testé
9ba27b4082 chore(ci): add multiplier to get effective pbs throughput value 2023-09-06 11:02:47 +02:00
dependabot[bot]
b164c90d75 chore(deps): bump tj-actions/changed-files from 38.1.3 to 38.2.1
Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 38.1.3 to 38.2.1.
- [Release notes](https://github.com/tj-actions/changed-files/releases)
- [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md)
- [Commits](c860b5c47f...2f7246cb26)

---
updated-dependencies:
- dependency-name: tj-actions/changed-files
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-04 16:11:54 +02:00
Arthur Meyre
ff893ca6ef chore(wopbs): rework the inner part of WopLUTBase to be easier to use
BREAKING CHANGE: Wopbs LUT structs for shortint and integer have changed
APIs
2023-09-04 13:19:37 +02:00
tmontaigu
2dd1e13dad fix(integer): add assert on scalar bits in scalar_div
To work the scalar div requires that the scalar type (u32, u64, etc)
has at least as many bits of precision that what the ciphertext
encrypts (e.g. if the ciphertext encrypt 32bits, the scalar type must
have at least 32 bits)

We were missing the assert which lead to misuse, so we add it.
2023-09-01 18:04:51 +02:00
tmontaigu
4393fce861 feat(integer): be smarter about parallel carry propagation usage
The parallel carry propagation algorithm is a lot faster
than the non parallel one, however to actually be faster
it requires the CPU to have enough threads.

This adds checks so that we use the parallel algorithm
only if this condition is met.

This should improve performance on smaller CPUs
typically found in standard laptops/desktops.
2023-09-01 18:04:34 +02:00
Arthur Meyre
4f10cfa6dd chore(tfhe): bump version to 0.4.0 2023-08-31 14:29:17 +02:00
Mayeul@Zama
a6e4488de2 feat(js_test): add warning for expected error lor 2023-08-31 09:28:28 +02:00
Mayeul@Zama
878f3fa448 style(shortint): allow needless_pass_by_ref_mut on smart ops 2023-08-31 09:28:28 +02:00
Mayeul@Zama
90b887a56f style(tfhe): rename module 2023-08-31 09:28:28 +02:00
Mayeul@Zama
7dc52cf4ef chore(toolchain): fix clippy lints 2023-08-31 09:28:28 +02:00
Mayeul@Zama
a69333ed37 chore(toolchain): format rust 2023-08-31 09:28:28 +02:00
Mayeul@Zama
ffad25449e chore(toolchain): update toolchain 2023-08-31 09:28:28 +02:00
tmontaigu
6d471856c7 chore(docs): add section about writing trait bounds
This how to shows how to write the trait bounds of function
for the high level API.
2023-08-30 17:14:45 +02:00
David Testé
65749cb39b chore(bench): benchmark shortint ops against multi-bit parameters 2023-08-30 13:31:46 +02:00
David Testé
bf36316c12 chore(bench): benchmark server key creation from compressed one 2023-08-30 13:31:46 +02:00
David Testé
d98bb0eb86 chore(bench): measure bootstrap compressed key sizes 2023-08-30 13:31:46 +02:00
Arthur Meyre
5747af6dce feat(core): add LwePackingKeyswitchKey entity and algorithms
BREAKING CHANGE:
- more stringent checks for ciphertext moduli on some operations
- uniformized order of certain parameters for keyswitching primitives
- uniformized some primitives name
2023-08-30 10:54:35 +02:00
David Testé
cf08436c7d chore(bench): add pbs_ks parameters to pbs benchmarks 2023-08-29 10:02:01 +02:00
dependabot[bot]
241bddccaf chore(deps): bump rtCamp/action-slack-notify from 2.2.0 to 2.2.1
Bumps [rtCamp/action-slack-notify](https://github.com/rtcamp/action-slack-notify) from 2.2.0 to 2.2.1.
- [Release notes](https://github.com/rtcamp/action-slack-notify/releases)
- [Commits](12e36fc18b...b24d75fe0e)

---
updated-dependencies:
- dependency-name: rtCamp/action-slack-notify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-28 13:39:57 +02:00
dependabot[bot]
8ce1984214 chore(deps): bump tj-actions/changed-files from 37.6.1 to 38.1.3
Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 37.6.1 to 38.1.3.
- [Release notes](https://github.com/tj-actions/changed-files/releases)
- [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md)
- [Commits](a0585ff990...c860b5c47f)

---
updated-dependencies:
- dependency-name: tj-actions/changed-files
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-28 10:17:42 +02:00
dependabot[bot]
82ef430dfa chore(deps): bump actions/checkout from 3.5.3 to 3.6.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.3 to 3.6.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](c85c95e3d7...f43a0e5ff2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-28 10:17:31 +02:00
tmontaigu
2348303b26 chore(ci): pin clap dependency 2023-08-25 21:03:42 +02:00
tmontaigu
a35386f740 chore(ci): remove ability for tests to be retried 2023-08-24 20:38:07 +02:00
tmontaigu
3df542c5f8 chore(bench): bench integer scalar ops up to 256 bits 2023-08-24 16:53:41 +02:00
David Testé
37623eedf3 chore(ci): add shortint pbs_ks parameters to security check 2023-08-23 09:05:45 +02:00
David Testé
fa8cf73d57 chore(ci): run parameters security check job unconditionally
Even if the parameters haven't changed in a commit, the lattice
estimator could have been updated. Hence the reason to run this
quick check on every push on main.
2023-08-23 09:05:45 +02:00
David Testé
872b20a4a1 chore(bench): add division and modulo ops to integer benchmarks 2023-08-23 09:05:16 +02:00
dependabot[bot]
4920e3b4df chore(deps): bump tj-actions/changed-files from 37.6.0 to 37.6.1
Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 37.6.0 to 37.6.1.
- [Release notes](https://github.com/tj-actions/changed-files/releases)
- [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md)
- [Commits](87697c0dca...a0585ff990)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-21 23:18:38 +02:00
David Testé
75a0881e9d fix(boolean): increase noise for tfhe_lib parameters set
This is done to match the values exposed in TFHE_LIB project.
2023-08-21 09:37:56 +02:00
dependabot[bot]
f67effc359 chore(deps): bump tj-actions/changed-files from 37.5.1 to 37.6.0
Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 37.5.1 to 37.6.0.
- [Release notes](https://github.com/tj-actions/changed-files/releases)
- [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md)
- [Commits](a96679dfee...87697c0dca)

---
updated-dependencies:
- dependency-name: tj-actions/changed-files
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-11 22:13:39 +02:00
David Testé
e8eb82f7ae chore(bench): use clean inputs for unchecked ops benchmarks
Previousely, unchecked_eq benchmark would fail at each run. Since
unchecked operations require clean inputs, we change the way we
instantiate benchmark functions.
2023-08-11 13:22:59 +02:00
David Testé
53018ddc36 chore(ci): ignore tfhe_lib_parameters check in lattice esitmator
This third-party parameters set is known to be less secure, so
there is no need to make the CI fail because of this set.
2023-08-11 11:08:59 +02:00
David Testé
9ef62acff1 chore(ci): change full suite benchmarks worflow description
This is done to avoid having the same name than the other
full benchmarks workflow.
2023-08-11 09:18:47 +02:00
aquint-zama
12220b2a18 chore(docs): design upgrade 2023-08-10 18:46:29 +02:00
tmontaigu
625c150dc1 feat(integer): start adding signed radix ciphertext
This introduces the SignedRadixCiphertext type
which encrypts signed values (i8, i16, etc).

Encryption and decryption functions are added.

This also makes the addition work with signed
values as the algorithm is the same.
2023-08-10 17:47:13 +02:00
David Testé
304932a861 chore(tfhe): refactor keycache in its own crate
The general parts of shortint keycache has been moved to its own
crate. This enable boolean layer to get access to traits without
having to import shortint::keycache module.
2023-08-10 16:42:40 +02:00
David Testé
59181d4717 chore(ci): add workflow to check security of parameters sets
Cryptographic parameters sets security is checked automatically
with a lattice estimator. The first step is to collect all the
parameters that need to be checked in in a file with a format
understable by Sagemath. Second, a lattice estimator is run in
a Sage script. Each parameters set is run against two attacks and
then security level is estimated from that.
These steps have been put into a GitHub workflow to perform
automatic checks.

Co-authored-by: Ben <ben.curtis@zama.ai>
2023-08-10 16:42:40 +02:00
David Testé
c5d93f4b38 chore(bench): Put integer comparisons ops in different groups
This is done to avoid hitting GitHub's hard limit of 6hours
maximum execution per job.
This commit also enable the cron job for the full benchmarks suite.
2023-08-10 11:16:43 +02:00
tmontaigu
7a465cd258 feat(integer): make full_propagate_parallelized more parallel
Using the functions that were introduced recently,
it is possible to make the full_propagate_parallelized method
more parallel than it was, resulting in faster computations.

The new carry propapagation should now be the cost of
a default add + one PBS, so ~400ms in 256 instead of ~3s.

However its probably slower for smaller number of blocks (eg 4 blocks)

This is done by extracting carry and messages in parallel,
then adding the carries to the correct message, the final step
is to use the single carry propapagation function.
2023-08-08 17:04:46 +02:00
tmontaigu
a0946ac509 feat(hlapi): add if_then_else 2023-08-08 11:35:37 +02:00
tmontaigu
5521f2a4a4 feat(integer): add if_then_else
This adds if_then_else (aka cmux / select)
to the integer API.

This also makes the min/max implementation use that
cmux instead of their own version of it, and allows
to save one pbs.
2023-08-08 11:35:37 +02:00
J-B Orfila
1a9e40c860 feat(boolean): add KS-PBS pattern choice to boolean
Co-authored-by: tmontaigu<thomas.montaigu@laposte.net>
2023-08-08 11:35:06 +02:00
David Testé
bc3e3e46a0 chore(bench): remove scalar prefix to default operations
This was making benchmark results parsing error-prone since scalar
operations must have the same name. It's the operand_type field
in the record parameters that acts as identifier (CipherText or
PlainText).
2023-08-04 08:45:46 +02:00
Arthur Meyre
60c87b6d95 chore(ci): the workflow was seen as ill formed by github
- it was sending error messages via email, silence it by having it well
formed
2023-08-02 15:14:29 +02:00
Arthur Meyre
df4c9f511d chore(ci): add placeholder workflow to be able to experiment with actions
- most of the time the workflow file needs to exist in main, with this it's
possible to experiment directly on ones branch as the file already exists
in main
2023-08-02 13:31:04 +02:00
David Testé
69bfd6556f chore(bench): prefix benchmarked function by its related layer
Replace ServerKey::[...] benchmark identifier by shortint::[...]
and integer::[...] to ease resutls parsing.
2023-08-02 10:26:06 +02:00
David Testé
7fad91e429 chore(ci): fix full benchmarks workflows
Workspace is cleaned between jobs so we can't factorize parts
of the benchmarks.
2023-08-02 10:26:06 +02:00
David Testé
0da30d5e58 chore(bench): benchmark with only one bit size for scalar 2023-08-02 10:26:06 +02:00
David Testé
97ce5b617a chore(bench): handle scalar bit size on integer results parsing 2023-08-02 10:26:06 +02:00
David Testé
a4723b03f3 chore(ci): install job dependencies right before running benchs
Since full benchmarks use a matrix to run jobs, we need to install
job dependencies (rustup, checkout repositories, ...) each time
because at the end of the job the workspace will be cleaned.
2023-08-02 10:26:06 +02:00
Arthur Meyre
d24d484bcf chore(bench): apply fix to circumvent rust's weird shift behavior 2023-08-02 10:26:06 +02:00
David Testé
9945cdd9b2 chore(ci): bench multiple scalar bit sizes and add operators
Scalar operations are benchmarked against scalar bit sizes.
Scalar division and modulo are added to the benchmark set.
2023-08-02 10:26:06 +02:00
tmontaigu
04a0aa0c31 feat(hlapi): allow scalar ops on values up to U256
This enables to use u128 and U256 as operands to
operations in the high level api.

BREAKING CHANGE: a breaking change in the C API for scalar operations
for FheUint128 and FheUint256 as they previously required
a u64 and now a U218 / U256 respectively.
2023-08-01 15:12:10 +02:00
Arthur Meyre
898c305acd refactor(keycache): refactor the named_params_impl macro
- allows to easily create named parameters from non standard shortint
parameter structs
- allows to reate such named parameters easily outside of the keycache mod
the way the macro was written before expanded to more macro calls that may
not have been in scope and rendered the macro virtually useless
2023-08-01 14:56:58 +02:00
dependabot[bot]
f5854db0b1 chore(deps): bump tj-actions/changed-files from 37.4.0 to 37.5.1
Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 37.4.0 to 37.5.1.
- [Release notes](https://github.com/tj-actions/changed-files/releases)
- [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md)
- [Commits](de0eba3279...a96679dfee)

---
updated-dependencies:
- dependency-name: tj-actions/changed-files
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-31 18:32:08 +02:00
Arthur Meyre
6a56af0b07 chore(ci): add a command to just build docs to see how it looks like
- add a target to lint doc building to replace the previous doc target
- adapt the top lib configuration for broken links to go from deny to warn
2023-07-28 11:52:01 +02:00
J-B Orfila
b7d830c57f docs: update the README for v0.3 2023-07-27 15:16:26 +02:00
David Testé
9b983745da chore(ci): create benchmarks workflows that runs the full suite
It will allow to runs benchmarks all the operations in shortint and
integer layers.
2023-07-27 12:52:42 +02:00
David Testé
10b1305e66 chore(ci): fast integer benchmarks to run on each push in main 2023-07-27 12:52:31 +02:00
233 changed files with 7066 additions and 2358 deletions

View File

@@ -5,13 +5,3 @@ failure-output = "final"
fail-fast = false
retries = 0
slow-timeout = "5m"
[[profile.ci.overrides]]
filter = 'test(/^.*param_message_1_carry_[567]_ks_pbs$/) or test(/^.*param_message_4_carry_4_ks_pbs$/)'
retries = 3
[[profile.ci.overrides]]
filter = 'test(/^.*param_message_[23]_carry_[23]_ks_pbs$/)'
retries = 1

View File

@@ -51,7 +51,7 @@ jobs:
echo "Fork git sha: ${{ inputs.fork_git_sha }}"
- name: Checkout tfhe-rs
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: ${{ inputs.fork_repo }}
ref: ${{ inputs.fork_git_sha }}
@@ -109,7 +109,7 @@ jobs:
- name: Slack Notification
if: ${{ always() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}

View File

@@ -50,7 +50,7 @@ jobs:
echo "Fork git sha: ${{ inputs.fork_git_sha }}"
- name: Checkout tfhe-rs
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: ${{ inputs.fork_repo }}
ref: ${{ inputs.fork_git_sha }}
@@ -76,7 +76,7 @@ jobs:
- name: Slack Notification
if: ${{ always() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}

View File

@@ -50,7 +50,7 @@ jobs:
echo "Fork git sha: ${{ inputs.fork_git_sha }}"
- name: Checkout tfhe-rs
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: ${{ inputs.fork_repo }}
ref: ${{ inputs.fork_git_sha }}
@@ -80,7 +80,7 @@ jobs:
- name: Slack Notification
if: ${{ always() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}

View File

@@ -50,7 +50,7 @@ jobs:
echo "Fork git sha: ${{ inputs.fork_git_sha }}"
- name: Checkout tfhe-rs
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: ${{ inputs.fork_repo }}
ref: ${{ inputs.fork_git_sha }}
@@ -100,7 +100,7 @@ jobs:
- name: Slack Notification
if: ${{ always() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}

View File

@@ -50,7 +50,7 @@ jobs:
echo "Fork git sha: ${{ inputs.fork_git_sha }}"
- name: Checkout tfhe-rs
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: ${{ inputs.fork_repo }}
ref: ${{ inputs.fork_git_sha }}
@@ -77,7 +77,7 @@ jobs:
- name: Slack Notification
if: ${{ always() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}

View File

@@ -43,7 +43,7 @@ jobs:
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
- name: Checkout tfhe-rs repo with tags
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
fetch-depth: 0
@@ -94,7 +94,7 @@ jobs:
path: ${{ env.RESULTS_FILENAME }}
- name: Checkout Slab repo
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: zama-ai/slab
path: slab
@@ -117,7 +117,7 @@ jobs:
- name: Slack Notification
if: ${{ failure() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}

View File

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

View File

@@ -10,7 +10,7 @@ jobs:
- name: Check first line
uses: gsactions/commit-message-checker@16fa2d5de096ae0d35626443bcd24f1e756cafee
with:
pattern: '^((feat|fix|chore|refactor|style|test|docs|doc)\(\w+\)\:) .+$'
pattern: '^((feat|fix|chore|refactor|style|test|docs|doc)(\(\w+\))?\:) .+$'
flags: "gs"
error: 'Your first line has to contain a commit type and scope like "feat(my_feature): msg".'
excludeDescription: "true" # optional: this excludes the description body of a pull request

View File

@@ -44,7 +44,7 @@ jobs:
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
- name: Checkout tfhe-rs repo with tags
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
fetch-depth: 0
@@ -61,7 +61,7 @@ jobs:
- name: Run benchmarks with AVX512
run: |
make AVX512_SUPPORT=ON bench_integer
make AVX512_SUPPORT=ON FAST_BENCH=TRUE bench_integer
- name: Parse benchmarks to csv
run: |
@@ -96,7 +96,7 @@ jobs:
path: ${{ env.RESULTS_FILENAME }}
- name: Checkout Slab repo
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: zama-ai/slab
path: slab
@@ -119,7 +119,7 @@ jobs:
- name: Slack Notification
if: ${{ failure() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}

View File

@@ -0,0 +1,128 @@
# Run all integer benchmarks on an AWS instance and return parsed results to Slab CI bot.
name: Integer full benchmarks
on:
workflow_dispatch:
inputs:
instance_id:
description: "Instance ID"
type: string
instance_image_id:
description: "Instance AMI ID"
type: string
instance_type:
description: "Instance product type"
type: string
runner_name:
description: "Action runner name"
type: string
request_id:
description: "Slab request ID"
type: string
env:
CARGO_TERM_COLOR: always
RESULTS_FILENAME: parsed_benchmark_results_${{ github.sha }}.json
ACTION_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
jobs:
integer-benchmarks:
name: Execute integer benchmarks for all operations flavor
runs-on: ${{ github.event.inputs.runner_name }}
if: ${{ !cancelled() }}
continue-on-error: true
strategy:
max-parallel: 1
matrix:
command: [ integer, integer_multi_bit]
op_flavor: [ default, default_comp, default_scalar, default_scalar_comp, smart, smart_comp, smart_scalar, smart_parallelized, smart_parallelized_comp, smart_scalar_parallelized, unchecked, unchecked_comp, unchecked_scalar, unchecked_scalar_comp, misc ]
steps:
- name: Instance configuration used
run: |
echo "IDs: ${{ inputs.instance_id }}"
echo "AMI: ${{ inputs.instance_image_id }}"
echo "Type: ${{ inputs.instance_type }}"
echo "Request ID: ${{ inputs.request_id }}"
- name: Checkout tfhe-rs repo with tags
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
fetch-depth: 0
- name: Get benchmark details
run: |
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
echo "COMMIT_DATE=$(git --no-pager show -s --format=%cd --date=iso8601-strict ${{ github.sha }})" >> "${GITHUB_ENV}"
echo "COMMIT_HASH=$(git describe --tags --dirty)" >> "${GITHUB_ENV}"
- name: Set up home
# "Install rust" step require root user to have a HOME directory which is not set.
run: |
echo "HOME=/home/ubuntu" >> "${GITHUB_ENV}"
- name: Install rust
uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af
with:
toolchain: nightly
override: true
- name: Checkout Slab repo
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: zama-ai/slab
path: slab
token: ${{ secrets.CONCRETE_ACTIONS_TOKEN }}
- name: Run benchmarks with AVX512
run: |
make AVX512_SUPPORT=ON BENCH_OP_FLAVOR=${{ matrix.op_flavor }} bench_${{ matrix.command }}
- name: Parse results
run: |
python3 ./ci/benchmark_parser.py target/criterion ${{ env.RESULTS_FILENAME }} \
--database tfhe_rs \
--hardware ${{ inputs.instance_type }} \
--project-version "${{ env.COMMIT_HASH }}" \
--branch ${{ github.ref_name }} \
--commit-date "${{ env.COMMIT_DATE }}" \
--bench-date "${{ env.BENCH_DATE }}" \
--walk-subdirs \
--name-suffix avx512 \
--throughput
- name: Upload parsed results artifact
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
with:
name: ${{ github.sha }}_${{ matrix.command }}_${{ matrix.op_flavor }}
path: ${{ env.RESULTS_FILENAME }}
- name: Send data to Slab
shell: bash
run: |
echo "Computing HMac on results file"
SIGNATURE="$(slab/scripts/hmac_calculator.sh ${{ env.RESULTS_FILENAME }} '${{ secrets.JOB_SECRET }}')"
echo "Sending results to Slab..."
curl -v -k \
-H "Content-Type: application/json" \
-H "X-Slab-Repository: ${{ github.repository }}" \
-H "X-Slab-Command: store_data_v2" \
-H "X-Hub-Signature-256: sha256=${SIGNATURE}" \
-d @${{ env.RESULTS_FILENAME }} \
${{ secrets.SLAB_URL }}
slack-notification:
name: Slack Notification
runs-on: ${{ github.event.inputs.runner_name }}
if: ${{ failure() }}
needs: integer-benchmarks
steps:
- name: Notify
continue-on-error: true
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
SLACK_ICON: https://pbs.twimg.com/profile_images/1274014582265298945/OjBKP9kn_400x400.png
SLACK_MESSAGE: "Integer full benchmarks failed. (${{ env.ACTION_RUN_URL }})"
SLACK_USERNAME: ${{ secrets.BOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

View File

@@ -44,7 +44,7 @@ jobs:
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
- name: Checkout tfhe-rs repo with tags
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
fetch-depth: 0
@@ -61,7 +61,7 @@ jobs:
- name: Run multi-bit benchmarks with AVX512
run: |
make AVX512_SUPPORT=ON bench_integer_multi_bit
make AVX512_SUPPORT=ON FAST_BENCH=TRUE bench_integer_multi_bit
- name: Parse benchmarks to csv
run: |
@@ -96,7 +96,7 @@ jobs:
path: ${{ env.RESULTS_FILENAME }}
- name: Checkout Slab repo
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: zama-ai/slab
path: slab
@@ -119,7 +119,7 @@ jobs:
- name: Slack Notification
if: ${{ failure() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}

View File

@@ -28,7 +28,7 @@ jobs:
runs-on: ["self-hosted", "m1mac"]
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
- name: Install latest stable
uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af
@@ -124,7 +124,7 @@ jobs:
- name: Slack Notification
if: ${{ needs.cargo-builds.result != 'skipped' }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ needs.cargo-builds.result }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}

View File

@@ -30,7 +30,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
fetch-depth: 0
@@ -74,7 +74,7 @@ jobs:
- name: Slack Notification
if: ${{ failure() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}

51
.github/workflows/parameters_check.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
# Perform a security check on all the cryptographic parameters set
name: Parameters curves security check
env:
CARGO_TERM_COLOR: always
ACTION_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
RUSTFLAGS: "-C target-cpu=native"
on:
push:
branches:
- "main"
workflow_dispatch:
jobs:
params-curves-security-check:
runs-on: ubuntu-latest
steps:
- name: Checkout tfhe-rs
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
- name: Checkout lattice-estimator
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: malb/lattice-estimator
path: lattice_estimator
- name: Install Sage
run: |
sudo apt update
sudo apt install -y sagemath
- name: Collect parameters
run: |
make write_params_to_file
- name: Perform security check
run: |
PYTHONPATH=lattice_estimator sage ci/lattice_estimator.sage
- name: Slack Notification
if: ${{ always() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
SLACK_ICON: https://pbs.twimg.com/profile_images/1274014582265298945/OjBKP9kn_400x400.png
SLACK_MESSAGE: "Security check for parameters finished with status: ${{ job.status }}. (${{ env.ACTION_RUN_URL }})"
SLACK_USERNAME: ${{ secrets.BOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

View File

@@ -43,7 +43,7 @@ jobs:
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
- name: Checkout tfhe-rs repo with tags
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
fetch-depth: 0
@@ -84,7 +84,7 @@ jobs:
path: ${{ env.RESULTS_FILENAME }}
- name: Checkout Slab repo
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: zama-ai/slab
path: slab
@@ -107,7 +107,7 @@ jobs:
- name: Slack Notification
if: ${{ failure() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}

View File

@@ -0,0 +1,14 @@
# Placeholder workflow file allowing running it without having to merge to main first
name: Placeholder Workflow
on:
workflow_dispatch:
jobs:
placeholder:
name: Placeholder
runs-on: ubuntu-latest
steps:
- run: |
echo "Hello this is a Placeholder Workflow"

View File

@@ -43,7 +43,7 @@ jobs:
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
- name: Checkout tfhe-rs repo with tags
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
fetch-depth: 0
@@ -94,7 +94,7 @@ jobs:
path: ${{ env.RESULTS_FILENAME }}
- name: Checkout Slab repo
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: zama-ai/slab
path: slab
@@ -117,7 +117,7 @@ jobs:
- name: Slack Notification
if: ${{ failure() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}

View File

@@ -0,0 +1,141 @@
# Run all shortint benchmarks on an AWS instance and return parsed results to Slab CI bot.
name: Shortint full benchmarks
on:
workflow_dispatch:
inputs:
instance_id:
description: "Instance ID"
type: string
instance_image_id:
description: "Instance AMI ID"
type: string
instance_type:
description: "Instance product type"
type: string
runner_name:
description: "Action runner name"
type: string
request_id:
description: "Slab request ID"
type: string
env:
CARGO_TERM_COLOR: always
RESULTS_FILENAME: parsed_benchmark_results_${{ github.sha }}.json
ACTION_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
jobs:
shortint-benchmarks:
name: Execute shortint benchmarks for all operations flavor
runs-on: ${{ github.event.inputs.runner_name }}
if: ${{ !cancelled() }}
strategy:
max-parallel: 1
matrix:
op_flavor: [ default, smart, unchecked ]
steps:
- name: Instance configuration used
run: |
echo "IDs: ${{ inputs.instance_id }}"
echo "AMI: ${{ inputs.instance_image_id }}"
echo "Type: ${{ inputs.instance_type }}"
echo "Request ID: ${{ inputs.request_id }}"
- name: Checkout tfhe-rs repo with tags
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
fetch-depth: 0
- name: Get benchmark details
run: |
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
echo "COMMIT_DATE=$(git --no-pager show -s --format=%cd --date=iso8601-strict ${{ github.sha }})" >> "${GITHUB_ENV}"
echo "COMMIT_HASH=$(git describe --tags --dirty)" >> "${GITHUB_ENV}"
- name: Set up home
# "Install rust" step require root user to have a HOME directory which is not set.
run: |
echo "HOME=/home/ubuntu" >> "${GITHUB_ENV}"
- name: Install rust
uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af
with:
toolchain: nightly
override: true
- name: Checkout Slab repo
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: zama-ai/slab
path: slab
token: ${{ secrets.CONCRETE_ACTIONS_TOKEN }}
- name: Run benchmarks with AVX512
run: |
make AVX512_SUPPORT=ON BENCH_OP_FLAVOR=${{ matrix.op_flavor }} bench_shortint
- name: Parse results
run: |
COMMIT_DATE="$(git --no-pager show -s --format=%cd --date=iso8601-strict ${{ github.sha }})"
COMMIT_HASH="$(git describe --tags --dirty)"
python3 ./ci/benchmark_parser.py target/criterion ${{ env.RESULTS_FILENAME }} \
--database tfhe_rs \
--hardware ${{ inputs.instance_type }} \
--project-version "${COMMIT_HASH}" \
--branch ${{ github.ref_name }} \
--commit-date "${COMMIT_DATE}" \
--bench-date "${{ env.BENCH_DATE }}" \
--walk-subdirs \
--name-suffix avx512 \
--throughput
# This small benchmark needs to be executed only once.
- name: Measure key sizes
if: matrix.op_flavor == 'default'
run: |
make measure_shortint_key_sizes
- name: Parse key sizes results
if: matrix.op_flavor == 'default'
run: |
python3 ./ci/benchmark_parser.py tfhe/shortint_key_sizes.csv ${{ env.RESULTS_FILENAME }} \
--key-sizes \
--append-results
- name: Upload parsed results artifact
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
with:
name: ${{ github.sha }}_shortint_${{ matrix.op_flavor }}
path: ${{ env.RESULTS_FILENAME }}
- name: Send data to Slab
shell: bash
run: |
echo "Computing HMac on results file"
SIGNATURE="$(slab/scripts/hmac_calculator.sh ${{ env.RESULTS_FILENAME }} '${{ secrets.JOB_SECRET }}')"
echo "Sending results to Slab..."
curl -v -k \
-H "Content-Type: application/json" \
-H "X-Slab-Repository: ${{ github.repository }}" \
-H "X-Slab-Command: store_data_v2" \
-H "X-Hub-Signature-256: sha256=${SIGNATURE}" \
-d @${{ env.RESULTS_FILENAME }} \
${{ secrets.SLAB_URL }}
slack-notification:
name: Slack Notification
runs-on: ${{ github.event.inputs.runner_name }}
if: ${{ failure() }}
needs: shortint-benchmarks
steps:
- name: Notify
continue-on-error: true
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
SLACK_ICON: https://pbs.twimg.com/profile_images/1274014582265298945/OjBKP9kn_400x400.png
SLACK_MESSAGE: "Shortint full benchmarks failed. (${{ env.ACTION_RUN_URL }})"
SLACK_USERNAME: ${{ secrets.BOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

View File

@@ -42,13 +42,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout tfhe-rs
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
fetch-depth: 0
- name: Check for file changes
id: changed-files
uses: tj-actions/changed-files@de0eba32790fb9bf87471b32855a30fc8f9d5fc6
uses: tj-actions/changed-files@2f7246cb26e8bb6709b6cbfc1fec7febfe82e96a
with:
files_yaml: |
common_benches:
@@ -85,7 +85,7 @@ jobs:
- .github/workflows/wasm_client_benchmark.yml
- name: Checkout Slab repo
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: zama-ai/slab
path: slab

View File

@@ -0,0 +1,42 @@
# Start all benchmark jobs, including full shortint and integer, on Slab CI bot.
name: Start full suite benchmarks
on:
schedule:
# Job will be triggered each Saturday at 1a.m.
- cron: '0 1 * * 6'
workflow_dispatch:
jobs:
start-benchmarks:
if: ${{ (github.event_name == 'schedule' && github.repository == 'zama-ai/tfhe-rs') || github.event_name == 'workflow_dispatch' }}
strategy:
matrix:
command: [ boolean_bench, shortint_full_bench, integer_full_bench, pbs_bench, wasm_client_bench ]
runs-on: ubuntu-latest
steps:
- name: Checkout tfhe-rs
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
fetch-depth: 0
- name: Checkout Slab repo
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: zama-ai/slab
path: slab
token: ${{ secrets.CONCRETE_ACTIONS_TOKEN }}
- name: Start AWS job in Slab
shell: bash
run: |
echo -n '{"command": "${{ matrix.command }}", "git_ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}' > command.json
SIGNATURE="$(slab/scripts/hmac_calculator.sh command.json '${{ secrets.JOB_SECRET }}')"
curl -v -k \
--fail-with-body \
-H "Content-Type: application/json" \
-H "X-Slab-Repository: ${{ github.repository }}" \
-H "X-Slab-Command: start_aws" \
-H "X-Hub-Signature-256: sha256=${SIGNATURE}" \
-d @command.json \
${{ secrets.SLAB_URL }}

View File

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

View File

@@ -43,7 +43,7 @@ jobs:
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
- name: Checkout tfhe-rs repo with tags
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
fetch-depth: 0
@@ -95,7 +95,7 @@ jobs:
path: ${{ env.RESULTS_FILENAME }}
- name: Checkout Slab repo
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
repository: zama-ai/slab
path: slab
@@ -118,7 +118,7 @@ jobs:
- name: Slack Notification
if: ${{ failure() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
env:
SLACK_COLOR: ${{ job.status }}
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}

View File

@@ -14,6 +14,7 @@ BIG_TESTS_INSTANCE?=FALSE
GEN_KEY_CACHE_MULTI_BIT_ONLY?=FALSE
PARSE_INTEGER_BENCH_CSV_FILE?=tfhe_rs_integer_benches.csv
FAST_TESTS?=FALSE
FAST_BENCH?=FALSE
BENCH_OP_FLAVOR?=DEFAULT
# This is done to avoid forgetting it, we still precise the RUSTFLAGS in the commands to be able to
# copy paste the command in the terminal and change them if required without forgetting the flags
@@ -140,7 +141,7 @@ clippy_tasks:
.PHONY: clippy_all_targets # Run clippy lints on all targets (benches, examples, etc.)
clippy_all_targets:
RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy --all-targets \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer,internal-keycache \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer \
-p tfhe -- --no-deps -D warnings
.PHONY: clippy_all # Run all clippy targets
@@ -198,7 +199,7 @@ build_tfhe_full: install_rs_build_toolchain
.PHONY: build_c_api # Build the C API for boolean, shortint and integer
build_c_api: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) build --profile $(CARGO_PROFILE) \
--features=$(TARGET_ARCH_FEATURE),boolean-c-api,shortint-c-api,high-level-c-api \
--features=$(TARGET_ARCH_FEATURE),boolean-c-api,shortint-c-api,high-level-c-api, \
-p tfhe
.PHONY: build_c_api_experimental_deterministic_fft # Build the C API for boolean, shortint and integer with experimental deterministic FFT
@@ -337,13 +338,22 @@ test_kreyvium: install_rs_build_toolchain
.PHONY: doc # Build rust doc
doc: install_rs_check_toolchain
RUSTDOCFLAGS="--html-in-header katex-header.html -Dwarnings" \
RUSTDOCFLAGS="--html-in-header katex-header.html" \
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" doc \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer --no-deps
.PHONY: docs # Build rust doc alias for doc
docs: doc
.PHONY: lint_doc # Build rust doc with linting enabled
lint_doc: install_rs_check_toolchain
RUSTDOCFLAGS="--html-in-header katex-header.html -Dwarnings" \
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" doc \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer --no-deps
.PHONY: lint_docs # Build rust doc with linting enabled alias for lint_doc
lint_docs: lint_doc
.PHONY: format_doc_latex # Format the documentation latex equations to avoid broken rendering.
format_doc_latex:
cargo xtask format_latex_doc
@@ -408,14 +418,15 @@ no_dbg_log:
.PHONY: bench_integer # Run benchmarks for integer
bench_integer: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" __TFHE_RS_BENCH_OP_FLAVOR=$(BENCH_OP_FLAVOR) \
RUSTFLAGS="$(RUSTFLAGS)" __TFHE_RS_BENCH_OP_FLAVOR=$(BENCH_OP_FLAVOR) __TFHE_RS_FAST_BENCH=$(FAST_BENCH) \
cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
--bench integer-bench \
--features=$(TARGET_ARCH_FEATURE),integer,internal-keycache,$(AVX512_FEATURE) -p tfhe --
.PHONY: bench_integer_multi_bit # Run benchmarks for integer using multi-bit parameters
bench_integer_multi_bit: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" __TFHE_RS_BENCH_TYPE=MULTI_BIT __TFHE_RS_BENCH_OP_FLAVOR=$(BENCH_OP_FLAVOR) \
RUSTFLAGS="$(RUSTFLAGS)" __TFHE_RS_BENCH_TYPE=MULTI_BIT \
__TFHE_RS_BENCH_OP_FLAVOR=$(BENCH_OP_FLAVOR) __TFHE_RS_FAST_BENCH=$(FAST_BENCH) \
cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
--bench integer-bench \
--features=$(TARGET_ARCH_FEATURE),integer,internal-keycache,$(AVX512_FEATURE) -p tfhe --
@@ -427,6 +438,15 @@ bench_shortint: install_rs_check_toolchain
--bench shortint-bench \
--features=$(TARGET_ARCH_FEATURE),shortint,internal-keycache,$(AVX512_FEATURE) -p tfhe
.PHONY: bench_shortint_multi_bit # Run benchmarks for shortint using multi-bit parameters
bench_shortint_multi_bit: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" __TFHE_RS_BENCH_TYPE=MULTI_BIT \
__TFHE_RS_BENCH_OP_FLAVOR=$(BENCH_OP_FLAVOR) \
cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
--bench shortint-bench \
--features=$(TARGET_ARCH_FEATURE),shortint,internal-keycache,$(AVX512_FEATURE) -p tfhe --
.PHONY: bench_boolean # Run benchmarks for boolean
bench_boolean: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
@@ -484,6 +504,12 @@ parse_wasm_benchmarks: install_rs_check_toolchain
--features=$(TARGET_ARCH_FEATURE),shortint,internal-keycache \
-- web_wasm_parallel_tests/test/benchmark_results
.PHONY: write_params_to_file # Gather all crypto parameters into a file with a Sage readable format.
write_params_to_file: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) run --profile $(CARGO_PROFILE) \
--example write_params_to_file \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,internal-keycache
#
# Real use case examples
#
@@ -509,10 +535,10 @@ sha256_bool: install_rs_check_toolchain
--features=$(TARGET_ARCH_FEATURE),boolean
.PHONY: pcc # pcc stands for pre commit checks
pcc: no_tfhe_typo no_dbg_log check_fmt doc clippy_all check_compile_tests
pcc: no_tfhe_typo no_dbg_log check_fmt lint_doc clippy_all check_compile_tests
.PHONY: fpcc # pcc stands for pre commit checks, the f stands for fast
fpcc: no_tfhe_typo no_dbg_log check_fmt doc clippy_fast check_compile_tests
fpcc: no_tfhe_typo no_dbg_log check_fmt lint_doc clippy_fast check_compile_tests
.PHONY: conformance # Automatically fix problems that can be fixed
conformance: fmt

148
README.md
View File

@@ -31,7 +31,9 @@ implementation. The goal is to have a stable, simple, high-performance, and
production-ready library for all the advanced features of TFHE.
## Getting Started
The steps to run a first example are described below.
### Cargo.toml configuration
To use the latest version of `TFHE-rs` in your project, you first need to add it as a dependency in your `Cargo.toml`:
+ For x86_64-based machines running Unix-like OSes:
@@ -57,95 +59,69 @@ 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
## A simple example
Here is a full example:
``` rust
use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint32, FheUint8};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Basic configuration to use homomorphic integers
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
// Key generation
let (client_key, server_keys) = generate_keys(config);
let clear_a = 1344u32;
let clear_b = 5u32;
let clear_c = 7u8;
// Encrypting the input data using the (private) client_key
// FheUint32: Encrypted equivalent to u32
let mut encrypted_a = FheUint32::try_encrypt(clear_a, &client_key)?;
let encrypted_b = FheUint32::try_encrypt(clear_b, &client_key)?;
// FheUint8: Encrypted equivalent to u8
let encrypted_c = FheUint8::try_encrypt(clear_c, &client_key)?;
// On the server side:
set_server_key(server_keys);
// Clear equivalent computations: 1344 * 8 = 10752
let encrypted_res_mul = &encrypted_a * &encrypted_b;
// Clear equivalent computations: 1344 >> 8 = 42
encrypted_a = &encrypted_res_mul >> &encrypted_b;
// Clear equivalent computations: let casted_a = a as u8;
let casted_a: FheUint8 = encrypted_a.cast_into();
// Clear equivalent computations: min(42, 7) = 7
let encrypted_res_min = &casted_a.min(&encrypted_c);
// Operation between clear and encrypted data:
// Clear equivalent computations: 7 & 1 = 1
let encrypted_res = encrypted_res_min & 1_u8;
// Decrypting on the client side:
let clear_res: u8 = encrypted_res.decrypt(&client_key);
assert_eq!(clear_res, 1_u8);
Ok(())
}
```
To run this code, use the following command:
<p align="center"> <code> cargo run --release </code> </p>
Note 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,
eg: `cargo run --release`.
Here is a full example evaluating a Boolean circuit:
```rust
use tfhe::boolean::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys();
// We use the client secret key to encrypt two messages:
let ct_1 = client_key.encrypt(true);
let ct_2 = client_key.encrypt(false);
// We use the server public key to execute a boolean circuit:
// if ((NOT ct_2) NAND (ct_1 AND ct_2)) then (NOT ct_2) else (ct_1 AND ct_2)
let ct_3 = server_key.not(&ct_2);
let ct_4 = server_key.and(&ct_1, &ct_2);
let ct_5 = server_key.nand(&ct_3, &ct_4);
let ct_6 = server_key.mux(&ct_5, &ct_3, &ct_4);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_6);
assert_eq!(output, true);
}
```
Another example of how the library can be used with shortints:
```rust
use tfhe::shortint::prelude::*;
fn main() {
// Generate a set of client/server keys
// with 2 bits of message and 2 bits of carry
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let msg1 = 3;
let msg2 = 2;
// Encrypt two messages using the (private) client key:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
// Homomorphically compute an addition
let ct_add = server_key.unchecked_add(&ct_1, &ct_2);
// Define the Hamming weight function
// f: x -> sum of the bits of x
let f = |x:u64| x.count_ones() as u64;
// Generate the lookup table for the function
let acc = server_key.generate_lookup_table(f);
// Compute the function over the ciphertext using the PBS
let ct_res = server_key.apply_lookup_table(&ct_add, &acc);
// Decrypt the ciphertext using the (private) client key
let output = client_key.decrypt(&ct_res);
assert_eq!(output, f(msg1 + msg2));
}
```
An example using integer:
```rust
use tfhe::integer::gen_keys_radix;
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
fn main() {
// We create keys to create 16 bits integers
// using 8 blocks of 2 bits
let (cks, sks) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2_KS_PBS, 8);
let clear_a = 2382u16;
let clear_b = 29374u16;
let mut a = cks.encrypt(clear_a as u64);
let mut b = cks.encrypt(clear_b as u64);
let encrypted_max = sks.smart_max_parallelized(&mut a, &mut b);
let decrypted_max: u64 = cks.decrypt(&encrypted_max);
assert_eq!(decrypted_max as u16, clear_a.max(clear_b))
}
```
## Contributing

View File

@@ -80,9 +80,9 @@ impl KreyviumStream<FheBool> {
// Initialization of Kreyvium registers: a has the secret key, b the input vector,
// and c a few ones.
let mut a_register = [false; 93].map(|x| FheBool::encrypt_trivial(x));
let mut b_register = [false; 84].map(|x| FheBool::encrypt_trivial(x));
let mut c_register = [false; 111].map(|x| FheBool::encrypt_trivial(x));
let mut a_register = [false; 93].map(FheBool::encrypt_trivial);
let mut b_register = [false; 84].map(FheBool::encrypt_trivial);
let mut c_register = [false; 111].map(FheBool::encrypt_trivial);
for i in 0..93 {
a_register[i] = key[128 - 93 + i].clone();
@@ -99,7 +99,7 @@ impl KreyviumStream<FheBool> {
key.reverse();
iv.reverse();
let iv = iv.map(|x| FheBool::encrypt_trivial(x));
let iv = iv.map(FheBool::encrypt_trivial);
unset_server_key();
KreyviumStream::<FheBool>::new_from_registers(
@@ -149,7 +149,7 @@ where
}
/// Computes one turn of the stream, updating registers and outputting the new bit.
pub fn next(&mut self) -> T {
pub fn next_bool(&mut self) -> T {
match &self.fhe_key {
Some(sk) => set_server_key(sk.clone()),
None => (),

View File

@@ -54,18 +54,15 @@ impl KreyviumStreamByte<u8> {
let mut c_byte_reg = [0u8; 14];
// Copy key bits into a register
for b in 0..12 {
a_byte_reg[b] = key_bytes[b + 4];
}
a_byte_reg.copy_from_slice(&key_bytes[4..]);
// Copy iv bits into a register
for b in 0..11 {
b_byte_reg[b] = iv_bytes[b + 5];
}
b_byte_reg.copy_from_slice(&iv_bytes[5..]);
// Copy a lot of ones in the c register
c_byte_reg[0] = 252;
for b in 1..8 {
c_byte_reg[b] = 255;
}
c_byte_reg[1..8].fill(255);
// Copy iv bits in the c register
c_byte_reg[8] = (iv_bytes[0] << 4) | 31;
for b in 9..14 {
@@ -100,23 +97,22 @@ impl KreyviumStreamByte<FheUint8> {
// Initialization of Kreyvium registers: a has the secret key, b the input vector,
// and c a few ones.
let mut a_byte_reg = [0u8; 12].map(|x| FheUint8::encrypt_trivial(x));
let mut b_byte_reg = [0u8; 11].map(|x| FheUint8::encrypt_trivial(x));
let mut c_byte_reg = [0u8; 14].map(|x| FheUint8::encrypt_trivial(x));
let mut a_byte_reg = [0u8; 12].map(FheUint8::encrypt_trivial);
let mut b_byte_reg = [0u8; 11].map(FheUint8::encrypt_trivial);
let mut c_byte_reg = [0u8; 14].map(FheUint8::encrypt_trivial);
// Copy key bits into a register
for b in 0..12 {
a_byte_reg[b] = key_bytes[b + 4].clone();
}
a_byte_reg.clone_from_slice(&key_bytes[4..]);
// Copy iv bits into a register
for b in 0..11 {
b_byte_reg[b] = FheUint8::encrypt_trivial(iv_bytes[b + 5]);
}
// Copy a lot of ones in the c register
c_byte_reg[0] = FheUint8::encrypt_trivial(252u8);
for b in 1..8 {
c_byte_reg[b] = FheUint8::encrypt_trivial(255u8);
}
c_byte_reg[1..8].fill_with(|| FheUint8::encrypt_trivial(255u8));
// Copy iv bits in the c register
c_byte_reg[8] = FheUint8::encrypt_trivial((&iv_bytes[0] << 4u8) | 31u8);
for b in 9..14 {
@@ -292,6 +288,6 @@ where
impl KreyviumStreamByte<FheUint8> {
pub fn get_server_key(&self) -> &ServerKey {
&self.fhe_key.as_ref().unwrap()
self.fhe_key.as_ref().unwrap()
}
}

View File

@@ -75,7 +75,7 @@ impl KreyviumStreamShortint {
}
/// Computes one turn of the stream, updating registers and outputting the new bit.
pub fn next(&mut self) -> Ciphertext {
pub fn next_ct(&mut self) -> Ciphertext {
let [o, a, b, c] = self.get_output_and_values(0);
self.a.push(a);
@@ -149,7 +149,7 @@ impl KreyviumStreamShortint {
.unchecked_add_assign(&mut new_c, c5);
self.internal_server_key
.unchecked_add_assign(&mut new_c, &temp_b);
self.internal_server_key.clear_carry_assign(&mut new_c);
self.internal_server_key.message_extract_assign(&mut new_c);
new_c
},
|| {

View File

@@ -1,3 +1,4 @@
#[allow(clippy::module_inception)]
mod kreyvium;
pub use kreyvium::KreyviumStream;

View File

@@ -56,7 +56,7 @@ fn get_hexadecimal_string_from_lsb_first_stream(a: Vec<bool>) -> String {
_ => (),
};
}
return hexadecimal;
hexadecimal
}
fn get_hexagonal_string_from_bytes(a: Vec<u8>) -> String {
@@ -65,7 +65,7 @@ fn get_hexagonal_string_from_bytes(a: Vec<u8>) -> String {
for test in a {
hexadecimal.push_str(&format!("{:02X?}", test));
}
return hexadecimal;
hexadecimal
}
fn get_hexagonal_string_from_u64(a: Vec<u64>) -> String {
@@ -73,7 +73,7 @@ fn get_hexagonal_string_from_u64(a: Vec<u64>) -> String {
for test in a {
hexadecimal.push_str(&format!("{:016X?}", test));
}
return hexadecimal;
hexadecimal
}
#[test]
@@ -86,7 +86,7 @@ fn kreyvium_test_1() {
let mut vec = Vec::<bool>::with_capacity(64);
while vec.len() < 64 {
vec.push(kreyvium.next());
vec.push(kreyvium.next_bool());
}
let hexadecimal = get_hexadecimal_string_from_lsb_first_stream(vec);
@@ -105,7 +105,7 @@ fn kreyvium_test_2() {
let mut vec = Vec::<bool>::with_capacity(64);
while vec.len() < 64 {
vec.push(kreyvium.next());
vec.push(kreyvium.next_bool());
}
let hexadecimal = get_hexadecimal_string_from_lsb_first_stream(vec);
@@ -124,7 +124,7 @@ fn kreyvium_test_3() {
let mut vec = Vec::<bool>::with_capacity(64);
while vec.len() < 64 {
vec.push(kreyvium.next());
vec.push(kreyvium.next_bool());
}
let hexadecimal = get_hexadecimal_string_from_lsb_first_stream(vec);
@@ -161,7 +161,7 @@ fn kreyvium_test_4() {
let mut vec = Vec::<bool>::with_capacity(64);
while vec.len() < 64 {
vec.push(kreyvium.next());
vec.push(kreyvium.next_bool());
}
let hexadecimal = get_hexadecimal_string_from_lsb_first_stream(vec);

View File

@@ -1,3 +1,4 @@
#[allow(clippy::module_inception)]
mod static_deque;
pub use static_deque::StaticDeque;
mod static_byte_deque;

View File

@@ -77,7 +77,7 @@ where
}
let byte_next: &T = &self.deque[i / 8 + 1];
return (byte << bit_idx) | (byte_next >> (8 - bit_idx as u8));
(byte << bit_idx) | (byte_next >> (8 - bit_idx))
}
}
@@ -101,7 +101,7 @@ mod tests {
assert!(deque.bit(7) == 0);
// second youngest: 128
assert!(deque.bit(8 + 0) == 0);
assert!(deque.bit(8) == 0);
assert!(deque.bit(8 + 1) == 0);
assert!(deque.bit(8 + 2) == 0);
assert!(deque.bit(8 + 3) == 0);
@@ -111,7 +111,7 @@ mod tests {
assert!(deque.bit(8 + 7) > 0);
// oldest: 64
assert!(deque.bit(16 + 0) == 0);
assert!(deque.bit(16) == 0);
assert!(deque.bit(16 + 1) == 0);
assert!(deque.bit(16 + 2) == 0);
assert!(deque.bit(16 + 3) == 0);

View File

@@ -1,3 +1,4 @@
#[allow(clippy::module_inception)]
mod trivium;
pub use trivium::TriviumStream;

View File

@@ -56,7 +56,7 @@ fn get_hexadecimal_string_from_lsb_first_stream(a: Vec<bool>) -> String {
_ => (),
};
}
return hexadecimal;
hexadecimal
}
fn get_hexagonal_string_from_bytes(a: Vec<u8>) -> String {
@@ -65,7 +65,7 @@ fn get_hexagonal_string_from_bytes(a: Vec<u8>) -> String {
for test in a {
hexadecimal.push_str(&format!("{:02X?}", test));
}
return hexadecimal;
hexadecimal
}
fn get_hexagonal_string_from_u64(a: Vec<u64>) -> String {
@@ -73,7 +73,7 @@ fn get_hexagonal_string_from_u64(a: Vec<u64>) -> String {
for test in a {
hexadecimal.push_str(&format!("{:016X?}", test));
}
return hexadecimal;
hexadecimal
}
#[test]
@@ -89,7 +89,7 @@ fn trivium_test_1() {
let mut vec = Vec::<bool>::with_capacity(512 * 8);
while vec.len() < 512 * 8 {
vec.push(trivium.next());
vec.push(trivium.next_bool());
}
let hexadecimal = get_hexadecimal_string_from_lsb_first_stream(vec);
@@ -114,7 +114,7 @@ fn trivium_test_2() {
let mut vec = Vec::<bool>::with_capacity(512 * 8);
while vec.len() < 512 * 8 {
vec.push(trivium.next());
vec.push(trivium.next_bool());
}
let hexadecimal = get_hexadecimal_string_from_lsb_first_stream(vec);
@@ -139,7 +139,7 @@ fn trivium_test_3() {
let mut vec = Vec::<bool>::with_capacity(512 * 8);
while vec.len() < 512 * 8 {
vec.push(trivium.next());
vec.push(trivium.next_bool());
}
let hexadecimal = get_hexadecimal_string_from_lsb_first_stream(vec);
@@ -182,7 +182,7 @@ fn trivium_test_4() {
let mut vec = Vec::<bool>::with_capacity(131072 * 8);
while vec.len() < 131072 * 8 {
vec.push(trivium.next());
vec.push(trivium.next_bool());
}
let hexadecimal = get_hexadecimal_string_from_lsb_first_stream(vec);

View File

@@ -66,9 +66,9 @@ impl TriviumStream<FheBool> {
// Initialization of Trivium registers: a has the secret key, b the input vector,
// and c a few ones.
let mut a_register = [false; 93].map(|x| FheBool::encrypt_trivial(x));
let mut b_register = [false; 84].map(|x| FheBool::encrypt_trivial(x));
let mut c_register = [false; 111].map(|x| FheBool::encrypt_trivial(x));
let mut a_register = [false; 93].map(FheBool::encrypt_trivial);
let mut b_register = [false; 84].map(FheBool::encrypt_trivial);
let mut c_register = [false; 111].map(FheBool::encrypt_trivial);
for i in 0..80 {
a_register[93 - 80 + i] = key[i].clone();
@@ -121,7 +121,7 @@ where
}
/// Computes one turn of the stream, updating registers and outputting the new bit.
pub fn next(&mut self) -> T {
pub fn next_bool(&mut self) -> T {
match &self.fhe_key {
Some(sk) => set_server_key(sk.clone()),
None => (),

View File

@@ -81,9 +81,9 @@ impl TriviumStreamByte<FheUint8> {
// Initialization of Trivium registers: a has the secret key, b the input vector,
// and c a few ones.
let mut a_byte_reg = [0u8; 12].map(|x| FheUint8::encrypt_trivial(x));
let mut b_byte_reg = [0u8; 11].map(|x| FheUint8::encrypt_trivial(x));
let mut c_byte_reg = [0u8; 14].map(|x| FheUint8::encrypt_trivial(x));
let mut a_byte_reg = [0u8; 12].map(FheUint8::encrypt_trivial);
let mut b_byte_reg = [0u8; 11].map(FheUint8::encrypt_trivial);
let mut c_byte_reg = [0u8; 14].map(FheUint8::encrypt_trivial);
for i in 0..10 {
a_byte_reg[12 - 10 + i] = key[i].clone();
@@ -236,6 +236,6 @@ where
impl TriviumStreamByte<FheUint8> {
pub fn get_server_key(&self) -> &ServerKey {
&self.fhe_key.as_ref().unwrap()
self.fhe_key.as_ref().unwrap()
}
}

View File

@@ -63,7 +63,7 @@ impl TriviumStreamShortint {
}
/// Computes one turn of the stream, updating registers and outputting the new bit.
pub fn next(&mut self) -> Ciphertext {
pub fn next_ct(&mut self) -> Ciphertext {
let [o, a, b, c] = self.get_output_and_values(0);
self.a.push(a);
@@ -113,7 +113,7 @@ impl TriviumStreamShortint {
.unchecked_add_assign(&mut new_a, a5);
self.internal_server_key
.unchecked_add_assign(&mut new_a, &temp_c);
self.internal_server_key.clear_carry_assign(&mut new_a);
self.internal_server_key.message_extract_assign(&mut new_a);
new_a
},
|| {
@@ -122,7 +122,7 @@ impl TriviumStreamShortint {
.unchecked_add_assign(&mut new_b, b5);
self.internal_server_key
.unchecked_add_assign(&mut new_b, &temp_a);
self.internal_server_key.clear_carry_assign(&mut new_b);
self.internal_server_key.message_extract_assign(&mut new_b);
new_b
},
)
@@ -135,7 +135,7 @@ impl TriviumStreamShortint {
.unchecked_add_assign(&mut new_c, c5);
self.internal_server_key
.unchecked_add_assign(&mut new_c, &temp_b);
self.internal_server_key.clear_carry_assign(&mut new_c);
self.internal_server_key.message_extract_assign(&mut new_c);
new_c
},
|| {

View File

@@ -108,12 +108,25 @@ def recursive_parse(directory, walk_subdirs=False, name_suffix="", compute_throu
)
)
# This is a special case where PBS are blasted as vector LWE ciphertext with
# variable length to saturate the machine. To get the actual throughput we need to
# multiply by the length of the vector.
if "PBS_throughput" in test_name and "chunk" in test_name:
try:
multiplier = int(test_name.split("chunk")[0].split("_")[-1])
except ValueError:
parsing_failures.append((full_name,
"failed to extract throughput multiplier"))
continue
else:
multiplier = 1
if stat_name == "mean" and compute_throughput:
test_suffix = "ops-per-sec"
test_name_parts.append(test_suffix)
result_values.append(
_create_point(
compute_ops_per_second(value),
multiplier * compute_ops_per_second(value),
"_".join(test_name_parts),
bench_class,
"throughput",
@@ -129,7 +142,7 @@ def recursive_parse(directory, walk_subdirs=False, name_suffix="", compute_throu
test_name_parts.append(test_suffix)
result_values.append(
_create_point(
compute_ops_per_dollar(value, hardware_hourly_cost),
multiplier * compute_ops_per_dollar(value, hardware_hourly_cost),
"_".join(test_name_parts),
bench_class,
"throughput",

75
ci/lattice_estimator.sage Executable file
View File

@@ -0,0 +1,75 @@
"""
lattice_estimator
-----------------
Test cryptographic parameters set against several attacks to estimate their security level.
"""
import pathlib
import sys
sys.path.insert(1, 'lattice-estimator')
from estimator import *
model = RC.BDGL16
def check_security(filename):
"""
Run lattice estimator to determine if a parameters set is secure or not.
:param filename: name of the file containing parameters set
:return: :class:`list` of parameters to update
"""
filepath = pathlib.Path("ci", filename)
load(filepath)
print(f"Parsing parameters in {filepath}")
to_update = []
for param in all_params:
if param.tag.startswith("TFHE_LIB_PARAMETERS"):
# This third-party parameters set is known to be less secure, just skip the analysis.
continue
print(f"\t{param.tag}...\t", end= "")
try:
# The lattice estimator is not able to manage such large dimension.
# If we have the security for smaller `n` then we have security for larger ones.
if param.n == 32768:
param = param.updated(n = 16384)
usvp_level = LWE.primal_usvp(param, red_cost_model = model)
dual_level = LWE.dual_hybrid(param, red_cost_model = model)
estimator_level = log(min(usvp_level["rop"], dual_level["rop"]),2 )
if estimator_level < 127:
print("FAIL")
reason = f"attained security level = {estimator_level} bits target is 128 bits"
to_update.append((param, reason))
continue
except Exception as err:
print("FAIL")
to_update.append((param, f"{repr(err)}"))
else:
print("OK")
return to_update
if __name__ == "__main__":
params_to_update = []
for params_filename in ("boolean_parameters_lattice_estimator.sage",
"shortint_classic_parameters_lattice_estimator.sage",
"shortint_multi_bit_parameters_lattice_estimator.sage"):
params_to_update.extend(check_security(params_filename))
if params_to_update:
print("Some parameters need update")
print("----------------------------")
for param, reason in params_to_update:
print(f"[{param.tag}] reason: {reason} (param)")
sys.exit(int(1)) # Explicit conversion is needed to make this call work
else:
print("All parameters passed the security check")

View File

@@ -21,8 +21,15 @@ def main(args):
split = bench_function_id.split("::")
(_, function_name, parameter_set, bits) = split
(bits, _) = bits.split("_")
bits = int(bits)
if "_scalar_" in bits:
(bits, scalar) = bits.split("_bits_scalar_")
bits = int(bits)
scalar = int(scalar)
else:
(bits, _) = bits.split("_")
bits = int(bits)
scalar = None
estimate_mean_ms = estimate_data["mean"]["point_estimate"] / 1000000
estimate_lower_bound_ms = (
@@ -37,6 +44,7 @@ def main(args):
function_name,
parameter_set,
bits,
scalar,
estimate_mean_ms,
estimate_lower_bound_ms,
estimate_upper_bound_ms,
@@ -51,7 +59,7 @@ def main(args):
with open(output_file, "w", encoding="utf-8") as output:
output.write(
"function_name,parameter_set,bits,mean_ms,"
"function_name,parameter_set,bits,scalar,mean_ms,"
"confidence_interval_lower_bound_ms,confidence_interval_upper_bound_ms\n"
)
# Sort by func_name, bit width and then parameters
@@ -62,12 +70,13 @@ def main(args):
function_name,
parameter_set,
bits,
scalar,
estimate_mean_ms,
estimate_lower_bound_ms,
estimate_upper_bound_ms,
) = dat
output.write(
f"{function_name},{parameter_set},{bits},{estimate_mean_ms},"
f"{function_name},{parameter_set},{bits},{scalar},{estimate_mean_ms},"
f"{estimate_lower_bound_ms},{estimate_upper_bound_ms}\n"
)

View File

@@ -38,6 +38,11 @@ workflow = "aws_tfhe_fast_tests.yml"
profile = "cpu-big"
check_run_name = "CPU AWS Fast Tests"
[command.integer_full_bench]
workflow = "integer_full_benchmark.yml"
profile = "bench"
check_run_name = "Integer CPU AWS Benchmarks Full Suite"
[command.integer_bench]
workflow = "integer_benchmark.yml"
profile = "bench"
@@ -48,6 +53,11 @@ workflow = "integer_multi_bit_benchmark.yml"
profile = "bench"
check_run_name = "Integer multi bit CPU AWS Benchmarks"
[command.shortint_full_bench]
workflow = "shortint_full_benchmark.yml"
profile = "bench"
check_run_name = "Shortint CPU AWS Benchmarks Full Suite"
[command.shortint_bench]
workflow = "shortint_benchmark.yml"
profile = "bench"

View File

@@ -142,7 +142,7 @@ and not test(/.*default_add_sequence_multi_thread_param_message_3_carry_3_ks_pbs
fi
num_cpu_threads="$(${nproc_bin})"
num_threads=$((num_cpu_threads * 2 / 3))
num_threads=$((num_cpu_threads * 1 / 2))
cargo "${RUST_TOOLCHAIN}" nextest run \
--tests \
--cargo-profile "${cargo_profile}" \

View File

@@ -1,6 +1,6 @@
[package]
name = "tfhe"
version = "0.3.0"
version = "0.4.0"
edition = "2021"
readme = "../README.md"
keywords = ["fully", "homomorphic", "encryption", "fhe", "cryptography"]
@@ -29,7 +29,9 @@ lazy_static = { version = "1.4.0" }
criterion = "0.4.0"
doc-comment = "0.3.3"
serde_json = "1.0.94"
clap = { version = "4.2.7", features = ["derive"] }
# clap has to be pinned as its minimum supported rust version
# changes often between minor releases, which breaks our CI
clap = { version = "=4.2.7", features = ["derive"] }
# Used in user documentation
bincode = "1.3.3"
fs2 = { version = "0.4.3" }
@@ -222,6 +224,11 @@ name = "micro_bench_and"
path = "examples/utilities/micro_bench_and.rs"
required-features = ["boolean"]
[[example]]
name = "write_params_to_file"
path = "examples/utilities/params_to_file.rs"
required-features = ["boolean", "shortint", "internal-keycache"]
# Real use-case examples
[[example]]

View File

@@ -4,14 +4,21 @@ use crate::utilities::{write_to_json, CryptoParametersRecord, OperatorType};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use tfhe::boolean::client_key::ClientKey;
use tfhe::boolean::parameters::{BooleanParameters, DEFAULT_PARAMETERS, TFHE_LIB_PARAMETERS};
use tfhe::boolean::parameters::{
BooleanParameters, DEFAULT_PARAMETERS, DEFAULT_PARAMETERS_KS_PBS,
PARAMETERS_ERROR_PROB_2_POW_MINUS_165, PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS,
TFHE_LIB_PARAMETERS,
};
use tfhe::boolean::prelude::BinaryBooleanGates;
use tfhe::boolean::server_key::ServerKey;
criterion_group!(
gates_benches,
bench_default_parameters,
bench_tfhe_lib_parameters
bench_default_parameters_ks_pbs,
bench_low_prob_parameters,
bench_low_prob_parameters_ks_pbs,
bench_tfhe_lib_parameters,
);
criterion_main!(gates_benches);
@@ -79,6 +86,26 @@ fn bench_default_parameters(c: &mut Criterion) {
benchs(c, DEFAULT_PARAMETERS, "DEFAULT_PARAMETERS");
}
fn bench_tfhe_lib_parameters(c: &mut Criterion) {
benchs(c, TFHE_LIB_PARAMETERS, "TFHE_LIB_PARAMETERS");
fn bench_default_parameters_ks_pbs(c: &mut Criterion) {
benchs(c, DEFAULT_PARAMETERS_KS_PBS, "DEFAULT_PARAMETERS_KS_PBS");
}
fn bench_low_prob_parameters(c: &mut Criterion) {
benchs(
c,
PARAMETERS_ERROR_PROB_2_POW_MINUS_165,
"PARAMETERS_ERROR_PROB_2_POW_MINUS_165",
);
}
fn bench_low_prob_parameters_ks_pbs(c: &mut Criterion) {
benchs(
c,
PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS,
"PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS",
);
}
fn bench_tfhe_lib_parameters(c: &mut Criterion) {
benchs(c, TFHE_LIB_PARAMETERS, " TFHE_LIB_PARAMETERS");
}

View File

@@ -5,13 +5,15 @@ use rayon::prelude::*;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use serde::Serialize;
use tfhe::boolean::parameters::{BooleanParameters, DEFAULT_PARAMETERS, TFHE_LIB_PARAMETERS};
use tfhe::boolean::parameters::{
BooleanParameters, DEFAULT_PARAMETERS, PARAMETERS_ERROR_PROB_2_POW_MINUS_165,
};
use tfhe::core_crypto::prelude::*;
use tfhe::shortint::keycache::NamedParam;
use tfhe::keycache::NamedParam;
use tfhe::shortint::parameters::*;
use tfhe::shortint::ClassicPBSParameters;
const SHORTINT_BENCH_PARAMS: [ClassicPBSParameters; 15] = [
const SHORTINT_BENCH_PARAMS: [ClassicPBSParameters; 19] = [
PARAM_MESSAGE_1_CARRY_0_KS_PBS,
PARAM_MESSAGE_1_CARRY_1_KS_PBS,
PARAM_MESSAGE_2_CARRY_0_KS_PBS,
@@ -27,11 +29,18 @@ const SHORTINT_BENCH_PARAMS: [ClassicPBSParameters; 15] = [
PARAM_MESSAGE_6_CARRY_0_KS_PBS,
PARAM_MESSAGE_7_CARRY_0_KS_PBS,
PARAM_MESSAGE_8_CARRY_0_KS_PBS,
PARAM_MESSAGE_1_CARRY_1_PBS_KS,
PARAM_MESSAGE_2_CARRY_2_PBS_KS,
PARAM_MESSAGE_3_CARRY_3_PBS_KS,
PARAM_MESSAGE_4_CARRY_4_PBS_KS,
];
const BOOLEAN_BENCH_PARAMS: [(&str, BooleanParameters); 2] = [
("BOOLEAN_DEFAULT_PARAMS", DEFAULT_PARAMETERS),
("BOOLEAN_TFHE_LIB_PARAMS", TFHE_LIB_PARAMETERS),
(
"BOOLEAN_TFHE_LIB_PARAMS",
PARAMETERS_ERROR_PROB_2_POW_MINUS_165,
),
];
criterion_group!(
@@ -469,8 +478,6 @@ fn pbs_throughput<Scalar: UnsignedTorus + CastInto<usize> + Sync + Send + Serial
params.ciphertext_modulus.unwrap(),
);
let lwe_vec = lwe_vec;
let fft = Fft::new(params.polynomial_size.unwrap());
let fft = fft.as_view();

View File

@@ -8,11 +8,14 @@ use std::env;
use criterion::{criterion_group, Criterion};
use itertools::iproduct;
use rand::prelude::*;
use rand::Rng;
use std::vec::IntoIter;
use tfhe::integer::keycache::KEY_CACHE;
use tfhe::integer::{RadixCiphertext, ServerKey};
use tfhe::shortint::keycache::NamedParam;
use tfhe::keycache::NamedParam;
use tfhe::integer::U256;
#[allow(unused_imports)]
use tfhe::shortint::parameters::{
@@ -20,6 +23,20 @@ use tfhe::shortint::parameters::{
PARAM_MESSAGE_4_CARRY_4_KS_PBS, PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_2_KS_PBS,
};
/// The type used to hold scalar values
/// It must be as big as the largest bit size tested
type ScalarType = U256;
const FAST_BENCH_BIT_SIZES: [usize; 1] = [32];
const BENCH_BIT_SIZES: [usize; 7] = [8, 16, 32, 40, 64, 128, 256];
fn gen_random_u256(rng: &mut ThreadRng) -> U256 {
let clearlow = rng.gen::<u128>();
let clearhigh = rng.gen::<u128>();
tfhe::integer::U256::from((clearlow, clearhigh))
}
/// An iterator that yields a succession of combinations
/// of parameters and a num_block to achieve a certain bit_size ciphertext
/// in radix decomposition
@@ -35,9 +52,19 @@ impl Default for ParamsAndNumBlocksIter {
Err(_) => false,
};
let is_fast_bench = match env::var("__TFHE_RS_FAST_BENCH") {
Ok(val) => val.to_lowercase() == "true",
Err(_) => false,
};
let bit_sizes = if is_fast_bench {
FAST_BENCH_BIT_SIZES.to_vec()
} else {
BENCH_BIT_SIZES.to_vec()
};
if is_multi_bit {
let params = vec![PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_2_KS_PBS.into()];
let bit_sizes = vec![8, 16, 32, 40, 64];
let params_and_bit_sizes = iproduct!(params, bit_sizes);
Self {
params_and_bit_sizes,
@@ -50,7 +77,6 @@ impl Default for ParamsAndNumBlocksIter {
// PARAM_MESSAGE_3_CARRY_3_KS_PBS.into(),
// PARAM_MESSAGE_4_CARRY_4_KS_PBS.into(),
];
let bit_sizes = vec![8, 16, 32, 40, 64, 128, 256];
let params_and_bit_sizes = iproduct!(params, bit_sizes);
Self {
params_and_bit_sizes,
@@ -95,23 +121,17 @@ fn bench_server_key_binary_function_dirty_inputs<F>(
let (cks, sks) = KEY_CACHE.get_from_params(param);
let encrypt_two_values = || {
let clearlow = rng.gen::<u128>();
let clearhigh = rng.gen::<u128>();
let clear_0 = tfhe::integer::U256::from((clearlow, clearhigh));
let clear_0 = gen_random_u256(&mut rng);
let mut ct_0 = cks.encrypt_radix(clear_0, num_block);
let clearlow = rng.gen::<u128>();
let clearhigh = rng.gen::<u128>();
let clear_1 = tfhe::integer::U256::from((clearlow, clearhigh));
let clear_1 = gen_random_u256(&mut rng);
let mut ct_1 = cks.encrypt_radix(clear_1, num_block);
// Raise the degree, so as to ensure worst case path in operations
let mut carry_mod = param.carry_modulus().0;
while carry_mod > 0 {
// Raise the degree, so as to ensure worst case path in operations
let clearlow = rng.gen::<u128>();
let clearhigh = rng.gen::<u128>();
let clear_2 = tfhe::integer::U256::from((clearlow, clearhigh));
let clear_2 = gen_random_u256(&mut rng);
let ct_2 = cks.encrypt_radix(clear_2, num_block);
sks.unchecked_add_assign(&mut ct_0, &ct_2);
sks.unchecked_add_assign(&mut ct_1, &ct_2);
@@ -169,14 +189,10 @@ fn bench_server_key_binary_function_clean_inputs<F>(
let (cks, sks) = KEY_CACHE.get_from_params(param);
let encrypt_two_values = || {
let clearlow = rng.gen::<u128>();
let clearhigh = rng.gen::<u128>();
let clear_0 = tfhe::integer::U256::from((clearlow, clearhigh));
let clear_0 = gen_random_u256(&mut rng);
let ct_0 = cks.encrypt_radix(clear_0, num_block);
let clearlow = rng.gen::<u128>();
let clearhigh = rng.gen::<u128>();
let clear_1 = tfhe::integer::U256::from((clearlow, clearhigh));
let clear_1 = gen_random_u256(&mut rng);
let ct_1 = cks.encrypt_radix(clear_1, num_block);
(ct_0, ct_1)
@@ -230,20 +246,14 @@ fn bench_server_key_unary_function_dirty_inputs<F>(
let (cks, sks) = KEY_CACHE.get_from_params(param);
let encrypt_one_value = || {
let clearlow = rng.gen::<u128>();
let clearhigh = rng.gen::<u128>();
let clear_0 = tfhe::integer::U256::from((clearlow, clearhigh));
let clear_0 = gen_random_u256(&mut rng);
let mut ct_0 = cks.encrypt_radix(clear_0, num_block);
// Raise the degree, so as to ensure worst case path in operations
let mut carry_mod = param.carry_modulus().0;
while carry_mod > 0 {
// Raise the degree, so as to ensure worst case path in operations
let clearlow = rng.gen::<u128>();
let clearhigh = rng.gen::<u128>();
let clear_2 = tfhe::integer::U256::from((clearlow, clearhigh));
let clear_2 = gen_random_u256(&mut rng);
let ct_2 = cks.encrypt_radix(clear_2, num_block);
sks.unchecked_add_assign(&mut ct_0, &ct_2);
@@ -301,10 +311,7 @@ fn bench_server_key_unary_function_clean_inputs<F>(
let (cks, sks) = KEY_CACHE.get_from_params(param);
let encrypt_one_value = || {
let clearlow = rng.gen::<u128>();
let clearhigh = rng.gen::<u128>();
let clear_0 = tfhe::integer::U256::from((clearlow, clearhigh));
let clear_0 = gen_random_u256(&mut rng);
cks.encrypt_radix(clear_0, num_block)
};
@@ -332,13 +339,15 @@ fn bench_server_key_unary_function_clean_inputs<F>(
bench_group.finish()
}
fn bench_server_key_binary_scalar_function_dirty_inputs<F>(
fn bench_server_key_binary_scalar_function_dirty_inputs<F, G>(
c: &mut Criterion,
bench_name: &str,
display_name: &str,
binary_op: F,
rng_func: G,
) where
F: Fn(&ServerKey, &mut RadixCiphertext, u64),
F: Fn(&ServerKey, &mut RadixCiphertext, ScalarType),
G: Fn(&mut ThreadRng, usize) -> ScalarType,
{
let mut bench_group = c.benchmark_group(bench_name);
bench_group
@@ -349,15 +358,14 @@ fn bench_server_key_binary_scalar_function_dirty_inputs<F>(
for (param, num_block, bit_size) in ParamsAndNumBlocksIter::default() {
let param_name = param.name();
let max_value_for_bit_size = ScalarType::MAX >> (ScalarType::BITS as usize - bit_size);
let bench_id = format!("{bench_name}::{param_name}::{bit_size}_bits");
bench_group.bench_function(&bench_id, |b| {
let (cks, sks) = KEY_CACHE.get_from_params(param);
let encrypt_one_value = || {
let clearlow = rng.gen::<u128>();
let clearhigh = rng.gen::<u128>();
let clear_0 = tfhe::integer::U256::from((clearlow, clearhigh));
let clear_0 = gen_random_u256(&mut rng);
let mut ct_0 = cks.encrypt_radix(clear_0, num_block);
// Raise the degree, so as to ensure worst case path in operations
@@ -373,7 +381,7 @@ fn bench_server_key_binary_scalar_function_dirty_inputs<F>(
carry_mod -= 1;
}
let clear_1 = rng.gen::<u64>();
let clear_1 = rng_func(&mut rng, bit_size) & max_value_for_bit_size;
(ct_0, clear_1)
};
@@ -401,13 +409,15 @@ fn bench_server_key_binary_scalar_function_dirty_inputs<F>(
bench_group.finish()
}
fn bench_server_key_binary_scalar_function_clean_inputs<F>(
fn bench_server_key_binary_scalar_function_clean_inputs<F, G>(
c: &mut Criterion,
bench_name: &str,
display_name: &str,
binary_op: F,
rng_func: G,
) where
F: Fn(&ServerKey, &mut RadixCiphertext, u64),
F: Fn(&ServerKey, &mut RadixCiphertext, ScalarType),
G: Fn(&mut ThreadRng, usize) -> ScalarType,
{
let mut bench_group = c.benchmark_group(bench_name);
bench_group
@@ -415,6 +425,91 @@ fn bench_server_key_binary_scalar_function_clean_inputs<F>(
.measurement_time(std::time::Duration::from_secs(60));
let mut rng = rand::thread_rng();
for (param, num_block, bit_size) in ParamsAndNumBlocksIter::default() {
if bit_size > ScalarType::BITS as usize {
break;
}
let param_name = param.name();
let max_value_for_bit_size = ScalarType::MAX >> (ScalarType::BITS as usize - bit_size);
let bench_id = format!("{bench_name}::{param_name}::{bit_size}_bits_scalar_{bit_size}");
bench_group.bench_function(&bench_id, |b| {
let (cks, sks) = KEY_CACHE.get_from_params(param);
let encrypt_one_value = || {
let clear_0 = gen_random_u256(&mut rng);
let ct_0 = cks.encrypt_radix(clear_0, num_block);
let clear_1 = rng_func(&mut rng, bit_size) & max_value_for_bit_size;
(ct_0, clear_1)
};
b.iter_batched(
encrypt_one_value,
|(mut ct_0, clear_1)| {
binary_op(&sks, &mut ct_0, clear_1);
},
criterion::BatchSize::SmallInput,
)
});
write_to_json::<u64, _>(
&bench_id,
param,
param.name(),
display_name,
&OperatorType::Atomic,
bit_size as u32,
vec![param.message_modulus().0.ilog2(); num_block],
);
}
bench_group.finish()
}
// Functions used to apply different way of selecting a scalar based on the context.
fn default_scalar(rng: &mut ThreadRng, _clear_bit_size: usize) -> ScalarType {
gen_random_u256(rng)
}
fn shift_scalar(_rng: &mut ThreadRng, _clear_bit_size: usize) -> ScalarType {
// Shifting by one is the worst case scenario.
ScalarType::ONE
}
fn mul_scalar(rng: &mut ThreadRng, _clear_bit_size: usize) -> ScalarType {
loop {
let scalar = gen_random_u256(rng);
// If scalar is power of two, it is just a shit, which is an happy path.
if !scalar.is_power_of_two() {
return scalar;
}
}
}
fn div_scalar(rng: &mut ThreadRng, clear_bit_size: usize) -> ScalarType {
loop {
let scalar = gen_random_u256(rng);
let max_for_bit_size = ScalarType::MAX >> (ScalarType::BITS as usize - clear_bit_size);
let scalar = scalar & max_for_bit_size;
if scalar != ScalarType::ZERO {
return scalar;
}
}
}
fn if_then_else_parallelized(c: &mut Criterion) {
let bench_name = "integer::if_then_else_parallelized";
let display_name = "if_then_else";
let mut bench_group = c.benchmark_group(bench_name);
bench_group
.sample_size(15)
.measurement_time(std::time::Duration::from_secs(60));
let mut rng = rand::thread_rng();
for (param, num_block, bit_size) in ParamsAndNumBlocksIter::default() {
let param_name = param.name();
@@ -422,22 +517,22 @@ fn bench_server_key_binary_scalar_function_clean_inputs<F>(
bench_group.bench_function(&bench_id, |b| {
let (cks, sks) = KEY_CACHE.get_from_params(param);
let encrypt_one_value = || {
let clearlow = rng.gen::<u128>();
let clearhigh = rng.gen::<u128>();
let clear_0 = tfhe::integer::U256::from((clearlow, clearhigh));
let encrypt_tree_values = || {
let clear_0 = gen_random_u256(&mut rng);
let ct_0 = cks.encrypt_radix(clear_0, num_block);
let clear_1 = rng.gen::<u64>();
let clear_1 = gen_random_u256(&mut rng);
let ct_1 = cks.encrypt_radix(clear_1, num_block);
(ct_0, clear_1)
let cond = sks.create_trivial_radix(rng.gen_bool(0.5) as u64, num_block);
(cond, ct_0, ct_1)
};
b.iter_batched(
encrypt_one_value,
|(mut ct_0, clear_1)| {
binary_op(&sks, &mut ct_0, clear_1);
encrypt_tree_values,
|(condition, true_ct, false_ct)| {
sks.if_then_else_parallelized(&condition, &true_ct, &false_ct)
},
criterion::BatchSize::SmallInput,
)
@@ -462,7 +557,7 @@ macro_rules! define_server_key_bench_unary_fn (
fn $server_key_method(c: &mut Criterion) {
bench_server_key_unary_function_dirty_inputs(
c,
concat!("ServerKey::", stringify!($server_key_method)),
concat!("integer::", stringify!($server_key_method)),
stringify!($name),
|server_key, lhs| {
server_key.$server_key_method(lhs);
@@ -476,7 +571,7 @@ macro_rules! define_server_key_bench_unary_default_fn (
fn $server_key_method(c: &mut Criterion) {
bench_server_key_unary_function_clean_inputs(
c,
concat!("ServerKey::", stringify!($server_key_method)),
concat!("integer::", stringify!($server_key_method)),
stringify!($name),
|server_key, lhs| {
server_key.$server_key_method(lhs);
@@ -490,7 +585,7 @@ macro_rules! define_server_key_bench_fn (
fn $server_key_method(c: &mut Criterion) {
bench_server_key_binary_function_dirty_inputs(
c,
concat!("ServerKey::", stringify!($server_key_method)),
concat!("integer::", stringify!($server_key_method)),
stringify!($name),
|server_key, lhs, rhs| {
server_key.$server_key_method(lhs, rhs);
@@ -504,7 +599,7 @@ macro_rules! define_server_key_bench_default_fn (
fn $server_key_method(c: &mut Criterion) {
bench_server_key_binary_function_clean_inputs(
c,
concat!("ServerKey::", stringify!($server_key_method)),
concat!("integer::", stringify!($server_key_method)),
stringify!($name),
|server_key, lhs, rhs| {
server_key.$server_key_method(lhs, rhs);
@@ -514,29 +609,33 @@ macro_rules! define_server_key_bench_default_fn (
);
macro_rules! define_server_key_bench_scalar_fn (
(method_name: $server_key_method:ident, display_name:$name:ident) => {
(method_name: $server_key_method:ident, display_name:$name:ident, rng_func:$($rng_fn:tt)*) => {
fn $server_key_method(c: &mut Criterion) {
bench_server_key_binary_scalar_function_dirty_inputs(
c,
concat!("ServerKey::", stringify!($server_key_method)),
concat!("integer::", stringify!($server_key_method)),
stringify!($name),
|server_key, lhs, rhs| {
server_key.$server_key_method(lhs, rhs);
})
},
$($rng_fn)*
)
}
}
);
macro_rules! define_server_key_bench_scalar_default_fn (
(method_name: $server_key_method:ident, display_name:$name:ident) => {
(method_name: $server_key_method:ident, display_name:$name:ident, rng_func:$($rng_fn:tt)*) => {
fn $server_key_method(c: &mut Criterion) {
bench_server_key_binary_scalar_function_clean_inputs(
c,
concat!("ServerKey::", stringify!($server_key_method)),
concat!("integer::", stringify!($server_key_method)),
stringify!($name),
|server_key, lhs, rhs| {
server_key.$server_key_method(lhs, rhs);
})
},
$($rng_fn)*
)
}
}
);
@@ -558,96 +657,167 @@ define_server_key_bench_fn!(method_name: smart_bitor_parallelized, display_name:
define_server_key_bench_default_fn!(method_name: add_parallelized, display_name: add);
define_server_key_bench_default_fn!(method_name: sub_parallelized, display_name: sub);
define_server_key_bench_default_fn!(method_name: mul_parallelized, display_name: mul);
define_server_key_bench_default_fn!(method_name: div_parallelized, display_name: div);
define_server_key_bench_default_fn!(method_name: rem_parallelized, display_name: modulo);
define_server_key_bench_default_fn!(method_name: bitand_parallelized, display_name: bitand);
define_server_key_bench_default_fn!(method_name: bitxor_parallelized, display_name: bitxor);
define_server_key_bench_default_fn!(method_name: bitor_parallelized, display_name: bitor);
define_server_key_bench_unary_default_fn!(method_name: bitnot_parallelized, display_name: bitnot);
define_server_key_bench_fn!(method_name: unchecked_add, display_name: add);
define_server_key_bench_fn!(method_name: unchecked_sub, display_name: sub);
define_server_key_bench_fn!(method_name: unchecked_mul, display_name: mul);
define_server_key_bench_fn!(method_name: unchecked_bitand, display_name: bitand);
define_server_key_bench_fn!(method_name: unchecked_bitor, display_name: bitor);
define_server_key_bench_fn!(method_name: unchecked_bitxor, display_name: bitxor);
define_server_key_bench_default_fn!(method_name: unchecked_add, display_name: add);
define_server_key_bench_default_fn!(method_name: unchecked_sub, display_name: sub);
define_server_key_bench_default_fn!(method_name: unchecked_mul, display_name: mul);
define_server_key_bench_default_fn!(method_name: unchecked_bitand, display_name: bitand);
define_server_key_bench_default_fn!(method_name: unchecked_bitor, display_name: bitor);
define_server_key_bench_default_fn!(method_name: unchecked_bitxor, display_name: bitxor);
define_server_key_bench_fn!(method_name: unchecked_mul_parallelized, display_name: mul);
define_server_key_bench_fn!(
define_server_key_bench_default_fn!(method_name: unchecked_mul_parallelized, display_name: mul);
define_server_key_bench_default_fn!(
method_name: unchecked_bitand_parallelized,
display_name: bitand
);
define_server_key_bench_fn!(
define_server_key_bench_default_fn!(
method_name: unchecked_bitor_parallelized,
display_name: bitor
);
define_server_key_bench_fn!(
define_server_key_bench_default_fn!(
method_name: unchecked_bitxor_parallelized,
display_name: bitxor
);
define_server_key_bench_scalar_fn!(method_name: smart_scalar_add, display_name: add);
define_server_key_bench_scalar_fn!(method_name: smart_scalar_sub, display_name: sub);
define_server_key_bench_scalar_fn!(method_name: smart_scalar_mul, display_name: mul);
define_server_key_bench_scalar_fn!(
method_name: smart_scalar_add,
display_name: add,
rng_func: default_scalar
);
define_server_key_bench_scalar_fn!(
method_name: smart_scalar_sub,
display_name: sub,
rng_func: default_scalar
);
define_server_key_bench_scalar_fn!(
method_name: smart_scalar_mul,
display_name: mul,
rng_func: mul_scalar
);
define_server_key_bench_scalar_fn!(
method_name: smart_scalar_add_parallelized,
display_name: add
display_name: add,
rng_func: default_scalar
);
define_server_key_bench_scalar_fn!(
method_name: smart_scalar_sub_parallelized,
display_name: sub
display_name: sub,
rng_func: default_scalar,
);
define_server_key_bench_scalar_fn!(
method_name: smart_scalar_mul_parallelized,
display_name: mul
display_name: mul,
rng_func: mul_scalar
);
define_server_key_bench_scalar_default_fn!(method_name: scalar_add_parallelized, display_name: add);
define_server_key_bench_scalar_default_fn!(method_name: scalar_sub_parallelized, display_name: sub);
define_server_key_bench_scalar_default_fn!(method_name: scalar_mul_parallelized, display_name: mul);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_add_parallelized,
display_name: add,
rng_func: default_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_sub_parallelized,
display_name: sub,
rng_func: default_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_mul_parallelized,
display_name: mul,
rng_func: mul_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_div_parallelized,
display_name: div,
rng_func: div_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_rem_parallelized,
display_name: modulo,
rng_func: div_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_left_shift_parallelized,
display_name: left_shift
display_name: left_shift,
rng_func: shift_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_right_shift_parallelized,
display_name: right_shift
display_name: right_shift,
rng_func: shift_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_rotate_left_parallelized,
display_name: rotate_left,
rng_func: shift_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_rotate_right_parallelized,
display_name: rotate_right,
rng_func: shift_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_eq_parallelized,
display_name: scalar_equal
display_name: equal,
rng_func: default_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_ne_parallelized,
display_name: scalar_not_equal
display_name: not_equal,
rng_func: default_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_le_parallelized,
display_name: scalar_less_or_equal
display_name: less_or_equal,
rng_func: default_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_lt_parallelized,
display_name: scalar_less_than
display_name: less_than,
rng_func: default_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_ge_parallelized,
display_name: scalar_greater_or_equal
display_name: greater_or_equal,
rng_func: default_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_gt_parallelized,
display_name: scalar_greater_than
display_name: greater_than,
rng_func: default_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_max_parallelized,
display_name: scalar_max
display_name: max,
rng_func: default_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: scalar_min_parallelized,
display_name: scalar_min
display_name: min,
rng_func: default_scalar
);
define_server_key_bench_scalar_fn!(method_name: unchecked_scalar_add, display_name: add);
define_server_key_bench_scalar_fn!(method_name: unchecked_scalar_sub, display_name: sub);
define_server_key_bench_scalar_fn!(method_name: unchecked_small_scalar_mul, display_name: mul);
define_server_key_bench_scalar_default_fn!(
method_name: unchecked_scalar_add,
display_name: add,
rng_func: default_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: unchecked_scalar_sub,
display_name: sub,
rng_func: default_scalar
);
define_server_key_bench_scalar_default_fn!(
method_name: unchecked_scalar_mul_parallelized,
display_name: mul,
rng_func: mul_scalar
);
define_server_key_bench_unary_fn!(method_name: smart_neg, display_name: negation);
define_server_key_bench_unary_fn!(method_name: smart_neg_parallelized, display_name: negation);
@@ -659,30 +829,30 @@ define_server_key_bench_unary_fn!(
display_name: carry_propagation
);
define_server_key_bench_fn!(method_name: unchecked_max, display_name: max);
define_server_key_bench_fn!(method_name: unchecked_min, display_name: min);
define_server_key_bench_fn!(method_name: unchecked_eq, display_name: equal);
define_server_key_bench_fn!(method_name: unchecked_lt, display_name: less_than);
define_server_key_bench_fn!(method_name: unchecked_le, display_name: less_or_equal);
define_server_key_bench_fn!(method_name: unchecked_gt, display_name: greater_than);
define_server_key_bench_fn!(method_name: unchecked_ge, display_name: greater_or_equal);
define_server_key_bench_default_fn!(method_name: unchecked_max, display_name: max);
define_server_key_bench_default_fn!(method_name: unchecked_min, display_name: min);
define_server_key_bench_default_fn!(method_name: unchecked_eq, display_name: equal);
define_server_key_bench_default_fn!(method_name: unchecked_lt, display_name: less_than);
define_server_key_bench_default_fn!(method_name: unchecked_le, display_name: less_or_equal);
define_server_key_bench_default_fn!(method_name: unchecked_gt, display_name: greater_than);
define_server_key_bench_default_fn!(method_name: unchecked_ge, display_name: greater_or_equal);
define_server_key_bench_fn!(method_name: unchecked_max_parallelized, display_name: max);
define_server_key_bench_fn!(method_name: unchecked_min_parallelized, display_name: min);
define_server_key_bench_fn!(method_name: unchecked_eq_parallelized, display_name: equal);
define_server_key_bench_fn!(
define_server_key_bench_default_fn!(method_name: unchecked_max_parallelized, display_name: max);
define_server_key_bench_default_fn!(method_name: unchecked_min_parallelized, display_name: min);
define_server_key_bench_default_fn!(method_name: unchecked_eq_parallelized, display_name: equal);
define_server_key_bench_default_fn!(
method_name: unchecked_lt_parallelized,
display_name: less_than
);
define_server_key_bench_fn!(
define_server_key_bench_default_fn!(
method_name: unchecked_le_parallelized,
display_name: less_or_equal
);
define_server_key_bench_fn!(
define_server_key_bench_default_fn!(
method_name: unchecked_gt_parallelized,
display_name: greater_than
);
define_server_key_bench_fn!(
define_server_key_bench_default_fn!(
method_name: unchecked_ge_parallelized,
display_name: greater_or_equal
);
@@ -746,6 +916,10 @@ criterion_group!(
smart_bitand,
smart_bitor,
smart_bitxor,
);
criterion_group!(
smart_ops_comp,
smart_max,
smart_min,
smart_eq,
@@ -763,6 +937,10 @@ criterion_group!(
smart_bitand_parallelized,
smart_bitor_parallelized,
smart_bitxor_parallelized,
);
criterion_group!(
smart_parallelized_ops_comp,
smart_max_parallelized,
smart_min_parallelized,
smart_eq_parallelized,
@@ -777,11 +955,21 @@ criterion_group!(
add_parallelized,
sub_parallelized,
mul_parallelized,
div_parallelized,
rem_parallelized,
neg_parallelized,
bitand_parallelized,
bitnot_parallelized,
bitor_parallelized,
bitxor_parallelized,
left_shift_parallelized,
right_shift_parallelized,
rotate_left_parallelized,
rotate_right_parallelized,
);
criterion_group!(
default_parallelized_ops_comp,
max_parallelized,
min_parallelized,
eq_parallelized,
@@ -790,10 +978,7 @@ criterion_group!(
le_parallelized,
gt_parallelized,
ge_parallelized,
left_shift_parallelized,
right_shift_parallelized,
rotate_left_parallelized,
rotate_right_parallelized,
if_then_else_parallelized,
);
criterion_group!(
@@ -815,8 +1000,16 @@ criterion_group!(
scalar_add_parallelized,
scalar_sub_parallelized,
scalar_mul_parallelized,
scalar_div_parallelized,
scalar_rem_parallelized,
scalar_left_shift_parallelized,
scalar_right_shift_parallelized,
scalar_rotate_left_parallelized,
scalar_rotate_right_parallelized,
);
criterion_group!(
default_scalar_parallelized_ops_comp,
scalar_eq_parallelized,
scalar_ne_parallelized,
scalar_lt_parallelized,
@@ -835,6 +1028,10 @@ criterion_group!(
unchecked_bitand,
unchecked_bitor,
unchecked_bitxor,
);
criterion_group!(
unchecked_ops_comp,
unchecked_max,
unchecked_min,
unchecked_eq,
@@ -848,7 +1045,14 @@ criterion_group!(
unchecked_scalar_ops,
unchecked_scalar_add,
unchecked_scalar_sub,
unchecked_small_scalar_mul,
unchecked_scalar_mul_parallelized,
unchecked_bitand_parallelized,
unchecked_bitor_parallelized,
unchecked_bitxor_parallelized,
);
criterion_group!(
unchecked_scalar_ops_comp,
unchecked_max_parallelized,
unchecked_min_parallelized,
unchecked_eq_parallelized,
@@ -856,9 +1060,6 @@ criterion_group!(
unchecked_le_parallelized,
unchecked_gt_parallelized,
unchecked_ge_parallelized,
unchecked_bitand_parallelized,
unchecked_bitor_parallelized,
unchecked_bitxor_parallelized,
);
criterion_group!(misc, full_propagate, full_propagate_parallelized);
@@ -868,13 +1069,19 @@ fn main() {
Ok(val) => {
match val.to_lowercase().as_str() {
"default" => default_parallelized_ops(),
"default_comp" => default_parallelized_ops_comp(),
"default_scalar" => default_scalar_parallelized_ops(),
"default_scalar_comp" => default_scalar_parallelized_ops_comp(),
"smart" => smart_ops(),
"smart_comp" => smart_ops_comp(),
"smart_scalar" => smart_scalar_ops(),
"smart_parallelized" => smart_parallelized_ops(),
"smart_parallelized_comp" => smart_parallelized_ops_comp(),
"smart_scalar_parallelized" => smart_scalar_parallelized_ops(),
"unchecked" => unchecked_ops(),
"unchecked_comp" => unchecked_ops_comp(),
"unchecked_scalar" => unchecked_scalar_ops(),
"unchecked_scalar_comp" => unchecked_scalar_ops_comp(),
"misc" => misc(),
_ => panic!("unknown benchmark operations flavor"),
};

View File

@@ -5,14 +5,15 @@ use crate::utilities::{write_to_json, OperatorType};
use std::env;
use criterion::{criterion_group, Criterion};
use tfhe::shortint::keycache::NamedParam;
use tfhe::keycache::NamedParam;
use tfhe::shortint::parameters::*;
use tfhe::shortint::{Ciphertext, ClassicPBSParameters, ServerKey, ShortintParameterSet};
use tfhe::shortint::{
Ciphertext, ClassicPBSParameters, CompressedServerKey, ServerKey, ShortintParameterSet,
};
use rand::Rng;
use tfhe::shortint::keycache::KEY_CACHE;
use tfhe::shortint::keycache::{KEY_CACHE, KEY_CACHE_WOPBS};
use tfhe::shortint::keycache::KEY_CACHE_WOPBS;
use tfhe::shortint::parameters::parameters_wopbs::WOPBS_PARAM_MESSAGE_4_NORM2_6_KS_PBS;
const SERVER_KEY_BENCH_PARAMS: [ClassicPBSParameters; 4] = [
@@ -40,20 +41,59 @@ const SERVER_KEY_BENCH_PARAMS_EXTENDED: [ClassicPBSParameters; 15] = [
PARAM_MESSAGE_8_CARRY_0_KS_PBS,
];
const SERVER_KEY_MULTI_BIT_BENCH_PARAMS: [MultiBitPBSParameters; 2] = [
PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_2_KS_PBS,
PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_3_KS_PBS,
];
const SERVER_KEY_MULTI_BIT_BENCH_PARAMS_EXTENDED: [MultiBitPBSParameters; 6] = [
PARAM_MULTI_BIT_MESSAGE_1_CARRY_1_GROUP_2_KS_PBS,
PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_2_KS_PBS,
PARAM_MULTI_BIT_MESSAGE_3_CARRY_3_GROUP_2_KS_PBS,
PARAM_MULTI_BIT_MESSAGE_1_CARRY_1_GROUP_3_KS_PBS,
PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_3_KS_PBS,
PARAM_MULTI_BIT_MESSAGE_3_CARRY_3_GROUP_3_KS_PBS,
];
enum BenchParamsSet {
Standard,
Extended,
}
fn benchmark_parameters(params_set: BenchParamsSet) -> Vec<PBSParameters> {
let is_multi_bit = match env::var("__TFHE_RS_BENCH_TYPE") {
Ok(val) => val.to_lowercase() == "multi_bit",
Err(_) => false,
};
if is_multi_bit {
let params = match params_set {
BenchParamsSet::Standard => SERVER_KEY_MULTI_BIT_BENCH_PARAMS.to_vec(),
BenchParamsSet::Extended => SERVER_KEY_MULTI_BIT_BENCH_PARAMS_EXTENDED.to_vec(),
};
params.iter().map(|p| (*p).into()).collect()
} else {
let params = match params_set {
BenchParamsSet::Standard => SERVER_KEY_BENCH_PARAMS.to_vec(),
BenchParamsSet::Extended => SERVER_KEY_BENCH_PARAMS_EXTENDED.to_vec(),
};
params.iter().map(|p| (*p).into()).collect()
}
}
fn bench_server_key_unary_function<F>(
c: &mut Criterion,
bench_name: &str,
display_name: &str,
unary_op: F,
params: &[ClassicPBSParameters],
params_set: BenchParamsSet,
) where
F: Fn(&ServerKey, &mut Ciphertext),
{
let mut bench_group = c.benchmark_group(bench_name);
for param in params.iter() {
let param: PBSParameters = (*param).into();
let keys = KEY_CACHE.get_from_param(param);
for param in benchmark_parameters(params_set).iter() {
let keys = KEY_CACHE.get_from_param(*param);
let (cks, sks) = (keys.client_key(), keys.server_key());
let mut rng = rand::thread_rng();
@@ -73,7 +113,7 @@ fn bench_server_key_unary_function<F>(
write_to_json::<u64, _>(
&bench_id,
param,
*param,
param.name(),
display_name,
&OperatorType::Atomic,
@@ -90,15 +130,14 @@ fn bench_server_key_binary_function<F>(
bench_name: &str,
display_name: &str,
binary_op: F,
params: &[ClassicPBSParameters],
params_set: BenchParamsSet,
) where
F: Fn(&ServerKey, &mut Ciphertext, &mut Ciphertext),
{
let mut bench_group = c.benchmark_group(bench_name);
for param in params.iter() {
let param: PBSParameters = (*param).into();
let keys = KEY_CACHE.get_from_param(param);
for param in benchmark_parameters(params_set).iter() {
let keys = KEY_CACHE.get_from_param(*param);
let (cks, sks) = (keys.client_key(), keys.server_key());
let mut rng = rand::thread_rng();
@@ -120,7 +159,7 @@ fn bench_server_key_binary_function<F>(
write_to_json::<u64, _>(
&bench_id,
param,
*param,
param.name(),
display_name,
&OperatorType::Atomic,
@@ -137,15 +176,14 @@ fn bench_server_key_binary_scalar_function<F>(
bench_name: &str,
display_name: &str,
binary_op: F,
params: &[ClassicPBSParameters],
params_set: BenchParamsSet,
) where
F: Fn(&ServerKey, &mut Ciphertext, u8),
{
let mut bench_group = c.benchmark_group(bench_name);
for param in params {
let param: PBSParameters = (*param).into();
let keys = KEY_CACHE.get_from_param(param);
for param in benchmark_parameters(params_set).iter() {
let keys = KEY_CACHE.get_from_param(*param);
let (cks, sks) = (keys.client_key(), keys.server_key());
let mut rng = rand::thread_rng();
@@ -166,7 +204,7 @@ fn bench_server_key_binary_scalar_function<F>(
write_to_json::<u64, _>(
&bench_id,
param,
*param,
param.name(),
display_name,
&OperatorType::Atomic,
@@ -183,15 +221,14 @@ fn bench_server_key_binary_scalar_division_function<F>(
bench_name: &str,
display_name: &str,
binary_op: F,
params: &[ClassicPBSParameters],
params_set: BenchParamsSet,
) where
F: Fn(&ServerKey, &mut Ciphertext, u8),
{
let mut bench_group = c.benchmark_group(bench_name);
for param in params {
let param: PBSParameters = (*param).into();
let keys = KEY_CACHE.get_from_param(param);
for param in benchmark_parameters(params_set).iter() {
let keys = KEY_CACHE.get_from_param(*param);
let (cks, sks) = (keys.client_key(), keys.server_key());
let mut rng = rand::thread_rng();
@@ -216,7 +253,7 @@ fn bench_server_key_binary_scalar_division_function<F>(
write_to_json::<u64, _>(
&bench_id,
param,
*param,
param.name(),
display_name,
&OperatorType::Atomic,
@@ -228,12 +265,11 @@ fn bench_server_key_binary_scalar_division_function<F>(
bench_group.finish()
}
fn carry_extract(c: &mut Criterion) {
fn carry_extract_bench(c: &mut Criterion, params_set: BenchParamsSet) {
let mut bench_group = c.benchmark_group("carry_extract");
for param in SERVER_KEY_BENCH_PARAMS {
let param: PBSParameters = param.into();
let keys = KEY_CACHE.get_from_param(param);
for param in benchmark_parameters(params_set).iter() {
let keys = KEY_CACHE.get_from_param(*param);
let (cks, sks) = (keys.client_key(), keys.server_key());
let mut rng = rand::thread_rng();
@@ -244,7 +280,7 @@ fn carry_extract(c: &mut Criterion) {
let ct_0 = cks.encrypt(clear_0);
let bench_id = format!("ServerKey::carry_extract::{}", param.name());
let bench_id = format!("shortint::carry_extract::{}", param.name());
bench_group.bench_function(&bench_id, |b| {
b.iter(|| {
let _ = sks.carry_extract(&ct_0);
@@ -253,7 +289,7 @@ fn carry_extract(c: &mut Criterion) {
write_to_json::<u64, _>(
&bench_id,
param,
*param,
param.name(),
"carry_extract",
&OperatorType::Atomic,
@@ -265,12 +301,11 @@ fn carry_extract(c: &mut Criterion) {
bench_group.finish()
}
fn programmable_bootstrapping(c: &mut Criterion) {
fn programmable_bootstrapping_bench(c: &mut Criterion, params_set: BenchParamsSet) {
let mut bench_group = c.benchmark_group("programmable_bootstrap");
for param in SERVER_KEY_BENCH_PARAMS {
let param: PBSParameters = param.into();
let keys = KEY_CACHE.get_from_param(param);
for param in benchmark_parameters(params_set).iter() {
let keys = KEY_CACHE.get_from_param(*param);
let (cks, sks) = (keys.client_key(), keys.server_key());
let mut rng = rand::thread_rng();
@@ -283,7 +318,7 @@ fn programmable_bootstrapping(c: &mut Criterion) {
let ctxt = cks.encrypt(clear_0);
let bench_id = format!("ServerKey::programmable_bootstrap::{}", param.name());
let bench_id = format!("shortint::programmable_bootstrap::{}", param.name());
bench_group.bench_function(&bench_id, |b| {
b.iter(|| {
@@ -293,7 +328,7 @@ fn programmable_bootstrapping(c: &mut Criterion) {
write_to_json::<u64, _>(
&bench_id,
param,
*param,
param.name(),
"pbs",
&OperatorType::Atomic,
@@ -305,6 +340,54 @@ fn programmable_bootstrapping(c: &mut Criterion) {
bench_group.finish();
}
fn server_key_from_compressed_key(c: &mut Criterion) {
let mut bench_group = c.benchmark_group("uncompress_key");
bench_group
.sample_size(10)
.measurement_time(std::time::Duration::from_secs(60));
let mut params = SERVER_KEY_BENCH_PARAMS_EXTENDED
.iter()
.map(|p| (*p).into())
.collect::<Vec<PBSParameters>>();
let multi_bit_params = SERVER_KEY_MULTI_BIT_BENCH_PARAMS_EXTENDED
.iter()
.map(|p| (*p).into())
.collect::<Vec<PBSParameters>>();
params.extend(&multi_bit_params);
for param in params.iter() {
let keys = KEY_CACHE.get_from_param(*param);
let sks_compressed = CompressedServerKey::new(keys.client_key());
let bench_id = format!("shortint::uncompress_key::{}", param.name());
bench_group.bench_function(&bench_id, |b| {
let clone_compressed_key = || sks_compressed.clone();
b.iter_batched(
clone_compressed_key,
|sks_cloned| {
let _ = ServerKey::from(sks_cloned);
},
criterion::BatchSize::PerIteration,
)
});
write_to_json::<u64, _>(
&bench_id,
*param,
param.name(),
"uncompress_key",
&OperatorType::Atomic,
param.message_modulus().0.ilog2(),
vec![param.message_modulus().0.ilog2()],
);
}
bench_group.finish();
}
// TODO: remove?
fn _bench_wopbs_param_message_8_norm2_5(c: &mut Criterion) {
let mut bench_group = c.benchmark_group("programmable_bootstrap");
@@ -334,57 +417,69 @@ fn _bench_wopbs_param_message_8_norm2_5(c: &mut Criterion) {
}
macro_rules! define_server_key_unary_bench_fn (
(method_name:$server_key_method:ident, display_name:$name:ident, $params:expr) => {
(method_name:$server_key_method:ident, display_name:$name:ident, $params_set:expr) => {
fn $server_key_method(c: &mut Criterion) {
bench_server_key_unary_function(
c,
concat!("ServerKey::", stringify!($server_key_method)),
concat!("shortint::", stringify!($server_key_method)),
stringify!($name),
|server_key, lhs| {
let _ = server_key.$server_key_method(lhs);},
$params)
$params_set)
}
}
);
macro_rules! define_server_key_bench_fn (
(method_name:$server_key_method:ident, display_name:$name:ident, $params:expr) => {
(method_name:$server_key_method:ident, display_name:$name:ident, $params_set:expr) => {
fn $server_key_method(c: &mut Criterion) {
bench_server_key_binary_function(
c,
concat!("ServerKey::", stringify!($server_key_method)),
concat!("shortint::", stringify!($server_key_method)),
stringify!($name),
|server_key, lhs, rhs| {
let _ = server_key.$server_key_method(lhs, rhs);},
$params)
$params_set)
}
}
);
macro_rules! define_server_key_scalar_bench_fn (
(method_name:$server_key_method:ident, display_name:$name:ident, $params:expr) => {
(method_name:$server_key_method:ident, display_name:$name:ident, $params_set:expr) => {
fn $server_key_method(c: &mut Criterion) {
bench_server_key_binary_scalar_function(
c,
concat!("ServerKey::", stringify!($server_key_method)),
concat!("shortint::", stringify!($server_key_method)),
stringify!($name),
|server_key, lhs, rhs| {
let _ = server_key.$server_key_method(lhs, rhs);},
$params)
$params_set)
}
}
);
macro_rules! define_server_key_scalar_div_bench_fn (
(method_name:$server_key_method:ident, display_name:$name:ident, $params:expr) => {
(method_name:$server_key_method:ident, display_name:$name:ident, $params_set:expr) => {
fn $server_key_method(c: &mut Criterion) {
bench_server_key_binary_scalar_division_function(
c,
concat!("ServerKey::", stringify!($server_key_method)),
concat!("shortint::", stringify!($server_key_method)),
stringify!($name),
|server_key, lhs, rhs| {
let _ = server_key.$server_key_method(lhs, rhs);},
$params)
$params_set)
}
}
);
macro_rules! define_custom_bench_fn (
(function_name:$function:ident, $params_set:expr) => {
fn $function(c: &mut Criterion) {
::paste::paste! {
[<$function _bench>](
c,
$params_set)
}
}
}
);
@@ -392,251 +487,258 @@ macro_rules! define_server_key_scalar_div_bench_fn (
define_server_key_unary_bench_fn!(
method_name: unchecked_neg,
display_name: negation,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: unchecked_add,
display_name: add,
&SERVER_KEY_BENCH_PARAMS_EXTENDED
BenchParamsSet::Extended
);
define_server_key_bench_fn!(
method_name: unchecked_sub,
display_name: sub,
&SERVER_KEY_BENCH_PARAMS_EXTENDED
BenchParamsSet::Extended
);
define_server_key_bench_fn!(
method_name: unchecked_mul_lsb,
display_name: mul,
&SERVER_KEY_BENCH_PARAMS_EXTENDED
BenchParamsSet::Extended
);
define_server_key_bench_fn!(
method_name: unchecked_mul_msb,
display_name: mul,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: unchecked_div,
display_name: div,
&SERVER_KEY_BENCH_PARAMS_EXTENDED
BenchParamsSet::Extended
);
define_server_key_bench_fn!(
method_name: smart_bitand,
display_name: bitand,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: smart_bitor,
display_name: bitor,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: smart_bitxor,
display_name: bitxor,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: smart_add,
display_name: add,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: smart_sub,
display_name: sub,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: smart_mul_lsb,
display_name: mul,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: bitand,
display_name: bitand,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: bitor,
display_name: bitor,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: bitxor,
display_name: bitxor,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: add,
display_name: add,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: sub,
display_name: sub,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: mul,
display_name: mul,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: div,
display_name: div,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: greater,
display_name: greater,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: greater_or_equal,
display_name: greater_or_equal,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: less,
display_name: less,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: less_or_equal,
display_name: less_or_equal,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: equal,
display_name: equal,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: not_equal,
display_name: not_equal,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_unary_bench_fn!(
method_name: neg,
display_name: negation,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: unchecked_greater,
display_name: greater_than,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: unchecked_less,
display_name: less_than,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_bench_fn!(
method_name: unchecked_equal,
display_name: equal,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_bench_fn!(
method_name: unchecked_scalar_add,
display_name: add,
&SERVER_KEY_BENCH_PARAMS_EXTENDED
BenchParamsSet::Extended
);
define_server_key_scalar_bench_fn!(
method_name: unchecked_scalar_sub,
display_name: sub,
&SERVER_KEY_BENCH_PARAMS_EXTENDED
BenchParamsSet::Extended
);
define_server_key_scalar_bench_fn!(
method_name: unchecked_scalar_mul,
display_name: mul,
&SERVER_KEY_BENCH_PARAMS_EXTENDED
BenchParamsSet::Extended
);
define_server_key_scalar_bench_fn!(
method_name: unchecked_scalar_left_shift,
display_name: left_shift,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_bench_fn!(
method_name: unchecked_scalar_right_shift,
display_name: right_shift,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_div_bench_fn!(
method_name: unchecked_scalar_div,
display_name: div,
&SERVER_KEY_BENCH_PARAMS_EXTENDED
BenchParamsSet::Extended
);
define_server_key_scalar_div_bench_fn!(
method_name: unchecked_scalar_mod,
display_name: modulo,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_bench_fn!(
method_name: scalar_add,
display_name: add,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_bench_fn!(
method_name: scalar_sub,
display_name: sub,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_bench_fn!(
method_name: scalar_mul,
display_name: mul,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_bench_fn!(
method_name: scalar_left_shift,
display_name: left_shift,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_bench_fn!(
method_name: scalar_right_shift,
display_name: right_shift,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_div_bench_fn!(
method_name: scalar_div,
display_name: div,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_div_bench_fn!(
method_name: scalar_mod,
display_name: modulo,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_bench_fn!(
method_name: scalar_greater,
display_name: greater,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_bench_fn!(
method_name: scalar_greater_or_equal,
display_name: greater_or_equal,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_bench_fn!(
method_name: scalar_less,
display_name: less,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_bench_fn!(
method_name: scalar_less_or_equal,
display_name: less_or_equal,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_div_bench_fn!(
method_name: scalar_equal,
display_name: equal,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_server_key_scalar_div_bench_fn!(
method_name: scalar_not_equal,
display_name: not_equal,
&SERVER_KEY_BENCH_PARAMS
BenchParamsSet::Standard
);
define_custom_bench_fn!(function_name: carry_extract, BenchParamsSet::Standard);
define_custom_bench_fn!(
function_name: programmable_bootstrapping,
BenchParamsSet::Standard
);
criterion_group!(
@@ -710,6 +812,8 @@ criterion_group!(
scalar_not_equal
);
criterion_group!(misc, server_key_from_compressed_key);
mod casting;
criterion_group!(
casting,
@@ -723,6 +827,7 @@ fn main() {
casting();
default_ops();
default_scalar_ops();
misc();
}
match env::var("__TFHE_RS_BENCH_OP_FLAVOR") {

View File

@@ -9,7 +9,7 @@
* [Benchmarks](getting_started/benchmarks.md)
* [Security and Cryptography](getting_started/security_and_cryptography.md)
## Tutorials
## Tutorials
* [Homomorphic Parity Bit](tutorials/parity_bit.md)
* [Homomorphic Case Changing on Latin String](tutorials/latin_fhe_string.md)
@@ -19,23 +19,24 @@
* [Compress Ciphertexts/Keys](how_to/compress.md)
* [Use Public Key Encryption](how_to/public_key.md)
* [Use Trivial Ciphertext](how_to/trivial_ciphertext.md)
* [Generic Function Bounds](how_to/trait_bounds.md)
* [Use Parallelized PBS](how_to/parallelized_pbs.md)
* [Use the C API](how_to/c_api.md)
* [Use the JS on WASM API](how_to/js_on_wasm_api.md)
## Fine-grained APIs
* [Quick Start](fine_grained_api/quick_start.md)
* [Boolean](fine_grained_api/Boolean/tutorial.md)
* [Boolean](fine_grained_api/Boolean/readme.md)
* [Operations](fine_grained_api/Boolean/operations.md)
* [Cryptographic Parameters](fine_grained_api/Boolean/parameters.md)
* [Serialization/Deserialization](fine_grained_api/Boolean/serialization.md)
* [Shortint](fine_grained_api/shortint/tutorial.md)
* [Shortint](fine_grained_api/shortint/readme.md)
* [Operations](fine_grained_api/shortint/operations.md)
* [Cryptographic Parameters](fine_grained_api/shortint/parameters.md)
* [Serialization/Deserialization](fine_grained_api/shortint/serialization.md)
* [Integer](fine_grained_api/integer/tutorial.md)
* [Integer](fine_grained_api/integer/readme.md)
* [Operations](fine_grained_api/integer/operations.md)
* [Cryptographic Parameters](fine_grained_api/integer/parameters.md)
* [Serialization/Deserialization](fine_grained_api/integer/serialization.md)

BIN
tfhe/docs/_static/carry.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 16 KiB

BIN
tfhe/docs/_static/multisum.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
tfhe/docs/_static/overflow.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
tfhe/docs/_static/sha256.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -17,11 +17,8 @@ The sha256 function processes the input data in blocks or chunks of 512 bits. Be
Or visually:
```
0 L L+1 L+1+k L+1+k+64
|-----------------------------------|---|--------------------------------|----------------------|
Original input (L bits) "1" bit "0" bits Encoding of the number L
```
![](../_static/sha256.png)
Where the numbers on the top represent the length of the padded input at each position, and L+1+k+64 is a multiple of 512 (the length of the padded input).
#### Operations and functions
@@ -63,7 +60,7 @@ Note that all these operations can be evaluated homomorphically. ROTR and SHR ca
#### Sha256 computation
As we have mentioned, the sha256 function works with chunks of 512 bits. For each chunk, we will compute 64 32-bit words. 16 will come from the 512 bits and the rest will be computed using the previous functions. After computing the 64 words, and still within the same chunk iteration, a compression loop will compute a hash value (8 32-bit words), again using the previous functions and some constants to mix everything up. When we finish the last chunk iteration, the resulting hash values will be the output of the sha256 function.
As we have mentioned, the sha256 function works with chunks of 512 bits. For each chunk, we will compute 64 32-bit words. 16 will come from the 512 bits and the rest will be computed using the previous functions. After computing the 64 words, and still within the same chunk iteration, a compression loop will compute a hash value (8 32-bit words), again using the previous functions and some constants to mix everything up. When we finish the last chunk iteration, the resulting hash values will be the output of the sha256 function.
Here is how this function looks like using arrays of 32 bools to represent words:
@@ -317,6 +314,6 @@ By using ```stdin``` we can supply the data to hash using a file instead of the
Our implementation also accepts hexadecimal inputs. To be considered as such, the input must start with "0x" and contain only valid hex digits (otherwise it's interpreted as text).
Finally see that padding is executed on the client side. This has the advantage of hiding the exact length of the input to the server, who already doesn't know anything about the contents of it but may extract information from the length.
Finally see that padding is executed on the client side. This has the advantage of hiding the exact length of the input to the server, who already doesn't know anything about the contents of it but may extract information from the length.
Another option would be to perform padding on the server side. The padding function would receive the encrypted input and pad it with trivial bit encryptions. We could then integrate the padding function inside the ```sha256_fhe``` function computed by the server.

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.3.0", features = [ "x86_64-unix" ] }
tfhe = { version = "0.4.0", 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.3.0", features = ["x86_64-unix"] }
tfhe = { version = "0.4.0", features = ["x86_64-unix"] }
```
For Apple Silicon or aarch64-based machines running Unix-like OSes:
```toml
tfhe = { version = "0.3.0", features = ["aarch64-unix"] }
tfhe = { version = "0.4.0", features = ["aarch64-unix"] }
```
For `x86_64`-based machines with the [`rdseed instruction`](https://en.wikipedia.org/wiki/RDRAND) running Windows:
```toml
tfhe = { version = "0.3.0", features = ["x86_64"] }
tfhe = { version = "0.4.0", features = ["x86_64"] }
```
### Commented code to double a 2-bit message in a leveled fashion and using a PBS with the `core_crypto` module.

View File

@@ -38,6 +38,7 @@ fn main() {
DecompositionLevelCount(2),
DecompositionBaseLog(2),
DecompositionLevelCount(5),
EncryptionKeyChoice::Small,
)
};
}

View File

@@ -367,14 +367,14 @@ fn main() {
let modulus = client_key.parameters.message_modulus().0 as u64;
// We use the private client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let mut ct_1 = client_key.encrypt(msg1);
let mut ct_2 = client_key.encrypt(msg2);
// Compute the lookup table for the bivariate functions
let acc = server_key.generate_lookup_table_bivariate(|x,y| (x.count_ones()
+ y.count_ones()) as u64 % modulus );
let ct_res = server_key.smart_apply_lookup_table_bivariate(&ct_1, &mut ct_2, &acc);
let ct_res = server_key.smart_apply_lookup_table_bivariate(&mut ct_1, &mut ct_2, &acc);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_res);

View File

@@ -2,7 +2,9 @@
Due to their nature, homomorphic operations are naturally slower than their clear equivalent. Some timings are exposed for basic operations. For completeness, benchmarks for other libraries are also given.
{% hint style="info" %}
All benchmarks were launched on an AWS m6i.metal with the following specifications: Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz and 512GB of RAM.
{% endhint %}
## Boolean
@@ -31,7 +33,7 @@ This measures the execution time of a single binary Boolean gate.
## Integer
This measures the execution time for some operation sets of tfhe-rs::integer.
| Operation \ Size | `FheUint8` | `FheUint16` | `FheUint32` | ` FheUint64` | `FheUint128` | `FheUint256` |
|--------------------------------------------------------|------------|-------------|-------------|--------------|--------------|--------------|
| Negation (`-`) | 80.4 ms | 106 ms | 132 ms | 193 ms | 257 ms | 348 ms |
@@ -48,7 +50,7 @@ This measures the execution time for some operation sets of tfhe-rs::integer.
All timings are related to parallelized Radix-based integer operations, where each block is encrypted using the default parameters (i.e., PARAM\_MESSAGE\_2\_CARRY\_2, more information about parameters can be found [here](../fine_grained_api/shortint/parameters.md)).
To ensure predictable timings, the operation flavor is the `default` one: the carry is propagated if needed. The operation costs could be reduced by using `unchecked`, `checked`, or `smart`.
To ensure predictable timings, the operation flavor is the `default` one: the carry is propagated if needed. The operation costs could be reduced by using `unchecked`, `checked`, or `smart`.
## Shortint

View File

@@ -7,11 +7,11 @@
To use `TFHE-rs` in your project, you first need to add it as a dependency in your `Cargo.toml`:
```toml
tfhe = { version = "0.3.0", features = [ "boolean", "shortint", "integer", "x86_64-unix" ] }
tfhe = { version = "0.4.0", features = [ "boolean", "shortint", "integer", "x86_64-unix" ] }
```
{% hint style="info" %}
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 possible performance, eg: `cargo run --release`.
{% hint style="success" %}
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 possible performance
{% endhint %}
@@ -27,6 +27,6 @@ TFHE-rs is supported on Linux (x86, aarch64), macOS (x86, aarch64) and Windows (
| Windows | `x86_64` | Unsupported |
{% hint style="info" %}
Users who have ARM devices can use TFHE-rs by compiling using the `nightly` toolchain (see
Users who have ARM devices can use TFHE-rs by compiling using the `nightly` toolchain (see
[Configuration](../how_to/rust_configuration.md) for more details).
{% endhint %}
{% endhint %}

View File

@@ -43,7 +43,7 @@ The list of supported operations is:
Native small homomorphic integer types (e.g., FheUint3 or FheUint4) easily compute various operations. In general, computing over encrypted data is as easy as computing over clear data, since the same operation symbol is used. The addition between two ciphertexts is done using the symbol `+` between two FheUint values. Many operations can be computed between a clear value (i.e. a scalar) and a ciphertext.
In Rust, operations on native types are modular. For example, computations on `u8` are carried out modulo 2^8. A similar idea applies for FheUintX, where operations are done modulo 2^X. For FheUint3, operations are done modulo 8 = 2^3.
In Rust, operations on native types are modular. For example, computations on `u8` are carried out modulo $$2^{8}$$. A similar idea applies for FheUintX, where operations are done modulo $$2^{X}$$. For FheUint3, operations are done modulo $$8 = 2^{3}$$.
### Arithmetic operations.
@@ -72,7 +72,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled().enable_default_uint3().build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 7;
let clear_b = 3;
let clear_c = 2;
@@ -85,10 +85,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
a = a * &b; // Clear equivalent computations: 7 * 3 mod 8 = 5
b = &b + &c; // Clear equivalent computations: 3 + 2 mod 8 = 5
b = b - 5; // Clear equivalent computations: 5 - 5 mod 8 = 0
let dec_a = a.decrypt(&keys);
let dec_b = b.decrypt(&keys);
// We homomorphically swapped values using bitwise operations
assert_eq!(dec_a, (clear_a * clear_b) % 8);
assert_eq!(dec_b, ((clear_b + clear_c) - 5) % 8);
@@ -124,20 +124,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled().enable_default_uint3().build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 7;
let clear_b = 3;
let mut a = FheUint3::try_encrypt(clear_a, &keys)?;
let mut b = FheUint3::try_encrypt(clear_b, &keys)?;
a = a ^ &b;
b = b ^ &a;
a = a ^ &b;
let dec_a = a.decrypt(&keys);
let dec_b = b.decrypt(&keys);
// We homomorphically swapped values using bitwise operations
assert_eq!(dec_a, clear_b);
assert_eq!(dec_b, clear_a);
@@ -179,13 +179,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled().enable_default_uint3().build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 7;
let clear_b = 3;
let mut a = FheUint3::try_encrypt(clear_a, &keys)?;
let mut b = FheUint3::try_encrypt(clear_b, &keys)?;
assert_eq!(a.gt(&b).decrypt(&keys) != 0, true);
assert_eq!(b.le(&a).decrypt(&keys) != 0, true);
@@ -237,13 +237,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled().enable_default_uint2().build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 1;
let clear_b = 3;
let a = FheUint2::try_encrypt(clear_a, &keys)?;
let b = FheUint2::try_encrypt(clear_b, &keys)?;
let c = a.bivariate_function(&b, std::cmp::max);
let decrypted = c.decrypt(&keys);
assert_eq!(decrypted, std::cmp::max(clear_a, clear_b) as u8);
@@ -286,7 +286,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled().enable_default_integers().build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 15_u64;
let clear_b = 27_u64;
let clear_c = 43_u64;
@@ -299,10 +299,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
a = a * &b; // Clear equivalent computations: 15 * 27 mod 256 = 149
b = &b + &c; // Clear equivalent computations: 27 + 43 mod 256 = 70
b = b - 76u8; // Clear equivalent computations: 70 - 76 mod 256 = 250
let dec_a: u8 = a.decrypt(&keys);
let dec_b: u8 = b.decrypt(&keys);
assert_eq!(dec_a, ((clear_a * clear_b) % 256_u64) as u8);
assert_eq!(dec_b, (((clear_b + clear_c).wrapping_sub(76_u64)) % 256_u64) as u8);
@@ -337,7 +337,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled().enable_default_integers().build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 164;
let clear_b = 212;

View File

@@ -46,7 +46,7 @@ fn main() {
The default configuration for x86 Unix machines:
```toml
tfhe = { version = "0.3.0", features = ["integer", "x86_64-unix"]}
tfhe = { version = "0.4.0", features = ["integer", "x86_64-unix"]}
```
Configuration options for different platforms can be found [here](../getting_started/installation.md). Other rust and homomorphic types features can be found [here](../how_to/rust_configuration.md).

View File

@@ -18,6 +18,8 @@ Then, a small random value called noise is added to the least significant bits.
$$plaintext = (\Delta * m) + e$$
$$m \in \mathbb{Z}_p$$
![](../_static/lwe.png)
To go from a **plaintext** to a **ciphertext,** one must encrypt the plaintext using a secret key.
@@ -68,7 +70,7 @@ In `TFHE-rs`, noise is encoded in the least significant bits of each plaintext.
The figure below illustrates this problem in the case of an addition, where an extra bit of noise is incurred as a result.
![Noise overtaking the plaintexts after homomorphic addition. Most significant bits are on the left.](../_static/fig7.png)
![Noise overtaking the plaintexts after homomorphic addition. Most significant bits are on the left.](../_static/overflow.png)
`TFHE-rs` offers the ability to automatically manage noise by performing bootstrapping operations to reset the noise.
@@ -84,7 +86,7 @@ Since encoded values have a fixed precision, operating on them can produce resul
As an example, consider adding two ciphertexts. Adding two values could end up outside the range of either ciphertext, and thus necessitate a carry, which would then be carried onto the first padding bit. In the figure below, each plaintext over 32 bits has one bit of padding on its left (i.e., the most significant bit). After the addition, the padding bit is no longer available, as it has been used in order for the carry. This is referred to as **consuming** bits of padding. Since no padding is left, there is no guarantee that further additions would yield correct results.
![](../_static/fig6.png)
![](../_static/carry.png)
### Security.

View File

@@ -20,8 +20,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 673;
let clear_b = 6;
let clear_a = 673u32;
let clear_b = 6u32;
let a = FheUint32::try_encrypt(clear_a, &keys)?;
let b = FheUint32::try_encrypt(clear_b, &keys)?;
@@ -51,8 +51,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 673;
let clear_b = 6;
let clear_a = 673u32;
let clear_b = 6u32;
let a = FheUint32::try_encrypt(clear_a, &keys)?;
let b = FheUint32::try_encrypt(clear_b, &keys)?;

View File

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

View File

@@ -0,0 +1,131 @@
# Generic Bounds
If you wish to write generic functions which use operators with mixed reference and non-reference,
it might get tricky at first to specify the trait [bounds](https://doc.rust-lang.org/rust-by-example/generics/bounds.html).
This page should serve as a _cookbook_ to help you.
Operators (+, *, >>, etc) are tied to traits in `std:::ops`, e.g. `+` is `std::ops::Add`,
so to write a generic function which uses the `+` operator, you need to use add `std::ops::Add`
as a trait bound.
Then, depending on if the left hand side / right hand side is an owned value or a reference, the trait bound
is slightly different. The table below shows the possibilities.
| operation | trait bound |
| ----------- | ------------------------------------- |
| `T $op T` | `T: $Op<T, Output=T>` |
| `T $op &T` | `T: for<'a> $Op<&'a T, Output=T>` |
| `&T $op T` | `for<'a> &'a T: $Op<T, Output=T>` |
| `&T $op &T` | `for<'a> &'a T: $Op<&'a T, Output=T>` |
{% hint style="info" %}
The `for<'a>` syntax is something called [Higher-Rank Trait Bounds](https://doc.rust-lang.org/nomicon/hrtb.html), often shortened as __HRTB__
{% endhint %}
{% hint style="info" %}
Writing generic functions will also allow you to call them using clear inputs,
only allowing easier debugging.
{% endhint %}
## Example
```rust
use std::ops::{Add, Mul};
use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint32, FheUint64};
pub fn ex1<'a, FheType, ClearType>(ct: &'a FheType, pt: ClearType) -> FheType
where
&'a FheType: Add<ClearType, Output = FheType>,
{
ct + pt
}
pub fn ex2<'a, FheType, ClearType>(a: &'a FheType, b: &'a FheType, pt: ClearType) -> FheType
where
&'a FheType: Mul<&'a FheType, Output = FheType>,
FheType: Add<ClearType, Output = FheType>,
{
(a * b) + pt
}
pub fn ex3<FheType, ClearType>(a: FheType, b: FheType, pt: ClearType) -> FheType
where
for<'a> &'a FheType: Add<&'a FheType, Output = FheType>,
FheType: Add<FheType, Output = FheType> + Add<ClearType, Output = FheType>,
{
let tmp = (&a + &b) + (&a + &b);
tmp + pt
}
pub fn ex4<FheType, ClearType>(a: FheType, b: FheType, pt: ClearType) -> FheType
where
FheType: Clone + Add<FheType, Output = FheType> + Add<ClearType, Output = FheType>,
{
let tmp = (a.clone() + b.clone()) + (a.clone() + b.clone());
tmp + pt
}
fn main() {
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
let (client_key, server_keys) = generate_keys(config);
set_server_key(server_keys);
// Use FheUint32
{
let clear_a = 46546u32;
let clear_b = 6469u32;
let clear_c = 64u32;
let a = FheUint32::try_encrypt(clear_a, &client_key).unwrap();
let b = FheUint32::try_encrypt(clear_b, &client_key).unwrap();
assert_eq!(
ex1(&clear_a, clear_c),
ex1(&a, clear_c).decrypt(&client_key)
);
assert_eq!(
ex2(&clear_a, &clear_b, clear_c),
ex2(&a, &b, clear_c).decrypt(&client_key)
);
assert_eq!(
ex3(clear_a, clear_b, clear_c),
ex3(a.clone(), b.clone(), clear_c).decrypt(&client_key)
);
assert_eq!(
ex4(clear_a, clear_b, clear_c),
ex4(a, b, clear_c).decrypt(&client_key)
);
}
// Use FheUint64
{
let clear_a = 46544866u64;
let clear_b = 6469446677u64;
let clear_c = 647897u64;
let a = FheUint64::try_encrypt(clear_a, &client_key).unwrap();
let b = FheUint64::try_encrypt(clear_b, &client_key).unwrap();
assert_eq!(
ex1(&clear_a, clear_c),
ex1(&a, clear_c).decrypt(&client_key)
);
assert_eq!(
ex2(&clear_a, &clear_b, clear_c),
ex2(&a, &b, clear_c).decrypt(&client_key)
);
assert_eq!(
ex3(clear_a, clear_b, clear_c),
ex3(a.clone(), b.clone(), clear_c).decrypt(&client_key)
);
assert_eq!(
ex4(clear_a, clear_b, clear_c),
ex4(a, b, clear_c).decrypt(&client_key)
);
}
}
```

View File

@@ -48,7 +48,7 @@ To use the `FheUint8` type, the `integer` feature must be activated:
[dependencies]
# Default configuration for x86 Unix machines:
tfhe = { version = "0.3.0", features = ["integer", "x86_64-unix"]}
tfhe = { version = "0.4.0", features = ["integer", "x86_64-unix"]}
```
Other configurations can be found [here](../getting_started/installation.md).

View File

@@ -21,7 +21,7 @@ To use Booleans, the `booleans` feature in our Cargo.toml must be enabled:
# Cargo.toml
# Default configuration for x86 Unix machines:
tfhe = { version = "0.3.0", features = ["boolean", "x86_64-unix"]}
tfhe = { version = "0.4.0", features = ["boolean", "x86_64-unix"]}
```
Other configurations can be found [here](../getting_started/installation.md).

View File

@@ -5,7 +5,7 @@ use crate::utilities::{write_to_json, OperatorType};
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::Path;
use tfhe::boolean::parameters::{DEFAULT_PARAMETERS, TFHE_LIB_PARAMETERS};
use tfhe::boolean::parameters::{DEFAULT_PARAMETERS, PARAMETERS_ERROR_PROB_2_POW_MINUS_165};
use tfhe::boolean::{client_key, server_key};
fn write_result(file: &mut File, name: &str, value: usize) {
@@ -15,9 +15,9 @@ fn write_result(file: &mut File, name: &str, value: usize) {
}
fn client_server_key_sizes(results_file: &Path) {
let boolean_params_vec = vec![
let boolean_params_vec = [
(DEFAULT_PARAMETERS, "DEFAULT_PARAMETERS"),
(TFHE_LIB_PARAMETERS, "TFHE_LIB_PARAMETERS"),
(PARAMETERS_ERROR_PROB_2_POW_MINUS_165, "TFHE_LIB_PARAMETERS"),
];
File::create(results_file).expect("create results file failed");
let mut file = OpenOptions::new()

View File

@@ -1,5 +1,6 @@
use clap::{Arg, ArgAction, Command};
use tfhe::shortint::keycache::{NamedParam, KEY_CACHE, KEY_CACHE_WOPBS};
use tfhe::keycache::NamedParam;
use tfhe::shortint::keycache::{KEY_CACHE, KEY_CACHE_WOPBS};
use tfhe::shortint::parameters::parameters_wopbs_message_carry::{
WOPBS_PARAM_MESSAGE_1_CARRY_1_KS_PBS, WOPBS_PARAM_MESSAGE_2_CARRY_2_KS_PBS,
WOPBS_PARAM_MESSAGE_3_CARRY_3_KS_PBS, WOPBS_PARAM_MESSAGE_4_CARRY_4_KS_PBS,

View File

@@ -7,8 +7,8 @@ use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::Path;
use tfhe::integer::U256;
use tfhe::keycache::NamedParam;
use tfhe::prelude::*;
use tfhe::shortint::keycache::NamedParam;
use tfhe::shortint::parameters::{
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS, PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS,
};

View File

@@ -1,11 +1,11 @@
use tfhe::boolean::client_key::ClientKey;
use tfhe::boolean::parameters::TFHE_LIB_PARAMETERS;
use tfhe::boolean::parameters::PARAMETERS_ERROR_PROB_2_POW_MINUS_165;
use tfhe::boolean::prelude::BinaryBooleanGates;
use tfhe::boolean::server_key::ServerKey;
fn main() {
// let (cks, sks) = gen_keys();
let cks = ClientKey::new(&TFHE_LIB_PARAMETERS);
let cks = ClientKey::new(&PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
let sks = ServerKey::new(&cks);
let left = false;

View File

@@ -0,0 +1,184 @@
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::Path;
use tfhe::boolean::parameters::{BooleanParameters, VEC_BOOLEAN_PARAM};
use tfhe::core_crypto::commons::dispersion::StandardDev;
use tfhe::core_crypto::commons::parameters::{GlweDimension, LweDimension, PolynomialSize};
use tfhe::keycache::NamedParam;
use tfhe::shortint::parameters::multi_bit::ALL_MULTI_BIT_PARAMETER_VEC;
use tfhe::shortint::parameters::{
ShortintParameterSet, ALL_PARAMETER_VEC, PARAM_MESSAGE_1_CARRY_1_PBS_KS,
PARAM_MESSAGE_2_CARRY_2_PBS_KS, PARAM_MESSAGE_3_CARRY_3_PBS_KS, PARAM_MESSAGE_4_CARRY_4_PBS_KS,
};
pub trait ParamDetails {
fn lwe_dimension(&self) -> LweDimension;
fn glwe_dimension(&self) -> GlweDimension;
fn lwe_modular_std_dev(&self) -> StandardDev;
fn glwe_modular_std_dev(&self) -> StandardDev;
fn polynomial_size(&self) -> PolynomialSize;
fn log_ciphertext_modulus(&self) -> usize;
}
impl ParamDetails for BooleanParameters {
fn lwe_dimension(&self) -> LweDimension {
self.lwe_dimension
}
fn glwe_dimension(&self) -> GlweDimension {
self.glwe_dimension
}
fn lwe_modular_std_dev(&self) -> StandardDev {
self.lwe_modular_std_dev
}
fn glwe_modular_std_dev(&self) -> StandardDev {
self.glwe_modular_std_dev
}
fn polynomial_size(&self) -> PolynomialSize {
self.polynomial_size
}
fn log_ciphertext_modulus(&self) -> usize {
32
}
}
impl ParamDetails for ShortintParameterSet {
fn lwe_dimension(&self) -> LweDimension {
self.lwe_dimension()
}
fn glwe_dimension(&self) -> GlweDimension {
self.glwe_dimension()
}
fn lwe_modular_std_dev(&self) -> StandardDev {
self.lwe_modular_std_dev()
}
fn glwe_modular_std_dev(&self) -> StandardDev {
self.glwe_modular_std_dev()
}
fn polynomial_size(&self) -> PolynomialSize {
self.polynomial_size()
}
fn log_ciphertext_modulus(&self) -> usize {
assert!(self.ciphertext_modulus().is_native_modulus());
64
}
}
///Function to print in the lattice_estimator format the parameters
/// Format: LWE.Parameters(n=722, q=2^32, Xs=ND.UniformMod(2),
/// Xe=ND.DiscreteGaussian(56139.60810663548), tag='test_lattice_estimator')
pub fn format_lwe_parameters_to_lattice_estimator<T: ParamDetails + NamedParam>(
param: &T,
) -> String {
let modular_std_dev =
param.log_ciphertext_modulus() as f64 + param.lwe_modular_std_dev().0.log2();
format!(
"{}_LWE = LWE.Parameters(\n n = {},\n q ={},\n Xs=ND.UniformMod(2), \n Xe=ND.DiscreteGaussian({}),\n tag='{}_lwe' \n)\n\n",
param.name(), param.lwe_dimension().0, (1u128<<param.log_ciphertext_modulus() as u128), 2.0_f64.powf(modular_std_dev), param.name())
}
///Function to print in the lattice_estimator format the parameters
/// Format: LWE.Parameters(n=722, q=2^32, Xs=ND.UniformMod(2),
/// Xe=ND.DiscreteGaussian(56139.60810663548), tag='test_lattice_estimator')
pub fn format_glwe_parameters_to_lattice_estimator<T: ParamDetails + NamedParam>(
param: &T,
) -> String {
let modular_std_dev =
param.log_ciphertext_modulus() as f64 + param.glwe_modular_std_dev().0.log2();
format!(
"{}_GLWE = LWE.Parameters(\n n = {},\n q = {},\n Xs=ND.UniformMod(2), \n Xe=ND.DiscreteGaussian({}),\n tag='{}_glwe' \n)\n\n",
param.name(), param.glwe_dimension().0*param.polynomial_size().0, (1u128<<param.log_ciphertext_modulus() as u128), 2.0_f64.powf(modular_std_dev), param.name())
}
fn write_file(file: &mut File, filename: &Path, line: impl Into<String>) {
let error_message = format!("unable to write file {}", filename.to_str().unwrap());
file.write_all(line.into().as_bytes())
.expect(&error_message);
}
fn write_all_params_in_file<T: ParamDetails + Copy + NamedParam>(filename: &str, params: &[T]) {
let path = Path::new(filename);
File::create(path).expect("create results file failed");
let mut file = OpenOptions::new()
.append(true)
.open(path)
.expect("cannot open parsed results file");
for params in params.iter() {
write_file(
&mut file,
path,
format_lwe_parameters_to_lattice_estimator(params),
);
write_file(
&mut file,
path,
format_glwe_parameters_to_lattice_estimator(params),
);
}
write_file(&mut file, path, "all_params = [\n");
for params in params.iter() {
let param_lwe_name = format!("{}_LWE,", params.name());
write_file(&mut file, path, param_lwe_name);
let param_glwe_name = format!("{}_GLWE,", params.name());
write_file(&mut file, path, param_glwe_name);
}
write_file(&mut file, path, "\n]\n");
}
fn main() {
let work_dir = std::env::current_dir().unwrap();
let mut new_work_dir = work_dir;
new_work_dir.push("ci");
std::env::set_current_dir(new_work_dir).unwrap();
write_all_params_in_file(
"boolean_parameters_lattice_estimator.sage",
&VEC_BOOLEAN_PARAM,
);
let classic_params_small = vec![
PARAM_MESSAGE_1_CARRY_1_PBS_KS,
PARAM_MESSAGE_2_CARRY_2_PBS_KS,
PARAM_MESSAGE_3_CARRY_3_PBS_KS,
PARAM_MESSAGE_4_CARRY_4_PBS_KS,
];
let all_classic_pbs = [ALL_PARAMETER_VEC.to_vec(), classic_params_small].concat();
let classic_pbs = all_classic_pbs
.iter()
.map(|p| ShortintParameterSet::from(*p))
.collect::<Vec<_>>();
write_all_params_in_file(
"shortint_classic_parameters_lattice_estimator.sage",
&classic_pbs,
);
let multi_bit_pbs = ALL_MULTI_BIT_PARAMETER_VEC
.iter()
.map(|p| ShortintParameterSet::from(*p))
.collect::<Vec<_>>();
write_all_params_in_file(
"shortint_multi_bit_parameters_lattice_estimator.sage",
&multi_bit_pbs,
);
// TODO perform this gathering later
// let wopbs = ALL_PARAMETER_VEC_WOPBS
// .iter()
// .map(|p| ShortintParameterSet::from(*p))
// .collect::<Vec<_>>();
// write_all_params_in_file(
// "shortint_wopbs_parameters_lattice_estimator.sage",
// &wopbs,
// );
}

View File

@@ -5,7 +5,8 @@ use crate::utilities::{write_to_json, OperatorType};
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::Path;
use tfhe::shortint::keycache::{NamedParam, KEY_CACHE};
use tfhe::keycache::NamedParam;
use tfhe::shortint::keycache::KEY_CACHE;
use tfhe::shortint::parameters::{
PARAM_MESSAGE_1_CARRY_1_KS_PBS, PARAM_MESSAGE_2_CARRY_2_KS_PBS, PARAM_MESSAGE_3_CARRY_3_KS_PBS,
PARAM_MESSAGE_4_CARRY_4_KS_PBS, PARAM_MULTI_BIT_MESSAGE_1_CARRY_1_GROUP_2_KS_PBS,
@@ -15,7 +16,7 @@ use tfhe::shortint::parameters::{
PARAM_MULTI_BIT_MESSAGE_3_CARRY_3_GROUP_2_KS_PBS,
PARAM_MULTI_BIT_MESSAGE_3_CARRY_3_GROUP_3_KS_PBS,
};
use tfhe::shortint::PBSParameters;
use tfhe::shortint::{CompressedServerKey, PBSParameters};
fn write_result(file: &mut File, name: &str, value: usize) {
let line = format!("{name},{value}\n");
@@ -55,8 +56,7 @@ fn client_server_key_sizes(results_file: &Path) {
let keys = KEY_CACHE.get_from_param(params);
// Client keys don't have public access to members, but the keys in there are small anyways
// let cks = keys.client_key();
let cks = keys.client_key();
let sks = keys.server_key();
let ksk_size = sks.key_switching_key_size_bytes();
let test_name = format!("shortint_key_sizes_{}_ksk", params.name());
@@ -98,6 +98,31 @@ fn client_server_key_sizes(results_file: &Path) {
bsk_size,
);
let sks_compressed = CompressedServerKey::new(cks);
let bsk_compressed_size = sks_compressed
.bootstrapping_key
.bootstrapping_key_size_bytes();
let test_name = format!("shortint_key_sizes_{}_bsk_compressed", params.name());
write_result(&mut file, &test_name, bsk_compressed_size);
write_to_json::<u64, _>(
&test_name,
params,
params.name(),
"BSK",
&operator,
0,
vec![],
);
println!(
"Element in BSK compressed: {}, size in bytes: {}",
sks_compressed
.bootstrapping_key
.bootstrapping_key_size_elements(),
bsk_compressed_size,
);
// Clear keys as we go to avoid filling the RAM
KEY_CACHE.clear_in_memory_cache()
}

View File

@@ -9,9 +9,9 @@ use std::fs;
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::Path;
use tfhe::keycache::NamedParam;
use tfhe::shortint::keycache::{
NamedParam, PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS_NAME,
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_NAME,
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS_NAME, PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS_NAME,
};
use tfhe::shortint::parameters::{
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS, PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS,

View File

@@ -46,6 +46,9 @@ test('hlapi_panic', (t) => {
let clientKey = TfheClientKey.generate(config);
let clear = 73;
console.log("\nThe following log is an expected error log:\n=======================\n")
try {
let _ = FheUint8.encrypt_with_client_key(clear, clientKey);
assert(false);

View File

@@ -124,11 +124,11 @@ impl ClientKey {
/// ```rust
/// # fn main() {
/// use tfhe::boolean::client_key::ClientKey;
/// use tfhe::boolean::parameters::TFHE_LIB_PARAMETERS;
/// use tfhe::boolean::parameters::PARAMETERS_ERROR_PROB_2_POW_MINUS_165;
/// use tfhe::boolean::prelude::*;
///
/// // Generate the client key:
/// let cks = ClientKey::new(&TFHE_LIB_PARAMETERS);
/// let cks = ClientKey::new(&PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
/// # }
/// ```
pub fn new(parameter_set: &BooleanParameters) -> ClientKey {

View File

@@ -4,7 +4,7 @@ use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::computation_buffers::ComputationBuffers;
use crate::core_crypto::commons::generators::{DeterministicSeeder, EncryptionRandomGenerator};
use crate::core_crypto::commons::math::random::{ActivatedRandomGenerator, Seeder};
use crate::core_crypto::commons::parameters::CiphertextModulus;
use crate::core_crypto::commons::parameters::{CiphertextModulus, PBSOrder};
use crate::core_crypto::entities::*;
use crate::core_crypto::fft_impl::fft64::math::fft::Fft;
use serde::{Deserialize, Serialize};
@@ -19,24 +19,27 @@ struct Memory {
buffer: Vec<u32>,
}
pub struct BuffersRef<'a> {
pub(crate) lookup_table: GlweCiphertextMutView<'a, u32>,
// For the intermediate keyswitch result in the case of a big ciphertext
pub(crate) buffer_lwe_after_ks: LweCiphertextMutView<'a, u32>,
// For the intermediate PBS result in the case of a smallciphertext
pub(crate) buffer_lwe_after_pbs: LweCiphertextMutView<'a, u32>,
}
impl Memory {
/// Return a tuple with buffers that matches the server key.
///
/// - The first element is the accumulator for bootstrap step.
/// - The second element is a lwe buffer where the result of the of the bootstrap should be
/// written
fn as_buffers(
&mut self,
server_key: &ServerKey,
) -> (GlweCiphertextView<'_, u32>, LweCiphertextMutView<'_, u32>) {
fn as_buffers(&mut self, server_key: &ServerKey) -> BuffersRef<'_> {
let num_elem_in_accumulator = server_key.bootstrapping_key.glwe_size().0
* server_key.bootstrapping_key.polynomial_size().0;
let num_elem_in_lwe = server_key
let num_elem_in_lwe_after_ks = server_key.key_switching_key.output_lwe_size().0;
let num_elem_in_lwe_after_pbs = server_key
.bootstrapping_key
.output_lwe_dimension()
.to_lwe_size()
.0;
let total_elem_needed = num_elem_in_accumulator + num_elem_in_lwe;
let total_elem_needed =
num_elem_in_accumulator + num_elem_in_lwe_after_ks + num_elem_in_lwe_after_pbs;
let all_elements = if self.buffer.len() < total_elem_needed {
self.buffer.resize(total_elem_needed, 0u32);
@@ -45,30 +48,35 @@ impl Memory {
&mut self.buffer[..total_elem_needed]
};
let (accumulator_elements, lwe_elements) =
let (accumulator_elements, other_elements) =
all_elements.split_at_mut(num_elem_in_accumulator);
{
let mut accumulator = GlweCiphertextMutView::from_container(
accumulator_elements,
server_key.bootstrapping_key.polynomial_size(),
CiphertextModulus::new_native(),
);
accumulator.get_mut_mask().as_mut().fill(0u32);
accumulator.get_mut_body().as_mut().fill(PLAINTEXT_TRUE);
}
let accumulator = GlweCiphertextView::from_container(
let mut acc = GlweCiphertext::from_container(
accumulator_elements,
server_key.bootstrapping_key.polynomial_size(),
CiphertextModulus::new_native(),
);
let lwe =
LweCiphertextMutView::from_container(lwe_elements, CiphertextModulus::new_native());
acc.get_mut_mask().as_mut().fill(0u32);
acc.get_mut_body().as_mut().fill(PLAINTEXT_TRUE);
(accumulator, lwe)
let (after_ks_elements, after_pbs_elements) =
other_elements.split_at_mut(num_elem_in_lwe_after_ks);
let buffer_lwe_after_ks = LweCiphertextMutView::from_container(
after_ks_elements,
CiphertextModulus::new_native(),
);
let buffer_lwe_after_pbs = LweCiphertextMutView::from_container(
after_pbs_elements,
CiphertextModulus::new_native(),
);
BuffersRef {
lookup_table: acc,
buffer_lwe_after_ks,
buffer_lwe_after_pbs,
}
}
}
@@ -86,6 +94,7 @@ impl Memory {
pub struct ServerKey {
pub(crate) bootstrapping_key: FourierLweBootstrapKeyOwned,
pub(crate) key_switching_key: LweKeyswitchKeyOwned<u32>,
pub(crate) pbs_order: PBSOrder,
}
impl ServerKey {
@@ -120,6 +129,7 @@ impl ServerKey {
pub struct CompressedServerKey {
pub(crate) bootstrapping_key: SeededLweBootstrapKeyOwned<u32>,
pub(crate) key_switching_key: SeededLweKeyswitchKeyOwned<u32>,
pub(crate) pbs_order: PBSOrder,
}
/// Perform ciphertext bootstraps on the CPU
@@ -204,6 +214,7 @@ impl Bootstrapper {
Ok(ServerKey {
bootstrapping_key: fourier_bsk,
key_switching_key: ksk,
pbs_order: cks.parameters.encryption_key_choice.into(),
})
}
@@ -249,6 +260,7 @@ impl Bootstrapper {
Ok(CompressedServerKey {
bootstrapping_key,
key_switching_key,
pbs_order: cks.parameters.encryption_key_choice.into(),
})
}
@@ -257,7 +269,11 @@ impl Bootstrapper {
input: &LweCiphertextOwned<u32>,
server_key: &ServerKey,
) -> Result<LweCiphertextOwned<u32>, Box<dyn Error>> {
let (accumulator, mut buffer_after_pbs) = self.memory.as_buffers(server_key);
let BuffersRef {
lookup_table: accumulator,
mut buffer_lwe_after_pbs,
..
} = self.memory.as_buffers(server_key);
let fourier_bsk = &server_key.bootstrapping_key;
@@ -277,7 +293,7 @@ impl Bootstrapper {
programmable_bootstrap_lwe_ciphertext_mem_optimized(
input,
&mut buffer_after_pbs,
&mut buffer_lwe_after_pbs,
&accumulator,
fourier_bsk,
fft,
@@ -285,7 +301,7 @@ impl Bootstrapper {
);
Ok(LweCiphertext::from_container(
buffer_after_pbs.as_ref().to_owned(),
buffer_lwe_after_pbs.as_ref().to_owned(),
input.ciphertext_modulus(),
))
}
@@ -315,7 +331,11 @@ impl Bootstrapper {
mut ciphertext: LweCiphertextOwned<u32>,
server_key: &ServerKey,
) -> Result<Ciphertext, Box<dyn Error>> {
let (accumulator, mut buffer_lwe_after_pbs) = self.memory.as_buffers(server_key);
let BuffersRef {
lookup_table,
mut buffer_lwe_after_pbs,
..
} = self.memory.as_buffers(server_key);
let fourier_bsk = &server_key.bootstrapping_key;
@@ -337,7 +357,7 @@ impl Bootstrapper {
programmable_bootstrap_lwe_ciphertext_mem_optimized(
&ciphertext,
&mut buffer_lwe_after_pbs,
&accumulator,
&lookup_table,
fourier_bsk,
fft,
stack,
@@ -352,6 +372,63 @@ impl Bootstrapper {
Ok(Ciphertext::Encrypted(ciphertext))
}
pub(crate) fn keyswitch_bootstrap(
&mut self,
mut ciphertext: LweCiphertextOwned<u32>,
server_key: &ServerKey,
) -> Result<Ciphertext, Box<dyn Error>> {
let BuffersRef {
lookup_table,
mut buffer_lwe_after_ks,
..
} = self.memory.as_buffers(server_key);
let fourier_bsk = &server_key.bootstrapping_key;
let fft = Fft::new(fourier_bsk.polynomial_size());
let fft = fft.as_view();
self.computation_buffers.resize(
programmable_bootstrap_lwe_ciphertext_mem_optimized_requirement::<u64>(
fourier_bsk.glwe_size(),
fourier_bsk.polynomial_size(),
fft,
)
.unwrap()
.unaligned_bytes_required(),
);
let stack = self.computation_buffers.stack();
// Keyswitch from large LWE key to the small one
keyswitch_lwe_ciphertext(
&server_key.key_switching_key,
&ciphertext,
&mut buffer_lwe_after_ks,
);
// Compute a bootstrap
programmable_bootstrap_lwe_ciphertext_mem_optimized(
&buffer_lwe_after_ks,
&mut ciphertext,
&lookup_table,
fourier_bsk,
fft,
stack,
);
Ok(Ciphertext::Encrypted(ciphertext))
}
pub(crate) fn apply_bootstrapping_pattern(
&mut self,
ct: LweCiphertextOwned<u32>,
server_key: &ServerKey,
) -> Result<Ciphertext, Box<dyn Error>> {
match server_key.pbs_order {
PBSOrder::KeyswitchBootstrap => self.keyswitch_bootstrap(ct, server_key),
PBSOrder::BootstrapKeyswitch => self.bootstrap_keyswitch(ct, server_key),
}
}
}
impl From<CompressedServerKey> for ServerKey {
@@ -359,6 +436,7 @@ impl From<CompressedServerKey> for ServerKey {
let CompressedServerKey {
key_switching_key,
bootstrapping_key,
pbs_order,
} = compressed_server_key;
let key_switching_key = key_switching_key.decompress_into_lwe_keyswitch_key();
@@ -380,6 +458,7 @@ impl From<CompressedServerKey> for ServerKey {
Self {
key_switching_key,
bootstrapping_key,
pbs_order,
}
}
}

View File

@@ -4,7 +4,9 @@
//! underlying `core_crypto` module.
use crate::boolean::ciphertext::{Ciphertext, CompressedCiphertext};
use crate::boolean::parameters::{BooleanKeySwitchingParameters, BooleanParameters};
use crate::boolean::parameters::{
BooleanKeySwitchingParameters, BooleanParameters, EncryptionKeyChoice,
};
use crate::boolean::{ClientKey, CompressedPublicKey, PublicKey, PLAINTEXT_FALSE, PLAINTEXT_TRUE};
use crate::core_crypto::algorithms::*;
use crate::core_crypto::entities::*;
@@ -15,7 +17,7 @@ use crate::core_crypto::commons::generators::{
DeterministicSeeder, EncryptionRandomGenerator, SecretRandomGenerator,
};
use crate::core_crypto::commons::math::random::{ActivatedRandomGenerator, Seeder};
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::parameters::{PBSOrder, *};
use crate::core_crypto::seeders::new_seeder;
#[cfg(test)]
@@ -101,39 +103,46 @@ impl BooleanEngine {
}
pub fn create_server_key(&mut self, cks: &ClientKey) -> ServerKey {
let server_key = self.bootstrapper.new_server_key(cks).unwrap();
server_key
self.bootstrapper.new_server_key(cks).unwrap()
}
pub fn create_compressed_server_key(&mut self, cks: &ClientKey) -> CompressedServerKey {
let server_key = self.bootstrapper.new_compressed_server_key(cks).unwrap();
server_key
self.bootstrapper.new_compressed_server_key(cks).unwrap()
}
pub fn create_public_key(&mut self, client_key: &ClientKey) -> PublicKey {
let client_parameters = client_key.parameters;
let (lwe_sk, encryption_noise) = match client_parameters.encryption_key_choice {
EncryptionKeyChoice::Big => (
client_key.glwe_secret_key.as_lwe_secret_key(),
client_key.parameters.glwe_modular_std_dev,
),
EncryptionKeyChoice::Small => {
let view = LweSecretKey::from_container(client_key.lwe_secret_key.as_ref());
(view, client_key.parameters.lwe_modular_std_dev)
}
};
// Formula is (n + 1) * log2(q) + 128
let zero_encryption_count = LwePublicKeyZeroEncryptionCount(
client_parameters.lwe_dimension.to_lwe_size().0 * LOG2_Q_32 + 128,
lwe_sk.lwe_dimension().to_lwe_size().0 * LOG2_Q_32 + 128,
);
#[cfg(not(feature = "__wasm_api"))]
let lwe_public_key: LwePublicKeyOwned<u32> = par_allocate_and_generate_new_lwe_public_key(
&client_key.lwe_secret_key,
&lwe_sk,
zero_encryption_count,
client_key.parameters.lwe_modular_std_dev,
encryption_noise,
CiphertextModulus::new_native(),
&mut self.encryption_generator,
);
#[cfg(feature = "__wasm_api")]
let lwe_public_key: LwePublicKeyOwned<u32> = allocate_and_generate_new_lwe_public_key(
&client_key.lwe_secret_key,
&lwe_sk,
zero_encryption_count,
client_key.parameters.lwe_modular_std_dev,
encryption_noise,
CiphertextModulus::new_native(),
&mut self.encryption_generator,
);
@@ -147,25 +156,36 @@ impl BooleanEngine {
pub fn create_compressed_public_key(&mut self, client_key: &ClientKey) -> CompressedPublicKey {
let client_parameters = client_key.parameters;
let (lwe_sk, encryption_noise) = match client_parameters.encryption_key_choice {
EncryptionKeyChoice::Big => (
client_key.glwe_secret_key.as_lwe_secret_key(),
client_key.parameters.glwe_modular_std_dev,
),
EncryptionKeyChoice::Small => {
let view = LweSecretKey::from_container(client_key.lwe_secret_key.as_ref());
(view, client_key.parameters.lwe_modular_std_dev)
}
};
// Formula is (n + 1) * log2(q) + 128
let zero_encryption_count = LwePublicKeyZeroEncryptionCount(
client_parameters.lwe_dimension.to_lwe_size().0 * LOG2_Q_32 + 128,
lwe_sk.lwe_dimension().to_lwe_size().0 * LOG2_Q_32 + 128,
);
#[cfg(not(feature = "__wasm_api"))]
let compressed_lwe_public_key = par_allocate_and_generate_new_seeded_lwe_public_key(
&client_key.lwe_secret_key,
&lwe_sk,
zero_encryption_count,
client_key.parameters.lwe_modular_std_dev,
encryption_noise,
CiphertextModulus::new_native(),
&mut self.bootstrapper.seeder,
);
#[cfg(feature = "__wasm_api")]
let compressed_lwe_public_key = allocate_and_generate_new_seeded_lwe_public_key(
&client_key.lwe_secret_key,
&lwe_sk,
zero_encryption_count,
client_key.parameters.lwe_modular_std_dev,
encryption_noise,
CiphertextModulus::new_native(),
&mut self.bootstrapper.seeder,
);
@@ -182,10 +202,31 @@ impl BooleanEngine {
cks2: &ClientKey,
params: BooleanKeySwitchingParameters,
) -> LweKeyswitchKeyOwned<u32> {
let (lwe_sk1, lwe_sk2) = match (
cks1.parameters.encryption_key_choice,
cks2.parameters.encryption_key_choice,
) {
(EncryptionKeyChoice::Big, EncryptionKeyChoice::Big) => (
cks1.glwe_secret_key.as_lwe_secret_key(),
cks2.glwe_secret_key.as_lwe_secret_key(),
),
(EncryptionKeyChoice::Small, EncryptionKeyChoice::Small) => {
let view1 = LweSecretKey::from_container(cks1.lwe_secret_key.as_ref());
let view2 = LweSecretKey::from_container(cks2.lwe_secret_key.as_ref());
(view1, view2)
}
(choice1, choice2) => panic!(
"EncryptionKeyChoice of cks1 and cks2 must be the same.\
cks1 has {:?}, cks2 has: {:?}
",
choice1, choice2
),
};
// Creation of the key switching key
allocate_and_generate_new_lwe_keyswitch_key(
&cks1.lwe_secret_key,
&cks2.lwe_secret_key,
&lwe_sk1,
&lwe_sk2,
params.ks_base_log,
params.ks_level,
cks2.parameters.lwe_modular_std_dev,
@@ -206,11 +247,22 @@ impl BooleanEngine {
Plaintext(PLAINTEXT_FALSE)
};
let (lwe_sk, encryption_noise) = match cks.parameters.encryption_key_choice {
EncryptionKeyChoice::Big => (
cks.glwe_secret_key.as_lwe_secret_key(),
cks.parameters.glwe_modular_std_dev,
),
EncryptionKeyChoice::Small => {
let view = LweSecretKey::from_container(cks.lwe_secret_key.as_ref());
(view, cks.parameters.lwe_modular_std_dev)
}
};
// encryption
let ct = allocate_and_encrypt_new_lwe_ciphertext(
&cks.lwe_secret_key,
&lwe_sk,
plain,
cks.parameters.lwe_modular_std_dev,
encryption_noise,
CiphertextModulus::new_native(),
&mut self.encryption_generator,
);
@@ -226,11 +278,22 @@ impl BooleanEngine {
Plaintext(PLAINTEXT_FALSE)
};
let (lwe_sk, encryption_noise) = match cks.parameters.encryption_key_choice {
EncryptionKeyChoice::Big => (
cks.glwe_secret_key.as_lwe_secret_key(),
cks.parameters.glwe_modular_std_dev,
),
EncryptionKeyChoice::Small => {
let view = LweSecretKey::from_container(cks.lwe_secret_key.as_ref());
(view, cks.parameters.lwe_modular_std_dev)
}
};
// encryption
let ct = allocate_and_encrypt_new_seeded_lwe_ciphertext(
&cks.lwe_secret_key,
&lwe_sk,
plain,
cks.parameters.lwe_modular_std_dev,
encryption_noise,
CiphertextModulus::new_native(),
&mut self.bootstrapper.seeder,
);
@@ -248,7 +311,7 @@ impl BooleanEngine {
let mut output = LweCiphertext::new(
0u32,
pks.parameters.lwe_dimension.to_lwe_size(),
pks.lwe_public_key.lwe_size(),
CiphertextModulus::new_native(),
);
@@ -275,7 +338,7 @@ impl BooleanEngine {
let mut output = LweCiphertext::new(
0u32,
compressed_pk.parameters.lwe_dimension.to_lwe_size(),
compressed_pk.compressed_lwe_public_key.lwe_size(),
CiphertextModulus::new_native(),
);
@@ -293,8 +356,15 @@ impl BooleanEngine {
match ct {
Ciphertext::Trivial(b) => *b,
Ciphertext::Encrypted(ciphertext) => {
let lwe_sk = match cks.parameters.encryption_key_choice {
EncryptionKeyChoice::Big => cks.glwe_secret_key.as_lwe_secret_key(),
EncryptionKeyChoice::Small => {
LweSecretKey::from_container(cks.lwe_secret_key.as_ref())
}
};
// decryption
let decrypted = decrypt_lwe_ciphertext(&cks.lwe_secret_key, ciphertext);
let decrypted = decrypt_lwe_ciphertext(&lwe_sk, ciphertext);
// cast as a u32
let decrypted_u32 = decrypted.0;
@@ -404,11 +474,20 @@ impl BooleanEngine {
} else {
Plaintext(PLAINTEXT_FALSE)
};
allocate_and_trivially_encrypt_new_lwe_ciphertext(
server_key
let lwe_size = match server_key.pbs_order {
PBSOrder::KeyswitchBootstrap => server_key
.key_switching_key
.input_key_lwe_dimension()
.to_lwe_size(),
PBSOrder::BootstrapKeyswitch => server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
};
allocate_and_trivially_encrypt_new_lwe_ciphertext(
lwe_size,
plain,
CiphertextModulus::new_native(),
)
@@ -463,10 +542,7 @@ impl BooleanEngine {
let mut buffer_lwe_before_pbs_o = LweCiphertext::new(
0u32,
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
ct_condition_ct.lwe_size(),
ct_condition_ct.ciphertext_modulus(),
);
@@ -487,23 +563,47 @@ impl BooleanEngine {
let cst = Plaintext(PLAINTEXT_FALSE);
lwe_ciphertext_plaintext_add_assign(&mut ct_temp_2, cst); // - 1/8
// Compute the first programmable bootstrapping with fixed test polynomial:
let mut ct_pbs_1 = bootstrapper
.bootstrap(buffer_lwe_before_pbs, server_key)
.unwrap();
match server_key.pbs_order {
PBSOrder::KeyswitchBootstrap => {
let ct_ks_1 = bootstrapper
.keyswitch(buffer_lwe_before_pbs, server_key)
.unwrap();
let ct_pbs_2 = bootstrapper.bootstrap(&ct_temp_2, server_key).unwrap();
// Compute the first programmable bootstrapping with fixed test polynomial:
let mut ct_pbs_1 = bootstrapper.bootstrap(&ct_ks_1, server_key).unwrap();
// Compute the linear combination to add the two results:
// buffer_lwe_pbs + ct_pbs_2 + (0,...,0, +1/8)
lwe_ciphertext_add_assign(&mut ct_pbs_1, &ct_pbs_2); // + buffer_lwe_pbs
let cst = Plaintext(PLAINTEXT_TRUE);
lwe_ciphertext_plaintext_add_assign(&mut ct_pbs_1, cst); // + 1/8
let ct_ks_2 = bootstrapper.keyswitch(&ct_temp_2, server_key).unwrap();
let ct_pbs_2 = bootstrapper.bootstrap(&ct_ks_2, server_key).unwrap();
let ct_ks = bootstrapper.keyswitch(&ct_pbs_1, server_key).unwrap();
// Compute the linear combination to add the two results:
// buffer_lwe_pbs + ct_pbs_2 + (0,...,0, +1/8)
lwe_ciphertext_add_assign(&mut ct_pbs_1, &ct_pbs_2); // + buffer_lwe_pbs
let cst = Plaintext(PLAINTEXT_TRUE);
lwe_ciphertext_plaintext_add_assign(&mut ct_pbs_1, cst); // + 1/8
// Output the result:
Ciphertext::Encrypted(ct_ks)
// Output the result:
Ciphertext::Encrypted(ct_pbs_1)
}
PBSOrder::BootstrapKeyswitch => {
// Compute the first programmable bootstrapping with fixed test polynomial:
let mut ct_pbs_1 = bootstrapper
.bootstrap(buffer_lwe_before_pbs, server_key)
.unwrap();
let ct_pbs_2 = bootstrapper.bootstrap(&ct_temp_2, server_key).unwrap();
// Compute the linear combination to add the two results:
// buffer_lwe_pbs + ct_pbs_2 + (0,...,0, +1/8)
lwe_ciphertext_add_assign(&mut ct_pbs_1, &ct_pbs_2); // + buffer_lwe_pbs
let cst = Plaintext(PLAINTEXT_TRUE);
lwe_ciphertext_plaintext_add_assign(&mut ct_pbs_1, cst); // + 1/8
let ct_ks = bootstrapper.keyswitch(&ct_pbs_1, server_key).unwrap();
// Output the result:
Ciphertext::Encrypted(ct_ks)
}
}
}
}
}
@@ -529,10 +629,7 @@ impl BinaryGatesEngine<&Ciphertext, &Ciphertext, ServerKey> for BooleanEngine {
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = LweCiphertext::new(
0u32,
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
ct_left_ct.lwe_size(),
ct_left_ct.ciphertext_modulus(),
);
@@ -547,7 +644,7 @@ impl BinaryGatesEngine<&Ciphertext, &Ciphertext, ServerKey> for BooleanEngine {
// compute the bootstrap and the key switch
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.apply_bootstrapping_pattern(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
@@ -572,10 +669,7 @@ impl BinaryGatesEngine<&Ciphertext, &Ciphertext, ServerKey> for BooleanEngine {
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = LweCiphertext::new(
0u32,
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
ct_left_ct.lwe_size(),
ct_left_ct.ciphertext_modulus(),
);
let bootstrapper = &mut self.bootstrapper;
@@ -590,7 +684,7 @@ impl BinaryGatesEngine<&Ciphertext, &Ciphertext, ServerKey> for BooleanEngine {
// compute the bootstrap and the key switch
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.apply_bootstrapping_pattern(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
@@ -615,10 +709,7 @@ impl BinaryGatesEngine<&Ciphertext, &Ciphertext, ServerKey> for BooleanEngine {
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = LweCiphertext::new(
0u32,
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
ct_left_ct.lwe_size(),
ct_left_ct.ciphertext_modulus(),
);
let bootstrapper = &mut self.bootstrapper;
@@ -634,7 +725,7 @@ impl BinaryGatesEngine<&Ciphertext, &Ciphertext, ServerKey> for BooleanEngine {
// compute the bootstrap and the key switch
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.apply_bootstrapping_pattern(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
@@ -659,10 +750,7 @@ impl BinaryGatesEngine<&Ciphertext, &Ciphertext, ServerKey> for BooleanEngine {
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = LweCiphertext::new(
0u32,
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
ct_left_ct.lwe_size(),
ct_left_ct.ciphertext_modulus(),
);
let bootstrapper = &mut self.bootstrapper;
@@ -676,7 +764,7 @@ impl BinaryGatesEngine<&Ciphertext, &Ciphertext, ServerKey> for BooleanEngine {
// compute the bootstrap and the key switch
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.apply_bootstrapping_pattern(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
@@ -701,10 +789,7 @@ impl BinaryGatesEngine<&Ciphertext, &Ciphertext, ServerKey> for BooleanEngine {
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = LweCiphertext::new(
0u32,
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
ct_left_ct.lwe_size(),
ct_left_ct.ciphertext_modulus(),
);
let bootstrapper = &mut self.bootstrapper;
@@ -721,7 +806,7 @@ impl BinaryGatesEngine<&Ciphertext, &Ciphertext, ServerKey> for BooleanEngine {
// compute the bootstrap and the key switch
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.apply_bootstrapping_pattern(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
@@ -746,10 +831,7 @@ impl BinaryGatesEngine<&Ciphertext, &Ciphertext, ServerKey> for BooleanEngine {
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = LweCiphertext::new(
0u32,
server_key
.bootstrapping_key
.input_lwe_dimension()
.to_lwe_size(),
ct_left_ct.lwe_size(),
ct_left_ct.ciphertext_modulus(),
);
let bootstrapper = &mut self.bootstrapper;
@@ -768,7 +850,7 @@ impl BinaryGatesEngine<&Ciphertext, &Ciphertext, ServerKey> for BooleanEngine {
// compute the bootstrap and the key switch
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.apply_bootstrapping_pattern(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}

View File

@@ -20,11 +20,27 @@
pub use crate::core_crypto::commons::dispersion::StandardDev;
pub use crate::core_crypto::commons::parameters::{
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
DecompositionBaseLog, DecompositionLevelCount, EncryptionKeyChoice, GlweDimension,
LweDimension, PolynomialSize,
};
#[cfg(any(test, doctest, feature = "internal-keycache"))]
use crate::keycache::NamedParam;
use serde::{Deserialize, Serialize};
/// A set of cryptographic parameters for homomorphic Boolean circuit evaluation.
/// The choice of encryption key for (`boolean ciphertext`)[`super::ciphertext::Ciphertext`].
///
/// * The `Big` choice means the big LWE key derived from the GLWE key is used to encrypt the input
/// ciphertext. This offers better performance but the (`public
/// key`)[`super::public_key::PublicKey`] can be extremely large and in some cases may not fit in
/// memory. When refreshing a ciphertext and/or evaluating a table lookup the PBS is computed
/// first followed by a keyswitch.
/// * The `Small` choice means the small LWE key is used to encrypt the input ciphertext.
/// Performance is not as good as in the `Big` case but (`public
/// key`)[`super::public_key::PublicKey`] sizes are much more manageable and shoud always fit in
/// memory. When refreshing a ciphertext and/or evaluating a table lookup the keyswitch is
/// computed first followed by a PBS.
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct BooleanParameters {
pub lwe_dimension: LweDimension,
@@ -36,6 +52,7 @@ pub struct BooleanParameters {
pub pbs_level: DecompositionLevelCount,
pub ks_base_log: DecompositionBaseLog,
pub ks_level: DecompositionLevelCount,
pub encryption_key_choice: EncryptionKeyChoice,
}
impl BooleanParameters {
@@ -58,6 +75,7 @@ impl BooleanParameters {
pbs_level: DecompositionLevelCount,
ks_base_log: DecompositionBaseLog,
ks_level: DecompositionLevelCount,
encryption_key_choice: EncryptionKeyChoice,
) -> BooleanParameters {
BooleanParameters {
lwe_dimension,
@@ -69,6 +87,7 @@ impl BooleanParameters {
pbs_level,
ks_level,
ks_base_log,
encryption_key_choice,
}
}
}
@@ -79,6 +98,7 @@ pub struct BooleanKeySwitchingParameters {
pub ks_base_log: DecompositionBaseLog,
pub ks_level: DecompositionLevelCount,
}
impl BooleanKeySwitchingParameters {
/// Constructs a new set of parameters for boolean circuit evaluation.
///
@@ -106,30 +126,87 @@ impl BooleanKeySwitchingParameters {
/// This parameter set allows to evaluate faster Boolean circuits than the `TFHE_LIB_PARAMETERS`
/// one.
pub const DEFAULT_PARAMETERS: BooleanParameters = BooleanParameters {
lwe_dimension: LweDimension(777),
glwe_dimension: GlweDimension(3),
lwe_dimension: LweDimension(722),
glwe_dimension: GlweDimension(2),
polynomial_size: PolynomialSize(512),
lwe_modular_std_dev: StandardDev(0.000003725679281679651),
glwe_modular_std_dev: StandardDev(0.0000000000034525330484572114),
pbs_base_log: DecompositionBaseLog(18),
pbs_level: DecompositionLevelCount(1),
ks_base_log: DecompositionBaseLog(4),
ks_level: DecompositionLevelCount(3),
lwe_modular_std_dev: StandardDev(0.000013071021089943935),
glwe_modular_std_dev: StandardDev(0.00000004990272175010415),
pbs_base_log: DecompositionBaseLog(6),
pbs_level: DecompositionLevelCount(3),
ks_base_log: DecompositionBaseLog(3),
ks_level: DecompositionLevelCount(4),
encryption_key_choice: EncryptionKeyChoice::Small,
};
pub const DEFAULT_PARAMETERS_KS_PBS: BooleanParameters = BooleanParameters {
lwe_dimension: LweDimension(664),
glwe_dimension: GlweDimension(2),
polynomial_size: PolynomialSize(512),
lwe_modular_std_dev: StandardDev(0.00003808282923459771),
glwe_modular_std_dev: StandardDev(0.00000004990272175010415),
pbs_base_log: DecompositionBaseLog(6),
pbs_level: DecompositionLevelCount(3),
ks_base_log: DecompositionBaseLog(3),
ks_level: DecompositionLevelCount(4),
encryption_key_choice: EncryptionKeyChoice::Big,
};
/// The secret keys generated with this parameter set are uniform binary.
/// This parameter set ensures a probability of error upper-bounded by $2^{-165}$ as the ones
/// proposed into [TFHE library](https://tfhe.github.io/tfhe/) for for 128-bits of security.
/// They are updated to the last security standards, so they differ from the original
/// publication.
pub const TFHE_LIB_PARAMETERS: BooleanParameters = BooleanParameters {
lwe_dimension: LweDimension(830),
/// This parameter set ensures a probability of error upper-bounded by $2^{-165}$
/// for for 128-bits of security.
pub const PARAMETERS_ERROR_PROB_2_POW_MINUS_165: BooleanParameters = BooleanParameters {
lwe_dimension: LweDimension(767),
glwe_dimension: GlweDimension(2),
polynomial_size: PolynomialSize(1024),
lwe_modular_std_dev: StandardDev(0.000001412290588219445),
glwe_modular_std_dev: StandardDev(0.00000000000000029403601535432533),
pbs_base_log: DecompositionBaseLog(23),
pbs_level: DecompositionLevelCount(1),
ks_base_log: DecompositionBaseLog(5),
ks_level: DecompositionLevelCount(3),
lwe_modular_std_dev: StandardDev(0.000005104350373791501),
glwe_modular_std_dev: StandardDev(0.0000000009313225746154785),
pbs_base_log: DecompositionBaseLog(10),
pbs_level: DecompositionLevelCount(2),
ks_base_log: DecompositionBaseLog(3),
ks_level: DecompositionLevelCount(5),
encryption_key_choice: EncryptionKeyChoice::Small,
};
pub const PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS: BooleanParameters = BooleanParameters {
lwe_dimension: LweDimension(700),
glwe_dimension: GlweDimension(1),
polynomial_size: PolynomialSize(1024),
lwe_modular_std_dev: StandardDev(0.0000196095987892077),
glwe_modular_std_dev: StandardDev(0.00000004990272175010415),
pbs_base_log: DecompositionBaseLog(5),
pbs_level: DecompositionLevelCount(4),
ks_base_log: DecompositionBaseLog(2),
ks_level: DecompositionLevelCount(7),
encryption_key_choice: EncryptionKeyChoice::Big,
};
/// Parameter sets given in TFHE-lib:
/// <https://github.com/tfhe/tfhe/blob/bc71bfae7ad9d5f8ce5f29bdfd691189bfe207f3/src/libtfhe/tfhe_gate_bootstrapping.cpp#L51>
/// Original security in 2020 was 129-bits, while it is currently around 120 bits.
pub const TFHE_LIB_PARAMETERS: BooleanParameters = BooleanParameters {
lwe_dimension: LweDimension(630),
glwe_dimension: GlweDimension(1),
polynomial_size: PolynomialSize(1024),
lwe_modular_std_dev: StandardDev(0.000030517578125),
glwe_modular_std_dev: StandardDev(0.00000002980232238769531),
pbs_base_log: DecompositionBaseLog(7),
pbs_level: DecompositionLevelCount(3),
ks_base_log: DecompositionBaseLog(2),
ks_level: DecompositionLevelCount(8),
encryption_key_choice: EncryptionKeyChoice::Small,
};
pub const VEC_BOOLEAN_PARAM: [BooleanParameters; 2] = [DEFAULT_PARAMETERS, TFHE_LIB_PARAMETERS];
#[cfg(any(test, doctest, feature = "internal-keycache"))]
impl NamedParam for BooleanParameters {
fn name(&self) -> &'static str {
if *self == DEFAULT_PARAMETERS {
"DEFAULT_PARAMETERS"
} else if *self == TFHE_LIB_PARAMETERS {
"TFHE_LIB_PARAMETERS"
} else {
panic!("Unknown parameters, missing name implementation")
}
}
}

View File

@@ -90,7 +90,7 @@ impl CompressedPublicKey {
mod tests {
use crate::boolean::prelude::{
BinaryBooleanGates, BooleanParameters, ClientKey, CompressedPublicKey, ServerKey,
DEFAULT_PARAMETERS, TFHE_LIB_PARAMETERS,
DEFAULT_PARAMETERS, PARAMETERS_ERROR_PROB_2_POW_MINUS_165,
};
use crate::boolean::random_boolean;
const NB_TEST: usize = 32;
@@ -102,7 +102,7 @@ mod tests {
#[test]
fn test_compressed_public_key_tfhe_lib_parameters() {
test_compressed_public_key(TFHE_LIB_PARAMETERS);
test_compressed_public_key(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
}
fn test_compressed_public_key(parameters: BooleanParameters) {

View File

@@ -82,7 +82,7 @@ impl From<CompressedPublicKey> for PublicKey {
mod tests {
use crate::boolean::prelude::{
BinaryBooleanGates, BooleanParameters, ClientKey, CompressedPublicKey, ServerKey,
DEFAULT_PARAMETERS, TFHE_LIB_PARAMETERS,
DEFAULT_PARAMETERS, PARAMETERS_ERROR_PROB_2_POW_MINUS_165,
};
use crate::boolean::random_boolean;
@@ -96,7 +96,7 @@ mod tests {
#[test]
fn test_public_key_tfhe_lib_parameters() {
test_public_key(TFHE_LIB_PARAMETERS);
test_public_key(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
}
fn test_public_key(parameters: BooleanParameters) {
@@ -131,7 +131,7 @@ mod tests {
#[test]
fn test_decompressing_public_key_tfhe_lib_parameters() {
test_decompressing_public_key(TFHE_LIB_PARAMETERS);
test_decompressing_public_key(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
}
fn test_decompressing_public_key(parameters: BooleanParameters) {

View File

@@ -59,6 +59,144 @@ mod default_parameters_tests {
}
}
mod low_prob_parameters_tests {
use super::*;
use crate::boolean::parameters::PARAMETERS_ERROR_PROB_2_POW_MINUS_165;
#[test]
fn test_encrypt_decrypt_lwe_secret_key_low_prob() {
test_encrypt_decrypt_lwe_secret_key(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
}
#[test]
fn test_and_gate_low_prob() {
test_and_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
}
#[test]
fn test_nand_gate_low_prob() {
test_nand_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
}
#[test]
fn test_or_gate_low_prob() {
test_or_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
}
#[test]
fn test_nor_gate_low_prob() {
test_nor_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
}
#[test]
fn test_xor_gate_low_prob() {
test_xor_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
}
#[test]
fn test_xnor_gate_low_prob() {
test_xnor_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
}
#[test]
fn test_not_gate_low_prob() {
test_not_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
}
#[test]
fn test_mux_gate_low_prob() {
test_mux_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
}
#[test]
fn test_deep_circuit_low_prob() {
test_deep_circuit(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
}
}
mod default_parameters_ks_pbs_tests {
use super::*;
use crate::boolean::parameters::DEFAULT_PARAMETERS_KS_PBS;
#[test]
fn test_encrypt_decrypt_lwe_secret_key_default_parameters_ks_pbs() {
test_encrypt_decrypt_lwe_secret_key(DEFAULT_PARAMETERS_KS_PBS);
}
#[test]
fn test_and_gate_default_parameters_ks_pbs() {
test_and_gate(DEFAULT_PARAMETERS_KS_PBS);
}
#[test]
fn test_nand_gate_default_parameters_ks_pbs() {
test_nand_gate(DEFAULT_PARAMETERS_KS_PBS);
}
#[test]
fn test_or_gate_default_parameters_ks_pbs() {
test_or_gate(DEFAULT_PARAMETERS_KS_PBS);
}
#[test]
fn test_nor_gate_default_parameters_ks_pbs() {
test_nor_gate(DEFAULT_PARAMETERS_KS_PBS);
}
#[test]
fn test_xor_gate_default_parameters_ks_pbs() {
test_xor_gate(DEFAULT_PARAMETERS_KS_PBS);
}
#[test]
fn test_xnor_gate_default_parameters_ks_pbs() {
test_xnor_gate(DEFAULT_PARAMETERS_KS_PBS);
}
#[test]
fn test_not_gate_default_parameters_ks_pbs() {
test_not_gate(DEFAULT_PARAMETERS_KS_PBS);
}
#[test]
fn test_mux_gate_default_parameters_ks_pbs() {
test_mux_gate(DEFAULT_PARAMETERS_KS_PBS);
}
#[test]
fn test_deep_circuit_default_parameters_ks_pbs() {
test_deep_circuit(DEFAULT_PARAMETERS_KS_PBS);
}
}
mod low_prob_parameters_ks_pbs_tests {
use super::*;
use crate::boolean::parameters::PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS;
#[test]
fn test_encrypt_decrypt_lwe_secret_key_low_probability_ks_pbs() {
test_encrypt_decrypt_lwe_secret_key(PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS);
}
#[test]
fn test_and_gate_low_probability_ks_pbs() {
test_and_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS);
}
#[test]
fn test_nand_gate_low_probability_ks_pbs() {
test_nand_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS);
}
#[test]
fn test_or_gate_low_probability_ks_pbs() {
test_or_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS);
}
#[test]
fn test_nor_gate_low_probability_ks_pbs() {
test_nor_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS);
}
#[test]
fn test_xor_gate_low_probability_ks_pbs() {
test_xor_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS);
}
#[test]
fn test_xnor_gate_low_probability_ks_pbs() {
test_xnor_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS);
}
#[test]
fn test_not_gate_low_probability_ks_pbs() {
test_not_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS);
}
#[test]
fn test_mux_gate_low_probability_ks_pbs() {
test_mux_gate(PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS);
}
#[test]
fn test_deep_circuit_low_probability_ks_pbs() {
test_deep_circuit(PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS);
}
}
mod tfhe_lib_parameters_tests {
use super::*;
use crate::boolean::parameters::TFHE_LIB_PARAMETERS;

View File

@@ -1,6 +1,5 @@
use crate::c_api::buffer::*;
use crate::c_api::utils::*;
use bincode;
use std::os::raw::c_int;
use crate::boolean;

View File

@@ -3,6 +3,43 @@ use crate::core_crypto::commons::parameters::{
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
};
#[repr(C)]
#[derive(Copy, Clone)]
pub enum BooleanEncryptionKeyChoice {
BooleanEncryptionKeyChoiceBig,
BooleanEncryptionKeyChoiceSmall,
}
impl From<BooleanEncryptionKeyChoice>
for crate::core_crypto::commons::parameters::EncryptionKeyChoice
{
fn from(value: BooleanEncryptionKeyChoice) -> Self {
match value {
BooleanEncryptionKeyChoice::BooleanEncryptionKeyChoiceBig => {
crate::core_crypto::commons::parameters::EncryptionKeyChoice::Big
}
BooleanEncryptionKeyChoice::BooleanEncryptionKeyChoiceSmall => {
crate::core_crypto::commons::parameters::EncryptionKeyChoice::Small
}
}
}
}
impl BooleanEncryptionKeyChoice {
// From::from cannot be marked as const, so we have to have
// our own function
const fn convert(rust_choice: crate::shortint::EncryptionKeyChoice) -> Self {
match rust_choice {
crate::core_crypto::commons::parameters::EncryptionKeyChoice::Big => {
Self::BooleanEncryptionKeyChoiceBig
}
crate::core_crypto::commons::parameters::EncryptionKeyChoice::Small => {
Self::BooleanEncryptionKeyChoiceSmall
}
}
}
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct BooleanParameters {
@@ -15,6 +52,7 @@ pub struct BooleanParameters {
pub pbs_level: usize,
pub ks_base_log: usize,
pub ks_level: usize,
pub encryption_key_choice: BooleanEncryptionKeyChoice,
}
impl From<BooleanParameters> for crate::boolean::parameters::BooleanParameters {
@@ -29,6 +67,7 @@ impl From<BooleanParameters> for crate::boolean::parameters::BooleanParameters {
pbs_level: DecompositionLevelCount(c_params.pbs_level),
ks_base_log: DecompositionBaseLog(c_params.ks_base_log),
ks_level: DecompositionLevelCount(c_params.ks_level),
encryption_key_choice: c_params.encryption_key_choice.into(),
}
}
}
@@ -51,6 +90,9 @@ impl BooleanParameters {
pbs_level: rust_params.pbs_level.0,
ks_base_log: rust_params.ks_base_log.0,
ks_level: rust_params.ks_level.0,
encryption_key_choice: BooleanEncryptionKeyChoice::convert(
rust_params.encryption_key_choice,
),
}
}
}
@@ -61,4 +103,14 @@ pub static BOOLEAN_PARAMETERS_SET_DEFAULT_PARAMETERS: BooleanParameters =
#[no_mangle]
pub static BOOLEAN_PARAMETERS_SET_TFHE_LIB_PARAMETERS: BooleanParameters =
BooleanParameters::convert(crate::boolean::parameters::TFHE_LIB_PARAMETERS);
BooleanParameters::convert(crate::boolean::parameters::PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
#[no_mangle]
pub static BOOLEAN_PARAMETERS_SET_DEFAULT_PARAMETERS_KS_PBS: BooleanParameters =
BooleanParameters::convert(crate::boolean::parameters::DEFAULT_PARAMETERS_KS_PBS);
#[no_mangle]
pub static BOOLEAN_PARAMETERS_SET_TFHE_LIB_PARAMETERS_KS_PBS: BooleanParameters =
BooleanParameters::convert(
crate::boolean::parameters::PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS,
);

View File

@@ -1,6 +1,5 @@
use crate::c_api::buffer::*;
use crate::c_api::utils::*;
use bincode;
use std::os::raw::c_int;
use crate::boolean;

View File

@@ -104,6 +104,8 @@ macro_rules! impl_operations_for_integer_type {
) -> c_int {
$crate::c_api::utils::catch_panic(|| {
let lhs = $crate::c_api::utils::get_ref_checked(lhs).unwrap();
let rhs = <$clear_scalar_type as $crate::c_api::high_level_api::utils::ToRustScalarType
>::to_rust_scalar_type(rhs);
let (q, r) = (&lhs.0).div_rem(rhs);
@@ -132,6 +134,26 @@ macro_rules! impl_operations_for_integer_type {
})
}
}
::paste::paste! {
#[no_mangle]
pub unsafe extern "C" fn [<$name:snake _if_then_else>](
condition_ct: *const $name,
then_ct: *const $name,
else_ct: *const $name,
result: *mut *mut $name,
) -> c_int {
$crate::c_api::utils::catch_panic(|| {
let condition_ct = &$crate::c_api::utils::get_ref_checked(condition_ct).unwrap().0;
let then_ct = &$crate::c_api::utils::get_ref_checked(then_ct).unwrap().0;
let else_ct = &$crate::c_api::utils::get_ref_checked(else_ct).unwrap().0;
let r = condition_ct.if_then_else(then_ct, else_ct);
*result = Box::into_raw(Box::new($name(r)));
})
}
}
};
}
@@ -230,8 +252,8 @@ create_integer_wrapper_type!(name: FheUint14, clear_scalar_type: u16);
create_integer_wrapper_type!(name: FheUint16, clear_scalar_type: u16);
create_integer_wrapper_type!(name: FheUint32, clear_scalar_type: u32);
create_integer_wrapper_type!(name: FheUint64, clear_scalar_type: u64);
create_integer_wrapper_type!(name: FheUint128, clear_scalar_type: u64);
create_integer_wrapper_type!(name: FheUint256, clear_scalar_type: u64);
create_integer_wrapper_type!(name: FheUint128, clear_scalar_type: U128);
create_integer_wrapper_type!(name: FheUint256, clear_scalar_type: U256);
impl_decrypt_on_type!(FheUint8, u8);
impl_try_encrypt_trivial_on_type!(FheUint8{crate::high_level_api::FheUint8}, u8);

View File

@@ -274,6 +274,53 @@ macro_rules! impl_binary_assign_fn_on_type {
};
}
/// The C Standard only define integers from u8 to u64.
/// So to support u128 and u256 we had to create our own C friendly
/// data types.
///
/// This trait exists to be able to easily write wrapper function
/// by allowing to generically go from a C API scalar type to a rust one
pub(in crate::c_api::high_level_api) trait ToRustScalarType {
type RustScalarType;
fn to_rust_scalar_type(self) -> Self::RustScalarType;
}
/// Implements the trait for when the C API type is the same
/// as the Rust API type (eg u64)
macro_rules! impl_to_rust_scalar_type(
($type:ty) => {
impl ToRustScalarType for $type {
type RustScalarType = $type;
fn to_rust_scalar_type(self) -> Self::RustScalarType {
self
}
}
}
);
impl_to_rust_scalar_type!(u8);
impl_to_rust_scalar_type!(u16);
impl_to_rust_scalar_type!(u32);
impl_to_rust_scalar_type!(u64);
impl ToRustScalarType for crate::c_api::high_level_api::u128::U128 {
type RustScalarType = u128;
fn to_rust_scalar_type(self) -> Self::RustScalarType {
u128::from(self)
}
}
impl ToRustScalarType for crate::c_api::high_level_api::u256::U256 {
type RustScalarType = crate::integer::U256;
fn to_rust_scalar_type(self) -> Self::RustScalarType {
crate::integer::U256::from(self)
}
}
#[cfg(feature = "integer")]
macro_rules! impl_scalar_binary_fn_on_type {
($wrapper_type:ty, $scalar_type:ty => $($binary_fn_name:ident),* $(,)?) => {
@@ -287,6 +334,7 @@ macro_rules! impl_scalar_binary_fn_on_type {
) -> c_int {
$crate::c_api::utils::catch_panic(|| {
let lhs = $crate::c_api::utils::get_ref_checked(lhs).unwrap();
let rhs = <$scalar_type as $crate::c_api::high_level_api::utils::ToRustScalarType>::to_rust_scalar_type(rhs);
let inner = (&lhs.0).$binary_fn_name(rhs);
@@ -310,6 +358,8 @@ macro_rules! impl_scalar_binary_assign_fn_on_type {
) -> c_int {
$crate::c_api::utils::catch_panic(|| {
let lhs = $crate::c_api::utils::get_mut_checked(lhs).unwrap();
let rhs = <$scalar_type as $crate::c_api::high_level_api::utils::ToRustScalarType
>::to_rust_scalar_type(rhs);
lhs.0.$binary_assign_fn_name(rhs);
})

View File

@@ -1,6 +1,5 @@
use crate::c_api::buffer::*;
use crate::c_api::utils::*;
use bincode;
use std::os::raw::c_int;
use crate::shortint;

View File

@@ -1,6 +1,5 @@
use crate::c_api::buffer::*;
use crate::c_api::utils::*;
use bincode;
use std::os::raw::c_int;
use crate::shortint;

View File

@@ -280,7 +280,7 @@ pub unsafe extern "C" fn shortint_server_key_unchecked_not_equal(
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_greater(
server_key: *const ShortintServerKey,
ct_left: *const ShortintCiphertext,
ct_left: *mut ShortintCiphertext,
right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
@@ -288,9 +288,9 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_greater(
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let res = server_key.0.smart_scalar_greater(&ct_left.0, right);
let res = server_key.0.smart_scalar_greater(&mut ct_left.0, right);
let heap_allocated_ct_result = Box::new(ShortintCiphertext(res));
@@ -301,7 +301,7 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_greater(
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_greater_or_equal(
server_key: *const ShortintServerKey,
ct_left: *const ShortintCiphertext,
ct_left: *mut ShortintCiphertext,
right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
@@ -309,11 +309,11 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_greater_or_equal(
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let res = server_key
.0
.smart_scalar_greater_or_equal(&ct_left.0, right);
.smart_scalar_greater_or_equal(&mut ct_left.0, right);
let heap_allocated_ct_result = Box::new(ShortintCiphertext(res));
@@ -324,7 +324,7 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_greater_or_equal(
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_less(
server_key: *const ShortintServerKey,
ct_left: *const ShortintCiphertext,
ct_left: *mut ShortintCiphertext,
right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
@@ -332,9 +332,9 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_less(
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let res = server_key.0.smart_scalar_less(&ct_left.0, right);
let res = server_key.0.smart_scalar_less(&mut ct_left.0, right);
let heap_allocated_ct_result = Box::new(ShortintCiphertext(res));
@@ -345,7 +345,7 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_less(
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_less_or_equal(
server_key: *const ShortintServerKey,
ct_left: *const ShortintCiphertext,
ct_left: *mut ShortintCiphertext,
right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
@@ -353,9 +353,11 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_less_or_equal(
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let res = server_key.0.smart_scalar_less_or_equal(&ct_left.0, right);
let res = server_key
.0
.smart_scalar_less_or_equal(&mut ct_left.0, right);
let heap_allocated_ct_result = Box::new(ShortintCiphertext(res));
@@ -366,7 +368,7 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_less_or_equal(
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_equal(
server_key: *const ShortintServerKey,
ct_left: *const ShortintCiphertext,
ct_left: *mut ShortintCiphertext,
right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
@@ -374,9 +376,9 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_equal(
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let res = server_key.0.smart_scalar_equal(&ct_left.0, right);
let res = server_key.0.smart_scalar_equal(&mut ct_left.0, right);
let heap_allocated_ct_result = Box::new(ShortintCiphertext(res));
@@ -387,7 +389,7 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_equal(
#[no_mangle]
pub unsafe extern "C" fn shortint_server_key_smart_scalar_not_equal(
server_key: *const ShortintServerKey,
ct_left: *const ShortintCiphertext,
ct_left: *mut ShortintCiphertext,
right: u8,
result: *mut *mut ShortintCiphertext,
) -> c_int {
@@ -395,9 +397,9 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_not_equal(
check_ptr_is_non_null_and_aligned(result).unwrap();
let server_key = get_ref_checked(server_key).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let res = server_key.0.smart_scalar_not_equal(&ct_left.0, right);
let res = server_key.0.smart_scalar_not_equal(&mut ct_left.0, right);
let heap_allocated_ct_result = Box::new(ShortintCiphertext(res));

View File

@@ -119,7 +119,7 @@ pub unsafe extern "C" fn shortint_server_key_generate_bivariate_pbs_lookup_table
pub unsafe extern "C" fn shortint_server_key_bivariate_programmable_bootstrap(
server_key: *const ShortintServerKey,
lookup_table: *const ShortintBivariatePBSLookupTable,
ct_left: *const ShortintCiphertext,
ct_left: *mut ShortintCiphertext,
ct_right: *mut ShortintCiphertext,
result: *mut *mut ShortintCiphertext,
) -> c_int {
@@ -132,14 +132,14 @@ pub unsafe extern "C" fn shortint_server_key_bivariate_programmable_bootstrap(
let server_key = get_ref_checked(server_key).unwrap();
let lookup_table = get_ref_checked(lookup_table).unwrap();
let ct_left = get_ref_checked(ct_left).unwrap();
let ct_left = get_mut_checked(ct_left).unwrap();
let ct_right = get_mut_checked(ct_right).unwrap();
let res = crate::shortint::engine::ShortintEngine::with_thread_local_mut(|engine| {
engine
.smart_apply_lookup_table_bivariate(
&server_key.0,
&ct_left.0,
&mut ct_left.0,
&mut ct_right.0,
&lookup_table.0,
)

View File

@@ -116,6 +116,26 @@ pub fn keyswitch_lwe_ciphertext<Scalar, KSKCont, InputCont, OutputCont>(
lwe_keyswitch_key.output_key_lwe_dimension(),
output_lwe_ciphertext.lwe_size().to_lwe_dimension(),
);
assert!(
lwe_keyswitch_key.ciphertext_modulus() == input_lwe_ciphertext.ciphertext_modulus(),
"Mismatched CiphertextModulus. \
LweKeyswitchKey CiphertextModulus: {:?}, input LweCiphertext CiphertextModulus {:?}.",
lwe_keyswitch_key.ciphertext_modulus(),
input_lwe_ciphertext.ciphertext_modulus()
);
assert!(
lwe_keyswitch_key.ciphertext_modulus() == output_lwe_ciphertext.ciphertext_modulus(),
"Mismatched CiphertextModulus. \
LweKeyswitchKey CiphertextModulus: {:?}, output LweCiphertext CiphertextModulus {:?}.",
lwe_keyswitch_key.ciphertext_modulus(),
output_lwe_ciphertext.ciphertext_modulus()
);
assert!(
lwe_keyswitch_key
.ciphertext_modulus()
.is_compatible_with_native_modulus(),
"This operation currently only supports power of 2 moduli"
);
// Clear the output ciphertext, as it will get updated gradually
output_lwe_ciphertext.as_mut().fill(Scalar::ZERO);

Some files were not shown because too many files have changed in this diff Show More