tfhe/shortint/atomic_pattern/
ks32.rs1use 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#[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 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 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 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}