concrete_integer/client_key/
multi_crt.rs

1use crate::ciphertext::KeyId;
2use crate::client_key::utils::i_crt;
3use crate::CrtMultiCiphertext;
4
5use concrete_shortint::parameters::MessageModulus;
6
7use serde::{Deserialize, Serialize};
8
9/// Generates a KeyId vector
10///
11/// Key Ids are used to choose a specific key for each block.
12pub fn gen_key_id(id: &[usize]) -> Vec<KeyId> {
13    id.iter().copied().map(KeyId).collect()
14}
15
16/// Client key for CRT decomposition.
17///
18/// As opposed to [crate::ClientKey] and [crate::CrtClientKey],
19/// not all blocks in the ciphertext will use the same parameters.
20#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
21pub struct CrtMultiClientKey {
22    pub(crate) keys: Vec<concrete_shortint::ClientKey>,
23    pub(crate) key_ids: Vec<KeyId>,
24}
25
26/// Creates a CrtMultiClientKey from a vector of shortint keys.
27///
28/// Each key will encrypt one block (in order).
29impl From<Vec<concrete_shortint::ClientKey>> for CrtMultiClientKey {
30    fn from(keys: Vec<concrete_shortint::ClientKey>) -> Self {
31        let key_ids = (0..keys.len()).map(KeyId).collect();
32
33        Self { keys, key_ids }
34    }
35}
36
37impl CrtMultiClientKey {
38    /// Create a client key.
39    ///
40    /// # Example
41    ///
42    /// ```rust
43    /// use concrete_integer::CrtMultiClientKey;
44    /// use concrete_shortint::parameters::{
45    ///     DEFAULT_PARAMETERS, PARAM_MESSAGE_2_CARRY_2, PARAM_MESSAGE_3_CARRY_3,
46    /// };
47    ///
48    /// // Generate the client key:
49    /// let cks = CrtMultiClientKey::new_many_keys(&[PARAM_MESSAGE_2_CARRY_2, PARAM_MESSAGE_3_CARRY_3]);
50    /// ```
51    pub fn new_many_keys(
52        parameter_set: &[concrete_shortint::parameters::Parameters],
53    ) -> CrtMultiClientKey {
54        let mut key = Vec::with_capacity(parameter_set.len());
55        let mut id = Vec::with_capacity(parameter_set.len());
56
57        for (i, param) in parameter_set.iter().enumerate() {
58            key.push(concrete_shortint::ClientKey::new(*param));
59            id.push(KeyId(i));
60        }
61        CrtMultiClientKey {
62            keys: key,
63            key_ids: id,
64        }
65    }
66
67    /// Encrypts an integer using the CRT decomposition, where each block is associated to
68    /// dedicated key.
69    ///
70    /// # Example
71    ///
72    /// ```rust
73    /// use concrete_integer::{gen_key_id, CrtMultiClientKey};
74    /// use concrete_shortint::parameters::{PARAM_MESSAGE_2_CARRY_2, PARAM_MESSAGE_3_CARRY_3};
75    ///
76    /// // Generate the client key:
77    /// let cks = CrtMultiClientKey::new_many_keys(&[PARAM_MESSAGE_2_CARRY_2, PARAM_MESSAGE_3_CARRY_3]);
78    ///
79    /// let msg = 15_u64;
80    ///
81    /// // Encryption of one message:
82    /// let basis: Vec<u64> = vec![2, 3, 5];
83    /// // The first two blocks are encrypted using the first key,
84    /// // the third block with the second key
85    /// let keys_id = gen_key_id(&[0, 0, 1]);
86    /// let ct = cks.encrypt(&msg, &basis, &keys_id);
87    ///
88    /// // Decryption:
89    /// let dec = cks.decrypt(&ct);
90    /// assert_eq!(msg, dec);
91    /// ```
92    pub fn encrypt(
93        &self,
94        message: &u64,
95        base_vec: &[u64],
96        keys_id: &[KeyId],
97    ) -> CrtMultiCiphertext {
98        // Empty vector of ciphertexts
99        let mut ctxt_vect: Vec<concrete_shortint::Ciphertext> = Vec::new();
100        let bv = base_vec.to_vec();
101        let keys = keys_id.to_vec();
102
103        // Put each decomposition into a new ciphertext
104        for (modulus, id) in base_vec.iter().zip(keys_id.iter()) {
105            // encryption
106            let ct = self.keys[id.0]
107                .encrypt_with_message_modulus(*message, MessageModulus(*modulus as usize));
108
109            // Put it in the vector of ciphertexts
110            ctxt_vect.push(ct);
111        }
112
113        CrtMultiCiphertext {
114            blocks: ctxt_vect,
115            moduli: bv,
116            key_ids: keys,
117        }
118    }
119
120    /// Decrypts an integer in the multi-key CRT settings.
121    ///
122    ///
123    /// # Example
124    ///
125    /// ```rust
126    /// use concrete_integer::{gen_key_id, CrtMultiClientKey};
127    /// use concrete_shortint::parameters::{PARAM_MESSAGE_2_CARRY_2, PARAM_MESSAGE_3_CARRY_3};
128    ///
129    /// // Generate the client key:
130    /// let cks = CrtMultiClientKey::new_many_keys(&[PARAM_MESSAGE_2_CARRY_2, PARAM_MESSAGE_3_CARRY_3]);
131    ///
132    /// let msg = 27_u64;
133    ///
134    /// // Encryption of one message:
135    /// let basis: Vec<u64> = vec![2, 3, 5];
136    /// // The first two blocks are encrypted using the first key,
137    /// // the third block with the second key
138    /// let keys_id = gen_key_id(&[0, 0, 1]);
139    /// let ct = cks.encrypt(&msg, &basis, &keys_id);
140    ///
141    /// // Decryption:
142    /// let dec = cks.decrypt(&ct);
143    /// assert_eq!(msg, dec);
144    /// ```
145    pub fn decrypt(&self, ctxt: &CrtMultiCiphertext) -> u64 {
146        let mut val: Vec<u64> = vec![];
147
148        // Decrypting each block individually
149        for ((c_i, b_i), k_i) in ctxt
150            .blocks
151            .iter()
152            .zip(ctxt.moduli.iter())
153            .zip(ctxt.key_ids.iter())
154        {
155            // Decrypt the component i of the integer and multiply it by the radix product
156            val.push(self.keys[k_i.0].decrypt_message_and_carry(c_i) % b_i);
157        }
158
159        // Computing the inverse CRT to recompose the message
160        let result = i_crt(&ctxt.moduli, &val);
161
162        let whole_modulus: u64 = ctxt.moduli.iter().copied().product();
163
164        result % whole_modulus
165    }
166}