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