|
| 1 | +//! Module containing primitives pertaining to [`GLWE ciphertext |
| 2 | +//! keyswitch`](`GlweKeyswitchKey#glwe-keyswitch`). |
| 3 | +
|
| 4 | +use crate::core_crypto::algorithms::polynomial_algorithms::*; |
| 5 | +use crate::core_crypto::commons::math::decomposition::{ |
| 6 | + SignedDecomposer, SignedDecomposerNonNative, |
| 7 | +}; |
| 8 | +use crate::core_crypto::commons::numeric::UnsignedInteger; |
| 9 | +use crate::core_crypto::commons::traits::*; |
| 10 | +use crate::core_crypto::entities::*; |
| 11 | + |
| 12 | +/// Keyswitch a [`GLWE ciphertext`](`GlweCiphertext`) encrypted under a |
| 13 | +/// [`GLWE secret key`](`GlweSecretKey`) to another [`GLWE secret key`](`GlweSecretKey`). |
| 14 | +/// |
| 15 | +/// # Formal Definition |
| 16 | +/// |
| 17 | +/// See [`GLWE keyswitch key`](`GlweKeyswitchKey#glwe-keyswitch`). |
| 18 | +/// |
| 19 | +/// # Example |
| 20 | +/// |
| 21 | +/// ``` |
| 22 | +/// use tfhe::core_crypto::prelude::*; |
| 23 | +/// |
| 24 | +/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct |
| 25 | +/// // computations |
| 26 | +/// // Define parameters for GlweKeyswitchKey creation |
| 27 | +/// let input_glwe_dimension = GlweDimension(2); |
| 28 | +/// let poly_size = PolynomialSize(512); |
| 29 | +/// let glwe_noise_distribution = Gaussian::from_dispersion_parameter( |
| 30 | +/// StandardDev(0.00000000000000000000007069849454709433), |
| 31 | +/// 0.0, |
| 32 | +/// ); |
| 33 | +/// let output_glwe_dimension = GlweDimension(1); |
| 34 | +/// let decomp_base_log = DecompositionBaseLog(21); |
| 35 | +/// let decomp_level_count = DecompositionLevelCount(2); |
| 36 | +/// let ciphertext_modulus = CiphertextModulus::new_native(); |
| 37 | +/// let delta = 1 << 59; |
| 38 | +/// |
| 39 | +/// // Create the PRNG |
| 40 | +/// let mut seeder = new_seeder(); |
| 41 | +/// let seeder = seeder.as_mut(); |
| 42 | +/// let mut encryption_generator = |
| 43 | +/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder); |
| 44 | +/// let mut secret_generator = |
| 45 | +/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed()); |
| 46 | +/// |
| 47 | +/// // Create the LweSecretKey |
| 48 | +/// let input_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key( |
| 49 | +/// input_glwe_dimension, |
| 50 | +/// poly_size, |
| 51 | +/// &mut secret_generator, |
| 52 | +/// ); |
| 53 | +/// let output_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key( |
| 54 | +/// output_glwe_dimension, |
| 55 | +/// poly_size, |
| 56 | +/// &mut secret_generator, |
| 57 | +/// ); |
| 58 | +/// |
| 59 | +/// let ksk = allocate_and_generate_new_glwe_keyswitch_key( |
| 60 | +/// &input_glwe_secret_key, |
| 61 | +/// &output_glwe_secret_key, |
| 62 | +/// decomp_base_log, |
| 63 | +/// decomp_level_count, |
| 64 | +/// glwe_noise_distribution, |
| 65 | +/// ciphertext_modulus, |
| 66 | +/// &mut encryption_generator, |
| 67 | +/// ); |
| 68 | +/// |
| 69 | +/// // Create the plaintext |
| 70 | +/// let msg = 3u64; |
| 71 | +/// let plaintext_list = PlaintextList::new(msg * delta, PlaintextCount(poly_size.0)); |
| 72 | +/// |
| 73 | +/// // Create a new GlweCiphertext |
| 74 | +/// let mut input_glwe = GlweCiphertext::new( |
| 75 | +/// 0u64, |
| 76 | +/// input_glwe_dimension.to_glwe_size(), |
| 77 | +/// poly_size, |
| 78 | +/// ciphertext_modulus, |
| 79 | +/// ); |
| 80 | +/// |
| 81 | +/// encrypt_glwe_ciphertext( |
| 82 | +/// &input_glwe_secret_key, |
| 83 | +/// &mut input_glwe, |
| 84 | +/// &plaintext_list, |
| 85 | +/// glwe_noise_distribution, |
| 86 | +/// &mut encryption_generator, |
| 87 | +/// ); |
| 88 | +/// |
| 89 | +/// let mut output_glwe = GlweCiphertext::new( |
| 90 | +/// 0u64, |
| 91 | +/// output_glwe_secret_key.glwe_dimension().to_glwe_size(), |
| 92 | +/// output_glwe_secret_key.polynomial_size(), |
| 93 | +/// ciphertext_modulus, |
| 94 | +/// ); |
| 95 | +/// |
| 96 | +/// keyswitch_glwe_ciphertext(&ksk, &input_glwe, &mut output_glwe); |
| 97 | +/// |
| 98 | +/// // Round and remove encoding |
| 99 | +/// // First create a decomposer working on the high 5 bits corresponding to our encoding. |
| 100 | +/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(5), DecompositionLevelCount(1)); |
| 101 | +/// |
| 102 | +/// let mut output_plaintext_list = PlaintextList::new(0u64, plaintext_list.plaintext_count()); |
| 103 | +/// |
| 104 | +/// decrypt_glwe_ciphertext( |
| 105 | +/// &output_glwe_secret_key, |
| 106 | +/// &output_glwe, |
| 107 | +/// &mut output_plaintext_list, |
| 108 | +/// ); |
| 109 | +/// |
| 110 | +/// // Get the raw vector |
| 111 | +/// let mut cleartext_list = output_plaintext_list.into_container(); |
| 112 | +/// // Remove the encoding |
| 113 | +/// cleartext_list |
| 114 | +/// .iter_mut() |
| 115 | +/// .for_each(|elt| *elt = decomposer.decode_plaintext(*elt)); |
| 116 | +/// // Get the list immutably |
| 117 | +/// let cleartext_list = cleartext_list; |
| 118 | +/// |
| 119 | +/// // Check we recovered the original message for each plaintext we encrypted |
| 120 | +/// cleartext_list.iter().for_each(|&elt| assert_eq!(elt, msg)); |
| 121 | +/// ``` |
| 122 | +pub fn keyswitch_glwe_ciphertext<Scalar, KSKCont, InputCont, OutputCont>( |
| 123 | + glwe_keyswitch_key: &GlweKeyswitchKey<KSKCont>, |
| 124 | + input_glwe_ciphertext: &GlweCiphertext<InputCont>, |
| 125 | + output_glwe_ciphertext: &mut GlweCiphertext<OutputCont>, |
| 126 | +) where |
| 127 | + Scalar: UnsignedInteger, |
| 128 | + KSKCont: Container<Element = Scalar>, |
| 129 | + InputCont: Container<Element = Scalar>, |
| 130 | + OutputCont: ContainerMut<Element = Scalar>, |
| 131 | +{ |
| 132 | + if glwe_keyswitch_key |
| 133 | + .ciphertext_modulus() |
| 134 | + .is_compatible_with_native_modulus() |
| 135 | + { |
| 136 | + keyswitch_glwe_ciphertext_native_mod_compatible( |
| 137 | + glwe_keyswitch_key, |
| 138 | + input_glwe_ciphertext, |
| 139 | + output_glwe_ciphertext, |
| 140 | + ) |
| 141 | + } else { |
| 142 | + keyswitch_glwe_ciphertext_other_mod( |
| 143 | + glwe_keyswitch_key, |
| 144 | + input_glwe_ciphertext, |
| 145 | + output_glwe_ciphertext, |
| 146 | + ) |
| 147 | + } |
| 148 | +} |
| 149 | + |
| 150 | +pub fn keyswitch_glwe_ciphertext_native_mod_compatible<Scalar, KSKCont, InputCont, OutputCont>( |
| 151 | + glwe_keyswitch_key: &GlweKeyswitchKey<KSKCont>, |
| 152 | + input_glwe_ciphertext: &GlweCiphertext<InputCont>, |
| 153 | + output_glwe_ciphertext: &mut GlweCiphertext<OutputCont>, |
| 154 | +) where |
| 155 | + Scalar: UnsignedInteger, |
| 156 | + KSKCont: Container<Element = Scalar>, |
| 157 | + InputCont: Container<Element = Scalar>, |
| 158 | + OutputCont: ContainerMut<Element = Scalar>, |
| 159 | +{ |
| 160 | + assert!( |
| 161 | + glwe_keyswitch_key.input_key_glwe_dimension() |
| 162 | + == input_glwe_ciphertext.glwe_size().to_glwe_dimension(), |
| 163 | + "Mismatched input GlweDimension. \ |
| 164 | + GlweKeyswitchKey input GlweDimension: {:?}, input GlweCiphertext GlweDimension {:?}.", |
| 165 | + glwe_keyswitch_key.input_key_glwe_dimension(), |
| 166 | + input_glwe_ciphertext.glwe_size().to_glwe_dimension(), |
| 167 | + ); |
| 168 | + assert!( |
| 169 | + glwe_keyswitch_key.output_key_glwe_dimension() |
| 170 | + == output_glwe_ciphertext.glwe_size().to_glwe_dimension(), |
| 171 | + "Mismatched output GlweDimension. \ |
| 172 | + GlweKeyswitchKey output GlweDimension: {:?}, output GlweCiphertext GlweDimension {:?}.", |
| 173 | + glwe_keyswitch_key.output_key_glwe_dimension(), |
| 174 | + output_glwe_ciphertext.glwe_size().to_glwe_dimension(), |
| 175 | + ); |
| 176 | + assert!( |
| 177 | + glwe_keyswitch_key.polynomial_size() == input_glwe_ciphertext.polynomial_size(), |
| 178 | + "Mismatched input PolynomialSize. \ |
| 179 | + GlweKeyswithcKey input PolynomialSize: {:?}, input GlweCiphertext PolynomialSize {:?}.", |
| 180 | + glwe_keyswitch_key.polynomial_size(), |
| 181 | + input_glwe_ciphertext.polynomial_size(), |
| 182 | + ); |
| 183 | + assert!( |
| 184 | + glwe_keyswitch_key.polynomial_size() == output_glwe_ciphertext.polynomial_size(), |
| 185 | + "Mismatched output PolynomialSize. \ |
| 186 | + GlweKeyswitchKey output PolynomialSize: {:?}, output GlweCiphertext PolynomialSize {:?}.", |
| 187 | + glwe_keyswitch_key.polynomial_size(), |
| 188 | + output_glwe_ciphertext.polynomial_size(), |
| 189 | + ); |
| 190 | + assert!(glwe_keyswitch_key |
| 191 | + .ciphertext_modulus() |
| 192 | + .is_compatible_with_native_modulus()); |
| 193 | + |
| 194 | + // Clear the output ciphertext, as it will get updated gradually |
| 195 | + output_glwe_ciphertext.as_mut().fill(Scalar::ZERO); |
| 196 | + |
| 197 | + // Copy the input body to the output ciphertext |
| 198 | + polynomial_wrapping_add_assign( |
| 199 | + &mut output_glwe_ciphertext.get_mut_body().as_mut_polynomial(), |
| 200 | + &input_glwe_ciphertext.get_body().as_polynomial(), |
| 201 | + ); |
| 202 | + |
| 203 | + // We instantiate a decomposer |
| 204 | + let decomposer = SignedDecomposer::new( |
| 205 | + glwe_keyswitch_key.decomposition_base_log(), |
| 206 | + glwe_keyswitch_key.decomposition_level_count(), |
| 207 | + ); |
| 208 | + |
| 209 | + for (keyswitch_key_block, input_mask_element) in glwe_keyswitch_key |
| 210 | + .iter() |
| 211 | + .zip(input_glwe_ciphertext.get_mask().as_polynomial_list().iter()) |
| 212 | + { |
| 213 | + let mut decomposition_iter = decomposer.decompose_slice(input_mask_element.as_ref()); |
| 214 | + // loop over the number of levels |
| 215 | + for level_key_ciphertext in keyswitch_key_block.iter() { |
| 216 | + let decomposed = decomposition_iter.next_term().unwrap(); |
| 217 | + polynomial_list_wrapping_sub_scalar_mul_assign( |
| 218 | + &mut output_glwe_ciphertext.as_mut_polynomial_list(), |
| 219 | + &level_key_ciphertext.as_polynomial_list(), |
| 220 | + &Polynomial::from_container(decomposed.as_slice()), |
| 221 | + ); |
| 222 | + } |
| 223 | + } |
| 224 | +} |
| 225 | + |
| 226 | +pub fn keyswitch_glwe_ciphertext_other_mod<Scalar, KSKCont, InputCont, OutputCont>( |
| 227 | + glwe_keyswitch_key: &GlweKeyswitchKey<KSKCont>, |
| 228 | + input_glwe_ciphertext: &GlweCiphertext<InputCont>, |
| 229 | + output_glwe_ciphertext: &mut GlweCiphertext<OutputCont>, |
| 230 | +) where |
| 231 | + Scalar: UnsignedInteger, |
| 232 | + KSKCont: Container<Element = Scalar>, |
| 233 | + InputCont: Container<Element = Scalar>, |
| 234 | + OutputCont: ContainerMut<Element = Scalar>, |
| 235 | +{ |
| 236 | + assert!( |
| 237 | + glwe_keyswitch_key.input_key_glwe_dimension() |
| 238 | + == input_glwe_ciphertext.glwe_size().to_glwe_dimension(), |
| 239 | + "Mismatched input GlweDimension. \ |
| 240 | + GlweKeyswitchKey input GlweDimension: {:?}, input GlweCiphertext GlweDimension {:?}.", |
| 241 | + glwe_keyswitch_key.input_key_glwe_dimension(), |
| 242 | + input_glwe_ciphertext.glwe_size().to_glwe_dimension(), |
| 243 | + ); |
| 244 | + assert!( |
| 245 | + glwe_keyswitch_key.output_key_glwe_dimension() |
| 246 | + == output_glwe_ciphertext.glwe_size().to_glwe_dimension(), |
| 247 | + "Mismatched output GlweDimension. \ |
| 248 | + GlweKeyswitchKey output GlweDimension: {:?}, output GlweCiphertext GlweDimension {:?}.", |
| 249 | + glwe_keyswitch_key.output_key_glwe_dimension(), |
| 250 | + output_glwe_ciphertext.glwe_size().to_glwe_dimension(), |
| 251 | + ); |
| 252 | + assert!( |
| 253 | + glwe_keyswitch_key.polynomial_size() == input_glwe_ciphertext.polynomial_size(), |
| 254 | + "Mismatched input PolynomialSize. \ |
| 255 | + GlweKeyswithcKey input PolynomialSize: {:?}, input GlweCiphertext PolynomialSize {:?}.", |
| 256 | + glwe_keyswitch_key.polynomial_size(), |
| 257 | + input_glwe_ciphertext.polynomial_size(), |
| 258 | + ); |
| 259 | + assert!( |
| 260 | + glwe_keyswitch_key.polynomial_size() == output_glwe_ciphertext.polynomial_size(), |
| 261 | + "Mismatched output PolynomialSize. \ |
| 262 | + GlweKeyswitchKey output PolynomialSize: {:?}, output GlweCiphertext PolynomialSize {:?}.", |
| 263 | + glwe_keyswitch_key.polynomial_size(), |
| 264 | + output_glwe_ciphertext.polynomial_size(), |
| 265 | + ); |
| 266 | + let ciphertext_modulus = glwe_keyswitch_key.ciphertext_modulus(); |
| 267 | + assert!(!ciphertext_modulus.is_compatible_with_native_modulus()); |
| 268 | + |
| 269 | + // Clear the output ciphertext, as it will get updated gradually |
| 270 | + output_glwe_ciphertext.as_mut().fill(Scalar::ZERO); |
| 271 | + |
| 272 | + // Copy the input body to the output ciphertext (no need to use non native addition here) |
| 273 | + polynomial_wrapping_add_assign( |
| 274 | + &mut output_glwe_ciphertext.get_mut_body().as_mut_polynomial(), |
| 275 | + &input_glwe_ciphertext.get_body().as_polynomial(), |
| 276 | + ); |
| 277 | + |
| 278 | + // We instantiate a decomposer |
| 279 | + let decomposer = SignedDecomposerNonNative::new( |
| 280 | + glwe_keyswitch_key.decomposition_base_log(), |
| 281 | + glwe_keyswitch_key.decomposition_level_count(), |
| 282 | + ciphertext_modulus, |
| 283 | + ); |
| 284 | + |
| 285 | + let mut scalar_poly = Polynomial::new(Scalar::ZERO, input_glwe_ciphertext.polynomial_size()); |
| 286 | + |
| 287 | + for (keyswitch_key_block, input_mask_element) in glwe_keyswitch_key |
| 288 | + .iter() |
| 289 | + .zip(input_glwe_ciphertext.get_mask().as_polynomial_list().iter()) |
| 290 | + { |
| 291 | + let mut decomposition_iter = decomposer.decompose_slice(input_mask_element.as_ref()); |
| 292 | + // loop over the number of levels |
| 293 | + for level_key_ciphertext in keyswitch_key_block.iter() { |
| 294 | + let decomposed = decomposition_iter.next_term().unwrap(); |
| 295 | + decomposed.modular_value(scalar_poly.as_mut()); |
| 296 | + polynomial_list_wrapping_sub_scalar_mul_assign_custom_mod( |
| 297 | + &mut output_glwe_ciphertext.as_mut_polynomial_list(), |
| 298 | + &level_key_ciphertext.as_polynomial_list(), |
| 299 | + &scalar_poly, |
| 300 | + ciphertext_modulus.get_custom_modulus().cast_into(), |
| 301 | + ); |
| 302 | + } |
| 303 | + } |
| 304 | +} |
0 commit comments