concrete_shortint/server_key/
mul.rs

1use super::ServerKey;
2use crate::ciphertext::Degree;
3use crate::engine::ShortintEngine;
4use crate::server_key::CheckError;
5use crate::server_key::CheckError::CarryFull;
6use crate::Ciphertext;
7
8impl ServerKey {
9    /// Multiplies two ciphertexts together without checks.
10    ///
11    /// Returns the "least significant bits" of the multiplication, i.e., the result modulus the
12    /// message_modulus.
13    ///
14    /// The result is returned in a _new_ ciphertext.
15    ///
16    /// # Example
17    ///
18    ///```rust
19    /// use concrete_shortint::gen_keys;
20    /// use concrete_shortint::parameters::PARAM_MESSAGE_1_CARRY_1;
21    ///
22    /// // Generate the client key and the server key
23    /// let (mut cks, mut sks) = gen_keys(PARAM_MESSAGE_1_CARRY_1);
24    ///
25    /// let clear_1 = 1;
26    /// let clear_2 = 1;
27    ///
28    /// // Encrypt two messages
29    /// let ct_1 = cks.encrypt(clear_1);
30    /// let ct_2 = cks.encrypt(clear_2);
31    ///
32    /// // Compute homomorphically a multiplication
33    /// let ct_res = sks.unchecked_mul_lsb(&ct_1, &ct_2);
34    /// // 2*3 == 6 == 01_10 (base 2)
35    /// // Only the message part is returned (lsb) so `ct_res` is:
36    /// // |      ct_res     |
37    /// // | carry | message |
38    /// // |-------|---------|
39    /// // |  0 0  |   1 0   |
40    ///
41    /// // Decrypt
42    /// let res = cks.decrypt(&ct_res);
43    /// let modulus = cks.parameters.message_modulus.0 as u64;
44    /// assert_eq!((clear_1 * clear_2) % modulus, res);
45    /// ```
46    pub fn unchecked_mul_lsb(&self, ct_left: &Ciphertext, ct_right: &Ciphertext) -> Ciphertext {
47        ShortintEngine::with_thread_local_mut(|engine| {
48            engine.unchecked_mul_lsb(self, ct_left, ct_right).unwrap()
49        })
50    }
51
52    /// Multiplies two ciphertexts together without checks.
53    ///
54    /// Returns the "least significant bits" of the multiplication, i.e., the result modulus the
55    /// message_modulus.
56    ///
57    /// The result is _assigned_ in the first ciphertext
58    ///
59    /// # Example
60    ///
61    ///```rust
62    /// use concrete_shortint::gen_keys;
63    /// use concrete_shortint::parameters::DEFAULT_PARAMETERS;
64    ///
65    /// // Generate the client key and the server key
66    /// let (mut cks, mut sks) = gen_keys(DEFAULT_PARAMETERS);
67    ///
68    /// let clear_1 = 3;
69    /// let clear_2 = 2;
70    ///
71    /// // Encrypt two messages
72    /// let mut ct_1 = cks.encrypt(clear_1);
73    /// let ct_2 = cks.encrypt(clear_2);
74    ///
75    /// // Compute homomorphically a multiplication
76    /// sks.unchecked_mul_lsb_assign(&mut ct_1, &ct_2);
77    ///
78    /// // Decrypt
79    /// let res = cks.decrypt(&ct_1);
80    /// let modulus = cks.parameters.message_modulus.0 as u64;
81    /// assert_eq!((clear_1 * clear_2) % modulus, res);
82    /// ```
83    pub fn unchecked_mul_lsb_assign(&self, ct_left: &mut Ciphertext, ct_right: &Ciphertext) {
84        ShortintEngine::with_thread_local_mut(|engine| {
85            engine
86                .unchecked_mul_lsb_assign(self, ct_left, ct_right)
87                .unwrap()
88        })
89    }
90
91    /// Multiplies two ciphertexts together without checks.
92    ///
93    /// Returns the "most significant bits" of the multiplication, i.e., the part in the carry
94    /// buffer.
95    ///
96    /// The result is returned in a _new_ ciphertext.
97    ///
98    /// # Example
99    ///
100    ///```rust
101    /// use concrete_shortint::gen_keys;
102    /// use concrete_shortint::parameters::DEFAULT_PARAMETERS;
103    ///
104    /// // Generate the client key and the server key
105    /// let (mut cks, mut sks) = gen_keys(DEFAULT_PARAMETERS);
106    ///
107    /// let clear_1 = 3;
108    /// let clear_2 = 2;
109    ///
110    /// // Encrypt two messages
111    /// let mut ct_1 = cks.encrypt(clear_1);
112    /// let mut ct_2 = cks.encrypt(clear_2);
113    ///
114    /// // Compute homomorphically a multiplication
115    /// let ct_res = sks.unchecked_mul_msb(&ct_1, &ct_2);
116    /// // 2*3 == 6 == 01_10 (base 2)
117    /// // however the ciphertext will contain only the carry buffer
118    /// // as the message, the ct_res is actually:
119    /// // |      ct_res     |
120    /// // | carry | message |
121    /// // |-------|---------|
122    /// // |  0 0  |   0 1   |
123    ///
124    /// // Decrypt
125    /// let res = cks.decrypt(&ct_res);
126    /// let modulus = cks.parameters.message_modulus.0 as u64;
127    /// assert_eq!((clear_1 * clear_2) / modulus, res);
128    /// ```
129    pub fn unchecked_mul_msb(&self, ct_left: &Ciphertext, ct_right: &Ciphertext) -> Ciphertext {
130        ShortintEngine::with_thread_local_mut(|engine| {
131            engine.unchecked_mul_msb(self, ct_left, ct_right).unwrap()
132        })
133    }
134
135    pub fn unchecked_mul_msb_assign(&self, ct_left: &mut Ciphertext, ct_right: &Ciphertext) {
136        ShortintEngine::with_thread_local_mut(|engine| {
137            engine
138                .unchecked_mul_msb_assign(self, ct_left, ct_right)
139                .unwrap()
140        })
141    }
142
143    /// Verifies if two ciphertexts can be multiplied together.
144    ///
145    /// # Example
146    ///
147    ///```rust
148    /// use concrete_shortint::{gen_keys, Parameters};
149    ///
150    /// // Generate the client key and the server key:
151    /// let (mut cks, mut sks) = gen_keys(Parameters::default());
152    ///
153    /// let msg = 2;
154    ///
155    /// // Encrypt two messages:
156    /// let ct_1 = cks.encrypt(msg);
157    /// let ct_2 = cks.encrypt(msg);
158    ///
159    /// // Check if we can perform a multiplication
160    /// let res = sks.is_mul_possible(&ct_1, &ct_2);
161    ///
162    /// assert_eq!(true, res);
163    /// ```
164    pub fn is_mul_possible(&self, ct1: &Ciphertext, ct2: &Ciphertext) -> bool {
165        self.is_functional_bivariate_pbs_possible(ct1, ct2)
166    }
167
168    /// Multiplies two ciphertexts together with checks.
169    ///
170    /// Returns the "least significant bits" of the multiplication, i.e., the result modulus the
171    /// message_modulus.
172    ///
173    /// If the operation can be performed, a _new_ ciphertext with the result is returned.
174    /// Otherwise [CheckError::CarryFull] is returned.
175    ///
176    /// # Example
177    ///
178    /// ```rust
179    /// use concrete_shortint::{gen_keys, Parameters};
180    ///
181    /// // Generate the client key and the server key:
182    /// let (mut cks, mut sks) = gen_keys(Parameters::default());
183    ///
184    /// // Encrypt two messages:
185    /// let ct_1 = cks.encrypt(2);
186    /// let ct_2 = cks.encrypt(1);
187    ///
188    /// // Compute homomorphically a multiplication:
189    /// let ct_res = sks.checked_mul_lsb(&ct_1, &ct_2);
190    ///
191    /// assert!(ct_res.is_ok());
192    ///
193    /// let ct_res = ct_res.unwrap();
194    /// let clear_res = cks.decrypt_message_and_carry(&ct_res);
195    /// let modulus = cks.parameters.message_modulus.0 as u64;
196    /// assert_eq!(clear_res % modulus, 2);
197    /// ```
198    pub fn checked_mul_lsb(
199        &self,
200        ct_left: &Ciphertext,
201        ct_right: &Ciphertext,
202    ) -> Result<Ciphertext, CheckError> {
203        if self.is_mul_possible(ct_left, ct_right) {
204            let ct_result = self.unchecked_mul_lsb(ct_left, ct_right);
205            Ok(ct_result)
206        } else {
207            Err(CarryFull)
208        }
209    }
210
211    /// Multiplies two ciphertexts together with checks.
212    ///
213    /// Returns the "least significant bits" of the multiplication, i.e., the result modulus the
214    /// message_modulus.
215    ///
216    /// If the operation can be performed, the result is assigned to the first ciphertext given
217    /// as a parameter.
218    /// Otherwise [CheckError::CarryFull] is returned.
219    ///
220    /// # Example
221    ///
222    /// ```rust
223    /// use concrete_shortint::{gen_keys, Parameters};
224    ///
225    /// // Generate the client key and the server key:
226    /// let (mut cks, mut sks) = gen_keys(Parameters::default());
227    ///
228    /// // Encrypt two messages:
229    /// let mut ct_1 = cks.encrypt(2);
230    /// let ct_2 = cks.encrypt(1);
231    ///
232    /// // Compute homomorphically a multiplication:
233    /// let ct_res = sks.checked_mul_lsb_assign(&mut ct_1, &ct_2);
234    ///
235    /// assert!(ct_res.is_ok());
236    ///
237    /// let clear_res = cks.decrypt_message_and_carry(&ct_1);
238    /// let modulus = cks.parameters.message_modulus.0 as u64;
239    /// assert_eq!(clear_res % modulus, 2);
240    /// ```
241    pub fn checked_mul_lsb_assign(
242        &self,
243        ct_left: &mut Ciphertext,
244        ct_right: &Ciphertext,
245    ) -> Result<(), CheckError> {
246        if self.is_mul_possible(ct_left, ct_right) {
247            self.unchecked_mul_lsb_assign(ct_left, ct_right);
248            Ok(())
249        } else {
250            Err(CarryFull)
251        }
252    }
253
254    /// Multiplies two ciphertexts together without checks.
255    ///
256    /// Returns the "most significant bits" of the multiplication, i.e., the part in the carry
257    /// buffer.
258    ///
259    /// If the operation can be performed, a _new_ ciphertext with the result is returned.
260    /// Otherwise [CheckError::CarryFull] is returned.
261    ///
262    /// # Example
263    ///
264    /// ```rust
265    /// use concrete_shortint::gen_keys;
266    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
267    ///
268    /// // Generate the client key and the server key:
269    /// let (mut cks, mut sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
270    ///
271    /// let msg_1 = 2;
272    /// let msg_2 = 2;
273    ///
274    /// // Encrypt two messages:
275    /// let ct_1 = cks.encrypt(msg_1);
276    /// let ct_2 = cks.encrypt(msg_2);
277    ///
278    /// // Compute homomorphically a multiplication:
279    /// let ct_res = sks.checked_mul_msb(&ct_1, &ct_2);
280    /// assert!(ct_res.is_ok());
281    ///
282    /// // 2*2 == 4 == 01_00 (base 2)
283    /// // however the ciphertext will contain only the carry buffer
284    /// // as the message, the ct_res is actually:
285    /// // |      ct_res     |
286    /// // | carry | message |
287    /// // |-------|---------|
288    /// // |  0 0  |   0 1   |
289    ///
290    /// let ct_res = ct_res.unwrap();
291    /// let clear_res = cks.decrypt(&ct_res);
292    /// assert_eq!(
293    ///     clear_res,
294    ///     (msg_1 * msg_2) / cks.parameters.message_modulus.0 as u64
295    /// );
296    /// ```
297    pub fn checked_mul_msb(
298        &self,
299        ct_left: &Ciphertext,
300        ct_right: &Ciphertext,
301    ) -> Result<Ciphertext, CheckError> {
302        if self.is_mul_possible(ct_left, ct_right) {
303            let ct_result = self.unchecked_mul_msb(ct_left, ct_right);
304            Ok(ct_result)
305        } else {
306            Err(CarryFull)
307        }
308    }
309
310    /// Multiply two ciphertexts together using one bit of carry only.
311    ///
312    /// The algorithm uses the (.)^2/4 trick.
313    /// For more information: page 4, §Computing a multiplication in
314    /// Chillotti, I., Joye, M., Ligier, D., Orfila, J. B., & Tap, S. (2020, December).
315    /// CONCRETE: Concrete operates on ciphertexts rapidly by extending TfhE.
316    /// In WAHC 2020–8th Workshop on Encrypted Computing & Applied Homomorphic Cryptography (Vol.
317    /// 15).
318    ///
319    /// # Example
320    ///
321    ///```rust
322    /// use concrete_shortint::parameters::PARAM_MESSAGE_1_CARRY_1;
323    /// use concrete_shortint::{gen_keys, Parameters};
324    ///
325    /// // Generate the client key and the server key:
326    /// let (mut cks, mut sks) = gen_keys(PARAM_MESSAGE_1_CARRY_1);
327    ///
328    /// let clear_1 = 1;
329    /// let clear_2 = 1;
330    ///
331    /// // Encrypt two messages
332    /// let mut ct_1 = cks.encrypt(clear_1);
333    /// let mut ct_2 = cks.encrypt(clear_2);
334    ///
335    /// // Compute homomorphically a multiplication
336    /// let ct_res = sks.unchecked_mul_lsb_small_carry(&mut ct_1, &mut ct_2);
337    ///
338    /// // Decrypt
339    /// let res = cks.decrypt(&ct_res);
340    /// assert_eq!((clear_2 * clear_1), res);
341    /// ```
342    pub fn unchecked_mul_lsb_small_carry(
343        &self,
344        ct_left: &mut Ciphertext,
345        ct_right: &mut Ciphertext,
346    ) -> Ciphertext {
347        ShortintEngine::with_thread_local_mut(|engine| {
348            engine
349                .unchecked_mul_lsb_small_carry_modulus(self, ct_left, ct_right)
350                .unwrap()
351        })
352    }
353
354    pub fn unchecked_mul_lsb_small_carry_assign(
355        &self,
356        ct_left: &mut Ciphertext,
357        ct_right: &mut Ciphertext,
358    ) {
359        ShortintEngine::with_thread_local_mut(|engine| {
360            engine
361                .unchecked_mul_lsb_small_carry_modulus_assign(self, ct_left, ct_right)
362                .unwrap()
363        })
364    }
365
366    /// Verifies if two ciphertexts can be multiplied together in the case where the carry
367    /// modulus is smaller than the message modulus.
368    ///
369    /// # Example
370    ///
371    ///```rust
372    /// use concrete_shortint::gen_keys;
373    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_1;
374    ///
375    /// // Generate the client key and the server key:
376    /// let (mut cks, mut sks) = gen_keys(PARAM_MESSAGE_2_CARRY_1);
377    ///
378    /// let msg = 2;
379    ///
380    /// // Encrypt two messages:
381    /// let ct_1 = cks.encrypt(msg);
382    /// let ct_2 = cks.encrypt(msg);
383    ///
384    /// // Check if we can perform a multiplication
385    /// let mut res = sks.is_mul_small_carry_possible(&ct_1, &ct_2);
386    ///
387    /// assert_eq!(true, res);
388    ///
389    /// //Encryption with a full carry buffer
390    /// let large_msg = 7;
391    /// let ct_3 = cks.unchecked_encrypt(large_msg);
392    ///
393    /// //  Check if we can perform a multiplication
394    /// res = sks.is_mul_small_carry_possible(&ct_1, &ct_3);
395    ///
396    /// assert_eq!(false, res);
397    /// ```
398    pub fn is_mul_small_carry_possible(&self, ct_left: &Ciphertext, ct_right: &Ciphertext) -> bool {
399        // Check if an addition is possible
400        let b1 = self.is_add_possible(ct_left, ct_right);
401        let b2 = self.is_sub_possible(ct_left, ct_right);
402        b1 & b2
403    }
404
405    /// Computes homomorphically a multiplication between two ciphertexts encrypting integer values.
406    ///
407    /// The operation is done using a small carry buffer.
408    ///
409    /// If the operation can be performed, a _new_ ciphertext with the result of the
410    /// multiplication is returned. Otherwise [CheckError::CarryFull] is returned.
411    ///
412    /// # Example
413    ///
414    /// ```rust
415    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
416    /// use concrete_shortint::{gen_keys, Parameters};
417    ///
418    /// // Generate the client key and the server key:
419    /// let (mut cks, mut sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
420    ///
421    /// let msg_1 = 2;
422    /// let msg_2 = 3;
423    ///
424    /// // Encrypt two messages:
425    /// let mut ct_1 = cks.encrypt(msg_1);
426    /// let mut ct_2 = cks.encrypt(msg_2);
427    ///
428    /// // Compute homomorphically a multiplication
429    /// let ct_res = sks.checked_mul_lsb_with_small_carry(&mut ct_1, &mut ct_2);
430    ///
431    /// assert!(ct_res.is_ok());
432    ///
433    /// let ct_res = ct_res.unwrap();
434    /// let clear_res = cks.decrypt(&ct_res);
435    /// let modulus = cks.parameters.message_modulus.0 as u64;
436    /// assert_eq!(clear_res % modulus, (msg_1 * msg_2) % modulus);
437    /// ```
438    pub fn checked_mul_lsb_with_small_carry(
439        &self,
440        ct_left: &mut Ciphertext,
441        ct_right: &mut Ciphertext,
442    ) -> Result<Ciphertext, CheckError> {
443        if self.is_mul_small_carry_possible(ct_left, ct_right) {
444            let mut ct_result = self.unchecked_mul_lsb_small_carry(ct_left, ct_right);
445            ct_result.degree = Degree(ct_left.degree.0 * 2);
446            Ok(ct_result)
447        } else {
448            Err(CarryFull)
449        }
450    }
451
452    /// Multiplies two ciphertexts.
453    ///
454    /// Returns the "least significant bits" of the multiplication, i.e., the result modulus the
455    /// message_modulus.
456    ///
457    /// The result is _assigned_ in the first ciphertext
458    ///
459    /// # Example
460    ///
461    /// ```rust
462    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_1;
463    /// use concrete_shortint::{gen_keys, Parameters};
464    ///
465    /// // Generate the client key and the server key:
466    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_1);
467    ///
468    /// // Encrypt two messages:
469    /// let msg1 = 5;
470    /// let msg2 = 3;
471    ///
472    /// let mut ct_1 = cks.unchecked_encrypt(msg1);
473    /// let mut ct_2 = cks.unchecked_encrypt(msg2);
474    ///
475    /// // Compute homomorphically a multiplication
476    /// sks.smart_mul_lsb_assign(&mut ct_1, &mut ct_2);
477    ///
478    /// let res = cks.decrypt(&ct_1);
479    /// let modulus = sks.message_modulus.0 as u64;
480    /// assert_eq!(res % modulus, (msg1 * msg2) % modulus);
481    /// ```
482    pub fn smart_mul_lsb_assign(&self, ct_left: &mut Ciphertext, ct_right: &mut Ciphertext) {
483        ShortintEngine::with_thread_local_mut(|engine| {
484            engine
485                .smart_mul_lsb_assign(self, ct_left, ct_right)
486                .unwrap()
487        })
488    }
489
490    pub fn smart_mul_msb_assign(&self, ct_left: &mut Ciphertext, ct_right: &mut Ciphertext) {
491        ShortintEngine::with_thread_local_mut(|engine| {
492            engine
493                .smart_mul_msb_assign(self, ct_left, ct_right)
494                .unwrap()
495        })
496    }
497
498    /// Multiply two ciphertexts together
499    ///
500    /// Returns the "least significant bits" of the multiplication, i.e., the result modulus the
501    /// message_modulus.
502    ///
503    /// # Example
504    ///
505    /// ```rust
506    /// use concrete_shortint::{gen_keys, Parameters};
507    ///
508    /// // Generate the client key and the server key:
509    /// let (cks, sks) = gen_keys(Parameters::default());
510    ///
511    /// // Encrypt two messages:
512    /// let msg1 = 12;
513    /// let msg2 = 13;
514    ///
515    /// let mut ct_left = cks.unchecked_encrypt(msg1);
516    /// // |      ct_left    |
517    /// // | carry | message |
518    /// // |-------|---------|
519    /// // |  1 1  |   0 0   |
520    /// let mut ct_right = cks.unchecked_encrypt(msg2);
521    /// // |      ct_right   |
522    /// // | carry | message |
523    /// // |-------|---------|
524    /// // |  1 1  |   0 1   |
525    ///
526    /// // Compute homomorphically a multiplication:
527    /// let ct_res = sks.smart_mul_lsb(&mut ct_left, &mut ct_right);
528    /// // |      ct_res     |
529    /// // | carry | message |
530    /// // |-------|---------|
531    /// // |  0 0  |   0 0   |
532    ///
533    /// let res = cks.decrypt(&ct_res);
534    /// let modulus = sks.message_modulus.0;
535    /// assert_eq!(res, (msg1 * msg2) % modulus as u64);
536    /// ```
537    pub fn smart_mul_lsb(&self, ct_left: &mut Ciphertext, ct_right: &mut Ciphertext) -> Ciphertext {
538        ShortintEngine::with_thread_local_mut(|engine| {
539            engine.smart_mul_lsb(self, ct_left, ct_right).unwrap()
540        })
541    }
542
543    /// Multiply two ciphertexts together
544    ///
545    /// Returns the "most significant bits" of the multiplication, i.e., the part in the carry
546    /// buffer.
547    ///
548    /// # Example
549    ///
550    /// ```rust
551    /// use concrete_shortint::{gen_keys, Parameters};
552    ///
553    /// // Generate the client key and the server key:
554    /// let (cks, sks) = gen_keys(Parameters::default());
555    ///
556    /// // Encrypt two messages:
557    /// let msg1 = 12;
558    /// let msg2 = 12;
559    ///
560    /// let mut ct_1 = cks.unchecked_encrypt(msg1);
561    /// let mut ct_2 = cks.unchecked_encrypt(msg2);
562    ///
563    /// // Compute homomorphically a multiplication:
564    /// let ct_res = sks.smart_mul_msb(&mut ct_1, &mut ct_2);
565    ///
566    /// let res = cks.decrypt(&ct_res);
567    /// let modulus = sks.carry_modulus.0;
568    /// assert_eq!(res, (msg1 * msg2) % modulus as u64);
569    /// ```
570    pub fn smart_mul_msb(&self, ct_left: &mut Ciphertext, ct_right: &mut Ciphertext) -> Ciphertext {
571        ShortintEngine::with_thread_local_mut(|engine| {
572            engine.smart_mul_msb(self, ct_left, ct_right).unwrap()
573        })
574    }
575}