1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
// This is linked to the standard ciphertext but has special code in it
use crate::core_crypto::commons::math::random::XofSeed;
use crate::integer::ciphertext::AsShortintCiphertextSlice;
use crate::integer::key_switching_key::KeySwitchingKeyMaterialView;
use crate::integer::CompactPublicKey;
pub use crate::shortint::ciphertext::{
ReRandomizationHashAlgo, ReRandomizationSeed, ReRandomizationSeedHasher,
};
use crate::shortint::Ciphertext;
use crate::Result;
#[cfg(feature = "zk-pok")]
use super::ProvenCompactCiphertextList;
#[derive(Clone, Copy)]
pub enum ReRandomizationKey<'key> {
LegacyDedicatedCPK {
cpk: &'key CompactPublicKey,
ksk: KeySwitchingKeyMaterialView<'key>,
},
DerivedCPKWithoutKeySwitch {
cpk: &'key CompactPublicKey,
},
}
impl ReRandomizationKey<'_> {
pub fn get_cpk_and_optional_ksk(
&self,
) -> (&CompactPublicKey, Option<&KeySwitchingKeyMaterialView<'_>>) {
match self {
ReRandomizationKey::LegacyDedicatedCPK { cpk, ksk } => (cpk, Some(ksk)),
ReRandomizationKey::DerivedCPKWithoutKeySwitch { cpk } => (cpk, None),
}
}
}
/// The context that will be hashed and used to generate unique [`ReRandomizationSeed`].
pub struct ReRandomizationContext {
/// The inner hasher
inner_context: crate::shortint::ciphertext::ReRandomizationContext,
/// The number of integer ciphertexts added to the context. This will define the number of
/// seeds that can be drawn from it
ct_count: u64,
/// Temporary buffer with all the individual shortint cts coefficients that will be hashed in
/// the context
ct_coeffs_buffer: Vec<u64>,
/// Temporary buffer with all the ciphertext metadata
meta_buffer: Vec<u8>,
/// A piece of data that should be unique to the function being called
fn_description: Vec<u8>,
}
impl ReRandomizationContext {
/// Create a new re-randomization context with the default seed hasher (blake3).
///
/// `rerand_seeder_domain_separator` is the domain separator that will be fed into the
/// seed generator.
/// `public_encryption_domain_separator` is the domain separator that will be used along this
/// seed to generate the encryptions of zero.
/// `fn_description` is a unique sequence of bytes that represents the functions called on the
/// re-randomized values.
///
/// (See [`XofSeed`] for more information)
///
/// # Example
/// ```rust
/// use tfhe::integer::ciphertext::ReRandomizationContext;
/// // Simulate a 256 bits nonce
/// let nonce: [u8; 256 / 8] = core::array::from_fn(|_| rand::random());
/// let _re_rand_context = ReRandomizationContext::new(
/// *b"TFHE_Rrd",
/// [b"FheUint64+FheUint64".as_slice(), &nonce],
/// *b"TFHE_Enc"
/// );
pub fn new<'a>(
rerand_seeder_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN],
fn_description: impl IntoIterator<Item = &'a [u8]>,
public_encryption_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN],
) -> Self {
Self {
inner_context: crate::shortint::ciphertext::ReRandomizationContext::new(
rerand_seeder_domain_separator,
public_encryption_domain_separator,
),
ct_coeffs_buffer: Vec::new(),
ct_count: 0,
meta_buffer: Vec::new(),
fn_description: fn_description.into_iter().flatten().copied().collect(),
}
}
/// Create a new re-randomization context with the provided seed hasher.
pub fn new_with_hasher<'a>(
fn_description: impl IntoIterator<Item = &'a [u8]>,
public_encryption_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN],
seed_hasher: ReRandomizationSeedHasher,
) -> Self {
Self {
inner_context: crate::shortint::ciphertext::ReRandomizationContext::new_with_hasher(
public_encryption_domain_separator,
seed_hasher,
),
ct_coeffs_buffer: Vec::new(),
ct_count: 0,
meta_buffer: Vec::new(),
fn_description: fn_description.into_iter().flatten().copied().collect(),
}
}
/// Add a new integer ciphertext to the context.
///
/// The ciphertexts added like this will be stored in a temporary buffer and only hashed during
/// the "finalize" step
pub fn add_ciphertext<T: AsShortintCiphertextSlice>(&mut self, ciphertext: &T) {
self.ct_coeffs_buffer.extend(
ciphertext
.as_ciphertext_slice()
.iter()
.flat_map(|ct| ct.ct.as_ref()),
);
self.ct_count += 1;
}
#[cfg(feature = "zk-pok")]
pub fn add_proven_ciphertext_list(&mut self, list: &ProvenCompactCiphertextList) {
self.ct_coeffs_buffer.extend(
list.ct_list
.proved_lists
.iter()
.flat_map(|list| list.0.ct_list.as_ref()),
);
self.meta_buffer.extend(
list.ct_list
.proved_lists
.iter()
.flat_map(|list| list.1.to_le_bytes()),
);
// We draw only one seed for the full list
self.ct_count += 1;
}
/// Add a metadata buffer to the context.
///
/// These bytes will be added to a temporary buffer and will only be hashed during the
/// "finalize" step
pub fn add_bytes(&mut self, data: &[u8]) {
self.meta_buffer.extend_from_slice(data);
}
/// Consumes the context to instantiate a seed generator
pub fn finalize(mut self) -> ReRandomizationSeedGen {
self.inner_context
.add_ciphertext_data_slice(&self.ct_coeffs_buffer);
self.inner_context.add_bytes(&self.meta_buffer);
self.inner_context.add_bytes(&self.fn_description);
ReRandomizationSeedGen {
inner: self.inner_context.finalize(),
remaining_seeds_count: self.ct_count,
}
}
}
/// A generator that can be used to obtain seeds needed to re-randomize individual ciphertexts.
///
/// This will refuse to generate more seeds than the number of ciphertext added into the context.
pub struct ReRandomizationSeedGen {
inner: crate::shortint::ciphertext::ReRandomizationSeedGen,
remaining_seeds_count: u64,
}
impl ReRandomizationSeedGen {
/// Generate the next seed from the seeder.
///
/// Returns an error if more seeds have been generated than the number of ciphertext added into
/// the context.
pub fn next_seed(&mut self) -> Result<ReRandomizationSeed> {
if self.remaining_seeds_count > 0 {
self.remaining_seeds_count -= 1;
Ok(self.inner.next_seed())
} else {
Err(crate::error!("Trying to draw more seeds than the number of ciphertexts that were added to the context"))
}
}
}
pub(crate) fn re_randomize_ciphertext_blocks(
blocks: &mut [Ciphertext],
re_randomization_key: ReRandomizationKey<'_>,
seed: ReRandomizationSeed,
) -> crate::Result<()> {
let (compact_public_key, key_switching_key_material) =
re_randomization_key.get_cpk_and_optional_ksk();
compact_public_key.key.re_randomize_ciphertexts(
blocks,
key_switching_key_material.map(|k| k.material).as_ref(),
seed,
)
}
pub struct PrfReRandomizationContext {
inner: crate::shortint::ciphertext::ReRandomizationContext,
}
impl PrfReRandomizationContext {
/// Create a new re-randomization context with the default seed hasher (blake3).
///
/// `rerand_seeder_domain_separator` is the domain separator that will be fed into the
/// seed generator.
/// `public_encryption_domain_separator` is the domain separator that will be used along this
/// seed to generate the encryptions of zero.
///
/// (See [`XofSeed`] for more information)
///
/// # Example
/// ```rust
/// use tfhe::integer::ciphertext::PrfReRandomizationContext;
/// let _re_rand_context = PrfReRandomizationContext::new(
/// *b"PRF_RRND",
/// *b"TFHE_Enc"
/// );
pub fn new(
rerand_seeder_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN],
public_encryption_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN],
) -> Self {
Self {
inner: crate::shortint::ciphertext::ReRandomizationContext::new(
rerand_seeder_domain_separator,
public_encryption_domain_separator,
),
}
}
/// Create a new re-randomization context with the provided seed hasher.
pub fn new_with_hasher(
public_encryption_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN],
seed_hasher: ReRandomizationSeedHasher,
) -> Self {
Self {
inner: crate::shortint::ciphertext::ReRandomizationContext::new_with_hasher(
public_encryption_domain_separator,
seed_hasher,
),
}
}
pub(crate) fn inner(&self) -> &crate::shortint::ciphertext::ReRandomizationContext {
&self.inner
}
}
impl Default for PrfReRandomizationContext {
fn default() -> Self {
Self {
inner: crate::shortint::ciphertext::ReRandomizationContext::new(
crate::shortint::oprf::TFHE_PRF_RERAND_DOMAIN_SEPARATOR,
crate::shortint::public_key::compact::TFHE_PKE_DOMAIN_SEPARATOR,
),
}
}
}