arnaucube
arnaucube/phantom-zonePublic Self-hosted repository
phantom-zone/src/lwe.rs
src/lwe.rsRaw
  1use std::{
  2    cell::RefCell,
  3    collections::btree_map::Values,
  4    fmt::{Debug, Display},
  5    marker::PhantomData,
  6};
  7
  8use itertools::{izip, Itertools};
  9use num_traits::{abs, PrimInt, ToPrimitive, Zero};
 10
 11use crate::{
 12    backend::{ArithmeticOps, GetModulus, Modulus, VectorOps},
 13    decomposer::Decomposer,
 14    random::{
 15        DefaultSecureRng, NewWithSeed, RandomFillGaussianInModulus, RandomGaussianElementInModulus,
 16        RandomFillUniformInModulus, DEFAULT_RNG,
 17    },
 18    utils::{fill_random_ternary_secret_with_hamming_weight, TryConvertFrom1, WithLocal},
 19    Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut, Secret,
 20};
 21
 22struct SeededLweKeySwitchingKey<Ro, S>
 23where
 24    Ro: Row,
 25{
 26    data: Ro,
 27    seed: S,
 28    to_lwe_n: usize,
 29    modulus: Ro::Element,
 30}
 31
 32impl<Ro: RowEntity, S> SeededLweKeySwitchingKey<Ro, S> {
 33    pub(crate) fn empty(
 34        from_lwe_n: usize,
 35        to_lwe_n: usize,
 36        d: usize,
 37        seed: S,
 38        modulus: Ro::Element,
 39    ) -> Self {
 40        let data = Ro::zeros(from_lwe_n * d);
 41        SeededLweKeySwitchingKey {
 42            data,
 43            to_lwe_n,
 44            seed,
 45            modulus,
 46        }
 47    }
 48}
 49
 50struct LweKeySwitchingKey<M, R> {
 51    data: M,
 52    _phantom: PhantomData<R>,
 53}
 54
 55impl<
 56        M: MatrixMut + MatrixEntity,
 57        R: NewWithSeed + RandomFillUniformInModulus<[M::MatElement], M::MatElement>,
 58    > From<&SeededLweKeySwitchingKey<M::R, R::Seed>> for LweKeySwitchingKey<M, R>
 59where
 60    M::R: RowMut,
 61    R::Seed: Clone,
 62    M::MatElement: Copy,
 63{
 64    fn from(value: &SeededLweKeySwitchingKey<M::R, R::Seed>) -> Self {
 65        let mut p_rng = R::new_with_seed(value.seed.clone());
 66        let mut data = M::zeros(value.data.as_ref().len(), value.to_lwe_n + 1);
 67        izip!(value.data.as_ref().iter(), data.iter_rows_mut()).for_each(|(bi, lwe_i)| {
 68            RandomFillUniformInModulus::random_fill(&mut p_rng, &value.modulus, &mut lwe_i.as_mut()[1..]);
 69            lwe_i.as_mut()[0] = *bi;
 70        });
 71        LweKeySwitchingKey {
 72            data,
 73            _phantom: PhantomData,
 74        }
 75    }
 76}
 77
 78trait LweCiphertext<M: Matrix> {}
 79
 80#[derive(Clone)]
 81pub struct LweSecret {
 82    pub(crate) values: Vec<i32>,
 83}
 84
 85impl Secret for LweSecret {
 86    type Element = i32;
 87    fn values(&self) -> &[Self::Element] {
 88        &self.values
 89    }
 90}
 91
 92impl LweSecret {
 93    pub(crate) fn random(hw: usize, n: usize) -> LweSecret {
 94        DefaultSecureRng::with_local_mut(|rng| {
 95            let mut out = vec![0i32; n];
 96            fill_random_ternary_secret_with_hamming_weight(&mut out, hw, rng);
 97
 98            LweSecret { values: out }
 99        })
100    }
101}
102
103pub(crate) fn lwe_key_switch<
104    M: Matrix,
105    Ro: AsMut<[M::MatElement]> + AsRef<[M::MatElement]>,
106    Op: VectorOps<Element = M::MatElement> + ArithmeticOps<Element = M::MatElement>,
107    D: Decomposer<Element = M::MatElement>,
108>(
109    lwe_out: &mut Ro,
110    lwe_in: &Ro,
111    lwe_ksk: &M,
112    operator: &Op,
113    decomposer: &D,
114) {
115    assert!(
116        lwe_ksk.dimension().0 == ((lwe_in.as_ref().len() - 1) * decomposer.decomposition_count())
117    );
118    assert!(lwe_out.as_ref().len() == lwe_ksk.dimension().1);
119
120    let lwe_in_a_decomposed = lwe_in
121        .as_ref()
122        .iter()
123        .skip(1)
124        .flat_map(|ai| decomposer.decompose(ai));
125    izip!(lwe_in_a_decomposed, lwe_ksk.iter_rows()).for_each(|(ai_j, beta_ij_lwe)| {
126        operator.elwise_fma_scalar_mut(lwe_out.as_mut(), beta_ij_lwe.as_ref(), &ai_j);
127    });
128
129    let out_b = operator.add(&lwe_out.as_ref()[0], &lwe_in.as_ref()[0]);
130    lwe_out.as_mut()[0] = out_b;
131}
132
133pub fn lwe_ksk_keygen<
134    Ro: Row + RowMut + RowEntity,
135    S,
136    Op: VectorOps<Element = Ro::Element>
137        + ArithmeticOps<Element = Ro::Element>
138        + GetModulus<Element = Ro::Element>,
139    R: RandomGaussianElementInModulus<Ro::Element, Op::M>,
140    PR: RandomFillUniformInModulus<[Ro::Element], Op::M>,
141>(
142    from_lwe_sk: &[S],
143    to_lwe_sk: &[S],
144    ksk_out: &mut Ro,
145    gadget: &[Ro::Element],
146    operator: &Op,
147    p_rng: &mut PR,
148    rng: &mut R,
149) where
150    Ro: TryConvertFrom1<[S], Op::M>,
151    Ro::Element: Zero + Debug,
152{
153    assert!(ksk_out.as_ref().len() == (from_lwe_sk.len() * gadget.len()));
154
155    let d = gadget.len();
156
157    let modulus = operator.modulus();
158    let mut neg_sk_in_m = Ro::try_convert_from(from_lwe_sk, modulus);
159    operator.elwise_neg_mut(neg_sk_in_m.as_mut());
160    let sk_out_m = Ro::try_convert_from(to_lwe_sk, modulus);
161
162    let mut scratch = Ro::zeros(to_lwe_sk.len());
163
164    izip!(neg_sk_in_m.as_ref(), ksk_out.as_mut().chunks_mut(d)).for_each(
165        |(neg_sk_in_si, d_lwes_partb)| {
166            izip!(gadget.iter(), d_lwes_partb.into_iter()).for_each(|(f, lwe_b)| {
167                // sample `a`
168                RandomFillUniformInModulus::random_fill(p_rng, &modulus, scratch.as_mut());
169
170                // a * z
171                let mut az = Ro::Element::zero();
172                izip!(scratch.as_ref().iter(), sk_out_m.as_ref()).for_each(|(ai, si)| {
173                    let ai_si = operator.mul(ai, si);
174                    az = operator.add(&az, &ai_si);
175                });
176
177                // a*z + (-s_i)*\beta^j + e
178                let mut b = operator.add(&az, &operator.mul(f, neg_sk_in_si));
179                let e = RandomGaussianElementInModulus::random(rng, &modulus);
180                b = operator.add(&b, &e);
181
182                *lwe_b = b;
183            })
184        },
185    );
186}
187
188/// Encrypts encoded message m as LWE ciphertext
189pub fn encrypt_lwe<
190    Ro: Row + RowMut,
191    Op: ArithmeticOps<Element = Ro::Element> + GetModulus<Element = Ro::Element>,
192    R: RandomGaussianElementInModulus<Ro::Element, Op::M> + RandomFillUniformInModulus<[Ro::Element], Op::M>,
193    S,
194>(
195    lwe_out: &mut Ro,
196    m: &Ro::Element,
197    s: &[S],
198    operator: &Op,
199    rng: &mut R,
200) where
201    Ro: TryConvertFrom1<[S], Op::M>,
202    Ro::Element: Zero,
203{
204    let s = Ro::try_convert_from(s, operator.modulus());
205    assert!(s.as_ref().len() == (lwe_out.as_ref().len() - 1));
206
207    // a*s
208    RandomFillUniformInModulus::random_fill(rng, operator.modulus(), &mut lwe_out.as_mut()[1..]);
209    let mut sa = Ro::Element::zero();
210    izip!(lwe_out.as_mut().iter().skip(1), s.as_ref()).for_each(|(ai, si)| {
211        let tmp = operator.mul(ai, si);
212        sa = operator.add(&tmp, &sa);
213    });
214
215    // b = a*s + e + m
216    let e = RandomGaussianElementInModulus::random(rng, operator.modulus());
217    let b = operator.add(&operator.add(&sa, &e), m);
218    lwe_out.as_mut()[0] = b;
219}
220
221pub fn decrypt_lwe<
222    Ro: Row,
223    Op: ArithmeticOps<Element = Ro::Element> + GetModulus<Element = Ro::Element>,
224    S,
225>(
226    lwe_ct: &Ro,
227    s: &[S],
228    operator: &Op,
229) -> Ro::Element
230where
231    Ro: TryConvertFrom1<[S], Op::M>,
232    Ro::Element: Zero,
233{
234    let s = Ro::try_convert_from(s, operator.modulus());
235
236    let mut sa = Ro::Element::zero();
237    izip!(lwe_ct.as_ref().iter().skip(1), s.as_ref()).for_each(|(ai, si)| {
238        let tmp = operator.mul(ai, si);
239        sa = operator.add(&tmp, &sa);
240    });
241
242    let b = &lwe_ct.as_ref()[0];
243    operator.sub(b, &sa)
244}
245
246/// Measures noise in input LWE ciphertext with reference of `ideal_m`
247///
248/// - ct: Input LWE ciphertext
249/// - s: corresponding secret
250/// - ideal_m: Ideal `encoded` message
251pub(crate) fn measure_noise_lwe<
252    Ro: Row,
253    Op: ArithmeticOps<Element = Ro::Element> + GetModulus<Element = Ro::Element>,
254    S,
255>(
256    ct: &Ro,
257    s: &[S],
258    operator: &Op,
259    ideal_m: &Ro::Element,
260) -> f64
261where
262    Ro: TryConvertFrom1<[S], Op::M>,
263    Ro::Element: Zero + ToPrimitive + PrimInt + Display,
264{
265    assert!(s.len() == ct.as_ref().len() - 1,);
266
267    let s = Ro::try_convert_from(s, &operator.modulus());
268    let mut sa = Ro::Element::zero();
269    izip!(s.as_ref().iter(), ct.as_ref().iter().skip(1)).for_each(|(si, ai)| {
270        sa = operator.add(&sa, &operator.mul(si, ai));
271    });
272    let m = operator.sub(&ct.as_ref()[0], &sa);
273
274    let mut diff = operator.sub(&m, ideal_m);
275    let q = operator.modulus();
276    return q.to_i64(&diff).to_f64().unwrap().abs().log2();
277}
278
279#[cfg(test)]
280mod tests {
281
282    use crate::{
283        backend::{ModInit, ModularOpsU64},
284        decomposer::{Decomposer, DefaultDecomposer},
285        lwe::{lwe_key_switch, measure_noise_lwe},
286        random::DefaultSecureRng,
287        rgsw::measure_noise,
288        Secret,
289    };
290
291    use super::{
292        decrypt_lwe, encrypt_lwe, lwe_ksk_keygen, LweKeySwitchingKey, LweSecret,
293        SeededLweKeySwitchingKey,
294    };
295
296    const K: usize = 50;
297
298    #[test]
299    fn encrypt_decrypt_works() {
300        let logq = 16;
301        let q = 1u64 << logq;
302        let lwe_n = 1024;
303        let logp = 3;
304
305        let modq_op = ModularOpsU64::new(q);
306        let lwe_sk = LweSecret::random(lwe_n >> 1, lwe_n);
307
308        let mut rng = DefaultSecureRng::new();
309
310        // encrypt
311        for m in 0..1u64 << logp {
312            let encoded_m = m << (logq - logp);
313            let mut lwe_ct = vec![0u64; lwe_n + 1];
314            encrypt_lwe(
315                &mut lwe_ct,
316                &encoded_m,
317                &lwe_sk.values(),
318                &modq_op,
319                &mut rng,
320            );
321            let encoded_m_back = decrypt_lwe(&lwe_ct, &lwe_sk.values(), &modq_op);
322            let m_back = ((((encoded_m_back as f64) * ((1 << logp) as f64)) / q as f64).round()
323                as u64)
324                % (1u64 << logp);
325            assert_eq!(m, m_back, "Expected {m} but got {m_back}");
326        }
327    }
328
329    #[test]
330    fn key_switch_works() {
331        let logq = 18;
332        let logp = 2;
333        let q = 1u64 << logq;
334        let lwe_in_n = 2048;
335        let lwe_out_n = 493;
336        let d_ks = 3;
337        let logb = 6;
338
339        let lwe_sk_in = LweSecret::random(lwe_in_n >> 1, lwe_in_n);
340        let lwe_sk_out = LweSecret::random(lwe_out_n >> 1, lwe_out_n);
341
342        let mut rng = DefaultSecureRng::new();
343        let modq_op = ModularOpsU64::new(q);
344
345        // genrate ksk
346        for _ in 0..K {
347            let mut ksk_seed = [0u8; 32];
348            rng.fill_bytes(&mut ksk_seed);
349            let mut seeded_ksk =
350                SeededLweKeySwitchingKey::empty(lwe_in_n, lwe_out_n, d_ks, ksk_seed, q);
351            let mut p_rng = DefaultSecureRng::new_seeded(ksk_seed);
352            let decomposer = DefaultDecomposer::new(q, logb, d_ks);
353            let gadget = decomposer.gadget_vector();
354            lwe_ksk_keygen(
355                &lwe_sk_in.values(),
356                &lwe_sk_out.values(),
357                &mut seeded_ksk.data,
358                &gadget,
359                &modq_op,
360                &mut p_rng,
361                &mut rng,
362            );
363            // println!("{:?}", ksk);
364            let ksk = LweKeySwitchingKey::<Vec<Vec<u64>>, DefaultSecureRng>::from(&seeded_ksk);
365
366            for m in 0..(1 << logp) {
367                // encrypt using lwe_sk_in
368                let encoded_m = m << (logq - logp);
369                let mut lwe_in_ct = vec![0u64; lwe_in_n + 1];
370                encrypt_lwe(
371                    &mut lwe_in_ct,
372                    &encoded_m,
373                    lwe_sk_in.values(),
374                    &modq_op,
375                    &mut rng,
376                );
377
378                // key switch from lwe_sk_in to lwe_sk_out
379                let decomposer = DefaultDecomposer::new(1u64 << logq, logb, d_ks);
380                let mut lwe_out_ct = vec![0u64; lwe_out_n + 1];
381                lwe_key_switch(
382                    &mut lwe_out_ct,
383                    &lwe_in_ct,
384                    &ksk.data,
385                    &modq_op,
386                    &decomposer,
387                );
388
389                // decrypt lwe_out_ct using lwe_sk_out
390                let encoded_m_back = decrypt_lwe(&lwe_out_ct, &lwe_sk_out.values(), &modq_op);
391                let m_back = ((((encoded_m_back as f64) * ((1 << logp) as f64)) / q as f64).round()
392                    as u64)
393                    % (1u64 << logp);
394                let noise =
395                    measure_noise_lwe(&lwe_out_ct, lwe_sk_out.values(), &modq_op, &encoded_m);
396                println!("Noise: {noise}");
397                assert_eq!(m, m_back, "Expected {m} but got {m_back}");
398                // dbg!(m, m_back);
399                // dbg!(encoded_m, encoded_m_back);
400            }
401        }
402    }
403}