mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-04-28 03:01:21 -04:00
Compare commits
13 Commits
feat/princ
...
mz/common_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffffce8079 | ||
|
|
80eb5d712a | ||
|
|
5ceb25bfc2 | ||
|
|
680c2e3eb6 | ||
|
|
8bc080355d | ||
|
|
0cc8d625e4 | ||
|
|
ec5d0da883 | ||
|
|
8ed5633300 | ||
|
|
cf07dcf6a3 | ||
|
|
20dad23256 | ||
|
|
d7380e4264 | ||
|
|
093ffb7699 | ||
|
|
c804b838cb |
@@ -4,9 +4,6 @@ ignore = [
|
||||
"RUSTSEC-2024-0436",
|
||||
# Ignoring unmaintained 'bincode' crate. Getting rid of it would be too complex on the short term.
|
||||
"RUSTSEC-2025-0141",
|
||||
# Ignoring unsoundness in 'rand' with custom logger. Rand update is currently blocked by
|
||||
# arkworks and we do not use custom loggers.
|
||||
"RUSTSEC-2026-0097",
|
||||
]
|
||||
|
||||
[output]
|
||||
|
||||
4
.github/workflows/aws_data_tests.yml
vendored
4
.github/workflows/aws_data_tests.yml
vendored
@@ -54,7 +54,7 @@ jobs:
|
||||
|
||||
- name: Retrieve data from cache
|
||||
id: retrieve-data-cache
|
||||
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||
with:
|
||||
path: |
|
||||
utils/tfhe-backward-compat-data/**/*.cbor
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
- name: Store data in cache
|
||||
if: steps.retrieve-data-cache.outputs.cache-hit != 'true'
|
||||
continue-on-error: true
|
||||
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||
with:
|
||||
path: |
|
||||
utils/tfhe-backward-compat-data/**/*.cbor
|
||||
|
||||
6
.github/workflows/aws_tfhe_fast_tests.yml
vendored
6
.github/workflows/aws_tfhe_fast_tests.yml
vendored
@@ -69,7 +69,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
dependencies:
|
||||
@@ -200,7 +200,7 @@ jobs:
|
||||
|
||||
- name: Node cache restoration
|
||||
id: node-cache
|
||||
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||
with:
|
||||
path: |
|
||||
~/.nvm
|
||||
@@ -213,7 +213,7 @@ jobs:
|
||||
make install_node
|
||||
|
||||
- name: Node cache save
|
||||
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||
if: steps.node-cache.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: |
|
||||
|
||||
2
.github/workflows/aws_tfhe_integer_tests.yml
vendored
2
.github/workflows/aws_tfhe_integer_tests.yml
vendored
@@ -56,7 +56,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
integer:
|
||||
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
integer:
|
||||
|
||||
2
.github/workflows/aws_tfhe_tests.yml
vendored
2
.github/workflows/aws_tfhe_tests.yml
vendored
@@ -78,7 +78,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
dependencies:
|
||||
|
||||
6
.github/workflows/aws_tfhe_wasm_tests.yml
vendored
6
.github/workflows/aws_tfhe_wasm_tests.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
wasm:
|
||||
@@ -92,7 +92,7 @@ jobs:
|
||||
|
||||
- name: Node cache restoration
|
||||
id: node-cache
|
||||
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||
with:
|
||||
path: |
|
||||
~/.nvm
|
||||
@@ -105,7 +105,7 @@ jobs:
|
||||
make install_node
|
||||
|
||||
- name: Node cache save
|
||||
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||
if: steps.node-cache.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: |
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
backward:
|
||||
|
||||
@@ -204,7 +204,7 @@ jobs:
|
||||
uses: foundry-rs/foundry-toolchain@8789b3e21e6c11b2697f5eb56eddae542f746c10
|
||||
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
@@ -232,7 +232,7 @@ jobs:
|
||||
working-directory: fhevm/coprocessor/fhevm-engine/tfhe-worker
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 20.x
|
||||
|
||||
|
||||
2
.github/workflows/benchmark_wasm_client.yml
vendored
2
.github/workflows/benchmark_wasm_client.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
wasm_bench:
|
||||
|
||||
@@ -124,7 +124,7 @@ jobs:
|
||||
|
||||
- name: Node cache restoration
|
||||
id: node-cache
|
||||
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||
with:
|
||||
path: |
|
||||
~/.nvm
|
||||
@@ -137,7 +137,7 @@ jobs:
|
||||
make install_node
|
||||
|
||||
- name: Node cache save
|
||||
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||
if: steps.node-cache.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: |
|
||||
|
||||
4
.github/workflows/cargo_build_common.yml
vendored
4
.github/workflows/cargo_build_common.yml
vendored
@@ -138,7 +138,7 @@ jobs:
|
||||
- name: Node cache restoration
|
||||
if: inputs.run-pcc-cpu-batch == 'pcc_batch_2'
|
||||
id: node-cache
|
||||
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||
with:
|
||||
path: |
|
||||
~/.nvm
|
||||
@@ -151,7 +151,7 @@ jobs:
|
||||
make install_node
|
||||
|
||||
- name: Node cache save
|
||||
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||
if: inputs.run-pcc-cpu-batch == 'pcc_batch_2' && steps.node-cache.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: |
|
||||
|
||||
2
.github/workflows/cargo_test_fft.yml
vendored
2
.github/workflows/cargo_test_fft.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
fft:
|
||||
|
||||
2
.github/workflows/cargo_test_ntt.yml
vendored
2
.github/workflows/cargo_test_ntt.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
ntt:
|
||||
|
||||
2
.github/workflows/ci_lint.yml
vendored
2
.github/workflows/ci_lint.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
echo "version=$(make zizmor_version)" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Check workflows security
|
||||
uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
|
||||
uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3
|
||||
with:
|
||||
advanced-security: 'false' # Print results directly in logs
|
||||
persona: pedantic
|
||||
|
||||
2
.github/workflows/code_coverage.yml
vendored
2
.github/workflows/code_coverage.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
tfhe:
|
||||
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
csprng:
|
||||
|
||||
2
.github/workflows/gpu_core_h100_tests.yml
vendored
2
.github/workflows/gpu_core_h100_tests.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
gpu:
|
||||
|
||||
2
.github/workflows/gpu_fast_tests.yml
vendored
2
.github/workflows/gpu_fast_tests.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
gpu:
|
||||
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
gpu:
|
||||
|
||||
2
.github/workflows/gpu_hlapi_h100_tests.yml
vendored
2
.github/workflows/gpu_hlapi_h100_tests.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
gpu:
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
gpu:
|
||||
|
||||
2
.github/workflows/gpu_memory_sanitizer.yml
vendored
2
.github/workflows/gpu_memory_sanitizer.yml
vendored
@@ -48,7 +48,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
gpu:
|
||||
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
gpu:
|
||||
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
gpu:
|
||||
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
gpu:
|
||||
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
gpu:
|
||||
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
gpu:
|
||||
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
gpu:
|
||||
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
gpu:
|
||||
|
||||
2
.github/workflows/gpu_zk_tests.yml
vendored
2
.github/workflows/gpu_zk_tests.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
gpu:
|
||||
|
||||
2
.github/workflows/hpu_hlapi_tests.yml
vendored
2
.github/workflows/hpu_hlapi_tests.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
hpu:
|
||||
|
||||
2
.github/workflows/make_release_tfhe.yml
vendored
2
.github/workflows/make_release_tfhe.yml
vendored
@@ -89,7 +89,7 @@ jobs:
|
||||
make build_web_js_api_parallel
|
||||
|
||||
- name: Authenticate on NPM
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: '24'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
4
.github/workflows/parameters_check.yml
vendored
4
.github/workflows/parameters_check.yml
vendored
@@ -53,7 +53,7 @@ jobs:
|
||||
|
||||
- name: Restore Sagemath image from cache
|
||||
id: docker-cache
|
||||
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||
with:
|
||||
path: /tmp/sagemath_image
|
||||
key: sagemath-image-${{ env.SAGEMATH_VERSION }}-${{ github.sha }}
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
- name: Store Sagemath image in cache
|
||||
if: steps.docker-cache.outputs.cache-hit != 'true'
|
||||
continue-on-error: true
|
||||
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||
with:
|
||||
path: /tmp/sagemath_image
|
||||
key: sagemath-image-${{ env.SAGEMATH_VERSION }}-${{ github.sha }}
|
||||
|
||||
@@ -27,7 +27,7 @@ members = [
|
||||
"utils/wasm-par-mq/web_tests",
|
||||
]
|
||||
|
||||
exclude = ["utils/tfhe-lints", "apps/trivium", "apps/princev2"]
|
||||
exclude = ["utils/tfhe-lints", "apps/trivium"]
|
||||
|
||||
[workspace.package]
|
||||
rust-version = "1.91.1"
|
||||
|
||||
18
Makefile
18
Makefile
@@ -288,7 +288,6 @@ fmt_internal: install_rs_check_toolchain
|
||||
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt $(FMT_CHECK)
|
||||
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" -Z unstable-options -C utils/tfhe-lints fmt $(FMT_CHECK)
|
||||
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" -Z unstable-options -C apps/trivium fmt $(FMT_CHECK)
|
||||
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" -Z unstable-options -C apps/princev2 fmt $(FMT_CHECK)
|
||||
for crate in `ls -1 $(BACKWARD_COMPAT_DATA_DIR)/crates/ | grep generate_`; do \
|
||||
echo "fmt $$crate"; \
|
||||
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" -Z unstable-options -C $(BACKWARD_COMPAT_DATA_DIR)/crates/$$crate fmt $(FMT_CHECK); \
|
||||
@@ -361,7 +360,7 @@ check_fmt_toml: install_taplo
|
||||
|
||||
.PHONY: check_typos # Check for typos in codebase
|
||||
check_typos: install_typos_checker
|
||||
@git ls-files ":!*.png" ":!*.cbor" ":!*.bcode" ":!*.ico" ":!*/twiddles.cu" | typos --file-list - && echo "No typos found"
|
||||
@git ls-files ":!*.png" ":!*.cbor" ":!*.bcode" ":!*.ico" ":!*/twiddles.cu" ":!*.hpu" | typos --file-list - && echo "No typos found"
|
||||
|
||||
.PHONY: clippy_gpu # Run clippy lints on tfhe with "gpu" enabled
|
||||
clippy_gpu: install_rs_check_toolchain
|
||||
@@ -514,11 +513,6 @@ clippy_trivium: install_rs_check_toolchain
|
||||
cd apps/trivium; RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy --all-targets \
|
||||
-p tfhe-trivium -- --no-deps -D warnings
|
||||
|
||||
.PHONY: clippy_princev2 # Run clippy lints on PRINCEv2 app
|
||||
clippy_princev2: install_rs_check_toolchain
|
||||
cd apps/princev2; RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy --all-targets \
|
||||
-p tfhe-princev2 -- --no-deps -D warnings
|
||||
|
||||
.PHONY: clippy_ws_tests # Run clippy on the workspace level tests
|
||||
clippy_ws_tests: install_rs_check_toolchain
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy --tests \
|
||||
@@ -613,7 +607,7 @@ clippy_test_vectors: install_rs_check_toolchain
|
||||
# MAKE SURE TO ALSO ADD IT TO A PCC BATCH BELOW
|
||||
.PHONY: clippy_all # Run all clippy targets
|
||||
clippy_all: clippy_rustdoc clippy clippy_boolean clippy_shortint clippy_integer clippy_all_targets \
|
||||
clippy_c_api clippy_js_wasm_api clippy_tasks clippy_core clippy_tfhe_csprng clippy_zk_pok clippy_zk_pok_wasm clippy_trivium clippy_princev2 \
|
||||
clippy_c_api clippy_js_wasm_api clippy_tasks clippy_core clippy_tfhe_csprng clippy_zk_pok clippy_zk_pok_wasm clippy_trivium \
|
||||
clippy_versionable clippy_safe_serialize clippy_tfhe_lints clippy_ws_tests clippy_bench clippy_param_dedup \
|
||||
clippy_test_vectors clippy_backward_compat_data clippy_wasm_par_mq
|
||||
|
||||
@@ -1800,6 +1794,13 @@ bench_boolean: install_rs_check_toolchain
|
||||
--bench boolean \
|
||||
--features=boolean,internal-keycache -p tfhe-benchmark
|
||||
|
||||
.PHONY: bench_common_mask # Run benchmarks for CM-PBS
|
||||
bench_common_mask: install_rs_check_toolchain
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
|
||||
--bench cm-bench \
|
||||
--features=experimental -p tfhe-benchmark
|
||||
|
||||
|
||||
.PHONY: bench_ks # Run benchmarks for keyswitch
|
||||
bench_ks: install_rs_check_toolchain
|
||||
RUSTFLAGS="$(RUSTFLAGS)" __TFHE_RS_PARAM_TYPE=$(BENCH_PARAM_TYPE) __TFHE_RS_PARAMS_SET=$(BENCH_PARAMS_SET) __TFHE_RS_BENCH_TYPE=$(BENCH_TYPE) \
|
||||
@@ -2293,7 +2294,6 @@ pcc_batch_6:
|
||||
$(call run_recipe_with_details,clippy_zk_pok)
|
||||
$(call run_recipe_with_details,clippy_zk_pok_wasm)
|
||||
$(call run_recipe_with_details,clippy_trivium)
|
||||
$(call run_recipe_with_details,clippy_princev2)
|
||||
$(call run_recipe_with_details,clippy_versionable)
|
||||
$(call run_recipe_with_details,clippy_safe_serialize)
|
||||
$(call run_recipe_with_details,clippy_param_dedup)
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
[package]
|
||||
name = "tfhe-princev2"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
rayon = "1.11.0"
|
||||
tfhe = { path = "../../tfhe", features = ["shortint"] }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.5.1"
|
||||
|
||||
[features]
|
||||
verbose-timings = []
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
opt-level = 3
|
||||
codegen-units = 1
|
||||
|
||||
[[bench]]
|
||||
name = "princev2"
|
||||
harness = false
|
||||
@@ -1,79 +0,0 @@
|
||||
# FHE implementation of PRINCEv2 using TFHE-rs
|
||||
|
||||
This crate implements homomorphic encryption and decryption of the PRINCEv2 block cipher [BEK+20] using TFHE-rs's shortint API. It takes FHE ciphertexts representing the plaintext (resp. ciphertext) block and the two halves of the PRINCEv2 key and produces FHE ciphertexts of the encrypted (resp. decrypted) block.
|
||||
|
||||
Inputs and outputs encrypt 64-bit integers that are represented as vectors of 2-bit nibbles, most significant nibble first, stacked in the lower part of the FHE message space of each ciphertext.
|
||||
|
||||
The cipher itself (a succession of S-box, Linear, Permutation, Xor layers) is evaluated under FHE using the `shortint` API, systematically operating on 4-bit lookup tables. More details on the FHE design can be found in [BJ26, Section 6].
|
||||
|
||||
## References
|
||||
|
||||
PRINCEv2 is specified in:
|
||||
> [BEK+20] Dusan Božilov, Maria Eichlseder, Miroslav Kneževic, Baptiste Lambin, Gregor Leander, Thorben Moos, Ventzislav Nikov, Shahram Rasoolzadeh, Yosuke Todo, and Friedrich Wiemer. *PRINCEv2: More security for (almost) no overhead.* In Selected Areas in Cryptography (SAC 2020), volume 12804 of LNCS, pp.483--511, Springer, 2020. DOI:10.1007/978-3-030-81652-0_19.
|
||||
|
||||
Test vectors are those of Appendix B of the paper.
|
||||
|
||||
More details on the FHE implementation design can be found in Section 6 of:
|
||||
> [BJ26] Olivier Bernard and Marc Joye. *Hash function constructions from lightweight block ciphers for fully homomorphic encryption*. Cryptology ePrint Archive, ePrint:2026/309, 2026.
|
||||
|
||||
## Layout
|
||||
|
||||
- `src/u64_conv.rs` — plaintext-side conversions between `u64` and the 32-element 2-bit-nibble vectors used on the FHE side; it exposes `u64_to_vec_u2` and `vec_u2_to_u64` as part of the encoding contract for the underlying plaintexts of the inputs and outputs.
|
||||
- `src/permute.rs` — generic permutation helper over ciphertext arrays.
|
||||
- `src/pv2_lut.rs` — precomputed S-box, inverse S-box, M-layer and round-constant lookup tables.
|
||||
- `src/pv2_cipher.rs` — the homomorphic round functions and the public `pv2_encrypt` / `pv2_decrypt` entry points.
|
||||
- `tests/pv2_kat.rs` — known-answer tests against the paper vectors.
|
||||
- `benches/princev2.rs` — benchmarks for a full call of `pv2_encrypt` (`pv2_decrypt` has exactly the same performance characteristics).
|
||||
|
||||
## Usage
|
||||
|
||||
```rust,no_run
|
||||
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M128;
|
||||
use tfhe::shortint::prelude::*;
|
||||
use tfhe_princev2::{pv2_encrypt, u64_to_vec_u2, vec_u2_to_u64};
|
||||
|
||||
let (s_key, ev_key) = tfhe::shortint::gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M128);
|
||||
|
||||
let encode = |x: u64| -> [Ciphertext; 32] {
|
||||
let nibbles = u64_to_vec_u2(x);
|
||||
let v: Vec<_> = nibbles.into_iter().map(|n| s_key.encrypt(n as u64)).collect();
|
||||
v.try_into().unwrap()
|
||||
};
|
||||
|
||||
let ct_m = encode(0x0123456789abcdef);
|
||||
let ct_k0 = encode(0x0123456789abcdef);
|
||||
let ct_k1 = encode(0xfedcba9876543210);
|
||||
|
||||
let mut ct_out: [Ciphertext; 32] = std::array::from_fn(|_| ev_key.create_trivial(0));
|
||||
pv2_encrypt(&ev_key, &mut ct_out, &ct_m, &ct_k0, &ct_k1);
|
||||
|
||||
let out_nibbles: [u8; 32] =
|
||||
std::array::from_fn(|i| s_key.decrypt_message_and_carry(&ct_out[i]) as u8);
|
||||
assert_eq!(vec_u2_to_u64(out_nibbles), 0x603cd95fa72a8704);
|
||||
```
|
||||
|
||||
## Running tests
|
||||
|
||||
```bash
|
||||
RAYON_NUM_THREADS=64 cargo test --release --test pv2_kat -- --test-threads=1
|
||||
```
|
||||
|
||||
Each KAT should take approximately 5 seconds (resp. 800ms) on 8 cores (resp. 64 cores) on an Amazon AWS hpc7a.96xlarge machine. There are currently 10 KATs (5 for encryption and same for decryption). Optimal timings depend on the hardware but will be structurally better using a number of threads which is a power of 2 up to 64; the best possible latency is obtained through 64 individual threads.
|
||||
|
||||
|
||||
## Optional verbose timings
|
||||
|
||||
```bash
|
||||
RAYON_NUM_THREADS=64 cargo test --release --test pv2_kat --features verbose-timings -- --test-threads=1 --nocapture
|
||||
```
|
||||
|
||||
This times each internal round function call and emits one `eprintln!` per such call.
|
||||
|
||||
|
||||
## Running benchmarks
|
||||
|
||||
```bash
|
||||
RAYON_NUM_THREADS=64 cargo bench --bench princev2
|
||||
```
|
||||
|
||||
Timings obtained on up to 64 cores of an `Amazon AWS hpc7a.96xlarge` machine can also be found in [BJ26, Table 6.1].
|
||||
@@ -1,86 +0,0 @@
|
||||
//! Benchmarks for homomorphic PRINCEv2 encryption (and decryption)
|
||||
//!
|
||||
//! Times one full call of `pv2_encrypt`, i.e., transciphering one block of 64 bits.
|
||||
//! Note that decryption `pv2_decrypt` follows exactly the same logic as encryption with different
|
||||
//! constants, hence it is not benched separately.
|
||||
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M128;
|
||||
use tfhe::shortint::prelude::*;
|
||||
|
||||
use tfhe_princev2::{pv2_encrypt, u64_to_vec_u2, vec_u2_to_u64};
|
||||
|
||||
// [NB] We don't expect pv2_decrypt() to behave differently from pv2_encrypt()
|
||||
criterion_group!(pv2_benches, bench_pv2_encrypt);
|
||||
criterion_main!(pv2_benches);
|
||||
|
||||
// KAT structure for Pv2 cipher
|
||||
struct Pv2Kat {
|
||||
name: &'static str,
|
||||
ptxt: u64,
|
||||
k0: u64,
|
||||
k1: u64,
|
||||
ctxt: u64,
|
||||
}
|
||||
|
||||
static PV2_KAT_LN2: Pv2Kat = Pv2Kat {
|
||||
// ptxt, k0, k1 are the first three u64 words of ln(2) fractional part.
|
||||
// ctxt was computed with the Sagemaths reference implementation and cross-checked here.
|
||||
name: "PRINCEv2 KAT from ln(2)",
|
||||
ptxt: 0xb17217f7d1cf79ab,
|
||||
k0: 0xc9e3b39803f2f6af,
|
||||
k1: 0x40f343267298b62d,
|
||||
ctxt: 0x40ac916b4598216d,
|
||||
};
|
||||
|
||||
/// Encrypt a u64 as 32 ciphertexts, each holding a 2-bit nibble in the low bits of the FHE message
|
||||
/// space. Most significant bits of the input are at index 0 in the output
|
||||
fn encrypt_u64_as_vec_u2l(s_key: &ClientKey, x: u64) -> [Ciphertext; 32] {
|
||||
let x_u2: [u8; 32] = u64_to_vec_u2(x);
|
||||
let ct: Vec<Ciphertext> = x_u2
|
||||
.into_iter()
|
||||
.map(|u2| s_key.encrypt(u2 as u64))
|
||||
.collect();
|
||||
ct.try_into().unwrap()
|
||||
}
|
||||
|
||||
/// Reverse of function encrypt_u64_as_vec_u2l()
|
||||
fn decrypt_vec_u2l_as_u64(s_key: &ClientKey, v: &[Ciphertext; 32]) -> u64 {
|
||||
let x_u2: [u8; 32] = std::array::from_fn(|n| s_key.decrypt_message_and_carry(&v[n]) as u8);
|
||||
let x: u64 = vec_u2_to_u64(x_u2);
|
||||
x
|
||||
}
|
||||
|
||||
/// Run benches for PRINCEv2 transciphering.
|
||||
fn bench_pv2_encrypt(c: &mut Criterion) {
|
||||
let (s_key, ev_key): (ClientKey, ServerKey) = // Params: Need 4-bits msg + nu >= 4
|
||||
tfhe::shortint::gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M128);
|
||||
|
||||
// Encryptions of inputs (k0,k1,m)
|
||||
let ct_k0: [Ciphertext; 32] = encrypt_u64_as_vec_u2l(&s_key, PV2_KAT_LN2.k0);
|
||||
let ct_k1: [Ciphertext; 32] = encrypt_u64_as_vec_u2l(&s_key, PV2_KAT_LN2.k1);
|
||||
let ct_m: [Ciphertext; 32] = encrypt_u64_as_vec_u2l(&s_key, PV2_KAT_LN2.ptxt);
|
||||
|
||||
// PRINCEv2 Enc in FHE
|
||||
let mut ct_out: [Ciphertext; 32] = std::array::from_fn(|_| ev_key.create_trivial(0)); // [NB] shortint::create_trivial() vs boolean::trivial_encrypt()
|
||||
c.bench_function("PRINCEv2 Trans-Encryption of one message block", |b| {
|
||||
b.iter(|| {
|
||||
pv2_encrypt(&ev_key, &mut ct_out, &ct_m, &ct_k0, &ct_k1);
|
||||
});
|
||||
});
|
||||
|
||||
// Testing the (de-)encrypted result
|
||||
let pt_out: u64 = decrypt_vec_u2l_as_u64(&s_key, &ct_out);
|
||||
assert_eq!(
|
||||
pt_out,
|
||||
PV2_KAT_LN2.ctxt,
|
||||
"{} failed: ptxt={:#018x}, k0={:#018x}, k1={:#018x}, expected={:#018x}, got={:#018x}",
|
||||
PV2_KAT_LN2.name,
|
||||
PV2_KAT_LN2.ptxt,
|
||||
PV2_KAT_LN2.k0,
|
||||
PV2_KAT_LN2.k1,
|
||||
PV2_KAT_LN2.ctxt,
|
||||
pt_out
|
||||
);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// Pure Rust Helpers
|
||||
mod u64_conv;
|
||||
pub use u64_conv::{u64_to_vec_u2, vec_u2_to_u64}; // For tests, part of the encoding contract for pv2_encrypt()/pv2_decrypt()
|
||||
mod permute;
|
||||
|
||||
// Cipher internals: pre-computed constants, s-box and perms
|
||||
mod pv2_cipher;
|
||||
mod pv2_lut;
|
||||
pub use pv2_cipher::{pv2_decrypt, pv2_encrypt};
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Apply Permutations on arrays
|
||||
* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
pub fn apply_perm_assign<T>(list: &mut [T], order: &[usize]) {
|
||||
assert_eq!(list.len(), order.len());
|
||||
let n: usize = order.len();
|
||||
let mut done: Vec<bool> = vec![false; n];
|
||||
|
||||
for i in 0..n {
|
||||
// Do not cycle multiple times
|
||||
if done[i] {
|
||||
continue;
|
||||
}
|
||||
let mut from = i;
|
||||
let mut to = order[i];
|
||||
|
||||
// Cycle always of length < n
|
||||
while to != i {
|
||||
list.swap(from, to);
|
||||
done[from] = true;
|
||||
from = to;
|
||||
to = order[to];
|
||||
}
|
||||
done[from] = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_perm_assign() {
|
||||
let perm: [usize; 16] = [
|
||||
0x0, 0x5, 0xa, 0xf, 0x4, 0x9, 0xe, 0x3, 0x8, 0xd, 0x2, 0x7, 0xc, 0x1, 0x6, 0xb,
|
||||
];
|
||||
let mut list: [u8; 16] = [
|
||||
0x8, 0x3, 0x2, 0xb, 0xd, 0x4, 0x6, 0xf, 0x1, 0x0, 0x8, 0xe, 0x7, 0x8, 0x5, 0x7,
|
||||
];
|
||||
let exp_res: [u8; 16] = [
|
||||
0x8, 0x4, 0x8, 0x7, 0xd, 0x0, 0x5, 0xb, 0x1, 0x8, 0x2, 0xf, 0x7, 0x3, 0x6, 0xe,
|
||||
];
|
||||
|
||||
apply_perm_assign(&mut list, &perm);
|
||||
assert_eq!(exp_res, list);
|
||||
|
||||
#[rustfmt::skip]
|
||||
let perm: [usize; 64] = [ // FHE_MP_PERM_FW
|
||||
0x00, 0x05, 0x0a, 0x0f, 0x12, 0x17, 0x18, 0x1d, 0x21, 0x26, 0x2b, 0x2c, 0x31, 0x36, 0x3b, 0x3c,
|
||||
0x13, 0x14, 0x19, 0x1e, 0x22, 0x27, 0x28, 0x2d, 0x32, 0x37, 0x38, 0x3d, 0x01, 0x06, 0x0b, 0x0c,
|
||||
0x23, 0x24, 0x29, 0x2e, 0x33, 0x34, 0x39, 0x3e, 0x02, 0x07, 0x08, 0x0d, 0x10, 0x15, 0x1a, 0x1f,
|
||||
0x30, 0x35, 0x3a, 0x3f, 0x03, 0x04, 0x09, 0x0e, 0x11, 0x16, 0x1b, 0x1c, 0x20, 0x25, 0x2a, 0x2f,
|
||||
];
|
||||
#[rustfmt::skip]
|
||||
let mut list: [u8; 64] = [ // Random
|
||||
0xc2, 0x0d, 0x97, 0xd6, 0xb0, 0x79, 0x1b, 0x43, 0xcd, 0x03, 0x33, 0xfe, 0x4b, 0x1c, 0x7f, 0xa9,
|
||||
0xc0, 0xc2, 0xa7, 0x17, 0x88, 0xbf, 0xa6, 0x49, 0x5d, 0xcd, 0x11, 0xee, 0xdc, 0xc4, 0x17, 0x90,
|
||||
0x12, 0x7e, 0x0d, 0xb0, 0x1f, 0x58, 0xf5, 0xf4, 0x9f, 0xcc, 0xdd, 0xca, 0x49, 0x5a, 0x0e, 0xd2,
|
||||
0xf6, 0x37, 0xe9, 0x40, 0x6d, 0x56, 0x79, 0x53, 0xd6, 0x63, 0x6f, 0x8a, 0xf5, 0xaa, 0x5b, 0x08,
|
||||
];
|
||||
#[rustfmt::skip]
|
||||
let exp_res: [u8; 64] = [
|
||||
0xc2, 0x79, 0x33, 0xa9, 0xa7, 0x49, 0x5d, 0xc4, 0x7e, 0xf5, 0xca, 0x49, 0x37, 0x79, 0x8a, 0xf5,
|
||||
0x17, 0x88, 0xcd, 0x17, 0x0d, 0xf4, 0x9f, 0x5a, 0xe9, 0x53, 0xd6, 0xaa, 0x0d, 0x1b, 0xfe, 0x4b,
|
||||
0xb0, 0x1f, 0xcc, 0x0e, 0x40, 0x6d, 0x63, 0x5b, 0x97, 0x43, 0xcd, 0x1c, 0xc0, 0xbf, 0x11, 0x90,
|
||||
0xf6, 0x56, 0x6f, 0x08, 0xd6, 0xb0, 0x03, 0x7f, 0xc2, 0xa6, 0xee, 0xdc, 0x12, 0x58, 0xdd, 0xd2,
|
||||
];
|
||||
|
||||
apply_perm_assign(&mut list, &perm);
|
||||
assert_eq!(exp_res, list);
|
||||
}
|
||||
@@ -1,537 +0,0 @@
|
||||
use rayon::prelude::*;
|
||||
use tfhe::shortint::prelude::*;
|
||||
|
||||
use crate::permute; // permute/shuffle/swap arrays
|
||||
use crate::pv2_lut; // fhe luts and constants for prince v2
|
||||
|
||||
/* Macro to monitor individual functions timings (feature related: "verbose-timings") */
|
||||
macro_rules! monitor {
|
||||
($fn:ident($( $a:expr ), *)) => {
|
||||
#[cfg(feature = "verbose-timings")]
|
||||
let t0 = std::time::Instant::now();
|
||||
$fn($( $a), *);
|
||||
#[cfg(feature = "verbose-timings")]
|
||||
eprintln!("{}:\t{:.4?}", stringify!($fn), t0.elapsed())
|
||||
}
|
||||
}
|
||||
|
||||
/* out_u4 = (in_u2q xor ct_k) as vec_u4
|
||||
* [Parallel:(32)/32/(16)] XOR stage -> u4 */
|
||||
fn pv2_xor_to_u4(
|
||||
ev_key: &ServerKey,
|
||||
out_u4: &mut [Ciphertext; 16],
|
||||
in_u2q: &[Ciphertext; 32],
|
||||
ct_k: &[Ciphertext; 32],
|
||||
) {
|
||||
// xor alternatively to pair of high/low bits
|
||||
let zlut_xor_fw = [
|
||||
ev_key.generate_lookup_table(|x: u64| pv2_lut::PV2_XOR_FW[0][x as usize] as u64),
|
||||
ev_key.generate_lookup_table(|x: u64| pv2_lut::PV2_XOR_FW[1][x as usize] as u64),
|
||||
];
|
||||
|
||||
/* "Bivariate" xor ------------------------------------------------------------------
|
||||
* Sum in_u2q + ct_k, apply xor LUT to high or low bit */
|
||||
/* [Sequential]
|
||||
let mut ct_hl: [Ciphertext; 32] = std::array::from_fn(|n| ev_key.unchecked_add(&in_u2q[n], &ct_k[n]));
|
||||
for n in 0..32 {
|
||||
ev_key.apply_lookup_table_assign(&mut ct_hl[n], &zlut_xor_fw[n & 0x1]);
|
||||
}
|
||||
// */
|
||||
//* [Parallel:32]
|
||||
let ct_hl: [Ciphertext; 32] = (0..32)
|
||||
.into_par_iter()
|
||||
.map(|n| {
|
||||
let both_n: Ciphertext = ev_key.unchecked_add(&in_u2q[n], &ct_k[n]);
|
||||
ev_key.apply_lookup_table(&both_n, &zlut_xor_fw[n & 0x1]) // Combined version faster?
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
// */
|
||||
// [Parallel:16] Sum by pairs
|
||||
/* (*out_u4) = (0..16).into_par_iter().map(|w| {
|
||||
ev_key.unchecked_add(&ct_hl[2*w], &ct_hl[2*w+1])
|
||||
}).collect::<Vec<_>>().try_into().unwrap();*/
|
||||
for w in 0..16 {
|
||||
out_u4[w] = ev_key.unchecked_add(&ct_hl[2 * w], &ct_hl[2 * w + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* out_b = (in_u2q xor ct_k) as vec_b
|
||||
* [Parallel:(32)/64] -> drifted bits */
|
||||
fn pv2_xor_to_b(
|
||||
ev_key: &ServerKey,
|
||||
out_b: &mut [Ciphertext; 64],
|
||||
in_u2q: &[Ciphertext; 32],
|
||||
ct_k: &[Ciphertext; 32],
|
||||
) {
|
||||
let zlut_xor_bh = [
|
||||
ev_key.generate_lookup_table(|x: u64| pv2_lut::PV2_XOR_BH[0][x as usize] as u64),
|
||||
ev_key.generate_lookup_table(|x: u64| pv2_lut::PV2_XOR_BH[1][x as usize] as u64),
|
||||
ev_key.generate_lookup_table(|x: u64| pv2_lut::PV2_XOR_BH[2][x as usize] as u64),
|
||||
ev_key.generate_lookup_table(|x: u64| pv2_lut::PV2_XOR_BH[3][x as usize] as u64),
|
||||
];
|
||||
let zlut_xor_bl = [
|
||||
ev_key.generate_lookup_table(|x: u64| pv2_lut::PV2_XOR_BL[0][x as usize] as u64),
|
||||
ev_key.generate_lookup_table(|x: u64| pv2_lut::PV2_XOR_BL[1][x as usize] as u64),
|
||||
ev_key.generate_lookup_table(|x: u64| pv2_lut::PV2_XOR_BL[2][x as usize] as u64),
|
||||
ev_key.generate_lookup_table(|x: u64| pv2_lut::PV2_XOR_BL[3][x as usize] as u64),
|
||||
];
|
||||
|
||||
// [Parallel:32] Sum in_u2q + ct_k --> could stay as iter? and assign in the following loop
|
||||
let ct_hl: [Ciphertext; 32] =
|
||||
std::array::from_fn(|n| ev_key.unchecked_add(&in_u2q[n], &ct_k[n]));
|
||||
|
||||
// Apply xor (incl. bit_extract) luts on each nibble
|
||||
/* [Sequential]
|
||||
for w in 0..16 {
|
||||
let b_pos = w & 0x3; // w mod 4 (b_pos:0123_b, so b_pos=0 is for (b << 3))
|
||||
out_b[4*w] = ev_key.apply_lookup_table(&ct_hl[2*w], &zlut_xor_bh[b_pos]);
|
||||
out_b[4*w+1] = ev_key.apply_lookup_table(&ct_hl[2*w], &zlut_xor_bl[b_pos]);
|
||||
out_b[4*w+2] = ev_key.apply_lookup_table(&ct_hl[2*w+1], &zlut_xor_bh[b_pos]);
|
||||
out_b[4*w+3] = ev_key.apply_lookup_table(&ct_hl[2*w+1], &zlut_xor_bl[b_pos]);
|
||||
} // */
|
||||
//* [Parallel:64] Apply xor (incl. bit_extract) luts on each nibble
|
||||
(*out_b) = (0..64)
|
||||
.into_par_iter()
|
||||
.map(|idx| {
|
||||
let n: usize = idx >> 1; // 2*w or 2*w+1
|
||||
let w: usize = idx >> 2;
|
||||
let b_pos: usize = w & 0x3;
|
||||
let zlut_bhl_pos = if (idx & 0x1) == 1 {
|
||||
&zlut_xor_bl[b_pos]
|
||||
} else {
|
||||
&zlut_xor_bh[b_pos]
|
||||
};
|
||||
ev_key.apply_lookup_table(&ct_hl[n], zlut_bhl_pos)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap(); // */
|
||||
}
|
||||
|
||||
// [Parallel:(32)/32]
|
||||
fn pv2_xor_to_u2(
|
||||
ev_key: &ServerKey,
|
||||
out_u2: &mut [Ciphertext; 32],
|
||||
in_u2q: &[Ciphertext; 32],
|
||||
ct_k: &[Ciphertext; 32],
|
||||
) {
|
||||
let zlut_xor =
|
||||
ev_key.generate_lookup_table(|x: u64| pv2_lut::PV2_XOR_TO_LOW[x as usize] as u64);
|
||||
|
||||
/* [Sequential]
|
||||
for n in 0..32 {
|
||||
out_u2[n] = ev_key.unchecked_add(&in_u2q[n], &ct_k[n]);
|
||||
ev_key.apply_lookup_table_assign(&mut out_u2[n], &zlut_xor);
|
||||
} // */
|
||||
// [Parallel:32] Apply xor luts on each nibble
|
||||
//* [Parallel:32]
|
||||
(*out_u2) = (0..32)
|
||||
.into_par_iter()
|
||||
.map(|n| {
|
||||
let both_n: Ciphertext = ev_key.unchecked_add(&in_u2q[n], &ct_k[n]);
|
||||
ev_key.apply_lookup_table(&both_n, &zlut_xor)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap(); // */
|
||||
}
|
||||
|
||||
// [Parallel:64/(32/16)/64/(32)] Fw Round
|
||||
// Forward round receives full 4-bit nibbles (16) and returns 2-bit nibbles (32) packed on high bits
|
||||
fn pv2_fw_round(
|
||||
ev_key: &ServerKey,
|
||||
out_u2q: &mut [Ciphertext; 32], // out: 2-bits (high)
|
||||
in_u4: &[Ciphertext; 16], // in: 4-bits (full)
|
||||
zlut: &[[u8; 16]; 16],
|
||||
) {
|
||||
/* S-Boxes ------------------------------------------------------------------------------------
|
||||
* . each 4-bit nibbles requires 4 applications of (same LUT + Bit extraction)
|
||||
* . extracted bits for word w go at position 3-w mod 4 (w=0 --> b000, w=1 --> 0b00, etc) */
|
||||
/* [Sequential]
|
||||
let mut ct_tmp: [Ciphertext; 64] = std::array::from_fn(|_| ev_key.create_trivial(0));
|
||||
for w in 0..16 {
|
||||
for b in 0..4 { // use apply_many_lookup_tables ?
|
||||
let zlut_b = ev_key.generate_lookup_table(
|
||||
|x:u64| (((zlut[w][x as usize] >> (3-b)) & 0x1) << ((3-w) % 4)) as u64
|
||||
);
|
||||
ct_tmp[b + 4*w] = ev_key.apply_lookup_table(&in_u4[w], &zlut_b);
|
||||
}
|
||||
} // */
|
||||
/* [Sequential::array]
|
||||
let mut ct_tmp: [Ciphertext; 64] = std::array::from_fn(|idx| {
|
||||
let w: usize = idx >> 2;
|
||||
let b: usize = idx & 0x3;
|
||||
let zlut_b = ev_key.generate_lookup_table(
|
||||
|x:u64| (((zlut[w][x as usize] >> (3-b)) & 0x1) << ((3-w) % 4)) as u64
|
||||
);
|
||||
ev_key.apply_lookup_table(&in_u4[w], &zlut_b) // ct_tmp[idx]
|
||||
}); // */
|
||||
//* [Parallel:64]
|
||||
let ct_tmp: [Ciphertext; 64] = (0..64)
|
||||
.into_par_iter()
|
||||
.map(|idx| {
|
||||
// idx = 4*w + b
|
||||
let w: usize = idx >> 2;
|
||||
let b: usize = idx & 0x3;
|
||||
let zlut_b = ev_key.generate_lookup_table(|x: u64| {
|
||||
// [Nb] w=0..15
|
||||
(((zlut[w][x as usize] >> (3 - b)) & 0x1) << (3 - (w % 4))) as u64
|
||||
});
|
||||
ev_key.apply_lookup_table(&in_u4[w], &zlut_b)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
// */
|
||||
/* Bridging Sbox --> MLayer ----------------------------------------------------------
|
||||
* So as to obtain 4-bit enc nibbles with: 048c, 159d, etc */
|
||||
// TODO(?): [Parallel:32/16]
|
||||
for w in 0..16 {
|
||||
// this uses u2q for some u4 ahead of time (as temporary holder)
|
||||
let oo: usize = 16 * (w / 4) + (w % 4);
|
||||
out_u2q[w] = ev_key.unchecked_add(&ct_tmp[oo], &ct_tmp[oo + 4]);
|
||||
out_u2q[w + 1] = ev_key.unchecked_add(&ct_tmp[oo + 8], &ct_tmp[oo + 12]);
|
||||
out_u2q[w] = ev_key.unchecked_add(&out_u2q[w], &out_u2q[w + 1]);
|
||||
}
|
||||
|
||||
/* M-layer: Apply exor matrices ------------------------------------------------------ */
|
||||
/* [Sequential]
|
||||
for w in 0..16 {
|
||||
for b in 0..4 {
|
||||
let zlut_ex = ev_key.generate_lookup_table(
|
||||
|x:u64| pv2_lut::PV2_EXOR_FW[w % 2][b][x as usize] as u64
|
||||
);
|
||||
ct_tmp[b + 4*w] = ev_key.apply_lookup_table(&out_u2q[w], &zlut_ex);
|
||||
}
|
||||
} // */
|
||||
//* [Parallel:64]
|
||||
let mut ct_tmp: [Ciphertext; 64] = (0..64)
|
||||
.into_par_iter()
|
||||
.map(|idx| {
|
||||
// idx = 4*w + b
|
||||
let w: usize = idx >> 2;
|
||||
let b: usize = idx & 0x3;
|
||||
let zlut_ex = ev_key
|
||||
.generate_lookup_table(|x: u64| pv2_lut::PV2_EXOR_FW[w % 2][b][x as usize] as u64);
|
||||
ev_key.apply_lookup_table(&out_u2q[w], &zlut_ex)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap(); // */
|
||||
// Apply Fhe Perm permutation + Permutation Layer
|
||||
// --> Directly assign correctly in above loop? as ct_tmp[INV_FHE_MP_PERM_FW[b + 4*w]] = ...
|
||||
permute::apply_perm_assign(&mut ct_tmp, &pv2_lut::FHE_MP_PERM_FW);
|
||||
|
||||
/* Bridging M-Layer --> Xor --------------------------------------------------------- */
|
||||
// [Parallel:32] Combine pairs
|
||||
for n in 0..32 {
|
||||
out_u2q[n] = ev_key.unchecked_add(&ct_tmp[2 * n], &ct_tmp[2 * n + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
// [Parallel:64/(32)/64/(32/16)/64/(32)/32/(32/16)/32]
|
||||
fn pv2_mid_round(
|
||||
ev_key: &ServerKey,
|
||||
out_u2q: &mut [Ciphertext; 32], // out: 2-bits (high)
|
||||
in_u4: &[Ciphertext; 16], // in: 4-bits (full)
|
||||
ct_k_fst: &[Ciphertext; 32],
|
||||
ct_k_scd: &[Ciphertext; 32],
|
||||
zlut_fst: &[[u8; 16]; 16],
|
||||
zlut_scd: &[[u8; 16]; 16],
|
||||
) {
|
||||
/* S-Boxes ------------------------------------------------------------------------------------
|
||||
* /!\ output for xor */
|
||||
/* [Sequential]
|
||||
for w in 0..16 {
|
||||
for b in 0..2 {
|
||||
let zlut_u2q = ev_key.generate_lookup_table(
|
||||
|x:u64| (((pv2_lut::PV2_5_S_M[w][x as usize] >> (2-2*b)) & 0x3) << 2) as u64
|
||||
);
|
||||
out_u2q[b + 2*w] = ev_key.apply_lookup_table(&in_u4[w], &zlut_u2q);
|
||||
}
|
||||
} // */
|
||||
//* [Parallel:64]
|
||||
(*out_u2q) = (0..32)
|
||||
.into_par_iter()
|
||||
.map(|n| {
|
||||
let w: usize = n >> 1;
|
||||
let b: usize = n & 0x1;
|
||||
let zlut_u2q = ev_key.generate_lookup_table(|x: u64| {
|
||||
(((zlut_fst[w][x as usize] >> (2 - 2 * b)) & 0x3) << 2) as u64
|
||||
});
|
||||
ev_key.apply_lookup_table(&in_u4[w], &zlut_u2q)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap(); // */
|
||||
/* XOR K0 [Parallel:(32)/64] --------------------------------------------------------- */
|
||||
let mut ct_tmp_b: [Ciphertext; 64] = std::array::from_fn(|_| ev_key.create_trivial(0));
|
||||
pv2_xor_to_b(ev_key, &mut ct_tmp_b, out_u2q, ct_k_fst);
|
||||
|
||||
/* Bridging to M-Layer --------------------------------------------------------------- */
|
||||
// [Parallel:32/16] Comb sum (048c,...)
|
||||
let mut ct_tmp_u4: [Ciphertext; 16] = std::array::from_fn(|_| ev_key.create_trivial(0));
|
||||
for w in 0..16 {
|
||||
// mm, use u2q for some u4 ahead of time
|
||||
let oo: usize = 16 * (w / 4) + (w % 4);
|
||||
out_u2q[w] = ev_key.unchecked_add(&ct_tmp_b[oo], &ct_tmp_b[oo + 4]);
|
||||
out_u2q[w + 1] = ev_key.unchecked_add(&ct_tmp_b[oo + 8], &ct_tmp_b[oo + 12]);
|
||||
ct_tmp_u4[w] = ev_key.unchecked_add(&out_u2q[w], &out_u2q[w + 1]);
|
||||
}
|
||||
|
||||
/* M-layer: Apply exor matrices ------------------------------------------------------ */
|
||||
/* [Sequential]
|
||||
for w in 0..16 {
|
||||
for b in 0..4 {
|
||||
let zlut_ex = ev_key.generate_lookup_table(
|
||||
|x:u64| pv2_lut::PV2_EXOR_FW[w % 2][b][x as usize] as u64
|
||||
);
|
||||
ct_tmp_b[b + 4*w] = ev_key.apply_lookup_table(&ct_tmp_u4[w], &zlut_ex);
|
||||
}
|
||||
} // */
|
||||
//* [Parallel:64]
|
||||
ct_tmp_b = (0..64)
|
||||
.into_par_iter()
|
||||
.map(|idx| {
|
||||
// idx = 4*w + b
|
||||
let w: usize = idx >> 2;
|
||||
let b: usize = idx & 0x3;
|
||||
let zlut_ex = ev_key
|
||||
.generate_lookup_table(|x: u64| pv2_lut::PV2_EXOR_FW[w % 2][b][x as usize] as u64);
|
||||
ev_key.apply_lookup_table(&ct_tmp_u4[w], &zlut_ex)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
// */
|
||||
// Apply Fhe Perm permutation
|
||||
permute::apply_perm_assign(&mut ct_tmp_b, &pv2_lut::FHE_M_PERM);
|
||||
|
||||
/* Bridging M-Layer --> Xor --------------------------------------------------------- */
|
||||
// [Parallel:32] Combine pairs
|
||||
for n in 0..32 {
|
||||
out_u2q[n] = ev_key.unchecked_add(&ct_tmp_b[2 * n], &ct_tmp_b[2 * n + 1]);
|
||||
}
|
||||
|
||||
/* XOR k1 [Parallel:(32)/32/(16)] --------------------------------------------------- */
|
||||
pv2_xor_to_u4(ev_key, &mut ct_tmp_u4, out_u2q, ct_k_scd);
|
||||
|
||||
/* S-Boxes ------------------------------------------------------------------------------------
|
||||
* . output 2,2 bits on position (32)00 */
|
||||
/* [Sequential]
|
||||
for w in 0..16 {
|
||||
for b in 0..2 {
|
||||
let zlut_u2q = ev_key.generate_lookup_table(
|
||||
|x:u64| (((pv2_lut::PV2_0_IS_0[w][x as usize] >> (2-2*b)) & 0x3) << 2) as u64
|
||||
);
|
||||
out_u2q[b + 2*w] = ev_key.apply_lookup_table(&ct_tmp_u4[w], &zlut_u2q);
|
||||
}
|
||||
}
|
||||
// */
|
||||
//* [Parallel:32]
|
||||
(*out_u2q) = (0..32)
|
||||
.into_par_iter()
|
||||
.map(|n| {
|
||||
let w: usize = n >> 1;
|
||||
let b: usize = n & 0x1;
|
||||
let zlut_u2q = ev_key.generate_lookup_table(|x: u64| {
|
||||
(((zlut_scd[w][x as usize] >> (2 - 2 * b)) & 0x3) << 2) as u64
|
||||
});
|
||||
ev_key.apply_lookup_table(&ct_tmp_u4[w], &zlut_u2q)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap(); // */
|
||||
}
|
||||
|
||||
// [Parallel:(32/16)/64/(32/16)/32]
|
||||
fn pv2_bw_round(
|
||||
ev_key: &ServerKey,
|
||||
out_u2q: &mut [Ciphertext; 32], // out: 2-bits (high)
|
||||
in_b: &[Ciphertext; 64], // in: 1-bits (<< w%4 = 333322221111000033...)
|
||||
zlut: &[[u8; 16]; 16],
|
||||
) {
|
||||
let mut ct_tmp_u4: [Ciphertext; 16] = std::array::from_fn(|_| ev_key.create_trivial(0)); // ...
|
||||
|
||||
// iPerm + M-Layer
|
||||
// [Parallel:32/16] Combined iPerm + comb sum (048c,etc)
|
||||
for w in 0..16 {
|
||||
let idx: [usize; 4] =
|
||||
std::array::from_fn(|b| (w & 0x3) + 4 * pv2_lut::IPERM[4 * (w >> 2) + b]);
|
||||
out_u2q[2 * w] = ev_key.unchecked_add(&in_b[idx[0]], &in_b[idx[1]]);
|
||||
out_u2q[2 * w + 1] = ev_key.unchecked_add(&in_b[idx[2]], &in_b[idx[3]]);
|
||||
ct_tmp_u4[w] = ev_key.unchecked_add(&out_u2q[2 * w], &out_u2q[2 * w + 1]);
|
||||
}
|
||||
|
||||
/* M-layer: Apply exor matrices ------------------------------------------------------ */
|
||||
/* [Sequential]
|
||||
let mut ct_tmp_b: [Ciphertext; 64] = std::array::from_fn(|_| ev_key.create_trivial(0));
|
||||
for w in 0..16 {
|
||||
for b in 0..4 {
|
||||
let zlut_ex = ev_key.generate_lookup_table(
|
||||
|x:u64| pv2_lut::PV2_EXOR_BW[w % 4][b][x as usize] as u64
|
||||
);
|
||||
ct_tmp_b[b + 4*w] = ev_key.apply_lookup_table(&ct_tmp_u4[w], &zlut_ex);
|
||||
}
|
||||
} // */
|
||||
//* [Parallel:64]
|
||||
let mut ct_tmp_b: [Ciphertext; 64] = (0..64)
|
||||
.into_par_iter()
|
||||
.map(|idx| {
|
||||
// idx = 4*w + b
|
||||
let w: usize = idx >> 2;
|
||||
let b: usize = idx & 0x3;
|
||||
let zlut_ex = ev_key
|
||||
.generate_lookup_table(|x: u64| pv2_lut::PV2_EXOR_BW[w % 4][b][x as usize] as u64);
|
||||
ev_key.apply_lookup_table(&ct_tmp_u4[w], &zlut_ex)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
// */
|
||||
// FHE Perm permutation
|
||||
permute::apply_perm_assign(&mut ct_tmp_b, &pv2_lut::FHE_M_PERM);
|
||||
|
||||
/* Bridging MLayer --> SBox ----------------------------------------------------------- */
|
||||
// [Parallel:32/16] Combine to u4 = sum[4*i:4*i+4] for i in range(16)
|
||||
for w in 0..16 {
|
||||
out_u2q[2 * w] = ev_key.unchecked_add(&ct_tmp_b[4 * w], &ct_tmp_b[4 * w + 1]);
|
||||
out_u2q[2 * w + 1] = ev_key.unchecked_add(&ct_tmp_b[4 * w + 2], &ct_tmp_b[4 * w + 3]);
|
||||
ct_tmp_u4[w] = ev_key.unchecked_add(&out_u2q[2 * w], &out_u2q[2 * w + 1]);
|
||||
}
|
||||
|
||||
/* S-Boxes ------------------------------------------------------------------------------------
|
||||
* . output 2,2 bits on position (32)00 */
|
||||
/* [Sequential]
|
||||
for w in 0..16 {
|
||||
for b in 0..2 {
|
||||
let zlut_u2q = ev_key.generate_lookup_table(
|
||||
|x:u64| (((zlut[w][x as usize] >> (2-2*b)) & 0x3) << 2) as u64
|
||||
);
|
||||
out_u2q[b + 2*w] = ev_key.apply_lookup_table(&ct_tmp_u4[w], &zlut_u2q);
|
||||
}
|
||||
}
|
||||
// */
|
||||
//* [Parallel:32]
|
||||
(*out_u2q) = (0..32)
|
||||
.into_par_iter()
|
||||
.map(|n| {
|
||||
let w: usize = n >> 1;
|
||||
let b: usize = n & 0x1;
|
||||
let zlut_u2q = ev_key.generate_lookup_table(|x: u64| {
|
||||
(((zlut[w][x as usize] >> (2 - 2 * b)) & 0x3) << 2) as u64
|
||||
});
|
||||
ev_key.apply_lookup_table(&ct_tmp_u4[w], &zlut_u2q)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap(); // */
|
||||
}
|
||||
|
||||
/* Encryption -----------------------------------------------------------------------------------
|
||||
* (Whitening + Fw Rounds + Mid Round + Bw Rounds + Whitening)
|
||||
*/
|
||||
|
||||
#[rustfmt::skip] // [skip] Each of 22 monitor! calls get split on 5 lines which destroys readability
|
||||
pub fn pv2_encrypt(
|
||||
ev_key: &ServerKey,
|
||||
ct_enc: &mut [Ciphertext; 32],
|
||||
ct_m: &[Ciphertext; 32],
|
||||
ct_k0: &[Ciphertext; 32],
|
||||
ct_k1: &[Ciphertext; 32],
|
||||
) {
|
||||
// Work buffers: u4, u2q, b (depending on the inner nibbles format, u2q = u2 <<2)
|
||||
let mut ct_u4: [Ciphertext; 16] = std::array::from_fn(|_| ev_key.create_trivial(0));
|
||||
let mut ct_b: [Ciphertext; 64] = std::array::from_fn(|_| ev_key.create_trivial(0));
|
||||
// [Parallel] + Init: ct_m << 2
|
||||
let mut ct_u2q: [Ciphertext; 32] =
|
||||
std::array::from_fn(|n| ev_key.unchecked_scalar_mul(&ct_m[n], 4));
|
||||
|
||||
// Whitening
|
||||
monitor!(pv2_xor_to_u4(ev_key, &mut ct_u4, &ct_u2q, ct_k0));
|
||||
// Forward rounds
|
||||
//*
|
||||
monitor!(pv2_fw_round(ev_key, &mut ct_u2q, &ct_u4, &pv2_lut::PV2_0_S_0));
|
||||
monitor!(pv2_xor_to_u4(ev_key, &mut ct_u4, &ct_u2q, ct_k1));
|
||||
monitor!(pv2_fw_round(ev_key, &mut ct_u2q, &ct_u4, &pv2_lut::PV2_1_S_2));
|
||||
monitor!(pv2_xor_to_u4(ev_key, &mut ct_u4, &ct_u2q, ct_k0));
|
||||
monitor!(pv2_fw_round(ev_key, &mut ct_u2q, &ct_u4, &pv2_lut::PV2_0_S_0));
|
||||
monitor!(pv2_xor_to_u4(ev_key, &mut ct_u4, &ct_u2q, ct_k1));
|
||||
monitor!(pv2_fw_round(ev_key, &mut ct_u2q, &ct_u4, &pv2_lut::PV2_3_S_4));
|
||||
monitor!(pv2_xor_to_u4(ev_key, &mut ct_u4, &ct_u2q, ct_k0));
|
||||
monitor!(pv2_fw_round(ev_key, &mut ct_u2q, &ct_u4, &pv2_lut::PV2_0_S_0));
|
||||
monitor!(pv2_xor_to_u4(ev_key, &mut ct_u4, &ct_u2q, ct_k1)); // */
|
||||
// Middle round
|
||||
//*
|
||||
monitor!(pv2_mid_round(ev_key, &mut ct_u2q, &ct_u4,
|
||||
ct_k0, ct_k1, &pv2_lut::PV2_5_S_M, &pv2_lut::PV2_0_IS_0)); // */
|
||||
// Backward rounds
|
||||
//*
|
||||
monitor!(pv2_xor_to_b(ev_key, &mut ct_b, &ct_u2q, ct_k0));
|
||||
monitor!(pv2_bw_round(ev_key, &mut ct_u2q, &ct_b, &pv2_lut::PV2_6_IS_7));
|
||||
monitor!(pv2_xor_to_b(ev_key, &mut ct_b, &ct_u2q, ct_k1));
|
||||
monitor!(pv2_bw_round(ev_key, &mut ct_u2q, &ct_b, &pv2_lut::PV2_0_IS_0));
|
||||
monitor!(pv2_xor_to_b(ev_key, &mut ct_b, &ct_u2q, ct_k0));
|
||||
monitor!(pv2_bw_round(ev_key, &mut ct_u2q, &ct_b, &pv2_lut::PV2_8_IS_9));
|
||||
monitor!(pv2_xor_to_b(ev_key, &mut ct_b, &ct_u2q, ct_k1));
|
||||
monitor!(pv2_bw_round(ev_key, &mut ct_u2q, &ct_b, &pv2_lut::PV2_0_IS_0));
|
||||
monitor!(pv2_xor_to_b(ev_key, &mut ct_b, &ct_u2q, ct_k0));
|
||||
monitor!(pv2_bw_round(ev_key, &mut ct_u2q, &ct_b, &pv2_lut::PV2_A_IS_B));
|
||||
// Last Xor to u2l
|
||||
monitor!(pv2_xor_to_u2(ev_key, ct_enc, &ct_u2q, ct_k1)); // */
|
||||
}
|
||||
|
||||
/* Decryption -----------------------------------------------------------------------------------
|
||||
* Inverse of pv2_encrypt().
|
||||
*/
|
||||
|
||||
#[rustfmt::skip] // [skip] Each of 22 monitor! calls get split on 5 lines which destroys readability
|
||||
pub fn pv2_decrypt(
|
||||
ev_key: &ServerKey,
|
||||
ct_dec: &mut [Ciphertext; 32],
|
||||
ct_c: &[Ciphertext; 32],
|
||||
ct_k0: &[Ciphertext; 32],
|
||||
ct_k1: &[Ciphertext; 32],
|
||||
) {
|
||||
// Work buffers: u4, u2q, b (depending on the inner nibbles format, u2q = u2 <<2)
|
||||
let mut ct_u4: [Ciphertext; 16] = std::array::from_fn(|_| ev_key.create_trivial(0));
|
||||
let mut ct_b: [Ciphertext; 64] = std::array::from_fn(|_| ev_key.create_trivial(0));
|
||||
// [Parallel] + Init: ct_m << 2
|
||||
let mut ct_u2q: [Ciphertext; 32] =
|
||||
std::array::from_fn(|n| ev_key.unchecked_scalar_mul(&ct_c[n], 4));
|
||||
|
||||
// Whitening
|
||||
monitor!(pv2_xor_to_u4(ev_key, &mut ct_u4, &ct_u2q, ct_k1));
|
||||
// Forward rounds
|
||||
//*
|
||||
monitor!(pv2_fw_round(ev_key, &mut ct_u2q, &ct_u4, &pv2_lut::PV2_B_S_A));
|
||||
monitor!(pv2_xor_to_u4(ev_key, &mut ct_u4, &ct_u2q, ct_k0));
|
||||
monitor!(pv2_fw_round(ev_key, &mut ct_u2q, &ct_u4, &pv2_lut::PV2_0_S_0));
|
||||
monitor!(pv2_xor_to_u4(ev_key, &mut ct_u4, &ct_u2q, ct_k1));
|
||||
monitor!(pv2_fw_round(ev_key, &mut ct_u2q, &ct_u4, &pv2_lut::PV2_9_S_8));
|
||||
monitor!(pv2_xor_to_u4(ev_key, &mut ct_u4, &ct_u2q, ct_k0));
|
||||
monitor!(pv2_fw_round(ev_key, &mut ct_u2q, &ct_u4, &pv2_lut::PV2_0_S_0));
|
||||
monitor!(pv2_xor_to_u4(ev_key, &mut ct_u4, &ct_u2q, ct_k1));
|
||||
monitor!(pv2_fw_round(ev_key, &mut ct_u2q, &ct_u4, &pv2_lut::PV2_7_S_6));
|
||||
monitor!(pv2_xor_to_u4(ev_key, &mut ct_u4, &ct_u2q, ct_k0)); // */
|
||||
// Middle round
|
||||
//*
|
||||
monitor!(pv2_mid_round(ev_key, &mut ct_u2q, &ct_u4,
|
||||
ct_k1, ct_k0, &pv2_lut::PV2_0_S_0, &pv2_lut::PV2_M_IS_5)); // */
|
||||
// Backward rounds
|
||||
//*
|
||||
monitor!(pv2_xor_to_b(ev_key, &mut ct_b, &ct_u2q, ct_k1));
|
||||
monitor!(pv2_bw_round(ev_key, &mut ct_u2q, &ct_b, &pv2_lut::PV2_0_IS_0));
|
||||
monitor!(pv2_xor_to_b(ev_key, &mut ct_b, &ct_u2q, ct_k0));
|
||||
monitor!(pv2_bw_round(ev_key, &mut ct_u2q, &ct_b, &pv2_lut::PV2_4_IS_3));
|
||||
monitor!(pv2_xor_to_b(ev_key, &mut ct_b, &ct_u2q, ct_k1));
|
||||
monitor!(pv2_bw_round(ev_key, &mut ct_u2q, &ct_b, &pv2_lut::PV2_0_IS_0));
|
||||
monitor!(pv2_xor_to_b(ev_key, &mut ct_b, &ct_u2q, ct_k0));
|
||||
monitor!(pv2_bw_round(ev_key, &mut ct_u2q, &ct_b, &pv2_lut::PV2_2_IS_1));
|
||||
monitor!(pv2_xor_to_b(ev_key, &mut ct_b, &ct_u2q, ct_k1));
|
||||
monitor!(pv2_bw_round(ev_key, &mut ct_u2q, &ct_b, &pv2_lut::PV2_0_IS_0));
|
||||
// Last Xor to u2l
|
||||
monitor!(pv2_xor_to_u2(ev_key, ct_dec, &ct_u2q, ct_k0)); // */
|
||||
}
|
||||
@@ -1,331 +0,0 @@
|
||||
/*
|
||||
* Prince v2 constant definitions and Look-up tables for FHE
|
||||
* --------------------------------------------------------------------------------- */
|
||||
use crate::u64_conv;
|
||||
|
||||
/* Permutations -------------------------------------------------------------------- */
|
||||
static PERM: [usize; 64 / 4] = [
|
||||
// Prince permutation layer on nibbles
|
||||
0x0, 0x5, 0xa, 0xf, 0x4, 0x9, 0xe, 0x3, 0x8, 0xd, 0x2, 0x7, 0xc, 0x1, 0x6, 0xb,
|
||||
];
|
||||
pub static IPERM: [usize; 64 / 4] = [
|
||||
// Prince inverse permutation on nibbles
|
||||
0x0, 0xd, 0xa, 0x7, 0x4, 0x1, 0xe, 0xb, 0x8, 0x5, 0x2, 0xf, 0xc, 0x9, 0x6, 0x3,
|
||||
];
|
||||
|
||||
// Permutation to apply on 16-bits nibbles after M0 if computed as exor( 0123 )
|
||||
// ---> bits 0c84,51d9,a62e,fb73
|
||||
// ---> TODO: put Perm in cycle notation so as to use swaps
|
||||
// (u16 bits) 0123...def (as an array of bits from msb to lsb)
|
||||
static FHE_M0_PERM: [usize; 16] = [
|
||||
0x0, 0x5, 0xa, 0xf, 0x3, 0x4, 0x9, 0xe, 0x2, 0x7, 0x8, 0xd, 0x1, 0x6, 0xb, 0xc,
|
||||
];
|
||||
|
||||
// Permutation to apply on 16-bits nibbles after M1 if computed as exor( 0123 )
|
||||
// --> bits c840,1d95,62ea,b73f
|
||||
static FHE_M1_PERM: [usize; 16] = [
|
||||
0x3, 0x4, 0x9, 0xe, 0x2, 0x7, 0x8, 0xd, 0x1, 0x6, 0xb, 0xc, 0x0, 0x5, 0xa, 0xf,
|
||||
];
|
||||
|
||||
// Combined overall bits permutation: (p0 | p1 | p1 | p0) with indexes 0..63
|
||||
// FHE_M_PERM = sum(( [_c + _n*16 for _c in _perm] for _n,_perm
|
||||
// in enumerate([FHE_M0_PERM,FHE_M1_PERM,FHE_M1_PERM,FHE_M0_PERM]) ), []);
|
||||
pub static FHE_M_PERM: [usize; 64] = {
|
||||
let mut n: usize = 0;
|
||||
let mut m_perm: [usize; 64] = [0; 64];
|
||||
|
||||
while n < 4 {
|
||||
let mut p_idx: usize = 0;
|
||||
while p_idx < 16 {
|
||||
m_perm[p_idx + n * 16] = n * 16
|
||||
+ match n {
|
||||
0 | 3 => FHE_M0_PERM[p_idx],
|
||||
1 | 2 => FHE_M1_PERM[p_idx],
|
||||
_ => unreachable!(),
|
||||
};
|
||||
p_idx += 1;
|
||||
}
|
||||
n += 1;
|
||||
}
|
||||
m_perm
|
||||
};
|
||||
|
||||
// Combined with Permutation layer (fw)
|
||||
// = [ fhe_M_Perm[ 4*Perm[_i >> 2] + (_i & 0x3) ] for _i in range(64) ]
|
||||
pub static FHE_MP_PERM_FW: [usize; 64] = {
|
||||
let mut b: usize = 0;
|
||||
let mut m_perm: [usize; 64] = [0; 64];
|
||||
|
||||
while b < 64 {
|
||||
// Unnatural, but just to see the same structure as above
|
||||
m_perm[b] = FHE_M_PERM[(PERM[b >> 2] << 2) + (b & 0x3)];
|
||||
b += 1;
|
||||
}
|
||||
m_perm
|
||||
};
|
||||
|
||||
/* Round constants ----------------------------------------------------------------- */
|
||||
const PRINCE_NRND: usize = 12; // Number of rounds (more precisely, nb of round constants / non-linear layers)
|
||||
|
||||
static _RC_ALPHA: u64 = 0xc0ac29b7c97c50dd; // see paper about symmetry of RC
|
||||
static _RC_BETA: u64 = 0x3f84d5b5b5470917; // see paper about symmetry of RC_V2
|
||||
#[rustfmt::skip]
|
||||
static RC_V2: [u64; PRINCE_NRND] = [
|
||||
0x0000000000000000, 0x13198a2e03707344, 0xa4093822299f31d0, 0x082efa98ec4e6c89,
|
||||
0x452821e638d01377, 0xbe5466cf34e90c6c, 0x7ef84f78fd955cb1, 0x7aacf4538d971a60,
|
||||
0xc882d32f25323c54, 0x9b8ded979cd838c7, 0xd3b5a399ca0c2399, 0x3f84d5b5b5470917,
|
||||
];
|
||||
#[rustfmt::skip]
|
||||
static RC_V2_IP_IM: [u64; PRINCE_NRND] = [ // iM . iP (RC) [from sage script]
|
||||
0x0000000000000000, 0x90ecdeb7cb7fc1ce, 0x81b2cb20a82a2928, 0x480cdfa91d749037,
|
||||
0xcb1a13467044d772, 0x9e8995b07a988c08, 0xe70338c395311a6a, 0x60dc22bf6e681c08,
|
||||
0x318672daf2dd0655, 0x2a74fad9b606e252, 0xe96673c424d657ac, 0xabc631f91e2ccb7a,
|
||||
];
|
||||
static RC_BETA_IM: u64 = 0x42f93b79daa0eea5; // iM (RC_BETA) [from sage script]
|
||||
|
||||
// Decomposed versions
|
||||
static ZRC_V2: [[u8; 64 / 4]; PRINCE_NRND] = array_u64_to_vec_u4(RC_V2);
|
||||
static ZRC_V2_IP_IM: [[u8; 64 / 4]; PRINCE_NRND] = array_u64_to_vec_u4(RC_V2_IP_IM);
|
||||
static ZRC_BETA_IM: [u8; 64 / 4] = u64_conv::u64_to_vec_u4(RC_BETA_IM);
|
||||
|
||||
// Emulating map on const for u64_to_vec_u4
|
||||
pub const fn array_u64_to_vec_u4<const N: usize>(tab: [u64; N]) -> [[u8; 64 / 4]; N] {
|
||||
let mut i: usize = 0;
|
||||
let mut mat: [[u8; 64 / 4]; N] = [[0; 64 / 4]; N];
|
||||
|
||||
while i < N {
|
||||
// for loop not allowed in const fn
|
||||
mat[i] = u64_conv::u64_to_vec_u4(tab[i]);
|
||||
i += 1;
|
||||
}
|
||||
mat
|
||||
}
|
||||
|
||||
/* (inv)SBox and derivatives ------------------------------------------------------- */
|
||||
static PV2_S: [u8; 1 << 4] = [
|
||||
// Forward SBox
|
||||
0xb, 0xf, 0x3, 0x2, 0xa, 0xc, 0x9, 0x1, 0x6, 0x7, 0x8, 0x0, 0xe, 0x5, 0xd, 0x4,
|
||||
];
|
||||
static PV2_IS: [u8; 1 << 4] = [
|
||||
// Backward SBox
|
||||
0xb, 0x7, 0x3, 0x2, 0xf, 0xd, 0x8, 0x9, 0xa, 0x6, 0x4, 0x0, 0x5, 0xe, 0xc, 0x1,
|
||||
];
|
||||
|
||||
// Combined RC+Sboxes for Encryption
|
||||
pub static PV2_0_S_0: [[u8; 1 << 4]; 64 / 4] = build_zlut_xsy(PV2_S, [0_u8; 16], [0_u8; 16]); // Not ideal
|
||||
pub static PV2_1_S_2: [[u8; 1 << 4]; 64 / 4] = build_zlut_xsy(PV2_S, ZRC_V2[1], ZRC_V2_IP_IM[2]);
|
||||
pub static PV2_3_S_4: [[u8; 1 << 4]; 64 / 4] = build_zlut_xsy(PV2_S, ZRC_V2[3], ZRC_V2_IP_IM[4]);
|
||||
pub static PV2_5_S_M: [[u8; 1 << 4]; 64 / 4] = build_zlut_xsy(PV2_S, ZRC_V2[5], ZRC_BETA_IM);
|
||||
pub static PV2_0_IS_0: [[u8; 1 << 4]; 64 / 4] = build_zlut_xsy(PV2_IS, [0_u8; 16], [0_u8; 16]); // Not ideal
|
||||
pub static PV2_6_IS_7: [[u8; 1 << 4]; 64 / 4] = build_zlut_xsy(PV2_IS, ZRC_V2_IP_IM[6], ZRC_V2[7]);
|
||||
pub static PV2_8_IS_9: [[u8; 1 << 4]; 64 / 4] = build_zlut_xsy(PV2_IS, ZRC_V2_IP_IM[8], ZRC_V2[9]);
|
||||
pub static PV2_A_IS_B: [[u8; 1 << 4]; 64 / 4] =
|
||||
build_zlut_xsy(PV2_IS, ZRC_V2_IP_IM[10], ZRC_V2[11]);
|
||||
|
||||
// Additional RC+Sboxes for Decryption
|
||||
pub static PV2_B_S_A: [[u8; 1 << 4]; 64 / 4] = build_zlut_xsy(PV2_S, ZRC_V2[11], ZRC_V2_IP_IM[10]);
|
||||
pub static PV2_9_S_8: [[u8; 1 << 4]; 64 / 4] = build_zlut_xsy(PV2_S, ZRC_V2[9], ZRC_V2_IP_IM[8]);
|
||||
pub static PV2_7_S_6: [[u8; 1 << 4]; 64 / 4] = build_zlut_xsy(PV2_S, ZRC_V2[7], ZRC_V2_IP_IM[6]);
|
||||
pub static PV2_M_IS_5: [[u8; 1 << 4]; 64 / 4] = build_zlut_xsy(PV2_IS, ZRC_BETA_IM, ZRC_V2[5]);
|
||||
pub static PV2_4_IS_3: [[u8; 1 << 4]; 64 / 4] = build_zlut_xsy(PV2_IS, ZRC_V2_IP_IM[4], ZRC_V2[3]);
|
||||
pub static PV2_2_IS_1: [[u8; 1 << 4]; 64 / 4] = build_zlut_xsy(PV2_IS, ZRC_V2_IP_IM[2], ZRC_V2[1]);
|
||||
|
||||
// Build special LUTs: SBox( x ^ inner ) ^ outer, depending on word index
|
||||
const fn build_zlut_xsy(
|
||||
sbox: [u8; 1 << 4],
|
||||
xor_inner: [u8; 64 / 4],
|
||||
xor_outer: [u8; 64 / 4],
|
||||
) -> [[u8; 1 << 4]; 64 / 4] {
|
||||
let mut zlut_xsy: [[u8; 1 << 4]; 64 / 4] = [[0; 1 << 4]; 64 / 4];
|
||||
let mut w: usize = 0;
|
||||
while w < 64 / 4 {
|
||||
// for loop not allowed in const fn
|
||||
let mut x: usize = 0;
|
||||
while x < (1 << 4) {
|
||||
zlut_xsy[w][x] = sbox[((x as u8) ^ xor_inner[w]) as usize] ^ xor_outer[w];
|
||||
x += 1;
|
||||
}
|
||||
w += 1;
|
||||
}
|
||||
zlut_xsy
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_xsy() {
|
||||
#[rustfmt::skip]
|
||||
let zlut_1s2: [[u8;16]; 16] = [
|
||||
[0x7, 0x3, 0xa, 0xb, 0x4, 0x2, 0x9, 0x1, 0xf, 0xe, 0x8, 0x0, 0xd, 0x6, 0xc, 0x5],
|
||||
[0x3, 0x2, 0xe, 0xa, 0x0, 0x8, 0xd, 0xb, 0x1, 0x9, 0x6, 0x7, 0x5, 0xc, 0x4, 0xf],
|
||||
[0x4, 0x0, 0x9, 0x8, 0x7, 0x1, 0xa, 0x2, 0xc, 0xd, 0xb, 0x3, 0xe, 0x5, 0xf, 0x6],
|
||||
[0x5, 0x4, 0x2, 0xa, 0x7, 0xc, 0x6, 0xf, 0xd, 0x9, 0x0, 0x1, 0xe, 0x8, 0x3, 0xb],
|
||||
[0xa, 0xb, 0x4, 0xc, 0x2, 0x9, 0x1, 0x8, 0x7, 0x3, 0xf, 0xe, 0x6, 0x0, 0x5, 0xd],
|
||||
[0x3, 0xb, 0xd, 0xc, 0x6, 0xf, 0x5, 0xe, 0x8, 0x9, 0x0, 0x4, 0x2, 0xa, 0x1, 0x7],
|
||||
[0x1, 0x0, 0x9, 0xd, 0xb, 0x3, 0x8, 0xe, 0xa, 0x2, 0x4, 0x5, 0xf, 0x6, 0xc, 0x7],
|
||||
[0xd, 0x4, 0xe, 0x5, 0x8, 0x0, 0x6, 0x7, 0x9, 0x1, 0xa, 0xc, 0x3, 0x2, 0xb, 0xf],
|
||||
[0x1, 0x5, 0x9, 0x8, 0x0, 0x6, 0x3, 0xb, 0xc, 0xd, 0x2, 0xa, 0x4, 0xf, 0x7, 0xe],
|
||||
[0xa, 0xb, 0x7, 0x3, 0x9, 0x1, 0x4, 0x2, 0x8, 0x0, 0xf, 0xe, 0xc, 0x5, 0xd, 0x6],
|
||||
[0x3, 0xb, 0xe, 0x8, 0x0, 0x1, 0xd, 0x9, 0x6, 0xf, 0x7, 0xc, 0x2, 0xa, 0x5, 0x4],
|
||||
[0x1, 0x5, 0x9, 0x8, 0x0, 0x6, 0x3, 0xb, 0xc, 0xd, 0x2, 0xa, 0x4, 0xf, 0x7, 0xe],
|
||||
[0x3, 0xb, 0xe, 0x8, 0x0, 0x1, 0xd, 0x9, 0x6, 0xf, 0x7, 0xc, 0x2, 0xa, 0x5, 0x4],
|
||||
[0xb, 0xa, 0x6, 0x2, 0x8, 0x0, 0x5, 0x3, 0x9, 0x1, 0xe, 0xf, 0xd, 0x4, 0xc, 0x7],
|
||||
[0x8, 0xe, 0xb, 0x3, 0x9, 0xd, 0x1, 0x0, 0xc, 0x7, 0xf, 0x6, 0x4, 0x5, 0xa, 0x2],
|
||||
[0x2, 0x4, 0x1, 0x9, 0x3, 0x7, 0xb, 0xa, 0x6, 0xd, 0x5, 0xc, 0xe, 0xf, 0x0, 0x8],
|
||||
];
|
||||
assert_eq!(zlut_1s2, PV2_1_S_2);
|
||||
|
||||
#[rustfmt::skip]
|
||||
let zlut_5sm: [[u8;16]; 16] = [
|
||||
[0x4, 0xc, 0x3, 0x2, 0x0, 0x9, 0x1, 0xa, 0x6, 0x7, 0xb, 0xf, 0x5, 0xd, 0x8, 0xe],
|
||||
[0xf, 0x6, 0xc, 0x7, 0xa, 0x2, 0x4, 0x5, 0xb, 0x3, 0x8, 0xe, 0x1, 0x0, 0x9, 0xd],
|
||||
[0x3, 0x5, 0xe, 0x6, 0x0, 0x4, 0xd, 0xc, 0xa, 0x1, 0xb, 0x2, 0x8, 0x9, 0xf, 0x7],
|
||||
[0x3, 0x5, 0x0, 0x8, 0x2, 0x6, 0xa, 0xb, 0x7, 0xc, 0x4, 0xd, 0xf, 0xe, 0x1, 0x9],
|
||||
[0xa, 0x2, 0x9, 0xf, 0x0, 0x1, 0x8, 0xc, 0xe, 0x7, 0xd, 0x6, 0xb, 0x3, 0x5, 0x4],
|
||||
[0x2, 0xa, 0x1, 0x7, 0x8, 0x9, 0x0, 0x4, 0x6, 0xf, 0x5, 0xe, 0x3, 0xb, 0xd, 0xc],
|
||||
[0x9, 0x2, 0xa, 0x3, 0x1, 0x0, 0xf, 0x7, 0xd, 0xb, 0xe, 0x6, 0xc, 0x8, 0x4, 0x5],
|
||||
[0xd, 0x4, 0xc, 0x7, 0x9, 0x1, 0xe, 0xf, 0x8, 0x0, 0x5, 0x3, 0xb, 0xa, 0x6, 0x2],
|
||||
[0xf, 0xe, 0x2, 0x6, 0xc, 0x4, 0x1, 0x7, 0xd, 0x5, 0xa, 0xb, 0x9, 0x0, 0x8, 0x3],
|
||||
[0x0, 0x6, 0x3, 0xb, 0x1, 0x5, 0x9, 0x8, 0x4, 0xf, 0x7, 0xe, 0xc, 0xd, 0x2, 0xa],
|
||||
[0x7, 0xe, 0x4, 0xf, 0x2, 0xa, 0xc, 0xd, 0x3, 0xb, 0x0, 0x6, 0x9, 0x8, 0x1, 0x5],
|
||||
[0x7, 0x6, 0x0, 0x8, 0x5, 0xe, 0x4, 0xd, 0xf, 0xb, 0x2, 0x3, 0xc, 0xa, 0x1, 0x9],
|
||||
[0x5, 0x1, 0xd, 0xc, 0x4, 0x2, 0x7, 0xf, 0x8, 0x9, 0x6, 0xe, 0x0, 0xb, 0x3, 0xa],
|
||||
[0x0, 0xb, 0x3, 0xa, 0x8, 0x9, 0x6, 0xe, 0x4, 0x2, 0x7, 0xf, 0x5, 0x1, 0xd, 0xc],
|
||||
[0x3, 0xb, 0x0, 0x6, 0x9, 0x8, 0x1, 0x5, 0x7, 0xe, 0x4, 0xf, 0x2, 0xa, 0xc, 0xd],
|
||||
[0xb, 0x0, 0x8, 0x1, 0x3, 0x2, 0xd, 0x5, 0xf, 0x9, 0xc, 0x4, 0xe, 0xa, 0x6, 0x7],
|
||||
];
|
||||
assert_eq!(zlut_5sm, PV2_5_S_M);
|
||||
|
||||
#[rustfmt::skip]
|
||||
let zlut_6is7: [[u8;16]; 16] = [
|
||||
[0xb, 0x6, 0x2, 0x9, 0x3, 0x7, 0xd, 0x1, 0xf, 0xe, 0x8, 0xa, 0x4, 0x5, 0xc, 0x0],
|
||||
[0x3, 0x2, 0x7, 0x5, 0x8, 0x9, 0xd, 0x1, 0xb, 0x6, 0x4, 0xf, 0xa, 0xe, 0xc, 0x0],
|
||||
[0x1, 0xd, 0x9, 0x8, 0x5, 0x7, 0x2, 0x3, 0x0, 0xc, 0xe, 0xa, 0xf, 0x4, 0x6, 0xb],
|
||||
[0xe, 0xf, 0xb, 0x7, 0x5, 0x4, 0x1, 0x3, 0xc, 0x8, 0xa, 0x6, 0xd, 0x0, 0x2, 0x9],
|
||||
[0xd, 0xc, 0x8, 0x4, 0x6, 0x7, 0x2, 0x0, 0xf, 0xb, 0x9, 0x5, 0xe, 0x3, 0x1, 0xa],
|
||||
[0xe, 0x2, 0x0, 0x4, 0x1, 0xa, 0x8, 0x5, 0xf, 0x3, 0x7, 0x6, 0xb, 0x9, 0xc, 0xd],
|
||||
[0x0, 0xb, 0x9, 0x4, 0xf, 0x3, 0x1, 0x5, 0xa, 0x8, 0xd, 0xc, 0xe, 0x2, 0x6, 0x7],
|
||||
[0x1, 0x0, 0x4, 0x8, 0xa, 0xb, 0xe, 0xc, 0x3, 0x7, 0x5, 0x9, 0x2, 0xf, 0xd, 0x6],
|
||||
[0xe, 0x2, 0x8, 0xc, 0x6, 0xd, 0x9, 0x4, 0xf, 0x3, 0xa, 0xb, 0x5, 0x7, 0x1, 0x0],
|
||||
[0x0, 0x2, 0x4, 0x5, 0xa, 0x6, 0xf, 0xe, 0x3, 0x8, 0xc, 0x1, 0xb, 0x7, 0xd, 0x9],
|
||||
[0xb, 0xa, 0xe, 0x2, 0x0, 0x1, 0x4, 0x6, 0x9, 0xd, 0xf, 0x3, 0x8, 0x5, 0x7, 0xc],
|
||||
[0x0, 0xc, 0x5, 0x4, 0xa, 0x8, 0xe, 0xf, 0x1, 0xd, 0x7, 0x3, 0x9, 0x2, 0x6, 0xb],
|
||||
[0x6, 0xa, 0x3, 0x2, 0xc, 0xe, 0x8, 0x9, 0x7, 0xb, 0x1, 0x5, 0xf, 0x4, 0x0, 0xd],
|
||||
[0xe, 0xa, 0x0, 0xc, 0x6, 0xb, 0xf, 0x4, 0x9, 0x8, 0x1, 0xd, 0x2, 0x3, 0x5, 0x7],
|
||||
[0xe, 0xf, 0x9, 0xb, 0x5, 0x4, 0xd, 0x1, 0xa, 0x7, 0x3, 0x8, 0x2, 0x6, 0xc, 0x0],
|
||||
[0x4, 0x0, 0xa, 0x6, 0xc, 0x1, 0x5, 0xe, 0x3, 0x2, 0xb, 0x7, 0x8, 0x9, 0xf, 0xd],
|
||||
];
|
||||
assert_eq!(zlut_6is7, PV2_6_IS_7);
|
||||
}
|
||||
|
||||
/* LUTs for M-layer (exors) -------------------------------------------------------- */
|
||||
// Bits (msb) 0123 (lsb) in u4
|
||||
static PV2_EXOR_TO_0: [[u8; 1 << 4]; 4] = build_zlut_exor(0);
|
||||
static PV2_EXOR_TO_1: [[u8; 1 << 4]; 4] = build_zlut_exor(1);
|
||||
static PV2_EXOR_TO_2: [[u8; 1 << 4]; 4] = build_zlut_exor(2);
|
||||
static PV2_EXOR_TO_3: [[u8; 1 << 4]; 4] = build_zlut_exor(3);
|
||||
#[rustfmt::skip]
|
||||
pub static PV2_EXOR_FW: [&[[u8; 1 << 4]; 4]; 2] = [
|
||||
&PV2_EXOR_TO_3, &PV2_EXOR_TO_2,
|
||||
];
|
||||
#[rustfmt::skip]
|
||||
pub static PV2_EXOR_BW: [&[[u8; 1 << 4]; 4]; 4] = [
|
||||
&PV2_EXOR_TO_3, &PV2_EXOR_TO_2, &PV2_EXOR_TO_1, &PV2_EXOR_TO_0,
|
||||
];
|
||||
|
||||
// e-xor(b) = xor of all bits except b. [Ex: e-xor(1010,0) = 0, e-xor(1010,1) = 1.]
|
||||
// Bits (msb) 0123 (lsb) in u4 [more convenient for describing the M-layer?]
|
||||
const fn u4_exor(x: u8, b: u8) -> u8 {
|
||||
assert!(b < 4 && x < 16);
|
||||
let ex_mask: u8 = 0xf - (1 << (3 - b));
|
||||
let mut c: u8 = x & ex_mask;
|
||||
c = (c & 0x3) ^ (c >> 2);
|
||||
c = (c & 0x1) ^ (c >> 1);
|
||||
c
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_u4_exor() {
|
||||
let x: u8 = 0b1010;
|
||||
assert_eq!(u4_exor(x, 0), 1);
|
||||
assert_eq!(u4_exor(x, 1), 0);
|
||||
assert_eq!(u4_exor(x, 2), 1);
|
||||
assert_eq!(u4_exor(x, 3), 0);
|
||||
|
||||
let x: u8 = 0b1110;
|
||||
assert_eq!(u4_exor(x, 0), 0);
|
||||
assert_eq!(u4_exor(x, 1), 0);
|
||||
assert_eq!(u4_exor(x, 2), 0);
|
||||
assert_eq!(u4_exor(x, 3), 1);
|
||||
}
|
||||
|
||||
// e-xor(b) = xor of all bits except b. [Ex: e-xor(1010,0) = 0, e-xor(1010,1) = 1.]
|
||||
// Bits (msb) 0123 (lsb) in u4 [more convenient for describing the M-layer?]
|
||||
const fn build_zlut_exor(to_b: u8) -> [[u8; 1 << 4]; 4] {
|
||||
let mut zlut_exor_to_b: [[u8; 1 << 4]; 4] = [[0; 1 << 4]; 4];
|
||||
let mut b: usize = 0;
|
||||
|
||||
while b < 4 {
|
||||
// for loop not allowed in const fn
|
||||
let mut x: usize = 0;
|
||||
while x < (1 << 4) {
|
||||
zlut_exor_to_b[b][x] = u4_exor(x as u8, b as u8) << to_b;
|
||||
x += 1;
|
||||
}
|
||||
b += 1;
|
||||
}
|
||||
zlut_exor_to_b
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_exor() {
|
||||
let zlut_exor_to_1: [[u8; 16]; 4] = [
|
||||
[0, 2, 2, 0, 2, 0, 0, 2, 0, 2, 2, 0, 2, 0, 0, 2],
|
||||
[0, 2, 2, 0, 0, 2, 2, 0, 2, 0, 0, 2, 2, 0, 0, 2],
|
||||
[0, 2, 0, 2, 2, 0, 2, 0, 2, 0, 2, 0, 0, 2, 0, 2],
|
||||
[0, 0, 2, 2, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 2, 2],
|
||||
];
|
||||
assert_eq!(zlut_exor_to_1, PV2_EXOR_TO_1);
|
||||
}
|
||||
|
||||
/* LUTs for xoring to high / low bits ---------------------------------------------- */
|
||||
// [Nb] Probably overkill in this case
|
||||
pub static PV2_XOR_TO_LOW: [u8; 1 << 4] = [
|
||||
// (01) ^ (23): (x & 3) ^^ (x >> 2) for x in range(16)
|
||||
0x0, 0x1, 0x2, 0x3, 0x1, 0x0, 0x3, 0x2, 0x2, 0x3, 0x0, 0x1, 0x3, 0x2, 0x1, 0x0,
|
||||
];
|
||||
static PV2_XOR_TO_HIGH: [u8; 1 << 4] = [
|
||||
// <above> << 2
|
||||
0x0, 0x4, 0x8, 0xc, 0x4, 0x0, 0xc, 0x8, 0x8, 0xc, 0x0, 0x4, 0xc, 0x8, 0x4, 0x0,
|
||||
];
|
||||
pub static PV2_XOR_FW: [&[u8; 1 << 4]; 2] = [&PV2_XOR_TO_HIGH, &PV2_XOR_TO_LOW];
|
||||
|
||||
// xoring + extract bits (low/high of 2 output bits)
|
||||
pub static PV2_XOR_BL: [[u8; 1 << 4]; 4] = build_zlut_xor_bhl(0);
|
||||
pub static PV2_XOR_BH: [[u8; 1 << 4]; 4] = build_zlut_xor_bhl(1);
|
||||
|
||||
const fn build_zlut_xor_bhl(hl: u8) -> [[u8; 1 << 4]; 4] {
|
||||
// [(((01) ^ (23) >> hl) << (3 - b)
|
||||
let mut zlut_xor_hl_to_b: [[u8; 1 << 4]; 4] = [[0; 1 << 4]; 4];
|
||||
let mut b: usize = 0;
|
||||
|
||||
while b < 4 {
|
||||
// for loop not allowed in const fn
|
||||
let mut x: usize = 0;
|
||||
while x < (1 << 4) {
|
||||
zlut_xor_hl_to_b[b][x] = ((PV2_XOR_TO_LOW[x] >> hl) & 0x1) << (3 - b);
|
||||
x += 1;
|
||||
}
|
||||
b += 1;
|
||||
}
|
||||
zlut_xor_hl_to_b
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_xor_bhl() {
|
||||
let zlut_xor_bh: [[u8; 16]; 4] = [
|
||||
[0, 0, 8, 8, 0, 0, 8, 8, 8, 8, 0, 0, 8, 8, 0, 0],
|
||||
[0, 0, 4, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 0, 0],
|
||||
[0, 0, 2, 2, 0, 0, 2, 2, 2, 2, 0, 0, 2, 2, 0, 0],
|
||||
[0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0],
|
||||
];
|
||||
assert_eq!(zlut_xor_bh, PV2_XOR_BH);
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Some bit manipulation converting u64 to vectors of 2/4-bit nibbles
|
||||
* ----------------------------------------------------------------------------------------------- */
|
||||
|
||||
// u64 -> [u4; 16], res[0] = 4 MSB bits of u64
|
||||
pub const fn u64_to_vec_u4(u: u64) -> [u8; 64 / 4] {
|
||||
let mut i: usize = 0;
|
||||
let mut v: [u8; 64 / 4] = [0; 64 / 4];
|
||||
|
||||
// "for" loop is unusable inside const
|
||||
while i < 64 / 4 {
|
||||
v[64 / 4 - i - 1] = ((u >> (4 * i)) & 0xf) as u8;
|
||||
i += 1;
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // kept for symmetry with u64_to_vec_u4(); might be useful to convert back decomposed constants
|
||||
pub const fn vec_u4_to_u64(v: [u8; 64 / 4]) -> u64 {
|
||||
let mut i: usize = 0;
|
||||
let mut u: u64 = 0;
|
||||
|
||||
// "for" loop is unusable inside const
|
||||
while i < 64 / 4 {
|
||||
u += (v[i] as u64) << (60 - 4 * i);
|
||||
i += 1;
|
||||
}
|
||||
u
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_u64_conv_vec_u4() {
|
||||
let u: u64 = 0x3f84d5b5b5470917;
|
||||
let u_dec: [u8; 16] = [
|
||||
0x3, 0xf, 0x8, 0x4, 0xd, 0x5, 0xb, 0x5, 0xb, 0x5, 0x4, 0x7, 0x0, 0x9, 0x1, 0x7,
|
||||
];
|
||||
assert_eq!(u_dec, u64_to_vec_u4(u));
|
||||
assert_eq!(u, vec_u4_to_u64(u_dec));
|
||||
|
||||
let u: u64 = 0x0ac6f9cd6e6f275d;
|
||||
let u_dec: [u8; 16] = [
|
||||
0x0, 0xa, 0xc, 0x6, 0xf, 0x9, 0xc, 0xd, 0x6, 0xe, 0x6, 0xf, 0x2, 0x7, 0x5, 0xd,
|
||||
];
|
||||
assert_eq!(u_dec, u64_to_vec_u4(u));
|
||||
assert_eq!(u, vec_u4_to_u64(u_dec));
|
||||
}
|
||||
|
||||
// u64 -> [u2; 32], res[0] = 2 MSB bits of u64
|
||||
pub const fn u64_to_vec_u2(u: u64) -> [u8; 64 / 2] {
|
||||
let mut i: usize = 0;
|
||||
let mut v: [u8; 64 / 2] = [0; 64 / 2];
|
||||
|
||||
while i < 64 / 2 {
|
||||
// for loop unusable inside const
|
||||
v[64 / 2 - i - 1] = ((u >> (2 * i)) & 0x3) as u8;
|
||||
i += 1;
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
pub const fn vec_u2_to_u64(v: [u8; 64 / 2]) -> u64 {
|
||||
let mut i: usize = 0;
|
||||
let mut u: u64 = 0;
|
||||
|
||||
while i < 64 / 2 {
|
||||
// for loop unusable inside const
|
||||
u += (v[i] as u64) << (62 - 2 * i);
|
||||
i += 1;
|
||||
}
|
||||
u
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_u64_conv_vec_u2() {
|
||||
let u: u64 = 0x603cd95fa72a8704;
|
||||
#[rustfmt::skip]
|
||||
let u_dec: [u8; 32] = [
|
||||
0x1, 0x2, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x3, 0x1, 0x2, 0x1, 0x1, 0x1, 0x3, 0x3,
|
||||
0x2, 0x2, 0x1, 0x3, 0x0, 0x2, 0x2, 0x2, 0x2, 0x0, 0x1, 0x3, 0x0, 0x0, 0x1, 0x0];
|
||||
assert_eq!(u_dec, u64_to_vec_u2(u));
|
||||
assert_eq!(u, vec_u2_to_u64(u_dec));
|
||||
|
||||
let u: u64 = 0xee873b2ec447944d;
|
||||
#[rustfmt::skip]
|
||||
let u_dec: [u8; 32] = [
|
||||
0x3, 0x2, 0x3, 0x2, 0x2, 0x0, 0x1, 0x3, 0x0, 0x3, 0x2, 0x3, 0x0, 0x2, 0x3, 0x2,
|
||||
0x3, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x3, 0x2, 0x1, 0x1, 0x0, 0x1, 0x0, 0x3, 0x1];
|
||||
assert_eq!(u_dec, u64_to_vec_u2(u));
|
||||
assert_eq!(u, vec_u2_to_u64(u_dec));
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
//! Known-answer tests against the PRINCEv2 paper test vectors.
|
||||
//!
|
||||
//! These tests run a full homomorphic PRINCEv2 encryption/decryption and assert that the decrypted
|
||||
//! ciphertext matches the values from PRINCEv2 specifications [BEK+20, Appendix B].
|
||||
//!
|
||||
//! [BEK+20] Dusan Božilov, Maria Eichlseder, Miroslav Kneževic, Baptiste Lambin, Gregor Leander,
|
||||
//! Thorben Moos, Ventzislav Nikov, Shahram Rasoolzadeh, Yosuke Todo, and Friedrich Wiemer.
|
||||
//! PRINCEv2: More security for (almost) no overhead. In Selected Areas in Cryptography (SAC 2020),
|
||||
//! volume 12804 of LNCS, pp.483--511, Springer, 2020. DOI:10.1007/978-3-030-81652-0_19.
|
||||
|
||||
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M128;
|
||||
use tfhe::shortint::prelude::*;
|
||||
|
||||
use tfhe_princev2::{pv2_decrypt, pv2_encrypt, u64_to_vec_u2, vec_u2_to_u64};
|
||||
|
||||
// KAT structure for Pv2 cipher
|
||||
struct Pv2Kat {
|
||||
name: &'static str,
|
||||
ptxt: u64,
|
||||
k0: u64,
|
||||
k1: u64,
|
||||
ctxt: u64,
|
||||
}
|
||||
|
||||
/// Test vectors from [BEK+20, Appendix B]
|
||||
static PV2_KATS_TABLE: [Pv2Kat; 5] = [
|
||||
Pv2Kat {
|
||||
name: "PRINCEv2 KAT #1",
|
||||
ptxt: 0x0000000000000000,
|
||||
k0: 0x0000000000000000,
|
||||
k1: 0x0000000000000000,
|
||||
ctxt: 0x0125fc7359441690,
|
||||
},
|
||||
Pv2Kat {
|
||||
name: "PRINCEv2 KAT #2",
|
||||
ptxt: 0xffffffffffffffff,
|
||||
k0: 0x0000000000000000,
|
||||
k1: 0x0000000000000000,
|
||||
ctxt: 0x832bd46f108e7857,
|
||||
},
|
||||
Pv2Kat {
|
||||
name: "PRINCEv2 KAT #3",
|
||||
ptxt: 0x0000000000000000,
|
||||
k0: 0xffffffffffffffff,
|
||||
k1: 0x0000000000000000,
|
||||
ctxt: 0xee873b2ec447944d,
|
||||
},
|
||||
Pv2Kat {
|
||||
name: "PRINCEv2 KAT #4",
|
||||
ptxt: 0x0000000000000000,
|
||||
k0: 0x0000000000000000,
|
||||
k1: 0xffffffffffffffff,
|
||||
ctxt: 0x0ac6f9cd6e6f275d,
|
||||
},
|
||||
Pv2Kat {
|
||||
name: "PRINCEv2 KAT #5",
|
||||
ptxt: 0x0123456789abcdef,
|
||||
k0: 0x0123456789abcdef,
|
||||
k1: 0xfedcba9876543210,
|
||||
ctxt: 0x603cd95fa72a8704,
|
||||
},
|
||||
];
|
||||
|
||||
/// Encrypt a u64 as 32 ciphertexts, each holding a 2-bit nibble in the low bits of the FHE message
|
||||
/// space. Most significant bits of the input are at index 0 in the output
|
||||
fn encrypt_u64_as_vec_u2l(s_key: &ClientKey, x: u64) -> [Ciphertext; 32] {
|
||||
let x_u2: [u8; 32] = u64_to_vec_u2(x);
|
||||
let ct: Vec<Ciphertext> = x_u2
|
||||
.into_iter()
|
||||
.map(|u2| s_key.encrypt(u2 as u64))
|
||||
.collect();
|
||||
ct.try_into().unwrap()
|
||||
}
|
||||
|
||||
/// Reverse of function encrypt_u64_as_vec_u2l()
|
||||
fn decrypt_vec_u2l_as_u64(s_key: &ClientKey, v: &[Ciphertext; 32]) -> u64 {
|
||||
let x_u2: [u8; 32] = std::array::from_fn(|n| s_key.decrypt_message_and_carry(&v[n]) as u8);
|
||||
let x: u64 = vec_u2_to_u64(x_u2);
|
||||
x
|
||||
}
|
||||
|
||||
/// Run KATs homomorphically for PRINCEv2 Encryption.
|
||||
/// [Note] Takes approximately 21s / KAT on 8 cores.
|
||||
#[test]
|
||||
fn pv2_enc_kat() {
|
||||
let (s_key, ev_key): (ClientKey, ServerKey) = // Params: Need 4-bits msg + nu >= 4
|
||||
tfhe::shortint::gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M128);
|
||||
|
||||
for tkat in &PV2_KATS_TABLE {
|
||||
// Encryptions of inputs (k0,k1,m)
|
||||
let ct_k0: [Ciphertext; 32] = encrypt_u64_as_vec_u2l(&s_key, tkat.k0);
|
||||
let ct_k1: [Ciphertext; 32] = encrypt_u64_as_vec_u2l(&s_key, tkat.k1);
|
||||
let ct_m: [Ciphertext; 32] = encrypt_u64_as_vec_u2l(&s_key, tkat.ptxt);
|
||||
|
||||
// PRINCEv2 Enc in FHE
|
||||
let mut ct_out: [Ciphertext; 32] = std::array::from_fn(|_| ev_key.create_trivial(0)); // [NB] shortint::create_trivial() vs boolean::trivial_encrypt()
|
||||
pv2_encrypt(&ev_key, &mut ct_out, &ct_m, &ct_k0, &ct_k1);
|
||||
|
||||
// Testing the (de-)encrypted result
|
||||
let pt_out: u64 = decrypt_vec_u2l_as_u64(&s_key, &ct_out);
|
||||
assert_eq!(
|
||||
pt_out, tkat.ctxt,
|
||||
"{} failed: ptxt={:#018x}, k0={:#018x}, k1={:#018x}, expected={:#018x}, got={:#018x}",
|
||||
tkat.name, tkat.ptxt, tkat.k0, tkat.k1, tkat.ctxt, pt_out
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pv2_dec_kat() {
|
||||
let (s_key, ev_key): (ClientKey, ServerKey) = // Params: Need 4-bits msg + nu >= 4
|
||||
tfhe::shortint::gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M128);
|
||||
|
||||
for tkat in &PV2_KATS_TABLE {
|
||||
// Encryptions of inputs (k0,k1,m)
|
||||
let ct_k0: [Ciphertext; 32] = encrypt_u64_as_vec_u2l(&s_key, tkat.k0);
|
||||
let ct_k1: [Ciphertext; 32] = encrypt_u64_as_vec_u2l(&s_key, tkat.k1);
|
||||
let ct_c: [Ciphertext; 32] = encrypt_u64_as_vec_u2l(&s_key, tkat.ctxt);
|
||||
|
||||
// PRINCEv2 Dec in FHE
|
||||
let mut ct_out: [Ciphertext; 32] = std::array::from_fn(|_| ev_key.create_trivial(0)); // [NB] shortint::create_trivial() vs boolean::trivial_encrypt()
|
||||
pv2_decrypt(&ev_key, &mut ct_out, &ct_c, &ct_k0, &ct_k1);
|
||||
|
||||
// Testing the (de-)encrypted result
|
||||
let pt_out: u64 = decrypt_vec_u2l_as_u64(&s_key, &ct_out);
|
||||
assert_eq!(
|
||||
pt_out, tkat.ptxt,
|
||||
"{} failed: ctxt={:#018x}, k0={:#018x}, k1={:#018x}, expected={:#018x}, got={:#018x}",
|
||||
tkat.name, tkat.ctxt, tkat.k0, tkat.k1, tkat.ptxt, pt_out
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,7 @@ impl HpuVarWrapped {
|
||||
{
|
||||
let mut inner = var.inner.lock().unwrap();
|
||||
|
||||
for (slot, ct) in std::iter::zip(inner.bundle.iter_mut(), ct.into_iter()) {
|
||||
for (slot, ct) in std::iter::zip(inner.bundle.iter_mut(), ct) {
|
||||
#[cfg(feature = "io-dump")]
|
||||
let params = ct.params().clone();
|
||||
for (id, cut) in ct.into_container().iter().enumerate() {
|
||||
|
||||
@@ -1 +1 @@
|
||||
nightly-2026-01-14
|
||||
nightly-2026-04-22
|
||||
|
||||
@@ -6,12 +6,11 @@ use std::process::Command;
|
||||
|
||||
const DIR_TO_IGNORE: [&str; 1] = ["apps/test-vectors"];
|
||||
|
||||
const FILES_TO_IGNORE: [&str; 12] = [
|
||||
const FILES_TO_IGNORE: [&str; 11] = [
|
||||
// This contains fragments of code that are unrelated to TFHE-rs
|
||||
"tfhe/docs/tutorials/sha256-bool.md",
|
||||
// TODO: This contains code that could be executed as a trivium docstring
|
||||
"apps/trivium/README.md",
|
||||
"apps/princev2/README.md",
|
||||
// TODO: should we test this ?
|
||||
"utils/tfhe-versionable/README.md",
|
||||
"utils/wasm-par-mq/README.md",
|
||||
|
||||
@@ -54,6 +54,7 @@ internal-keycache = ["tfhe/internal-keycache"]
|
||||
avx512 = ["tfhe/avx512"]
|
||||
pbs-stats = ["tfhe/pbs-stats"]
|
||||
zk-pok = ["tfhe/zk-pok", "dep:tfhe-zk-pok"]
|
||||
experimental = ["tfhe/experimental"]
|
||||
|
||||
[[bench]]
|
||||
name = "boolean"
|
||||
@@ -230,3 +231,9 @@ required-features = ["integer", "internal-keycache"]
|
||||
name = "wasm_benchmarks_parser"
|
||||
path = "src/bin/wasm_benchmarks_parser.rs"
|
||||
required-features = ["shortint", "internal-keycache"]
|
||||
|
||||
[[bench]]
|
||||
name = "cm-bench"
|
||||
path = "benches/core_crypto/cm_bench.rs"
|
||||
harness = false
|
||||
required-features = ["experimental"]
|
||||
|
||||
211
tfhe-benchmark/benches/core_crypto/cm_bench.rs
Normal file
211
tfhe-benchmark/benches/core_crypto/cm_bench.rs
Normal file
@@ -0,0 +1,211 @@
|
||||
use cm_fft64::programmable_bootstrap_cm_lwe_ciphertext;
|
||||
use criterion::{black_box, criterion_main, Criterion};
|
||||
use tfhe::core_crypto::experimental::prelude::cm_lwe_keyswitch_key_generation::allocate_and_generate_new_cm_lwe_keyswitch_key;
|
||||
use tfhe::core_crypto::experimental::prelude::cm_modulus_switch_noise_reduction::improve_lwe_ciphertext_modulus_switch_noise_for_binary_key_cm;
|
||||
use tfhe::core_crypto::experimental::prelude::*;
|
||||
use tfhe::core_crypto::prelude::*;
|
||||
|
||||
fn cm_bench(c: &mut Criterion) {
|
||||
let bench_cm_params_2_minus_64: Vec<CmApParams> = vec![
|
||||
CM_PARAM_2_2_MINUS_64,
|
||||
CM_PARAM_4_2_MINUS_64,
|
||||
CM_PARAM_6_2_MINUS_64,
|
||||
CM_PARAM_8_2_MINUS_64,
|
||||
CM_PARAM_10_2_MINUS_64,
|
||||
CM_PARAM_2_4_MINUS_64,
|
||||
CM_PARAM_4_4_MINUS_64,
|
||||
CM_PARAM_6_4_MINUS_64,
|
||||
CM_PARAM_8_4_MINUS_64,
|
||||
CM_PARAM_10_4_MINUS_64,
|
||||
CM_PARAM_2_6_MINUS_64,
|
||||
CM_PARAM_4_6_MINUS_64,
|
||||
CM_PARAM_6_6_MINUS_64,
|
||||
CM_PARAM_8_6_MINUS_64,
|
||||
CM_PARAM_10_6_MINUS_64,
|
||||
CM_PARAM_2_8_MINUS_64,
|
||||
CM_PARAM_4_8_MINUS_64,
|
||||
CM_PARAM_6_8_MINUS_64,
|
||||
CM_PARAM_8_8_MINUS_64,
|
||||
CM_PARAM_10_8_MINUS_64,
|
||||
];
|
||||
|
||||
cm_bench_for_pfail(c, &bench_cm_params_2_minus_64, "2^-64");
|
||||
|
||||
let bench_cm_params_2_minus_128: Vec<CmApParams> = vec![
|
||||
CM_PARAM_2_2_MINUS_128,
|
||||
CM_PARAM_4_2_MINUS_128,
|
||||
CM_PARAM_6_2_MINUS_128,
|
||||
CM_PARAM_8_2_MINUS_128,
|
||||
CM_PARAM_10_2_MINUS_128,
|
||||
CM_PARAM_2_4_MINUS_128,
|
||||
CM_PARAM_4_4_MINUS_128,
|
||||
CM_PARAM_6_4_MINUS_128,
|
||||
CM_PARAM_8_4_MINUS_128,
|
||||
CM_PARAM_10_4_MINUS_128,
|
||||
CM_PARAM_2_6_MINUS_128,
|
||||
CM_PARAM_4_6_MINUS_128,
|
||||
CM_PARAM_6_6_MINUS_128,
|
||||
CM_PARAM_8_6_MINUS_128,
|
||||
CM_PARAM_10_6_MINUS_128,
|
||||
CM_PARAM_2_8_MINUS_128,
|
||||
CM_PARAM_4_8_MINUS_128,
|
||||
CM_PARAM_6_8_MINUS_128,
|
||||
CM_PARAM_8_8_MINUS_128,
|
||||
CM_PARAM_10_8_MINUS_128,
|
||||
];
|
||||
|
||||
cm_bench_for_pfail(c, &bench_cm_params_2_minus_128, "2^-128");
|
||||
}
|
||||
|
||||
fn cm_bench_for_pfail(c: &mut Criterion, bench_cm_params: &[CmApParams], p_fail: &str) {
|
||||
let mut bench_group = c.benchmark_group("Common Mask Benchmarks");
|
||||
bench_group.sample_size(10);
|
||||
|
||||
// Create the PRNG
|
||||
let mut seeder = new_seeder();
|
||||
let seeder = seeder.as_mut();
|
||||
let mut encryption_generator =
|
||||
EncryptionRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed(), seeder);
|
||||
let mut secret_generator = SecretRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed());
|
||||
|
||||
for cm_param in bench_cm_params {
|
||||
let cm_dimension = cm_param.cm_dimension;
|
||||
|
||||
let ciphertext_modulus = cm_param.ciphertext_modulus;
|
||||
|
||||
let bench_name = format!(
|
||||
"KS-CM-PBS_p={}_w={}_pfail={p_fail}",
|
||||
cm_param.precision, cm_dimension.0,
|
||||
);
|
||||
|
||||
let lwe_noise_distribution = cm_param.lwe_noise_distribution;
|
||||
|
||||
assert_eq!(
|
||||
cm_param.ciphertext_modulus,
|
||||
CiphertextModulus::<u64>::new_native()
|
||||
);
|
||||
let encoding_with_padding = 1 << 63;
|
||||
let glwe_dimension = cm_param.glwe_dimension;
|
||||
let polynomial_size = cm_param.polynomial_size;
|
||||
|
||||
let msg_modulus = 1u64 << cm_param.precision;
|
||||
let delta = encoding_with_padding / msg_modulus;
|
||||
|
||||
let f = |x| x;
|
||||
|
||||
let accumulator = cm_generate_programmable_bootstrap_glwe_lut(
|
||||
polynomial_size,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
msg_modulus.cast_into(),
|
||||
cm_param.ciphertext_modulus,
|
||||
delta,
|
||||
f,
|
||||
);
|
||||
|
||||
let CmBootstrapKeys {
|
||||
small_lwe_sk,
|
||||
big_lwe_sk,
|
||||
bsk,
|
||||
fbsk,
|
||||
} = generate_cm_pbs_keys(cm_param, &mut encryption_generator, &mut secret_generator);
|
||||
drop(bsk);
|
||||
|
||||
let cm_lwe_keyswitch_key = allocate_and_generate_new_cm_lwe_keyswitch_key(
|
||||
&big_lwe_sk,
|
||||
&small_lwe_sk,
|
||||
cm_dimension,
|
||||
cm_param.base_log_ks,
|
||||
cm_param.level_ks,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let plaintexts = PlaintextList::from_container(vec![0_u64; cm_dimension.0]);
|
||||
|
||||
let ct_in = allocate_and_encrypt_new_cm_lwe_ciphertext(
|
||||
&big_lwe_sk,
|
||||
&plaintexts,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let mut ct_after_ks = CmLweCiphertext::new(
|
||||
0u64,
|
||||
cm_lwe_keyswitch_key.output_lwe_dimension(),
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let mut ct_out = CmLweCiphertext::new(
|
||||
0u64,
|
||||
fbsk.output_lwe_dimension(),
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let max_nb_zeros_n = cm_param.max_nb_zeros_n.ceil() as usize;
|
||||
|
||||
let mut encryptions_of_zero = CmLweCiphertextList::new(
|
||||
0,
|
||||
cm_param.lwe_dimension,
|
||||
cm_dimension,
|
||||
CmLweCiphertextCount(max_nb_zeros_n),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let plaintext_list = PlaintextList::new(0, PlaintextCount(cm_dimension.0));
|
||||
|
||||
let plaintext_lists: Vec<_> = (0..max_nb_zeros_n)
|
||||
.map(|_| plaintext_list.clone())
|
||||
.collect();
|
||||
|
||||
encrypt_cm_lwe_ciphertext_list(
|
||||
&small_lwe_sk,
|
||||
&mut encryptions_of_zero,
|
||||
&plaintext_lists,
|
||||
lwe_noise_distribution,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let log_modulus = polynomial_size.to_blind_rotation_input_modulus_log();
|
||||
|
||||
{
|
||||
bench_group.bench_function(&bench_name, |b| {
|
||||
b.iter(|| {
|
||||
cm_keyswitch_lwe_ciphertext(&cm_lwe_keyswitch_key, &ct_in, &mut ct_after_ks);
|
||||
|
||||
improve_lwe_ciphertext_modulus_switch_noise_for_binary_key_cm(
|
||||
&mut ct_after_ks,
|
||||
&encryptions_of_zero,
|
||||
cm_param.r_sigma_factor_n,
|
||||
cm_param.ms_bound_n,
|
||||
cm_param.ms_input_variance_n,
|
||||
log_modulus,
|
||||
);
|
||||
|
||||
programmable_bootstrap_cm_lwe_ciphertext(
|
||||
&ct_after_ks,
|
||||
&mut ct_out,
|
||||
&accumulator.as_view(),
|
||||
&fbsk,
|
||||
);
|
||||
|
||||
black_box(&mut ct_out);
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bench_group.finish();
|
||||
}
|
||||
|
||||
pub fn cm_group() {
|
||||
let mut criterion: Criterion<_> = (Criterion::default()).configure_from_args();
|
||||
|
||||
cm_bench(&mut criterion);
|
||||
}
|
||||
|
||||
criterion_main!(cm_group);
|
||||
@@ -168,7 +168,7 @@ mod generic_tests {
|
||||
fn test_xof_seed_getters() {
|
||||
let seed_bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
|
||||
let bits = u128::from_le_bytes(seed_bytes);
|
||||
let dsep = [b't', b'f', b'h', b'e', b'k', b's', b'p', b's'];
|
||||
let dsep = *b"tfheksps";
|
||||
let seed = XofSeed::new_u128(bits, dsep);
|
||||
|
||||
let s = u128::from_le_bytes(seed.seed().try_into().unwrap());
|
||||
|
||||
@@ -64,7 +64,7 @@ tfhe-fft = { version = "0.10.1", path = "../tfhe-fft", features = [
|
||||
"serde",
|
||||
"fft128",
|
||||
] }
|
||||
tfhe-ntt = { version = "0.7.0", path = "../tfhe-ntt" }
|
||||
tfhe-ntt = { version = "0.7.1", path = "../tfhe-ntt" }
|
||||
pulp = { workspace = true, features = ["default"] }
|
||||
tfhe-cuda-backend = { version = "0.14.0", path = "../backends/tfhe-cuda-backend", optional = true }
|
||||
aligned-vec = { workspace = true, features = ["default", "serde"] }
|
||||
|
||||
@@ -75,11 +75,11 @@
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="594.0" y="420.0">121 ms</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="678.0" y="420.0">165 ms</text>
|
||||
<text dominant-baseline="middle" text-anchor="start" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="6" y="460.0">Leading / Trailing zeros/ones</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="342.0" y="460.0">88.4 ms</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="426.0" y="460.0">148 ms</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="510.0" y="460.0">169 ms</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="594.0" y="460.0">222 ms</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="678.0" y="460.0">275 ms</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="342.0" y="460.0">67.2 ms</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="426.0" y="460.0">70.6 ms</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="510.0" y="460.0">89.8 ms</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="594.0" y="460.0">92.6 ms</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="678.0" y="460.0">113 ms</text>
|
||||
<text dominant-baseline="middle" text-anchor="start" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="6" y="500.0">Log2</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="342.0" y="500.0">110 ms</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="426.0" y="500.0">163 ms</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -75,11 +75,11 @@
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="594.0" y="420.0">32.5 ops/s</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="678.0" y="420.0">14.0 ops/s</text>
|
||||
<text dominant-baseline="middle" text-anchor="start" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="6" y="460.0">Leading / Trailing zeros/ones</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="342.0" y="460.0">625 ops/s</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="426.0" y="460.0">247 ops/s</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="510.0" y="460.0">108 ops/s</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="594.0" y="460.0">44.1 ops/s</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="678.0" y="460.0">19.0 ops/s</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="342.0" y="460.0">824 ops/s</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="426.0" y="460.0">487 ops/s</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="510.0" y="460.0">222 ops/s</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="594.0" y="460.0">119 ops/s</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="678.0" y="460.0">57.8 ops/s</text>
|
||||
<text dominant-baseline="middle" text-anchor="start" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="6" y="500.0">Log2</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="342.0" y="500.0">542 ops/s</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="14" font-weight="normal" fill="black" x="426.0" y="500.0">220 ops/s</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -85,7 +85,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let public_key = tfhe::CompactPublicKey::try_new(&client_key).unwrap();
|
||||
// This can be left empty, but if provided allows to tie the proof to arbitrary data
|
||||
let metadata = [b'T', b'F', b'H', b'E', b'-', b'r', b's'];
|
||||
let metadata = b"TFHE-rs";
|
||||
|
||||
let clear_a = random::<u64>();
|
||||
let clear_b = random::<u64>();
|
||||
@@ -93,7 +93,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let proven_compact_list = tfhe::ProvenCompactCiphertextList::builder(&public_key)
|
||||
.push(clear_a)
|
||||
.push(clear_b)
|
||||
.build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Verify)?;
|
||||
.build_with_proof_packed(&crs, metadata, ZkComputeLoad::Verify)?;
|
||||
|
||||
// Server side
|
||||
let result = {
|
||||
@@ -101,7 +101,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// Verify the proofs and expand the ciphertexts
|
||||
let expander =
|
||||
proven_compact_list.verify_and_expand(&crs, &public_key, &metadata)?;
|
||||
proven_compact_list.verify_and_expand(&crs, &public_key, metadata)?;
|
||||
let a: tfhe::FheUint64 = expander.get(0)?.unwrap();
|
||||
let b: tfhe::FheUint64 = expander.get(1)?.unwrap();
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ pub fn main() {
|
||||
let rerand_domain_separator = *b"TFHE_Rrd";
|
||||
|
||||
let crs = CompactPkeCrs::from_config(config, 2048).unwrap();
|
||||
let metadata = [b'r', b'e', b'r', b'a', b'n', b'd'];
|
||||
let metadata = b"rerand";
|
||||
|
||||
set_server_key(sks);
|
||||
|
||||
@@ -132,7 +132,7 @@ pub fn main() {
|
||||
.push(clear_a)
|
||||
.push(clear_b)
|
||||
.push(false)
|
||||
.build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof)
|
||||
.build_with_proof_packed(&crs, metadata, ZkComputeLoad::Proof)
|
||||
.unwrap();
|
||||
|
||||
// Simulate a 256 bits nonce
|
||||
@@ -151,7 +151,7 @@ pub fn main() {
|
||||
|
||||
// Verify, re_randomize and expand
|
||||
let expander = compact_list
|
||||
.verify_re_randomize_and_expand(&crs, &cpk, &metadata, seed_gen.next_seed().unwrap())
|
||||
.verify_re_randomize_and_expand(&crs, &cpk, metadata, seed_gen.next_seed().unwrap())
|
||||
.unwrap();
|
||||
|
||||
let a: FheUint64 = expander.get(0).unwrap().unwrap();
|
||||
|
||||
@@ -46,7 +46,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let server_key = tfhe::ServerKey::new(&client_key);
|
||||
let public_key = tfhe::CompactPublicKey::try_new(&client_key).unwrap();
|
||||
// This can be left empty, but if provided allows to tie the proof to arbitrary data
|
||||
let metadata = [b'T', b'F', b'H', b'E', b'-', b'r', b's'];
|
||||
let metadata = b"TFHE-rs";
|
||||
|
||||
let clear_a = rng.gen::<u64>();
|
||||
let clear_b = rng.gen::<u64>();
|
||||
@@ -54,7 +54,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let proven_compact_list = tfhe::ProvenCompactCiphertextList::builder(&public_key)
|
||||
.push(clear_a)
|
||||
.push(clear_b)
|
||||
.build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Verify)?;
|
||||
.build_with_proof_packed(&crs, metadata, ZkComputeLoad::Verify)?;
|
||||
|
||||
// Server side
|
||||
let result = {
|
||||
@@ -62,7 +62,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// Verify the ciphertexts
|
||||
let expander =
|
||||
proven_compact_list.verify_and_expand(&crs, &public_key, &metadata)?;
|
||||
proven_compact_list.verify_and_expand(&crs, &public_key, metadata)?;
|
||||
let a: tfhe::FheUint64 = expander.get(0)?.unwrap();
|
||||
let b: tfhe::FheUint64 = expander.get(1)?.unwrap();
|
||||
|
||||
@@ -118,7 +118,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let server_key = tfhe::ServerKey::new(&client_key);
|
||||
let public_key = tfhe::CompactPublicKey::try_new(&client_key).unwrap();
|
||||
// This can be left empty, but if provided allows to tie the proof to arbitrary data
|
||||
let metadata = [b'T', b'F', b'H', b'E', b'-', b'r', b's'];
|
||||
let metadata = b"TFHE-rs";
|
||||
|
||||
let clear_a = rng.gen::<u64>();
|
||||
let clear_b = rng.gen::<u64>();
|
||||
@@ -126,7 +126,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let proven_compact_list = tfhe::ProvenCompactCiphertextList::builder(&public_key)
|
||||
.push(clear_a)
|
||||
.push(clear_b)
|
||||
.build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Verify)?;
|
||||
.build_with_proof_packed(&crs, metadata, ZkComputeLoad::Verify)?;
|
||||
|
||||
// Server side
|
||||
let result = {
|
||||
@@ -134,7 +134,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// Verify the ciphertexts
|
||||
let expander =
|
||||
proven_compact_list.verify_and_expand(&crs, &public_key, &metadata)?;
|
||||
proven_compact_list.verify_and_expand(&crs, &public_key, metadata)?;
|
||||
let a: tfhe::FheUint64 = expander.get(0)?.unwrap();
|
||||
let b: tfhe::FheUint64 = expander.get(1)?.unwrap();
|
||||
|
||||
|
||||
@@ -252,9 +252,7 @@ where
|
||||
))
|
||||
}
|
||||
|
||||
const NON_ESCAPABLE_SYMBOLS: [u8; 14] = [
|
||||
b'&', b';', b':', b',', b'`', b'~', b'-', b'_', b'!', b'@', b'#', b'%', b'\'', b'\"',
|
||||
];
|
||||
const NON_ESCAPABLE_SYMBOLS: [u8; 14] = *b"&;:,`~-_!@#%'\"";
|
||||
|
||||
fn atom<Input>() -> impl Parser<Input, Output = RegExpr>
|
||||
where
|
||||
|
||||
@@ -329,13 +329,14 @@ fn encrypt_constant_ggsw_level_matrix_row<Scalar, NoiseDistribution, KeyCont, Ou
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let mut body = row_as_glwe.get_mut_body();
|
||||
|
||||
if row_index < last_row_index {
|
||||
// Not the last row
|
||||
let sk_poly_list = glwe_secret_key.as_polynomial_list();
|
||||
let sk_poly = sk_poly_list.get(row_index);
|
||||
|
||||
// Copy the key polynomial to the output body, to avoid allocating a temporary buffer
|
||||
let mut body = row_as_glwe.get_mut_body();
|
||||
body.as_mut().copy_from_slice(sk_poly.as_ref());
|
||||
|
||||
let ciphertext_modulus = body.ciphertext_modulus();
|
||||
@@ -352,7 +353,6 @@ fn encrypt_constant_ggsw_level_matrix_row<Scalar, NoiseDistribution, KeyCont, Ou
|
||||
}
|
||||
} else {
|
||||
// The last row needs a slightly different treatment
|
||||
let mut body = row_as_glwe.get_mut_body();
|
||||
let ciphertext_modulus = body.ciphertext_modulus();
|
||||
|
||||
body.as_mut().fill(Scalar::ZERO);
|
||||
|
||||
@@ -1251,7 +1251,7 @@ pub fn encrypt_lwe_ciphertext_iterator_with_seeded_public_key<Scalar, KeyCont, O
|
||||
}
|
||||
}
|
||||
|
||||
for (output_ct, plaintext) in output.iter_mut().zip(encoded.into_iter()) {
|
||||
for (output_ct, plaintext) in output.iter_mut().zip(encoded) {
|
||||
lwe_ciphertext_plaintext_add_assign(output_ct, plaintext);
|
||||
}
|
||||
}
|
||||
@@ -2123,7 +2123,7 @@ pub fn encrypt_lwe_ciphertext_with_compact_public_key<
|
||||
///
|
||||
/// // We can add custom metadata that will be required for verification, allowing to tie the proof
|
||||
/// // to some arbitrary data.
|
||||
/// let metadata = [b'T', b'F', b'H', b'E', b'-', b'r', b's'];
|
||||
/// let metadata = b"TFHE-rs";
|
||||
///
|
||||
/// // Create the PRNG
|
||||
/// let mut seeder = new_seeder();
|
||||
@@ -2170,14 +2170,14 @@ pub fn encrypt_lwe_ciphertext_with_compact_public_key<
|
||||
/// glwe_noise_distribution,
|
||||
/// encryption_generator.noise_generator_mut(),
|
||||
/// &crs,
|
||||
/// &metadata,
|
||||
/// metadata,
|
||||
/// ZkComputeLoad::Proof,
|
||||
/// )
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // verify the ciphertext list with the proof
|
||||
/// assert!(
|
||||
/// verify_lwe_ciphertext(&lwe, &lwe_compact_public_key, &proof, &crs, &metadata).is_valid()
|
||||
/// verify_lwe_ciphertext(&lwe, &lwe_compact_public_key, &proof, &crs, metadata).is_valid()
|
||||
/// );
|
||||
///
|
||||
/// let decrypted_plaintext = decrypt_lwe_ciphertext(&lwe_secret_key, &lwe);
|
||||
@@ -2572,7 +2572,7 @@ pub fn encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
|
||||
///
|
||||
/// // We can add custom metadata that will be required for verification, allowing to tie the proof
|
||||
/// // to some arbitrary data.
|
||||
/// let metadata = [b'T', b'F', b'H', b'E', b'-', b'r', b's'];
|
||||
/// let metadata = b"TFHE-rs";
|
||||
///
|
||||
/// // Create the PRNG
|
||||
/// let mut seeder = new_seeder();
|
||||
@@ -2623,7 +2623,7 @@ pub fn encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
|
||||
/// glwe_noise_distribution,
|
||||
/// encryption_generator.noise_generator_mut(),
|
||||
/// &crs,
|
||||
/// &metadata,
|
||||
/// metadata,
|
||||
/// ZkComputeLoad::Proof,
|
||||
/// )
|
||||
/// .unwrap();
|
||||
@@ -2634,7 +2634,7 @@ pub fn encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
|
||||
/// &lwe_compact_public_key,
|
||||
/// &proof,
|
||||
/// &crs,
|
||||
/// &metadata,
|
||||
/// metadata,
|
||||
/// )
|
||||
/// .is_valid());
|
||||
///
|
||||
@@ -3045,7 +3045,7 @@ pub fn par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
|
||||
///
|
||||
/// // We can add custom metadata that will be required for verification, allowing to tie the proof
|
||||
/// // to some arbitrary data.
|
||||
/// let metadata = [b'T', b'F', b'H', b'E', b'-', b'r', b's'];
|
||||
/// let metadata = b"TFHE-rs";
|
||||
///
|
||||
/// // Create the PRNG
|
||||
/// let mut seeder = new_seeder();
|
||||
@@ -3096,7 +3096,7 @@ pub fn par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
|
||||
/// glwe_noise_distribution,
|
||||
/// encryption_generator.noise_generator_mut(),
|
||||
/// &crs,
|
||||
/// &metadata,
|
||||
/// metadata,
|
||||
/// ZkComputeLoad::Proof,
|
||||
/// )
|
||||
/// .unwrap();
|
||||
@@ -3107,7 +3107,7 @@ pub fn par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
|
||||
/// &lwe_compact_public_key,
|
||||
/// &proof,
|
||||
/// &crs,
|
||||
/// &metadata,
|
||||
/// metadata,
|
||||
/// )
|
||||
/// .is_valid());
|
||||
///
|
||||
|
||||
@@ -1010,7 +1010,7 @@ fn lwe_compact_public_encrypt_prove_verify_decrypt_custom_mod<Scalar>(
|
||||
let message_modulus_log = params.message_modulus_log;
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
|
||||
let metadata = [b'c', b'o', b'r', b'e'];
|
||||
let metadata = b"core";
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
let mut random_generator = RandomGenerator::<DefaultRandomGenerator>::new(rsc.seeder.seed());
|
||||
@@ -1073,7 +1073,7 @@ fn lwe_compact_public_encrypt_prove_verify_decrypt_custom_mod<Scalar>(
|
||||
glwe_noise_distribution,
|
||||
rsc.encryption_random_generator.noise_generator_mut(),
|
||||
crs,
|
||||
&metadata,
|
||||
metadata,
|
||||
ZkComputeLoad::Proof,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1090,13 +1090,13 @@ fn lwe_compact_public_encrypt_prove_verify_decrypt_custom_mod<Scalar>(
|
||||
assert_eq!(msg, decoded);
|
||||
|
||||
// Verify the proof
|
||||
assert!(verify_lwe_ciphertext(&ct, &pk, &proof, crs, &metadata).is_valid());
|
||||
assert!(verify_lwe_ciphertext(&ct, &pk, &proof, crs, metadata).is_valid());
|
||||
|
||||
// verify proof with invalid ciphertext
|
||||
let index = random_generator.gen::<usize>() % ct.as_ref().len();
|
||||
let value_to_add = random_generator.gen::<Scalar>();
|
||||
ct.as_mut()[index] = ct.as_mut()[index].wrapping_add(value_to_add);
|
||||
assert!(verify_lwe_ciphertext(&ct, &pk, &proof, crs, &metadata).is_invalid());
|
||||
assert!(verify_lwe_ciphertext(&ct, &pk, &proof, crs, metadata).is_invalid());
|
||||
}
|
||||
|
||||
// In coverage, we break after one while loop iteration, changing message values does
|
||||
@@ -1122,7 +1122,7 @@ fn test_par_compact_lwe_list_public_key_encryption_and_proof() {
|
||||
let glwe_noise_distribution = TUniform::new(9);
|
||||
let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
|
||||
let metadata = [b'c', b'o', b'r', b'e'];
|
||||
let metadata = b"core";
|
||||
|
||||
let delta_log = 59;
|
||||
let delta = 1u64 << delta_log;
|
||||
@@ -1195,7 +1195,7 @@ fn test_par_compact_lwe_list_public_key_encryption_and_proof() {
|
||||
glwe_noise_distribution,
|
||||
encryption_random_generator.noise_generator_mut(),
|
||||
&crs,
|
||||
&metadata,
|
||||
metadata,
|
||||
ZkComputeLoad::Proof,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1205,7 +1205,7 @@ fn test_par_compact_lwe_list_public_key_encryption_and_proof() {
|
||||
&compact_lwe_pk,
|
||||
&proof,
|
||||
&crs,
|
||||
&metadata
|
||||
metadata
|
||||
)
|
||||
.is_valid());
|
||||
|
||||
@@ -1236,7 +1236,7 @@ fn test_par_compact_lwe_list_public_key_encryption_and_proof() {
|
||||
&compact_lwe_pk,
|
||||
&proof,
|
||||
&crs,
|
||||
&metadata
|
||||
metadata
|
||||
)
|
||||
.is_invalid());
|
||||
|
||||
@@ -1284,7 +1284,7 @@ fn test_par_compact_lwe_list_public_key_encryption_and_proof() {
|
||||
glwe_noise_distribution,
|
||||
encryption_random_generator.noise_generator_mut(),
|
||||
&crs,
|
||||
&metadata,
|
||||
metadata,
|
||||
ZkComputeLoad::Proof,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1294,7 +1294,7 @@ fn test_par_compact_lwe_list_public_key_encryption_and_proof() {
|
||||
&compact_lwe_pk,
|
||||
&proof,
|
||||
&crs,
|
||||
&metadata
|
||||
metadata
|
||||
)
|
||||
.is_valid());
|
||||
|
||||
@@ -1325,7 +1325,7 @@ fn test_par_compact_lwe_list_public_key_encryption_and_proof() {
|
||||
&compact_lwe_pk,
|
||||
&proof,
|
||||
&crs,
|
||||
&metadata
|
||||
metadata
|
||||
)
|
||||
.is_invalid());
|
||||
|
||||
|
||||
@@ -538,4 +538,5 @@ macro_rules! create_parameterized_test_with_non_native_parameters {
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use {create_parameterized_test, create_parameterized_test_with_non_native_parameters};
|
||||
pub(crate) use create_parameterized_test;
|
||||
pub(crate) use create_parameterized_test_with_non_native_parameters;
|
||||
|
||||
@@ -19,7 +19,7 @@ pub fn pbs_variance_132_bits_security_gaussian(
|
||||
lwe_dimension.0 as f64,
|
||||
glwe_dimension.0 as f64,
|
||||
polynomial_size.0 as f64,
|
||||
var_min.0 as f64,
|
||||
var_min.0,
|
||||
decomposition_level_count.0 as f64,
|
||||
decomposition_base_log.0 as f64,
|
||||
ciphertext_modulus,
|
||||
@@ -45,7 +45,7 @@ pub fn pbs_variance_132_bits_security_tuniform(
|
||||
lwe_dimension.0 as f64,
|
||||
glwe_dimension.0 as f64,
|
||||
polynomial_size.0 as f64,
|
||||
var_min.0 as f64,
|
||||
var_min.0,
|
||||
decomposition_level_count.0 as f64,
|
||||
decomposition_base_log.0 as f64,
|
||||
ciphertext_modulus,
|
||||
|
||||
@@ -53,7 +53,7 @@ macro_rules! implement_gaussian {
|
||||
for a in uniform_rand_bytes_v.iter_mut() {
|
||||
*a = generator.generate_next();
|
||||
}
|
||||
let size = <$T>::BITS as i32;
|
||||
let size = <$T as crate::core_crypto::commons::numeric::Numeric>::BITS as i32;
|
||||
let mut u: $T = <$S>::from_le_bytes(uniform_rand_bytes_u).cast_into();
|
||||
u *= <$T>::TWO.powi(-size + 1);
|
||||
let mut v: $T = <$S>::from_le_bytes(uniform_rand_bytes_v).cast_into();
|
||||
|
||||
@@ -102,7 +102,7 @@ macro_rules! implement {
|
||||
self.floor()
|
||||
}
|
||||
fn to_bit_string(&self) -> String {
|
||||
if Self::BITS == 32 {
|
||||
if <Self as Numeric>::BITS == 32 {
|
||||
let mut bit_string = format!("{:032b}", self.to_bits());
|
||||
bit_string.insert(1, ' ');
|
||||
bit_string.insert(10, ' ');
|
||||
|
||||
@@ -90,6 +90,162 @@ impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C> for
|
||||
Self::from_container(from, ciphertext_modulus)
|
||||
}
|
||||
}
|
||||
pub type GlweBodyOwned<Scalar> = GlweBody<Vec<Scalar>>;
|
||||
pub type GlweBodyView<'data, Scalar> = GlweBody<&'data [Scalar]>;
|
||||
pub type GlweBodyMutView<'data, Scalar> = GlweBody<&'data mut [Scalar]>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GlweBodyList<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
pub type GlweBodyListView<'data, Scalar> = GlweBodyList<&'data [Scalar]>;
|
||||
pub type GlweBodyListMutView<'data, Scalar> = GlweBodyList<&'data mut [Scalar]>;
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for GlweBodyList<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for GlweBodyList<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'data, T: UnsignedInteger> CreateFrom<&'data [T]> for GlweBodyListView<'data, T> {
|
||||
type Metadata = GlweBodyListCreationMetadata<T>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: &[T], meta: Self::Metadata) -> GlweBodyListView<'_, T> {
|
||||
let GlweBodyListCreationMetadata {
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
GlweBodyList {
|
||||
data: from,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'data, T: UnsignedInteger> CreateFrom<&'data mut [T]> for GlweBodyListMutView<'data, T> {
|
||||
type Metadata = GlweBodyListCreationMetadata<T>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: &mut [T], meta: Self::Metadata) -> GlweBodyListMutView<'_, T> {
|
||||
let GlweBodyListCreationMetadata {
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
GlweBodyList {
|
||||
data: from,
|
||||
ciphertext_modulus,
|
||||
polynomial_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> GlweBodyList<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
assert!(container.container_len().is_multiple_of(polynomial_size.0));
|
||||
|
||||
Self {
|
||||
data: container,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<Scalar> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> GlweBodyList<C> {
|
||||
pub fn as_mut_polynomial_list(&mut self) -> PolynomialListMutView<'_, Scalar> {
|
||||
let polynomial_size = self.polynomial_size();
|
||||
PolynomialList::from_container(self.as_mut(), polynomial_size)
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata used in the [`CreateFrom`] implementation to create [`LweBodyList`] entities.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GlweBodyListCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for GlweBodyList<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = GlweCiphertextCreationMetadata<Self::Element>;
|
||||
|
||||
type EntityView<'this>
|
||||
= GlweBodyView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = GlweBodyListCreationMetadata<Self::Element>;
|
||||
|
||||
type SelfView<'this>
|
||||
= GlweBodyListView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
GlweCiphertextCreationMetadata {
|
||||
polynomial_size: self.polynomial_size,
|
||||
ciphertext_modulus: self.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
self.polynomial_size.0
|
||||
}
|
||||
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
GlweBodyListCreationMetadata {
|
||||
polynomial_size: self.polynomial_size,
|
||||
ciphertext_modulus: self.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for GlweBodyList<C>
|
||||
{
|
||||
type EntityMutView<'this>
|
||||
= GlweBodyMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfMutView<'this>
|
||||
= GlweBodyListMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
|
||||
/// A convenience structure to easily manipulate the mask of a [`GlweCiphertext`].
|
||||
#[derive(Clone, Debug)]
|
||||
|
||||
@@ -150,6 +150,10 @@ impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> LweBodyList<C> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn lwe_body_count(&self) -> LweBodyCount {
|
||||
LweBodyCount(self.data.container_len())
|
||||
}
|
||||
@@ -259,6 +263,10 @@ impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> LweMask<C> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
/// Return the [`LweDimension`] of the [`LweMask`].
|
||||
///
|
||||
/// See [`LweMask::from_container`] for usage.
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
use crate::core_crypto::algorithms::polynomial_algorithms::*;
|
||||
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
use crate::core_crypto::commons::math::torus::UnsignedTorus;
|
||||
use crate::core_crypto::commons::numeric::CastInto;
|
||||
use crate::core_crypto::commons::parameters::{
|
||||
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, MonomialDegree, PolynomialSize,
|
||||
};
|
||||
use crate::core_crypto::commons::traits::{
|
||||
ContiguousEntityContainer, ContiguousEntityContainerMut,
|
||||
};
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::fft_impl::common::modulus_switch;
|
||||
use crate::core_crypto::fft_impl::fft64::math::fft::FftView;
|
||||
use aligned_vec::CACHELINE_ALIGN;
|
||||
use dyn_stack::{PodStack, StackReq};
|
||||
use itertools::{izip, Itertools};
|
||||
|
||||
pub fn cm_blind_rotate_requirement<Scalar>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
fft: FftView<'_>,
|
||||
) -> StackReq {
|
||||
StackReq::any_of(&[
|
||||
// tmp_poly allocation
|
||||
StackReq::new_aligned::<Scalar>(polynomial_size.0, CACHELINE_ALIGN),
|
||||
StackReq::all_of(&[
|
||||
// ct1 allocation
|
||||
StackReq::new_aligned::<Scalar>(
|
||||
cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size),
|
||||
CACHELINE_ALIGN,
|
||||
),
|
||||
// external product
|
||||
cm_add_external_product_assign_requirement::<Scalar>(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
fft,
|
||||
),
|
||||
]),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn cm_bootstrap_requirement<Scalar>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
fft: FftView<'_>,
|
||||
) -> StackReq {
|
||||
cm_blind_rotate_requirement::<Scalar>(glwe_dimension, cm_dimension, polynomial_size, fft).and(
|
||||
StackReq::new_aligned::<Scalar>(
|
||||
cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size),
|
||||
CACHELINE_ALIGN,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn cm_blind_rotate_assign_requirement<Scalar>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
fft: FftView<'_>,
|
||||
) -> StackReq {
|
||||
StackReq::all_of(&[
|
||||
StackReq::new_aligned::<Scalar>(
|
||||
cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size),
|
||||
CACHELINE_ALIGN,
|
||||
),
|
||||
cm_cmux_requirement::<Scalar>(glwe_dimension, cm_dimension, polynomial_size, fft),
|
||||
])
|
||||
}
|
||||
|
||||
// CastInto required for PBS modulus switch which returns a usize
|
||||
pub fn cm_blind_rotate_assign<InputScalar, OutputScalar>(
|
||||
cm_bsk: FourierCmLweBootstrapKeyView<'_>,
|
||||
mut luts: CmGlweCiphertextMutView<'_, OutputScalar>,
|
||||
lwe: CmLweCiphertextView<'_, InputScalar>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) where
|
||||
InputScalar: UnsignedTorus + CastInto<usize>,
|
||||
OutputScalar: UnsignedTorus,
|
||||
{
|
||||
let mask = lwe.get_mask();
|
||||
let bodies = lwe.get_bodies();
|
||||
|
||||
let lut_poly_size = luts.polynomial_size();
|
||||
let ciphertext_modulus = luts.ciphertext_modulus();
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
let log_modulus = lut_poly_size.to_blind_rotation_input_modulus_log();
|
||||
|
||||
luts.get_mut_bodies()
|
||||
.as_mut_polynomial_list()
|
||||
.iter_mut()
|
||||
.zip_eq(bodies.iter())
|
||||
.for_each(|(mut poly, body)| {
|
||||
let monomial_degree =
|
||||
MonomialDegree(modulus_switch((*body.data).cast_into(), log_modulus));
|
||||
|
||||
let (tmp_poly, _) = stack.make_aligned_raw(poly.as_ref().len(), CACHELINE_ALIGN);
|
||||
|
||||
let mut tmp_poly = Polynomial::from_container(tmp_poly);
|
||||
tmp_poly.as_mut().copy_from_slice(poly.as_ref());
|
||||
polynomial_wrapping_monic_monomial_div(&mut poly, &tmp_poly, monomial_degree);
|
||||
});
|
||||
|
||||
// We initialize the ct_0 used for the successive cmuxes
|
||||
let mut ct0 = luts;
|
||||
let (ct1, stack) = stack.make_aligned_raw(ct0.as_ref().len(), CACHELINE_ALIGN);
|
||||
let mut ct1 = CmGlweCiphertextMutView::from_container(
|
||||
ct1,
|
||||
cm_bsk.glwe_dimension(),
|
||||
cm_bsk.cm_dimension(),
|
||||
lut_poly_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
for (lwe_mask_element, bootstrap_key_ggsw) in
|
||||
izip!(mask.as_ref().iter(), cm_bsk.into_cm_ggsw_iter())
|
||||
{
|
||||
if *lwe_mask_element != InputScalar::ZERO {
|
||||
let monomial_degree =
|
||||
MonomialDegree(modulus_switch((*lwe_mask_element).cast_into(), log_modulus));
|
||||
|
||||
// we effectively inline the body of cmux here, merging the initial subtraction
|
||||
// operation with the monic polynomial multiplication, then performing the external
|
||||
// product manually
|
||||
|
||||
// We rotate ct_1 and subtract ct_0 (first step of cmux) by performing
|
||||
// ct_1 <- (ct_0 * X^{a_hat}) - ct_0
|
||||
for (mut ct1_poly, ct0_poly) in izip!(
|
||||
ct1.as_mut_polynomial_list().iter_mut(),
|
||||
ct0.as_polynomial_list().iter(),
|
||||
) {
|
||||
polynomial_wrapping_monic_monomial_mul_and_subtract(
|
||||
&mut ct1_poly,
|
||||
&ct0_poly,
|
||||
monomial_degree,
|
||||
);
|
||||
}
|
||||
|
||||
// as_mut_view is required to keep borrow rules consistent
|
||||
// second step of cmux
|
||||
cm_add_external_product_assign(
|
||||
ct0.as_mut_view(),
|
||||
bootstrap_key_ggsw,
|
||||
ct1.as_view(),
|
||||
fft,
|
||||
stack,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if !ciphertext_modulus.is_native_modulus() {
|
||||
// When we convert back from the fourier domain, integer values will contain up to 53
|
||||
// MSBs with information. In our representation of power of 2 moduli < native modulus we
|
||||
// fill the MSBs and leave the LSBs empty, this usage of the signed decomposer allows to
|
||||
// round while keeping the data in the MSBs
|
||||
let signed_decomposer = SignedDecomposer::new(
|
||||
DecompositionBaseLog(ciphertext_modulus.get_custom_modulus().ilog2() as usize),
|
||||
DecompositionLevelCount(1),
|
||||
);
|
||||
ct0.as_mut()
|
||||
.iter_mut()
|
||||
.for_each(|x| *x = signed_decomposer.closest_representable(*x));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_bootstrap<InputScalar, OutputScalar>(
|
||||
cm_bsk: FourierCmLweBootstrapKeyView<'_>,
|
||||
mut lwe_out: CmLweCiphertextMutView<'_, OutputScalar>,
|
||||
lwe_in: CmLweCiphertextView<'_, InputScalar>,
|
||||
accumulator: CmGlweCiphertextView<'_, OutputScalar>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) where
|
||||
// CastInto required for PBS modulus switch which returns a usize
|
||||
InputScalar: UnsignedTorus + CastInto<usize>,
|
||||
OutputScalar: UnsignedTorus,
|
||||
{
|
||||
assert!(lwe_in.ciphertext_modulus().is_power_of_two());
|
||||
assert!(lwe_out.ciphertext_modulus().is_power_of_two());
|
||||
assert_eq!(
|
||||
lwe_out.ciphertext_modulus(),
|
||||
accumulator.ciphertext_modulus()
|
||||
);
|
||||
|
||||
let (local_accumulator_data, stack) =
|
||||
stack.collect_aligned(CACHELINE_ALIGN, accumulator.as_ref().iter().copied());
|
||||
let mut local_accumulator = CmGlweCiphertextMutView::from_container(
|
||||
local_accumulator_data,
|
||||
accumulator.glwe_dimension(),
|
||||
accumulator.cm_dimension(),
|
||||
accumulator.polynomial_size(),
|
||||
accumulator.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
cm_blind_rotate_assign(
|
||||
cm_bsk,
|
||||
local_accumulator.as_mut_view(),
|
||||
lwe_in.as_view(),
|
||||
fft,
|
||||
stack,
|
||||
);
|
||||
|
||||
extract_lwe_sample_from_cm_glwe_ciphertext(&local_accumulator, &mut lwe_out, MonomialDegree(0));
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
//! Module containing primitives pertaining to
|
||||
//! [`CommonMask GGSW ciphertext encryption`](`CmGgswCiphertext`).
|
||||
|
||||
use crate::core_crypto::algorithms::slice_algorithms::*;
|
||||
use crate::core_crypto::algorithms::*;
|
||||
use crate::core_crypto::commons::ciphertext_modulus::CiphertextModulusKind;
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::decomposition::DecompositionLevel;
|
||||
use crate::core_crypto::commons::math::random::{Distribution, Uniform};
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::encrypt_cm_glwe_ciphertext_assign;
|
||||
use crate::core_crypto::prelude::{Cleartext, GlweSecretKey};
|
||||
use itertools::Itertools;
|
||||
use rayon::prelude::*;
|
||||
|
||||
pub fn encrypt_constant_cm_ggsw_ciphertext<Scalar, NoiseDistribution, KeyCont, OutputCont, Gen>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
output: &mut CmGgswCiphertext<OutputCont>,
|
||||
// TODO: Use a CleartextList
|
||||
cleartexts: &[Cleartext<Scalar>],
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
for glwe_secret_key in glwe_secret_keys {
|
||||
assert!(
|
||||
output.polynomial_size() == glwe_secret_key.polynomial_size(),
|
||||
"Mismatch between polynomial sizes of output ciphertexts and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output.polynomial_size(),
|
||||
glwe_secret_key.polynomial_size()
|
||||
);
|
||||
|
||||
assert!(
|
||||
output.glwe_dimension() == glwe_secret_key.glwe_dimension(),
|
||||
"Mismatch between GlweDimension of output ciphertexts and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output.glwe_dimension(),
|
||||
glwe_secret_key.glwe_dimension()
|
||||
);
|
||||
}
|
||||
|
||||
// Generators used to have same sequential and parallel key generation
|
||||
let gen_iter = generator
|
||||
.try_fork_from_config(output.encryption_fork_config(Uniform, noise_distribution))
|
||||
.expect("Failed to split generator into ggsw levels");
|
||||
|
||||
let decomp_base_log = output.decomposition_base_log();
|
||||
let decomp_level_count = output.decomposition_level_count();
|
||||
let ciphertext_modulus = output.ciphertext_modulus();
|
||||
|
||||
for (level_index, (mut level_matrix, mut generator)) in
|
||||
output.iter_mut().zip(gen_iter).enumerate()
|
||||
{
|
||||
let decomp_level = DecompositionLevel(decomp_level_count.0 - level_index);
|
||||
|
||||
let factors = cleartexts
|
||||
.iter()
|
||||
.map(|cleartext| {
|
||||
ggsw_encryption_multiplicative_factor(
|
||||
ciphertext_modulus,
|
||||
decomp_level,
|
||||
decomp_base_log,
|
||||
*cleartext,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
// We iterate over the rows of the level matrix, the last row needs special treatment
|
||||
let gen_iter = generator
|
||||
.try_fork_from_config(level_matrix.encryption_fork_config(Uniform, noise_distribution))
|
||||
.expect("Failed to split generator into glwe");
|
||||
|
||||
let last_row_index = level_matrix.glwe_dimension().0;
|
||||
|
||||
for ((row_index, mut row_as_glwe), mut generator) in level_matrix
|
||||
.as_mut_cm_glwe_list()
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.zip(gen_iter)
|
||||
{
|
||||
encrypt_constant_cm_ggsw_level_matrix_row(
|
||||
glwe_secret_keys,
|
||||
(row_index, last_row_index),
|
||||
&factors,
|
||||
&mut row_as_glwe,
|
||||
noise_distribution,
|
||||
&mut generator,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn par_encrypt_constant_cm_ggsw_ciphertext<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
KeyCont,
|
||||
OutputCont,
|
||||
Gen,
|
||||
>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
output: &mut CmGgswCiphertext<OutputCont>,
|
||||
cleartexts: &[Cleartext<Scalar>],
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution> + Sync + Send,
|
||||
NoiseDistribution: Distribution + Sync,
|
||||
KeyCont: Container<Element = Scalar> + Sync,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ParallelByteRandomGenerator,
|
||||
{
|
||||
for glwe_secret_key in glwe_secret_keys {
|
||||
assert!(
|
||||
output.polynomial_size() == glwe_secret_key.polynomial_size(),
|
||||
"Mismatch between polynomial sizes of output ciphertexts and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output.polynomial_size(),
|
||||
glwe_secret_key.polynomial_size()
|
||||
);
|
||||
|
||||
assert!(
|
||||
output.glwe_dimension() == glwe_secret_key.glwe_dimension(),
|
||||
"Mismatch between GlweDimension of output ciphertexts and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output.glwe_dimension(),
|
||||
glwe_secret_key.glwe_dimension()
|
||||
);
|
||||
}
|
||||
|
||||
// Generators used to have same sequential and parallel key generation
|
||||
let gen_iter = generator
|
||||
.par_try_fork_from_config(output.encryption_fork_config(Uniform, noise_distribution))
|
||||
.expect("Failed to split generator into ggsw levels");
|
||||
|
||||
let decomp_base_log = output.decomposition_base_log();
|
||||
let decomp_level_count = output.decomposition_level_count();
|
||||
let ciphertext_modulus = output.ciphertext_modulus();
|
||||
|
||||
output.par_iter_mut().zip(gen_iter).enumerate().for_each(
|
||||
|(level_index, (mut level_matrix, mut generator))| {
|
||||
let decomp_level = DecompositionLevel(decomp_level_count.0 - level_index);
|
||||
|
||||
let factors = cleartexts
|
||||
.iter()
|
||||
.map(|cleartext| {
|
||||
ggsw_encryption_multiplicative_factor(
|
||||
ciphertext_modulus,
|
||||
decomp_level,
|
||||
decomp_base_log,
|
||||
*cleartext,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
// We iterate over the rows of the level matrix, the last row needs special treatment
|
||||
let gen_iter = generator
|
||||
.par_try_fork_from_config(
|
||||
level_matrix.encryption_fork_config(Uniform, noise_distribution),
|
||||
)
|
||||
.expect("Failed to split generator into glwe");
|
||||
|
||||
let last_row_index = level_matrix.glwe_dimension().0;
|
||||
|
||||
level_matrix
|
||||
.as_mut_cm_glwe_list()
|
||||
.par_iter_mut()
|
||||
.enumerate()
|
||||
.zip(gen_iter)
|
||||
.for_each(|((row_index, mut row_as_glwe), mut generator)| {
|
||||
encrypt_constant_cm_ggsw_level_matrix_row(
|
||||
glwe_secret_keys,
|
||||
(row_index, last_row_index),
|
||||
&factors,
|
||||
&mut row_as_glwe,
|
||||
noise_distribution,
|
||||
&mut generator,
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn encrypt_constant_cm_ggsw_level_matrix_row<Scalar, NoiseDistribution, KeyCont, OutputCont, Gen>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
(row_index, first_body_row_index): (usize, usize),
|
||||
factors: &[Scalar],
|
||||
row_as_glwe: &mut CmGlweCiphertext<OutputCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert!(matches!(
|
||||
row_as_glwe.ciphertext_modulus().kind(),
|
||||
CiphertextModulusKind::Native | CiphertextModulusKind::NonNativePowerOfTwo
|
||||
));
|
||||
|
||||
let mut bodies = row_as_glwe.get_mut_bodies();
|
||||
|
||||
if row_index < first_body_row_index {
|
||||
// Mask row
|
||||
|
||||
// The Matrix must encode polynomail list
|
||||
// [factor1 * sk1[row_index], factor2 * sk2[row_index], ...]
|
||||
|
||||
for ((glwe_secret_key, mut body), factor) in glwe_secret_keys
|
||||
.iter()
|
||||
.zip_eq(bodies.iter_mut())
|
||||
.zip_eq(factors.iter())
|
||||
{
|
||||
let sk_poly_list = glwe_secret_key.as_polynomial_list();
|
||||
let sk_poly = sk_poly_list.get(row_index);
|
||||
|
||||
// Copy the key polynomial to the output body, to avoid allocating a temporary buffer
|
||||
body.as_mut().copy_from_slice(sk_poly.as_ref());
|
||||
|
||||
slice_wrapping_scalar_mul_assign(body.as_mut(), *factor)
|
||||
}
|
||||
} else {
|
||||
// Body rows
|
||||
|
||||
// The Matrix must encrypt polynomial list
|
||||
// [0, ..., -factor_i * X^0, ..., 0]
|
||||
// with i = body_row_index
|
||||
|
||||
bodies.as_mut().fill(Scalar::ZERO);
|
||||
|
||||
let body_row_index = row_index - first_body_row_index;
|
||||
|
||||
let encoded = factors[body_row_index].wrapping_neg();
|
||||
|
||||
let mut body = bodies.get_mut(body_row_index);
|
||||
|
||||
// set the constant coefficient (X^0)
|
||||
body.as_mut()[0] = encoded;
|
||||
}
|
||||
encrypt_cm_glwe_ciphertext_assign(glwe_secret_keys, row_as_glwe, noise_distribution, generator);
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
use crate::core_crypto::commons::math::torus::UnsignedTorus;
|
||||
use crate::core_crypto::commons::parameters::{
|
||||
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, PolynomialSize,
|
||||
};
|
||||
use crate::core_crypto::commons::traits::Split;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::fft_impl::fft64::crypto::ggsw::{collect_next_term, update_with_fmadd};
|
||||
use crate::core_crypto::fft_impl::fft64::math;
|
||||
use crate::core_crypto::prelude::{ContiguousEntityContainer, ContiguousEntityContainerMut};
|
||||
use aligned_vec::CACHELINE_ALIGN;
|
||||
use dyn_stack::{PodStack, StackReq};
|
||||
use itertools::izip;
|
||||
use math::decomposition::TensorSignedDecompositionLendingIter;
|
||||
use math::fft::FftView;
|
||||
use math::polynomial::FourierPolynomialMutView;
|
||||
use tfhe_fft::c64;
|
||||
|
||||
pub fn cm_add_external_product_assign_requirement<Scalar>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
fft: FftView<'_>,
|
||||
) -> StackReq {
|
||||
let align = CACHELINE_ALIGN;
|
||||
let standard_scratch = StackReq::new_aligned::<Scalar>(
|
||||
cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size),
|
||||
align,
|
||||
);
|
||||
let fourier_polynomial_size = polynomial_size.to_fourier_polynomial_size();
|
||||
let fourier_scratch = StackReq::new_aligned::<c64>(
|
||||
cm_glwe_ciphertext_fourier_size(glwe_dimension, cm_dimension, fourier_polynomial_size),
|
||||
align,
|
||||
);
|
||||
let fourier_scratch_single = StackReq::new_aligned::<c64>(fourier_polynomial_size.0, align);
|
||||
|
||||
let substack3 = fft.forward_scratch();
|
||||
let substack2 = substack3.and(fourier_scratch_single);
|
||||
let substack1 = substack2.and(standard_scratch);
|
||||
let substack0 = StackReq::any_of(&[substack1.and(standard_scratch), fft.backward_scratch()]);
|
||||
substack0.and(fourier_scratch)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "__profiling", inline(never))]
|
||||
pub fn cm_add_external_product_assign<Scalar>(
|
||||
mut out: CmGlweCiphertextMutView<'_, Scalar>,
|
||||
ggsw: FourierCmGgswCiphertextView<'_>,
|
||||
glwe: CmGlweCiphertextView<Scalar>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
{
|
||||
// we check that the polynomial sizes match
|
||||
debug_assert_eq!(ggsw.polynomial_size(), glwe.polynomial_size());
|
||||
debug_assert_eq!(ggsw.polynomial_size(), out.polynomial_size());
|
||||
// we check that the glwe sizes match
|
||||
debug_assert_eq!(ggsw.glwe_dimension(), glwe.glwe_dimension());
|
||||
debug_assert_eq!(ggsw.glwe_dimension(), out.glwe_dimension());
|
||||
|
||||
let align = CACHELINE_ALIGN;
|
||||
let fourier_poly_size = ggsw.polynomial_size().to_fourier_polynomial_size().0;
|
||||
|
||||
// we round the input mask and body
|
||||
let decomposer = SignedDecomposer::<Scalar>::new(
|
||||
ggsw.decomposition_base_log(),
|
||||
ggsw.decomposition_level_count(),
|
||||
);
|
||||
|
||||
let (output_fft_buffer, substack0) = stack.make_aligned_raw::<c64>(
|
||||
fourier_poly_size * (ggsw.glwe_dimension().0 + ggsw.cm_dimension().0),
|
||||
align,
|
||||
);
|
||||
// output_fft_buffer is initially uninitialized, considered to be implicitly zero, to avoid
|
||||
// the cost of filling it up with zeros. `is_output_uninit` is set to `false` once
|
||||
// it has been fully initialized for the first time.
|
||||
let mut is_output_uninit = true;
|
||||
|
||||
{
|
||||
// ------------------------------------------------------ EXTERNAL PRODUCT IN FOURIER DOMAIN
|
||||
// In this section, we perform the external product in the fourier domain, and accumulate
|
||||
// the result in the output_fft_buffer variable.
|
||||
let (mut decomposition, substack1) = TensorSignedDecompositionLendingIter::new(
|
||||
glwe.as_ref()
|
||||
.iter()
|
||||
.map(|s| decomposer.init_decomposer_state(*s)),
|
||||
DecompositionBaseLog(decomposer.base_log),
|
||||
DecompositionLevelCount(decomposer.level_count),
|
||||
substack0,
|
||||
);
|
||||
|
||||
// We loop through the levels (we reverse to match the order of the decomposition iterator.)
|
||||
ggsw.into_levels().for_each(|ggsw_decomp_matrix| {
|
||||
// We retrieve the decomposition of this level.
|
||||
let (glwe_level, glwe_decomp_term, substack2) =
|
||||
collect_next_term(&mut decomposition, substack1, align);
|
||||
let glwe_decomp_term = CmGlweCiphertextView::from_container(
|
||||
&*glwe_decomp_term,
|
||||
ggsw.glwe_dimension(),
|
||||
ggsw.cm_dimension(),
|
||||
ggsw.polynomial_size(),
|
||||
out.ciphertext_modulus(),
|
||||
);
|
||||
debug_assert_eq!(ggsw_decomp_matrix.decomposition_level(), glwe_level);
|
||||
|
||||
// For each level we have to add the result of the vector-matrix product between the
|
||||
// decomposition of the glwe, and the ggsw level matrix to the output. To do so, we
|
||||
// iteratively add to the output, the product between every line of the matrix, and
|
||||
// the corresponding (scalar) polynomial in the glwe decomposition:
|
||||
//
|
||||
// ggsw_mat ggsw_mat
|
||||
// glwe_dec | - - - - | < glwe_dec | - - - - |
|
||||
// | - - - | x | - - - - | | - - - | x | - - - - | <
|
||||
// ^ | - - - - | ^ | - - - - |
|
||||
//
|
||||
// t = 1 t = 2 ...
|
||||
|
||||
izip!(
|
||||
ggsw_decomp_matrix.into_rows(),
|
||||
glwe_decomp_term.as_polynomial_list().iter()
|
||||
)
|
||||
.for_each(|(ggsw_row, glwe_poly)| {
|
||||
let (mut fourier, substack3) =
|
||||
substack2.make_aligned_raw::<c64>(fourier_poly_size, align);
|
||||
// We perform the forward fft transform for the glwe polynomial
|
||||
let fourier = fft
|
||||
.forward_as_integer(
|
||||
FourierPolynomialMutView { data: &mut fourier },
|
||||
glwe_poly,
|
||||
substack3,
|
||||
)
|
||||
.data;
|
||||
// Now we loop through the polynomials of the output, and add the
|
||||
// corresponding product of polynomials.
|
||||
|
||||
update_with_fmadd(
|
||||
output_fft_buffer,
|
||||
ggsw_row.data(),
|
||||
fourier,
|
||||
is_output_uninit,
|
||||
fourier_poly_size,
|
||||
);
|
||||
|
||||
// we initialized `output_fft_buffer, so we can set this to false
|
||||
is_output_uninit = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------------------------------- TRANSFORMATION OF RESULT TO STANDARD DOMAIN
|
||||
// In this section, we bring the result from the fourier domain, back to the standard
|
||||
// domain, and add it to the output.
|
||||
//
|
||||
// We iterate over the polynomials in the output.
|
||||
if !is_output_uninit {
|
||||
izip!(
|
||||
out.as_mut_polynomial_list().iter_mut(),
|
||||
output_fft_buffer
|
||||
.into_chunks(fourier_poly_size)
|
||||
.map(|slice| FourierPolynomialMutView { data: slice }),
|
||||
)
|
||||
.for_each(|(out, fourier)| {
|
||||
// The fourier buffer is not re-used afterwards so we can use the in-place version of
|
||||
// the add_backward_as_torus function
|
||||
fft.add_backward_in_place_as_torus(out, fourier, substack0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_cmux_requirement<Scalar>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
fft: FftView<'_>,
|
||||
) -> StackReq {
|
||||
cm_add_external_product_assign_requirement::<Scalar>(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
fft,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn cm_cmux<Scalar: UnsignedTorus>(
|
||||
ct0: CmGlweCiphertextMutView<'_, Scalar>,
|
||||
mut ct1: CmGlweCiphertextMutView<'_, Scalar>,
|
||||
ggsw: FourierCmGgswCiphertextView<'_>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) {
|
||||
izip!(ct1.as_mut(), ct0.as_ref()).for_each(|(c1, c0)| {
|
||||
*c1 = c1.wrapping_sub(*c0);
|
||||
});
|
||||
cm_add_external_product_assign(ct0, ggsw, ct1.as_view(), fft, stack);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use dyn_stack::PodBuffer;
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::*;
|
||||
use crate::core_crypto::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_cm_external_product() {
|
||||
let glwe_dimension = GlweDimension(2);
|
||||
let cm_dimension = CmDimension(2);
|
||||
let polynomial_size = PolynomialSize(64);
|
||||
let decomp_base_log = DecompositionBaseLog(8);
|
||||
let decomp_level_count = DecompositionLevelCount(3);
|
||||
let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
|
||||
let noise_distribution =
|
||||
DynamicDistribution::new_gaussian_from_std_dev(StandardDev(0.0000006791658447437413));
|
||||
|
||||
let fft = Fft::new(polynomial_size);
|
||||
let fft = fft.as_view();
|
||||
|
||||
let mut mem = PodBuffer::new(StackReq::new_aligned::<u64>(100_000, 512));
|
||||
|
||||
let mut seeder = new_seeder();
|
||||
let seeder = seeder.as_mut();
|
||||
let mut encryption_generator =
|
||||
EncryptionRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed(), seeder);
|
||||
let mut secret_generator =
|
||||
SecretRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed());
|
||||
|
||||
let glwe_secret_keys = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_generate_new_binary_glwe_secret_key(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
&mut secret_generator,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
let mut ggsw = CmGgswCiphertext::new(
|
||||
0u64,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let cleartexts = (0..cm_dimension.0).map(|_| Cleartext(1)).collect_vec();
|
||||
|
||||
encrypt_constant_cm_ggsw_ciphertext(
|
||||
&glwe_secret_keys,
|
||||
&mut ggsw,
|
||||
&cleartexts,
|
||||
noise_distribution,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let mut ggsw_fourier = FourierCmGgswCiphertext::new(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
);
|
||||
|
||||
let stack = PodStack::new(&mut mem);
|
||||
|
||||
ggsw_fourier
|
||||
.as_mut_view()
|
||||
.fill_with_forward_fourier(ggsw.as_view(), fft, stack);
|
||||
|
||||
let mut glwe_in = CmGlweCiphertext::new(
|
||||
0u64,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let input_plaintext_list = PlaintextList::from_container(
|
||||
(0..cm_dimension.0 * polynomial_size.0)
|
||||
.map(|i| (i as u64 + 1) << 60)
|
||||
.collect_vec(),
|
||||
);
|
||||
|
||||
encrypt_cm_glwe_ciphertext(
|
||||
&glwe_secret_keys,
|
||||
&mut glwe_in,
|
||||
&input_plaintext_list,
|
||||
noise_distribution,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let mut glwe_out = CmGlweCiphertext::new(
|
||||
0u64,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let stack = PodStack::new(&mut mem);
|
||||
|
||||
super::cm_add_external_product_assign(
|
||||
glwe_out.as_mut_view(),
|
||||
ggsw_fourier.as_view(),
|
||||
glwe_in.as_view(),
|
||||
fft,
|
||||
stack,
|
||||
);
|
||||
|
||||
let mut decrypted =
|
||||
PlaintextList::new(0, PlaintextCount(cm_dimension.0 * polynomial_size.0));
|
||||
|
||||
decrypt_cm_glwe_ciphertext(&glwe_secret_keys, &glwe_out, &mut decrypted);
|
||||
|
||||
for (i, j) in input_plaintext_list.iter().zip_eq(decrypted.iter()) {
|
||||
let diff = j.0.wrapping_sub(*i.0) as i64;
|
||||
|
||||
assert!(diff.abs() < (1 << 57));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
//! Module containing primitives pertaining to
|
||||
//! [`CommonMask GLWE ciphertext encryption`](`GlweCiphertext`).
|
||||
|
||||
use crate::core_crypto::algorithms::polynomial_algorithms::*;
|
||||
use crate::core_crypto::algorithms::slice_algorithms::{
|
||||
slice_wrapping_scalar_div_assign, slice_wrapping_scalar_mul_assign,
|
||||
};
|
||||
use crate::core_crypto::commons::ciphertext_modulus::CiphertextModulusKind;
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::random::{Distribution, Uniform};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::CmDimension;
|
||||
use itertools::Itertools;
|
||||
|
||||
pub fn fill_cm_glwe_mask_and_bodies_for_encryption_assign<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
KeyCont,
|
||||
BodyCont,
|
||||
MaskCont,
|
||||
Gen,
|
||||
>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
output_mask: &mut GlweMask<MaskCont>,
|
||||
output_bodies: &mut GlweBodyList<BodyCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
BodyCont: ContainerMut<Element = Scalar>,
|
||||
MaskCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert_eq!(
|
||||
output_mask.ciphertext_modulus(),
|
||||
output_bodies.ciphertext_modulus(),
|
||||
"Mismatched moduli between output_mask ({:?}) and output_body ({:?})",
|
||||
output_mask.ciphertext_modulus(),
|
||||
output_bodies.ciphertext_modulus()
|
||||
);
|
||||
|
||||
let ciphertext_modulus = output_bodies.ciphertext_modulus();
|
||||
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
generator
|
||||
.fill_slice_with_random_uniform_mask_custom_mod(output_mask.as_mut(), ciphertext_modulus);
|
||||
generator.unsigned_integer_slice_wrapping_add_random_noise_from_distribution_custom_mod_assign(
|
||||
output_bodies.as_mut(),
|
||||
noise_distribution,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
if !ciphertext_modulus.is_native_modulus() {
|
||||
let torus_scaling = ciphertext_modulus.get_power_of_two_scaling_to_native_torus();
|
||||
slice_wrapping_scalar_mul_assign(output_mask.as_mut(), torus_scaling);
|
||||
slice_wrapping_scalar_mul_assign(output_bodies.as_mut(), torus_scaling);
|
||||
}
|
||||
|
||||
for (glwe_secret_key, mut output_body) in
|
||||
glwe_secret_keys.iter().zip_eq(output_bodies.iter_mut())
|
||||
{
|
||||
polynomial_wrapping_add_multisum_assign(
|
||||
&mut output_body.as_mut_polynomial(),
|
||||
&output_mask.as_polynomial_list(),
|
||||
&glwe_secret_key.as_polynomial_list(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encrypt_cm_glwe_ciphertext_assign<Scalar, NoiseDistribution, KeyCont, OutputCont, Gen>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
output: &mut CmGlweCiphertext<OutputCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
for glwe_secret_key in glwe_secret_keys {
|
||||
assert!(
|
||||
output.glwe_dimension() == glwe_secret_key.glwe_dimension(),
|
||||
"Mismatch between GlweDimension of output ciphertext and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output.glwe_dimension(),
|
||||
glwe_secret_key.glwe_dimension()
|
||||
);
|
||||
assert!(
|
||||
output.polynomial_size() == glwe_secret_key.polynomial_size(),
|
||||
"Mismatch between PolynomialSize of output ciphertext and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output.polynomial_size(),
|
||||
glwe_secret_key.polynomial_size()
|
||||
);
|
||||
}
|
||||
|
||||
let (mut mask, mut bodies) = output.get_mut_mask_and_bodies();
|
||||
|
||||
fill_cm_glwe_mask_and_bodies_for_encryption_assign(
|
||||
glwe_secret_keys,
|
||||
&mut mask,
|
||||
&mut bodies,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn fill_cm_glwe_mask_and_bodies_for_encryption<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
KeyCont,
|
||||
InputCont,
|
||||
BodyCont,
|
||||
MaskCont,
|
||||
Gen,
|
||||
>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
output_mask: &mut GlweMask<MaskCont>,
|
||||
output_bodies: &mut GlweBodyList<BodyCont>,
|
||||
encoded: &PlaintextList<InputCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
BodyCont: ContainerMut<Element = Scalar>,
|
||||
MaskCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert_eq!(
|
||||
output_mask.ciphertext_modulus(),
|
||||
output_bodies.ciphertext_modulus()
|
||||
);
|
||||
|
||||
let ciphertext_modulus = output_bodies.ciphertext_modulus();
|
||||
|
||||
let polynomial_size = output_bodies.polynomial_size();
|
||||
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
generator
|
||||
.fill_slice_with_random_uniform_mask_custom_mod(output_mask.as_mut(), ciphertext_modulus);
|
||||
generator.fill_slice_with_random_noise_from_distribution_custom_mod(
|
||||
output_bodies.as_mut(),
|
||||
noise_distribution,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
for ((glwe_secret_key, mut output_body), encoded) in glwe_secret_keys
|
||||
.iter()
|
||||
.zip_eq(output_bodies.iter_mut())
|
||||
.zip_eq(encoded.chunks_exact(polynomial_size.0))
|
||||
{
|
||||
polynomial_wrapping_add_assign(
|
||||
&mut output_body.as_mut_polynomial(),
|
||||
&encoded.as_polynomial(),
|
||||
);
|
||||
|
||||
if !ciphertext_modulus.is_native_modulus() {
|
||||
let torus_scaling = ciphertext_modulus.get_power_of_two_scaling_to_native_torus();
|
||||
slice_wrapping_scalar_mul_assign(output_mask.as_mut(), torus_scaling);
|
||||
slice_wrapping_scalar_mul_assign(output_body.as_mut(), torus_scaling);
|
||||
}
|
||||
|
||||
polynomial_wrapping_add_multisum_assign(
|
||||
&mut output_body.as_mut_polynomial(),
|
||||
&output_mask.as_polynomial_list(),
|
||||
&glwe_secret_key.as_polynomial_list(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encrypt_cm_glwe_ciphertext<Scalar, NoiseDistribution, KeyCont, InputCont, OutputCont, Gen>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
output_glwe_ciphertext: &mut CmGlweCiphertext<OutputCont>,
|
||||
input_plaintext_list: &PlaintextList<InputCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert!(
|
||||
output_glwe_ciphertext.polynomial_size().0 * output_glwe_ciphertext.cm_dimension().0
|
||||
== input_plaintext_list.plaintext_count().0,
|
||||
"Mismatch between PolynomialSize of output ciphertext PlaintextCount of input. \
|
||||
Got {:?} in output, and {:?} in input.",
|
||||
output_glwe_ciphertext.polynomial_size(),
|
||||
input_plaintext_list.plaintext_count()
|
||||
);
|
||||
|
||||
for glwe_secret_key in glwe_secret_keys {
|
||||
assert!(
|
||||
output_glwe_ciphertext.glwe_dimension() == glwe_secret_key.glwe_dimension(),
|
||||
"Mismatch between GlweDimension of output ciphertext and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output_glwe_ciphertext.glwe_dimension(),
|
||||
glwe_secret_key.glwe_dimension()
|
||||
);
|
||||
assert!(
|
||||
output_glwe_ciphertext.polynomial_size() == glwe_secret_key.polynomial_size(),
|
||||
"Mismatch between PolynomialSize of output ciphertext and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output_glwe_ciphertext.polynomial_size(),
|
||||
glwe_secret_key.polynomial_size()
|
||||
);
|
||||
}
|
||||
|
||||
let (mut mask, mut bodies) = output_glwe_ciphertext.get_mut_mask_and_bodies();
|
||||
|
||||
fill_cm_glwe_mask_and_bodies_for_encryption(
|
||||
glwe_secret_keys,
|
||||
&mut mask,
|
||||
&mut bodies,
|
||||
input_plaintext_list,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn decrypt_cm_glwe_ciphertext<Scalar, KeyCont, InputCont, OutputCont>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
input_cm_glwe_ciphertext: &CmGlweCiphertext<InputCont>,
|
||||
output_plaintext_list: &mut PlaintextList<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
assert!(
|
||||
output_plaintext_list.plaintext_count().0
|
||||
== input_cm_glwe_ciphertext.polynomial_size().0 * glwe_secret_keys.len(),
|
||||
"Mismatched output PlaintextCount {:?} and input PolynomialSize {:?}",
|
||||
output_plaintext_list.plaintext_count(),
|
||||
input_cm_glwe_ciphertext.polynomial_size()
|
||||
);
|
||||
for glwe_secret_key in glwe_secret_keys {
|
||||
assert!(
|
||||
glwe_secret_key.glwe_dimension() == input_cm_glwe_ciphertext.glwe_dimension(),
|
||||
"Mismatched GlweDimension between glwe_secret_key {:?} and input_glwe_ciphertext {:?}",
|
||||
glwe_secret_key.glwe_dimension(),
|
||||
input_cm_glwe_ciphertext.glwe_dimension()
|
||||
);
|
||||
assert!(
|
||||
glwe_secret_key.polynomial_size() == input_cm_glwe_ciphertext.polynomial_size(),
|
||||
"Mismatched PolynomialSize between glwe_secret_key {:?} and input_glwe_ciphertext {:?}",
|
||||
glwe_secret_key.polynomial_size(),
|
||||
input_cm_glwe_ciphertext.polynomial_size()
|
||||
);
|
||||
}
|
||||
|
||||
let polynomial_size = input_cm_glwe_ciphertext.polynomial_size();
|
||||
|
||||
let ciphertext_modulus = input_cm_glwe_ciphertext.ciphertext_modulus();
|
||||
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
let (mask, bodies) = input_cm_glwe_ciphertext.get_mask_and_bodies();
|
||||
|
||||
for ((glwe_secret_key, body), mut output_plaintext_list) in glwe_secret_keys
|
||||
.iter()
|
||||
.zip_eq(bodies.iter())
|
||||
.zip_eq(output_plaintext_list.chunks_exact_mut(polynomial_size.0))
|
||||
{
|
||||
output_plaintext_list
|
||||
.as_mut()
|
||||
.copy_from_slice(body.as_ref());
|
||||
|
||||
polynomial_wrapping_sub_multisum_assign(
|
||||
&mut output_plaintext_list.as_mut_polynomial(),
|
||||
&mask.as_polynomial_list(),
|
||||
&glwe_secret_key.as_polynomial_list(),
|
||||
);
|
||||
}
|
||||
|
||||
if !ciphertext_modulus.is_native_modulus() {
|
||||
slice_wrapping_scalar_div_assign(
|
||||
output_plaintext_list.as_mut(),
|
||||
ciphertext_modulus.get_power_of_two_scaling_to_native_torus(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trivially_encrypt_cm_glwe_ciphertext<Scalar, InputCont, OutputCont>(
|
||||
output: &mut CmGlweCiphertext<OutputCont>,
|
||||
encoded: &PlaintextList<InputCont>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert!(
|
||||
encoded.plaintext_count().0 == output.polynomial_size().0 * output.cm_dimension().0,
|
||||
"Mismatched input PlaintextCount {:?} and output PolynomialSize * CmDimension {:?}",
|
||||
encoded.plaintext_count(),
|
||||
output.polynomial_size().0 * output.cm_dimension().0
|
||||
);
|
||||
|
||||
let (mut mask, mut body) = output.get_mut_mask_and_bodies();
|
||||
|
||||
mask.as_mut().fill(Scalar::ZERO);
|
||||
body.as_mut().copy_from_slice(encoded.as_ref());
|
||||
|
||||
let ciphertext_modulus = body.ciphertext_modulus();
|
||||
|
||||
match ciphertext_modulus.kind() {
|
||||
CiphertextModulusKind::Native | CiphertextModulusKind::Other => (),
|
||||
CiphertextModulusKind::NonNativePowerOfTwo => {
|
||||
slice_wrapping_scalar_mul_assign(
|
||||
body.as_mut(),
|
||||
ciphertext_modulus.get_power_of_two_scaling_to_native_torus(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocate_and_trivially_encrypt_new_cm_glwe_ciphertext<Scalar, InputCont>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
encoded: &PlaintextList<InputCont>,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> CmGlweCiphertextOwned<Scalar>
|
||||
where
|
||||
Scalar: UnsignedTorus,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
{
|
||||
let mut new_ct = CmGlweCiphertextOwned::new(
|
||||
Scalar::ZERO,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let mut bodies = new_ct.get_mut_bodies();
|
||||
bodies.as_mut().copy_from_slice(encoded.as_ref());
|
||||
|
||||
// Manage the non native power of 2 encoding
|
||||
if ciphertext_modulus.kind() == CiphertextModulusKind::NonNativePowerOfTwo {
|
||||
slice_wrapping_scalar_mul_assign(
|
||||
bodies.as_mut(),
|
||||
ciphertext_modulus.get_power_of_two_scaling_to_native_torus(),
|
||||
);
|
||||
}
|
||||
|
||||
new_ct
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
//! Module containing primitives pertaining to the operation usually referred to as a
|
||||
//! _sample extract_ in the literature. Allowing to extract a single
|
||||
//! [`CommonMask LWE Ciphertext`](`CmLweCiphertext`) from a given [`CommonMask GLWE
|
||||
//! ciphertext`](`CmGlweCiphertext`).
|
||||
|
||||
use crate::core_crypto::algorithms::slice_algorithms::*;
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::entities::*;
|
||||
use itertools::Itertools;
|
||||
use rayon::prelude::*;
|
||||
|
||||
pub fn extract_lwe_sample_from_cm_glwe_ciphertext<Scalar, InputCont, OutputCont>(
|
||||
input_glwe: &CmGlweCiphertext<InputCont>,
|
||||
output_lwe: &mut CmLweCiphertext<OutputCont>,
|
||||
nth: MonomialDegree,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let in_lwe_dim = input_glwe
|
||||
.glwe_dimension()
|
||||
.to_equivalent_lwe_dimension(input_glwe.polynomial_size());
|
||||
|
||||
let out_lwe_dim = output_lwe.lwe_dimension();
|
||||
|
||||
assert_eq!(
|
||||
in_lwe_dim, out_lwe_dim,
|
||||
"Mismatch between equivalent LweDimension of input ciphertext and output ciphertext. \
|
||||
Got {in_lwe_dim:?} for input and {out_lwe_dim:?} for output.",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_glwe.ciphertext_modulus(),
|
||||
output_lwe.ciphertext_modulus(),
|
||||
"Mismatched moduli between input_glwe ({:?}) and output_lwe ({:?})",
|
||||
input_glwe.ciphertext_modulus(),
|
||||
output_lwe.ciphertext_modulus()
|
||||
);
|
||||
|
||||
// We retrieve the bodies and masks of the two ciphertexts.
|
||||
let (mut lwe_mask, mut lwe_bodies) = output_lwe.get_mut_mask_and_bodies();
|
||||
let (glwe_mask, glwe_bodies) = input_glwe.get_mask_and_bodies();
|
||||
|
||||
// We copy the body
|
||||
|
||||
for (lwe_body, glwe_body) in lwe_bodies.iter_mut().zip_eq(glwe_bodies.iter()) {
|
||||
*lwe_body.data = glwe_body.as_ref()[nth.0];
|
||||
}
|
||||
|
||||
// We copy the mask (each polynomial is in the wrong order)
|
||||
lwe_mask.as_mut().copy_from_slice(glwe_mask.as_ref());
|
||||
|
||||
// We compute the number of elements which must be
|
||||
// turned into their opposite
|
||||
let opposite_count = input_glwe.polynomial_size().0 - nth.0 - 1;
|
||||
let ciphertext_modulus = input_glwe.ciphertext_modulus();
|
||||
|
||||
if ciphertext_modulus.is_compatible_with_native_modulus() {
|
||||
// We loop through the polynomials
|
||||
for lwe_mask_poly in lwe_mask
|
||||
.as_mut()
|
||||
.chunks_exact_mut(input_glwe.polynomial_size().0)
|
||||
{
|
||||
// We reverse the polynomial
|
||||
lwe_mask_poly.reverse();
|
||||
// We compute the opposite of the proper coefficients
|
||||
slice_wrapping_opposite_assign(&mut lwe_mask_poly[0..opposite_count]);
|
||||
// We rotate the polynomial properly
|
||||
lwe_mask_poly.rotate_left(opposite_count);
|
||||
}
|
||||
} else {
|
||||
let modulus: Scalar = ciphertext_modulus.get_custom_modulus().cast_into();
|
||||
// We loop through the polynomials
|
||||
for lwe_mask_poly in lwe_mask
|
||||
.as_mut()
|
||||
.chunks_exact_mut(input_glwe.polynomial_size().0)
|
||||
{
|
||||
// We reverse the polynomial
|
||||
lwe_mask_poly.reverse();
|
||||
// We compute the opposite of the proper coefficients
|
||||
slice_wrapping_opposite_assign_custom_mod(
|
||||
&mut lwe_mask_poly[0..opposite_count],
|
||||
modulus,
|
||||
);
|
||||
// We rotate the polynomial properly
|
||||
lwe_mask_poly.rotate_left(opposite_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn par_extract_lwe_sample_from_cm_glwe_ciphertext<Scalar, InputCont, OutputCont>(
|
||||
input_glwe: &CmGlweCiphertext<InputCont>,
|
||||
output_lwe_list: &mut CmLweCiphertextList<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger + Send + Sync,
|
||||
InputCont: Container<Element = Scalar> + Sync,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let thread_count = ThreadCount(rayon::current_num_threads());
|
||||
par_extract_lwe_sample_from_cm_glwe_ciphertext_with_thread_count(
|
||||
input_glwe,
|
||||
output_lwe_list,
|
||||
thread_count,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn par_extract_lwe_sample_from_cm_glwe_ciphertext_with_thread_count<
|
||||
Scalar,
|
||||
InputCont,
|
||||
OutputCont,
|
||||
>(
|
||||
input_glwe: &CmGlweCiphertext<InputCont>,
|
||||
output_lwe_list: &mut CmLweCiphertextList<OutputCont>,
|
||||
thread_count: ThreadCount,
|
||||
) where
|
||||
Scalar: UnsignedInteger + Send + Sync,
|
||||
InputCont: Container<Element = Scalar> + Sync,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let in_lwe_dim = input_glwe
|
||||
.glwe_dimension()
|
||||
.to_equivalent_lwe_dimension(input_glwe.polynomial_size());
|
||||
|
||||
let out_lwe_dim = output_lwe_list.lwe_dimension();
|
||||
|
||||
assert_eq!(
|
||||
in_lwe_dim, out_lwe_dim,
|
||||
"Mismatch between equivalent LweDimension of input ciphertext and output ciphertext. \
|
||||
Got {in_lwe_dim:?} for input and {out_lwe_dim:?} for output.",
|
||||
);
|
||||
|
||||
assert!(
|
||||
input_glwe.polynomial_size().0 <= output_lwe_list.cm_lwe_ciphertext_count().0,
|
||||
"The output LweCiphertextList does not have enough space ({:?}) \
|
||||
to extract all input CmGlweCiphertext coefficients ({})",
|
||||
output_lwe_list.cm_lwe_ciphertext_count(),
|
||||
input_glwe.polynomial_size().0
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_glwe.ciphertext_modulus(),
|
||||
output_lwe_list.ciphertext_modulus(),
|
||||
"Mismatched moduli between input_glwe ({:?}) and output_lwe ({:?})",
|
||||
input_glwe.ciphertext_modulus(),
|
||||
output_lwe_list.ciphertext_modulus()
|
||||
);
|
||||
|
||||
let polynomial_size = input_glwe.polynomial_size();
|
||||
let (glwe_mask, glwe_body) = input_glwe.get_mask_and_bodies();
|
||||
|
||||
let thread_count = thread_count.0.min(rayon::current_num_threads());
|
||||
let chunk_size = polynomial_size.0.div_ceil(thread_count);
|
||||
|
||||
glwe_body
|
||||
.as_ref()
|
||||
.par_chunks(chunk_size)
|
||||
.zip(output_lwe_list.par_chunks_mut(chunk_size))
|
||||
.enumerate()
|
||||
.for_each(
|
||||
|(chunk_idx, (glwe_body_chunk, mut output_lwe_list_chunk))| {
|
||||
for (coeff_idx, (glwe_coeff, mut output_lwe)) in glwe_body_chunk
|
||||
.iter()
|
||||
.zip(output_lwe_list_chunk.iter_mut())
|
||||
.enumerate()
|
||||
{
|
||||
let nth = chunk_idx * chunk_size + coeff_idx;
|
||||
|
||||
let (mut lwe_mask, mut lwe_body) = output_lwe.get_mut_mask_and_bodies();
|
||||
|
||||
// We copy the body
|
||||
lwe_body.as_mut().fill(*glwe_coeff);
|
||||
|
||||
// We copy the mask (each polynomial is in the wrong order)
|
||||
lwe_mask.as_mut().copy_from_slice(glwe_mask.as_ref());
|
||||
|
||||
// We compute the number of elements which must be
|
||||
// turned into their opposite
|
||||
let opposite_count = input_glwe.polynomial_size().0 - nth - 1;
|
||||
let ciphertext_modulus = input_glwe.ciphertext_modulus();
|
||||
|
||||
if ciphertext_modulus.is_compatible_with_native_modulus() {
|
||||
// We loop through the polynomials
|
||||
for lwe_mask_poly in lwe_mask
|
||||
.as_mut()
|
||||
.chunks_exact_mut(input_glwe.polynomial_size().0)
|
||||
{
|
||||
// We reverse the polynomial
|
||||
lwe_mask_poly.reverse();
|
||||
// We compute the opposite of the proper coefficients
|
||||
slice_wrapping_opposite_assign(&mut lwe_mask_poly[0..opposite_count]);
|
||||
// We rotate the polynomial properly
|
||||
lwe_mask_poly.rotate_left(opposite_count);
|
||||
}
|
||||
} else {
|
||||
let modulus: Scalar = ciphertext_modulus.get_custom_modulus().cast_into();
|
||||
// We loop through the polynomials
|
||||
for lwe_mask_poly in lwe_mask
|
||||
.as_mut()
|
||||
.chunks_exact_mut(input_glwe.polynomial_size().0)
|
||||
{
|
||||
// We reverse the polynomial
|
||||
lwe_mask_poly.reverse();
|
||||
// We compute the opposite of the proper coefficients
|
||||
slice_wrapping_opposite_assign_custom_mod(
|
||||
&mut lwe_mask_poly[0..opposite_count],
|
||||
modulus,
|
||||
);
|
||||
// We rotate the polynomial properly
|
||||
lwe_mask_poly.rotate_left(opposite_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
//! Module containing primitives pertaining to the conversion of
|
||||
//! [`CommonMask LWE bootstrap keys`](`CmLweBootstrapKey`) to various representations/numerical
|
||||
//! domains like the Fourier domain.
|
||||
|
||||
use crate::core_crypto::commons::computation_buffers::ComputationBuffers;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::entities::*;
|
||||
use crate::core_crypto::fft_impl::fft64::math::fft::{Fft, FftView};
|
||||
use dyn_stack::{PodStack, StackReq};
|
||||
use tfhe_fft::c64;
|
||||
|
||||
pub fn convert_standard_cm_lwe_bootstrap_key_to_fourier<Scalar, InputCont, OutputCont>(
|
||||
input_bsk: &CmLweBootstrapKey<InputCont>,
|
||||
output_bsk: &mut FourierCmLweBootstrapKey<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = c64>,
|
||||
{
|
||||
let mut buffers = ComputationBuffers::new();
|
||||
|
||||
let fft = Fft::new(input_bsk.polynomial_size());
|
||||
let fft = fft.as_view();
|
||||
|
||||
buffers.resize(
|
||||
convert_standard_cm_lwe_bootstrap_key_to_fourier_mem_optimized_requirement(fft)
|
||||
.unaligned_bytes_required(),
|
||||
);
|
||||
|
||||
let stack = buffers.stack();
|
||||
|
||||
convert_standard_cm_lwe_bootstrap_key_to_fourier_mem_optimized(
|
||||
input_bsk, output_bsk, fft, stack,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn convert_standard_cm_lwe_bootstrap_key_to_fourier_mem_optimized<
|
||||
Scalar,
|
||||
InputCont,
|
||||
OutputCont,
|
||||
>(
|
||||
input_bsk: &CmLweBootstrapKey<InputCont>,
|
||||
output_bsk: &mut FourierCmLweBootstrapKey<OutputCont>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = c64>,
|
||||
{
|
||||
assert_eq!(
|
||||
input_bsk.polynomial_size(),
|
||||
output_bsk.polynomial_size(),
|
||||
"Mismatched PolynomialSize between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.polynomial_size(),
|
||||
output_bsk.polynomial_size(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.glwe_dimension(),
|
||||
output_bsk.glwe_dimension(),
|
||||
"Mismatched GlweSize"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.decomposition_base_log(),
|
||||
output_bsk.decomposition_base_log(),
|
||||
"Mismatched DecompositionBaseLog between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.decomposition_base_log(),
|
||||
output_bsk.decomposition_base_log(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.decomposition_level_count(),
|
||||
output_bsk.decomposition_level_count(),
|
||||
"Mismatched DecompositionLevelCount between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.decomposition_level_count(),
|
||||
output_bsk.decomposition_level_count(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.input_lwe_dimension(),
|
||||
output_bsk.input_lwe_dimension(),
|
||||
"Mismatched input LweDimension between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.input_lwe_dimension(),
|
||||
output_bsk.input_lwe_dimension(),
|
||||
);
|
||||
|
||||
output_bsk
|
||||
.as_mut_view()
|
||||
.fill_with_forward_fourier(input_bsk.as_view(), fft, stack);
|
||||
}
|
||||
|
||||
pub fn par_convert_standard_cm_lwe_bootstrap_key_to_fourier<Scalar, InputCont, OutputCont>(
|
||||
input_bsk: &CmLweBootstrapKey<InputCont>,
|
||||
output_bsk: &mut FourierCmLweBootstrapKey<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = c64>,
|
||||
{
|
||||
assert_eq!(
|
||||
input_bsk.polynomial_size(),
|
||||
output_bsk.polynomial_size(),
|
||||
"Mismatched PolynomialSize between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.polynomial_size(),
|
||||
output_bsk.polynomial_size(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.glwe_dimension(),
|
||||
output_bsk.glwe_dimension(),
|
||||
"Mismatched GlweSize"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.decomposition_base_log(),
|
||||
output_bsk.decomposition_base_log(),
|
||||
"Mismatched DecompositionBaseLog between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.decomposition_base_log(),
|
||||
output_bsk.decomposition_base_log(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.decomposition_level_count(),
|
||||
output_bsk.decomposition_level_count(),
|
||||
"Mismatched DecompositionLevelCount between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.decomposition_level_count(),
|
||||
output_bsk.decomposition_level_count(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.input_lwe_dimension(),
|
||||
output_bsk.input_lwe_dimension(),
|
||||
"Mismatched input LweDimension between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.input_lwe_dimension(),
|
||||
output_bsk.input_lwe_dimension(),
|
||||
);
|
||||
|
||||
let fft = Fft::new(input_bsk.polynomial_size());
|
||||
let fft = fft.as_view();
|
||||
|
||||
output_bsk
|
||||
.as_mut_view()
|
||||
.par_fill_with_forward_fourier(input_bsk.as_view(), fft);
|
||||
}
|
||||
|
||||
pub fn convert_standard_cm_lwe_bootstrap_key_to_fourier_mem_optimized_requirement(
|
||||
fft: FftView<'_>,
|
||||
) -> StackReq {
|
||||
fft.forward_scratch()
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
//! Module containing primitives pertaining to the generation of
|
||||
//! [`standard CommonMask LWE bootstrap keys`](`CmLweBootstrapKey`).
|
||||
|
||||
use crate::core_crypto::algorithms::*;
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::random::{Distribution, Uniform};
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::prelude::*;
|
||||
use itertools::Itertools;
|
||||
use rayon::prelude::*;
|
||||
|
||||
pub fn par_generate_cm_lwe_bootstrap_key<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
InputKeyCont,
|
||||
OutputKeyCont,
|
||||
OutputCont,
|
||||
Gen,
|
||||
>(
|
||||
input_lwe_secret_keys: &[LweSecretKey<InputKeyCont>],
|
||||
output_glwe_secret_keys: &[GlweSecretKey<OutputKeyCont>],
|
||||
output: &mut CmLweBootstrapKey<OutputCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution> + Sync + Send,
|
||||
NoiseDistribution: Distribution + Sync,
|
||||
InputKeyCont: Container<Element = Scalar> + std::fmt::Debug,
|
||||
OutputKeyCont: Container<Element = Scalar> + Sync,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ParallelByteRandomGenerator,
|
||||
{
|
||||
for input_lwe_secret_key in input_lwe_secret_keys {
|
||||
assert!(
|
||||
output.input_lwe_dimension() == input_lwe_secret_key.lwe_dimension(),
|
||||
"Mismatched LweDimension between input LWE secret key and LWE bootstrap key. \
|
||||
Input LWE secret key LweDimension: {:?}, LWE bootstrap key input LweDimension {:?}.",
|
||||
input_lwe_secret_key.lwe_dimension(),
|
||||
output.input_lwe_dimension()
|
||||
);
|
||||
}
|
||||
let key_len = input_lwe_secret_keys[0].as_view().into_container().len();
|
||||
|
||||
for output_glwe_secret_key in output_glwe_secret_keys {
|
||||
assert!(
|
||||
output.glwe_dimension() == output_glwe_secret_key.glwe_dimension(),
|
||||
"Mismatched GlweSize between output GLWE secret key and LWE bootstrap key. \
|
||||
Output GLWE secret key GlweSize: {:?}, LWE bootstrap key GlweSize {:?}.",
|
||||
output_glwe_secret_key.glwe_dimension(),
|
||||
output.glwe_dimension()
|
||||
);
|
||||
|
||||
assert!(
|
||||
output.polynomial_size() == output_glwe_secret_key.polynomial_size(),
|
||||
"Mismatched PolynomialSize between output GLWE secret key and LWE bootstrap key. \
|
||||
Output GLWE secret key PolynomialSize: {:?}, LWE bootstrap key PolynomialSize {:?}.",
|
||||
output_glwe_secret_key.polynomial_size(),
|
||||
output.polynomial_size()
|
||||
);
|
||||
}
|
||||
|
||||
let gen_iter = generator
|
||||
.par_try_fork_from_config(output.encryption_fork_config(Uniform, noise_distribution))
|
||||
.unwrap();
|
||||
|
||||
let transposed_keys = (0..key_len)
|
||||
.map(|i| {
|
||||
input_lwe_secret_keys
|
||||
.iter()
|
||||
.map(|key| Cleartext(key.as_view().into_container()[i]))
|
||||
.collect_vec()
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
output
|
||||
.par_iter_mut()
|
||||
.zip(transposed_keys.par_iter())
|
||||
.zip(gen_iter)
|
||||
.for_each(|((mut ggsw, input_key_element), mut generator)| {
|
||||
par_encrypt_constant_cm_ggsw_ciphertext(
|
||||
output_glwe_secret_keys,
|
||||
&mut ggsw,
|
||||
input_key_element,
|
||||
noise_distribution,
|
||||
&mut generator,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CmBootstrapKeys<Scalar: UnsignedInteger> {
|
||||
pub small_lwe_sk: Vec<LweSecretKey<Vec<Scalar>>>,
|
||||
pub big_lwe_sk: Vec<LweSecretKey<Vec<Scalar>>>,
|
||||
pub bsk: CmLweBootstrapKeyOwned<Scalar>,
|
||||
pub fbsk: FourierCmLweBootstrapKeyOwned,
|
||||
}
|
||||
|
||||
pub fn generate_cm_pbs_keys(
|
||||
params: &CmApParams,
|
||||
encryption_random_generator: &mut EncryptionRandomGenerator<DefaultRandomGenerator>,
|
||||
secret_random_generator: &mut SecretRandomGenerator<DefaultRandomGenerator>,
|
||||
) -> CmBootstrapKeys<u64> {
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
|
||||
let cm_dimension = params.cm_dimension;
|
||||
|
||||
let glwe_noise_distribution = params.glwe_noise_distribution;
|
||||
|
||||
// Create the LweSecretKey
|
||||
let input_lwe_secret_keys = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_generate_new_binary_lwe_secret_key(
|
||||
params.lwe_dimension,
|
||||
secret_random_generator,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
let output_glwe_secret_keys = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_generate_new_binary_glwe_secret_key(
|
||||
params.glwe_dimension,
|
||||
params.polynomial_size,
|
||||
secret_random_generator,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
let output_lwe_secret_keys = output_glwe_secret_keys
|
||||
.iter()
|
||||
.map(|a| a.clone().into_lwe_secret_key())
|
||||
.collect_vec();
|
||||
|
||||
let mut bsk = CmLweBootstrapKey::new(
|
||||
0,
|
||||
params.glwe_dimension,
|
||||
cm_dimension,
|
||||
params.polynomial_size,
|
||||
params.base_log_bs,
|
||||
params.level_bs,
|
||||
params.lwe_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
par_generate_cm_lwe_bootstrap_key(
|
||||
&input_lwe_secret_keys,
|
||||
&output_glwe_secret_keys,
|
||||
&mut bsk,
|
||||
glwe_noise_distribution,
|
||||
encryption_random_generator,
|
||||
);
|
||||
|
||||
let mut fbsk = FourierCmLweBootstrapKey::new(
|
||||
params.lwe_dimension,
|
||||
params.glwe_dimension,
|
||||
cm_dimension,
|
||||
params.polynomial_size,
|
||||
params.base_log_bs,
|
||||
params.level_bs,
|
||||
);
|
||||
|
||||
par_convert_standard_cm_lwe_bootstrap_key_to_fourier(&bsk, &mut fbsk);
|
||||
|
||||
CmBootstrapKeys {
|
||||
small_lwe_sk: input_lwe_secret_keys,
|
||||
big_lwe_sk: output_lwe_secret_keys,
|
||||
bsk,
|
||||
fbsk,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
//! Module containing primitives pertaining to `CommonMask LWE ciphertext encryption and
|
||||
//! decryption`.
|
||||
|
||||
use crate::core_crypto::algorithms::slice_algorithms::*;
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::random::{Distribution, RandomGenerable};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use itertools::Itertools;
|
||||
|
||||
pub fn fill_cm_lwe_mask_and_bodies_for_encryption<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
KeyCont,
|
||||
EncodedCont,
|
||||
OutputMaskCont,
|
||||
OutputBodyCont,
|
||||
Gen,
|
||||
>(
|
||||
lwe_secret_keys: &[LweSecretKey<KeyCont>],
|
||||
output_mask: &mut LweMask<OutputMaskCont>,
|
||||
output_bodies: &mut LweBodyList<OutputBodyCont>,
|
||||
encoded: &PlaintextList<EncodedCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus + RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
EncodedCont: Container<Element = Scalar>,
|
||||
OutputMaskCont: ContainerMut<Element = Scalar>,
|
||||
OutputBodyCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert_eq!(
|
||||
output_mask.ciphertext_modulus(),
|
||||
output_bodies.ciphertext_modulus(),
|
||||
"Mismatched moduli between mask ({:?}) and body ({:?})",
|
||||
output_mask.ciphertext_modulus(),
|
||||
output_bodies.ciphertext_modulus()
|
||||
);
|
||||
|
||||
let ciphertext_modulus = output_mask.ciphertext_modulus();
|
||||
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
// generate a randomly uniform mask
|
||||
generator
|
||||
.fill_slice_with_random_uniform_mask_custom_mod(output_mask.as_mut(), ciphertext_modulus);
|
||||
|
||||
for ((sk, body), encoded) in lwe_secret_keys
|
||||
.iter()
|
||||
.zip_eq(output_bodies.iter_mut())
|
||||
.zip_eq(encoded.iter())
|
||||
{
|
||||
// generate an error from the given noise_distribution
|
||||
let noise = generator
|
||||
.random_noise_from_distribution_custom_mod(noise_distribution, ciphertext_modulus);
|
||||
// compute the multisum between the secret key and the mask
|
||||
let mask_key_dot_product = slice_wrapping_dot_product(output_mask.as_ref(), sk.as_ref());
|
||||
|
||||
// Store sum(ai * si) + delta * m + e in the body
|
||||
*body.data = mask_key_dot_product
|
||||
.wrapping_add(*encoded.0)
|
||||
.wrapping_add(noise);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encrypt_cm_lwe_ciphertext<Scalar, NoiseDistribution, KeyCont, EncodedCont, OutputCont, Gen>(
|
||||
lwe_secret_keys: &[LweSecretKey<KeyCont>],
|
||||
output: &mut CmLweCiphertext<OutputCont>,
|
||||
encoded: &PlaintextList<EncodedCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus + RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
EncodedCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let (mut mask, mut body) = output.get_mut_mask_and_bodies();
|
||||
|
||||
fill_cm_lwe_mask_and_bodies_for_encryption(
|
||||
lwe_secret_keys,
|
||||
&mut mask,
|
||||
&mut body,
|
||||
encoded,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn decrypt_cm_lwe_ciphertext<Scalar, KeyCont, InputCont>(
|
||||
lwe_secret_keys: &[LweSecretKey<KeyCont>],
|
||||
cm_lwe_ciphertext: &CmLweCiphertext<InputCont>,
|
||||
) -> Vec<Plaintext<Scalar>>
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
{
|
||||
for lwe_secret_key in lwe_secret_keys {
|
||||
assert!(
|
||||
cm_lwe_ciphertext.lwe_dimension() == lwe_secret_key.lwe_dimension(),
|
||||
"Mismatch between LweDimension of output ciphertext and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
cm_lwe_ciphertext.lwe_dimension(),
|
||||
lwe_secret_key.lwe_dimension()
|
||||
);
|
||||
}
|
||||
|
||||
let ciphertext_modulus = cm_lwe_ciphertext.ciphertext_modulus();
|
||||
|
||||
assert!(ciphertext_modulus.is_native_modulus());
|
||||
|
||||
let (mask, bodies) = cm_lwe_ciphertext.get_mask_and_bodies();
|
||||
|
||||
bodies
|
||||
.iter()
|
||||
.zip_eq(lwe_secret_keys.iter())
|
||||
.map(|(body, lwe_secret_key)| {
|
||||
let mask_key_dot_product =
|
||||
slice_wrapping_dot_product(mask.as_ref(), lwe_secret_key.as_ref());
|
||||
|
||||
Plaintext(body.data.wrapping_sub(mask_key_dot_product))
|
||||
})
|
||||
.collect_vec()
|
||||
}
|
||||
|
||||
pub fn allocate_and_encrypt_new_cm_lwe_ciphertext<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
KeyCont,
|
||||
EncodedCont,
|
||||
Gen,
|
||||
>(
|
||||
lwe_secret_keys: &[LweSecretKey<KeyCont>],
|
||||
encoded: &PlaintextList<EncodedCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) -> CmLweCiphertextOwned<Scalar>
|
||||
where
|
||||
Scalar: UnsignedTorus + RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
EncodedCont: Container<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let mut new_ct = CmLweCiphertextOwned::new(
|
||||
Scalar::ZERO,
|
||||
lwe_secret_keys[0].lwe_dimension(),
|
||||
CmDimension(encoded.as_ref().len()),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
encrypt_cm_lwe_ciphertext(
|
||||
lwe_secret_keys,
|
||||
&mut new_ct,
|
||||
encoded,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
|
||||
new_ct
|
||||
}
|
||||
|
||||
pub fn encrypt_cm_lwe_ciphertext_list<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
KeyCont,
|
||||
OutputCont,
|
||||
InputCont,
|
||||
Gen,
|
||||
>(
|
||||
lwe_secret_keys: &[LweSecretKey<KeyCont>],
|
||||
output: &mut CmLweCiphertextList<OutputCont>,
|
||||
encoded: &[PlaintextList<InputCont>],
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus + RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
for (mut output, encoded) in output.iter_mut().zip_eq(encoded.iter()) {
|
||||
encrypt_cm_lwe_ciphertext(
|
||||
lwe_secret_keys,
|
||||
&mut output,
|
||||
encoded,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
//! Module containing primitives pertaining to [`CommonMask LWE ciphertext
|
||||
//! keyswitch`](`CmLweKeyswitchKey`).
|
||||
|
||||
use crate::core_crypto::algorithms::slice_algorithms::*;
|
||||
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
use crate::core_crypto::commons::parameters::ThreadCount;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use itertools::Itertools;
|
||||
use rayon::prelude::*;
|
||||
|
||||
pub fn cm_keyswitch_lwe_ciphertext<Scalar, KSKCont, InputCont, OutputCont>(
|
||||
cm_lwe_keyswitch_key: &CmLweKeyswitchKey<KSKCont>,
|
||||
input_lwe_ciphertext: &CmLweCiphertext<InputCont>,
|
||||
output_lwe_ciphertext: &mut CmLweCiphertext<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
KSKCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.input_lwe_dimension() == input_lwe_ciphertext.lwe_dimension(),
|
||||
"Mismatched input LweDimension. \
|
||||
CmLweKeyswitchKey input LweDimension: {:?}, input CmLweCiphertext LweDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.input_lwe_dimension(),
|
||||
input_lwe_ciphertext.lwe_dimension(),
|
||||
);
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.output_lwe_dimension() == output_lwe_ciphertext.lwe_dimension(),
|
||||
"Mismatched output LweDimension. \
|
||||
CmLweKeyswitchKey output LweDimension: {:?}, output CmLweCiphertext LweDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.output_lwe_dimension(),
|
||||
output_lwe_ciphertext.lwe_dimension(),
|
||||
);
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.cm_dimension() == input_lwe_ciphertext.cm_dimension(),
|
||||
"Mismatched CmDimension. \
|
||||
CmLweKeyswitchKey CmDimension: {:?}, input CmLweCiphertext CmDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.cm_dimension(),
|
||||
input_lwe_ciphertext.cm_dimension(),
|
||||
);
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.cm_dimension() == output_lwe_ciphertext.cm_dimension(),
|
||||
"Mismatched CmDimension. \
|
||||
CmLweKeyswitchKey CmDimension: {:?}, output CmLweCiphertext CmDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.cm_dimension(),
|
||||
output_lwe_ciphertext.cm_dimension(),
|
||||
);
|
||||
|
||||
let output_ciphertext_modulus = output_lwe_ciphertext.ciphertext_modulus();
|
||||
|
||||
assert_eq!(
|
||||
cm_lwe_keyswitch_key.ciphertext_modulus(),
|
||||
output_ciphertext_modulus,
|
||||
"Mismatched CiphertextModulus. \
|
||||
CmLweKeyswitchKey CiphertextModulus: {:?}, output CmLweCiphertext CiphertextModulus {:?}.",
|
||||
cm_lwe_keyswitch_key.ciphertext_modulus(),
|
||||
output_ciphertext_modulus
|
||||
);
|
||||
assert!(
|
||||
output_ciphertext_modulus.is_compatible_with_native_modulus(),
|
||||
"This operation currently only supports power of 2 moduli"
|
||||
);
|
||||
|
||||
let input_ciphertext_modulus = input_lwe_ciphertext.ciphertext_modulus();
|
||||
|
||||
assert!(
|
||||
input_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);
|
||||
|
||||
// Copy the input body to the output ciphertext
|
||||
output_lwe_ciphertext
|
||||
.get_mut_bodies()
|
||||
.as_mut()
|
||||
.copy_from_slice(input_lwe_ciphertext.get_bodies().as_ref());
|
||||
|
||||
// We instantiate a decomposer
|
||||
let decomposer = SignedDecomposer::new(
|
||||
cm_lwe_keyswitch_key.decomposition_base_log(),
|
||||
cm_lwe_keyswitch_key.decomposition_level_count(),
|
||||
);
|
||||
|
||||
for (keyswitch_key_block, &input_mask_element) in cm_lwe_keyswitch_key
|
||||
.iter()
|
||||
.zip(input_lwe_ciphertext.get_mask().as_ref())
|
||||
{
|
||||
let decomposition_iter = decomposer.decompose(input_mask_element);
|
||||
// Loop over the levels
|
||||
for (level_key_ciphertext, decomposed) in keyswitch_key_block.iter().zip(decomposition_iter)
|
||||
{
|
||||
slice_wrapping_sub_scalar_mul_assign(
|
||||
output_lwe_ciphertext.as_mut(),
|
||||
level_key_ciphertext.as_ref(),
|
||||
decomposed.value(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn par_cm_keyswitch_lwe_ciphertext<Scalar, KSKCont, InputCont, OutputCont>(
|
||||
cm_lwe_keyswitch_key: &CmLweKeyswitchKey<KSKCont>,
|
||||
input_lwe_ciphertext: &CmLweCiphertext<InputCont>,
|
||||
output_lwe_ciphertext: &mut CmLweCiphertext<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger + Send + Sync,
|
||||
KSKCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let thread_count = ThreadCount(rayon::current_num_threads());
|
||||
par_cm_keyswitch_lwe_ciphertext_with_thread_count(
|
||||
cm_lwe_keyswitch_key,
|
||||
input_lwe_ciphertext,
|
||||
output_lwe_ciphertext,
|
||||
thread_count,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn par_cm_keyswitch_lwe_ciphertext_with_thread_count<Scalar, KSKCont, InputCont, OutputCont>(
|
||||
cm_lwe_keyswitch_key: &CmLweKeyswitchKey<KSKCont>,
|
||||
input_lwe_ciphertext: &CmLweCiphertext<InputCont>,
|
||||
output_lwe_ciphertext: &mut CmLweCiphertext<OutputCont>,
|
||||
thread_count: ThreadCount,
|
||||
) where
|
||||
Scalar: UnsignedInteger + Send + Sync,
|
||||
KSKCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.input_lwe_dimension() == input_lwe_ciphertext.lwe_dimension(),
|
||||
"Mismatched input LweDimension. \
|
||||
CmLweKeyswitchKey input LweDimension: {:?}, input CmLweCiphertext LweDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.input_lwe_dimension(),
|
||||
input_lwe_ciphertext.lwe_dimension(),
|
||||
);
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.output_lwe_dimension() == output_lwe_ciphertext.lwe_dimension(),
|
||||
"Mismatched output LweDimension. \
|
||||
CmLweKeyswitchKey output LweDimension: {:?}, output CmLweCiphertext LweDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.output_lwe_dimension(),
|
||||
output_lwe_ciphertext.lwe_dimension(),
|
||||
);
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.cm_dimension() == input_lwe_ciphertext.cm_dimension(),
|
||||
"Mismatched CmDimension. \
|
||||
CmLweKeyswitchKey CmDimension: {:?}, input CmLweCiphertext CmDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.cm_dimension(),
|
||||
input_lwe_ciphertext.cm_dimension(),
|
||||
);
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.cm_dimension() == output_lwe_ciphertext.cm_dimension(),
|
||||
"Mismatched CmDimension. \
|
||||
CmLweKeyswitchKey CmDimension: {:?}, output CmLweCiphertext CmDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.cm_dimension(),
|
||||
output_lwe_ciphertext.cm_dimension(),
|
||||
);
|
||||
|
||||
let output_ciphertext_modulus = output_lwe_ciphertext.ciphertext_modulus();
|
||||
|
||||
assert_eq!(
|
||||
cm_lwe_keyswitch_key.ciphertext_modulus(),
|
||||
output_ciphertext_modulus,
|
||||
"Mismatched CiphertextModulus. \
|
||||
CmLweKeyswitchKey CiphertextModulus: {:?}, output CmLweCiphertext CiphertextModulus {:?}.",
|
||||
cm_lwe_keyswitch_key.ciphertext_modulus(),
|
||||
output_ciphertext_modulus
|
||||
);
|
||||
assert!(
|
||||
output_ciphertext_modulus.is_compatible_with_native_modulus(),
|
||||
"This operation currently only supports power of 2 moduli"
|
||||
);
|
||||
|
||||
let input_ciphertext_modulus = input_lwe_ciphertext.ciphertext_modulus();
|
||||
|
||||
assert!(
|
||||
input_ciphertext_modulus.is_compatible_with_native_modulus(),
|
||||
"This operation currently only supports power of 2 moduli"
|
||||
);
|
||||
|
||||
assert!(
|
||||
thread_count.0 != 0,
|
||||
"Got thread_count == 0, this is not supported"
|
||||
);
|
||||
|
||||
// Clear the output ciphertext, as it will get updated gradually
|
||||
output_lwe_ciphertext.as_mut().fill(Scalar::ZERO);
|
||||
|
||||
let output_lwe_dimension = output_lwe_ciphertext.lwe_dimension();
|
||||
|
||||
let cm_dimension = output_lwe_ciphertext.cm_dimension();
|
||||
|
||||
// We instantiate a decomposer
|
||||
let decomposer = SignedDecomposer::new(
|
||||
cm_lwe_keyswitch_key.decomposition_base_log(),
|
||||
cm_lwe_keyswitch_key.decomposition_level_count(),
|
||||
);
|
||||
|
||||
// Don't go above the current number of threads
|
||||
let thread_count = thread_count.0.min(rayon::current_num_threads());
|
||||
let mut intermediate_accumulators = Vec::with_capacity(thread_count);
|
||||
|
||||
// Smallest chunk_size such that thread_count * chunk_size >= input_lwe_dimension
|
||||
let chunk_size = input_lwe_ciphertext
|
||||
.lwe_dimension()
|
||||
.0
|
||||
.div_ceil(thread_count);
|
||||
|
||||
cm_lwe_keyswitch_key
|
||||
.par_chunks(chunk_size)
|
||||
.zip(
|
||||
input_lwe_ciphertext
|
||||
.get_mask()
|
||||
.as_ref()
|
||||
.par_chunks(chunk_size),
|
||||
)
|
||||
.map(|(keyswitch_key_block_chunk, input_mask_element_chunk)| {
|
||||
let mut buffer = CmLweCiphertext::new(
|
||||
Scalar::ZERO,
|
||||
output_lwe_dimension,
|
||||
cm_dimension,
|
||||
output_ciphertext_modulus,
|
||||
);
|
||||
|
||||
for (keyswitch_key_block, &input_mask_element) in keyswitch_key_block_chunk
|
||||
.iter()
|
||||
.zip(input_mask_element_chunk.iter())
|
||||
{
|
||||
let decomposition_iter = decomposer.decompose(input_mask_element);
|
||||
// Loop over the levels
|
||||
for (level_key_ciphertext, decomposed) in
|
||||
keyswitch_key_block.iter().zip(decomposition_iter)
|
||||
{
|
||||
slice_wrapping_sub_scalar_mul_assign(
|
||||
buffer.as_mut(),
|
||||
level_key_ciphertext.as_ref(),
|
||||
decomposed.value(),
|
||||
);
|
||||
}
|
||||
}
|
||||
buffer
|
||||
})
|
||||
.collect_into_vec(&mut intermediate_accumulators);
|
||||
|
||||
let reduced = intermediate_accumulators
|
||||
.par_iter_mut()
|
||||
.reduce_with(|lhs, rhs| {
|
||||
lhs.as_mut()
|
||||
.iter_mut()
|
||||
.zip(rhs.as_ref().iter())
|
||||
.for_each(|(dst, &src)| *dst = (*dst).wrapping_add(src));
|
||||
|
||||
lhs
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let reduced = reduced.as_view();
|
||||
|
||||
output_lwe_ciphertext
|
||||
.get_mut_mask()
|
||||
.as_mut()
|
||||
.copy_from_slice(reduced.get_mask().as_ref());
|
||||
|
||||
let reduced_ksed_bodies = reduced.get_bodies();
|
||||
|
||||
// Add the reduced body of the keyswitch to the output body to complete the keyswitch
|
||||
for ((out, in1), in2) in output_lwe_ciphertext
|
||||
.get_mut_bodies()
|
||||
.as_mut()
|
||||
.iter_mut()
|
||||
.zip_eq(input_lwe_ciphertext.get_bodies().as_ref())
|
||||
.zip_eq(reduced_ksed_bodies.as_ref())
|
||||
{
|
||||
*out = in1.wrapping_add(*in2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
//! Module containing primitives pertaining to [`CommonMask LWE keyswitch keys
|
||||
//! generation`](`CmLweKeyswitchKey`)
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::decomposition::{DecompositionLevel, DecompositionTerm};
|
||||
use crate::core_crypto::commons::math::random::{Distribution, Uniform};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::algorithms::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
|
||||
pub fn generate_cm_lwe_keyswitch_key<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
InputKeyCont,
|
||||
OutputKeyCont,
|
||||
KSKeyCont,
|
||||
Gen,
|
||||
>(
|
||||
input_cm_lwe_sks: &[LweSecretKey<InputKeyCont>],
|
||||
output_cm_lwe_sks: &[LweSecretKey<OutputKeyCont>],
|
||||
cm_lwe_keyswitch_key: &mut CmLweKeyswitchKey<KSKeyCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution> + UnsignedTorus,
|
||||
NoiseDistribution: Distribution,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
OutputKeyCont: Container<Element = Scalar>,
|
||||
KSKeyCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
for input_cm_lwe_sk in input_cm_lwe_sks {
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.input_lwe_dimension() == input_cm_lwe_sk.lwe_dimension(),
|
||||
"The destination CmLweKeyswitchKey input LweDimension is not equal \
|
||||
to the input LweSecretKey LweDimension. Destination: {:?}, input: {:?}",
|
||||
cm_lwe_keyswitch_key.input_lwe_dimension(),
|
||||
input_cm_lwe_sk.lwe_dimension()
|
||||
);
|
||||
}
|
||||
for output_cm_lwe_sk in output_cm_lwe_sks {
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.output_lwe_dimension() == output_cm_lwe_sk.lwe_dimension(),
|
||||
"The destination CmLweKeyswitchKey output LweDimension is not equal \
|
||||
to the output LweSecretKey LweDimension. Destination: {:?}, output: {:?}",
|
||||
cm_lwe_keyswitch_key.output_lwe_dimension(),
|
||||
output_cm_lwe_sk.lwe_dimension()
|
||||
);
|
||||
}
|
||||
|
||||
let decomp_base_log = cm_lwe_keyswitch_key.decomposition_base_log();
|
||||
let decomp_level_count = cm_lwe_keyswitch_key.decomposition_level_count();
|
||||
let cm_dimension = cm_lwe_keyswitch_key.cm_dimension();
|
||||
let ciphertext_modulus = cm_lwe_keyswitch_key.ciphertext_modulus();
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
// The plaintexts used to encrypt a key element will be stored in this buffer
|
||||
let mut decomposition_plaintexts_buffers = (0..decomp_level_count.0)
|
||||
.map(|_| PlaintextListOwned::new(Scalar::ZERO, PlaintextCount(cm_dimension.0)))
|
||||
.collect_vec();
|
||||
|
||||
// Iterate over the input key elements and the destination lwe_keyswitch_key memory
|
||||
for (input_key_bit_index, mut keyswitch_key_block) in
|
||||
cm_lwe_keyswitch_key.iter_mut().enumerate()
|
||||
{
|
||||
for (pt_index, input_cm_lwe_sk) in input_cm_lwe_sks.iter().enumerate() {
|
||||
// We fill the buffer with the powers of the key elements
|
||||
for (level, message) in (1..=decomp_level_count.0)
|
||||
.rev()
|
||||
.map(DecompositionLevel)
|
||||
.zip_eq(decomposition_plaintexts_buffers.iter_mut())
|
||||
{
|
||||
// Here we take the decomposition term from the native torus, bring it to the torus
|
||||
// we are working with by dividing by the scaling factor and the
|
||||
// encryption will take care of mapping that back to the native
|
||||
// torus
|
||||
message.as_mut()[pt_index] = DecompositionTerm::new(
|
||||
level,
|
||||
decomp_base_log,
|
||||
input_cm_lwe_sk.as_ref()[input_key_bit_index],
|
||||
)
|
||||
.to_recomposition_summand()
|
||||
.wrapping_div(ciphertext_modulus.get_power_of_two_scaling_to_native_torus());
|
||||
}
|
||||
}
|
||||
|
||||
encrypt_cm_lwe_ciphertext_list(
|
||||
output_cm_lwe_sks,
|
||||
&mut keyswitch_key_block,
|
||||
&decomposition_plaintexts_buffers,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn allocate_and_generate_new_cm_lwe_keyswitch_key<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
InputKeyCont,
|
||||
OutputKeyCont,
|
||||
Gen,
|
||||
>(
|
||||
input_cm_lwe_sks: &[LweSecretKey<InputKeyCont>],
|
||||
output_cm_lwe_sks: &[LweSecretKey<OutputKeyCont>],
|
||||
cm_dimension: CmDimension,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
noise_distribution: NoiseDistribution,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) -> CmLweKeyswitchKeyOwned<Scalar>
|
||||
where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution> + UnsignedTorus,
|
||||
NoiseDistribution: Distribution,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
OutputKeyCont: Container<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let mut new_cm_lwe_keyswitch_key = CmLweKeyswitchKeyOwned::new(
|
||||
Scalar::ZERO,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_cm_lwe_sks[0].lwe_dimension(),
|
||||
output_cm_lwe_sks[0].lwe_dimension(),
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
generate_cm_lwe_keyswitch_key(
|
||||
input_cm_lwe_sks,
|
||||
output_cm_lwe_sks,
|
||||
&mut new_cm_lwe_keyswitch_key,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
|
||||
new_cm_lwe_keyswitch_key
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
//! Module containing primitives pertaining to [`CommonMask LWE ciphertext`](`CmLweCiphertext`)
|
||||
//! linear algebra, like addition, multiplication, etc.
|
||||
|
||||
use crate::core_crypto::algorithms::slice_algorithms::*;
|
||||
use crate::core_crypto::commons::ciphertext_modulus::CiphertextModulusKind;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::CmLweCiphertext;
|
||||
|
||||
pub fn cm_lwe_ciphertext_add_assign<Scalar, LhsCont, RhsCont>(
|
||||
lhs: &mut CmLweCiphertext<LhsCont>,
|
||||
rhs: &CmLweCiphertext<RhsCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
LhsCont: ContainerMut<Element = Scalar>,
|
||||
RhsCont: Container<Element = Scalar>,
|
||||
{
|
||||
let ciphertext_modulus = rhs.ciphertext_modulus();
|
||||
if ciphertext_modulus.is_compatible_with_native_modulus() {
|
||||
cm_lwe_ciphertext_add_assign_native_mod_compatible(lhs, rhs);
|
||||
} else {
|
||||
cm_lwe_ciphertext_add_assign_other_mod(lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_add_assign_native_mod_compatible<Scalar, LhsCont, RhsCont>(
|
||||
lhs: &mut CmLweCiphertext<LhsCont>,
|
||||
rhs: &CmLweCiphertext<RhsCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
LhsCont: ContainerMut<Element = Scalar>,
|
||||
RhsCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between lhs ({:?}) and rhs ({:?}) CmLweCiphertext",
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus()
|
||||
);
|
||||
let ciphertext_modulus = rhs.ciphertext_modulus();
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
slice_wrapping_add_assign(lhs.as_mut(), rhs.as_ref());
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_add_assign_other_mod<Scalar, LhsCont, RhsCont>(
|
||||
lhs: &mut CmLweCiphertext<LhsCont>,
|
||||
rhs: &CmLweCiphertext<RhsCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
LhsCont: ContainerMut<Element = Scalar>,
|
||||
RhsCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between lhs ({:?}) and rhs ({:?}) CmLweCiphertext",
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus()
|
||||
);
|
||||
let ciphertext_modulus = rhs.ciphertext_modulus();
|
||||
assert!(!ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
slice_wrapping_add_assign_custom_mod(
|
||||
lhs.as_mut(),
|
||||
rhs.as_ref(),
|
||||
ciphertext_modulus.get_custom_modulus().cast_into(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_add<Scalar, OutputCont, LhsCont, RhsCont>(
|
||||
output: &mut CmLweCiphertext<OutputCont>,
|
||||
lhs: &CmLweCiphertext<LhsCont>,
|
||||
rhs: &CmLweCiphertext<RhsCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
LhsCont: Container<Element = Scalar>,
|
||||
RhsCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between lhs ({:?}) and rhs ({:?}) CmLweCiphertext",
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
output.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between output ({:?}) and rhs ({:?}) CmLweCiphertext",
|
||||
output.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus()
|
||||
);
|
||||
|
||||
slice_wrapping_add(output.as_mut(), lhs.as_ref(), rhs.as_ref());
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_plaintext_add_assign<Scalar, InCont>(
|
||||
lhs: &mut CmLweCiphertext<InCont>,
|
||||
rhs: Plaintext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let ciphertext_modulus = lhs.ciphertext_modulus();
|
||||
if ciphertext_modulus.is_compatible_with_native_modulus() {
|
||||
cm_lwe_ciphertext_plaintext_add_assign_native_mod_compatible(lhs, rhs);
|
||||
} else {
|
||||
cm_lwe_ciphertext_plaintext_add_assign_other_mod(lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_plaintext_add_assign_native_mod_compatible<Scalar, InCont>(
|
||||
lhs: &mut CmLweCiphertext<InCont>,
|
||||
rhs: Plaintext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let mut bodies = lhs.get_mut_bodies();
|
||||
let ciphertext_modulus = bodies.ciphertext_modulus();
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
let plaintext = match ciphertext_modulus.kind() {
|
||||
CiphertextModulusKind::Native => rhs.0,
|
||||
// Manage power of 2 encoding
|
||||
CiphertextModulusKind::NonNativePowerOfTwo => rhs
|
||||
.0
|
||||
.wrapping_mul(ciphertext_modulus.get_power_of_two_scaling_to_native_torus()),
|
||||
CiphertextModulusKind::Other => unreachable!(),
|
||||
};
|
||||
|
||||
for body in bodies.as_mut() {
|
||||
*body = body.wrapping_add(plaintext);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_plaintext_add_assign_other_mod<Scalar, InCont>(
|
||||
lhs: &mut CmLweCiphertext<InCont>,
|
||||
rhs: Plaintext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let mut bodies = lhs.get_mut_bodies();
|
||||
let ciphertext_modulus = bodies.ciphertext_modulus();
|
||||
assert!(!ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
for body in bodies.as_mut() {
|
||||
*body = body
|
||||
.wrapping_add_custom_mod(rhs.0, ciphertext_modulus.get_custom_modulus().cast_into());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_plaintext_sub_assign<Scalar, InCont>(
|
||||
lhs: &mut CmLweCiphertext<InCont>,
|
||||
rhs: Plaintext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let ciphertext_modulus = lhs.ciphertext_modulus();
|
||||
if ciphertext_modulus.is_compatible_with_native_modulus() {
|
||||
cm_lwe_ciphertext_plaintext_sub_assign_native_mod_compatible(lhs, rhs);
|
||||
} else {
|
||||
cm_lwe_ciphertext_plaintext_sub_assign_other_mod(lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_plaintext_sub_assign_native_mod_compatible<Scalar, InCont>(
|
||||
lhs: &mut CmLweCiphertext<InCont>,
|
||||
rhs: Plaintext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let mut bodies = lhs.get_mut_bodies();
|
||||
let ciphertext_modulus = bodies.ciphertext_modulus();
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
let plaintext = match ciphertext_modulus.kind() {
|
||||
CiphertextModulusKind::Native => rhs.0,
|
||||
// Manage power of 2 encoding
|
||||
CiphertextModulusKind::NonNativePowerOfTwo => rhs
|
||||
.0
|
||||
.wrapping_mul(ciphertext_modulus.get_power_of_two_scaling_to_native_torus()),
|
||||
CiphertextModulusKind::Other => unreachable!(),
|
||||
};
|
||||
|
||||
for body in bodies.as_mut() {
|
||||
*body = body.wrapping_sub(plaintext);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_plaintext_sub_assign_other_mod<Scalar, InCont>(
|
||||
lhs: &mut CmLweCiphertext<InCont>,
|
||||
rhs: Plaintext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let mut bodies = lhs.get_mut_bodies();
|
||||
let ciphertext_modulus = bodies.ciphertext_modulus();
|
||||
assert!(!ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
for body in bodies.as_mut() {
|
||||
*body = body
|
||||
.wrapping_sub_custom_mod(rhs.0, ciphertext_modulus.get_custom_modulus().cast_into());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_opposite_assign<Scalar, InCont>(ct: &mut CmLweCiphertext<InCont>)
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
slice_wrapping_opposite_assign(ct.as_mut());
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_cleartext_mul_assign<Scalar, InCont>(
|
||||
lhs: &mut CmLweCiphertext<InCont>,
|
||||
rhs: Cleartext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
slice_wrapping_scalar_mul_assign(lhs.as_mut(), rhs.0);
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_cleartext_mul<Scalar, InputCont, OutputCont>(
|
||||
output: &mut CmLweCiphertext<OutputCont>,
|
||||
lhs: &CmLweCiphertext<InputCont>,
|
||||
rhs: Cleartext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
output.ciphertext_modulus(),
|
||||
lhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between output ({:?}) and lhs ({:?}) CmLweCiphertext",
|
||||
output.ciphertext_modulus(),
|
||||
lhs.ciphertext_modulus()
|
||||
);
|
||||
output.as_mut().copy_from_slice(lhs.as_ref());
|
||||
cm_lwe_ciphertext_cleartext_mul_assign(output, rhs);
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_sub_assign<Scalar, LhsCont, RhsCont>(
|
||||
lhs: &mut CmLweCiphertext<LhsCont>,
|
||||
rhs: &CmLweCiphertext<RhsCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
LhsCont: ContainerMut<Element = Scalar>,
|
||||
RhsCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between lhs ({:?}) and rhs ({:?}) CmLweCiphertext",
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus()
|
||||
);
|
||||
|
||||
slice_wrapping_sub_assign(lhs.as_mut(), rhs.as_ref());
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_sub<Scalar, OutputCont, LhsCont, RhsCont>(
|
||||
output: &mut CmLweCiphertext<OutputCont>,
|
||||
lhs: &CmLweCiphertext<LhsCont>,
|
||||
rhs: &CmLweCiphertext<RhsCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
LhsCont: Container<Element = Scalar>,
|
||||
RhsCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between lhs ({:?}) and rhs ({:?}) CmLweCiphertext",
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
output.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between output ({:?}) and rhs ({:?}) CmLweCiphertext",
|
||||
output.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus()
|
||||
);
|
||||
|
||||
output.as_mut().copy_from_slice(lhs.as_ref());
|
||||
cm_lwe_ciphertext_sub_assign(output, rhs);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
//! Module containing primitives pertaining to `CommonMask LWE ciphertext
|
||||
//! packing`.
|
||||
|
||||
use crate::core_crypto::algorithms::slice_algorithms::*;
|
||||
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
use crate::core_crypto::commons::numeric::UnsignedInteger;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::{CmLweCiphertext, CmLwePackingKey};
|
||||
use itertools::Itertools;
|
||||
|
||||
pub fn pack_lwe_ciphertexts_into_cm<Scalar, KSKCont, InputCont, OutputCont>(
|
||||
cm_lwe_packing_key: &CmLwePackingKey<KSKCont>,
|
||||
// TODO: support both &[LweCiphertext] and &LweCiphertextList
|
||||
input_lwe_ciphertexts: &[LweCiphertext<InputCont>],
|
||||
output_cm_lwe_ciphertext: &mut CmLweCiphertext<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
KSKCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let input_ciphertext_modulus = input_lwe_ciphertexts[0].ciphertext_modulus();
|
||||
|
||||
for input_lwe_ciphertext in input_lwe_ciphertexts {
|
||||
assert_eq!(
|
||||
cm_lwe_packing_key.input_key_lwe_dimension(),
|
||||
input_lwe_ciphertext.lwe_size().to_lwe_dimension(),
|
||||
"Mismatched input LweDimension. \
|
||||
CmLwePackingKey input LweDimension: {:?}, input CmLweCiphertext LweDimension {:?}.",
|
||||
cm_lwe_packing_key.input_key_lwe_dimension(),
|
||||
input_lwe_ciphertext.lwe_size().to_lwe_dimension(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_lwe_ciphertext.ciphertext_modulus(),
|
||||
input_ciphertext_modulus,
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
cm_lwe_packing_key.output_lwe_dimension(),
|
||||
output_cm_lwe_ciphertext.lwe_dimension(),
|
||||
"Mismatched output LweDimension. \
|
||||
CmLwePackingKey output LweDimension: {:?}, output CmLweCiphertext LweDimension {:?}.",
|
||||
cm_lwe_packing_key.output_lwe_dimension(),
|
||||
output_cm_lwe_ciphertext.lwe_dimension(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cm_lwe_packing_key.output_cm_dimension(),
|
||||
output_cm_lwe_ciphertext.cm_dimension(),
|
||||
"Mismatched output LweDimension. \
|
||||
CmLwePackingKey output LweDimension: {:?}, output CmLweCiphertext LweDimension {:?}.",
|
||||
cm_lwe_packing_key.output_lwe_dimension(),
|
||||
output_cm_lwe_ciphertext.lwe_dimension(),
|
||||
);
|
||||
|
||||
let output_ciphertext_modulus = output_cm_lwe_ciphertext.ciphertext_modulus();
|
||||
|
||||
assert_eq!(
|
||||
cm_lwe_packing_key.ciphertext_modulus(),
|
||||
output_ciphertext_modulus,
|
||||
"Mismatched CiphertextModulus. \
|
||||
CmLwePackingKey CiphertextModulus: {:?}, output CmLweCiphertext CiphertextModulus {:?}.",
|
||||
cm_lwe_packing_key.ciphertext_modulus(),
|
||||
output_ciphertext_modulus
|
||||
);
|
||||
assert!(
|
||||
output_ciphertext_modulus.is_compatible_with_native_modulus(),
|
||||
"This operation currently only supports power of 2 moduli"
|
||||
);
|
||||
|
||||
assert!(
|
||||
input_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_cm_lwe_ciphertext.as_mut().fill(Scalar::ZERO);
|
||||
|
||||
for (i, (key_part, input_lwe_ciphertext)) in cm_lwe_packing_key
|
||||
.iter()
|
||||
.zip_eq(input_lwe_ciphertexts.iter())
|
||||
.enumerate()
|
||||
{
|
||||
let mut bodies = output_cm_lwe_ciphertext.get_mut_bodies();
|
||||
|
||||
let body = &mut bodies.as_mut()[i];
|
||||
|
||||
*body = body.wrapping_add(*input_lwe_ciphertext.get_body().data);
|
||||
|
||||
// We instantiate a decomposer
|
||||
let decomposer = SignedDecomposer::new(
|
||||
cm_lwe_packing_key.decomposition_base_log(),
|
||||
cm_lwe_packing_key.decomposition_level_count(),
|
||||
);
|
||||
|
||||
for (keyswitch_key_block, &input_mask_element) in key_part
|
||||
.iter()
|
||||
.zip_eq(input_lwe_ciphertext.get_mask().as_ref())
|
||||
{
|
||||
let decomposition_iter = decomposer.decompose(input_mask_element);
|
||||
// Loop over the levels
|
||||
|
||||
for (level_key_ciphertext, decomposed) in
|
||||
keyswitch_key_block.iter().zip_eq(decomposition_iter)
|
||||
{
|
||||
slice_wrapping_sub_scalar_mul_assign(
|
||||
output_cm_lwe_ciphertext.as_mut(),
|
||||
level_key_ciphertext.into_container(),
|
||||
decomposed.value(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
//! Module containing primitives pertaining to
|
||||
//! [`CommonMask LWE packing keys generation`](`CmLwePackingKey`)
|
||||
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::decomposition::{DecompositionLevel, DecompositionTerm};
|
||||
use crate::core_crypto::commons::math::random::{Distribution, RandomGenerable};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::algorithms::*;
|
||||
use crate::core_crypto::experimental::prelude::{
|
||||
CmDimension, CmLwePackingKey, CmLwePackingKeyOwned,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
pub fn generate_cm_lwe_packing_key<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
InputKeyCont,
|
||||
OutputKeyCont,
|
||||
KSKeyCont,
|
||||
Gen,
|
||||
>(
|
||||
input_lwe_sk: &LweSecretKey<InputKeyCont>,
|
||||
output_lwe_sks: &[LweSecretKey<OutputKeyCont>],
|
||||
lwe_keyswitch_key: &mut CmLwePackingKey<KSKeyCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus + RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
NoiseDistribution: Distribution,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
OutputKeyCont: Container<Element = Scalar>,
|
||||
KSKeyCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert!(
|
||||
lwe_keyswitch_key.input_key_lwe_dimension() == input_lwe_sk.lwe_dimension(),
|
||||
"The destination CmLwePackingKey input LweDimension is not equal \
|
||||
to the input LweSecretKey LweDimension. Destination: {:?}, input: {:?}",
|
||||
lwe_keyswitch_key.input_key_lwe_dimension(),
|
||||
input_lwe_sk.lwe_dimension()
|
||||
);
|
||||
|
||||
for output_lwe_sk in output_lwe_sks {
|
||||
assert!(
|
||||
lwe_keyswitch_key.output_lwe_dimension() == output_lwe_sk.lwe_dimension(),
|
||||
"The destination CmLwePackingKey output LweDimension is not equal \
|
||||
to the output LweSecretKey LweDimension. Destination: {:?}, output: {:?}",
|
||||
lwe_keyswitch_key.output_lwe_dimension(),
|
||||
output_lwe_sk.lwe_dimension()
|
||||
);
|
||||
}
|
||||
|
||||
let cm_dimension = CmDimension(output_lwe_sks.len());
|
||||
|
||||
let decomp_base_log = lwe_keyswitch_key.decomposition_base_log();
|
||||
let decomp_level_count = lwe_keyswitch_key.decomposition_level_count();
|
||||
let ciphertext_modulus = lwe_keyswitch_key.ciphertext_modulus();
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
// The plaintexts used to encrypt a key element will be stored in this buffer
|
||||
let mut decomposition_plaintexts_buffer =
|
||||
PlaintextListOwned::new(Scalar::ZERO, PlaintextCount(decomp_level_count.0));
|
||||
|
||||
// Iterate over the input key elements and the destination lwe_keyswitch_key memory
|
||||
for (i, mut keyswitch_key_block) in lwe_keyswitch_key.iter_mut().enumerate() {
|
||||
for (input_key_element, mut keyswitch_key_block_block) in input_lwe_sk
|
||||
.as_ref()
|
||||
.iter()
|
||||
.zip_eq(keyswitch_key_block.iter_mut())
|
||||
{
|
||||
// We fill the buffer with the powers of the key elements
|
||||
for (level, message) in (1..=decomp_level_count.0)
|
||||
.rev()
|
||||
.map(DecompositionLevel)
|
||||
.zip_eq(decomposition_plaintexts_buffer.iter_mut())
|
||||
{
|
||||
// Here we take the decomposition term from the native torus, bring it to the
|
||||
// torus
|
||||
// we are working with by dividing by the scaling factor and the
|
||||
// encryption will take care of mapping that back to the native
|
||||
// torus
|
||||
*message.0 = DecompositionTerm::new(level, decomp_base_log, *input_key_element)
|
||||
.to_recomposition_summand()
|
||||
.wrapping_div(ciphertext_modulus.get_power_of_two_scaling_to_native_torus());
|
||||
}
|
||||
|
||||
let list = decomposition_plaintexts_buffer
|
||||
.iter()
|
||||
.map(|decomposition_plaintext| {
|
||||
PlaintextList::from_container(
|
||||
(0..cm_dimension.0)
|
||||
.map(|j| {
|
||||
if i == j {
|
||||
*decomposition_plaintext.0
|
||||
} else {
|
||||
Scalar::ZERO
|
||||
}
|
||||
})
|
||||
.collect_vec(),
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
encrypt_cm_lwe_ciphertext_list(
|
||||
output_lwe_sks,
|
||||
&mut keyswitch_key_block_block,
|
||||
&list,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocate_and_generate_new_cm_lwe_packing_key<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
InputKeyCont,
|
||||
OutputKeyCont,
|
||||
Gen,
|
||||
>(
|
||||
input_lwe_sk: &LweSecretKey<InputKeyCont>,
|
||||
output_lwe_sk: &[LweSecretKey<OutputKeyCont>],
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
noise_distribution: NoiseDistribution,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) -> CmLwePackingKeyOwned<Scalar>
|
||||
where
|
||||
Scalar: UnsignedTorus + RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
NoiseDistribution: Distribution,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
OutputKeyCont: Container<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let mut new_lwe_keyswitch_key = CmLwePackingKeyOwned::new(
|
||||
Scalar::ZERO,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_sk.lwe_dimension(),
|
||||
output_lwe_sk[0].lwe_dimension(),
|
||||
CmDimension(output_lwe_sk.len()),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
generate_cm_lwe_packing_key(
|
||||
input_lwe_sk,
|
||||
output_lwe_sk,
|
||||
&mut new_lwe_keyswitch_key,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
|
||||
new_lwe_keyswitch_key
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
//! Module containing primitives pertaining to the `CommonMask LWE programmable
|
||||
//! bootstrap` using 64 bits FFT for polynomial multiplication.
|
||||
|
||||
use crate::core_crypto::commons::computation_buffers::ComputationBuffers;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::fft_impl::fft64::math::fft::{Fft, FftView};
|
||||
use dyn_stack::PodStack;
|
||||
use tfhe_fft::c64;
|
||||
|
||||
pub fn programmable_bootstrap_cm_lwe_ciphertext<
|
||||
InputScalar,
|
||||
OutputScalar,
|
||||
InputCont,
|
||||
OutputCont,
|
||||
AccCont,
|
||||
KeyCont,
|
||||
>(
|
||||
input: &CmLweCiphertext<InputCont>,
|
||||
output: &mut CmLweCiphertext<OutputCont>,
|
||||
accumulator: &CmGlweCiphertext<AccCont>,
|
||||
fourier_bsk: &FourierCmLweBootstrapKey<KeyCont>,
|
||||
) where
|
||||
// CastInto required for PBS modulus switch which returns a usize
|
||||
InputScalar: UnsignedTorus + CastInto<usize>,
|
||||
OutputScalar: UnsignedTorus,
|
||||
InputCont: Container<Element = InputScalar>,
|
||||
OutputCont: ContainerMut<Element = OutputScalar>,
|
||||
AccCont: Container<Element = OutputScalar>,
|
||||
KeyCont: Container<Element = c64>,
|
||||
{
|
||||
assert!(
|
||||
input.ciphertext_modulus().is_power_of_two(),
|
||||
"This operation requires the input to have a power of two modulus."
|
||||
);
|
||||
assert_eq!(
|
||||
output.ciphertext_modulus(),
|
||||
accumulator.ciphertext_modulus()
|
||||
);
|
||||
|
||||
let mut buffers = ComputationBuffers::new();
|
||||
|
||||
let fft = Fft::new(fourier_bsk.polynomial_size());
|
||||
let fft = fft.as_view();
|
||||
|
||||
buffers.resize(
|
||||
cm_bootstrap_requirement::<OutputScalar>(
|
||||
fourier_bsk.glwe_dimension(),
|
||||
fourier_bsk.cm_dimension(),
|
||||
fourier_bsk.polynomial_size(),
|
||||
fft,
|
||||
)
|
||||
.unaligned_bytes_required(),
|
||||
);
|
||||
|
||||
let stack = buffers.stack();
|
||||
|
||||
cm_programmable_bootstrap_lwe_ciphertext_mem_optimized(
|
||||
input,
|
||||
output,
|
||||
accumulator,
|
||||
fourier_bsk,
|
||||
fft,
|
||||
stack,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn cm_programmable_bootstrap_lwe_ciphertext_mem_optimized<
|
||||
InputScalar,
|
||||
OutputScalar,
|
||||
InputCont,
|
||||
OutputCont,
|
||||
AccCont,
|
||||
KeyCont,
|
||||
>(
|
||||
input: &CmLweCiphertext<InputCont>,
|
||||
output: &mut CmLweCiphertext<OutputCont>,
|
||||
accumulator: &CmGlweCiphertext<AccCont>,
|
||||
fourier_bsk: &FourierCmLweBootstrapKey<KeyCont>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) where
|
||||
// CastInto required for PBS modulus switch which returns a usize
|
||||
InputScalar: UnsignedTorus + CastInto<usize>,
|
||||
OutputScalar: UnsignedTorus,
|
||||
InputCont: Container<Element = InputScalar>,
|
||||
OutputCont: ContainerMut<Element = OutputScalar>,
|
||||
AccCont: Container<Element = OutputScalar>,
|
||||
KeyCont: Container<Element = c64>,
|
||||
{
|
||||
assert_eq!(
|
||||
accumulator.ciphertext_modulus(),
|
||||
output.ciphertext_modulus(),
|
||||
"Mismatched moduli between accumulator ({:?}) and output ({:?})",
|
||||
accumulator.ciphertext_modulus(),
|
||||
output.ciphertext_modulus()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fourier_bsk.input_lwe_dimension(),
|
||||
input.lwe_dimension(),
|
||||
"Mismatched input LweDimension. \
|
||||
FourierLweBootstrapKey input LweDimension: {:?}, input CmLweCiphertext LweDimension {:?}.",
|
||||
fourier_bsk.input_lwe_dimension(),
|
||||
input.lwe_dimension(),
|
||||
);
|
||||
assert_eq!(
|
||||
fourier_bsk.output_lwe_dimension(),
|
||||
output.lwe_dimension(),
|
||||
"Mismatched output LweDimension. \
|
||||
FourierLweBootstrapKey input LweDimension: {:?}, input CmLweCiphertext LweDimension {:?}.",
|
||||
fourier_bsk.output_lwe_dimension(),
|
||||
output.lwe_dimension(),
|
||||
);
|
||||
assert_eq!(
|
||||
fourier_bsk.cm_dimension(),
|
||||
input.cm_dimension(),
|
||||
"Mismatched input CmDimension. \
|
||||
FourierLweBootstrapKey CmDimension: {:?}, input CmLweCiphertext CmDimension {:?}.",
|
||||
fourier_bsk.cm_dimension(),
|
||||
input.cm_dimension(),
|
||||
);
|
||||
assert_eq!(
|
||||
fourier_bsk.cm_dimension(),
|
||||
output.cm_dimension(),
|
||||
"Mismatched output CmDimension. \
|
||||
FourierLweBootstrapKey CmDimension: {:?}, output CmLweCiphertext CmDimension {:?}.",
|
||||
fourier_bsk.cm_dimension(),
|
||||
output.cm_dimension(),
|
||||
);
|
||||
|
||||
cm_bootstrap(
|
||||
fourier_bsk.as_view(),
|
||||
output.as_mut_view(),
|
||||
input.as_view(),
|
||||
accumulator.as_view(),
|
||||
fft,
|
||||
stack,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
pub mod cm_fft64;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::allocate_and_trivially_encrypt_new_cm_glwe_ciphertext;
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
|
||||
// TODO: add support for one function per cm slot
|
||||
pub fn cm_generate_programmable_bootstrap_glwe_lut<F, Scalar: UnsignedTorus + CastFrom<usize>>(
|
||||
polynomial_size: PolynomialSize,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
message_modulus: usize,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
delta: Scalar,
|
||||
f: F,
|
||||
) -> CmGlweCiphertextOwned<Scalar>
|
||||
where
|
||||
F: Fn(Scalar) -> Scalar,
|
||||
{
|
||||
// N/(p/2) = size of each block, to correct noise from the input we introduce the
|
||||
// notion of box, which manages redundancy to yield a denoised value
|
||||
// for several noisy values around a true input value.
|
||||
let box_size = polynomial_size.0 / message_modulus;
|
||||
|
||||
let accumulator_scalar = build_lut(
|
||||
polynomial_size,
|
||||
message_modulus,
|
||||
ciphertext_modulus,
|
||||
delta,
|
||||
f,
|
||||
box_size,
|
||||
);
|
||||
|
||||
let accumulator_scalar = (0..cm_dimension.0)
|
||||
.flat_map(|_| accumulator_scalar.iter())
|
||||
.copied()
|
||||
.collect_vec();
|
||||
|
||||
let accumulator_plaintext = PlaintextList::from_container(accumulator_scalar);
|
||||
|
||||
allocate_and_trivially_encrypt_new_cm_glwe_ciphertext(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
&accumulator_plaintext,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
fn build_lut<F, Scalar: UnsignedTorus + CastFrom<usize>>(
|
||||
polynomial_size: PolynomialSize,
|
||||
message_modulus: usize,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
delta: Scalar,
|
||||
f: F,
|
||||
box_size: usize,
|
||||
) -> Vec<Scalar>
|
||||
where
|
||||
F: Fn(Scalar) -> Scalar,
|
||||
{
|
||||
// Create the accumulator
|
||||
let mut accumulator_scalar = vec![Scalar::ZERO; polynomial_size.0];
|
||||
|
||||
// Fill each box with the encoded denoised value
|
||||
for i in 0..message_modulus {
|
||||
let index = i * box_size;
|
||||
accumulator_scalar[index..index + box_size]
|
||||
.iter_mut()
|
||||
.for_each(|a| *a = f(Scalar::cast_from(i)) * delta);
|
||||
}
|
||||
|
||||
let half_box_size = box_size / 2;
|
||||
|
||||
if ciphertext_modulus.is_compatible_with_native_modulus() {
|
||||
// Negate the first half_box_size coefficients to manage negacyclicity and rotate
|
||||
for a_i in accumulator_scalar[0..half_box_size].iter_mut() {
|
||||
*a_i = (*a_i).wrapping_neg();
|
||||
}
|
||||
} else {
|
||||
let modulus: Scalar = ciphertext_modulus.get_custom_modulus().cast_into();
|
||||
for a_i in accumulator_scalar[0..half_box_size].iter_mut() {
|
||||
*a_i = (*a_i).wrapping_neg_custom_mod(modulus);
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate the accumulator
|
||||
accumulator_scalar.rotate_left(half_box_size);
|
||||
accumulator_scalar
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
use crate::core_crypto::commons::dispersion::Variance;
|
||||
use crate::core_crypto::commons::parameters::{NoiseEstimationMeasureBound, RSigmaFactor};
|
||||
use crate::core_crypto::commons::traits::{Container, ContainerMut, UnsignedInteger};
|
||||
use crate::core_crypto::experimental::entities::{CmLweCiphertext, CmLweCiphertextList};
|
||||
use crate::core_crypto::experimental::prelude::cm_lwe_ciphertext_add_assign;
|
||||
use crate::core_crypto::prelude::modulus_switch_noise_reduction::{
|
||||
measure_modulus_switch_noise_estimation_for_binary_key, Candidate, CandidateResult,
|
||||
};
|
||||
use crate::core_crypto::prelude::{
|
||||
CiphertextModulus, CiphertextModulusLog, ContiguousEntityContainer, DispersionParameter,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
pub fn choose_candidate_to_improve_modulus_switch_noise_for_binary_key<Scalar, C1, C2>(
|
||||
lwe: &CmLweCiphertext<C1>,
|
||||
encryptions_of_zero: &CmLweCiphertextList<C2>,
|
||||
r_sigma_factor: RSigmaFactor,
|
||||
bound: NoiseEstimationMeasureBound,
|
||||
input_variance: Variance,
|
||||
log_modulus: CiphertextModulusLog,
|
||||
) -> CandidateResult
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
C1: Container<Element = Scalar>,
|
||||
C2: Container<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
lwe.lwe_dimension(),
|
||||
encryptions_of_zero.lwe_dimension(),
|
||||
"input lwe size (={:?}) != encryptions of zero lwe size (={:?})",
|
||||
lwe.lwe_dimension(),
|
||||
encryptions_of_zero.lwe_dimension(),
|
||||
);
|
||||
assert_eq!(
|
||||
lwe.ciphertext_modulus(),
|
||||
encryptions_of_zero.ciphertext_modulus(),
|
||||
"input ciphertext_modulus (={:?}) != encryptions of zero ciphertext_modulus (={:?})",
|
||||
lwe.ciphertext_modulus(),
|
||||
encryptions_of_zero.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
assert_ne!(
|
||||
encryptions_of_zero.cm_lwe_ciphertext_count().0,
|
||||
0,
|
||||
"Expected at least one encryption of zero"
|
||||
);
|
||||
assert_eq!(
|
||||
lwe.ciphertext_modulus(),
|
||||
CiphertextModulus::new_native(),
|
||||
"Non native modulus are not supported, got {}",
|
||||
lwe.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
let modulus = lwe.ciphertext_modulus().raw_modulus_float();
|
||||
|
||||
let input_variance = input_variance.get_modular_variance(modulus);
|
||||
|
||||
let mask = lwe.get_mask();
|
||||
|
||||
let mask = mask.as_ref();
|
||||
|
||||
let base_measure = measure_modulus_switch_noise_estimation_for_binary_key(
|
||||
r_sigma_factor,
|
||||
input_variance,
|
||||
log_modulus,
|
||||
mask.iter().copied(),
|
||||
Scalar::ZERO,
|
||||
);
|
||||
|
||||
let mut best_candidate = Candidate::NoAddition;
|
||||
let mut best_measure = base_measure;
|
||||
|
||||
if base_measure <= bound.0 {
|
||||
return CandidateResult::SatisfyingBound(best_candidate);
|
||||
}
|
||||
|
||||
for (index, encryption_of_zero) in encryptions_of_zero.iter().enumerate() {
|
||||
let encryption_of_zero_mask = encryption_of_zero.get_mask();
|
||||
|
||||
let encryption_of_zero_mask = encryption_of_zero_mask.as_ref();
|
||||
|
||||
let mask_sum = mask
|
||||
.iter()
|
||||
.zip_eq(encryption_of_zero_mask.iter())
|
||||
.map(|(a, b)| a.wrapping_add(*b));
|
||||
|
||||
let measure = measure_modulus_switch_noise_estimation_for_binary_key(
|
||||
r_sigma_factor,
|
||||
input_variance,
|
||||
log_modulus,
|
||||
mask_sum,
|
||||
Scalar::ZERO,
|
||||
);
|
||||
|
||||
if measure < best_measure {
|
||||
best_measure = measure;
|
||||
best_candidate = Candidate::AddEncryptionOfZero { index };
|
||||
}
|
||||
|
||||
if measure <= bound.0 {
|
||||
return CandidateResult::SatisfyingBound(best_candidate);
|
||||
}
|
||||
}
|
||||
|
||||
CandidateResult::BestNotSatisfyingBound(best_candidate)
|
||||
}
|
||||
|
||||
pub fn improve_lwe_ciphertext_modulus_switch_noise_for_binary_key_cm<Scalar, C1, C2>(
|
||||
lwe: &mut CmLweCiphertext<C1>,
|
||||
encryptions_of_zero: &CmLweCiphertextList<C2>,
|
||||
r_sigma_factor: RSigmaFactor,
|
||||
bound: NoiseEstimationMeasureBound,
|
||||
input_variance: Variance,
|
||||
log_modulus: CiphertextModulusLog,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
C1: ContainerMut<Element = Scalar>,
|
||||
C2: Container<Element = Scalar>,
|
||||
{
|
||||
let candidate = choose_candidate_to_improve_modulus_switch_noise_for_binary_key(
|
||||
lwe,
|
||||
encryptions_of_zero,
|
||||
r_sigma_factor,
|
||||
bound,
|
||||
input_variance,
|
||||
log_modulus,
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
assert!(
|
||||
matches!(candidate, CandidateResult::SatisfyingBound(_)),
|
||||
"MS noise reduction bound not reached for any candidate"
|
||||
);
|
||||
|
||||
let candidate = match candidate {
|
||||
CandidateResult::SatisfyingBound(candidate) => candidate,
|
||||
CandidateResult::BestNotSatisfyingBound(candidate) => candidate,
|
||||
};
|
||||
|
||||
match candidate {
|
||||
Candidate::NoAddition => {}
|
||||
Candidate::AddEncryptionOfZero { index } => {
|
||||
let encryption_of_zero = encryptions_of_zero.get(index);
|
||||
|
||||
cm_lwe_ciphertext_add_assign(lwe, &encryption_of_zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,31 @@
|
||||
pub mod cm_bootstrap;
|
||||
pub mod cm_ggsw_encryption;
|
||||
pub mod cm_ggsw_external_product;
|
||||
pub mod cm_glwe_encryption;
|
||||
pub mod cm_glwe_sample_extraction;
|
||||
pub mod cm_lwe_bootstrap_key_conversion;
|
||||
pub mod cm_lwe_bootstrap_key_generation;
|
||||
pub mod cm_lwe_encryption;
|
||||
pub mod cm_lwe_keyswitch;
|
||||
pub mod cm_lwe_keyswitch_key_generation;
|
||||
pub mod cm_lwe_linear_algebra;
|
||||
pub mod cm_lwe_packing;
|
||||
pub mod cm_lwe_packing_key_generation;
|
||||
pub mod cm_lwe_programmable_bootstrapping;
|
||||
pub mod cm_modulus_switch_noise_reduction;
|
||||
pub mod cm_params;
|
||||
|
||||
pub use cm_bootstrap::*;
|
||||
pub use cm_ggsw_encryption::*;
|
||||
pub use cm_ggsw_external_product::*;
|
||||
pub use cm_glwe_encryption::*;
|
||||
pub use cm_glwe_sample_extraction::*;
|
||||
pub use cm_lwe_bootstrap_key_conversion::*;
|
||||
pub use cm_lwe_bootstrap_key_generation::*;
|
||||
pub use cm_lwe_encryption::*;
|
||||
pub use cm_lwe_keyswitch::*;
|
||||
pub use cm_lwe_linear_algebra::*;
|
||||
pub use cm_lwe_packing::*;
|
||||
pub use cm_lwe_packing_key_generation::*;
|
||||
pub use cm_lwe_programmable_bootstrapping::*;
|
||||
pub use cm_params::*;
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod common_mask_algorithms;
|
||||
pub mod glwe_fast_keyswitch;
|
||||
pub mod glwe_partial_sample_extraction;
|
||||
pub mod lwe_shrinking_keyswitch;
|
||||
@@ -8,6 +9,7 @@ pub mod pseudo_ggsw_encryption;
|
||||
pub mod shared_glwe_secret_key_generation;
|
||||
pub mod shared_lwe_secret_key_generation;
|
||||
|
||||
pub use common_mask_algorithms::*;
|
||||
pub use glwe_fast_keyswitch::*;
|
||||
pub use glwe_partial_sample_extraction::*;
|
||||
pub use lwe_shrinking_keyswitch::*;
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
|
||||
use crate::core_crypto::commons::computation_buffers::ComputationBuffers;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::entities::fourier_pseudo_ggsw_ciphertext::{
|
||||
fill_with_forward_fourier_scratch, PseudoFourierGgswCiphertext,
|
||||
};
|
||||
use crate::core_crypto::experimental::entities::fourier_pseudo_ggsw_ciphertext::PseudoFourierGgswCiphertext;
|
||||
use crate::core_crypto::experimental::entities::pseudo_ggsw_ciphertext::PseudoGgswCiphertext;
|
||||
use crate::core_crypto::fft_impl::fft64::math::fft::{Fft, FftView};
|
||||
use dyn_stack::{PodStack, StackReq};
|
||||
@@ -67,5 +65,5 @@ pub fn convert_standard_pseudo_ggsw_ciphertext_to_fourier_mem_optimized<
|
||||
pub fn convert_standard_pseudo_ggsw_ciphertext_to_fourier_mem_optimized_requirement(
|
||||
fft: FftView<'_>,
|
||||
) -> StackReq {
|
||||
fill_with_forward_fourier_scratch(fft)
|
||||
fft.forward_scratch()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
use crate::core_crypto::experimental::prelude::{
|
||||
allocate_and_encrypt_new_cm_lwe_ciphertext, CmDimension,
|
||||
};
|
||||
use crate::core_crypto::prelude::*;
|
||||
use itertools::Itertools;
|
||||
|
||||
#[test]
|
||||
fn cm_encryption() {
|
||||
let lwe_dimension = LweDimension(742);
|
||||
let lwe_noise_distribution =
|
||||
Gaussian::from_dispersion_parameter(StandardDev(0.0000000007069849454709433), 0.);
|
||||
let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
|
||||
let cm_dimension = CmDimension(10);
|
||||
|
||||
// Create the PRNG
|
||||
let mut seeder = new_seeder();
|
||||
let seeder = seeder.as_mut();
|
||||
let mut encryption_generator =
|
||||
EncryptionRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed(), seeder);
|
||||
let mut secret_generator = SecretRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed());
|
||||
|
||||
// Create the LweSecretKey
|
||||
let lwe_secret_keys = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
// Create the plaintext
|
||||
|
||||
let plaintext = (0..cm_dimension.0).map(|i| (i as u64) << 55).collect_vec();
|
||||
|
||||
let plaintext_list = PlaintextList::from_container(plaintext.as_slice());
|
||||
|
||||
// Create a new LweCiphertext
|
||||
let lwe = allocate_and_encrypt_new_cm_lwe_ciphertext(
|
||||
&lwe_secret_keys,
|
||||
&plaintext_list,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
for i in 0..cm_dimension.0 {
|
||||
let decrypted_plaintext =
|
||||
decrypt_lwe_ciphertext(&lwe_secret_keys[i], &lwe.extract_lwe_ciphertext(i));
|
||||
|
||||
// Round and remove encoding
|
||||
// First create a decomposer working on the high 4 bits corresponding to our encoding.
|
||||
let decomposer = SignedDecomposer::new(DecompositionBaseLog(9), DecompositionLevelCount(1));
|
||||
|
||||
let rounded = decomposer.closest_representable(decrypted_plaintext.0);
|
||||
|
||||
// Remove the encoding
|
||||
let cleartext = rounded >> 55;
|
||||
|
||||
// Check we recovered the original message
|
||||
assert_eq!(cleartext, plaintext[i] >> 55);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
use super::super::cm_params::{CmApParams, CM_PARAM_2_2_MINUS_64};
|
||||
use super::super::*;
|
||||
use crate::core_crypto::experimental::prelude::cm_lwe_keyswitch_key_generation::allocate_and_generate_new_cm_lwe_keyswitch_key;
|
||||
use crate::core_crypto::prelude::misc::check_encrypted_content_respects_mod;
|
||||
use crate::core_crypto::prelude::*;
|
||||
use itertools::Itertools;
|
||||
|
||||
const NB_TESTS: usize = 10;
|
||||
|
||||
#[test]
|
||||
fn cm_keyswitch() {
|
||||
cm_keyswitch_generic(&CM_PARAM_2_2_MINUS_64);
|
||||
}
|
||||
|
||||
fn cm_keyswitch_generic(params: &CmApParams) {
|
||||
let in_lwe_dimension = params
|
||||
.glwe_dimension
|
||||
.to_equivalent_lwe_dimension(params.polynomial_size);
|
||||
|
||||
let out_lwe_dimension = params.lwe_dimension;
|
||||
|
||||
let in_lwe_noise_distribution = params.glwe_noise_distribution;
|
||||
|
||||
let key_lwe_noise_distribution = params.lwe_noise_distribution;
|
||||
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
let ks_decomp_base_log = params.base_log_ks;
|
||||
let ks_decomp_level_count = params.level_ks;
|
||||
|
||||
let cm_dimension = params.cm_dimension;
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let msg_modulus = 1 << params.precision;
|
||||
let mut msg = msg_modulus;
|
||||
let delta: u64 = encoding_with_padding / msg_modulus;
|
||||
|
||||
for _ in 0..NB_TESTS {
|
||||
let lwe_sks_in = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_generate_new_binary_lwe_secret_key(
|
||||
in_lwe_dimension,
|
||||
&mut rsc.secret_random_generator,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
let lwe_sks_out = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_generate_new_binary_lwe_secret_key(
|
||||
out_lwe_dimension,
|
||||
&mut rsc.secret_random_generator,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
let cm_lwe_keyswitch_key = allocate_and_generate_new_cm_lwe_keyswitch_key(
|
||||
&lwe_sks_in,
|
||||
&lwe_sks_out,
|
||||
cm_dimension,
|
||||
ks_decomp_base_log,
|
||||
ks_decomp_level_count,
|
||||
key_lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&cm_lwe_keyswitch_key,
|
||||
ciphertext_modulus
|
||||
));
|
||||
while msg != 0 {
|
||||
msg = msg.wrapping_sub(1);
|
||||
|
||||
let pts = PlaintextList::from_container(
|
||||
(0..cm_dimension.0).map(|_| msg * delta).collect_vec(),
|
||||
);
|
||||
|
||||
let ct = allocate_and_encrypt_new_cm_lwe_ciphertext(
|
||||
&lwe_sks_in,
|
||||
&pts,
|
||||
in_lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
let mut output_ct =
|
||||
CmLweCiphertext::new(0, out_lwe_dimension, cm_dimension, ciphertext_modulus);
|
||||
|
||||
cm_keyswitch_lwe_ciphertext(&cm_lwe_keyswitch_key, &ct, &mut output_ct);
|
||||
|
||||
for (i, lwe_sk_out) in lwe_sks_out.iter().enumerate() {
|
||||
let output_ct = output_ct.extract_lwe_ciphertext(i);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&output_ct,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let decrypted = decrypt_lwe_ciphertext(lwe_sk_out, &output_ct);
|
||||
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
|
||||
assert_eq!(msg, decoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::prelude::misc::check_encrypted_content_respects_mod;
|
||||
use crate::core_crypto::prelude::test::{
|
||||
get_encoding_with_padding, round_decode, ClassicTestParams, TestResources,
|
||||
TEST_PARAMS_4_BITS_NATIVE_U64,
|
||||
};
|
||||
use crate::core_crypto::prelude::*;
|
||||
use itertools::Itertools;
|
||||
|
||||
const NB_TESTS: usize = 10;
|
||||
|
||||
#[test]
|
||||
fn cm_packing() {
|
||||
cm_packing_generic(TEST_PARAMS_4_BITS_NATIVE_U64);
|
||||
}
|
||||
|
||||
fn cm_packing_generic(params: ClassicTestParams<u64>) {
|
||||
let in_lwe_dimension = LweDimension(13);
|
||||
|
||||
let out_lwe_dimension = LweDimension(10);
|
||||
|
||||
let lwe_noise_distribution = params.lwe_noise_distribution;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
let message_modulus_log = params.message_modulus_log;
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
let ks_decomp_base_log = params.ks_base_log;
|
||||
let ks_decomp_level_count = params.ks_level;
|
||||
|
||||
let cm_dimension = CmDimension(10);
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let msg_modulus = 1 << message_modulus_log.0;
|
||||
let mut msg = msg_modulus;
|
||||
let delta: u64 = encoding_with_padding / msg_modulus;
|
||||
|
||||
for _ in 0..NB_TESTS {
|
||||
let lwe_sk_in = allocate_and_generate_new_binary_lwe_secret_key(
|
||||
in_lwe_dimension,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
|
||||
let lwe_sks_out = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_generate_new_binary_lwe_secret_key(
|
||||
out_lwe_dimension,
|
||||
&mut rsc.secret_random_generator,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
let packing_key = allocate_and_generate_new_cm_lwe_packing_key(
|
||||
&lwe_sk_in,
|
||||
&lwe_sks_out,
|
||||
ks_decomp_base_log,
|
||||
ks_decomp_level_count,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&packing_key,
|
||||
ciphertext_modulus
|
||||
));
|
||||
while msg != 0 {
|
||||
msg = msg.wrapping_sub(1);
|
||||
|
||||
let ct = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_encrypt_new_lwe_ciphertext(
|
||||
&lwe_sk_in,
|
||||
Plaintext(msg * delta),
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
let mut output_ct =
|
||||
CmLweCiphertext::new(0, out_lwe_dimension, cm_dimension, ciphertext_modulus);
|
||||
|
||||
pack_lwe_ciphertexts_into_cm(&packing_key, &ct, &mut output_ct);
|
||||
|
||||
for (i, lwe_sk_out) in lwe_sks_out.iter().enumerate() {
|
||||
let output_ct = output_ct.extract_lwe_ciphertext(i);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&output_ct,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let decrypted = decrypt_lwe_ciphertext(lwe_sk_out, &output_ct);
|
||||
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
|
||||
assert_eq!(msg, decoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
use super::super::cm_lwe_keyswitch_key_generation::allocate_and_generate_new_cm_lwe_keyswitch_key;
|
||||
use super::super::cm_params::{CmApParams, CM_PARAM_2_2_MINUS_64};
|
||||
use super::super::*;
|
||||
use crate::core_crypto::commons::test_tools::{
|
||||
check_both_ratio_under, normality_test_f64, random_uint_between, variance,
|
||||
};
|
||||
use crate::core_crypto::experimental::prelude::cm_modulus_switch_noise_reduction::improve_lwe_ciphertext_modulus_switch_noise_for_binary_key_cm;
|
||||
use crate::core_crypto::prelude::misc::torus_modular_diff;
|
||||
use crate::core_crypto::prelude::*;
|
||||
use cm_fft64::programmable_bootstrap_cm_lwe_ciphertext;
|
||||
use itertools::Itertools;
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
|
||||
#[cfg(not(tarpaulin))]
|
||||
const NB_TESTS: usize = 100;
|
||||
#[cfg(tarpaulin)]
|
||||
const NB_TESTS: usize = 1;
|
||||
|
||||
#[test]
|
||||
fn test_cm_pbs_lut_application_test_param() {
|
||||
test_cm_pbs_lut_application(&CM_PARAM_2_2_MINUS_64)
|
||||
}
|
||||
|
||||
fn test_cm_pbs_lut_application(params: &CmApParams) {
|
||||
let cm_dimension = params.cm_dimension;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
|
||||
let lwe_noise_distribution = params.lwe_noise_distribution;
|
||||
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
let glwe_dimension = params.glwe_dimension;
|
||||
let polynomial_size = params.polynomial_size;
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let msg_modulus = 1u64 << params.precision;
|
||||
let delta = encoding_with_padding / msg_modulus;
|
||||
|
||||
let CmBootstrapKeys {
|
||||
small_lwe_sk,
|
||||
big_lwe_sk,
|
||||
bsk,
|
||||
fbsk,
|
||||
} = generate_cm_pbs_keys(
|
||||
params,
|
||||
&mut rsc.encryption_random_generator,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
drop(bsk);
|
||||
|
||||
for _ in 0..NB_TESTS {
|
||||
let random_lut: Vec<u64> = (0..msg_modulus)
|
||||
.map(|_| random_uint_between(0u64..msg_modulus))
|
||||
.collect();
|
||||
let f = |x: u64| random_lut[x as usize];
|
||||
|
||||
let accumulator = cm_generate_programmable_bootstrap_glwe_lut(
|
||||
polynomial_size,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
msg_modulus.cast_into(),
|
||||
ciphertext_modulus,
|
||||
delta,
|
||||
f,
|
||||
);
|
||||
|
||||
let msgs: Vec<u64> = (0..cm_dimension.0)
|
||||
.map(|_| random_uint_between(0u64..msg_modulus))
|
||||
.collect();
|
||||
|
||||
let plaintexts =
|
||||
PlaintextList::from_container(msgs.iter().map(|&m| m * delta).collect_vec());
|
||||
|
||||
let ct = allocate_and_encrypt_new_cm_lwe_ciphertext(
|
||||
&small_lwe_sk,
|
||||
&plaintexts,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
let mut output_ct = CmLweCiphertext::new(
|
||||
0u64,
|
||||
fbsk.output_lwe_dimension(),
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
programmable_bootstrap_cm_lwe_ciphertext(&ct, &mut output_ct, &accumulator, &fbsk);
|
||||
|
||||
let decrypted = decrypt_cm_lwe_ciphertext(&big_lwe_sk, &output_ct);
|
||||
|
||||
for (decrypted, &input) in decrypted.iter().zip(msgs.iter()) {
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
assert_eq!(decoded, f(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cm_pbs_ap_sequence_test_param() {
|
||||
test_cm_pbs_ap_sequence(&CM_PARAM_2_2_MINUS_64)
|
||||
}
|
||||
|
||||
fn test_cm_pbs_ap_sequence(params: &CmApParams) {
|
||||
let cm_dimension = params.cm_dimension;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
|
||||
let lwe_noise_distribution = params.lwe_noise_distribution;
|
||||
|
||||
let glwe_noise_distribution = params.glwe_noise_distribution;
|
||||
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
let glwe_dimension = params.glwe_dimension;
|
||||
let polynomial_size = params.polynomial_size;
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let msg_modulus = 1u64 << params.precision;
|
||||
let delta = encoding_with_padding / msg_modulus;
|
||||
|
||||
let f = |x| x;
|
||||
|
||||
let accumulator = cm_generate_programmable_bootstrap_glwe_lut(
|
||||
polynomial_size,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
msg_modulus.cast_into(),
|
||||
ciphertext_modulus,
|
||||
delta,
|
||||
f,
|
||||
);
|
||||
|
||||
let CmBootstrapKeys {
|
||||
small_lwe_sk,
|
||||
big_lwe_sk,
|
||||
bsk,
|
||||
fbsk,
|
||||
} = generate_cm_pbs_keys(
|
||||
params,
|
||||
&mut rsc.encryption_random_generator,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
drop(bsk);
|
||||
|
||||
let cm_lwe_keyswitch_key = allocate_and_generate_new_cm_lwe_keyswitch_key(
|
||||
&big_lwe_sk,
|
||||
&small_lwe_sk,
|
||||
cm_dimension,
|
||||
params.base_log_ks,
|
||||
params.level_ks,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
let mut ct_big;
|
||||
|
||||
let mut ct_small = CmLweCiphertext::new(
|
||||
0u64,
|
||||
small_lwe_sk[0].lwe_dimension(),
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let max_nb_zeros_n = params.max_nb_zeros_n;
|
||||
|
||||
let mut encryptions_of_zero = CmLweCiphertextList::new(
|
||||
0,
|
||||
params.lwe_dimension,
|
||||
cm_dimension,
|
||||
max_nb_zeros_n,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let plaintext_list = PlaintextList::new(0, PlaintextCount(cm_dimension.0));
|
||||
|
||||
let plaintext_lists: Vec<_> = (0..max_nb_zeros_n.0)
|
||||
.map(|_| plaintext_list.clone())
|
||||
.collect();
|
||||
|
||||
encrypt_cm_lwe_ciphertext_list(
|
||||
&small_lwe_sk,
|
||||
&mut encryptions_of_zero,
|
||||
&plaintext_lists,
|
||||
lwe_noise_distribution,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
let log_modulus = polynomial_size.to_blind_rotation_input_modulus_log();
|
||||
|
||||
for _ in 0..NB_TESTS {
|
||||
// Use zero messages so that multiplication by nu does not change the encrypted value
|
||||
let msgs: Vec<u64> = vec![0u64; cm_dimension.0];
|
||||
|
||||
let plaintexts =
|
||||
PlaintextList::from_container(msgs.iter().map(|&m| m * delta).collect_vec());
|
||||
|
||||
ct_big = allocate_and_encrypt_new_cm_lwe_ciphertext(
|
||||
&big_lwe_sk,
|
||||
&plaintexts,
|
||||
glwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
// depth to bootstrap non clean cts
|
||||
for _ in 0..3 {
|
||||
cm_lwe_ciphertext_cleartext_mul_assign(&mut ct_big, Cleartext(params.nu as u64));
|
||||
|
||||
cm_keyswitch_lwe_ciphertext(&cm_lwe_keyswitch_key, &ct_big, &mut ct_small);
|
||||
|
||||
improve_lwe_ciphertext_modulus_switch_noise_for_binary_key_cm(
|
||||
&mut ct_small,
|
||||
&encryptions_of_zero,
|
||||
params.r_sigma_factor_n,
|
||||
params.ms_bound_n,
|
||||
params.ms_input_variance_n,
|
||||
log_modulus,
|
||||
);
|
||||
|
||||
programmable_bootstrap_cm_lwe_ciphertext(&ct_small, &mut ct_big, &accumulator, &fbsk);
|
||||
|
||||
let decrypted = decrypt_cm_lwe_ciphertext(&big_lwe_sk, &ct_big);
|
||||
|
||||
for (decrypted, &input) in decrypted.iter().zip(msgs.iter()) {
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
assert_eq!(decoded, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cm_pbs_noise_analysis_test_param() {
|
||||
test_cm_pbs_noise_analysis(&CM_PARAM_2_2_MINUS_64)
|
||||
}
|
||||
|
||||
fn test_cm_pbs_noise_analysis(params: &CmApParams) {
|
||||
let cm_dimension = params.cm_dimension;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
|
||||
let small_lwe_noise_distribution = params.lwe_noise_distribution;
|
||||
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
let glwe_dimension = params.glwe_dimension;
|
||||
let polynomial_size = params.polynomial_size;
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let msg_modulus = 1u64 << params.precision;
|
||||
let delta = encoding_with_padding / msg_modulus;
|
||||
|
||||
let f = |x| x;
|
||||
|
||||
let accumulator = cm_generate_programmable_bootstrap_glwe_lut(
|
||||
polynomial_size,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
msg_modulus.cast_into(),
|
||||
ciphertext_modulus,
|
||||
delta,
|
||||
f,
|
||||
);
|
||||
|
||||
let CmBootstrapKeys {
|
||||
small_lwe_sk,
|
||||
big_lwe_sk,
|
||||
bsk,
|
||||
fbsk,
|
||||
} = generate_cm_pbs_keys(
|
||||
params,
|
||||
&mut rsc.encryption_random_generator,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
drop(bsk);
|
||||
|
||||
let noises: Vec<Vec<f64>> = (0..2000)
|
||||
.into_par_iter()
|
||||
.map(move |i| {
|
||||
let msg = i % msg_modulus;
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let plaintexts = PlaintextList::from_container(
|
||||
(0..cm_dimension.0).map(|_| msg * delta).collect_vec(),
|
||||
);
|
||||
|
||||
let ct = allocate_and_encrypt_new_cm_lwe_ciphertext(
|
||||
&small_lwe_sk,
|
||||
&plaintexts,
|
||||
small_lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
let mut output_ct = CmLweCiphertext::new(
|
||||
0u64,
|
||||
fbsk.output_lwe_dimension(),
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
programmable_bootstrap_cm_lwe_ciphertext(&ct, &mut output_ct, &accumulator, &fbsk);
|
||||
|
||||
let decrypted = decrypt_cm_lwe_ciphertext(&big_lwe_sk, &output_ct);
|
||||
|
||||
let mut noises = vec![];
|
||||
for (decrypted, plaintext) in decrypted.iter().zip(plaintexts.iter()) {
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
|
||||
assert_eq!(f(plaintext.0 / delta), decoded);
|
||||
|
||||
let noise = torus_modular_diff(
|
||||
decrypted.0,
|
||||
f(plaintext.0 / delta) * delta,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
noises.push(noise);
|
||||
}
|
||||
noises
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Transpose from [[pbs0_slot0, pbs0_slot1, ...], [pbs1_slot0, pbs1_slot1, ...], ...]
|
||||
// into [[pbs0_slot0, pbs1_slot0, ...], [pbs0_slot1, pbs1_slot1, ...], ...]
|
||||
// To analyse noise for each slot independently
|
||||
let noises_separated: Vec<Vec<f64>> = (0..cm_dimension.0)
|
||||
.map(|i| noises.iter().map(|list| list[i]).collect_vec())
|
||||
.collect_vec();
|
||||
|
||||
let expected_variance = cm_pbs_variance_132_bits_security_gaussian_impl(
|
||||
params.lwe_dimension.0 as f64,
|
||||
params.glwe_dimension.0 as f64,
|
||||
params.polynomial_size.0 as f64,
|
||||
(1_u64 << params.base_log_bs.0) as f64,
|
||||
params.level_bs.0 as f64,
|
||||
2_f64.powi(64),
|
||||
);
|
||||
|
||||
println!("expected_variance: {expected_variance:e}");
|
||||
|
||||
for noises in &noises_separated {
|
||||
let variance = variance(noises);
|
||||
|
||||
println!("variance: {:?}", variance.0);
|
||||
|
||||
let test_result = normality_test_f64(noises, 0.001);
|
||||
|
||||
assert!(test_result.null_hypothesis_is_valid);
|
||||
|
||||
println!("p_value normality: {}", test_result.p_value);
|
||||
|
||||
// TODO: confidence interval to reconsider
|
||||
assert!(check_both_ratio_under(expected_variance, variance.0, 1.1));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_pbs_variance_132_bits_security_gaussian_impl(
|
||||
input_lwe_dimension: f64,
|
||||
output_glwe_dimension: f64,
|
||||
output_polynomial_size: f64,
|
||||
decomposition_base: f64,
|
||||
decomposition_level_count: f64,
|
||||
modulus: f64,
|
||||
) -> f64 {
|
||||
input_lwe_dimension
|
||||
* (2.06537277069845e-33
|
||||
* decomposition_base.powf(2.0)
|
||||
* decomposition_level_count
|
||||
* output_polynomial_size.powf(2.0)
|
||||
* (output_glwe_dimension + 2.0)
|
||||
+ decomposition_level_count
|
||||
* output_polynomial_size
|
||||
* ((-0.0497829131652661 * output_glwe_dimension * output_polynomial_size
|
||||
+ 5.31469187675068)
|
||||
.exp2()
|
||||
+ 16.0 * modulus.powf(-2.0))
|
||||
* ((1_f64 / 12.0) * decomposition_base.powf(2.0) + 0.166666666666667)
|
||||
* (output_glwe_dimension + 2.0)
|
||||
+ (1_f64 / 12.0) * modulus.powf(-2.0)
|
||||
+ (1_f64 / 2.0)
|
||||
* output_glwe_dimension
|
||||
* output_polynomial_size
|
||||
* (0.0208333333333333 * modulus.powf(-2.0)
|
||||
+ 0.0416666666666667
|
||||
* decomposition_base.powf(-2.0 * decomposition_level_count))
|
||||
+ (1_f64 / 24.0) * decomposition_base.powf(-2.0 * decomposition_level_count))
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
mod cm_lwe_keyswitch;
|
||||
mod cm_lwe_packing;
|
||||
mod cm_lwe_programmable_bootstrapping;
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::core_crypto::algorithms::test::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
|
||||
mod cm_lwe_encryption;
|
||||
mod common_mask;
|
||||
mod lwe_fast_keyswitch;
|
||||
mod lwe_stair_keyswitch;
|
||||
|
||||
@@ -43,3 +43,11 @@ impl crate::core_crypto::commons::parameters::LweDimension {
|
||||
LweSecretKeyUnsharedCoefCount(self.0 - shared_coef_count.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// The number of ciphertexts in an lwe ciphertext list.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct CmLweCiphertextCount(pub usize);
|
||||
|
||||
/// The number of scalar in an LWE mask, or the length of an LWE secret key.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
|
||||
pub struct CmDimension(pub usize);
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
use crate::core_crypto::commons::parameters::{
|
||||
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
|
||||
};
|
||||
use crate::core_crypto::commons::traits::{Container, IntoContainerOwned, Split};
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::fft_impl::fft64::math::fft::{
|
||||
par_convert_polynomials_list_to_fourier, FftView, FourierPolynomialList,
|
||||
};
|
||||
use crate::core_crypto::prelude::{ContiguousEntityContainer, UnsignedTorus};
|
||||
use aligned_vec::{avec, ABox};
|
||||
use dyn_stack::PodStack;
|
||||
use itertools::izip;
|
||||
use tfhe_fft::c64;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(bound(deserialize = "C: IntoContainerOwned"))]
|
||||
pub struct FourierCmLweBootstrapKey<C: Container<Element = c64>> {
|
||||
fourier: FourierPolynomialList<C>,
|
||||
input_lwe_dimension: LweDimension,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
}
|
||||
|
||||
pub type FourierCmLweBootstrapKeyView<'a> = FourierCmLweBootstrapKey<&'a [c64]>;
|
||||
pub type FourierCmLweBootstrapKeyMutView<'a> = FourierCmLweBootstrapKey<&'a mut [c64]>;
|
||||
|
||||
impl<C: Container<Element = c64>> FourierCmLweBootstrapKey<C> {
|
||||
pub fn from_container(
|
||||
data: C,
|
||||
input_lwe_dimension: LweDimension,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
data.container_len(),
|
||||
input_lwe_dimension.0
|
||||
* decomposition_level_count.0
|
||||
* (glwe_dimension.0 + cm_dimension.0)
|
||||
* cm_glwe_ciphertext_fourier_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size.to_fourier_polynomial_size()
|
||||
)
|
||||
);
|
||||
Self {
|
||||
fourier: FourierPolynomialList {
|
||||
data,
|
||||
polynomial_size,
|
||||
},
|
||||
input_lwe_dimension,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_cm_ggsw_iter(self) -> impl DoubleEndedIterator<Item = FourierCmGgswCiphertext<C>>
|
||||
where
|
||||
C: Split,
|
||||
{
|
||||
self.fourier
|
||||
.data
|
||||
.split_into(self.input_lwe_dimension.0)
|
||||
.map(move |slice| {
|
||||
FourierCmGgswCiphertext::from_container(
|
||||
slice,
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.fourier.polynomial_size,
|
||||
self.decomposition_base_log,
|
||||
self.decomposition_level_count,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn input_lwe_dimension(&self) -> LweDimension {
|
||||
self.input_lwe_dimension
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.fourier.polynomial_size
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomposition_base_log
|
||||
}
|
||||
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomposition_level_count
|
||||
}
|
||||
|
||||
pub fn output_lwe_dimension(&self) -> LweDimension {
|
||||
self.glwe_dimension
|
||||
.to_equivalent_lwe_dimension(self.polynomial_size())
|
||||
}
|
||||
|
||||
pub fn data(self) -> C {
|
||||
self.fourier.data
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> FourierCmLweBootstrapKeyView<'_> {
|
||||
FourierCmLweBootstrapKeyView {
|
||||
fourier: FourierPolynomialList {
|
||||
data: self.fourier.data.as_ref(),
|
||||
polynomial_size: self.fourier.polynomial_size,
|
||||
},
|
||||
input_lwe_dimension: self.input_lwe_dimension,
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
decomposition_base_log: self.decomposition_base_log,
|
||||
decomposition_level_count: self.decomposition_level_count,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_mut_view(&mut self) -> FourierCmLweBootstrapKeyMutView<'_>
|
||||
where
|
||||
C: AsMut<[c64]>,
|
||||
{
|
||||
FourierCmLweBootstrapKeyMutView {
|
||||
fourier: FourierPolynomialList {
|
||||
data: self.fourier.data.as_mut(),
|
||||
polynomial_size: self.fourier.polynomial_size,
|
||||
},
|
||||
input_lwe_dimension: self.input_lwe_dimension,
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
decomposition_base_log: self.decomposition_base_log,
|
||||
decomposition_level_count: self.decomposition_level_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type FourierCmLweBootstrapKeyOwned = FourierCmLweBootstrapKey<ABox<[c64]>>;
|
||||
|
||||
impl FourierCmLweBootstrapKey<ABox<[c64]>> {
|
||||
pub fn new(
|
||||
input_lwe_dimension: LweDimension,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
) -> Self {
|
||||
let boxed = avec![
|
||||
c64::default();
|
||||
input_lwe_dimension.0
|
||||
* decomposition_level_count.0
|
||||
* (glwe_dimension.0 + cm_dimension.0)
|
||||
* cm_glwe_ciphertext_fourier_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size.to_fourier_polynomial_size()
|
||||
)
|
||||
]
|
||||
.into_boxed_slice();
|
||||
|
||||
FourierCmLweBootstrapKey::from_container(
|
||||
boxed,
|
||||
input_lwe_dimension,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl FourierCmLweBootstrapKeyMutView<'_> {
|
||||
pub fn fill_with_forward_fourier<Scalar: UnsignedTorus>(
|
||||
mut self,
|
||||
coef_bsk: CmLweBootstrapKey<&'_ [Scalar]>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) {
|
||||
for (fourier_ggsw, standard_ggsw) in
|
||||
izip!(self.as_mut_view().into_cm_ggsw_iter(), coef_bsk.iter())
|
||||
{
|
||||
fourier_ggsw.fill_with_forward_fourier(standard_ggsw, fft, stack);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn par_fill_with_forward_fourier<Scalar: UnsignedTorus>(
|
||||
self,
|
||||
coef_bsk: CmLweBootstrapKey<&'_ [Scalar]>,
|
||||
fft: FftView<'_>,
|
||||
) {
|
||||
let polynomial_size = self.polynomial_size();
|
||||
par_convert_polynomials_list_to_fourier(
|
||||
self.data(),
|
||||
coef_bsk.into_container(),
|
||||
polynomial_size,
|
||||
fft,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,489 @@
|
||||
use crate::core_crypto::commons::math::decomposition::DecompositionLevel;
|
||||
use crate::core_crypto::commons::math::torus::UnsignedTorus;
|
||||
use crate::core_crypto::commons::parameters::{
|
||||
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, PolynomialSize,
|
||||
};
|
||||
use crate::core_crypto::commons::traits::{Container, IntoContainerOwned, Split};
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::fft_impl::fft64::math;
|
||||
use crate::core_crypto::prelude::ContiguousEntityContainer;
|
||||
use aligned_vec::{avec, ABox};
|
||||
use dyn_stack::PodStack;
|
||||
use itertools::izip;
|
||||
use math::fft::{FftView, FourierPolynomialList};
|
||||
use math::polynomial::FourierPolynomialMutView;
|
||||
use tfhe_fft::c64;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(bound(deserialize = "C: IntoContainerOwned"))]
|
||||
pub struct FourierCmGgswCiphertext<C: Container<Element = c64>> {
|
||||
fourier: FourierPolynomialList<C>,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct FourierCmGgswLevelMatrix<C: Container<Element = c64>> {
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_level: DecompositionLevel,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct FourierCmGgswLevelRow<C: Container<Element = c64>> {
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_level: DecompositionLevel,
|
||||
}
|
||||
|
||||
pub type FourierCmGgswCiphertextView<'a> = FourierCmGgswCiphertext<&'a [c64]>;
|
||||
pub type FourierCmGgswCiphertextMutView<'a> = FourierCmGgswCiphertext<&'a mut [c64]>;
|
||||
pub type FourierCmGgswLevelMatrixView<'a> = FourierCmGgswLevelMatrix<&'a [c64]>;
|
||||
pub type FourierCmGgswLevelMatrixMutView<'a> = FourierCmGgswLevelMatrix<&'a mut [c64]>;
|
||||
pub type FourierCmGgswLevelRowView<'a> = FourierCmGgswLevelRow<&'a [c64]>;
|
||||
pub type FourierCmGgswLevelRowMutView<'a> = FourierCmGgswLevelRow<&'a mut [c64]>;
|
||||
|
||||
impl<C: Container<Element = c64>> FourierCmGgswCiphertext<C> {
|
||||
pub fn from_container(
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
data.container_len(),
|
||||
(glwe_dimension.0 + cm_dimension.0)
|
||||
* decomposition_level_count.0
|
||||
* cm_glwe_ciphertext_fourier_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size.to_fourier_polynomial_size()
|
||||
)
|
||||
);
|
||||
|
||||
Self {
|
||||
fourier: FourierPolynomialList {
|
||||
data,
|
||||
polynomial_size,
|
||||
},
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.fourier.polynomial_size
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomposition_base_log
|
||||
}
|
||||
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomposition_level_count
|
||||
}
|
||||
|
||||
pub fn data(self) -> C {
|
||||
self.fourier.data
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> FourierCmGgswCiphertextView<'_>
|
||||
where
|
||||
C: AsRef<[c64]>,
|
||||
{
|
||||
FourierCmGgswCiphertextView {
|
||||
fourier: FourierPolynomialList {
|
||||
data: self.fourier.data.as_ref(),
|
||||
polynomial_size: self.fourier.polynomial_size,
|
||||
},
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
decomposition_base_log: self.decomposition_base_log,
|
||||
decomposition_level_count: self.decomposition_level_count,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_mut_view(&mut self) -> FourierCmGgswCiphertextMutView<'_>
|
||||
where
|
||||
C: AsMut<[c64]>,
|
||||
{
|
||||
FourierCmGgswCiphertextMutView {
|
||||
fourier: FourierPolynomialList {
|
||||
data: self.fourier.data.as_mut(),
|
||||
polynomial_size: self.fourier.polynomial_size,
|
||||
},
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
decomposition_base_log: self.decomposition_base_log,
|
||||
decomposition_level_count: self.decomposition_level_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Container<Element = c64>> FourierCmGgswLevelMatrix<C> {
|
||||
pub fn new(
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_level: DecompositionLevel,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
data.container_len(),
|
||||
cm_fourier_ggsw_level_matrix_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size.to_fourier_polynomial_size()
|
||||
),
|
||||
);
|
||||
Self {
|
||||
data,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_level,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_rows(self) -> impl DoubleEndedIterator<Item = FourierCmGgswLevelRow<C>>
|
||||
where
|
||||
C: Split,
|
||||
{
|
||||
let row_count = self.row_count();
|
||||
self.data
|
||||
.split_into(row_count)
|
||||
.map(move |slice| FourierCmGgswLevelRow {
|
||||
data: slice,
|
||||
polynomial_size: self.polynomial_size,
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
decomposition_level: self.decomposition_level,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn row_count(&self) -> usize {
|
||||
self.glwe_dimension.0 + self.cm_dimension.0
|
||||
}
|
||||
|
||||
pub fn decomposition_level(&self) -> DecompositionLevel {
|
||||
self.decomposition_level
|
||||
}
|
||||
|
||||
pub fn data(self) -> C {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Container<Element = c64>> FourierCmGgswLevelRow<C> {
|
||||
pub fn new(
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_level: DecompositionLevel,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
data.container_len(),
|
||||
cm_glwe_ciphertext_fourier_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size.to_fourier_polynomial_size()
|
||||
)
|
||||
);
|
||||
Self {
|
||||
data,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_level,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn decomposition_level(&self) -> DecompositionLevel {
|
||||
self.decomposition_level
|
||||
}
|
||||
|
||||
pub fn data(self) -> C {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FourierCmGgswCiphertextView<'a> {
|
||||
pub fn into_levels(self) -> impl DoubleEndedIterator<Item = FourierCmGgswLevelMatrixView<'a>> {
|
||||
let decomposition_level_count = self.decomposition_level_count.0;
|
||||
self.fourier
|
||||
.data
|
||||
.split_into(decomposition_level_count)
|
||||
.enumerate()
|
||||
.map(move |(i, slice)| {
|
||||
FourierCmGgswLevelMatrixView::new(
|
||||
slice,
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.fourier.polynomial_size,
|
||||
DecompositionLevel(decomposition_level_count - i),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FourierCmGgswCiphertextMutView<'_> {
|
||||
pub fn fill_with_forward_fourier<Scalar: UnsignedTorus>(
|
||||
self,
|
||||
coef_ggsw: CmGgswCiphertextView<'_, Scalar>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) {
|
||||
debug_assert_eq!(coef_ggsw.polynomial_size(), self.polynomial_size());
|
||||
let fourier_poly_size = coef_ggsw.polynomial_size().to_fourier_polynomial_size().0;
|
||||
|
||||
for (fourier_poly, coef_poly) in izip!(
|
||||
self.data().into_chunks(fourier_poly_size),
|
||||
coef_ggsw.as_polynomial_list().iter()
|
||||
) {
|
||||
fft.forward_as_torus(
|
||||
FourierPolynomialMutView { data: fourier_poly },
|
||||
coef_poly,
|
||||
stack,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FourierCmGgswCiphertext<ABox<[c64]>> {
|
||||
pub fn new(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
) -> Self {
|
||||
let boxed = avec![
|
||||
c64::default();
|
||||
polynomial_size.to_fourier_polynomial_size().0
|
||||
* (glwe_dimension.0+cm_dimension.0)
|
||||
* (glwe_dimension.0+cm_dimension.0)
|
||||
* decomposition_level_count.0
|
||||
]
|
||||
.into_boxed_slice();
|
||||
|
||||
FourierCmGgswCiphertext::from_container(
|
||||
boxed,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub struct FourierCmGgswCiphertextList<C: Container<Element = c64>> {
|
||||
fourier: FourierPolynomialList<C>,
|
||||
cm_dimension: CmDimension,
|
||||
glwe_dimension: GlweDimension,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
pub type FourierCmGgswCiphertextListView<'a> = FourierCmGgswCiphertextList<&'a [c64]>;
|
||||
pub type FourierCmGgswCiphertextListMutView<'a> = FourierCmGgswCiphertextList<&'a mut [c64]>;
|
||||
|
||||
impl<C: Container<Element = c64>> FourierCmGgswCiphertextList<C> {
|
||||
pub fn new(
|
||||
data: C,
|
||||
count: usize,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
data.container_len(),
|
||||
count
|
||||
* decomposition_level_count.0
|
||||
* (glwe_dimension.0 + cm_dimension.0)
|
||||
* cm_glwe_ciphertext_fourier_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size.to_fourier_polynomial_size()
|
||||
)
|
||||
);
|
||||
|
||||
Self {
|
||||
fourier: FourierPolynomialList {
|
||||
data,
|
||||
polynomial_size,
|
||||
},
|
||||
count,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
decomposition_level_count,
|
||||
decomposition_base_log,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data(self) -> C {
|
||||
self.fourier.data
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.fourier.polynomial_size
|
||||
}
|
||||
|
||||
pub fn count(&self) -> usize {
|
||||
self.count
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomposition_level_count
|
||||
}
|
||||
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomposition_base_log
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> FourierCmGgswCiphertextListView<'_> {
|
||||
let fourier = FourierPolynomialList {
|
||||
data: self.fourier.data.as_ref(),
|
||||
polynomial_size: self.fourier.polynomial_size,
|
||||
};
|
||||
FourierCmGgswCiphertextListView {
|
||||
fourier,
|
||||
count: self.count,
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
decomposition_level_count: self.decomposition_level_count,
|
||||
decomposition_base_log: self.decomposition_base_log,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_mut_view(&mut self) -> FourierCmGgswCiphertextListMutView<'_>
|
||||
where
|
||||
C: AsMut<[c64]>,
|
||||
{
|
||||
let fourier = FourierPolynomialList {
|
||||
data: self.fourier.data.as_mut(),
|
||||
polynomial_size: self.fourier.polynomial_size,
|
||||
};
|
||||
FourierCmGgswCiphertextListMutView {
|
||||
fourier,
|
||||
count: self.count,
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
decomposition_level_count: self.decomposition_level_count,
|
||||
decomposition_base_log: self.decomposition_base_log,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_ggsw_iter(self) -> impl DoubleEndedIterator<Item = FourierCmGgswCiphertext<C>>
|
||||
where
|
||||
C: Split,
|
||||
{
|
||||
self.fourier.data.split_into(self.count).map(move |slice| {
|
||||
FourierCmGgswCiphertext::from_container(
|
||||
slice,
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.fourier.polynomial_size,
|
||||
self.decomposition_base_log,
|
||||
self.decomposition_level_count,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn split_at(self, mid: usize) -> (Self, Self)
|
||||
where
|
||||
C: Split,
|
||||
{
|
||||
let polynomial_size = self.fourier.polynomial_size;
|
||||
let glwe_dimension = self.glwe_dimension;
|
||||
let cm_dimension = self.cm_dimension;
|
||||
|
||||
let decomposition_level_count = self.decomposition_level_count;
|
||||
let decomposition_base_log = self.decomposition_base_log;
|
||||
|
||||
let (left, right) = self.fourier.data.split_at(
|
||||
mid * (glwe_dimension.0 + cm_dimension.0)
|
||||
* decomposition_level_count.0
|
||||
* cm_glwe_ciphertext_fourier_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size.to_fourier_polynomial_size(),
|
||||
),
|
||||
);
|
||||
(
|
||||
Self::new(
|
||||
left,
|
||||
mid,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
),
|
||||
Self::new(
|
||||
right,
|
||||
self.count - mid,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,607 @@
|
||||
//! Module containing the definition of the CmGgswCiphertext.
|
||||
|
||||
use super::cm_glwe_ciphertext::cm_glwe_ciphertext_encryption_mask_sample_count;
|
||||
use super::cm_glwe_ciphertext_list::{CmGlweCiphertextListMutView, CmGlweCiphertextListView};
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGeneratorForkConfig;
|
||||
use crate::core_crypto::commons::math::random::{Distribution, RandomGenerable};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::prelude::{
|
||||
glwe_ciphertext_encryption_mask_sample_count, glwe_ciphertext_encryption_noise_sample_count,
|
||||
PolynomialListMutView, PolynomialListView,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CmGgswCiphertext<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for CmGgswCiphertext<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for CmGgswCiphertext<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_ciphertext_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
) -> usize {
|
||||
decomp_level_count.0 * cm_ggsw_level_matrix_size(glwe_dimension, cm_dimension, polynomial_size)
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_level_matrix_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> usize {
|
||||
(glwe_dimension.0 + cm_dimension.0)
|
||||
* cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size)
|
||||
}
|
||||
|
||||
pub fn fourier_cm_ggsw_ciphertext_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
fourier_polynomial_size: FourierPolynomialSize,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
) -> usize {
|
||||
decomp_level_count.0
|
||||
* fourier_cm_ggsw_level_matrix_size(glwe_dimension, cm_dimension, fourier_polynomial_size)
|
||||
}
|
||||
|
||||
pub fn fourier_cm_ggsw_level_matrix_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
fourier_polynomial_size: FourierPolynomialSize,
|
||||
) -> usize {
|
||||
(glwe_dimension.0 + cm_dimension.0)
|
||||
* cm_glwe_ciphertext_fourier_size(glwe_dimension, cm_dimension, fourier_polynomial_size)
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_ciphertext_encryption_mask_sample_count(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
) -> EncryptionMaskSampleCount {
|
||||
decomp_level_count.0
|
||||
* cm_ggsw_level_matrix_encryption_mask_sample_count(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_level_matrix_encryption_mask_sample_count(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> EncryptionMaskSampleCount {
|
||||
(glwe_dimension.0 + cm_dimension.0)
|
||||
* cm_glwe_ciphertext_encryption_mask_sample_count(glwe_dimension, polynomial_size)
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_ciphertext_encryption_noise_sample_count(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
) -> EncryptionNoiseSampleCount {
|
||||
decomp_level_count.0
|
||||
* cm_ggsw_level_matrix_encryption_noise_sample_count(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_level_matrix_encryption_noise_sample_count(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> EncryptionNoiseSampleCount {
|
||||
cm_dimension.0
|
||||
* (glwe_dimension.0 + cm_dimension.0)
|
||||
* glwe_ciphertext_encryption_noise_sample_count(polynomial_size)
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_ciphertext_encryption_fork_config<Scalar, MaskDistribution, NoiseDistribution>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
mask_distribution: MaskDistribution,
|
||||
noise_distribution: NoiseDistribution,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> EncryptionRandomGeneratorForkConfig
|
||||
where
|
||||
Scalar: UnsignedInteger
|
||||
+ RandomGenerable<MaskDistribution, CustomModulus = Scalar>
|
||||
+ RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
MaskDistribution: Distribution,
|
||||
NoiseDistribution: Distribution,
|
||||
{
|
||||
let ggsw_level_matrix_mask_sample_count = cm_ggsw_level_matrix_encryption_mask_sample_count(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
);
|
||||
let ggsw_level_matrix_noise_sample_count = cm_ggsw_level_matrix_encryption_noise_sample_count(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
);
|
||||
|
||||
let modulus = ciphertext_modulus.get_custom_modulus_as_optional_scalar();
|
||||
|
||||
EncryptionRandomGeneratorForkConfig::new(
|
||||
decomposition_level_count.0,
|
||||
ggsw_level_matrix_mask_sample_count,
|
||||
mask_distribution,
|
||||
ggsw_level_matrix_noise_sample_count,
|
||||
noise_distribution,
|
||||
modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_level_matrix_encryption_fork_config<Scalar, MaskDistribution, NoiseDistribution>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
mask_distribution: MaskDistribution,
|
||||
noise_distribution: NoiseDistribution,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> EncryptionRandomGeneratorForkConfig
|
||||
where
|
||||
Scalar: UnsignedInteger
|
||||
+ RandomGenerable<MaskDistribution, CustomModulus = Scalar>
|
||||
+ RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
MaskDistribution: Distribution,
|
||||
NoiseDistribution: Distribution,
|
||||
{
|
||||
let glwe_ciphertext_mask_sample_count =
|
||||
glwe_ciphertext_encryption_mask_sample_count(glwe_dimension, polynomial_size);
|
||||
let glwe_ciphertext_noise_sample_count =
|
||||
glwe_ciphertext_encryption_noise_sample_count(polynomial_size);
|
||||
|
||||
let modulus = ciphertext_modulus.get_custom_modulus_as_optional_scalar();
|
||||
|
||||
EncryptionRandomGeneratorForkConfig::new(
|
||||
glwe_dimension.0 + cm_dimension.0,
|
||||
glwe_ciphertext_mask_sample_count,
|
||||
mask_distribution,
|
||||
glwe_ciphertext_noise_sample_count,
|
||||
noise_distribution,
|
||||
modulus,
|
||||
)
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmGgswCiphertext<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
container.container_len() > 0,
|
||||
"Got an empty container to create a CmGgswCiphertext"
|
||||
);
|
||||
|
||||
let matrix_size = cm_ggsw_level_matrix_size(glwe_dimension, cm_dimension, polynomial_size);
|
||||
|
||||
assert!(
|
||||
container.container_len().is_multiple_of(matrix_size),
|
||||
"The provided container length is not valid. \
|
||||
It needs to be dividable by glwe_dimension * glwe_dimension * polynomial_size: {}. \
|
||||
Got container length: {} and glwe_dimension: {glwe_dimension:?}, \
|
||||
cm_dimension: {cm_dimension:?}, polynomial_size: {polynomial_size:?}.",
|
||||
matrix_size,
|
||||
container.container_len()
|
||||
);
|
||||
|
||||
Self {
|
||||
data: container,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
DecompositionLevelCount(self.data.container_len() / self.ggsw_level_matrix_size())
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
|
||||
pub fn ggsw_level_matrix_size(&self) -> usize {
|
||||
// GlweDimension GlweCiphertext(glwe_dimension,cm_dimension,polynomial_size) per level
|
||||
cm_ggsw_level_matrix_size(self.glwe_dimension, self.cm_dimension, self.polynomial_size)
|
||||
}
|
||||
|
||||
pub fn as_polynomial_list(&self) -> PolynomialListView<'_, Scalar> {
|
||||
PolynomialListView::from_container(self.as_ref(), self.polynomial_size)
|
||||
}
|
||||
|
||||
pub fn as_cm_glwe_list(&self) -> CmGlweCiphertextListView<'_, Scalar> {
|
||||
CmGlweCiphertextListView::from_container(
|
||||
self.as_ref(),
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> CmGgswCiphertextView<'_, Scalar> {
|
||||
CmGgswCiphertextView::from_container(
|
||||
self.as_ref(),
|
||||
self.glwe_dimension(),
|
||||
self.cm_dimension,
|
||||
self.polynomial_size(),
|
||||
self.decomposition_base_log(),
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn encryption_fork_config<MaskDistribution, NoiseDistribution>(
|
||||
&self,
|
||||
mask_distribution: MaskDistribution,
|
||||
noise_distribution: NoiseDistribution,
|
||||
) -> EncryptionRandomGeneratorForkConfig
|
||||
where
|
||||
MaskDistribution: Distribution,
|
||||
NoiseDistribution: Distribution,
|
||||
Scalar: RandomGenerable<MaskDistribution, CustomModulus = Scalar>
|
||||
+ RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
{
|
||||
cm_ggsw_ciphertext_encryption_fork_config(
|
||||
self.glwe_dimension(),
|
||||
self.cm_dimension,
|
||||
self.polynomial_size(),
|
||||
self.decomposition_level_count(),
|
||||
mask_distribution,
|
||||
noise_distribution,
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> CmGgswCiphertext<C> {
|
||||
pub fn as_mut_polynomial_list(&mut self) -> PolynomialListMutView<'_, Scalar> {
|
||||
let polynomial_size = self.polynomial_size;
|
||||
PolynomialListMutView::from_container(self.as_mut(), polynomial_size)
|
||||
}
|
||||
|
||||
pub fn as_mut_cm_glwe_list(&mut self) -> CmGlweCiphertextListMutView<'_, Scalar> {
|
||||
let polynomial_size = self.polynomial_size;
|
||||
let glwe_dimension = self.glwe_dimension;
|
||||
let cm_dimension = self.cm_dimension;
|
||||
let ciphertext_modulus: CiphertextModulus<Scalar> = self.ciphertext_modulus;
|
||||
CmGlweCiphertextListMutView::from_container(
|
||||
self.as_mut(),
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_mut_view(&mut self) -> CmGgswCiphertextMutView<'_, Scalar> {
|
||||
let glwe_dimension = self.glwe_dimension;
|
||||
let cm_dimension = self.cm_dimension;
|
||||
let polynomial_size = self.polynomial_size();
|
||||
let decomp_base_log = self.decomposition_base_log();
|
||||
let ciphertext_modulus = self.ciphertext_modulus;
|
||||
CmGgswCiphertextMutView::from_container(
|
||||
self.as_mut(),
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub type CmGgswCiphertextOwned<Scalar> = CmGgswCiphertext<Vec<Scalar>>;
|
||||
|
||||
pub type CmGgswCiphertextView<'data, Scalar> = CmGgswCiphertext<&'data [Scalar]>;
|
||||
|
||||
pub type CmGgswCiphertextMutView<'data, Scalar> = CmGgswCiphertext<&'data mut [Scalar]>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> CmGgswCiphertextOwned<Scalar> {
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
Self::from_container(
|
||||
vec![
|
||||
fill_with;
|
||||
cm_ggsw_ciphertext_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_level_count
|
||||
)
|
||||
],
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmGgswCiphertextCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub cm_dimension: CmDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub decomp_base_log: DecompositionBaseLog,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for CmGgswCiphertext<C>
|
||||
{
|
||||
type Metadata = CmGgswCiphertextCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let CmGgswCiphertextCreationMetadata {
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
Self::from_container(
|
||||
from,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CmGgswLevelMatrix<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmGgswLevelMatrix<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
container.container_len()
|
||||
== cm_ggsw_level_matrix_size(glwe_dimension, cm_dimension, polynomial_size),
|
||||
"The provided container length is not valid. \
|
||||
Expected length of {} (glwe_dimension * glwe_dimension * polynomial_size), got {}",
|
||||
cm_ggsw_level_matrix_size(glwe_dimension, cm_dimension, polynomial_size),
|
||||
container.container_len(),
|
||||
);
|
||||
|
||||
Self {
|
||||
data: container,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
|
||||
pub fn as_cm_glwe_list(&self) -> CmGlweCiphertextListView<'_, C::Element> {
|
||||
CmGlweCiphertextListView::from_container(
|
||||
self.data.as_ref(),
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn encryption_fork_config<MaskDistribution, NoiseDistribution>(
|
||||
&self,
|
||||
mask_distribution: MaskDistribution,
|
||||
noise_distribution: NoiseDistribution,
|
||||
) -> EncryptionRandomGeneratorForkConfig
|
||||
where
|
||||
MaskDistribution: Distribution,
|
||||
NoiseDistribution: Distribution,
|
||||
Scalar: RandomGenerable<MaskDistribution, CustomModulus = Scalar>
|
||||
+ RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
{
|
||||
cm_ggsw_level_matrix_encryption_fork_config(
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size(),
|
||||
mask_distribution,
|
||||
noise_distribution,
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> CmGgswLevelMatrix<C> {
|
||||
pub fn as_mut_cm_glwe_list(&mut self) -> CmGlweCiphertextListMutView<'_, C::Element> {
|
||||
CmGlweCiphertextListMutView::from_container(
|
||||
self.data.as_mut(),
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_fourier_ggsw_level_matrix_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
fourier_polynomial_size: FourierPolynomialSize,
|
||||
) -> usize {
|
||||
(glwe_dimension.0 + cm_dimension.0)
|
||||
* cm_glwe_ciphertext_fourier_size(glwe_dimension, cm_dimension, fourier_polynomial_size)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmGgswLevelMatrixCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub cm_dimension: CmDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for CmGgswLevelMatrix<C>
|
||||
{
|
||||
type Metadata = CmGgswLevelMatrixCreationMetadata<C::Element>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let CmGgswLevelMatrixCreationMetadata {
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
Self::from_container(
|
||||
from,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for CmGgswCiphertext<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = CmGgswLevelMatrixCreationMetadata<Self::Element>;
|
||||
|
||||
type EntityView<'this>
|
||||
= CmGgswLevelMatrix<&'this [Self::Element]>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = ();
|
||||
|
||||
type SelfView<'this>
|
||||
= DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
CmGgswLevelMatrixCreationMetadata {
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
polynomial_size: self.polynomial_size,
|
||||
ciphertext_modulus: self.ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
self.ggsw_level_matrix_size()
|
||||
}
|
||||
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
unimplemented!(
|
||||
"This function is not supported for CmGgswCiphertext. \
|
||||
At the moment it does not make sense to return 'sub' CmGgswCiphertext."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for CmGgswCiphertext<C>
|
||||
{
|
||||
type EntityMutView<'this>
|
||||
= CmGgswLevelMatrix<&'this mut [Self::Element]>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfMutView<'this>
|
||||
= DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
//! Module containing the definition of the GgswCiphertextList.
|
||||
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGeneratorForkConfig;
|
||||
use crate::core_crypto::commons::math::random::{Distribution, RandomGenerable};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use cm_ggsw_ciphertext::{
|
||||
cm_ggsw_ciphertext_encryption_mask_sample_count,
|
||||
cm_ggsw_ciphertext_encryption_noise_sample_count, cm_ggsw_ciphertext_size,
|
||||
CmGgswCiphertextCreationMetadata, CmGgswCiphertextMutView, CmGgswCiphertextView,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CmGgswCiphertextList<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for CmGgswCiphertextList<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for CmGgswCiphertextList<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_ciphertext_list_size(
|
||||
ciphertext_count: GgswCiphertextCount,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
) -> usize {
|
||||
ciphertext_count.0
|
||||
* cm_ggsw_ciphertext_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_level_count,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn cm_ggsw_ciphertext_list_encryption_fork_config<Scalar, MaskDistribution, NoiseDistribution>(
|
||||
ggsw_ciphertext_count: GgswCiphertextCount,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
mask_distribution: MaskDistribution,
|
||||
noise_distribution: NoiseDistribution,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> EncryptionRandomGeneratorForkConfig
|
||||
where
|
||||
Scalar: UnsignedInteger
|
||||
+ RandomGenerable<MaskDistribution, CustomModulus = Scalar>
|
||||
+ RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
MaskDistribution: Distribution,
|
||||
NoiseDistribution: Distribution,
|
||||
{
|
||||
let ggsw_mask_sample_count = cm_ggsw_ciphertext_encryption_mask_sample_count(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_level_count,
|
||||
);
|
||||
let ggsw_noise_sample_count = cm_ggsw_ciphertext_encryption_noise_sample_count(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_level_count,
|
||||
);
|
||||
|
||||
let modulus = ciphertext_modulus.get_custom_modulus_as_optional_scalar();
|
||||
|
||||
EncryptionRandomGeneratorForkConfig::new(
|
||||
ggsw_ciphertext_count.0,
|
||||
ggsw_mask_sample_count,
|
||||
mask_distribution,
|
||||
ggsw_noise_sample_count,
|
||||
noise_distribution,
|
||||
modulus,
|
||||
)
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmGgswCiphertextList<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
let ggsw_size = cm_ggsw_ciphertext_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_level_count,
|
||||
);
|
||||
|
||||
assert!(
|
||||
container.container_len().is_multiple_of(ggsw_size),
|
||||
"The provided container length is not valid. \
|
||||
It needs to be dividable by decomp_level_count * glwe_dimension * glwe_dimension * polynomial_size: \
|
||||
{}.Got container length: {} and decomp_level_count: {decomp_level_count:?}, \
|
||||
glwe_dimension: {glwe_dimension:?}, cm_dimension: {cm_dimension:?} polynomial_size: {polynomial_size:?}",
|
||||
ggsw_size,
|
||||
container.container_len()
|
||||
);
|
||||
|
||||
Self {
|
||||
data: container,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomp_level_count
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_ciphertext_count(&self) -> GgswCiphertextCount {
|
||||
GgswCiphertextCount(
|
||||
self.data.container_len()
|
||||
/ cm_ggsw_ciphertext_size(
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size,
|
||||
self.decomp_level_count,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn as_polynomial_list(&self) -> PolynomialListView<'_, Scalar> {
|
||||
PolynomialList::from_container(self.as_ref(), self.polynomial_size())
|
||||
}
|
||||
|
||||
pub fn encryption_fork_config<MaskDistribution, NoiseDistribution>(
|
||||
&self,
|
||||
mask_distribution: MaskDistribution,
|
||||
noise_distribution: NoiseDistribution,
|
||||
) -> EncryptionRandomGeneratorForkConfig
|
||||
where
|
||||
MaskDistribution: Distribution,
|
||||
NoiseDistribution: Distribution,
|
||||
Scalar: RandomGenerable<MaskDistribution, CustomModulus = Scalar>
|
||||
+ RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
{
|
||||
cm_ggsw_ciphertext_list_encryption_fork_config(
|
||||
self.cm_ggsw_ciphertext_count(),
|
||||
self.glwe_dimension(),
|
||||
self.cm_dimension,
|
||||
self.polynomial_size(),
|
||||
self.decomposition_level_count(),
|
||||
mask_distribution,
|
||||
noise_distribution,
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> CmGgswCiphertextList<C> {
|
||||
pub fn as_mut_polynomial_list(&mut self) -> PolynomialListMutView<'_, Scalar> {
|
||||
let polynomial_size = self.polynomial_size();
|
||||
PolynomialList::from_container(self.as_mut(), polynomial_size)
|
||||
}
|
||||
}
|
||||
|
||||
pub type CmGgswCiphertextListOwned<Scalar> = CmGgswCiphertextList<Vec<Scalar>>;
|
||||
|
||||
pub type CmGgswCiphertextListView<'data, Scalar> = CmGgswCiphertextList<&'data [Scalar]>;
|
||||
|
||||
pub type CmGgswCiphertextListMutView<'data, Scalar> = CmGgswCiphertextList<&'data mut [Scalar]>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> CmGgswCiphertextListOwned<Scalar> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
ciphertext_count: GgswCiphertextCount,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
Self::from_container(
|
||||
vec![
|
||||
fill_with;
|
||||
cm_ggsw_ciphertext_list_size(
|
||||
ciphertext_count,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_level_count
|
||||
)
|
||||
],
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmGgswCiphertextListCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub cm_dimension: CmDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub decomp_base_log: DecompositionBaseLog,
|
||||
pub decomp_level_count: DecompositionLevelCount,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for CmGgswCiphertextList<C>
|
||||
{
|
||||
type Metadata = CmGgswCiphertextListCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let CmGgswCiphertextListCreationMetadata {
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
Self::from_container(
|
||||
from,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for CmGgswCiphertextList<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = CmGgswCiphertextCreationMetadata<Self::Element>;
|
||||
|
||||
type EntityView<'this>
|
||||
= CmGgswCiphertextView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = CmGgswCiphertextListCreationMetadata<Self::Element>;
|
||||
|
||||
type SelfView<'this>
|
||||
= CmGgswCiphertextListView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
CmGgswCiphertextCreationMetadata {
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
polynomial_size: self.polynomial_size,
|
||||
decomp_base_log: self.decomp_base_log,
|
||||
ciphertext_modulus: self.ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
cm_ggsw_ciphertext_size(
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size,
|
||||
self.decomp_level_count,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
CmGgswCiphertextListCreationMetadata {
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
polynomial_size: self.polynomial_size,
|
||||
decomp_base_log: self.decomp_base_log,
|
||||
decomp_level_count: self.decomp_level_count,
|
||||
ciphertext_modulus: self.ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for CmGgswCiphertextList<C>
|
||||
{
|
||||
type EntityMutView<'this>
|
||||
= CmGgswCiphertextMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfMutView<'this>
|
||||
= CmGgswCiphertextListMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
//! Module containing the definition of the CmGlweCiphertext.
|
||||
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
|
||||
pub fn cm_glwe_ciphertext_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> usize {
|
||||
(glwe_dimension.0 + cm_dimension.0) * polynomial_size.0
|
||||
}
|
||||
|
||||
pub fn cm_glwe_ciphertext_fourier_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
fourier_polynomial_size: FourierPolynomialSize,
|
||||
) -> usize {
|
||||
(glwe_dimension.0 + cm_dimension.0) * fourier_polynomial_size.0
|
||||
}
|
||||
|
||||
pub fn cm_glwe_ciphertext_mask_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> usize {
|
||||
glwe_dimension
|
||||
.to_equivalent_lwe_dimension(polynomial_size)
|
||||
.0
|
||||
}
|
||||
|
||||
pub fn cm_glwe_ciphertext_encryption_mask_sample_count(
|
||||
glwe_dimension: GlweDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> EncryptionMaskSampleCount {
|
||||
EncryptionMaskSampleCount(cm_glwe_ciphertext_mask_size(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn cm_glwe_ciphertext_encryption_noise_sample_count(
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> EncryptionNoiseSampleCount {
|
||||
EncryptionNoiseSampleCount(cm_dimension.0 * polynomial_size.0)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CmGlweCiphertext<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for CmGlweCiphertext<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for CmGlweCiphertext<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmGlweCiphertext<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
container.container_len(),
|
||||
cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size)
|
||||
);
|
||||
|
||||
Self {
|
||||
data: container,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
|
||||
pub fn get_mask_and_bodies(&self) -> (GlweMask<&[Scalar]>, GlweBodyList<&[Scalar]>) {
|
||||
let (mask, bodies) = self.data.as_ref().split_at(cm_glwe_ciphertext_mask_size(
|
||||
self.glwe_dimension(),
|
||||
self.polynomial_size,
|
||||
));
|
||||
|
||||
(
|
||||
GlweMask::from_container(mask, self.polynomial_size, self.ciphertext_modulus),
|
||||
GlweBodyList::from_container(bodies, self.polynomial_size, self.ciphertext_modulus),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_bodies(&self) -> GlweBodyList<&[Scalar]> {
|
||||
let bodies = &self.data.as_ref()
|
||||
[cm_glwe_ciphertext_mask_size(self.glwe_dimension(), self.polynomial_size)..];
|
||||
|
||||
GlweBodyList::from_container(bodies, self.polynomial_size, self.ciphertext_modulus)
|
||||
}
|
||||
|
||||
pub fn get_mask(&self) -> GlweMask<&[Scalar]> {
|
||||
GlweMask::from_container(
|
||||
&self.as_ref()
|
||||
[0..cm_glwe_ciphertext_mask_size(self.glwe_dimension(), self.polynomial_size)],
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_polynomial_list(&self) -> PolynomialList<&'_ [Scalar]> {
|
||||
PolynomialList::from_container(self.as_ref(), self.polynomial_size)
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> CmGlweCiphertext<&'_ [Scalar]> {
|
||||
CmGlweCiphertext {
|
||||
data: self.data.as_ref(),
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
polynomial_size: self.polynomial_size,
|
||||
cm_dimension: self.cm_dimension,
|
||||
ciphertext_modulus: self.ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> CmGlweCiphertext<C> {
|
||||
pub fn get_mut_mask_and_bodies(
|
||||
&mut self,
|
||||
) -> (GlweMask<&mut [Scalar]>, GlweBodyList<&mut [Scalar]>) {
|
||||
let glwe_dimension = self.glwe_dimension();
|
||||
let polynomial_size = self.polynomial_size();
|
||||
let ciphertext_modulus = self.ciphertext_modulus();
|
||||
|
||||
let (mask, bodies) = self
|
||||
.data
|
||||
.as_mut()
|
||||
.split_at_mut(cm_glwe_ciphertext_mask_size(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
));
|
||||
|
||||
(
|
||||
GlweMask::from_container(mask, polynomial_size, ciphertext_modulus),
|
||||
GlweBodyList::from_container(bodies, polynomial_size, ciphertext_modulus),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_mut_bodies(&mut self) -> GlweBodyList<&mut [Scalar]> {
|
||||
let glwe_dimension = self.glwe_dimension();
|
||||
let polynomial_size = self.polynomial_size();
|
||||
let ciphertext_modulus = self.ciphertext_modulus();
|
||||
|
||||
let bodies = &mut self.data.as_mut()
|
||||
[cm_glwe_ciphertext_mask_size(glwe_dimension, polynomial_size)..];
|
||||
|
||||
GlweBodyList::from_container(bodies, polynomial_size, ciphertext_modulus)
|
||||
}
|
||||
|
||||
pub fn get_mut_mask(&mut self) -> GlweMask<&mut [Scalar]> {
|
||||
let polynomial_size = self.polynomial_size();
|
||||
let glwe_dimension = self.glwe_dimension();
|
||||
let ciphertext_modulus = self.ciphertext_modulus();
|
||||
|
||||
GlweMask::from_container(
|
||||
&mut self.as_mut()[0..cm_glwe_ciphertext_mask_size(glwe_dimension, polynomial_size)],
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_mut_polynomial_list(&mut self) -> PolynomialList<&'_ mut [Scalar]> {
|
||||
let polynomial_size = self.polynomial_size;
|
||||
PolynomialList::from_container(self.as_mut(), polynomial_size)
|
||||
}
|
||||
|
||||
pub fn as_mut_view(&mut self) -> CmGlweCiphertext<&'_ mut [Scalar]> {
|
||||
CmGlweCiphertext {
|
||||
data: self.data.as_mut(),
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
polynomial_size: self.polynomial_size,
|
||||
ciphertext_modulus: self.ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type CmGlweCiphertextOwned<Scalar> = CmGlweCiphertext<Vec<Scalar>>;
|
||||
|
||||
pub type CmGlweCiphertextView<'data, Scalar> = CmGlweCiphertext<&'data [Scalar]>;
|
||||
|
||||
pub type CmGlweCiphertextMutView<'data, Scalar> = CmGlweCiphertext<&'data mut [Scalar]>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> CmGlweCiphertextOwned<Scalar> {
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
Self::from_container(
|
||||
vec![fill_with; cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size)],
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmGlweCiphertextCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub cm_dimension: CmDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for CmGlweCiphertext<C>
|
||||
{
|
||||
type Metadata = CmGlweCiphertextCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let CmGlweCiphertextCreationMetadata {
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
Self::from_container(
|
||||
from,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
//! Module containing the definition of the CmGlweCiphertextList.
|
||||
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CmGlweCiphertextList<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for CmGlweCiphertextList<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for CmGlweCiphertextList<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmGlweCiphertextList<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
container.container_len().is_multiple_of(cm_glwe_ciphertext_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
)),
|
||||
"The provided container length is not valid. \
|
||||
It needs to be dividable by (glwe_dimension + cm_dimension) * polynomial_size. \
|
||||
Got container length: {}, glwe_dimension: {glwe_dimension:?}, cm_dimension: {cm_dimension:?}, polynomial_size: {polynomial_size:?}.",
|
||||
container.container_len()
|
||||
);
|
||||
Self {
|
||||
data: container,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn glwe_ciphertext_count(&self) -> GlweCiphertextCount {
|
||||
GlweCiphertextCount(
|
||||
self.data.container_len()
|
||||
/ cm_glwe_ciphertext_size(
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
}
|
||||
|
||||
pub type CmGlweCiphertextListOwned<Scalar> = CmGlweCiphertextList<Vec<Scalar>>;
|
||||
|
||||
pub type CmGlweCiphertextListView<'data, Scalar> = CmGlweCiphertextList<&'data [Scalar]>;
|
||||
|
||||
pub type CmGlweCiphertextListMutView<'data, Scalar> = CmGlweCiphertextList<&'data mut [Scalar]>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> CmGlweCiphertextListOwned<Scalar> {
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_count: GlweCiphertextCount,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
Self::from_container(
|
||||
vec![
|
||||
fill_with;
|
||||
cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size)
|
||||
* ciphertext_count.0
|
||||
],
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_fn<F>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_count: GlweCiphertextCount,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
mut fill_with: F,
|
||||
) -> Self
|
||||
where
|
||||
F: FnMut(usize, usize) -> Scalar,
|
||||
{
|
||||
let ciphertext_size =
|
||||
cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size);
|
||||
let container: Vec<_> = (0..ciphertext_count.0)
|
||||
.flat_map(move |i| (0..ciphertext_size).map(move |j| (i, j)))
|
||||
.map(|(i, j)| fill_with(i, j))
|
||||
.collect();
|
||||
Self::from_container(
|
||||
container,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmGlweCiphertextListCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub cm_dimension: CmDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for CmGlweCiphertextList<C>
|
||||
{
|
||||
type Metadata = CmGlweCiphertextListCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let CmGlweCiphertextListCreationMetadata {
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
Self::from_container(
|
||||
from,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for CmGlweCiphertextList<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = CmGlweCiphertextCreationMetadata<Self::Element>;
|
||||
|
||||
type EntityView<'this>
|
||||
= CmGlweCiphertextView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = CmGlweCiphertextListCreationMetadata<Self::Element>;
|
||||
|
||||
type SelfView<'this>
|
||||
= CmGlweCiphertextListView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
CmGlweCiphertextCreationMetadata {
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
polynomial_size: self.polynomial_size(),
|
||||
ciphertext_modulus: self.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
cm_glwe_ciphertext_size(
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size(),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
CmGlweCiphertextListCreationMetadata {
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
polynomial_size: self.polynomial_size(),
|
||||
ciphertext_modulus: self.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for CmGlweCiphertextList<C>
|
||||
{
|
||||
type EntityMutView<'this>
|
||||
= CmGlweCiphertextMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfMutView<'this>
|
||||
= CmGlweCiphertextListMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user