Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9581baa
Adopt cargo workspace
itzmeanjan May 10, 2025
4c6417e
Update Cargo.toml files of workspace members
itzmeanjan May 10, 2025
9931942
Add example PIR server using tokio async runtime
itzmeanjan May 10, 2025
530ed55
Add example PIR client using tokio async runtime
itzmeanjan May 10, 2025
1e0b939
Add documentation for chalametpir client crate
itzmeanjan May 10, 2025
de99f71
Add documentation for chalametpir server crate
itzmeanjan May 10, 2025
094f1e0
Add documentation for chalametpir common crate
itzmeanjan May 10, 2025
c04c0bd
Mention chalamet-pir workspace root readme on member crate readme
itzmeanjan May 10, 2025
2016403
Change workspace member crate names
itzmeanjan May 10, 2025
f7b1715
Address doc test failure for chalametpir-client
itzmeanjan May 10, 2025
a12829c
Update workspace root README file with latest project details
itzmeanjan May 10, 2025
7f4f6e6
Add `wasm` feature for common dependency crate
itzmeanjan May 10, 2025
1451bc2
Introduce new `wasm` feature in client crate
itzmeanjan May 10, 2025
e91e719
Reformat sources
itzmeanjan May 10, 2025
120f642
Run tests for github actions CI windows-11 arm64 machines
itzmeanjan May 11, 2025
d370604
Remove single file example
itzmeanjan May 11, 2025
28188da
In integrations lib crate move all `dependencies` to `dev-dependencie…
itzmeanjan May 11, 2025
8d504ee
Update cargo config file to run tests for `wasm` target-family
itzmeanjan May 11, 2025
dc8274b
Update github actions CI script to build and run tests targeting wasm…
itzmeanjan May 11, 2025
41671ec
Fix typo in doc test
itzmeanjan May 11, 2025
c6a1cc5
Install wasmtime using cargo
itzmeanjan May 11, 2025
dd5f14e
Final touch to all of the project documentation files
itzmeanjan May 11, 2025
32b724b
Bump version everywhere to `0.7.0` - ready to release 🎉
itzmeanjan May 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
[build]
[target.wasm32-wasip1]
runner = "wasmtime"

[target.'cfg(not(target_family = "wasm")))']
rustflags = ["-C", "target-cpu=native"]
21 changes: 16 additions & 5 deletions .github/workflows/test_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -35,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
cargo install wasmtime-cli --locked
cargo test -p chalametpir_common --target wasm32-wasip1 --features wasm --no-default-features --profile test-release
51 changes: 7 additions & 44 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,48 +1,11 @@
[package]
name = "chalamet_pir"
version = "0.6.0"
edition = "2024"
resolver = "2"
rust-version = "1.85.0"
authors = ["Anjan Roy <hello@itzmeanjan.in>"]
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 = [
"chalametpir_client",
"chalametpir_common",
"chalametpir_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"
Expand Down
165 changes: 38 additions & 127 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. 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.
* **`process_response`:** Decrypts the server's response and extracts the requested value.
Expand Down Expand Up @@ -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 `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).
Expand All @@ -96,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
```


Expand All @@ -105,9 +115,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
Expand All @@ -129,135 +138,37 @@ 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]
chalamet_pir = "=0.6.0"
# Or, if you want to offload server-setup to a GPU.
# chalamet_pir = { 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 chalamet_pir::{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.

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.
> [!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 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'
```
31 changes: 31 additions & 0 deletions chalametpir_client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[package]
name = "chalametpir_client"
version = "0.7.0"
edition = "2024"
resolver = "3"
rust-version = "1.85.0"
authors = ["Anjan Roy <hello@itzmeanjan.in>"]
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 = [
"priv-info-retrieval",
"lwe-pir",
"frodo-pir",
"chalamet-pir",
"pir-server",
"key-value-databases",
]
categories = ["cryptography", "data-structures", "concurrency"]

[dependencies]
chalametpir_common = { path = "../chalametpir_common", version = "=0.7.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 = []
56 changes: 56 additions & 0 deletions chalametpir_client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# 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
chalametpir_client = "=0.7.0"
```

```rust
use chalametpir_client::{Client, SEED_BYTE_LEN};

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];

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);
}
};
}
```

> [!IMPORTANT]
> ChalametPIR clients can run in-browser, by enabling `wasm` feature.

> [!NOTE]
> More documentation on ChalametPIR [here](../README.md).
Loading