Compare commits

...

2 Commits

Author SHA1 Message Date
Loris
03635afa8f toolchain changed 2024-05-16 11:36:40 +02:00
J-B Orfila
4c659b5c70 Artifacts CCS2024 2024-04-23 11:00:00 +02:00
36 changed files with 6579 additions and 17 deletions

View File

@@ -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: |

View File

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

View File

@@ -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"

View 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);
// }
// }

View 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);

View 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);

View File

@@ -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))));
// }
// }

View File

@@ -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;
}
}
}

View File

@@ -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
}

View File

@@ -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);
///

View File

@@ -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
}

View 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,
)
}

View File

@@ -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
// // // }

View File

@@ -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
}

View File

@@ -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())
}

View 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(),
);
}
}
}

View File

@@ -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
}

View File

@@ -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::*;

View 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)
}

View 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);
}

View 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
});

View File

@@ -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),
};

View 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
});

View File

@@ -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),
};

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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);

View 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,
}
}
}

View File

@@ -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::*;

View 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;
}

View File

@@ -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,

View File

@@ -1,5 +1,6 @@
pub mod bootstrap;
pub mod ggsw;
pub mod pseudo_ggsw;
pub mod wop_pbs;
#[cfg(test)]

View 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);
}
}

View File

@@ -1 +1 @@
nightly-2024-02-13
nightly-2024-04-29