From 93463554061802b42e90bfb6dff9b969b1954164 Mon Sep 17 00:00:00 2001 From: Sam Hadow Date: Wed, 21 May 2025 21:56:49 +0200 Subject: [PATCH] broken bootstrapping implementation --- src/bootstrapping.rs | 127 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 115 insertions(+), 12 deletions(-) diff --git a/src/bootstrapping.rs b/src/bootstrapping.rs index b47577d..9632696 100644 --- a/src/bootstrapping.rs +++ b/src/bootstrapping.rs @@ -76,18 +76,60 @@ pub fn generate_bootstrap_key( } -// /// Evaluates the decryption circuit to refresh a ciphertext (lower the noise level) -// pub fn bootstrap( -// ciphertext: &Integer, -// bk: &BootstrapKey, -// pk: &PublicKey, -// rho: u32, -// sk: &PrivateKey, -// ) -> Integer { -// // TODO: actual implementation -// let m = decrypt_bit(ciphertext, &sk.p); -// encrypt_bit_asym(m, pk, rho) -// } +pub fn encrypt_and_evaluate( + c: &Integer, + bootstrap_key: &BootstrapKey, + precision_bits: u32, +) -> Vec { + let mut result = Vec::with_capacity(bootstrap_key.y.len()); + + let scale = Integer::from(1) << precision_bits; + let scale_rat = Rational::from(&scale); + + for y_i in &bootstrap_key.y { + let prod = Rational::from(c) * y_i; + let scaled = prod.clone() * &scale_rat; + + let floored = scaled.clone().floor(); + let frac = &scaled - floored; + + result.push(frac); + } + + result +} + +pub fn bootstrap( + c: &Integer, + z: &[Rational], + encrypted_sk_bits: &[Integer], + pk: &PublicKey, + precision_bits: u32, +) -> Integer { + let int_scale = Integer::from(1) << precision_bits; + let scale_rat = Rational::from(&int_scale); + let half_rat = Rational::from((1, 2)); + let x0 = &pk.xs[0]; + + let mut acc = Integer::from(0); + for (z_i, enc_s_i) in z.iter().zip(encrypted_sk_bits.iter()) { + let rounded: Integer = (z_i.clone() * &scale_rat + &half_rat).floor_ref().into(); + + acc += &rounded * enc_s_i; + } + + acc %= x0; + + let inv_scale = int_scale + .invert(x0) + .expect("2^precision_bits and x0 must be coprime"); + let adjusted_sum = (acc * inv_scale) % x0; + + let mut diff = c.clone(); + diff -= &adjusted_sum; + diff.modulo(x0) +} + #[cfg(test)] mod tests { @@ -165,4 +207,65 @@ mod tests { bound ); } + + #[test] + fn test_bootstrapping_preserve_m() { + let eta = 1000; // secret key bit size + let rho = 30; // initial noise + let theta = 1000; // Hamming weight of hint support + let n_theta = 1000; // total hint size + let kappa = 1000; // precision for y_i + + let (sk, pk) = generate_keys(rho, eta, rho, theta); + let bk = generate_bootstrap_key(&pk, &sk.p, kappa, theta, n_theta, rho); + + for m in 0..=1 { + let mut c = encrypt_bit_asym(m, &pk, rho); + for _ in 0..=100 { + + let encrypted_z = encrypt_and_evaluate(&c, &bk, kappa); + c = bootstrap(&c, &encrypted_z, &bk.enc_s, &pk, kappa); + + let m_boot = decrypt_bit(&c, &sk.p); + assert_eq!( + m, m_boot, + "Bootstrap failed: expected {}, got {}", + m, m_boot + ); + } + } + } + + #[test] + fn test_bootstrapping() { + let eta = 1000; // secret key bit size + let rho = 30; // initial noise + let theta = 1000; // Hamming weight of hint support + let n_theta = 1000; // total hint size + let kappa = 1000; // precision for y_i + + let (sk, pk) = generate_keys(rho, eta, rho, theta); + let bk = generate_bootstrap_key(&pk, &sk.p, kappa, theta, n_theta, rho); + + // let c0 = encrypt_bit_asym(0u8, &pk, rho); + let c1 = encrypt_bit_asym(1u8, &pk, rho); + + for m in 0..=1 { + let mut c = encrypt_bit_asym(m, &pk, rho); + for _ in 0..=100 { + // c += c0.clone(); + c *= c1.clone(); + + let encrypted_z = encrypt_and_evaluate(&c, &bk, kappa*2); + c = bootstrap(&c, &encrypted_z, &bk.enc_s, &pk, kappa*2); + + let m_boot = decrypt_bit(&c, &sk.p); + assert_eq!( + m, m_boot, + "Bootstrap failed: expected {}, got {}", + m, m_boot + ); + } + } + } }