1use super::super::parameters::CiphertextConformanceParams;
3use super::common::*;
4use crate::conformance::ParameterSetConformant;
5use crate::core_crypto::entities::*;
6use crate::core_crypto::prelude::{allocate_and_trivially_encrypt_new_lwe_ciphertext, LweSize};
7use crate::shortint::backward_compatibility::ciphertext::CiphertextVersions;
8use crate::shortint::ciphertext::ReRandomizationSeed;
9use crate::shortint::key_switching_key::KeySwitchingKeyMaterialView;
10use crate::shortint::parameters::{AtomicPatternKind, CarryModulus, MessageModulus};
11use crate::shortint::public_key::compact::CompactPublicKey;
12use crate::shortint::{CiphertextModulus, PaddingBit, ShortintEncoding};
13use serde::{Deserialize, Serialize};
14use std::fmt::Debug;
15use tfhe_versionable::Versionize;
16
17#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Versionize)]
18#[versionize(CiphertextVersions)]
19#[must_use]
20pub struct Ciphertext {
21 pub ct: LweCiphertextOwned<u64>,
22 pub degree: Degree,
23 noise_level: NoiseLevel,
26 pub message_modulus: MessageModulus,
27 pub carry_modulus: CarryModulus,
28 pub atomic_pattern: AtomicPatternKind,
29}
30
31impl crate::named::Named for Ciphertext {
32 const NAME: &'static str = "shortint::Ciphertext";
33}
34
35impl ParameterSetConformant for Ciphertext {
36 type ParameterSet = CiphertextConformanceParams;
37
38 fn is_conformant(&self, param: &CiphertextConformanceParams) -> bool {
39 let Self {
40 ct,
41 degree,
42 noise_level,
43 message_modulus,
44 carry_modulus,
45 atomic_pattern,
46 } = self;
47
48 ct.is_conformant(¶m.ct_params)
49 && *message_modulus == param.message_modulus
50 && *carry_modulus == param.carry_modulus
51 && *atomic_pattern == param.atomic_pattern
52 && *degree == param.degree
53 && *noise_level == param.noise_level
54 }
55}
56
57impl Clone for Ciphertext {
63 fn clone(&self) -> Self {
64 let Self {
65 ct: src_ct,
66 degree: src_degree,
67 message_modulus: src_message_modulus,
68 carry_modulus: src_carry_modulus,
69 atomic_pattern: src_atomic_pattern,
70 noise_level: src_noise_level,
71 } = self;
72
73 Self {
74 ct: src_ct.clone(),
75 degree: *src_degree,
76 message_modulus: *src_message_modulus,
77 carry_modulus: *src_carry_modulus,
78 atomic_pattern: *src_atomic_pattern,
79 noise_level: *src_noise_level,
80 }
81 }
82
83 fn clone_from(&mut self, source: &Self) {
84 let Self {
85 ct: dst_ct,
86 degree: dst_degree,
87 message_modulus: dst_message_modulus,
88 carry_modulus: dst_carry_modulus,
89 atomic_pattern: dst_atomic_pattern,
90 noise_level: dst_noise_level,
91 } = self;
92
93 let Self {
94 ct: src_ct,
95 degree: src_degree,
96 message_modulus: src_message_modulus,
97 carry_modulus: src_carry_modulus,
98 atomic_pattern: src_atomic_pattern,
99 noise_level: src_noise_level,
100 } = source;
101
102 if dst_ct.ciphertext_modulus() != src_ct.ciphertext_modulus()
103 || dst_ct.lwe_size() != src_ct.lwe_size()
104 {
105 *dst_ct = src_ct.clone();
106 } else {
107 dst_ct.as_mut().copy_from_slice(src_ct.as_ref());
108 }
109 *dst_degree = *src_degree;
110 *dst_message_modulus = *src_message_modulus;
111 *dst_carry_modulus = *src_carry_modulus;
112 *dst_atomic_pattern = *src_atomic_pattern;
113 *dst_noise_level = *src_noise_level;
114 }
115}
116
117impl Ciphertext {
118 pub fn new(
119 ct: LweCiphertextOwned<u64>,
120 degree: Degree,
121 noise_level: NoiseLevel,
122 message_modulus: MessageModulus,
123 carry_modulus: CarryModulus,
124 atomic_pattern: AtomicPatternKind,
125 ) -> Self {
126 Self {
127 ct,
128 degree,
129 noise_level,
130 message_modulus,
131 carry_modulus,
132 atomic_pattern,
133 }
134 }
135 pub fn carry_is_empty(&self) -> bool {
136 self.degree.get() < self.message_modulus.0
137 }
138
139 pub fn is_trivial(&self) -> bool {
140 self.noise_level() == NoiseLevel::ZERO
141 && self.ct.get_mask().as_ref().iter().all(|&x| x == 0u64)
142 }
143
144 pub fn noise_level(&self) -> NoiseLevel {
145 self.noise_level
146 }
147
148 #[cfg_attr(any(feature = "noise-asserts", test), track_caller)]
149 pub fn set_noise_level(&mut self, noise_level: NoiseLevel, max_noise_level: MaxNoiseLevel) {
150 if cfg!(feature = "noise-asserts") || cfg!(test) {
151 max_noise_level.validate(noise_level).unwrap()
152 } else {
153 let _ = max_noise_level;
154 }
155 self.noise_level = noise_level;
156 }
157
158 pub fn set_noise_level_to_nominal(&mut self) {
159 self.noise_level = NoiseLevel::NOMINAL;
160 }
161
162 pub fn decrypt_trivial(&self) -> Result<u64, NotTrivialCiphertextError> {
207 self.decrypt_trivial_message_and_carry()
208 .map(|x| x % self.message_modulus.0)
209 }
210
211 pub(crate) fn encoding(&self, padding_bit: PaddingBit) -> ShortintEncoding<u64> {
212 ShortintEncoding {
213 ciphertext_modulus: self.ct.ciphertext_modulus(),
214 message_modulus: self.message_modulus,
215 carry_modulus: self.carry_modulus,
216 padding_bit,
217 }
218 }
219
220 pub fn decrypt_trivial_message_and_carry(&self) -> Result<u64, NotTrivialCiphertextError> {
245 if self.is_trivial() {
246 let decoded = self
247 .encoding(PaddingBit::Yes)
248 .decode(Plaintext(*self.ct.get_body().data))
249 .0;
250 Ok(decoded)
251 } else {
252 Err(NotTrivialCiphertextError)
253 }
254 }
255
256 pub fn re_randomize_with_compact_public_key_encryption(
268 &mut self,
269 compact_public_key: &CompactPublicKey,
270 key_switching_key_material: &KeySwitchingKeyMaterialView<'_>,
271 seed: ReRandomizationSeed,
272 ) -> crate::Result<()> {
273 compact_public_key.re_randomize_ciphertexts(
274 std::slice::from_mut(self),
275 key_switching_key_material,
276 seed,
277 )
278 }
279}
280
281pub(crate) fn unchecked_create_trivial_with_lwe_size(
282 value: Cleartext<u64>,
283 lwe_size: LweSize,
284 message_modulus: MessageModulus,
285 carry_modulus: CarryModulus,
286 atomic_pattern: AtomicPatternKind,
287 ciphertext_modulus: CiphertextModulus,
288) -> Ciphertext {
289 let encoded = ShortintEncoding {
290 ciphertext_modulus,
291 message_modulus,
292 carry_modulus,
293 padding_bit: PaddingBit::Yes,
294 }
295 .encode(value);
296
297 let ct =
298 allocate_and_trivially_encrypt_new_lwe_ciphertext(lwe_size, encoded, ciphertext_modulus);
299
300 let degree = Degree::new(value.0);
301
302 Ciphertext::new(
303 ct,
304 degree,
305 NoiseLevel::ZERO,
306 message_modulus,
307 carry_modulus,
308 atomic_pattern,
309 )
310}
311
312#[cfg(test)]
313mod tests {
314 use super::*;
315 use crate::shortint::ciphertext::ReRandomizationContext;
316 use crate::shortint::key_switching_key::KeySwitchingKeyBuildHelper;
317 use crate::shortint::keycache::KEY_CACHE;
318 use crate::shortint::parameters::test_params::{
319 TEST_COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
320 TEST_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
321 TEST_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
322 TEST_PARAM_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128_ZKV2,
323 };
324 use crate::shortint::public_key::compact::CompactPrivateKey;
325 use crate::shortint::CiphertextModulus;
326
327 #[test]
328 fn test_clone_from_same_lwe_size_and_modulus_ci_run_filter() {
329 let mut c1 = Ciphertext {
330 ct: LweCiphertextOwned::from_container(
331 vec![1u64; 256],
332 CiphertextModulus::new_native(),
333 ),
334 degree: Degree::new(1),
335 message_modulus: MessageModulus(1),
336 carry_modulus: CarryModulus(1),
337 atomic_pattern: AtomicPatternKind::Standard(PBSOrder::KeyswitchBootstrap),
338 noise_level: NoiseLevel::NOMINAL,
339 };
340
341 let c2 = Ciphertext {
342 ct: LweCiphertextOwned::from_container(
343 vec![2323858949u64; 256],
344 CiphertextModulus::new_native(),
345 ),
346 degree: Degree::new(42),
347 message_modulus: MessageModulus(2),
348 carry_modulus: CarryModulus(2),
349 atomic_pattern: AtomicPatternKind::Standard(PBSOrder::BootstrapKeyswitch),
350 noise_level: NoiseLevel::NOMINAL,
351 };
352
353 assert_ne!(c1, c2);
354
355 c1.clone_from(&c2);
356 assert_eq!(c1, c2);
357 }
358
359 #[test]
360 fn test_clone_from_same_lwe_size_different_modulus_ci_run_filter() {
361 let mut c1 = Ciphertext {
362 ct: LweCiphertextOwned::from_container(
363 vec![1u64; 256],
364 CiphertextModulus::try_new_power_of_2(32).unwrap(),
365 ),
366 degree: Degree::new(1),
367 message_modulus: MessageModulus(1),
368 carry_modulus: CarryModulus(1),
369 atomic_pattern: AtomicPatternKind::Standard(PBSOrder::KeyswitchBootstrap),
370 noise_level: NoiseLevel::NOMINAL,
371 };
372
373 let c2 = Ciphertext {
374 ct: LweCiphertextOwned::from_container(
375 vec![2323858949u64; 256],
376 CiphertextModulus::new_native(),
377 ),
378 degree: Degree::new(42),
379 message_modulus: MessageModulus(2),
380 carry_modulus: CarryModulus(2),
381 atomic_pattern: AtomicPatternKind::Standard(PBSOrder::BootstrapKeyswitch),
382 noise_level: NoiseLevel::NOMINAL,
383 };
384
385 assert_ne!(c1, c2);
386
387 c1.clone_from(&c2);
388 assert_eq!(c1, c2);
389 }
390
391 #[test]
392 fn test_clone_from_different_lwe_size_same_modulus_ci_run_filter() {
393 let mut c1 = Ciphertext {
394 ct: LweCiphertextOwned::from_container(
395 vec![1u64; 512],
396 CiphertextModulus::new_native(),
397 ),
398 degree: Degree::new(1),
399 message_modulus: MessageModulus(1),
400 carry_modulus: CarryModulus(1),
401 atomic_pattern: AtomicPatternKind::Standard(PBSOrder::KeyswitchBootstrap),
402 noise_level: NoiseLevel::NOMINAL,
403 };
404
405 let c2 = Ciphertext {
406 ct: LweCiphertextOwned::from_container(
407 vec![2323858949u64; 256],
408 CiphertextModulus::new_native(),
409 ),
410 degree: Degree::new(42),
411 message_modulus: MessageModulus(2),
412 carry_modulus: CarryModulus(2),
413 atomic_pattern: AtomicPatternKind::Standard(PBSOrder::BootstrapKeyswitch),
414 noise_level: NoiseLevel::NOMINAL,
415 };
416
417 assert_ne!(c1, c2);
418
419 c1.clone_from(&c2);
420 assert_eq!(c1, c2);
421 }
422
423 #[test]
424 fn test_re_randomize_ciphertext_ci_run_filter() {
425 let params = TEST_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
426 let comp_params = TEST_COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
427 let cpk_params = TEST_PARAM_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128_ZKV2;
428 let ks_params = TEST_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
429
430 let key_entry = KEY_CACHE.get_from_param(params);
431 let (cks, sks) = (key_entry.client_key(), key_entry.server_key());
433 let cpk_private_key = CompactPrivateKey::new(cpk_params);
434 let cpk = CompactPublicKey::new(&cpk_private_key);
435 let ksk_material =
436 KeySwitchingKeyBuildHelper::new((&cpk_private_key, None), (cks, sks), ks_params)
437 .key_switching_key_material;
438 let ksk_material = ksk_material.as_view();
439
440 let private_compression_key = cks.new_compression_private_key(comp_params);
441 let (compression_key, decompression_key) =
442 cks.new_compression_decompression_keys(&private_compression_key);
443
444 let msg = cks.parameters().message_modulus().0 - 1;
445
446 for _ in 0..10 {
447 let ct = cks.encrypt(msg);
448
449 let compressed = compression_key.compress_ciphertexts_into_list(&[ct]);
450
451 let decompressed = decompression_key.unpack(&compressed, 0).unwrap();
452
453 let mut re_randomizer_context = ReRandomizationContext::new(*b"TFHE_Rrd", *b"TFHE_Enc");
454 re_randomizer_context.add_ciphertext(&decompressed);
455
456 let mut seed_gen = re_randomizer_context.finalize();
457
458 let seed = seed_gen.next_seed();
459
460 let mut re_randomized = decompressed.clone();
461 re_randomized
462 .re_randomize_with_compact_public_key_encryption(&cpk, &ksk_material, seed)
463 .unwrap();
464
465 assert_ne!(decompressed, re_randomized);
466
467 let pbsed = sks.bitand(&re_randomized, &re_randomized);
468
469 let dec = cks.decrypt_message_and_carry(&pbsed);
470
471 assert_eq!(dec, msg);
472 }
473 }
474}