mirror of
https://github.com/arnaucube/fhe-study.git
synced 2026-01-24 04:33:52 +01:00
tfhe: add bootstrapping impl
add initial version of bootstrapping implementation
This commit is contained in:
30
README.md
30
README.md
@@ -52,3 +52,33 @@ let m4 = S::decode::<T>(&p4_recovered);
|
||||
|
||||
// m4 is equal to (m1+m2)*m3
|
||||
```
|
||||
|
||||
|
||||
## Status of the implementation
|
||||
|
||||
- TFHE
|
||||
- {TLWE, TGLWE, TLev, TGLev, TGSW, TGGSW} encryption & decryption
|
||||
- addition of ciphertexts, addition & multiplication of ciphertext by a plaintext
|
||||
- external products of ciphertexts
|
||||
- TGSW x TLWE
|
||||
- TGGSW x TGLWE
|
||||
- TGSW & TGGSW CMux gate
|
||||
- blind rotation, key switching, mod switching
|
||||
- bootstrapping
|
||||
- CKKS
|
||||
- encoding & decoding
|
||||
- encryption & decryption
|
||||
- addition & substraction of ciphertexts
|
||||
- BFV
|
||||
- encryption & decryption
|
||||
- addition & substraction of ciphertexts
|
||||
- addition & multiplication of ciphertext by a plaintext
|
||||
- multiplication of ciphertexts with relinearization
|
||||
- GFHE (generalized FHE)
|
||||
- {GLWE & GLev} encryption & decryption
|
||||
- key switching, mod switching
|
||||
- addition & substraction of ciphertexts
|
||||
- addition & multiplication of ciphertext by a plaintext
|
||||
- arith
|
||||
- base arithmetic for $\mathbb{Z}_q,~~ R_q=\mathbb{Z}_q[X]/(X^N+1),~~ R=\mathbb{Z}[X]/(X^N+1),~~ \mathbb{T}_q[X]/(X^N +1)$
|
||||
- NTT implementation (negacyclic convolution)
|
||||
|
||||
@@ -85,14 +85,11 @@ impl<const N: usize> Ring for Tn<N> {
|
||||
impl<const N: usize> Tn<N> {
|
||||
// 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<T64> = vec![c[h..N], c[0..h].iter().map(|&c_i| -c_i).collect()].concat();
|
||||
dbg!(&h);
|
||||
let r: Vec<T64> = c[h..N]
|
||||
.iter()
|
||||
.copied()
|
||||
|
||||
@@ -132,3 +132,49 @@ impl<const N: usize, const K: usize> Mul<Vec<Tn<N>>> for TGLev<N, K> {
|
||||
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::<N, K>::new_key::<KN>(&mut rng)?;
|
||||
|
||||
let m1: Rq<T, N> = Rq::rand_u64(&mut rng, msg_dist)?;
|
||||
let p1: Tn<N> = TGLev::<N, K>::encode::<T>(&m1);
|
||||
|
||||
let m2: Rq<T, N> = Rq::rand_u64(&mut rng, msg_dist)?;
|
||||
let p2: Tn<N> = TGLWE::<N, K>::encode::<T>(&m2); // scaled by delta
|
||||
|
||||
let tgsw = TGGSW::<N, K>::encrypt_s(&mut rng, beta, l, &sk, &p1)?;
|
||||
let tlwe = TGLWE::<N, K>::encrypt_s(&mut rng, &sk, &p2)?;
|
||||
|
||||
let res: TGLWE<N, K> = tgsw * tlwe;
|
||||
|
||||
// let p_recovered = res.decrypt(&sk, beta);
|
||||
let p_recovered = res.decrypt(&sk);
|
||||
// downscaled by delta^-1
|
||||
let res_recovered = TGLWE::<N, K>::decode::<T>(&p_recovered);
|
||||
|
||||
// assert_eq!(m1 * m2, m_recovered);
|
||||
assert_eq!((m1.to_r() * m2.to_r()).to_rq::<T>(), res_recovered);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,21 +132,37 @@ pub fn blind_rotation<const N: usize, const K: usize, const KN: usize, const KN2
|
||||
c_j
|
||||
}
|
||||
|
||||
pub fn bootstrapping<const N: usize, const K: usize, const KN: usize, const KN2: u64>(
|
||||
btk: BootstrappingKey<N, K, KN>,
|
||||
table: TGLWE<N, K>,
|
||||
c: TLWE<KN>,
|
||||
) -> TLWE<KN> {
|
||||
let rotated: TGLWE<N, K> = blind_rotation::<N, K, KN, KN2>(c, btk.clone(), table);
|
||||
let c_h: TLWE<KN> = rotated.sample_extraction(0);
|
||||
let r = c_h.key_switch(2, 64, &btk.1);
|
||||
r
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BootstrappingKey<const N: usize, const K: usize, const KN: usize>(pub Vec<TGGSW<N, K>>);
|
||||
pub struct BootstrappingKey<const N: usize, const K: usize, const KN: usize>(
|
||||
pub Vec<TGGSW<N, K>>,
|
||||
pub KSK<KN>,
|
||||
);
|
||||
impl<const N: usize, const K: usize, const KN: usize> BootstrappingKey<N, K, KN> {
|
||||
pub fn from_sk(mut rng: impl Rng, sk: &tglwe::SecretKey<N, K>) -> Result<Self> {
|
||||
let (beta, l) = (2u32, 64u32); // TMP
|
||||
//
|
||||
let s: TR<Tn<N>, K> = sk.0 .0.clone();
|
||||
let (sk2, _) = TLWE::<KN>::new_key(&mut rng)?; // TLWE<KN> compatible with TGLWE<N,K>
|
||||
|
||||
// each btk_j = TGGSW_sk(s_i)
|
||||
let btk: Vec<TGGSW<N, K>> = s
|
||||
.iter()
|
||||
.map(|s_i| TGGSW::<N, K>::encrypt_s(&mut rng, beta, l, sk, s_i))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let ksk = TLWE::<KN>::new_ksk(&mut rng, beta, l, &sk.to_tlwe(), &sk2)?;
|
||||
|
||||
Ok(Self(btk))
|
||||
Ok(Self(btk, ksk))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,4 +415,44 @@ 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<N, K> = compute_lookup_table::<T, K, N>();
|
||||
println!("table took: {:?}", start.elapsed());
|
||||
|
||||
let (sk, _) = TGLWE::<N, K>::new_key::<KN>(&mut rng)?;
|
||||
let sk_tlwe: SecretKey<KN> = sk.to_tlwe::<KN>();
|
||||
|
||||
let start = Instant::now();
|
||||
let btk = BootstrappingKey::<N, K, KN>::from_sk(&mut rng, &sk)?;
|
||||
println!("btk took: {:?}", start.elapsed());
|
||||
|
||||
let msg_dist = Uniform::new(0_u64, T);
|
||||
let m = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
|
||||
dbg!(&m);
|
||||
let p = TLWE::<K>::encode::<T>(&m); // plaintext
|
||||
|
||||
let c = TLWE::<KN>::encrypt_s(&mut rng, &sk_tlwe, &p)?;
|
||||
|
||||
let start = Instant::now();
|
||||
// the ugly const generics are temporary
|
||||
let bootstrapped: TLWE<KN> =
|
||||
bootstrapping::<N, K, KN, { K as u64 * N as u64 }>(btk, table, c);
|
||||
println!("bootstrapping took: {:?}", start.elapsed());
|
||||
|
||||
let p_recovered: T64 = bootstrapped.decrypt(&sk_tlwe);
|
||||
let m_recovered = TLWE::<KN>::decode::<T>(&p_recovered);
|
||||
dbg!(&m_recovered);
|
||||
assert_eq!(m_recovered, m);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user