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}