diff --git a/arith/src/ring_torus.rs b/arith/src/ring_torus.rs index 1a8192d..46e134c 100644 --- a/arith/src/ring_torus.rs +++ b/arith/src/ring_torus.rs @@ -85,14 +85,11 @@ impl Ring for Tn { impl Tn { // multiply self by X^-h pub fn left_rotate(&self, h: usize) -> Self { - dbg!(&h); - dbg!(&N); let h = h % N; assert!(h < N); let c = self.0; // c[h], c[h+1], c[h+2], ..., c[n-1], -c[0], -c[1], ..., -c[h-1] // let r: Vec = vec![c[h..N], c[0..h].iter().map(|&c_i| -c_i).collect()].concat(); - dbg!(&h); let r: Vec = c[h..N] .iter() .copied() diff --git a/tfhe/src/tggsw.rs b/tfhe/src/tggsw.rs index fac7c22..663cd03 100644 --- a/tfhe/src/tggsw.rs +++ b/tfhe/src/tggsw.rs @@ -132,3 +132,49 @@ impl Mul>> for TGLev { r } } + +#[cfg(test)] +mod tests { + use anyhow::Result; + use rand::distributions::Uniform; + + use super::*; + #[test] + fn test_external_product() -> Result<()> { + const T: u64 = 16; // plaintext modulus + const K: usize = 4; + const N: usize = 64; + const KN: usize = K * N; + + let beta: u32 = 2; + let l: u32 = 64; + + let mut rng = rand::thread_rng(); + let msg_dist = Uniform::new(0_u64, T); + + for _ in 0..50 { + let (sk, _) = TGLWE::::new_key::(&mut rng)?; + + let m1: Rq = Rq::rand_u64(&mut rng, msg_dist)?; + let p1: Tn = TGLev::::encode::(&m1); + + let m2: Rq = Rq::rand_u64(&mut rng, msg_dist)?; + let p2: Tn = TGLWE::::encode::(&m2); // scaled by delta + + let tgsw = TGGSW::::encrypt_s(&mut rng, beta, l, &sk, &p1)?; + let tlwe = TGLWE::::encrypt_s(&mut rng, &sk, &p2)?; + + let res: TGLWE = tgsw * tlwe; + + // let p_recovered = res.decrypt(&sk, beta); + let p_recovered = res.decrypt(&sk); + // downscaled by delta^-1 + let res_recovered = TGLWE::::decode::(&p_recovered); + + // assert_eq!(m1 * m2, m_recovered); + assert_eq!((m1.to_r() * m2.to_r()).to_rq::(), res_recovered); + } + + Ok(()) + } +} diff --git a/tfhe/src/tlwe.rs b/tfhe/src/tlwe.rs index c087332..b905c33 100644 --- a/tfhe/src/tlwe.rs +++ b/tfhe/src/tlwe.rs @@ -132,21 +132,37 @@ pub fn blind_rotation( + btk: BootstrappingKey, + table: TGLWE, + c: TLWE, +) -> TLWE { + let rotated: TGLWE = blind_rotation::(c, btk.clone(), table); + let c_h: TLWE = rotated.sample_extraction(0); + let r = c_h.key_switch(2, 64, &btk.1); + r +} + #[derive(Clone, Debug)] -pub struct BootstrappingKey(pub Vec>); +pub struct BootstrappingKey( + pub Vec>, + pub KSK, +); impl BootstrappingKey { pub fn from_sk(mut rng: impl Rng, sk: &tglwe::SecretKey) -> Result { let (beta, l) = (2u32, 64u32); // TMP // let s: TR, K> = sk.0 .0.clone(); + let (sk2, _) = TLWE::::new_key(&mut rng)?; // TLWE compatible with TGLWE // each btk_j = TGGSW_sk(s_i) let btk: Vec> = s .iter() .map(|s_i| TGGSW::::encrypt_s(&mut rng, beta, l, sk, s_i)) .collect::>>()?; + let ksk = TLWE::::new_ksk(&mut rng, beta, l, &sk.to_tlwe(), &sk2)?; - Ok(Self(btk)) + Ok(Self(btk, ksk)) } } @@ -399,4 +415,45 @@ mod tests { Ok(()) } + + #[test] + fn test_bootstrapping() -> Result<()> { + const T: u64 = 128; // plaintext modulus + const K: usize = 1; + const N: usize = 1024; + const KN: usize = K * N; + let mut rng = rand::thread_rng(); + + let start = Instant::now(); + let table: TGLWE = compute_lookup_table::(); + println!("table took: {:?}", start.elapsed()); + + let (sk, _) = TGLWE::::new_key::(&mut rng)?; + let sk_tlwe: SecretKey = sk.to_tlwe::(); + + let start = Instant::now(); + let btk = BootstrappingKey::::from_sk(&mut rng, &sk)?; + println!("btk took: {:?}", start.elapsed()); + + let msg_dist = Uniform::new(0_u64, T); + let m = Rq::::rand_u64(&mut rng, msg_dist)?; + // let m = Rq::::from_vec(vec![Zq(5)]); + dbg!(&m); + let p = TLWE::::encode::(&m); // plaintext + + let c = TLWE::::encrypt_s(&mut rng, &sk_tlwe, &p)?; + + let start = Instant::now(); + // the ugly const generics are temporary + let bootstrapped: TLWE = + bootstrapping::(btk, table, c); + println!("bootstrapping took: {:?}", start.elapsed()); + + let p_recovered: T64 = bootstrapped.decrypt(&sk_tlwe); + let m_recovered = TLWE::::decode::(&p_recovered); + dbg!(&m_recovered); + assert_eq!(m_recovered, m); + + Ok(()) + } }