1use rand::Rng;
8
9use crate::gadget::{bit_decomp, flatten_matrix, powers_of_2};
10use crate::gsw::{encrypt, homomorphic_add, Ciphertext, GswPublicKey, GswSecretKey};
11use crate::modular::mod_q;
12use crate::params::Params;
13
14#[derive(Clone, Debug)]
16pub struct EvaluationKey {
17 pub encryptions: Vec<Ciphertext>,
18 #[allow(dead_code)]
19 params: Params,
20}
21
22pub fn gen_evaluation_key<R: Rng>(
24 rng: &mut R,
25 sk: &GswSecretKey,
26 pk: &GswPublicKey,
27) -> EvaluationKey {
28 let params = sk.params();
29 let bits = bit_decomp(&sk.s, params);
30
31 let encryptions: Vec<Ciphertext> = bits
32 .iter()
33 .map(|&b| encrypt(rng, pk, b as u8))
34 .collect();
35
36 EvaluationKey {
37 encryptions,
38 params: params.clone(),
39 }
40}
41
42fn homomorphic_linear_fixed(
44 params: &Params,
45 cts: &[Ciphertext],
46 coefficients: &[u64],
47) -> Ciphertext {
48 assert_eq!(cts.len(), coefficients.len());
49 let n = params.n_expanded;
50 let q = params.q;
51
52 let mut result = None;
53
54 for (ct, &coeff) in cts.iter().zip(coefficients.iter()) {
55 if coeff == 0 {
56 continue;
57 }
58
59 let mut scaled = vec![vec![0u64; n]; n];
60 for i in 0..n {
61 for j in 0..n {
62 scaled[i][j] = mod_q((ct[i][j] as i64) * (coeff as i64), q);
63 }
64 }
65 let scaled_flat = flatten_matrix(&scaled, params);
66
67 result = Some(match result {
68 None => scaled_flat,
69 Some(acc) => homomorphic_add(params, &acc, &scaled_flat),
70 });
71 }
72
73 result.unwrap_or_else(|| vec![vec![0u64; n]; n])
74}
75
76pub fn decrypt_linear_part_clear(sk: &GswSecretKey, ct: &Ciphertext) -> u64 {
79 let params = sk.params();
80 let q = params.q;
81 let l = params.l;
82 let n_expanded = params.n_expanded;
83 let row_idx = l - 1;
84
85 let v = powers_of_2(&sk.s, params);
86 let mut dot: i64 = 0;
87 for j in 0..n_expanded {
88 dot += (ct[row_idx][j] as i64) * (v[j] as i64);
89 }
90 mod_q(dot, q)
91}
92
93pub fn bootstrap(
96 params: &Params,
97 noisy_ct: &Ciphertext,
98 ek: &EvaluationKey,
99) -> Ciphertext {
100 let l = params.l;
101 let n_expanded = params.n_expanded;
102 let row_idx = l - 1;
103 let q = params.q;
104
105 let mut coefficients = vec![0u64; n_expanded];
106 let c_row = &noisy_ct[row_idx];
107
108 for i in 0..n_expanded {
109 let block = i / l;
110 let k = i % l;
111 let mut coef: i64 = 0;
112 for j_bit in 0..l {
113 let j = block * l + j_bit;
114 let term = mod_q(
115 (c_row[j] as i64) * ((1i64 << (k + j_bit)) as i64),
116 q,
117 ) as i64;
118 coef = mod_q(coef + term, q) as i64;
119 }
120 coefficients[i] = mod_q(coef, q) as u64;
121 }
122
123 homomorphic_linear_fixed(params, &ek.encryptions, &coefficients)
124}