poulpy/core/src/keys.rs
core/src/keys.rsRaw
1use backend::{
2 Backend, FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxDft, ScalarZnxDftAlloc, ScalarZnxDftOps, ScratchOwned, VecZnxDft,
3 ZnxInfos, ZnxZero,
4};
5use sampling::source::Source;
6
7use crate::{GLWECiphertextFourier, Infos};
8
9#[derive(Clone, Copy, Debug)]
10pub(crate) enum SecretDistribution {
11 TernaryFixed(usize), // Ternary with fixed Hamming weight
12 TernaryProb(f64), // Ternary with probabilistic Hamming weight
13 BinaryFixed(usize), // Binary with fixed Hamming weight
14 BinaryProb(f64), // Binary with probabilistic Hamming weight
15 BinaryBlock(usize), // Binary split in block of size 2^k
16 ZERO, // Debug mod
17 NONE, // Unitialized
18}
19
20pub struct LWESecret<T> {
21 pub(crate) data: ScalarZnx<T>,
22 pub(crate) dist: SecretDistribution,
23}
24
25impl LWESecret<Vec<u8>> {
26 pub fn alloc(n: usize) -> Self {
27 Self {
28 data: ScalarZnx::new(n, 1),
29 dist: SecretDistribution::NONE,
30 }
31 }
32}
33
34impl<DataSelf> LWESecret<DataSelf> {
35 pub fn n(&self) -> usize {
36 self.data.n()
37 }
38
39 pub fn log_n(&self) -> usize {
40 self.data.log_n()
41 }
42
43 pub fn rank(&self) -> usize {
44 self.data.cols()
45 }
46}
47
48impl<D: AsRef<[u8]> + AsMut<[u8]>> LWESecret<D> {
49 pub fn fill_ternary_prob(&mut self, prob: f64, source: &mut Source) {
50 self.data.fill_ternary_prob(0, prob, source);
51 self.dist = SecretDistribution::TernaryProb(prob);
52 }
53
54 pub fn fill_ternary_hw(&mut self, hw: usize, source: &mut Source) {
55 self.data.fill_ternary_hw(0, hw, source);
56 self.dist = SecretDistribution::TernaryFixed(hw);
57 }
58
59 pub fn fill_binary_prob(&mut self, prob: f64, source: &mut Source) {
60 self.data.fill_binary_prob(0, prob, source);
61 self.dist = SecretDistribution::BinaryProb(prob);
62 }
63
64 pub fn fill_binary_hw(&mut self, hw: usize, source: &mut Source) {
65 self.data.fill_binary_hw(0, hw, source);
66 self.dist = SecretDistribution::BinaryFixed(hw);
67 }
68
69 pub fn fill_binary_block(&mut self, block_size: usize, source: &mut Source) {
70 self.data.fill_binary_block(0, block_size, source);
71 self.dist = SecretDistribution::BinaryBlock(block_size);
72 }
73
74 pub fn fill_zero(&mut self) {
75 self.data.zero();
76 self.dist = SecretDistribution::ZERO;
77 }
78}
79
80pub struct GLWESecret<T, B: Backend> {
81 pub(crate) data: ScalarZnx<T>,
82 pub(crate) data_fourier: ScalarZnxDft<T, B>,
83 pub(crate) dist: SecretDistribution,
84}
85
86impl<B: Backend> GLWESecret<Vec<u8>, B> {
87 pub fn alloc(module: &Module<B>, rank: usize) -> Self {
88 Self {
89 data: module.new_scalar_znx(rank),
90 data_fourier: module.new_scalar_znx_dft(rank),
91 dist: SecretDistribution::NONE,
92 }
93 }
94
95 pub fn bytes_of(module: &Module<B>, rank: usize) -> usize {
96 module.bytes_of_scalar_znx(rank) + module.bytes_of_scalar_znx_dft(rank)
97 }
98}
99
100impl<DataSelf, B: Backend> GLWESecret<DataSelf, B> {
101 pub fn n(&self) -> usize {
102 self.data.n()
103 }
104
105 pub fn log_n(&self) -> usize {
106 self.data.log_n()
107 }
108
109 pub fn rank(&self) -> usize {
110 self.data.cols()
111 }
112}
113
114impl<S: AsMut<[u8]> + AsRef<[u8]>> GLWESecret<S, FFT64> {
115 pub fn fill_ternary_prob(&mut self, module: &Module<FFT64>, prob: f64, source: &mut Source) {
116 (0..self.rank()).for_each(|i| {
117 self.data.fill_ternary_prob(i, prob, source);
118 });
119 self.prep_fourier(module);
120 self.dist = SecretDistribution::TernaryProb(prob);
121 }
122
123 pub fn fill_ternary_hw(&mut self, module: &Module<FFT64>, hw: usize, source: &mut Source) {
124 (0..self.rank()).for_each(|i| {
125 self.data.fill_ternary_hw(i, hw, source);
126 });
127 self.prep_fourier(module);
128 self.dist = SecretDistribution::TernaryFixed(hw);
129 }
130
131 pub fn fill_binary_prob(&mut self, module: &Module<FFT64>, prob: f64, source: &mut Source) {
132 (0..self.rank()).for_each(|i| {
133 self.data.fill_binary_prob(i, prob, source);
134 });
135 self.prep_fourier(module);
136 self.dist = SecretDistribution::BinaryProb(prob);
137 }
138
139 pub fn fill_binary_hw(&mut self, module: &Module<FFT64>, hw: usize, source: &mut Source) {
140 (0..self.rank()).for_each(|i| {
141 self.data.fill_binary_hw(i, hw, source);
142 });
143 self.prep_fourier(module);
144 self.dist = SecretDistribution::BinaryFixed(hw);
145 }
146
147 pub fn fill_binary_block(&mut self, module: &Module<FFT64>, block_size: usize, source: &mut Source) {
148 (0..self.rank()).for_each(|i| {
149 self.data.fill_binary_block(i, block_size, source);
150 });
151 self.prep_fourier(module);
152 self.dist = SecretDistribution::BinaryBlock(block_size);
153 }
154
155 pub fn fill_zero(&mut self) {
156 self.data.zero();
157 self.dist = SecretDistribution::ZERO;
158 }
159
160 pub(crate) fn prep_fourier(&mut self, module: &Module<FFT64>) {
161 (0..self.rank()).for_each(|i| {
162 module.svp_prepare(&mut self.data_fourier, i, &self.data, i);
163 });
164 }
165}
166
167pub struct GLWEPublicKey<D, B: Backend> {
168 pub(crate) data: GLWECiphertextFourier<D, B>,
169 pub(crate) dist: SecretDistribution,
170}
171
172impl<B: Backend> GLWEPublicKey<Vec<u8>, B> {
173 pub fn alloc(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self {
174 Self {
175 data: GLWECiphertextFourier::alloc(module, basek, k, rank),
176 dist: SecretDistribution::NONE,
177 }
178 }
179
180 pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rank: usize) -> usize {
181 GLWECiphertextFourier::<Vec<u8>, B>::bytes_of(module, basek, k, rank)
182 }
183}
184
185impl<T, B: Backend> Infos for GLWEPublicKey<T, B> {
186 type Inner = VecZnxDft<T, B>;
187
188 fn inner(&self) -> &Self::Inner {
189 &self.data.data
190 }
191
192 fn basek(&self) -> usize {
193 self.data.basek
194 }
195
196 fn k(&self) -> usize {
197 self.data.k
198 }
199}
200
201impl<T, B: Backend> GLWEPublicKey<T, B> {
202 pub fn rank(&self) -> usize {
203 self.cols() - 1
204 }
205}
206
207impl<C: AsRef<[u8]> + AsMut<[u8]>> GLWEPublicKey<C, FFT64> {
208 pub fn generate_from_sk<S: AsRef<[u8]>>(
209 &mut self,
210 module: &Module<FFT64>,
211 sk: &GLWESecret<S, FFT64>,
212 source_xa: &mut Source,
213 source_xe: &mut Source,
214 sigma: f64,
215 ) {
216 #[cfg(debug_assertions)]
217 {
218 match sk.dist {
219 SecretDistribution::NONE => panic!("invalid sk: SecretDistribution::NONE"),
220 _ => {}
221 }
222 }
223
224 // Its ok to allocate scratch space here since pk is usually generated only once.
225 let mut scratch: ScratchOwned = ScratchOwned::new(GLWECiphertextFourier::encrypt_sk_scratch_space(
226 module,
227 self.basek(),
228 self.k(),
229 self.rank(),
230 ));
231
232 self.data
233 .encrypt_zero_sk(module, sk, source_xa, source_xe, sigma, scratch.borrow());
234 self.dist = sk.dist;
235 }
236}