Browse Source

add GLWE ciphertext-ciphertext addition, and ciphertext-plaintext addition

gfhe-over-ring-trait
arnaucube 1 month ago
parent
commit
92b6f50ccc
3 changed files with 85 additions and 4 deletions
  1. +2
    -0
      README.md
  2. +1
    -1
      arith/src/tuple_ring.rs
  3. +82
    -3
      generalized-fhe/src/glwe.rs

+ 2
- 0
README.md

@ -5,3 +5,5 @@ Implementations from scratch done while studying some FHE papers; do not use in
- `generalized-fhe`: contains the structs and logic for RLWE, GLWE, GLev, GGSW, RGSW cryptosystems, which can be used by concrete FHE schemes.
- `bfv`: https://eprint.iacr.org/2012/144.pdf scheme implementation
- `ckks`: https://eprint.iacr.org/2016/421.pdf scheme implementation
`cargo test --release`

+ 1
- 1
arith/src/tuple_ring.rs

@ -13,7 +13,7 @@ use crate::Ring;
/// Tuple of K Ring (Rq) elements. We use Vec<R> to allocate it in the heap,
/// since if using a fixed-size array it would overflow the stack.
#[derive(Clone, Debug)]
pub struct TR<R: Ring, const K: usize>(Vec<R>);
pub struct TR<R: Ring, const K: usize>(pub Vec<R>);
impl<R: Ring, const K: usize> TR<R, K> {
pub fn rand(mut rng: impl Rng, dist: impl Distribution<f64>) -> Self {

+ 82
- 3
generalized-fhe/src/glwe.rs

@ -1,10 +1,9 @@
use anyhow::Result;
use itertools::zip_eq;
use rand::Rng;
use rand_distr::{Normal, Uniform};
use std::{array, ops};
use std::ops::Add;
use arith::{Ring, Rq, R, TR};
use arith::{Ring, Rq, TR};
const ERR_SIGMA: f64 = 3.2;
@ -80,6 +79,24 @@ impl GLWE {
}
}
impl<const Q: u64, const N: usize, const K: usize> Add<GLWE<Q, N, K>> for GLWE<Q, N, K> {
type Output = Self;
fn add(self, other: Self) -> Self {
let a: TR<Rq<Q, N>, K> = self.0 + other.0;
let b: Rq<Q, N> = self.1 + other.1;
Self(a, b)
}
}
impl<const Q: u64, const N: usize, const K: usize> Add<Rq<Q, N>> for GLWE<Q, N, K> {
type Output = Self;
fn add(self, plaintext: Rq<Q, N>) -> Self {
let a: TR<Rq<Q, N>, K> = self.0;
let b: Rq<Q, N> = self.1 + plaintext;
Self(a, b)
}
}
#[cfg(test)]
mod tests {
use anyhow::Result;
@ -112,4 +129,66 @@ mod tests {
Ok(())
}
#[test]
fn test_addition() -> Result<()> {
const Q: u64 = 2u64.pow(16) + 1;
const N: usize = 128;
const T: u64 = 20;
const K: usize = 16;
type S = GLWE<Q, N, K>;
let delta: u64 = Q / T; // floored
let mut rng = rand::thread_rng();
for _ in 0..200 {
let (sk, pk) = S::new_key(&mut rng)?;
let msg_dist = Uniform::new(0_u64, T);
let m1 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?;
let m2 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?;
let c1 = S::encrypt(&mut rng, &pk, &m1, delta)?;
let c2 = S::encrypt(&mut rng, &pk, &m2, delta)?;
let c3 = c1 + c2;
let m3_recovered = c3.decrypt(&sk, delta);
assert_eq!(m1 + m2, m3_recovered);
}
Ok(())
}
#[test]
fn test_add_plaintext() -> Result<()> {
const Q: u64 = 2u64.pow(16) + 1;
const N: usize = 128;
const T: u64 = 32;
const K: usize = 16;
type S = GLWE<Q, N, K>;
let delta: u64 = Q / T; // floored
let mut rng = rand::thread_rng();
for _ in 0..200 {
let (sk, pk) = S::new_key(&mut rng)?;
let msg_dist = Uniform::new(0_u64, T);
let m1 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?;
let m2 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?;
let m2_scaled: Rq<Q, N> = m2.remodule::<Q>() * delta;
let c1 = S::encrypt(&mut rng, &pk, &m1, delta)?;
let c3 = c1 + m2_scaled;
let m3_recovered = c3.decrypt(&sk, delta);
assert_eq!(m1 + m2, m3_recovered);
}
Ok(())
}
}

Loading…
Cancel
Save