Update rust-engine/src/lwe.rs
Browse files- rust-engine/src/lwe.rs +48 -47
rust-engine/src/lwe.rs
CHANGED
|
@@ -39,13 +39,12 @@ impl RingLwe {
|
|
| 39 |
(a - t) as i16
|
| 40 |
}
|
| 41 |
|
| 42 |
-
/// Fast CBD sampling
|
| 43 |
fn sample_cbd(rng: &mut ChaCha20Rng) -> i16 {
|
| 44 |
-
let x:
|
| 45 |
-
//
|
| 46 |
-
|
| 47 |
-
let
|
| 48 |
-
let b = ((x >> 1) & 0x55555555).count_ones() as i16;
|
| 49 |
a - b
|
| 50 |
}
|
| 51 |
|
|
@@ -63,17 +62,18 @@ impl RingLwe {
|
|
| 63 |
let zeta = ZETAS[k];
|
| 64 |
k += 1;
|
| 65 |
for j in start..(start + len) {
|
|
|
|
| 66 |
let t = Self::f_reduce(zeta as i32 * p[j + len] as i32);
|
| 67 |
p[j + len] = p[j] - t;
|
| 68 |
p[j] = p[j] + t;
|
| 69 |
-
|
| 70 |
-
//
|
| 71 |
-
if p[j] > 4096
|
| 72 |
-
if p[j+len] > 4096 || p[j+len] < -4096 { p[j+len] = Self::f_reduce(p[j+len] as i32); }
|
| 73 |
}
|
| 74 |
}
|
| 75 |
len >>= 1;
|
| 76 |
}
|
|
|
|
| 77 |
}
|
| 78 |
|
| 79 |
/// Inverse NTT
|
|
@@ -85,6 +85,7 @@ impl RingLwe {
|
|
| 85 |
let zeta = ZETAS[k];
|
| 86 |
k -= 1;
|
| 87 |
for j in start..(start + len) {
|
|
|
|
| 88 |
let t = p[j];
|
| 89 |
p[j] = Self::f_reduce((t + p[j + len]) as i32);
|
| 90 |
let val = p[j + len] - t;
|
|
@@ -113,12 +114,12 @@ impl RingLwe {
|
|
| 113 |
let mut seed = [0u8; 32];
|
| 114 |
rng.fill(&mut seed);
|
| 115 |
|
| 116 |
-
// Expand seed to
|
| 117 |
let mut a_rng = ChaCha20Rng::from_seed(seed);
|
| 118 |
-
let mut
|
| 119 |
let uniform = Uniform::new(0, self.q);
|
| 120 |
for i in 0..N {
|
| 121 |
-
|
| 122 |
}
|
| 123 |
|
| 124 |
let mut s = [0i16; N];
|
|
@@ -128,32 +129,29 @@ impl RingLwe {
|
|
| 128 |
e[i] = Self::sample_cbd(&mut rng);
|
| 129 |
}
|
| 130 |
|
| 131 |
-
// Domain: NTT
|
| 132 |
-
let mut a_ntt = a;
|
| 133 |
let mut s_ntt = s;
|
| 134 |
-
|
| 135 |
self.ntt(&mut s_ntt);
|
|
|
|
| 136 |
|
| 137 |
-
let mut
|
| 138 |
-
self.intt(&mut as_prod);
|
| 139 |
-
|
| 140 |
-
let mut t = [0i16; N];
|
| 141 |
for i in 0..N {
|
| 142 |
-
|
|
|
|
| 143 |
}
|
| 144 |
|
| 145 |
-
(seed,
|
| 146 |
}
|
| 147 |
|
| 148 |
-
pub fn encrypt(&self, seed: &[u8; 32],
|
| 149 |
let mut rng = ChaCha20Rng::from_entropy();
|
| 150 |
|
| 151 |
-
// Expand public a from seed
|
| 152 |
let mut a_rng = ChaCha20Rng::from_seed(*seed);
|
| 153 |
-
let mut
|
| 154 |
let uniform = Uniform::new(0, self.q);
|
| 155 |
for i in 0..N {
|
| 156 |
-
|
| 157 |
}
|
| 158 |
|
| 159 |
let mut r = [0i16; N];
|
|
@@ -165,48 +163,51 @@ impl RingLwe {
|
|
| 165 |
e2[i] = Self::sample_cbd(&mut rng);
|
| 166 |
}
|
| 167 |
|
| 168 |
-
let mut a_ntt = a;
|
| 169 |
let mut r_ntt = r;
|
| 170 |
-
|
| 171 |
self.ntt(&mut r_ntt);
|
|
|
|
| 172 |
|
| 173 |
-
|
| 174 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
|
| 176 |
-
|
|
|
|
| 177 |
for i in 0..N {
|
| 178 |
-
|
| 179 |
}
|
| 180 |
-
|
| 181 |
-
let mut t_ntt = *t;
|
| 182 |
-
self.ntt(&mut t_ntt);
|
| 183 |
-
let mut tr = Self::pointwise_mul(&t_ntt, &r_ntt);
|
| 184 |
self.intt(&mut tr);
|
| 185 |
|
| 186 |
let m_encoded = (ptxt as i16 * (self.q / 2)) % self.q;
|
| 187 |
let mut v = [0i16; N];
|
| 188 |
for i in 0..N {
|
| 189 |
-
|
|
|
|
|
|
|
| 190 |
}
|
| 191 |
|
| 192 |
-
(
|
| 193 |
}
|
| 194 |
|
| 195 |
-
pub fn decrypt(&self,
|
| 196 |
-
let mut
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
let mut su = Self::pointwise_mul(&s_ntt, &u_ntt);
|
| 202 |
self.intt(&mut su);
|
| 203 |
|
| 204 |
let mut ones = 0;
|
| 205 |
let mut zeros = 0;
|
| 206 |
-
|
|
|
|
| 207 |
let m_approx = Self::f_reduce((v[i] - su[i]) as i32);
|
| 208 |
|
| 209 |
-
// Constant-ish distance check
|
| 210 |
let val = if m_approx < 0 { m_approx + self.q } else { m_approx };
|
| 211 |
let dist_to_0 = if val < self.q / 2 { val } else { self.q - val };
|
| 212 |
let dist_to_q2 = (val - self.q / 2).abs();
|
|
|
|
| 39 |
(a - t) as i16
|
| 40 |
}
|
| 41 |
|
| 42 |
+
/// Fast CBD sampling for η = 2
|
| 43 |
fn sample_cbd(rng: &mut ChaCha20Rng) -> i16 {
|
| 44 |
+
let x: u8 = rng.gen();
|
| 45 |
+
// Sample 1 coefficient from 4 bits (a=2bits, b=2bits)
|
| 46 |
+
let a = (x & 0x3).count_ones() as i16;
|
| 47 |
+
let b = ((x >> 2) & 0x3).count_ones() as i16;
|
|
|
|
| 48 |
a - b
|
| 49 |
}
|
| 50 |
|
|
|
|
| 62 |
let zeta = ZETAS[k];
|
| 63 |
k += 1;
|
| 64 |
for j in start..(start + len) {
|
| 65 |
+
// Forward butterfly
|
| 66 |
let t = Self::f_reduce(zeta as i32 * p[j + len] as i32);
|
| 67 |
p[j + len] = p[j] - t;
|
| 68 |
p[j] = p[j] + t;
|
| 69 |
+
|
| 70 |
+
// Frequent reduction to prevent overflow
|
| 71 |
+
if p[j].abs() > 4096 { p[j] = Self::f_reduce(p[j] as i32); }
|
|
|
|
| 72 |
}
|
| 73 |
}
|
| 74 |
len >>= 1;
|
| 75 |
}
|
| 76 |
+
for i in 0..N { p[i] = Self::f_reduce(p[i] as i32); }
|
| 77 |
}
|
| 78 |
|
| 79 |
/// Inverse NTT
|
|
|
|
| 85 |
let zeta = ZETAS[k];
|
| 86 |
k -= 1;
|
| 87 |
for j in start..(start + len) {
|
| 88 |
+
// Inverse butterfly
|
| 89 |
let t = p[j];
|
| 90 |
p[j] = Self::f_reduce((t + p[j + len]) as i32);
|
| 91 |
let val = p[j + len] - t;
|
|
|
|
| 114 |
let mut seed = [0u8; 32];
|
| 115 |
rng.fill(&mut seed);
|
| 116 |
|
| 117 |
+
// Expand seed to A in NTT domain
|
| 118 |
let mut a_rng = ChaCha20Rng::from_seed(seed);
|
| 119 |
+
let mut a_ntt = [0i16; N];
|
| 120 |
let uniform = Uniform::new(0, self.q);
|
| 121 |
for i in 0..N {
|
| 122 |
+
a_ntt[i] = uniform.sample(&mut a_rng);
|
| 123 |
}
|
| 124 |
|
| 125 |
let mut s = [0i16; N];
|
|
|
|
| 129 |
e[i] = Self::sample_cbd(&mut rng);
|
| 130 |
}
|
| 131 |
|
|
|
|
|
|
|
| 132 |
let mut s_ntt = s;
|
| 133 |
+
let mut e_ntt = e;
|
| 134 |
self.ntt(&mut s_ntt);
|
| 135 |
+
self.ntt(&mut e_ntt);
|
| 136 |
|
| 137 |
+
let mut t_ntt = [0i16; N];
|
|
|
|
|
|
|
|
|
|
| 138 |
for i in 0..N {
|
| 139 |
+
let prod = Self::f_reduce(a_ntt[i] as i32 * s_ntt[i] as i32);
|
| 140 |
+
t_ntt[i] = Self::f_reduce((prod + e_ntt[i]) as i32);
|
| 141 |
}
|
| 142 |
|
| 143 |
+
(seed, t_ntt, s_ntt)
|
| 144 |
}
|
| 145 |
|
| 146 |
+
pub fn encrypt(&self, seed: &[u8; 32], t_ntt: &Poly, ptxt: i32) -> (Poly, Poly) {
|
| 147 |
let mut rng = ChaCha20Rng::from_entropy();
|
| 148 |
|
| 149 |
+
// Expand public a from seed in NTT domain
|
| 150 |
let mut a_rng = ChaCha20Rng::from_seed(*seed);
|
| 151 |
+
let mut a_ntt = [0i16; N];
|
| 152 |
let uniform = Uniform::new(0, self.q);
|
| 153 |
for i in 0..N {
|
| 154 |
+
a_ntt[i] = uniform.sample(&mut a_rng);
|
| 155 |
}
|
| 156 |
|
| 157 |
let mut r = [0i16; N];
|
|
|
|
| 163 |
e2[i] = Self::sample_cbd(&mut rng);
|
| 164 |
}
|
| 165 |
|
|
|
|
| 166 |
let mut r_ntt = r;
|
| 167 |
+
let mut e1_ntt = e1;
|
| 168 |
self.ntt(&mut r_ntt);
|
| 169 |
+
self.ntt(&mut e1_ntt);
|
| 170 |
|
| 171 |
+
// u = a * r + e1 (in NTT domain)
|
| 172 |
+
let mut u_ntt = [0i16; N];
|
| 173 |
+
for i in 0..N {
|
| 174 |
+
let prod = Self::f_reduce(a_ntt[i] as i32 * r_ntt[i] as i32);
|
| 175 |
+
u_ntt[i] = Self::f_reduce((prod + e1_ntt[i]) as i32);
|
| 176 |
+
}
|
| 177 |
|
| 178 |
+
// v = t * r + e2 + m * q/2
|
| 179 |
+
let mut tr_ntt = [0i16; N];
|
| 180 |
for i in 0..N {
|
| 181 |
+
tr_ntt[i] = Self::f_reduce(t_ntt[i] as i32 * r_ntt[i] as i32);
|
| 182 |
}
|
| 183 |
+
let mut tr = tr_ntt;
|
|
|
|
|
|
|
|
|
|
| 184 |
self.intt(&mut tr);
|
| 185 |
|
| 186 |
let m_encoded = (ptxt as i16 * (self.q / 2)) % self.q;
|
| 187 |
let mut v = [0i16; N];
|
| 188 |
for i in 0..N {
|
| 189 |
+
// Encode message in first 32 coefficients for density to reduce noise sum
|
| 190 |
+
let m = if i < 32 { m_encoded } else { 0 };
|
| 191 |
+
v[i] = Self::f_reduce((tr[i] + e2[i] + m) as i32);
|
| 192 |
}
|
| 193 |
|
| 194 |
+
(u_ntt, v)
|
| 195 |
}
|
| 196 |
|
| 197 |
+
pub fn decrypt(&self, s_ntt: &Poly, u_ntt: &Poly, v: &Poly) -> i32 {
|
| 198 |
+
let mut su_ntt = [0i16; N];
|
| 199 |
+
for i in 0..N {
|
| 200 |
+
su_ntt[i] = Self::f_reduce(s_ntt[i] as i32 * u_ntt[i] as i32);
|
| 201 |
+
}
|
| 202 |
+
let mut su = su_ntt;
|
|
|
|
| 203 |
self.intt(&mut su);
|
| 204 |
|
| 205 |
let mut ones = 0;
|
| 206 |
let mut zeros = 0;
|
| 207 |
+
// Check only the indices where we encoded the message
|
| 208 |
+
for i in 0..32 {
|
| 209 |
let m_approx = Self::f_reduce((v[i] - su[i]) as i32);
|
| 210 |
|
|
|
|
| 211 |
let val = if m_approx < 0 { m_approx + self.q } else { m_approx };
|
| 212 |
let dist_to_0 = if val < self.q / 2 { val } else { self.q - val };
|
| 213 |
let dist_to_q2 = (val - self.q / 2).abs();
|