Skip to content

Commit

Permalink
feat(compute): Add support for bitwise OR operation
Browse files Browse the repository at this point in the history
  • Loading branch information
10d9e committed Oct 9, 2024
1 parent 0306bb8 commit 46a6b2c
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 3 deletions.
62 changes: 60 additions & 2 deletions benchmark/benches/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,47 @@ fn gateway_encrypted_bitwise_xor() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}

fn tfhe_encrypted_bitwise_or() -> Result<(), Box<dyn std::error::Error>> {
use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint128};
// Basic configuration to use homomorphic integers
let config = ConfigBuilder::default().build();

// Key generation
let (client_key, server_keys) = generate_keys(config);

let clear_a = 12297829382473034410u128;
let clear_b = 42424242424242424242u128;

// Encrypting the input data using the (private) client_key
let encrypted_a = FheUint128::try_encrypt(clear_a, &client_key).unwrap();
let encrypted_b = FheUint128::try_encrypt(clear_b, &client_key).unwrap();

// On the server side:
set_server_key(server_keys);

let encrypted_res_mul = &encrypted_a | &encrypted_b;

let clear_res: u128 = encrypted_res_mul.decrypt(&client_key);
assert_eq!(clear_res, clear_a | clear_b);

Ok(())
}

fn gateway_encrypted_bitwise_or() -> Result<(), Box<dyn ::std::error::Error>> {
use compute::uint::GarbledUint128;

let clear_a = 12297829382473034410u128;
let clear_b = 42424242424242424242u128;

let a = GarbledUint128::from_u128(clear_a);
let b = GarbledUint128::from_u128(clear_b);

let result = &a | &b;
assert_eq!(result.to_u128(), clear_a | clear_b);
Ok(())
}

fn tfhe_encrypted_bitwise_not() -> Result<(), Box<dyn std::error::Error>> {
use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint128};
Expand Down Expand Up @@ -278,6 +319,20 @@ fn benchmark_tfhe_encrypted_subtraction(c: &mut Criterion) {
});
}

// Benchmark 11: Benchmarking benchmark_gateway_encrypted_bitwise_or
fn benchmark_gateway_encrypted_bitwise_or(c: &mut Criterion) {
c.bench_function("gateway_encrypted_bitwise_or", |b| {
b.iter(gateway_encrypted_bitwise_or)
});
}

// Benchmark 12: Benchmarking benchmark_tfhe_encrypted_bitwise_or
fn benchmark_tfhe_encrypted_bitwise_or(c: &mut Criterion) {
c.bench_function("tfhe_encrypted_bitwise_or", |b| {
b.iter(tfhe_encrypted_bitwise_or)
});
}

// Configure Criterion with a sample size of 10
fn custom_criterion() -> Criterion {
Criterion::default().sample_size(10)
Expand All @@ -287,7 +342,8 @@ fn custom_criterion() -> Criterion {
criterion_group!(
name = benches;
config = custom_criterion();
targets = benchmark_gateway_encrypted_addition,
targets =
benchmark_gateway_encrypted_addition,
benchmark_tfhe_encrypted_addition,
benchmark_gateway_encrypted_subtraction,
benchmark_tfhe_encrypted_subtraction,
Expand All @@ -296,7 +352,9 @@ criterion_group!(
benchmark_gateway_encrypted_bitwise_xor,
benchmark_tfhe_encrypted_bitwise_xor,
benchmark_gateway_encrypted_bitwise_not,
benchmark_tfhe_encrypted_bitwise_not
benchmark_tfhe_encrypted_bitwise_not,
benchmark_gateway_encrypted_bitwise_or,
benchmark_tfhe_encrypted_bitwise_or,

);
criterion_main!(benches);
117 changes: 116 additions & 1 deletion compute/src/operations/bitwise.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::uint::GarbledUint;
use std::ops::{BitAnd, BitXor, Not, Shl, Shr};
use std::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
use tandem::{Circuit, Gate};

// Helper function to build and simulate a circuit for binary operations
Expand Down Expand Up @@ -125,6 +125,75 @@ impl<const N: usize> Not for &GarbledUint<N> {
}
}

// Helper function to build and simulate a circuit for OR operation
fn build_and_simulate_or<const N: usize>(
lhs: &GarbledUint<N>,
rhs: Option<&GarbledUint<N>>,
) -> GarbledUint<N> {
let mut gates = Vec::new();

// Push input gates for both Uint<N> objects (lhs and rhs)
for _ in 0..N {
gates.push(Gate::InContrib); // From first Uint<N> (lhs)
}

for _ in 0..N {
gates.push(Gate::InEval); // From second Uint<N> (rhs)
}

// Define gates for each bit in lhs and rhs
let mut output_indices = Vec::with_capacity(N);

for i in 0..N {
// OR(a, b) = (a ⊕ b) ⊕ (a & b)

// Step 1: XOR gate for (a ⊕ b)
let xor_gate = Gate::Xor(i as u32, (N + i) as u32);
let xor_gate_idx = gates.len() as u32;
gates.push(xor_gate);

// Step 2: AND gate for (a & b)
let and_gate = Gate::And(i as u32, (N + i) as u32);
let and_gate_idx = gates.len() as u32;
gates.push(and_gate);

// Step 3: XOR gate for final OR result (a ⊕ b) ⊕ (a & b)
let final_or_gate = Gate::Xor(xor_gate_idx, and_gate_idx);
gates.push(final_or_gate);

// Step 4: Store the output index of this bit's OR result
output_indices.push(gates.len() as u32 - 1);
}

// Create the circuit
let program = Circuit::new(gates, output_indices);

// Simulate the circuit
let bits_rhs = rhs.map_or(lhs.bits.clone(), |r| r.bits.clone());
let result = lhs.simulate(&program, &lhs.bits, &bits_rhs).unwrap();

// Return the resulting Uint<N>
GarbledUint::new(result)
}

// Implement the OR operation for GarbledUint<N>
impl<const N: usize> BitOr for GarbledUint<N> {
type Output = Self;

fn bitor(self, rhs: Self) -> Self::Output {
build_and_simulate_or(&self, Some(&rhs))
}
}

// Implement the OR operation for &GarbledUint<N>
impl<const N: usize> BitOr for &GarbledUint<N> {
type Output = GarbledUint<N>;

fn bitor(self, rhs: Self) -> Self::Output {
build_and_simulate_or(self, Some(rhs))
}
}

// Helper function for shift operations
fn shift_bits_left<const N: usize>(bits: &mut Vec<bool>, shift: usize) {
for _ in 0..shift {
Expand Down Expand Up @@ -314,6 +383,52 @@ mod tests {
assert_eq!(result.to_u128(), 170 & 85); // Expected result of AND between 10101010 and 01010101
}

#[test]
fn test_from_u8_or() {
let a = GarbledUint8::from_u8(170); // Binary 10101010
let b = GarbledUint8::from_u8(85); // Binary 01010101

let result = a | b;
assert_eq!(result.to_u8(), 170 | 85); // Expected result of OR between 10101010 and 01010101
}

#[test]
fn test_from_u16_or() {
let a = GarbledUint16::from_u16(43690); // Binary 1010101010101010
let b = GarbledUint16::from_u16(21845); // Binary 0101010101010101

let result = a | b;
assert_eq!(result.to_u16(), 43690 | 21845); // Expected result of OR between 1010101010101010 and 0101010101010101
}

#[test]
fn test_from_u32_or() {
let a = GarbledUint32::from_u32(2863311530); // Binary 10101010101010101010101010101010
let b = GarbledUint32::from_u32(1431655765); // Binary 01010101010101010101010101010101

let result = a | b;
assert_eq!(result.to_u32(), 2863311530 | 1431655765); // Expected result of OR between 10101010101010101010101010101010 and 01010101010101010101010101010101
}

#[test]
fn test_from_u64_or() {
let a = GarbledUint64::from_u64(12297829382473034410); // Binary 1010101010101010101010101010101010101010101010101010101010101010
let b = GarbledUint64::from_u64(6148914691236517205); // Binary 0101010101010101010101010101010101010101010101010101010101010101

let result = a | b;
assert_eq!(result.to_u64(), 12297829382473034410 | 6148914691236517205);
// Expected result of OR between 1010101010101010101010101010101010101010101010101010101010101010 and 0101010101010101010101010101010101010101010101010101010101010101
}

#[test]
fn test_from_u128_or() {
let a = GarbledUint128::from_u128(170); // Binary 10101010
let b = GarbledUint128::from_u128(85); // Binary 01010101

let result = a | b;
assert_eq!(result.to_u128(), 170 | 85); // Expected result of OR between 10101010 and 01010101
}

#[test]
fn test_from_u8_not() {
let a = GarbledUint8::from_u8(170); // Binary 10101010
Expand Down

0 comments on commit 46a6b2c

Please sign in to comment.