poulpy/rlwe/src/trace.rs
rlwe/src/trace.rsRaw
1use crate::{
2 automorphism::AutomorphismKey, ciphertext::Ciphertext, elem::ElemCommon, parameters::Parameters,
3};
4use base2k::{
5 Module, VecZnx, VecZnxBig, VecZnxBigOps, VecZnxDft, VecZnxDftOps, VmpPMatOps, assert_alignement,
6};
7use std::collections::HashMap;
8
9pub fn trace_galois_elements(module: &Module) -> Vec<i64> {
10 let mut gal_els: Vec<i64> = Vec::new();
11 (0..module.log_n()).for_each(|i| {
12 if i == 0 {
13 gal_els.push(-1);
14 } else {
15 gal_els.push(module.galois_element(1 << (i - 1)));
16 }
17 });
18 gal_els
19}
20
21impl Parameters {
22 pub fn trace_tmp_bytes(&self, res_logq: usize, in_logq: usize, gct_logq: usize) -> usize {
23 self.automorphism_tmp_bytes(res_logq, in_logq, gct_logq)
24 }
25}
26
27pub fn trace_tmp_bytes(
28 module: &Module,
29 c_cols: usize,
30 a_cols: usize,
31 b_rows: usize,
32 b_cols: usize,
33) -> usize {
34 return module.vmp_apply_dft_to_dft_tmp_bytes(c_cols, a_cols, b_rows, b_cols)
35 + 2 * module.bytes_of_vec_znx_dft(std::cmp::min(c_cols, a_cols));
36}
37
38pub fn trace_inplace(
39 module: &Module,
40 a: &mut Ciphertext<VecZnx>,
41 start: usize,
42 end: usize,
43 b: &HashMap<i64, AutomorphismKey>,
44 b_cols: usize,
45 tmp_bytes: &mut [u8],
46) {
47 let cols: usize = a.cols();
48
49 let b_rows: usize;
50
51 if let Some((_, key)) = b.iter().next() {
52 b_rows = key.value.rows();
53 #[cfg(debug_assertions)]
54 {
55 println!("{} {}", b_cols, key.value.cols());
56 assert!(b_cols <= key.value.cols())
57 }
58 } else {
59 panic!("b: HashMap<i64, AutomorphismKey>, is empty")
60 }
61
62 #[cfg(debug_assertions)]
63 {
64 assert!(start <= end);
65 assert!(end <= module.n());
66 assert!(tmp_bytes.len() >= trace_tmp_bytes(module, cols, cols, b_rows, b_cols));
67 assert_alignement(tmp_bytes.as_ptr());
68 }
69
70 let cols: usize = std::cmp::min(b_cols, a.cols());
71
72 let (tmp_bytes_b1_dft, tmp_bytes) = tmp_bytes.split_at_mut(module.bytes_of_vec_znx_dft(cols));
73 let (tmp_bytes_res_dft, tmp_bytes) =
74 tmp_bytes.split_at_mut(module.bytes_of_vec_znx_dft(b_cols));
75
76 let mut a1_dft: VecZnxDft = module.new_vec_znx_dft_from_bytes_borrow(cols, tmp_bytes_b1_dft);
77 let mut res_dft: VecZnxDft =
78 module.new_vec_znx_dft_from_bytes_borrow(b_cols, tmp_bytes_res_dft);
79 let mut res_big: VecZnxBig = res_dft.as_vec_znx_big();
80
81 let log_base2k: usize = a.log_base2k();
82
83 (start..end).for_each(|i| {
84 a.at_mut(0).rsh(log_base2k, 1, tmp_bytes);
85 a.at_mut(1).rsh(log_base2k, 1, tmp_bytes);
86
87 let p: i64;
88 if i == 0 {
89 p = -1;
90 } else {
91 p = module.galois_element(1 << (i - 1));
92 }
93
94 if let Some(key) = b.get(&p) {
95 module.vec_znx_dft(&mut a1_dft, a.at(1));
96
97 // a[0] = NORMALIZE(a[0] + AUTO(a[0] + IDFT(<DFT(a[1]), key[0]>)))
98 module.vmp_apply_dft_to_dft(&mut res_dft, &a1_dft, key.value.at(0), tmp_bytes);
99 module.vec_znx_idft_tmp_a(&mut res_big, &mut res_dft);
100 module.vec_znx_big_add_small_inplace(&mut res_big, a.at(0));
101 module.vec_znx_big_automorphism_inplace(p, &mut res_big);
102 module.vec_znx_big_add_small_inplace(&mut res_big, a.at(0));
103 module.vec_znx_big_normalize(a.log_base2k(), a.at_mut(0), &mut res_big, tmp_bytes);
104
105 // a[1] = NORMALIZE(a[1] + AUTO(IDFT(<DFT(a[1]), key[1]>)))
106 module.vmp_apply_dft_to_dft(&mut res_dft, &a1_dft, key.value.at(1), tmp_bytes);
107 module.vec_znx_idft_tmp_a(&mut res_big, &mut res_dft);
108 module.vec_znx_big_automorphism_inplace(p, &mut res_big);
109 module.vec_znx_big_add_small_inplace(&mut res_big, a.at(1));
110 module.vec_znx_big_normalize(a.log_base2k(), a.at_mut(1), &mut res_big, tmp_bytes);
111 } else {
112 panic!("b[{}] is empty", p)
113 }
114 })
115}
116
117#[cfg(test)]
118mod test {
119 use super::{trace_galois_elements, trace_inplace};
120 use crate::{
121 automorphism::AutomorphismKey,
122 ciphertext::Ciphertext,
123 decryptor::decrypt_rlwe,
124 elem::ElemCommon,
125 encryptor::encrypt_rlwe_sk,
126 keys::SecretKey,
127 parameters::{DEFAULT_SIGMA, Parameters, ParametersLiteral},
128 plaintext::Plaintext,
129 };
130 use base2k::{BACKEND, Encoding, Module, SvpPPol, SvpPPolOps, VecZnx, alloc_aligned};
131 use sampling::source::{Source, new_seed};
132 use std::collections::HashMap;
133
134 #[test]
135 fn test_trace_inplace() {
136 let log_base2k: usize = 10;
137 let log_q: usize = 50;
138 let log_p: usize = 15;
139
140 // Basic parameters with enough limbs to test edge cases
141 let params_lit: ParametersLiteral = ParametersLiteral {
142 backend: BACKEND::FFT64,
143 log_n: 12,
144 log_q: log_q,
145 log_p: log_p,
146 log_base2k: log_base2k,
147 log_scale: 20,
148 xe: 3.2,
149 xs: 1 << 11,
150 };
151
152 let params: Parameters = Parameters::new(¶ms_lit);
153
154 let module: &Module = params.module();
155 let log_q: usize = params.log_q();
156 let log_qp: usize = params.log_qp();
157 let gct_rows: usize = params.cols_q();
158 let gct_cols: usize = params.cols_qp();
159
160 // scratch space
161 let mut tmp_bytes: Vec<u8> = alloc_aligned(
162 params.decrypt_rlwe_tmp_byte(log_q)
163 | params.encrypt_rlwe_sk_tmp_bytes(log_q)
164 | params.automorphism_key_new_tmp_bytes(gct_rows, log_qp)
165 | params.automorphism_tmp_bytes(log_q, log_q, log_qp),
166 );
167
168 // Samplers for public and private randomness
169 let mut source_xe: Source = Source::new(new_seed());
170 let mut source_xa: Source = Source::new(new_seed());
171 let mut source_xs: Source = Source::new(new_seed());
172
173 let mut sk: SecretKey = SecretKey::new(module);
174 sk.fill_ternary_hw(params.xs(), &mut source_xs);
175 let mut sk_svp_ppol: SvpPPol = module.new_svp_ppol();
176 module.svp_prepare(&mut sk_svp_ppol, &sk.0);
177
178 let gal_els: Vec<i64> = trace_galois_elements(module);
179
180 let auto_keys: HashMap<i64, AutomorphismKey> = AutomorphismKey::new_many(
181 module,
182 &gal_els,
183 &sk,
184 log_base2k,
185 gct_rows,
186 log_qp,
187 &mut source_xa,
188 &mut source_xe,
189 DEFAULT_SIGMA,
190 &mut tmp_bytes,
191 );
192
193 let mut data: Vec<i64> = vec![0i64; params.n()];
194
195 data.iter_mut()
196 .enumerate()
197 .for_each(|(i, x)| *x = 1 + i as i64);
198
199 let log_k: usize = 2 * log_base2k;
200
201 let mut ct: Ciphertext<VecZnx> = params.new_ciphertext(log_q);
202 let mut pt: Plaintext = params.new_plaintext(log_q);
203
204 pt.at_mut(0).encode_vec_i64(log_base2k, log_k, &data, 32);
205 pt.at_mut(0).normalize(log_base2k, &mut tmp_bytes);
206
207 pt.at(0).decode_vec_i64(log_base2k, log_k, &mut data);
208
209 pt.at(0).print(pt.cols(), 16);
210
211 encrypt_rlwe_sk(
212 module,
213 &mut ct.elem_mut(),
214 Some(pt.at(0)),
215 &sk_svp_ppol,
216 &mut source_xa,
217 &mut source_xe,
218 params.xe(),
219 &mut tmp_bytes,
220 );
221
222 trace_inplace(module, &mut ct, 0, 4, &auto_keys, gct_cols, &mut tmp_bytes);
223 trace_inplace(
224 module,
225 &mut ct,
226 4,
227 module.log_n(),
228 &auto_keys,
229 gct_cols,
230 &mut tmp_bytes,
231 );
232
233 // pt = dec(auto(ct)) - auto(pt)
234 decrypt_rlwe(
235 module,
236 pt.elem_mut(),
237 ct.elem(),
238 &sk_svp_ppol,
239 &mut tmp_bytes,
240 );
241
242 pt.at(0).print(pt.cols(), 16);
243
244 pt.at(0).decode_vec_i64(log_base2k, log_k, &mut data);
245
246 println!("trace: {:?}", &data[..16]);
247 }
248}