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}