Compare commits
74 Commits
0.3.1
...
clean_smar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce625df1f6 | ||
|
|
5f16704fa8 | ||
|
|
d686cd86b5 | ||
|
|
a2a3f10341 | ||
|
|
d7ac3b6d15 | ||
|
|
44b9241c83 | ||
|
|
9df1725efc | ||
|
|
55d97ed794 | ||
|
|
d10c11720e | ||
|
|
f1e0742b9f | ||
|
|
e6136ca418 | ||
|
|
0805cd69e0 | ||
|
|
ae3c261d1e | ||
|
|
95dcf95e88 | ||
|
|
b92d6400f4 | ||
|
|
9ba27b4082 | ||
|
|
b164c90d75 | ||
|
|
ff893ca6ef | ||
|
|
2dd1e13dad | ||
|
|
4393fce861 | ||
|
|
4f10cfa6dd | ||
|
|
a6e4488de2 | ||
|
|
878f3fa448 | ||
|
|
90b887a56f | ||
|
|
7dc52cf4ef | ||
|
|
a69333ed37 | ||
|
|
ffad25449e | ||
|
|
6d471856c7 | ||
|
|
65749cb39b | ||
|
|
bf36316c12 | ||
|
|
d98bb0eb86 | ||
|
|
5747af6dce | ||
|
|
cf08436c7d | ||
|
|
241bddccaf | ||
|
|
8ce1984214 | ||
|
|
82ef430dfa | ||
|
|
2348303b26 | ||
|
|
a35386f740 | ||
|
|
3df542c5f8 | ||
|
|
37623eedf3 | ||
|
|
fa8cf73d57 | ||
|
|
872b20a4a1 | ||
|
|
4920e3b4df | ||
|
|
75a0881e9d | ||
|
|
f67effc359 | ||
|
|
e8eb82f7ae | ||
|
|
53018ddc36 | ||
|
|
9ef62acff1 | ||
|
|
12220b2a18 | ||
|
|
625c150dc1 | ||
|
|
304932a861 | ||
|
|
59181d4717 | ||
|
|
c5d93f4b38 | ||
|
|
7a465cd258 | ||
|
|
a0946ac509 | ||
|
|
5521f2a4a4 | ||
|
|
1a9e40c860 | ||
|
|
bc3e3e46a0 | ||
|
|
60c87b6d95 | ||
|
|
df4c9f511d | ||
|
|
69bfd6556f | ||
|
|
7fad91e429 | ||
|
|
0da30d5e58 | ||
|
|
97ce5b617a | ||
|
|
a4723b03f3 | ||
|
|
d24d484bcf | ||
|
|
9945cdd9b2 | ||
|
|
04a0aa0c31 | ||
|
|
898c305acd | ||
|
|
f5854db0b1 | ||
|
|
6a56af0b07 | ||
|
|
b7d830c57f | ||
|
|
9b983745da | ||
|
|
10b1305e66 |
@@ -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
|
||||
|
||||
|
||||
4
.github/workflows/aws_tfhe_fast_tests.yml
vendored
@@ -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 }}
|
||||
|
||||
4
.github/workflows/aws_tfhe_integer_tests.yml
vendored
@@ -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 }}
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
4
.github/workflows/aws_tfhe_tests.yml
vendored
@@ -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 }}
|
||||
|
||||
4
.github/workflows/aws_tfhe_wasm_tests.yml
vendored
@@ -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 }}
|
||||
|
||||
6
.github/workflows/boolean_benchmark.yml
vendored
@@ -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 }}
|
||||
|
||||
2
.github/workflows/cargo_build.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
|
||||
|
||||
- name: Run pcc checks
|
||||
run: |
|
||||
|
||||
2
.github/workflows/check_commit.yml
vendored
@@ -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
|
||||
|
||||
8
.github/workflows/integer_benchmark.yml
vendored
@@ -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 }}
|
||||
|
||||
128
.github/workflows/integer_full_benchmark.yml
vendored
Normal 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 }}
|
||||
@@ -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 }}
|
||||
|
||||
4
.github/workflows/m1_tests.yml
vendored
@@ -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 }}
|
||||
|
||||
4
.github/workflows/make_release.yml
vendored
@@ -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
@@ -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 }}
|
||||
6
.github/workflows/pbs_benchmark.yml
vendored
@@ -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 }}
|
||||
|
||||
14
.github/workflows/placeholder_workflow.yml
vendored
Normal 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"
|
||||
6
.github/workflows/shortint_benchmark.yml
vendored
@@ -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 }}
|
||||
|
||||
141
.github/workflows/shortint_full_benchmark.yml
vendored
Normal 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 }}
|
||||
6
.github/workflows/start_benchmarks.yml
vendored
@@ -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
|
||||
|
||||
42
.github/workflows/start_full_benchmarks.yml
vendored
Normal 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 }}
|
||||
2
.github/workflows/sync_on_push.yml
vendored
@@ -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
|
||||
|
||||
6
.github/workflows/wasm_client_benchmark.yml
vendored
@@ -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 }}
|
||||
|
||||
40
Makefile
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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 => (),
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
},
|
||||
|| {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#[allow(clippy::module_inception)]
|
||||
mod kreyvium;
|
||||
pub use kreyvium::KreyviumStream;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#[allow(clippy::module_inception)]
|
||||
mod static_deque;
|
||||
pub use static_deque::StaticDeque;
|
||||
mod static_byte_deque;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#[allow(clippy::module_inception)]
|
||||
mod trivium;
|
||||
pub use trivium::TriviumStream;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 => (),
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
},
|
||||
|| {
|
||||
|
||||
@@ -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
@@ -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")
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
10
ci/slab.toml
@@ -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"
|
||||
|
||||
@@ -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}" \
|
||||
|
||||
@@ -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]]
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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"),
|
||||
};
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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
|
After Width: | Height: | Size: 15 KiB |
BIN
tfhe/docs/_static/ciphertext-representation.png
vendored
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 23 KiB |
BIN
tfhe/docs/_static/fig6.png
vendored
|
Before Width: | Height: | Size: 15 KiB |
BIN
tfhe/docs/_static/fig7.png
vendored
|
Before Width: | Height: | Size: 12 KiB |
BIN
tfhe/docs/_static/fig8.png
vendored
|
Before Width: | Height: | Size: 12 KiB |
BIN
tfhe/docs/_static/integer-ciphertext.png
vendored
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 32 KiB |
BIN
tfhe/docs/_static/lwe.png
vendored
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 16 KiB |
BIN
tfhe/docs/_static/multisum.png
vendored
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
tfhe/docs/_static/overflow.png
vendored
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
tfhe/docs/_static/sha256.png
vendored
Normal file
|
After Width: | Height: | Size: 20 KiB |
@@ -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
|
||||
```
|
||||

|
||||
|
||||
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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -38,6 +38,7 @@ fn main() {
|
||||
DecompositionLevelCount(2),
|
||||
DecompositionBaseLog(2),
|
||||
DecompositionLevelCount(5),
|
||||
EncryptionKeyChoice::Small,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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$$
|
||||
|
||||

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

|
||||

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

|
||||

|
||||
|
||||
|
||||
### Security.
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
|
||||
@@ -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"
|
||||
```
|
||||
|
||||
|
||||
131
tfhe/docs/how_to/trait_bounds.md
Normal 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
184
tfhe/examples/utilities/params_to_file.rs
Normal 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,
|
||||
// );
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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);
|
||||
|
||||