Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions crates/fhe/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,20 @@ harness = false
name = "bfv_rgsw"
harness = false

[[example]]
name = "bfv_basic"

[[example]]
name = "bfv_ops"

[[example]]
name = "mulpir"

[[example]]
name = "sealpir"

[[example]]
name = "rgsw"

[[example]]
name = "voting"
2 changes: 1 addition & 1 deletion crates/fhe/benches/bfv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub fn bfv_benchmark(c: &mut Criterion) {
group.warm_up_time(Duration::from_millis(600));
group.measurement_time(Duration::from_millis(1000));

for par in BfvParameters::default_parameters_128(20) {
for par in BfvParameters::default_parameters_128(20).unwrap() {
let sk = SecretKey::random(&par, &mut rng);
let ek = if par.moduli().len() > 1 {
Some(
Expand Down
2 changes: 1 addition & 1 deletion crates/fhe/benches/bfv_optimized_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub fn bfv_benchmark(c: &mut Criterion) {
group.warm_up_time(Duration::from_secs(1));
group.measurement_time(Duration::from_secs(1));

for par in BfvParameters::default_parameters_128(20).skip(2) {
for par in BfvParameters::default_parameters_128(20).unwrap() {
for size in [10, 128, 1000] {
let sk = SecretKey::random(&par, &mut rng);
let pt1 =
Expand Down
2 changes: 1 addition & 1 deletion crates/fhe/benches/bfv_rgsw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub fn bfv_rgsw_benchmark(c: &mut Criterion) {
group.warm_up_time(Duration::from_secs(1));
group.measurement_time(Duration::from_secs(1));

for par in BfvParameters::default_parameters_128(20).skip(2) {
for par in BfvParameters::default_parameters_128(20).unwrap() {
let mut rng = rng();
let sk = SecretKey::random(&par, &mut rng);

Expand Down
41 changes: 41 additions & 0 deletions crates/fhe/examples/bfv_basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use std::error::Error;

use fhe::bfv::{BfvParameters, Encoding, Plaintext, PublicKey, SecretKey};
use fhe_traits::{FheDecoder, FheDecrypter, FheEncoder, FheEncrypter};
use rand::rng;

fn main() -> Result<(), Box<dyn Error>> {
let mut rng = rng();
// Use default parameters
let params = BfvParameters::default_parameters_128(16)?
.nth(2)
.ok_or("Could not generate parameters")?;

// Generate keys
let sk = SecretKey::random(&params, &mut rng);
let pk = PublicKey::new(&sk, &mut rng);

// ----- Without SIMD -----
let pt_a = Plaintext::try_encode(&[3u64], Encoding::poly(), &params)?;
let pt_b = Plaintext::try_encode(&[5u64], Encoding::poly(), &params)?;
let ct_a = pk.try_encrypt(&pt_a, &mut rng)?;
let ct_b = pk.try_encrypt(&pt_b, &mut rng)?;
let ct_sum = &ct_a + &ct_b;
let pt_sum = sk.try_decrypt(&ct_sum)?;
let res = Vec::<u64>::try_decode(&pt_sum, Encoding::poly())?;
println!("3 + 5 = {}", res[0]);

// ----- With SIMD -----
let v1 = vec![1u64, 2, 3, 4];
let v2 = vec![5u64, 6, 7, 8];
let pt_v1 = Plaintext::try_encode(&v1, Encoding::simd(), &params)?;
let pt_v2 = Plaintext::try_encode(&v2, Encoding::simd(), &params)?;
let ct_v1 = pk.try_encrypt(&pt_v1, &mut rng)?;
let ct_v2 = pk.try_encrypt(&pt_v2, &mut rng)?;
let ct_vsum = &ct_v1 + &ct_v2;
let pt_vsum = sk.try_decrypt(&ct_vsum)?;
let res_v = Vec::<u64>::try_decode(&pt_vsum, Encoding::simd())?;
println!("{:?} + {:?} = {:?}", v1, v2, &res_v[..v1.len()]);

Ok(())
}
152 changes: 152 additions & 0 deletions crates/fhe/examples/bfv_ops.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
mod util;

use std::error::Error;
use std::sync::Arc;

use fhe::bfv::{
BfvParameters, Ciphertext, Encoding, EvaluationKeyBuilder, Plaintext, PublicKey,
RelinearizationKey, SecretKey,
};
use fhe_traits::{FheDecoder, FheDecrypter, FheEncoder, FheEncrypter};
use rand::rng;
use util::timeit::timeit;

fn weighted_sum_plain(
cts: &[Ciphertext],
weights: &[u64],
params: &Arc<BfvParameters>,
sk: &SecretKey,
) -> Result<u64, Box<dyn Error>> {
let mut acc = Ciphertext::zero(params);
for (ct, w) in cts.iter().zip(weights.iter()) {
let pt_w = Plaintext::try_encode(&[*w], Encoding::poly(), params)?;
acc += &(ct * &pt_w);
}
let pt = sk.try_decrypt(&acc)?;
let v = Vec::<u64>::try_decode(&pt, Encoding::poly())?;
Ok(v[0])
}

fn weighted_sum_simd(
ct: &Ciphertext,
weights: &Plaintext,
ek: &fhe::bfv::EvaluationKey,
sk: &SecretKey,
) -> Result<u64, Box<dyn Error>> {
let tmp = ct * weights;
let summed = ek.computes_inner_sum(&tmp)?;
let pt = sk.try_decrypt(&summed)?;
let v = Vec::<u64>::try_decode(&pt, Encoding::simd())?;
Ok(v[0])
}

fn main() -> Result<(), Box<dyn Error>> {
let mut rng = rng();
let params = BfvParameters::default_parameters_128(20)
.unwrap()
.nth(2) // first parameters do not support key switching
.unwrap();
let sk = SecretKey::random(&params, &mut rng);
let pk = PublicKey::new(&sk, &mut rng);
let ek = EvaluationKeyBuilder::new_leveled(&sk, 0, 0)?
.enable_inner_sum()?
.build(&mut rng)?;
let rk = RelinearizationKey::new(&sk, &mut rng)?;

// ----- Weighted sum without SIMD -----
let values = [1u64, 2, 3];
let weights = [4u64, 5, 6];
timeit!("inner product (no SIMD)", {
let cts: Vec<Ciphertext> = values
.iter()
.map(|v| {
let pt = Plaintext::try_encode(&[*v], Encoding::poly(), &params)?;
Ok(pk.try_encrypt(&pt, &mut rng)?)
})
.collect::<Result<_, Box<dyn Error>>>()?;
let ws_plain = weighted_sum_plain(&cts, &weights, &params, &sk)?;
println!("Weighted sum (no SIMD) = {ws_plain}");
});

// ----- Weighted sum with SIMD -----
let pt_vals = Plaintext::try_encode(&values, Encoding::simd(), &params)?;
let ct_vals = pk.try_encrypt(&pt_vals, &mut rng)?;
let pt_ws = Plaintext::try_encode(&weights, Encoding::simd(), &params)?;
timeit!("inner product (SIMD)", {
let ws_simd = weighted_sum_simd(&ct_vals, &pt_ws, &ek, &sk)?;
println!("Weighted sum (SIMD) = {ws_simd}");
});

// ----- Inner product without SIMD -----
let v1 = [1u64, 2, 3];
let v2 = [7u64, 8, 9];
let ct_v1: Vec<Ciphertext> = v1
.iter()
.map(|v| {
let pt = Plaintext::try_encode(&[*v], Encoding::poly(), &params)?;
Ok(pk.try_encrypt(&pt, &mut rng)?)
})
.collect::<Result<_, Box<dyn Error>>>()?;
let ct_v2: Vec<Ciphertext> = v2
.iter()
.map(|v| {
let pt = Plaintext::try_encode(&[*v], Encoding::poly(), &params)?;
Ok(pk.try_encrypt(&pt, &mut rng)?)
})
.collect::<Result<_, Box<dyn Error>>>()?;
let mut acc = Ciphertext::zero(&params);
for (a, b) in ct_v1.iter().zip(ct_v2.iter()) {
let mut prod = a * b;
rk.relinearizes(&mut prod)?;
acc += &prod;
}
let pt = sk.try_decrypt(&acc)?;
let ip_plain = Vec::<u64>::try_decode(&pt, Encoding::poly())?[0];
println!("Inner product (no SIMD) = {ip_plain}");

// ----- Inner product with SIMD -----
let pt1 = Plaintext::try_encode(&v1, Encoding::simd(), &params)?;
let pt2 = Plaintext::try_encode(&v2, Encoding::simd(), &params)?;
let ct1 = pk.try_encrypt(&pt1, &mut rng)?;
let ct2 = pk.try_encrypt(&pt2, &mut rng)?;
let mut prod = &ct1 * &ct2;
rk.relinearizes(&mut prod)?;
let summed = ek.computes_inner_sum(&prod)?;
let pt = sk.try_decrypt(&summed)?;
let ip_simd = Vec::<u64>::try_decode(&pt, Encoding::simd())?[0];
println!("Inner product (SIMD) = {ip_simd}");

// ----- Polynomial evaluation without SIMD -----
let x = 3u64;
let pt_x = Plaintext::try_encode(&[x], Encoding::poly(), &params)?;
let ct_x = pk.try_encrypt(&pt_x, &mut rng)?;
let mut ct_x2 = &ct_x * &ct_x; // x^2
rk.relinearizes(&mut ct_x2)?;
let pt_three = Plaintext::try_encode(&[3u64], Encoding::poly(), &params)?;
let pt_two = Plaintext::try_encode(&[2u64], Encoding::poly(), &params)?;
let pt_one = Plaintext::try_encode(&[1u64], Encoding::poly(), &params)?;
let mut ct_res = &ct_x2 * &pt_three;
ct_res += &(&ct_x * &pt_two);
ct_res += &pt_one;
let pt = sk.try_decrypt(&ct_res)?;
let poly_plain = Vec::<u64>::try_decode(&pt, Encoding::poly())?[0];
println!("Polynomial (no SIMD) = {poly_plain}");

// ----- Polynomial evaluation with SIMD -----
let x_vec = [1u64, 2, 3, 4];
let pt_xv = Plaintext::try_encode(&x_vec, Encoding::simd(), &params)?;
let ct_xv = pk.try_encrypt(&pt_xv, &mut rng)?;
let mut ct_xv2 = &ct_xv * &ct_xv;
rk.relinearizes(&mut ct_xv2)?;
let pt_three_v = Plaintext::try_encode(&vec![3u64; x_vec.len()], Encoding::simd(), &params)?;
let pt_two_v = Plaintext::try_encode(&vec![2u64; x_vec.len()], Encoding::simd(), &params)?;
let pt_one_v = Plaintext::try_encode(&vec![1u64; x_vec.len()], Encoding::simd(), &params)?;
let mut ct_res_v = &ct_xv2 * &pt_three_v;
ct_res_v += &(&ct_xv * &pt_two_v);
ct_res_v += &pt_one_v;
let pt = sk.try_decrypt(&ct_res_v)?;
let poly_simd = Vec::<u64>::try_decode(&pt, Encoding::simd())?;
println!("Polynomial (SIMD) = {:?}", &poly_simd[..x_vec.len()]);

Ok(())
}
50 changes: 50 additions & 0 deletions crates/fhe/examples/rgsw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use std::error::Error;

use fhe::bfv::{BfvParameters, Ciphertext, Encoding, Plaintext, RGSWCiphertext, SecretKey};
use fhe_traits::{FheDecoder, FheDecrypter, FheEncoder, FheEncrypter, Serialize};
use rand::rng;

fn main() -> Result<(), Box<dyn Error>> {
let mut rng = rng();
let params = BfvParameters::default_parameters_128(20)
.unwrap()
.nth(2)
.unwrap();
let sk = SecretKey::random(&params, &mut rng);

let v1 = vec![1u64, 2, 3, 4];
let v2 = vec![5u64, 6, 7, 8];
let pt1 = Plaintext::try_encode(&v1, Encoding::simd(), &params)?;
let pt2 = Plaintext::try_encode(&v2, Encoding::simd(), &params)?;
let ct1: Ciphertext = sk.try_encrypt(&pt1, &mut rng)?;
let ct2: Ciphertext = sk.try_encrypt(&pt2, &mut rng)?;
let ct2_rgsw: RGSWCiphertext = sk.try_encrypt(&pt2, &mut rng)?;

let mut product = &ct1 * &ct2_rgsw;
let expected = &ct1 * &ct2;

println!("Noise in product: {}", unsafe {
sk.measure_noise(&product)?
});
println!("Size of product: {} bytes", product.to_bytes().len());
println!("Noise in expected: {}", unsafe {
sk.measure_noise(&product)?
});

product.switch_to_level(product.max_switchable_level())?;
println!("Noise in product: {}", unsafe {
sk.measure_noise(&product)?
});
println!("Size of product: {} bytes", product.to_bytes().len());

let pt_prod = sk.try_decrypt(&product)?;
let pt_exp = sk.try_decrypt(&expected)?;
assert_eq!(pt_prod, pt_exp);
let decoded = Vec::<u64>::try_decode(&pt_prod, Encoding::simd())?;
println!(
"RGSW external product successful: {:?}",
&decoded[..v1.len()]
);

Ok(())
}
Loading
Loading