diff --git a/benchmark/benches/benchmarks.rs b/benchmark/benches/benchmarks.rs index 582da53..28e816a 100644 --- a/benchmark/benches/benchmarks.rs +++ b/benchmark/benches/benchmarks.rs @@ -128,6 +128,47 @@ fn gateway_encrypted_bitwise_xor() -> Result<(), Box> { Ok(()) } +fn tfhe_encrypted_bitwise_or() -> Result<(), Box> { + 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> { + 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> { use tfhe::prelude::*; use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint128}; @@ -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) @@ -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, @@ -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); diff --git a/compute/src/operations/bitwise.rs b/compute/src/operations/bitwise.rs index 6cccdd9..5ea9179 100644 --- a/compute/src/operations/bitwise.rs +++ b/compute/src/operations/bitwise.rs @@ -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 @@ -125,6 +125,75 @@ impl Not for &GarbledUint { } } +// Helper function to build and simulate a circuit for OR operation +fn build_and_simulate_or( + lhs: &GarbledUint, + rhs: Option<&GarbledUint>, +) -> GarbledUint { + let mut gates = Vec::new(); + + // Push input gates for both Uint objects (lhs and rhs) + for _ in 0..N { + gates.push(Gate::InContrib); // From first Uint (lhs) + } + + for _ in 0..N { + gates.push(Gate::InEval); // From second Uint (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 + GarbledUint::new(result) +} + +// Implement the OR operation for GarbledUint +impl BitOr for GarbledUint { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + build_and_simulate_or(&self, Some(&rhs)) + } +} + +// Implement the OR operation for &GarbledUint +impl BitOr for &GarbledUint { + type Output = GarbledUint; + + fn bitor(self, rhs: Self) -> Self::Output { + build_and_simulate_or(self, Some(rhs)) + } +} + // Helper function for shift operations fn shift_bits_left(bits: &mut Vec, shift: usize) { for _ in 0..shift { @@ -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