Commit dc9c56c1 authored by Sai Tarun Inaganti's avatar Sai Tarun Inaganti

No commit message

No commit message
parents
/target
Cargo.lock
[package]
name = "fhew"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
fftw = { version = "*" }
ndarray = { version = "*" }
num = { version = "*" }
rand = { version = "*" }
strum = { version = "*" }
\ No newline at end of file
use std::process::exit;
use fftw::types::*;
use ::fhew::{
*,
BinGate::*,
lwe::*,
fhew::*
};
use rand::Rng;
fn help(cmd: &String) {
eprintln!("\nusage: {} n\n", cmd);
eprintln!(" Generate a secret key sk and evaluation key ek, and repeat the following test n times:");
eprintln!(" - generate random bits b1,b2,b3,b4");
eprintln!(" - compute ciphertexts c1, c2, c3 and c4 encrypting b1, b2, b3 and b4 under sk");
eprintln!(" - homomorphically compute the encrypted (c1 NAND c2) NAND (c3 NAND c4)");
eprintln!(" - decrypt all the intermediate results and check correctness");
eprintln!("\n If any of the tests fails, print ERROR and stop immediately.\n");
exit(0);
}
fn cleartext_gate(v1: bool, v2: bool, gate: BinGate) -> bool {
match gate {
OR => v1 || v2,
AND => v1 && v2,
NOR => !(v1 || v2),
NAND => !(v1 && v2)
}
}
fn eprint_gate(gate: BinGate) {
match gate {
OR => eprint!(" OR\t"),
AND => eprint!(" AND\t"),
NOR => eprint!(" NOT\t"),
NAND => eprint!(" NAND\t")
}
}
fn main() {
// assert_eq!(q, 512);
let mut rng = rand::thread_rng();
let mut ffto: FFT = Default::default();
// fftSetup(&mut ffto);
let mut tTestMSB: RingFFT = Default::default();
let args: Vec<String> = std::env::args().collect();
if args.len() != 2 {
help(&args[0]);
}
let count: i32 = args[1].parse().unwrap();
eprintln!("Setting up FHEW");
fhew::setup(&mut ffto, &mut tTestMSB);
eprint!("Generating secret key ... ");
let mut lwe_sk: lwe::SecretKey = Default::default();
lwe::keyGen(&mut lwe_sk, &mut rng);
eprintln!("Done.\n");
eprintln!("Generating evaluation key ... this may take a while ... ");
let mut ek: EvalKey = Default::default();
fhew::keyGen(&mut ek, &lwe_sk, &mut rng, &mut ffto);
eprintln!("Done.\n");
eprintln!("Testing depth-2 homomorphic circuits {} times.", count);
eprintln!("Circuit shape : (a GATE NOT(b)) GATE (c GATE d)\n");
let (mut v1, mut v2): (i32, i32);
let (mut sv1, mut sv2): (i32, i32) = (2, 2);
let (mut se1, mut se2, mut e1, mut e2, mut e12): (CipherText, CipherText, CipherText, CipherText, CipherText)
= Default::default();
for i in 1..(3*count) {
if i % 3 != 0 {
v1 = rng.gen::<i32>() % 2;
v2 = rng.gen::<i32>() % 2;
lwe::encrypt(&mut e1, &lwe_sk, v1, &mut rng);
lwe::encrypt(&mut e2, &lwe_sk, v2, &mut rng);
if i % 3 == 1 {
eprint!(" NOT\tEnc({}) = ", v2);
let e2_temp = e2.clone();
fhew::hom_not(&mut e2, &e2_temp);
let notv2 = lwe::decrypt(&lwe_sk, &e2);
eprintln!("Enc({})", v2);
if !(notv2 == !v2) {
eprintln!("ERROR: incorrect NOT Homomorphic computation at iteration {}", i+1);
exit(1);
}
v2 = !v2;
}
} else {
v1 = sv1;
v2 = sv2;
e1 = se1.clone();
e2 = se2.clone();
}
let gate: BinGate = match rng.gen::<usize>() % 4 {
0 => BinGate::OR,
1 => BinGate::AND,
2 => BinGate::NOR,
3 => BinGate::NAND,
_ => BinGate::OR
};
eprint!("Enc({})", v1);
eprint_gate(gate);
eprint!("Enc({}) = ", v2);
fhew::hom_gate(&mut e12, gate, &ek, &e1, &e2, &mut ffto, &tTestMSB, &mut rng);
let v12: i32 = lwe::decrypt(&lwe_sk, &e12);
eprintln!("Enc({})", v12);
match i % 3 {
0 => eprintln!(""),
1 => {
sv1 = v12;
se1 = e12.clone();
},
2 => {
sv2 = v12;
se2 = e12.clone();
}
_ => ()
}
if cleartext_gate(v1 != 0, v2 != 0, gate) != (v12 != 0) {
eprintln!("\n ERROR: incorrect Homomorphic Gate computation at iteration {}", i+1);
exit(1);
}
}
eprintln!("\nPassed all tests!\n");
}
\ No newline at end of file
use rand::Rng;
use fftw::types::*;
use crate::{
*,
BinGate::*,
lwe::*
};
use std::fs::File;
use std::io::{
BufRead,
Write
};
#[derive(Clone)]
struct CtModQ(Array1<CtModQ1>); // [CtModQ1; K2];
impl Default for CtModQ {
fn default() -> Self {
CtModQ(Array::default(K2))
}
}
#[derive(Clone)]
struct CtModQ1(Array1<RingModQ>); // [RingModQ; 2];
impl Default for CtModQ1 {
fn default() -> Self {
CtModQ1(Array::default(2))
}
}
#[derive(Clone)]
struct DctModQ(Array1<DctModQ1>); // [DctModQ1; K2];
impl Default for DctModQ {
fn default() -> Self {
DctModQ(Array::default(K2))
}
}
#[derive(Clone)]
struct DctModQ1(Array1<RingModQ>); // [RingModQ; K2];
impl Default for DctModQ1 {
fn default() -> Self {
DctModQ1(Array::default(K2))
}
}
#[derive(Clone)]
struct DctFFT(Array1<DctFFT1>); // [DctFFT1; K2];
impl Default for DctFFT {
fn default() -> Self {
DctFFT(Array::default(K2))
}
}
#[derive(Clone)]
struct DctFFT1(Array1<RingFFT>); // [RingFFT; K2];
impl Default for DctFFT1 {
fn default() -> Self {
DctFFT1(Array::default(K2))
}
}
#[derive(Clone)]
pub struct CtFFT(Array1<CtFFT1>); // [CtFFT1; K2];
impl Default for CtFFT {
fn default() -> Self {
CtFFT(Array::default(K2))
}
}
#[derive(Clone)]
struct CtFFT1(Array1<RingFFT>); // [RingFFT; 2];
impl Default for CtFFT1 {
fn default() -> Self {
CtFFT1(Array::default(2))
}
}
#[derive(Clone)]
pub struct BootstrappingKey(Array3<CtFFT>); // [[[CtFFT; BS_EXP]; BS_BASE]; n];
impl Default for BootstrappingKey {
fn default() -> Self {
BootstrappingKey(Array::default((n,BS_BASE,BS_EXP)))
}
}
#[derive(Default,Clone)]
pub struct EvalKey {
pub BSkey: BootstrappingKey,
pub KSkey: lwe::SwitchingKey
}
pub fn setup(ffto: &mut FFT, tTestMSB: &mut RingFFT) {
fft_setup(ffto);
let mut tmsb: RingModQ = Default::default();
tmsb.0[0] = Wrapping(-1);
for i in 1..N {
tmsb.0[i] = Wrapping(1);
}
fft_forward(ffto, &tmsb, tTestMSB);
}
fn fhewEncrypt(ct: &mut CtFFT, skFFT: &RingFFT, m: i32, rng: &mut rand::rngs::ThreadRng, ffto: &mut FFT) {
let mut ai: RingFFT = Default::default();
let mut res: CtModQ = Default::default();
let qi = q as i32;
let Ni = N as i32;
let mut mm: i32 = (((m & qi) + qi) % qi) * (2*Ni/qi);
let mut sign: i32 = 1;
if mm >= Ni {
mm -= Ni;
sign = -1;
}
// println!("stage 1");
for i in 0..K2 {
for k in 0..(Ni as usize) {
res.0[i].0[0].0[k] = rng.gen();
}
fft_forward(ffto, &((res.0[i]).0[0]), &mut ai);
for k in 0..(N2 as usize) {
ai.0[k] = ai.0[k] * skFFT.0[k];
}
fft_backward(ffto, &ai, &mut res.0[i].0[1]);
for k in 0..(Ni as usize) {
// println!("i = {}, k = {}, res = {}", i, k, res.0[i].0[1].0[k]);
res.0[i].0[1].0[k] += Wrapping(sample(&CHI1, rng) as i32);
}
}
// println!("stage 2");
for i in 0..K {
res.0[2*i].0[0].0[mm as usize] += Wrapping(sign) * vgprime[i];
res.0[2*i+1].0[1].0[mm as usize] += Wrapping(sign) * vgprime[i];
}
// println!("stage 3");
for i in 0..K2 {
for j in 0..2 {
fft_forward(ffto, &res.0[i].0[j], &mut ct.0[i].0[j]);
}
}
// println!("stage 4");
}
pub fn keyGen(ek: &mut EvalKey, lweSk: &lwe::SecretKey, rng: &mut rand::rngs::ThreadRng, ffto: &mut FFT) {
let mut fhewSK: SecretKeyN = Default::default();
keyGenN(&mut fhewSK, rng);
switchingKeyGen(&mut ek.KSkey, &lweSk, &fhewSK, rng);
let mut fhewSkFFT: RingFFT = Default::default();
let mut fhew_rmq: RingModQ = Default::default();
for i in 0..N {
fhew_rmq.0[i] = Wrapping(fhewSK.0[i]);
}
fft_forward(ffto, &fhew_rmq, &mut fhewSkFFT);
for i in 0..n {
for j in 1..BS_BASE {
for k in 0..BS_EXP {
ek.BSkey.0[[i,j,k]] = Default::default();
fhewEncrypt(&mut ek.BSkey.0[[i,j,k]], &fhewSkFFT, lweSk.0[i]*(j as i32)*(BS_TABLE[k] as i32), rng, ffto);
}
}
}
}
fn addToAcc(acc: &mut CtFFT1, c: &CtFFT, ffto: &mut FFT) {
let mut ct: CtModQ1 = Default::default();
let mut dct: DctModQ1 = Default::default();
let mut dctFFT: DctFFT1 = Default::default();
for j in 0..2 {
fft_backward(ffto, &acc.0[j], &mut ct.0[j]);
}
for j in 0..2 {
for k in 0..N {
let mut t: ZmodQ = ct.0[j].0[k] * v_inverse;
for l in 0..K {
let r: ZmodQ = Wrapping((t.0 << g_bits_32[l]) >> g_bits_32[l]);
t = Wrapping((t - r).0 >> g_bits[l]);
dct.0[j+2*1].0[k] = r;
}
}
}
for j in 0..K2 {
fft_forward(ffto, &dct.0[j], &mut dctFFT.0[j]);
}
for j in 0..2 {
for k in 0..N2 {
acc.0[j].0[k] = c64::new(0.0,0.0);
for l in 0..K2 {
acc.0[j].0[k] += dctFFT.0[l].0[k] * c.0[l].0[j].0[k];
}
}
}
}
fn initializeAcc(acc: &mut CtFFT1, m: i32, ffto: &mut FFT) {
let mut res: CtModQ1 = Default::default();
let qi = q as i32;
let Ni = N as i32;
let mut mm = (((m % qi) + qi) % qi) * (2*Ni/qi);
let mut sign = 1;
if mm >= Ni {
mm -= Ni;
sign = -1;
}
for j in 0..2 {
for k in 0..N {
res.0[j].0[k] = Wrapping(0);
}
}
res.0[1].0[mm as usize] += Wrapping(sign) * vgprime[0];
for j in 0..2 {
fft_forward(ffto, &res.0[j], &mut acc.0[j]);
}
}
fn memberTest(t: &RingFFT, c: &CtFFT1, ffto: &mut FFT) -> CipherTextQN {
let mut temp: RingFFT = Default::default();
let mut tempModQ: RingModQ = Default::default();
let mut ct: CipherTextQN = Default::default();
for i in 0..N2 {
temp.0[i] = (c.0[0].0[i] * t.0[i]).conj();
}
fft_backward(ffto, &temp, &mut tempModQ);
for i in 0..N {
ct.a[i] = tempModQ.0[i];
}
for i in 0..N2 {
temp.0[i] = c.0[1].0[i] * t.0[i];
}
fft_backward(ffto, &temp, &mut tempModQ);
ct.b = v + tempModQ.0[0];
ct
}
pub fn hom_gate(
res: &mut CipherText,
gate: BinGate,
ek: &EvalKey,
ct1: &CipherText,
ct2: &CipherText,
ffto: &mut FFT,
tTestMSB: &RingFFT,
rng: &mut rand::rngs::ThreadRng
) {
let mut e12: CipherText = Default::default();
let qi = q as i32;
for i in 0..n {
if ((ct1.a[i] - ct2.a[i]) % qi) != 0 && ((ct1.a[i] + ct2.a[i]) % qi) != 0 {
break;
}
if i == n - 1 {
panic!("ERROR: Please only use independant ciphertexts as inputs.");
}
}
for i in 0..n {
e12.a[i] = (2*qi - (ct1.a[i] + ct2.a[i])) % qi;
}
e12.b = (GATE_CONST[gate as usize] as i32) - (ct1.b + ct2.b) % qi;
let mut acc: CtFFT1 = Default::default();
initializeAcc(&mut acc, (e12.b + qi/4) % qi, ffto);
for i in 0..n {
let mut a = (qi - e12.a[i] % qi) % qi;
for k in 0..BS_EXP {
let a0 = a % (BS_BASE as i32);
if a0 != 0 {
addToAcc(&mut acc, &ek.BSkey.0[[i,a0 as usize,k]], ffto);
}
a /= BS_BASE as i32;
}
}
let eQN: CipherTextQN = memberTest(tTestMSB, &acc, ffto);
let mut eQ: CipherTextQ = Default::default();
keySwitch(&mut eQ, &ek.KSkey, &eQN);
modSwitch(res, &eQ, rng);
}
pub fn hom_nand(
res: &mut CipherText,
ek: &EvalKey,
ct1: &CipherText,
ct2: &CipherText,
ffto: &mut FFT,
tTestMSB: &RingFFT,
rng: &mut rand::rngs::ThreadRng) {
hom_gate(res, NAND, ek, ct1, ct2, ffto, tTestMSB, rng)
}
pub fn hom_not(res: &mut CipherText, ct: &CipherText) {
let qi = q as i32;
for i in 0..n {
res.a[i] = (qi - ct.a[i]) % qi;
}
res.b = (5 * qi / 4 - ct.b) % qi;
}
pub fn fwriteEK(ek: &EvalKey, f: &mut File) {
for i in 0..n {
for j in 1..BS_BASE {
for k in 0..BS_EXP {
for l in 0..K2 {
for m in 0..2 {
for n_ in 0..N2 {
writeln!(*f, "{}", ek.BSkey.0[[i,j,k]].0[l].0[m].0[n_]).unwrap();
}
}
}
}
}
}
for i in 0..N {
for j in 0..KS_BASE {
for k in 0..KS_EXP {
for l in 0..n {
writeln!(*f, "{}", ek.KSkey.0[[i,j,k]].a[l]).unwrap();
}
writeln!(*f, "{}", ek.KSkey.0[[i,j,k]].b).unwrap();
}
}
}
// f.sync_all().unwrap();
}
pub fn freadEK(f: &File) -> EvalKey {
use std::str::FromStr;
let mut ek: EvalKey = Default::default();
let mut b = std::io::BufReader::new(f);
let mut s = String::new();
for i in 0..n {
for j in 1..BS_BASE {
for k in 0..BS_EXP {
for l in 0..K2 {
for m in 0..2 {
for n_ in 0..N2 {
b.read_line(&mut s).unwrap();
ek.BSkey.0[[i,j,k]].0[l].0[m].0[n_] = c64::from_str(&s).unwrap();
}
}
}
}
}
}
for i in 0..N {
for j in 0..KS_BASE {
for k in 0..KS_EXP {
for l in 0..n {
b.read_line(&mut s).unwrap();
ek.KSkey.0[[i,j,k]].a[l] = Wrapping(i32::from_str(&s).unwrap());
}
b.read_line(&mut s).unwrap();
ek.KSkey.0[[i,j,k]].b = Wrapping(i32::from_str(&s).unwrap());
}
}
}
// f.sync_all().unwrap();
ek
}
\ No newline at end of file
#![allow(dead_code)]
use fftw::{
array::AlignedVec,
plan::*,
types::*
};
use ndarray::*;
use rand::Rng;
use std::num::Wrapping;
pub const n: usize = 500;
pub const N: usize = 1024;
pub const N2: usize = N/2;
pub const K: usize = 3;
pub const K2: usize = 6;
pub const Q: usize = 1 << 32;
pub const q: usize = 512;
pub const q2: usize = 256;
type ZmodQ = Wrapping<i32>;
type UZmodQ = Wrapping<u32>;
const v: ZmodQ = Wrapping((1 << 29) + 1);
const v_inverse: ZmodQ = Wrapping(-536870911); // 3758096385;
const vgprime: [ZmodQ; 3] = [Wrapping(536870913), Wrapping(2048), Wrapping(4194304)]; // [v, v<<11, v<<22];
const g_bits: [isize; 3] = [11, 11, 10];
const g_bits_32: [isize; 3] = [21, 21, 22];
pub const KS_BASE: usize = 25;
pub const KS_EXP: usize = 7;
pub const KS_TABLE: [i32; 7] = [
1,
25,
25*25,
25*25*25,
25*25*25*25,
25*25*25*25*25,
25*25*25*25*25*25
];
pub const BS_BASE: usize = 23;
pub const BS_EXP: usize = 2;
pub const BS_TABLE: [usize; 2] = [1, 23];
#[derive(Clone)]
pub struct RingModQ(pub Array1<ZmodQ>); // [ZmodQ; N];
impl Default for RingModQ {
fn default() -> RingModQ {
RingModQ(Array::default(N))
}
}
#[derive(Clone)]
pub struct RingFFT(pub Array1<c64>); // [c64; N2];
impl Default for RingFFT {
fn default() -> RingFFT {
RingFFT(Array::default(N2))
}
}
#[derive(Copy,Clone)]
pub enum BinGate { OR, AND, NOR, NAND }
const GATE_CONST: [usize; 4] = [15*q/8, 9*q/8, 11*q/8, 13*q/8];
pub mod lwe;
pub mod fhew;
struct Distrib {
std_dev: f64,
max: i32,
offset: i32,
table: &'static [f64]
}
fn sample(chi: &Distrib, rng: &mut rand::rngs::ThreadRng) -> i32 {
// dbg!(chi.std_dev);
if chi.max != 0 {
// println!("path 1");
let r: f64 = rng.gen();
for i in 0..chi.max {
if r <= chi.table[i as usize] {
return i - chi.offset;
}
}
panic!("Sampling Error: distribution table ending before (double) 1.0");
}
let mut r: f64;
let s = chi.std_dev;
if s < 500.0 {
// println!("path 2");
let mut x: i32;
let maxx= (s*8.0).ceil() as i32;
loop {
x = rng.gen::<i32>() % (2*maxx + 1) - maxx;
r = rng.gen();
// println!("x = {}, y = {}, z = {}", r, x, s);
if r < (- (x*x) as f64 / (2.0*s*s)).exp() {
return x;
}
}
} else {
// println!("path 3");
let mut x: f64;
loop {
x = 16.0 * rng.gen::<f64>() - 8.0;
r = rng.gen();
// println!("r = {}\tx = {}\ts = {}", r, x, s);
if r < (- x*x / 2.0).exp() {
return (0.5 + x*s).floor() as i32;
}
}
}
}
const CHI1_TABLE: [f64; 23] = [
1.12011750313263e-14, 2.38717233762211e-12, 3.04966971020178e-10,
2.34394541319773e-8, 1.08538196465647e-6, 0.0000303513786306856,
0.000514575939439740, 0.00532464699317562, 0.0340111330223921,
0.136723892128727, 0.357520614142345, 0.642479385857655,
0.863276107871273, 0.965988866977608, 0.994675353006824,
0.999485424060560, 0.999969648621369, 0.999998914618035,
0.999999976560546, 0.999999999695033, 0.999999999997613,
0.999999999999989, 1.00000000000000
];
const CHI1: Distrib = Distrib {
std_dev: 1.4,
max: 23,
offset: 11,
table: &CHI1_TABLE
};
const NO_TABLE: [f64; 1] = [1.0];
const CHI2: Distrib = Distrib {
std_dev: (1 << 17) as f64,
max: 0,
offset: 0,
table: &NO_TABLE
};
const CHI3: Distrib = Distrib {
std_dev: 6.0,
max: 0,
offset: 0,
table: &NO_TABLE
};
const BINARY_TABLE: [f64; 3] = [
0.25,
0.75,
1.0
];
const CHI_BINARY: Distrib = Distrib {
std_dev: 0.0,
max: 3,
offset: 1,
table: &BINARY_TABLE
};
pub struct FFT {
pub in_: AlignedVec<f64>,
pub out: AlignedVec<c64>,
pub plan_fft_forw: fftw::plan::R2CPlan64,
pub plan_fft_back: fftw::plan::C2RPlan64
}
impl Default for FFT {
fn default() -> Self {
FFT {
in_: AlignedVec::new(N*2),
out: AlignedVec::new(N+1),
plan_fft_forw: R2CPlan64::aligned(&[N*2], Flag::PATIENT).unwrap(),
plan_fft_back: C2RPlan64::aligned(&[N*2], Flag::PATIENT).unwrap()
}
}
}
pub fn fft_setup(ffto: &mut FFT) {
*ffto = Default::default();
}
pub fn fft_forward(ffto: &mut FFT, val: &RingModQ, res: &mut RingFFT) {
for k in 0..N {
ffto.in_[k] = val.0[k].0 as f64;
ffto.in_[k+N] = 0.0;
}
ffto.plan_fft_forw.r2c(&mut ffto.in_, &mut ffto.out).unwrap();
for k in 0..N2 {
res.0[k] = ffto.out[2*k+1];
}
}
pub fn fft_backward(ffto: &mut FFT, val: &RingFFT, res: &mut RingModQ) {
for k in 0..N2 {
ffto.out[2*k+1] = val.0[k]/c64::new(N as f64,0.0);
ffto.out[2*k] = c64::new(0.0,0.0);
}
ffto.plan_fft_back.c2r(&mut ffto.out, &mut ffto.in_).unwrap();
for k in 0..N {
res.0[k] = Wrapping(ffto.in_[k].round() as i32);
}
}
use rand::Rng;
use crate::*;
#[derive(Clone)]
pub struct CipherText {
pub a: Array1<i32>, // [isize; n],
pub b: i32
}
impl Default for CipherText {
fn default() -> Self {
CipherText {
a: Array::default(n),
b: 0
}
}
}
#[derive(Clone)]
pub struct CipherTextQ {
pub a: Array1<ZmodQ>, // [ZmodQ; n],
pub b: ZmodQ
}
impl Default for CipherTextQ {
fn default() -> Self {
CipherTextQ {
a: Array::default(n),
b: Default::default()
}
}
}
#[derive(Clone)]
pub struct CipherTextQN {
pub a: Array1<ZmodQ>, // [ZmodQ; n],
pub b: ZmodQ
}
impl Default for CipherTextQN {
fn default() -> Self {
CipherTextQN {
a: Array::default(n),
b: Default::default()
}
}
}
#[derive(Clone)]
pub struct SecretKey(pub Array1<i32>); // [isize; n];
impl Default for SecretKey {
fn default() -> Self {
SecretKey(Array::default(n))
}
}
#[derive(Clone)]
pub struct SecretKeyN(pub Array1<i32>); // [isize; N];
impl Default for SecretKeyN {
fn default() -> Self {
SecretKeyN(Array::default(N))
}
}
const qi: i32 = q as i32;
pub fn keyGen(sk: &mut SecretKey, rng: &mut rand::rngs::ThreadRng) {
loop {
let mut s = 0;
let mut ss = 0;
for i in 0..n {
sk.0[i] = sample(&CHI_BINARY, rng);
s += sk.0[i];
ss += (sk.0[i]).abs();
}
if s.abs() > 5 || (ss - (n as i32) / 2).abs() > 5 {
continue;
} else {
break;
}
}
}
pub fn keyGenN(sk: &mut SecretKeyN, rng: &mut rand::rngs::ThreadRng) {
for i in 0..N {
sk.0[i] = sample(&CHI1, rng);
}
}
pub fn encrypt(ct: &mut CipherText, sk: &SecretKey, m: i32, rng: &mut rand::rngs::ThreadRng) {
ct.b = (m % 4) * qi / 4 + sample(&CHI3, rng);
for i in 0..n {
ct.a[i] = rng.gen::<i32>() % qi;
ct.b = (ct.b + ct.a[i] * sk.0[i]) % qi;
}
}
pub fn decrypt(sk: &SecretKey, ct: &CipherText) -> i32 {
let mut r = ct.b;
for i in 0..n {
r -= ct.a[i] * sk.0[i];
}
r = ((r % qi) + qi + qi/8) % qi;
4 * r / qi
}
#[derive(Clone)]
pub struct SwitchingKey(pub Array3<CipherTextQ>); // [[[CipherTextQ; KS_EXP]; KS_BASE]; N];
impl Default for SwitchingKey {
fn default() -> Self {
SwitchingKey(Array::default((N,KS_BASE,KS_EXP)))
}
}
pub fn switchingKeyGen(res: &mut SwitchingKey, new_sk: &SecretKey, old_sk: &SecretKeyN, rng: &mut rand::rngs::ThreadRng) {
for i in 0..N {
for j in 0..KS_BASE {
for k in 0..KS_EXP {
let mut ct: CipherTextQ = Default::default();
ct.b = - Wrapping(old_sk.0[i]) * Wrapping(j as i32) * Wrapping(KS_TABLE[k]) + Wrapping(sample(&CHI2, rng));
for l in 0..n {
ct.a[l] = rng.gen();
let addend = ct.a[l] * Wrapping(new_sk.0[l]);
// dbg!(ct.b, addend, isize::MAX, isize::MIN);
// let upper = addend <= isize::MAX - ct.b;
// let lower = addend >= isize::MIN - ct.b;
// ct.b = if upper && lower {
// ct.b + addend
// } else if !upper && lower {
// isize::MIN + (addend - (isize::MAX - ct.b)) - 1
// } else {
// isize::MAX - (- addend - (ct.b - isize::MIN)) + 1
// };
ct.b = ct.b + addend;
// dbg!(ct.b);
}
res.0[[i,j,k]] = ct;
}
}
}
}
pub fn keySwitch(res: &mut CipherTextQ, ksk: &SwitchingKey, ct: &CipherTextQN) {
for k in 0..n {
res.a[k] = Wrapping(0);
}
res.b = ct.b;
for i in 0..N {
let a: UZmodQ = Wrapping(- ct.a[i].0 as u32);
for j in 0..KS_BASE {
let a0: UZmodQ = a % Wrapping(KS_BASE as u32);
for k in 0..n {
res.a[k] -= ksk.0[[i,a0.0 as usize,j]].a[k];
res.b -= ksk.0[[i,a0.0 as usize,j]].b;
}
}
}
}
pub fn modSwitch(ct: &mut CipherText, c: &CipherTextQ, rng: &mut rand::rngs::ThreadRng) {
for i in 0..n {
ct.a[i] = round_qQ(c.a[i]);
}
ct.b = round_qQ(c.b);
}
pub fn round_qQ(v_: ZmodQ) -> i32 {
(0.5 + (v_.0 as f64) * (q as f64) / (Q as f64)).floor() as i32
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment