|
| 1 | +use shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::{ |
| 2 | + V0_11_PARAM_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_ZKV1, |
| 3 | + V0_11_PARAM_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_ZKV1, |
| 4 | +}; |
| 5 | +use shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::{ |
| 6 | + V0_11_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_ZKV1, |
| 7 | + V0_11_PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_ZKV1, |
| 8 | +}; |
| 9 | +use shortint::parameters::{CompactPublicKeyEncryptionParameters, ShortintKeySwitchingParameters}; |
| 10 | +use shortint::ClassicPBSParameters; |
| 11 | + |
| 12 | +use crate::core_crypto::algorithms::lwe_encryption::decrypt_lwe_ciphertext; |
| 13 | +use crate::core_crypto::algorithms::test::noise_distribution::lwe_encryption_noise::lwe_compact_public_key_encryption_expected_variance; |
| 14 | +use crate::core_crypto::commons::math::random::DynamicDistribution; |
| 15 | +use crate::core_crypto::commons::test_tools::{variance, variance_confidence_interval}; |
| 16 | +use crate::prelude::*; |
| 17 | +use crate::shortint::parameters::classic::tuniform::p_fail_2_minus_64::ks_pbs::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; |
| 18 | +use crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; |
| 19 | +use crate::shortint::parameters::compact_public_key_only::CompactCiphertextListExpansionKind; |
| 20 | +use crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; |
| 21 | +use crate::shortint::parameters::ShortintParameterSet; |
| 22 | +use crate::*; |
| 23 | + |
| 24 | +#[test] |
| 25 | +fn test_noise_check_secret_key_encryption_noise_tuniform() { |
| 26 | + let params_as_shortint_parameter_set = ShortintParameterSet::new_pbs_param_set( |
| 27 | + PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64.into(), |
| 28 | + ); |
| 29 | + |
| 30 | + let modulus_as_f64 = if params_as_shortint_parameter_set |
| 31 | + .ciphertext_modulus() |
| 32 | + .is_native_modulus() |
| 33 | + { |
| 34 | + 2.0f64.powi(u64::BITS as i32) |
| 35 | + } else { |
| 36 | + params_as_shortint_parameter_set |
| 37 | + .ciphertext_modulus() |
| 38 | + .get_custom_modulus() as f64 |
| 39 | + }; |
| 40 | + |
| 41 | + let encryption_noise = params_as_shortint_parameter_set.encryption_noise_distribution(); |
| 42 | + let (inclusive_min_val, inclusive_max_val, expected_variance) = match encryption_noise { |
| 43 | + DynamicDistribution::Gaussian(_gaussian) => { |
| 44 | + panic!("This test is written for TUniform, wrong parameter set used") |
| 45 | + } |
| 46 | + DynamicDistribution::TUniform(tuniform) => ( |
| 47 | + tuniform.min_value_inclusive(), |
| 48 | + tuniform.max_value_inclusive(), |
| 49 | + tuniform.variance(modulus_as_f64), |
| 50 | + ), |
| 51 | + }; |
| 52 | + |
| 53 | + let expected_encryption_noise = match params_as_shortint_parameter_set.encryption_key_choice() { |
| 54 | + shortint::EncryptionKeyChoice::Big => { |
| 55 | + params_as_shortint_parameter_set.glwe_noise_distribution() |
| 56 | + } |
| 57 | + shortint::EncryptionKeyChoice::Small => { |
| 58 | + params_as_shortint_parameter_set.lwe_noise_distribution() |
| 59 | + } |
| 60 | + }; |
| 61 | + |
| 62 | + assert_eq!(encryption_noise, expected_encryption_noise); |
| 63 | + |
| 64 | + let config = |
| 65 | + ConfigBuilder::with_custom_parameters(PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64).build(); |
| 66 | + |
| 67 | + const NB_TEST: usize = 16000; |
| 68 | + |
| 69 | + let mut noise_samples = Vec::with_capacity(NB_TEST); |
| 70 | + for _ in 0..NB_TEST { |
| 71 | + let cks = ClientKey::generate(config); |
| 72 | + let encrypted = FheUint2::encrypt(0u32, &cks); |
| 73 | + |
| 74 | + // Drop to lower level APIs to get to the noise |
| 75 | + let integer_key = cks.into_raw_parts().0; |
| 76 | + let shortint_key: &crate::shortint::ClientKey = integer_key.as_ref(); |
| 77 | + |
| 78 | + let radix = encrypted.into_raw_parts().0; |
| 79 | + assert!(radix.blocks.len() == 1); |
| 80 | + let shortint_block = radix.blocks.into_iter().next().unwrap(); |
| 81 | + |
| 82 | + let noise = shortint_key.decrypt_no_decode(&shortint_block).0; |
| 83 | + let signed_noise: i64 = noise as i64; |
| 84 | + |
| 85 | + assert!( |
| 86 | + signed_noise >= inclusive_min_val, |
| 87 | + "{signed_noise} is not >= {inclusive_min_val}" |
| 88 | + ); |
| 89 | + |
| 90 | + assert!( |
| 91 | + signed_noise <= inclusive_max_val, |
| 92 | + "{signed_noise} is not <= {inclusive_max_val}" |
| 93 | + ); |
| 94 | + |
| 95 | + // Rescale |
| 96 | + noise_samples.push(signed_noise as f64 / modulus_as_f64); |
| 97 | + } |
| 98 | + |
| 99 | + let measured_variance = variance(&noise_samples); |
| 100 | + let measured_confidence_interval = |
| 101 | + variance_confidence_interval(noise_samples.len() as f64, measured_variance, 0.999); |
| 102 | + |
| 103 | + // For --no-capture inspection |
| 104 | + println!("measured_variance={measured_variance:?}"); |
| 105 | + println!("expected_variance={expected_variance:?}"); |
| 106 | + println!( |
| 107 | + "lower_bound={:?}", |
| 108 | + measured_confidence_interval.lower_bound() |
| 109 | + ); |
| 110 | + println!( |
| 111 | + "upper_bound={:?}", |
| 112 | + measured_confidence_interval.upper_bound() |
| 113 | + ); |
| 114 | + |
| 115 | + assert!(measured_confidence_interval.variance_is_in_interval(expected_variance)); |
| 116 | +} |
| 117 | + |
| 118 | +#[test] |
| 119 | +fn test_noise_check_compact_public_key_encryption_noise_tuniform() { |
| 120 | + noise_check_compact_public_key_encryption_noise_tuniform( |
| 121 | + V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, |
| 122 | + V0_11_PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, |
| 123 | + PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, |
| 124 | + ) |
| 125 | +} |
| 126 | + |
| 127 | +#[test] |
| 128 | +fn test_noise_check_compact_public_key_encryption_noise_tuniform_zkv1_small() { |
| 129 | + noise_check_compact_public_key_encryption_noise_tuniform( |
| 130 | + V0_11_PARAM_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_ZKV1, |
| 131 | + V0_11_PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_ZKV1, |
| 132 | + PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, |
| 133 | + ) |
| 134 | +} |
| 135 | + |
| 136 | +#[test] |
| 137 | +fn test_noise_check_compact_public_key_encryption_noise_tuniform_zkv1_big() { |
| 138 | + noise_check_compact_public_key_encryption_noise_tuniform( |
| 139 | + V0_11_PARAM_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_ZKV1, |
| 140 | + V0_11_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_ZKV1, |
| 141 | + PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, |
| 142 | + ) |
| 143 | +} |
| 144 | + |
| 145 | +fn noise_check_compact_public_key_encryption_noise_tuniform( |
| 146 | + mut cpke_params: CompactPublicKeyEncryptionParameters, |
| 147 | + ksk_params: ShortintKeySwitchingParameters, |
| 148 | + block_params: ClassicPBSParameters, |
| 149 | +) { |
| 150 | + // Hack to avoid server key needs and get the ciphertext directly |
| 151 | + cpke_params.expansion_kind = |
| 152 | + CompactCiphertextListExpansionKind::NoCasting(block_params.encryption_key_choice.into()); |
| 153 | + |
| 154 | + let modulus_as_f64 = if cpke_params.ciphertext_modulus.is_native_modulus() { |
| 155 | + 2.0f64.powi(u64::BITS as i32) |
| 156 | + } else { |
| 157 | + cpke_params.ciphertext_modulus.get_custom_modulus() as f64 |
| 158 | + }; |
| 159 | + |
| 160 | + let encryption_noise = cpke_params.encryption_noise_distribution; |
| 161 | + let encryption_variance = match encryption_noise { |
| 162 | + DynamicDistribution::Gaussian(_gaussian) => { |
| 163 | + panic!("This test is written for TUniform, wrong parameter set used") |
| 164 | + } |
| 165 | + DynamicDistribution::TUniform(tuniform) => tuniform.variance(modulus_as_f64), |
| 166 | + }; |
| 167 | + |
| 168 | + let expected_variance = lwe_compact_public_key_encryption_expected_variance( |
| 169 | + encryption_variance, |
| 170 | + cpke_params.encryption_lwe_dimension, |
| 171 | + ); |
| 172 | + |
| 173 | + let config = ConfigBuilder::with_custom_parameters(block_params) |
| 174 | + .use_dedicated_compact_public_key_parameters((cpke_params, ksk_params)) |
| 175 | + .build(); |
| 176 | + |
| 177 | + const NB_TEST: usize = 16000; |
| 178 | + |
| 179 | + let mut noise_samples = Vec::with_capacity(NB_TEST); |
| 180 | + for _ in 0..NB_TEST { |
| 181 | + let cks = ClientKey::generate(config); |
| 182 | + let cpk = CompactPublicKey::new(&cks); |
| 183 | + |
| 184 | + let mut builder = CompactCiphertextList::builder(&cpk); |
| 185 | + builder |
| 186 | + .push_with_num_bits(0u32, cpke_params.message_modulus.0.ilog2() as usize) |
| 187 | + .unwrap(); |
| 188 | + let list = builder.build(); |
| 189 | + let expanded = list.expand().unwrap(); |
| 190 | + let encrypted: FheUint2 = expanded.get(0).unwrap().unwrap(); |
| 191 | + |
| 192 | + // Drop to lower level APIs to get to the noise |
| 193 | + let integer_key = cks.into_raw_parts().1.unwrap().0; |
| 194 | + let shortint_key = integer_key.into_raw_parts(); |
| 195 | + let core_key = shortint_key.into_raw_parts().0; |
| 196 | + |
| 197 | + let radix = encrypted.into_raw_parts().0; |
| 198 | + assert!(radix.blocks.len() == 1); |
| 199 | + let shortint_block = radix.blocks.into_iter().next().unwrap(); |
| 200 | + let lwe_ct = shortint_block.ct; |
| 201 | + |
| 202 | + // This is directly the noise as we encrypted a 0 |
| 203 | + let noise = decrypt_lwe_ciphertext(&core_key, &lwe_ct).0; |
| 204 | + let signed_noise: i64 = noise as i64; |
| 205 | + |
| 206 | + // Rescale |
| 207 | + noise_samples.push(signed_noise as f64 / modulus_as_f64); |
| 208 | + } |
| 209 | + |
| 210 | + let measured_variance = variance(&noise_samples); |
| 211 | + let measured_confidence_interval = |
| 212 | + variance_confidence_interval(noise_samples.len() as f64, measured_variance, 0.999); |
| 213 | + |
| 214 | + // For --no-capture inspection |
| 215 | + println!("measured_variance={measured_variance:?}"); |
| 216 | + println!("expected_variance={expected_variance:?}"); |
| 217 | + println!( |
| 218 | + "lower_bound={:?}", |
| 219 | + measured_confidence_interval.lower_bound() |
| 220 | + ); |
| 221 | + println!( |
| 222 | + "upper_bound={:?}", |
| 223 | + measured_confidence_interval.upper_bound() |
| 224 | + ); |
| 225 | + |
| 226 | + assert!(measured_confidence_interval.variance_is_in_interval(expected_variance)); |
| 227 | +} |
0 commit comments