From 9581baae6556150e3c906690419b15ee450aed02 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 10 May 2025 11:06:46 +0400 Subject: [PATCH 01/23] Adopt cargo workspace Signed-off-by: Anjan Roy --- Cargo.toml | 51 +++-------------- chalamet_pir_client/Cargo.toml | 25 ++++++++ {src => chalamet_pir_client/src}/client.rs | 16 +++--- chalamet_pir_client/src/lib.rs | 4 ++ chalamet_pir_common/Cargo.toml | 28 +++++++++ .../src}/binary_fuse_filter.rs | 3 +- .../src}/branch_opt_util.rs | 0 .../src}/error.rs | 0 chalamet_pir_common/src/lib.rs | 7 +++ .../src}/matrix.rs | 57 ++++--------------- .../src}/params.rs | 0 .../src}/serialization.rs | 4 +- chalamet_pir_common/src/utils.rs | 38 +++++++++++++ chalamet_pir_server/Cargo.toml | 27 +++++++++ .../shaders}/mat_transpose.glsl | 0 .../shaders}/mat_x_mat.glsl | 0 .../src/gpu/gpu_utils.rs | 17 +++--- .../src/gpu}/mat_transpose_shader.rs | 0 .../src/gpu}/mat_x_mat_shader.rs | 0 chalamet_pir_server/src/gpu/mod.rs | 3 + {src => chalamet_pir_server/src}/lib.rs | 11 ++-- {src => chalamet_pir_server/src}/server.rs | 31 +++++----- integrations/Cargo.toml | 46 +++++++++++++++ .../benches}/offline_phase.rs | 17 +++--- .../benches}/online_phase.rs | 25 ++++---- integrations/src/lib.rs | 1 + .../kw_pir.rs => integrations/src/main.rs | 8 ++- {src => integrations/src}/test_pir.rs | 10 ++-- src/pir_internals/mod.rs | 13 ----- 29 files changed, 269 insertions(+), 173 deletions(-) create mode 100644 chalamet_pir_client/Cargo.toml rename {src => chalamet_pir_client/src}/client.rs (98%) create mode 100644 chalamet_pir_client/src/lib.rs create mode 100644 chalamet_pir_common/Cargo.toml rename {src/pir_internals => chalamet_pir_common/src}/binary_fuse_filter.rs (99%) rename {src/pir_internals => chalamet_pir_common/src}/branch_opt_util.rs (100%) rename {src/pir_internals => chalamet_pir_common/src}/error.rs (100%) create mode 100644 chalamet_pir_common/src/lib.rs rename {src/pir_internals => chalamet_pir_common/src}/matrix.rs (97%) rename {src/pir_internals => chalamet_pir_common/src}/params.rs (100%) rename {src/pir_internals => chalamet_pir_common/src}/serialization.rs (99%) create mode 100644 chalamet_pir_common/src/utils.rs create mode 100644 chalamet_pir_server/Cargo.toml rename {shaders => chalamet_pir_server/shaders}/mat_transpose.glsl (100%) rename {shaders => chalamet_pir_server/shaders}/mat_x_mat.glsl (100%) rename src/pir_internals/gpu.rs => chalamet_pir_server/src/gpu/gpu_utils.rs (99%) rename {src/pir_internals => chalamet_pir_server/src/gpu}/mat_transpose_shader.rs (100%) rename {src/pir_internals => chalamet_pir_server/src/gpu}/mat_x_mat_shader.rs (100%) create mode 100644 chalamet_pir_server/src/gpu/mod.rs rename {src => chalamet_pir_server/src}/lib.rs (96%) rename {src => chalamet_pir_server/src}/server.rs (93%) create mode 100644 integrations/Cargo.toml rename {benches => integrations/benches}/offline_phase.rs (80%) rename {benches => integrations/benches}/online_phase.rs (81%) create mode 100644 integrations/src/lib.rs rename examples/kw_pir.rs => integrations/src/main.rs (97%) rename {src => integrations/src}/test_pir.rs (97%) delete mode 100644 src/pir_internals/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 3f14c29..2cdfded 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,48 +1,11 @@ -[package] -name = "chalamet_pir" -version = "0.6.0" -edition = "2024" -resolver = "2" -rust-version = "1.85.0" -authors = ["Anjan Roy "] -description = "Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases" -readme = "README.md" -repository = "https://github.com/itzmeanjan/ChalametPIR.git" -license = "MPL-2.0" -keywords = [ - "priv-info-retrieval", - "lwe-pir", - "frodo-pir", - "chalamet-pir", - "gpu", +[workspace] +members = [ + "chalamet_pir_client", + "chalamet_pir_common", + "chalamet_pir_server", + "integrations", ] -categories = ["cryptography", "data-structures", "concurrency"] - -[dependencies] -turboshake = "=0.4.1" -rayon = "=1.10.0" -rand = "=0.9.1" -rand_chacha = "=0.9.0" -vulkano = { version = "=0.35.1", optional = true } -vulkano-shaders = { version = "=0.35.0", optional = true } - -[dev-dependencies] -test-case = "=3.3.1" -divan = "=0.1.21" -unicode-xid = "=0.2.6" - -[[bench]] -name = "offline_phase" -harness = false - -[[bench]] -name = "online_phase" -harness = false -required-features = ["mutate_internal_client_state"] - -[features] -mutate_internal_client_state = [] -gpu = ["dep:vulkano", "dep:vulkano-shaders"] +resolver = "3" [profile.optimized] inherits = "release" diff --git a/chalamet_pir_client/Cargo.toml b/chalamet_pir_client/Cargo.toml new file mode 100644 index 0000000..7dec973 --- /dev/null +++ b/chalamet_pir_client/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "chalamet_pir_client" +version = "0.6.0" +edition = "2024" +resolver = "3" +rust-version = "1.85.0" +authors = ["Anjan Roy "] +description = "Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases" +readme = "README.md" +repository = "https://github.com/itzmeanjan/ChalametPIR.git" +license = "MPL-2.0" +keywords = [ + "priv-info-retrieval", + "lwe-pir", + "frodo-pir", + "chalamet-pir", + "gpu", +] +categories = ["cryptography", "data-structures", "concurrency"] + +[dependencies] +chalamet_pir_common = { path = "../chalamet_pir_common" } + +[features] +mutate_internal_client_state = [] diff --git a/src/client.rs b/chalamet_pir_client/src/client.rs similarity index 98% rename from src/client.rs rename to chalamet_pir_client/src/client.rs index 9db66e2..248f87c 100644 --- a/src/client.rs +++ b/chalamet_pir_client/src/client.rs @@ -1,12 +1,10 @@ -use crate::{ - ChalametPIRError, - pir_internals::{ - binary_fuse_filter::{self, BinaryFuseFilter}, - branch_opt_util, - matrix::Matrix, - params::{HASHED_KEY_BYTE_LEN, LWE_DIMENSION, SEED_BYTE_LEN}, - serialization, - }, +use chalamet_pir_common::{ + binary_fuse_filter::{self, BinaryFuseFilter}, + branch_opt_util, + error::ChalametPIRError, + matrix::Matrix, + params::{HASHED_KEY_BYTE_LEN, LWE_DIMENSION, SEED_BYTE_LEN}, + serialization, }; use std::collections::HashMap; diff --git a/chalamet_pir_client/src/lib.rs b/chalamet_pir_client/src/lib.rs new file mode 100644 index 0000000..49bd2e4 --- /dev/null +++ b/chalamet_pir_client/src/lib.rs @@ -0,0 +1,4 @@ +mod client; + +pub use chalamet_pir_common::{error::ChalametPIRError, params::SEED_BYTE_LEN}; +pub use client::{Client, Query}; diff --git a/chalamet_pir_common/Cargo.toml b/chalamet_pir_common/Cargo.toml new file mode 100644 index 0000000..100176a --- /dev/null +++ b/chalamet_pir_common/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "chalamet_pir_common" +version = "0.6.0" +edition = "2024" +resolver = "3" +rust-version = "1.85.0" +authors = ["Anjan Roy "] +description = "Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases" +readme = "README.md" +repository = "https://github.com/itzmeanjan/ChalametPIR.git" +license = "MPL-2.0" +keywords = [ + "priv-info-retrieval", + "lwe-pir", + "frodo-pir", + "chalamet-pir", + "gpu", +] +categories = ["cryptography", "data-structures", "concurrency"] + +[dependencies] +turboshake = "=0.4.1" +rand = "=0.9.1" +rand_chacha = "=0.9.0" +rayon = "=1.10.0" + +[dev-dependencies] +test-case = "=3.3.1" diff --git a/src/pir_internals/binary_fuse_filter.rs b/chalamet_pir_common/src/binary_fuse_filter.rs similarity index 99% rename from src/pir_internals/binary_fuse_filter.rs rename to chalamet_pir_common/src/binary_fuse_filter.rs index d6d7c75..2fd23a8 100644 --- a/src/pir_internals/binary_fuse_filter.rs +++ b/chalamet_pir_common/src/binary_fuse_filter.rs @@ -1,5 +1,5 @@ use super::{error::ChalametPIRError, params}; -use crate::pir_internals::branch_opt_util; +use crate::branch_opt_util; use rand::prelude::*; use rand_chacha::ChaCha20Rng; use std::collections::HashMap; @@ -435,7 +435,6 @@ impl BinaryFuseFilter { )) } - #[cfg(test)] pub fn bits_per_entry(&self) -> f64 { ((self.num_fingerprints as f64) * (self.mat_elem_bit_len as f64)) / (self.filter_size as f64) } diff --git a/src/pir_internals/branch_opt_util.rs b/chalamet_pir_common/src/branch_opt_util.rs similarity index 100% rename from src/pir_internals/branch_opt_util.rs rename to chalamet_pir_common/src/branch_opt_util.rs diff --git a/src/pir_internals/error.rs b/chalamet_pir_common/src/error.rs similarity index 100% rename from src/pir_internals/error.rs rename to chalamet_pir_common/src/error.rs diff --git a/chalamet_pir_common/src/lib.rs b/chalamet_pir_common/src/lib.rs new file mode 100644 index 0000000..2bcbc9c --- /dev/null +++ b/chalamet_pir_common/src/lib.rs @@ -0,0 +1,7 @@ +pub mod binary_fuse_filter; +pub mod branch_opt_util; +pub mod error; +pub mod matrix; +pub mod params; +pub mod serialization; +pub mod utils; diff --git a/src/pir_internals/matrix.rs b/chalamet_pir_common/src/matrix.rs similarity index 97% rename from src/pir_internals/matrix.rs rename to chalamet_pir_common/src/matrix.rs index 915b57b..40b282e 100644 --- a/src/pir_internals/matrix.rs +++ b/chalamet_pir_common/src/matrix.rs @@ -1,5 +1,6 @@ -use crate::pir_internals::{ +use crate::{ binary_fuse_filter, branch_opt_util, + error::ChalametPIRError, params::{HASHED_KEY_BYTE_LEN, MAX_CIPHER_TEXT_BIT_LEN, MIN_CIPHER_TEXT_BIT_LEN, SEED_BYTE_LEN}, serialization, }; @@ -15,8 +16,6 @@ use turboshake::TurboShake128; #[cfg(test)] use std::ops::Neg; -use super::error::ChalametPIRError; - #[derive(Clone, Debug, PartialEq)] pub struct Matrix { rows: u32, @@ -90,7 +89,7 @@ impl Matrix { } pub fn row_wise_compress(self, mat_elem_bit_len: usize) -> Result { - if branch_opt_util::unlikely(!(mat_elem_bit_len >= MIN_CIPHER_TEXT_BIT_LEN && mat_elem_bit_len <= MAX_CIPHER_TEXT_BIT_LEN)) { + if branch_opt_util::unlikely(!(MIN_CIPHER_TEXT_BIT_LEN..=MAX_CIPHER_TEXT_BIT_LEN).contains(&mat_elem_bit_len)) { return Err(ChalametPIRError::ImpossibleEncodedDBMatrixElementBitLength); } @@ -1092,56 +1091,20 @@ impl Neg for &Matrix { } #[cfg(test)] -pub mod test { +mod test { use crate::{ - SEED_BYTE_LEN, - pir_internals::{ - binary_fuse_filter::BinaryFuseFilter, - error::ChalametPIRError, - matrix::Matrix, - params::{MAX_CIPHER_TEXT_BIT_LEN, MIN_CIPHER_TEXT_BIT_LEN, SERVER_SETUP_MAX_ATTEMPT_COUNT}, - }, + binary_fuse_filter::BinaryFuseFilter, + error::ChalametPIRError, + matrix::Matrix, + params::SEED_BYTE_LEN, + params::{MAX_CIPHER_TEXT_BIT_LEN, MIN_CIPHER_TEXT_BIT_LEN, SERVER_SETUP_MAX_ATTEMPT_COUNT}, + utils::generate_random_kv_database, }; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use std::collections::HashMap; use test_case::test_case; - /// Generates a random key-value database with the requested number of key-value pairs. - /// - /// # Arguments - /// - /// * `num_kv_pairs` - The number of key-value pairs to generate. - /// - /// # Returns - /// - /// * `HashMap, Vec>` - A HashMap containing the generated key-value pairs. - /// The keys and values are randomly generated byte arrays with lengths between fixed minimum and maximum values. - pub fn generate_random_kv_database(num_kv_pairs: usize) -> HashMap, Vec> { - const MIN_KEY_BYTE_LEN: usize = 16; - const MAX_KEY_BYTE_LEN: usize = 32; - const MIN_VALUE_BYTE_LEN: usize = 1; - const MAX_VALUE_BYTE_LEN: usize = 512; - - let mut kv = HashMap::with_capacity(num_kv_pairs); - let mut rng = ChaCha8Rng::from_os_rng(); - - for _ in 0..num_kv_pairs { - let key_byte_len = rng.random_range(MIN_KEY_BYTE_LEN..=MAX_KEY_BYTE_LEN); - let value_byte_len = rng.random_range(MIN_VALUE_BYTE_LEN..=MAX_VALUE_BYTE_LEN); - - let mut key = vec![0u8; key_byte_len]; - let mut value = vec![0u8; value_byte_len]; - - rng.fill_bytes(&mut key); - rng.fill_bytes(&mut value); - - kv.insert(key, value); - } - - kv - } - #[test] fn encode_kv_database_using_3_wise_xor_filter_and_recover_values() { const ARITY: u32 = 3; diff --git a/src/pir_internals/params.rs b/chalamet_pir_common/src/params.rs similarity index 100% rename from src/pir_internals/params.rs rename to chalamet_pir_common/src/params.rs diff --git a/src/pir_internals/serialization.rs b/chalamet_pir_common/src/serialization.rs similarity index 99% rename from src/pir_internals/serialization.rs rename to chalamet_pir_common/src/serialization.rs index 2686fda..1b70bba 100644 --- a/src/pir_internals/serialization.rs +++ b/chalamet_pir_common/src/serialization.rs @@ -1,4 +1,4 @@ -use super::{branch_opt_util, error::ChalametPIRError, params}; +use crate::{branch_opt_util, error::ChalametPIRError, params}; use std::cmp::min; use turboshake::TurboShake128; @@ -227,7 +227,7 @@ pub fn u64_to_le_bytes(word: u64, bytes: &mut [u8]) { #[cfg(test)] mod test { - use crate::pir_internals::{ + use crate::{ params, serialization::{decode_kv_from_row, encode_kv_as_row}, }; diff --git a/chalamet_pir_common/src/utils.rs b/chalamet_pir_common/src/utils.rs new file mode 100644 index 0000000..1ba5be7 --- /dev/null +++ b/chalamet_pir_common/src/utils.rs @@ -0,0 +1,38 @@ +use rand::prelude::*; +use rand_chacha::ChaCha8Rng; +use std::collections::HashMap; + +/// Generates a random key-value database with the requested number of key-value pairs. +/// +/// # Arguments +/// +/// * `num_kv_pairs` - The number of key-value pairs to generate. +/// +/// # Returns +/// +/// * `HashMap, Vec>` - A HashMap containing the generated key-value pairs. +/// The keys and values are randomly generated byte arrays with lengths between fixed minimum and maximum values. +pub fn generate_random_kv_database(num_kv_pairs: usize) -> HashMap, Vec> { + const MIN_KEY_BYTE_LEN: usize = 16; + const MAX_KEY_BYTE_LEN: usize = 32; + const MIN_VALUE_BYTE_LEN: usize = 1; + const MAX_VALUE_BYTE_LEN: usize = 512; + + let mut kv = HashMap::with_capacity(num_kv_pairs); + let mut rng = ChaCha8Rng::from_os_rng(); + + for _ in 0..num_kv_pairs { + let key_byte_len = rng.random_range(MIN_KEY_BYTE_LEN..=MAX_KEY_BYTE_LEN); + let value_byte_len = rng.random_range(MIN_VALUE_BYTE_LEN..=MAX_VALUE_BYTE_LEN); + + let mut key = vec![0u8; key_byte_len]; + let mut value = vec![0u8; value_byte_len]; + + rng.fill_bytes(&mut key); + rng.fill_bytes(&mut value); + + kv.insert(key, value); + } + + kv +} diff --git a/chalamet_pir_server/Cargo.toml b/chalamet_pir_server/Cargo.toml new file mode 100644 index 0000000..bb95af2 --- /dev/null +++ b/chalamet_pir_server/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "chalamet_pir_server" +version = "0.6.0" +edition = "2024" +resolver = "3" +rust-version = "1.85.0" +authors = ["Anjan Roy "] +description = "Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases" +readme = "README.md" +repository = "https://github.com/itzmeanjan/ChalametPIR.git" +license = "MPL-2.0" +keywords = [ + "priv-info-retrieval", + "lwe-pir", + "frodo-pir", + "chalamet-pir", + "gpu", +] +categories = ["cryptography", "data-structures", "concurrency"] + +[dependencies] +chalamet_pir_common = { path = "../chalamet_pir_common" } +vulkano = { version = "=0.35.1", optional = true } +vulkano-shaders = { version = "=0.35.0", optional = true } + +[features] +gpu = ["dep:vulkano", "dep:vulkano-shaders"] diff --git a/shaders/mat_transpose.glsl b/chalamet_pir_server/shaders/mat_transpose.glsl similarity index 100% rename from shaders/mat_transpose.glsl rename to chalamet_pir_server/shaders/mat_transpose.glsl diff --git a/shaders/mat_x_mat.glsl b/chalamet_pir_server/shaders/mat_x_mat.glsl similarity index 100% rename from shaders/mat_x_mat.glsl rename to chalamet_pir_server/shaders/mat_x_mat.glsl diff --git a/src/pir_internals/gpu.rs b/chalamet_pir_server/src/gpu/gpu_utils.rs similarity index 99% rename from src/pir_internals/gpu.rs rename to chalamet_pir_server/src/gpu/gpu_utils.rs index f0629f8..5d2bf81 100644 --- a/src/pir_internals/gpu.rs +++ b/chalamet_pir_server/src/gpu/gpu_utils.rs @@ -1,20 +1,15 @@ pub use std::sync::Arc; pub use vulkano::{ - buffer::Subbuffer, - command_buffer::allocator::StandardCommandBufferAllocator, - device::{Device, Queue}, - memory::allocator::StandardMemoryAllocator, -}; - -use super::{mat_transpose_shader, mat_x_mat_shader, matrix::Matrix}; -use crate::ChalametPIRError; -use vulkano::{ VulkanLibrary, + buffer::Subbuffer, buffer::{Buffer, BufferCreateInfo, BufferUsage}, + command_buffer::allocator::StandardCommandBufferAllocator, command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, CopyBufferInfo, PrimaryCommandBufferAbstract}, descriptor_set::{DescriptorSet, WriteDescriptorSet, allocator::StandardDescriptorSetAllocator}, + device::{Device, Queue}, device::{DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, QueueFlags, physical::PhysicalDeviceType}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, + memory::allocator::StandardMemoryAllocator, memory::allocator::{AllocationCreateInfo, MemoryTypeFilter}, pipeline::{ ComputePipeline, Pipeline, PipelineBindPoint, PipelineLayout, PipelineShaderStageCreateInfo, compute::ComputePipelineCreateInfo, @@ -23,6 +18,10 @@ use vulkano::{ sync::GpuFuture, }; +use super::{mat_transpose_shader, mat_x_mat_shader}; +use crate::ChalametPIRError; +use chalamet_pir_common::matrix::Matrix; + pub fn setup_gpu() -> Result<(Arc, Arc, Arc, Arc), ChalametPIRError> { let library = VulkanLibrary::new().map_err(|_| ChalametPIRError::VulkanLibraryNotFound)?; let instance = Instance::new( diff --git a/src/pir_internals/mat_transpose_shader.rs b/chalamet_pir_server/src/gpu/mat_transpose_shader.rs similarity index 100% rename from src/pir_internals/mat_transpose_shader.rs rename to chalamet_pir_server/src/gpu/mat_transpose_shader.rs diff --git a/src/pir_internals/mat_x_mat_shader.rs b/chalamet_pir_server/src/gpu/mat_x_mat_shader.rs similarity index 100% rename from src/pir_internals/mat_x_mat_shader.rs rename to chalamet_pir_server/src/gpu/mat_x_mat_shader.rs diff --git a/chalamet_pir_server/src/gpu/mod.rs b/chalamet_pir_server/src/gpu/mod.rs new file mode 100644 index 0000000..c10759f --- /dev/null +++ b/chalamet_pir_server/src/gpu/mod.rs @@ -0,0 +1,3 @@ +pub mod gpu_utils; +pub mod mat_transpose_shader; +pub mod mat_x_mat_shader; diff --git a/src/lib.rs b/chalamet_pir_server/src/lib.rs similarity index 96% rename from src/lib.rs rename to chalamet_pir_server/src/lib.rs index 9c57822..1c21e87 100644 --- a/src/lib.rs +++ b/chalamet_pir_server/src/lib.rs @@ -77,11 +77,10 @@ //! //! For more see README in ChalametPIR repository @ . -pub use pir_internals::error::ChalametPIRError; -pub use pir_internals::params::SEED_BYTE_LEN; -pub mod client; -pub mod server; +#[cfg(feature = "gpu")] +mod gpu; -mod pir_internals; +mod server; -mod test_pir; +pub use chalamet_pir_common::{error::ChalametPIRError, params::SEED_BYTE_LEN}; +pub use server::Server; diff --git a/src/server.rs b/chalamet_pir_server/src/server.rs similarity index 93% rename from src/server.rs rename to chalamet_pir_server/src/server.rs index 89067fc..6b13d66 100644 --- a/src/server.rs +++ b/chalamet_pir_server/src/server.rs @@ -1,15 +1,14 @@ -#[cfg(feature = "gpu")] -use crate::pir_internals::gpu; -use crate::{ - ChalametPIRError, - pir_internals::{ - branch_opt_util, - matrix::Matrix, - params::{LWE_DIMENSION, SEED_BYTE_LEN, SERVER_SETUP_MAX_ATTEMPT_COUNT}, - }, +use chalamet_pir_common::{ + branch_opt_util, + error::ChalametPIRError, + matrix::Matrix, + params::{LWE_DIMENSION, SEED_BYTE_LEN, SERVER_SETUP_MAX_ATTEMPT_COUNT}, }; use std::collections::HashMap; +#[cfg(feature = "gpu")] +use crate::gpu::gpu_utils; + /// Represents the server in the Keyword Private Information Retrieval (PIR) scheme ChalametPIR. /// /// The server stores an encoded database matrix, in transposed form, and then row-wise compressed, to optimize query response time. @@ -115,7 +114,7 @@ impl Server { let pub_mat_a = unsafe { Matrix::generate_from_seed(pub_mat_a_num_rows, pub_mat_a_num_cols, seed_μ).unwrap_unchecked() }; - let (device, queue, mem_alloc, cmd_buf_alloc) = gpu::setup_gpu()?; + let (device, queue, mem_alloc, cmd_buf_alloc) = gpu_utils::setup_gpu()?; let hint_mat_m_num_rows = pub_mat_a_num_rows; let hint_mat_m_num_cols = parsed_db_mat_d.num_cols(); @@ -125,12 +124,12 @@ impl Server { let parsed_db_mat_d_byte_len = parsed_db_mat_d.num_bytes() as u64; let parsed_db_mat_d_wg_count = [parsed_db_mat_d.num_rows().div_ceil(8), parsed_db_mat_d.num_cols().div_ceil(8), 1]; - let pub_mat_a_buf = gpu::transfer_mat_to_device(queue.clone(), mem_alloc.clone(), cmd_buf_alloc.clone(), pub_mat_a)?; - let parsed_db_mat_d_buf = gpu::transfer_mat_to_device(queue.clone(), mem_alloc.clone(), cmd_buf_alloc.clone(), parsed_db_mat_d.clone())?; - let hint_mat_m_buf = gpu::get_empty_host_readable_buffer(mem_alloc.clone(), hint_mat_m_byte_len)?; - let transposed_parsed_db_mat_d_buf = gpu::get_empty_host_readable_buffer(mem_alloc.clone(), parsed_db_mat_d_byte_len)?; + let pub_mat_a_buf = gpu_utils::transfer_mat_to_device(queue.clone(), mem_alloc.clone(), cmd_buf_alloc.clone(), pub_mat_a)?; + let parsed_db_mat_d_buf = gpu_utils::transfer_mat_to_device(queue.clone(), mem_alloc.clone(), cmd_buf_alloc.clone(), parsed_db_mat_d.clone())?; + let hint_mat_m_buf = gpu_utils::get_empty_host_readable_buffer(mem_alloc.clone(), hint_mat_m_byte_len)?; + let transposed_parsed_db_mat_d_buf = gpu_utils::get_empty_host_readable_buffer(mem_alloc.clone(), parsed_db_mat_d_byte_len)?; - gpu::mat_x_mat( + gpu_utils::mat_x_mat( device.clone(), queue.clone(), cmd_buf_alloc.clone(), @@ -140,7 +139,7 @@ impl Server { hint_mat_m_wg_count, )?; - gpu::mat_transpose( + gpu_utils::mat_transpose( device.clone(), queue.clone(), cmd_buf_alloc.clone(), diff --git a/integrations/Cargo.toml b/integrations/Cargo.toml new file mode 100644 index 0000000..b4f50b7 --- /dev/null +++ b/integrations/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "chalamet_pir_integrations" +version = "0.6.0" +edition = "2024" +resolver = "3" +rust-version = "1.85.0" +authors = ["Anjan Roy "] +description = "Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases" +readme = "README.md" +repository = "https://github.com/itzmeanjan/ChalametPIR.git" +license = "MPL-2.0" +keywords = [ + "priv-info-retrieval", + "lwe-pir", + "frodo-pir", + "chalamet-pir", + "gpu", +] +categories = ["cryptography", "data-structures", "concurrency"] + +[dependencies] +rand = "=0.9.1" +rand_chacha = "=0.9.0" +test-case = "=3.3.1" +divan = "=0.1.21" +unicode-xid = "=0.2.6" +chalamet_pir_server = { path = "../chalamet_pir_server" } +chalamet_pir_client = { path = "../chalamet_pir_client", features = [ + "mutate_internal_client_state", +] } + +[dev-dependencies] +rand = "=0.9.1" +rand_chacha = "=0.9.0" +test-case = "=3.3.1" +divan = "=0.1.21" +unicode-xid = "=0.2.6" +chalamet_pir_common = { path = "../chalamet_pir_common" } + +[[bench]] +name = "offline_phase" +harness = false + +[[bench]] +name = "online_phase" +harness = false diff --git a/benches/offline_phase.rs b/integrations/benches/offline_phase.rs similarity index 80% rename from benches/offline_phase.rs rename to integrations/benches/offline_phase.rs index dc84fcb..eefad86 100644 --- a/benches/offline_phase.rs +++ b/integrations/benches/offline_phase.rs @@ -1,8 +1,11 @@ -use chalamet_pir::{client, server}; +use std::{collections::HashMap, time::Duration}; + +use chalamet_pir_client::Client; +use chalamet_pir_server::Server; + use divan; use rand::prelude::*; use rand_chacha::ChaCha8Rng; -use std::{collections::HashMap, time::Duration}; fn main() { divan::main(); @@ -60,12 +63,12 @@ fn server_setup(bencher: divan::Bencher, db_config: &DBConfig) let kv = generate_random_kv_database(&mut rng, db_config.db_entry_count, db_config.key_byte_len, db_config.value_byte_len); let kv_as_ref = kv.iter().map(|(k, v)| (k.as_slice(), v.as_slice())).collect::>(); - let mut seed_μ = [0u8; chalamet_pir::SEED_BYTE_LEN]; + let mut seed_μ = [0u8; chalamet_pir_server::SEED_BYTE_LEN]; rng.fill_bytes(&mut seed_μ); bencher .with_inputs(|| (kv_as_ref.clone(), seed_μ)) - .bench_values(|(kv, seed)| server::Server::setup::(divan::black_box(&seed), divan::black_box(kv))); + .bench_values(|(kv, seed)| Server::setup::(divan::black_box(&seed), divan::black_box(kv))); } #[divan::bench(args = ARGS, consts = ARITIES, max_time = Duration::from_secs(300), skip_ext_time = true)] @@ -75,9 +78,9 @@ fn client_setup(bencher: divan::Bencher, db_config: &DBConfig) let kv = generate_random_kv_database(&mut rng, db_config.db_entry_count, db_config.key_byte_len, db_config.value_byte_len); let kv_as_ref = kv.iter().map(|(k, v)| (k.as_slice(), v.as_slice())).collect::>(); - let mut seed_μ = [0u8; chalamet_pir::SEED_BYTE_LEN]; + let mut seed_μ = [0u8; chalamet_pir_server::SEED_BYTE_LEN]; rng.fill_bytes(&mut seed_μ); - let (_, hint_bytes, filter_param_bytes) = server::Server::setup::(&seed_μ, kv_as_ref).expect("Server setup failed"); - bencher.bench(|| client::Client::setup(divan::black_box(&seed_μ), divan::black_box(&hint_bytes), divan::black_box(&filter_param_bytes))); + let (_, hint_bytes, filter_param_bytes) = Server::setup::(&seed_μ, kv_as_ref).expect("Server setup failed"); + bencher.bench(|| Client::setup(divan::black_box(&seed_μ), divan::black_box(&hint_bytes), divan::black_box(&filter_param_bytes))); } diff --git a/benches/online_phase.rs b/integrations/benches/online_phase.rs similarity index 81% rename from benches/online_phase.rs rename to integrations/benches/online_phase.rs index 61206be..9e26aec 100644 --- a/benches/online_phase.rs +++ b/integrations/benches/online_phase.rs @@ -1,8 +1,11 @@ -use chalamet_pir::{client, server}; +use std::{collections::HashMap, time::Duration}; + +use chalamet_pir_client::Client; +use chalamet_pir_server::Server; + use divan; use rand::prelude::*; use rand_chacha::ChaCha8Rng; -use std::{collections::HashMap, time::Duration}; fn main() { divan::main(); @@ -60,11 +63,11 @@ fn client_query(bencher: divan::Bencher, db_config: &DBConfig) let kv = generate_random_kv_database(&mut rng, db_config.db_entry_count, db_config.key_byte_len, db_config.value_byte_len); let kv_as_ref = kv.iter().map(|(k, v)| (k.as_slice(), v.as_slice())).collect::>(); - let mut seed_μ = [0u8; chalamet_pir::SEED_BYTE_LEN]; + let mut seed_μ = [0u8; chalamet_pir_server::SEED_BYTE_LEN]; rng.fill_bytes(&mut seed_μ); - let (_, hint_bytes, filter_param_bytes) = server::Server::setup::(&seed_μ, kv_as_ref.clone()).unwrap(); - let client = client::Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes).unwrap(); + let (_, hint_bytes, filter_param_bytes) = Server::setup::(&seed_μ, kv_as_ref.clone()).unwrap(); + let client = Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes).unwrap(); let (&key, _) = kv_as_ref.iter().last().unwrap(); @@ -81,11 +84,11 @@ fn server_respond(bencher: divan::Bencher, db_config: &DBConfi let kv = generate_random_kv_database(&mut rng, db_config.db_entry_count, db_config.key_byte_len, db_config.value_byte_len); let kv_as_ref = kv.iter().map(|(k, v)| (k.as_slice(), v.as_slice())).collect::>(); - let mut seed_μ = [0u8; chalamet_pir::SEED_BYTE_LEN]; + let mut seed_μ = [0u8; chalamet_pir_server::SEED_BYTE_LEN]; rng.fill_bytes(&mut seed_μ); - let (server, hint_bytes, filter_param_bytes) = server::Server::setup::(&seed_μ, kv_as_ref.clone()).unwrap(); - let mut client = client::Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes).unwrap(); + let (server, hint_bytes, filter_param_bytes) = Server::setup::(&seed_μ, kv_as_ref.clone()).unwrap(); + let mut client = Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes).unwrap(); let (&key, _) = kv_as_ref.iter().last().unwrap(); let query_bytes = client.query(key).unwrap(); @@ -100,11 +103,11 @@ fn client_process_response(bencher: divan::Bencher, db_config: let kv = generate_random_kv_database(&mut rng, db_config.db_entry_count, db_config.key_byte_len, db_config.value_byte_len); let kv_as_ref = kv.iter().map(|(k, v)| (k.as_slice(), v.as_slice())).collect::>(); - let mut seed_μ = [0u8; chalamet_pir::SEED_BYTE_LEN]; + let mut seed_μ = [0u8; chalamet_pir_server::SEED_BYTE_LEN]; rng.fill_bytes(&mut seed_μ); - let (server, hint_bytes, filter_param_bytes) = server::Server::setup::(&seed_μ, kv_as_ref.clone()).unwrap(); - let mut client = client::Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes).unwrap(); + let (server, hint_bytes, filter_param_bytes) = Server::setup::(&seed_μ, kv_as_ref.clone()).unwrap(); + let mut client = Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes).unwrap(); let (&key, _) = kv_as_ref.iter().last().unwrap(); let query_bytes = client.query(key).unwrap(); diff --git a/integrations/src/lib.rs b/integrations/src/lib.rs new file mode 100644 index 0000000..511ee9c --- /dev/null +++ b/integrations/src/lib.rs @@ -0,0 +1 @@ +mod test_pir; diff --git a/examples/kw_pir.rs b/integrations/src/main.rs similarity index 97% rename from examples/kw_pir.rs rename to integrations/src/main.rs index 903b90d..84b9a6d 100644 --- a/examples/kw_pir.rs +++ b/integrations/src/main.rs @@ -1,8 +1,10 @@ -use chalamet_pir::{SEED_BYTE_LEN, client::Client, server::Server}; +use std::{collections::HashMap, time::Instant}; + +use chalamet_pir_client::Client; +use chalamet_pir_server::{SEED_BYTE_LEN, Server}; + use rand::prelude::*; use rand_chacha::ChaCha8Rng; -use std::collections::HashMap; -use std::time::Instant; use unicode_xid::UnicodeXID; /// Generates a toy Key-Value database with a specified number of entries. diff --git a/src/test_pir.rs b/integrations/src/test_pir.rs similarity index 97% rename from src/test_pir.rs rename to integrations/src/test_pir.rs index d8e2978..17db813 100644 --- a/src/test_pir.rs +++ b/integrations/src/test_pir.rs @@ -1,11 +1,13 @@ #![cfg(test)] -use crate::ChalametPIRError; -use crate::pir_internals::matrix::test::generate_random_kv_database; -use crate::{client::Client, server::Server}; +use std::collections::HashMap; + +use chalamet_pir_client::Client; +use chalamet_pir_common::utils::generate_random_kv_database; +use chalamet_pir_server::{ChalametPIRError, Server}; + use rand::prelude::*; use rand_chacha::ChaCha8Rng; -use std::collections::HashMap; #[test] fn test_keyword_pir_with_3_wise_xor_filter() { diff --git a/src/pir_internals/mod.rs b/src/pir_internals/mod.rs deleted file mode 100644 index edad805..0000000 --- a/src/pir_internals/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub mod binary_fuse_filter; -pub mod branch_opt_util; -pub mod error; -pub mod matrix; -pub mod params; -pub mod serialization; - -#[cfg(feature = "gpu")] -pub mod gpu; -#[cfg(feature = "gpu")] -pub mod mat_transpose_shader; -#[cfg(feature = "gpu")] -pub mod mat_x_mat_shader; From 4c6417e1535af43e477423e6da71405293e224c8 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 10 May 2025 11:29:25 +0400 Subject: [PATCH 02/23] Update Cargo.toml files of workspace members Signed-off-by: Anjan Roy --- chalamet_pir_client/Cargo.toml | 9 +++++---- chalamet_pir_common/Cargo.toml | 4 ++-- chalamet_pir_server/Cargo.toml | 8 +++++--- integrations/Cargo.toml | 17 ++--------------- 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/chalamet_pir_client/Cargo.toml b/chalamet_pir_client/Cargo.toml index 7dec973..ba1a63e 100644 --- a/chalamet_pir_client/Cargo.toml +++ b/chalamet_pir_client/Cargo.toml @@ -5,8 +5,8 @@ edition = "2024" resolver = "3" rust-version = "1.85.0" authors = ["Anjan Roy "] -description = "Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases" -readme = "README.md" +description = "Client Implementation of ChalametPIR: Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases" +readme = "../README.md" repository = "https://github.com/itzmeanjan/ChalametPIR.git" license = "MPL-2.0" keywords = [ @@ -14,12 +14,13 @@ keywords = [ "lwe-pir", "frodo-pir", "chalamet-pir", - "gpu", + "pir-server", + "key-value-databases", ] categories = ["cryptography", "data-structures", "concurrency"] [dependencies] -chalamet_pir_common = { path = "../chalamet_pir_common" } +chalamet_pir_common = { path = "../chalamet_pir_common", version = "=0.6.0" } [features] mutate_internal_client_state = [] diff --git a/chalamet_pir_common/Cargo.toml b/chalamet_pir_common/Cargo.toml index 100176a..b6de00c 100644 --- a/chalamet_pir_common/Cargo.toml +++ b/chalamet_pir_common/Cargo.toml @@ -5,8 +5,8 @@ edition = "2024" resolver = "3" rust-version = "1.85.0" authors = ["Anjan Roy "] -description = "Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases" -readme = "README.md" +description = "Common Utilities for ChalametPIR: Private Information Retrieval for Key-Value Databases" +readme = "../README.md" repository = "https://github.com/itzmeanjan/ChalametPIR.git" license = "MPL-2.0" keywords = [ diff --git a/chalamet_pir_server/Cargo.toml b/chalamet_pir_server/Cargo.toml index bb95af2..c66c0ae 100644 --- a/chalamet_pir_server/Cargo.toml +++ b/chalamet_pir_server/Cargo.toml @@ -5,8 +5,8 @@ edition = "2024" resolver = "3" rust-version = "1.85.0" authors = ["Anjan Roy "] -description = "Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases" -readme = "README.md" +description = "Server Implementation of ChalametPIR: Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases" +readme = "../README.md" repository = "https://github.com/itzmeanjan/ChalametPIR.git" license = "MPL-2.0" keywords = [ @@ -15,11 +15,13 @@ keywords = [ "frodo-pir", "chalamet-pir", "gpu", + "pir-server", + "key-value-databases", ] categories = ["cryptography", "data-structures", "concurrency"] [dependencies] -chalamet_pir_common = { path = "../chalamet_pir_common" } +chalamet_pir_common = { path = "../chalamet_pir_common", version = "=0.6.0" } vulkano = { version = "=0.35.1", optional = true } vulkano-shaders = { version = "=0.35.0", optional = true } diff --git a/integrations/Cargo.toml b/integrations/Cargo.toml index b4f50b7..ece50c8 100644 --- a/integrations/Cargo.toml +++ b/integrations/Cargo.toml @@ -5,24 +5,14 @@ edition = "2024" resolver = "3" rust-version = "1.85.0" authors = ["Anjan Roy "] -description = "Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases" -readme = "README.md" +description = "Integration tests, benchmarks and examples for ChalametPIR" repository = "https://github.com/itzmeanjan/ChalametPIR.git" license = "MPL-2.0" -keywords = [ - "priv-info-retrieval", - "lwe-pir", - "frodo-pir", - "chalamet-pir", - "gpu", -] -categories = ["cryptography", "data-structures", "concurrency"] +publish = false [dependencies] rand = "=0.9.1" rand_chacha = "=0.9.0" -test-case = "=3.3.1" -divan = "=0.1.21" unicode-xid = "=0.2.6" chalamet_pir_server = { path = "../chalamet_pir_server" } chalamet_pir_client = { path = "../chalamet_pir_client", features = [ @@ -30,11 +20,8 @@ chalamet_pir_client = { path = "../chalamet_pir_client", features = [ ] } [dev-dependencies] -rand = "=0.9.1" -rand_chacha = "=0.9.0" test-case = "=3.3.1" divan = "=0.1.21" -unicode-xid = "=0.2.6" chalamet_pir_common = { path = "../chalamet_pir_common" } [[bench]] From 9931942e67f9efdf69a07b40a2764e9f0083ee6d Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 10 May 2025 12:58:33 +0400 Subject: [PATCH 03/23] Add example PIR server using tokio async runtime Signed-off-by: Anjan Roy --- chalamet_pir_server/Cargo.toml | 5 ++ chalamet_pir_server/examples/server.rs | 95 ++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 chalamet_pir_server/examples/server.rs diff --git a/chalamet_pir_server/Cargo.toml b/chalamet_pir_server/Cargo.toml index c66c0ae..147a140 100644 --- a/chalamet_pir_server/Cargo.toml +++ b/chalamet_pir_server/Cargo.toml @@ -25,5 +25,10 @@ chalamet_pir_common = { path = "../chalamet_pir_common", version = "=0.6.0" } vulkano = { version = "=0.35.1", optional = true } vulkano-shaders = { version = "=0.35.0", optional = true } +[dev-dependencies] +rand = "=0.9.1" +rand_chacha = "=0.9.0" +tokio = { version = "=1.45.0", features = ["full"] } + [features] gpu = ["dep:vulkano", "dep:vulkano-shaders"] diff --git a/chalamet_pir_server/examples/server.rs b/chalamet_pir_server/examples/server.rs new file mode 100644 index 0000000..dc6866e --- /dev/null +++ b/chalamet_pir_server/examples/server.rs @@ -0,0 +1,95 @@ +use std::sync::Arc; +use std::{collections::HashMap, error::Error}; + +use chalamet_pir_server::{SEED_BYTE_LEN, Server}; + +use rand::prelude::*; +use rand_chacha::ChaCha8Rng; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::TcpListener; + +const ARITY_OF_BINARY_FUSE_FILTER: u32 = 3; +const HOST_IP: &str = "127.0.0.1"; +const HOST_PORT: u16 = 8080; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let server_address = format!("{}:{}", HOST_IP, HOST_PORT); + + let listener = TcpListener::bind(&server_address).await.expect("Failed to setup TCP listener for PIR server"); + println!("PIR Server listening @ {}", &server_address); + + let mut rng = ChaCha8Rng::from_os_rng(); + let mut seed_μ = [0u8; SEED_BYTE_LEN]; + rng.fill_bytes(&mut seed_μ); + + let mut db: HashMap<&[u8], &[u8]> = HashMap::new(); + db.insert(b"apple", b"red"); + db.insert(b"banana", b"yellow"); + db.insert(b"grape", b"purple"); + db.insert(b"orange", b"orange"); + db.insert(b"lemon", b"yellow"); + db.insert(b"blueberry", b"blue"); + db.insert(b"kiwi", b"brown"); + db.insert(b"watermelon", b"green"); + db.insert(b"strawberry", b"red"); + db.insert(b"peach", b"pink"); + db.insert(b"pineapple", b"yellow"); + db.insert(b"cherry", b"red"); + db.insert(b"avocado", b"green"); + db.insert(b"plum", b"purple"); + db.insert(b"cantaloupe", b"orange"); + + let (server, hint_bytes, filter_param_bytes) = Server::setup::(&seed_μ, db).expect("PIR server setup failed"); + + let arced_server = Arc::new(server); + let arced_hint = Arc::new(hint_bytes); + let arced_filter_param = Arc::new(filter_param_bytes); + + loop { + let (mut stream, _) = listener.accept().await?; + let peer_address = stream.peer_addr().unwrap(); + println!("New connection from PIR client @ {}", peer_address); + + // Cheap cloning, because they are Arced ! + let cloned_server = arced_server.clone(); + let cloned_hint = arced_hint.clone(); + let cloned_filter_param = arced_filter_param.clone(); + + tokio::spawn(async move { + // Send seed to PIR client + stream.write_all(&seed_μ).await.unwrap(); + + // Send hint to PIR client + let hint_len = cloned_hint.len() as u32; + stream.write_all(&hint_len.to_le_bytes()).await.unwrap(); + stream.write_all(&cloned_hint).await.unwrap(); + + // Send Binary Fuse Filter parameters to PIR client + let filter_len = cloned_filter_param.len() as u32; + stream.write_all(&filter_len.to_le_bytes()).await.unwrap(); + stream.write_all(&cloned_filter_param).await.unwrap(); + + println!("Sent setup data to PIR client @ {}", peer_address); + + // Receive query from PIR client + let mut query_len_buf = [0u8; 4]; + stream.read_exact(&mut query_len_buf).await.unwrap(); + + let query_len = u32::from_le_bytes(query_len_buf) as usize; + + let mut query = vec![0u8; query_len]; + stream.read_exact(&mut query).await.unwrap(); + + println!("Received query of length {}B, from PIR client @ {}", query_len, peer_address); + let response = cloned_server.respond(&query).expect("PIR server failed to respond"); + + // Send response to PIR client + let response_len = response.len() as u32; + stream.write_all(&response_len.to_le_bytes()).await.unwrap(); + stream.write_all(&response).await.unwrap(); + + println!("Sent response of length {}B, to PIR client @ {}", response_len, peer_address); + }); + } +} From 530ed550bceef825968e95fe54a4cc614322b166 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 10 May 2025 12:58:55 +0400 Subject: [PATCH 04/23] Add example PIR client using tokio async runtime Signed-off-by: Anjan Roy --- chalamet_pir_client/Cargo.toml | 3 + chalamet_pir_client/examples/client.rs | 76 ++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 chalamet_pir_client/examples/client.rs diff --git a/chalamet_pir_client/Cargo.toml b/chalamet_pir_client/Cargo.toml index ba1a63e..6d840b2 100644 --- a/chalamet_pir_client/Cargo.toml +++ b/chalamet_pir_client/Cargo.toml @@ -22,5 +22,8 @@ categories = ["cryptography", "data-structures", "concurrency"] [dependencies] chalamet_pir_common = { path = "../chalamet_pir_common", version = "=0.6.0" } +[dev-dependencies] +tokio = { version = "=1.45.0", features = ["full"] } + [features] mutate_internal_client_state = [] diff --git a/chalamet_pir_client/examples/client.rs b/chalamet_pir_client/examples/client.rs new file mode 100644 index 0000000..7329bda --- /dev/null +++ b/chalamet_pir_client/examples/client.rs @@ -0,0 +1,76 @@ +use std::error::Error; + +use chalamet_pir_client::{Client, SEED_BYTE_LEN}; + +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::TcpStream; + +const SERVER_IP: &str = "127.0.0.1"; +const SERVER_PORT: u16 = 8080; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let server_address = format!("{}:{}", SERVER_IP, SERVER_PORT); + + let mut stream = TcpStream::connect(&server_address).await.expect("Failed to connect to PIR server"); + println!("Connected to PIR server @ {}", &server_address); + + // Receive seed from PIR server + let mut seed_μ = [0u8; SEED_BYTE_LEN]; + stream.read_exact(&mut seed_μ).await?; + + // Receive hint from PIR server + let mut hint_len_buf = [0u8; 4]; + stream.read_exact(&mut hint_len_buf).await?; + + let hint_len = u32::from_le_bytes(hint_len_buf) as usize; + + let mut hint_bytes = vec![0u8; hint_len]; + stream.read_exact(&mut hint_bytes).await?; + + // Receive Binary Fuse Filter parameters from PIR server + let mut filter_len_buf = [0u8; 4]; + stream.read_exact(&mut filter_len_buf).await?; + + let filter_len = u32::from_le_bytes(filter_len_buf) as usize; + + let mut filter_param_bytes = vec![0u8; filter_len]; + stream.read_exact(&mut filter_param_bytes).await?; + + println!("Received setup data from PIR server"); + + let mut client = Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes).expect("PIR client setup failed"); + + let key = b"banana"; + if let Ok(query) = client.query(key) { + println!("Generated query for key: {:?}", key); + + // Send query to PIR server + let query_len = query.len() as u32; + stream.write_all(&query_len.to_le_bytes()).await?; + stream.write_all(&query).await?; + + println!("Sent query of length {}B", query_len); + + // Receive response from PIR server + let mut response_len_buf = [0u8; 4]; + stream.read_exact(&mut response_len_buf).await?; + + let response_len = u32::from_le_bytes(response_len_buf) as usize; + + let mut response = vec![0u8; response_len]; + stream.read_exact(&mut response).await?; + + println!("Received response of length {}B", response_len); + + if let Ok(value) = client.process_response(key, &response) { + println!("Retrieved value: '{}'", String::from_utf8_lossy(&value)); + } else { + println!("Failed to retrieve value."); + } + } else { + println!("Failed to generate query."); + } + + Ok(()) +} From 1e0b93935c7ef9673ebdd4f10810bc2607b649f3 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 10 May 2025 13:10:23 +0400 Subject: [PATCH 05/23] Add documentation for chalametpir client crate Signed-off-by: Anjan Roy --- chalamet_pir_client/Cargo.toml | 2 +- chalamet_pir_client/README.md | 51 ++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 chalamet_pir_client/README.md diff --git a/chalamet_pir_client/Cargo.toml b/chalamet_pir_client/Cargo.toml index 6d840b2..eaa3721 100644 --- a/chalamet_pir_client/Cargo.toml +++ b/chalamet_pir_client/Cargo.toml @@ -6,7 +6,7 @@ resolver = "3" rust-version = "1.85.0" authors = ["Anjan Roy "] description = "Client Implementation of ChalametPIR: Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases" -readme = "../README.md" +readme = "README.md" repository = "https://github.com/itzmeanjan/ChalametPIR.git" license = "MPL-2.0" keywords = [ diff --git a/chalamet_pir_client/README.md b/chalamet_pir_client/README.md new file mode 100644 index 0000000..d1cf844 --- /dev/null +++ b/chalamet_pir_client/README.md @@ -0,0 +1,51 @@ +# ChalametPIR Client + +Client Implementation of ChalametPIR: Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases. + +This crate provides the client-side implementation for the ChalametPIR protocol. It includes functionality for: + +- Setting up the PIR client with parameters received from the server. +- Generating private information retrieval (PIR) queries for specific keys. +- Processing responses received from the server to recover the desired value. + +Key components: + +- `Client`: The main struct for interacting with the PIR client. It handles query generation and response processing. +- `Query`: Represents a PIR query, containing the secret vector needed to recover the value from the server's response. + +## Usage Example + +Add these dependencies to your `Cargo.toml`: + +```toml +rand = "=0.9.1" +rand_chacha = "=0.9.0" +chalamet_pir_client = "=0.6.0" +``` + +```rust +use chalamet_pir_client::{Client, SEED_BYTE_LEN}; +use rand::prelude::*; +use rand_chacha::ChaCha8Rng; + +fn main() { + // Assume seed, hint_bytes and filter_param_bytes are received from the PIR server + let seed_μ = [0u8; SEED_BYTE_LEN]; + let hint_bytes = vec![0u8; 0]; + let filter_param_bytes = vec![0u8; 0]; + + let mut client = Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes).expect("Client setup failed"); + + let key = b"example_key"; + if let Ok(query) = client.query(key) { + println!("Generated query for key: {:?}", key); + + // Send query to PIR server + + let response = vec![0u8; 0]; + if let Ok(value) = client.process_response(key, &response) { + println!("Received response {:?}", response); + } + } +} +``` From de99f71e42465a3d4dccdaeaa930efaa4a19b72f Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 10 May 2025 15:30:21 +0400 Subject: [PATCH 06/23] Add documentation for chalametpir server crate Signed-off-by: Anjan Roy --- chalamet_pir_server/Cargo.toml | 2 +- chalamet_pir_server/README.md | 57 ++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 chalamet_pir_server/README.md diff --git a/chalamet_pir_server/Cargo.toml b/chalamet_pir_server/Cargo.toml index 147a140..b979d15 100644 --- a/chalamet_pir_server/Cargo.toml +++ b/chalamet_pir_server/Cargo.toml @@ -6,7 +6,7 @@ resolver = "3" rust-version = "1.85.0" authors = ["Anjan Roy "] description = "Server Implementation of ChalametPIR: Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases" -readme = "../README.md" +readme = "README.md" repository = "https://github.com/itzmeanjan/ChalametPIR.git" license = "MPL-2.0" keywords = [ diff --git a/chalamet_pir_server/README.md b/chalamet_pir_server/README.md new file mode 100644 index 0000000..f33c7b3 --- /dev/null +++ b/chalamet_pir_server/README.md @@ -0,0 +1,57 @@ +# ChalametPIR Server + +Server Implementation of ChalametPIR: Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases. + +This crate provides the server-side implementation for the ChalametPIR protocol. It includes functionality for: + +- Setting up the PIR server with a key-value database. +- Responding to PIR queries from clients s.t. the server itself doesn't learn what the client looked up. + +Key components: + +- `Server`: The main struct for handling PIR requests. It contains the encoded database and methods for responding to client queries. + +## Usage Example + +Add these dependencies to your `Cargo.toml`: + +```toml +rand = "=0.9.1" +rand_chacha = "=0.9.0" +chalamet_pir_server = "=0.6.0" +``` + +```rust +use std::collections::HashMap; + +use chalamet_pir_server::{SEED_BYTE_LEN, Server}; + +use rand::prelude::*; +use rand_chacha::ChaCha8Rng; + +fn main() { + let mut rng = ChaCha8Rng::from_os_rng(); + let mut seed_μ = [0u8; SEED_BYTE_LEN]; + rng.fill_bytes(&mut seed_μ); + + let mut db: HashMap<&[u8], &[u8]> = HashMap::new(); + db.insert(b"key1", b"value1"); + db.insert(b"key2", b"value2"); + + let (server, hint_bytes, filter_param_bytes) = Server::setup::<3>(&seed_μ, db).expect("Server setup failed"); + + // Start handling client PIR queries + loop { + // First send seed, hint and filter params to PIR client + // so that it can set itself up. + + // Assume query_bytes is received from the client + let query_bytes = vec![0u8; 0]; + + if let Ok(response) = server.respond(&query_bytes) { + // Send the response to the client... + println!("Generated response of size: {} bytes", response.len()); + } + } +} +``` From 094f1e022407fcd87022890fa19ea6afdb71ae1b Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 10 May 2025 15:41:34 +0400 Subject: [PATCH 07/23] Add documentation for chalametpir common crate Signed-off-by: Anjan Roy --- chalamet_pir_common/README.md | 13 +++++++++++++ chalamet_pir_common/src/branch_opt_util.rs | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 chalamet_pir_common/README.md diff --git a/chalamet_pir_common/README.md b/chalamet_pir_common/README.md new file mode 100644 index 0000000..8c9cbc8 --- /dev/null +++ b/chalamet_pir_common/README.md @@ -0,0 +1,13 @@ +# ChalametPIR Common + +Common Utilities for ChalametPIR: Private Information Retrieval for Key-Value Databases. + +This crate provides common utilities and data structures used by both the client and server implementations of the ChalametPIR protocol. It includes: + +- Matrix operations: A `Matrix` struct for efficient matrix manipulation, including multiplication and addition. +- Binary Fuse Filter: Implementation of Binary Fuse Filters for encoding key-value databases. +- Error handling: A unified `ChalametPIRError` enum for reporting errors across the client and server. +- Parameters: Constants and parameters used in the ChalametPIR protocol. + +> [!NOTE] +> This crate is not supposed to be used by you on its own, rather it is a common dependency of both `chalamet_pir_server` and `chalamet_pir_client` crates. diff --git a/chalamet_pir_common/src/branch_opt_util.rs b/chalamet_pir_common/src/branch_opt_util.rs index e2d2a91..39e9ce6 100644 --- a/chalamet_pir_common/src/branch_opt_util.rs +++ b/chalamet_pir_common/src/branch_opt_util.rs @@ -1,5 +1,5 @@ -// Rust equivalent for C++'s compiler hint on which branch is more/ less likely https://en.cppreference.com/w/cpp/language/attributes/likely. -// Collects inspiration from https://users.rust-lang.org/t/compiler-hint-for-unlikely-likely-for-if-branches/62102/4. +/// Rust equivalent for C++'s compiler hint on which branch is more/ less likely https://en.cppreference.com/w/cpp/language/attributes/likely. +/// Collects inspiration from https://users.rust-lang.org/t/compiler-hint-for-unlikely-likely-for-if-branches/62102/4. #[inline] #[cold] From c04c0bdca9636cbf431df9fc0c4b9b76c0ae5ae0 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 10 May 2025 15:46:29 +0400 Subject: [PATCH 08/23] Mention chalamet-pir workspace root readme on member crate readme Signed-off-by: Anjan Roy --- chalamet_pir_client/README.md | 3 +++ chalamet_pir_server/README.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/chalamet_pir_client/README.md b/chalamet_pir_client/README.md index d1cf844..bdfb9fa 100644 --- a/chalamet_pir_client/README.md +++ b/chalamet_pir_client/README.md @@ -49,3 +49,6 @@ fn main() { } } ``` + +> [!NOTE] +> More documentation on ChalametPIR [here](../README.md). diff --git a/chalamet_pir_server/README.md b/chalamet_pir_server/README.md index f33c7b3..47c6789 100644 --- a/chalamet_pir_server/README.md +++ b/chalamet_pir_server/README.md @@ -55,3 +55,6 @@ fn main() { } } ``` + +> [!NOTE] +> More documentation on ChalametPIR [here](../README.md). From 201640342cef091577adec68db41332781d021ce Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 10 May 2025 16:28:37 +0400 Subject: [PATCH 09/23] Change workspace member crate names Signed-off-by: Anjan Roy --- Cargo.toml | 6 +- README.md | 8 +- chalamet_pir_client/src/lib.rs | 4 - chalamet_pir_server/src/lib.rs | 86 ------------------- .../Cargo.toml | 4 +- .../README.md | 8 +- .../examples/client.rs | 2 +- .../src/client.rs | 2 +- chalametpir_client/src/lib.rs | 61 +++++++++++++ .../Cargo.toml | 2 +- .../README.md | 2 +- .../src/binary_fuse_filter.rs | 0 .../src/branch_opt_util.rs | 0 .../src/error.rs | 0 .../src/lib.rs | 0 .../src/matrix.rs | 0 .../src/params.rs | 0 .../src/serialization.rs | 0 .../src/utils.rs | 0 .../Cargo.toml | 4 +- .../README.md | 6 +- .../examples/server.rs | 2 +- .../shaders/mat_transpose.glsl | 0 .../shaders/mat_x_mat.glsl | 0 .../src/gpu/gpu_utils.rs | 12 +-- .../src/gpu/mat_transpose_shader.rs | 0 .../src/gpu/mat_x_mat_shader.rs | 0 .../src/gpu/mod.rs | 0 chalametpir_server/src/lib.rs | 78 +++++++++++++++++ .../src/server.rs | 2 +- integrations/Cargo.toml | 8 +- integrations/benches/offline_phase.rs | 8 +- integrations/benches/online_phase.rs | 10 +-- integrations/src/main.rs | 4 +- integrations/src/test_pir.rs | 6 +- 35 files changed, 186 insertions(+), 139 deletions(-) delete mode 100644 chalamet_pir_client/src/lib.rs delete mode 100644 chalamet_pir_server/src/lib.rs rename {chalamet_pir_client => chalametpir_client}/Cargo.toml (86%) rename {chalamet_pir_client => chalametpir_client}/README.md (90%) rename {chalamet_pir_client => chalametpir_client}/examples/client.rs (97%) rename {chalamet_pir_client => chalametpir_client}/src/client.rs (99%) create mode 100644 chalametpir_client/src/lib.rs rename {chalamet_pir_common => chalametpir_common}/Cargo.toml (95%) rename {chalamet_pir_common => chalametpir_common}/README.md (88%) rename {chalamet_pir_common => chalametpir_common}/src/binary_fuse_filter.rs (100%) rename {chalamet_pir_common => chalametpir_common}/src/branch_opt_util.rs (100%) rename {chalamet_pir_common => chalametpir_common}/src/error.rs (100%) rename {chalamet_pir_common => chalametpir_common}/src/lib.rs (100%) rename {chalamet_pir_common => chalametpir_common}/src/matrix.rs (100%) rename {chalamet_pir_common => chalametpir_common}/src/params.rs (100%) rename {chalamet_pir_common => chalametpir_common}/src/serialization.rs (100%) rename {chalamet_pir_common => chalametpir_common}/src/utils.rs (100%) rename {chalamet_pir_server => chalametpir_server}/Cargo.toml (89%) rename {chalamet_pir_server => chalametpir_server}/README.md (94%) rename {chalamet_pir_server => chalametpir_server}/examples/server.rs (98%) rename {chalamet_pir_server => chalametpir_server}/shaders/mat_transpose.glsl (100%) rename {chalamet_pir_server => chalametpir_server}/shaders/mat_x_mat.glsl (100%) rename {chalamet_pir_server => chalametpir_server}/src/gpu/gpu_utils.rs (96%) rename {chalamet_pir_server => chalametpir_server}/src/gpu/mat_transpose_shader.rs (100%) rename {chalamet_pir_server => chalametpir_server}/src/gpu/mat_x_mat_shader.rs (100%) rename {chalamet_pir_server => chalametpir_server}/src/gpu/mod.rs (100%) create mode 100644 chalametpir_server/src/lib.rs rename {chalamet_pir_server => chalametpir_server}/src/server.rs (99%) diff --git a/Cargo.toml b/Cargo.toml index 2cdfded..8b8fd6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [workspace] members = [ - "chalamet_pir_client", - "chalamet_pir_common", - "chalamet_pir_server", + "chalametpir_client", + "chalametpir_common", + "chalametpir_server", "integrations", ] resolver = "3" diff --git a/README.md b/README.md index db2f9d0..1488abc 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ rustc 1.85.1 (e71f9a9a9 2025-01-27) If you plan to offload server-setup to GPU, you need to install Vulkan drivers and library for your target setup. I followed https://linux.how2shout.com/how-to-install-vulkan-on-ubuntu-24-04-or-22-04-lts-linux on Ubuntu 24.04 LTS, with Nvidia GPUs - it was easy to setup. ## Testing -The `chalamet_pir` library includes comprehensive tests to ensure functional correctness. +The `chalametpir` library includes comprehensive tests to ensure functional correctness. - **Property -based Tests:** Verify individual components: matrix operations (multiplication, addition), Binary Fuse Filter construction (3-wise and 4-wise XOR, including bits-per-entry (BPE) validation), and serialization/deserialization of `Matrix` and `BinaryFuseFilter`. - **Integration Tests:** Cover end-to-end PIR protocol functionality: key-value database encoding/decoding (parameterized by database size, key/value lengths, and filter arity), and client-server interaction to verify correct value retrieval without key disclosure (tested with both 3-wise and 4-wise XOR filters). @@ -133,9 +133,9 @@ First, add this library crate as a dependency in your Cargo.toml file. ```toml [dependencies] -chalamet_pir = "=0.6.0" +chalametpir = "=0.6.0" # Or, if you want to offload server-setup to a GPU. -# chalamet_pir = { version = "=0.6.0", features = ["gpu"] } +# chalametpir = { version = "=0.6.0", features = ["gpu"] } rand = "=0.9.0" rand_chacha = "=0.9.0" ``` @@ -143,7 +143,7 @@ rand_chacha = "=0.9.0" Then, let's code a very simple keyword PIR scheme: ```rust -use chalamet_pir::{client::Client, server::Server, SEED_BYTE_LEN}; +use chalametpir::{client::Client, server::Server, SEED_BYTE_LEN}; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use std::collections::HashMap; diff --git a/chalamet_pir_client/src/lib.rs b/chalamet_pir_client/src/lib.rs deleted file mode 100644 index 49bd2e4..0000000 --- a/chalamet_pir_client/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod client; - -pub use chalamet_pir_common::{error::ChalametPIRError, params::SEED_BYTE_LEN}; -pub use client::{Client, Query}; diff --git a/chalamet_pir_server/src/lib.rs b/chalamet_pir_server/src/lib.rs deleted file mode 100644 index 1c21e87..0000000 --- a/chalamet_pir_server/src/lib.rs +++ /dev/null @@ -1,86 +0,0 @@ -//! ChalametPIR: A Rust library implementation of the Chalamet **P**rivate **I**nformation **R**etrieval (PIR) protocol, described in . -//! -//! This crate provides a Rust library implementation of the ChalametPIR protocol, enabling efficient and private retrieval of value associated with a key, from encoded key-value database, stored server-side. -//! It leverages Binary Fuse Filters for efficient indexing and storage of key-value database and LWE-based encryption for data confidentiality. -//! -//! ## Features -//! -//! * **Secure Private Information Retrieval:** Allows clients to retrieve value from a PIR server without disclosing corresponding key. Server learns neither the value nor the queried key. -//! * **Error Handling:** Comprehensive error handling to catch and report issues during setup, query generation, and response processing. -//! * **Flexibility:** Supports both 3-wise and 4-wise XOR Binary Fuse Filters, allowing a choice between trade-offs in client/server computation and communication costs. -//! * **Efficient:** It supports offloading parts of the server-setup phase to a GPU, using Vulkan Compute API, which can drastically reduce time taken to setup PIR server, for large key-value databases. -//! -//! ## Usage -//! -//! This crate is designed to be used in conjunction with other crates which provides communication mechanism between clients and server. -//! You'll typically interact with the `Client` and `Server` structs to perform/ handle queries and process responses. -//! -//! Add ChalametPIR as dependency to your `Cargo.toml`: -//! -//! ```toml -//! [dependencies] -//! chalametpir = "=0.6.0" -//! # Or, if you want to offload server-setup to GPU. -//! # chalamet_pir = { version = "=0.6.0", features = ["gpu"] } -//! rand = "=0.9.0" -//! rand_chacha = "=0.9.0" -//! ``` -//! -//! Then, you can use it in your code: -//! -//! ```rust -//! use chalamet_pir::{client::Client, server::Server, SEED_BYTE_LEN}; -//! use rand::prelude::*; -//! use rand_chacha::ChaCha8Rng; -//! use std::collections::HashMap; -//! -//! // Example database (replace with your own) -//! let mut db: HashMap<&[u8], &[u8]> = HashMap::new(); -//! db.insert(b"apple", b"red"); -//! db.insert(b"banana", b"yellow"); -//! -//! // Server setup (offline phase) -//! let mut rng = ChaCha8Rng::from_os_rng(); -//! let mut seed_μ = [0u8; SEED_BYTE_LEN]; // You'll want to generate a cryptographically secure random seed -//! rng.fill_bytes(&mut seed_μ); -//! -//! let (server, hint_bytes, filter_param_bytes) = Server::setup::<3>(&seed_μ, db.clone()).expect("Server setup failed"); -//! -//! // Client setup (offline phase) -//! let mut client = Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes).expect("Client setup failed"); -//! -//! // Client query (online phase) -//! let key = b"banana"; -//! if let Ok(query) = client.query(key) { -//! // Send `query` to the server -//! -//! // Server response (online phase) -//! let response = server.respond(&query).expect("Server failed to respond"); -//! -//! // Client processes the response (online phase) -//! if let Ok(value) = client.process_response(key, &response) { -//! assert_eq!(value, b"yellow"); -//! println!("Retrieved value: '{}'", String::from_utf8_lossy(&value)); // Should print "yellow" -//! } else { -//! assert!(false); -//! println!("Failed to retrieve value."); -//! } -//! } else { -//! println!("Failed to generate query."); -//! } -//! ``` -//! -//! ## Modules -//! -//! * `server`: Contains the `Server` struct and associated methods for setting up a PIR server from a key-value database and responding to client queries. -//! * `client`: Contains the `Client` struct and associated methods for generating PIR queries and decoding server responses. -//! -//! For more see README in ChalametPIR repository @ . - -#[cfg(feature = "gpu")] -mod gpu; - -mod server; - -pub use chalamet_pir_common::{error::ChalametPIRError, params::SEED_BYTE_LEN}; -pub use server::Server; diff --git a/chalamet_pir_client/Cargo.toml b/chalametpir_client/Cargo.toml similarity index 86% rename from chalamet_pir_client/Cargo.toml rename to chalametpir_client/Cargo.toml index eaa3721..a934620 100644 --- a/chalamet_pir_client/Cargo.toml +++ b/chalametpir_client/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "chalamet_pir_client" +name = "chalametpir_client" version = "0.6.0" edition = "2024" resolver = "3" @@ -20,7 +20,7 @@ keywords = [ categories = ["cryptography", "data-structures", "concurrency"] [dependencies] -chalamet_pir_common = { path = "../chalamet_pir_common", version = "=0.6.0" } +chalametpir_common = { path = "../chalametpir_common", version = "=0.6.0" } [dev-dependencies] tokio = { version = "=1.45.0", features = ["full"] } diff --git a/chalamet_pir_client/README.md b/chalametpir_client/README.md similarity index 90% rename from chalamet_pir_client/README.md rename to chalametpir_client/README.md index bdfb9fa..b66bfd5 100644 --- a/chalamet_pir_client/README.md +++ b/chalametpir_client/README.md @@ -18,15 +18,11 @@ Key components: Add these dependencies to your `Cargo.toml`: ```toml -rand = "=0.9.1" -rand_chacha = "=0.9.0" -chalamet_pir_client = "=0.6.0" +chalametpir_client = "=0.6.0" ``` ```rust -use chalamet_pir_client::{Client, SEED_BYTE_LEN}; -use rand::prelude::*; -use rand_chacha::ChaCha8Rng; +use chalametpir_client::{Client, SEED_BYTE_LEN}; fn main() { // Assume seed, hint_bytes and filter_param_bytes are received from the PIR server diff --git a/chalamet_pir_client/examples/client.rs b/chalametpir_client/examples/client.rs similarity index 97% rename from chalamet_pir_client/examples/client.rs rename to chalametpir_client/examples/client.rs index 7329bda..bc0ce77 100644 --- a/chalamet_pir_client/examples/client.rs +++ b/chalametpir_client/examples/client.rs @@ -1,6 +1,6 @@ use std::error::Error; -use chalamet_pir_client::{Client, SEED_BYTE_LEN}; +use chalametpir_client::{Client, SEED_BYTE_LEN}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; diff --git a/chalamet_pir_client/src/client.rs b/chalametpir_client/src/client.rs similarity index 99% rename from chalamet_pir_client/src/client.rs rename to chalametpir_client/src/client.rs index 248f87c..b097891 100644 --- a/chalamet_pir_client/src/client.rs +++ b/chalametpir_client/src/client.rs @@ -1,4 +1,4 @@ -use chalamet_pir_common::{ +use chalametpir_common::{ binary_fuse_filter::{self, BinaryFuseFilter}, branch_opt_util, error::ChalametPIRError, diff --git a/chalametpir_client/src/lib.rs b/chalametpir_client/src/lib.rs new file mode 100644 index 0000000..714522b --- /dev/null +++ b/chalametpir_client/src/lib.rs @@ -0,0 +1,61 @@ +//! ChalametPIR: A Rust library implementation of the Chalamet **P**rivate **I**nformation **R**etrieval (PIR) protocol, described in . +//! +//! This crate provides a Rust library implementation of the ChalametPIR Client, enabling efficient and private lookup of value associated with a key, from encoded key-value database, stored PIR server-side. +//! It leverages Binary Fuse Filters for efficient indexing and storage of key-value database and LWE-based encryption for data confidentiality. +//! +//! +//! ## Features +//! +//! * **Secure Private Information Retrieval:** Allows PIR clients to retrieve value from a PIR server without disclosing corresponding key. Server learns neither the value nor the queried key. +//! * **Error Handling:** Comprehensive error handling to catch and report issues during setup, query generation, and response processing. +//! +//! ## Usage +//! +//! This crate is designed to be used in conjunction with other crates which provides communication mechanism between PIR clients and server. +//! See examples. You'll typically interact with the `Client` struct to setup PIR client using server provided seed, hint and filter params. Also for +//! creating PIR queries and processing response received from PIR server. There is also a `Query` struct, which generally holds +//! the LWE secret vector for a specific queried key and uses it to decode server response. +//! +//! +//! Add this crate as dependency to your `Cargo.toml`: +//! +//! ```toml +//! [dependencies] +//! chalametpir_client = "=0.6.0" +//! ``` +//! +//! Then, you can use it in your code: +//! +//! ```rust +//! use chalametpir_client::{Client, SEED_BYTE_LEN}; +//! use rand::prelude::*; +//! use rand_chacha::ChaCha8Rng; +//! +//! fn main() { +//! // Assume seed, hint_bytes and filter_param_bytes are received from the PIR server +//! let seed_μ = [0u8; SEED_BYTE_LEN]; +//! let hint_bytes = vec![0u8; 0]; +//! let filter_param_bytes = vec![0u8; 0]; +//! +//! let mut client = Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes).expect("Client setup failed"); +//! +//! let key = b"example_key"; +//! if let Ok(query) = client.query(key) { +//! println!("Generated query for key: {:?}", key); +//! +//! // Send query to PIR server +//! +//! let response = vec![0u8; 0]; +//! if let Ok(value) = client.process_response(key, &response) { +//! println!("Received response {:?}", response); +//! } +//! } +//! } +//! ``` +//! +//! For more see README in ChalametPIR repository @ . + +mod client; + +pub use chalametpir_common::{error::ChalametPIRError, params::SEED_BYTE_LEN}; +pub use client::{Client, Query}; diff --git a/chalamet_pir_common/Cargo.toml b/chalametpir_common/Cargo.toml similarity index 95% rename from chalamet_pir_common/Cargo.toml rename to chalametpir_common/Cargo.toml index b6de00c..c28b2ca 100644 --- a/chalamet_pir_common/Cargo.toml +++ b/chalametpir_common/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "chalamet_pir_common" +name = "chalametpir_common" version = "0.6.0" edition = "2024" resolver = "3" diff --git a/chalamet_pir_common/README.md b/chalametpir_common/README.md similarity index 88% rename from chalamet_pir_common/README.md rename to chalametpir_common/README.md index 8c9cbc8..5291d83 100644 --- a/chalamet_pir_common/README.md +++ b/chalametpir_common/README.md @@ -10,4 +10,4 @@ This crate provides common utilities and data structures used by both the client - Parameters: Constants and parameters used in the ChalametPIR protocol. > [!NOTE] -> This crate is not supposed to be used by you on its own, rather it is a common dependency of both `chalamet_pir_server` and `chalamet_pir_client` crates. +> This crate is not supposed to be used by you on its own, rather it is a common dependency of both `chalametpir_server` and `chalametpir_client` crates. diff --git a/chalamet_pir_common/src/binary_fuse_filter.rs b/chalametpir_common/src/binary_fuse_filter.rs similarity index 100% rename from chalamet_pir_common/src/binary_fuse_filter.rs rename to chalametpir_common/src/binary_fuse_filter.rs diff --git a/chalamet_pir_common/src/branch_opt_util.rs b/chalametpir_common/src/branch_opt_util.rs similarity index 100% rename from chalamet_pir_common/src/branch_opt_util.rs rename to chalametpir_common/src/branch_opt_util.rs diff --git a/chalamet_pir_common/src/error.rs b/chalametpir_common/src/error.rs similarity index 100% rename from chalamet_pir_common/src/error.rs rename to chalametpir_common/src/error.rs diff --git a/chalamet_pir_common/src/lib.rs b/chalametpir_common/src/lib.rs similarity index 100% rename from chalamet_pir_common/src/lib.rs rename to chalametpir_common/src/lib.rs diff --git a/chalamet_pir_common/src/matrix.rs b/chalametpir_common/src/matrix.rs similarity index 100% rename from chalamet_pir_common/src/matrix.rs rename to chalametpir_common/src/matrix.rs diff --git a/chalamet_pir_common/src/params.rs b/chalametpir_common/src/params.rs similarity index 100% rename from chalamet_pir_common/src/params.rs rename to chalametpir_common/src/params.rs diff --git a/chalamet_pir_common/src/serialization.rs b/chalametpir_common/src/serialization.rs similarity index 100% rename from chalamet_pir_common/src/serialization.rs rename to chalametpir_common/src/serialization.rs diff --git a/chalamet_pir_common/src/utils.rs b/chalametpir_common/src/utils.rs similarity index 100% rename from chalamet_pir_common/src/utils.rs rename to chalametpir_common/src/utils.rs diff --git a/chalamet_pir_server/Cargo.toml b/chalametpir_server/Cargo.toml similarity index 89% rename from chalamet_pir_server/Cargo.toml rename to chalametpir_server/Cargo.toml index b979d15..5126bc3 100644 --- a/chalamet_pir_server/Cargo.toml +++ b/chalametpir_server/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "chalamet_pir_server" +name = "chalametpir_server" version = "0.6.0" edition = "2024" resolver = "3" @@ -21,7 +21,7 @@ keywords = [ categories = ["cryptography", "data-structures", "concurrency"] [dependencies] -chalamet_pir_common = { path = "../chalamet_pir_common", version = "=0.6.0" } +chalametpir_common = { path = "../chalametpir_common", version = "=0.6.0" } vulkano = { version = "=0.35.1", optional = true } vulkano-shaders = { version = "=0.35.0", optional = true } diff --git a/chalamet_pir_server/README.md b/chalametpir_server/README.md similarity index 94% rename from chalamet_pir_server/README.md rename to chalametpir_server/README.md index 47c6789..7c6f747 100644 --- a/chalamet_pir_server/README.md +++ b/chalametpir_server/README.md @@ -18,13 +18,13 @@ Add these dependencies to your `Cargo.toml`: ```toml rand = "=0.9.1" rand_chacha = "=0.9.0" -chalamet_pir_server = "=0.6.0" +chalametpir_server = "=0.6.0" ``` ```rust use std::collections::HashMap; -use chalamet_pir_server::{SEED_BYTE_LEN, Server}; +use chalametpir_server::{SEED_BYTE_LEN, Server}; use rand::prelude::*; use rand_chacha::ChaCha8Rng; @@ -52,6 +52,8 @@ fn main() { // Send the response to the client... println!("Generated response of size: {} bytes", response.len()); } + + break; } } ``` diff --git a/chalamet_pir_server/examples/server.rs b/chalametpir_server/examples/server.rs similarity index 98% rename from chalamet_pir_server/examples/server.rs rename to chalametpir_server/examples/server.rs index dc6866e..1484d13 100644 --- a/chalamet_pir_server/examples/server.rs +++ b/chalametpir_server/examples/server.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use std::{collections::HashMap, error::Error}; -use chalamet_pir_server::{SEED_BYTE_LEN, Server}; +use chalametpir_server::{SEED_BYTE_LEN, Server}; use rand::prelude::*; use rand_chacha::ChaCha8Rng; diff --git a/chalamet_pir_server/shaders/mat_transpose.glsl b/chalametpir_server/shaders/mat_transpose.glsl similarity index 100% rename from chalamet_pir_server/shaders/mat_transpose.glsl rename to chalametpir_server/shaders/mat_transpose.glsl diff --git a/chalamet_pir_server/shaders/mat_x_mat.glsl b/chalametpir_server/shaders/mat_x_mat.glsl similarity index 100% rename from chalamet_pir_server/shaders/mat_x_mat.glsl rename to chalametpir_server/shaders/mat_x_mat.glsl diff --git a/chalamet_pir_server/src/gpu/gpu_utils.rs b/chalametpir_server/src/gpu/gpu_utils.rs similarity index 96% rename from chalamet_pir_server/src/gpu/gpu_utils.rs rename to chalametpir_server/src/gpu/gpu_utils.rs index 5d2bf81..7d62db6 100644 --- a/chalamet_pir_server/src/gpu/gpu_utils.rs +++ b/chalametpir_server/src/gpu/gpu_utils.rs @@ -1,26 +1,26 @@ pub use std::sync::Arc; pub use vulkano::{ - VulkanLibrary, buffer::Subbuffer, buffer::{Buffer, BufferCreateInfo, BufferUsage}, command_buffer::allocator::StandardCommandBufferAllocator, command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, CopyBufferInfo, PrimaryCommandBufferAbstract}, - descriptor_set::{DescriptorSet, WriteDescriptorSet, allocator::StandardDescriptorSetAllocator}, + descriptor_set::{allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet}, + device::{physical::PhysicalDeviceType, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, QueueFlags}, device::{Device, Queue}, - device::{DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, QueueFlags, physical::PhysicalDeviceType}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, memory::allocator::StandardMemoryAllocator, memory::allocator::{AllocationCreateInfo, MemoryTypeFilter}, pipeline::{ - ComputePipeline, Pipeline, PipelineBindPoint, PipelineLayout, PipelineShaderStageCreateInfo, compute::ComputePipelineCreateInfo, - layout::PipelineDescriptorSetLayoutCreateInfo, + compute::ComputePipelineCreateInfo, layout::PipelineDescriptorSetLayoutCreateInfo, ComputePipeline, Pipeline, PipelineBindPoint, PipelineLayout, + PipelineShaderStageCreateInfo, }, sync::GpuFuture, + VulkanLibrary, }; use super::{mat_transpose_shader, mat_x_mat_shader}; use crate::ChalametPIRError; -use chalamet_pir_common::matrix::Matrix; +use chalametpir_common::matrix::Matrix; pub fn setup_gpu() -> Result<(Arc, Arc, Arc, Arc), ChalametPIRError> { let library = VulkanLibrary::new().map_err(|_| ChalametPIRError::VulkanLibraryNotFound)?; diff --git a/chalamet_pir_server/src/gpu/mat_transpose_shader.rs b/chalametpir_server/src/gpu/mat_transpose_shader.rs similarity index 100% rename from chalamet_pir_server/src/gpu/mat_transpose_shader.rs rename to chalametpir_server/src/gpu/mat_transpose_shader.rs diff --git a/chalamet_pir_server/src/gpu/mat_x_mat_shader.rs b/chalametpir_server/src/gpu/mat_x_mat_shader.rs similarity index 100% rename from chalamet_pir_server/src/gpu/mat_x_mat_shader.rs rename to chalametpir_server/src/gpu/mat_x_mat_shader.rs diff --git a/chalamet_pir_server/src/gpu/mod.rs b/chalametpir_server/src/gpu/mod.rs similarity index 100% rename from chalamet_pir_server/src/gpu/mod.rs rename to chalametpir_server/src/gpu/mod.rs diff --git a/chalametpir_server/src/lib.rs b/chalametpir_server/src/lib.rs new file mode 100644 index 0000000..3d9625e --- /dev/null +++ b/chalametpir_server/src/lib.rs @@ -0,0 +1,78 @@ +//! ChalametPIR: A Rust library implementation of the Chalamet **P**rivate **I**nformation **R**etrieval (PIR) protocol, described in . +//! +//! This crate provides a Rust library implementation of the ChalametPIR Server, enabling efficient and private retrieval of value associated with a key, from encoded key-value database, stored server-side. +//! It leverages Binary Fuse Filters for efficient indexing and storage of key-value database and LWE-based encryption for data confidentiality. +//! +//! +//! ## Features +//! +//! * **Secure Private Information Retrieval:** Allows clients to retrieve value from a PIR server without disclosing corresponding key. Server learns neither the value nor the queried key. +//! * **Error Handling:** Comprehensive error handling to catch and report issues during setup and responding to client queries. +//! * **Flexibility:** Supports both 3-wise and 4-wise XOR Binary Fuse Filters, allowing a choice between trade-offs in client/server computation and communication costs. +//! * **Efficient:** It supports offloading parts of the server-setup phase to a GPU, using Vulkan Compute API, which can drastically reduce time taken to setup PIR server, for large key-value databases. Look for `gpu` feature. +//! +//! ## Usage +//! +//! This crate is designed to be used in conjunction with other crates which provides communication mechanism between PIR clients and server. +//! See examples. You'll typically interact with the `Server` struct to setup PIR server from a key-value database and respond to PIR client queries. +//! +//! Add this crate as dependency to your `Cargo.toml`: +//! +//! ```toml +//! [dependencies] +//! chalametpir_server = "=0.6.0" +//! # Or, if you want to offload server-setup to GPU. +//! # chalametpir_server = { version = "=0.6.0", features = ["gpu"] } +//! +//! rand = "=0.9.1" +//! rand_chacha = "=0.9.0" +//! ``` +//! +//! Then, you can use it in your code: +//! +//! ```rust +//! use std::collections::HashMap; +//! +//! use chalametpir_server::{SEED_BYTE_LEN, Server}; +//! +//! use rand::prelude::*; +//! use rand_chacha::ChaCha8Rng; +//! +//! fn main() { +//! let mut rng = ChaCha8Rng::from_os_rng(); +//! let mut seed_μ = [0u8; SEED_BYTE_LEN]; +//! rng.fill_bytes(&mut seed_μ); +//! +//! let mut db: HashMap<&[u8], &[u8]> = HashMap::new(); +//! db.insert(b"key1", b"value1"); +//! db.insert(b"key2", b"value2"); +//! +//! let (server, hint_bytes, filter_param_bytes) = Server::setup::<3>(&seed_μ, db).expect("Server setup failed"); +//! +//! // Start handling client PIR queries +//! loop { +//! // First send seed, hint and filter params to PIR client +//! // so that it can set itself up. +//! +//! // Assume query_bytes is received from the client +//! let query_bytes = vec![0u8; 0]; +//! +//! if let Ok(response) = server.respond(&query_bytes) { +//! // Send the response to the client... +//! println!("Generated response of size: {} bytes", response.len()); +//! } +//! +//! break; +//! } +//! } +//! ``` +//! +//! For more see README in ChalametPIR repository @ . + +#[cfg(feature = "gpu")] +mod gpu; + +mod server; + +pub use chalametpir_common::{error::ChalametPIRError, params::SEED_BYTE_LEN}; +pub use server::Server; diff --git a/chalamet_pir_server/src/server.rs b/chalametpir_server/src/server.rs similarity index 99% rename from chalamet_pir_server/src/server.rs rename to chalametpir_server/src/server.rs index 6b13d66..e8c0e59 100644 --- a/chalamet_pir_server/src/server.rs +++ b/chalametpir_server/src/server.rs @@ -1,4 +1,4 @@ -use chalamet_pir_common::{ +use chalametpir_common::{ branch_opt_util, error::ChalametPIRError, matrix::Matrix, diff --git a/integrations/Cargo.toml b/integrations/Cargo.toml index ece50c8..eaec06c 100644 --- a/integrations/Cargo.toml +++ b/integrations/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "chalamet_pir_integrations" +name = "chalametpir_integrations" version = "0.6.0" edition = "2024" resolver = "3" @@ -14,15 +14,15 @@ publish = false rand = "=0.9.1" rand_chacha = "=0.9.0" unicode-xid = "=0.2.6" -chalamet_pir_server = { path = "../chalamet_pir_server" } -chalamet_pir_client = { path = "../chalamet_pir_client", features = [ +chalametpir_server = { path = "../chalametpir_server" } +chalametpir_client = { path = "../chalametpir_client", features = [ "mutate_internal_client_state", ] } [dev-dependencies] test-case = "=3.3.1" divan = "=0.1.21" -chalamet_pir_common = { path = "../chalamet_pir_common" } +chalametpir_common = { path = "../chalametpir_common" } [[bench]] name = "offline_phase" diff --git a/integrations/benches/offline_phase.rs b/integrations/benches/offline_phase.rs index eefad86..8107763 100644 --- a/integrations/benches/offline_phase.rs +++ b/integrations/benches/offline_phase.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, time::Duration}; -use chalamet_pir_client::Client; -use chalamet_pir_server::Server; +use chalametpir_client::Client; +use chalametpir_server::Server; use divan; use rand::prelude::*; @@ -63,7 +63,7 @@ fn server_setup(bencher: divan::Bencher, db_config: &DBConfig) let kv = generate_random_kv_database(&mut rng, db_config.db_entry_count, db_config.key_byte_len, db_config.value_byte_len); let kv_as_ref = kv.iter().map(|(k, v)| (k.as_slice(), v.as_slice())).collect::>(); - let mut seed_μ = [0u8; chalamet_pir_server::SEED_BYTE_LEN]; + let mut seed_μ = [0u8; chalametpir_server::SEED_BYTE_LEN]; rng.fill_bytes(&mut seed_μ); bencher @@ -78,7 +78,7 @@ fn client_setup(bencher: divan::Bencher, db_config: &DBConfig) let kv = generate_random_kv_database(&mut rng, db_config.db_entry_count, db_config.key_byte_len, db_config.value_byte_len); let kv_as_ref = kv.iter().map(|(k, v)| (k.as_slice(), v.as_slice())).collect::>(); - let mut seed_μ = [0u8; chalamet_pir_server::SEED_BYTE_LEN]; + let mut seed_μ = [0u8; chalametpir_server::SEED_BYTE_LEN]; rng.fill_bytes(&mut seed_μ); let (_, hint_bytes, filter_param_bytes) = Server::setup::(&seed_μ, kv_as_ref).expect("Server setup failed"); diff --git a/integrations/benches/online_phase.rs b/integrations/benches/online_phase.rs index 9e26aec..0474f77 100644 --- a/integrations/benches/online_phase.rs +++ b/integrations/benches/online_phase.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, time::Duration}; -use chalamet_pir_client::Client; -use chalamet_pir_server::Server; +use chalametpir_client::Client; +use chalametpir_server::Server; use divan; use rand::prelude::*; @@ -63,7 +63,7 @@ fn client_query(bencher: divan::Bencher, db_config: &DBConfig) let kv = generate_random_kv_database(&mut rng, db_config.db_entry_count, db_config.key_byte_len, db_config.value_byte_len); let kv_as_ref = kv.iter().map(|(k, v)| (k.as_slice(), v.as_slice())).collect::>(); - let mut seed_μ = [0u8; chalamet_pir_server::SEED_BYTE_LEN]; + let mut seed_μ = [0u8; chalametpir_server::SEED_BYTE_LEN]; rng.fill_bytes(&mut seed_μ); let (_, hint_bytes, filter_param_bytes) = Server::setup::(&seed_μ, kv_as_ref.clone()).unwrap(); @@ -84,7 +84,7 @@ fn server_respond(bencher: divan::Bencher, db_config: &DBConfi let kv = generate_random_kv_database(&mut rng, db_config.db_entry_count, db_config.key_byte_len, db_config.value_byte_len); let kv_as_ref = kv.iter().map(|(k, v)| (k.as_slice(), v.as_slice())).collect::>(); - let mut seed_μ = [0u8; chalamet_pir_server::SEED_BYTE_LEN]; + let mut seed_μ = [0u8; chalametpir_server::SEED_BYTE_LEN]; rng.fill_bytes(&mut seed_μ); let (server, hint_bytes, filter_param_bytes) = Server::setup::(&seed_μ, kv_as_ref.clone()).unwrap(); @@ -103,7 +103,7 @@ fn client_process_response(bencher: divan::Bencher, db_config: let kv = generate_random_kv_database(&mut rng, db_config.db_entry_count, db_config.key_byte_len, db_config.value_byte_len); let kv_as_ref = kv.iter().map(|(k, v)| (k.as_slice(), v.as_slice())).collect::>(); - let mut seed_μ = [0u8; chalamet_pir_server::SEED_BYTE_LEN]; + let mut seed_μ = [0u8; chalametpir_server::SEED_BYTE_LEN]; rng.fill_bytes(&mut seed_μ); let (server, hint_bytes, filter_param_bytes) = Server::setup::(&seed_μ, kv_as_ref.clone()).unwrap(); diff --git a/integrations/src/main.rs b/integrations/src/main.rs index 84b9a6d..2018004 100644 --- a/integrations/src/main.rs +++ b/integrations/src/main.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, time::Instant}; -use chalamet_pir_client::Client; -use chalamet_pir_server::{SEED_BYTE_LEN, Server}; +use chalametpir_client::Client; +use chalametpir_server::{SEED_BYTE_LEN, Server}; use rand::prelude::*; use rand_chacha::ChaCha8Rng; diff --git a/integrations/src/test_pir.rs b/integrations/src/test_pir.rs index 17db813..ee31df4 100644 --- a/integrations/src/test_pir.rs +++ b/integrations/src/test_pir.rs @@ -2,9 +2,9 @@ use std::collections::HashMap; -use chalamet_pir_client::Client; -use chalamet_pir_common::utils::generate_random_kv_database; -use chalamet_pir_server::{ChalametPIRError, Server}; +use chalametpir_client::Client; +use chalametpir_common::utils::generate_random_kv_database; +use chalametpir_server::{ChalametPIRError, Server}; use rand::prelude::*; use rand_chacha::ChaCha8Rng; From f7b17156f7de39e0748e68deae9a0a2d910ffe45 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 10 May 2025 16:38:24 +0400 Subject: [PATCH 10/23] Address doc test failure for chalametpir-client Signed-off-by: Anjan Roy --- chalametpir_client/README.md | 27 +++++++++++++++------------ chalametpir_client/src/lib.rs | 29 +++++++++++++++-------------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/chalametpir_client/README.md b/chalametpir_client/README.md index b66bfd5..fdfba76 100644 --- a/chalametpir_client/README.md +++ b/chalametpir_client/README.md @@ -30,19 +30,22 @@ fn main() { let hint_bytes = vec![0u8; 0]; let filter_param_bytes = vec![0u8; 0]; - let mut client = Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes).expect("Client setup failed"); - - let key = b"example_key"; - if let Ok(query) = client.query(key) { - println!("Generated query for key: {:?}", key); - - // Send query to PIR server - - let response = vec![0u8; 0]; - if let Ok(value) = client.process_response(key, &response) { - println!("Received response {:?}", response); + match Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes) { + Ok(mut client) => { + let key = b"example_key"; + if let Ok(query) = client.query(key) { + println!("Generated query for key: {:?}", key); + // Send query to PIR server + let response = vec![0u8; 0]; + if let Ok(value) = client.process_response(key, &response) { + println!("Received response {:?}", response); + } + } + } + Err(err) => { + println!("Client setup failed: {}", err); } - } + }; } ``` diff --git a/chalametpir_client/src/lib.rs b/chalametpir_client/src/lib.rs index 714522b..d2236d1 100644 --- a/chalametpir_client/src/lib.rs +++ b/chalametpir_client/src/lib.rs @@ -28,8 +28,6 @@ //! //! ```rust //! use chalametpir_client::{Client, SEED_BYTE_LEN}; -//! use rand::prelude::*; -//! use rand_chacha::ChaCha8Rng; //! //! fn main() { //! // Assume seed, hint_bytes and filter_param_bytes are received from the PIR server @@ -37,19 +35,22 @@ //! let hint_bytes = vec![0u8; 0]; //! let filter_param_bytes = vec![0u8; 0]; //! -//! let mut client = Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes).expect("Client setup failed"); -//! -//! let key = b"example_key"; -//! if let Ok(query) = client.query(key) { -//! println!("Generated query for key: {:?}", key); -//! -//! // Send query to PIR server -//! -//! let response = vec![0u8; 0]; -//! if let Ok(value) = client.process_response(key, &response) { -//! println!("Received response {:?}", response); +//! match Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes) { +//! Ok(mut client) => { +//! let key = b"example_key"; +//! if let Ok(query) = client.query(key) { +//! println!("Generated query for key: {:?}", key); +//! // Send query to PIR server +//! let response = vec![0u8; 0]; +//! if let Ok(value) = client.process_response(key, &response) { +//! println!("Received response {:?}", response); +//! } +//! } +//! } +//! Err(err) => { +//! println!("Client setup failed: {}", err); //! } -//! } +//! }; //! } //! ``` //! From a12829c6c57b0ebb8ab5c2794e8dc54688eea59a Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 10 May 2025 17:22:28 +0400 Subject: [PATCH 11/23] Update workspace root README file with latest project details Signed-off-by: Anjan Roy --- README.md | 154 +++++++--------------------------- chalametpir_server/README.md | 5 +- chalametpir_server/src/lib.rs | 5 +- 3 files changed, 36 insertions(+), 128 deletions(-) diff --git a/README.md b/README.md index 1488abc..c523772 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,14 @@ ChalametPIR allows a client to retrieve a specific value from a key-value databa The protocol has two participants: **Server:** +Implemented by [chalametpir_server](./chalametpir_server) crate. + * **`setup`:** Initializes the server with a seed, a key-value database, generating a public matrix, a hint matrix, and a Binary Fuse Filter (3-wise XOR or 4-wise XOR, configurable at compile time). It returns serialized representations of the hint matrix and filter parameters. This phase can be completed offline and is completely client-agnostic. But it is very compute-intensive, which is why this library allows you to offload expensive matrix multiplication and transposition to a GPU, gated behind the opt-in `gpu` feature. For large key-value databases (e.g., with >= $2^{18}$ entries), I recommend enabling the `gpu` feature, as it can significantly reduce the cost of the server-setup phase. * **`respond`:** Processes a client's encrypted query, returning an encrypted response vector. **Client:** +Implemented by [chalametpir_client](./chalametpir_client) crate. + * **`setup`:** Initializes the client using the seed, serialized hint matrix and filter parameters received from the server. * **`query`:** Generates an encrypted PIR query for a given key, which can be sent to server. * **`process_response`:** Decrypts the server's response and extracts the requested value. @@ -82,7 +86,7 @@ rustc 1.85.1 (e71f9a9a9 2025-01-27) If you plan to offload server-setup to GPU, you need to install Vulkan drivers and library for your target setup. I followed https://linux.how2shout.com/how-to-install-vulkan-on-ubuntu-24-04-or-22-04-lts-linux on Ubuntu 24.04 LTS, with Nvidia GPUs - it was easy to setup. ## Testing -The `chalametpir` library includes comprehensive tests to ensure functional correctness. +The ChalametPIR library includes comprehensive tests to ensure functional correctness. - **Property -based Tests:** Verify individual components: matrix operations (multiplication, addition), Binary Fuse Filter construction (3-wise and 4-wise XOR, including bits-per-entry (BPE) validation), and serialization/deserialization of `Matrix` and `BinaryFuseFilter`. - **Integration Tests:** Cover end-to-end PIR protocol functionality: key-value database encoding/decoding (parameterized by database size, key/value lengths, and filter arity), and client-server interaction to verify correct value retrieval without key disclosure (tested with both 3-wise and 4-wise XOR filters). @@ -105,9 +109,8 @@ Performance benchmarks are included to evaluate the efficiency of the PIR scheme To run the benchmarks, execute the following command from the root of the project: ```bash -# For benchmarking the online phase of the PIR, -# you need to enable feature `mutate_internal_client_state`. -cargo bench --features mutate_internal_client_state --profile optimized +# Run all benchmarks. +cargo bench --profile optimized # For benchmarking only the server-setup phase, offloaded to the GPU. cargo bench --features gpu --profile optimized --bench offline_phase -q server_setup @@ -129,135 +132,34 @@ cargo bench --features gpu --profile optimized --bench offline_phase -q server_s > More about AWS EC2 instances @ https://aws.amazon.com/ec2/instance-types. ## Usage -First, add this library crate as a dependency in your Cargo.toml file. - -```toml -[dependencies] -chalametpir = "=0.6.0" -# Or, if you want to offload server-setup to a GPU. -# chalametpir = { version = "=0.6.0", features = ["gpu"] } -rand = "=0.9.0" -rand_chacha = "=0.9.0" -``` -Then, let's code a very simple keyword PIR scheme: - -```rust -use chalametpir::{client::Client, server::Server, SEED_BYTE_LEN}; -use rand::prelude::*; -use rand_chacha::ChaCha8Rng; -use std::collections::HashMap; - -fn main() { - // Example database (replace with your own) - let mut db: HashMap<&[u8], &[u8]> = HashMap::new(); - db.insert(b"apple", b"red"); - db.insert(b"banana", b"yellow"); - - // Server setup (offline phase) - let mut rng = ChaCha8Rng::from_os_rng(); - let mut seed_μ = [0u8; SEED_BYTE_LEN]; // You'll want to generate a cryptographically secure random seed - rng.fill_bytes(&mut seed_μ); - - let (server, hint_bytes, filter_param_bytes) = Server::setup::<3>(&seed_μ, db.clone()).expect("Server setup failed"); - - // Client setup (offline phase) - let mut client = Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes).expect("Client setup failed"); - - // Client query (online phase) - let key = b"banana"; - if let Ok(query) = client.query(key) { - // Send `query` to the server - - // Server response (online phase) - let response = server.respond(&query).expect("Server failed to respond"); - - // Client processes the response (online phase) - if let Ok(value) = client.process_response(key, &response) { - println!("Retrieved value: '{}'", String::from_utf8_lossy(&value)); // Should print "yellow" - } else { - println!("Failed to retrieve value."); - } - } else { - println!("Failed to generate query."); - } -} -``` +- For understanding how PIR server library crate `chalametpir_server` can be used, read [this](./chalametpir_server/README.md). +- While for using PIR client library crate `chalametpir_client`, read [this](./chalametpir_client/README.md). The constant parameter `ARITY` (3 or 4) in `Server::setup` controls the type of Binary Fuse Filter used to encode the KV database, which affects size of the query vector and the encoded database dimensions, stored in-memory server-side. This implementation should allow you to run PIR queries on a KV database with at max 2^42 (~4 trillion) number of entries. -I maintain one example [program](./examples/kw_pir.rs) which demonstrates usage of the ChalametPIR API. +I maintain two example binaries, implementing PIR server and client execution flow. ```bash -cargo run --example kw_pir --profile optimized +# First, issue following command on one terminal window. +$ cargo run --example server --profile optimized + +PIR Server listening @ 127.0.0.1:8080 +New connection from PIR client @ 127.0.0.1:43322 +Sent setup data to PIR client @ 127.0.0.1:43322 +Received query of length 200B, from PIR client @ 127.0.0.1:43322 +Sent response of length 104B, to PIR client @ 127.0.0.1:43322 +... ``` ```bash -# Using 3-wise XOR Binary Fuse Filter -ChalametPIR: -Number of entries in Key-Value Database : 65536 -Size of each key : 8.0B -Size of each value : 4.0B -Arity of Binary Fuse Filter : 3 -Seed size : 32.0B -Hint size : 207.9KB -Filter parameters size : 68.0B -Query size : 304.0KB -Response size : 128.0B - -✅ '64187' maps to 'b', in 274.995µs -⚠️ Random key '112599' is not present in DB -⚠️ Random key '108662' is not present in DB -⚠️ Random key '79395' is not present in DB -⚠️ Random key '72638' is not present in DB -⚠️ Random key '123690' is not present in DB -⚠️ Random key '69344' is not present in DB -⚠️ Random key '69155' is not present in DB -✅ '5918' maps to 'J', in 165.606µs -⚠️ Random key '128484' is not present in DB -⚠️ Random key '79290' is not present in DB -⚠️ Random key '104015' is not present in DB -⚠️ Random key '111256' is not present in DB -⚠️ Random key '124342' is not present in DB -⚠️ Random key '74982' is not present in DB -⚠️ Random key '93082' is not present in DB -✅ '32800' maps to 'b', in 233.29µs -✅ '20236' maps to 'Q', in 233.531µs -✅ '47334' maps to 'p', in 223.548µs -✅ '12225' maps to 'U', in 209.217µs - -# --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- - -# Using 4-wise XOR Binary Fuse Filter -ChalametPIR: -Number of entries in Key-Value Database : 65536 -Size of each key : 8.0B -Size of each value : 4.0B -Arity of Binary Fuse Filter : 4 -Seed size : 32.0B -Hint size : 207.9KB -Filter parameters size : 68.0B -Query size : 292.0KB -Response size : 128.0B - -✅ '13239' maps to 'T', in 241.21µs -⚠️ Random key '112983' is not present in DB -⚠️ Random key '89821' is not present in DB -✅ '63385' maps to 'I', in 188.06µs -⚠️ Random key '123914' is not present in DB -⚠️ Random key '119919' is not present in DB -⚠️ Random key '72903' is not present in DB -⚠️ Random key '93634' is not present in DB -⚠️ Random key '68582' is not present in DB -✅ '55692' maps to 'n', in 359.112µs -⚠️ Random key '68191' is not present in DB -⚠️ Random key '92762' is not present in DB -✅ '997' maps to 'v', in 302.626µs -⚠️ Random key '123011' is not present in DB -✅ '37638' maps to 'F', in 240.428µs -⚠️ Random key '75802' is not present in DB -⚠️ Random key '80496' is not present in DB -✅ '42586' maps to 'T', in 224.29µs -✅ '25911' maps to 'u', in 250.494µs -✅ '15478' maps to 'S', in 257.656µs +# And then run this command on another terminal window. +$ cargo run --example client --profile optimized + +Connected to PIR server @ 127.0.0.1:8080 +Received setup data from PIR server +Generated query for key: [98, 97, 110, 97, 110, 97] +Sent query of length 200B +Received response of length 104B +Retrieved value: 'yellow' ``` diff --git a/chalametpir_server/README.md b/chalametpir_server/README.md index 7c6f747..00c25ee 100644 --- a/chalametpir_server/README.md +++ b/chalametpir_server/README.md @@ -30,6 +30,9 @@ use rand::prelude::*; use rand_chacha::ChaCha8Rng; fn main() { + // Can be either 3 or 4, denoting usage of 3-wise or 4-wise xor binary fuse filter for PIR server setup. + const ARITY: u32 = 3; + let mut rng = ChaCha8Rng::from_os_rng(); let mut seed_μ = [0u8; SEED_BYTE_LEN]; rng.fill_bytes(&mut seed_μ); @@ -38,7 +41,7 @@ fn main() { db.insert(b"key1", b"value1"); db.insert(b"key2", b"value2"); - let (server, hint_bytes, filter_param_bytes) = Server::setup::<3>(&seed_μ, db).expect("Server setup failed"); + let (server, hint_bytes, filter_param_bytes) = Server::setup::(&seed_μ, db).expect("Server setup failed"); // Start handling client PIR queries loop { diff --git a/chalametpir_server/src/lib.rs b/chalametpir_server/src/lib.rs index 3d9625e..ba8de42 100644 --- a/chalametpir_server/src/lib.rs +++ b/chalametpir_server/src/lib.rs @@ -39,6 +39,9 @@ //! use rand_chacha::ChaCha8Rng; //! //! fn main() { +//! // Can be either 3 or 4, denoting usage of 3-wise or 4-wise xor binary fuse filter for PIR server setup. +//! const ARITY; u32 = 3; +//! //! let mut rng = ChaCha8Rng::from_os_rng(); //! let mut seed_μ = [0u8; SEED_BYTE_LEN]; //! rng.fill_bytes(&mut seed_μ); @@ -47,7 +50,7 @@ //! db.insert(b"key1", b"value1"); //! db.insert(b"key2", b"value2"); //! -//! let (server, hint_bytes, filter_param_bytes) = Server::setup::<3>(&seed_μ, db).expect("Server setup failed"); +//! let (server, hint_bytes, filter_param_bytes) = Server::setup::(&seed_μ, db).expect("Server setup failed"); //! //! // Start handling client PIR queries //! loop { From 7f4f6e617d9ca6e3c1eca72ec4af3412a316c61d Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 10 May 2025 23:10:23 +0400 Subject: [PATCH 12/23] Add `wasm` feature for common dependency crate Signed-off-by: Anjan Roy --- chalametpir_common/Cargo.toml | 9 +- chalametpir_common/src/binary_fuse_filter.rs | 28 +++- chalametpir_common/src/matrix.rs | 144 +++++++++++++++++-- chalametpir_common/src/serialization.rs | 18 ++- chalametpir_common/src/utils.rs | 42 +++++- 5 files changed, 225 insertions(+), 16 deletions(-) diff --git a/chalametpir_common/Cargo.toml b/chalametpir_common/Cargo.toml index c28b2ca..03ea13c 100644 --- a/chalametpir_common/Cargo.toml +++ b/chalametpir_common/Cargo.toml @@ -20,9 +20,14 @@ categories = ["cryptography", "data-structures", "concurrency"] [dependencies] turboshake = "=0.4.1" -rand = "=0.9.1" -rand_chacha = "=0.9.0" +rand = { version = "=0.9.1", optional = true } +rand_chacha = { version = "=0.9.0", optional = true } rayon = "=1.10.0" +tinyrand = { version = "=0.5.0", optional = true } [dev-dependencies] test-case = "=3.3.1" + +[features] +wasm = ["dep:tinyrand"] +default = ["dep:rand", "dep:rand_chacha"] diff --git a/chalametpir_common/src/binary_fuse_filter.rs b/chalametpir_common/src/binary_fuse_filter.rs index 2fd23a8..7b8a6f3 100644 --- a/chalametpir_common/src/binary_fuse_filter.rs +++ b/chalametpir_common/src/binary_fuse_filter.rs @@ -1,10 +1,16 @@ -use super::{error::ChalametPIRError, params}; -use crate::branch_opt_util; -use rand::prelude::*; -use rand_chacha::ChaCha20Rng; use std::collections::HashMap; + +use super::{branch_opt_util, error::ChalametPIRError, params}; use turboshake::TurboShake128; +#[cfg(feature = "wasm")] +use tinyrand::{Rand, StdRand}; + +#[cfg(not(feature = "wasm"))] +use rand::prelude::*; +#[cfg(not(feature = "wasm"))] +use rand_chacha::ChaCha20Rng; + #[derive(Clone, Debug)] pub struct BinaryFuseFilter { pub seed: [u8; 32], @@ -87,10 +93,17 @@ impl BinaryFuseFilter { let mut ultimate_size = 0; let mut seed = [0u8; 32]; + + #[cfg(feature = "wasm")] + let mut rng = StdRand::default(); + #[cfg(not(feature = "wasm"))] let mut rng = ChaCha20Rng::from_os_rng(); for _ in 0..max_attempt_count { + #[cfg(not(feature = "wasm"))] rng.fill_bytes(&mut seed); + #[cfg(feature = "wasm")] + seed.fill_with(|| rng.next_u32() as u8); for (idx, val) in start_pos.iter_mut().enumerate() { *val = (((idx as u64) * (db_size as u64)) >> block_bits) as usize; @@ -289,10 +302,17 @@ impl BinaryFuseFilter { let mut ultimate_size = 0; let mut seed = [0u8; 32]; + + #[cfg(feature = "wasm")] + let mut rng = StdRand::default(); + #[cfg(not(feature = "wasm"))] let mut rng = ChaCha20Rng::from_os_rng(); for _ in 0..max_attempt_count { + #[cfg(not(feature = "wasm"))] rng.fill_bytes(&mut seed); + #[cfg(feature = "wasm")] + seed.fill_with(|| rng.next_u32() as u8); for (idx, val) in start_pos.iter_mut().enumerate().take(start_pos_len) { *val = (((idx as u64) * (db_size as u64)) >> block_bits) as usize; diff --git a/chalametpir_common/src/matrix.rs b/chalametpir_common/src/matrix.rs index 40b282e..4c8e418 100644 --- a/chalametpir_common/src/matrix.rs +++ b/chalametpir_common/src/matrix.rs @@ -1,18 +1,25 @@ +use std::{ + collections::HashMap, + ops::{Add, Index, IndexMut, Mul}, +}; + use crate::{ binary_fuse_filter, branch_opt_util, error::ChalametPIRError, params::{HASHED_KEY_BYTE_LEN, MAX_CIPHER_TEXT_BIT_LEN, MIN_CIPHER_TEXT_BIT_LEN, SEED_BYTE_LEN}, serialization, }; -use rand::prelude::*; -use rand_chacha::ChaCha8Rng; + use rayon::prelude::*; -use std::{ - collections::HashMap, - ops::{Add, Index, IndexMut, Mul}, -}; use turboshake::TurboShake128; +#[cfg(not(feature = "wasm"))] +use rand::prelude::*; +#[cfg(not(feature = "wasm"))] +use rand_chacha::ChaCha8Rng; +#[cfg(feature = "wasm")] +use tinyrand::{Rand, StdRand}; + #[cfg(test)] use std::ops::Neg; @@ -570,19 +577,32 @@ impl Matrix { const TERNARY_INTERVAL_SIZE: u32 = (u32::MAX - 2) / 3; const TERNARY_REJECTION_SAMPLING_MAX: u32 = TERNARY_INTERVAL_SIZE * 3; + #[cfg(feature = "wasm")] + let mut rng = StdRand::default(); + #[cfg(not(feature = "wasm"))] let mut rng = ChaCha8Rng::from_os_rng(); + let mut vec = Matrix::new(rows, cols)?; let num_elems = vec.num_elems(); let mut elem_idx = 0; while branch_opt_util::likely(elem_idx < num_elems) { + #[cfg(not(feature = "wasm"))] let mut val = rng.random::(); + #[cfg(feature = "wasm")] + let mut val = rng.next_u32(); + #[cfg(not(feature = "wasm"))] while branch_opt_util::unlikely(val > TERNARY_REJECTION_SAMPLING_MAX) { val = rng.random::(); } + #[cfg(feature = "wasm")] + while branch_opt_util::unlikely(val > TERNARY_REJECTION_SAMPLING_MAX) { + val = rng.next_u32(); + } + let ternary = if val <= TERNARY_INTERVAL_SIZE { 0 } else if val > TERNARY_INTERVAL_SIZE && val <= 2 * TERNARY_INTERVAL_SIZE { @@ -1092,6 +1112,8 @@ impl Neg for &Matrix { #[cfg(test)] mod test { + use std::collections::HashMap; + use crate::{ binary_fuse_filter::BinaryFuseFilter, error::ChalametPIRError, @@ -1100,10 +1122,16 @@ mod test { params::{MAX_CIPHER_TEXT_BIT_LEN, MIN_CIPHER_TEXT_BIT_LEN, SERVER_SETUP_MAX_ATTEMPT_COUNT}, utils::generate_random_kv_database, }; + + use test_case::test_case; + + #[cfg(feature = "wasm")] + use tinyrand::{Rand, RandRange, StdRand}; + + #[cfg(not(feature = "wasm"))] use rand::prelude::*; + #[cfg(not(feature = "wasm"))] use rand_chacha::ChaCha8Rng; - use std::collections::HashMap; - use test_case::test_case; #[test] fn encode_kv_database_using_3_wise_xor_filter_and_recover_values() { @@ -1112,13 +1140,23 @@ mod test { const MIN_NUM_KV_PAIRS: usize = 1usize << 8; const MAX_NUM_KV_PAIRS: usize = 1usize << 16; + #[cfg(feature = "wasm")] + let mut rng = StdRand::default(); + #[cfg(not(feature = "wasm"))] let mut rng = ChaCha8Rng::from_os_rng(); const NUM_TEST_ITERATIONS: usize = 100; let mut test_iter = 0; while test_iter < NUM_TEST_ITERATIONS { + #[cfg(feature = "wasm")] + let num_kv_pairs_in_db = rng.next_range(MIN_NUM_KV_PAIRS..(MAX_NUM_KV_PAIRS + 1)); + #[cfg(not(feature = "wasm"))] let num_kv_pairs_in_db = rng.random_range(MIN_NUM_KV_PAIRS..=MAX_NUM_KV_PAIRS); + + #[cfg(feature = "wasm")] + let mat_elem_bit_len = rng.next_range(MIN_CIPHER_TEXT_BIT_LEN..(MAX_CIPHER_TEXT_BIT_LEN + 1)); + #[cfg(not(feature = "wasm"))] let mat_elem_bit_len = rng.random_range(MIN_CIPHER_TEXT_BIT_LEN..=MAX_CIPHER_TEXT_BIT_LEN); let kv_db = generate_random_kv_database(num_kv_pairs_in_db); @@ -1151,13 +1189,23 @@ mod test { const MIN_NUM_KV_PAIRS: usize = 1usize << 8; const MAX_NUM_KV_PAIRS: usize = 1usize << 16; + #[cfg(feature = "wasm")] + let mut rng = StdRand::default(); + #[cfg(not(feature = "wasm"))] let mut rng = ChaCha8Rng::from_os_rng(); const NUM_TEST_ITERATIONS: usize = 100; let mut test_iter = 0; while test_iter < NUM_TEST_ITERATIONS { + #[cfg(feature = "wasm")] + let num_kv_pairs_in_db = rng.next_range(MIN_NUM_KV_PAIRS..(MAX_NUM_KV_PAIRS + 1)); + #[cfg(not(feature = "wasm"))] let num_kv_pairs_in_db = rng.random_range(MIN_NUM_KV_PAIRS..=MAX_NUM_KV_PAIRS); + + #[cfg(feature = "wasm")] + let mat_elem_bit_len = rng.next_range(MIN_CIPHER_TEXT_BIT_LEN..(MAX_CIPHER_TEXT_BIT_LEN + 1)); + #[cfg(not(feature = "wasm"))] let mat_elem_bit_len = rng.random_range(MIN_CIPHER_TEXT_BIT_LEN..=MAX_CIPHER_TEXT_BIT_LEN); let kv_db = generate_random_kv_database(num_kv_pairs_in_db); @@ -1230,15 +1278,29 @@ mod test { const MIN_MATRIX_DIM: u32 = 1; const MAX_MATRIX_DIM: u32 = 1024; + #[cfg(feature = "wasm")] + let mut rng = StdRand::default(); + #[cfg(not(feature = "wasm"))] let mut rng = ChaCha8Rng::from_os_rng(); let mut seed = [0u8; SEED_BYTE_LEN]; + + #[cfg(not(feature = "wasm"))] rng.fill_bytes(&mut seed); + #[cfg(feature = "wasm")] + seed.fill_with(|| rng.next_u32() as u8); let mut current_attempt_count = 0; while current_attempt_count < NUM_ATTEMPT_MATRIX_MULTIPLICATIONS { + #[cfg(not(feature = "wasm"))] let num_rows = rng.random_range(MIN_MATRIX_DIM..=MAX_MATRIX_DIM); + #[cfg(feature = "wasm")] + let num_rows = rng.next_range(MIN_MATRIX_DIM..(MAX_MATRIX_DIM + 1)); + + #[cfg(not(feature = "wasm"))] let num_cols = rng.random_range(MIN_MATRIX_DIM..=MAX_MATRIX_DIM); + #[cfg(feature = "wasm")] + let num_cols = rng.next_range(MIN_MATRIX_DIM..(MAX_MATRIX_DIM + 1)); let matrix_a = Matrix::generate_from_seed(num_rows, num_cols, &seed).expect("Matrix must be generated from seed"); let matrix_i = Matrix::identity(num_cols).expect("Identity matrix must be created"); @@ -1260,19 +1322,37 @@ mod test { const MIN_ROW_VECTOR_DIM: u32 = 1; const MAX_ROW_VECTOR_DIM: u32 = 1024; + #[cfg(feature = "wasm")] + let mut rng = StdRand::default(); + #[cfg(not(feature = "wasm"))] let mut rng = ChaCha8Rng::from_os_rng(); let mut seed = [0u8; SEED_BYTE_LEN]; + + #[cfg(not(feature = "wasm"))] rng.fill_bytes(&mut seed); + #[cfg(feature = "wasm")] + seed.fill_with(|| rng.next_u32() as u8); let mut current_attempt_count = 0; while current_attempt_count < NUM_ATTEMPT_VECTOR_MATRIX_MULTIPLICATIONS { let vec_num_rows = 1; + #[cfg(not(feature = "wasm"))] let vec_num_cols = rng.random_range(MIN_ROW_VECTOR_DIM..=MAX_ROW_VECTOR_DIM); + #[cfg(feature = "wasm")] + let vec_num_cols = rng.next_range(MIN_ROW_VECTOR_DIM..(MAX_ROW_VECTOR_DIM + 1)); + let mat_num_rows = vec_num_cols; + #[cfg(not(feature = "wasm"))] let mat_num_cols = rng.random_range(MIN_ROW_VECTOR_DIM..=MAX_ROW_VECTOR_DIM); + #[cfg(feature = "wasm")] + let mat_num_cols = rng.next_range(MIN_ROW_VECTOR_DIM..(MAX_ROW_VECTOR_DIM + 1)); + let mat_num_elems = (mat_num_rows * mat_num_cols) as usize; + #[cfg(not(feature = "wasm"))] let mat_elem_bit_len = rng.random_range(MIN_CIPHER_TEXT_BIT_LEN..=MAX_CIPHER_TEXT_BIT_LEN); + #[cfg(feature = "wasm")] + let mat_elem_bit_len = rng.next_range(MIN_CIPHER_TEXT_BIT_LEN..(MAX_CIPHER_TEXT_BIT_LEN + 1)); let row_vector = Matrix::generate_from_seed(vec_num_rows, vec_num_cols, &seed).expect("Row vector must be generated from seed"); let all_ones = Matrix::from_values(mat_num_rows, mat_num_cols, vec![1; mat_num_elems]).expect("Matrix of ones must be created"); @@ -1301,15 +1381,29 @@ mod test { const MIN_MATRIX_DIM: u32 = 1; const MAX_MATRIX_DIM: u32 = 1024; + #[cfg(feature = "wasm")] + let mut rng = StdRand::default(); + #[cfg(not(feature = "wasm"))] let mut rng = ChaCha8Rng::from_os_rng(); let mut seed = [0u8; SEED_BYTE_LEN]; + + #[cfg(not(feature = "wasm"))] rng.fill_bytes(&mut seed); + #[cfg(feature = "wasm")] + seed.fill_with(|| rng.next_u32() as u8); let mut current_attempt_count = 0; while current_attempt_count < NUM_ATTEMPT_MATRIX_ADDITIONS { + #[cfg(not(feature = "wasm"))] let num_rows = rng.random_range(MIN_MATRIX_DIM..=MAX_MATRIX_DIM); + #[cfg(feature = "wasm")] + let num_rows = rng.next_range(MIN_MATRIX_DIM..(MAX_MATRIX_DIM + 1)); + + #[cfg(not(feature = "wasm"))] let num_cols = rng.random_range(MIN_MATRIX_DIM..=MAX_MATRIX_DIM); + #[cfg(feature = "wasm")] + let num_cols = rng.next_range(MIN_MATRIX_DIM..(MAX_MATRIX_DIM + 1)); let matrix_a = Matrix::generate_from_seed(num_rows, num_cols, &seed).expect("Matrix must be generated from seed"); let matrix_neg_a = (-&matrix_a).expect("Must be able to negate matrix"); @@ -1357,15 +1451,29 @@ mod test { const MIN_MATRIX_DIM: u32 = 1; const MAX_MATRIX_DIM: u32 = 1024; + #[cfg(feature = "wasm")] + let mut rng = StdRand::default(); + #[cfg(not(feature = "wasm"))] let mut rng = ChaCha8Rng::from_os_rng(); let mut seed = [0u8; SEED_BYTE_LEN]; + + #[cfg(not(feature = "wasm"))] rng.fill_bytes(&mut seed); + #[cfg(feature = "wasm")] + seed.fill_with(|| rng.next_u32() as u8); let mut current_attempt_count = 0; while current_attempt_count < NUM_ATTEMPT_MATRIX_SERIALIZATIONS { + #[cfg(not(feature = "wasm"))] let num_rows = rng.random_range(MIN_MATRIX_DIM..=MAX_MATRIX_DIM); + #[cfg(feature = "wasm")] + let num_rows = rng.next_range(MIN_MATRIX_DIM..(MAX_MATRIX_DIM + 1)); + + #[cfg(not(feature = "wasm"))] let num_cols = rng.random_range(MIN_MATRIX_DIM..=MAX_MATRIX_DIM); + #[cfg(feature = "wasm")] + let num_cols = rng.next_range(MIN_MATRIX_DIM..(MAX_MATRIX_DIM + 1)); let matrix_a = Matrix::generate_from_seed(num_rows, num_cols, &seed).expect("Matrix must be generated from seed"); let matrix_a_bytes = matrix_a.to_bytes(); @@ -1416,14 +1524,24 @@ mod test { const MIN_NUM_KV_PAIRS: usize = 1_000; const MAX_NUM_KV_PAIRS: usize = 10_000; + #[cfg(feature = "wasm")] + let mut rng = StdRand::default(); + #[cfg(not(feature = "wasm"))] let mut rng = ChaCha8Rng::from_os_rng(); const NUM_TEST_ITERATIONS: usize = 100; let mut test_iter = 0; while test_iter < NUM_TEST_ITERATIONS { + #[cfg(not(feature = "wasm"))] let num_kv_pairs = rng.random_range(MIN_NUM_KV_PAIRS..=MAX_NUM_KV_PAIRS); + #[cfg(feature = "wasm")] + let num_kv_pairs = rng.next_range(MIN_NUM_KV_PAIRS..(MAX_NUM_KV_PAIRS + 1)); + + #[cfg(not(feature = "wasm"))] let mat_elem_bit_len = rng.random_range(MIN_CIPHER_TEXT_BIT_LEN..=MAX_CIPHER_TEXT_BIT_LEN); + #[cfg(feature = "wasm")] + let mat_elem_bit_len = rng.next_range(MIN_CIPHER_TEXT_BIT_LEN..(MAX_CIPHER_TEXT_BIT_LEN + 1)); let kv_db = generate_random_kv_database(num_kv_pairs); let kv_db_as_ref = kv_db.iter().map(|(k, v)| (k.as_slice(), v.as_slice())).collect::>(); @@ -1449,14 +1567,24 @@ mod test { const MIN_NUM_KV_PAIRS: usize = 1_000; const MAX_NUM_KV_PAIRS: usize = 10_000; + #[cfg(feature = "wasm")] + let mut rng = StdRand::default(); + #[cfg(not(feature = "wasm"))] let mut rng = ChaCha8Rng::from_os_rng(); const NUM_TEST_ITERATIONS: usize = 100; let mut test_iter = 0; while test_iter < NUM_TEST_ITERATIONS { + #[cfg(not(feature = "wasm"))] let num_kv_pairs = rng.random_range(MIN_NUM_KV_PAIRS..=MAX_NUM_KV_PAIRS); + #[cfg(feature = "wasm")] + let num_kv_pairs = rng.next_range(MIN_NUM_KV_PAIRS..(MAX_NUM_KV_PAIRS + 1)); + + #[cfg(not(feature = "wasm"))] let mat_elem_bit_len = rng.random_range(MIN_CIPHER_TEXT_BIT_LEN..=MAX_CIPHER_TEXT_BIT_LEN); + #[cfg(feature = "wasm")] + let mat_elem_bit_len = rng.next_range(MIN_CIPHER_TEXT_BIT_LEN..(MAX_CIPHER_TEXT_BIT_LEN + 1)); let kv_db = generate_random_kv_database(num_kv_pairs); let kv_db_as_ref = kv_db.iter().map(|(k, v)| (k.as_slice(), v.as_slice())).collect::>(); diff --git a/chalametpir_common/src/serialization.rs b/chalametpir_common/src/serialization.rs index 1b70bba..7bc6794 100644 --- a/chalametpir_common/src/serialization.rs +++ b/chalametpir_common/src/serialization.rs @@ -231,9 +231,15 @@ mod test { params, serialization::{decode_kv_from_row, encode_kv_as_row}, }; + use turboshake::TurboShake128; + + #[cfg(feature = "wasm")] + use tinyrand::{Rand, StdRand}; + + #[cfg(not(feature = "wasm"))] use rand::prelude::*; + #[cfg(not(feature = "wasm"))] use rand_chacha::ChaCha8Rng; - use turboshake::TurboShake128; #[test] fn encode_kv_as_row_and_recover() { @@ -246,6 +252,9 @@ mod test { const MIN_MAT_ELEM_BIT_LEN: usize = 7; const MAX_MAT_ELEM_BIT_LEN: usize = 11; + #[cfg(feature = "wasm")] + let mut rng = StdRand::default(); + #[cfg(not(feature = "wasm"))] let mut rng = ChaCha8Rng::from_os_rng(); for key_byte_len in MIN_KEY_BYTE_LEN..=MAX_KEY_BYTE_LEN { @@ -254,8 +263,15 @@ mod test { let mut key = vec![0u8; key_byte_len]; let mut value = vec![0u8; value_byte_len]; + #[cfg(not(feature = "wasm"))] rng.fill_bytes(&mut key); + #[cfg(feature = "wasm")] + key.fill_with(|| rng.next_u32() as u8); + + #[cfg(not(feature = "wasm"))] rng.fill_bytes(&mut value); + #[cfg(feature = "wasm")] + value.fill_with(|| rng.next_u32() as u8); let hashed_key = { let mut hasher = TurboShake128::default(); diff --git a/chalametpir_common/src/utils.rs b/chalametpir_common/src/utils.rs index 1ba5be7..4b3f465 100644 --- a/chalametpir_common/src/utils.rs +++ b/chalametpir_common/src/utils.rs @@ -1,6 +1,12 @@ +use std::collections::HashMap; + +#[cfg(feature = "wasm")] +use tinyrand::{Rand, RandRange, StdRand}; + +#[cfg(not(feature = "wasm"))] use rand::prelude::*; +#[cfg(not(feature = "wasm"))] use rand_chacha::ChaCha8Rng; -use std::collections::HashMap; /// Generates a random key-value database with the requested number of key-value pairs. /// @@ -12,6 +18,7 @@ use std::collections::HashMap; /// /// * `HashMap, Vec>` - A HashMap containing the generated key-value pairs. /// The keys and values are randomly generated byte arrays with lengths between fixed minimum and maximum values. +#[cfg(not(feature = "wasm"))] pub fn generate_random_kv_database(num_kv_pairs: usize) -> HashMap, Vec> { const MIN_KEY_BYTE_LEN: usize = 16; const MAX_KEY_BYTE_LEN: usize = 32; @@ -36,3 +43,36 @@ pub fn generate_random_kv_database(num_kv_pairs: usize) -> HashMap, Vec< kv } + +/// Generates a random key-value database with the requested number of key-value pairs. +/// +/// # Arguments +/// +/// * `num_kv_pairs` - The number of key-value pairs to generate. +/// +/// # Returns +/// +/// * `HashMap, Vec>` - A HashMap containing the generated key-value pairs. +/// The keys and values are randomly generated byte arrays with lengths between fixed minimum and maximum values. +#[cfg(feature = "wasm")] +pub fn generate_random_kv_database(num_kv_pairs: usize) -> HashMap, Vec> { + const MIN_KEY_BYTE_LEN: usize = 16; + const MAX_KEY_BYTE_LEN: usize = 32; + const MIN_VALUE_BYTE_LEN: usize = 1; + const MAX_VALUE_BYTE_LEN: usize = 512; + + let mut kv = HashMap::with_capacity(num_kv_pairs); + let mut rng = StdRand::default(); + + for _ in 0..num_kv_pairs { + let key_byte_len = rng.next_range(MIN_KEY_BYTE_LEN..(MAX_KEY_BYTE_LEN + 1)); + let value_byte_len = rng.next_range(MIN_VALUE_BYTE_LEN..(MAX_VALUE_BYTE_LEN + 1)); + + let key = (0..key_byte_len).map(|_| rng.next_u32() as u8).collect::>(); + let value = (0..value_byte_len).map(|_| rng.next_u32() as u8).collect::>(); + + kv.insert(key, value); + } + + kv +} From 1451bc27a37e32a548cb30217d08d67be275ec46 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 10 May 2025 23:12:18 +0400 Subject: [PATCH 13/23] Introduce new `wasm` feature in client crate Signed-off-by: Anjan Roy --- chalametpir_client/Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/chalametpir_client/Cargo.toml b/chalametpir_client/Cargo.toml index a934620..1cce4bf 100644 --- a/chalametpir_client/Cargo.toml +++ b/chalametpir_client/Cargo.toml @@ -20,10 +20,12 @@ keywords = [ categories = ["cryptography", "data-structures", "concurrency"] [dependencies] -chalametpir_common = { path = "../chalametpir_common", version = "=0.6.0" } +chalametpir_common = { path = "../chalametpir_common", version = "=0.6.0", default-features = false } [dev-dependencies] tokio = { version = "=1.45.0", features = ["full"] } [features] +wasm = ["chalametpir_common/wasm"] +default = ["chalametpir_common/default"] mutate_internal_client_state = [] From e91e719e2fc2d66c8ce94eaad4e8c7740cc178c2 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 10 May 2025 23:12:48 +0400 Subject: [PATCH 14/23] Reformat sources Signed-off-by: Anjan Roy --- chalametpir_server/src/gpu/gpu_utils.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/chalametpir_server/src/gpu/gpu_utils.rs b/chalametpir_server/src/gpu/gpu_utils.rs index 7d62db6..b4e3d44 100644 --- a/chalametpir_server/src/gpu/gpu_utils.rs +++ b/chalametpir_server/src/gpu/gpu_utils.rs @@ -1,21 +1,21 @@ pub use std::sync::Arc; pub use vulkano::{ + VulkanLibrary, buffer::Subbuffer, buffer::{Buffer, BufferCreateInfo, BufferUsage}, command_buffer::allocator::StandardCommandBufferAllocator, command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, CopyBufferInfo, PrimaryCommandBufferAbstract}, - descriptor_set::{allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet}, - device::{physical::PhysicalDeviceType, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, QueueFlags}, + descriptor_set::{DescriptorSet, WriteDescriptorSet, allocator::StandardDescriptorSetAllocator}, device::{Device, Queue}, + device::{DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, QueueFlags, physical::PhysicalDeviceType}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, memory::allocator::StandardMemoryAllocator, memory::allocator::{AllocationCreateInfo, MemoryTypeFilter}, pipeline::{ - compute::ComputePipelineCreateInfo, layout::PipelineDescriptorSetLayoutCreateInfo, ComputePipeline, Pipeline, PipelineBindPoint, PipelineLayout, - PipelineShaderStageCreateInfo, + ComputePipeline, Pipeline, PipelineBindPoint, PipelineLayout, PipelineShaderStageCreateInfo, compute::ComputePipelineCreateInfo, + layout::PipelineDescriptorSetLayoutCreateInfo, }, sync::GpuFuture, - VulkanLibrary, }; use super::{mat_transpose_shader, mat_x_mat_shader}; From 120f64241be221dfaa6bf486bd557b681a8789cc Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sun, 11 May 2025 15:40:59 +0400 Subject: [PATCH 15/23] Run tests for github actions CI windows-11 arm64 machines Signed-off-by: Anjan Roy --- .github/workflows/test_ci.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index 9dac301..2e80a16 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -19,10 +19,13 @@ jobs: # for available targets. os: [ ubuntu-latest, # x86-64 - ubuntu-24.04-arm, # aarch64 - macos-latest, # aarch64 + ubuntu-24.04-arm, # arm64 + macos-13, # x86_64 - windows-latest # x86_64 + macos-latest, # arm64 + + windows-latest, # x86_64 + windows-11-arm # arm64 ] steps: From d370604ed7415275663d1c9c7323bb554bb29898 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sun, 11 May 2025 15:43:00 +0400 Subject: [PATCH 16/23] Remove single file example Signed-off-by: Anjan Roy --- integrations/src/main.rs | 138 --------------------------------------- 1 file changed, 138 deletions(-) delete mode 100644 integrations/src/main.rs diff --git a/integrations/src/main.rs b/integrations/src/main.rs deleted file mode 100644 index 2018004..0000000 --- a/integrations/src/main.rs +++ /dev/null @@ -1,138 +0,0 @@ -use std::{collections::HashMap, time::Instant}; - -use chalametpir_client::Client; -use chalametpir_server::{SEED_BYTE_LEN, Server}; - -use rand::prelude::*; -use rand_chacha::ChaCha8Rng; -use unicode_xid::UnicodeXID; - -/// Generates a toy Key-Value database with a specified number of entries. -/// Each key is a usize, and each value is a randomly chosen Unicode character -/// that is either ASCII graphic, alphanumeric, and a valid Unicode identifier start. -/// -/// # Arguments -/// -/// * `rng` - A mutable reference to a ChaCha8Rng random number generator. This is used to generate random values. -/// -/// # Returns -/// -/// A HashMap containing the generated Key-Value pairs. -fn make_toy_kv_db(rng: &mut ChaCha8Rng) -> HashMap { - const NUM_DB_ENTRIES: usize = u16::MAX as usize + 1; - - (0..NUM_DB_ENTRIES) - .map(|db_entry_index| { - let c = loop { - let mut buf = [0u8; 4]; - rng.fill_bytes(&mut buf); - - let s = String::from_utf8_lossy(&buf); - if let Some(c) = s.chars().next() { - if (c.is_ascii_graphic() || c.is_alphanumeric()) && c.is_xid_start() { - break c; - } - } - }; - - (db_entry_index, c) - }) - .collect::>() -} - -fn format_bytes(bytes: usize) -> String { - let suffixes = ["B", "KB", "MB", "GB"]; - let mut index = 0; - let mut size = bytes as f64; - - while size >= 1024.0 && index < 3 { - size /= 1024.0; - index += 1; - } - - format!("{:.1}{}", size, suffixes[index]) -} - -fn main() { - const ARITY: u32 = 3; - - let mut rng = ChaCha8Rng::from_os_rng(); - - // Make a sample Key-Value database. - let kv_db = make_toy_kv_db(&mut rng); - let kv_db_as_bytes = kv_db - .iter() - .map(|(k, v)| (k.to_le_bytes(), v.encode_utf8(&mut [0u8; 4]).as_bytes().to_vec())) - .collect::>>(); - let kv_db_as_ref = kv_db_as_bytes.iter().map(|(k, v)| (k.as_slice(), v.as_slice())).collect::>(); - - let key_byte_len = std::mem::size_of_val(kv_db.keys().next().unwrap()); - let value_byte_len = std::mem::size_of_val(kv_db.values().next().unwrap()); - - println!("ChalametPIR:"); - println!("Number of entries in Key-Value Database : {}", kv_db.len()); - println!("Size of each key : {}", format_bytes(key_byte_len)); - println!("Size of each value : {}", format_bytes(value_byte_len)); - println!("Arity of Binary Fuse Filter : {}", ARITY); - - // Sample seed for producing public LWE matrix A. - let mut seed_μ = [0u8; SEED_BYTE_LEN]; - rng.fill_bytes(&mut seed_μ); - - // Setup PIR server, for given KV database. - let (server_handle, hint_bytes, filter_param_bytes) = Server::setup::(&seed_μ, kv_db_as_ref.clone()).expect("Server setup failed"); - - println!("Seed size : {}", format_bytes(seed_μ.len())); - println!("Hint size : {}", format_bytes(hint_bytes.len())); - println!("Filter parameters size : {}", format_bytes(filter_param_bytes.len())); - - // Setup a PIR client, given seed, hint bytes and filter param bytes, received from server. - let mut client_handle = Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes).expect("Client setup failed"); - - // Sample n -many random valid/ invalid keys and attempt to query them using PIR scheme. - // See if valid keys can be retrieved successfully. And absent keys can't be retrieved. - - let total_num_keys_to_be_queried = 20; - let mut num_keys_quried = 0; - while num_keys_quried < total_num_keys_to_be_queried { - let random_key = rng.random_range(0..kv_db.len() * 2); - let is_random_key_in_db = kv_db.contains_key(&random_key); - - let key_as_bytes = random_key.to_le_bytes(); - if let Ok(query) = client_handle.query(&key_as_bytes) { - if num_keys_quried == 0 { - println!("Query size : {}", format_bytes(query.len())); - } - - let respond_begin = Instant::now(); - if let Ok(response) = server_handle.respond(query.as_slice()) { - let respond_end = Instant::now(); - - if num_keys_quried == 0 { - println!("Response size : {}\n", format_bytes(response.len())); - } - - if let Ok(received_value_bytes) = client_handle.process_response(key_as_bytes.as_slice(), response.as_slice()) { - assert!(is_random_key_in_db); - let &expected_value = kv_db.get(&random_key).expect("Key must be present in the DB!"); - - let received_value = String::from_utf8_lossy(received_value_bytes.as_slice()).chars().next().unwrap(); - if received_value == expected_value { - println!("✅ '{}' maps to '{}', in {:?}", random_key, received_value, (respond_end - respond_begin)); - } else { - println!("🚫 Didn't receive expected value for key '{}'!", random_key); - } - } else { - assert!(!is_random_key_in_db); - println!("⚠️ Random key '{}' is not present in DB", random_key); - } - } else { - println!("⛔ Failed to receive a response for queried key '{}'", random_key); - } - } else { - println!("⛔ Failed to prepare a query for key '{}'", random_key); - } - - num_keys_quried += 1; - } -} From 28188da031e1e8ab02d1ba632cf62de20369c58f Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sun, 11 May 2025 15:44:40 +0400 Subject: [PATCH 17/23] In integrations lib crate move all `dependencies` to `dev-dependencies` section Signed-off-by: Anjan Roy --- integrations/Cargo.toml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/integrations/Cargo.toml b/integrations/Cargo.toml index eaec06c..b7270b9 100644 --- a/integrations/Cargo.toml +++ b/integrations/Cargo.toml @@ -10,20 +10,18 @@ repository = "https://github.com/itzmeanjan/ChalametPIR.git" license = "MPL-2.0" publish = false -[dependencies] +[dev-dependencies] +test-case = "=3.3.1" +divan = "=0.1.21" rand = "=0.9.1" rand_chacha = "=0.9.0" unicode-xid = "=0.2.6" +chalametpir_common = { path = "../chalametpir_common" } chalametpir_server = { path = "../chalametpir_server" } chalametpir_client = { path = "../chalametpir_client", features = [ "mutate_internal_client_state", ] } -[dev-dependencies] -test-case = "=3.3.1" -divan = "=0.1.21" -chalametpir_common = { path = "../chalametpir_common" } - [[bench]] name = "offline_phase" harness = false @@ -31,3 +29,4 @@ harness = false [[bench]] name = "online_phase" harness = false +required-features = ["chalametpir_client/mutate_internal_client_state"] From 8d504ee2dbe87f229e67fb212cc54efee08c8f6d Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sun, 11 May 2025 16:21:00 +0400 Subject: [PATCH 18/23] Update cargo config file to run tests for `wasm` target-family Signed-off-by: Anjan Roy --- .cargo/config.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index ddff440..05d9c7e 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,5 @@ -[build] +[target.wasm32-wasip1] +runner = "wasmtime" + +[target.'cfg(not(target_family = "wasm")))'] rustflags = ["-C", "target-cpu=native"] From dc8274bbc82caafc1fed96f6fd6cf8b94b7e09f0 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sun, 11 May 2025 16:37:32 +0400 Subject: [PATCH 19/23] Update github actions CI script to build and run tests targeting wasm environment Signed-off-by: Anjan Roy --- .github/workflows/test_ci.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index 2e80a16..37f61e5 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -38,5 +38,13 @@ jobs: - name: Build and Test on ${{ matrix.os }} run: cargo test --profile test-release - - name: Run Example on ${{ matrix.os }} - run: cargo run --example kw_pir --profile optimized + - name: Build ChalametPIR Client Crate for wasm32 target + run: | + rustup target add wasm32-unknown-unknown + cargo build -p chalametpir_client --target wasm32-unknown-unknown --features wasm --no-default-features --profile test-release + + - name: Run ChalametPIR Common Crate Tests on wasm32 target + run: | + rustup target add wasm32-wasip1 + curl https://wasmtime.dev/install.sh -sSf | bash + cargo test -p chalametpir_common --target wasm32-wasip1 --features wasm --no-default-features --profile test-release From 41671ec1081978b4a40cc15e9f012144564bf5a2 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sun, 11 May 2025 17:09:20 +0400 Subject: [PATCH 20/23] Fix typo in doc test Signed-off-by: Anjan Roy --- chalametpir_server/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chalametpir_server/src/lib.rs b/chalametpir_server/src/lib.rs index ba8de42..3fc4a2d 100644 --- a/chalametpir_server/src/lib.rs +++ b/chalametpir_server/src/lib.rs @@ -40,7 +40,7 @@ //! //! fn main() { //! // Can be either 3 or 4, denoting usage of 3-wise or 4-wise xor binary fuse filter for PIR server setup. -//! const ARITY; u32 = 3; +//! const ARITY: u32 = 3; //! //! let mut rng = ChaCha8Rng::from_os_rng(); //! let mut seed_μ = [0u8; SEED_BYTE_LEN]; From c6a1cc55efa4fd8471f2be93e9f656cacdd6bd7b Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sun, 11 May 2025 17:11:47 +0400 Subject: [PATCH 21/23] Install wasmtime using cargo Signed-off-by: Anjan Roy --- .github/workflows/test_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index 37f61e5..1828384 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -46,5 +46,5 @@ jobs: - name: Run ChalametPIR Common Crate Tests on wasm32 target run: | rustup target add wasm32-wasip1 - curl https://wasmtime.dev/install.sh -sSf | bash + cargo install wasmtime-cli --locked cargo test -p chalametpir_common --target wasm32-wasip1 --features wasm --no-default-features --profile test-release From dd5f14e6fe866905d1ed43e1e31ddb15e914a8ff Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sun, 11 May 2025 21:11:29 +0400 Subject: [PATCH 22/23] Final touch to all of the project documentation files Signed-off-by: Anjan Roy --- README.md | 13 +++++++++++-- chalametpir_client/README.md | 3 +++ chalametpir_common/README.md | 6 ++++++ chalametpir_server/README.md | 3 +++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c523772..5a8dd04 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Implemented by [chalametpir_server](./chalametpir_server) crate. * **`respond`:** Processes a client's encrypted query, returning an encrypted response vector. **Client:** -Implemented by [chalametpir_client](./chalametpir_client) crate. +Implemented by [chalametpir_client](./chalametpir_client) crate. PIR clients can run in-browser, by enabling `wasm` (Web Assembly) feature. * **`setup`:** Initializes the client using the seed, serialized hint matrix and filter parameters received from the server. * **`query`:** Generates an encrypted PIR query for a given key, which can be sent to server. @@ -100,6 +100,12 @@ cargo test --profile test-release # For testing if offloading to GPU works as expected. cargo test --features gpu --profile test-release + +# Testing chalametpir-common lib crate on web assembly target, using `wasmtime`. +# Note, chalametpir-server lib crate is not wasm friendly. +rustup target add wasm32-unknown-unknown wasm32-wasip1 +cargo install wasmtime-cli --locked +cargo test -p chalametpir_common --target wasm32-wasip1 --features wasm --no-default-features --profile test-release ``` @@ -136,7 +142,10 @@ cargo bench --features gpu --profile optimized --bench offline_phase -q server_s - For understanding how PIR server library crate `chalametpir_server` can be used, read [this](./chalametpir_server/README.md). - While for using PIR client library crate `chalametpir_client`, read [this](./chalametpir_client/README.md). -The constant parameter `ARITY` (3 or 4) in `Server::setup` controls the type of Binary Fuse Filter used to encode the KV database, which affects size of the query vector and the encoded database dimensions, stored in-memory server-side. This implementation should allow you to run PIR queries on a KV database with at max 2^42 (~4 trillion) number of entries. +The constant parameter `ARITY` (3 or 4) in `Server::setup` controls the type of Binary Fuse Filter used to encode the KV database, which affects size of the query vector and the encoded database dimensions, stored in-memory server-side. + +> [!IMPORTANT] +> This implementation should allow you to run PIR queries on a KV database with at max 2^42 (~4 trillion) number of entries. There doesn't exist any limit on how large each key or value needs to be. Keys and values can be of variable length. If values have variable length, database encoder routine pads it to the maximum value byte length present in that key-value database. I maintain two example binaries, implementing PIR server and client execution flow. diff --git a/chalametpir_client/README.md b/chalametpir_client/README.md index fdfba76..8be988e 100644 --- a/chalametpir_client/README.md +++ b/chalametpir_client/README.md @@ -49,5 +49,8 @@ fn main() { } ``` +> [!IMPORTANT] +> ChalametPIR clients can run in-browser, by enabling `wasm` feature. + > [!NOTE] > More documentation on ChalametPIR [here](../README.md). diff --git a/chalametpir_common/README.md b/chalametpir_common/README.md index 5291d83..ccdc720 100644 --- a/chalametpir_common/README.md +++ b/chalametpir_common/README.md @@ -11,3 +11,9 @@ This crate provides common utilities and data structures used by both the client > [!NOTE] > This crate is not supposed to be used by you on its own, rather it is a common dependency of both `chalametpir_server` and `chalametpir_client` crates. + +> [!IMPORTANT] +> This crate is Web Assembly environment friendly. So you can use it in wasm family of targets, by enabling `wasm` feature. + +> [!NOTE] +> More documentation on ChalametPIR [here](../README.md). diff --git a/chalametpir_server/README.md b/chalametpir_server/README.md index 00c25ee..166a5f6 100644 --- a/chalametpir_server/README.md +++ b/chalametpir_server/README.md @@ -61,5 +61,8 @@ fn main() { } ``` +> [!IMPORTANT] +> This crate allows you to offload compute-heavy server-setup phase to a GPU, if you enable `gpu` feature. + > [!NOTE] > More documentation on ChalametPIR [here](../README.md). From 32b724b1b5d0d714295a6a50c29727b866075b21 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sun, 11 May 2025 21:15:33 +0400 Subject: [PATCH 23/23] =?UTF-8?q?Bump=20version=20everywhere=20to=20`0.7.0?= =?UTF-8?q?`=20-=20ready=20to=20release=20=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anjan Roy --- chalametpir_client/Cargo.toml | 4 ++-- chalametpir_client/README.md | 2 +- chalametpir_client/src/lib.rs | 5 ++++- chalametpir_common/Cargo.toml | 2 +- chalametpir_server/Cargo.toml | 4 ++-- chalametpir_server/README.md | 2 +- chalametpir_server/src/lib.rs | 4 ++-- integrations/Cargo.toml | 2 +- 8 files changed, 14 insertions(+), 11 deletions(-) diff --git a/chalametpir_client/Cargo.toml b/chalametpir_client/Cargo.toml index 1cce4bf..2686cfb 100644 --- a/chalametpir_client/Cargo.toml +++ b/chalametpir_client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chalametpir_client" -version = "0.6.0" +version = "0.7.0" edition = "2024" resolver = "3" rust-version = "1.85.0" @@ -20,7 +20,7 @@ keywords = [ categories = ["cryptography", "data-structures", "concurrency"] [dependencies] -chalametpir_common = { path = "../chalametpir_common", version = "=0.6.0", default-features = false } +chalametpir_common = { path = "../chalametpir_common", version = "=0.7.0", default-features = false } [dev-dependencies] tokio = { version = "=1.45.0", features = ["full"] } diff --git a/chalametpir_client/README.md b/chalametpir_client/README.md index 8be988e..d6b6018 100644 --- a/chalametpir_client/README.md +++ b/chalametpir_client/README.md @@ -18,7 +18,7 @@ Key components: Add these dependencies to your `Cargo.toml`: ```toml -chalametpir_client = "=0.6.0" +chalametpir_client = "=0.7.0" ``` ```rust diff --git a/chalametpir_client/src/lib.rs b/chalametpir_client/src/lib.rs index d2236d1..e508f76 100644 --- a/chalametpir_client/src/lib.rs +++ b/chalametpir_client/src/lib.rs @@ -21,7 +21,10 @@ //! //! ```toml //! [dependencies] -//! chalametpir_client = "=0.6.0" +//! chalametpir_client = "=0.7.0" +//! +//! # Or, if you want to run the client on wasm environments. +//! # chalametpir_client = { version = "=0.7.0", features = "wasm", default-features = false} //! ``` //! //! Then, you can use it in your code: diff --git a/chalametpir_common/Cargo.toml b/chalametpir_common/Cargo.toml index 03ea13c..d71cce7 100644 --- a/chalametpir_common/Cargo.toml +++ b/chalametpir_common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chalametpir_common" -version = "0.6.0" +version = "0.7.0" edition = "2024" resolver = "3" rust-version = "1.85.0" diff --git a/chalametpir_server/Cargo.toml b/chalametpir_server/Cargo.toml index 5126bc3..01bf2ed 100644 --- a/chalametpir_server/Cargo.toml +++ b/chalametpir_server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chalametpir_server" -version = "0.6.0" +version = "0.7.0" edition = "2024" resolver = "3" rust-version = "1.85.0" @@ -21,7 +21,7 @@ keywords = [ categories = ["cryptography", "data-structures", "concurrency"] [dependencies] -chalametpir_common = { path = "../chalametpir_common", version = "=0.6.0" } +chalametpir_common = { path = "../chalametpir_common", version = "=0.7.0" } vulkano = { version = "=0.35.1", optional = true } vulkano-shaders = { version = "=0.35.0", optional = true } diff --git a/chalametpir_server/README.md b/chalametpir_server/README.md index 166a5f6..b45d3dd 100644 --- a/chalametpir_server/README.md +++ b/chalametpir_server/README.md @@ -18,7 +18,7 @@ Add these dependencies to your `Cargo.toml`: ```toml rand = "=0.9.1" rand_chacha = "=0.9.0" -chalametpir_server = "=0.6.0" +chalametpir_server = "=0.7.0" ``` ```rust diff --git a/chalametpir_server/src/lib.rs b/chalametpir_server/src/lib.rs index 3fc4a2d..23039ed 100644 --- a/chalametpir_server/src/lib.rs +++ b/chalametpir_server/src/lib.rs @@ -20,9 +20,9 @@ //! //! ```toml //! [dependencies] -//! chalametpir_server = "=0.6.0" +//! chalametpir_server = "=0.7.0" //! # Or, if you want to offload server-setup to GPU. -//! # chalametpir_server = { version = "=0.6.0", features = ["gpu"] } +//! # chalametpir_server = { version = "=0.7.0", features = ["gpu"] } //! //! rand = "=0.9.1" //! rand_chacha = "=0.9.0" diff --git a/integrations/Cargo.toml b/integrations/Cargo.toml index b7270b9..03c9bd5 100644 --- a/integrations/Cargo.toml +++ b/integrations/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chalametpir_integrations" -version = "0.6.0" +version = "0.7.0" edition = "2024" resolver = "3" rust-version = "1.85.0"