- Implement split-blinding protocol with C, C_r dual evaluation - Add 7 security proof tests for unlinkability properties - Add benchmarks: ~101µs (109x faster than OT-based) - Note: Server can compute C - C_r fingerprint (documented limitation)
231 lines
7.7 KiB
Rust
231 lines
7.7 KiB
Rust
//! Benchmarks comparing Ring-LPR OPRF (OT-based) vs Fast OPRF (OT-free)
|
|
//!
|
|
//! Run with: cargo bench
|
|
|
|
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
|
|
use rand::SeedableRng;
|
|
use rand_chacha::ChaCha20Rng;
|
|
|
|
use opaque_lattice::oprf::fast_oprf::{
|
|
PublicParams, ServerKey, client_blind as fast_client_blind, client_finalize as fast_finalize,
|
|
evaluate as fast_evaluate, server_evaluate as fast_server_evaluate,
|
|
};
|
|
use opaque_lattice::oprf::ring_lpr::{
|
|
RingLprKey, client_blind as lpr_client_blind, client_finalize as lpr_finalize,
|
|
server_evaluate as lpr_server_evaluate,
|
|
};
|
|
use opaque_lattice::oprf::unlinkable_oprf::{
|
|
UnlinkablePublicParams, UnlinkableServerKey, client_blind_unlinkable,
|
|
client_finalize_unlinkable, evaluate_unlinkable, server_evaluate_unlinkable,
|
|
};
|
|
|
|
/// Benchmark Fast OPRF (OT-free) - full protocol
|
|
fn bench_fast_oprf(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("fast_oprf");
|
|
|
|
let pp = PublicParams::generate(b"benchmark-params");
|
|
let key = ServerKey::generate(&pp, b"benchmark-key");
|
|
let password = b"benchmark-password-12345";
|
|
|
|
// Benchmark client blind
|
|
group.bench_function("client_blind", |b| {
|
|
b.iter(|| fast_client_blind(&pp, password))
|
|
});
|
|
|
|
// Benchmark server evaluate
|
|
let (state, blinded) = fast_client_blind(&pp, password);
|
|
group.bench_function("server_evaluate", |b| {
|
|
b.iter(|| fast_server_evaluate(&key, &blinded))
|
|
});
|
|
|
|
// Benchmark client finalize
|
|
let response = fast_server_evaluate(&key, &blinded);
|
|
group.bench_function("client_finalize", |b| {
|
|
let state = state.clone();
|
|
b.iter(|| fast_finalize(&state, key.public_key(), &response))
|
|
});
|
|
|
|
// Benchmark full protocol
|
|
group.bench_function("full_protocol", |b| {
|
|
b.iter(|| fast_evaluate(&pp, &key, password))
|
|
});
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark Ring-LPR OPRF (OT-based) - full protocol
|
|
fn bench_ring_lpr_oprf(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("ring_lpr_oprf");
|
|
|
|
let mut rng = ChaCha20Rng::seed_from_u64(12345);
|
|
let key = RingLprKey::generate(&mut rng);
|
|
let password = b"benchmark-password-12345";
|
|
|
|
// Benchmark client blind
|
|
group.bench_function("client_blind", |b| {
|
|
let mut rng = ChaCha20Rng::seed_from_u64(99999);
|
|
b.iter(|| lpr_client_blind(&mut rng, password))
|
|
});
|
|
|
|
// Benchmark server evaluate
|
|
let mut rng2 = ChaCha20Rng::seed_from_u64(88888);
|
|
let (state, blinded) = lpr_client_blind(&mut rng2, password).unwrap();
|
|
group.bench_function("server_evaluate", |b| {
|
|
b.iter(|| lpr_server_evaluate(&key, &blinded))
|
|
});
|
|
|
|
// Benchmark client finalize
|
|
let evaluated = lpr_server_evaluate(&key, &blinded).unwrap();
|
|
group.bench_function("client_finalize", |b| {
|
|
let mut rng = ChaCha20Rng::seed_from_u64(77777);
|
|
let (state, _) = lpr_client_blind(&mut rng, password).unwrap();
|
|
b.iter(|| {
|
|
// Need to re-create state each time since finalize consumes it
|
|
let mut rng = ChaCha20Rng::seed_from_u64(77777);
|
|
let (state, _) = lpr_client_blind(&mut rng, password).unwrap();
|
|
lpr_finalize(state, &evaluated)
|
|
})
|
|
});
|
|
|
|
// Benchmark full protocol
|
|
group.bench_function("full_protocol", |b| {
|
|
let mut rng = ChaCha20Rng::seed_from_u64(66666);
|
|
b.iter(|| {
|
|
let (state, blinded) = lpr_client_blind(&mut rng, password).unwrap();
|
|
let evaluated = lpr_server_evaluate(&key, &blinded).unwrap();
|
|
lpr_finalize(state, &evaluated)
|
|
})
|
|
});
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Compare all three protocols side-by-side
|
|
fn bench_comparison(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("oprf_comparison");
|
|
|
|
let pp = PublicParams::generate(b"benchmark-params");
|
|
let fast_key = ServerKey::generate(&pp, b"benchmark-key");
|
|
|
|
let unlink_pp = UnlinkablePublicParams::generate(b"benchmark-params");
|
|
let unlink_key = UnlinkableServerKey::generate(&unlink_pp, b"benchmark-key");
|
|
|
|
let mut rng = ChaCha20Rng::seed_from_u64(12345);
|
|
let lpr_key = RingLprKey::generate(&mut rng);
|
|
|
|
let passwords = [
|
|
b"short".as_slice(),
|
|
b"medium-password-123".as_slice(),
|
|
b"this-is-a-very-long-password-that-tests-longer-inputs".as_slice(),
|
|
];
|
|
|
|
for password in &passwords {
|
|
let len = password.len();
|
|
|
|
group.bench_with_input(BenchmarkId::new("fast_oprf", len), password, |b, pwd| {
|
|
b.iter(|| fast_evaluate(&pp, &fast_key, pwd))
|
|
});
|
|
|
|
group.bench_with_input(
|
|
BenchmarkId::new("unlinkable_oprf", len),
|
|
password,
|
|
|b, pwd| b.iter(|| evaluate_unlinkable(&unlink_pp, &unlink_key, pwd)),
|
|
);
|
|
|
|
group.bench_with_input(
|
|
BenchmarkId::new("ring_lpr_oprf", len),
|
|
password,
|
|
|b, pwd| {
|
|
let mut rng = ChaCha20Rng::seed_from_u64(55555);
|
|
b.iter(|| {
|
|
let (state, blinded) = lpr_client_blind(&mut rng, pwd).unwrap();
|
|
let evaluated = lpr_server_evaluate(&lpr_key, &blinded).unwrap();
|
|
lpr_finalize(state, &evaluated)
|
|
})
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark Unlinkable OPRF - full protocol
|
|
fn bench_unlinkable_oprf(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("unlinkable_oprf");
|
|
|
|
let pp = UnlinkablePublicParams::generate(b"benchmark-params");
|
|
let key = UnlinkableServerKey::generate(&pp, b"benchmark-key");
|
|
let password = b"benchmark-password-12345";
|
|
|
|
group.bench_function("client_blind", |b| {
|
|
b.iter(|| client_blind_unlinkable(&pp, password))
|
|
});
|
|
|
|
let (state, blinded) = client_blind_unlinkable(&pp, password);
|
|
group.bench_function("server_evaluate", |b| {
|
|
b.iter(|| server_evaluate_unlinkable(&key, &blinded))
|
|
});
|
|
|
|
let response = server_evaluate_unlinkable(&key, &blinded);
|
|
group.bench_function("client_finalize", |b| {
|
|
let state = state.clone();
|
|
b.iter(|| client_finalize_unlinkable(&state, key.public_key(), &response))
|
|
});
|
|
|
|
group.bench_function("full_protocol", |b| {
|
|
b.iter(|| evaluate_unlinkable(&pp, &key, password))
|
|
});
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark message sizes
|
|
fn bench_message_sizes(c: &mut Criterion) {
|
|
println!("\n=== Message Size Comparison ===\n");
|
|
|
|
// Fast OPRF messages
|
|
let pp = PublicParams::generate(b"benchmark-params");
|
|
let fast_key = ServerKey::generate(&pp, b"benchmark-key");
|
|
let (_, blinded) = fast_client_blind(&pp, b"password");
|
|
let response = fast_server_evaluate(&fast_key, &blinded);
|
|
|
|
println!("Fast OPRF:");
|
|
println!(" Client -> Server (BlindedInput): ~{} bytes", 256 * 4); // RingElement
|
|
println!(" Server -> Client (Response): ~{} bytes", 256 * 4 + 256); // RingElement + helper
|
|
|
|
// Ring-LPR OPRF messages
|
|
let mut rng = ChaCha20Rng::seed_from_u64(12345);
|
|
let lpr_key = RingLprKey::generate(&mut rng);
|
|
let (_, lpr_blinded) = lpr_client_blind(&mut rng, b"password").unwrap();
|
|
let lpr_evaluated = lpr_server_evaluate(&lpr_key, &lpr_blinded).unwrap();
|
|
|
|
let lpr_blind_size = lpr_blinded.to_bytes().len();
|
|
let lpr_eval_size = lpr_evaluated.to_bytes().len();
|
|
|
|
println!("\nRing-LPR OPRF:");
|
|
println!(
|
|
" Client -> Server (BlindedInput): {} bytes",
|
|
lpr_blind_size
|
|
);
|
|
println!(
|
|
" Server -> Client (EvaluatedOutput): {} bytes",
|
|
lpr_eval_size
|
|
);
|
|
|
|
println!(
|
|
"\nSpeedup factor (message size): {:.1}x",
|
|
lpr_blind_size as f64 / (256.0 * 4.0)
|
|
);
|
|
println!();
|
|
}
|
|
|
|
criterion_group!(
|
|
benches,
|
|
bench_fast_oprf,
|
|
bench_unlinkable_oprf,
|
|
bench_ring_lpr_oprf,
|
|
bench_comparison,
|
|
);
|
|
|
|
criterion_main!(benches);
|