arnaucube
arnaucube/fhe-studyPublic Self-hosted repository
fhe-study/ckks/src/lib.rs
ckks/src/lib.rsRaw
  1//! Implementation of CKKS https://eprint.iacr.org/2016/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 arith::{Rq, C, R};
  9
 10use anyhow::Result;
 11use rand::Rng;
 12use rand_distr::{Normal, Uniform};
 13
 14pub mod encoder;
 15pub use encoder::Encoder;
 16
 17// error deviation for the Gaussian(Normal) distribution
 18// sigma=3.2 from: https://eprint.iacr.org/2016/421.pdf page 17
 19const ERR_SIGMA: f64 = 3.2;
 20
 21#[derive(Debug)]
 22pub struct PublicKey<const Q: u64, const N: usize>(Rq<Q, N>, Rq<Q, N>);
 23
 24pub struct SecretKey<const Q: u64, const N: usize>(Rq<Q, N>);
 25
 26pub struct CKKS<const Q: u64, const N: usize> {
 27    encoder: Encoder<Q, N>,
 28}
 29
 30impl<const Q: u64, const N: usize> CKKS<Q, N> {
 31    pub fn new(delta: C<f64>) -> Self {
 32        let encoder = Encoder::<Q, N>::new(delta);
 33        Self { encoder }
 34    }
 35    /// generate a new key pair (privK, pubK)
 36    pub fn new_key(&self, mut rng: impl Rng) -> Result<(SecretKey<Q, N>, PublicKey<Q, N>)> {
 37        let Xi_key = Uniform::new(-1_f64, 1_f64);
 38        let Xi_err = Normal::new(0_f64, ERR_SIGMA)?;
 39
 40        let e = Rq::<Q, N>::rand_f64(&mut rng, Xi_err)?;
 41
 42        let mut s = Rq::<Q, N>::rand_f64(&mut rng, Xi_key)?;
 43        // since s is going to be multiplied by other Rq elements, already
 44        // compute its NTT
 45        s.compute_evals();
 46
 47        let a = Rq::<Q, N>::rand_f64(&mut rng, Xi_key)?;
 48
 49        let pk: PublicKey<Q, N> = PublicKey((&(-a) * &s) + e, a.clone());
 50        Ok((SecretKey(s), pk))
 51    }
 52
 53    // encrypts a plaintext \in R=Z_Q[X]/(X^N+1)
 54    fn encrypt(
 55        &self, // TODO maybe rm?
 56        mut rng: impl Rng,
 57        pk: &PublicKey<Q, N>,
 58        m: &R<N>,
 59    ) -> Result<(Rq<Q, N>, Rq<Q, N>)> {
 60        let Xi_key = Uniform::new(-1_f64, 1_f64);
 61        let Xi_err = Normal::new(0_f64, ERR_SIGMA)?;
 62
 63        let e_0 = Rq::<Q, N>::rand_f64(&mut rng, Xi_err)?;
 64        let e_1 = Rq::<Q, N>::rand_f64(&mut rng, Xi_err)?;
 65
 66        let v = Rq::<Q, N>::rand_f64(&mut rng, Xi_key)?;
 67
 68        let m: Rq<Q, N> = Rq::<Q, N>::from(*m);
 69
 70        Ok((m + e_0 + v * pk.0.clone(), v * pk.1.clone() + e_1))
 71    }
 72
 73    fn decrypt(
 74        &self, // TODO maybe rm?
 75        sk: SecretKey<Q, N>,
 76        c: (Rq<Q, N>, Rq<Q, N>),
 77    ) -> Result<R<N>> {
 78        let m = c.0.clone() + c.1 * sk.0;
 79        Ok(m.mod_centered_q())
 80    }
 81
 82    pub fn encode_and_encrypt(
 83        &self,
 84        mut rng: impl Rng,
 85        pk: &PublicKey<Q, N>,
 86        z: &[C<f64>],
 87    ) -> Result<(Rq<Q, N>, Rq<Q, N>)> {
 88        let m: R<N> = self.encoder.encode(&z)?; // polynomial (encoded vec) \in R
 89
 90        self.encrypt(&mut rng, pk, &m)
 91    }
 92
 93    pub fn decrypt_and_decode(
 94        &self,
 95        sk: SecretKey<Q, N>,
 96        c: (Rq<Q, N>, Rq<Q, N>),
 97    ) -> Result<Vec<C<f64>>> {
 98        let d = self.decrypt(sk, c)?;
 99
100        self.encoder.decode(&d)
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn test_encrypt_decrypt() -> Result<()> {
110        const Q: u64 = 2u64.pow(16) + 1;
111        const T: u64 = 16;
112        const N: usize = 8;
113        let scale_factor_u64 = 512_u64; // delta
114        let scale_factor = C::<f64>::new(512.0, 0.0); // delta
115
116        let mut rng = rand::thread_rng();
117
118        for _ in 0..1000 {
119            let ckks = CKKS::<Q, N>::new(scale_factor);
120
121            let (sk, pk) = ckks.new_key(&mut rng)?;
122
123            let m_raw: R<N> = Rq::<Q, N>::rand_f64(&mut rng, Uniform::new(0_f64, T as f64))?.to_r();
124            let m = m_raw * scale_factor_u64;
125
126            let ct = ckks.encrypt(&mut rng, &pk, &m)?;
127            let m_decrypted = ckks.decrypt(sk, ct)?;
128
129            let m_decrypted: Vec<u64> = m_decrypted
130                .coeffs()
131                .iter()
132                .map(|e| (*e as f64 / (scale_factor_u64 as f64)).round() as u64)
133                .collect();
134            let m_decrypted = Rq::<Q, N>::from_vec_u64(m_decrypted);
135            assert_eq!(m_decrypted, Rq::<Q, N>::from(m_raw));
136        }
137
138        Ok(())
139    }
140
141    #[test]
142    fn test_encode_encrypt_decrypt_decode() -> Result<()> {
143        const Q: u64 = 2u64.pow(16) + 1;
144        const T: u64 = 16;
145        const N: usize = 4;
146        let scale_factor = C::<f64>::new(512.0, 0.0); // delta
147
148        let mut rng = rand::thread_rng();
149
150        for _ in 0..1000 {
151            let ckks = CKKS::<Q, N>::new(scale_factor);
152            let (sk, pk) = ckks.new_key(&mut rng)?;
153
154            let z: Vec<C<f64>> = std::iter::repeat_with(|| C::<f64>::rand(&mut rng, T))
155                .take(N / 2)
156                .collect();
157            let m: R<N> = ckks.encoder.encode(&z)?;
158
159            // sanity check
160            {
161                let z_decoded = ckks.encoder.decode(&m)?;
162                let rounded_z_decoded: Vec<C<f64>> = z_decoded
163                    .iter()
164                    .map(|c| C::<f64>::new(c.re.round(), c.im.round()))
165                    .collect();
166                assert_eq!(rounded_z_decoded, z);
167            }
168
169            let ct = ckks.encrypt(&mut rng, &pk, &m)?;
170            let m_decrypted = ckks.decrypt(sk, ct)?;
171
172            let z_decrypted = ckks.encoder.decode(&m_decrypted)?;
173
174            let rounded_z_decrypted: Vec<C<f64>> = z_decrypted
175                .iter()
176                .map(|&c| C::<f64>::new(c.re.round(), c.im.round()))
177                .collect();
178            assert_eq!(rounded_z_decrypted, z);
179        }
180
181        Ok(())
182    }
183}