bootstrap key generation
This commit is contained in:
@ -1,78 +1,168 @@
|
||||
use crate::dghv::decrypt_bit;
|
||||
use crate::dghv_asym::{encrypt_bit_asym, PrivateKey, PublicKey};
|
||||
use rug::Integer;
|
||||
use crate::dghv_asym::{encrypt_bit_asym, PublicKey};
|
||||
use crate::utils::generate_random_integer_range;
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::rngs::StdRng;
|
||||
use rand::SeedableRng;
|
||||
use rug::{Integer, Rational};
|
||||
|
||||
/// Bootstrap key, encrypted bits of the private key
|
||||
/// Bootstrap key, hint vector and encrypted bits of the private key
|
||||
pub struct BootstrapKey {
|
||||
pub encrypted_bits: Vec<Integer>,
|
||||
pub y: Vec<Rational>,
|
||||
pub enc_s: Vec<Integer>,
|
||||
}
|
||||
|
||||
/// Generates bootstrap key
|
||||
/// bootstrap key is a Vec with each bit of the private key encrypted with the public key
|
||||
pub fn generate_bootstrap_key(pk: &PublicKey, sk: &Integer, rho: u32) -> BootstrapKey {
|
||||
let eta = sk.significant_bits();
|
||||
let bits = get_bits(sk, eta);
|
||||
let encrypted_bits: Vec<Integer> = bits
|
||||
.into_iter()
|
||||
.map(|bit| encrypt_bit_asym(bit, pk, rho))
|
||||
.collect();
|
||||
|
||||
BootstrapKey { encrypted_bits }
|
||||
}
|
||||
|
||||
/// Evaluates the decryption circuit to refresh a ciphertext (lower the noise level)
|
||||
pub fn bootstrap(
|
||||
ciphertext: &Integer,
|
||||
bk: &BootstrapKey,
|
||||
/// Generates a bootstrap key
|
||||
/// pk: public key
|
||||
/// sk: secret integer p
|
||||
/// kappa: precision parameter (bits) for y_i
|
||||
/// theta: Hamming weight of the secret support
|
||||
/// n_theta: total number of hint elements
|
||||
/// rho: noise parameter (encryption)
|
||||
pub fn generate_bootstrap_key(
|
||||
pk: &PublicKey,
|
||||
sk: &Integer,
|
||||
kappa: u32,
|
||||
theta: usize,
|
||||
n_theta: usize,
|
||||
rho: u32,
|
||||
sk: &PrivateKey,
|
||||
) -> Integer {
|
||||
// TODO: actual implementation
|
||||
let m = decrypt_bit(ciphertext, &sk.p);
|
||||
encrypt_bit_asym(m, pk, rho)
|
||||
) -> BootstrapKey {
|
||||
// xp = ⌊2^κ / p⌋
|
||||
let two_k = Integer::from(1) << kappa;
|
||||
let xp: Integer = &two_k / sk.clone();
|
||||
|
||||
// support vector s, Hamming weight theta
|
||||
let mut rng = StdRng::from_os_rng();
|
||||
let mut indices: Vec<usize> = (0..n_theta).collect();
|
||||
indices.shuffle(&mut rng);
|
||||
let s: Vec<usize> = indices.iter().cloned().take(theta).collect();
|
||||
|
||||
let mut s_bits = vec![0u8; n_theta];
|
||||
for &i in &s {
|
||||
s_bits[i] = 1;
|
||||
}
|
||||
|
||||
// u[i] = xp (mod 2^(k+1))
|
||||
let mut u = vec![Integer::from(0); n_theta];
|
||||
let two_k1 = Integer::from(1) << (kappa + 1);
|
||||
for i in 0..n_theta {
|
||||
if !s.contains(&i) {
|
||||
u[i] = generate_random_integer_range(0, kappa + 1);
|
||||
}
|
||||
}
|
||||
if theta > 0 {
|
||||
for &i in s.iter().take(theta - 1) {
|
||||
u[i] = generate_random_integer_range(0, kappa + 1);
|
||||
}
|
||||
|
||||
let mut sum_s_minus_last = Integer::from(0);
|
||||
for &i in s.iter().take(theta - 1) {
|
||||
sum_s_minus_last += &u[i];
|
||||
}
|
||||
|
||||
let s_last = s[theta - 1];
|
||||
let diff = xp.clone() - &sum_s_minus_last;
|
||||
let mut remainder = diff % &two_k1;
|
||||
if remainder < 0 {
|
||||
remainder += &two_k1;
|
||||
}
|
||||
u[s_last] = remainder;
|
||||
}
|
||||
|
||||
// y[i] = u[i] / 2^κ
|
||||
let y: Vec<Rational> = u.iter().map(|ui| Rational::from(ui) / Rational::from(&two_k)).collect();
|
||||
let enc_s: Vec<Integer> = s_bits.iter().map(|&bit| encrypt_bit_asym(bit, &pk, rho)).collect();
|
||||
|
||||
BootstrapKey { y, enc_s }
|
||||
}
|
||||
|
||||
// extract bits from an Integer
|
||||
fn get_bits(n: &Integer, num_bits: u32) -> Vec<u8> {
|
||||
(0..num_bits).map(|i| n.get_bit(i) as u8).collect()
|
||||
}
|
||||
|
||||
// /// 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)
|
||||
// }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::dghv_asym::generate_keys;
|
||||
use crate::dghv::decrypt_bit;
|
||||
|
||||
#[test]
|
||||
fn test_bootstrap_key_generation() {
|
||||
let eta = 1024;
|
||||
let gamma = 10000;
|
||||
let rho = 64;
|
||||
let rho = 10;
|
||||
let theta = 10;
|
||||
let (sk, pk) = generate_keys(gamma, eta, rho, theta);
|
||||
let bk = generate_bootstrap_key(&pk, &sk.p, rho);
|
||||
let n_theta = 50;
|
||||
let kappa = 100;
|
||||
|
||||
assert_eq!(bk.encrypted_bits.len(), eta as usize);
|
||||
for bit in bk.encrypted_bits {
|
||||
assert!(bit >= Integer::from(0));
|
||||
assert!(bit < pk.xs[0]);
|
||||
let (sk, pk) = generate_keys(rho, eta, rho, theta);
|
||||
let bootstrap_key = generate_bootstrap_key(&pk, &sk.p, kappa, theta, n_theta, rho);
|
||||
|
||||
let y = bootstrap_key.y;
|
||||
let enc_s = bootstrap_key.enc_s;
|
||||
|
||||
let s_bits: Vec<u8> = enc_s.iter().map(|c| {
|
||||
let m = decrypt_bit(c, &sk.p);
|
||||
assert!(m == 0 || m == 1, "Decrypted bit must be 0 or 1");
|
||||
m
|
||||
}).collect();
|
||||
let hamming_weight = s_bits.iter().filter(|&&b| b == 1).count();
|
||||
assert_eq!(hamming_weight, theta, "Hamming weight of s should be {}", theta);
|
||||
assert_eq!(s_bits.len(), n_theta, "s_bits length should be {}", n_theta);
|
||||
|
||||
let two_k = Integer::from(1) << kappa;
|
||||
let two_k1 = Integer::from(1) << (kappa + 1);
|
||||
let xp = two_k.clone() / &sk.p;
|
||||
let u: Vec<Integer> = y.iter().map(|yi| Integer::from((yi.clone() * &two_k).floor_ref())).collect();
|
||||
|
||||
let mut sum_u_s = Integer::from(0);
|
||||
for (i, &bit) in s_bits.iter().enumerate() {
|
||||
if bit == 1 {
|
||||
sum_u_s += &u[i];
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bootstrapping() {
|
||||
let eta = 1024;
|
||||
let gamma = 10000;
|
||||
let rho = 64;
|
||||
let theta = 10;
|
||||
let (sk, pk) = generate_keys(gamma, eta, rho, theta);
|
||||
let bk = generate_bootstrap_key(&pk, &sk.p, rho);
|
||||
let diff = (sum_u_s.clone() - &xp) % &two_k1;
|
||||
assert_eq!(diff, 0, "Sum of u_i over S should equal xp mod 2^(κ+1)");
|
||||
|
||||
let m = 1;
|
||||
let c = encrypt_bit_asym(m, &pk, rho);
|
||||
let bootstrapped_c = bootstrap(&c, &bk, &pk, rho, &sk);
|
||||
let decrypted_m = decrypt_bit(&bootstrapped_c, &sk.p);
|
||||
for ui in &u {
|
||||
assert!(ui >= &0, "u_i should be non-negative");
|
||||
assert!(ui < &two_k1, "u_i should be less than 2^(κ+1)");
|
||||
}
|
||||
|
||||
assert_eq!(decrypted_m, m);
|
||||
for (i, yi) in y.iter().enumerate() {
|
||||
let expected_yi = Rational::from(&u[i]) / Rational::from(&two_k);
|
||||
assert_eq!(yi, &expected_yi, "y[{}] should equal u[{}] / 2^κ", i, i);
|
||||
assert!(yi < &Rational::from(2), "y[{}] should be less than 2", i);
|
||||
}
|
||||
|
||||
assert_eq!(enc_s.len(), n_theta, "enc_s length should be {}", n_theta);
|
||||
|
||||
let mut sum_y_s = Rational::from(0);
|
||||
for (i, &bit) in s_bits.iter().enumerate() {
|
||||
if bit == 1 {
|
||||
sum_y_s += &y[i];
|
||||
}
|
||||
}
|
||||
|
||||
let xp_rational = Rational::from((&xp, two_k.clone()));
|
||||
let one_over_p = Rational::from(1) / Rational::from(&sk.p);
|
||||
let delta_p = xp_rational - one_over_p;
|
||||
let bound = Rational::from(1) / Rational::from(two_k);
|
||||
|
||||
assert!(
|
||||
delta_p.clone().abs() < bound,
|
||||
"Error |Δp| = {} should be < 2^(-κ) = {}",
|
||||
delta_p,
|
||||
bound
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use rand::rngs::StdRng;
|
||||
use rand::Rng;
|
||||
use rand::SeedableRng;
|
||||
use rug::rand::RandState;
|
||||
use rug::Integer;
|
||||
|
||||
pub fn generate_random_odd_integer(num_bits: u32) -> Integer {
|
||||
@ -24,3 +25,11 @@ pub fn generate_random_integer(num_bits: u32) -> Integer {
|
||||
}
|
||||
x
|
||||
}
|
||||
|
||||
pub fn generate_random_integer_range(start: u32, finish: u32) -> Integer {
|
||||
assert!(finish > start, "finish must be greater than start");
|
||||
let k = finish - start;
|
||||
let mut rand = RandState::new();
|
||||
let i = Integer::from(Integer::random_bits(k, &mut rand)) << start;
|
||||
i
|
||||
}
|
||||
|
Reference in New Issue
Block a user