Skip to main content

tfhe/shortint/engine/
client_side.rs

1//! All the `ShortintEngine` method related to client side (encrypt / decrypt)
2
3use super::ShortintEngine;
4use crate::core_crypto::algorithms::*;
5use crate::core_crypto::commons::math::random::{Distribution, RandomGenerable};
6use crate::core_crypto::entities::*;
7use crate::shortint::ciphertext::{Degree, NoiseLevel};
8use crate::shortint::client_key::atomic_pattern::{
9    AtomicPatternClientKey, EncryptionAtomicPattern, StandardAtomicPatternClientKey,
10};
11use crate::shortint::client_key::GenericClientKey;
12use crate::shortint::parameters::{CarryModulus, MessageModulus, ModulusSwitchType};
13use crate::shortint::{
14    Ciphertext, ClassicPBSParameters, ClientKey, CompressedCiphertext, MaxNoiseLevel, PaddingBit,
15    ShortintEncoding, ShortintParameterSet,
16};
17
18impl ShortintEngine {
19    pub fn new_client_key<P>(&mut self, parameters: P) -> ClientKey
20    where
21        P: TryInto<ShortintParameterSet>,
22        <P as TryInto<ShortintParameterSet>>::Error: std::fmt::Debug,
23    {
24        let shortint_params: ShortintParameterSet = parameters.try_into().unwrap();
25
26        let atomic_pattern = if let Some(wopbs_params) = shortint_params.wopbs_parameters() {
27            // TODO
28            // Manually manage the wopbs only case as a workaround pending wopbs rework
29            // WOPBS used for PBS have no known failure probability at the moment, putting 1.0 for
30            // now
31            let pbs_params = shortint_params.pbs_parameters().unwrap_or_else(|| {
32                ClassicPBSParameters {
33                    lwe_dimension: wopbs_params.lwe_dimension,
34                    glwe_dimension: wopbs_params.glwe_dimension,
35                    polynomial_size: wopbs_params.polynomial_size,
36                    lwe_noise_distribution: wopbs_params.lwe_noise_distribution,
37                    glwe_noise_distribution: wopbs_params.glwe_noise_distribution,
38                    pbs_base_log: wopbs_params.pbs_base_log,
39                    pbs_level: wopbs_params.pbs_level,
40                    ks_base_log: wopbs_params.ks_base_log,
41                    ks_level: wopbs_params.ks_level,
42                    message_modulus: wopbs_params.message_modulus,
43                    carry_modulus: wopbs_params.carry_modulus,
44                    max_noise_level: MaxNoiseLevel::from_msg_carry_modulus(
45                        wopbs_params.message_modulus,
46                        wopbs_params.carry_modulus,
47                    ),
48                    log2_p_fail: 1.0,
49                    ciphertext_modulus: wopbs_params.ciphertext_modulus,
50                    encryption_key_choice: wopbs_params.encryption_key_choice,
51                    modulus_switch_noise_reduction_params: ModulusSwitchType::Standard,
52                }
53                .into()
54            });
55
56            let std_ck = StandardAtomicPatternClientKey::new_with_engine(
57                pbs_params,
58                Some(wopbs_params),
59                self,
60            );
61            AtomicPatternClientKey::Standard(std_ck)
62        } else if let Some(ap_params) = shortint_params.ap_parameters() {
63            AtomicPatternClientKey::new_with_engine(ap_params, self)
64        } else {
65            panic!("Invalid parameters, missing Atomic Pattern or WOPBS params")
66        };
67
68        ClientKey { atomic_pattern }
69    }
70
71    pub fn encrypt<AP: EncryptionAtomicPattern>(
72        &mut self,
73        client_key: &GenericClientKey<AP>,
74        message: u64,
75    ) -> Ciphertext {
76        self.encrypt_with_message_modulus(
77            client_key,
78            message,
79            client_key.parameters().message_modulus(),
80        )
81    }
82
83    pub fn encrypt_compressed<AP: EncryptionAtomicPattern>(
84        &mut self,
85        client_key: &GenericClientKey<AP>,
86        message: u64,
87    ) -> CompressedCiphertext {
88        self.encrypt_with_message_modulus_compressed(
89            client_key,
90            message,
91            client_key.parameters().message_modulus(),
92        )
93    }
94
95    fn encrypt_inner_ct<KeyCont, NoiseDistribution>(
96        &mut self,
97        client_key_parameters: &ShortintParameterSet,
98        client_lwe_sk: &LweSecretKey<KeyCont>,
99        noise_distribution: NoiseDistribution,
100        message: u64,
101        message_modulus: MessageModulus,
102    ) -> LweCiphertextOwned<u64>
103    where
104        NoiseDistribution: Distribution,
105        u64: RandomGenerable<NoiseDistribution, CustomModulus = u64>,
106        KeyCont: crate::core_crypto::commons::traits::Container<Element = u64>,
107    {
108        let m = Cleartext(message % message_modulus.0);
109
110        let encoded =
111            ShortintEncoding::from_parameters(*client_key_parameters, PaddingBit::Yes).encode(m);
112
113        allocate_and_encrypt_new_lwe_ciphertext(
114            client_lwe_sk,
115            encoded,
116            noise_distribution,
117            client_key_parameters.ciphertext_modulus(),
118            &mut self.encryption_generator,
119        )
120    }
121
122    pub(crate) fn encrypt_with_message_modulus<AP: EncryptionAtomicPattern>(
123        &mut self,
124        client_key: &GenericClientKey<AP>,
125        message: u64,
126        message_modulus: MessageModulus,
127    ) -> Ciphertext {
128        let params_atomic_pattern = client_key.parameters().atomic_pattern();
129
130        let (encryption_lwe_sk, encryption_noise_distribution) =
131            client_key.encryption_key_and_noise();
132
133        let ct = self.encrypt_inner_ct(
134            &client_key.parameters(),
135            &encryption_lwe_sk,
136            encryption_noise_distribution,
137            message,
138            message_modulus,
139        );
140
141        //This ensures that the space message_modulus*carry_modulus < param.message_modulus *
142        // param.carry_modulus
143        let carry_modulus = (client_key.parameters().message_modulus().0
144            * client_key.parameters().carry_modulus().0)
145            / message_modulus.0;
146
147        Ciphertext::new(
148            ct,
149            Degree::new(message_modulus.0 - 1),
150            NoiseLevel::NOMINAL,
151            message_modulus,
152            CarryModulus(carry_modulus),
153            params_atomic_pattern,
154        )
155    }
156
157    pub(crate) fn encrypt_with_message_and_carry_modulus<AP: EncryptionAtomicPattern>(
158        &mut self,
159        client_key: &GenericClientKey<AP>,
160        message: u64,
161        message_modulus: MessageModulus,
162        carry_modulus: CarryModulus,
163    ) -> Ciphertext {
164        assert!(
165            message_modulus.0 * carry_modulus.0
166                <= client_key.parameters().message_modulus().0
167                    * client_key.parameters().carry_modulus().0,
168            "MessageModulus * CarryModulus should be \
169            smaller or equal to the max given by the parameter set."
170        );
171
172        let atomic_pattern = client_key.parameters().atomic_pattern();
173
174        let (encryption_lwe_sk, encryption_noise_distribution) =
175            client_key.encryption_key_and_noise();
176
177        let ct = self.encrypt_inner_ct(
178            &client_key.parameters(),
179            &encryption_lwe_sk,
180            encryption_noise_distribution,
181            message,
182            message_modulus,
183        );
184
185        Ciphertext::new(
186            ct,
187            Degree::new(message_modulus.0 - 1),
188            NoiseLevel::NOMINAL,
189            message_modulus,
190            carry_modulus,
191            atomic_pattern,
192        )
193    }
194
195    pub(crate) fn encrypt_with_message_modulus_compressed<AP: EncryptionAtomicPattern>(
196        &mut self,
197        client_key: &GenericClientKey<AP>,
198        message: u64,
199        message_modulus: MessageModulus,
200    ) -> CompressedCiphertext {
201        // This ensures that the space message_modulus*carry_modulus < param.message_modulus *
202        // param.carry_modulus
203        let carry_modulus = (client_key.parameters().message_modulus().0
204            * client_key.parameters().carry_modulus().0)
205            / message_modulus.0;
206
207        let m = Cleartext(message % message_modulus.0);
208
209        let encoded =
210            ShortintEncoding::from_parameters(client_key.parameters(), PaddingBit::Yes).encode(m);
211
212        let atomic_pattern = client_key.parameters().atomic_pattern();
213
214        let (encryption_lwe_sk, encryption_noise_distribution) =
215            client_key.encryption_key_and_noise();
216
217        let ct = allocate_and_encrypt_new_seeded_lwe_ciphertext(
218            &encryption_lwe_sk,
219            encoded,
220            encryption_noise_distribution,
221            client_key.parameters().ciphertext_modulus(),
222            &mut self.seeder,
223        );
224
225        CompressedCiphertext {
226            ct,
227            degree: Degree::new(message_modulus.0 - 1),
228            message_modulus,
229            carry_modulus: CarryModulus(carry_modulus),
230            atomic_pattern,
231            noise_level: NoiseLevel::NOMINAL,
232        }
233    }
234
235    pub(crate) fn unchecked_encrypt<AP: EncryptionAtomicPattern>(
236        &mut self,
237        client_key: &GenericClientKey<AP>,
238        message: u64,
239    ) -> Ciphertext {
240        let atomic_pattern = client_key.parameters().atomic_pattern();
241
242        let (encryption_lwe_sk, encryption_noise_distribution) =
243            client_key.encryption_key_and_noise();
244
245        let encoded = ShortintEncoding::from_parameters(client_key.parameters(), PaddingBit::Yes)
246            .encode(Cleartext(message));
247
248        let ct = allocate_and_encrypt_new_lwe_ciphertext(
249            &encryption_lwe_sk,
250            encoded,
251            encryption_noise_distribution,
252            client_key.parameters().ciphertext_modulus(),
253            &mut self.encryption_generator,
254        );
255
256        Ciphertext::new(
257            ct,
258            Degree::new(
259                client_key.parameters().message_modulus().0
260                    * client_key.parameters().carry_modulus().0
261                    - 1,
262            ),
263            NoiseLevel::NOMINAL,
264            client_key.parameters().message_modulus(),
265            client_key.parameters().carry_modulus(),
266            atomic_pattern,
267        )
268    }
269
270    pub(crate) fn encrypt_without_padding<AP: EncryptionAtomicPattern>(
271        &mut self,
272        client_key: &GenericClientKey<AP>,
273        message: u64,
274    ) -> Ciphertext {
275        let encoded = ShortintEncoding::from_parameters(client_key.parameters(), PaddingBit::No)
276            .encode(Cleartext(message));
277
278        let atomic_pattern = client_key.parameters().atomic_pattern();
279
280        let (encryption_lwe_sk, encryption_noise_distribution) =
281            client_key.encryption_key_and_noise();
282
283        let ct = allocate_and_encrypt_new_lwe_ciphertext(
284            &encryption_lwe_sk,
285            encoded,
286            encryption_noise_distribution,
287            client_key.parameters().ciphertext_modulus(),
288            &mut self.encryption_generator,
289        );
290
291        Ciphertext::new(
292            ct,
293            Degree::new(client_key.parameters().message_modulus().0 - 1),
294            NoiseLevel::NOMINAL,
295            client_key.parameters().message_modulus(),
296            client_key.parameters().carry_modulus(),
297            atomic_pattern,
298        )
299    }
300
301    pub(crate) fn encrypt_without_padding_compressed<AP: EncryptionAtomicPattern>(
302        &mut self,
303        client_key: &GenericClientKey<AP>,
304        message: u64,
305    ) -> CompressedCiphertext {
306        let encoded = ShortintEncoding::from_parameters(client_key.parameters(), PaddingBit::No)
307            .encode(Cleartext(message));
308
309        let atomic_pattern = client_key.parameters().atomic_pattern();
310
311        let (encryption_lwe_sk, encryption_noise_distribution) =
312            client_key.encryption_key_and_noise();
313
314        let ct = allocate_and_encrypt_new_seeded_lwe_ciphertext(
315            &encryption_lwe_sk,
316            encoded,
317            encryption_noise_distribution,
318            client_key.parameters().ciphertext_modulus(),
319            &mut self.seeder,
320        );
321
322        CompressedCiphertext {
323            ct,
324            degree: Degree::new(client_key.parameters().message_modulus().0 - 1),
325            message_modulus: client_key.parameters().message_modulus(),
326            carry_modulus: client_key.parameters().carry_modulus(),
327            atomic_pattern,
328            noise_level: NoiseLevel::NOMINAL,
329        }
330    }
331
332    pub(crate) fn encrypt_native_crt<AP: EncryptionAtomicPattern>(
333        &mut self,
334        client_key: &GenericClientKey<AP>,
335        message: u64,
336        message_modulus: MessageModulus,
337    ) -> Ciphertext {
338        let carry_modulus = CarryModulus(1);
339        let m = (message % message_modulus.0) as u128;
340        let shifted_message = (m * (1 << 64) / message_modulus.0 as u128) as u64;
341
342        let encoded = Plaintext(shifted_message);
343
344        let atomic_pattern = client_key.parameters().atomic_pattern();
345
346        let (encryption_lwe_sk, encryption_noise_distribution) =
347            client_key.encryption_key_and_noise();
348
349        let ct = allocate_and_encrypt_new_lwe_ciphertext(
350            &encryption_lwe_sk,
351            encoded,
352            encryption_noise_distribution,
353            client_key.parameters().ciphertext_modulus(),
354            &mut self.encryption_generator,
355        );
356
357        Ciphertext::new(
358            ct,
359            Degree::new(message_modulus.0 - 1),
360            NoiseLevel::NOMINAL,
361            message_modulus,
362            carry_modulus,
363            atomic_pattern,
364        )
365    }
366
367    pub(crate) fn encrypt_native_crt_compressed<AP: EncryptionAtomicPattern>(
368        &mut self,
369        client_key: &GenericClientKey<AP>,
370        message: u64,
371        message_modulus: MessageModulus,
372    ) -> CompressedCiphertext {
373        let carry_modulus = CarryModulus(1);
374        let m = (message % message_modulus.0) as u128;
375        let shifted_message = (m * (1 << 64) / message_modulus.0 as u128) as u64;
376
377        let encoded = Plaintext(shifted_message);
378
379        let atomic_pattern = client_key.parameters().atomic_pattern();
380
381        let (encryption_lwe_sk, encryption_noise_distribution) =
382            client_key.encryption_key_and_noise();
383
384        let ct = allocate_and_encrypt_new_seeded_lwe_ciphertext(
385            &encryption_lwe_sk,
386            encoded,
387            encryption_noise_distribution,
388            client_key.parameters().ciphertext_modulus(),
389            &mut self.seeder,
390        );
391
392        CompressedCiphertext {
393            ct,
394            degree: Degree::new(message_modulus.0 - 1),
395            message_modulus,
396            carry_modulus,
397            atomic_pattern,
398            noise_level: NoiseLevel::NOMINAL,
399        }
400    }
401}