//! Implementation of TFHE https://eprint.iacr.org/2018/421.pdf #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(clippy::upper_case_acronyms)] #![allow(dead_code)] // TMP use anyhow::Result; use rand::Rng; use rand_distr::{Normal, Uniform}; use std::array; use arith::{Ring, Rq, Tn, T64}; use gfhe::{glwe, GLWE}; pub mod tlev; pub mod tlwe; #[derive(Clone, Debug)] pub struct SecretKey(glwe::SecretKey, K>); #[derive(Clone, Debug)] pub struct PublicKey(glwe::PublicKey, K>); #[derive(Clone, Debug)] pub struct TLWE(pub GLWE, K>); impl TLWE { pub fn new_key(rng: impl Rng) -> Result<(SecretKey, PublicKey)> { let (sk, pk) = GLWE::new_key(rng)?; Ok((SecretKey(sk), PublicKey(pk))) } pub fn encode(m: &Rq) -> Tn<1> { let delta = u64::MAX / P; // floored let coeffs = m.coeffs(); Tn(array::from_fn(|i| T64(coeffs[i].0 * delta))) } pub fn decode(p: &Tn<1>) -> Rq { let p = p.mul_div_round(P, u64::MAX); Rq::::from_vec_u64(p.coeffs().iter().map(|c| c.0).collect()) } pub fn encrypt_s(rng: impl Rng, sk: &SecretKey, p: &Tn<1>) -> Result { let glwe = GLWE::encrypt_s(rng, &sk.0, p)?; Ok(Self(glwe)) } pub fn encrypt(rng: impl Rng, pk: &PublicKey, p: &Tn<1>) -> Result { let glwe = GLWE::encrypt(rng, &pk.0, p)?; Ok(Self(glwe)) } pub fn decrypt(&self, sk: &SecretKey) -> Tn<1> { self.0.decrypt(&sk.0) } } #[cfg(test)] mod tests { use anyhow::Result; use rand::distributions::Uniform; use super::*; #[test] fn test_encrypt_decrypt() -> Result<()> { const T: u64 = 128; // plaintext modulus const K: usize = 16; type S = TLWE; // 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_f64, T as f64); let m = Rq::::rand(&mut rng, msg_dist); // msg // let m: Rq = m.remodule::(); let p = S::encode::(&m); // plaintext let c = S::encrypt(&mut rng, &pk, &p)?; // ciphertext let p_recovered = c.decrypt(&sk); let m_recovered = S::decode::(&p_recovered); assert_eq!(m, m_recovered); // same but using encrypt_s (with sk instead of pk)) let c = S::encrypt_s(&mut rng, &sk, &p)?; let p_recovered = c.decrypt(&sk); let m_recovered = S::decode::(&p_recovered); assert_eq!(m.remodule::(), m_recovered.remodule::()); } Ok(()) } }