arnaucube
arnaucube/fhe-studyPublic Self-hosted repository
fhe-study/tfhe/src/lib.rs
tfhe/src/lib.rsRaw
 1//! Implementation of TFHE https://eprint.iacr.org/2018/421.pdf
 2#![allow(non_snake_case)]
 3#![allow(non_upper_case_globals)]
 4#![allow(non_camel_case_types)]
 5#![allow(clippy::upper_case_acronyms)]
 6#![allow(dead_code)] // TMP
 7
 8use anyhow::Result;
 9use rand::Rng;
10use rand_distr::{Normal, Uniform};
11use std::array;
12
13use arith::{Ring, Rq, Tn, T64};
14use gfhe::{glwe, GLWE};
15
16pub mod tlev;
17pub mod tlwe;
18
19#[derive(Clone, Debug)]
20pub struct SecretKey<const K: usize>(glwe::SecretKey<Tn<1>, K>);
21#[derive(Clone, Debug)]
22pub struct PublicKey<const K: usize>(glwe::PublicKey<Tn<1>, K>);
23
24#[derive(Clone, Debug)]
25pub struct TLWE<const K: usize>(pub GLWE<Tn<1>, K>);
26
27impl<const K: usize> TLWE<K> {
28    pub fn new_key(rng: impl Rng) -> Result<(SecretKey<K>, PublicKey<K>)> {
29        let (sk, pk) = GLWE::new_key(rng)?;
30        Ok((SecretKey(sk), PublicKey(pk)))
31    }
32
33    pub fn encode<const P: u64>(m: &Rq<P, 1>) -> Tn<1> {
34        let delta = u64::MAX / P; // floored
35        let coeffs = m.coeffs();
36        Tn(array::from_fn(|i| T64(coeffs[i].0 * delta)))
37    }
38    pub fn decode<const P: u64>(p: &Tn<1>) -> Rq<P, 1> {
39        let p = p.mul_div_round(P, u64::MAX);
40        Rq::<P, 1>::from_vec_u64(p.coeffs().iter().map(|c| c.0).collect())
41    }
42    pub fn encrypt_s(rng: impl Rng, sk: &SecretKey<K>, p: &Tn<1>) -> Result<Self> {
43        let glwe = GLWE::encrypt_s(rng, &sk.0, p)?;
44        Ok(Self(glwe))
45    }
46
47    pub fn encrypt(rng: impl Rng, pk: &PublicKey<K>, p: &Tn<1>) -> Result<Self> {
48        let glwe = GLWE::encrypt(rng, &pk.0, p)?;
49        Ok(Self(glwe))
50    }
51
52    pub fn decrypt(&self, sk: &SecretKey<K>) -> Tn<1> {
53        self.0.decrypt(&sk.0)
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use anyhow::Result;
60    use rand::distributions::Uniform;
61
62    use super::*;
63
64    #[test]
65    fn test_encrypt_decrypt() -> Result<()> {
66        const T: u64 = 128; // plaintext modulus
67        const K: usize = 16;
68        type S = TLWE<K>;
69
70        // let delta: u64 = Q / T; // floored
71        let mut rng = rand::thread_rng();
72
73        for _ in 0..200 {
74            let (sk, pk) = S::new_key(&mut rng)?;
75
76            let msg_dist = Uniform::new(0_f64, T as f64);
77            let m = Rq::<T, 1>::rand(&mut rng, msg_dist); // msg
78
79            // let m: Rq<Q, N> = m.remodule::<Q>();
80
81            let p = S::encode::<T>(&m); // plaintext
82            let c = S::encrypt(&mut rng, &pk, &p)?; // ciphertext
83            let p_recovered = c.decrypt(&sk);
84            let m_recovered = S::decode::<T>(&p_recovered);
85
86            assert_eq!(m, m_recovered);
87
88            // same but using encrypt_s (with sk instead of pk))
89            let c = S::encrypt_s(&mut rng, &sk, &p)?;
90            let p_recovered = c.decrypt(&sk);
91            let m_recovered = S::decode::<T>(&p_recovered);
92
93            assert_eq!(m.remodule::<T>(), m_recovered.remodule::<T>());
94        }
95
96        Ok(())
97    }
98}