add prototype

This commit is contained in:
Janmajaya Mall
2023-12-12 21:58:59 +05:30
commit 743d567ba0
5 changed files with 531 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

359
Cargo.lock generated Normal file
View File

@@ -0,0 +1,359 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aligned-vec"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bfv"
version = "0.1.0"
source = "git+https://github.com/Janmajayamall/bfv?branch=pk#34090532ce04ee6c143753edab11f902c057837c"
dependencies = [
"concrete-ntt",
"crypto-bigint",
"itertools",
"ndarray",
"num-bigint",
"num-bigint-dig",
"num-traits",
"rand",
"rand_chacha",
"seq-macro",
"traits",
]
[[package]]
name = "bytemuck"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "concrete-ntt"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1167c583765f273205c691a0a8bff23a925aa5bc66f2448031e4a6e518cd64d7"
dependencies = [
"aligned-vec",
"pulp",
]
[[package]]
name = "crypto-bigint"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef"
dependencies = [
"rand_core",
"subtle",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "getrandom"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
dependencies = [
"spin",
]
[[package]]
name = "libc"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "libm"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "matrixmultiply"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "mp-psi"
version = "0.1.0"
dependencies = [
"bfv",
"itertools",
"rand",
]
[[package]]
name = "ndarray"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
dependencies = [
"matrixmultiply",
"num-complex",
"num-integer",
"num-traits",
"rawpointer",
]
[[package]]
name = "num-bigint"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
"rand",
]
[[package]]
name = "num-bigint-dig"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
dependencies = [
"byteorder",
"lazy_static",
"libm",
"num-integer",
"num-iter",
"num-traits",
"rand",
"serde",
"smallvec",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "pulp"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "866e8018d6397b0717100dd4a7948fc8cbc8c4b8ce3e39e98a0e1e878d3ba925"
dependencies = [
"bytemuck",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]]
name = "seq-macro"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
[[package]]
name = "serde"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "smallvec"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "2.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "traits"
version = "0.1.0"
source = "git+https://github.com/Janmajayamall/bfv?branch=pk#34090532ce04ee6c143753edab11f902c057837c"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"

11
Cargo.toml Normal file
View File

@@ -0,0 +1,11 @@
[package]
name = "mp-psi"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bfv = {git = "https://github.com/Janmajayamall/bfv", branch = "pk"}
rand = "0.8.5"
itertools = "0.10.5"

1
README.md Normal file
View File

@@ -0,0 +1 @@
Multi-party PSI using Multi-party BFV

159
src/main.rs Normal file
View File

@@ -0,0 +1,159 @@
use bfv::{
BfvParameters, CollectiveDecryption, CollectivePublicKeyGenerator, CollectiveRlkGenerator,
Encoding, EvaluationKey, Evaluator, PartySecret, Plaintext, Poly, PublicKey, SecretKey,
};
use itertools::{izip, Itertools};
use rand::{distributions::Uniform, thread_rng, Rng};
struct Party {
secret: SecretKey,
bit_vector: Vec<u64>,
}
impl Party {
fn random(params: &BfvParameters, hamming_weight: usize) -> Party {
let mut rng = thread_rng();
let secret = SecretKey::random_with_params(params, &mut rng);
let mut bit_vector = vec![0; params.degree];
(0..hamming_weight).into_iter().for_each(|_| {
let sample_index = rng.sample(Uniform::new(0, params.degree));
bit_vector[sample_index] = 1;
});
Party { secret, bit_vector }
}
}
fn plain_psi(parties: &[Party]) -> Vec<u64> {
let mut common = parties[0].bit_vector.clone();
parties.iter().skip(1).for_each(|party_i| {
izip!(common.iter_mut(), party_i.bit_vector.iter()).for_each(|(b_out, b_in)| {
*b_out *= b_in;
});
});
common
}
fn main() {
let mut params = BfvParameters::new(&[50, 50, 50], 65537, 1 << 15);
params.enable_hybrid_key_switching(&[50, 50]);
params.enable_pke();
let hw = 21000;
let parties = vec![Party::random(&params, hw), Party::random(&params, hw)];
// Collective public key generation //
let crs = [0u8; 32];
let mut rng = thread_rng();
// Each party generates their share
let shares = parties
.iter()
.map(|party_i| {
CollectivePublicKeyGenerator::generate_share(&params, &party_i.secret, crs, &mut rng)
})
.collect::<Vec<Poly>>();
// After each party has broadcasted their share, any one can generate public key
let public_key =
CollectivePublicKeyGenerator::aggregate_shares_and_finalise(&params, &shares, crs);
// Collective relinearization key generation //
// This is a 2 round protocol
let crs = [0u8; 32];
let level = 0;
// Each party generates a ephemeral state
let parties_state = parties
.iter()
.map(|_| CollectiveRlkGenerator::init_state(&params, &mut rng))
.collect_vec();
// Each party generates share1
let mut share1s_part0 = vec![];
let mut share1s_part1 = vec![];
izip!(parties.iter(), parties_state.iter()).for_each(|(party_i, internal_state_i)| {
// Party i brodcasts `part0` and `part1`
let (part0, part1) = CollectiveRlkGenerator::generate_share_1(
&params,
&party_i.secret,
internal_state_i,
crs,
level,
&mut rng,
);
share1s_part0.push(part0);
share1s_part1.push(part1);
});
// After each party has broadcasted their share1, each party proceeds to aggregate share1s and generate their share2.
let (share1s_part0_agg, share1s_part1_agg) =
CollectiveRlkGenerator::aggregate_shares_1(&params, &share1s_part0, &share1s_part1, level);
// Round 2
// Each party generates share2
let mut share2s_part0 = vec![];
let mut share2s_part1 = vec![];
izip!(parties.iter(), parties_state.iter()).for_each(|(party_i, internal_state_i)| {
// Party i brodcasts `part0` and `part1`
let (part0, part1) = CollectiveRlkGenerator::generate_share_2(
&params,
&party_i.secret,
&share1s_part0_agg,
&share1s_part1_agg,
internal_state_i,
level,
&mut rng,
);
share2s_part0.push(part0);
share2s_part1.push(part1);
});
// Each party broadcasts their share2, after which they aggregate the received shares and finalise the protocol
let rlk = CollectiveRlkGenerator::aggregate_shares_2(
&params,
&share2s_part0,
&share2s_part1,
share1s_part1_agg,
level,
);
// PSI //
// Two users use the public key to encryt their `bit_vector`s. Note that unless the two users come together to collecticely decrypt, their inputs stay private.
// Party 1
let pt1 = Plaintext::encode(&parties[0].bit_vector, &params, Encoding::default());
let ct1 = public_key.encrypt(&params, &pt1, &mut rng);
// Party 2
let pt2 = Plaintext::encode(&parties[1].bit_vector, &params, Encoding::default());
let ct2 = public_key.encrypt(&params, &pt2, &mut rng);
// Each party sends their ciphertext to the other party and both of them evaluate the FHE circuit (FHE circuit is simple ciphertext multiplication)
let evaluation_key = EvaluationKey::new_raw(&[level], vec![rlk], &[], &[], vec![]);
let evaluator = Evaluator::new(params);
let ct1ct2 = evaluator.mul(&ct1, &ct2);
let psi_ct = evaluator.relinearize(&ct1ct2, &evaluation_key);
// Collective decryption //
// Each party can independently generate their share to decrypt `psi_ct`. Unless the other party has evaluated the FHE circuit maliciously, they should be able to decrypt `psi_ct` after receiving other party's share.
// As it is often the case in MPC, the last party to send their share has leverage to not send the share and prevent the other party from learning PSI output.
let party1_share = CollectiveDecryption::generate_share(
evaluator.params(),
&psi_ct,
&parties[0].secret,
&mut rng,
);
let party2_share = CollectiveDecryption::generate_share(
&evaluator.params(),
&psi_ct,
&parties[1].secret,
&mut rng,
);
// With shares from party1 and party2, one can decrypt the ciphertext
let psi_output_pt = CollectiveDecryption::aggregate_share_and_decrypt(
evaluator.params(),
&psi_ct,
&[party1_share, party2_share],
);
let psi_output: Vec<u64> = psi_output_pt.decode(Encoding::default(), evaluator.params());
let expected_outpout = plain_psi(&parties);
assert_eq!(psi_output, expected_outpout);
}