tfhe/shortint/atomic_pattern/
ks32.rs

1use serde::{Deserialize, Serialize};
2use tfhe_csprng::seeders::Seed;
3use tfhe_versionable::Versionize;
4
5use super::{
6    apply_blind_rotate, apply_programmable_bootstrap, AtomicPattern, AtomicPatternKind,
7    AtomicPatternMut,
8};
9use crate::conformance::ParameterSetConformant;
10use crate::core_crypto::prelude::{
11    allocate_and_generate_new_lwe_keyswitch_key, extract_lwe_sample_from_glwe_ciphertext,
12    keyswitch_lwe_ciphertext_with_scalar_change, CiphertextModulus as CoreCiphertextModulus,
13    LweCiphertext, LweCiphertextOwned, LweDimension, LweKeyswitchKeyOwned, LweSecretKey,
14    MonomialDegree, MsDecompressionType,
15};
16use crate::shortint::backward_compatibility::atomic_pattern::KS32AtomicPatternServerKeyVersions;
17use crate::shortint::ciphertext::{CompressedModulusSwitchedCiphertext, Degree, NoiseLevel};
18use crate::shortint::engine::ShortintEngine;
19use crate::shortint::oprf::generate_pseudo_random_from_pbs;
20use crate::shortint::parameters::KeySwitch32PBSParameters;
21use crate::shortint::server_key::{
22    decompress_and_apply_lookup_table, switch_modulus_and_compress, LookupTableOwned,
23    LookupTableSize, ManyLookupTableOwned, ShortintBootstrappingKey,
24};
25use crate::shortint::{Ciphertext, CiphertextModulus, ClientKey};
26
27/// The definition of the server key elements used in the
28/// [`KeySwitch32`](AtomicPatternKind::KeySwitch32) atomic pattern
29#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)]
30#[versionize(KS32AtomicPatternServerKeyVersions)]
31pub struct KS32AtomicPatternServerKey {
32    pub key_switching_key: LweKeyswitchKeyOwned<u32>,
33    pub bootstrapping_key: ShortintBootstrappingKey<u32>,
34    pub ciphertext_modulus: CiphertextModulus,
35}
36
37impl ParameterSetConformant for KS32AtomicPatternServerKey {
38    type ParameterSet = KeySwitch32PBSParameters;
39
40    fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
41        let Self {
42            key_switching_key,
43            bootstrapping_key,
44            ciphertext_modulus,
45        } = self;
46
47        let pbs_conformance_params = parameter_set.into();
48
49        let pbs_key_ok = bootstrapping_key.is_conformant(&pbs_conformance_params);
50
51        let ks_conformance_params = parameter_set.into();
52
53        let ks_key_ok = key_switching_key.is_conformant(&ks_conformance_params);
54
55        *ciphertext_modulus == pbs_conformance_params.ciphertext_modulus && pbs_key_ok && ks_key_ok
56    }
57}
58
59impl KS32AtomicPatternServerKey {
60    pub fn new(cks: &ClientKey, engine: &mut ShortintEngine) -> Self {
61        let params = &cks.parameters;
62
63        let pbs_params = params.ks32_parameters().unwrap();
64
65        let in_key = LweSecretKey::from_container(
66            cks.small_lwe_secret_key()
67                .as_ref()
68                .iter()
69                .copied()
70                .map(|x| x as u32)
71                .collect::<Vec<_>>(),
72        );
73
74        let out_key = &cks.glwe_secret_key;
75
76        let bootstrapping_key_base =
77            engine.new_bootstrapping_key_ks32(pbs_params, &in_key, out_key);
78
79        // Creation of the key switching key
80        let key_switching_key = allocate_and_generate_new_lwe_keyswitch_key(
81            &cks.large_lwe_secret_key(),
82            &in_key,
83            params.ks_base_log(),
84            params.ks_level(),
85            pbs_params.lwe_noise_distribution(),
86            pbs_params.post_keyswitch_ciphertext_modulus(),
87            &mut engine.encryption_generator,
88        );
89
90        Self::from_raw_parts(
91            key_switching_key,
92            bootstrapping_key_base,
93            params.ciphertext_modulus(),
94        )
95    }
96
97    pub fn from_raw_parts(
98        key_switching_key: LweKeyswitchKeyOwned<u32>,
99        bootstrapping_key: ShortintBootstrappingKey<u32>,
100        ciphertext_modulus: CiphertextModulus,
101    ) -> Self {
102        assert_eq!(
103            key_switching_key.input_key_lwe_dimension(),
104            bootstrapping_key.output_lwe_dimension(),
105            "Mismatch between the input LweKeyswitchKey LweDimension ({:?}) \
106            and the ShortintBootstrappingKey output LweDimension ({:?})",
107            key_switching_key.input_key_lwe_dimension(),
108            bootstrapping_key.output_lwe_dimension()
109        );
110
111        assert_eq!(
112            key_switching_key.output_key_lwe_dimension(),
113            bootstrapping_key.input_lwe_dimension(),
114            "Mismatch between the output LweKeyswitchKey LweDimension ({:?}) \
115            and the ShortintBootstrappingKey input LweDimension ({:?})",
116            key_switching_key.output_key_lwe_dimension(),
117            bootstrapping_key.input_lwe_dimension()
118        );
119
120        Self {
121            key_switching_key,
122            bootstrapping_key,
123            ciphertext_modulus,
124        }
125    }
126
127    pub fn intermediate_lwe_dimension(&self) -> LweDimension {
128        self.key_switching_key.output_key_lwe_dimension()
129    }
130}
131
132impl AtomicPattern for KS32AtomicPatternServerKey {
133    fn ciphertext_lwe_dimension(&self) -> LweDimension {
134        self.key_switching_key.input_key_lwe_dimension()
135    }
136
137    fn ciphertext_modulus(&self) -> CiphertextModulus {
138        self.ciphertext_modulus
139    }
140
141    fn ciphertext_decompression_method(&self) -> MsDecompressionType {
142        match &self.bootstrapping_key {
143            ShortintBootstrappingKey::Classic { .. } => MsDecompressionType::ClassicPbs,
144            ShortintBootstrappingKey::MultiBit { fourier_bsk, .. } => {
145                MsDecompressionType::MultiBitPbs(fourier_bsk.grouping_factor())
146            }
147        }
148    }
149
150    fn apply_lookup_table_assign(&self, ct: &mut Ciphertext, acc: &LookupTableOwned) {
151        ShortintEngine::with_thread_local_mut(|engine| {
152            let (mut ciphertext_buffer, buffers) = engine.get_buffers(
153                self.intermediate_lwe_dimension(),
154                self.intermediate_ciphertext_modulus(),
155            );
156
157            keyswitch_lwe_ciphertext_with_scalar_change(
158                &self.key_switching_key,
159                &ct.ct,
160                &mut ciphertext_buffer,
161            );
162
163            apply_programmable_bootstrap(
164                &self.bootstrapping_key,
165                &ciphertext_buffer,
166                &mut ct.ct,
167                &acc.acc,
168                buffers,
169            );
170        });
171    }
172
173    fn apply_many_lookup_table(
174        &self,
175        ct: &Ciphertext,
176        acc: &ManyLookupTableOwned,
177    ) -> Vec<Ciphertext> {
178        self.keyswitch_programmable_bootstrap_many_lut(ct, acc)
179    }
180
181    fn lookup_table_size(&self) -> LookupTableSize {
182        LookupTableSize::new(
183            self.bootstrapping_key.glwe_size(),
184            self.bootstrapping_key.polynomial_size(),
185        )
186    }
187
188    fn kind(&self) -> AtomicPatternKind {
189        AtomicPatternKind::KeySwitch32
190    }
191
192    fn deterministic_execution(&self) -> bool {
193        self.bootstrapping_key.deterministic_pbs_execution()
194    }
195
196    fn generate_oblivious_pseudo_random(
197        &self,
198        seed: Seed,
199        random_bits_count: u64,
200        full_bits_count: u64,
201    ) -> (LweCiphertextOwned<u64>, Degree) {
202        generate_pseudo_random_from_pbs(
203            &self.bootstrapping_key,
204            seed,
205            random_bits_count,
206            full_bits_count,
207            self.ciphertext_modulus(),
208        )
209    }
210
211    fn switch_modulus_and_compress(&self, ct: &Ciphertext) -> CompressedModulusSwitchedCiphertext {
212        let compressed_modulus_switched_lwe_ciphertext =
213            ShortintEngine::with_thread_local_mut(|engine| {
214                let (mut ciphertext_buffer, _) = engine.get_buffers(
215                    self.intermediate_lwe_dimension(),
216                    self.intermediate_ciphertext_modulus(),
217                );
218
219                keyswitch_lwe_ciphertext_with_scalar_change(
220                    &self.key_switching_key,
221                    &ct.ct,
222                    &mut ciphertext_buffer,
223                );
224                switch_modulus_and_compress(ciphertext_buffer.as_view(), &self.bootstrapping_key)
225            });
226
227        CompressedModulusSwitchedCiphertext {
228            compressed_modulus_switched_lwe_ciphertext,
229            degree: ct.degree,
230            message_modulus: ct.message_modulus,
231            carry_modulus: ct.carry_modulus,
232            atomic_pattern: ct.atomic_pattern,
233        }
234    }
235
236    fn decompress_and_apply_lookup_table(
237        &self,
238        compressed_ct: &CompressedModulusSwitchedCiphertext,
239        lut: &LookupTableOwned,
240    ) -> Ciphertext {
241        let mut output = LweCiphertext::new(
242            0,
243            self.ciphertext_lwe_dimension().to_lwe_size(),
244            self.ciphertext_modulus(),
245        );
246
247        ShortintEngine::with_thread_local_mut(|engine| {
248            let buffers = engine.get_computation_buffers();
249            decompress_and_apply_lookup_table(
250                compressed_ct,
251                &lut.acc,
252                &self.bootstrapping_key,
253                &mut output.as_mut_view(),
254                buffers,
255            );
256        });
257
258        Ciphertext::new(
259            output,
260            lut.degree,
261            NoiseLevel::NOMINAL,
262            compressed_ct.message_modulus,
263            compressed_ct.carry_modulus,
264            compressed_ct.atomic_pattern,
265        )
266    }
267}
268
269impl AtomicPatternMut for KS32AtomicPatternServerKey {
270    fn set_deterministic_execution(&mut self, new_deterministic_execution: bool) {
271        self.bootstrapping_key
272            .set_deterministic_pbs_execution(new_deterministic_execution)
273    }
274}
275
276impl KS32AtomicPatternServerKey {
277    pub(crate) fn keyswitch_programmable_bootstrap_many_lut(
278        &self,
279        ct: &Ciphertext,
280        lut: &ManyLookupTableOwned,
281    ) -> Vec<Ciphertext> {
282        let mut acc = lut.acc.clone();
283
284        ShortintEngine::with_thread_local_mut(|engine| {
285            let (mut ciphertext_buffer, buffers) = engine.get_buffers(
286                self.intermediate_lwe_dimension(),
287                self.intermediate_ciphertext_modulus(),
288            );
289
290            // Compute a key switch
291            keyswitch_lwe_ciphertext_with_scalar_change(
292                &self.key_switching_key,
293                &ct.ct,
294                &mut ciphertext_buffer,
295            );
296
297            apply_blind_rotate(
298                &self.bootstrapping_key,
299                &ciphertext_buffer.as_view(),
300                &mut acc,
301                buffers,
302            );
303        });
304
305        // The accumulator has been rotated, we can now proceed with the various sample extractions
306        let function_count = lut.function_count();
307        let mut outputs = Vec::with_capacity(function_count);
308
309        for (fn_idx, output_degree) in lut.per_function_output_degree.iter().enumerate() {
310            let monomial_degree = MonomialDegree(fn_idx * lut.sample_extraction_stride);
311            let mut output_shortint_ct = ct.clone();
312
313            extract_lwe_sample_from_glwe_ciphertext(
314                &acc,
315                &mut output_shortint_ct.ct,
316                monomial_degree,
317            );
318
319            output_shortint_ct.degree = *output_degree;
320            output_shortint_ct.set_noise_level_to_nominal();
321            outputs.push(output_shortint_ct);
322        }
323
324        outputs
325    }
326
327    fn intermediate_ciphertext_modulus(&self) -> CoreCiphertextModulus<u32> {
328        self.key_switching_key.ciphertext_modulus()
329    }
330}