Skip to main content

tfhe/shortint/atomic_pattern/
ks32.rs

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