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}