Skip to main content

tfhe/shortint/ciphertext/
standard.rs

1//! Module with the definition of the Ciphertext.
2use 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    // For correctness reasons this field MUST remain private, this forces the use of the accessor
24    // which has noise checks enabled on demand
25    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(&param.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
57// Use destructuring to also have a compile error
58// if ever a new member is added to Ciphertext
59// and is not handled here.
60//
61// And a warning if a member is destructured but not used.
62impl 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    /// Decrypts a trivial ciphertext
163    ///
164    /// Trivial ciphertexts are ciphertexts which are not encrypted
165    /// meaning they can be decrypted by any key, or even without a key.
166    ///
167    /// For debugging it can be useful to use trivial ciphertext to speed up
168    /// execution, and use [Self::decrypt_trivial] to decrypt temporary values
169    /// and debug.
170    ///
171    ///
172    /// # Example
173    ///
174    /// ```rust
175    /// use tfhe::shortint::gen_keys;
176    /// use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
177    ///
178    /// // Generate the client key and the server key:
179    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
180    ///
181    /// let msg = 1;
182    /// let msg2 = 2;
183    ///
184    /// // Trivial encryption
185    /// let trivial_ct = sks.create_trivial(msg);
186    /// let non_trivial_ct = cks.encrypt(msg2);
187    ///
188    /// let res = trivial_ct.decrypt_trivial();
189    /// assert_eq!(Ok(1), res);
190    ///
191    /// let res = non_trivial_ct.decrypt_trivial();
192    /// assert!(res.is_err());
193    ///
194    /// // Doing operations that mixes trivial and non trivial
195    /// // will always return a non trivial
196    /// let ct_res = sks.add(&trivial_ct, &non_trivial_ct);
197    /// let res = ct_res.decrypt_trivial();
198    /// assert!(res.is_err());
199    ///
200    /// // Doing operations using only trivial ciphertexts
201    /// // will return a trivial
202    /// let ct_res = sks.add(&trivial_ct, &trivial_ct);
203    /// let res = ct_res.decrypt_trivial();
204    /// assert_eq!(Ok(2), res);
205    /// ```
206    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    /// See [Self::decrypt_trivial].
221    /// # Example
222    ///
223    /// ```rust
224    /// use tfhe::shortint::gen_keys;
225    /// use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
226    ///
227    /// // Generate the client key and the server key:
228    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
229    ///
230    /// let msg = 2u64;
231    /// let clear = 3u64;
232    ///
233    /// let mut trivial_ct = sks.create_trivial(msg);
234    ///
235    /// sks.unchecked_scalar_add_assign(&mut trivial_ct, clear as u8);
236    ///
237    /// let res = trivial_ct.decrypt_trivial();
238    /// let expected = (msg + clear) % PARAM_MESSAGE_2_CARRY_2_KS_PBS.message_modulus.0;
239    /// assert_eq!(Ok(expected), res);
240    ///
241    /// let res = trivial_ct.decrypt_trivial_message_and_carry();
242    /// assert_eq!(Ok(msg + clear), res);
243    /// ```
244    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    /// This function can be called after decompressing a [`Ciphertext`] from a
257    /// [`CompressedCiphertextList`](super::compressed_ciphertext_list::CompressedCiphertextList) to
258    /// re-randomize it before any computations.
259    ///
260    /// This function only supports [`PBSOrder::KeyswitchBootstrap`] ordered
261    /// [`Ciphertext`]/[`ServerKey`](crate::shortint::ServerKey).
262    ///
263    /// It uses a [`CompactPublicKey`] to generate a new encryption of 0, a
264    /// [`KeySwitchingKeyMaterialView`] is required to keyswitch between the secret key used to
265    /// generate the [`CompactPublicKey`] to the "big"/post PBS/GLWE secret key from the
266    /// [`ServerKey`](crate::shortint::ServerKey).
267    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        // Generate the client key and the server key:
432        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}