Skip to content

Commit b1cdc91

Browse files
authored
Merge pull request #13 from itzmeanjan/adopt-cargo-workspace
Add support for running ChalametPIR Clients on WASM target
2 parents 7f469fa + 32b724b commit b1cdc91

39 files changed

Lines changed: 1014 additions & 553 deletions

.cargo/config.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1-
[build]
1+
[target.wasm32-wasip1]
2+
runner = "wasmtime"
3+
4+
[target.'cfg(not(target_family = "wasm")))']
25
rustflags = ["-C", "target-cpu=native"]

.github/workflows/test_ci.yml

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ jobs:
1919
# for available targets.
2020
os: [
2121
ubuntu-latest, # x86-64
22-
ubuntu-24.04-arm, # aarch64
23-
macos-latest, # aarch64
22+
ubuntu-24.04-arm, # arm64
23+
2424
macos-13, # x86_64
25-
windows-latest # x86_64
25+
macos-latest, # arm64
26+
27+
windows-latest, # x86_64
28+
windows-11-arm # arm64
2629
]
2730

2831
steps:
@@ -35,5 +38,13 @@ jobs:
3538
- name: Build and Test on ${{ matrix.os }}
3639
run: cargo test --profile test-release
3740

38-
- name: Run Example on ${{ matrix.os }}
39-
run: cargo run --example kw_pir --profile optimized
41+
- name: Build ChalametPIR Client Crate for wasm32 target
42+
run: |
43+
rustup target add wasm32-unknown-unknown
44+
cargo build -p chalametpir_client --target wasm32-unknown-unknown --features wasm --no-default-features --profile test-release
45+
46+
- name: Run ChalametPIR Common Crate Tests on wasm32 target
47+
run: |
48+
rustup target add wasm32-wasip1
49+
cargo install wasmtime-cli --locked
50+
cargo test -p chalametpir_common --target wasm32-wasip1 --features wasm --no-default-features --profile test-release

Cargo.toml

Lines changed: 7 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,11 @@
1-
[package]
2-
name = "chalamet_pir"
3-
version = "0.6.0"
4-
edition = "2024"
5-
resolver = "2"
6-
rust-version = "1.85.0"
7-
authors = ["Anjan Roy <hello@itzmeanjan.in>"]
8-
description = "Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases"
9-
readme = "README.md"
10-
repository = "https://github.com/itzmeanjan/ChalametPIR.git"
11-
license = "MPL-2.0"
12-
keywords = [
13-
"priv-info-retrieval",
14-
"lwe-pir",
15-
"frodo-pir",
16-
"chalamet-pir",
17-
"gpu",
1+
[workspace]
2+
members = [
3+
"chalametpir_client",
4+
"chalametpir_common",
5+
"chalametpir_server",
6+
"integrations",
187
]
19-
categories = ["cryptography", "data-structures", "concurrency"]
20-
21-
[dependencies]
22-
turboshake = "=0.4.1"
23-
rayon = "=1.10.0"
24-
rand = "=0.9.1"
25-
rand_chacha = "=0.9.0"
26-
vulkano = { version = "=0.35.1", optional = true }
27-
vulkano-shaders = { version = "=0.35.0", optional = true }
28-
29-
[dev-dependencies]
30-
test-case = "=3.3.1"
31-
divan = "=0.1.21"
32-
unicode-xid = "=0.2.6"
33-
34-
[[bench]]
35-
name = "offline_phase"
36-
harness = false
37-
38-
[[bench]]
39-
name = "online_phase"
40-
harness = false
41-
required-features = ["mutate_internal_client_state"]
42-
43-
[features]
44-
mutate_internal_client_state = []
45-
gpu = ["dep:vulkano", "dep:vulkano-shaders"]
8+
resolver = "3"
469

4710
[profile.optimized]
4811
inherits = "release"

README.md

Lines changed: 38 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@ ChalametPIR allows a client to retrieve a specific value from a key-value databa
1414
The protocol has two participants:
1515

1616
**Server:**
17+
Implemented by [chalametpir_server](./chalametpir_server) crate.
18+
1719
* **`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.
1820
* **`respond`:** Processes a client's encrypted query, returning an encrypted response vector.
1921

2022
**Client:**
23+
Implemented by [chalametpir_client](./chalametpir_client) crate. PIR clients can run in-browser, by enabling `wasm` (Web Assembly) feature.
24+
2125
* **`setup`:** Initializes the client using the seed, serialized hint matrix and filter parameters received from the server.
2226
* **`query`:** Generates an encrypted PIR query for a given key, which can be sent to server.
2327
* **`process_response`:** Decrypts the server's response and extracts the requested value.
@@ -82,7 +86,7 @@ rustc 1.85.1 (e71f9a9a9 2025-01-27)
8286
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.
8387

8488
## Testing
85-
The `chalamet_pir` library includes comprehensive tests to ensure functional correctness.
89+
The ChalametPIR library includes comprehensive tests to ensure functional correctness.
8690

8791
- **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`.
8892
- **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).
@@ -96,6 +100,12 @@ cargo test --profile test-release
96100

97101
# For testing if offloading to GPU works as expected.
98102
cargo test --features gpu --profile test-release
103+
104+
# Testing chalametpir-common lib crate on web assembly target, using `wasmtime`.
105+
# Note, chalametpir-server lib crate is not wasm friendly.
106+
rustup target add wasm32-unknown-unknown wasm32-wasip1
107+
cargo install wasmtime-cli --locked
108+
cargo test -p chalametpir_common --target wasm32-wasip1 --features wasm --no-default-features --profile test-release
99109
```
100110

101111

@@ -105,9 +115,8 @@ Performance benchmarks are included to evaluate the efficiency of the PIR scheme
105115
To run the benchmarks, execute the following command from the root of the project:
106116

107117
```bash
108-
# For benchmarking the online phase of the PIR,
109-
# you need to enable feature `mutate_internal_client_state`.
110-
cargo bench --features mutate_internal_client_state --profile optimized
118+
# Run all benchmarks.
119+
cargo bench --profile optimized
111120

112121
# For benchmarking only the server-setup phase, offloaded to the GPU.
113122
cargo bench --features gpu --profile optimized --bench offline_phase -q server_setup
@@ -129,135 +138,37 @@ cargo bench --features gpu --profile optimized --bench offline_phase -q server_s
129138
> More about AWS EC2 instances @ https://aws.amazon.com/ec2/instance-types.
130139
131140
## Usage
132-
First, add this library crate as a dependency in your Cargo.toml file.
133-
134-
```toml
135-
[dependencies]
136-
chalamet_pir = "=0.6.0"
137-
# Or, if you want to offload server-setup to a GPU.
138-
# chalamet_pir = { version = "=0.6.0", features = ["gpu"] }
139-
rand = "=0.9.0"
140-
rand_chacha = "=0.9.0"
141-
```
142141

143-
Then, let's code a very simple keyword PIR scheme:
144-
145-
```rust
146-
use chalamet_pir::{client::Client, server::Server, SEED_BYTE_LEN};
147-
use rand::prelude::*;
148-
use rand_chacha::ChaCha8Rng;
149-
use std::collections::HashMap;
150-
151-
fn main() {
152-
// Example database (replace with your own)
153-
let mut db: HashMap<&[u8], &[u8]> = HashMap::new();
154-
db.insert(b"apple", b"red");
155-
db.insert(b"banana", b"yellow");
156-
157-
// Server setup (offline phase)
158-
let mut rng = ChaCha8Rng::from_os_rng();
159-
let mut seed_μ = [0u8; SEED_BYTE_LEN]; // You'll want to generate a cryptographically secure random seed
160-
rng.fill_bytes(&mut seed_μ);
161-
162-
let (server, hint_bytes, filter_param_bytes) = Server::setup::<3>(&seed_μ, db.clone()).expect("Server setup failed");
163-
164-
// Client setup (offline phase)
165-
let mut client = Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes).expect("Client setup failed");
166-
167-
// Client query (online phase)
168-
let key = b"banana";
169-
if let Ok(query) = client.query(key) {
170-
// Send `query` to the server
171-
172-
// Server response (online phase)
173-
let response = server.respond(&query).expect("Server failed to respond");
174-
175-
// Client processes the response (online phase)
176-
if let Ok(value) = client.process_response(key, &response) {
177-
println!("Retrieved value: '{}'", String::from_utf8_lossy(&value)); // Should print "yellow"
178-
} else {
179-
println!("Failed to retrieve value.");
180-
}
181-
} else {
182-
println!("Failed to generate query.");
183-
}
184-
}
185-
```
142+
- For understanding how PIR server library crate `chalametpir_server` can be used, read [this](./chalametpir_server/README.md).
143+
- While for using PIR client library crate `chalametpir_client`, read [this](./chalametpir_client/README.md).
144+
145+
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.
186146

187-
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.
147+
> [!IMPORTANT]
148+
> 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.
188149
189-
I maintain one example [program](./examples/kw_pir.rs) which demonstrates usage of the ChalametPIR API.
150+
I maintain two example binaries, implementing PIR server and client execution flow.
190151

191152
```bash
192-
cargo run --example kw_pir --profile optimized
153+
# First, issue following command on one terminal window.
154+
$ cargo run --example server --profile optimized
155+
156+
PIR Server listening @ 127.0.0.1:8080
157+
New connection from PIR client @ 127.0.0.1:43322
158+
Sent setup data to PIR client @ 127.0.0.1:43322
159+
Received query of length 200B, from PIR client @ 127.0.0.1:43322
160+
Sent response of length 104B, to PIR client @ 127.0.0.1:43322
161+
...
193162
```
194163

195164
```bash
196-
# Using 3-wise XOR Binary Fuse Filter
197-
ChalametPIR:
198-
Number of entries in Key-Value Database : 65536
199-
Size of each key : 8.0B
200-
Size of each value : 4.0B
201-
Arity of Binary Fuse Filter : 3
202-
Seed size : 32.0B
203-
Hint size : 207.9KB
204-
Filter parameters size : 68.0B
205-
Query size : 304.0KB
206-
Response size : 128.0B
207-
208-
'64187' maps to 'b', in 274.995µs
209-
⚠️ Random key '112599' is not present in DB
210-
⚠️ Random key '108662' is not present in DB
211-
⚠️ Random key '79395' is not present in DB
212-
⚠️ Random key '72638' is not present in DB
213-
⚠️ Random key '123690' is not present in DB
214-
⚠️ Random key '69344' is not present in DB
215-
⚠️ Random key '69155' is not present in DB
216-
'5918' maps to 'J', in 165.606µs
217-
⚠️ Random key '128484' is not present in DB
218-
⚠️ Random key '79290' is not present in DB
219-
⚠️ Random key '104015' is not present in DB
220-
⚠️ Random key '111256' is not present in DB
221-
⚠️ Random key '124342' is not present in DB
222-
⚠️ Random key '74982' is not present in DB
223-
⚠️ Random key '93082' is not present in DB
224-
'32800' maps to 'b', in 233.29µs
225-
'20236' maps to 'Q', in 233.531µs
226-
'47334' maps to 'p', in 223.548µs
227-
'12225' maps to 'U', in 209.217µs
228-
229-
# --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
230-
231-
# Using 4-wise XOR Binary Fuse Filter
232-
ChalametPIR:
233-
Number of entries in Key-Value Database : 65536
234-
Size of each key : 8.0B
235-
Size of each value : 4.0B
236-
Arity of Binary Fuse Filter : 4
237-
Seed size : 32.0B
238-
Hint size : 207.9KB
239-
Filter parameters size : 68.0B
240-
Query size : 292.0KB
241-
Response size : 128.0B
242-
243-
'13239' maps to 'T', in 241.21µs
244-
⚠️ Random key '112983' is not present in DB
245-
⚠️ Random key '89821' is not present in DB
246-
'63385' maps to 'I', in 188.06µs
247-
⚠️ Random key '123914' is not present in DB
248-
⚠️ Random key '119919' is not present in DB
249-
⚠️ Random key '72903' is not present in DB
250-
⚠️ Random key '93634' is not present in DB
251-
⚠️ Random key '68582' is not present in DB
252-
'55692' maps to 'n', in 359.112µs
253-
⚠️ Random key '68191' is not present in DB
254-
⚠️ Random key '92762' is not present in DB
255-
'997' maps to 'v', in 302.626µs
256-
⚠️ Random key '123011' is not present in DB
257-
'37638' maps to 'F', in 240.428µs
258-
⚠️ Random key '75802' is not present in DB
259-
⚠️ Random key '80496' is not present in DB
260-
'42586' maps to 'T', in 224.29µs
261-
'25911' maps to 'u', in 250.494µs
262-
'15478' maps to 'S', in 257.656µs
165+
# And then run this command on another terminal window.
166+
$ cargo run --example client --profile optimized
167+
168+
Connected to PIR server @ 127.0.0.1:8080
169+
Received setup data from PIR server
170+
Generated query for key: [98, 97, 110, 97, 110, 97]
171+
Sent query of length 200B
172+
Received response of length 104B
173+
Retrieved value: 'yellow'
263174
```

chalametpir_client/Cargo.toml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[package]
2+
name = "chalametpir_client"
3+
version = "0.7.0"
4+
edition = "2024"
5+
resolver = "3"
6+
rust-version = "1.85.0"
7+
authors = ["Anjan Roy <hello@itzmeanjan.in>"]
8+
description = "Client Implementation of ChalametPIR: Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases"
9+
readme = "README.md"
10+
repository = "https://github.com/itzmeanjan/ChalametPIR.git"
11+
license = "MPL-2.0"
12+
keywords = [
13+
"priv-info-retrieval",
14+
"lwe-pir",
15+
"frodo-pir",
16+
"chalamet-pir",
17+
"pir-server",
18+
"key-value-databases",
19+
]
20+
categories = ["cryptography", "data-structures", "concurrency"]
21+
22+
[dependencies]
23+
chalametpir_common = { path = "../chalametpir_common", version = "=0.7.0", default-features = false }
24+
25+
[dev-dependencies]
26+
tokio = { version = "=1.45.0", features = ["full"] }
27+
28+
[features]
29+
wasm = ["chalametpir_common/wasm"]
30+
default = ["chalametpir_common/default"]
31+
mutate_internal_client_state = []

chalametpir_client/README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# ChalametPIR Client
2+
3+
Client Implementation of ChalametPIR: Simple, Stateful, Single-Server Private Information Retrieval for Key-Value Databases.
4+
5+
This crate provides the client-side implementation for the ChalametPIR protocol. It includes functionality for:
6+
7+
- Setting up the PIR client with parameters received from the server.
8+
- Generating private information retrieval (PIR) queries for specific keys.
9+
- Processing responses received from the server to recover the desired value.
10+
11+
Key components:
12+
13+
- `Client`: The main struct for interacting with the PIR client. It handles query generation and response processing.
14+
- `Query`: Represents a PIR query, containing the secret vector needed to recover the value from the server's response.
15+
16+
## Usage Example
17+
18+
Add these dependencies to your `Cargo.toml`:
19+
20+
```toml
21+
chalametpir_client = "=0.7.0"
22+
```
23+
24+
```rust
25+
use chalametpir_client::{Client, SEED_BYTE_LEN};
26+
27+
fn main() {
28+
// Assume seed, hint_bytes and filter_param_bytes are received from the PIR server
29+
let seed_μ = [0u8; SEED_BYTE_LEN];
30+
let hint_bytes = vec![0u8; 0];
31+
let filter_param_bytes = vec![0u8; 0];
32+
33+
match Client::setup(&seed_μ, &hint_bytes, &filter_param_bytes) {
34+
Ok(mut client) => {
35+
let key = b"example_key";
36+
if let Ok(query) = client.query(key) {
37+
println!("Generated query for key: {:?}", key);
38+
// Send query to PIR server
39+
let response = vec![0u8; 0];
40+
if let Ok(value) = client.process_response(key, &response) {
41+
println!("Received response {:?}", response);
42+
}
43+
}
44+
}
45+
Err(err) => {
46+
println!("Client setup failed: {}", err);
47+
}
48+
};
49+
}
50+
```
51+
52+
> [!IMPORTANT]
53+
> ChalametPIR clients can run in-browser, by enabling `wasm` feature.
54+
55+
> [!NOTE]
56+
> More documentation on ChalametPIR [here](../README.md).

0 commit comments

Comments
 (0)