Browse Source

add modulus switching to GLWE ciphertexts (and Zq,Rq)

pull/1/head
arnaucube 3 months ago
parent
commit
4a082b9187
8 changed files with 60 additions and 16 deletions
  1. +1
    -1
      Cargo.toml
  2. +1
    -1
      README.md
  3. +1
    -4
      arith/src/ringq.rs
  4. +2
    -2
      arith/src/traits.rs
  5. +1
    -1
      gfhe/Cargo.toml
  6. +1
    -1
      gfhe/README.md
  7. +53
    -6
      gfhe/src/glwe.rs
  8. +0
    -0
      gfhe/src/lib.rs

+ 1
- 1
Cargo.toml

@ -1,7 +1,7 @@
[workspace] [workspace]
members = [ members = [
"arith", "arith",
"generalized-fhe",
"gfhe",
"bfv", "bfv",
"ckks" "ckks"
] ]

+ 1
- 1
README.md

@ -2,7 +2,7 @@
Implementations from scratch done while studying some FHE papers; do not use in production. Implementations from scratch done while studying some FHE papers; do not use in production.
- `arith`: contains $\mathbb{Z}_q$, $R_q=\mathbb{Z}_q[X]/(X^N+1)$ and $R=\mathbb{Z}[X]/(X^N+1)$ arithmetic implementations, together with the NTT implementation. - `arith`: contains $\mathbb{Z}_q$, $R_q=\mathbb{Z}_q[X]/(X^N+1)$ and $R=\mathbb{Z}[X]/(X^N+1)$ arithmetic implementations, together with the NTT implementation.
- `generalized-fhe`: contains the structs and logic for RLWE, GLWE, GLev, GGSW, RGSW cryptosystems, which can be used by concrete FHE schemes.
- `gfhe`: (gfhe=generalized-fhe) contains the structs and logic for RLWE, GLWE, GLev, GGSW, RGSW cryptosystems, and modulus switching and key switching methods, which can be used by concrete FHE schemes.
- `bfv`: https://eprint.iacr.org/2012/144.pdf scheme implementation - `bfv`: https://eprint.iacr.org/2012/144.pdf scheme implementation
- `ckks`: https://eprint.iacr.org/2016/421.pdf scheme implementation - `ckks`: https://eprint.iacr.org/2016/421.pdf scheme implementation

+ 1
- 4
arith/src/ringq.rs

@ -49,9 +49,6 @@ impl Ring for Rq {
} }
} }
// TODO define a trait "PolynomialRingTrait" or similar, so that when other structs use it can just
// use the trait and not need to add '<Q, N>' to their params
impl<const Q: u64, const N: usize> From<crate::ring::R<N>> for Rq<Q, N> { impl<const Q: u64, const N: usize> From<crate::ring::R<N>> for Rq<Q, N> {
fn from(r: crate::ring::R<N>) -> Self { fn from(r: crate::ring::R<N>) -> Self {
Self::from_vec( Self::from_vec(
@ -165,7 +162,7 @@ impl Rq {
} }
/// perform the mod switch operation from Q to Q', where Q2=Q' /// perform the mod switch operation from Q to Q', where Q2=Q'
fn mod_switch<const Q2: u64>(&self) -> Rq<Q2, N> {
pub fn mod_switch<const Q2: u64>(&self) -> Rq<Q2, N> {
Rq::<Q2, N> { Rq::<Q2, N> {
coeffs: array::from_fn(|i| self.coeffs[i].mod_switch::<Q2>()), coeffs: array::from_fn(|i| self.coeffs[i].mod_switch::<Q2>()),
evals: None, evals: None,

+ 2
- 2
arith/src/traits.rs

@ -4,7 +4,7 @@ use std::fmt::Debug;
use std::iter::Sum; use std::iter::Sum;
use std::ops::{Add, AddAssign, Mul, Sub, SubAssign}; use std::ops::{Add, AddAssign, Mul, Sub, SubAssign};
/// represents a ring element
/// Represents a ring element. Currently implemented by ring.rs#R and ringq.rs#Rq.
pub trait Ring: pub trait Ring:
Sized Sized
+ Add<Output = Self> + Add<Output = Self>
@ -25,6 +25,6 @@ pub trait Ring:
fn coeffs(&self) -> Vec<Self::C>; fn coeffs(&self) -> Vec<Self::C>;
fn zero() -> Self; fn zero() -> Self;
fn rand(rng: impl Rng, dist: impl Distribution<f64>) -> Self;
// note/wip/warning: dist (0,q) with f64, will output more '0=q' elements than other values // note/wip/warning: dist (0,q) with f64, will output more '0=q' elements than other values
fn rand(rng: impl Rng, dist: impl Distribution<f64>) -> Self;
} }

generalized-fhe/Cargo.toml → gfhe/Cargo.toml

@ -1,5 +1,5 @@
[package] [package]
name = "generalized-fhe"
name = "gfhe"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"

generalized-fhe/README.md → gfhe/README.md

@ -1,2 +1,2 @@
# common
# gfhe - generalized-fhe
Contains the structs and logic for RLWE, GLWE, GLev, GGSW, RGSW cryptosystems, which can be used by concrete FHE schemes. Contains the structs and logic for RLWE, GLWE, GLev, GGSW, RGSW cryptosystems, which can be used by concrete FHE schemes.

generalized-fhe/src/glwe.rs → gfhe/src/glwe.rs

@ -69,14 +69,22 @@ impl GLWE {
pub fn decrypt<const T: u64>(&self, sk: &SecretKey<Q, N, K>, delta: u64) -> Rq<T, N> { pub fn decrypt<const T: u64>(&self, sk: &SecretKey<Q, N, K>, delta: u64) -> Rq<T, N> {
let (d, b): (TR<Rq<Q, N>, K>, Rq<Q, N>) = (self.0.clone(), self.1); let (d, b): (TR<Rq<Q, N>, K>, Rq<Q, N>) = (self.0.clone(), self.1);
let r: Rq<Q, N> = b - &d * &sk.0; let r: Rq<Q, N> = b - &d * &sk.0;
let r_scaled: Vec<f64> = r
.coeffs()
.iter()
.map(|e| (e.0 as f64 / delta as f64).round())
.collect();
let r = Rq::<Q, N>::from_vec_f64(r_scaled);
let r = r.mul_div_round(T, Q);
// let r_scaled: Vec<f64> = r
// .coeffs()
// .iter()
// // .map(|e| (e.0 as f64 / delta as f64).round())
// .map(|e| e.mul_div_round(T, Q))
// .collect();
// let r = Rq::<Q, N>::from_vec_f64(r_scaled);
r.remodule::<T>() r.remodule::<T>()
} }
pub fn mod_switch<const P: u64>(&self) -> GLWE<P, N, K> {
let a: TR<Rq<P, N>, K> = TR(self.0 .0.iter().map(|r| r.mod_switch::<P>()).collect());
let b: Rq<P, N> = self.1.mod_switch::<P>();
GLWE(a, b)
}
} }
impl<const Q: u64, const N: usize, const K: usize> Add<GLWE<Q, N, K>> for GLWE<Q, N, K> { impl<const Q: u64, const N: usize, const K: usize> Add<GLWE<Q, N, K>> for GLWE<Q, N, K> {
@ -233,4 +241,43 @@ mod tests {
Ok(()) Ok(())
} }
#[test]
fn test_mod_switch() -> Result<()> {
const Q: u64 = 2u64.pow(16) + 1;
const P: u64 = 2u64.pow(8) + 1;
// note: wip, Q and P chosen so that P/Q is an integer
const N: usize = 8;
const T: u64 = 8; // plaintext modulus, must be a prime or power of a prime
const K: usize = 16;
type S = GLWE<Q, N, K>;
let delta: u64 = Q / T; // floored
let mut rng = rand::thread_rng();
dbg!(P as f64 / Q as f64);
dbg!(delta);
dbg!(delta as f64 * P as f64 / Q as f64);
dbg!(delta as f64 * (P as f64 / Q as f64));
for _ in 0..200 {
let (sk, pk) = S::new_key(&mut rng)?;
let msg_dist = Uniform::new(0_u64, T);
let m = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?;
let c = S::encrypt(&mut rng, &pk, &m, delta)?;
let c2 = c.mod_switch::<P>();
let sk2: SecretKey<P, N, K> =
SecretKey(TR(sk.0 .0.iter().map(|s_i| s_i.remodule::<P>()).collect()));
let delta2: u64 = ((P as f64 * delta as f64) / Q as f64).round() as u64;
let m_recovered = c2.decrypt(&sk2, delta2);
assert_eq!(m, m_recovered);
}
Ok(())
}
} }

generalized-fhe/src/lib.rs → gfhe/src/lib.rs


Loading…
Cancel
Save