mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-11 07:38:08 -05:00
Compare commits
9 Commits
al/doc_erc
...
al/pfail_g
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb285b6c6e | ||
|
|
fafe7b5816 | ||
|
|
e76906a084 | ||
|
|
9b9542149f | ||
|
|
3f34300130 | ||
|
|
387f8d92b6 | ||
|
|
47fe167466 | ||
|
|
5fdf942d90 | ||
|
|
da1d55e808 |
86
tfhe/examples/p_fail.rs
Normal file
86
tfhe/examples/p_fail.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use rayon::prelude::*;
|
||||
use tfhe::core_crypto::prelude::*;
|
||||
use tfhe::shortint::ciphertext::MaxNoiseLevel;
|
||||
use tfhe::shortint::engine::ShortintEngine;
|
||||
use tfhe::shortint::gen_keys;
|
||||
use tfhe::shortint::parameters::multi_bit::MultiBitPBSParameters;
|
||||
use tfhe::shortint::parameters::{CarryModulus, MessageModulus};
|
||||
|
||||
pub const PARAM_MULTI_BIT_GROUP_3_MESSAGE_4_CARRY_2_KS_PBS_GAUSSIAN_2M5_5: MultiBitPBSParameters =
|
||||
MultiBitPBSParameters {
|
||||
lwe_dimension: LweDimension(891),
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(2048),
|
||||
lwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
|
||||
1.3292631075564801e-06,
|
||||
)),
|
||||
glwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
|
||||
2.845267479601915e-15,
|
||||
)),
|
||||
pbs_base_log: DecompositionBaseLog(21),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
ks_base_log: DecompositionBaseLog(4),
|
||||
ks_level: DecompositionLevelCount(4),
|
||||
message_modulus: MessageModulus(8),
|
||||
carry_modulus: CarryModulus(4),
|
||||
max_noise_level: MaxNoiseLevel::new(5),
|
||||
log2_p_fail: -5.5,
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
encryption_key_choice: EncryptionKeyChoice::Big,
|
||||
grouping_factor: LweBskGroupingFactor(3),
|
||||
deterministic_execution: false,
|
||||
};
|
||||
|
||||
pub fn main() {
|
||||
let fhe_params = PARAM_MULTI_BIT_GROUP_3_MESSAGE_4_CARRY_2_KS_PBS_GAUSSIAN_2M5_5;
|
||||
|
||||
let max_scalar_mul = fhe_params.max_noise_level.get() as u8;
|
||||
|
||||
let expected_fails = 500;
|
||||
|
||||
println!("running");
|
||||
// let num_pbs = (1 << 6) * expected_fails;
|
||||
let num_pbs = (2.0_f32.powf(5.5).ceil() as i32) * expected_fails;
|
||||
|
||||
let (cks, sks) = gen_keys(fhe_params);
|
||||
let lut = sks.generate_lookup_table(|x| x);
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
let actual_fails: u32 = (0..num_pbs)
|
||||
.into_par_iter()
|
||||
.map(|_i| {
|
||||
// let mut engine = ShortintEngine::new();
|
||||
// let cks = engine.new_client_key(fhe_params.into());
|
||||
// let sks = engine.new_server_key(&cks);
|
||||
|
||||
// let mut ct = engine.encrypt(&cks, 0);
|
||||
|
||||
// let lut = sks.generate_lookup_table(|x| x);
|
||||
|
||||
let mut ct = cks.encrypt(0);
|
||||
|
||||
// Get baseline noise after PBS
|
||||
sks.unchecked_scalar_mul_assign(&mut ct, max_scalar_mul);
|
||||
sks.apply_lookup_table_assign(&mut ct, &lut);
|
||||
|
||||
// // PBS with baseline noise as input
|
||||
// sks.unchecked_scalar_mul_assign(&mut ct, max_scalar_mul);
|
||||
// sks.apply_lookup_table_assign(&mut ct, &lut);
|
||||
|
||||
let dec = cks.decrypt(&ct);
|
||||
|
||||
if dec != 0 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
.sum();
|
||||
|
||||
let elapsed = start.elapsed();
|
||||
|
||||
println!("Elapsed: {elapsed:?}");
|
||||
println!("Expected fails: {expected_fails}");
|
||||
println!("Got fails: {actual_fails}");
|
||||
}
|
||||
1960
tfhe/examples/p_fail_gpu.rs
Normal file
1960
tfhe/examples/p_fail_gpu.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,211 @@
|
||||
use super::*;
|
||||
use crate::core_crypto::commons::noise_formulas::lwe_multi_bit_programmable_bootstrap::multi_bit_pbs_variance_132_bits_security_gaussian_gf_3;
|
||||
use crate::core_crypto::commons::noise_formulas::secure_noise::minimal_lwe_variance_for_132_bits_security_gaussian;
|
||||
use crate::core_crypto::commons::test_tools::{torus_modular_diff, variance};
|
||||
use rayon::prelude::*;
|
||||
|
||||
// This is 1 / 16 which is exactly representable in an f64 (even an f32)
|
||||
// 1 / 32 is too strict and fails the tests
|
||||
const RELATIVE_TOLERANCE: f64 = 0.0625;
|
||||
|
||||
const NB_TESTS: usize = 1000;
|
||||
|
||||
fn lwe_encrypt_multi_bit_pbs_group_3_decrypt_custom_mod<Scalar>(params: MultiBitTestParams<Scalar>)
|
||||
where
|
||||
Scalar: UnsignedTorus + Sync + Send + CastFrom<usize> + CastInto<usize>,
|
||||
{
|
||||
let input_lwe_dimension = params.input_lwe_dimension;
|
||||
let lwe_noise_distribution = params.lwe_noise_distribution;
|
||||
let glwe_noise_distribution = params.glwe_noise_distribution;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
let message_modulus_log = params.message_modulus_log;
|
||||
let msg_modulus = Scalar::ONE.shl(message_modulus_log.0);
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
let glwe_dimension = params.glwe_dimension;
|
||||
let polynomial_size = params.polynomial_size;
|
||||
let pbs_decomposition_base_log = params.decomp_base_log;
|
||||
let pbs_decomposition_level_count = params.decomp_level_count;
|
||||
let grouping_factor = params.grouping_factor;
|
||||
assert_eq!(grouping_factor.0, 3);
|
||||
|
||||
let modulus_as_f64 = if ciphertext_modulus.is_native_modulus() {
|
||||
2.0f64.powi(Scalar::BITS as i32)
|
||||
} else {
|
||||
ciphertext_modulus.get_custom_modulus() as f64
|
||||
};
|
||||
|
||||
let expected_variance = multi_bit_pbs_variance_132_bits_security_gaussian_gf_3(
|
||||
input_lwe_dimension,
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
pbs_decomposition_base_log,
|
||||
pbs_decomposition_level_count,
|
||||
modulus_as_f64,
|
||||
);
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let f = |x: Scalar| x;
|
||||
|
||||
let delta: Scalar = encoding_with_padding / msg_modulus;
|
||||
let mut msg = msg_modulus;
|
||||
|
||||
let num_samples = NB_TESTS * <Scalar as CastInto<usize>>::cast_into(msg);
|
||||
let mut noise_samples = Vec::with_capacity(num_samples);
|
||||
|
||||
let input_lwe_secret_key = allocate_and_generate_new_binary_lwe_secret_key(
|
||||
input_lwe_dimension,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
|
||||
let output_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
|
||||
let output_lwe_secret_key = output_glwe_secret_key.as_lwe_secret_key();
|
||||
|
||||
let fbsk = {
|
||||
let bsk = allocate_and_generate_new_lwe_multi_bit_bootstrap_key(
|
||||
&input_lwe_secret_key,
|
||||
&output_glwe_secret_key,
|
||||
pbs_decomposition_base_log,
|
||||
pbs_decomposition_level_count,
|
||||
grouping_factor,
|
||||
glwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&*bsk,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let mut fbsk = FourierLweMultiBitBootstrapKey::new(
|
||||
bsk.input_lwe_dimension(),
|
||||
bsk.glwe_size(),
|
||||
bsk.polynomial_size(),
|
||||
bsk.decomposition_base_log(),
|
||||
bsk.decomposition_level_count(),
|
||||
bsk.grouping_factor(),
|
||||
);
|
||||
|
||||
par_convert_standard_lwe_multi_bit_bootstrap_key_to_fourier(&bsk, &mut fbsk);
|
||||
|
||||
fbsk
|
||||
};
|
||||
|
||||
let accumulator = generate_programmable_bootstrap_glwe_lut(
|
||||
polynomial_size,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
msg_modulus.cast_into(),
|
||||
ciphertext_modulus,
|
||||
delta,
|
||||
f,
|
||||
);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&accumulator,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
while msg != Scalar::ZERO {
|
||||
msg = msg.wrapping_sub(Scalar::ONE);
|
||||
|
||||
let current_run_samples: Vec<_> = (0..NB_TESTS)
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let plaintext = Plaintext(msg * delta);
|
||||
|
||||
let lwe_ciphertext_in = allocate_and_encrypt_new_lwe_ciphertext(
|
||||
&input_lwe_secret_key,
|
||||
plaintext,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&lwe_ciphertext_in,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let mut out_pbs_ct = LweCiphertext::new(
|
||||
Scalar::ZERO,
|
||||
output_lwe_secret_key.lwe_dimension().to_lwe_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
multi_bit_programmable_bootstrap_lwe_ciphertext(
|
||||
&lwe_ciphertext_in,
|
||||
&mut out_pbs_ct,
|
||||
&accumulator,
|
||||
&fbsk,
|
||||
params.thread_count,
|
||||
true,
|
||||
);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&out_pbs_ct,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let decrypted = decrypt_lwe_ciphertext(&output_lwe_secret_key, &out_pbs_ct);
|
||||
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
|
||||
assert_eq!(decoded, f(msg));
|
||||
|
||||
torus_modular_diff(plaintext.0, decrypted.0, ciphertext_modulus)
|
||||
})
|
||||
.collect();
|
||||
|
||||
noise_samples.extend(current_run_samples);
|
||||
}
|
||||
|
||||
let measured_variance = variance(&noise_samples);
|
||||
|
||||
let minimal_variance = minimal_lwe_variance_for_132_bits_security_gaussian(
|
||||
fbsk.output_lwe_dimension(),
|
||||
if ciphertext_modulus.is_native_modulus() {
|
||||
2.0f64.powi(Scalar::BITS as i32)
|
||||
} else {
|
||||
ciphertext_modulus.get_custom_modulus() as f64
|
||||
},
|
||||
);
|
||||
|
||||
// Have a log even if it's a test to have a trace in no capture mode to eyeball variances
|
||||
println!("measured_variance={measured_variance:?}");
|
||||
println!("expected_variance={expected_variance:?}");
|
||||
println!("minimal_variance={minimal_variance:?}");
|
||||
|
||||
if measured_variance.0 < expected_variance.0 {
|
||||
// We are in the clear as long as we have at least the noise for security
|
||||
assert!(
|
||||
measured_variance.0 >= minimal_variance.0,
|
||||
"Found insecure variance after PBS\n\
|
||||
measure_variance={measured_variance:?}\n\
|
||||
minimal_variance={minimal_variance:?}"
|
||||
);
|
||||
} else {
|
||||
// Check we are not too far from the expected variance if we are bigger
|
||||
let var_abs_diff = (expected_variance.0 - measured_variance.0).abs();
|
||||
let tolerance_threshold = RELATIVE_TOLERANCE * expected_variance.0;
|
||||
|
||||
assert!(
|
||||
var_abs_diff < tolerance_threshold,
|
||||
"Absolute difference for variance: {var_abs_diff}, \
|
||||
tolerance threshold: {tolerance_threshold}, \
|
||||
got variance: {measured_variance:?}, \
|
||||
expected variance: {expected_variance:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
create_parametrized_test!(lwe_encrypt_multi_bit_pbs_group_3_decrypt_custom_mod {
|
||||
NOISE_TEST_PARAMS_MULTI_BIT_GROUP_3_4_BITS_NATIVE_U64_132_BITS_GAUSSIAN
|
||||
});
|
||||
@@ -2,7 +2,9 @@ use super::*;
|
||||
|
||||
mod lwe_encryption_noise;
|
||||
mod lwe_keyswitch_noise;
|
||||
mod lwe_multi_bit_programmable_bootstrapping_noise;
|
||||
mod lwe_programmable_bootstrapping_noise;
|
||||
mod pfail_multi_bit;
|
||||
|
||||
#[allow(clippy::excessive_precision)]
|
||||
pub const NOISE_TEST_PARAMS_4_BITS_NATIVE_U64_132_BITS_GAUSSIAN: ClassicTestParams<u64> =
|
||||
@@ -28,3 +30,41 @@ pub const NOISE_TEST_PARAMS_4_BITS_NATIVE_U64_132_BITS_GAUSSIAN: ClassicTestPara
|
||||
message_modulus_log: MessageModulusLog(4),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
#[allow(clippy::excessive_precision)]
|
||||
pub const NOISE_TEST_PARAMS_MULTI_BIT_GROUP_3_4_BITS_NATIVE_U64_132_BITS_GAUSSIAN:
|
||||
MultiBitTestParams<u64> = MultiBitTestParams {
|
||||
input_lwe_dimension: LweDimension(837),
|
||||
lwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
|
||||
3.3747142481837397e-06,
|
||||
)),
|
||||
decomp_base_log: DecompositionBaseLog(21),
|
||||
decomp_level_count: DecompositionLevelCount(1),
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(2048),
|
||||
glwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
|
||||
2.845267479601915e-15,
|
||||
)),
|
||||
message_modulus_log: MessageModulusLog(4),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
grouping_factor: LweBskGroupingFactor(3),
|
||||
thread_count: ThreadCount(8),
|
||||
};
|
||||
#[allow(clippy::excessive_precision)]
|
||||
pub const PFAIL_TEST_PARAMS_MULTI_BIT_GROUP_3_6_BITS_NATIVE_U64_132_BITS_GAUSSIAN:
|
||||
MultiBitTestParams<u64> = MultiBitTestParams {
|
||||
input_lwe_dimension: LweDimension(522),
|
||||
lwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
|
||||
0.0007736698118352694,
|
||||
)),
|
||||
decomp_base_log: DecompositionBaseLog(21),
|
||||
decomp_level_count: DecompositionLevelCount(1),
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(4096),
|
||||
glwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
|
||||
0.0000000000000000002168404344971009,
|
||||
)),
|
||||
message_modulus_log: MessageModulusLog(6),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
grouping_factor: LweBskGroupingFactor(3),
|
||||
thread_count: ThreadCount(8),
|
||||
};
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
use super::*;
|
||||
use crate::core_crypto::commons::noise_formulas::lwe_multi_bit_programmable_bootstrap::multi_bit_pbs_variance_132_bits_security_gaussian_gf_3;
|
||||
use crate::core_crypto::commons::noise_formulas::secure_noise::minimal_lwe_variance_for_132_bits_security_gaussian;
|
||||
use crate::core_crypto::commons::test_tools::{torus_modular_diff, variance};
|
||||
use rayon::prelude::*;
|
||||
|
||||
// This is 1 / 16 which is exactly representable in an f64 (even an f32)
|
||||
// 1 / 32 is too strict and fails the tests
|
||||
const RELATIVE_TOLERANCE: f64 = 0.0625;
|
||||
|
||||
const NB_TESTS: usize = 1000;
|
||||
fn pfail_multi_bit_pbs_group_3<Scalar>(params: MultiBitTestParams<Scalar>)
|
||||
where
|
||||
Scalar: UnsignedTorus + Sync + Send + CastFrom<usize> + CastInto<usize>,
|
||||
{
|
||||
let input_lwe_dimension = params.input_lwe_dimension;
|
||||
let lwe_noise_distribution = params.lwe_noise_distribution;
|
||||
let glwe_noise_distribution = params.glwe_noise_distribution;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
let message_modulus_log = params.message_modulus_log;
|
||||
let msg_modulus = Scalar::ONE.shl(message_modulus_log.0);
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
let glwe_dimension = params.glwe_dimension;
|
||||
let polynomial_size = params.polynomial_size;
|
||||
let pbs_decomposition_base_log = params.decomp_base_log;
|
||||
let pbs_decomposition_level_count = params.decomp_level_count;
|
||||
let grouping_factor = params.grouping_factor;
|
||||
assert_eq!(grouping_factor.0, 3);
|
||||
|
||||
let modulus_as_f64 = if ciphertext_modulus.is_native_modulus() {
|
||||
2.0f64.powi(Scalar::BITS as i32)
|
||||
} else {
|
||||
ciphertext_modulus.get_custom_modulus() as f64
|
||||
};
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let f = |x: Scalar| x;
|
||||
|
||||
let delta: Scalar = encoding_with_padding / msg_modulus;
|
||||
let mut msg = msg_modulus;
|
||||
|
||||
let expected_fails = 100;
|
||||
let pfail = 2.0_f64.powi(-14);
|
||||
let num_samples = ((expected_fails as f64) / pfail) as usize;
|
||||
|
||||
let input_lwe_secret_key = allocate_and_generate_new_binary_lwe_secret_key(
|
||||
input_lwe_dimension,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
|
||||
let output_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
|
||||
let output_lwe_secret_key = output_glwe_secret_key.as_lwe_secret_key();
|
||||
|
||||
let fbsk = {
|
||||
let bsk = allocate_and_generate_new_lwe_multi_bit_bootstrap_key(
|
||||
&input_lwe_secret_key,
|
||||
&output_glwe_secret_key,
|
||||
pbs_decomposition_base_log,
|
||||
pbs_decomposition_level_count,
|
||||
grouping_factor,
|
||||
glwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
let mut fbsk = FourierLweMultiBitBootstrapKey::new(
|
||||
bsk.input_lwe_dimension(),
|
||||
bsk.glwe_size(),
|
||||
bsk.polynomial_size(),
|
||||
bsk.decomposition_base_log(),
|
||||
bsk.decomposition_level_count(),
|
||||
bsk.grouping_factor(),
|
||||
);
|
||||
|
||||
par_convert_standard_lwe_multi_bit_bootstrap_key_to_fourier(&bsk, &mut fbsk);
|
||||
|
||||
fbsk
|
||||
};
|
||||
|
||||
let accumulator = generate_programmable_bootstrap_glwe_lut(
|
||||
polynomial_size,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
msg_modulus.cast_into(),
|
||||
ciphertext_modulus,
|
||||
delta,
|
||||
f,
|
||||
);
|
||||
|
||||
let msg = Scalar::ZERO;
|
||||
let fails: i32 = (0..num_samples).into_par_iter().map(|_| {
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let plaintext = Plaintext(msg * delta);
|
||||
|
||||
let lwe_ciphertext_in = allocate_and_encrypt_new_lwe_ciphertext(
|
||||
&input_lwe_secret_key,
|
||||
plaintext,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
let mut out_pbs_ct = LweCiphertext::new(
|
||||
Scalar::ZERO,
|
||||
output_lwe_secret_key.lwe_dimension().to_lwe_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
multi_bit_programmable_bootstrap_lwe_ciphertext(
|
||||
&lwe_ciphertext_in,
|
||||
&mut out_pbs_ct,
|
||||
&accumulator,
|
||||
&fbsk,
|
||||
params.thread_count,
|
||||
true,
|
||||
);
|
||||
|
||||
let decrypted = decrypt_lwe_ciphertext(&output_lwe_secret_key, &out_pbs_ct);
|
||||
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
|
||||
if decoded == f(msg) {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}).sum();
|
||||
panic!("Got fails: {}, expected fails: {}", fails, expected_fails);
|
||||
}
|
||||
|
||||
|
||||
create_parametrized_test!(pfail_multi_bit_pbs_group_3 {
|
||||
PFAIL_TEST_PARAMS_MULTI_BIT_GROUP_3_6_BITS_NATIVE_U64_132_BITS_GAUSSIAN
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
// This file was autogenerated, do not modify by hand.
|
||||
use crate::core_crypto::commons::dispersion::Variance;
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
|
||||
/// This formula is only valid if the proper noise distributions are used and
|
||||
/// if the keys used are encrypted using secure noise given by the
|
||||
/// [`minimal_glwe_variance`](`super::secure_noise`)
|
||||
/// and [`minimal_lwe_variance`](`super::secure_noise`) family of functions.
|
||||
pub fn multi_bit_pbs_variance_132_bits_security_gaussian_gf_3(
|
||||
input_lwe_dimension: LweDimension,
|
||||
output_glwe_dimension: GlweDimension,
|
||||
output_polynomial_size: PolynomialSize,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
modulus: f64,
|
||||
) -> Variance {
|
||||
Variance(multi_bit_pbs_variance_132_bits_security_gaussian_gf_3_impl(
|
||||
input_lwe_dimension.0 as f64,
|
||||
output_glwe_dimension.0 as f64,
|
||||
output_polynomial_size.0 as f64,
|
||||
2.0f64.powi(decomposition_base_log.0 as i32),
|
||||
decomposition_level_count.0 as f64,
|
||||
modulus,
|
||||
))
|
||||
}
|
||||
|
||||
/// This formula is only valid if the proper noise distributions are used and
|
||||
/// if the keys used are encrypted using secure noise given by the
|
||||
/// [`minimal_glwe_variance`](`super::secure_noise`)
|
||||
/// and [`minimal_lwe_variance`](`super::secure_noise`) family of functions.
|
||||
pub fn multi_bit_pbs_variance_132_bits_security_gaussian_gf_3_impl(
|
||||
input_lwe_dimension: f64,
|
||||
output_glwe_dimension: f64,
|
||||
output_polynomial_size: f64,
|
||||
decomposition_base: f64,
|
||||
decomposition_level_count: f64,
|
||||
modulus: f64,
|
||||
) -> f64 {
|
||||
(1_f64 / 3_f64)
|
||||
* input_lwe_dimension
|
||||
* (3.44492863492271e-32
|
||||
* decomposition_base.powf(2.0)
|
||||
* decomposition_level_count
|
||||
* output_polynomial_size.powf(2.0)
|
||||
* (output_glwe_dimension + 1.0)
|
||||
+ 8.0
|
||||
* decomposition_level_count
|
||||
* output_polynomial_size
|
||||
* ((4.0 - 2.88539008177793 * modulus.ln()).exp2()
|
||||
+ (-0.0497829131652661 * output_glwe_dimension * output_polynomial_size
|
||||
+ 5.31469187675068)
|
||||
.exp2())
|
||||
* ((1_f64 / 12.0) * decomposition_base.powf(2.0) + 0.166666666666667)
|
||||
* (output_glwe_dimension + 1.0)
|
||||
+ (1_f64 / 12.0) * modulus.powf(-2.0)
|
||||
+ (1_f64 / 2.0)
|
||||
* output_glwe_dimension
|
||||
* output_polynomial_size
|
||||
* (0.0208333333333333 * modulus.powf(-2.0)
|
||||
+ 0.0416666666666667
|
||||
* decomposition_base.powf(-2.0 * decomposition_level_count))
|
||||
+ (1_f64 / 24.0) * decomposition_base.powf(-2.0 * decomposition_level_count))
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
// This file was autogenerated, do not modify by hand.
|
||||
pub mod lwe_keyswitch;
|
||||
pub mod lwe_multi_bit_programmable_bootstrap;
|
||||
pub mod lwe_programmable_bootstrap;
|
||||
pub mod secure_noise;
|
||||
|
||||
@@ -6,6 +6,7 @@ mod lwe_linear_algebra;
|
||||
mod lwe_multi_bit_programmable_bootstrapping;
|
||||
mod lwe_packing_keyswitch;
|
||||
mod lwe_programmable_bootstrapping;
|
||||
mod noise_distribution;
|
||||
|
||||
pub struct CudaPackingKeySwitchKeys<Scalar: UnsignedInteger> {
|
||||
pub lwe_sk: LweSecretKey<Vec<Scalar>>,
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
use super::*;
|
||||
use crate::core_crypto::commons::noise_formulas::lwe_multi_bit_programmable_bootstrap::multi_bit_pbs_variance_132_bits_security_gaussian_gf_3;
|
||||
use crate::core_crypto::commons::noise_formulas::secure_noise::minimal_lwe_variance_for_132_bits_security_gaussian;
|
||||
use crate::core_crypto::commons::test_tools::{torus_modular_diff, variance};
|
||||
use crate::core_crypto::gpu::glwe_ciphertext_list::CudaGlweCiphertextList;
|
||||
use crate::core_crypto::gpu::lwe_bootstrap_key::CudaLweBootstrapKey;
|
||||
use crate::core_crypto::gpu::lwe_ciphertext_list::CudaLweCiphertextList;
|
||||
use crate::core_crypto::gpu::lwe_multi_bit_bootstrap_key::CudaLweMultiBitBootstrapKey;
|
||||
use crate::core_crypto::gpu::vec::CudaVec;
|
||||
use crate::core_crypto::gpu::{cuda_multi_bit_programmable_bootstrap_lwe_ciphertext, CudaStreams};
|
||||
use itertools::Itertools;
|
||||
use rayon::prelude::*;
|
||||
|
||||
// This is 1 / 16 which is exactly representable in an f64 (even an f32)
|
||||
// 1 / 32 is too strict and fails the tests
|
||||
const RELATIVE_TOLERANCE: f64 = 0.0625;
|
||||
|
||||
const NB_TESTS: usize = 1000;
|
||||
|
||||
fn lwe_encrypt_multi_bit_pbs_decrypt_custom_mod<Scalar>(params: MultiBitTestParams<Scalar>)
|
||||
where
|
||||
Scalar: UnsignedTorus + Sync + Send + CastFrom<usize> + CastInto<usize>,
|
||||
{
|
||||
let input_lwe_dimension = params.input_lwe_dimension;
|
||||
let lwe_noise_distribution = params.lwe_noise_distribution;
|
||||
let glwe_noise_distribution = params.glwe_noise_distribution;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
let message_modulus_log = params.message_modulus_log;
|
||||
let msg_modulus = Scalar::ONE.shl(message_modulus_log.0);
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
let glwe_dimension = params.glwe_dimension;
|
||||
let polynomial_size = params.polynomial_size;
|
||||
let pbs_decomposition_base_log = params.decomp_base_log;
|
||||
let pbs_decomposition_level_count = params.decomp_level_count;
|
||||
let grouping_factor = params.grouping_factor;
|
||||
let number_of_messages = 1;
|
||||
|
||||
let gpu_index = 0;
|
||||
let stream = CudaStreams::new_single_gpu(gpu_index);
|
||||
|
||||
let modulus_as_f64 = if ciphertext_modulus.is_native_modulus() {
|
||||
2.0f64.powi(Scalar::BITS as i32)
|
||||
} else {
|
||||
ciphertext_modulus.get_custom_modulus() as f64
|
||||
};
|
||||
|
||||
let expected_variance = multi_bit_pbs_variance_132_bits_security_gaussian_gf_3(
|
||||
input_lwe_dimension,
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
pbs_decomposition_base_log,
|
||||
pbs_decomposition_level_count,
|
||||
modulus_as_f64,
|
||||
);
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let f = |x: Scalar| x;
|
||||
|
||||
let delta: Scalar = encoding_with_padding / msg_modulus;
|
||||
let mut msg = msg_modulus;
|
||||
|
||||
let num_samples = NB_TESTS * <Scalar as CastInto<usize>>::cast_into(msg);
|
||||
let mut noise_samples = Vec::with_capacity(num_samples);
|
||||
|
||||
let input_lwe_secret_key = allocate_and_generate_new_binary_lwe_secret_key(
|
||||
input_lwe_dimension,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
|
||||
let output_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
|
||||
let output_lwe_secret_key = output_glwe_secret_key.as_lwe_secret_key();
|
||||
let output_lwe_dimension = output_lwe_secret_key.lwe_dimension();
|
||||
|
||||
let accumulator = generate_programmable_bootstrap_glwe_lut(
|
||||
polynomial_size,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
msg_modulus.cast_into(),
|
||||
ciphertext_modulus,
|
||||
delta,
|
||||
f,
|
||||
);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&accumulator,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let mut bsk = LweMultiBitBootstrapKey::new(
|
||||
Scalar::ZERO,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
pbs_decomposition_base_log,
|
||||
pbs_decomposition_level_count,
|
||||
input_lwe_dimension,
|
||||
grouping_factor,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
par_generate_lwe_multi_bit_bootstrap_key(
|
||||
&input_lwe_secret_key,
|
||||
&output_glwe_secret_key,
|
||||
&mut bsk,
|
||||
glwe_noise_distribution,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&*bsk,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let d_bsk = CudaLweMultiBitBootstrapKey::from_lwe_multi_bit_bootstrap_key(&bsk, &stream);
|
||||
|
||||
while msg != Scalar::ZERO {
|
||||
msg = msg.wrapping_sub(Scalar::ONE);
|
||||
|
||||
let current_run_samples: Vec<_> = (0..NB_TESTS)
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let plaintext = Plaintext(msg * delta);
|
||||
|
||||
let lwe_ciphertext_in = allocate_and_encrypt_new_lwe_ciphertext(
|
||||
&input_lwe_secret_key,
|
||||
plaintext,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&lwe_ciphertext_in,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let d_lwe_ciphertext_in =
|
||||
CudaLweCiphertextList::from_lwe_ciphertext(&lwe_ciphertext_in, &stream);
|
||||
let mut d_out_pbs_ct = CudaLweCiphertextList::new(
|
||||
output_lwe_dimension,
|
||||
LweCiphertextCount(1),
|
||||
ciphertext_modulus,
|
||||
&stream,
|
||||
);
|
||||
let d_accumulator =
|
||||
CudaGlweCiphertextList::from_glwe_ciphertext(&accumulator, &stream);
|
||||
|
||||
let mut test_vector_indexes: Vec<Scalar> = vec![Scalar::ZERO; number_of_messages];
|
||||
for (i, ind) in test_vector_indexes.iter_mut().enumerate() {
|
||||
*ind = <usize as CastInto<Scalar>>::cast_into(i);
|
||||
}
|
||||
|
||||
let mut d_test_vector_indexes =
|
||||
unsafe { CudaVec::<Scalar>::new_async(number_of_messages, &stream, 0) };
|
||||
unsafe {
|
||||
d_test_vector_indexes.copy_from_cpu_async(&test_vector_indexes, &stream, 0)
|
||||
};
|
||||
|
||||
let num_blocks = d_lwe_ciphertext_in.0.lwe_ciphertext_count.0;
|
||||
let lwe_indexes_usize: Vec<usize> = (0..num_blocks).collect_vec();
|
||||
let lwe_indexes = lwe_indexes_usize
|
||||
.iter()
|
||||
.map(|&x| <usize as CastInto<Scalar>>::cast_into(x))
|
||||
.collect_vec();
|
||||
let mut d_output_indexes =
|
||||
unsafe { CudaVec::<Scalar>::new_async(num_blocks, &stream, 0) };
|
||||
let mut d_input_indexes =
|
||||
unsafe { CudaVec::<Scalar>::new_async(num_blocks, &stream, 0) };
|
||||
unsafe {
|
||||
d_input_indexes.copy_from_cpu_async(&lwe_indexes, &stream, 0);
|
||||
d_output_indexes.copy_from_cpu_async(&lwe_indexes, &stream, 0);
|
||||
}
|
||||
|
||||
cuda_multi_bit_programmable_bootstrap_lwe_ciphertext(
|
||||
&d_lwe_ciphertext_in,
|
||||
&mut d_out_pbs_ct,
|
||||
&d_accumulator,
|
||||
&d_test_vector_indexes,
|
||||
&d_output_indexes,
|
||||
&d_input_indexes,
|
||||
&d_bsk,
|
||||
&stream,
|
||||
);
|
||||
|
||||
let out_pbs_ct = d_out_pbs_ct.into_lwe_ciphertext(&stream);
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&out_pbs_ct,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let decrypted = decrypt_lwe_ciphertext(&output_lwe_secret_key, &out_pbs_ct);
|
||||
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
|
||||
assert_eq!(decoded, f(msg));
|
||||
|
||||
torus_modular_diff(plaintext.0, decrypted.0, ciphertext_modulus)
|
||||
})
|
||||
.collect();
|
||||
|
||||
noise_samples.extend(current_run_samples);
|
||||
}
|
||||
|
||||
let measured_variance = variance(&noise_samples);
|
||||
|
||||
let minimal_variance = minimal_lwe_variance_for_132_bits_security_gaussian(
|
||||
bsk.output_lwe_dimension(),
|
||||
if ciphertext_modulus.is_native_modulus() {
|
||||
2.0f64.powi(Scalar::BITS as i32)
|
||||
} else {
|
||||
ciphertext_modulus.get_custom_modulus() as f64
|
||||
},
|
||||
);
|
||||
|
||||
// Have a log even if it's a test to have a trace in no capture mode to eyeball variances
|
||||
println!("measured_variance={measured_variance:?}");
|
||||
println!("expected_variance={expected_variance:?}");
|
||||
println!("minimal_variance={minimal_variance:?}");
|
||||
|
||||
if measured_variance.0 < expected_variance.0 {
|
||||
// We are in the clear as long as we have at least the noise for security
|
||||
assert!(
|
||||
measured_variance.0 >= minimal_variance.0,
|
||||
"Found insecure variance after PBS\n\
|
||||
measure_variance={measured_variance:?}\n\
|
||||
minimal_variance={minimal_variance:?}"
|
||||
);
|
||||
} else {
|
||||
// Check we are not too far from the expected variance if we are bigger
|
||||
let var_abs_diff = (expected_variance.0 - measured_variance.0).abs();
|
||||
let tolerance_threshold = RELATIVE_TOLERANCE * expected_variance.0;
|
||||
|
||||
assert!(
|
||||
var_abs_diff < tolerance_threshold,
|
||||
"Absolute difference for variance: {var_abs_diff}, \
|
||||
tolerance threshold: {tolerance_threshold}, \
|
||||
got variance: {measured_variance:?}, \
|
||||
expected variance: {expected_variance:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
create_parametrized_test!(lwe_encrypt_multi_bit_pbs_decrypt_custom_mod {
|
||||
NOISE_TEST_PARAMS_GPU_MULTI_BIT_GROUP_3_4_BITS_NATIVE_U64_132_BITS_GAUSSIAN
|
||||
});
|
||||
@@ -0,0 +1,248 @@
|
||||
use super::*;
|
||||
use crate::core_crypto::commons::noise_formulas::lwe_programmable_bootstrap::pbs_variance_132_bits_security_gaussian;
|
||||
use crate::core_crypto::commons::noise_formulas::secure_noise::minimal_lwe_variance_for_132_bits_security_gaussian;
|
||||
use crate::core_crypto::commons::test_tools::{torus_modular_diff, variance};
|
||||
use crate::core_crypto::gpu::glwe_ciphertext_list::CudaGlweCiphertextList;
|
||||
use crate::core_crypto::gpu::lwe_bootstrap_key::CudaLweBootstrapKey;
|
||||
use crate::core_crypto::gpu::lwe_ciphertext_list::CudaLweCiphertextList;
|
||||
use crate::core_crypto::gpu::vec::CudaVec;
|
||||
use crate::core_crypto::gpu::{cuda_programmable_bootstrap_lwe_ciphertext, CudaStreams};
|
||||
use itertools::Itertools;
|
||||
use rayon::prelude::*;
|
||||
|
||||
// This is 1 / 16 which is exactly representable in an f64 (even an f32)
|
||||
// 1 / 32 is too strict and fails the tests
|
||||
const RELATIVE_TOLERANCE: f64 = 0.0625;
|
||||
|
||||
const NB_TESTS: usize = 1000;
|
||||
|
||||
fn lwe_encrypt_pbs_decrypt_custom_mod<Scalar>(params: ClassicTestParams<Scalar>)
|
||||
where
|
||||
Scalar: UnsignedTorus + Sync + Send + CastFrom<usize> + CastInto<usize>,
|
||||
{
|
||||
let input_lwe_dimension = params.lwe_dimension;
|
||||
let lwe_noise_distribution = params.lwe_noise_distribution;
|
||||
let glwe_noise_distribution = params.glwe_noise_distribution;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
let message_modulus_log = params.message_modulus_log;
|
||||
let msg_modulus = Scalar::ONE.shl(message_modulus_log.0);
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
let glwe_dimension = params.glwe_dimension;
|
||||
let polynomial_size = params.polynomial_size;
|
||||
let pbs_decomposition_base_log = params.pbs_base_log;
|
||||
let pbs_decomposition_level_count = params.pbs_level;
|
||||
let number_of_messages = 1;
|
||||
|
||||
let gpu_index = 0;
|
||||
let stream = CudaStreams::new_single_gpu(gpu_index);
|
||||
|
||||
let modulus_as_f64 = if ciphertext_modulus.is_native_modulus() {
|
||||
2.0f64.powi(Scalar::BITS as i32)
|
||||
} else {
|
||||
ciphertext_modulus.get_custom_modulus() as f64
|
||||
};
|
||||
|
||||
let expected_variance = pbs_variance_132_bits_security_gaussian(
|
||||
input_lwe_dimension,
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
pbs_decomposition_base_log,
|
||||
pbs_decomposition_level_count,
|
||||
modulus_as_f64,
|
||||
);
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let f = |x: Scalar| x;
|
||||
|
||||
let delta: Scalar = encoding_with_padding / msg_modulus;
|
||||
let mut msg = msg_modulus;
|
||||
|
||||
let num_samples = NB_TESTS * <Scalar as CastInto<usize>>::cast_into(msg);
|
||||
let mut noise_samples = Vec::with_capacity(num_samples);
|
||||
|
||||
let input_lwe_secret_key = allocate_and_generate_new_binary_lwe_secret_key(
|
||||
input_lwe_dimension,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
|
||||
let output_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
|
||||
let output_lwe_secret_key = output_glwe_secret_key.as_lwe_secret_key();
|
||||
let output_lwe_dimension = output_lwe_secret_key.lwe_dimension();
|
||||
|
||||
let accumulator = generate_programmable_bootstrap_glwe_lut(
|
||||
polynomial_size,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
msg_modulus.cast_into(),
|
||||
ciphertext_modulus,
|
||||
delta,
|
||||
f,
|
||||
);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&accumulator,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let mut bsk = LweBootstrapKey::new(
|
||||
Scalar::ZERO,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
pbs_decomposition_base_log,
|
||||
pbs_decomposition_level_count,
|
||||
input_lwe_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
par_generate_lwe_bootstrap_key(
|
||||
&input_lwe_secret_key,
|
||||
&output_glwe_secret_key,
|
||||
&mut bsk,
|
||||
glwe_noise_distribution,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&*bsk,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let d_bsk = CudaLweBootstrapKey::from_lwe_bootstrap_key(&bsk, &stream);
|
||||
while msg != Scalar::ZERO {
|
||||
msg = msg.wrapping_sub(Scalar::ONE);
|
||||
|
||||
let current_run_samples: Vec<_> = (0..NB_TESTS)
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let plaintext = Plaintext(msg * delta);
|
||||
|
||||
let lwe_ciphertext_in = allocate_and_encrypt_new_lwe_ciphertext(
|
||||
&input_lwe_secret_key,
|
||||
plaintext,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&lwe_ciphertext_in,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let d_lwe_ciphertext_in =
|
||||
CudaLweCiphertextList::from_lwe_ciphertext(&lwe_ciphertext_in, &stream);
|
||||
let mut d_out_pbs_ct = CudaLweCiphertextList::new(
|
||||
output_lwe_dimension,
|
||||
LweCiphertextCount(1),
|
||||
ciphertext_modulus,
|
||||
&stream,
|
||||
);
|
||||
let d_accumulator =
|
||||
CudaGlweCiphertextList::from_glwe_ciphertext(&accumulator, &stream);
|
||||
|
||||
let mut test_vector_indexes: Vec<Scalar> = vec![Scalar::ZERO; number_of_messages];
|
||||
for (i, ind) in test_vector_indexes.iter_mut().enumerate() {
|
||||
*ind = <usize as CastInto<Scalar>>::cast_into(i);
|
||||
}
|
||||
|
||||
let mut d_test_vector_indexes =
|
||||
unsafe { CudaVec::<Scalar>::new_async(number_of_messages, &stream, 0) };
|
||||
unsafe {
|
||||
d_test_vector_indexes.copy_from_cpu_async(&test_vector_indexes, &stream, 0)
|
||||
};
|
||||
|
||||
let num_blocks = d_lwe_ciphertext_in.0.lwe_ciphertext_count.0;
|
||||
let lwe_indexes_usize: Vec<usize> = (0..num_blocks).collect_vec();
|
||||
let lwe_indexes = lwe_indexes_usize
|
||||
.iter()
|
||||
.map(|&x| <usize as CastInto<Scalar>>::cast_into(x))
|
||||
.collect_vec();
|
||||
let mut d_output_indexes =
|
||||
unsafe { CudaVec::<Scalar>::new_async(num_blocks, &stream, 0) };
|
||||
let mut d_input_indexes =
|
||||
unsafe { CudaVec::<Scalar>::new_async(num_blocks, &stream, 0) };
|
||||
unsafe {
|
||||
d_input_indexes.copy_from_cpu_async(&lwe_indexes, &stream, 0);
|
||||
d_output_indexes.copy_from_cpu_async(&lwe_indexes, &stream, 0);
|
||||
}
|
||||
|
||||
cuda_programmable_bootstrap_lwe_ciphertext(
|
||||
&d_lwe_ciphertext_in,
|
||||
&mut d_out_pbs_ct,
|
||||
&d_accumulator,
|
||||
&d_test_vector_indexes,
|
||||
&d_output_indexes,
|
||||
&d_input_indexes,
|
||||
LweCiphertextCount(num_blocks),
|
||||
&d_bsk,
|
||||
&stream,
|
||||
);
|
||||
|
||||
let out_pbs_ct = d_out_pbs_ct.into_lwe_ciphertext(&stream);
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&out_pbs_ct,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let decrypted = decrypt_lwe_ciphertext(&output_lwe_secret_key, &out_pbs_ct);
|
||||
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
|
||||
assert_eq!(decoded, f(msg));
|
||||
|
||||
torus_modular_diff(plaintext.0, decrypted.0, ciphertext_modulus)
|
||||
})
|
||||
.collect();
|
||||
|
||||
noise_samples.extend(current_run_samples);
|
||||
}
|
||||
|
||||
let measured_variance = variance(&noise_samples);
|
||||
|
||||
let minimal_variance = minimal_lwe_variance_for_132_bits_security_gaussian(
|
||||
bsk.output_lwe_dimension(),
|
||||
if ciphertext_modulus.is_native_modulus() {
|
||||
2.0f64.powi(Scalar::BITS as i32)
|
||||
} else {
|
||||
ciphertext_modulus.get_custom_modulus() as f64
|
||||
},
|
||||
);
|
||||
|
||||
// Have a log even if it's a test to have a trace in no capture mode to eyeball variances
|
||||
println!("measured_variance={measured_variance:?}");
|
||||
println!("expected_variance={expected_variance:?}");
|
||||
println!("minimal_variance={minimal_variance:?}");
|
||||
|
||||
if measured_variance.0 < expected_variance.0 {
|
||||
// We are in the clear as long as we have at least the noise for security
|
||||
assert!(
|
||||
measured_variance.0 >= minimal_variance.0,
|
||||
"Found insecure variance after PBS\n\
|
||||
measure_variance={measured_variance:?}\n\
|
||||
minimal_variance={minimal_variance:?}"
|
||||
);
|
||||
} else {
|
||||
// Check we are not too far from the expected variance if we are bigger
|
||||
let var_abs_diff = (expected_variance.0 - measured_variance.0).abs();
|
||||
let tolerance_threshold = RELATIVE_TOLERANCE * expected_variance.0;
|
||||
|
||||
assert!(
|
||||
var_abs_diff < tolerance_threshold,
|
||||
"Absolute difference for variance: {var_abs_diff}, \
|
||||
tolerance threshold: {tolerance_threshold}, \
|
||||
got variance: {measured_variance:?}, \
|
||||
expected variance: {expected_variance:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
create_parametrized_test!(lwe_encrypt_pbs_decrypt_custom_mod {
|
||||
NOISE_TEST_PARAMS_4_BITS_NATIVE_U64_132_BITS_GAUSSIAN
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
use super::*;
|
||||
|
||||
mod lwe_multi_bit_programmable_bootstrapping_noise;
|
||||
mod lwe_programmable_bootstrapping_noise;
|
||||
|
||||
#[allow(clippy::excessive_precision)]
|
||||
pub const NOISE_TEST_PARAMS_4_BITS_NATIVE_U64_132_BITS_GAUSSIAN: ClassicTestParams<u64> =
|
||||
ClassicTestParams {
|
||||
lwe_dimension: LweDimension(841),
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(2048),
|
||||
lwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
|
||||
3.1496674685772435e-06,
|
||||
)),
|
||||
glwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
|
||||
2.845267479601915e-15,
|
||||
)),
|
||||
pbs_base_log: DecompositionBaseLog(22),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
ks_level: DecompositionLevelCount(5),
|
||||
ks_base_log: DecompositionBaseLog(3),
|
||||
pfks_level: DecompositionLevelCount(0),
|
||||
pfks_base_log: DecompositionBaseLog(0),
|
||||
pfks_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(0.0)),
|
||||
cbs_level: DecompositionLevelCount(0),
|
||||
cbs_base_log: DecompositionBaseLog(0),
|
||||
message_modulus_log: MessageModulusLog(4),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
};
|
||||
#[allow(clippy::excessive_precision)]
|
||||
pub const NOISE_TEST_PARAMS_GPU_MULTI_BIT_GROUP_3_4_BITS_NATIVE_U64_132_BITS_GAUSSIAN:
|
||||
MultiBitTestParams<u64> = MultiBitTestParams {
|
||||
input_lwe_dimension: LweDimension(837),
|
||||
lwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
|
||||
3.3747142481837397e-06,
|
||||
)),
|
||||
decomp_base_log: DecompositionBaseLog(21),
|
||||
decomp_level_count: DecompositionLevelCount(1),
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(2048),
|
||||
glwe_noise_distribution: DynamicDistribution::new_gaussian_from_std_dev(StandardDev(
|
||||
2.845267479601915e-15,
|
||||
)),
|
||||
message_modulus_log: MessageModulusLog(4),
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
grouping_factor: LweBskGroupingFactor(3),
|
||||
thread_count: ThreadCount(1),
|
||||
};
|
||||
@@ -734,7 +734,7 @@ impl CudaServerKey {
|
||||
T::from(CudaRadixCiphertext::new(trimmed_ct_list, trimmed_ct_info))
|
||||
}
|
||||
|
||||
pub(crate) fn generate_lookup_table<F>(&self, f: F) -> LookupTableOwned
|
||||
pub fn generate_lookup_table<F>(&self, f: F) -> LookupTableOwned
|
||||
where
|
||||
F: Fn(u64) -> u64,
|
||||
{
|
||||
@@ -826,7 +826,7 @@ impl CudaServerKey {
|
||||
///
|
||||
/// - `streams` __must__ be synchronized to guarantee computation has finished, and inputs must
|
||||
/// not be dropped until streams is synchronised
|
||||
pub(crate) unsafe fn apply_lookup_table_async(
|
||||
pub unsafe fn apply_lookup_table_async(
|
||||
&self,
|
||||
output: &mut CudaRadixCiphertext,
|
||||
input: &CudaRadixCiphertext,
|
||||
@@ -1005,7 +1005,7 @@ impl CudaServerKey {
|
||||
///
|
||||
/// - `stream` __must__ be synchronized to guarantee computation has finished, and inputs must
|
||||
/// not be dropped until stream is synchronised
|
||||
pub unsafe fn apply_many_lookup_table_async(
|
||||
pub(crate) unsafe fn apply_many_lookup_table_async(
|
||||
&self,
|
||||
input: &CudaRadixCiphertext,
|
||||
lut: &ManyLookupTableOwned,
|
||||
|
||||
Reference in New Issue
Block a user