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