mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-11 07:38:08 -05:00
Compare commits
2 Commits
release/0.
...
tmp_ccs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03635afa8f | ||
|
|
4c659b5c70 |
12
.github/workflows/pbs_benchmark.yml
vendored
12
.github/workflows/pbs_benchmark.yml
vendored
@@ -66,9 +66,17 @@ jobs:
|
||||
toolchain: nightly
|
||||
override: true
|
||||
|
||||
- name: Run benchmarks with AVX512
|
||||
- name: bench_ac2023_CJP
|
||||
run: |
|
||||
make AVX512_SUPPORT=ON bench_pbs
|
||||
make AVX512_SUPPORT=ON bench_ac2023_CJP
|
||||
|
||||
- name: bench_ac2023_stairKS
|
||||
run: |
|
||||
make AVX512_SUPPORT=ON bench_ac2023_stairKS
|
||||
|
||||
- name: bench_ac2023_fastKS
|
||||
run: |
|
||||
make AVX512_SUPPORT=ON bench_ac2023_fastKS
|
||||
|
||||
- name: Parse results
|
||||
run: |
|
||||
|
||||
36
Makefile
36
Makefile
@@ -340,6 +340,24 @@ test_core_crypto: install_rs_build_toolchain install_rs_check_toolchain
|
||||
--features=$(TARGET_ARCH_FEATURE),experimental,$(AVX512_FEATURE) -p $(TFHE_SPEC) -- core_crypto::; \
|
||||
fi
|
||||
|
||||
.PHONY: test_ccs_2024_stair_ks # Run the tests of the core_crypto module including experimental ones
|
||||
test_ccs_2024_stair_ks: install_rs_build_toolchain install_rs_check_toolchain
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) \
|
||||
--features=$(TARGET_ARCH_FEATURE),experimental -p $(TFHE_SPEC) -- core_crypto::algorithms::test::lwe_stair_keyswitch
|
||||
@if [[ "$(AVX512_SUPPORT)" == "ON" ]]; then \
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) test --profile $(CARGO_PROFILE) \
|
||||
--features=$(TARGET_ARCH_FEATURE),experimental,$(AVX512_FEATURE) -p $(TFHE_SPEC) -- core_crypto::algorithms::test::lwe_stair_keyswitch; \
|
||||
fi
|
||||
|
||||
.PHONY: test_ccs_2024_fft_shrinking_ks # Run the tests of the core_crypto module including experimental ones
|
||||
test_ccs_2024_fft_shrinking_ks: install_rs_build_toolchain install_rs_check_toolchain
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) \
|
||||
--features=$(TARGET_ARCH_FEATURE),experimental -p $(TFHE_SPEC) -- core_crypto::algorithms::test::lwe_fast_keyswitch
|
||||
@if [[ "$(AVX512_SUPPORT)" == "ON" ]]; then \
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) test --profile $(CARGO_PROFILE) \
|
||||
--features=$(TARGET_ARCH_FEATURE),experimental,$(AVX512_FEATURE) -p $(TFHE_SPEC) -- core_crypto::algorithms::test::lwe_fast_keyswitch; \
|
||||
fi
|
||||
|
||||
.PHONY: test_boolean # Run the tests of the boolean module
|
||||
test_boolean: install_rs_build_toolchain
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) \
|
||||
@@ -606,6 +624,24 @@ ci_bench_web_js_api_parallel: build_web_js_api_parallel
|
||||
nvm use node && \
|
||||
$(MAKE) -C tfhe/web_wasm_parallel_tests bench-ci
|
||||
|
||||
.PHONY: bench_ccs_2024_cjp # Run benchmarks for PBS
|
||||
bench_ccs_2024_cjp: install_rs_check_toolchain
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
|
||||
--bench ccs-2024-cjp \
|
||||
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,internal-keycache,$(AVX512_FEATURE) -p $(TFHE_SPEC)
|
||||
|
||||
.PHONY: bench_ccs_2024_fft_shrinking_ks # Run benchmarks for PBS
|
||||
bench_ccs_2024_fft_shrinking_ks: install_rs_check_toolchain
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
|
||||
--bench ccs-2024-fft-shrinking-ks \
|
||||
--features=$(TARGET_ARCH_FEATURE),internal-keycache,$(AVX512_FEATURE) -p $(TFHE_SPEC)
|
||||
|
||||
.PHONY: bench_ccs_2024_stair_ks # Run benchmarks for PBS
|
||||
bench_ccs_2024_stair_ks: install_rs_check_toolchain
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
|
||||
--bench ccs-2024-stair-ks \
|
||||
--features=$(TARGET_ARCH_FEATURE),internal-keycache,$(AVX512_FEATURE) -p $(TFHE_SPEC)
|
||||
|
||||
#
|
||||
# Utility tools
|
||||
#
|
||||
|
||||
84
README_CCS.md
Normal file
84
README_CCS.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Description
|
||||
In what follows, we provide instructions on how to run benchmarks from the paper entitled "New Secret Keys for Enhanced Performance in (T)FHE". In particular, Table 2 and Figure 4 (in the Appendix) can be easily reproduced using this code.
|
||||
|
||||
The implementation of the techniques from the aforementioned paper has been integrated into the TFHE-rs library, version 0.4.
|
||||
|
||||
Modified or added source files are located in `tfhe/src/core_crypto/`:
|
||||
- `algorithms/pseudo_ggsw_encryption.rs`
|
||||
- `algorithms/pseudo_ggsw_conversion.rs`
|
||||
- `algorithms/lwe_shrinking_keyswitch_key_generation.rs`
|
||||
- `algorithms/lwe_shrinking_keyswitch.rs`
|
||||
- `algorithms/lwe_secret_key_generation.rs`
|
||||
- `algorithms/lwe_partial_secret_key_generation.rs`
|
||||
- `algorithms/lwe_fast_keyswitch_key_generation.rs`
|
||||
- `algorithms/lwe_fast_keyswitch.rs`
|
||||
- `algorithms/glwe_secret_key_generation.rs`
|
||||
- `algorithms/glwe_partial_secret_key_generation.rs`
|
||||
- `algorithms/glwe_partial_sample_extraction.rs`
|
||||
|
||||
Test files are located in `tfhe/src/core_crypto/algorithms/test`:
|
||||
- `lwe_stair_keyswitch.rs`
|
||||
- `lwe_fast_keyswitch.rs`
|
||||
|
||||
Benchmarks are located in `tfhe/benches/core_crypto`:
|
||||
- `ccs_2024_cjp.rs`
|
||||
- `ccs_2024_fft_shrinking_ks.rs`
|
||||
- `ccs_2024_stair_ks.rs`
|
||||
|
||||
# Dependencies
|
||||
Tested on Linux and Mac OS with Rust >= 1.75 (see [here](https://www.rust-lang.org/tools/install) a guide to install Rust).
|
||||
|
||||
# How to run benchmarks
|
||||
At the root of the project (i.e., in the TFHE-rs folder), enter the following commands to run the benchmarks:
|
||||
- `make bench_ccs_2024_cjp`: Returns the timings associated with the CJP-based bootstrapping (Table 2, Line 1 + Figure 4, blue line);
|
||||
- `make bench_ccs_2024_stair_ks`: Returns the timings associated with the "All+Stair-KS"-based bootstrapping (Table 2, Line 2 + Figure 4, red line);
|
||||
- `make bench_ccs_2024_fft_shrinking_ks`: Returns the timings associated with the "All+FFT Shrinking-KS"-based bootstrapping (Table 2, Line 3 + Figure 4, green line);
|
||||
|
||||
This outputs the timings depending on the input precision.
|
||||
Since large precision (>= 6 bits) might be long to execute, particularly on a laptop, these are disable by default. To choose which precision to launch, please uncomment lines associated to the parameter names into the `param_vec` variable, inside the `criterion_bench` function inside one of the benchmark files.
|
||||
|
||||
For instance, to launch only the precision 7 of the stair-KS benchmark, the correct `param_vec` variable (line 353 of `ccs_2024_stair_ks.rs`) looks like:
|
||||
```rust
|
||||
let param_vec = [
|
||||
// PRECISION_1_STAIR,
|
||||
// PRECISION_2_STAIR,
|
||||
// PRECISION_3_STAIR,
|
||||
// PRECISION_4_STAIR,
|
||||
// PRECISION_5_STAIR,
|
||||
// PRECISION_6_STAIR,
|
||||
PRECISION_7_STAIR
|
||||
// PRECISION_8_STAIR,
|
||||
// PRECISION_9_STAIR,
|
||||
// PRECISION_10_STAIR,
|
||||
// PRECISION_11_STAIR,
|
||||
];
|
||||
|
||||
```
|
||||
Running the command `make bench_ccs_2024_stair_ks`will give the correct benchmark.
|
||||
|
||||
|
||||
# How to run the tests
|
||||
At the root of the project (i.e., in the TFHE-rs folder), enter the following commands to run the tests:
|
||||
- `make test_ccs_2024_stair_ks`: Returns the timings associated with the "All+Stair-KS"-based bootstrapping (Table 2, Line 2 + Figure 4, red line);
|
||||
- `make test_ccs_2024_fft_shrinking_ks`: Returns the timings associated with the "All+FFT Shrinking-KS"-based bootstrapping (Table 2, Line 3 + Figure 4, green line);
|
||||
|
||||
As for the benchmarks, all precision are not enabled by default. To add precision in the test, associated parameters must be uncommented in the macro `create_parametrized_test!` (located at the end of each test file)
|
||||
|
||||
For instance, in the file `lwe_fast_keyswitch.rs`, testing only the precision will look like this:
|
||||
```rust
|
||||
create_parametrized_test!(lwe_encrypt_fast_ks_decrypt_custom_mod {
|
||||
PRECISION_1_FAST_KS,
|
||||
PRECISION_2_FAST_KS,
|
||||
PRECISION_3_FAST_KS,
|
||||
PRECISION_4_FAST_KS,
|
||||
PRECISION_5_FAST_KS,
|
||||
PRECISION_6_FAST_KS,
|
||||
PRECISION_7_FAST_KS,
|
||||
PRECISION_8_FAST_KS,
|
||||
// PRECISION_9_FAST_KS,
|
||||
PRECISION_10_FAST_KS
|
||||
// PRECISION_11_FAST_KS
|
||||
});
|
||||
```
|
||||
Please note that the last parameter MUST NOT be followed by a comma `,` to correctly compile.
|
||||
|
||||
@@ -185,6 +185,23 @@ rustdoc-args = ["--html-in-header", "katex-header.html"]
|
||||
# Benches #
|
||||
# #
|
||||
###########
|
||||
[[bench]]
|
||||
name = "ccs-2024-fft-shrinking-ks"
|
||||
path = "benches/core_crypto/ccs_2024_fft_shrinking_ks.rs"
|
||||
harness = false
|
||||
required-features = ["internal-keycache"]
|
||||
|
||||
[[bench]]
|
||||
name = "ccs-2024-cjp"
|
||||
path = "benches/core_crypto/ccs_2024_cjp.rs"
|
||||
harness = false
|
||||
required-features = ["internal-keycache"]
|
||||
|
||||
[[bench]]
|
||||
name = "ccs-2024-stair-ks"
|
||||
path = "benches/core_crypto/ccs_2024_stair_ks.rs"
|
||||
harness = false
|
||||
required-features = ["internal-keycache"]
|
||||
|
||||
[[bench]]
|
||||
name = "pbs-bench"
|
||||
|
||||
445
tfhe/benches/core_crypto/ccs_2024_cjp.rs
Normal file
445
tfhe/benches/core_crypto/ccs_2024_cjp.rs
Normal file
@@ -0,0 +1,445 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use tfhe::core_crypto::prelude::*;
|
||||
use tfhe::keycache::NamedParam;
|
||||
use tfhe::named_params_impl;
|
||||
|
||||
fn generate_accumulator<F, Scalar: UnsignedTorus + CastFrom<usize>>(
|
||||
polynomial_size: PolynomialSize,
|
||||
glwe_size: GlweSize,
|
||||
message_modulus: usize,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
delta: Scalar,
|
||||
f: F,
|
||||
) -> GlweCiphertextOwned<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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Rotate the accumulator
|
||||
accumulator_scalar.rotate_left(half_box_size);
|
||||
|
||||
let accumulator_plaintext = PlaintextList::from_container(accumulator_scalar);
|
||||
|
||||
allocate_and_trivially_encrypt_new_glwe_ciphertext(
|
||||
glwe_size,
|
||||
&accumulator_plaintext,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
named_params_impl!(
|
||||
CJPParam<u64> =>
|
||||
PRECISION_1_CJP,
|
||||
PRECISION_2_CJP,
|
||||
PRECISION_3_CJP,
|
||||
PRECISION_4_CJP,
|
||||
PRECISION_5_CJP,
|
||||
PRECISION_6_CJP,
|
||||
PRECISION_7_CJP,
|
||||
PRECISION_8_CJP,
|
||||
PRECISION_9_CJP,
|
||||
PRECISION_10_CJP,
|
||||
PRECISION_11_CJP,
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct CJPParam<Scalar: UnsignedTorus> {
|
||||
pub log_precision: usize,
|
||||
pub _log_mu: usize,
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub std_dev_bsk: StandardDev,
|
||||
pub lwe_dimension: LweDimension,
|
||||
pub ks1_lwe_modular_std_dev: StandardDev,
|
||||
pub pbs_level: DecompositionLevelCount,
|
||||
pub pbs_base_log: DecompositionBaseLog,
|
||||
pub ks1_level: DecompositionLevelCount,
|
||||
pub ks1_base_log: DecompositionBaseLog,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
// CJP
|
||||
// p,log(nu), k, N, stddev, n, stddev, br_l,br_b, ks_l,ks_b, cost
|
||||
// 1, 1, 5, 8, -31.07, 588, -12.66, 1, 15, 3, 3, 31445684
|
||||
// 2, 2, 6, 8, -37.88, 668, -14.79, 1, 18, 3, 4, 42387300
|
||||
// 3, 3, 4, 9, -51.49, 720, -16.17, 1, 21, 3, 4, 63333680
|
||||
// 4, 4, 2, 10, -51.49, 788, -17.98, 1, 23, 3, 4, 78607596
|
||||
// 5, 5, 1, 11, -51.49, 840, -19.36, 1, 23, 6, 3, 118525112
|
||||
// 6, 6, 1, 12, -62.00, 840, -19.36, 2, 14, 5, 3, 347139256
|
||||
// 7, 7, 1, 13, -62.00, 896, -20.85, 2, 15, 6, 3, 797064320
|
||||
// 8, 8, 1, 14, -62.00, 968, -22.77, 3, 11, 6, 3, 2351201336
|
||||
// 9, 9, 1, 15, -62.00, 1024, -24.26, 4, 9, 7, 3, 6510246912
|
||||
// 10, 10, 1, 16, -62.00, 1096, -26.17, 6, 6, 12, 2, 20813446072
|
||||
// 11, 11, 1, 17, -62.00, 1132, -27.13, 20, 2, 13, 2, 128439942036
|
||||
|
||||
pub const PRECISION_1_CJP: CJPParam<u64> = CJPParam {
|
||||
log_precision: 1,
|
||||
_log_mu: 1,
|
||||
glwe_dimension: GlweDimension(5),
|
||||
polynomial_size: PolynomialSize(256),
|
||||
std_dev_bsk: StandardDev(4.43606636507407e-10),
|
||||
lwe_dimension: LweDimension(588),
|
||||
ks1_lwe_modular_std_dev: StandardDev(0.000154511302974888),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(15),
|
||||
ks1_level: DecompositionLevelCount(3),
|
||||
ks1_base_log: DecompositionBaseLog(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// 2, 2, 6, 8, -37.88, 668, -14.79, 1, 18, 3, 4, 42387300
|
||||
pub const PRECISION_2_CJP: CJPParam<u64> = CJPParam {
|
||||
log_precision: 2,
|
||||
_log_mu: 2,
|
||||
glwe_dimension: GlweDimension(6),
|
||||
polynomial_size: PolynomialSize(256),
|
||||
std_dev_bsk: StandardDev(3.95351839879752e-12),
|
||||
lwe_dimension: LweDimension(668),
|
||||
ks1_lwe_modular_std_dev: StandardDev(0.0000352993220185940),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(18),
|
||||
ks1_level: DecompositionLevelCount(3),
|
||||
ks1_base_log: DecompositionBaseLog(4),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// 3, 3, 4, 9, -51.49, 720, -16.17, 1, 21, 3, 4, 63333680
|
||||
pub const PRECISION_3_CJP: CJPParam<u64> = CJPParam {
|
||||
log_precision: 3,
|
||||
_log_mu: 3,
|
||||
glwe_dimension: GlweDimension(4),
|
||||
polynomial_size: PolynomialSize(512),
|
||||
std_dev_bsk: StandardDev(3.16202663074765e-16),
|
||||
lwe_dimension: LweDimension(720),
|
||||
ks1_lwe_modular_std_dev: StandardDev(0.0000135626629816676),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(21),
|
||||
ks1_level: DecompositionLevelCount(3),
|
||||
ks1_base_log: DecompositionBaseLog(4),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// 4, 4, 2, 10, -51.49, 788, -17.98, 1, 23, 3, 4, 78607596
|
||||
pub const PRECISION_4_CJP: CJPParam<u64> = CJPParam {
|
||||
log_precision: 4,
|
||||
_log_mu: 4,
|
||||
glwe_dimension: GlweDimension(2),
|
||||
polynomial_size: PolynomialSize(1024),
|
||||
std_dev_bsk: StandardDev(3.16202663074765e-16),
|
||||
lwe_dimension: LweDimension(788),
|
||||
ks1_lwe_modular_std_dev: StandardDev(3.86794845500957e-6),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(23),
|
||||
ks1_level: DecompositionLevelCount(3),
|
||||
ks1_base_log: DecompositionBaseLog(4),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// 5, 5, 1, 11, -51.49, 840, -19.36, 1, 23, 6, 3, 118525112
|
||||
pub const PRECISION_5_CJP: CJPParam<u64> = CJPParam {
|
||||
log_precision: 5,
|
||||
_log_mu: 5,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(2048),
|
||||
std_dev_bsk: StandardDev(3.16202663074765e-16),
|
||||
lwe_dimension: LweDimension(840),
|
||||
ks1_lwe_modular_std_dev: StandardDev(1.48613849575138e-6),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(23),
|
||||
ks1_level: DecompositionLevelCount(6),
|
||||
ks1_base_log: DecompositionBaseLog(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// 6, 6, 1, 12, -62.00, 840, -19.36, 2, 14, 5, 3, 347139256
|
||||
pub const PRECISION_6_CJP: CJPParam<u64> = CJPParam {
|
||||
log_precision: 6,
|
||||
_log_mu: 6,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(4096),
|
||||
std_dev_bsk: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(840),
|
||||
ks1_lwe_modular_std_dev: StandardDev(1.48613849575138e-6),
|
||||
pbs_level: DecompositionLevelCount(2),
|
||||
pbs_base_log: DecompositionBaseLog(14),
|
||||
ks1_level: DecompositionLevelCount(5),
|
||||
ks1_base_log: DecompositionBaseLog(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// 7, 7, 1, 13, -62.00, 896, -20.85, 2, 15, 6, 3, 797064320
|
||||
pub const PRECISION_7_CJP: CJPParam<u64> = CJPParam {
|
||||
log_precision: 7,
|
||||
_log_mu: 7,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(8192),
|
||||
std_dev_bsk: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(896),
|
||||
ks1_lwe_modular_std_dev: StandardDev(5.29083953889772e-7),
|
||||
pbs_level: DecompositionLevelCount(2),
|
||||
pbs_base_log: DecompositionBaseLog(15),
|
||||
ks1_level: DecompositionLevelCount(6),
|
||||
ks1_base_log: DecompositionBaseLog(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// 8, 8, 1, 14, -62.00, 968, -22.77, 3, 11, 6, 3, 2351201336
|
||||
pub const PRECISION_8_CJP: CJPParam<u64> = CJPParam {
|
||||
log_precision: 8,
|
||||
_log_mu: 8,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(16384),
|
||||
std_dev_bsk: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(968),
|
||||
ks1_lwe_modular_std_dev: StandardDev(1.39812821058259e-7),
|
||||
pbs_level: DecompositionLevelCount(3),
|
||||
pbs_base_log: DecompositionBaseLog(11),
|
||||
ks1_level: DecompositionLevelCount(6),
|
||||
ks1_base_log: DecompositionBaseLog(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// 9, 9, 1, 15, -62.00, 1024, -24.26, 4, 9, 7, 3, 6510246912
|
||||
pub const PRECISION_9_CJP: CJPParam<u64> = CJPParam {
|
||||
log_precision: 9,
|
||||
_log_mu: 9,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(32768),
|
||||
std_dev_bsk: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(1024),
|
||||
ks1_lwe_modular_std_dev: StandardDev(4.97751187937479e-8),
|
||||
pbs_level: DecompositionLevelCount(4),
|
||||
pbs_base_log: DecompositionBaseLog(9),
|
||||
ks1_level: DecompositionLevelCount(7),
|
||||
ks1_base_log: DecompositionBaseLog(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// 10, 10, 1, 16, -62.00, 1096, -26.17, 6, 6, 12, 2, 20813446072
|
||||
pub const PRECISION_10_CJP: CJPParam<u64> = CJPParam {
|
||||
log_precision: 10,
|
||||
_log_mu: 10,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(65536),
|
||||
std_dev_bsk: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(1096),
|
||||
ks1_lwe_modular_std_dev: StandardDev(1.32447880680348e-8),
|
||||
pbs_level: DecompositionLevelCount(6),
|
||||
pbs_base_log: DecompositionBaseLog(6),
|
||||
ks1_level: DecompositionLevelCount(12),
|
||||
ks1_base_log: DecompositionBaseLog(2),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// 11, 11, 1, 17, -62.00, 1132, -27.13, 20, 2, 13, 2, 128439942036
|
||||
pub const PRECISION_11_CJP: CJPParam<u64> = CJPParam {
|
||||
log_precision: 11,
|
||||
_log_mu: 11,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(131072),
|
||||
std_dev_bsk: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(1132),
|
||||
ks1_lwe_modular_std_dev: StandardDev(6.80857487193794e-9),
|
||||
pbs_level: DecompositionLevelCount(20),
|
||||
pbs_base_log: DecompositionBaseLog(2),
|
||||
ks1_level: DecompositionLevelCount(13),
|
||||
ks1_base_log: DecompositionBaseLog(2),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
fn criterion_bench(c: &mut Criterion) {
|
||||
let param_vec = [
|
||||
PRECISION_1_CJP,
|
||||
PRECISION_2_CJP,
|
||||
PRECISION_3_CJP,
|
||||
PRECISION_4_CJP,
|
||||
PRECISION_5_CJP,
|
||||
PRECISION_6_CJP,
|
||||
PRECISION_7_CJP,
|
||||
PRECISION_8_CJP,
|
||||
PRECISION_9_CJP,
|
||||
PRECISION_10_CJP,
|
||||
PRECISION_11_CJP,
|
||||
];
|
||||
|
||||
for params in param_vec {
|
||||
let log_precision = params.log_precision;
|
||||
let _log_mu = params._log_mu;
|
||||
let glwe_dimension = params.glwe_dimension;
|
||||
let polynomial_size = params.polynomial_size;
|
||||
let std_dev_bsk = params.std_dev_bsk;
|
||||
let lwe_dimension = params.lwe_dimension;
|
||||
let ks1_lwe_modular_std_dev = params.ks1_lwe_modular_std_dev;
|
||||
let pbs_level = params.pbs_level;
|
||||
let pbs_base_mpg = params.pbs_base_log;
|
||||
let ks1_level = params.ks1_level;
|
||||
let ks1_base_log = params.ks1_base_log;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
|
||||
let precision = 1 << (log_precision);
|
||||
// let mu = 1<< _log_mu;
|
||||
// let carry = (mu*(precision -1) +1)/precision;
|
||||
// let log_carry = ((carry as f32).log2().ceil()) as usize;
|
||||
// let delta_log = 63 - (log_precision + log_carry);
|
||||
|
||||
//println!("Delta log = {:?}", delta_log);
|
||||
|
||||
let delta_log = 63 - log_precision;
|
||||
|
||||
//TODO: to randomize
|
||||
let msg = 1;
|
||||
let pt = Plaintext(msg << delta_log);
|
||||
|
||||
// Create the PRNG
|
||||
let mut seeder = new_seeder();
|
||||
let seeder = seeder.as_mut();
|
||||
let mut encryption_generator =
|
||||
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
let mut secret_generator =
|
||||
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
|
||||
let small_lwe_secret_key =
|
||||
allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
|
||||
let large_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
&mut secret_generator,
|
||||
);
|
||||
let large_lwe_secret_key = large_glwe_secret_key.clone().into_lwe_secret_key();
|
||||
|
||||
//KSK Generation
|
||||
let ksk_large_to_small = allocate_and_generate_new_lwe_keyswitch_key(
|
||||
&large_lwe_secret_key,
|
||||
&small_lwe_secret_key,
|
||||
ks1_base_log,
|
||||
ks1_level,
|
||||
ks1_lwe_modular_std_dev,
|
||||
ciphertext_modulus,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
//Encryption
|
||||
let mut large_lwe = LweCiphertext::new(
|
||||
0u64,
|
||||
large_lwe_secret_key.lwe_dimension().to_lwe_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
encrypt_lwe_ciphertext(
|
||||
&large_lwe_secret_key,
|
||||
&mut large_lwe,
|
||||
pt,
|
||||
std_dev_bsk,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
//KS
|
||||
let mut small_lwe =
|
||||
LweCiphertext::new(0u64, lwe_dimension.to_lwe_size(), ciphertext_modulus);
|
||||
|
||||
//PBS PART
|
||||
let mut bsk = LweBootstrapKey::new(
|
||||
0,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
pbs_base_mpg,
|
||||
pbs_level,
|
||||
lwe_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
par_generate_lwe_bootstrap_key(
|
||||
&small_lwe_secret_key,
|
||||
&large_glwe_secret_key,
|
||||
&mut bsk,
|
||||
std_dev_bsk,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let mut fbsk = FourierLweBootstrapKey::new(
|
||||
small_lwe_secret_key.lwe_dimension(),
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
pbs_base_mpg,
|
||||
pbs_level,
|
||||
);
|
||||
|
||||
convert_standard_lwe_bootstrap_key_to_fourier(&bsk, &mut fbsk);
|
||||
|
||||
drop(bsk);
|
||||
|
||||
let accumulator = generate_accumulator(
|
||||
polynomial_size,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
precision,
|
||||
ciphertext_modulus,
|
||||
1 << delta_log,
|
||||
|x| x & 1,
|
||||
);
|
||||
|
||||
let mut out_pbs_ct = LweCiphertext::new(
|
||||
0u64,
|
||||
large_lwe_secret_key.lwe_dimension().to_lwe_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let bench_id = format!("CJP::{}", params.name());
|
||||
|
||||
c.bench_function(&bench_id, |b| {
|
||||
b.iter(|| {
|
||||
keyswitch_lwe_ciphertext(&ksk_large_to_small, &large_lwe, &mut small_lwe);
|
||||
programmable_bootstrap_lwe_ciphertext(
|
||||
&small_lwe,
|
||||
&mut out_pbs_ct,
|
||||
&accumulator,
|
||||
&fbsk,
|
||||
);
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_bench);
|
||||
criterion_main!(benches);
|
||||
|
||||
// #[test]
|
||||
// fn test_CJP() {
|
||||
// let param_vec = [
|
||||
// PRECISION_1_CJP,
|
||||
// PRECISION_2_CJP,
|
||||
// PRECISION_3_CJP,
|
||||
// PRECISION_4_CJP,
|
||||
// PRECISION_5_CJP,
|
||||
// // PRECISION_6_CJP,
|
||||
// // PRECISION_7_CJP,
|
||||
// // PRECISION_8_CJP,
|
||||
// // PRECISION_9_CJP,
|
||||
// // PRECISION_10_CJP,
|
||||
// // PRECISION_11_CJP,
|
||||
// ];
|
||||
// for params in param_vec {
|
||||
// to_gen_test_CJP(params);
|
||||
// }
|
||||
// }
|
||||
581
tfhe/benches/core_crypto/ccs_2024_fft_shrinking_ks.rs
Normal file
581
tfhe/benches/core_crypto/ccs_2024_fft_shrinking_ks.rs
Normal file
@@ -0,0 +1,581 @@
|
||||
#![allow(clippy::excessive_precision)]
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use tfhe::core_crypto::prelude::*;
|
||||
use tfhe::keycache::NamedParam;
|
||||
use tfhe::named_params_impl;
|
||||
|
||||
fn generate_accumulator<F, Scalar: UnsignedTorus + CastFrom<usize>>(
|
||||
polynomial_size: PolynomialSize,
|
||||
glwe_size: GlweSize,
|
||||
message_modulus: usize,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
delta: Scalar,
|
||||
f: F,
|
||||
) -> GlweCiphertextOwned<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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Rotate the accumulator
|
||||
accumulator_scalar.rotate_left(half_box_size);
|
||||
|
||||
let accumulator_plaintext = PlaintextList::from_container(accumulator_scalar);
|
||||
|
||||
allocate_and_trivially_encrypt_new_glwe_ciphertext(
|
||||
glwe_size,
|
||||
&accumulator_plaintext,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
named_params_impl!(
|
||||
FastKSParam<u64> =>
|
||||
PRECISION_1_FAST_KS,
|
||||
PRECISION_2_FAST_KS,
|
||||
PRECISION_3_FAST_KS,
|
||||
PRECISION_4_FAST_KS,
|
||||
PRECISION_5_FAST_KS,
|
||||
PRECISION_6_FAST_KS,
|
||||
PRECISION_7_FAST_KS,
|
||||
PRECISION_8_FAST_KS,
|
||||
PRECISION_9_FAST_KS,
|
||||
PRECISION_10_FAST_KS,
|
||||
PRECISION_11_FAST_KS
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct FastKSParam<Scalar: UnsignedTorus> {
|
||||
pub log_precision: usize,
|
||||
pub _log_mu: usize,
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub phi_bsk: usize,
|
||||
pub std_dev_bsk: StandardDev,
|
||||
pub lwe_dimension: LweDimension,
|
||||
pub ks1_lwe_modular_std_dev: StandardDev,
|
||||
pub pbs_level: DecompositionLevelCount,
|
||||
pub pbs_base_log: DecompositionBaseLog,
|
||||
pub ks1_level: DecompositionLevelCount,
|
||||
pub ks1_base_log: DecompositionBaseLog,
|
||||
pub ks1_polynomial_size: PolynomialSize,
|
||||
pub ks_in_glwe_dimension: GlweDimension,
|
||||
pub phi_in: usize,
|
||||
pub ks_out_glwe_dimension: GlweDimension,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
pub const PRECISION_1_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: 1,
|
||||
_log_mu: 1,
|
||||
glwe_dimension: GlweDimension(5),
|
||||
polynomial_size: PolynomialSize(256),
|
||||
phi_bsk: 1280,
|
||||
std_dev_bsk: StandardDev(4.436066365074074e-10),
|
||||
lwe_dimension: LweDimension(534),
|
||||
ks1_lwe_modular_std_dev: StandardDev(0.0004192214045106218),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(15),
|
||||
ks1_level: DecompositionLevelCount(9),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(256),
|
||||
ks_in_glwe_dimension: GlweDimension(3),
|
||||
phi_in: 746,
|
||||
ks_out_glwe_dimension: GlweDimension(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
pub const PRECISION_2_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: 2,
|
||||
_log_mu: 2,
|
||||
glwe_dimension: GlweDimension(6),
|
||||
polynomial_size: PolynomialSize(256),
|
||||
phi_bsk: 1536,
|
||||
std_dev_bsk: StandardDev(3.953518398797519e-12),
|
||||
lwe_dimension: LweDimension(590),
|
||||
ks1_lwe_modular_std_dev: StandardDev(0.0001492480807729575),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(18),
|
||||
ks1_level: DecompositionLevelCount(11),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(1024),
|
||||
ks_in_glwe_dimension: GlweDimension(1),
|
||||
phi_in: 946,
|
||||
ks_out_glwe_dimension: GlweDimension(1),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
pub const PRECISION_3_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: 3,
|
||||
_log_mu: 3,
|
||||
glwe_dimension: GlweDimension(3),
|
||||
polynomial_size: PolynomialSize(512),
|
||||
phi_bsk: 1536,
|
||||
std_dev_bsk: StandardDev(3.953518398797519e-12),
|
||||
lwe_dimension: LweDimension(686),
|
||||
ks1_lwe_modular_std_dev: StandardDev(2.5308824029981747e-05),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(18),
|
||||
ks1_level: DecompositionLevelCount(13),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(1024),
|
||||
ks_in_glwe_dimension: GlweDimension(1),
|
||||
phi_in: 850,
|
||||
ks_out_glwe_dimension: GlweDimension(1),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
pub const PRECISION_4_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: 4,
|
||||
_log_mu: 4,
|
||||
glwe_dimension: GlweDimension(2),
|
||||
polynomial_size: PolynomialSize(1024),
|
||||
phi_bsk: 2048,
|
||||
std_dev_bsk: StandardDev(3.162026630747649e-16),
|
||||
lwe_dimension: LweDimension(682),
|
||||
ks1_lwe_modular_std_dev: StandardDev(2.7313997525878062e-05),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(23),
|
||||
ks1_level: DecompositionLevelCount(14),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(512),
|
||||
ks_in_glwe_dimension: GlweDimension(3),
|
||||
phi_in: 1366,
|
||||
ks_out_glwe_dimension: GlweDimension(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
pub const PRECISION_5_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: 5,
|
||||
_log_mu: 5,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(2048),
|
||||
phi_bsk: 2048,
|
||||
std_dev_bsk: StandardDev(3.162026630747649e-16),
|
||||
lwe_dimension: LweDimension(766),
|
||||
ks1_lwe_modular_std_dev: StandardDev(5.822216831056818e-06),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(23),
|
||||
ks1_level: DecompositionLevelCount(15),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(512),
|
||||
ks_in_glwe_dimension: GlweDimension(3),
|
||||
phi_in: 1282,
|
||||
ks_out_glwe_dimension: GlweDimension(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
pub const PRECISION_6_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: 6,
|
||||
_log_mu: 6,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(4096),
|
||||
phi_bsk: 2443,
|
||||
std_dev_bsk: StandardDev(2.168404344971009e-19),
|
||||
lwe_dimension: LweDimension(774),
|
||||
ks1_lwe_modular_std_dev: StandardDev(4.998754134591537e-06),
|
||||
pbs_level: DecompositionLevelCount(2),
|
||||
pbs_base_log: DecompositionBaseLog(14),
|
||||
ks1_level: DecompositionLevelCount(15),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(2048),
|
||||
ks_in_glwe_dimension: GlweDimension(1),
|
||||
phi_in: 1669,
|
||||
ks_out_glwe_dimension: GlweDimension(1),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
pub const PRECISION_7_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: 7,
|
||||
_log_mu: 7,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(8192),
|
||||
phi_bsk: 2443,
|
||||
std_dev_bsk: StandardDev(2.168404344971009e-19),
|
||||
lwe_dimension: LweDimension(818),
|
||||
ks1_lwe_modular_std_dev: StandardDev(2.2215530137414073e-06),
|
||||
pbs_level: DecompositionLevelCount(2),
|
||||
pbs_base_log: DecompositionBaseLog(14),
|
||||
ks1_level: DecompositionLevelCount(16),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(2048),
|
||||
ks_in_glwe_dimension: GlweDimension(1),
|
||||
phi_in: 1625,
|
||||
ks_out_glwe_dimension: GlweDimension(1),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
pub const PRECISION_8_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: 8,
|
||||
_log_mu: 8,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(16384),
|
||||
phi_bsk: 2443,
|
||||
std_dev_bsk: StandardDev(2.168404344971009e-19),
|
||||
lwe_dimension: LweDimension(854),
|
||||
ks1_lwe_modular_std_dev: StandardDev(1.1499479557902908e-06),
|
||||
pbs_level: DecompositionLevelCount(3),
|
||||
pbs_base_log: DecompositionBaseLog(11),
|
||||
ks1_level: DecompositionLevelCount(18),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(2048),
|
||||
ks_in_glwe_dimension: GlweDimension(1),
|
||||
phi_in: 1589,
|
||||
ks_out_glwe_dimension: GlweDimension(1),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
pub const PRECISION_9_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: 9,
|
||||
_log_mu: 9,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(32768),
|
||||
phi_bsk: 2443,
|
||||
std_dev_bsk: StandardDev(2.168404344971009e-19),
|
||||
lwe_dimension: LweDimension(902),
|
||||
ks1_lwe_modular_std_dev: StandardDev(4.7354340335704556e-07),
|
||||
pbs_level: DecompositionLevelCount(4),
|
||||
pbs_base_log: DecompositionBaseLog(8),
|
||||
ks1_level: DecompositionLevelCount(18),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(2048),
|
||||
ks_in_glwe_dimension: GlweDimension(1),
|
||||
phi_in: 1541,
|
||||
ks_out_glwe_dimension: GlweDimension(1),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
pub const PRECISION_10_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: 10,
|
||||
_log_mu: 10,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(65536),
|
||||
phi_bsk: 2443,
|
||||
std_dev_bsk: StandardDev(2.168404344971009e-19),
|
||||
lwe_dimension: LweDimension(938),
|
||||
ks1_lwe_modular_std_dev: StandardDev(2.434282602565751e-07),
|
||||
pbs_level: DecompositionLevelCount(6),
|
||||
pbs_base_log: DecompositionBaseLog(6),
|
||||
ks1_level: DecompositionLevelCount(20),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(512),
|
||||
ks_in_glwe_dimension: GlweDimension(3),
|
||||
phi_in: 1505,
|
||||
ks_out_glwe_dimension: GlweDimension(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
pub const PRECISION_11_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: 11,
|
||||
_log_mu: 11,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(131072),
|
||||
phi_bsk: 2443,
|
||||
std_dev_bsk: StandardDev(2.168404344971009e-19),
|
||||
lwe_dimension: LweDimension(1018),
|
||||
ks1_lwe_modular_std_dev: StandardDev(5.56131000242714e-08),
|
||||
pbs_level: DecompositionLevelCount(13),
|
||||
pbs_base_log: DecompositionBaseLog(3),
|
||||
ks1_level: DecompositionLevelCount(22),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(512),
|
||||
ks_in_glwe_dimension: GlweDimension(3),
|
||||
phi_in: 1425,
|
||||
ks_out_glwe_dimension: GlweDimension(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
fn criterion_bench(c: &mut Criterion) {
|
||||
let param_vec = [
|
||||
PRECISION_1_FAST_KS,
|
||||
PRECISION_2_FAST_KS,
|
||||
PRECISION_3_FAST_KS,
|
||||
PRECISION_4_FAST_KS,
|
||||
PRECISION_5_FAST_KS,
|
||||
PRECISION_6_FAST_KS,
|
||||
PRECISION_7_FAST_KS,
|
||||
PRECISION_8_FAST_KS,
|
||||
PRECISION_9_FAST_KS,
|
||||
PRECISION_10_FAST_KS,
|
||||
PRECISION_11_FAST_KS,
|
||||
];
|
||||
|
||||
for params in param_vec {
|
||||
let log_precision = params.log_precision;
|
||||
let _log_mu = params._log_mu;
|
||||
let glwe_dimension = params.glwe_dimension;
|
||||
let polynomial_size = params.polynomial_size;
|
||||
let phi_bsk = params.phi_bsk;
|
||||
let std_dev_bsk = params.std_dev_bsk;
|
||||
let lwe_dimension = params.lwe_dimension;
|
||||
let ks1_lwe_modular_std_dev = params.ks1_lwe_modular_std_dev;
|
||||
let pbs_level = params.pbs_level;
|
||||
let pbs_base_mpg = params.pbs_base_log;
|
||||
let ks1_level = params.ks1_level;
|
||||
let ks1_base_log = params.ks1_base_log;
|
||||
let ks1_polynomial_size = params.ks1_polynomial_size;
|
||||
let ks_in_glwe_dimension = params.ks_in_glwe_dimension;
|
||||
let phi_in = params.phi_in;
|
||||
let ks_out_glwe_dimension = params.ks_out_glwe_dimension;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
|
||||
let precision = 1 << (log_precision);
|
||||
let delta_log = 63 - log_precision;
|
||||
|
||||
//TODO: to randomize
|
||||
let msg = 1;
|
||||
let pt = Plaintext(msg << delta_log);
|
||||
|
||||
// Create the PRNG
|
||||
let mut seeder = new_seeder();
|
||||
let seeder = seeder.as_mut();
|
||||
let mut encryption_generator =
|
||||
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
let mut secret_generator =
|
||||
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
|
||||
//Shared and partial key generations
|
||||
let glwe_secret_key = allocate_and_generate_new_binary_partial_glwe_secret_key(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
PartialGlweSecretKeyRandomCoefCount(phi_bsk),
|
||||
&mut secret_generator,
|
||||
);
|
||||
|
||||
let large_lwe_secret_key = glwe_secret_key.clone().into_lwe_secret_key();
|
||||
|
||||
let mut large_glwe_secret_key_without_shared =
|
||||
GlweSecretKey::new_empty_key(0u64, ks_in_glwe_dimension, ks1_polynomial_size);
|
||||
|
||||
large_glwe_secret_key_without_shared.as_mut()[0..phi_bsk - lwe_dimension.0]
|
||||
.copy_from_slice(&glwe_secret_key.as_ref()[lwe_dimension.0..phi_bsk]);
|
||||
|
||||
let small_glwe_secret_key =
|
||||
allocate_and_generate_new_shared_glwe_secret_key_from_glwe_secret_key(
|
||||
// large_glwe_secret_key_without_shared.clone(),
|
||||
&glwe_secret_key,
|
||||
ks_out_glwe_dimension,
|
||||
lwe_dimension.0,
|
||||
ks1_polynomial_size,
|
||||
);
|
||||
|
||||
let mut ggsw = PseudoGgswCiphertext::new(
|
||||
0u64,
|
||||
ks_in_glwe_dimension.to_glwe_size(),
|
||||
ks_out_glwe_dimension.to_glwe_size(),
|
||||
ks1_polynomial_size,
|
||||
ks1_base_log,
|
||||
ks1_level,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
//Encryption
|
||||
let mut large_lwe = LweCiphertext::new(
|
||||
0u64,
|
||||
large_lwe_secret_key.lwe_dimension().to_lwe_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
encrypt_lwe_ciphertext(
|
||||
&large_lwe_secret_key,
|
||||
&mut large_lwe,
|
||||
pt,
|
||||
std_dev_bsk,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let mut split_large_lwe =
|
||||
LweCiphertext::new(0u64, lwe_dimension.to_lwe_size(), ciphertext_modulus);
|
||||
|
||||
split_large_lwe.as_mut()[0..lwe_dimension.0]
|
||||
.iter_mut()
|
||||
.zip(large_lwe.as_ref()[0..lwe_dimension.0].iter())
|
||||
.for_each(|(dst, &src)| *dst = src);
|
||||
|
||||
let body = split_large_lwe.get_mut_body().data;
|
||||
*body = *large_lwe.get_body().data;
|
||||
|
||||
let mut split_large_lwe_to_ks = LweCiphertext::new(
|
||||
0u64,
|
||||
LweSize(phi_bsk - lwe_dimension.0 + 1),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
split_large_lwe_to_ks.as_mut()[0..phi_bsk - lwe_dimension.0]
|
||||
.iter_mut()
|
||||
.zip(large_lwe.as_ref()[lwe_dimension.0..phi_bsk].iter())
|
||||
.for_each(|(dst, &src)| *dst = src);
|
||||
|
||||
let mut nks_kin_glwe = GlweCiphertext::new(
|
||||
0u64,
|
||||
ks_in_glwe_dimension.to_glwe_size(),
|
||||
ks1_polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
encrypt_pseudo_ggsw_ciphertext(
|
||||
&small_glwe_secret_key,
|
||||
&large_glwe_secret_key_without_shared,
|
||||
&mut ggsw,
|
||||
ks1_lwe_modular_std_dev,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
//Memory stuff
|
||||
let fft = Fft::new(ks1_polynomial_size);
|
||||
let fft = fft.as_view();
|
||||
let mut buffers = ComputationBuffers::new();
|
||||
|
||||
let buffer_size_req =
|
||||
add_external_product_fast_keyswitch_assign_mem_optimized_requirement::<u64>(
|
||||
small_glwe_secret_key.glwe_dimension(),
|
||||
ks1_polynomial_size,
|
||||
fft,
|
||||
)
|
||||
.unwrap()
|
||||
.unaligned_bytes_required();
|
||||
|
||||
let buffer_size_req = buffer_size_req.max(
|
||||
convert_standard_ggsw_ciphertext_to_fourier_mem_optimized_requirement(fft)
|
||||
.unwrap()
|
||||
.unaligned_bytes_required(),
|
||||
);
|
||||
|
||||
buffers.resize(10 * buffer_size_req);
|
||||
|
||||
//To Fourier
|
||||
let mut fourier_ggsw = PseudoFourierGgswCiphertext::new(
|
||||
ks_in_glwe_dimension.to_glwe_size(),
|
||||
ks_out_glwe_dimension.to_glwe_size(),
|
||||
ks1_polynomial_size,
|
||||
ks1_base_log,
|
||||
ks1_level,
|
||||
);
|
||||
|
||||
convert_standard_pseudo_ggsw_ciphertext_to_fourier_mem_optimized(
|
||||
&ggsw,
|
||||
&mut fourier_ggsw,
|
||||
fft,
|
||||
buffers.stack(),
|
||||
);
|
||||
|
||||
let mut glwe_after_ks = GlweCiphertext::new(
|
||||
0u64,
|
||||
ks_out_glwe_dimension.to_glwe_size(),
|
||||
ks1_polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let mut small_lwe_secret_key = LweSecretKey::new_empty_key(0u64, lwe_dimension);
|
||||
small_lwe_secret_key.as_mut()[0..lwe_dimension.0].copy_from_slice(
|
||||
&glwe_secret_key.clone().into_lwe_secret_key().as_ref()[0..lwe_dimension.0],
|
||||
);
|
||||
|
||||
let mut bsk = LweBootstrapKey::new(
|
||||
0,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
pbs_base_mpg,
|
||||
pbs_level,
|
||||
lwe_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
par_generate_lwe_bootstrap_key(
|
||||
&small_lwe_secret_key,
|
||||
&glwe_secret_key,
|
||||
&mut bsk,
|
||||
std_dev_bsk,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let mut fbsk = FourierLweBootstrapKey::new(
|
||||
small_lwe_secret_key.lwe_dimension(),
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
pbs_base_mpg,
|
||||
pbs_level,
|
||||
);
|
||||
|
||||
convert_standard_lwe_bootstrap_key_to_fourier(&bsk, &mut fbsk);
|
||||
|
||||
drop(bsk);
|
||||
|
||||
let accumulator = generate_accumulator(
|
||||
polynomial_size,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
precision,
|
||||
ciphertext_modulus,
|
||||
1 << delta_log,
|
||||
|x| x,
|
||||
);
|
||||
|
||||
let mut out_pbs_ct = LweCiphertext::new(
|
||||
0u64,
|
||||
large_lwe_secret_key.lwe_dimension().to_lwe_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let bench_id = format!("FAST_KS::{}", params.name());
|
||||
|
||||
c.bench_function(&bench_id, |b| {
|
||||
b.iter(|| {
|
||||
partial_convert_lwe_ciphertext_into_constant_glwe_ciphertext(
|
||||
&split_large_lwe_to_ks,
|
||||
&mut nks_kin_glwe,
|
||||
phi_in,
|
||||
);
|
||||
|
||||
let large_glwe_without_shared = nks_kin_glwe.clone();
|
||||
|
||||
add_external_product_fast_keyswitch_assign_mem_optimized(
|
||||
&mut glwe_after_ks,
|
||||
&fourier_ggsw,
|
||||
&large_glwe_without_shared,
|
||||
fft,
|
||||
buffers.stack(),
|
||||
);
|
||||
|
||||
//Encryption
|
||||
let mut lwe_after_partial_sample_extract =
|
||||
LweCiphertext::new(0u64, lwe_dimension.to_lwe_size(), ciphertext_modulus);
|
||||
partial_extract_lwe_sample_from_glwe_ciphertext(
|
||||
&glwe_after_ks,
|
||||
&mut lwe_after_partial_sample_extract,
|
||||
MonomialDegree(0),
|
||||
lwe_dimension.0,
|
||||
);
|
||||
|
||||
//Sum with initial ct
|
||||
lwe_after_partial_sample_extract.as_mut()[0..lwe_dimension.0]
|
||||
.iter_mut()
|
||||
.zip(split_large_lwe.as_ref()[0..lwe_dimension.0].iter())
|
||||
.for_each(|(dst, &src)| *dst = dst.wrapping_add(src));
|
||||
|
||||
let body = lwe_after_partial_sample_extract.get_mut_body().data;
|
||||
*body = body.wrapping_add(*split_large_lwe.get_body().data);
|
||||
|
||||
programmable_bootstrap_lwe_ciphertext(
|
||||
&lwe_after_partial_sample_extract,
|
||||
&mut out_pbs_ct,
|
||||
&accumulator,
|
||||
&fbsk,
|
||||
);
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_bench);
|
||||
criterion_main!(benches);
|
||||
575
tfhe/benches/core_crypto/ccs_2024_stair_ks.rs
Normal file
575
tfhe/benches/core_crypto/ccs_2024_stair_ks.rs
Normal file
@@ -0,0 +1,575 @@
|
||||
#![allow(clippy::excessive_precision)]
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use tfhe::core_crypto::prelude::*;
|
||||
use tfhe::keycache::NamedParam;
|
||||
use tfhe::named_params_impl;
|
||||
|
||||
fn generate_accumulator<F, Scalar: UnsignedTorus + CastFrom<usize>>(
|
||||
polynomial_size: PolynomialSize,
|
||||
glwe_size: GlweSize,
|
||||
message_modulus: usize,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
delta: Scalar,
|
||||
f: F,
|
||||
) -> GlweCiphertextOwned<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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Rotate the accumulator
|
||||
accumulator_scalar.rotate_left(half_box_size);
|
||||
|
||||
let accumulator_plaintext = PlaintextList::from_container(accumulator_scalar);
|
||||
|
||||
allocate_and_trivially_encrypt_new_glwe_ciphertext(
|
||||
glwe_size,
|
||||
&accumulator_plaintext,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
named_params_impl!(
|
||||
StairKSParam<u64> =>
|
||||
PRECISION_1_STAIR,
|
||||
PRECISION_2_STAIR,
|
||||
PRECISION_3_STAIR,
|
||||
PRECISION_4_STAIR,
|
||||
PRECISION_5_STAIR,
|
||||
PRECISION_6_STAIR,
|
||||
PRECISION_7_STAIR,
|
||||
PRECISION_8_STAIR,
|
||||
PRECISION_9_STAIR,
|
||||
PRECISION_10_STAIR,
|
||||
PRECISION_11_STAIR,
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct StairKSParam<Scalar: UnsignedTorus> {
|
||||
pub log_precision: usize,
|
||||
pub _log_mu: usize,
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub phi: usize,
|
||||
pub std_dev_bsk: StandardDev,
|
||||
pub lwe_dimension: LweDimension,
|
||||
pub ks1_lwe_modular_std_dev: StandardDev,
|
||||
pub ks2_lwe_modular_std_dev: StandardDev,
|
||||
pub pbs_level: DecompositionLevelCount,
|
||||
pub pbs_base_log: DecompositionBaseLog,
|
||||
pub ks1_level: DecompositionLevelCount,
|
||||
pub ks1_base_log: DecompositionBaseLog,
|
||||
pub ks2_level: DecompositionLevelCount,
|
||||
pub ks2_base_log: DecompositionBaseLog,
|
||||
pub size1: usize,
|
||||
pub size2: usize,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
// p,log(nu), k, N, phi, stddev, n, stddev, stddev, br_l,br_b, ksl1,ksb1, ksl2,ksb2, size1,
|
||||
// size2, cost 1, 1, 5, 8, 1280, -31.07, 532, -17.82, -11.17, 1, 15, 1, 9, 4,
|
||||
// 2, 498, 250, 26700580
|
||||
pub const PRECISION_1_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: 1,
|
||||
_log_mu: 1,
|
||||
glwe_dimension: GlweDimension(5),
|
||||
polynomial_size: PolynomialSize(256),
|
||||
phi: 1280,
|
||||
std_dev_bsk: StandardDev(4.43606636507407e-10),
|
||||
lwe_dimension: LweDimension(532),
|
||||
ks1_lwe_modular_std_dev: StandardDev(4.32160905950851e-6),
|
||||
ks2_lwe_modular_std_dev: StandardDev(0.000434005215413364),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(15),
|
||||
ks1_level: DecompositionLevelCount(1),
|
||||
ks1_base_log: DecompositionBaseLog(9),
|
||||
ks2_level: DecompositionLevelCount(4),
|
||||
ks2_base_log: DecompositionBaseLog(2),
|
||||
size1: 498,
|
||||
size2: 250,
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// p,log(nu), k, N, phi, stddev, n, stddev, stddev, br_l,br_b, ksl1,ksb1, ksl2,ksb2, size1,
|
||||
// size2, cost 2, 2, 6, 8, 1536, -37.88, 576, -20.85, -12.34, 1, 18, 1, 10, 5,
|
||||
// 2, 640, 320, 35091584
|
||||
pub const PRECISION_2_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: 2,
|
||||
_log_mu: 2,
|
||||
glwe_dimension: GlweDimension(6),
|
||||
polynomial_size: PolynomialSize(256),
|
||||
phi: 1536,
|
||||
std_dev_bsk: StandardDev(3.953518398797519e-12),
|
||||
lwe_dimension: LweDimension(576),
|
||||
ks1_lwe_modular_std_dev: StandardDev(5.290839538897724e-07),
|
||||
ks2_lwe_modular_std_dev: StandardDev(0.00019288117965414483),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(18),
|
||||
ks1_level: DecompositionLevelCount(1),
|
||||
ks1_base_log: DecompositionBaseLog(10),
|
||||
ks2_level: DecompositionLevelCount(5),
|
||||
ks2_base_log: DecompositionBaseLog(2),
|
||||
size1: 640,
|
||||
size2: 320,
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// p,log(nu), k, N, phi, stddev, n, stddev, stddev, br_l,br_b, ksl1,ksb1, ksl2,ksb2, size1,
|
||||
// size2, cost 3, 3, 3, 9, 1536, -37.88, 648, -22.13, -14.25, 1, 18, 2, 7, 6,
|
||||
// 2, 592, 296, 42686328
|
||||
pub const PRECISION_3_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: 3,
|
||||
_log_mu: 3,
|
||||
glwe_dimension: GlweDimension(3),
|
||||
polynomial_size: PolynomialSize(512),
|
||||
phi: 1536,
|
||||
std_dev_bsk: StandardDev(3.95351839879752e-12),
|
||||
lwe_dimension: LweDimension(648),
|
||||
ks1_lwe_modular_std_dev: StandardDev(2.17874395902014e-7),
|
||||
ks2_lwe_modular_std_dev: StandardDev(5.132424409507535e-05),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(18),
|
||||
ks1_level: DecompositionLevelCount(2),
|
||||
ks1_base_log: DecompositionBaseLog(7),
|
||||
ks2_level: DecompositionLevelCount(6),
|
||||
ks2_base_log: DecompositionBaseLog(2),
|
||||
size1: 592,
|
||||
size2: 296,
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// p,log(nu), k, N, phi, stddev, n, stddev, stddev, br_l,br_b, ksl1,ksb1, ksl2,ksb2, size1,
|
||||
// size2, cost 4, 4, 2, 10, 2048, -51.49, 664, -26.97, -14.68, 1, 22, 1, 13, 6,
|
||||
// 2, 922, 462, 65150660
|
||||
pub const PRECISION_4_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: 4,
|
||||
_log_mu: 4,
|
||||
glwe_dimension: GlweDimension(2),
|
||||
polynomial_size: PolynomialSize(1024),
|
||||
phi: 2048,
|
||||
std_dev_bsk: StandardDev(3.16202663074765e-16),
|
||||
lwe_dimension: LweDimension(664),
|
||||
ks1_lwe_modular_std_dev: StandardDev(7.60713313301797e-9),
|
||||
ks2_lwe_modular_std_dev: StandardDev(0.0000380960250519291),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(22),
|
||||
ks1_level: DecompositionLevelCount(1),
|
||||
ks1_base_log: DecompositionBaseLog(13),
|
||||
ks2_level: DecompositionLevelCount(6),
|
||||
ks2_base_log: DecompositionBaseLog(2),
|
||||
size1: 922,
|
||||
size2: 462,
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// p,log(nu), k, N, phi, stddev, n, stddev, stddev, br_l,br_b, ksl1,ksb1, ksl2,ksb2, size1,
|
||||
// size2, cost 5, 5, 1, 11, 2048, -51.49, 732, -28.17, -16.49, 1, 23, 2, 9, 7,
|
||||
// 2, 877, 439, 94962998
|
||||
pub const PRECISION_5_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: 5,
|
||||
_log_mu: 5,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(2048),
|
||||
phi: 2048,
|
||||
std_dev_bsk: StandardDev(3.16202663074765e-16),
|
||||
lwe_dimension: LweDimension(732),
|
||||
ks1_lwe_modular_std_dev: StandardDev(3.31119701700870e-9),
|
||||
ks2_lwe_modular_std_dev: StandardDev(0.0000108646407745138),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(23),
|
||||
ks1_level: DecompositionLevelCount(2),
|
||||
ks1_base_log: DecompositionBaseLog(9),
|
||||
ks2_level: DecompositionLevelCount(7),
|
||||
ks2_base_log: DecompositionBaseLog(2),
|
||||
size1: 877,
|
||||
size2: 439,
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// p,log(nu), k, N, phi, stddev, n, stddev, stddev, br_l,br_b, ksl1,ksb1, ksl2,ksb2, size1,
|
||||
// size2, cost 6, 6, 1, 12, 2443, -62.00, 748, -31.94, -16.91, 2, 14, 1, 16, 8,
|
||||
// 2, 1130, 565, 283238205
|
||||
pub const PRECISION_6_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: 6,
|
||||
_log_mu: 6,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(4096),
|
||||
phi: 2443,
|
||||
std_dev_bsk: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(748),
|
||||
ks1_lwe_modular_std_dev: StandardDev(2.42717974083759e-10),
|
||||
ks2_lwe_modular_std_dev: StandardDev(8.12050004923523e-6),
|
||||
pbs_level: DecompositionLevelCount(2),
|
||||
pbs_base_log: DecompositionBaseLog(14),
|
||||
ks1_level: DecompositionLevelCount(1),
|
||||
ks1_base_log: DecompositionBaseLog(16),
|
||||
ks2_level: DecompositionLevelCount(8),
|
||||
ks2_base_log: DecompositionBaseLog(2),
|
||||
size1: 1130,
|
||||
size2: 565,
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// p,log(nu), k, N, phi, stddev, n, stddev, stddev, br_l,br_b, ksl1,ksb1, ksl2,ksb2, size1,
|
||||
// size2, cost 7, 7, 1, 13, 2443, -62.00, 776, -32.45, -17.66, 2, 15, 2, 10, 16,
|
||||
// 1, 1111, 556, 614607038
|
||||
pub const PRECISION_7_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: 7,
|
||||
_log_mu: 7,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(8192),
|
||||
phi: 2443,
|
||||
std_dev_bsk: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(776),
|
||||
ks1_lwe_modular_std_dev: StandardDev(1.70442007475721e-10),
|
||||
ks2_lwe_modular_std_dev: StandardDev(4.82847821796524e-6),
|
||||
pbs_level: DecompositionLevelCount(2),
|
||||
pbs_base_log: DecompositionBaseLog(15),
|
||||
ks1_level: DecompositionLevelCount(2),
|
||||
ks1_base_log: DecompositionBaseLog(10),
|
||||
ks2_level: DecompositionLevelCount(16),
|
||||
ks2_base_log: DecompositionBaseLog(1),
|
||||
size1: 1111,
|
||||
size2: 556,
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// p,log(nu), k, N, phi, stddev, n, stddev, stddev, br_l,br_b, ksl1,ksb1, ksl2,ksb2, size1,
|
||||
// size2, cost 8, 8, 1, 14, 2443, -62.00, 816, -33.17, -18.72, 3, 11, 2, 9, 17,
|
||||
// 1, 1084, 543, 1795749574
|
||||
pub const PRECISION_8_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: 8,
|
||||
_log_mu: 8,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(16384),
|
||||
phi: 2443,
|
||||
std_dev_bsk: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(816),
|
||||
ks1_lwe_modular_std_dev: StandardDev(1.03474906781522e-10),
|
||||
ks2_lwe_modular_std_dev: StandardDev(2.31589295271883e-6),
|
||||
pbs_level: DecompositionLevelCount(3),
|
||||
pbs_base_log: DecompositionBaseLog(11),
|
||||
ks1_level: DecompositionLevelCount(2),
|
||||
ks1_base_log: DecompositionBaseLog(9),
|
||||
ks2_level: DecompositionLevelCount(17),
|
||||
ks2_base_log: DecompositionBaseLog(1),
|
||||
size1: 1084,
|
||||
size2: 543,
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// p,log(nu), k, N, phi, stddev, n, stddev, stddev, br_l,br_b, ksl1,ksb1, ksl2,ksb2, size1,
|
||||
// size2, cost 9, 9, 1, 15, 2443, -62.00, 860, -33.94, -19.89, 4, 8, 2, 10, 18,
|
||||
// 1, 1055, 528, 4992007842
|
||||
pub const PRECISION_9_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: 9,
|
||||
_log_mu: 9,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(32768),
|
||||
phi: 2443,
|
||||
std_dev_bsk: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(860),
|
||||
ks1_lwe_modular_std_dev: StandardDev(6.06794935209399e-11),
|
||||
ks2_lwe_modular_std_dev: StandardDev(1.02923225069468e-6),
|
||||
pbs_level: DecompositionLevelCount(4),
|
||||
pbs_base_log: DecompositionBaseLog(8),
|
||||
ks1_level: DecompositionLevelCount(2),
|
||||
ks1_base_log: DecompositionBaseLog(10),
|
||||
ks2_level: DecompositionLevelCount(18),
|
||||
ks2_base_log: DecompositionBaseLog(1),
|
||||
size1: 1055,
|
||||
size2: 528,
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// p,log(nu), k, N, phi, stddev, n, stddev, stddev, br_l,br_b, ksl1,ksb1, ksl2,ksb2, size1,
|
||||
// size2, cost 10, 10, 1, 16, 2443, -62.00, 904, -34.71, -21.06, 6, 6, 2, 11, 19,
|
||||
// 1, 1026, 513, 15555548076
|
||||
pub const PRECISION_10_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: 10,
|
||||
_log_mu: 10,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(65536),
|
||||
phi: 2443,
|
||||
std_dev_bsk: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(904),
|
||||
ks1_lwe_modular_std_dev: StandardDev(3.55835153515238e-11),
|
||||
ks2_lwe_modular_std_dev: StandardDev(4.77994271508188e-14),
|
||||
pbs_level: DecompositionLevelCount(6),
|
||||
pbs_base_log: DecompositionBaseLog(6),
|
||||
ks1_level: DecompositionLevelCount(2),
|
||||
ks1_base_log: DecompositionBaseLog(11),
|
||||
ks2_level: DecompositionLevelCount(19),
|
||||
ks2_base_log: DecompositionBaseLog(1),
|
||||
size1: 1026,
|
||||
size2: 513,
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
// p,log(nu), k, N, phi, stddev, n, stddev, stddev, br_l,br_b, ksl1,ksb1, ksl2,ksb2, size1,
|
||||
// size2, cost 11, 11, 1, 17, 2443, -62.00, 984, -36.15, -23.19, 12, 3, 2, 11, 21,
|
||||
// 1, 972, 487, 66586908138
|
||||
pub const PRECISION_11_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: 11,
|
||||
_log_mu: 11,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(131072),
|
||||
phi: 2443,
|
||||
std_dev_bsk: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(984),
|
||||
ks1_lwe_modular_std_dev: StandardDev(1.31149203314392e-11),
|
||||
ks2_lwe_modular_std_dev: StandardDev(1.04499545254235e-7),
|
||||
pbs_level: DecompositionLevelCount(12),
|
||||
pbs_base_log: DecompositionBaseLog(3),
|
||||
ks1_level: DecompositionLevelCount(2),
|
||||
ks1_base_log: DecompositionBaseLog(11),
|
||||
ks2_level: DecompositionLevelCount(21),
|
||||
ks2_base_log: DecompositionBaseLog(1),
|
||||
size1: 972,
|
||||
size2: 487,
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
fn criterion_bench(c: &mut Criterion) {
|
||||
let param_vec = [
|
||||
PRECISION_1_STAIR,
|
||||
PRECISION_2_STAIR,
|
||||
PRECISION_3_STAIR,
|
||||
PRECISION_4_STAIR,
|
||||
PRECISION_5_STAIR,
|
||||
PRECISION_6_STAIR,
|
||||
PRECISION_7_STAIR,
|
||||
PRECISION_8_STAIR,
|
||||
PRECISION_9_STAIR,
|
||||
PRECISION_10_STAIR,
|
||||
PRECISION_11_STAIR,
|
||||
];
|
||||
|
||||
for params in param_vec {
|
||||
let log_precision = params.log_precision;
|
||||
let _log_mu = params._log_mu;
|
||||
let glwe_dimension = params.glwe_dimension;
|
||||
let polynomial_size = params.polynomial_size;
|
||||
let std_dev_bsk = params.std_dev_bsk;
|
||||
let lwe_dimension = params.lwe_dimension;
|
||||
let ks1_lwe_modular_std_dev = params.ks1_lwe_modular_std_dev;
|
||||
let ks2_lwe_modular_std_dev = params.ks2_lwe_modular_std_dev;
|
||||
let pbs_level = params.pbs_level;
|
||||
let pbs_base_mpg = params.pbs_base_log;
|
||||
let ks1_level = params.ks1_level;
|
||||
let ks1_base_log = params.ks1_base_log;
|
||||
let ks2_level = params.ks2_level;
|
||||
let ks2_base_log = params.ks2_base_log;
|
||||
let size1 = params.size1;
|
||||
let size2 = params.size2;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
|
||||
let precision = 1 << (log_precision);
|
||||
// let mu = 1<< _log_mu;
|
||||
// let carry = (mu*(precision -1) +1)/precision;
|
||||
// let log_carry = ((carry as f32).log2().ceil()) as usize;
|
||||
// let delta_log = 63 - (log_precision + log_carry);
|
||||
|
||||
let delta_log = 63 - log_precision;
|
||||
|
||||
//TODO: to randomize
|
||||
let msg = 1;
|
||||
let pt = Plaintext(msg << delta_log);
|
||||
|
||||
// Create the PRNG
|
||||
let mut seeder = new_seeder();
|
||||
let seeder = seeder.as_mut();
|
||||
let mut encryption_generator =
|
||||
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
let mut secret_generator =
|
||||
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
|
||||
//Shared and partial key generations
|
||||
let large_lwe_dimension_phi = LweDimension(lwe_dimension.0 + size1 + size2);
|
||||
let large_lwe_dimension = LweDimension(glwe_dimension.0 * polynomial_size.0);
|
||||
|
||||
let glwe_secret_key = allocate_and_generate_new_binary_partial_glwe_secret_key(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
PartialGlweSecretKeyRandomCoefCount(large_lwe_dimension_phi.0),
|
||||
&mut secret_generator,
|
||||
);
|
||||
|
||||
let large_lwe_secret_key = glwe_secret_key.clone().into_lwe_secret_key();
|
||||
|
||||
// let large_lwe_secret_key = allocate_and_generate_new_binary_lwe_partial_secret_key
|
||||
// (large_lwe_dimension, &mut secret_generator, large_lwe_dimension_phi);
|
||||
// large_lwe_dimension_phi - size1 == lwe_dimension.0 + size2
|
||||
let inter_phi = lwe_dimension.0 + size2;
|
||||
let inter_lwe_secret_key =
|
||||
allocate_and_generate_new_shared_lwe_secret_key_from_lwe_secret_key(
|
||||
&large_lwe_secret_key,
|
||||
SharedLweSecretKeyCommonCoefCount(inter_phi),
|
||||
);
|
||||
|
||||
let small_lwe_secret_key =
|
||||
allocate_and_generate_new_shared_lwe_secret_key_from_lwe_secret_key(
|
||||
&inter_lwe_secret_key,
|
||||
SharedLweSecretKeyCommonCoefCount(lwe_dimension.0),
|
||||
);
|
||||
|
||||
// println!("Large Key Dimension = {:?}",large_lwe_secret_key.lwe_dimension().0);
|
||||
// println!("Inter Key Dimension = {:?}",inter_lwe_secret_key.lwe_dimension().0);
|
||||
// println!("Small Key Dimension = {:?}",small_lwe_secret_key.lwe_dimension().0);
|
||||
//
|
||||
//
|
||||
//
|
||||
// println!("Large phi = {:?}",large_lwe_dimension_phi);
|
||||
// println!("Inter phi = {:?}",inter_phi);
|
||||
|
||||
//Shrinking KSK generations
|
||||
let ksk_large_to_inter = allocate_and_generate_new_lwe_shrinking_keyswitch_key(
|
||||
&large_lwe_secret_key,
|
||||
&inter_lwe_secret_key,
|
||||
SharedLweSecretKeyCommonCoefCount(inter_phi),
|
||||
ks1_base_log,
|
||||
ks1_level,
|
||||
ks1_lwe_modular_std_dev,
|
||||
ciphertext_modulus,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let ksk_inter_to_small = allocate_and_generate_new_lwe_shrinking_keyswitch_key(
|
||||
&inter_lwe_secret_key,
|
||||
&small_lwe_secret_key,
|
||||
SharedLweSecretKeyCommonCoefCount(lwe_dimension.0),
|
||||
ks2_base_log,
|
||||
ks2_level,
|
||||
ks2_lwe_modular_std_dev,
|
||||
ciphertext_modulus,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
//Encryption
|
||||
let mut large_lwe =
|
||||
LweCiphertext::new(0u64, large_lwe_dimension.to_lwe_size(), ciphertext_modulus);
|
||||
|
||||
encrypt_lwe_ciphertext(
|
||||
&large_lwe_secret_key,
|
||||
&mut large_lwe,
|
||||
pt,
|
||||
std_dev_bsk,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
//Shrinking KS
|
||||
let mut inter_lwe =
|
||||
LweCiphertext::new(0, LweDimension(inter_phi).to_lwe_size(), ciphertext_modulus);
|
||||
|
||||
let mut small_lwe = LweCiphertext::new(0, lwe_dimension.to_lwe_size(), ciphertext_modulus);
|
||||
|
||||
//PBS PART
|
||||
|
||||
let mut bsk = LweBootstrapKey::new(
|
||||
0,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
pbs_base_mpg,
|
||||
pbs_level,
|
||||
lwe_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
par_generate_lwe_bootstrap_key(
|
||||
&small_lwe_secret_key,
|
||||
&glwe_secret_key,
|
||||
&mut bsk,
|
||||
std_dev_bsk,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let mut fbsk = FourierLweBootstrapKey::new(
|
||||
small_lwe_secret_key.lwe_dimension(),
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
pbs_base_mpg,
|
||||
pbs_level,
|
||||
);
|
||||
|
||||
convert_standard_lwe_bootstrap_key_to_fourier(&bsk, &mut fbsk);
|
||||
|
||||
drop(bsk);
|
||||
|
||||
let accumulator = generate_accumulator(
|
||||
polynomial_size,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
precision,
|
||||
ciphertext_modulus,
|
||||
1 << delta_log,
|
||||
|x| x,
|
||||
);
|
||||
|
||||
let mut out_pbs_ct = LweCiphertext::new(
|
||||
0u64,
|
||||
large_lwe_secret_key.lwe_dimension().to_lwe_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let decomposer = SignedDecomposer::new(
|
||||
DecompositionBaseLog(log_precision + 1),
|
||||
DecompositionLevelCount(1),
|
||||
);
|
||||
|
||||
shrinking_keyswitch_lwe_ciphertext(&ksk_large_to_inter, &large_lwe, &mut inter_lwe);
|
||||
|
||||
let dec_inter = decrypt_lwe_ciphertext(&inter_lwe_secret_key, &inter_lwe);
|
||||
let decoded = decomposer.closest_representable(dec_inter.0) >> delta_log;
|
||||
assert_eq!(decoded, msg, "Err after first shrinking KS");
|
||||
|
||||
shrinking_keyswitch_lwe_ciphertext(&ksk_inter_to_small, &inter_lwe, &mut small_lwe);
|
||||
|
||||
let dec_small = decrypt_lwe_ciphertext(&small_lwe_secret_key, &small_lwe);
|
||||
let decoded = decomposer.closest_representable(dec_small.0) >> delta_log;
|
||||
assert_eq!(decoded, msg, "Err after second shrinking KS");
|
||||
|
||||
programmable_bootstrap_lwe_ciphertext(&small_lwe, &mut out_pbs_ct, &accumulator, &fbsk);
|
||||
|
||||
let dec_large = decrypt_lwe_ciphertext(&large_lwe_secret_key, &out_pbs_ct);
|
||||
let decoded = decomposer.closest_representable(dec_large.0) >> delta_log;
|
||||
assert_eq!(decoded, msg, "Err after PBS");
|
||||
|
||||
let bench_id = format!("stairKS::{}", params.name());
|
||||
|
||||
c.bench_function(&bench_id, |b| {
|
||||
b.iter(|| {
|
||||
shrinking_keyswitch_lwe_ciphertext(&ksk_large_to_inter, &large_lwe, &mut inter_lwe);
|
||||
|
||||
shrinking_keyswitch_lwe_ciphertext(&ksk_inter_to_small, &inter_lwe, &mut small_lwe);
|
||||
|
||||
programmable_bootstrap_lwe_ciphertext(
|
||||
&small_lwe,
|
||||
&mut out_pbs_ct,
|
||||
&accumulator,
|
||||
&fbsk,
|
||||
);
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_bench);
|
||||
criterion_main!(benches);
|
||||
@@ -853,3 +853,302 @@ where
|
||||
|
||||
Plaintext(decoded)
|
||||
}
|
||||
|
||||
// /// Encrypt a plaintext in a [`GGSW ciphertext`](`GgswCiphertext`) in the constant coefficient.
|
||||
// ///
|
||||
// /// See the [`GGSW ciphertext formal definition`](`GgswCiphertext#ggsw-encryption`) for the
|
||||
// /// definition of the encryption algorithm.
|
||||
// ///
|
||||
// /// ```
|
||||
// /// use tfhe::core_crypto::prelude::*;
|
||||
// ///
|
||||
// /// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
// /// // computations
|
||||
// /// // Define parameters for GgswCiphertext creation
|
||||
// /// let glwe_size = GlweSize(2);
|
||||
// /// let polynomial_size = PolynomialSize(1024);
|
||||
// /// let decomp_base_log = DecompositionBaseLog(8);
|
||||
// /// let decomp_level_count = DecompositionLevelCount(3);
|
||||
// /// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
|
||||
// /// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
// ///
|
||||
// /// // Create the PRNG
|
||||
// /// let mut seeder = new_seeder();
|
||||
// /// let seeder = seeder.as_mut();
|
||||
// /// let mut encryption_generator =
|
||||
// /// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
// /// let mut secret_generator =
|
||||
// /// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
// ///
|
||||
// /// // Create the GlweSecretKey
|
||||
// /// let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
// /// glwe_size.to_glwe_dimension(),
|
||||
// /// polynomial_size,
|
||||
// /// &mut secret_generator,
|
||||
// /// );
|
||||
// ///
|
||||
// /// // Create the plaintext
|
||||
// /// let plaintext = Plaintext(3u64);
|
||||
// ///
|
||||
// /// // Create a new GgswCiphertext
|
||||
// /// let mut ggsw = GgswCiphertext::new(
|
||||
// /// 0u64,
|
||||
// /// glwe_size,
|
||||
// /// polynomial_size,
|
||||
// /// decomp_base_log,
|
||||
// /// decomp_level_count,
|
||||
// /// ciphertext_modulus,
|
||||
// /// );
|
||||
// ///
|
||||
// /// encrypt_constant_ggsw_ciphertext(
|
||||
// /// &glwe_secret_key,
|
||||
// /// &mut ggsw,
|
||||
// /// plaintext,
|
||||
// /// glwe_modular_std_dev,
|
||||
// /// &mut encryption_generator,
|
||||
// /// );
|
||||
// ///
|
||||
// /// let decrypted = decrypt_constant_ggsw_ciphertext(&glwe_secret_key, &ggsw);
|
||||
// /// assert_eq!(decrypted, plaintext);
|
||||
// /// ```
|
||||
// pub fn encrypt_polynomial_ggsw_ciphertext<Scalar, KeyCont, OutputCont, Gen, PlainCont>(
|
||||
// glwe_secret_key: &GlweSecretKey<KeyCont>,
|
||||
// output: &mut GgswCiphertext<OutputCont>,
|
||||
// encoded: &mut PlaintextList<PlainCont>,
|
||||
// noise_parameters: impl DispersionParameter,
|
||||
// generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
// ) where Scalar: UnsignedTorus, KeyCont: Container<Element = Scalar>, PlainCont:
|
||||
// ContainerMut<Element = Scalar>, OutputCont: ContainerMut<Element = Scalar>, Gen:
|
||||
// ByteRandomGenerator,
|
||||
// {
|
||||
// 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_size().to_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_size().to_glwe_dimension(),
|
||||
// glwe_secret_key.glwe_dimension()
|
||||
// );
|
||||
//
|
||||
// // Generators used to have same sequential and parallel key generation
|
||||
// let gen_iter = generator
|
||||
// .fork_ggsw_to_ggsw_levels::<Scalar>(
|
||||
// output.decomposition_level_count(),
|
||||
// output.glwe_size(),
|
||||
// output.polynomial_size(),
|
||||
// )
|
||||
// .expect("Failed to split generator into ggsw levels");
|
||||
//
|
||||
// let output_glwe_size = output.glwe_size();
|
||||
// let output_polynomial_size = output.polynomial_size();
|
||||
// let decomp_base_log = output.decomposition_base_log();
|
||||
// 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(level_index + 1);
|
||||
// // We scale the factor down from the native torus to whatever our torus is, the
|
||||
// // encryption process will scale it back up
|
||||
// let one= Scalar::ONE;
|
||||
// let factor = one
|
||||
// .wrapping_neg()
|
||||
// .wrapping_mul(Scalar::ONE << (Scalar::BITS - (decomp_base_log.0 * decomp_level.0)))
|
||||
// .wrapping_div(ciphertext_modulus.get_power_of_two_scaling_to_native_torus());
|
||||
//
|
||||
// slice_wrapping_scalar_mul_assign(encoded.as_mut_polynomial().into_container(),
|
||||
// factor);
|
||||
//
|
||||
// // We iterate over the rows of the level matrix, the last row needs special treatment
|
||||
// let gen_iter = generator
|
||||
// .fork_ggsw_level_to_glwe::<Scalar>(output_glwe_size, output_polynomial_size)
|
||||
// .expect("Failed to split generator into glwe");
|
||||
//
|
||||
// let last_row_index = level_matrix.glwe_size().0 - 1;
|
||||
//
|
||||
// for ((row_index, mut row_as_glwe), mut generator) in level_matrix
|
||||
// .as_mut_glwe_list()
|
||||
// .iter_mut()
|
||||
// .enumerate()
|
||||
// .zip(gen_iter)
|
||||
// {
|
||||
// encrypt_polynomial_ggsw_level_matrix_row(
|
||||
// glwe_secret_key,
|
||||
// (row_index, last_row_index),
|
||||
// &polynomial,
|
||||
// &mut row_as_glwe,
|
||||
// noise_parameters,
|
||||
// &mut generator,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// Convenience function to encrypt a row of a [`GgswLevelMatrix`] irrespective of the current
|
||||
// row /// being encrypted. Allows to share code between sequential
|
||||
// ([`encrypt_constant_ggsw_ciphertext`]) /// and parallel
|
||||
// ([`par_encrypt_constant_ggsw_ciphertext`]) variants of the GGSW ciphertext /// encryption.
|
||||
// ///
|
||||
// /// You probably don't want to use this function directly.
|
||||
// fn encrypt_polynomial_ggsw_level_matrix_row<Scalar, KeyCont, OutputCont, Gen>(
|
||||
// glwe_secret_key: &GlweSecretKey<KeyCont>,
|
||||
// (row_index, last_row_index): (usize, usize),
|
||||
// factor: &Polynomial<InCount>,
|
||||
// row_as_glwe: &mut GlweCiphertext<OutputCont>,
|
||||
// noise_parameters: impl DispersionParameter,
|
||||
// generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
// ) where Scalar: UnsignedTorus, KeyCont: Container<Element = Scalar>, OutputCont:
|
||||
// ContainerMut<Element = Scalar>, Gen: ByteRandomGenerator,
|
||||
// {
|
||||
// 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());
|
||||
//
|
||||
// // slice_wrapping_scalar_mul_assign(body.as_mut(), factor);
|
||||
// let mut tmp_body = Polynomial::new(Scalar::ZERO, glwe_secret_key.polynomial_size());
|
||||
// tmp_body.as_mut().copy_from_slice(body.as_ref());
|
||||
// polynomial_karatsuba_wrapping_mul(&mut body.as_mut_polynomial(), &tmp_body, factor);
|
||||
//
|
||||
// encrypt_glwe_ciphertext_assign(glwe_secret_key, row_as_glwe, noise_parameters,
|
||||
// generator); } else {
|
||||
// // The last row needs a slightly different treatment
|
||||
// let mut body = row_as_glwe.get_mut_body();
|
||||
//
|
||||
// body.as_mut().fill(Scalar::ZERO);
|
||||
// body.as_mut()[0] = factor.wrapping_neg();
|
||||
//
|
||||
// encrypt_glwe_ciphertext_assign(glwe_secret_key, row_as_glwe, noise_parameters,
|
||||
// generator); }
|
||||
// }
|
||||
//
|
||||
// /// Decrypt a [`GGSW ciphertext`](`GgswCiphertext`) only yielding the plaintext from the constant
|
||||
// /// term of the polynomial.
|
||||
// ///
|
||||
// /// # Example
|
||||
// ///
|
||||
// /// ```
|
||||
// /// use tfhe::core_crypto::prelude::*;
|
||||
// ///
|
||||
// /// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
// /// // computations
|
||||
// /// // Define parameters for GgswCiphertext creation
|
||||
// /// let glwe_size = GlweSize(2);
|
||||
// /// let polynomial_size = PolynomialSize(4);
|
||||
// /// let decomp_base_log = DecompositionBaseLog(8);
|
||||
// /// let decomp_level_count = DecompositionLevelCount(3);
|
||||
// /// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
|
||||
// /// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
// ///
|
||||
// /// // Create the PRNG
|
||||
// /// let mut seeder = new_seeder();
|
||||
// /// let seeder = seeder.as_mut();
|
||||
// /// let mut encryption_generator =
|
||||
// /// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
// /// let mut secret_generator =
|
||||
// /// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
// ///
|
||||
// /// // Create the GlweSecretKey
|
||||
// /// let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
// /// glwe_size.to_glwe_dimension(),
|
||||
// /// polynomial_size,
|
||||
// /// &mut secret_generator,
|
||||
// /// );
|
||||
// ///
|
||||
// /// // Create the plaintext
|
||||
// /// let plaintext = PlaintextList::new(3u64, PlaintextCount(4));
|
||||
// ///
|
||||
// /// // Create a new GgswCiphertext
|
||||
// /// let mut ggsw = GgswCiphertext::new(
|
||||
// /// 0u64,
|
||||
// /// glwe_size,
|
||||
// /// polynomial_size,
|
||||
// /// decomp_base_log,
|
||||
// /// decomp_level_count,
|
||||
// /// ciphertext_modulus,
|
||||
// /// );
|
||||
// ///
|
||||
// /// encrypt_polynomial_ggsw_ciphertext(
|
||||
// /// &glwe_secret_key,
|
||||
// /// &mut ggsw,
|
||||
// /// &plaintext,
|
||||
// /// glwe_modular_std_dev,
|
||||
// /// &mut encryption_generator,
|
||||
// /// );
|
||||
// ///
|
||||
// ///
|
||||
// /// let mut decrypted = PlaintextList::new(0u64, PlaintextCount(polynomial_size.0));
|
||||
// /// decrypt_polynomial_ggsw_ciphertext(&glwe_secret_key, &ggsw, &mut decrypted);
|
||||
// /// assert_eq!(decrypted, plaintext);
|
||||
// /// ```
|
||||
// pub fn decrypt_polynomial_ggsw_ciphertext<Scalar, KeyCont, InputCont, OutputCont>(
|
||||
// glwe_secret_key: &GlweSecretKey<KeyCont>,
|
||||
// ggsw_ciphertext: &GgswCiphertext<InputCont>,
|
||||
// output_plaintext_list: &mut PlaintextList<OutputCont>,
|
||||
// )
|
||||
// where
|
||||
// Scalar: UnsignedTorus,
|
||||
// KeyCont: Container<Element = Scalar>,
|
||||
// InputCont: Container<Element = Scalar>,
|
||||
// OutputCont: ContainerMut<Element = Scalar>,
|
||||
//
|
||||
// {
|
||||
// assert!(
|
||||
// ggsw_ciphertext.polynomial_size() == glwe_secret_key.polynomial_size(),
|
||||
// "Mismatch between polynomial sizes of input ciphertext and input secret key. \
|
||||
// Got {:?} in output, and {:?} in secret key.",
|
||||
// ggsw_ciphertext.polynomial_size(),
|
||||
// glwe_secret_key.polynomial_size()
|
||||
// );
|
||||
//
|
||||
// assert!(
|
||||
// ggsw_ciphertext.glwe_size().to_glwe_dimension() == glwe_secret_key.glwe_dimension(),
|
||||
// "Mismatch between GlweDimension of input ciphertext and input secret key. \
|
||||
// Got {:?} in output, and {:?} in secret key.",
|
||||
// ggsw_ciphertext.glwe_size().to_glwe_dimension(),
|
||||
// glwe_secret_key.glwe_dimension()
|
||||
// );
|
||||
//
|
||||
// let level_matrix = ggsw_ciphertext.last().unwrap();
|
||||
// let level_matrix_as_glwe_list = level_matrix.as_glwe_list();
|
||||
// let last_row = level_matrix_as_glwe_list.last().unwrap();
|
||||
// let decomp_level = ggsw_ciphertext.decomposition_level_count();
|
||||
//
|
||||
//
|
||||
// decrypt_glwe_ciphertext(glwe_secret_key, &last_row, output_plaintext_list);
|
||||
//
|
||||
// let decomp_base_log = ggsw_ciphertext.decomposition_base_log();
|
||||
//
|
||||
// let decomposer = SignedDecomposer::new(decomp_base_log, decomp_level);
|
||||
// let mut decoded = PlaintextList::new(Scalar::ZERO, PlaintextCount(ggsw_ciphertext
|
||||
// .polynomial_size().0));
|
||||
//
|
||||
//
|
||||
// for (plaintext_ref, mut decoded_ref) in output_plaintext_list.iter().zip(decoded.iter_mut
|
||||
// ()) {
|
||||
// //let plaintext_ref = decrypted_plaintext_list.get(0);
|
||||
//
|
||||
// // Glwe decryption maps to a smaller torus potentially, map back to the native torus
|
||||
// let rounded = decomposer.closest_representable(
|
||||
// (*plaintext_ref.0).wrapping_mul(
|
||||
// ggsw_ciphertext
|
||||
// .ciphertext_modulus()
|
||||
// .get_power_of_two_scaling_to_native_torus(),
|
||||
// ),
|
||||
// );
|
||||
// decoded_ref = PlaintextRefMut(& mut rounded.wrapping_div(Scalar::ONE << (Scalar::BITS -
|
||||
// (decomp_base_log.0 *
|
||||
// decomp_level.0))));
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
//! Module containing primitives pertaining to the operation usually referred to as a
|
||||
//! _sample extract_ in the literature. Allowing to extract a single
|
||||
//! [`LWE Ciphertext`](`LweCiphertext`) from a given [`GLWE ciphertext`](`GlweCiphertext`).
|
||||
|
||||
use crate::core_crypto::commons::numeric::UnsignedInteger;
|
||||
use crate::core_crypto::commons::parameters::MonomialDegree;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
/// Extract the nth coefficient from the body of a [`GLWE Ciphertext`](`GlweCiphertext`) as an
|
||||
/// [`LWE ciphertext`](`LweCiphertext`).
|
||||
///
|
||||
/// # Formal definition
|
||||
///
|
||||
/// This operation is usually referred to as a _sample extract_ in the literature.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for GlweCiphertext creation
|
||||
/// let glwe_size = GlweSize(2);
|
||||
/// let polynomial_size = PolynomialSize(8);
|
||||
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
/// let phi = PartialGlweSecretKeyRandomCoefCount(
|
||||
/// glwe_size.to_glwe_dimension().0 * polynomial_size.0 - 4,
|
||||
/// );
|
||||
///
|
||||
/// // Create the PRNG
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
/// let mut encryption_generator =
|
||||
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
/// let mut secret_generator =
|
||||
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
///
|
||||
/// // Create the GlweSecretKey
|
||||
/// let glwe_secret_key = allocate_and_generate_new_binary_partial_glwe_secret_key(
|
||||
/// glwe_size.to_glwe_dimension(),
|
||||
/// polynomial_size,
|
||||
/// phi,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
///
|
||||
/// // Create the plaintext
|
||||
/// let msg = 3u64;
|
||||
/// let encoded_msg = msg << 60;
|
||||
/// let mut plaintext_list = PlaintextList::new(encoded_msg, PlaintextCount(polynomial_size.0));
|
||||
///
|
||||
/// let special_value = 15;
|
||||
/// *plaintext_list.get_mut(0).0 = 15 << 60;
|
||||
///
|
||||
/// // Create a new GlweCiphertext
|
||||
/// let mut glwe = GlweCiphertext::new(0u64, glwe_size, polynomial_size, ciphertext_modulus);
|
||||
///
|
||||
/// encrypt_glwe_ciphertext(
|
||||
/// &glwe_secret_key,
|
||||
/// &mut glwe,
|
||||
/// &plaintext_list,
|
||||
/// glwe_modular_std_dev,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// // Now we get the equivalent LweSecretKey from the GlweSecretKey
|
||||
/// let equivalent_lwe_sk = glwe_secret_key.clone().into_lwe_secret_key();
|
||||
///
|
||||
/// let mut extracted_sample = LweCiphertext::new(
|
||||
/// 0u64,
|
||||
/// equivalent_lwe_sk.lwe_dimension().to_lwe_size(),
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// partial_extract_lwe_sample_from_glwe_ciphertext(
|
||||
/// &glwe,
|
||||
/// &mut extracted_sample,
|
||||
/// MonomialDegree(0),
|
||||
/// phi.0,
|
||||
/// );
|
||||
///
|
||||
/// let decrypted_plaintext = decrypt_lwe_ciphertext(&equivalent_lwe_sk, &extracted_sample);
|
||||
///
|
||||
/// // Round and remove encoding
|
||||
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
|
||||
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
|
||||
///
|
||||
/// let recovered_message = decomposer.closest_representable(decrypted_plaintext.0) >> 60;
|
||||
///
|
||||
/// // We check we recover our special value instead of the 3 stored in all other slots of the
|
||||
/// // GlweCiphertext
|
||||
/// assert_eq!(special_value, recovered_message);
|
||||
/// ```
|
||||
pub fn partial_extract_lwe_sample_from_glwe_ciphertext<Scalar, InputCont, OutputCont>(
|
||||
input_glwe: &GlweCiphertext<InputCont>,
|
||||
output_lwe: &mut LweCiphertext<OutputCont>,
|
||||
nth: MonomialDegree,
|
||||
phi: usize,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
// assert!(
|
||||
// input_glwe.glwe_size().to_glwe_dimension().0 * input_glwe.polynomial_size().0
|
||||
// == output_lwe.lwe_size().to_lwe_dimension().0,
|
||||
// "Mismatch between equivalent LweDimension of input ciphertext and output ciphertext. \
|
||||
// Got {:?} for input and {:?} for output.",
|
||||
// LweDimension(input_glwe.glwe_size().to_glwe_dimension().0 *
|
||||
// input_glwe.polynomial_size().0), output_lwe.lwe_size().to_lwe_dimension(),
|
||||
// );
|
||||
//
|
||||
// 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()
|
||||
// );
|
||||
|
||||
output_lwe.as_mut().fill(Scalar::ZERO);
|
||||
|
||||
// // We retrieve the bodies and masks of the two ciphertexts.
|
||||
let (mut lwe_mask, lwe_body) = output_lwe.get_mut_mask_and_body();
|
||||
let (glwe_mask, glwe_body) = input_glwe.get_mask_and_body();
|
||||
//
|
||||
// // We copy the body
|
||||
*lwe_body.data = glwe_body.as_ref()[nth.0];
|
||||
|
||||
//
|
||||
//We copy the mask (each polynomial is in the wrong order)
|
||||
lwe_mask.as_mut()[0..phi].copy_from_slice(&glwe_mask.as_ref()[0..phi]);
|
||||
|
||||
//
|
||||
//
|
||||
// // We compute the number of elements which must be
|
||||
// // turned into their opposite
|
||||
// let opposite_count = input_glwe.polynomial_size().0 - nth.0 - 1;
|
||||
//
|
||||
// // We loop through the polynomials
|
||||
// for lwe_mask_poly in lwe_mask
|
||||
// .as_mut()
|
||||
// .chunks_exact_mut(input_glwe.polynomial_size().0).filter(|x| x != 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);
|
||||
// }
|
||||
|
||||
// println!("GLWE MASK = {:?}", glwe_mask);
|
||||
|
||||
let big_n = input_glwe.polynomial_size().0;
|
||||
//let lwe_mask_out = LweMask::from_container(vec![0; phi], input_glwe.ciphertext_modulus());
|
||||
for i in 0..phi {
|
||||
let alpha = i / big_n;
|
||||
let beta = big_n.wrapping_sub(i) % big_n;
|
||||
//let gamma:u32 = (1 - ((beta == 0) as u32)) as u32;
|
||||
lwe_mask.as_mut()[i] = glwe_mask.as_polynomial_list().get(alpha)[beta];
|
||||
// let mut mask_coef = glwe_mask.as_polynomial_list().as_view().get
|
||||
// (alpha as usize).get(beta as usize).unwrap().to_owned();
|
||||
if beta != 0 {
|
||||
// println!("### IN LOOP BEFORE mask_coef = {:?}", mask_coef);
|
||||
// mask_coef = mask_coef.wrapping_neg();
|
||||
lwe_mask.as_mut()[i] = lwe_mask.as_mut()[i].wrapping_neg()
|
||||
// println!("### IN LOOP AFTER mask_coef = {:?}", mask_coef);
|
||||
}
|
||||
// container_mask_out[i] = mask_coef;
|
||||
}
|
||||
|
||||
// println!("container_mask_out = {:?}", container_mask_out);
|
||||
// lwe_mask.as_mut().iter_mut().zip(container_mask_out.iter()).for_each(|(dst, &src)|
|
||||
// *dst = src);
|
||||
// println!("LWE MASK OUTPUT = {:?}", lwe_mask);
|
||||
// println!("OUTPUT = {:?}", output_lwe.get_mask_and_body());
|
||||
}
|
||||
|
||||
/// This operation does the opposite of
|
||||
/// [`extract_lwe_sample_from_glwe_ciphertext`](`super::extract_lwe_sample_from_glwe_ciphertext`)
|
||||
/// and inserts the body of [`an LWE ciphertext`](`LweCiphertext`) in the first coefficient of
|
||||
/// [`a GLWE ciphertext`](`GlweCiphertext`) and fills the mask to have a valid GLWE ciphertext. The
|
||||
/// rest of the mask and body are filled with zeros.
|
||||
///
|
||||
/// For an `input_lwe` encrypted under [`an LWE secret key`](`super::super::entities::LweSecretKey`)
|
||||
/// that shares `phi` coefficients with [`an output GLWE secret
|
||||
/// key`](`super::super::entities::GlweSecretKey`), it only requires on the order of `phi`
|
||||
/// computations instead of `k * N` computations where `phi` is smaller than `k * N` (hence the
|
||||
/// partial name). The `output_glwe` can be decrypted with the output GLWE secret key which shares
|
||||
/// parts of its coefficients with the input LWE secret key.
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for GlweCiphertext creation
|
||||
/// let glwe_size = GlweSize(2);
|
||||
/// let polynomial_size = PolynomialSize(8);
|
||||
/// let lwe_dimension = LweDimension(glwe_size.to_glwe_dimension().0 * polynomial_size.0);
|
||||
/// let lwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
/// let phi = PartialGlweSecretKeyRandomCoefCount(2);
|
||||
///
|
||||
/// // Create the PRNG
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
/// let mut encryption_generator =
|
||||
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
/// let mut secret_generator =
|
||||
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
///
|
||||
/// // Create the GlweSecretKey
|
||||
/// let glwe_secret_key = allocate_and_generate_new_binary_partial_glwe_secret_key(
|
||||
/// glwe_size.to_glwe_dimension(),
|
||||
/// polynomial_size,
|
||||
/// phi,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
///
|
||||
/// println!("PARTIAL GLWE secret key = {:?}", glwe_secret_key);
|
||||
///
|
||||
/// let lwe_secret_key = glwe_secret_key.clone().into_lwe_secret_key();
|
||||
///
|
||||
/// println!("PARTIAL LWE secret key = {:?}", lwe_secret_key);
|
||||
///
|
||||
/// // Create the plaintext
|
||||
/// let msg = 3u64;
|
||||
/// let encoded_msg = msg << 60;
|
||||
/// let mut plaintext = Plaintext(encoded_msg);
|
||||
///
|
||||
/// // Create a new LweCiphertext
|
||||
/// let mut lwe = LweCiphertext::new(0u64, lwe_dimension.to_lwe_size(), ciphertext_modulus);
|
||||
///
|
||||
/// let mut glwe = GlweCiphertext::new(0u64, glwe_size, polynomial_size, ciphertext_modulus);
|
||||
///
|
||||
/// encrypt_lwe_ciphertext(
|
||||
/// &lwe_secret_key,
|
||||
/// &mut lwe,
|
||||
/// plaintext,
|
||||
/// lwe_modular_std_dev,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// partial_convert_lwe_ciphertext_into_constant_glwe_ciphertext(&lwe, &mut glwe, phi.0);
|
||||
///
|
||||
/// let mut output_plaintext_list = PlaintextList::new(0u64, PlaintextCount(polynomial_size.0));
|
||||
///
|
||||
/// decrypt_glwe_ciphertext(&glwe_secret_key, &glwe, &mut output_plaintext_list);
|
||||
///
|
||||
/// let decrypted_plaintext = output_plaintext_list.into_container()[0];
|
||||
/// // Round and remove encoding
|
||||
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
|
||||
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
|
||||
///
|
||||
/// let recovered_message = decomposer.closest_representable(decrypted_plaintext) >> 60;
|
||||
///
|
||||
/// // We check we recover our special value instead of the 3 stored in all other slots of the
|
||||
/// // GlweCiphertext
|
||||
/// assert_eq!(msg, recovered_message);
|
||||
/// ```
|
||||
pub fn partial_convert_lwe_ciphertext_into_constant_glwe_ciphertext<Scalar, InputCont, OutputCont>(
|
||||
input_lwe: &LweCiphertext<InputCont>,
|
||||
output_glwe: &mut GlweCiphertext<OutputCont>,
|
||||
phi: usize,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
assert!(phi <= input_lwe.lwe_size().to_lwe_dimension().0);
|
||||
assert!(phi <= output_glwe.get_mask().as_ref().len());
|
||||
|
||||
// B' is set to the LWE body only so the rest of the mask should be zeroed out
|
||||
// if the index is greater than the shared dimension then the mask element is zeroed out as well
|
||||
// So clear the output once and then run the algorithm
|
||||
output_glwe.as_mut().fill(Scalar::ZERO);
|
||||
|
||||
let big_n = output_glwe.polynomial_size().0;
|
||||
|
||||
// We retrieve the bodies and masks of the two ciphertexts.
|
||||
let (lwe_mask, lwe_body) = input_lwe.get_mask_and_body();
|
||||
let (mut glwe_mask, mut glwe_body) = output_glwe.get_mut_mask_and_body();
|
||||
|
||||
// We copy the body
|
||||
glwe_body.as_mut()[0] = *lwe_body.data;
|
||||
|
||||
let glwe_mask_slice = glwe_mask.as_mut();
|
||||
|
||||
for (i, &lwe_mask_element) in lwe_mask.as_ref()[..phi].iter().enumerate() {
|
||||
// alpha = index of the current polynomial being considered
|
||||
let alpha = i / big_n;
|
||||
// beta = index in the polynomial = 0 if i is the first element of the output polynomial
|
||||
// otherwise the end of the polynomial is reversed and negated
|
||||
// Example with N = 512
|
||||
// LWE: | 0 1 ... 511 | 512 513 ... 1023| ...
|
||||
// | |
|
||||
// | ________|
|
||||
// v |
|
||||
// GLWE: | 0 -511 ... -1 | 512 -1023 ... -513 | ...
|
||||
let beta = big_n.wrapping_sub(i) % big_n;
|
||||
if beta != 0 {
|
||||
glwe_mask_slice[alpha * big_n + beta] = lwe_mask_element.wrapping_neg();
|
||||
} else {
|
||||
glwe_mask_slice[alpha * big_n + beta] = lwe_mask_element;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//! Module containing primitives pertaining to the generation of
|
||||
//! [`GLWE secret keys`](`GlweSecretKey`).
|
||||
|
||||
use crate::core_crypto::commons::generators::SecretRandomGenerator;
|
||||
use crate::core_crypto::commons::math::random::{RandomGenerable, UniformBinary};
|
||||
use crate::core_crypto::commons::numeric::Numeric;
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
/// Fill a [`GLWE secret key`](`GlweSecretKey`) with a predefined number of uniformly random binary
|
||||
/// coefficients which can be smaller than the input key element count.
|
||||
pub fn generate_binary_partial_glwe_secret_key<Scalar, KeyCont, Gen>(
|
||||
glwe_secret_key: &mut GlweSecretKey<KeyCont>,
|
||||
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount,
|
||||
generator: &mut SecretRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: RandomGenerable<UniformBinary> + Numeric,
|
||||
KeyCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert!(
|
||||
partial_glwe_secret_key_fill.0 <= glwe_secret_key.as_ref().len(),
|
||||
"partial_glwe_secret_key_fill ({partial_glwe_secret_key_fill:?}) \
|
||||
must be smaller than the total glwe_secret_key length ({}).",
|
||||
glwe_secret_key.as_ref().len()
|
||||
);
|
||||
|
||||
// Generate random coefficients to partially fill the key
|
||||
generator.fill_slice_with_random_uniform_binary(
|
||||
&mut glwe_secret_key.as_mut()[..partial_glwe_secret_key_fill.0],
|
||||
);
|
||||
}
|
||||
|
||||
/// Allocate a new [`GLWE secret key`](`GlweSecretKey`) and fill it with uniformly random binary
|
||||
/// coefficients.
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// let glwe_size = GlweSize(2);
|
||||
/// let polynomial_size = PolynomialSize(8);
|
||||
/// let partial_glwe_secret_key_fill = PartialGlweSecretKeyRandomCoefCount(5);
|
||||
///
|
||||
/// // Create the PRNG
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
/// let mut secret_generator =
|
||||
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
///
|
||||
/// let mut glwe_secret_key: GlweSecretKeyOwned<u64> =
|
||||
/// allocate_and_generate_new_binary_partial_glwe_secret_key(
|
||||
/// glwe_size.to_glwe_dimension(),
|
||||
/// polynomial_size,
|
||||
/// partial_glwe_secret_key_fill,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
/// assert!(glwe_secret_key.as_ref()[partial_glwe_secret_key_fill.0..]
|
||||
/// .iter()
|
||||
/// .all(|x| *x == 0u64));
|
||||
/// ```
|
||||
pub fn allocate_and_generate_new_binary_partial_glwe_secret_key<Scalar, Gen>(
|
||||
glwe_dimension: GlweDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount,
|
||||
generator: &mut SecretRandomGenerator<Gen>,
|
||||
) -> GlweSecretKeyOwned<Scalar>
|
||||
where
|
||||
Scalar: RandomGenerable<UniformBinary> + Numeric,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let mut glwe_secret_key =
|
||||
GlweSecretKeyOwned::new_empty_key(Scalar::ZERO, glwe_dimension, polynomial_size);
|
||||
|
||||
generate_binary_partial_glwe_secret_key(
|
||||
&mut glwe_secret_key,
|
||||
partial_glwe_secret_key_fill,
|
||||
generator,
|
||||
);
|
||||
|
||||
glwe_secret_key
|
||||
}
|
||||
@@ -25,8 +25,8 @@ use rayon::prelude::*;
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for GlweCiphertext creation
|
||||
/// let glwe_size = GlweSize(2);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let glwe_size = GlweSize(4);
|
||||
/// let polynomial_size = PolynomialSize(4);
|
||||
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
@@ -51,11 +51,10 @@ use rayon::prelude::*;
|
||||
/// let mut plaintext_list = PlaintextList::new(encoded_msg, PlaintextCount(polynomial_size.0));
|
||||
///
|
||||
/// let special_value = 15;
|
||||
/// *plaintext_list.get_mut(42).0 = 15 << 60;
|
||||
/// *plaintext_list.get_mut(0).0 = 15 << 60;
|
||||
///
|
||||
/// // Create a new GlweCiphertext
|
||||
/// let mut glwe = GlweCiphertext::new(0u64, glwe_size, polynomial_size, ciphertext_modulus);
|
||||
///
|
||||
/// encrypt_glwe_ciphertext(
|
||||
/// &glwe_secret_key,
|
||||
/// &mut glwe,
|
||||
@@ -74,7 +73,7 @@ use rayon::prelude::*;
|
||||
/// );
|
||||
///
|
||||
/// // Here we chose to extract sample at index 42 (corresponding to the MonomialDegree(42))
|
||||
/// extract_lwe_sample_from_glwe_ciphertext(&glwe, &mut extracted_sample, MonomialDegree(42));
|
||||
/// extract_lwe_sample_from_glwe_ciphertext(&glwe, &mut extracted_sample, MonomialDegree(0));
|
||||
///
|
||||
/// let decrypted_plaintext = decrypt_lwe_ciphertext(&equivalent_lwe_sk, &extracted_sample);
|
||||
///
|
||||
|
||||
@@ -69,3 +69,49 @@ pub fn generate_binary_glwe_secret_key<Scalar, InCont, Gen>(
|
||||
{
|
||||
generator.fill_slice_with_random_uniform_binary(glwe_secret_key.as_mut())
|
||||
}
|
||||
|
||||
pub fn allocate_and_generate_new_binary_shared_glwe_secret_key<Scalar, Gen>(
|
||||
glwe_dimension_large: GlweDimension,
|
||||
glwe_dimension_small: GlweDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
generator: &mut SecretRandomGenerator<Gen>,
|
||||
) -> (GlweSecretKeyOwned<Scalar>, GlweSecretKeyOwned<Scalar>)
|
||||
where
|
||||
Scalar: RandomGenerable<UniformBinary> + Numeric,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let mut large_glwe_secret_key =
|
||||
GlweSecretKeyOwned::new_empty_key(Scalar::ZERO, glwe_dimension_large, polynomial_size);
|
||||
|
||||
generate_binary_glwe_secret_key(&mut large_glwe_secret_key, generator);
|
||||
|
||||
let mut small_glwe_secret_key =
|
||||
GlweSecretKeyOwned::new_empty_key(Scalar::ZERO, glwe_dimension_small, polynomial_size);
|
||||
small_glwe_secret_key
|
||||
.as_mut()
|
||||
.iter_mut()
|
||||
.zip(large_glwe_secret_key.as_ref().iter())
|
||||
.for_each(|(dst, &src)| *dst = src);
|
||||
(large_glwe_secret_key, small_glwe_secret_key)
|
||||
}
|
||||
|
||||
pub fn allocate_and_generate_new_shared_glwe_secret_key_from_glwe_secret_key<Scalar, InCont>(
|
||||
in_large_glwe_key: &GlweSecretKey<InCont>,
|
||||
glwe_dimension_out: GlweDimension,
|
||||
phi: usize,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> GlweSecretKeyOwned<Scalar>
|
||||
where
|
||||
Scalar: RandomGenerable<UniformBinary> + Numeric,
|
||||
InCont: Container<Element = Scalar>,
|
||||
{
|
||||
// let lwe_dimension_small = LweDimension(phi);
|
||||
let mut small_glwe_secret_key =
|
||||
GlweSecretKey::new_empty_key(Scalar::ZERO, glwe_dimension_out, polynomial_size);
|
||||
|
||||
small_glwe_secret_key.as_mut()[0..phi]
|
||||
.iter_mut()
|
||||
.zip(in_large_glwe_key.as_ref())
|
||||
.for_each(|(dst, &src)| *dst = src);
|
||||
small_glwe_secret_key
|
||||
}
|
||||
|
||||
234
tfhe/src/core_crypto/algorithms/lwe_fast_keyswitch.rs
Normal file
234
tfhe/src/core_crypto/algorithms/lwe_fast_keyswitch.rs
Normal file
@@ -0,0 +1,234 @@
|
||||
// //! Module containing primitives pertaining to the [`LWE programmable
|
||||
// //! bootstrap`](`LweBootstrapKey#programmable-bootstrapping`).
|
||||
//
|
||||
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::fft_impl::fft64::crypto::pseudo_ggsw::{
|
||||
add_external_product_pseudo_ggsw_assign as impl_add_external_product_assign,
|
||||
add_external_product_pseudo_ggsw_assign_scratch as impl_add_external_product_assign_scratch,
|
||||
PseudoFourierGgswCiphertext,
|
||||
};
|
||||
use crate::core_crypto::fft_impl::fft64::math::fft::FftView;
|
||||
use concrete_fft::c64;
|
||||
use dyn_stack::{PodStack, SizeOverflow, StackReq};
|
||||
//
|
||||
//
|
||||
// /// Memory optimized version of [`add_external_product_assign`], the caller must provide a
|
||||
// properly /// configured [`FftView`] object and a `PodStack` used as a memory buffer having a
|
||||
// capacity at /// least as large as the result of
|
||||
// [`add_external_product_assign_mem_optimized_requirement`]. ///
|
||||
// /// Compute the external product of `ggsw` and `glwe`, and add the result to `out`.
|
||||
// ///
|
||||
// /// Strictly speaking this function computes:
|
||||
// ///
|
||||
// /// ```text
|
||||
// /// out <- out + glwe * ggsw
|
||||
// /// ```
|
||||
// ///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for GgswCiphertext creation
|
||||
/// let glwe_size_out = GlweSize(2);
|
||||
/// let glwe_size_in = GlweSize(2);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let decomp_base_log = DecompositionBaseLog(8);
|
||||
/// let decomp_level_count = DecompositionLevelCount(2);
|
||||
/// let glwe_modular_std_dev = StandardDev(5.96046447753906e-25);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // Create the PRNG
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
/// let mut encryption_generator =
|
||||
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
/// let mut secret_generator =
|
||||
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
///
|
||||
/// // Create the GlweSecretKey
|
||||
/// let glwe_secret_key_out = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
/// glwe_size_out.to_glwe_dimension(),
|
||||
/// polynomial_size,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
/// //let glwe_secret_key_out = GlweSecretKey::new_empty_key(0u64, glwe_size_out
|
||||
/// //.to_glwe_dimension(),
|
||||
/// //polynomial_size);
|
||||
///
|
||||
/// // Create the GlweSecretKey
|
||||
/// let glwe_secret_key_in = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
/// glwe_size_in.to_glwe_dimension(),
|
||||
/// polynomial_size,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
/// //let mut cont = vec![0u64; polynomial_size.0*glwe_size_in.to_glwe_dimension().0];
|
||||
/// // cont[0] = 1;
|
||||
/// //let glwe_secret_key_in = GlweSecretKey::from_container(cont,polynomial_size);
|
||||
///
|
||||
/// // Create a new GgswCiphertext
|
||||
/// let mut ggsw = PseudoGgswCiphertext::new(
|
||||
/// 0u64,
|
||||
/// glwe_size_in,
|
||||
/// glwe_size_out,
|
||||
/// polynomial_size,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// encrypt_pseudo_ggsw_ciphertext(
|
||||
/// &glwe_secret_key_out,
|
||||
/// &glwe_secret_key_in,
|
||||
/// &mut ggsw,
|
||||
/// glwe_modular_std_dev,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// let ct_plaintext = Plaintext(3 << 60);
|
||||
///
|
||||
/// let ct_plaintexts = PlaintextList::new(ct_plaintext.0, PlaintextCount(polynomial_size.0));
|
||||
/// //let ct_cont = vec![0_u64; polynomial_size.0*glwe_size_in.0];
|
||||
/// //let mut ct = GlweCiphertext::from_container(ct_cont, polynomial_size, ciphertext_modulus);
|
||||
/// //ct.get_mut_body().as_mut_polynomial()[0] = 1<<60;
|
||||
/// let mut ct = GlweCiphertext::new(0u64, glwe_size_in, polynomial_size, ciphertext_modulus);
|
||||
///
|
||||
/// //trivially_encrypt_glwe_ciphertext(&mut ct, &ct_plaintexts);
|
||||
/// encrypt_glwe_ciphertext(
|
||||
/// &glwe_secret_key_in,
|
||||
/// &mut ct,
|
||||
/// &ct_plaintexts,
|
||||
/// glwe_modular_std_dev,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// let fft = Fft::new(polynomial_size);
|
||||
/// let fft = fft.as_view();
|
||||
/// let mut buffers = ComputationBuffers::new();
|
||||
///
|
||||
/// let buffer_size_req =
|
||||
/// add_external_product_fast_keyswitch_assign_mem_optimized_requirement::<u64>(
|
||||
/// glwe_size_out.to_glwe_dimension(),
|
||||
/// polynomial_size,
|
||||
/// fft,
|
||||
/// )
|
||||
/// .unwrap()
|
||||
/// .unaligned_bytes_required();
|
||||
///
|
||||
/// let buffer_size_req = buffer_size_req.max(
|
||||
/// convert_standard_ggsw_ciphertext_to_fourier_mem_optimized_requirement(fft)
|
||||
/// .unwrap()
|
||||
/// .unaligned_bytes_required(),
|
||||
/// );
|
||||
///
|
||||
/// buffers.resize(10 * buffer_size_req);
|
||||
///
|
||||
/// let mut fourier_ggsw = PseudoFourierGgswCiphertext::new(
|
||||
/// glwe_size_in,
|
||||
/// glwe_size_out,
|
||||
/// polynomial_size,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// );
|
||||
///
|
||||
/// convert_standard_pseudo_ggsw_ciphertext_to_fourier_mem_optimized(
|
||||
/// &ggsw,
|
||||
/// &mut fourier_ggsw,
|
||||
/// fft,
|
||||
/// buffers.stack(),
|
||||
/// );
|
||||
///
|
||||
/// //println!("Fourier GGSW = {:?}", fourier_ggsw);
|
||||
///
|
||||
/// let mut ct_out = GlweCiphertext::new(0u64, glwe_size_out, polynomial_size, ciphertext_modulus);
|
||||
///
|
||||
/// println!("Input Secret Key: {:?}\n", glwe_secret_key_in);
|
||||
/// println!("Ouput Secret Key: {:?}\n", glwe_secret_key_out);
|
||||
/// println!("Ct IN = {:?}\n", ct);
|
||||
/// println!("GGSW = {:?}\n", ggsw);
|
||||
/// println!("GGSW Fourier = {:?}\n", fourier_ggsw);
|
||||
///
|
||||
/// add_external_product_fast_keyswitch_assign_mem_optimized(
|
||||
/// &mut ct_out,
|
||||
/// &fourier_ggsw,
|
||||
/// &ct,
|
||||
/// fft,
|
||||
/// buffers.stack(),
|
||||
/// );
|
||||
///
|
||||
/// println!("Ct OUT = {:?}\n", ct_out);
|
||||
///
|
||||
/// let mut output_plaintext_list = PlaintextList::new(0u64, ct_plaintexts.plaintext_count());
|
||||
///
|
||||
/// decrypt_glwe_ciphertext(&glwe_secret_key_out, &ct_out, &mut output_plaintext_list);
|
||||
///
|
||||
/// let signed_decomposer =
|
||||
/// SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
|
||||
///
|
||||
/// output_plaintext_list
|
||||
/// .iter_mut()
|
||||
/// .for_each(|x| *x.0 = signed_decomposer.closest_representable(*x.0));
|
||||
///
|
||||
/// println!("PlaintextList OUT = {:?}\n", output_plaintext_list);
|
||||
///
|
||||
/// // As we cloned the input ciphertext for the output, the external product result is added to the
|
||||
/// // originally contained value, hence why we expect ct_plaintext + ct_plaintext * msg_ggsw
|
||||
/// //assert!(output_plaintext_list
|
||||
/// // .iter()
|
||||
/// // .all(|x| *x.0 == ct_plaintext.0), "{:?}", output_plaintext_list);
|
||||
/// assert_eq!(output_plaintext_list.into_container()[0], ct_plaintext.0);
|
||||
/// ```
|
||||
pub fn add_external_product_fast_keyswitch_assign_mem_optimized<
|
||||
Scalar,
|
||||
OutputGlweCont,
|
||||
InputGlweCont,
|
||||
GgswCont,
|
||||
>(
|
||||
out: &mut GlweCiphertext<OutputGlweCont>,
|
||||
ggsw: &PseudoFourierGgswCiphertext<GgswCont>,
|
||||
glwe: &GlweCiphertext<InputGlweCont>,
|
||||
fft: FftView<'_>,
|
||||
stack: PodStack<'_>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
OutputGlweCont: ContainerMut<Element = Scalar>,
|
||||
GgswCont: Container<Element = c64>,
|
||||
InputGlweCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(out.ciphertext_modulus(), glwe.ciphertext_modulus());
|
||||
|
||||
impl_add_external_product_assign(out.as_mut_view(), ggsw.as_view(), glwe, fft, stack);
|
||||
|
||||
let ciphertext_modulus = out.ciphertext_modulus();
|
||||
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),
|
||||
);
|
||||
out.as_mut()
|
||||
.iter_mut()
|
||||
.for_each(|x| *x = signed_decomposer.closest_representable(*x));
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the required memory for [`add_external_product_fast_keyswitch_assign_mem_optimized`].
|
||||
pub fn add_external_product_fast_keyswitch_assign_mem_optimized_requirement<Scalar>(
|
||||
glwe_dimension: GlweDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
fft: FftView<'_>,
|
||||
) -> Result<StackReq, SizeOverflow> {
|
||||
//Trick to rebrand the dimension as a size to avoid code duplication
|
||||
impl_add_external_product_assign_scratch::<Scalar>(
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
fft,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,588 @@
|
||||
// use crate::core_crypto::algorithms::*;
|
||||
// use crate::core_crypto::commons::dispersion::DispersionParameter;
|
||||
// use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
// use crate::core_crypto::commons::math::random::ActivatedRandomGenerator;
|
||||
// use crate::core_crypto::commons::parameters::*;
|
||||
// use crate::core_crypto::commons::traits::*;
|
||||
// use crate::core_crypto::entities::*;
|
||||
// use rayon::prelude::*;
|
||||
//
|
||||
// /// Fill an [`LWE bootstrap key`](`LweBootstrapKey`) with an actual bootstrapping key constructed
|
||||
// /// from an input key [`LWE secret key`](`LweSecretKey`) and an output key
|
||||
// /// [`GLWE secret key`](`GlweSecretKey`)
|
||||
// ///
|
||||
// /// Consider using [`par_generate_lwe_bootstrap_key`] for better key generation times.
|
||||
// ///
|
||||
// /// ```
|
||||
// /// use tfhe::core_crypto::prelude::*;
|
||||
// ///
|
||||
// /// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
// /// // computations
|
||||
// /// // Define parameters for LweBootstrapKey creation
|
||||
// /// let input_lwe_dimension = LweDimension(742);
|
||||
// /// let lwe_modular_std_dev = StandardDev(0.000007069849454709433);
|
||||
// /// let output_lwe_dimension = LweDimension(2048);
|
||||
// /// let decomp_base_log = DecompositionBaseLog(3);
|
||||
// /// let decomp_level_count = DecompositionLevelCount(5);
|
||||
// /// let glwe_dimension = GlweDimension(1);
|
||||
// /// let polynomial_size = PolynomialSize(1024);
|
||||
// /// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
|
||||
// /// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
// ///
|
||||
// /// // Create the PRNG
|
||||
// /// let mut seeder = new_seeder();
|
||||
// /// let seeder = seeder.as_mut();
|
||||
// /// let mut encryption_generator =
|
||||
// /// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
// /// let mut secret_generator =
|
||||
// /// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
// ///
|
||||
// /// // Create the LweSecretKey
|
||||
// /// let input_lwe_secret_key =
|
||||
// /// allocate_and_generate_new_binary_lwe_secret_key(input_lwe_dimension, &mut
|
||||
// secret_generator); /// let output_glwe_secret_key =
|
||||
// allocate_and_generate_new_binary_glwe_secret_key( /// glwe_dimension,
|
||||
// /// polynomial_size,
|
||||
// /// &mut secret_generator,
|
||||
// /// );
|
||||
// ///
|
||||
// /// let mut bsk = LweBootstrapKey::new(
|
||||
// /// 0u64,
|
||||
// /// glwe_dimension.to_glwe_size(),
|
||||
// /// polynomial_size,
|
||||
// /// decomp_base_log,
|
||||
// /// decomp_level_count,
|
||||
// /// input_lwe_dimension,
|
||||
// /// ciphertext_modulus,
|
||||
// /// );
|
||||
// ///
|
||||
// /// generate_lwe_bootstrap_key(
|
||||
// /// &input_lwe_secret_key,
|
||||
// /// &output_glwe_secret_key,
|
||||
// /// &mut bsk,
|
||||
// /// glwe_modular_std_dev,
|
||||
// /// &mut encryption_generator,
|
||||
// /// );
|
||||
// ///
|
||||
// /// for (ggsw, &input_key_bit) in bsk.iter().zip(input_lwe_secret_key.as_ref()) {
|
||||
// /// let decrypted_ggsw = decrypt_constant_ggsw_ciphertext(&output_glwe_secret_key, &ggsw);
|
||||
// /// assert_eq!(decrypted_ggsw.0, input_key_bit)
|
||||
// /// }
|
||||
// /// ```
|
||||
// pub fn generate_glwe_fast_keyswitch_key<Scalar, InputKeyCont, OutputKeyCont, OutputCont, Gen>(
|
||||
// input_lwe_secret_key: &GlweSecretKey<InputKeyCont>,
|
||||
// output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
|
||||
// output: &mut LweBootstrapKey<OutputCont>,
|
||||
// noise_parameters: impl DispersionParameter,
|
||||
// generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
// ) where Scalar: UnsignedTorus, InputKeyCont: Container<Element = Scalar>, OutputKeyCont:
|
||||
// Container<Element = Scalar>, OutputCont: ContainerMut<Element = Scalar>, Gen:
|
||||
// ByteRandomGenerator,
|
||||
// {
|
||||
// // 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()
|
||||
// // );
|
||||
// //
|
||||
// // assert!(
|
||||
// // output.glwe_size() == output_glwe_secret_key.glwe_dimension().to_glwe_size(),
|
||||
// // "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().to_glwe_size(),
|
||||
// // output.glwe_size()
|
||||
// // );
|
||||
// //
|
||||
// // 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
|
||||
// .fork_bsk_to_ggsw::<Scalar>(
|
||||
// output.input_lwe_dimension(),
|
||||
// output.decomposition_level_count(),
|
||||
// output.glwe_size(),
|
||||
// output.polynomial_size(),
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// for ((mut ggsw, input_key_element), mut generator) in output
|
||||
// .iter_mut()
|
||||
// .zip(input_lwe_secret_key.as_polynomial_list().as_view().iter())
|
||||
// .zip(gen_iter)
|
||||
// {
|
||||
// let key = &GlweSecretKey::from_container(input_key_element.into_container(),
|
||||
// input_lwe_secret_key.polynomial_size());
|
||||
// encrypt_pseudo_ggsw_ciphertext(
|
||||
// output_glwe_secret_key,
|
||||
// key,
|
||||
// &mut ggsw,
|
||||
// noise_parameters,
|
||||
// &mut generator,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// //
|
||||
// // /// Allocate a new [`LWE bootstrap key`](`LweBootstrapKey`) and fill it with an actual
|
||||
// bootstrapping // /// key constructed from an input key [`LWE secret key`](`LweSecretKey`) and an
|
||||
// output key // /// [`GLWE secret key`](`GlweSecretKey`).
|
||||
// // ///
|
||||
// // /// Consider using [`par_allocate_and_generate_new_lwe_bootstrap_key`] for better key
|
||||
// generation // /// times.
|
||||
// // pub fn allocate_and_generate_new_glwe_fast_keyswitch_key<Scalar, InputKeyCont, OutputKeyCont,
|
||||
// Gen>( // input_lwe_secret_key: &GlweSecretKey<InputKeyCont>,
|
||||
// // output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
|
||||
// // decomp_base_log: DecompositionBaseLog,
|
||||
// // decomp_level_count: DecompositionLevelCount,
|
||||
// // noise_parameters: impl DispersionParameter,
|
||||
// // ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
// // generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
// // ) -> LweBootstrapKeyOwned<Scalar>
|
||||
// // where
|
||||
// // Scalar: UnsignedTorus,
|
||||
// // InputKeyCont: Container<Element = Scalar>,
|
||||
// // OutputKeyCont: Container<Element = Scalar>,
|
||||
// // Gen: ByteRandomGenerator,
|
||||
// // {
|
||||
// // let mut bsk = LweBootstrapKeyOwned::new(
|
||||
// // Scalar::ZERO,
|
||||
// // output_glwe_secret_key.glwe_dimension().to_glwe_size(),
|
||||
// // output_glwe_secret_key.polynomial_size(),
|
||||
// // decomp_base_log,
|
||||
// // decomp_level_count,
|
||||
// // LweDimension(input_lwe_secret_key.glwe_dimension().0 * input_lwe_secret_key
|
||||
// // .polynomial_size().0),
|
||||
// // ciphertext_modulus,
|
||||
// // );
|
||||
// //
|
||||
// // generate_glwe_fast_keyswitch_key(
|
||||
// // input_lwe_secret_key,
|
||||
// // output_glwe_secret_key,
|
||||
// // &mut bsk,
|
||||
// // noise_parameters,
|
||||
// // generator,
|
||||
// // );
|
||||
// //
|
||||
// // bsk
|
||||
// // }
|
||||
// //
|
||||
// // // /// Parallel variant of [`generate_lwe_bootstrap_key`], it is recommended to use this
|
||||
// function for // // /// better key generation times as LWE bootstrapping keys can be quite large.
|
||||
// // // ///
|
||||
// // // /// # Example
|
||||
// // // ///
|
||||
// // // /// ```
|
||||
// // // /// use tfhe::core_crypto::prelude::*;
|
||||
// // // ///
|
||||
// // // /// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield
|
||||
// correct // // /// // computations
|
||||
// // // /// // Define parameters for LweBootstrapKey creation
|
||||
// // // /// let input_lwe_dimension = LweDimension(742);
|
||||
// // // /// let lwe_modular_std_dev = StandardDev(0.000007069849454709433);
|
||||
// // // /// let output_lwe_dimension = LweDimension(2048);
|
||||
// // // /// let decomp_base_log = DecompositionBaseLog(3);
|
||||
// // // /// let decomp_level_count = DecompositionLevelCount(5);
|
||||
// // // /// let glwe_dimension = GlweDimension(1);
|
||||
// // // /// let polynomial_size = PolynomialSize(1024);
|
||||
// // // /// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
|
||||
// // // /// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
// // // ///
|
||||
// // // /// // Create the PRNG
|
||||
// // // /// let mut seeder = new_seeder();
|
||||
// // // /// let seeder = seeder.as_mut();
|
||||
// // // /// let mut encryption_generator =
|
||||
// // // /// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
// // // /// let mut secret_generator =
|
||||
// // // /// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
// // // ///
|
||||
// // // /// // Create the LweSecretKey
|
||||
// // // /// let input_lwe_secret_key =
|
||||
// // // /// allocate_and_generate_new_binary_lwe_secret_key(input_lwe_dimension, &mut
|
||||
// secret_generator); // // /// let output_glwe_secret_key =
|
||||
// allocate_and_generate_new_binary_glwe_secret_key( // // /// glwe_dimension,
|
||||
// // // /// polynomial_size,
|
||||
// // // /// &mut secret_generator,
|
||||
// // // /// );
|
||||
// // // ///
|
||||
// // // /// let mut bsk = LweBootstrapKey::new(
|
||||
// // // /// 0u64,
|
||||
// // // /// glwe_dimension.to_glwe_size(),
|
||||
// // // /// polynomial_size,
|
||||
// // // /// decomp_base_log,
|
||||
// // // /// decomp_level_count,
|
||||
// // // /// input_lwe_dimension,
|
||||
// // // /// ciphertext_modulus,
|
||||
// // // /// );
|
||||
// // // ///
|
||||
// // // /// par_generate_lwe_bootstrap_key(
|
||||
// // // /// &input_lwe_secret_key,
|
||||
// // // /// &output_glwe_secret_key,
|
||||
// // // /// &mut bsk,
|
||||
// // // /// glwe_modular_std_dev,
|
||||
// // // /// &mut encryption_generator,
|
||||
// // // /// );
|
||||
// // // ///
|
||||
// // // /// assert!(bsk.as_ref().iter().all(|&x| x == 0) == false);
|
||||
// // // /// ```
|
||||
// // // pub fn par_generate_lwe_bootstrap_key<Scalar, InputKeyCont, OutputKeyCont, OutputCont,
|
||||
// Gen>( // // input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
|
||||
// // // output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
|
||||
// // // output: &mut LweBootstrapKey<OutputCont>,
|
||||
// // // noise_parameters: impl DispersionParameter + Sync,
|
||||
// // // generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
// // // ) where
|
||||
// // // Scalar: UnsignedTorus + Sync + Send,
|
||||
// // // InputKeyCont: Container<Element = Scalar>,
|
||||
// // // OutputKeyCont: Container<Element = Scalar> + Sync,
|
||||
// // // OutputCont: ContainerMut<Element = Scalar>,
|
||||
// // // Gen: ParallelByteRandomGenerator,
|
||||
// // // {
|
||||
// // // 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()
|
||||
// // // );
|
||||
// // //
|
||||
// // // assert!(
|
||||
// // // output.glwe_size() == output_glwe_secret_key.glwe_dimension().to_glwe_size(),
|
||||
// // // "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().to_glwe_size(),
|
||||
// // // output.glwe_size()
|
||||
// // // );
|
||||
// // //
|
||||
// // // 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_fork_bsk_to_ggsw::<Scalar>(
|
||||
// // // output.input_lwe_dimension(),
|
||||
// // // output.decomposition_level_count(),
|
||||
// // // output.glwe_size(),
|
||||
// // // output.polynomial_size(),
|
||||
// // // )
|
||||
// // // .unwrap();
|
||||
// // //
|
||||
// // // output
|
||||
// // // .par_iter_mut()
|
||||
// // // .zip(input_lwe_secret_key.as_ref().par_iter())
|
||||
// // // .zip(gen_iter)
|
||||
// // // .for_each(|((mut ggsw, &input_key_element), mut generator)| {
|
||||
// // // par_encrypt_constant_ggsw_ciphertext(
|
||||
// // // output_glwe_secret_key,
|
||||
// // // &mut ggsw,
|
||||
// // // Plaintext(input_key_element),
|
||||
// // // noise_parameters,
|
||||
// // // &mut generator,
|
||||
// // // );
|
||||
// // // });
|
||||
// // // }
|
||||
// // //
|
||||
// // // /// Parallel variant of [`allocate_and_generate_new_lwe_bootstrap_key`], it is recommended
|
||||
// to use // // /// this function for better key generation times as LWE bootstrapping keys can be
|
||||
// quite large. // // ///
|
||||
// // // /// See [`programmable_bootstrap_lwe_ciphertext`] for usage.
|
||||
// // // pub fn par_allocate_and_generate_new_lwe_bootstrap_key<Scalar, InputKeyCont, OutputKeyCont,
|
||||
// Gen>( // // input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
|
||||
// // // output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
|
||||
// // // decomp_base_log: DecompositionBaseLog,
|
||||
// // // decomp_level_count: DecompositionLevelCount,
|
||||
// // // noise_parameters: impl DispersionParameter + Sync,
|
||||
// // // ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
// // // generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
// // // ) -> LweBootstrapKeyOwned<Scalar>
|
||||
// // // where
|
||||
// // // Scalar: UnsignedTorus + Sync + Send,
|
||||
// // // InputKeyCont: Container<Element = Scalar>,
|
||||
// // // OutputKeyCont: Container<Element = Scalar> + Sync,
|
||||
// // // Gen: ParallelByteRandomGenerator,
|
||||
// // // {
|
||||
// // // let mut bsk = LweBootstrapKeyOwned::new(
|
||||
// // // Scalar::ZERO,
|
||||
// // // output_glwe_secret_key.glwe_dimension().to_glwe_size(),
|
||||
// // // output_glwe_secret_key.polynomial_size(),
|
||||
// // // decomp_base_log,
|
||||
// // // decomp_level_count,
|
||||
// // // input_lwe_secret_key.lwe_dimension(),
|
||||
// // // ciphertext_modulus,
|
||||
// // // );
|
||||
// // //
|
||||
// // // par_generate_lwe_bootstrap_key(
|
||||
// // // input_lwe_secret_key,
|
||||
// // // output_glwe_secret_key,
|
||||
// // // &mut bsk,
|
||||
// // // noise_parameters,
|
||||
// // // generator,
|
||||
// // // );
|
||||
// // //
|
||||
// // // bsk
|
||||
// // // }
|
||||
// // //
|
||||
// // // /// Fill a [`seeded LWE bootstrap key`](`SeededLweBootstrapKey`) with an actual seeded
|
||||
// bootstrapping // // /// key constructed from an input key [`LWE secret key`](`LweSecretKey`) and
|
||||
// an output key // // /// [`GLWE secret key`](`GlweSecretKey`)
|
||||
// // // ///
|
||||
// // // /// Consider using [`par_generate_seeded_lwe_bootstrap_key`] for better key generation
|
||||
// times. // // pub fn generate_seeded_lwe_bootstrap_key<
|
||||
// // // Scalar,
|
||||
// // // InputKeyCont,
|
||||
// // // OutputKeyCont,
|
||||
// // // OutputCont,
|
||||
// // // NoiseSeeder,
|
||||
// // // >(
|
||||
// // // input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
|
||||
// // // output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
|
||||
// // // output: &mut SeededLweBootstrapKey<OutputCont>,
|
||||
// // // noise_parameters: impl DispersionParameter,
|
||||
// // // noise_seeder: &mut NoiseSeeder,
|
||||
// // // ) where
|
||||
// // // Scalar: UnsignedTorus,
|
||||
// // // InputKeyCont: Container<Element = Scalar>,
|
||||
// // // OutputKeyCont: Container<Element = Scalar>,
|
||||
// // // OutputCont: ContainerMut<Element = Scalar>,
|
||||
// // // // Maybe Sized allows to pass Box<dyn Seeder>.
|
||||
// // // NoiseSeeder: Seeder + ?Sized,
|
||||
// // // {
|
||||
// // // 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()
|
||||
// // // );
|
||||
// // //
|
||||
// // // assert!(
|
||||
// // // output.glwe_size() == output_glwe_secret_key.glwe_dimension().to_glwe_size(),
|
||||
// // // "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().to_glwe_size(),
|
||||
// // // output.glwe_size()
|
||||
// // // );
|
||||
// // //
|
||||
// // // 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 mut generator = EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(
|
||||
// // // output.compression_seed().seed,
|
||||
// // // noise_seeder,
|
||||
// // // );
|
||||
// // //
|
||||
// // // let gen_iter = generator
|
||||
// // // .fork_bsk_to_ggsw::<Scalar>(
|
||||
// // // output.input_lwe_dimension(),
|
||||
// // // output.decomposition_level_count(),
|
||||
// // // output.glwe_size(),
|
||||
// // // output.polynomial_size(),
|
||||
// // // )
|
||||
// // // .unwrap();
|
||||
// // //
|
||||
// // // for ((mut ggsw, &input_key_element), mut generator) in output
|
||||
// // // .iter_mut()
|
||||
// // // .zip(input_lwe_secret_key.as_ref())
|
||||
// // // .zip(gen_iter)
|
||||
// // // {
|
||||
// // // encrypt_constant_seeded_ggsw_ciphertext_with_existing_generator(
|
||||
// // // output_glwe_secret_key,
|
||||
// // // &mut ggsw,
|
||||
// // // Plaintext(input_key_element),
|
||||
// // // noise_parameters,
|
||||
// // // &mut generator,
|
||||
// // // );
|
||||
// // // }
|
||||
// // // }
|
||||
// // //
|
||||
// // // /// Allocate a new [`seeded LWE bootstrap key`](`SeededLweBootstrapKey`) and fill it with
|
||||
// an actual // // /// seeded bootstrapping key constructed from an input key [`LWE secret
|
||||
// key`](`LweSecretKey`) and an // // /// output key [`GLWE secret key`](`GlweSecretKey`)
|
||||
// // // ///
|
||||
// // // /// Consider using [`par_allocate_and_generate_new_seeded_lwe_bootstrap_key`] for better
|
||||
// key // // /// generation times.
|
||||
// // // pub fn allocate_and_generate_new_seeded_lwe_bootstrap_key<
|
||||
// // // Scalar,
|
||||
// // // InputKeyCont,
|
||||
// // // OutputKeyCont,
|
||||
// // // NoiseSeeder,
|
||||
// // // >(
|
||||
// // // input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
|
||||
// // // output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
|
||||
// // // decomp_base_log: DecompositionBaseLog,
|
||||
// // // decomp_level_count: DecompositionLevelCount,
|
||||
// // // noise_parameters: impl DispersionParameter,
|
||||
// // // ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
// // // noise_seeder: &mut NoiseSeeder,
|
||||
// // // ) -> SeededLweBootstrapKeyOwned<Scalar>
|
||||
// // // where
|
||||
// // // Scalar: UnsignedTorus,
|
||||
// // // InputKeyCont: Container<Element = Scalar>,
|
||||
// // // OutputKeyCont: Container<Element = Scalar>,
|
||||
// // // // Maybe Sized allows to pass Box<dyn Seeder>.
|
||||
// // // NoiseSeeder: Seeder + ?Sized,
|
||||
// // // {
|
||||
// // // let mut bsk = SeededLweBootstrapKeyOwned::new(
|
||||
// // // Scalar::ZERO,
|
||||
// // // output_glwe_secret_key.glwe_dimension().to_glwe_size(),
|
||||
// // // output_glwe_secret_key.polynomial_size(),
|
||||
// // // decomp_base_log,
|
||||
// // // decomp_level_count,
|
||||
// // // input_lwe_secret_key.lwe_dimension(),
|
||||
// // // noise_seeder.seed().into(),
|
||||
// // // ciphertext_modulus,
|
||||
// // // );
|
||||
// // //
|
||||
// // // generate_seeded_lwe_bootstrap_key(
|
||||
// // // input_lwe_secret_key,
|
||||
// // // output_glwe_secret_key,
|
||||
// // // &mut bsk,
|
||||
// // // noise_parameters,
|
||||
// // // noise_seeder,
|
||||
// // // );
|
||||
// // //
|
||||
// // // bsk
|
||||
// // // }
|
||||
// // //
|
||||
// // // /// Parallel variant of [`generate_seeded_lwe_bootstrap_key`], it is recommended to use
|
||||
// this // // /// function for better key generation times as LWE bootstrapping keys can be quite
|
||||
// large. // // pub fn par_generate_seeded_lwe_bootstrap_key<
|
||||
// // // Scalar,
|
||||
// // // InputKeyCont,
|
||||
// // // OutputKeyCont,
|
||||
// // // OutputCont,
|
||||
// // // NoiseSeeder,
|
||||
// // // >(
|
||||
// // // input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
|
||||
// // // output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
|
||||
// // // output: &mut SeededLweBootstrapKey<OutputCont>,
|
||||
// // // noise_parameters: impl DispersionParameter + Sync,
|
||||
// // // noise_seeder: &mut NoiseSeeder,
|
||||
// // // ) where
|
||||
// // // Scalar: UnsignedTorus + Sync + Send,
|
||||
// // // InputKeyCont: Container<Element = Scalar>,
|
||||
// // // OutputKeyCont: Container<Element = Scalar> + Sync,
|
||||
// // // OutputCont: ContainerMut<Element = Scalar>,
|
||||
// // // // Maybe Sized allows to pass Box<dyn Seeder>.
|
||||
// // // NoiseSeeder: Seeder + ?Sized,
|
||||
// // // {
|
||||
// // // 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()
|
||||
// // // );
|
||||
// // //
|
||||
// // // assert!(
|
||||
// // // output.glwe_size() == output_glwe_secret_key.glwe_dimension().to_glwe_size(),
|
||||
// // // "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().to_glwe_size(),
|
||||
// // // output.glwe_size()
|
||||
// // // );
|
||||
// // //
|
||||
// // // 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 mut generator = EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(
|
||||
// // // output.compression_seed().seed,
|
||||
// // // noise_seeder,
|
||||
// // // );
|
||||
// // //
|
||||
// // // let gen_iter = generator
|
||||
// // // .par_fork_bsk_to_ggsw::<Scalar>(
|
||||
// // // output.input_lwe_dimension(),
|
||||
// // // output.decomposition_level_count(),
|
||||
// // // output.glwe_size(),
|
||||
// // // output.polynomial_size(),
|
||||
// // // )
|
||||
// // // .unwrap();
|
||||
// // //
|
||||
// // // output
|
||||
// // // .par_iter_mut()
|
||||
// // // .zip(input_lwe_secret_key.as_ref().par_iter())
|
||||
// // // .zip(gen_iter)
|
||||
// // // .for_each(|((mut ggsw, &input_key_element), mut generator)| {
|
||||
// // // par_encrypt_constant_seeded_ggsw_ciphertext_with_existing_generator(
|
||||
// // // output_glwe_secret_key,
|
||||
// // // &mut ggsw,
|
||||
// // // Plaintext(input_key_element),
|
||||
// // // noise_parameters,
|
||||
// // // &mut generator,
|
||||
// // // );
|
||||
// // // })
|
||||
// // // }
|
||||
// // //
|
||||
// // // /// Parallel variant of [`allocate_and_generate_new_seeded_lwe_bootstrap_key`], it is
|
||||
// recommended to // // /// use this function for better key generation times as LWE bootstrapping
|
||||
// keys can be quite large. // // pub fn par_allocate_and_generate_new_seeded_lwe_bootstrap_key<
|
||||
// // // Scalar,
|
||||
// // // InputKeyCont,
|
||||
// // // OutputKeyCont,
|
||||
// // // NoiseSeeder,
|
||||
// // // >(
|
||||
// // // input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
|
||||
// // // output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
|
||||
// // // decomp_base_log: DecompositionBaseLog,
|
||||
// // // decomp_level_count: DecompositionLevelCount,
|
||||
// // // noise_parameters: impl DispersionParameter + Sync,
|
||||
// // // ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
// // // noise_seeder: &mut NoiseSeeder,
|
||||
// // // ) -> SeededLweBootstrapKeyOwned<Scalar>
|
||||
// // // where
|
||||
// // // Scalar: UnsignedTorus + Sync + Send,
|
||||
// // // InputKeyCont: Container<Element = Scalar>,
|
||||
// // // OutputKeyCont: Container<Element = Scalar> + Sync,
|
||||
// // // // Maybe Sized allows to pass Box<dyn Seeder>.
|
||||
// // // NoiseSeeder: Seeder + ?Sized,
|
||||
// // // {
|
||||
// // // let mut bsk = SeededLweBootstrapKeyOwned::new(
|
||||
// // // Scalar::ZERO,
|
||||
// // // output_glwe_secret_key.glwe_dimension().to_glwe_size(),
|
||||
// // // output_glwe_secret_key.polynomial_size(),
|
||||
// // // decomp_base_log,
|
||||
// // // decomp_level_count,
|
||||
// // // input_lwe_secret_key.lwe_dimension(),
|
||||
// // // noise_seeder.seed().into(),
|
||||
// // // ciphertext_modulus,
|
||||
// // // );
|
||||
// // //
|
||||
// // // par_generate_seeded_lwe_bootstrap_key(
|
||||
// // // input_lwe_secret_key,
|
||||
// // // output_glwe_secret_key,
|
||||
// // // &mut bsk,
|
||||
// // // noise_parameters,
|
||||
// // // noise_seeder,
|
||||
// // // );
|
||||
// // //
|
||||
// // // bsk
|
||||
// // // }
|
||||
@@ -0,0 +1,68 @@
|
||||
//! Module containing primitives pertaining to the generation of
|
||||
//! [`LWE secret keys`](`LweSecretKey`).
|
||||
|
||||
use crate::core_crypto::commons::generators::SecretRandomGenerator;
|
||||
use crate::core_crypto::commons::math::random::{RandomGenerable, UniformBinary};
|
||||
use crate::core_crypto::commons::numeric::Numeric;
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::prelude::generate_binary_lwe_secret_key;
|
||||
|
||||
/// Allocate a new [`LWE secret key`](`LweSecretKey`) and fill it with uniformly random binary
|
||||
/// coefficients.
|
||||
/// phi: the number of non-zero coefficients
|
||||
/// See [`encrypt_lwe_ciphertext`](`super::lwe_encryption::encrypt_lwe_ciphertext`) for usage.
|
||||
///
|
||||
/// /// Fill an [`LWE secret key`](`LweSecretKey`) with uniformly random binary coefficients.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for LweCiphertext creation
|
||||
/// let lwe_dimension = LweDimension(20);
|
||||
/// let phi = LweDimension(15);
|
||||
///
|
||||
/// // Create the PRNG20
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
/// let mut secret_generator =
|
||||
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
///
|
||||
/// let mut lwe_secret_key: LweSecretKeyOwned<u64> =
|
||||
/// allocate_and_generate_new_binary_lwe_partial_secret_key(
|
||||
/// lwe_dimension,
|
||||
/// &mut secret_generator,
|
||||
/// phi,
|
||||
/// );
|
||||
/// // Check all coefficients are not zero as we just generated a new key
|
||||
/// // Note probability of this assert failing is (1/2)^lwe_dimension or ~4.3 * 10^-224 for an LWE
|
||||
/// // dimension of 742.
|
||||
/// assert!(lwe_secret_key.as_ref().iter().all(|&elt| elt == 0) == false);
|
||||
/// ```
|
||||
pub fn allocate_and_generate_new_binary_lwe_partial_secret_key<Scalar, Gen>(
|
||||
lwe_dimension: LweDimension,
|
||||
generator: &mut SecretRandomGenerator<Gen>,
|
||||
phi: LweDimension,
|
||||
) -> LweSecretKeyOwned<Scalar>
|
||||
where
|
||||
Scalar: RandomGenerable<UniformBinary> + Numeric,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let mut lwe_secret_key = LweSecretKeyOwned::new_empty_key(Scalar::ZERO, phi);
|
||||
generate_binary_lwe_secret_key(&mut lwe_secret_key, generator);
|
||||
let mut lwe_secret_key_out = LweSecretKeyOwned::new_empty_key(Scalar::ZERO, lwe_dimension);
|
||||
lwe_secret_key_out
|
||||
.as_mut()
|
||||
.iter_mut()
|
||||
.zip(lwe_secret_key.as_ref().iter())
|
||||
.for_each(|(dst, &src)| *dst = src);
|
||||
//println!("Input Key = {:?}", lwe_secret_key.into_container().to_vec());
|
||||
//println!("Ouput Key = {:?}", lwe_secret_key_out.clone().into_container().to_vec());
|
||||
|
||||
lwe_secret_key_out
|
||||
}
|
||||
@@ -64,3 +64,45 @@ pub fn generate_binary_lwe_secret_key<Scalar, InCont, Gen>(
|
||||
{
|
||||
generator.fill_slice_with_random_uniform_binary(lwe_secret_key.as_mut())
|
||||
}
|
||||
|
||||
pub fn allocate_and_generate_new_binary_shared_lwe_secret_key<Scalar, Gen>(
|
||||
lwe_dimension_large: LweDimension,
|
||||
lwe_dimension_small: LweDimension,
|
||||
generator: &mut SecretRandomGenerator<Gen>,
|
||||
) -> (LweSecretKeyOwned<Scalar>, LweSecretKeyOwned<Scalar>)
|
||||
where
|
||||
Scalar: RandomGenerable<UniformBinary> + Numeric,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let mut large_lwe_secret_key =
|
||||
LweSecretKeyOwned::new_empty_key(Scalar::ZERO, lwe_dimension_large);
|
||||
|
||||
generate_binary_lwe_secret_key(&mut large_lwe_secret_key, generator);
|
||||
|
||||
let mut small_lwe_secret_key = LweSecretKey::new_empty_key(Scalar::ZERO, lwe_dimension_small);
|
||||
small_lwe_secret_key
|
||||
.as_mut()
|
||||
.iter_mut()
|
||||
.zip(large_lwe_secret_key.as_ref().iter())
|
||||
.for_each(|(dst, &src)| *dst = src);
|
||||
(large_lwe_secret_key, small_lwe_secret_key)
|
||||
}
|
||||
|
||||
pub fn allocate_and_generate_new_shared_lwe_secret_key_from_lwe_secret_key<Scalar, InCont>(
|
||||
in_large_lwe_key: &LweSecretKey<InCont>,
|
||||
shared_lwe_dimension: SharedLweSecretKeyCommonCoefCount,
|
||||
) -> LweSecretKeyOwned<Scalar>
|
||||
where
|
||||
Scalar: RandomGenerable<UniformBinary> + Numeric,
|
||||
InCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert!(
|
||||
shared_lwe_dimension.0 <= in_large_lwe_key.lwe_dimension().0,
|
||||
"shared_lwe_dimension ({shared_lwe_dimension:?}) \
|
||||
must be smaller than the total in_large_lwe_key length ({:?}).",
|
||||
in_large_lwe_key.lwe_dimension()
|
||||
);
|
||||
|
||||
let lwe_dimension_small = LweDimension(shared_lwe_dimension.0);
|
||||
LweSecretKey::from_container(in_large_lwe_key.as_ref()[..lwe_dimension_small.0].to_vec())
|
||||
}
|
||||
|
||||
150
tfhe/src/core_crypto/algorithms/lwe_shrinking_keyswitch.rs
Normal file
150
tfhe/src/core_crypto/algorithms/lwe_shrinking_keyswitch.rs
Normal file
@@ -0,0 +1,150 @@
|
||||
//! Module containing primitives pertaining to [`LWE ciphertext
|
||||
//! keyswitch`](`LweKeyswitchKey#lwe-keyswitch`).
|
||||
|
||||
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::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
// From ct_1 to ct_0 with n_1 > n_0 and shared randomness
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for LweShrinkingKeyswitchKey creation
|
||||
/// let large_lwe_dimension = LweDimension(10);
|
||||
/// let lwe_modular_std_dev = StandardDev(0.0000000000000000000000000000000007069849454709433);
|
||||
/// let small_lwe_dimension = LweDimension(5);
|
||||
/// let decomp_base_log = DecompositionBaseLog(30);
|
||||
/// let decomp_level_count = DecompositionLevelCount(1);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // Create the PRNG
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
/// let mut encryption_generator =
|
||||
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
/// let mut secret_generator =
|
||||
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
///
|
||||
/// let shared_randomness_dimension = LweDimension(large_lwe_dimension.0 - small_lwe_dimension.0);
|
||||
/// // Create the LweSecretKey
|
||||
/// let large_lwe_secret_key =
|
||||
/// allocate_and_generate_new_binary_lwe_secret_key(large_lwe_dimension, &mut secret_generator);
|
||||
/// let mut small_lwe_secret_key = LweSecretKey::new_empty_key(0_u64, small_lwe_dimension);
|
||||
/// small_lwe_secret_key
|
||||
/// .as_mut()
|
||||
/// .iter_mut()
|
||||
/// .zip(large_lwe_secret_key.as_ref().iter())
|
||||
/// .for_each(|(dst, &src)| *dst = src);
|
||||
///
|
||||
/// let ksk = allocate_and_generate_new_lwe_shrinking_keyswitch_key(
|
||||
/// &large_lwe_secret_key,
|
||||
/// &small_lwe_secret_key,
|
||||
/// shared_randomness_dimension,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// lwe_modular_std_dev,
|
||||
/// ciphertext_modulus,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// // Create the plaintext
|
||||
/// let msg = 3u64;
|
||||
/// let plaintext = Plaintext(msg << 60);
|
||||
///
|
||||
/// // Create a new LweCiphertext
|
||||
/// let input_lwe = allocate_and_encrypt_new_lwe_ciphertext(
|
||||
/// &large_lwe_secret_key,
|
||||
/// plaintext,
|
||||
/// lwe_modular_std_dev,
|
||||
/// ciphertext_modulus,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// let mut output_lwe = LweCiphertext::new(
|
||||
/// 0,
|
||||
/// small_lwe_secret_key.lwe_dimension().to_lwe_size(),
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// shrinking_keyswitch_lwe_ciphertext(&ksk, &input_lwe, &mut output_lwe);
|
||||
///
|
||||
/// let decrypted_plaintext = decrypt_lwe_ciphertext(&small_lwe_secret_key, &output_lwe);
|
||||
///
|
||||
/// // Round and remove encoding
|
||||
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
|
||||
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
|
||||
///
|
||||
/// let rounded = decomposer.closest_representable(decrypted_plaintext.0);
|
||||
///
|
||||
/// // Remove the encoding
|
||||
/// let cleartext = rounded >> 60;
|
||||
///
|
||||
/// // Check we recovered the original message
|
||||
/// assert_eq!(cleartext, msg);
|
||||
/// ```
|
||||
pub fn shrinking_keyswitch_lwe_ciphertext<Scalar, KSKCont, InputCont, OutputCont>(
|
||||
lwe_shrinking_keyswitch_key: &LweShrinkingKeyswitchKey<KSKCont>,
|
||||
input_lwe_ciphertext: &LweCiphertext<InputCont>,
|
||||
output_lwe_ciphertext: &mut LweCiphertext<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
KSKCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
input_lwe_ciphertext.lwe_size().to_lwe_dimension(),
|
||||
LweDimension(
|
||||
output_lwe_ciphertext.lwe_size().to_lwe_dimension().0
|
||||
+ lwe_shrinking_keyswitch_key
|
||||
.unshared_randomness_lwe_dimension()
|
||||
.0
|
||||
)
|
||||
);
|
||||
|
||||
// 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_body().data = *input_lwe_ciphertext.get_body().data;
|
||||
|
||||
let shared_randomness_lwe_dimension =
|
||||
lwe_shrinking_keyswitch_key.shared_randomness_lwe_dimension();
|
||||
|
||||
let input_lwe_ciphertext_mask = input_lwe_ciphertext.get_mask();
|
||||
let (input_shared_mask_slice, input_unshared_mask_slice) = input_lwe_ciphertext_mask
|
||||
.as_ref()
|
||||
.split_at(shared_randomness_lwe_dimension.0);
|
||||
|
||||
// Copy the shared elements of the mask
|
||||
output_lwe_ciphertext.get_mut_mask().as_mut()[0..shared_randomness_lwe_dimension.0]
|
||||
.copy_from_slice(input_shared_mask_slice);
|
||||
|
||||
// We instantiate a decomposer
|
||||
let decomposer = SignedDecomposer::new(
|
||||
lwe_shrinking_keyswitch_key.decomposition_base_log(),
|
||||
lwe_shrinking_keyswitch_key.decomposition_level_count(),
|
||||
);
|
||||
|
||||
for (keyswitch_key_block, &input_mask_element) in lwe_shrinking_keyswitch_key
|
||||
.as_lwe_keyswitch_key()
|
||||
.iter()
|
||||
.zip(input_unshared_mask_slice.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(
|
||||
output_lwe_ciphertext.as_mut(),
|
||||
level_key_ciphertext.as_ref(),
|
||||
decomposed.value(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
//! Module containing primitives pertaining to [`LWE shrinking keyswitch keys
|
||||
//! generation`](`LweKeyswitchKey#key-switching-key`) and [`seeded LWE shrinking keyswitch keys
|
||||
//! generation`](`SeededLweKeyswitchKey`).
|
||||
|
||||
//TODO: Seeded part not done
|
||||
|
||||
use crate::core_crypto::algorithms::*;
|
||||
use crate::core_crypto::commons::dispersion::DispersionParameter;
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
pub fn generate_lwe_shrinking_keyswitch_key<Scalar, InputKeyCont, OutputKeyCont, KSKeyCont, Gen>(
|
||||
input_lwe_sk: &LweSecretKey<InputKeyCont>,
|
||||
output_lwe_sk: &LweSecretKey<OutputKeyCont>,
|
||||
lwe_keyswitch_key: &mut LweShrinkingKeyswitchKey<KSKeyCont>,
|
||||
noise_parameters: impl DispersionParameter,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
OutputKeyCont: Container<Element = Scalar>,
|
||||
KSKeyCont: ContainerMut<Element = Scalar> + std::fmt::Debug,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let unshared_randomness_lwe_dimension = lwe_keyswitch_key.unshared_randomness_lwe_dimension();
|
||||
|
||||
assert!(
|
||||
lwe_keyswitch_key.output_key_lwe_dimension() == output_lwe_sk.lwe_dimension(),
|
||||
"The destination LweKeyswitchKey output LweDimension is not equal \
|
||||
to the output LweSecretKey LweDimension. Destination: {:?}, output: {:?}",
|
||||
lwe_keyswitch_key.output_key_lwe_dimension(),
|
||||
output_lwe_sk.lwe_dimension()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_lwe_sk.lwe_dimension().0,
|
||||
output_lwe_sk.lwe_dimension().0 + unshared_randomness_lwe_dimension.0
|
||||
);
|
||||
|
||||
let shared_randomness_lwe_dimension = lwe_keyswitch_key.shared_randomness_lwe_dimension();
|
||||
|
||||
let shrunk_input_lwe_secret_key =
|
||||
LweSecretKey::from_container(&input_lwe_sk.as_ref()[shared_randomness_lwe_dimension.0..]);
|
||||
generate_lwe_keyswitch_key(
|
||||
&shrunk_input_lwe_secret_key,
|
||||
output_lwe_sk,
|
||||
&mut lwe_keyswitch_key.as_mut_lwe_keyswitch_key(),
|
||||
noise_parameters,
|
||||
generator,
|
||||
)
|
||||
}
|
||||
|
||||
/// Allocate a new [`LWE keyswitch key`](`LweKeyswitchKey`) and fill it with an actual keyswitching
|
||||
/// key constructed from an input and an output key [`LWE secret key`](`LweSecretKey`).
|
||||
///
|
||||
/// See [`shrinking_keyswitch_lwe_ciphertext`] for usage.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn allocate_and_generate_new_lwe_shrinking_keyswitch_key<
|
||||
Scalar,
|
||||
InputKeyCont,
|
||||
OutputKeyCont,
|
||||
Gen,
|
||||
>(
|
||||
input_lwe_sk: &LweSecretKey<InputKeyCont>,
|
||||
output_lwe_sk: &LweSecretKey<OutputKeyCont>,
|
||||
shared_randomness_coef_count: SharedLweSecretKeyCommonCoefCount,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
noise_parameters: impl DispersionParameter,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) -> LweShrinkingKeyswitchKeyOwned<Scalar>
|
||||
where
|
||||
Scalar: UnsignedTorus,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
OutputKeyCont: Container<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let mut new_lwe_keyswitch_key = LweShrinkingKeyswitchKeyOwned::new(
|
||||
Scalar::ZERO,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_sk.lwe_dimension(),
|
||||
output_lwe_sk.lwe_dimension(),
|
||||
shared_randomness_coef_count,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
generate_lwe_shrinking_keyswitch_key(
|
||||
input_lwe_sk,
|
||||
output_lwe_sk,
|
||||
&mut new_lwe_keyswitch_key,
|
||||
noise_parameters,
|
||||
generator,
|
||||
);
|
||||
|
||||
new_lwe_keyswitch_key
|
||||
}
|
||||
@@ -6,6 +6,8 @@ pub mod ggsw_conversion;
|
||||
pub mod ggsw_encryption;
|
||||
pub mod glwe_encryption;
|
||||
pub mod glwe_linear_algebra;
|
||||
pub mod glwe_partial_sample_extraction;
|
||||
pub mod glwe_partial_secret_key_generation;
|
||||
pub mod glwe_sample_extraction;
|
||||
pub mod glwe_secret_key_generation;
|
||||
pub mod lwe_bootstrap_key_conversion;
|
||||
@@ -13,6 +15,8 @@ pub mod lwe_bootstrap_key_generation;
|
||||
pub mod lwe_compact_ciphertext_list_expansion;
|
||||
pub mod lwe_compact_public_key_generation;
|
||||
pub mod lwe_encryption;
|
||||
pub mod lwe_fast_keyswitch;
|
||||
pub mod lwe_fast_keyswitch_key_generation;
|
||||
pub mod lwe_keyswitch;
|
||||
pub mod lwe_keyswitch_key_generation;
|
||||
pub mod lwe_linear_algebra;
|
||||
@@ -21,14 +25,19 @@ pub mod lwe_multi_bit_bootstrap_key_generation;
|
||||
pub mod lwe_multi_bit_programmable_bootstrapping;
|
||||
pub mod lwe_packing_keyswitch;
|
||||
pub mod lwe_packing_keyswitch_key_generation;
|
||||
pub mod lwe_partial_secret_key_generation;
|
||||
pub mod lwe_private_functional_packing_keyswitch;
|
||||
pub mod lwe_private_functional_packing_keyswitch_key_generation;
|
||||
pub mod lwe_programmable_bootstrapping;
|
||||
pub mod lwe_public_key_generation;
|
||||
pub mod lwe_secret_key_generation;
|
||||
pub mod lwe_shrinking_keyswitch;
|
||||
pub mod lwe_shrinking_keyswitch_key_generation;
|
||||
pub mod lwe_wopbs;
|
||||
pub mod misc;
|
||||
pub mod polynomial_algorithms;
|
||||
pub mod pseudo_ggsw_conversion;
|
||||
pub mod pseudo_ggsw_encryption;
|
||||
pub mod seeded_ggsw_ciphertext_decompression;
|
||||
pub mod seeded_ggsw_ciphertext_list_decompression;
|
||||
pub mod seeded_glwe_ciphertext_decompression;
|
||||
@@ -52,6 +61,8 @@ pub use ggsw_conversion::*;
|
||||
pub use ggsw_encryption::*;
|
||||
pub use glwe_encryption::*;
|
||||
pub use glwe_linear_algebra::*;
|
||||
pub use glwe_partial_sample_extraction::*;
|
||||
pub use glwe_partial_secret_key_generation::*;
|
||||
pub use glwe_sample_extraction::*;
|
||||
pub use glwe_secret_key_generation::*;
|
||||
pub use lwe_bootstrap_key_conversion::*;
|
||||
@@ -59,6 +70,9 @@ pub use lwe_bootstrap_key_generation::*;
|
||||
pub use lwe_compact_ciphertext_list_expansion::*;
|
||||
pub use lwe_compact_public_key_generation::*;
|
||||
pub use lwe_encryption::*;
|
||||
pub use lwe_fast_keyswitch::*;
|
||||
// TODO: the key gen was simplified and the code was left in a half done state
|
||||
// pub use lwe_fast_keyswitch_key_generation::*;
|
||||
pub use lwe_keyswitch::*;
|
||||
pub use lwe_keyswitch_key_generation::*;
|
||||
pub use lwe_linear_algebra::*;
|
||||
@@ -67,12 +81,17 @@ pub use lwe_multi_bit_bootstrap_key_generation::*;
|
||||
pub use lwe_multi_bit_programmable_bootstrapping::*;
|
||||
pub use lwe_packing_keyswitch::*;
|
||||
pub use lwe_packing_keyswitch_key_generation::*;
|
||||
pub use lwe_partial_secret_key_generation::*;
|
||||
pub use lwe_private_functional_packing_keyswitch::*;
|
||||
pub use lwe_private_functional_packing_keyswitch_key_generation::*;
|
||||
pub use lwe_programmable_bootstrapping::*;
|
||||
pub use lwe_public_key_generation::*;
|
||||
pub use lwe_secret_key_generation::*;
|
||||
pub use lwe_shrinking_keyswitch::*;
|
||||
pub use lwe_shrinking_keyswitch_key_generation::*;
|
||||
pub use lwe_wopbs::*;
|
||||
pub use pseudo_ggsw_conversion::*;
|
||||
pub use pseudo_ggsw_encryption::*;
|
||||
pub use seeded_ggsw_ciphertext_decompression::*;
|
||||
pub use seeded_ggsw_ciphertext_list_decompression::*;
|
||||
pub use seeded_glwe_ciphertext_decompression::*;
|
||||
|
||||
74
tfhe/src/core_crypto/algorithms/pseudo_ggsw_conversion.rs
Normal file
74
tfhe/src/core_crypto/algorithms/pseudo_ggsw_conversion.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
//! Module containing primitives pertaining to the conversion of
|
||||
//! [`standard GGSW ciphertexts`](`GgswCiphertext`) 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::entities::*;
|
||||
use crate::core_crypto::fft_impl::fft64::crypto::pseudo_ggsw::{
|
||||
fill_with_forward_fourier_scratch, PseudoFourierGgswCiphertext,
|
||||
};
|
||||
use crate::core_crypto::fft_impl::fft64::math::fft::{Fft, FftView};
|
||||
use concrete_fft::c64;
|
||||
use dyn_stack::{PodStack, SizeOverflow, StackReq};
|
||||
|
||||
/// Convert a [`GGSW ciphertext`](`GgswCiphertext`) with standard coefficients to the Fourier
|
||||
/// domain.
|
||||
///
|
||||
/// If you want to manage the computation memory manually you can use
|
||||
/// [`convert_standard_pseudo_ggsw_ciphertext_to_fourier_mem_optimized`].
|
||||
pub fn convert_standard_pseudo_ggsw_ciphertext_to_fourier<Scalar, InputCont, OutputCont>(
|
||||
input_ggsw: &PseudoGgswCiphertext<InputCont>,
|
||||
output_ggsw: &mut PseudoFourierGgswCiphertext<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = c64>,
|
||||
{
|
||||
let fft = Fft::new(output_ggsw.polynomial_size());
|
||||
let fft = fft.as_view();
|
||||
|
||||
let mut buffers = ComputationBuffers::new();
|
||||
buffers.resize(
|
||||
convert_standard_pseudo_ggsw_ciphertext_to_fourier_mem_optimized_requirement(fft)
|
||||
.unwrap()
|
||||
.unaligned_bytes_required(),
|
||||
);
|
||||
|
||||
convert_standard_pseudo_ggsw_ciphertext_to_fourier_mem_optimized(
|
||||
input_ggsw,
|
||||
output_ggsw,
|
||||
fft,
|
||||
buffers.stack(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Memory optimized version of [`convert_standard_pseudo_ggsw_ciphertext_to_fourier`].
|
||||
///
|
||||
/// See TODO for usage.
|
||||
pub fn convert_standard_pseudo_ggsw_ciphertext_to_fourier_mem_optimized<
|
||||
Scalar,
|
||||
InputCont,
|
||||
OutputCont,
|
||||
>(
|
||||
input_ggsw: &PseudoGgswCiphertext<InputCont>,
|
||||
output_ggsw: &mut PseudoFourierGgswCiphertext<OutputCont>,
|
||||
fft: FftView<'_>,
|
||||
stack: PodStack<'_>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = c64>,
|
||||
{
|
||||
output_ggsw
|
||||
.as_mut_view()
|
||||
.fill_with_forward_fourier(input_ggsw, fft, stack);
|
||||
}
|
||||
|
||||
/// Return the required memory for
|
||||
/// [`convert_standard_pseudo_ggsw_ciphertext_to_fourier_mem_optimized`].
|
||||
pub fn convert_standard_pseudo_ggsw_ciphertext_to_fourier_mem_optimized_requirement(
|
||||
fft: FftView<'_>,
|
||||
) -> Result<StackReq, SizeOverflow> {
|
||||
fill_with_forward_fourier_scratch(fft)
|
||||
}
|
||||
191
tfhe/src/core_crypto/algorithms/pseudo_ggsw_encryption.rs
Normal file
191
tfhe/src/core_crypto/algorithms/pseudo_ggsw_encryption.rs
Normal file
@@ -0,0 +1,191 @@
|
||||
use crate::core_crypto::algorithms::slice_algorithms::*;
|
||||
use crate::core_crypto::algorithms::*;
|
||||
use crate::core_crypto::commons::dispersion::DispersionParameter;
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::decomposition::DecompositionLevel;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
/// Encrypt a plaintext in a [`GGSW ciphertext`](`GgswCiphertext`) in the constant coefficient.
|
||||
///
|
||||
/// See the [`GGSW ciphertext formal definition`](`GgswCiphertext#ggsw-encryption`) for the
|
||||
/// definition of the encryption algorithm.
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for GgswCiphertext creation
|
||||
/// let glwe_size_out = GlweSize(2);
|
||||
/// let polynomial_size = PolynomialSize(4);
|
||||
/// let glwe_size_in = GlweSize(3);
|
||||
/// let polynomial_size_to_encrypt = PolynomialSize(4);
|
||||
/// let decomp_base_log = DecompositionBaseLog(8);
|
||||
/// let decomp_level_count = DecompositionLevelCount(2);
|
||||
/// let glwe_modular_std_dev =
|
||||
/// StandardDev(0.00000000000000000000000000000000000000029403601535432533);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // Create the PRNG
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
/// let mut encryption_generator =
|
||||
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
/// let mut secret_generator =
|
||||
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
///
|
||||
/// // Create the GlweSecretKey
|
||||
/// let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
/// glwe_size_out.to_glwe_dimension(),
|
||||
/// polynomial_size,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
/// // Create the GlweSecretKey
|
||||
/// let glwe_secret_key_to_encrypt = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
/// glwe_size_in.to_glwe_dimension(),
|
||||
/// polynomial_size_to_encrypt,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
///
|
||||
/// // Create the plaintext
|
||||
/// let plaintext = Plaintext(3u64);
|
||||
///
|
||||
/// // Create a new GgswCiphertext
|
||||
/// let mut ggsw = PseudoGgswCiphertext::new(
|
||||
/// 0u64,
|
||||
/// glwe_size_in,
|
||||
/// glwe_size_out,
|
||||
/// polynomial_size,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// encrypt_pseudo_ggsw_ciphertext(
|
||||
/// &glwe_secret_key,
|
||||
/// &glwe_secret_key_to_encrypt,
|
||||
/// &mut ggsw,
|
||||
/// glwe_modular_std_dev,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
/// println!("GGSW = {:?}", ggsw);
|
||||
/// let decrypted = decrypt_constant_ggsw_ciphertext(&glwe_secret_key, &ggsw);
|
||||
/// assert_eq!(decrypted, plaintext);
|
||||
/// ```
|
||||
pub fn encrypt_pseudo_ggsw_ciphertext<Scalar, KeyCont, OutputCont, Gen>(
|
||||
glwe_secret_key_out: &GlweSecretKey<KeyCont>,
|
||||
glwe_secret_key_in: &GlweSecretKey<KeyCont>,
|
||||
output: &mut PseudoGgswCiphertext<OutputCont>,
|
||||
noise_parameters: impl DispersionParameter,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert!(
|
||||
output.polynomial_size() == glwe_secret_key_out.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_out.polynomial_size()
|
||||
);
|
||||
|
||||
// Generators used to have same sequential and parallel key generation
|
||||
let gen_iter = generator
|
||||
.fork_pseudo_ggsw_to_ggsw_levels::<Scalar>(
|
||||
output.decomposition_level_count(),
|
||||
output.glwe_size_in(),
|
||||
output.glwe_size_out(),
|
||||
output.polynomial_size(),
|
||||
)
|
||||
.expect("Failed to split generator into ggsw levels");
|
||||
|
||||
let output_glwe_size_in = output.glwe_size_in();
|
||||
let ouptut_glwe_size_out = output.glwe_size_out();
|
||||
let output_polynomial_size = output.polynomial_size();
|
||||
let decomp_base_log = output.decomposition_base_log();
|
||||
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(level_index + 1);
|
||||
// We scale the factor down from the native torus to whatever our torus is, the
|
||||
// encryption process will scale it back up
|
||||
let encoded = Scalar::ONE;
|
||||
let factor = encoded
|
||||
.wrapping_neg()
|
||||
.wrapping_mul(Scalar::ONE << (Scalar::BITS - (decomp_base_log.0 * decomp_level.0)))
|
||||
.wrapping_div(ciphertext_modulus.get_power_of_two_scaling_to_native_torus());
|
||||
|
||||
// We iterate over the rows of the level matrix, the last row needs special treatment
|
||||
let gen_iter = generator
|
||||
.fork_pseudo_ggsw_level_to_glwe::<Scalar>(
|
||||
output_glwe_size_in,
|
||||
ouptut_glwe_size_out,
|
||||
output_polynomial_size,
|
||||
)
|
||||
.expect("Failed to split generator into glwe");
|
||||
|
||||
for ((row_index, mut row_as_glwe), mut generator) in level_matrix
|
||||
.as_mut_glwe_list()
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.zip(gen_iter)
|
||||
{
|
||||
encrypt_pseudo_ggsw_level_matrix_row(
|
||||
glwe_secret_key_out,
|
||||
glwe_secret_key_in,
|
||||
row_index,
|
||||
factor,
|
||||
&mut row_as_glwe,
|
||||
noise_parameters,
|
||||
&mut generator,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encrypt_pseudo_ggsw_level_matrix_row<Scalar, KeyCont, OutputCont, Gen>(
|
||||
glwe_secret_key: &GlweSecretKey<KeyCont>,
|
||||
glwe_secret_key_to_encrypt: &GlweSecretKey<KeyCont>,
|
||||
row_index: usize,
|
||||
factor: Scalar,
|
||||
row_as_glwe: &mut GlweCiphertext<OutputCont>,
|
||||
noise_parameters: impl DispersionParameter,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let sk_poly_list = glwe_secret_key_to_encrypt.as_polynomial_list();
|
||||
let sk_poly = sk_poly_list.get(row_index);
|
||||
|
||||
// let poly_size = row_as_glwe.polynomial_size();
|
||||
// let ct_mod = row_as_glwe.ciphertext_modulus();
|
||||
// let glwe_size = row_as_glwe.glwe_size();
|
||||
|
||||
// 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());
|
||||
|
||||
slice_wrapping_scalar_mul_assign(body.as_mut(), factor);
|
||||
//println!("[ggsw_encryption_row] KEY VALUE = {:?}", body.as_ref());
|
||||
|
||||
encrypt_glwe_ciphertext_assign(glwe_secret_key, row_as_glwe, noise_parameters, generator);
|
||||
// let mut ct = GlweCiphertext::new(Scalar::ZERO, glwe_size, poly_size,
|
||||
// ct_mod);
|
||||
// trivially_encrypt_glwe_ciphertext(&mut ct,
|
||||
// &PlaintextList::from_container(body.as_polynomial ().into_container()));
|
||||
|
||||
//TODO: to remove for tests
|
||||
// let mut pt = PlaintextList::new(Scalar::ZERO, PlaintextCount(glwe_secret_key
|
||||
// .polynomial_size().0 * glwe_secret_key.glwe_dimension().0));
|
||||
// decrypt_glwe_ciphertext(glwe_secret_key, &row_as_glwe, &mut pt);
|
||||
// println!("[ggsw_encryption_row] DECRYTPTED GLWE ROW = {:?}", pt);
|
||||
}
|
||||
617
tfhe/src/core_crypto/algorithms/test/lwe_fast_keyswitch.rs
Normal file
617
tfhe/src/core_crypto/algorithms/test/lwe_fast_keyswitch.rs
Normal file
@@ -0,0 +1,617 @@
|
||||
use super::*;
|
||||
use crate::core_crypto::algorithms::test::MessageModulusLog;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct FastKSParam<Scalar: UnsignedInteger> {
|
||||
pub log_precision: MessageModulusLog,
|
||||
/// This value is unused but allows to identify the parameter optimization that was done
|
||||
pub _log_mu: usize,
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
// phi_bsk = number of coefficients != 0 in a given key for partial keys
|
||||
// this is the general phi_bsk for a GLWE secret key, which saturates to 2443 according to the
|
||||
// paper
|
||||
pub bsk_partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount,
|
||||
pub bsk_glwe_std_dev: StandardDev,
|
||||
pub lwe_dimension: LweDimension,
|
||||
pub ks1_lwe_modular_std_dev: StandardDev,
|
||||
pub pbs_level: DecompositionLevelCount,
|
||||
pub pbs_base_log: DecompositionBaseLog,
|
||||
pub ks1_level: DecompositionLevelCount,
|
||||
pub ks1_base_log: DecompositionBaseLog,
|
||||
pub ks1_polynomial_size: PolynomialSize,
|
||||
pub ks_in_glwe_dimension: GlweDimension,
|
||||
pub phi_in: usize,
|
||||
pub ks_out_glwe_dimension: GlweDimension,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub const PRECISION_1_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: MessageModulusLog(1),
|
||||
_log_mu: 1,
|
||||
glwe_dimension: GlweDimension(5),
|
||||
polynomial_size: PolynomialSize(256),
|
||||
bsk_partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(1280),
|
||||
bsk_glwe_std_dev: StandardDev(4.436066365074074e-10),
|
||||
lwe_dimension: LweDimension(534),
|
||||
ks1_lwe_modular_std_dev: StandardDev(0.0004192214045106218),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(15),
|
||||
ks1_level: DecompositionLevelCount(9),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(256),
|
||||
ks_in_glwe_dimension: GlweDimension(3),
|
||||
phi_in: 746,
|
||||
ks_out_glwe_dimension: GlweDimension(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
#[allow(dead_code)]
|
||||
pub const PRECISION_2_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: MessageModulusLog(2),
|
||||
_log_mu: 2,
|
||||
glwe_dimension: GlweDimension(6),
|
||||
polynomial_size: PolynomialSize(256),
|
||||
bsk_partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(1536),
|
||||
bsk_glwe_std_dev: StandardDev(3.953518398797519e-12),
|
||||
lwe_dimension: LweDimension(590),
|
||||
ks1_lwe_modular_std_dev: StandardDev(0.0001492480807729575),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(18),
|
||||
ks1_level: DecompositionLevelCount(11),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(1024),
|
||||
ks_in_glwe_dimension: GlweDimension(1),
|
||||
phi_in: 946,
|
||||
ks_out_glwe_dimension: GlweDimension(1),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
#[allow(dead_code)]
|
||||
pub const PRECISION_3_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: MessageModulusLog(3),
|
||||
_log_mu: 3,
|
||||
glwe_dimension: GlweDimension(3),
|
||||
polynomial_size: PolynomialSize(512),
|
||||
bsk_partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(1536),
|
||||
bsk_glwe_std_dev: StandardDev(3.953518398797519e-12),
|
||||
lwe_dimension: LweDimension(686),
|
||||
ks1_lwe_modular_std_dev: StandardDev(2.5308824029981747e-5),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(18),
|
||||
ks1_level: DecompositionLevelCount(13),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(1024),
|
||||
ks_in_glwe_dimension: GlweDimension(1),
|
||||
phi_in: 850,
|
||||
ks_out_glwe_dimension: GlweDimension(1),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
#[allow(dead_code)]
|
||||
pub const PRECISION_4_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: MessageModulusLog(4),
|
||||
_log_mu: 4,
|
||||
glwe_dimension: GlweDimension(2),
|
||||
polynomial_size: PolynomialSize(1024),
|
||||
bsk_partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2048),
|
||||
bsk_glwe_std_dev: StandardDev(3.162026630747649e-16),
|
||||
lwe_dimension: LweDimension(682),
|
||||
ks1_lwe_modular_std_dev: StandardDev(2.7313997525878062e-5),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(23),
|
||||
ks1_level: DecompositionLevelCount(14),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(512),
|
||||
ks_in_glwe_dimension: GlweDimension(3), // Original value
|
||||
// ks_in_glwe_dimension: GlweDimension(4), // Test non square pseudo GGSWs
|
||||
phi_in: 1366,
|
||||
ks_out_glwe_dimension: GlweDimension(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
#[allow(dead_code)]
|
||||
pub const PRECISION_5_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: MessageModulusLog(5),
|
||||
_log_mu: 5,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(2048),
|
||||
bsk_partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2048),
|
||||
bsk_glwe_std_dev: StandardDev(3.162026630747649e-16),
|
||||
lwe_dimension: LweDimension(766),
|
||||
ks1_lwe_modular_std_dev: StandardDev(5.822216831056818e-06),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(23),
|
||||
ks1_level: DecompositionLevelCount(15),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(512),
|
||||
ks_in_glwe_dimension: GlweDimension(3),
|
||||
phi_in: 1282,
|
||||
ks_out_glwe_dimension: GlweDimension(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
#[allow(dead_code)]
|
||||
pub const PRECISION_6_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: MessageModulusLog(6),
|
||||
_log_mu: 6,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(4096),
|
||||
bsk_partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2443),
|
||||
bsk_glwe_std_dev: StandardDev(2.168404344971009e-19),
|
||||
lwe_dimension: LweDimension(774),
|
||||
ks1_lwe_modular_std_dev: StandardDev(4.998754134591537e-06),
|
||||
pbs_level: DecompositionLevelCount(2),
|
||||
pbs_base_log: DecompositionBaseLog(14),
|
||||
ks1_level: DecompositionLevelCount(15),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(2048),
|
||||
ks_in_glwe_dimension: GlweDimension(1),
|
||||
phi_in: 1669,
|
||||
ks_out_glwe_dimension: GlweDimension(1),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
#[allow(dead_code)]
|
||||
pub const PRECISION_7_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: MessageModulusLog(7),
|
||||
_log_mu: 7,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(8192),
|
||||
bsk_partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2443),
|
||||
bsk_glwe_std_dev: StandardDev(2.168404344971009e-19),
|
||||
lwe_dimension: LweDimension(818),
|
||||
ks1_lwe_modular_std_dev: StandardDev(2.2215530137414073e-06),
|
||||
pbs_level: DecompositionLevelCount(2),
|
||||
pbs_base_log: DecompositionBaseLog(14),
|
||||
ks1_level: DecompositionLevelCount(16),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(2048),
|
||||
ks_in_glwe_dimension: GlweDimension(1),
|
||||
phi_in: 1625,
|
||||
ks_out_glwe_dimension: GlweDimension(1),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
#[allow(dead_code)]
|
||||
pub const PRECISION_8_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: MessageModulusLog(8),
|
||||
_log_mu: 8,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(16384),
|
||||
bsk_partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2443),
|
||||
bsk_glwe_std_dev: StandardDev(2.168404344971009e-19),
|
||||
lwe_dimension: LweDimension(854),
|
||||
ks1_lwe_modular_std_dev: StandardDev(1.1499479557902908e-06),
|
||||
pbs_level: DecompositionLevelCount(3),
|
||||
pbs_base_log: DecompositionBaseLog(11),
|
||||
ks1_level: DecompositionLevelCount(18),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(2048),
|
||||
ks_in_glwe_dimension: GlweDimension(1),
|
||||
phi_in: 1589,
|
||||
ks_out_glwe_dimension: GlweDimension(1),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
#[allow(dead_code)]
|
||||
pub const PRECISION_9_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: MessageModulusLog(9),
|
||||
_log_mu: 9,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(32768),
|
||||
bsk_partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2443),
|
||||
bsk_glwe_std_dev: StandardDev(2.168404344971009e-19),
|
||||
lwe_dimension: LweDimension(902),
|
||||
ks1_lwe_modular_std_dev: StandardDev(4.7354340335704556e-07),
|
||||
pbs_level: DecompositionLevelCount(4),
|
||||
pbs_base_log: DecompositionBaseLog(8),
|
||||
ks1_level: DecompositionLevelCount(18),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(2048),
|
||||
ks_in_glwe_dimension: GlweDimension(1),
|
||||
phi_in: 1541,
|
||||
ks_out_glwe_dimension: GlweDimension(1),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
#[allow(dead_code)]
|
||||
pub const PRECISION_10_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: MessageModulusLog(10),
|
||||
_log_mu: 10,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(65536),
|
||||
bsk_partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2443),
|
||||
bsk_glwe_std_dev: StandardDev(2.168404344971009e-19),
|
||||
lwe_dimension: LweDimension(938),
|
||||
ks1_lwe_modular_std_dev: StandardDev(2.434282602565751e-07),
|
||||
pbs_level: DecompositionLevelCount(6),
|
||||
pbs_base_log: DecompositionBaseLog(6),
|
||||
ks1_level: DecompositionLevelCount(20),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(512),
|
||||
ks_in_glwe_dimension: GlweDimension(3),
|
||||
phi_in: 1505,
|
||||
ks_out_glwe_dimension: GlweDimension(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
#[allow(dead_code)]
|
||||
pub const PRECISION_11_FAST_KS: FastKSParam<u64> = FastKSParam {
|
||||
log_precision: MessageModulusLog(11),
|
||||
_log_mu: 11,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(131072),
|
||||
bsk_partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2443),
|
||||
bsk_glwe_std_dev: StandardDev(2.168404344971009e-19),
|
||||
lwe_dimension: LweDimension(1018),
|
||||
ks1_lwe_modular_std_dev: StandardDev(5.56131000242714e-08),
|
||||
pbs_level: DecompositionLevelCount(13),
|
||||
pbs_base_log: DecompositionBaseLog(3),
|
||||
ks1_level: DecompositionLevelCount(22),
|
||||
ks1_base_log: DecompositionBaseLog(1),
|
||||
ks1_polynomial_size: PolynomialSize(512),
|
||||
ks_in_glwe_dimension: GlweDimension(3),
|
||||
phi_in: 1425,
|
||||
ks_out_glwe_dimension: GlweDimension(3),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
|
||||
fn lwe_encrypt_fast_ks_decrypt_custom_mod<
|
||||
Scalar: UnsignedTorus + CastFrom<usize> + CastInto<usize> + Sync + Send,
|
||||
>(
|
||||
params: FastKSParam<Scalar>,
|
||||
) {
|
||||
let log_precision = params.log_precision;
|
||||
let glwe_dimension = params.glwe_dimension;
|
||||
let polynomial_size = params.polynomial_size;
|
||||
let bsk_partial_glwe_secret_key_fill = params.bsk_partial_glwe_secret_key_fill;
|
||||
let bsk_glwe_std_dev = params.bsk_glwe_std_dev;
|
||||
let lwe_dimension = params.lwe_dimension;
|
||||
let ks1_lwe_modular_std_dev = params.ks1_lwe_modular_std_dev;
|
||||
let pbs_level = params.pbs_level;
|
||||
let pbs_base_log = params.pbs_base_log;
|
||||
let ks1_level = params.ks1_level;
|
||||
let ks1_base_log = params.ks1_base_log;
|
||||
let ks1_polynomial_size = params.ks1_polynomial_size;
|
||||
let ks_in_glwe_dimension = params.ks_in_glwe_dimension;
|
||||
let phi_in = params.phi_in;
|
||||
let ks_out_glwe_dimension = params.ks_out_glwe_dimension;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
|
||||
let msg_modulus = Scalar::ONE.shl(log_precision.0);
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
const NB_TESTS: usize = 10;
|
||||
let mut msg = msg_modulus;
|
||||
let delta: Scalar = encoding_with_padding / msg_modulus;
|
||||
|
||||
let f = |x| x;
|
||||
|
||||
// Shared and partial key generations
|
||||
let glwe_secret_key = allocate_and_generate_new_binary_partial_glwe_secret_key(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
bsk_partial_glwe_secret_key_fill,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
|
||||
let large_lwe_secret_key = glwe_secret_key.clone().into_lwe_secret_key();
|
||||
|
||||
// Shared small lwe secret key sharing lwe_dimension coeffs with the glwe_secret_key
|
||||
let mut small_lwe_secret_key = LweSecretKey::new_empty_key(Scalar::ZERO, lwe_dimension);
|
||||
small_lwe_secret_key.as_mut()[0..lwe_dimension.0]
|
||||
.copy_from_slice(&glwe_secret_key.as_ref()[0..lwe_dimension.0]);
|
||||
|
||||
// Corresponds to the small LWE sk with only lwe_dimension coeffs that are non zero and
|
||||
// shared with the glwe_secret_key
|
||||
let small_glwe_secret_key =
|
||||
allocate_and_generate_new_shared_glwe_secret_key_from_glwe_secret_key(
|
||||
&glwe_secret_key,
|
||||
ks_out_glwe_dimension,
|
||||
lwe_dimension.0,
|
||||
ks1_polynomial_size,
|
||||
);
|
||||
|
||||
let mut large_glwe_secret_key_unshared =
|
||||
GlweSecretKey::new_empty_key(Scalar::ZERO, ks_in_glwe_dimension, ks1_polynomial_size);
|
||||
|
||||
// Get the unshared coefficients in the large glwe secret key which is used for the input of
|
||||
// the KS
|
||||
large_glwe_secret_key_unshared.as_mut()
|
||||
[0..bsk_partial_glwe_secret_key_fill.0 - lwe_dimension.0]
|
||||
.copy_from_slice(
|
||||
&glwe_secret_key.as_ref()[lwe_dimension.0..bsk_partial_glwe_secret_key_fill.0],
|
||||
);
|
||||
|
||||
//Memory stuff
|
||||
let fft_ks = Fft::new(ks1_polynomial_size);
|
||||
let fft_ks = fft_ks.as_view();
|
||||
let mut ks_buffers = ComputationBuffers::new();
|
||||
let mut pbs_buffers = ComputationBuffers::new();
|
||||
|
||||
let fft_pbs = Fft::new(polynomial_size);
|
||||
let fft_pbs = fft_pbs.as_view();
|
||||
|
||||
let ks_buffer_size_req =
|
||||
add_external_product_fast_keyswitch_assign_mem_optimized_requirement::<Scalar>(
|
||||
small_glwe_secret_key.glwe_dimension(),
|
||||
ks1_polynomial_size,
|
||||
fft_ks,
|
||||
)
|
||||
.unwrap()
|
||||
.try_unaligned_bytes_required()
|
||||
.unwrap();
|
||||
|
||||
let ks_buffer_size_req = ks_buffer_size_req.max(
|
||||
convert_standard_ggsw_ciphertext_to_fourier_mem_optimized_requirement(fft_ks)
|
||||
.unwrap()
|
||||
.unaligned_bytes_required(),
|
||||
);
|
||||
let pbs_buffer_size_req = programmable_bootstrap_lwe_ciphertext_mem_optimized_requirement::<
|
||||
Scalar,
|
||||
>(glwe_dimension.to_glwe_size(), polynomial_size, fft_pbs)
|
||||
.unwrap()
|
||||
.try_unaligned_bytes_required()
|
||||
.unwrap();
|
||||
|
||||
ks_buffers.resize(ks_buffer_size_req);
|
||||
pbs_buffers.resize(pbs_buffer_size_req);
|
||||
|
||||
let mut bsk = LweBootstrapKey::new(
|
||||
Scalar::ZERO,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
pbs_base_log,
|
||||
pbs_level,
|
||||
lwe_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
par_generate_lwe_bootstrap_key(
|
||||
&small_lwe_secret_key,
|
||||
&glwe_secret_key,
|
||||
&mut bsk,
|
||||
bsk_glwe_std_dev,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
let mut fbsk = FourierLweBootstrapKey::new(
|
||||
small_lwe_secret_key.lwe_dimension(),
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
pbs_base_log,
|
||||
pbs_level,
|
||||
);
|
||||
|
||||
convert_standard_lwe_bootstrap_key_to_fourier_mem_optimized(
|
||||
&bsk,
|
||||
&mut fbsk,
|
||||
fft_pbs,
|
||||
pbs_buffers.stack(),
|
||||
);
|
||||
|
||||
drop(bsk);
|
||||
|
||||
let accumulator = generate_accumulator(
|
||||
polynomial_size,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
msg_modulus.cast_into(),
|
||||
ciphertext_modulus,
|
||||
delta,
|
||||
f,
|
||||
);
|
||||
|
||||
let mut ggsw = PseudoGgswCiphertext::new(
|
||||
Scalar::ZERO,
|
||||
ks_in_glwe_dimension.to_glwe_size(),
|
||||
ks_out_glwe_dimension.to_glwe_size(),
|
||||
ks1_polynomial_size,
|
||||
ks1_base_log,
|
||||
ks1_level,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
encrypt_pseudo_ggsw_ciphertext(
|
||||
&small_glwe_secret_key,
|
||||
&large_glwe_secret_key_unshared,
|
||||
&mut ggsw,
|
||||
ks1_lwe_modular_std_dev,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
//To Fourier
|
||||
let mut fourier_ggsw = PseudoFourierGgswCiphertext::new(
|
||||
ks_in_glwe_dimension.to_glwe_size(),
|
||||
ks_out_glwe_dimension.to_glwe_size(),
|
||||
ks1_polynomial_size,
|
||||
ks1_base_log,
|
||||
ks1_level,
|
||||
);
|
||||
|
||||
convert_standard_pseudo_ggsw_ciphertext_to_fourier_mem_optimized(
|
||||
&ggsw,
|
||||
&mut fourier_ggsw,
|
||||
fft_ks,
|
||||
ks_buffers.stack(),
|
||||
);
|
||||
|
||||
while msg != Scalar::ZERO {
|
||||
msg = msg.wrapping_sub(Scalar::ONE);
|
||||
println!("msg={msg}");
|
||||
|
||||
for test_iteration in 0..NB_TESTS {
|
||||
println!("test_iteration={test_iteration}");
|
||||
let plaintext = Plaintext(msg * delta);
|
||||
|
||||
let mut large_lwe_ciphertext_ap_input = allocate_and_encrypt_new_lwe_ciphertext(
|
||||
&large_lwe_secret_key,
|
||||
plaintext,
|
||||
bsk_glwe_std_dev,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
let mut large_lwe_shared_part = LweCiphertext::new(
|
||||
Scalar::ZERO,
|
||||
lwe_dimension.to_lwe_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let mut large_lwe_unshared_part_requires_keyswitch = LweCiphertext::new(
|
||||
Scalar::ZERO,
|
||||
LweSize(bsk_partial_glwe_secret_key_fill.0 - lwe_dimension.0 + 1),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let mut large_glwe_unshared_requires_keyswitch = GlweCiphertext::new(
|
||||
Scalar::ZERO,
|
||||
ks_in_glwe_dimension.to_glwe_size(),
|
||||
ks1_polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let mut glwe_unshared_after_ks = GlweCiphertext::new(
|
||||
Scalar::ZERO,
|
||||
ks_out_glwe_dimension.to_glwe_size(),
|
||||
ks1_polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let mut small_lwe_unshared_after_ks = LweCiphertext::new(
|
||||
Scalar::ZERO,
|
||||
lwe_dimension.to_lwe_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
// Check the AP works for several iterations
|
||||
for ap_iteration in 0..NB_TESTS {
|
||||
println!("ap_iteration={ap_iteration}");
|
||||
let decrypted =
|
||||
decrypt_lwe_ciphertext(&large_lwe_secret_key, &large_lwe_ciphertext_ap_input);
|
||||
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
|
||||
assert_eq!(decoded, f(msg));
|
||||
|
||||
// Copy the shared part in the smaller ciphertext
|
||||
large_lwe_shared_part
|
||||
.get_mut_mask()
|
||||
.as_mut()
|
||||
.copy_from_slice(
|
||||
&large_lwe_ciphertext_ap_input.get_mask().as_ref()[..lwe_dimension.0],
|
||||
);
|
||||
*large_lwe_shared_part.get_mut_body().data =
|
||||
*large_lwe_ciphertext_ap_input.get_body().data;
|
||||
|
||||
// Copy the unshared part to be keyswitched
|
||||
large_lwe_unshared_part_requires_keyswitch
|
||||
.get_mut_mask()
|
||||
.as_mut()
|
||||
.copy_from_slice(
|
||||
&large_lwe_ciphertext_ap_input.get_mask().as_ref()
|
||||
[lwe_dimension.0..bsk_partial_glwe_secret_key_fill.0],
|
||||
);
|
||||
|
||||
partial_convert_lwe_ciphertext_into_constant_glwe_ciphertext(
|
||||
&large_lwe_unshared_part_requires_keyswitch,
|
||||
&mut large_glwe_unshared_requires_keyswitch,
|
||||
phi_in,
|
||||
);
|
||||
|
||||
glwe_unshared_after_ks.as_mut().fill(Scalar::ZERO);
|
||||
|
||||
add_external_product_fast_keyswitch_assign_mem_optimized(
|
||||
&mut glwe_unshared_after_ks,
|
||||
&fourier_ggsw,
|
||||
&large_glwe_unshared_requires_keyswitch,
|
||||
fft_ks,
|
||||
ks_buffers.stack(),
|
||||
);
|
||||
|
||||
partial_extract_lwe_sample_from_glwe_ciphertext(
|
||||
&glwe_unshared_after_ks,
|
||||
&mut small_lwe_unshared_after_ks,
|
||||
MonomialDegree(0),
|
||||
lwe_dimension.0,
|
||||
);
|
||||
|
||||
// Sum with initial ct's shared part
|
||||
small_lwe_unshared_after_ks
|
||||
.get_mut_mask()
|
||||
.as_mut()
|
||||
.iter_mut()
|
||||
.zip(large_lwe_shared_part.as_ref()[0..lwe_dimension.0].iter())
|
||||
.for_each(|(dst, &src)| *dst = dst.wrapping_add(src));
|
||||
// Sum the body
|
||||
let body = small_lwe_unshared_after_ks.get_mut_body().data;
|
||||
*body = (*body).wrapping_add(*large_lwe_shared_part.get_body().data);
|
||||
|
||||
let decrypted =
|
||||
decrypt_lwe_ciphertext(&small_lwe_secret_key, &small_lwe_unshared_after_ks);
|
||||
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
|
||||
assert_eq!(decoded, f(msg));
|
||||
|
||||
programmable_bootstrap_lwe_ciphertext_mem_optimized(
|
||||
&small_lwe_unshared_after_ks,
|
||||
&mut large_lwe_ciphertext_ap_input,
|
||||
&accumulator,
|
||||
&fbsk,
|
||||
fft_pbs,
|
||||
pbs_buffers.stack(),
|
||||
);
|
||||
|
||||
let decrypted =
|
||||
decrypt_lwe_ciphertext(&large_lwe_secret_key, &large_lwe_ciphertext_ap_input);
|
||||
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
|
||||
assert_eq!(decoded, f(msg));
|
||||
|
||||
////////////
|
||||
// Remove the input message to get 0
|
||||
lwe_ciphertext_plaintext_sub_assign(&mut large_lwe_ciphertext_ap_input, plaintext);
|
||||
|
||||
let decrypted =
|
||||
decrypt_lwe_ciphertext(&large_lwe_secret_key, &large_lwe_ciphertext_ap_input);
|
||||
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
|
||||
assert_eq!(Scalar::ZERO, decoded, "Error after sub");
|
||||
////////////
|
||||
|
||||
// multiply by a cleartext, will still output 0 but is the biggest noise growth
|
||||
// possible
|
||||
lwe_ciphertext_cleartext_mul_assign(
|
||||
&mut large_lwe_ciphertext_ap_input,
|
||||
Cleartext(msg_modulus),
|
||||
);
|
||||
let decrypted =
|
||||
decrypt_lwe_ciphertext(&large_lwe_secret_key, &large_lwe_ciphertext_ap_input);
|
||||
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
assert_eq!(Scalar::ZERO, decoded, "Error after mul");
|
||||
////////////
|
||||
|
||||
// Add back the input plaintext to still be doing an overall identity computation
|
||||
lwe_ciphertext_plaintext_add_assign(&mut large_lwe_ciphertext_ap_input, plaintext);
|
||||
let decrypted =
|
||||
decrypt_lwe_ciphertext(&large_lwe_secret_key, &large_lwe_ciphertext_ap_input);
|
||||
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
assert_eq!(msg, decoded, "Error after add");
|
||||
////////////
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create_parametrized_test!(lwe_encrypt_fast_ks_decrypt_custom_mod {
|
||||
PRECISION_1_FAST_KS,
|
||||
PRECISION_2_FAST_KS,
|
||||
PRECISION_3_FAST_KS,
|
||||
PRECISION_4_FAST_KS,
|
||||
PRECISION_5_FAST_KS,
|
||||
PRECISION_6_FAST_KS
|
||||
// PRECISION_7_FAST_KS,
|
||||
// PRECISION_8_FAST_KS
|
||||
// PRECISION_9_FAST_KS,
|
||||
// PRECISION_10_FAST_KS,
|
||||
// PRECISION_11_FAST_KS
|
||||
});
|
||||
@@ -144,7 +144,7 @@ pub const TEST_PARAMS_4_BITS_NATIVE_U128: TestParams<u128> = TestParams {
|
||||
pfks_modular_std_dev: StandardDev(0.00000000000000029403601535432533),
|
||||
cbs_level: DecompositionLevelCount(0),
|
||||
cbs_base_log: DecompositionBaseLog(0),
|
||||
message_modulus_log: CiphertextModulusLog(4),
|
||||
message_modulus_log: MessageModulusLog(4),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
@@ -163,7 +163,7 @@ pub const TEST_PARAMS_3_BITS_127_U128: TestParams<u128> = TestParams {
|
||||
pfks_modular_std_dev: StandardDev(0.00000000000000029403601535432533),
|
||||
cbs_level: DecompositionLevelCount(0),
|
||||
cbs_base_log: DecompositionBaseLog(0),
|
||||
message_modulus_log: CiphertextModulusLog(3),
|
||||
message_modulus_log: MessageModulusLog(3),
|
||||
ciphertext_modulus: CiphertextModulus::new(1 << 127),
|
||||
};
|
||||
|
||||
|
||||
495
tfhe/src/core_crypto/algorithms/test/lwe_stair_keyswitch.rs
Normal file
495
tfhe/src/core_crypto/algorithms/test/lwe_stair_keyswitch.rs
Normal file
@@ -0,0 +1,495 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct StairKSParam<Scalar: UnsignedInteger> {
|
||||
pub log_precision: MessageModulusLog,
|
||||
/// This value is unused but allows to identify the parameter optimization that was done
|
||||
pub _log_mu: usize,
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
// phi = number of coefficients != 0 in a given key for partial keys
|
||||
// this is the general phi for a GLWE secret key, which saturates to 2443 according to the
|
||||
// paper
|
||||
// phi == lwe_dimension + ks1_unshared_coeff_count + ks2_unshared_coeff_count
|
||||
pub partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount,
|
||||
pub bsk_glwe_modular_std_dev: StandardDev,
|
||||
pub lwe_dimension: LweDimension,
|
||||
pub ks1_lwe_modular_std_dev: StandardDev,
|
||||
pub ks2_lwe_modular_std_dev: StandardDev,
|
||||
pub pbs_level: DecompositionLevelCount,
|
||||
pub pbs_base_log: DecompositionBaseLog,
|
||||
pub ks1_level: DecompositionLevelCount,
|
||||
pub ks1_base_log: DecompositionBaseLog,
|
||||
pub ks2_level: DecompositionLevelCount,
|
||||
pub ks2_base_log: DecompositionBaseLog,
|
||||
/// The number of elements being dropped when going from the large key to the inter key
|
||||
pub ks1_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount,
|
||||
/// The number of elements being dropped when going from the inter key to the small key
|
||||
pub ks2_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
pub const PRECISION_1_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: MessageModulusLog(1),
|
||||
_log_mu: 1,
|
||||
glwe_dimension: GlweDimension(5),
|
||||
polynomial_size: PolynomialSize(256),
|
||||
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(1280),
|
||||
bsk_glwe_modular_std_dev: StandardDev(4.43606636507407e-10),
|
||||
lwe_dimension: LweDimension(532),
|
||||
ks1_lwe_modular_std_dev: StandardDev(4.32160905950851e-6),
|
||||
ks2_lwe_modular_std_dev: StandardDev(0.000434005215413364),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(15),
|
||||
ks1_level: DecompositionLevelCount(1),
|
||||
ks1_base_log: DecompositionBaseLog(9),
|
||||
ks2_level: DecompositionLevelCount(4),
|
||||
ks2_base_log: DecompositionBaseLog(2),
|
||||
ks1_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(498),
|
||||
ks2_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(250),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
pub const PRECISION_2_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: MessageModulusLog(2),
|
||||
_log_mu: 2,
|
||||
glwe_dimension: GlweDimension(6),
|
||||
polynomial_size: PolynomialSize(256),
|
||||
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(1536),
|
||||
bsk_glwe_modular_std_dev: StandardDev(3.953518398797519e-12),
|
||||
lwe_dimension: LweDimension(576),
|
||||
ks1_lwe_modular_std_dev: StandardDev(5.290839538897724e-07),
|
||||
ks2_lwe_modular_std_dev: StandardDev(0.00019288117965414483),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(18),
|
||||
ks1_level: DecompositionLevelCount(1),
|
||||
ks1_base_log: DecompositionBaseLog(10),
|
||||
ks2_level: DecompositionLevelCount(5),
|
||||
ks2_base_log: DecompositionBaseLog(2),
|
||||
ks1_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(640),
|
||||
ks2_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(320),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
pub const PRECISION_3_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: MessageModulusLog(3),
|
||||
_log_mu: 3,
|
||||
glwe_dimension: GlweDimension(3),
|
||||
polynomial_size: PolynomialSize(512),
|
||||
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(1536),
|
||||
bsk_glwe_modular_std_dev: StandardDev(3.95351839879752e-12),
|
||||
lwe_dimension: LweDimension(648),
|
||||
ks1_lwe_modular_std_dev: StandardDev(2.17874395902014e-7),
|
||||
ks2_lwe_modular_std_dev: StandardDev(5.132424409507535e-05),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(18),
|
||||
ks1_level: DecompositionLevelCount(2),
|
||||
ks1_base_log: DecompositionBaseLog(7),
|
||||
ks2_level: DecompositionLevelCount(6),
|
||||
ks2_base_log: DecompositionBaseLog(2),
|
||||
ks1_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(592),
|
||||
ks2_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(296),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
pub const PRECISION_4_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: MessageModulusLog(4),
|
||||
_log_mu: 4,
|
||||
glwe_dimension: GlweDimension(2),
|
||||
polynomial_size: PolynomialSize(1024),
|
||||
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2048),
|
||||
bsk_glwe_modular_std_dev: StandardDev(3.16202663074765e-16),
|
||||
lwe_dimension: LweDimension(664),
|
||||
ks1_lwe_modular_std_dev: StandardDev(7.60713313301797e-9),
|
||||
ks2_lwe_modular_std_dev: StandardDev(0.0000380960250519291),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(22),
|
||||
ks1_level: DecompositionLevelCount(1),
|
||||
ks1_base_log: DecompositionBaseLog(13),
|
||||
ks2_level: DecompositionLevelCount(6),
|
||||
ks2_base_log: DecompositionBaseLog(2),
|
||||
ks1_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(922),
|
||||
ks2_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(462),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
pub const PRECISION_5_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: MessageModulusLog(5),
|
||||
_log_mu: 5,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(2048),
|
||||
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2048),
|
||||
bsk_glwe_modular_std_dev: StandardDev(3.16202663074765e-16),
|
||||
lwe_dimension: LweDimension(732),
|
||||
ks1_lwe_modular_std_dev: StandardDev(3.31119701700870e-9),
|
||||
ks2_lwe_modular_std_dev: StandardDev(0.0000108646407745138),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
pbs_base_log: DecompositionBaseLog(23),
|
||||
ks1_level: DecompositionLevelCount(2),
|
||||
ks1_base_log: DecompositionBaseLog(9),
|
||||
ks2_level: DecompositionLevelCount(7),
|
||||
ks2_base_log: DecompositionBaseLog(2),
|
||||
ks1_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(877),
|
||||
ks2_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(439),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
pub const PRECISION_6_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: MessageModulusLog(6),
|
||||
_log_mu: 6,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(4096),
|
||||
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2443),
|
||||
bsk_glwe_modular_std_dev: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(748),
|
||||
ks1_lwe_modular_std_dev: StandardDev(2.42717974083759e-10),
|
||||
ks2_lwe_modular_std_dev: StandardDev(8.12050004923523e-6),
|
||||
pbs_level: DecompositionLevelCount(2),
|
||||
pbs_base_log: DecompositionBaseLog(14),
|
||||
ks1_level: DecompositionLevelCount(1),
|
||||
ks1_base_log: DecompositionBaseLog(16),
|
||||
ks2_level: DecompositionLevelCount(8),
|
||||
ks2_base_log: DecompositionBaseLog(2),
|
||||
ks1_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(1130),
|
||||
ks2_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(565),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
pub const PRECISION_7_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: MessageModulusLog(7),
|
||||
_log_mu: 7,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(8192),
|
||||
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2443),
|
||||
bsk_glwe_modular_std_dev: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(776),
|
||||
ks1_lwe_modular_std_dev: StandardDev(1.70442007475721e-10),
|
||||
ks2_lwe_modular_std_dev: StandardDev(4.82847821796524e-6),
|
||||
pbs_level: DecompositionLevelCount(2),
|
||||
pbs_base_log: DecompositionBaseLog(15),
|
||||
ks1_level: DecompositionLevelCount(2),
|
||||
ks1_base_log: DecompositionBaseLog(10),
|
||||
ks2_level: DecompositionLevelCount(16),
|
||||
ks2_base_log: DecompositionBaseLog(1),
|
||||
ks1_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(1111),
|
||||
ks2_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(556),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
pub const PRECISION_8_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: MessageModulusLog(8),
|
||||
_log_mu: 8,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(16384),
|
||||
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2443),
|
||||
bsk_glwe_modular_std_dev: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(816),
|
||||
ks1_lwe_modular_std_dev: StandardDev(1.03474906781522e-10),
|
||||
ks2_lwe_modular_std_dev: StandardDev(2.31589295271883e-6),
|
||||
pbs_level: DecompositionLevelCount(3),
|
||||
pbs_base_log: DecompositionBaseLog(11),
|
||||
ks1_level: DecompositionLevelCount(2),
|
||||
ks1_base_log: DecompositionBaseLog(9),
|
||||
ks2_level: DecompositionLevelCount(17),
|
||||
ks2_base_log: DecompositionBaseLog(1),
|
||||
ks1_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(1084),
|
||||
ks2_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(543),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
pub const PRECISION_9_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: MessageModulusLog(9),
|
||||
_log_mu: 9,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(32768),
|
||||
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2443),
|
||||
bsk_glwe_modular_std_dev: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(860),
|
||||
ks1_lwe_modular_std_dev: StandardDev(6.06794935209399e-11),
|
||||
ks2_lwe_modular_std_dev: StandardDev(1.02923225069468e-6),
|
||||
pbs_level: DecompositionLevelCount(4),
|
||||
pbs_base_log: DecompositionBaseLog(8),
|
||||
ks1_level: DecompositionLevelCount(2),
|
||||
ks1_base_log: DecompositionBaseLog(10),
|
||||
ks2_level: DecompositionLevelCount(18),
|
||||
ks2_base_log: DecompositionBaseLog(1),
|
||||
ks1_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(1055),
|
||||
ks2_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(528),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
pub const PRECISION_10_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: MessageModulusLog(10),
|
||||
_log_mu: 10,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(65536),
|
||||
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2443),
|
||||
bsk_glwe_modular_std_dev: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(904),
|
||||
ks1_lwe_modular_std_dev: StandardDev(3.55835153515238e-11),
|
||||
ks2_lwe_modular_std_dev: StandardDev(4.77994271508188e-14),
|
||||
pbs_level: DecompositionLevelCount(6),
|
||||
pbs_base_log: DecompositionBaseLog(6),
|
||||
ks1_level: DecompositionLevelCount(2),
|
||||
ks1_base_log: DecompositionBaseLog(11),
|
||||
ks2_level: DecompositionLevelCount(19),
|
||||
ks2_base_log: DecompositionBaseLog(1),
|
||||
ks1_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(1026),
|
||||
ks2_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(513),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
pub const PRECISION_11_STAIR: StairKSParam<u64> = StairKSParam {
|
||||
log_precision: MessageModulusLog(11),
|
||||
_log_mu: 11,
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(131072),
|
||||
partial_glwe_secret_key_fill: PartialGlweSecretKeyRandomCoefCount(2443),
|
||||
bsk_glwe_modular_std_dev: StandardDev(2.16840434497101e-19),
|
||||
lwe_dimension: LweDimension(984),
|
||||
ks1_lwe_modular_std_dev: StandardDev(1.31149203314392e-11),
|
||||
ks2_lwe_modular_std_dev: StandardDev(1.04499545254235e-7),
|
||||
pbs_level: DecompositionLevelCount(12),
|
||||
pbs_base_log: DecompositionBaseLog(3),
|
||||
ks1_level: DecompositionLevelCount(2),
|
||||
ks1_base_log: DecompositionBaseLog(11),
|
||||
ks2_level: DecompositionLevelCount(21),
|
||||
ks2_base_log: DecompositionBaseLog(1),
|
||||
ks1_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(972),
|
||||
ks2_unshared_coeff_count: SharedLweSecretKeyDifferingCoefCount(487),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
fn lwe_encrypt_stair_keyswitch_pbs_decrypt_custom_mod<
|
||||
Scalar: UnsignedTorus + Send + Sync + CastFrom<usize> + CastInto<usize>,
|
||||
>(
|
||||
params: StairKSParam<Scalar>,
|
||||
) {
|
||||
let log_precision = params.log_precision;
|
||||
let glwe_dimension = params.glwe_dimension;
|
||||
let polynomial_size = params.polynomial_size;
|
||||
let partial_glwe_secret_key_fill = params.partial_glwe_secret_key_fill;
|
||||
let std_dev_bsk = params.bsk_glwe_modular_std_dev;
|
||||
let lwe_dimension = params.lwe_dimension;
|
||||
let ks1_lwe_modular_std_dev = params.ks1_lwe_modular_std_dev;
|
||||
let ks2_lwe_modular_std_dev = params.ks2_lwe_modular_std_dev;
|
||||
let pbs_level = params.pbs_level;
|
||||
let pbs_base_mpg = params.pbs_base_log;
|
||||
let ks1_level = params.ks1_level;
|
||||
let ks1_base_log = params.ks1_base_log;
|
||||
let ks2_level = params.ks2_level;
|
||||
let ks2_base_log = params.ks2_base_log;
|
||||
let ks1_unshared_coeff_count = params.ks1_unshared_coeff_count;
|
||||
let ks2_unshared_coeff_count = params.ks2_unshared_coeff_count;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
|
||||
let msg_modulus = Scalar::ONE.shl(log_precision.0);
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
const NB_TESTS: usize = 10;
|
||||
let mut msg = msg_modulus;
|
||||
let delta: Scalar = encoding_with_padding / msg_modulus;
|
||||
|
||||
let large_lwe_dimension = LweDimension(glwe_dimension.0 * polynomial_size.0);
|
||||
// Our algorithm is set up so that this equality holds, but in practice it could be more generic
|
||||
// if we had an intermediate LweDimension being defined
|
||||
assert_eq!(
|
||||
partial_glwe_secret_key_fill.0,
|
||||
lwe_dimension.0 + ks1_unshared_coeff_count.0 + ks2_unshared_coeff_count.0
|
||||
);
|
||||
|
||||
while msg != Scalar::ZERO {
|
||||
msg = msg.wrapping_sub(Scalar::ONE);
|
||||
|
||||
let glwe_secret_key = allocate_and_generate_new_binary_partial_glwe_secret_key(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
partial_glwe_secret_key_fill,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
|
||||
let large_lwe_secret_key = glwe_secret_key.clone().into_lwe_secret_key();
|
||||
let large_lwe_dimension_without_zeros = LweDimension(partial_glwe_secret_key_fill.0);
|
||||
|
||||
let inter_shared_coef_count =
|
||||
large_lwe_dimension_without_zeros.shared_coef_count_from(ks1_unshared_coeff_count);
|
||||
let inter_lwe_secret_key =
|
||||
allocate_and_generate_new_shared_lwe_secret_key_from_lwe_secret_key(
|
||||
&large_lwe_secret_key,
|
||||
inter_shared_coef_count,
|
||||
);
|
||||
|
||||
let small_shared_coef_count =
|
||||
inter_shared_coef_count.shared_coef_count_from(ks2_unshared_coeff_count);
|
||||
let small_lwe_secret_key =
|
||||
allocate_and_generate_new_shared_lwe_secret_key_from_lwe_secret_key(
|
||||
&inter_lwe_secret_key,
|
||||
small_shared_coef_count,
|
||||
);
|
||||
|
||||
//Shrinking KSK generations
|
||||
let ksk_large_to_inter = allocate_and_generate_new_lwe_shrinking_keyswitch_key(
|
||||
&large_lwe_secret_key,
|
||||
&inter_lwe_secret_key,
|
||||
inter_shared_coef_count,
|
||||
ks1_base_log,
|
||||
ks1_level,
|
||||
ks1_lwe_modular_std_dev,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
assert!(check_content_respects_mod(
|
||||
&ksk_large_to_inter,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let ksk_inter_to_small = allocate_and_generate_new_lwe_shrinking_keyswitch_key(
|
||||
&inter_lwe_secret_key,
|
||||
&small_lwe_secret_key,
|
||||
small_shared_coef_count,
|
||||
ks2_base_log,
|
||||
ks2_level,
|
||||
ks2_lwe_modular_std_dev,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
assert!(check_content_respects_mod(
|
||||
&ksk_inter_to_small,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
//PBS PART
|
||||
let mut bsk = LweBootstrapKey::new(
|
||||
Scalar::ZERO,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
pbs_base_mpg,
|
||||
pbs_level,
|
||||
lwe_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
par_generate_lwe_bootstrap_key(
|
||||
&small_lwe_secret_key,
|
||||
&glwe_secret_key,
|
||||
&mut bsk,
|
||||
std_dev_bsk,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
assert!(check_content_respects_mod(&*bsk, ciphertext_modulus));
|
||||
|
||||
let mut fbsk = FourierLweBootstrapKey::new(
|
||||
small_lwe_secret_key.lwe_dimension(),
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
pbs_base_mpg,
|
||||
pbs_level,
|
||||
);
|
||||
|
||||
convert_standard_lwe_bootstrap_key_to_fourier(&bsk, &mut fbsk);
|
||||
drop(bsk);
|
||||
|
||||
let accumulator = generate_accumulator(
|
||||
polynomial_size,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
msg_modulus.cast_into(),
|
||||
ciphertext_modulus,
|
||||
delta,
|
||||
|x| x,
|
||||
);
|
||||
|
||||
for _ in 0..NB_TESTS {
|
||||
let plaintext = Plaintext(msg * delta);
|
||||
|
||||
//Encryption
|
||||
let mut large_lwe = LweCiphertext::new(
|
||||
Scalar::ZERO,
|
||||
large_lwe_dimension.to_lwe_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
encrypt_lwe_ciphertext(
|
||||
&large_lwe_secret_key,
|
||||
&mut large_lwe,
|
||||
plaintext,
|
||||
std_dev_bsk,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
assert!(check_content_respects_mod(&large_lwe, ciphertext_modulus));
|
||||
|
||||
//Shrinking KS
|
||||
let mut inter_lwe = LweCiphertext::new(
|
||||
Scalar::ZERO,
|
||||
inter_lwe_secret_key.lwe_dimension().to_lwe_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let mut small_lwe = LweCiphertext::new(
|
||||
Scalar::ZERO,
|
||||
lwe_dimension.to_lwe_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let mut out_pbs_ct = LweCiphertext::new(
|
||||
Scalar::ZERO,
|
||||
large_lwe_secret_key.lwe_dimension().to_lwe_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
// Check the AP works for several iterations
|
||||
for _ in 0..NB_TESTS {
|
||||
shrinking_keyswitch_lwe_ciphertext(&ksk_large_to_inter, &large_lwe, &mut inter_lwe);
|
||||
|
||||
assert!(check_content_respects_mod(&inter_lwe, ciphertext_modulus));
|
||||
|
||||
let dec_inter = decrypt_lwe_ciphertext(&inter_lwe_secret_key, &inter_lwe);
|
||||
let decoded = round_decode(dec_inter.0, delta) % msg_modulus;
|
||||
assert_eq!(decoded, msg, "Err after first shrinking KS");
|
||||
|
||||
shrinking_keyswitch_lwe_ciphertext(&ksk_inter_to_small, &inter_lwe, &mut small_lwe);
|
||||
|
||||
assert!(check_content_respects_mod(&small_lwe, ciphertext_modulus));
|
||||
|
||||
let dec_small = decrypt_lwe_ciphertext(&small_lwe_secret_key, &small_lwe);
|
||||
let decoded = round_decode(dec_small.0, delta) % msg_modulus;
|
||||
assert_eq!(decoded, msg, "Err after second shrinking KS");
|
||||
|
||||
programmable_bootstrap_lwe_ciphertext(
|
||||
&small_lwe,
|
||||
&mut out_pbs_ct,
|
||||
&accumulator,
|
||||
&fbsk,
|
||||
);
|
||||
|
||||
assert!(check_content_respects_mod(&out_pbs_ct, ciphertext_modulus));
|
||||
|
||||
let dec_large = decrypt_lwe_ciphertext(&large_lwe_secret_key, &out_pbs_ct);
|
||||
let decoded = round_decode(dec_large.0, delta) % msg_modulus;
|
||||
assert_eq!(decoded, msg, "Err after PBS");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn lwe_encrypt_stair_keyswitch_pbs_decrypt_custom_mod_params_stair_4() {
|
||||
// lwe_encrypt_stair_keyswitch_pbs_decrypt_custom_mod(PRECISION_4_STAIR)
|
||||
// }
|
||||
|
||||
|
||||
create_parametrized_test!(lwe_encrypt_stair_keyswitch_pbs_decrypt_custom_mod{
|
||||
PRECISION_1_STAIR,
|
||||
PRECISION_2_STAIR,
|
||||
PRECISION_3_STAIR,
|
||||
PRECISION_4_STAIR,
|
||||
PRECISION_5_STAIR,
|
||||
PRECISION_6_STAIR
|
||||
// PRECISION_7_STAIR,
|
||||
// PRECISION_8_STAIR
|
||||
// PRECISION_9_STAIR,
|
||||
// PRECISION_10_STAIR,
|
||||
// PRECISION_11_STAIR
|
||||
});
|
||||
@@ -9,6 +9,7 @@ mod glwe_sample_extraction;
|
||||
mod lwe_bootstrap_key_generation;
|
||||
mod lwe_compact_public_key_generation;
|
||||
mod lwe_encryption;
|
||||
mod lwe_fast_keyswitch;
|
||||
mod lwe_keyswitch;
|
||||
mod lwe_keyswitch_key_generation;
|
||||
mod lwe_linear_algebra;
|
||||
@@ -17,7 +18,8 @@ mod lwe_multi_bit_programmable_bootstrapping;
|
||||
mod lwe_packing_keyswitch;
|
||||
mod lwe_packing_keyswitch_key_generation;
|
||||
mod lwe_private_functional_packing_keyswitch;
|
||||
mod lwe_programmable_bootstrapping;
|
||||
pub(crate) mod lwe_programmable_bootstrapping;
|
||||
mod lwe_stair_keyswitch;
|
||||
mod noise_distribution;
|
||||
|
||||
pub struct TestResources {
|
||||
@@ -26,6 +28,9 @@ pub struct TestResources {
|
||||
pub secret_random_generator: SecretRandomGenerator<ActivatedRandomGenerator>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct MessageModulusLog(pub usize);
|
||||
|
||||
impl TestResources {
|
||||
pub fn new() -> Self {
|
||||
let mut seeder = new_seeder();
|
||||
@@ -56,7 +61,7 @@ pub struct TestParams<Scalar: UnsignedTorus> {
|
||||
pub pfks_modular_std_dev: StandardDev,
|
||||
pub cbs_level: DecompositionLevelCount,
|
||||
pub cbs_base_log: DecompositionBaseLog,
|
||||
pub message_modulus_log: CiphertextModulusLog,
|
||||
pub message_modulus_log: MessageModulusLog,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
@@ -76,7 +81,7 @@ pub const TEST_PARAMS_4_BITS_NATIVE_U64: TestParams<u64> = TestParams {
|
||||
pfks_modular_std_dev: StandardDev(0.00000000000000029403601535432533),
|
||||
cbs_level: DecompositionLevelCount(0),
|
||||
cbs_base_log: DecompositionBaseLog(0),
|
||||
message_modulus_log: CiphertextModulusLog(4),
|
||||
message_modulus_log: MessageModulusLog(4),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
@@ -95,7 +100,7 @@ pub const TEST_PARAMS_3_BITS_63_U64: TestParams<u64> = TestParams {
|
||||
pfks_modular_std_dev: StandardDev(0.00000000000000029403601535432533),
|
||||
cbs_level: DecompositionLevelCount(0),
|
||||
cbs_base_log: DecompositionBaseLog(0),
|
||||
message_modulus_log: CiphertextModulusLog(3),
|
||||
message_modulus_log: MessageModulusLog(3),
|
||||
ciphertext_modulus: CiphertextModulus::new(1 << 63),
|
||||
};
|
||||
|
||||
@@ -114,7 +119,7 @@ pub const DUMMY_NATIVE_U32: TestParams<u32> = TestParams {
|
||||
pfks_modular_std_dev: StandardDev(0.00000000000000029403601535432533),
|
||||
cbs_level: DecompositionLevelCount(0),
|
||||
cbs_base_log: DecompositionBaseLog(0),
|
||||
message_modulus_log: CiphertextModulusLog(4),
|
||||
message_modulus_log: MessageModulusLog(4),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
|
||||
@@ -133,7 +138,7 @@ pub const DUMMY_31_U32: TestParams<u32> = TestParams {
|
||||
pfks_modular_std_dev: StandardDev(0.00000000000000029403601535432533),
|
||||
cbs_level: DecompositionLevelCount(0),
|
||||
cbs_base_log: DecompositionBaseLog(0),
|
||||
message_modulus_log: CiphertextModulusLog(3),
|
||||
message_modulus_log: MessageModulusLog(3),
|
||||
ciphertext_modulus: CiphertextModulus::new(1 << 31),
|
||||
};
|
||||
|
||||
|
||||
@@ -125,6 +125,31 @@ impl<G: ByteRandomGenerator> MaskRandomGenerator<G> {
|
||||
self.try_fork(glwe_size.0, mask_bytes)
|
||||
}
|
||||
|
||||
// Forks the generator, when splitting a ggsw into level matrices.
|
||||
pub(crate) fn fork_pseudo_ggsw_to_ggsw_levels<T: UnsignedInteger>(
|
||||
&mut self,
|
||||
level: DecompositionLevelCount,
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> Result<impl Iterator<Item = Self>, ForkError> {
|
||||
let mask_bytes =
|
||||
mask_bytes_per_pseudo_ggsw_level::<T>(glwe_size_in, glwe_size_out, polynomial_size);
|
||||
self.try_fork(level.0, mask_bytes)
|
||||
}
|
||||
|
||||
// Forks the generator, when splitting a pseudo ggsw level matrix to glwe.
|
||||
pub(crate) fn fork_pseudo_ggsw_level_to_glwe<T: UnsignedInteger>(
|
||||
&mut self,
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> Result<impl Iterator<Item = Self>, ForkError> {
|
||||
let mask_bytes =
|
||||
mask_bytes_per_glwe::<T>(glwe_size_out.to_glwe_dimension(), polynomial_size);
|
||||
self.try_fork(glwe_size_in.to_glwe_dimension().0, mask_bytes)
|
||||
}
|
||||
|
||||
// Forks the generator, when splitting a ggsw into level matrices.
|
||||
pub(crate) fn fork_gsw_to_gsw_levels<T: UnsignedInteger>(
|
||||
&mut self,
|
||||
@@ -392,6 +417,15 @@ fn mask_bytes_per_ggsw<T: UnsignedInteger>(
|
||||
level.0 * mask_bytes_per_ggsw_level::<T>(glwe_size, poly_size)
|
||||
}
|
||||
|
||||
fn mask_bytes_per_pseudo_ggsw_level<T: UnsignedInteger>(
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
poly_size: PolynomialSize,
|
||||
) -> usize {
|
||||
glwe_size_in.to_glwe_dimension().0
|
||||
* mask_bytes_per_glwe::<T>(glwe_size_out.to_glwe_dimension(), poly_size)
|
||||
}
|
||||
|
||||
fn mask_bytes_per_pfpksk_chunk<T: UnsignedInteger>(
|
||||
level: DecompositionLevelCount,
|
||||
glwe_size: GlweSize,
|
||||
|
||||
@@ -149,6 +149,44 @@ impl<G: ByteRandomGenerator> EncryptionRandomGenerator<G> {
|
||||
self.map_to_encryption_generator(mask_iter, noise_iter)
|
||||
}
|
||||
|
||||
// Forks the generator, when splitting a ggsw into level matrices.
|
||||
pub(crate) fn fork_pseudo_ggsw_to_ggsw_levels<T: UnsignedInteger>(
|
||||
&mut self,
|
||||
level: DecompositionLevelCount,
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> Result<impl Iterator<Item = Self>, ForkError> {
|
||||
let mask_iter = self.mask.fork_pseudo_ggsw_to_ggsw_levels::<T>(
|
||||
level,
|
||||
glwe_size_in,
|
||||
glwe_size_out,
|
||||
polynomial_size,
|
||||
)?;
|
||||
let noise_iter =
|
||||
self.noise
|
||||
.fork_pseudo_ggsw_to_ggsw_levels(level, glwe_size_in, polynomial_size)?;
|
||||
self.map_to_encryption_generator(mask_iter, noise_iter)
|
||||
}
|
||||
|
||||
// Forks the generator, when splitting a pseudo ggsw level matrix to glwe.
|
||||
pub(crate) fn fork_pseudo_ggsw_level_to_glwe<T: UnsignedInteger>(
|
||||
&mut self,
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> Result<impl Iterator<Item = Self>, ForkError> {
|
||||
let mask_iter = self.mask.fork_pseudo_ggsw_level_to_glwe::<T>(
|
||||
glwe_size_in,
|
||||
glwe_size_out,
|
||||
polynomial_size,
|
||||
)?;
|
||||
let noise_iter = self
|
||||
.noise
|
||||
.fork_pseudo_ggsw_level_to_glwe(glwe_size_in, polynomial_size)?;
|
||||
self.map_to_encryption_generator(mask_iter, noise_iter)
|
||||
}
|
||||
|
||||
// Forks the generator, when splitting a ggsw into level matrices.
|
||||
pub(crate) fn fork_gsw_to_gsw_levels<T: UnsignedInteger>(
|
||||
&mut self,
|
||||
|
||||
@@ -211,6 +211,27 @@ impl<G: ByteRandomGenerator> NoiseRandomGenerator<G> {
|
||||
self.try_fork(glwe_size.0, noise_bytes)
|
||||
}
|
||||
|
||||
// Forks the generator, when splitting a ggsw into level matrices.
|
||||
pub(crate) fn fork_pseudo_ggsw_to_ggsw_levels(
|
||||
&mut self,
|
||||
level: DecompositionLevelCount,
|
||||
glwe_size_in: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> Result<impl Iterator<Item = Self>, ForkError> {
|
||||
let noise_bytes = noise_bytes_per_ggsw_level(GlweSize(glwe_size_in.0 - 1), polynomial_size);
|
||||
self.try_fork(level.0, noise_bytes)
|
||||
}
|
||||
|
||||
// Forks the generator, when splitting a pseudo ggsw level matrix to glwe.
|
||||
pub(crate) fn fork_pseudo_ggsw_level_to_glwe(
|
||||
&mut self,
|
||||
glwe_size_in: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> Result<impl Iterator<Item = Self>, ForkError> {
|
||||
let noise_bytes = noise_bytes_per_glwe(polynomial_size);
|
||||
self.try_fork(glwe_size_in.to_glwe_dimension().0, noise_bytes)
|
||||
}
|
||||
|
||||
// Forks the generator, when splitting a ggsw into level matrices.
|
||||
pub(crate) fn fork_gsw_to_gsw_levels(
|
||||
&mut self,
|
||||
|
||||
@@ -73,6 +73,32 @@ impl LweDimension {
|
||||
pub fn to_lwe_size(&self) -> LweSize {
|
||||
LweSize(self.0 + 1)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn shared_coef_count_from(
|
||||
&self,
|
||||
unshared_coef_count: SharedLweSecretKeyDifferingCoefCount,
|
||||
) -> SharedLweSecretKeyCommonCoefCount {
|
||||
assert!(
|
||||
unshared_coef_count.0 <= self.0,
|
||||
"unshared_coef_count {unshared_coef_count:?} must be smaller than self {:?}",
|
||||
*self
|
||||
);
|
||||
SharedLweSecretKeyCommonCoefCount(self.0 - unshared_coef_count.0)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn unshared_coef_count_from(
|
||||
&self,
|
||||
shared_coef_count: SharedLweSecretKeyCommonCoefCount,
|
||||
) -> SharedLweSecretKeyDifferingCoefCount {
|
||||
assert!(
|
||||
shared_coef_count.0 <= self.0,
|
||||
"shared_coef_count {shared_coef_count:?} must be smaller than self {:?}",
|
||||
*self
|
||||
);
|
||||
SharedLweSecretKeyDifferingCoefCount(self.0 - shared_coef_count.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// The number of LWE encryptions of 0 in an LWE public key.
|
||||
@@ -265,3 +291,34 @@ pub enum PBSOrder {
|
||||
/// key realm.
|
||||
BootstrapKeyswitch = 1,
|
||||
}
|
||||
|
||||
/// The number of non zero elements in a partial GLWE secret key
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
|
||||
pub struct PartialGlweSecretKeyRandomCoefCount(pub usize);
|
||||
|
||||
/// The number of zero elements completing a partial GLWE secret key
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
|
||||
pub struct PartialGlweSecretKeyZeroCoefCount(pub usize);
|
||||
|
||||
/// The number of shared elements between two LWE secret keys
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
|
||||
pub struct SharedLweSecretKeyCommonCoefCount(pub usize);
|
||||
|
||||
impl SharedLweSecretKeyCommonCoefCount {
|
||||
#[track_caller]
|
||||
pub fn shared_coef_count_from(
|
||||
&self,
|
||||
unshared_coef_count: SharedLweSecretKeyDifferingCoefCount,
|
||||
) -> Self {
|
||||
assert!(
|
||||
unshared_coef_count.0 <= self.0,
|
||||
"unshared_lwe_dimension {unshared_coef_count:?} must be smaller than self {:?}",
|
||||
*self
|
||||
);
|
||||
Self(self.0 - unshared_coef_count.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// The number of non shared elements between two LWE secret keys
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
|
||||
pub struct SharedLweSecretKeyDifferingCoefCount(pub usize);
|
||||
|
||||
266
tfhe/src/core_crypto/entities/lwe_shrinking_keyswitch_key.rs
Normal file
266
tfhe/src/core_crypto/entities/lwe_shrinking_keyswitch_key.rs
Normal file
@@ -0,0 +1,266 @@
|
||||
//! Module containing the definition of the [`LweShrinkingKeyswitchKey`].
|
||||
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
/// An [`LWE shrinking keyswitch key`](`LweShrinkingKeyswitchKey`) is an [`LWE keyswitch
|
||||
/// key`](`LweKeyswitchKey`) where the input and output key share a part of their randomness.
|
||||
///
|
||||
/// See [`the formal definition of an LWE keyswitch key`](`LweKeyswitchKey#formal-definition`).
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LweShrinkingKeyswitchKey<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
lwe_ksk: LweKeyswitchKey<C>,
|
||||
input_key_lwe_dimension: LweDimension,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for LweShrinkingKeyswitchKey<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.lwe_ksk.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for LweShrinkingKeyswitchKey<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.lwe_ksk.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> LweShrinkingKeyswitchKey<C> {
|
||||
/// Create an [`LweShrinkingKeyswitchKey`] from an existing container.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function only wraps a container in the appropriate type. If you want to generate an LWE
|
||||
/// shrinking keyswitch key you need to use
|
||||
/// [`crate::core_crypto::algorithms::generate_lwe_shrinking_keyswitch_key`] using this key as
|
||||
/// output.
|
||||
///
|
||||
/// This docstring exhibits [`LweShrinkingKeyswitchKey`] primitives usage.
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// todo!();
|
||||
/// ```
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_key_lwe_dimension: LweDimension,
|
||||
output_lwe_size: LweSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
let lwe_ksk = LweKeyswitchKey::from_container(
|
||||
container,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
output_lwe_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let output_key_lwe_dimension = lwe_ksk.output_key_lwe_dimension();
|
||||
let unshared_randomness_coef_count =
|
||||
SharedLweSecretKeyDifferingCoefCount(lwe_ksk.input_key_lwe_dimension().0);
|
||||
let shared_randomness_coef_count =
|
||||
input_key_lwe_dimension.shared_coef_count_from(unshared_randomness_coef_count);
|
||||
|
||||
assert_eq!(
|
||||
input_key_lwe_dimension.0,
|
||||
output_key_lwe_dimension.0 + unshared_randomness_coef_count.0
|
||||
);
|
||||
|
||||
assert!(
|
||||
output_key_lwe_dimension.0 <= input_key_lwe_dimension.0,
|
||||
"The output LweDimension ({output_key_lwe_dimension:?}) \
|
||||
must be smaller than the input LweDimension ({input_key_lwe_dimension:?}) \
|
||||
for an LweShrinkingKeyswitchKey."
|
||||
);
|
||||
assert!(
|
||||
shared_randomness_coef_count.0 <= output_key_lwe_dimension.0,
|
||||
"The shared randomness coefficient count ({shared_randomness_coef_count:?}) \
|
||||
must be smaller than the output LweDimension ({output_key_lwe_dimension:?}) \
|
||||
for an LweShrinkingKeyswitchKey."
|
||||
);
|
||||
|
||||
Self {
|
||||
lwe_ksk,
|
||||
input_key_lwe_dimension,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_lwe_keyswitch_key(&self) -> LweKeyswitchKey<&'_ [Scalar]> {
|
||||
self.lwe_ksk.as_view()
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionBaseLog`] of the [`LweShrinkingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LweShrinkingKeyswitchKey::from_container`] for usage.
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.lwe_ksk.decomposition_base_log()
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionLevelCount`] of the [`LweShrinkingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LweShrinkingKeyswitchKey::from_container`] for usage.
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.lwe_ksk.decomposition_level_count()
|
||||
}
|
||||
|
||||
/// Return the input [`LweDimension`] of the [`LweShrinkingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LweShrinkingKeyswitchKey::from_container`] for usage.
|
||||
pub fn input_key_lwe_dimension(&self) -> LweDimension {
|
||||
self.input_key_lwe_dimension
|
||||
}
|
||||
|
||||
/// Return the output [`LweDimension`] of the [`LweShrinkingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LweShrinkingKeyswitchKey::from_container`] for usage.
|
||||
pub fn output_key_lwe_dimension(&self) -> LweDimension {
|
||||
self.output_lwe_size().to_lwe_dimension()
|
||||
}
|
||||
|
||||
/// Return the output [`LweSize`] of the [`LweShrinkingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LweShrinkingKeyswitchKey::from_container`] for usage.
|
||||
pub fn output_lwe_size(&self) -> LweSize {
|
||||
self.lwe_ksk.output_lwe_size()
|
||||
}
|
||||
|
||||
/// Return the unshared [`SharedLweSecretKeyDifferingCoefCount`] of randomness of the input and
|
||||
/// output [`LweSecretKey`] used to build this [`LweShrinkingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LweShrinkingKeyswitchKey::from_container`] for usage.
|
||||
pub fn unshared_randomness_lwe_dimension(&self) -> SharedLweSecretKeyDifferingCoefCount {
|
||||
SharedLweSecretKeyDifferingCoefCount(self.lwe_ksk.input_key_lwe_dimension().0)
|
||||
}
|
||||
|
||||
/// Return the shared [`SharedLweSecretKeyCommonCoefCount`] of randomness of the input and
|
||||
/// output [`LweSecretKey`] used to build this [`LweShrinkingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LweShrinkingKeyswitchKey::from_container`] for usage.
|
||||
pub fn shared_randomness_lwe_dimension(&self) -> SharedLweSecretKeyCommonCoefCount {
|
||||
self.input_key_lwe_dimension()
|
||||
.shared_coef_count_from(self.unshared_randomness_lwe_dimension())
|
||||
}
|
||||
|
||||
/// Return the number of elements in an encryption of an input [`LweSecretKey`] element of the
|
||||
/// current [`LweShrinkingKeyswitchKey`].
|
||||
pub fn input_key_element_encrypted_size(&self) -> usize {
|
||||
self.lwe_ksk.input_key_element_encrypted_size()
|
||||
}
|
||||
|
||||
/// Consume the entity and return its underlying container.
|
||||
///
|
||||
/// See [`LweShrinkingKeyswitchKey::from_container`] for usage.
|
||||
pub fn into_container(self) -> C {
|
||||
self.lwe_ksk.into_container()
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<Scalar> {
|
||||
self.lwe_ksk.ciphertext_modulus()
|
||||
}
|
||||
|
||||
/// Return a view of the [`LweShrinkingKeyswitchKey`]. This is useful if an algorithm takes a
|
||||
/// view by value.
|
||||
pub fn as_view(&self) -> LweShrinkingKeyswitchKey<&'_ [Scalar]> {
|
||||
LweShrinkingKeyswitchKey::from_container(
|
||||
self.as_ref(),
|
||||
self.decomposition_base_log(),
|
||||
self.decomposition_level_count(),
|
||||
self.input_key_lwe_dimension(),
|
||||
self.output_lwe_size(),
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> LweShrinkingKeyswitchKey<C> {
|
||||
pub fn as_mut_lwe_keyswitch_key(&mut self) -> LweKeyswitchKey<&'_ mut [Scalar]> {
|
||||
self.lwe_ksk.as_mut_view()
|
||||
}
|
||||
|
||||
/// Mutable variant of [`LweShrinkingKeyswitchKey::as_view`].
|
||||
pub fn as_mut_view(&mut self) -> LweShrinkingKeyswitchKey<&'_ mut [Scalar]> {
|
||||
let decomposition_base_log = self.decomposition_base_log();
|
||||
let decomposition_level_count = self.decomposition_level_count();
|
||||
let input_key_lwe_dimension = self.input_key_lwe_dimension();
|
||||
let output_lwe_size = self.output_lwe_size();
|
||||
let ciphertext_modulus = self.ciphertext_modulus();
|
||||
LweShrinkingKeyswitchKey::from_container(
|
||||
self.as_mut(),
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
input_key_lwe_dimension,
|
||||
output_lwe_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`LweShrinkingKeyswitchKey`] owning the memory for its own storage.
|
||||
pub type LweShrinkingKeyswitchKeyOwned<Scalar> = LweShrinkingKeyswitchKey<Vec<Scalar>>;
|
||||
/// A [`LweShrinkingKeyswitchKey`] immutably borrowing memory for its own storage.
|
||||
pub type LweShrinkingKeyswitchKeyView<'data, Scalar> = LweShrinkingKeyswitchKey<&'data [Scalar]>;
|
||||
/// A [`LweShrinkingKeyswitchKey`] mutably borrowing memory for its own storage.
|
||||
pub type LweShrinkingKeyswitchKeyMutView<'data, Scalar> =
|
||||
LweShrinkingKeyswitchKey<&'data mut [Scalar]>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> LweShrinkingKeyswitchKeyOwned<Scalar> {
|
||||
/// Allocate memory and create a new owned [`LweShrinkingKeyswitchKey`].
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
|
||||
/// type. If you want to generate an LWE shrinking keysiwtch key you need to use
|
||||
/// [`crate::core_crypto::algorithms::generate_lwe_shrinking_keyswitch_key`] using this key as
|
||||
/// output.
|
||||
///
|
||||
/// See [`LweShrinkingKeyswitchKey::from_container`] for usage.
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_key_lwe_dimension: LweDimension,
|
||||
output_key_lwe_dimension: LweDimension,
|
||||
shared_randomness_coef_count: SharedLweSecretKeyCommonCoefCount,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
output_key_lwe_dimension.0 <= input_key_lwe_dimension.0,
|
||||
"The output LweDimension ({output_key_lwe_dimension:?}) \
|
||||
must be smaller than the input LweDimension ({input_key_lwe_dimension:?}) \
|
||||
for an LweShrinkingKeyswitchKey."
|
||||
);
|
||||
assert!(
|
||||
shared_randomness_coef_count.0 <= output_key_lwe_dimension.0,
|
||||
"The shared randomness coefficient count ({shared_randomness_coef_count:?}) \
|
||||
must be smaller than the output LweDimension ({output_key_lwe_dimension:?}) \
|
||||
for an LweShrinkingKeyswitchKey."
|
||||
);
|
||||
|
||||
let unshared_randomness_coef_count =
|
||||
input_key_lwe_dimension.unshared_coef_count_from(shared_randomness_coef_count);
|
||||
|
||||
assert_eq!(
|
||||
input_key_lwe_dimension.0,
|
||||
output_key_lwe_dimension.0 + unshared_randomness_coef_count.0
|
||||
);
|
||||
|
||||
Self {
|
||||
lwe_ksk: LweKeyswitchKey::new(
|
||||
fill_with,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
LweDimension(unshared_randomness_coef_count.0),
|
||||
output_key_lwe_dimension,
|
||||
ciphertext_modulus,
|
||||
),
|
||||
input_key_lwe_dimension,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,12 @@ pub mod lwe_private_functional_packing_keyswitch_key;
|
||||
pub mod lwe_private_functional_packing_keyswitch_key_list;
|
||||
pub mod lwe_public_key;
|
||||
pub mod lwe_secret_key;
|
||||
pub mod lwe_shrinking_keyswitch_key;
|
||||
pub mod plaintext;
|
||||
pub mod plaintext_list;
|
||||
pub mod polynomial;
|
||||
pub mod polynomial_list;
|
||||
pub mod pseudo_ggsw_ciphertext;
|
||||
pub mod seeded_ggsw_ciphertext;
|
||||
pub mod seeded_ggsw_ciphertext_list;
|
||||
pub mod seeded_glwe_ciphertext;
|
||||
@@ -51,6 +53,10 @@ pub use crate::core_crypto::fft_impl::fft64::crypto::bootstrap::{
|
||||
pub use crate::core_crypto::fft_impl::fft64::crypto::ggsw::{
|
||||
FourierGgswCiphertext, FourierGgswCiphertextList, FourierGgswLevelMatrix, FourierGgswLevelRow,
|
||||
};
|
||||
pub use crate::core_crypto::fft_impl::fft64::crypto::pseudo_ggsw::{
|
||||
PseudoFourierGgswCiphertext, PseudoFourierGgswLevelMatrix, PseudoFourierGgswLevelRow,
|
||||
};
|
||||
|
||||
pub use crate::core_crypto::fft_impl::fft64::math::polynomial::FourierPolynomial;
|
||||
pub use cleartext::*;
|
||||
pub use ggsw_ciphertext::*;
|
||||
@@ -71,10 +77,12 @@ pub use lwe_private_functional_packing_keyswitch_key::*;
|
||||
pub use lwe_private_functional_packing_keyswitch_key_list::*;
|
||||
pub use lwe_public_key::*;
|
||||
pub use lwe_secret_key::*;
|
||||
pub use lwe_shrinking_keyswitch_key::*;
|
||||
pub use plaintext::*;
|
||||
pub use plaintext_list::*;
|
||||
pub use polynomial::*;
|
||||
pub use polynomial_list::*;
|
||||
pub use pseudo_ggsw_ciphertext::*;
|
||||
pub use seeded_ggsw_ciphertext::*;
|
||||
pub use seeded_ggsw_ciphertext_list::*;
|
||||
pub use seeded_glwe_ciphertext::*;
|
||||
|
||||
571
tfhe/src/core_crypto/entities/pseudo_ggsw_ciphertext.rs
Normal file
571
tfhe/src/core_crypto/entities/pseudo_ggsw_ciphertext.rs
Normal file
@@ -0,0 +1,571 @@
|
||||
//! Module containing the definition of the PseudoGgswCiphertext.
|
||||
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct PseudoGgswCiphertext<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for PseudoGgswCiphertext<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for PseudoGgswCiphertext<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of elements in a [`PseudoGgswCiphertext`] given a [`GlweSize`],
|
||||
/// [`PolynomialSize`] and [`DecompositionLevelCount`].
|
||||
pub fn pseudo_ggsw_ciphertext_size(
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
) -> usize {
|
||||
decomp_level_count.0
|
||||
* pseudo_ggsw_level_matrix_size(glwe_size_in, glwe_size_out, polynomial_size)
|
||||
}
|
||||
|
||||
/// Return the number of elements in a [`GgswLevelMatrix`] given a [`GlweSize`] and
|
||||
/// [`PolynomialSize`].
|
||||
pub fn pseudo_ggsw_level_matrix_size(
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> usize {
|
||||
glwe_size_in.to_glwe_dimension().0 * glwe_size_out.0 * polynomial_size.0
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> PseudoGgswCiphertext<C> {
|
||||
/// Create a [`PseudoGgswCiphertext`] from an existing container.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function only wraps a container in the appropriate type. If you want to encrypt data
|
||||
/// you need to use [`crate::core_crypto::algorithms::encrypt_constant_ggsw_ciphertext`] or its
|
||||
/// parallel counterpart
|
||||
/// [`crate::core_crypto::algorithms::par_encrypt_constant_ggsw_ciphertext`] using
|
||||
/// this ciphertext as output.
|
||||
///
|
||||
/// This docstring exhibits [`PseudoGgswCiphertext`] primitives usage.
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for PseudoGgswCiphertext creation
|
||||
/// let glwe_size_in = GlweSize(2);
|
||||
/// let glwe_size_out = GlweSize(3);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let decomp_base_log = DecompositionBaseLog(8);
|
||||
/// let decomp_level_count = DecompositionLevelCount(3);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // Create a new PseudoGgswCiphertext
|
||||
/// let ggsw = PseudoGgswCiphertext::new(
|
||||
/// 0u64,
|
||||
/// glwe_size_in,
|
||||
/// glwe_size_out,
|
||||
/// polynomial_size,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(ggsw.glwe_size_in(), glwe_size_in);
|
||||
/// assert_eq!(ggsw.glwe_size_out(), glwe_size_out);
|
||||
/// assert_eq!(ggsw.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(ggsw.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(ggsw.decomposition_level_count(), decomp_level_count);
|
||||
/// assert_eq!(ggsw.ciphertext_modulus(), ciphertext_modulus);
|
||||
/// assert_eq!(
|
||||
/// ggsw.pseudo_ggsw_level_matrix_size(),
|
||||
/// pseudo_ggsw_level_matrix_size(glwe_size_in, glwe_size_out, polynomial_size)
|
||||
/// );
|
||||
///
|
||||
/// // Demonstrate how to recover the allocated container
|
||||
/// let underlying_container: Vec<u64> = ggsw.into_container();
|
||||
///
|
||||
/// // Recreate a ciphertext using from_container
|
||||
/// let ggsw = PseudoGgswCiphertext::from_container(
|
||||
/// underlying_container,
|
||||
/// glwe_size_in,
|
||||
/// glwe_size_out,
|
||||
/// polynomial_size,
|
||||
/// decomp_base_log,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(ggsw.glwe_size_in(), glwe_size_in);
|
||||
/// assert_eq!(ggsw.glwe_size_out(), glwe_size_out);
|
||||
/// assert_eq!(ggsw.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(ggsw.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(ggsw.decomposition_level_count(), decomp_level_count);
|
||||
/// assert_eq!(ggsw.ciphertext_modulus(), ciphertext_modulus);
|
||||
/// assert_eq!(
|
||||
/// ggsw.pseudo_ggsw_level_matrix_size(),
|
||||
/// pseudo_ggsw_level_matrix_size(glwe_size_in, glwe_size_out, polynomial_size)
|
||||
/// );
|
||||
/// ```
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
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 PseudoGgswCiphertext"
|
||||
);
|
||||
assert!(
|
||||
container.container_len()
|
||||
% (glwe_size_in.to_glwe_dimension().0 * glwe_size_out.0 * polynomial_size.0)
|
||||
== 0,
|
||||
"The provided container length is not valid. \
|
||||
It needs to be dividable by glwe_dimension_in * glwe_size_out * polynomial_size: {}. \
|
||||
Got container length: {} and glwe_dimension_in: {:?}, glwe_size_out: \
|
||||
{glwe_size_out:?}\
|
||||
polynomial_size: {polynomial_size:?}.",
|
||||
glwe_size_in.0 * glwe_size_out.0 * polynomial_size.0,
|
||||
container.container_len(),
|
||||
glwe_size_in.to_glwe_dimension()
|
||||
);
|
||||
|
||||
Self {
|
||||
data: container,
|
||||
glwe_size_in,
|
||||
glwe_size_out,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the [`PolynomialSize`] of the [`PseudoGgswCiphertext`].
|
||||
///
|
||||
/// See [`PseudoGgswCiphertext::from_container`] for usage.
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
/// Return the [`GlweSize`] of the [`PseudoGgswCiphertext`].
|
||||
///
|
||||
/// See [`PseudoGgswCiphertext::from_container`] for usage.
|
||||
pub fn glwe_size_in(&self) -> GlweSize {
|
||||
self.glwe_size_in
|
||||
}
|
||||
|
||||
/// Return the [`GlweSize`] of the [`PseudoGgswCiphertext`].
|
||||
///
|
||||
/// See [`PseudoGgswCiphertext::from_container`] for usage.
|
||||
pub fn glwe_size_out(&self) -> GlweSize {
|
||||
self.glwe_size_out
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionBaseLog`] of the [`PseudoGgswCiphertext`].
|
||||
///
|
||||
/// See [`PseudoGgswCiphertext::from_container`] for usage.
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionLevelCount`] of the [`PseudoGgswCiphertext`].
|
||||
///
|
||||
/// See [`PseudoGgswCiphertext::from_container`] for usage.
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
DecompositionLevelCount(self.data.container_len() / self.pseudo_ggsw_level_matrix_size())
|
||||
}
|
||||
|
||||
/// Return the [`CiphertextModulus`] of the [`PseudoGgswCiphertext`].
|
||||
///
|
||||
/// See [`PseudoGgswCiphertext::from_container`] for usage.
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
|
||||
/// Return the size in number of elements of a single [`GgswLevelMatrix`] of the current
|
||||
/// [`PseudoGgswCiphertext`].
|
||||
///
|
||||
/// See [`PseudoGgswCiphertext::from_container`] for usage.
|
||||
pub fn pseudo_ggsw_level_matrix_size(&self) -> usize {
|
||||
// GlweSize GlweCiphertext(glwe_size, polynomial_size) per level
|
||||
pseudo_ggsw_level_matrix_size(self.glwe_size_in, self.glwe_size_out, self.polynomial_size)
|
||||
}
|
||||
|
||||
/// Interpret the [`PseudoGgswCiphertext`] as a [`PolynomialList`].
|
||||
pub fn as_polynomial_list(&self) -> PolynomialListView<'_, Scalar> {
|
||||
PolynomialListView::from_container(self.as_ref(), self.polynomial_size)
|
||||
}
|
||||
|
||||
/// Interpret the [`PseudoGgswCiphertext`] as a [`GlweCiphertextList`].
|
||||
pub fn as_glwe_list(&self) -> GlweCiphertextListView<'_, Scalar> {
|
||||
GlweCiphertextListView::from_container(
|
||||
self.as_ref(),
|
||||
self.glwe_size_out,
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
/// Return a view of the [`PseudoGgswCiphertext`]. This is useful if an algorithm takes a view
|
||||
/// by value.
|
||||
pub fn as_view(&self) -> PseudoGgswCiphertextView<'_, Scalar> {
|
||||
PseudoGgswCiphertextView::from_container(
|
||||
self.as_ref(),
|
||||
self.glwe_size_in(),
|
||||
self.glwe_size_out(),
|
||||
self.polynomial_size(),
|
||||
self.decomposition_base_log(),
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Consume the entity and return its underlying container.
|
||||
///
|
||||
/// See [`PseudoGgswCiphertext::from_container`] for usage.
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> PseudoGgswCiphertext<C> {
|
||||
/// Mutable variant of [`PseudoGgswCiphertext::as_polynomial_list`].
|
||||
pub fn as_mut_polynomial_list(&mut self) -> PolynomialListMutView<'_, Scalar> {
|
||||
let polynomial_size = self.polynomial_size;
|
||||
PolynomialListMutView::from_container(self.as_mut(), polynomial_size)
|
||||
}
|
||||
|
||||
/// Mutable variant of [`PseudoGgswCiphertext::as_glwe_list`].
|
||||
pub fn as_mut_glwe_list(&mut self) -> GlweCiphertextListMutView<'_, Scalar> {
|
||||
let polynomial_size = self.polynomial_size;
|
||||
let glwe_size_out = self.glwe_size_out;
|
||||
let ciphertext_modulus = self.ciphertext_modulus;
|
||||
GlweCiphertextListMutView::from_container(
|
||||
self.as_mut(),
|
||||
glwe_size_out,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
/// Mutable variant of [`PseudoGgswCiphertext::as_view`].
|
||||
pub fn as_mut_view(&mut self) -> PseudoGgswCiphertextMutView<'_, Scalar> {
|
||||
let glwe_size_in = self.glwe_size_in();
|
||||
let glwe_size_out = self.glwe_size_out();
|
||||
let polynomial_size = self.polynomial_size();
|
||||
let decomp_base_log = self.decomposition_base_log();
|
||||
let ciphertext_modulus = self.ciphertext_modulus;
|
||||
PseudoGgswCiphertextMutView::from_container(
|
||||
self.as_mut(),
|
||||
glwe_size_in,
|
||||
glwe_size_out,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`PseudoGgswCiphertext`] owning the memory for its own storage.
|
||||
pub type PseudoGgswCiphertextOwned<Scalar> = PseudoGgswCiphertext<Vec<Scalar>>;
|
||||
/// A [`PseudoGgswCiphertext`] immutably borrowing memory for its own storage.
|
||||
pub type PseudoGgswCiphertextView<'data, Scalar> = PseudoGgswCiphertext<&'data [Scalar]>;
|
||||
/// A [`PseudoGgswCiphertext`] immutably borrowing memory for its own storage.
|
||||
pub type PseudoGgswCiphertextMutView<'data, Scalar> = PseudoGgswCiphertext<&'data mut [Scalar]>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> PseudoGgswCiphertextOwned<Scalar> {
|
||||
/// Allocate memory and create a new owned [`PseudoGgswCiphertext`].
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
|
||||
/// type. If you want to encrypt data you need to use
|
||||
/// [`crate::core_crypto::algorithms::encrypt_constant_ggsw_ciphertext`] or its parallel
|
||||
/// counterpart [`crate::core_crypto::algorithms::par_encrypt_constant_ggsw_ciphertext`]
|
||||
/// using this ciphertext as output.
|
||||
///
|
||||
/// See [`PseudoGgswCiphertext::from_container`] for usage.
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
Self::from_container(
|
||||
vec![
|
||||
fill_with;
|
||||
pseudo_ggsw_ciphertext_size(
|
||||
glwe_size_in,
|
||||
glwe_size_out,
|
||||
polynomial_size,
|
||||
decomp_level_count
|
||||
)
|
||||
],
|
||||
glwe_size_in,
|
||||
glwe_size_out,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata used in the [`CreateFrom`] implementation to create [`PseudoGgswCiphertext`] entities.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PseudoGgswCiphertextCreationMetadata<Scalar: UnsignedInteger>(
|
||||
pub GlweSize,
|
||||
pub GlweSize,
|
||||
pub PolynomialSize,
|
||||
pub DecompositionBaseLog,
|
||||
pub CiphertextModulus<Scalar>,
|
||||
);
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for PseudoGgswCiphertext<C>
|
||||
{
|
||||
type Metadata = PseudoGgswCiphertextCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let PseudoGgswCiphertextCreationMetadata(
|
||||
glwe_size_in,
|
||||
glwe_size_out,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
ciphertext_modulus,
|
||||
) = meta;
|
||||
Self::from_container(
|
||||
from,
|
||||
glwe_size_in,
|
||||
glwe_size_out,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience structure to more easily write iterators on a [`PseudoGgswCiphertext`] levels.
|
||||
pub struct PseudoGgswLevelMatrix<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> PseudoGgswLevelMatrix<C> {
|
||||
/// Create a [`GgswLevelMatrix`] from an existing container.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This docstring exhibits [`GgswLevelMatrix`] primitives usage.
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for GgswLevelMatrix creation
|
||||
/// let glwe_size_in = GlweSize(3);
|
||||
/// let glwe_size_out = GlweSize(2);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// let container =
|
||||
/// vec![0u64; pseudo_ggsw_level_matrix_size(glwe_size_in, glwe_size_out, polynomial_size)];
|
||||
///
|
||||
/// // Create a new GgswLevelMatrix
|
||||
/// let ggsw_level_matrix = PseudoGgswLevelMatrix::from_container(
|
||||
/// container,
|
||||
/// glwe_size_in,
|
||||
/// glwe_size_out,
|
||||
/// polynomial_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(ggsw_level_matrix.glwe_size_in(), glwe_size_in);
|
||||
/// assert_eq!(ggsw_level_matrix.glwe_size_out(), glwe_size_out);
|
||||
/// assert_eq!(ggsw_level_matrix.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(ggsw_level_matrix.ciphertext_modulus(), ciphertext_modulus);
|
||||
/// ```
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
container.container_len()
|
||||
== pseudo_ggsw_level_matrix_size(glwe_size_in, glwe_size_out, polynomial_size),
|
||||
"The provided container length is not valid. \
|
||||
Expected length of {} (glwe_size * glwe_size * polynomial_size), got {}",
|
||||
pseudo_ggsw_level_matrix_size(glwe_size_in, glwe_size_out, polynomial_size),
|
||||
container.container_len(),
|
||||
);
|
||||
|
||||
Self {
|
||||
data: container,
|
||||
glwe_size_in,
|
||||
glwe_size_out,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the [`GlweSize`] of the [`GgswLevelMatrix`].
|
||||
///
|
||||
/// See [`GgswLevelMatrix::from_container`] for usage.
|
||||
pub fn glwe_size_in(&self) -> GlweSize {
|
||||
self.glwe_size_in
|
||||
}
|
||||
|
||||
pub fn glwe_size_out(&self) -> GlweSize {
|
||||
self.glwe_size_out
|
||||
}
|
||||
|
||||
/// Return the [`PolynomialSize`] of the [`GgswLevelMatrix`].
|
||||
///
|
||||
/// See [`GgswLevelMatrix::from_container`] for usage.
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
/// Return the [`CiphertextModulus`] of the [`GgswLevelMatrix`].
|
||||
///
|
||||
/// See [`GgswLevelMatrix::from_container`] for usage.
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
|
||||
/// Interpret the [`GgswLevelMatrix`] as a [`GlweCiphertextList`].
|
||||
pub fn as_glwe_list(&self) -> GlweCiphertextListView<'_, C::Element> {
|
||||
GlweCiphertextListView::from_container(
|
||||
self.data.as_ref(),
|
||||
self.glwe_size_out,
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> PseudoGgswLevelMatrix<C> {
|
||||
/// Mutable variant of [`GgswLevelMatrix::as_glwe_list`]
|
||||
pub fn as_mut_glwe_list(&mut self) -> GlweCiphertextListMutView<'_, C::Element> {
|
||||
GlweCiphertextListMutView::from_container(
|
||||
self.data.as_mut(),
|
||||
self.glwe_size_out,
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata used in the [`CreateFrom`] implementation to create [`GgswLevelMatrix`] entities.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PseudoGgswLevelMatrixCreationMetadata<Scalar: UnsignedInteger>(
|
||||
pub GlweSize,
|
||||
pub GlweSize,
|
||||
pub PolynomialSize,
|
||||
pub CiphertextModulus<Scalar>,
|
||||
);
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for PseudoGgswLevelMatrix<C>
|
||||
{
|
||||
type Metadata = PseudoGgswLevelMatrixCreationMetadata<C::Element>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let PseudoGgswLevelMatrixCreationMetadata(
|
||||
glwe_size_in,
|
||||
glwe_size_out,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
) = meta;
|
||||
Self::from_container(
|
||||
from,
|
||||
glwe_size_in,
|
||||
glwe_size_out,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for PseudoGgswCiphertext<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = PseudoGgswLevelMatrixCreationMetadata<Self::Element>;
|
||||
|
||||
type EntityView<'this> = PseudoGgswLevelMatrix<&'this [Self::Element]>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = ();
|
||||
|
||||
type SelfView<'this> = DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
PseudoGgswLevelMatrixCreationMetadata(
|
||||
self.glwe_size_in,
|
||||
self.glwe_size_out,
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
self.pseudo_ggsw_level_matrix_size()
|
||||
}
|
||||
|
||||
/// Unimplemented for [`PseudoGgswCiphertext`]. At the moment it does not make sense to
|
||||
/// return "sub" PseudoGgswCiphertext.
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
unimplemented!(
|
||||
"This function is not supported for PseudoGgswCiphertext. \
|
||||
At the moment it does not make sense to return 'sub' PseudoGgswCiphertext."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for PseudoGgswCiphertext<C>
|
||||
{
|
||||
type EntityMutView<'this> = PseudoGgswLevelMatrix<&'this mut [Self::Element]>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfMutView<'this> = DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
@@ -599,7 +599,7 @@ pub fn add_external_product_assign<Scalar, InputGlweCont>(
|
||||
}
|
||||
|
||||
#[cfg_attr(__profiling, inline(never))]
|
||||
fn collect_next_term<'a, Scalar: UnsignedTorus>(
|
||||
pub(crate) fn collect_next_term<'a, Scalar: UnsignedTorus>(
|
||||
decomposition: &mut TensorSignedDecompositionLendingIter<'_, Scalar>,
|
||||
substack1: &'a mut PodStack,
|
||||
align: usize,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod bootstrap;
|
||||
pub mod ggsw;
|
||||
pub mod pseudo_ggsw;
|
||||
pub mod wop_pbs;
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
501
tfhe/src/core_crypto/fft_impl/fft64/crypto/pseudo_ggsw.rs
Normal file
501
tfhe/src/core_crypto/fft_impl/fft64/crypto/pseudo_ggsw.rs
Normal file
@@ -0,0 +1,501 @@
|
||||
use super::super::math::decomposition::TensorSignedDecompositionLendingIter;
|
||||
use super::super::math::fft::{FftView, FourierPolynomialList};
|
||||
use super::super::math::polynomial::{FourierPolynomialMutView, FourierPolynomialView};
|
||||
use crate::core_crypto::commons::math::decomposition::{DecompositionLevel, SignedDecomposer};
|
||||
use crate::core_crypto::commons::math::torus::UnsignedTorus;
|
||||
use crate::core_crypto::commons::parameters::{
|
||||
DecompositionBaseLog, DecompositionLevelCount, GlweSize, PolynomialSize,
|
||||
};
|
||||
use crate::core_crypto::commons::traits::{
|
||||
Container, ContiguousEntityContainer, ContiguousEntityContainerMut, IntoContainerOwned, Split,
|
||||
};
|
||||
use crate::core_crypto::commons::utils::izip;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::fft_impl::fft64::crypto::ggsw::{collect_next_term, update_with_fmadd};
|
||||
use aligned_vec::{avec, ABox, CACHELINE_ALIGN};
|
||||
use concrete_fft::c64;
|
||||
use dyn_stack::{PodStack, ReborrowMut, SizeOverflow, StackReq};
|
||||
|
||||
/// A GGSW ciphertext in the Fourier domain.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(bound(deserialize = "C: IntoContainerOwned"))]
|
||||
pub struct PseudoFourierGgswCiphertext<C: Container<Element = c64>> {
|
||||
fourier: FourierPolynomialList<C>,
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
}
|
||||
|
||||
/// A matrix containing a single level of gadget decomposition, in the Fourier domain.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct PseudoFourierGgswLevelMatrix<C: Container<Element = c64>> {
|
||||
data: C,
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
row_count: usize,
|
||||
decomposition_level: DecompositionLevel,
|
||||
}
|
||||
|
||||
/// A row of a GGSW level matrix, in the Fourier domain.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct PseudoFourierGgswLevelRow<C: Container<Element = c64>> {
|
||||
data: C,
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_level: DecompositionLevel,
|
||||
}
|
||||
|
||||
pub type PseudoFourierGgswCiphertextView<'a> = PseudoFourierGgswCiphertext<&'a [c64]>;
|
||||
pub type PseudoFourierGgswCiphertextMutView<'a> = PseudoFourierGgswCiphertext<&'a mut [c64]>;
|
||||
pub type PseudoFourierGgswLevelMatrixView<'a> = PseudoFourierGgswLevelMatrix<&'a [c64]>;
|
||||
pub type PseudoFourierGgswLevelMatrixMutView<'a> = PseudoFourierGgswLevelMatrix<&'a mut [c64]>;
|
||||
pub type PseudoFourierGgswLevelRowView<'a> = PseudoFourierGgswLevelRow<&'a [c64]>;
|
||||
pub type PseudoFourierGgswLevelRowMutView<'a> = PseudoFourierGgswLevelRow<&'a mut [c64]>;
|
||||
|
||||
impl<C: Container<Element = c64>> PseudoFourierGgswCiphertext<C> {
|
||||
pub fn from_container(
|
||||
data: C,
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
data.container_len(),
|
||||
polynomial_size.to_fourier_polynomial_size().0
|
||||
* glwe_size_in.to_glwe_dimension().0
|
||||
* glwe_size_out.0
|
||||
* decomposition_level_count.0
|
||||
);
|
||||
|
||||
Self {
|
||||
fourier: FourierPolynomialList {
|
||||
data,
|
||||
polynomial_size,
|
||||
},
|
||||
glwe_size_in,
|
||||
glwe_size_out,
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.fourier.polynomial_size
|
||||
}
|
||||
|
||||
pub fn glwe_size_in(&self) -> GlweSize {
|
||||
self.glwe_size_in
|
||||
}
|
||||
|
||||
pub fn glwe_size_out(&self) -> GlweSize {
|
||||
// Le truc qui foire en k = 1 ? TODO
|
||||
self.glwe_size_out
|
||||
}
|
||||
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) -> PseudoFourierGgswCiphertextView<'_>
|
||||
where
|
||||
C: AsRef<[c64]>,
|
||||
{
|
||||
PseudoFourierGgswCiphertextView {
|
||||
fourier: FourierPolynomialList {
|
||||
data: self.fourier.data.as_ref(),
|
||||
polynomial_size: self.fourier.polynomial_size,
|
||||
},
|
||||
glwe_size_in: self.glwe_size_in,
|
||||
glwe_size_out: self.glwe_size_out,
|
||||
decomposition_base_log: self.decomposition_base_log,
|
||||
decomposition_level_count: self.decomposition_level_count,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_mut_view(&mut self) -> PseudoFourierGgswCiphertextMutView<'_>
|
||||
where
|
||||
C: AsMut<[c64]>,
|
||||
{
|
||||
PseudoFourierGgswCiphertextMutView {
|
||||
fourier: FourierPolynomialList {
|
||||
data: self.fourier.data.as_mut(),
|
||||
polynomial_size: self.fourier.polynomial_size,
|
||||
},
|
||||
glwe_size_in: self.glwe_size_in,
|
||||
glwe_size_out: self.glwe_size_out,
|
||||
decomposition_base_log: self.decomposition_base_log,
|
||||
decomposition_level_count: self.decomposition_level_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Container<Element = c64>> PseudoFourierGgswLevelMatrix<C> {
|
||||
pub fn new(
|
||||
data: C,
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
row_count: usize,
|
||||
decomposition_level: DecompositionLevel,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
data.container_len(),
|
||||
polynomial_size.to_fourier_polynomial_size().0 * glwe_size_out.0 * row_count
|
||||
);
|
||||
Self {
|
||||
data,
|
||||
glwe_size_in,
|
||||
glwe_size_out,
|
||||
polynomial_size,
|
||||
row_count,
|
||||
decomposition_level,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an iterator over the rows of the level matrices.
|
||||
pub fn into_rows(self) -> impl DoubleEndedIterator<Item = PseudoFourierGgswLevelRow<C>>
|
||||
where
|
||||
C: Split,
|
||||
{
|
||||
self.data
|
||||
.split_into(self.row_count)
|
||||
.map(move |slice| PseudoFourierGgswLevelRow {
|
||||
data: slice,
|
||||
polynomial_size: self.polynomial_size,
|
||||
glwe_size_out: self.glwe_size_out,
|
||||
decomposition_level: self.decomposition_level,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn glwe_size_in(&self) -> GlweSize {
|
||||
self.glwe_size_in
|
||||
}
|
||||
|
||||
pub fn glwe_size_out(&self) -> GlweSize {
|
||||
self.glwe_size_out
|
||||
}
|
||||
|
||||
pub fn row_count(&self) -> usize {
|
||||
self.row_count
|
||||
}
|
||||
|
||||
pub fn decomposition_level(&self) -> DecompositionLevel {
|
||||
self.decomposition_level
|
||||
}
|
||||
|
||||
pub fn data(self) -> C {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Container<Element = c64>> PseudoFourierGgswLevelRow<C> {
|
||||
pub fn new(
|
||||
data: C,
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_level: DecompositionLevel,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
data.container_len(),
|
||||
polynomial_size.to_fourier_polynomial_size().0 * glwe_size_out.0
|
||||
);
|
||||
Self {
|
||||
data,
|
||||
glwe_size_out,
|
||||
polynomial_size,
|
||||
decomposition_level,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn glwe_size_out(&self) -> GlweSize {
|
||||
self.glwe_size_out
|
||||
}
|
||||
|
||||
pub fn decomposition_level(&self) -> DecompositionLevel {
|
||||
self.decomposition_level
|
||||
}
|
||||
|
||||
pub fn data(self) -> C {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PseudoFourierGgswCiphertextView<'a> {
|
||||
/// Return an iterator over the level matrices.
|
||||
pub fn into_levels(
|
||||
self,
|
||||
) -> impl DoubleEndedIterator<Item = PseudoFourierGgswLevelMatrixView<'a>> {
|
||||
self.fourier
|
||||
.data
|
||||
.split_into(self.decomposition_level_count.0)
|
||||
.enumerate()
|
||||
.map(move |(i, slice)| {
|
||||
PseudoFourierGgswLevelMatrixView::new(
|
||||
slice,
|
||||
self.glwe_size_in,
|
||||
self.glwe_size_out,
|
||||
self.fourier.polynomial_size,
|
||||
self.glwe_size_in.to_glwe_dimension().0,
|
||||
DecompositionLevel(i + 1),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the required memory for
|
||||
/// [`PseudoFourierGgswCiphertextMutView::fill_with_forward_fourier`].
|
||||
pub fn fill_with_forward_fourier_scratch(fft: FftView<'_>) -> Result<StackReq, SizeOverflow> {
|
||||
fft.forward_scratch()
|
||||
}
|
||||
|
||||
impl<'a> PseudoFourierGgswCiphertextMutView<'a> {
|
||||
/// Fill a GGSW ciphertext with the Fourier transform of a GGSW ciphertext in the standard
|
||||
/// domain.
|
||||
pub fn fill_with_forward_fourier<
|
||||
Scalar: UnsignedTorus,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
>(
|
||||
self,
|
||||
coef_ggsw: &PseudoGgswCiphertext<InputCont>,
|
||||
fft: FftView<'_>,
|
||||
mut stack: 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.rb_mut(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
type PseudoFourierGgswCiphertextOwned = PseudoFourierGgswCiphertext<ABox<[c64]>>;
|
||||
|
||||
impl PseudoFourierGgswCiphertext<ABox<[c64]>> {
|
||||
pub fn new(
|
||||
glwe_size_in: GlweSize,
|
||||
glwe_size_out: GlweSize,
|
||||
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_size_in.to_glwe_dimension().0
|
||||
* glwe_size_out.0
|
||||
* decomposition_level_count.0
|
||||
]
|
||||
.into_boxed_slice();
|
||||
|
||||
Self::from_container(
|
||||
boxed,
|
||||
glwe_size_in,
|
||||
glwe_size_out,
|
||||
polynomial_size,
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the required memory for [`add_external_product_pseudo_ggsw_assign`].
|
||||
pub fn add_external_product_pseudo_ggsw_assign_scratch<Scalar>(
|
||||
glwe_size_out: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
fft: FftView<'_>,
|
||||
) -> Result<StackReq, SizeOverflow> {
|
||||
let align = CACHELINE_ALIGN;
|
||||
let standard_scratch =
|
||||
StackReq::try_new_aligned::<Scalar>(glwe_size_out.0 * polynomial_size.0, align)?;
|
||||
let fourier_polynomial_size = polynomial_size.to_fourier_polynomial_size().0;
|
||||
let fourier_scratch =
|
||||
StackReq::try_new_aligned::<c64>(glwe_size_out.0 * fourier_polynomial_size, align)?;
|
||||
let fourier_scratch_single = StackReq::try_new_aligned::<c64>(fourier_polynomial_size, align)?;
|
||||
|
||||
let substack3 = fft.forward_scratch()?;
|
||||
let substack2 = substack3.try_and(fourier_scratch_single)?;
|
||||
let substack1 = substack2.try_and(standard_scratch)?;
|
||||
let substack0 = StackReq::try_any_of([
|
||||
substack1.try_and(standard_scratch)?,
|
||||
fft.backward_scratch()?,
|
||||
])?;
|
||||
substack0.try_and(fourier_scratch)
|
||||
}
|
||||
|
||||
/// Perform the external product of `ggsw` and `glwe`, and adds the result to `out`.
|
||||
#[cfg_attr(__profiling, inline(never))]
|
||||
pub fn add_external_product_pseudo_ggsw_assign<Scalar, InputGlweCont>(
|
||||
mut out: GlweCiphertextMutView<'_, Scalar>,
|
||||
ggsw: PseudoFourierGgswCiphertextView<'_>,
|
||||
glwe: &GlweCiphertext<InputGlweCont>,
|
||||
fft: FftView<'_>,
|
||||
stack: PodStack<'_>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
InputGlweCont: Container<Element = Scalar>,
|
||||
{
|
||||
// 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_size_out(), out.glwe_size());
|
||||
|
||||
//println!("%%%%%% INSIDE EXTERNAL PRODUCT %%%%%%%%%%");
|
||||
|
||||
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(),
|
||||
);
|
||||
// println!("going in first substack");
|
||||
|
||||
let (mut output_fft_buffer, mut substack0) =
|
||||
stack.make_aligned_raw::<c64>(fourier_poly_size * ggsw.glwe_size_out().0, align);
|
||||
// println!("First substack done");
|
||||
// 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 output_fft_buffer = &mut *output_fft_buffer;
|
||||
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, mut substack1) = TensorSignedDecompositionLendingIter::new(
|
||||
glwe.as_ref()
|
||||
.iter()
|
||||
.map(|s| decomposer.closest_representable(*s)),
|
||||
DecompositionBaseLog(decomposer.base_log),
|
||||
DecompositionLevelCount(decomposer.level_count),
|
||||
substack0.rb_mut(),
|
||||
);
|
||||
|
||||
// We loop through the levels (we reverse to match the order of the decomposition iterator.)
|
||||
ggsw.into_levels().rev().for_each(|ggsw_decomp_matrix| {
|
||||
// We retrieve the decomposition of this level.
|
||||
let (glwe_level, glwe_decomp_term, mut substack2) =
|
||||
collect_next_term(&mut decomposition, &mut substack1, align);
|
||||
let glwe_decomp_term = GlweCiphertextView::from_container(
|
||||
&*glwe_decomp_term,
|
||||
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 ...
|
||||
|
||||
// println!("ggsw_decomp_matrix.into_rows() = {:?}\n ",ggsw_decomp_matrix);
|
||||
// println!("glwe_decomp_term.as_poly = {:?}\n",glwe_decomp_term.as_polynomial_list());
|
||||
|
||||
izip!(
|
||||
ggsw_decomp_matrix.into_rows(),
|
||||
glwe_decomp_term.get_mask().as_polynomial_list().iter()
|
||||
)
|
||||
.for_each(|(ggsw_row, glwe_poly)| {
|
||||
// println!("GGSW_ROW = {:?}\n", ggsw_row);
|
||||
// println!("GLWE_POLY = {:?}\n", glwe_poly);
|
||||
|
||||
let (mut fourier, substack3) = substack2
|
||||
.rb_mut()
|
||||
.make_aligned_raw::<c64>(fourier_poly_size, align);
|
||||
//println!("Second substack done");
|
||||
|
||||
// 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.
|
||||
|
||||
//println!("GLWE_POLY FOURIER = {:?}\n", fourier);
|
||||
|
||||
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;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//println!("Ouput FFT Buffer = {:?}\n", output_fft_buffer);
|
||||
|
||||
// -------------------------------------------- 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| FourierPolynomialView { data: slice }),
|
||||
)
|
||||
.for_each(|(out, fourier)| {
|
||||
fft.add_backward_as_torus(out, fourier, substack0.rb_mut());
|
||||
});
|
||||
}
|
||||
//We copy the body
|
||||
//as_mut().copy_from_slice(glwe.get_body().as_ref());
|
||||
|
||||
for (dst, src) in out
|
||||
.get_mut_body()
|
||||
.as_mut_polynomial()
|
||||
.iter_mut()
|
||||
.zip(glwe.get_body().as_polynomial().iter())
|
||||
{
|
||||
*dst = dst.wrapping_add(*src);
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
nightly-2024-02-13
|
||||
nightly-2024-04-29
|
||||
|
||||
Reference in New Issue
Block a user