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::{elem::Infos, glwe_ciphertext_fourier::GLWECiphertextFourier};
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 ZERO, // Debug mod
14 NONE,
15}
16
17pub struct SecretKey<T> {
18 pub(crate) data: ScalarZnx<T>,
19 pub(crate) dist: SecretDistribution,
20}
21
22impl SecretKey<Vec<u8>> {
23 pub fn alloc<B: Backend>(module: &Module<B>, rank: usize) -> Self {
24 Self {
25 data: module.new_scalar_znx(rank),
26 dist: SecretDistribution::NONE,
27 }
28 }
29
30 pub fn bytes_of(module: &Module<FFT64>, rank: usize) -> usize {
31 module.bytes_of_scalar_znx(rank + 1)
32 }
33}
34
35impl<DataSelf> SecretKey<DataSelf> {
36 pub fn n(&self) -> usize {
37 self.data.n()
38 }
39
40 pub fn log_n(&self) -> usize {
41 self.data.log_n()
42 }
43
44 pub fn rank(&self) -> usize {
45 self.data.cols()
46 }
47}
48
49impl<S: AsMut<[u8]> + AsRef<[u8]>> SecretKey<S> {
50 pub fn fill_ternary_prob(&mut self, prob: f64, source: &mut Source) {
51 (0..self.rank()).for_each(|i| {
52 self.data.fill_ternary_prob(i, prob, source);
53 });
54 self.dist = SecretDistribution::TernaryProb(prob);
55 }
56
57 pub fn fill_ternary_hw(&mut self, hw: usize, source: &mut Source) {
58 (0..self.rank()).for_each(|i| {
59 self.data.fill_ternary_hw(i, hw, source);
60 });
61 self.dist = SecretDistribution::TernaryFixed(hw);
62 }
63
64 pub fn fill_zero(&mut self) {
65 self.data.zero();
66 self.dist = SecretDistribution::ZERO;
67 }
68}
69
70pub struct SecretKeyFourier<T, B: Backend> {
71 pub(crate) data: ScalarZnxDft<T, B>,
72 pub(crate) dist: SecretDistribution,
73}
74
75impl<DataSelf, B: Backend> SecretKeyFourier<DataSelf, B> {
76 pub fn n(&self) -> usize {
77 self.data.n()
78 }
79
80 pub fn log_n(&self) -> usize {
81 self.data.log_n()
82 }
83
84 pub fn rank(&self) -> usize {
85 self.data.cols()
86 }
87}
88
89impl<B: Backend> SecretKeyFourier<Vec<u8>, B> {
90 pub fn alloc(module: &Module<B>, rank: usize) -> Self {
91 Self {
92 data: module.new_scalar_znx_dft(rank),
93 dist: SecretDistribution::NONE,
94 }
95 }
96
97 pub fn bytes_of(module: &Module<B>, rank: usize) -> usize {
98 module.bytes_of_scalar_znx_dft(rank + 1)
99 }
100}
101
102impl<D: AsRef<[u8]> + AsMut<[u8]>> SecretKeyFourier<D, FFT64> {
103 pub fn dft<S: AsRef<[u8]>>(&mut self, module: &Module<FFT64>, sk: &SecretKey<S>) {
104 #[cfg(debug_assertions)]
105 {
106 match sk.dist {
107 SecretDistribution::NONE => panic!("invalid sk: SecretDistribution::NONE"),
108 _ => {}
109 }
110
111 assert_eq!(self.n(), module.n());
112 assert_eq!(sk.n(), module.n());
113 assert_eq!(self.rank(), sk.rank());
114 }
115
116 (0..self.rank()).for_each(|i| {
117 module.svp_prepare(&mut self.data, i, &sk.data, i);
118 });
119 self.dist = sk.dist;
120 }
121}
122
123pub struct GLWEPublicKey<D, B: Backend> {
124 pub(crate) data: GLWECiphertextFourier<D, B>,
125 pub(crate) dist: SecretDistribution,
126}
127
128impl<B: Backend> GLWEPublicKey<Vec<u8>, B> {
129 pub fn alloc(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self {
130 Self {
131 data: GLWECiphertextFourier::alloc(module, basek, k, rank),
132 dist: SecretDistribution::NONE,
133 }
134 }
135
136 pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rank: usize) -> usize {
137 GLWECiphertextFourier::<Vec<u8>, B>::bytes_of(module, basek, k, rank)
138 }
139}
140
141impl<T, B: Backend> Infos for GLWEPublicKey<T, B> {
142 type Inner = VecZnxDft<T, B>;
143
144 fn inner(&self) -> &Self::Inner {
145 &self.data.data
146 }
147
148 fn basek(&self) -> usize {
149 self.data.basek
150 }
151
152 fn k(&self) -> usize {
153 self.data.k
154 }
155}
156
157impl<T, B: Backend> GLWEPublicKey<T, B> {
158 pub fn rank(&self) -> usize {
159 self.cols() - 1
160 }
161}
162
163impl<C: AsRef<[u8]> + AsMut<[u8]>> GLWEPublicKey<C, FFT64> {
164 pub fn generate_from_sk<S: AsRef<[u8]>>(
165 &mut self,
166 module: &Module<FFT64>,
167 sk_dft: &SecretKeyFourier<S, FFT64>,
168 source_xa: &mut Source,
169 source_xe: &mut Source,
170 sigma: f64,
171 ) {
172 #[cfg(debug_assertions)]
173 {
174 match sk_dft.dist {
175 SecretDistribution::NONE => panic!("invalid sk_dft: SecretDistribution::NONE"),
176 _ => {}
177 }
178 }
179
180 // Its ok to allocate scratch space here since pk is usually generated only once.
181 let mut scratch: ScratchOwned = ScratchOwned::new(GLWECiphertextFourier::encrypt_sk_scratch_space(
182 module,
183 self.rank(),
184 self.size(),
185 ));
186 self.data.encrypt_zero_sk(
187 module,
188 sk_dft,
189 source_xa,
190 source_xe,
191 sigma,
192 scratch.borrow(),
193 );
194 self.dist = sk_dft.dist;
195 }
196}