From 14d280f02beb61647cb0aea953fb94cd3265bdd7 Mon Sep 17 00:00:00 2001 From: Jun Feng Date: Mon, 13 May 2024 14:12:30 +0800 Subject: [PATCH 1/6] update issue template --- .github/ISSUE_TEMPLATE/bug_report.md | 32 ------------ .github/ISSUE_TEMPLATE/bug_report.yml | 57 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 -------- .github/ISSUE_TEMPLATE/feature_request.yml | 44 +++++++++++++++++ 4 files changed, 101 insertions(+), 52 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index ca6ef662..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: "[BUG]" -labels: bug -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. -2. -3. -4. - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - - -**Additional context** -- version: -- operating system: -- memory: -- cpu: -- config: diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..2b690964 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,57 @@ +name: Bug Report +description: File a bug report. +title: "[Bug]: " +labels: ["bug"] +assignees: + - 6fj + - qxzhou1010 +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: textarea + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. + placeholder: | + Tell us what you see! + validations: + required: true + - type: textarea + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. In this environment... + 1. With this config... + 1. Run '...' + 1. See error... + validations: + required: true + - type: textarea + attributes: + label: Expected behavior + description: A clear and concise description of what you expected to happen. + placeholder: | + I expect the output to be... + validations: + required: true + - type: input + attributes: + label: Version + placeholder: eg. 0.4.0.dev240424 + validations: + required: true + - type: input + attributes: + label: Operating system + placeholder: eg. CentOS 7 x64 + validations: + required: true + - type: input + attributes: + label: Hardware Resources + placeholder: eg. 8c16g + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 6269982f..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: "[Feature]" -labels: enhancement -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..19f1ef72 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,44 @@ +name: Feature request +description: Suggest an idea for this project +title: "[Feature]: " +labels: ["enhancement"] +assignees: + - 6fj + - qxzhou1010 +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this fature request! + - type: textarea + attributes: + label: Related problem + description: Is your feature request related to a problem? Please describe. + placeholder: | + A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + validations: + required: true + - type: textarea + attributes: + label: Solution + description: Describe the solution you'd like. + placeholder: | + A clear and concise description of what you want to happen. + validations: + required: true + - type: textarea + attributes: + label: Alternatives + description: Describe alternatives you've considered. + placeholder: | + A clear and concise description of any alternative solutions or features you've considered. + validations: + required: true + - type: textarea + attributes: + label: Additional context + description: Additional context you would like to provide. + placeholder: | + Add any other context or screenshots about the feature request here. + validations: + required: true From c50d26af71d9075dd968fe39ac60b801aa8ae353 Mon Sep 17 00:00:00 2001 From: Jun FENG <99384777+6fj@users.noreply.github.com> Date: Tue, 14 May 2024 15:03:29 +0800 Subject: [PATCH 2/6] repo-sync-2024-05-14T14:38:40+0800 (#127) --- README.md | 11 +++++- RELEASE.md | 6 +++ bazel/patches/boost.patch | 2 +- docker/Dockerfile | 2 +- docker/README.md | 2 +- docs/user_guide/pir.rst | 6 +-- docs/user_guide/psi.rst | 4 +- docs/user_guide/psi_v2.rst | 4 +- psi/apsi/pir.cc | 3 -- psi/legacy/bucket_psi.cc | 6 ++- psi/main.cc | 10 +++++ psi/rr22/rr22_psi.cc | 7 +++- psi/rr22/rr22_psi_test.cc | 44 +++++++++++++++++++++ psi/seal_pir/seal_mpir_test.cc | 42 ++++++++++++++++---- psi/seal_pir/seal_pir.cc | 72 +++++++++++++++------------------- psi/seal_pir/seal_pir.h | 6 +++ psi/seal_pir/seal_pir_test.cc | 52 ++++++++++++++++++------ psi/version.h | 2 +- 18 files changed, 199 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index 9cd9a228..26e8d747 100644 --- a/README.md +++ b/README.md @@ -119,13 +119,20 @@ sender.config: In the first terminal, run the following command ```bash -docker run -it --rm --network host --mount type=bind,source=/tmp/receiver,target=/root/receiver --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:latest bash -c "./main --config receiver/receiver.config" +docker run -it --rm --network host --mount type=bind,source=/tmp/receiver,target=/root/receiver --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:latest --config receiver/receiver.config ``` In the other terminal, run the following command simultaneously. ```bash -docker run -it --rm --network host --mount type=bind,source=/tmp/sender,target=/root/sender --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:latest bash -c "./main --config sender/sender.config" +docker run -it --rm --network host --mount type=bind,source=/tmp/sender,target=/root/sender --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:latest --config sender/sender.config +``` + +You could also pass a minified JSON config directly. A minified JSON is a compact one without white space and line breaks. + +e.g. +``` +docker run -it --rm --network host --mount type=bind,source=/tmp/sender,target=/root/sender --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:latest --json '{"psi_config":{"protocol_config":{"protocol":"PROTOCOL_KKRT","role":"ROLE_RECEIVER","broadcast_result":true},"input_config":{"type":"IO_TYPE_FILE_CSV","path":"/root/receiver/receiver_input.csv"},"output_config":{"type":"IO_TYPE_FILE_CSV","path":"/root/receiver/receiver_output.csv"},"keys":["id0","id1"],"debug_options":{"trace_path":"/root/receiver/receiver.trace"}},"self_link_party":"receiver","link_config":{"parties":[{"id":"receiver","host":"127.0.0.1:5300"},{"id":"sender","host":"127.0.0.1:5400"}]}}' ``` ## Building SecretFlow PSI Library diff --git a/RELEASE.md b/RELEASE.md index fb6a3abc..b8e5a169 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -5,6 +5,12 @@ > - `[API]` prefix for API changes. > - `[Improvement]` prefix for implementation improvement. +## v0.4.0.dev240514 +- [API] add entrypoint for docker file. +- [API] allow passing config JSON directly to main. +- [Bugfix] fix ic mode. +- [Bugfix] fix RR22, SealPIR and APSI. + ## v0.4.0.dev240401 - [Improvement] upgrade download uri of xz. diff --git a/bazel/patches/boost.patch b/bazel/patches/boost.patch index cc414e72..6772b61f 100644 --- a/bazel/patches/boost.patch +++ b/bazel/patches/boost.patch @@ -39,4 +39,4 @@ index 8277dbb..afc9569 100644 + strip_prefix = "xz-5.4.6", ) - maybe( + maybe( \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile index e4b06cd9..d7b1ef2f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -46,4 +46,4 @@ LABEL kuscia.secretflow.deploy-templates=$deploy_templates # run as root for now WORKDIR /root -CMD ["/bin/bash"] +ENTRYPOINT ["./main"] diff --git a/docker/README.md b/docker/README.md index 58d67cb6..a12c364e 100644 --- a/docker/README.md +++ b/docker/README.md @@ -7,7 +7,7 @@ docker run -it --rm --mount type=bind,source="$(pwd)/../../psi",target=/home/ # build psi dev docker ```bash -sh build.sh -v -u -l +bash build.sh -v -u -l ``` - *-u* means upload docker to reg. - *-l* means tag docker as *latest* as well. diff --git a/docs/user_guide/pir.rst b/docs/user_guide/pir.rst index d8fc9ff2..351b93a8 100644 --- a/docs/user_guide/pir.rst +++ b/docs/user_guide/pir.rst @@ -198,7 +198,7 @@ Setup Phase .. code-block:: bash - docker run -it --rm --network host --mount type=bind,source=/tmp/server,target=/root/server --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:0.1.0beta bash -c "./main --config server/apsi_server_setup.json" + docker run -it --rm --network host --mount type=bind,source=/tmp/server,target=/root/server --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:0.1.0beta --config server/apsi_server_setup.json Online Phase >>>>>>>>>>>> @@ -209,14 +209,14 @@ In the server's terminal. .. code-block:: bash - docker run -it --rm --network host --mount type=bind,source=/tmp/server,target=/root/server --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:0.1.0beta bash -c "./main --config server/apsi_server_online.json" + docker run -it --rm --network host --mount type=bind,source=/tmp/server,target=/root/server --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:0.1.0beta --config server/apsi_server_online.json In the client's terminal. .. code-block:: bash - docker run -it --rm --network host --mount type=bind,source=/tmp/client,target=/root/client --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:0.1.0beta bash -c "./main --config client/apsi_client.json" + docker run -it --rm --network host --mount type=bind,source=/tmp/client,target=/root/client --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:0.1.0beta --config client/apsi_client.json More examples diff --git a/docs/user_guide/psi.rst b/docs/user_guide/psi.rst index d03fc3d1..1935c4c7 100644 --- a/docs/user_guide/psi.rst +++ b/docs/user_guide/psi.rst @@ -69,12 +69,12 @@ Run PSI In the first terminal, run the following command:: - docker run -it --rm --network host --mount type=bind,source=/tmp/receiver,target=/root/receiver --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:latest bash -c "./main --config receiver/receiver.config" + docker run -it --rm --network host --mount type=bind,source=/tmp/receiver,target=/root/receiver --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:latest --config receiver/receiver.config In the other terminal, run the following command simultaneously:: - docker run -it --rm --network host --mount type=bind,source=/tmp/sender,target=/root/sender --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:latest bash -c "./main --config sender/sender.config" + docker run -it --rm --network host --mount type=bind,source=/tmp/sender,target=/root/sender --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:latest --config sender/sender.config Building from source diff --git a/docs/user_guide/psi_v2.rst b/docs/user_guide/psi_v2.rst index 9d341c4f..fd0987e6 100644 --- a/docs/user_guide/psi_v2.rst +++ b/docs/user_guide/psi_v2.rst @@ -134,12 +134,12 @@ Run PSI In the first terminal, run the following command:: - docker run -it --rm --network host --mount type=bind,source=/tmp/receiver,target=/root/receiver --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:0.1.0beta bash -c "./main --config receiver/receiver.config" + docker run -it --rm --network host --mount type=bind,source=/tmp/receiver,target=/root/receiver --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:0.1.0beta --config receiver/receiver.config In the other terminal, run the following command simultaneously:: - docker run -it --rm --network host --mount type=bind,source=/tmp/sender,target=/root/sender --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:0.1.0beta bash -c "./main --config sender/sender.config" + docker run -it --rm --network host --mount type=bind,source=/tmp/sender,target=/root/sender --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:0.1.0beta --config sender/sender.config Building from source diff --git a/psi/apsi/pir.cc b/psi/apsi/pir.cc index 9fe617fb..1f2ab31f 100644 --- a/psi/apsi/pir.cc +++ b/psi/apsi/pir.cc @@ -514,9 +514,6 @@ PirResultReport PirServerFull( size_t count_per_query = config.apsi_server_config().num_per_query(); SPDLOG_INFO("server_data_count:{}", server_data_count); - YACL_ENFORCE(server_data_count <= config.bucket_size(), - "data_count:{} bucket_size:{}", config.bucket_size()); - ::apsi::PSIParams psi_params = GetPsiParams(count_per_query, server_data_count, config.apsi_server_config().max_items_per_bin()); diff --git a/psi/legacy/bucket_psi.cc b/psi/legacy/bucket_psi.cc index 9f89c5cc..771bba11 100644 --- a/psi/legacy/bucket_psi.cc +++ b/psi/legacy/bucket_psi.cc @@ -399,8 +399,10 @@ void BucketPsi::Init() { } SPDLOG_INFO("bucket size set to {}", config_.bucket_size()); - // Test connection. - lctx_->ConnectToMesh(); + if (!ic_mode_) { + // Test connection. + lctx_->ConnectToMesh(); + } MemoryPsiConfig config; config.set_psi_type(config_.psi_type()); diff --git a/psi/main.cc b/psi/main.cc index 230e0df9..a1ae8f58 100644 --- a/psi/main.cc +++ b/psi/main.cc @@ -28,6 +28,7 @@ #include "psi/proto/psi_v2.pb.h" DEFINE_string(config, "", "file path of launch config in JSON format."); +DEFINE_string(json, "", "config in JSON format."); DEFINE_string(kuscia, "", "file path of kuscia task config in JSON format."); std::string GenerateVersion() { @@ -54,6 +55,15 @@ int main(int argc, char* argv[]) { SPDLOG_INFO("Kuscia task id: {}", kuscia_config.task_id); launch_config = kuscia_config.launch_config; + } else if (!FLAGS_json.empty()) { + google::protobuf::util::JsonParseOptions json_parse_options; + json_parse_options.ignore_unknown_fields = false; // optional + auto status = google::protobuf::util::JsonStringToMessage( + FLAGS_json, &launch_config, json_parse_options); + + YACL_ENFORCE(status.ok(), + "Launch config JSON string couldn't be parsed: {}", + FLAGS_json); } else { YACL_ENFORCE(std::filesystem::exists(FLAGS_config), "Config file[{}] doesn't exist.", FLAGS_config); diff --git a/psi/rr22/rr22_psi.cc b/psi/rr22/rr22_psi.cc index 5b087286..0c3933a5 100644 --- a/psi/rr22/rr22_psi.cc +++ b/psi/rr22/rr22_psi.cc @@ -151,8 +151,11 @@ std::vector Rr22PsiReceiverInternal( YACL_ENFORCE(sender_size <= receiver_size); - size_t mask_size = ComputeTruncateSize(sender_size, receiver_size, - options.ssp, options.malicious); + size_t mask_size = sizeof(uint128_t); + if (options.compress) { + mask_size = ComputeTruncateSize(sender_size, receiver_size, options.ssp, + options.malicious); + } Rr22OprfReceiver oprf_receiver(kRr22OprfBinSize, options.ssp, options.mode, options.code_type, options.malicious); diff --git a/psi/rr22/rr22_psi_test.cc b/psi/rr22/rr22_psi_test.cc index 664b9148..ecbcba71 100644 --- a/psi/rr22/rr22_psi_test.cc +++ b/psi/rr22/rr22_psi_test.cc @@ -107,6 +107,50 @@ TEST_P(Rr22PsiTest, CorrectTest) { EXPECT_EQ(indices_psi, indices); } +TEST_P(Rr22PsiTest, CompressParamsFalseTest) { + auto params = GetParam(); + + auto lctxs = yacl::link::test::SetupWorld("ab", 2); + + uint128_t seed = yacl::MakeUint128(0, 0); + yacl::crypto::Prg prng(seed); + + size_t item_size = params.items_num; + + std::vector inputs_a; + std::vector inputs_b; + std::vector indices; + + std::tie(inputs_a, inputs_b, indices) = GenerateTestData(item_size); + + Rr22PsiOptions psi_options(40, 0, false); + + psi_options.mode = params.mode; + psi_options.malicious = params.malicious; + + auto psi_sender_proc = std::async( + [&] { Rr22PsiSenderInternal(psi_options, lctxs[0], inputs_a); }); + auto psi_receiver_proc = std::async( + [&] { return Rr22PsiReceiverInternal(psi_options, lctxs[1], inputs_b); }); + + psi_sender_proc.get(); + std::vector indices_psi = psi_receiver_proc.get(); + std::sort(indices_psi.begin(), indices_psi.end()); + + SPDLOG_INFO("{}?={}", indices.size(), indices_psi.size()); + EXPECT_EQ(indices.size(), indices_psi.size()); + +#if 0 + for (size_t i = 0; i < indices.size(); ++i) { + SPDLOG_INFO("i:{} index:{} a:{}, b:{}", i, indices_psi[i], + inputs_a[indices_psi[i]], inputs_b[indices_psi[i]]); + } +#endif + + EXPECT_EQ(indices_psi, indices); +} + + INSTANTIATE_TEST_SUITE_P( CorrectTest_Instances, Rr22PsiTest, testing::Values(TestParams{35, Rr22PsiMode::FastMode}, diff --git a/psi/seal_pir/seal_mpir_test.cc b/psi/seal_pir/seal_mpir_test.cc index 997439ce..38d44537 100644 --- a/psi/seal_pir/seal_mpir_test.cc +++ b/psi/seal_pir/seal_mpir_test.cc @@ -31,7 +31,7 @@ struct TestParams { size_t batch_number; size_t element_number; size_t element_size = 288; - size_t poly_degree = 8192; // now only support 8192 + size_t poly_degree = 4096; // now we support 4096 and 8192 }; std::vector GenerateDbData(TestParams params) { @@ -80,6 +80,13 @@ TEST_P(SealMultiPirTest, Works) { size_t element_number = params.element_number; size_t element_size = params.element_size; size_t batch_number = params.batch_number; + + SPDLOG_INFO( + "N (poly degree): {}, batch_number: {}, element_size: {} bytes, " + "element_number: 2^{} = {}", + params.poly_degree, batch_number, params.element_size, + std::log2(params.element_number), params.element_number); + // size_t batch_number = 256; double factor = 1.5; size_t hash_num = 3; @@ -109,8 +116,6 @@ TEST_P(SealMultiPirTest, Works) { ::psi::seal_pir::MultiQueryOptions options{ {params.poly_degree, element_number, element_size}, batch_number}; - SPDLOG_INFO("element_number:{}", options.seal_options.element_number); - ::psi::seal_pir::MultiQueryServer mpir_server(options, cuckoo_params, seed_server); @@ -136,6 +141,8 @@ TEST_P(SealMultiPirTest, Works) { mpir_server.SetGaloisKeys(galkey); // do pir query/answer + const auto pir_start_time = std::chrono::system_clock::now(); + std::future pir_service_func = std::async([&] { return mpir_server.DoMultiPirAnswer(ctxs[0]); }); std::future>> pir_client_func = std::async( @@ -144,6 +151,11 @@ TEST_P(SealMultiPirTest, Works) { pir_service_func.get(); std::vector> query_reply_bytes = pir_client_func.get(); + const auto pir_end_time = std::chrono::system_clock::now(); + const DurationMillis pir_time = pir_end_time - pir_start_time; + + SPDLOG_INFO("pir time(online) : {} ms", pir_time.count()); + EXPECT_EQ(query_reply_bytes.size(), query_index.size()); for (size_t idx = 0; idx < query_reply_bytes.size(); ++idx) { @@ -171,11 +183,25 @@ TEST_P(SealMultiPirTest, Works) { INSTANTIATE_TEST_SUITE_P( Works_Instances, SealMultiPirTest, - testing::Values(TestParams{32, 1000}, // element size default 288B - TestParams{32, 1000, 10}, // - TestParams{32, 1000, 400}, // - TestParams{64, 10000}, // element size default 288B - TestParams{64, 10000, 20}) // + testing::Values(TestParams{32, 1000}, // element size default 288B + TestParams{32, 1000, 288, 8192}, + + TestParams{32, 1000, 10}, // + TestParams{32, 1000, 10, 8192}, // + + TestParams{32, 1000, 400}, // + TestParams{32, 1000, 400, 8192}, // + + TestParams{64, 10000}, // element size default 288B + TestParams{64, 10000, 288, 8192}, + + TestParams{64, 10000, 20}, + TestParams{64, 10000, 20, 8192}, + + // large data num + TestParams{64, 1 << 20, 20}, + TestParams{64, 1 << 21, 20}, + TestParams{64, 1 << 22, 20}) // ); } // namespace psi::seal_pir diff --git a/psi/seal_pir/seal_pir.cc b/psi/seal_pir/seal_pir.cc index b8958524..94bd993a 100644 --- a/psi/seal_pir/seal_pir.cc +++ b/psi/seal_pir/seal_pir.cc @@ -178,9 +178,11 @@ void SealPir::SetPolyModulusDegree(size_t degree) { if (degree == 8192) { seal_params_->set_plain_modulus(seal::PlainModulus::Batching(degree, 17)); - //} //else if (degree == 4096) { - // seal_params_->set_plain_modulus(seal::PlainModulus::Batching(degree, - // 38)); + } else if (degree == 4096) { + // ref: + // https://github.com/microsoft/SealPIR/blob/ee1a5a3922fc9250f9bb4e2416ff5d02bfef7e52/src/main.cpp#L22 + // note that logt = 20 + seal_params_->set_plain_modulus(seal::PlainModulus::Batching(degree, 20)); } else { YACL_THROW("poly_modulus_degree {} is not support.", degree); } @@ -479,8 +481,6 @@ void SealPirServer::SetDatabase( std::vector SealPirServer::ExpandQuery( const seal::Ciphertext &encrypted, std::uint32_t m) { - uint64_t plain_mod = seal_params_->plain_modulus().value(); - seal::GaloisKeys &galkey = galois_key_; // Assume that m is a power of 2. If not, round it to the next power of 2. @@ -502,16 +502,9 @@ std::vector SealPirServer::ExpandQuery( for (size_t j = 0; j < logm - 1; j++) { std::vector results2(1 << (j + 1)); int step = 1 << j; - seal::Plaintext pt0(n); - seal::Plaintext pt1(n); - - pt0.set_zero(); - pt0[n - step] = plain_mod - 1; int index_raw = (n << 1) - (1 << j); int index = (index_raw * galois_elts[j]) % (n << 1); - pt1.set_zero(); - pt1[index] = 1; // int nstep = -step; yacl::parallel_for(0, step, [&](int64_t begin, int64_t end) { @@ -527,8 +520,8 @@ std::vector SealPirServer::ExpandQuery( evaluator_->apply_galois(c0, galois_elts[j], galkey, t0); evaluator_->add(c0, t0, results2[k]); - evaluator_->multiply_plain(c0, pt0, c1); - evaluator_->multiply_plain(t0, pt1, t1); + multiply_power_of_x(c0, c1, index_raw); + multiply_power_of_x(t0, t1, index); evaluator_->add(c1, t1, results2[k + step]); } }); @@ -539,16 +532,9 @@ std::vector SealPirServer::ExpandQuery( std::vector results2(results.size() << 1); seal::Plaintext two("2"); - seal::Plaintext pt0(n); - seal::Plaintext pt1(n); - - pt0.set_zero(); - pt0[n - results.size()] = plain_mod - 1; int index_raw = (n << 1) - (1 << (logm - 1)); int index = (index_raw * galois_elts[logm - 1]) % (n << 1); - pt1.set_zero(); - pt1[index] = 1; for (uint32_t k = 0; k < results.size(); k++) { if (k >= (m - (1 << (logm - 1)))) { // corner case. @@ -564,8 +550,9 @@ std::vector SealPirServer::ExpandQuery( evaluator_->apply_galois(c0, galois_elts[logm - 1], galkey, t0); evaluator_->add(c0, t0, results2[k]); - evaluator_->multiply_plain(c0, pt0, c1); - evaluator_->multiply_plain(t0, pt1, t1); + multiply_power_of_x(c0, c1, index_raw); + multiply_power_of_x(t0, t1, index); + evaluator_->add(c1, t1, results2[k + results.size()]); } } @@ -576,6 +563,24 @@ std::vector SealPirServer::ExpandQuery( return new_vec; } +void SealPirServer::multiply_power_of_x(const seal::Ciphertext &encrypted, + seal::Ciphertext &destination, + uint32_t index) { + auto coeff_mod_count = seal_params_->coeff_modulus().size() - 1; + auto coeff_count = seal_params_->poly_modulus_degree(); + auto encrypted_count = encrypted.size(); + + destination = encrypted; + for (size_t i = 0; i < encrypted_count; i++) { + for (size_t j = 0; j < coeff_mod_count; j++) { + seal::util::negacyclic_shift_poly_coeffmod( + encrypted.data(i) + (j * coeff_count), coeff_count, index, + seal_params_->coeff_modulus()[j], + destination.data(i) + (j * coeff_count)); + } + } +} + std::vector SealPirServer::GenerateReply( const std::vector> &query_ciphers, size_t start_pos) { @@ -685,7 +690,7 @@ std::vector SealPirServer::GenerateReply( seal::Ciphertext temp; for (j += 1; j < n_i; j++) { if ((*cur)[k + j * product].is_zero()) { - SPDLOG_INFO("cur[{}] is zero, k:{}, j:{}", (k + j * product), k, j); + // SPDLOG_INFO("cur[{}] is zero, k:{}, j:{}", (k + j * product), k, j); continue; } evaluator_->multiply_plain(expanded_query[j], (*cur)[k + j * product], @@ -725,7 +730,7 @@ std::vector SealPirServer::GenerateReply( } product *= pir_params_.expansion_ratio; // multiply by expansion rate. } - SPDLOG_INFO("Server: {}-th recursion level finished", (i + 1)); + // SPDLOG_INFO("Server: {}-th recursion level finished", (i + 1)); } SPDLOG_INFO("reply generated! "); // This should never get here @@ -1041,22 +1046,7 @@ void SealPirClient::ComputeInverseScales() { seal_params_->plain_modulus().value() - pow(2, logt - mod); } // get mod inverse - { - BN_CTX *bn_ctx = BN_CTX_new(); - BIGNUM *bn_t = BN_new(); - BIGNUM *bn_m = BN_new(); - BIGNUM *ret = BN_new(); - BN_set_word(bn_t, t); - BN_set_word(bn_m, 1 << logm); - - BN_mod_inverse(ret, bn_m, bn_t, bn_ctx); - inverse_scale = BN_get_word(ret); - - BN_free(bn_t); - BN_free(bn_m); - BN_free(ret); - BN_CTX_free(bn_ctx); - } + seal::util::try_invert_uint_mod((1 << logm), t, inverse_scale); inverse_scales_.push_back(inverse_scale); if ((inverse_scale << logm) % t != 1) { YACL_THROW("get inverse wrong"); diff --git a/psi/seal_pir/seal_pir.h b/psi/seal_pir/seal_pir.h index 0a75b29b..63323e91 100644 --- a/psi/seal_pir/seal_pir.h +++ b/psi/seal_pir/seal_pir.h @@ -222,6 +222,12 @@ class SealPirServer : public SealPir { seal::Plaintext *plain_ptr, int logt); std::vector DecomposeToPlaintexts( const seal::Ciphertext &encrypted); + + // compute encrypted * x^{-2^j} + // ref: + // https://github.com/microsoft/SealPIR/blob/ee1a5a3922fc9250f9bb4e2416ff5d02bfef7e52/src/pir_server.cpp#L397 + void multiply_power_of_x(const seal::Ciphertext &encrypted, + seal::Ciphertext &destination, uint32_t index); }; class SealPirClient : public SealPir { diff --git a/psi/seal_pir/seal_pir_test.cc b/psi/seal_pir/seal_pir_test.cc index 65a2fc26..d927e781 100644 --- a/psi/seal_pir/seal_pir_test.cc +++ b/psi/seal_pir/seal_pir_test.cc @@ -25,6 +25,7 @@ namespace psi::seal_pir { namespace { struct TestParams { + size_t N = 4096; size_t element_number; size_t element_size = 288; size_t query_size = 0; @@ -52,10 +53,15 @@ using DurationMillis = std::chrono::duration; class SealPirTest : public testing::TestWithParam {}; TEST_P(SealPirTest, Works) { - size_t n = 8192; auto params = GetParam(); + size_t n = params.N; auto ctxs = yacl::link::test::SetupWorld(2); + SPDLOG_INFO( + "N(poly degree): {}, element_size: {} bytes, element_number: 2^{} = {}", + n, params.element_size, std::log2(params.element_number), + params.element_number); + std::vector db_data = GenerateDbData(params); psi::seal_pir::SealPirOptions options{n, params.element_number, @@ -121,17 +127,37 @@ TEST_P(SealPirTest, Works) { INSTANTIATE_TEST_SUITE_P( Works_Instances, SealPirTest, - testing::Values(TestParams{1000}, // element size default 288B - TestParams{1000, 10}, // - TestParams{1000, 20}, // - TestParams{1000, 400}, // - TestParams{3000}, // element size default 288B - TestParams{3000, 10}, // - TestParams{3000, 20}, // - TestParams{3000, 400}, // - // - TestParams{203, 288, 100}, // - TestParams{3000, 288, 1000}) // -); + testing::Values(TestParams{4096, 1000}, // element size default 288B + TestParams{4096, 1000, 10}, // + TestParams{4096, 1000, 20}, // + TestParams{4096, 1000, 400}, // + TestParams{4096, 3000}, // element size default 288B + TestParams{4096, 3000, 10}, // + TestParams{4096, 3000, 20}, // + TestParams{4096, 3000, 400}, // + TestParams{4096, 203, 288, 100}, // + TestParams{4096, 3000, 288, 1000}, + // N = 8192 + TestParams{8192, 1000}, // element size default 288B + TestParams{8192, 1000, 10}, // + TestParams{8192, 1000, 20}, // + TestParams{8192, 1000, 400}, // + TestParams{8192, 3000}, // element size default 288B + TestParams{8192, 3000, 10}, // + TestParams{8192, 3000, 20}, // + TestParams{8192, 3000, 400}, // + TestParams{8192, 203, 288, 100}, // + TestParams{8192, 3000, 288, 1000}, // + // larger data num + TestParams{4096, 1 << 12}, TestParams{4096, 1 << 13}, + TestParams{4096, 1 << 14}, TestParams{4096, 1 << 15}, + TestParams{4096, 1 << 16}, TestParams{4096, 1 << 17}, + TestParams{4096, 1 << 18}, TestParams{4096, 1 << 19}, + // small element_size to avoid out of memory + TestParams{4096, 1 << 20, 10}, + TestParams{4096, 1 << 21, 10}, + TestParams{4096, 1 << 22, 10}, + TestParams{4096, 1 << 23, 10}, + TestParams{4096, 1 << 24, 10})); } // namespace psi::seal_pir diff --git a/psi/version.h b/psi/version.h index 28e335ba..a229f678 100644 --- a/psi/version.h +++ b/psi/version.h @@ -17,4 +17,4 @@ #define PSI_VERSION_MAJOR 0 #define PSI_VERSION_MINOR 4 #define PSI_VERSION_PATCH 0 -#define PSI_DEV_IDENTIFIER ".dev240401" +#define PSI_DEV_IDENTIFIER ".dev240514" From 79d6e818d593d199f27bb519aa7e18e104a06d7b Mon Sep 17 00:00:00 2001 From: Jamie-Cui Date: Thu, 16 May 2024 18:33:03 +0800 Subject: [PATCH 3/6] Bump: yacl (#128) * Bump: yacl * format --- bazel/repositories.bzl | 6 +++--- psi/kkrt/BUILD.bazel | 6 +++--- psi/kkrt/kkrt_psi.cc | 6 +++--- psi/kkrt/kkrt_psi.h | 2 +- psi/kkrt/receiver.h | 2 +- psi/kkrt/sender.h | 2 +- psi/rr22/BUILD.bazel | 2 +- psi/rr22/rr22_oprf.h | 2 +- psi/rr22/rr22_psi_test.cc | 1 - 9 files changed, 14 insertions(+), 15 deletions(-) diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 5bbd792e..131ac903 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -51,10 +51,10 @@ def _yacl(): http_archive, name = "yacl", urls = [ - "https://github.com/secretflow/yacl/archive/refs/tags/0.4.4b3.tar.gz", + "https://github.com/secretflow/yacl/archive/refs/tags/0.4.5b0.tar.gz", ], - strip_prefix = "yacl-0.4.4b3", - sha256 = "c6b5f32e92d2e31c1c5d7176792965fcf332d1ae892ab8b049d2e66f6f47e4f2", + strip_prefix = "yacl-0.4.5b0", + sha256 = "68d1dbeb255d404606d3ba9380b915fbbe3886cde575bbe89795657286742bd2", ) def _bazel_platform(): diff --git a/psi/kkrt/BUILD.bazel b/psi/kkrt/BUILD.bazel index 1b2c4c66..fc22612d 100644 --- a/psi/kkrt/BUILD.bazel +++ b/psi/kkrt/BUILD.bazel @@ -27,9 +27,9 @@ psi_cc_library( "@com_google_absl//absl/strings", "@yacl//yacl/crypto/hash:hash_utils", "@yacl//yacl/crypto/rand", - "@yacl//yacl/kernels/algorithms:base_ot", - "@yacl//yacl/kernels/algorithms:iknp_ote", - "@yacl//yacl/kernels/algorithms:kkrt_ote", + "@yacl//yacl/kernel/algorithms:base_ot", + "@yacl//yacl/kernel/algorithms:iknp_ote", + "@yacl//yacl/kernel/algorithms:kkrt_ote", "@yacl//yacl/link", ], ) diff --git a/psi/kkrt/kkrt_psi.cc b/psi/kkrt/kkrt_psi.cc index 50519ab7..7d5e652d 100644 --- a/psi/kkrt/kkrt_psi.cc +++ b/psi/kkrt/kkrt_psi.cc @@ -25,9 +25,9 @@ #include "yacl/crypto/hash/hash_utils.h" #include "yacl/crypto/rand/rand.h" #include "yacl/crypto/tools/prg.h" -#include "yacl/kernels/algorithms/base_ot.h" -#include "yacl/kernels/algorithms/iknp_ote.h" -#include "yacl/kernels/algorithms/kkrt_ote.h" +#include "yacl/kernel/algorithms/base_ot.h" +#include "yacl/kernel/algorithms/iknp_ote.h" +#include "yacl/kernel/algorithms/kkrt_ote.h" #include "psi/utils/communication.h" #include "psi/utils/cuckoo_index.h" diff --git a/psi/kkrt/kkrt_psi.h b/psi/kkrt/kkrt_psi.h index c97bf8df..3201e8f0 100644 --- a/psi/kkrt/kkrt_psi.h +++ b/psi/kkrt/kkrt_psi.h @@ -21,7 +21,7 @@ #include #include -#include "yacl/kernels/algorithms/ot_store.h" +#include "yacl/kernel/algorithms/ot_store.h" #include "yacl/link/link.h" // diff --git a/psi/kkrt/receiver.h b/psi/kkrt/receiver.h index d2cd81d6..aa7d694a 100644 --- a/psi/kkrt/receiver.h +++ b/psi/kkrt/receiver.h @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -#include "yacl/kernels/algorithms/ot_store.h" +#include "yacl/kernel/algorithms/ot_store.h" #include "psi/interface.h" #include "psi/utils/hash_bucket_cache.h" diff --git a/psi/kkrt/sender.h b/psi/kkrt/sender.h index 54353739..d9b3cee9 100644 --- a/psi/kkrt/sender.h +++ b/psi/kkrt/sender.h @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -#include "yacl/kernels/algorithms/ot_store.h" +#include "yacl/kernel/algorithms/ot_store.h" #include "psi/interface.h" #include "psi/utils/hash_bucket_cache.h" diff --git a/psi/rr22/BUILD.bazel b/psi/rr22/BUILD.bazel index a2f38147..a8668cc9 100644 --- a/psi/rr22/BUILD.bazel +++ b/psi/rr22/BUILD.bazel @@ -51,7 +51,7 @@ psi_cc_library( "@yacl//yacl/crypto/rand", "@yacl//yacl/crypto/tools:prg", "@yacl//yacl/crypto/tools:ro", - "@yacl//yacl/kernels/algorithms:silent_vole", + "@yacl//yacl/kernel/algorithms:silent_vole", "@yacl//yacl/link", "@yacl//yacl/math/f2k", "@yacl//yacl/utils:parallel", diff --git a/psi/rr22/rr22_oprf.h b/psi/rr22/rr22_oprf.h index c2b5af73..6579dce1 100644 --- a/psi/rr22/rr22_oprf.h +++ b/psi/rr22/rr22_oprf.h @@ -18,7 +18,7 @@ #include #include "yacl/base/int128.h" -#include "yacl/kernels/algorithms/silent_vole.h" +#include "yacl/kernel/algorithms/silent_vole.h" #include "yacl/link/context.h" #include "psi/rr22/okvs/baxos.h" diff --git a/psi/rr22/rr22_psi_test.cc b/psi/rr22/rr22_psi_test.cc index ecbcba71..afcb0807 100644 --- a/psi/rr22/rr22_psi_test.cc +++ b/psi/rr22/rr22_psi_test.cc @@ -150,7 +150,6 @@ TEST_P(Rr22PsiTest, CompressParamsFalseTest) { EXPECT_EQ(indices_psi, indices); } - INSTANTIATE_TEST_SUITE_P( CorrectTest_Instances, Rr22PsiTest, testing::Values(TestParams{35, Rr22PsiMode::FastMode}, From 0519556e5ee2d22eecac116e82f6e766ed735af6 Mon Sep 17 00:00:00 2001 From: Jun FENG <99384777+6fj@users.noreply.github.com> Date: Fri, 17 May 2024 13:36:35 +0800 Subject: [PATCH 4/6] bump version to v0.4.0.dev240517 (#130) --- RELEASE.md | 3 +++ psi/seal_pir/seal_mpir_test.cc | 6 ++---- psi/seal_pir/seal_pir.cc | 4 ++-- psi/version.h | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index b8e5a169..18d351eb 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -5,6 +5,9 @@ > - `[API]` prefix for API changes. > - `[Improvement]` prefix for implementation improvement. +## v0.4.0.dev240517 +- [Improvement] upgrade yacl to 0.4.5b0. + ## v0.4.0.dev240514 - [API] add entrypoint for docker file. - [API] allow passing config JSON directly to main. diff --git a/psi/seal_pir/seal_mpir_test.cc b/psi/seal_pir/seal_mpir_test.cc index 38d44537..a7a7a79f 100644 --- a/psi/seal_pir/seal_mpir_test.cc +++ b/psi/seal_pir/seal_mpir_test.cc @@ -195,12 +195,10 @@ INSTANTIATE_TEST_SUITE_P( TestParams{64, 10000}, // element size default 288B TestParams{64, 10000, 288, 8192}, - TestParams{64, 10000, 20}, - TestParams{64, 10000, 20, 8192}, + TestParams{64, 10000, 20}, TestParams{64, 10000, 20, 8192}, // large data num - TestParams{64, 1 << 20, 20}, - TestParams{64, 1 << 21, 20}, + TestParams{64, 1 << 20, 20}, TestParams{64, 1 << 21, 20}, TestParams{64, 1 << 22, 20}) // ); diff --git a/psi/seal_pir/seal_pir.cc b/psi/seal_pir/seal_pir.cc index 94bd993a..73b960ee 100644 --- a/psi/seal_pir/seal_pir.cc +++ b/psi/seal_pir/seal_pir.cc @@ -532,7 +532,6 @@ std::vector SealPirServer::ExpandQuery( std::vector results2(results.size() << 1); seal::Plaintext two("2"); - int index_raw = (n << 1) - (1 << (logm - 1)); int index = (index_raw * galois_elts[logm - 1]) % (n << 1); @@ -690,7 +689,8 @@ std::vector SealPirServer::GenerateReply( seal::Ciphertext temp; for (j += 1; j < n_i; j++) { if ((*cur)[k + j * product].is_zero()) { - // SPDLOG_INFO("cur[{}] is zero, k:{}, j:{}", (k + j * product), k, j); + // SPDLOG_INFO("cur[{}] is zero, k:{}, j:{}", (k + j * product), k, + // j); continue; } evaluator_->multiply_plain(expanded_query[j], (*cur)[k + j * product], diff --git a/psi/version.h b/psi/version.h index a229f678..373379c9 100644 --- a/psi/version.h +++ b/psi/version.h @@ -17,4 +17,4 @@ #define PSI_VERSION_MAJOR 0 #define PSI_VERSION_MINOR 4 #define PSI_VERSION_PATCH 0 -#define PSI_DEV_IDENTIFIER ".dev240514" +#define PSI_DEV_IDENTIFIER ".dev240517" From da1b6b5646771ecf929d98d798360298d59fa8b8 Mon Sep 17 00:00:00 2001 From: Jun FENG <99384777+6fj@users.noreply.github.com> Date: Tue, 21 May 2024 21:43:01 +0800 Subject: [PATCH 5/6] repo-sync-2024-05-21T20:55:08+0800 (#132) --- RELEASE.md | 3 + docs/_static/ecdh_oprf_psi.jpg | Bin 0 -> 145149 bytes docs/_static/ecdh_oprf_psi.png | Bin 127195 -> 0 bytes docs/development/psi_protocol_intro.rst | 41 +- docs/reference/psi_config.md | 1 - docs/user_guide/psi.rst | 20 +- psi/bc22/BUILD.bazel | 110 ----- psi/bc22/bc22_psi.cc | 560 ----------------------- psi/bc22/bc22_psi.h | 91 ---- psi/bc22/bc22_psi_benchmark.cc | 147 ------ psi/bc22/bc22_psi_test.cc | 121 ----- psi/bc22/emp_vole.cc | 146 ------ psi/bc22/emp_vole.h | 92 ---- psi/bc22/emp_vole_test.cc | 112 ----- psi/bc22/generalized_cuckoo_hash.cc | 262 ----------- psi/bc22/generalized_cuckoo_hash.h | 123 ----- psi/bc22/generalized_cuckoo_hash_test.cc | 128 ------ psi/legacy/BUILD.bazel | 13 - psi/legacy/bc22_2party_psi.cc | 59 --- psi/legacy/bc22_2party_psi.h | 43 -- psi/legacy/bucket_psi_test.cc | 15 - psi/legacy/memory_psi.cc | 3 +- psi/legacy/memory_psi_test.cc | 14 +- psi/proto/psi.proto | 8 +- psi/version.h | 2 +- 25 files changed, 17 insertions(+), 2097 deletions(-) create mode 100644 docs/_static/ecdh_oprf_psi.jpg delete mode 100644 docs/_static/ecdh_oprf_psi.png delete mode 100644 psi/bc22/BUILD.bazel delete mode 100644 psi/bc22/bc22_psi.cc delete mode 100644 psi/bc22/bc22_psi.h delete mode 100644 psi/bc22/bc22_psi_benchmark.cc delete mode 100644 psi/bc22/bc22_psi_test.cc delete mode 100644 psi/bc22/emp_vole.cc delete mode 100644 psi/bc22/emp_vole.h delete mode 100644 psi/bc22/emp_vole_test.cc delete mode 100644 psi/bc22/generalized_cuckoo_hash.cc delete mode 100644 psi/bc22/generalized_cuckoo_hash.h delete mode 100644 psi/bc22/generalized_cuckoo_hash_test.cc delete mode 100644 psi/legacy/bc22_2party_psi.cc delete mode 100644 psi/legacy/bc22_2party_psi.h diff --git a/RELEASE.md b/RELEASE.md index 18d351eb..86770ef8 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -5,6 +5,9 @@ > - `[API]` prefix for API changes. > - `[Improvement]` prefix for implementation improvement. +## v0.4.0.dev240521 +- [API] remove BC22 protocol + ## v0.4.0.dev240517 - [Improvement] upgrade yacl to 0.4.5b0. diff --git a/docs/_static/ecdh_oprf_psi.jpg b/docs/_static/ecdh_oprf_psi.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c59c55e532f2db87dc9451b83c240426796ef503 GIT binary patch literal 145149 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 be~vKdx&OWD73`nif2%e2e`_3A|D5`7Mo0H` literal 0 HcmV?d00001 diff --git a/docs/_static/ecdh_oprf_psi.png b/docs/_static/ecdh_oprf_psi.png deleted file mode 100644 index b8b8229c5842079a0c8f5635220351bd07347d2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 127195 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�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 z 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 QALCC=8TxbSP3uSh2mjO;82|tP diff --git a/docs/development/psi_protocol_intro.rst b/docs/development/psi_protocol_intro.rst index 86daf196..72b637cb 100644 --- a/docs/development/psi_protocol_intro.rst +++ b/docs/development/psi_protocol_intro.rst @@ -6,7 +6,6 @@ SecretFlow SPU implements the following PSI protocols, - Semi-honest ECDH-based two-party PSI protocol [HFH99]_ - Semi-honest ECDH-based three-party PSI protocol - Semi-honest OT-based two-party PSI protocol [KKRT16]_ -- Semi-honest OT-based two-party PSI protocol (with improved communication efficiency) [BC22]_ - Differentially Private (DP) PSI Protocol [DP-PSI]_ - Unbalanced PSI Protocol - Semi-honest and Malicious VOLE-based two-party PSI protocol [RS21]_ [RR22]_ @@ -120,42 +119,6 @@ We use 3-way stash-less CuckooHash proposed in [PSZ18]_. 6. Receiver compares two BaRK-OPRFs set and obtains the intersection. -BC22 PCG-PSI ------------- - -Pseudorandom Correlation Generator (PCG), is a primitive introduced in the work of Boyle et -al. [BCG+19b]_, [BCGI18]_, [SGRR19]_, [BCG+19a]_, [CIK+20]_. The goal of PCG is to compress long sources -of correlated randomness without violating security. - -Boyle et al. have designed multiple concretely efficient PCGs -for specific correlations, such as vector oblivious linear evaluation (VOLE) or batch oblivious linear -evaluation (BOLE). These primitives are at the heart of modern secure computation protocols with low -communication overhead.The VOLE functionality allows a receiver to learn a secret linear combination -of two vectors held by a sender and constructed (with sublinear communication) under variants -of the syndrome decoding assumption. - -[BC22]_ uses PCG to speed up private set intersection protocols, minimizing computation and communication. -We implement semi-honest version psi in [BC22]_ and use PCG/VOLE from [WYKW21]_ . [BC22]_ PSI protocol -requires only 30 seconds for the case of larger sets ( :math:`2^{24}` items each) of long strings (128 bits), -and reduces 1/3 communication than [KKRT16]_. - -.. figure:: ../_static/pcg_psi.png - -1. Sender and Receiver agree on :math:`(3,2)`-Generalized CuckooHash :math:`h_1,h_2: {\{0,1\}}^{*} \rightarrow [m]` - -2. Receiver inserts each x into bin :math:`h_1(x)` or :math:`h_2(x)` - -3. Sender inserts each y into bin :math:`h_1(y)` and :math:`h_2(y)` - -4. Run PCG/VOLE from [WYKW21]_, :math:`w_i = \Delta * u_i + v_i`, Receiver gets :math:`w_i` and :math:`\Delta`, - Sender gets :math:`u_i` and :math:`v_i`, for each :math:`bin_i` - -5. Receiver sends Masked Bin Polynomial Coefficients to Sender, and receives BaRK-OPRF values - -6. Sender sends all BaRK-OPRF values for each :math:`{\{y_i\}}_{i=1}^{n_2}` to Receiver - -7. Receiver compares two BaRK-OPRFs sets and gets intersection. - Differentially Private PSI -------------------------- @@ -240,7 +203,7 @@ An Oblivious Pseudorandom Function (OPRF) is a two-party protocol between client output of a Pseudorandom Function (PRF). [draft-irtf-cfrg-voprf-10]_ specifies OPRF, VOPRF, and POPRF protocols built upon prime-order groups. -.. figure:: ../_static/ecdh_oprf_psi.png +.. figure:: ../_static/ecdh_oprf_psi.jpg - Offline Phase @@ -409,8 +372,6 @@ Reference Efficient two-round OT extension and silent non-interactive secure computation. In ACM CCS 2019, pages 291–308. ACM Press, November 2019. -.. [BC22] Private Set Intersection from Pseudorandom Correlation Generators - .. [Ber06] Daniel J. Bernstein. Curve25519: new diffie-hellman speed records. In In Public Key Cryptography (PKC), Springer-Verlag LNCS 3958, page 2006, 2006. (Cited on page 4.) diff --git a/docs/reference/psi_config.md b/docs/reference/psi_config.md index e30ef8fc..8c6f7d16 100644 --- a/docs/reference/psi_config.md +++ b/docs/reference/psi_config.md @@ -162,7 +162,6 @@ The algorithm type of psi. | INVALID_PSI_TYPE | 0 | none | | ECDH_PSI_2PC | 1 | DDH based PSI | | KKRT_PSI_2PC | 2 | Efficient Batched Oblivious PRF with Applications to Private Set Intersection https://eprint.iacr.org/2016/799.pdf | -| BC22_PSI_2PC | 3 | PSI from Pseudorandom Correlation Generators https://eprint.iacr.org/2022/334 | | ECDH_PSI_3PC | 4 | Multi-party PSI based on ECDH (Say A, B, C (receiver)) notice: two-party intersection cardinarlity leak (|A intersect B|) | | ECDH_PSI_NPC | 5 | Iterative running 2-party ecdh psi to get n-party PSI. Notice: two-party intersection leak | | KKRT_PSI_NPC | 6 | Iterative running 2-party kkrt psi to get n-party PSI. Notice: two-party intersection leak | diff --git a/docs/user_guide/psi.rst b/docs/user_guide/psi.rst index 1935c4c7..208a01f8 100644 --- a/docs/user_guide/psi.rst +++ b/docs/user_guide/psi.rst @@ -157,26 +157,10 @@ Intel(R) Xeon(R) Platinum 8269CY CPU @ 2.50GHz | | online | 25.434s | 100.68s | 415.94s | 1672.21s | +-----------+---------+---------+---------+---------+----------+ -bc22 pcg-psi Benchmark ->>>>>>>>>>>>>>>>>>>>>> - -Intel(R) Xeon(R) Platinum 8269CY CPU @ 2.50GHz - -+-----------+---------+---------+---------+----------+---------+---------+ -| bandwidth | 2^18 | 2^20 | 2^21 | 2^22 | 2^23 | 2^24 | -+===========+=========+=========+=========+==========+=========+=========+ -| LAN | 1.261s | 2.191s | 3.503s | 6.51s | 13.012s | 26.71s | -+-----------+---------+---------+---------+----------+---------+---------+ -| 100Mbps | 2.417s | 6.054s | 11.314s | 21.864s | 43.778s | 88.29s | -+-----------+---------+---------+---------+----------+---------+---------+ -| 10Mbps | 18.826s | 50.038s | 96.516s | 186.097s | 369.84s | 737.71s | -+-----------+---------+---------+---------+----------+---------+---------+ - Security Tips ------------- -Warning: `KKRT16 `_ and -`BC22 PCG `_ are semi-honest PSI protocols, +Warning: `KKRT16 `_ is semi-honest PSI protocols, and may be attacked in malicious model. -We recommend using KKRT16 and BC22_PCG PSI protocol as one-way PSI, i.e., one party gets the final intersection result. +We recommend using KKRT16 PSI protocol as one-way PSI, i.e., one party gets the final intersection result. diff --git a/psi/bc22/BUILD.bazel b/psi/bc22/BUILD.bazel deleted file mode 100644 index 543f129a..00000000 --- a/psi/bc22/BUILD.bazel +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright 2021 Ant Group Co., Ltd. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("//bazel:psi.bzl", "psi_cc_binary", "psi_cc_library", "psi_cc_test") -load("@yacl//bazel:yacl.bzl", "AES_COPT_FLAGS") - -package(default_visibility = ["//visibility:public"]) - -psi_cc_library( - name = "bc22_psi", - srcs = ["bc22_psi.cc"], - hdrs = ["bc22_psi.h"], - copts = AES_COPT_FLAGS, - deps = [ - ":emp_vole", - ":generalized_cuckoo_hash", - "//psi/utils:communication", - "//psi/utils:serialize", - "@com_google_absl//absl/container:flat_hash_set", - "@com_google_absl//absl/strings", - "@yacl//yacl/base:exception", - "@yacl//yacl/base:int128", - "@yacl//yacl/crypto/rand", - "@yacl//yacl/link", - "@yacl//yacl/utils:parallel", - ], -) - -psi_cc_test( - name = "bc22_psi_test", - srcs = ["bc22_psi_test.cc"], - deps = [ - ":bc22_psi", - ], -) - -psi_cc_library( - name = "emp_vole", - srcs = ["emp_vole.cc"], - hdrs = ["emp_vole.h"], - copts = AES_COPT_FLAGS, - deps = [ - "//psi/utils:communication", - "//psi/utils:emp_io_adapter", - "//psi/utils:serialize", - "@com_github_emptoolkit_emp_zk//:emp-zk", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/types:span", - "@yacl//yacl/base:exception", - "@yacl//yacl/crypto/rand", - "@yacl//yacl/link", - ], -) - -psi_cc_test( - name = "emp_vole_test", - srcs = ["emp_vole_test.cc"], - copts = AES_COPT_FLAGS, - deps = [ - ":emp_vole", - "@com_google_absl//absl/container:flat_hash_set", - "@yacl//yacl/link", - ], -) - -psi_cc_library( - name = "generalized_cuckoo_hash", - srcs = ["generalized_cuckoo_hash.cc"], - hdrs = ["generalized_cuckoo_hash.h"], - deps = [ - "//psi/utils:cuckoo_index", - "@com_google_absl//absl/strings", - "@yacl//yacl/base:byte_container_view", - "@yacl//yacl/base:exception", - "@yacl//yacl/base:int128", - "@yacl//yacl/crypto/hash:hash_utils", - "@yacl//yacl/crypto/rand", - "@yacl//yacl/crypto/tools:prg", - "@yacl//yacl/link", - "@yacl//yacl/utils:parallel", - ], -) - -psi_cc_test( - name = "generalized_cuckoo_hash_test", - srcs = ["generalized_cuckoo_hash_test.cc"], - deps = [ - ":generalized_cuckoo_hash", - ], -) - -psi_cc_binary( - name = "bc22_psi_benchmark", - srcs = ["bc22_psi_benchmark.cc"], - deps = [ - ":bc22_psi", - "@com_github_google_benchmark//:benchmark_main", - ], -) diff --git a/psi/bc22/bc22_psi.cc b/psi/bc22/bc22_psi.cc deleted file mode 100644 index 4f364fb4..00000000 --- a/psi/bc22/bc22_psi.cc +++ /dev/null @@ -1,560 +0,0 @@ -// Copyright 2021 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "psi/bc22/bc22_psi.h" - -#include -#include -#include -#include -#include - -#include "absl/container/flat_hash_set.h" -#include "absl/strings/escaping.h" -#include "openssl/rand.h" -#include "spdlog/spdlog.h" -#include "yacl/crypto/rand/rand.h" -#include "yacl/utils/parallel.h" - -#include "psi/bc22/emp_vole.h" -#include "psi/utils/serialize.h" - -#include "psi/utils/serializable.pb.h" - -namespace psi::bc22 { - -namespace { - -constexpr size_t kDefaultHashNum = 2; -constexpr size_t kMaxItemsPerBin = 3; - -constexpr size_t kPcgPsiBatchSize = 4096; -constexpr size_t kPcgPsiLogBatchSize = kPcgPsiBatchSize * 100; - -constexpr size_t kMaxCompareBytes = 13; - -// BC22 section 2.3 -// H(i, vi) = H(i, wi-delta*ui) -std::vector BaRKOPRFHash(size_t bin_idx, - WolverineVoleFieldType value) { - std::string hash_input(sizeof(WolverineVoleFieldType) + sizeof(size_t), '\0'); - std::memcpy(hash_input.data(), &bin_idx, sizeof(size_t)); - std::memcpy(hash_input.data() + sizeof(size_t), &value, - sizeof(WolverineVoleFieldType)); - auto hash_res = yacl::crypto::Blake3(hash_input); - return {hash_res.begin(), hash_res.end()}; -} - -} // namespace - -Bc22PcgPsi::Bc22PcgPsi(std::shared_ptr link_ctx, - PsiRoleType role) - : link_ctx_(std::move(link_ctx)), - role_(role), - batch_size_(kPcgPsiBatchSize) {} - -void Bc22PcgPsi::ExchangeItemsNumber(size_t self_item_num) { - // exchange items number, compute compare bytes size - // oprf compare bits: 40 + log2(n1) + log2(n2) - - yacl::Buffer self_count_buffer = utils::SerializeSize(self_item_num); - link_ctx_->SendAsyncThrottled( - link_ctx_->NextRank(), self_count_buffer, - fmt::format("send items count: {}", self_item_num)); - - yacl::Buffer peer_items_num_buffer = - link_ctx_->Recv(link_ctx_->NextRank(), fmt::format("peer items number")); - peer_items_num_ = utils::DeserializeSize(peer_items_num_buffer); -} - -void Bc22PcgPsi::RunPsi(absl::Span items) { - ExchangeItemsNumber(items.size()); - - size_t compare_bytes_size = - utils::GetCompareBytesLength(items.size(), peer_items_num_); - - SPDLOG_INFO("self size:{}, peer size:{} compare_bytes_size:{}", items.size(), - peer_items_num_, compare_bytes_size); - - if (role_ == PsiRoleType::Sender) { - cuckoo_options_ = - GetCuckooHashOption(kMaxItemsPerBin, kDefaultHashNum, peer_items_num_); - // sender alice - - // call mBaRK-OPRF - std::string oprfs = RunmBaRKOprfSender(items, compare_bytes_size); - - // send sender's oprfs to receiver - PcgPsiSendOprf(items, oprfs, compare_bytes_size); - } else if (role_ == PsiRoleType::Receiver) { - cuckoo_options_ = - GetCuckooHashOption(kMaxItemsPerBin, kDefaultHashNum, items.size()); - // receiver bob - - // call mBaRK-OPRF - std::vector oprf_encode_vec = - RunmBaRKOprfReceiver(items, compare_bytes_size); - - // receive sender's oprfs, and compute intersection - PcgPsiRecvOprf(items, oprf_encode_vec, compare_bytes_size); - } else { - YACL_THROW("wrong psi role type: {}", static_cast(role_)); - } -} - -// mBaRK-OPRF -std::string Bc22PcgPsi::RunmBaRKOprfSender(absl::Span items, - size_t compare_bytes_size) { - WolverineVole vole(role_, link_ctx_); - - SimpleHashTable simple_table(cuckoo_options_); - - // insert simple hash table - std::future table_thread = std::async([&] { - SPDLOG_INFO("begin insert simple hash table"); - - simple_table.Insert(items); - - SPDLOG_INFO("after insert simple hash table"); - }); - - uint64_t vole_count_needed = cuckoo_options_.NumBins() * kMaxItemsPerBin; - - // vole extension - SPDLOG_INFO("begin pcg vole extension"); - // wi = delta * ui + vi - // alice : delta wi - // bob : ui || vi as one __uint128_t - std::vector vole_blocks = - vole.Extend(vole_count_needed); - SPDLOG_INFO("after pcg vole extension"); - - WolverineVoleFieldType delta = vole.Delta(); - - const size_t coeff_byte_size = - kMaxItemsPerBin * sizeof(WolverineVoleFieldType); - - // sender alice - // recv masked polynomial coeff - size_t recv_bin_idx = 0; - size_t bins_num = cuckoo_options_.NumBins(); - SPDLOG_INFO("cuckoo_options_.NumBins: {}", bins_num); - - SPDLOG_INFO("begin recv receiver's masked coeff"); - - std::vector> - masked_coeffs(bins_num); - - while (true) { - yacl::Buffer masked_coeff_buffer = link_ctx_->Recv( - link_ctx_->NextRank(), fmt::format("recv {} bin", recv_bin_idx)); - YACL_ENFORCE((masked_coeff_buffer.size() % coeff_byte_size) == 0); - - size_t num_bin = masked_coeff_buffer.size() / coeff_byte_size; - - std::memcpy(&masked_coeffs[recv_bin_idx], - reinterpret_cast(masked_coeff_buffer.data()), - masked_coeff_buffer.size()); - - recv_bin_idx += num_bin; - // every kPcgPsiLogBatchSize bin print percentage - if (recv_bin_idx % kPcgPsiLogBatchSize == 0) { - SPDLOG_INFO( - "recv receiver's masked coeff, recv_bin_idx: {} Bins_Num:{} " - "percentage:{}", - recv_bin_idx, bins_num, (double)recv_bin_idx / bins_num); - } - - if (recv_bin_idx == bins_num) { - break; - } - } - - SPDLOG_INFO("after recv receiver's masked coeff, recv_bin_idx: {}", - recv_bin_idx); - - table_thread.get(); - - const std::vector> &bins = simple_table.bins(); - - const std::vector &items_hash_low64 = - simple_table.GetItemsHashLow64(); - - SPDLOG_INFO("role:{} items:{} bins size: {},items_hash_low64 size: {}", - (role_ == PsiRoleType::Sender) ? "sender" : "receiver", - items.size(), bins.size(), items_hash_low64.size()); - - std::string oprfs; - oprfs.resize(items.size() * cuckoo_options_.num_hash * kMaxCompareBytes); - - // shuffle sender's oprfs - std::mt19937 rng(yacl::crypto::SecureRandU64()); - - std::vector shuffled_idx_vec(items.size()); - std::iota(shuffled_idx_vec.begin(), shuffled_idx_vec.end(), 0); - std::shuffle(shuffled_idx_vec.begin(), shuffled_idx_vec.end(), rng); - - // randomize conflict oprfs - const std::vector &conflict_idx = simple_table.GetConflictIdx(); - - for (auto i : conflict_idx) { - size_t shuffled_idx = shuffled_idx_vec[i]; - - YACL_ENFORCE( - RAND_bytes(reinterpret_cast( - &oprfs[((shuffled_idx * cuckoo_options_.num_hash)) * - compare_bytes_size]), - 2 * compare_bytes_size) == 1); - } - - // compute sender's oprf - yacl::parallel_for(0, bins.size(), [&](int64_t begin, int64_t end) { - for (int64_t bin_idx = begin; bin_idx < end; ++bin_idx) { - std::vector oprf_key(kMaxItemsPerBin); - - size_t vole_start = bin_idx * kMaxItemsPerBin; - - std::array masked_coeff = - masked_coeffs[bin_idx]; - - // delta * masked_coeff_i + w_i - // = delta * coeff_i - delta * u_i + w_i - // = delta * coeff_i + v_i - for (size_t j = 0; j < kMaxItemsPerBin; ++j) { - WolverineVoleFieldType tmp = mod(delta * masked_coeff[j], pr); - - oprf_key[j] = mod(vole_blocks[vole_start + j] + tmp, pr); - } - - for (size_t j = 0; j < bins[bin_idx].size(); ++j) { - size_t item_idx = bins[bin_idx][j].InputIdx(); - size_t hash_idx = bins[bin_idx][j].HashIdx(); - - absl::string_view item_hash_str = absl::string_view( - reinterpret_cast(&items_hash_low64[item_idx]), - sizeof(uint64_t)); - - // delta * Poly(x) + vi_0 + vi_1*x+vi_1*x^2 - WolverineVoleFieldType eval = - EvaluatePolynomial(absl::MakeSpan(oprf_key), item_hash_str, delta); - - std::vector hash_res = BaRKOPRFHash(bin_idx, eval); - - // copy to shuffled pos - size_t shuffled_idx = shuffled_idx_vec[item_idx]; - - std::memcpy( - &oprfs[((shuffled_idx * cuckoo_options_.num_hash) + hash_idx) * - compare_bytes_size], - hash_res.data(), compare_bytes_size); - } - } - }); - - SPDLOG_INFO("after compute sender's oprf"); - - return oprfs; -} - -std::vector Bc22PcgPsi::RunmBaRKOprfReceiver( - absl::Span items, size_t compare_bytes_size) { - WolverineVole vole(role_, link_ctx_); - - GeneralizedCuckooHashTable cuckoo_table(cuckoo_options_, kMaxItemsPerBin); - - uint64_t vole_count_needed = cuckoo_options_.NumBins() * kMaxItemsPerBin; - - std::future table_thread = std::async([&] { - SPDLOG_INFO("begin insert hash table"); - - cuckoo_table.Insert(items); - - SPDLOG_INFO("after insert hash table"); - }); - - // emp vole extension - - SPDLOG_INFO("begin pcg vole extension"); - // wi = delta * ui + vi - // alice : delta wi - // bob : ui || vi as one __uint128_t - std::vector vole_blocks = - vole.Extend(vole_count_needed); - SPDLOG_INFO("after pcg vole extension"); - - table_thread.get(); - - const std::vector> &bins = cuckoo_table.bins(); - - const std::vector &items_hash_low64 = - cuckoo_table.GetItemsHashLow64(); - - SPDLOG_INFO("role:{} items:{} bins size: {}, items_hash_low64 size: {}", - (role_ == PsiRoleType::Sender) ? "sender" : "receiver", - items.size(), bins.size(), items_hash_low64.size()); - - const size_t coeff_byte_size = - kMaxItemsPerBin * sizeof(WolverineVoleFieldType); - - // receiver bob - // send mask - std::vector oprf_encode_vec(items.size()); - - SPDLOG_INFO("begin compute and send receiver's masked coeff"); - - for (size_t idx = 0; idx < bins.size(); idx += batch_size_) { - size_t current_batch_size = std::min(batch_size_, bins.size() - idx); - - yacl::Buffer masked_coeff_buffer(coeff_byte_size * current_batch_size); - - std::vector oprf_blocks_batch(current_batch_size); - - yacl::parallel_for(0, current_batch_size, [&](int64_t begin, int64_t end) { - for (int64_t j = begin; j < end; ++j) { - size_t bin_idx = idx + j; - - std::vector bin_data(kMaxItemsPerBin); - size_t pos = j * coeff_byte_size; - - size_t k = 0; - for (; k < bins[bin_idx].size(); ++k) { - bin_data[k] = absl::string_view( - reinterpret_cast( - &items_hash_low64[bins[bin_idx][k].InputIdx()]), - sizeof(uint64_t)); // use 64 bit - } - - for (; k < kMaxItemsPerBin; ++k) { - std::string buf(sizeof(uint64_t), '\0'); - YACL_ENFORCE(RAND_bytes(reinterpret_cast(buf.data()), - buf.length()) == 1); - bin_data[k] = buf; - } - std::vector coeff_blocks = - GetPolynomialCoefficients(bin_data); - - // use vole mask polynomial coefficient - // coeff_i - ui - size_t vole_start = (idx + j) * kMaxItemsPerBin; - for (k = 0; k < coeff_blocks.size(); ++k) { - coeff_blocks[k] = - mod(coeff_blocks[k] + (vole_blocks[vole_start + k] >> 64), pr); - } - - // copy to masked_coeff send buffer - std::memcpy( - reinterpret_cast(masked_coeff_buffer.data()) + pos, - coeff_blocks.data(), coeff_byte_size); - - // get vi_0, vi_1, vi_2, i: bin index - std::vector coeff_vole(kMaxItemsPerBin); - for (k = 0; k < coeff_vole.size(); ++k) { - coeff_vole[k] = vole_blocks[vole_start + k] & 0xFFFFFFFFFFFFFFFFLL; - } - - for (k = 0; k < bins[bin_idx].size(); ++k) { - size_t item_index = bins[bin_idx][k].InputIdx(); - - // get item_hash as x, - absl::string_view item_hash_str = absl::string_view( - reinterpret_cast(&items_hash_low64[item_index]), - sizeof(uint64_t)); - - // compute vi_0 + vi_1*x +vi_2*x^2, i: bin index - WolverineVoleFieldType eval = - EvaluatePolynomial(coeff_vole, item_hash_str, 0); - - // compute oprf, H(i, vi_0 + vi_1*x +vi_2*x^2), i: bin index - std::vector hash_res = BaRKOPRFHash(bin_idx, eval); - - oprf_encode_vec[item_index] = - absl::string_view(reinterpret_cast(hash_res.data()), - hash_res.size()) - .substr(0, compare_bytes_size); - } - } - }); - - link_ctx_->SendAsyncThrottled( - link_ctx_->NextRank(), masked_coeff_buffer, - fmt::format("send {} bin", current_batch_size)); - } - SPDLOG_INFO("after send receiver's masked coeff"); - - return oprf_encode_vec; -} - -void Bc22PcgPsi::PcgPsiSendOprf(absl::Span items, - const std::string &oprfs, - size_t compare_bytes_size) { - SPDLOG_INFO("begin send sender's oprf"); - // send oprf - // batch send oprf - for (size_t i = 0; i < items.size(); i += batch_size_) { - size_t current_batch_size = std::min(batch_size_, items.size() - i); - bool is_last_batch = false; - - if ((i + current_batch_size) == items.size()) { - is_last_batch = true; - } - - proto::PsiDataBatchProto proto; - proto.set_item_num(current_batch_size); - std::string flatten_bytes = oprfs.substr( - i * cuckoo_options_.num_hash * compare_bytes_size, - current_batch_size * cuckoo_options_.num_hash * compare_bytes_size); - - proto.set_flatten_bytes(flatten_bytes); - proto.set_is_last_batch(is_last_batch); - yacl::Buffer oprf_buffer(proto.ByteSizeLong()); - proto.SerializeToArray(oprf_buffer.data(), oprf_buffer.size()); - - link_ctx_->SendAsyncThrottled( - link_ctx_->NextRank(), oprf_buffer, - fmt::format("send oprf buffer, bytes: {}", oprf_buffer.size())); - } - - SPDLOG_INFO("after send sender's oprf"); -} - -void Bc22PcgPsi::PcgPsiRecvOprf(absl::Span items, - const std::vector &oprf_encode_vec, - size_t compare_bytes_size) { - SPDLOG_INFO("begin recv sender's oprf"); - - // recv oprf - std::string sender_oprf( - peer_items_num_ * cuckoo_options_.num_hash * compare_bytes_size, '\0'); - - size_t oprf_count = 0; - while (true) { - yacl::Buffer oprf_buffer = - link_ctx_->Recv(link_ctx_->NextRank(), fmt::format("recv oprf buffer")); - - proto::PsiDataBatchProto proto; - proto.ParseFromArray(oprf_buffer.data(), oprf_buffer.size()); - - size_t current_batch_size = proto.item_num(); - const std::string &flatten_bytes = proto.flatten_bytes(); - bool is_last_batch = proto.is_last_batch(); - - SPDLOG_DEBUG( - "recv oprf_buffer size:{} item_num:{} " - "is_last_batch:{} " - "flatten_bytes:{}", - oprf_buffer.size(), current_batch_size, is_last_batch, - flatten_bytes.length()); - - std::memcpy(&sender_oprf[oprf_count * cuckoo_options_.num_hash * - compare_bytes_size], - flatten_bytes.data(), flatten_bytes.length()); - - oprf_count += current_batch_size; - - // every kPcgPsiLogBatchSize bin print percentage - if (oprf_count % kPcgPsiLogBatchSize == 0) { - SPDLOG_INFO( - "recv sender's oprf, oprf_count: {} peer_items_num_:{} " - "percentage:{}", - oprf_count, peer_items_num_, (double)oprf_count / peer_items_num_); - } - - SPDLOG_DEBUG(" oprf_count:{}", oprf_count); - - if (is_last_batch) { - break; - } - } - - SPDLOG_INFO("after recv sender's oprf"); - - // https://abseil.io/docs/cpp/guides/container#abslflat_hash_map-and-abslflat_hash_set - // absl flat_hash_set faster than std::unordered_set - std::vector> sender_oprf_set( - cuckoo_options_.num_hash); - - auto sender_oprf_insert_proc = [&](size_t hash_idx) -> void { - size_t oprf_idx = 0; - for (size_t i = 0; i < peer_items_num_; ++i) { - std::string oprf = sender_oprf.substr( - (oprf_idx + hash_idx) * compare_bytes_size, compare_bytes_size); - sender_oprf_set[hash_idx].insert(std::move(oprf)); - - oprf_idx += cuckoo_options_.num_hash; - } - }; - - std::vector> f_set; - for (size_t i = 0; i < cuckoo_options_.num_hash; ++i) { - f_set.push_back(std::async(sender_oprf_insert_proc, i)); - } - - for (size_t i = 0; i < cuckoo_options_.num_hash; ++i) { - f_set[i].get(); - } - - SPDLOG_INFO("after insert unordered_set"); - - SPDLOG_INFO("begin compute intersection"); - - std::vector results2; - - std::vector result_by_hash0(cuckoo_options_.num_hash); - std::vector result_by_hash1(cuckoo_options_.num_hash); - - auto intersection_proc = [&](size_t begin, size_t end, - std::vector *results_vec, - std::vector *result_by_hash) -> void { - for (size_t i = begin; i < end; ++i) { - for (size_t j = 0; j < cuckoo_options_.num_hash; ++j) { - auto it = sender_oprf_set[j].find(oprf_encode_vec[i]); - if (it != sender_oprf_set[j].end()) { - (*results_vec).emplace_back(items[i]); - (*result_by_hash)[j]++; - break; - } - } - } - }; - - std::future f_intersection = - std::async(intersection_proc, items.size() / 2, items.size(), &results2, - &result_by_hash1); - - intersection_proc(0, items.size() / 2, &results_, &result_by_hash0); - - f_intersection.get(); - - SPDLOG_INFO("result:{}, result2:{}", results_.size(), results2.size()); - - results_.insert(results_.end(), results2.begin(), results2.end()); - - result_by_hash0[0] += result_by_hash1[0]; - result_by_hash0[1] += result_by_hash1[1]; - SPDLOG_INFO("hash_count0:{}, hash_count1:{}", result_by_hash0[0], - result_by_hash0[1]); - - // sender use only one cuckooHash to insert SimpleHash - // may leak some information about receiver's items hash - if ((result_by_hash0[0] == 0) || (result_by_hash0[1] == 0)) { - SPDLOG_WARN("*** may be attacked"); - } - - SPDLOG_INFO("oprf_count:{} intersection size:{}", oprf_count, - results_.size()); - - SPDLOG_INFO("after compute intersection"); -} - -} // namespace psi::bc22 diff --git a/psi/bc22/bc22_psi.h b/psi/bc22/bc22_psi.h deleted file mode 100644 index 1094102e..00000000 --- a/psi/bc22/bc22_psi.h +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2021 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include -#include - -#include "absl/types/span.h" -#include "yacl/base/int128.h" - -#include "psi/bc22/generalized_cuckoo_hash.h" -#include "psi/utils/communication.h" -#include "psi/utils/serialize.h" - -namespace psi::bc22 { - -// PSI from Pseudorandom Correlation Generators -// https://eprint.iacr.org/2022/334 -// Journées C2 2022 – Hendaye April 10-15 2022 (inria.fr) - -// VOLE -// Wolverine: Fast, Scalable, and Communication-Efficient -// Zero-Knowledge Proofs for Boolean and Arithmetic Circuits -// https://eprint.iacr.org/2020/925 -// https://github.com/emp-toolkit/emp-zk - -class Bc22PcgPsi { - public: - Bc22PcgPsi(std::shared_ptr link_ctx, PsiRoleType role); - - void RunPsi(absl::Span items); - - std::vector GetIntersection() { - if (role_ == PsiRoleType::Receiver) { - return results_; - } else { - YACL_THROW("Bc22PcgPsi only Receiver get intersection"); - } - } - - private: - // exchange items number, compute compare bytes size - void ExchangeItemsNumber(size_t self_item_num); - - // mBaRK-OPRF sender/receiver - std::string RunmBaRKOprfSender(absl::Span items, - size_t compare_bytes_size); - - std::vector RunmBaRKOprfReceiver( - absl::Span items, size_t compare_bytes_size); - - // send/recv oprf - void PcgPsiSendOprf(absl::Span items, - const std::string &oprfs, size_t compare_bytes_size); - - void PcgPsiRecvOprf(absl::Span items, - const std::vector &oprf_encode_vec, size_t); - - // cuckoo_options - CuckooIndex::Options cuckoo_options_; - - // Provides the link for the rank world. - std::shared_ptr link_ctx_; - - // psi role sender/receiver - PsiRoleType role_; - - // batch send/recv data size - size_t batch_size_; - - // peer's item size - size_t peer_items_num_ = 0; - - // intersection result - std::vector results_; -}; - -} // namespace psi::bc22 diff --git a/psi/bc22/bc22_psi_benchmark.cc b/psi/bc22/bc22_psi_benchmark.cc deleted file mode 100644 index d0c23246..00000000 --- a/psi/bc22/bc22_psi_benchmark.cc +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2021 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include - -#include "absl/strings/escaping.h" -#include "absl/strings/str_split.h" -#include "benchmark/benchmark.h" -#include "spdlog/spdlog.h" -#include "yacl/link/test_util.h" - -#include "psi/bc22/bc22_psi.h" - -namespace psi::bc22 { - -namespace { - -std::vector CreateRangeItems(size_t start_pos, size_t size) { - std::vector ret(size); - - auto gen_items_proc = [&](size_t begin, size_t end) -> void { - for (size_t i = begin; i < end; ++i) { - ret[i] = std::to_string(start_pos + i); - } - }; - - std::future f_gen = std::async(gen_items_proc, size / 2, size); - - gen_items_proc(0, size / 2); - - f_gen.get(); - - return ret; -} - -std::shared_ptr CreateContext( - int self_rank, yacl::link::ContextDesc& lctx_desc) { - std::shared_ptr link_ctx; - - yacl::link::FactoryBrpc factory; - link_ctx = factory.CreateContext(lctx_desc, self_rank); - link_ctx->ConnectToMesh(); - - return link_ctx; -} - -std::vector> CreateLinks( - const std::string& host_str) { - std::vector hosts = absl::StrSplit(host_str, ','); - yacl::link::ContextDesc lctx_desc; - for (size_t rank = 0; rank < hosts.size(); rank++) { - const std::string id = fmt::format("party{}", rank); - lctx_desc.parties.push_back({id, hosts[rank]}); - } - - auto proc = [&](int self_rank) -> std::shared_ptr { - return CreateContext(self_rank, lctx_desc); - }; - - size_t world_size = hosts.size(); - std::vector>> f_links( - world_size); - for (size_t i = 0; i < world_size; i++) { - f_links[i] = std::async(proc, i); - } - - std::vector> links(world_size); - for (size_t i = 0; i < world_size; i++) { - links[i] = f_links[i].get(); - } - - return links; -} - -constexpr char kLinkAddrAB[] = "127.0.0.1:9532,127.0.0.1:9533"; -constexpr uint32_t kLinkRecvTimeout = 30 * 60 * 1000; -constexpr uint32_t kLinkWindowSize = 16; - -} // namespace - -static void BM_PcgPsi(benchmark::State& state) { - for (auto _ : state) { - state.PauseTiming(); - size_t n = state.range(0); - auto alice_data = CreateRangeItems(1, n); - auto bob_data = CreateRangeItems(2, n); - - auto ctxs = CreateLinks(kLinkAddrAB); - - ctxs[0]->SetThrottleWindowSize(kLinkWindowSize); - ctxs[1]->SetThrottleWindowSize(kLinkWindowSize); - - ctxs[0]->SetRecvTimeout(kLinkRecvTimeout); - ctxs[1]->SetRecvTimeout(kLinkRecvTimeout); - - state.ResumeTiming(); - - Bc22PcgPsi pcg_psi_send(ctxs[0], PsiRoleType::Sender); - Bc22PcgPsi pcg_psi_recv(ctxs[1], PsiRoleType::Receiver); - - std::future send_thread = - std::async([&] { pcg_psi_send.RunPsi(alice_data); }); - - std::future recv_thread = - std::async([&] { return pcg_psi_recv.RunPsi(bob_data); }); - - send_thread.get(); - recv_thread.get(); - - std::vector intersection = pcg_psi_recv.GetIntersection(); - - SPDLOG_INFO("intersection:{}", intersection.size()); - auto stats0 = ctxs[0]->GetStats(); - auto stats1 = ctxs[1]->GetStats(); - SPDLOG_INFO("sender ctx0 sent_bytes:{} recv_bytes:{}", - stats0->sent_bytes.load(), stats0->recv_bytes.load()); - SPDLOG_INFO("receiver ctx1 sent_bytes:{} recv_bytes:{}", - stats1->sent_bytes.load(), stats1->recv_bytes.load()); - } -} - -// [256k, 512k, 1m, 2m, 4m, 8m, 16m] -BENCHMARK(BM_PcgPsi) - ->Unit(benchmark::kMillisecond) - ->Arg(256 << 10) - ->Arg(512 << 10) - ->Arg(1 << 20) - ->Arg(2 << 20) - ->Arg(4 << 20) - ->Arg(8 << 20) - ->Arg(16 << 20); - -} // namespace psi::bc22 diff --git a/psi/bc22/bc22_psi_test.cc b/psi/bc22/bc22_psi_test.cc deleted file mode 100644 index de4af50a..00000000 --- a/psi/bc22/bc22_psi_test.cc +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2021 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "psi/bc22/bc22_psi.h" - -#include -#include -#include -#include - -#include "absl/container/flat_hash_set.h" -#include "absl/strings/escaping.h" -#include "gtest/gtest.h" -#include "spdlog/spdlog.h" -#include "yacl/link/test_util.h" -#include "yacl/utils/parallel.h" - -namespace psi::bc22 { - -namespace { - -std::vector CreateRangeItems(size_t start_pos, size_t size) { - std::vector ret(size); - - auto gen_items_proc = [&](size_t begin, size_t end) -> void { - for (size_t i = begin; i < end; ++i) { - ret[i] = std::to_string(start_pos + i); - } - }; - - std::future f_gen = std::async(gen_items_proc, size / 2, size); - - gen_items_proc(0, size / 2); - - f_gen.get(); - - return ret; -} - -std::vector GetIntersection( - const std::vector& items_a, - const std::vector& items_b) { - absl::flat_hash_set set(items_a.begin(), items_a.end()); - std::vector ret; - for (const auto& s : items_b) { - if (set.count(s) != 0) { - ret.push_back(s); - } - } - return ret; -} - -// Special test data -// [20000000, ] [20000001, ] -// 5560969 和 21906809 2-hash simple-hash conflict -// constexpr size_t kTestItemsSize = 9000000; -// -// constexpr size_t kTestItemsSize = 10000000; - -} // namespace - -class PcgPsiTest : public testing::TestWithParam {}; - -TEST_P(PcgPsiTest, Works) { - auto params = GetParam(); - size_t items_size = params; - auto ctxs = yacl::link::test::SetupWorld(2); - - std::vector alice_data = CreateRangeItems(20000000, items_size); - std::vector bob_data = CreateRangeItems(20000001, items_size); - - std::vector intersection_std = - GetIntersection(alice_data, bob_data); - - std::sort(intersection_std.begin(), intersection_std.end()); - - Bc22PcgPsi pcg_psi_send(ctxs[0], PsiRoleType::Sender); - Bc22PcgPsi pcg_psi_recv(ctxs[1], PsiRoleType::Receiver); - - std::future send_thread = - std::async([&] { pcg_psi_send.RunPsi(alice_data); }); - - std::future recv_thread = - std::async([&] { return pcg_psi_recv.RunPsi(bob_data); }); - - send_thread.get(); - recv_thread.get(); - std::vector intersection = pcg_psi_recv.GetIntersection(); - - std::sort(intersection.begin(), intersection.end()); - - size_t std_size = intersection_std.size(); - size_t size2 = intersection.size(); - SPDLOG_INFO("intersection_std size:{} intersection_size:{} diff: {}", - std_size, size2, (std_size - size2)); - - EXPECT_EQ(intersection, intersection_std); - - auto stats0 = ctxs[0]->GetStats(); - auto stats1 = ctxs[1]->GetStats(); - SPDLOG_INFO("sender ctx0 sent_bytes:{} recv_bytes:{}", - stats0->sent_bytes.load(), stats0->recv_bytes.load()); - SPDLOG_INFO("receiver ctx1 sent_bytes:{} recv_bytes:{}", - stats1->sent_bytes.load(), stats1->recv_bytes.load()); -} - -INSTANTIATE_TEST_SUITE_P(Works_Instances, PcgPsiTest, - testing::Values(10000, 100000, 1000000)); - -} // namespace psi::bc22 diff --git a/psi/bc22/emp_vole.cc b/psi/bc22/emp_vole.cc deleted file mode 100644 index eefef41f..00000000 --- a/psi/bc22/emp_vole.cc +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2021 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "psi/bc22/emp_vole.h" - -#include - -#include "spdlog/spdlog.h" -#include "yacl/crypto/rand/rand.h" - -namespace psi::bc22 { - -WolverineVole::WolverineVole(PsiRoleType psi_role, - std::shared_ptr link_ctx) - : party_((psi_role == PsiRoleType::Sender) ? emp::ALICE : emp::BOB), - link_ctx_(std::move(link_ctx)) { - // set EmpIoAdapter - for (size_t i = 0; i < kVoleSilentOTThreads; ++i) { - silent_ios_[i] = std::make_unique(link_ctx_); - ios_[i] = silent_ios_[i].get(); - } - - emp_zk_vole_ = std::make_unique>( - party_, kVoleSilentOTThreads, ios_); - - SPDLOG_INFO("party {}, begin svole setup", - (party_ == emp::ALICE) ? "alice" : "bob"); - Setup(); - SPDLOG_INFO("party {}, after svole setup", - (party_ == emp::ALICE) ? "alice" : "bob"); -} - -void WolverineVole::Setup() { - if (party_ == emp::ALICE) { - delta_ = yacl::crypto::SecureRandSeed(); - delta_ = - delta_ & (static_cast(0xFFFFFFFFFFFFFFFFLL)); - delta_ = mod(delta_, pr); - emp_zk_vole_->setup(delta_); - } else { - emp_zk_vole_->setup(); - } -} - -std::vector WolverineVole::Extend(size_t vole_num) { - std::vector vole_blocks(vole_num); - - emp_zk_vole_->extend(vole_blocks.data(), vole_blocks.size()); - - return vole_blocks; -} - -std::vector GetPolynomialCoefficients( - const std::vector& bin_data) { - YACL_ENFORCE(bin_data.size() <= 3); - - std::vector block_coeffs(bin_data.size()); - - std::vector t(bin_data.size()); - for (size_t i = 0; i < bin_data.size(); ++i) { - YACL_ENFORCE(bin_data[0].length() <= sizeof(WolverineVoleFieldType), - "{}>{}", bin_data[0].length(), sizeof(WolverineVoleFieldType)); - t[i] = 0; - std::memcpy(&t[i], bin_data[i].data(), bin_data[i].length()); - t[i] = mod(t[i], pr); - } - - if (bin_data.size() == 1) { - block_coeffs[0] = pr - mod(t[0], pr); - } else if (bin_data.size() == 2) { - block_coeffs[0] = mult_mod(t[0], t[1]); - block_coeffs[1] = pr - mod(t[0] + t[1], pr); - } else if (bin_data.size() == 3) { - WolverineVoleFieldType d01; - WolverineVoleFieldType d02; - WolverineVoleFieldType d12; - - d01 = mult_mod(t[0], t[1]); - d02 = mult_mod(t[0], t[2]); - d12 = mult_mod(t[1], t[2]); - - WolverineVoleFieldType tmp = mult_mod(d01, t[2]); - while (tmp > pr) { - tmp -= pr; - } - - block_coeffs[0] = pr - tmp; - - block_coeffs[1] = mod(d01 + d02 + d12, pr); - block_coeffs[2] = pr - mod(t[0] + t[1] + t[2], pr); - } - - return block_coeffs; -} - -WolverineVoleFieldType EvaluatePolynomial( - absl::Span coeffs, WolverineVoleFieldType x, - WolverineVoleFieldType high_coeff) { - std::vector xp(coeffs.size() + 1); - - std::vector block_coeffs; - for (unsigned __int128 coeff : coeffs) { - block_coeffs.push_back(static_cast(coeff)); - } - - WolverineVoleFieldType xx = mod(x, pr); - - xp[0] = 1; - block_coeffs.push_back(high_coeff); - - for (size_t i = 1; i < xp.size(); ++i) { - xp[i] = mult_mod(xp[i - 1], xx); - } - - WolverineVoleFieldType result = 0; - for (size_t i = 0; i < xp.size(); ++i) { - result += mult_mod(xp[i], block_coeffs[i]); - result = mod(result, pr); - } - - return result; -} - -WolverineVoleFieldType EvaluatePolynomial( - absl::Span coeffs, std::string_view x, - WolverineVoleFieldType high_coeff) { - WolverineVoleFieldType block_x = 0; - - YACL_ENFORCE(x.length() <= sizeof(WolverineVoleFieldType)); - std::memcpy(&block_x, x.data(), x.length()); - - return EvaluatePolynomial(coeffs, block_x, high_coeff); -} - -} // namespace psi::bc22 diff --git a/psi/bc22/emp_vole.h b/psi/bc22/emp_vole.h deleted file mode 100644 index 0d7ed048..00000000 --- a/psi/bc22/emp_vole.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2021 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include -#include - -#include "absl/types/span.h" -#include "emp-tool/utils/block.h" -#include "emp-tool/utils/f2k.h" -#include "emp-zk/emp-vole/emp-vole.h" -#include "yacl/base/exception.h" -#include "yacl/link/link.h" - -#include "psi/utils/communication.h" -#include "psi/utils/emp_io_adapter.h" -#include "psi/utils/serialize.h" - -namespace psi::bc22 { - -inline constexpr size_t kVoleSilentOTThreads = 1; - -// VOLE -// Wolverine: Fast, Scalable, and Communication-Efficient -// Zero-Knowledge Proofs for Boolean and Arithmetic Circuits -// https://eprint.iacr.org/2020/925 -// https://github.com/emp-toolkit/emp-zk - -using WolverineVoleFieldType = __uint128_t; - -class WolverineVole { - public: - WolverineVole(PsiRoleType psi_role, - std::shared_ptr link_ctx); - - // extend baseVole get vole_num voles - // Filed: mersenne prime 2^61 - 1 - // wi = delta * ui + vi - // alice : delta, wi - // bob : ui || vi as one __uint128_t - std::vector Extend(size_t vole_num); - - // get delta - WolverineVoleFieldType Delta() { - if (party_ == emp::ALICE) { - return delta_; - } else { - YACL_THROW("party: {} without delta", party_); - } - } - - private: - // setup - // alice set delta - // call baseVole - void Setup(); - - int party_; - std::shared_ptr link_ctx_; - - WolverineVoleFieldType delta_; - - std::array, kVoleSilentOTThreads> silent_ios_; - EmpIoAdapter *ios_[kVoleSilentOTThreads]; - std::unique_ptr> emp_zk_vole_; -}; - -std::vector GetPolynomialCoefficients( - const std::vector &bin_data); - -WolverineVoleFieldType EvaluatePolynomial( - absl::Span coeffs, std::string_view x, - WolverineVoleFieldType high_coeff = 1); - -WolverineVoleFieldType EvaluatePolynomial( - absl::Span coeffs, WolverineVoleFieldType x, - WolverineVoleFieldType high_coeff); - -} // namespace psi::bc22 diff --git a/psi/bc22/emp_vole_test.cc b/psi/bc22/emp_vole_test.cc deleted file mode 100644 index 4fb01af0..00000000 --- a/psi/bc22/emp_vole_test.cc +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2021 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "psi/bc22/emp_vole.h" - -#include -#include -#include -#include - -#include "absl/strings/escaping.h" -#include "gtest/gtest.h" -#include "spdlog/spdlog.h" -#include "yacl/crypto/rand/rand.h" -#include "yacl/link/test_util.h" - -#include "psi/utils/serialize.h" - -namespace psi::bc22 { - -class EmpVoleTest : public testing::TestWithParam {}; - -TEST_P(EmpVoleTest, Works) { - auto params = GetParam(); - - auto ctxs = yacl::link::test::SetupWorld(2); - - uint64_t vole_need = params; - - std::vector vole_alice; - std::vector vole_bob; - - WolverineVoleFieldType delta = 0; - - std::future vole_alice_thread = std::async([&] { - WolverineVole vole(PsiRoleType::Sender, ctxs[0]); - - vole_alice = vole.Extend(vole_need); - delta = vole.Delta(); - }); - - std::future vole_bob_thread = std::async([&] { - WolverineVole vole(PsiRoleType::Receiver, ctxs[1]); - - vole_bob = vole.Extend(vole_need); - }); - - vole_alice_thread.get(); - vole_bob_thread.get(); - - EXPECT_EQ(vole_alice.size(), vole_need); - EXPECT_EQ(vole_bob.size(), vole_need); - - auto stats0 = ctxs[0]->GetStats(); - auto stats1 = ctxs[1]->GetStats(); - SPDLOG_INFO("sender/alice ctx0 sent_bytes:{} recv_bytes:{}", - stats0->sent_bytes.load(), stats0->recv_bytes.load()); - SPDLOG_INFO("receiver/bob ctx1 sent_bytes:{} recv_bytes:{}", - stats1->sent_bytes.load(), stats1->recv_bytes.load()); - - // check vole - // wi = delta * ui + vi - // sender/alice : delta wi - // receiver/bob : ui || vi as one __uint128_t - for (size_t i = 0; i < vole_need; ++i) { - // delta * ui - WolverineVoleFieldType tmp = mod(delta * (vole_bob[i] >> 64), pr); - // delta * ui + vi - tmp = mod(tmp + vole_alice[i], pr); - // check wi = delta * ui + vi - EXPECT_EQ(tmp, (vole_bob[i] & 0xFFFFFFFFFFFFFFFFLL)); - } -} - -INSTANTIATE_TEST_SUITE_P(Works_Instances, EmpVoleTest, - testing::Values(10000, 100000, 1000000)); - -TEST(EmpVoleTest, PolynomialTest) { - std::mt19937 rng(yacl::crypto::SecureRandU64()); - - for (size_t idx = 1; idx < 4; ++idx) { - std::vector points(idx); - - for (auto& point : points) { - point = std::to_string(rng()); - } - - std::vector<__uint128_t> coeffs = GetPolynomialCoefficients(points); - - for (auto& point : points) { - WolverineVoleFieldType result = - EvaluatePolynomial(absl::MakeSpan(coeffs), point); - WolverineVoleFieldType tt; - memcpy(&tt, &result, sizeof(tt)); - - EXPECT_EQ(result, 0); - } - } -} - -} // namespace psi::bc22 diff --git a/psi/bc22/generalized_cuckoo_hash.cc b/psi/bc22/generalized_cuckoo_hash.cc deleted file mode 100644 index c97c8618..00000000 --- a/psi/bc22/generalized_cuckoo_hash.cc +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2021 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "psi/bc22/generalized_cuckoo_hash.h" - -#include -#include - -#include "absl/strings/escaping.h" -#include "spdlog/spdlog.h" -#include "yacl/base/int128.h" -#include "yacl/crypto/rand/rand.h" -#include "yacl/utils/parallel.h" - -namespace psi::bc22 { - -namespace { - -constexpr size_t kDefaultHashNum = 2; - -// Permutation-Based Hashing -// [PSSZ15] In USENIX Security 2015 -// Phasing: Private set intersection using permutation-based hashing -// [BC22] Section 2.4 -// h0(x) = hash(x), -// h1(x) = h0(x) xor fingerprint(x). -std::vector GetBinIdx(const CuckooIndex::Options &options, - uint128_t item_hash, uint64_t item_hash_u64) { - YACL_ENFORCE(options.num_hash == kDefaultHashNum); - size_t num_bins = options.NumBins(); - - CuckooIndex::HashRoom hash_room(item_hash); - - size_t bin_idx = hash_room.GetHash(0); - - std::vector hash_bin_idx(options.num_hash); - - hash_bin_idx[0] = bin_idx % num_bins; - hash_bin_idx[1] = (bin_idx ^ item_hash_u64) % num_bins; - - return hash_bin_idx; -} - -} // namespace - -CuckooIndex::Options GetCuckooHashOption(size_t bin_size, size_t hash_num, - size_t items_size) { - CuckooIndex::Options options; - - options.num_input = items_size; - options.num_stash = 0; - options.num_hash = hash_num; - - YACL_ENFORCE(hash_num == kDefaultHashNum, "just support 2 hash"); - - if (hash_num == kDefaultHashNum) { - if (bin_size == 2) { - options.scale_factor = 1; - } else if (bin_size == 3) { - options.scale_factor = 0.6; - } else { - YACL_THROW("unsupported"); - } - } else { - YACL_THROW("unsupported"); - } - return options; -} - -GeneralizedCuckooHashTable::GeneralizedCuckooHashTable( - CuckooIndex::Options options, size_t bin_data_num, uint128_t seed) - : gch_options_(options), - max_items_per_bin_(bin_data_num), - seed_(seed), - gen_(yacl::crypto::SecureRandU64()) { - size_t table_size = gch_options_.NumBins(); - bins_.resize(table_size); - - uniform_hash_idx_ = - std::uniform_int_distribution(0, gch_options_.num_hash - 1); - uniform_data_idx_ = - std::uniform_int_distribution(0, max_items_per_bin_ - 1); -} - -void GeneralizedCuckooHashTable::Insert(yacl::ByteContainerView item_data, - size_t input_offset) { - CuckooIndex::Bin candidate; - candidate.set_encoded(input_offset); - - int64_t level = gch_options_.max_try_count; - size_t bin_idx; - while ((level--) != 0) { - size_t rand_hash_idx = uniform_hash_idx_(gen_); - - for (uint32_t i = 0; i < gch_options_.num_hash; i++) { - size_t hash_idx = (rand_hash_idx + i) % gch_options_.num_hash; - - bin_idx = hashes_[candidate.InputIdx()][hash_idx]; - - if (bins_[bin_idx].size() < max_items_per_bin_) { - uint64_t next_candid = - CuckooIndex::Bin::Encode(candidate.InputIdx(), hash_idx); - candidate.set_encoded(next_candid); - - bins_[bin_idx].push_back(candidate); - inserted_items_++; - - return; - } - } - - // random select bin_idx and idx in bin, swap candidate - size_t rand_data_idx = uniform_data_idx_(gen_); - - rand_hash_idx = uniform_hash_idx_(gen_); - - bin_idx = hashes_[candidate.InputIdx()][rand_hash_idx]; - - uint64_t next_candid = - CuckooIndex::Bin::Encode(candidate.InputIdx(), rand_hash_idx); - candidate.set_encoded(next_candid); - - candidate = CuckooIndex::Bin( - bins_[bin_idx][rand_data_idx].Swap(candidate.encoded())); - } - - YACL_THROW( - "Error insert, level:{} insert item_data:{}", level, - absl::BytesToHexString(absl::string_view( - reinterpret_cast(item_data.data()), item_data.size()))); -} - -void GeneralizedCuckooHashTable::Insert(yacl::ByteContainerView item) { - uint128_t item_hash = yacl::crypto::Blake3_128(item); - size_t input_offset = hashes_.size(); - - // hash_bin_idx - std::pair items_hash_u64 = - yacl::DecomposeUInt128(item_hash); - std::vector hash_bin_idx = - GetBinIdx(gch_options_, item_hash, items_hash_u64.first); - hashes_.push_back(hash_bin_idx); - - items_hash_low64_.push_back(items_hash_u64.second); - - Insert(item, input_offset); -} - -void GeneralizedCuckooHashTable::Insert(absl::Span items) { - size_t input_offset = items_hash_low64_.size(); - items_hash_low64_.resize(input_offset + items.size()); - hashes_.resize(input_offset + items.size()); - - yacl::parallel_for(0, items.size(), [&](int64_t begin, int64_t end) { - for (int i = begin; i < end; ++i) { - uint128_t item_hash = yacl::crypto::Blake3_128(items[i]); - - std::pair items_hash_u64 = - yacl::DecomposeUInt128(item_hash); - - items_hash_low64_[input_offset + i] = items_hash_u64.second; - hashes_[input_offset + i] = - GetBinIdx(gch_options_, item_hash, items_hash_u64.first); - } - }); - - for (size_t i = 0; i < items.size(); ++i) { - Insert(items[i], input_offset + i); - } -} - -SimpleHashTable::SimpleHashTable(CuckooIndex::Options options, uint128_t seed) - : gch_options_(options), seed_(seed) { - size_t table_size = gch_options_.NumBins(); - - bins_.resize(table_size); -} - -void SimpleHashTable::Insert(yacl::ByteContainerView item_data, - const std::vector &hash_bin_idx) { - CuckooIndex::Bin candidate; - candidate.set_encoded(inserted_items_); - - std::set idx_set(hash_bin_idx.begin(), hash_bin_idx.end()); - - size_t bin_idx; - - if (idx_set.size() < hash_bin_idx.size()) { - SPDLOG_WARN("hash conflict: bin_idx:{}, data:{}", hash_bin_idx[0], - absl::BytesToHexString(absl::string_view( - reinterpret_cast(item_data.data()), - item_data.size()))); - - conflict_idx_.push_back(candidate.InputIdx()); - - bin_idx = hash_bin_idx[0]; - uint64_t next_candid = CuckooIndex::Bin::Encode(candidate.InputIdx(), 0); - candidate.set_encoded(next_candid); - bins_[bin_idx].push_back(candidate); - - } else { - for (size_t i = 0; i < gch_options_.num_hash; i++) { - bin_idx = hash_bin_idx[i]; - - uint64_t next_candid = CuckooIndex::Bin::Encode(candidate.InputIdx(), i); - candidate.set_encoded(next_candid); - bins_[bin_idx].push_back(candidate); - } - } - - inserted_items_++; -} - -void SimpleHashTable::Insert(yacl::ByteContainerView item) { - uint128_t item_hash = yacl::crypto::Blake3_128(item); - - std::vector hash_bin_idx; - std::pair item_hash_u64 = - yacl::DecomposeUInt128(item_hash); - - items_hash_low64_.push_back(item_hash_u64.second); - - hash_bin_idx = GetBinIdx(gch_options_, item_hash, item_hash_u64.first); - - Insert(item, hash_bin_idx); -} - -void SimpleHashTable::Insert(absl::Span items) { - size_t input_offset = items_hash_low64_.size(); - items_hash_low64_.resize(input_offset + items.size()); - - std::vector> hash_bin_idx(items.size()); - - yacl::parallel_for(0, items.size(), [&](int64_t begin, int64_t end) { - for (int i = begin; i < end; ++i) { - uint128_t item_hash = yacl::crypto::Blake3_128(items[i]); - - std::pair item_hash_u64 = - yacl::DecomposeUInt128(item_hash); - hash_bin_idx[i] = GetBinIdx(gch_options_, item_hash, item_hash_u64.first); - - items_hash_low64_[input_offset + i] = item_hash_u64.second; - } - }); - - for (size_t i = 0; i < items.size(); ++i) { - Insert(items[i], hash_bin_idx[i]); - } -} - -} // namespace psi::bc22 diff --git a/psi/bc22/generalized_cuckoo_hash.h b/psi/bc22/generalized_cuckoo_hash.h deleted file mode 100644 index 1dd84d82..00000000 --- a/psi/bc22/generalized_cuckoo_hash.h +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2021 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include "absl/types/span.h" -#include "yacl/base/byte_container_view.h" -#include "yacl/base/exception.h" -#include "yacl/base/int128.h" -#include "yacl/crypto/hash/hash_utils.h" - -#include "psi/utils/cuckoo_index.h" - -namespace psi::bc22 { - -// GeneralizedCuckooHash options -// now support (2,2), (3,2) gch -// Reference: -// DW07. M. Dietzfelbinger and C. Weidling. -// Balanced allocation and dictionaries with tightly packed constant size bins -// -CuckooIndex::Options GetCuckooHashOption(size_t bin_size, size_t hash_num, - size_t items_size); - -// abstract interface for psi hash table -class IPsiHashTable { - public: - virtual ~IPsiHashTable() {} - - virtual void Insert(absl::Span items) = 0; -}; - -class GeneralizedCuckooHashTable : public IPsiHashTable { - public: - explicit GeneralizedCuckooHashTable(CuckooIndex::Options options, - size_t bin_data_num, uint128_t seed = 0); - - void Insert(yacl::ByteContainerView item_data, size_t input_offset); - void Insert(yacl::ByteContainerView item); - void Insert(absl::Span items) override; - - const std::vector> &bins() const { - return bins_; - } - - const std::vector &GetItemsHashLow64() const { - return items_hash_low64_; - } - - // Returns the current fill rate of the hash table and stash. - inline double FillRate() const noexcept { - return static_cast(inserted_items_) / - (static_cast(gch_options_.NumBins()) * max_items_per_bin_); - } - - const CuckooIndex::Options &GetCuckooOptions() const { return gch_options_; } - - size_t GetMaxItemsPerBin() const { return max_items_per_bin_; } - - protected: - CuckooIndex::Options gch_options_; - // max data number per bin - size_t max_items_per_bin_; - uint128_t seed_; - std::vector> bins_; - std::vector> hashes_; - std::vector items_hash_low64_; - size_t inserted_items_ = 0; - - // Randomness source for location function sampling. - std::mt19937_64 gen_; - - std::uniform_int_distribution uniform_hash_idx_; - std::uniform_int_distribution uniform_data_idx_; -}; - -class SimpleHashTable : public IPsiHashTable { - public: - explicit SimpleHashTable(CuckooIndex::Options options, uint128_t seed = 0); - - void Insert(yacl::ByteContainerView item_data, - const std::vector &hash_bin_idx); - void Insert(yacl::ByteContainerView item); - void Insert(absl::Span items) override; - - const std::vector> &bins() const { - return bins_; - } - - const std::vector &GetItemsHashLow64() const { - return items_hash_low64_; - } - - const CuckooIndex::Options &GetCuckooOptions() const { return gch_options_; } - - const std::vector &GetConflictIdx() const { return conflict_idx_; } - - protected: - CuckooIndex::Options gch_options_; - uint128_t seed_; - std::vector> bins_; - std::vector items_hash_low64_; - size_t inserted_items_ = 0; - std::vector conflict_idx_; -}; - -} // namespace psi::bc22 diff --git a/psi/bc22/generalized_cuckoo_hash_test.cc b/psi/bc22/generalized_cuckoo_hash_test.cc deleted file mode 100644 index e97b6c47..00000000 --- a/psi/bc22/generalized_cuckoo_hash_test.cc +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2021 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "psi/bc22/generalized_cuckoo_hash.h" - -#include -#include - -#include "absl/strings/escaping.h" -#include "gtest/gtest.h" -#include "spdlog/spdlog.h" - -namespace psi::bc22 { - -namespace { - -std::vector CreateRangeItems(size_t begin, size_t size) { - std::vector ret; - for (size_t i = 0; i < size; i++) { - ret.push_back(std::to_string(begin + i)); - } - return ret; -} - -} // namespace - -class GchTest : public testing::TestWithParam {}; - -TEST(GchTest, BasicTest) { - std::random_device rd; - std::mt19937 rng(rd()); - - std::vector items(1000); - for (size_t idx = 1; idx < items.size(); ++idx) { - items[idx] = std::to_string(rng()); - } - - size_t bin_size = 2; - size_t hash_num = 2; - CuckooIndex::Options cuckoo_options = - GetCuckooHashOption(bin_size, hash_num, items.size()); - - GeneralizedCuckooHashTable gch(cuckoo_options, bin_size, 0); - SimpleHashTable simple_table(cuckoo_options); - - gch.Insert(absl::MakeSpan(items)); - simple_table.Insert(absl::MakeSpan(items)); - - const std::vector> &bins = gch.bins(); - const std::vector> &simple_table_bins = - simple_table.bins(); - - SPDLOG_INFO("bin size: {}, simple_table_bins size: {}", bins.size(), - simple_table_bins.size()); - - size_t bin_items[4] = { - 0, - }; - size_t c1 = 0; - size_t c2 = 0; - std::vector hash_data_num(hash_num); - memset(hash_data_num.data(), 0, hash_data_num.size() * sizeof(size_t)); - - for (size_t i = 0; i < bins.size(); ++i) { - if (simple_table_bins[i].size() < bins[i].size()) { - SPDLOG_INFO("****{}****", i); - } - for (const auto &bin : bins[i]) { - // size_t item_idx = bins[i][j].InputIdx(); - size_t hash_idx = bin.HashIdx(); - hash_data_num[hash_idx]++; - } - bin_items[bins[i].size()]++; - c1 += bins[i].size(); - c2 += simple_table_bins[i].size(); - } - SPDLOG_INFO("bin0:{}, bin1:{}, bin2:{}", bin_items[0], bin_items[1], - bin_items[2]); - SPDLOG_INFO("hash_data_num[0]:{} hash_data_num[1]:{}", hash_data_num[0], - hash_data_num[1]); - SPDLOG_INFO("c1:{} c2:{}", c1, c2); - SPDLOG_INFO("FillRate:{}", gch.FillRate()); - - // conflict index - const std::vector conflict_idx = simple_table.GetConflictIdx(); - SPDLOG_INFO("conflict_idx: {}", conflict_idx.size()); -} - -TEST(GchTest, CuckooHashTest) { - std::vector alice_data = CreateRangeItems(20000000, 30000); - - size_t bin_size = 3; - size_t hash_num = 2; - CuckooIndex::Options cuckoo_options = - GetCuckooHashOption(bin_size, hash_num, alice_data.size()); - - GeneralizedCuckooHashTable gch(cuckoo_options, bin_size, 0); - SimpleHashTable simple_table(cuckoo_options); - - gch.Insert(absl::MakeSpan(alice_data)); - simple_table.Insert(absl::MakeSpan(alice_data)); - - const std::vector> &bins = gch.bins(); - - const std::vector &items_hash = gch.GetItemsHashLow64(); - - SPDLOG_INFO("items size:{} bins:{} items:{}", alice_data.size(), bins.size(), - items_hash.size()); - - EXPECT_EQ(items_hash.size(), alice_data.size()); - - // conflict index - const std::vector conflict_idx = simple_table.GetConflictIdx(); - SPDLOG_INFO("conflict_idx: {}", conflict_idx.size()); -} - -} // namespace psi::bc22 diff --git a/psi/legacy/BUILD.bazel b/psi/legacy/BUILD.bazel index 68080aa0..23a8707b 100644 --- a/psi/legacy/BUILD.bazel +++ b/psi/legacy/BUILD.bazel @@ -86,18 +86,6 @@ psi_cc_library( ], ) -psi_cc_library( - name = "bc22_2party_psi", - srcs = ["bc22_2party_psi.cc"], - hdrs = ["bc22_2party_psi.h"], - deps = [ - ":base_operator", - ":factory", - "//psi/bc22:bc22_psi", - ], - alwayslink = True, -) - psi_cc_library( name = "dp_2party_psi", srcs = ["dp_2party_psi.cc"], @@ -126,7 +114,6 @@ psi_cc_library( psi_cc_library( name = "operator", deps = [ - ":bc22_2party_psi", ":dp_2party_psi", ":ecdh_3party_psi", ":kkrt_2party_psi", diff --git a/psi/legacy/bc22_2party_psi.cc b/psi/legacy/bc22_2party_psi.cc deleted file mode 100644 index 4e942f0d..00000000 --- a/psi/legacy/bc22_2party_psi.cc +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2022 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#include "psi/legacy/bc22_2party_psi.h" - -#include - -#include "psi/bc22/bc22_psi.h" -#include "psi/legacy/factory.h" - -namespace psi { - -Bc22PcgPsiOperator::Options Bc22PcgPsiOperator::ParseConfig( - const MemoryPsiConfig& config, - const std::shared_ptr& lctx) { - return {lctx, config.receiver_rank()}; -} - -Bc22PcgPsiOperator::Bc22PcgPsiOperator(const Options& options) - : PsiBaseOperator(options.lctx), options_(options) {} - -std::vector Bc22PcgPsiOperator::OnRun( - const std::vector& inputs) { - auto role = link_ctx_->Rank() == options_.receiver_rank - ? PsiRoleType::Receiver - : PsiRoleType::Sender; - bc22::Bc22PcgPsi pcg_psi(link_ctx_, role); - pcg_psi.RunPsi(inputs); - if (role == PsiRoleType::Receiver) { - return pcg_psi.GetIntersection(); - } else { - return {}; - } -} - -namespace { - -std::unique_ptr CreateOperator( - const MemoryPsiConfig& config, - const std::shared_ptr& lctx) { - auto options = Bc22PcgPsiOperator::ParseConfig(config, lctx); - return std::make_unique(options); -} - -REGISTER_OPERATOR(BC22_PSI_2PC, CreateOperator); - -} // namespace - -} // namespace psi \ No newline at end of file diff --git a/psi/legacy/bc22_2party_psi.h b/psi/legacy/bc22_2party_psi.h deleted file mode 100644 index d474e856..00000000 --- a/psi/legacy/bc22_2party_psi.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2022 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include "psi/legacy/base_operator.h" - -namespace psi { - -class Bc22PcgPsiOperator : public PsiBaseOperator { - public: - struct Options { - std::shared_ptr lctx; - - size_t receiver_rank; - }; - - static Options ParseConfig(const MemoryPsiConfig& config, - const std::shared_ptr& lctx); - - public: - explicit Bc22PcgPsiOperator(const Options& options); - ~Bc22PcgPsiOperator() = default; - - std::vector OnRun( - const std::vector& inputs) override final; - - private: - Options options_; -}; - -} // namespace psi \ No newline at end of file diff --git a/psi/legacy/bucket_psi_test.cc b/psi/legacy/bucket_psi_test.cc index 502330d3..54919dd1 100644 --- a/psi/legacy/bucket_psi_test.cc +++ b/psi/legacy/bucket_psi_test.cc @@ -257,20 +257,6 @@ INSTANTIATE_TEST_SUITE_P( false, 3, }, - TestParams{ - {5, 6}, - {"id,value\nc测试,c\nb测试,b\na测试,a\nn测试,n\nj测试,j\n", - "id,value\nb测试,b\nc测试,c\nj测试,t\na测试,a\nk测试,k\nq测试," - "q\n"}, - {"id,value\na测试,a\nb测试,b\nc测试,c\nj测试,j\n", - "id,value\na测试,a\nb测试,b\nc测试,c\nj测试,t\n"}, - {{"id"}, {"id"}}, - PsiType::BC22_PSI_2PC, - 64, - true, - false, - 4, - }, TestParams{ {3, 3, 3}, {"id,value\nc测试,c\nb测试,b\na测试,a\n", @@ -481,7 +467,6 @@ INSTANTIATE_TEST_SUITE_P(FailedWorks_Instances, BucketTaskPsiTestFailedTest, // invalid link world size FailedTestParams{3, 0, PsiType::KKRT_PSI_2PC}, FailedTestParams{4, 0, PsiType::ECDH_PSI_2PC}, - FailedTestParams{1, 0, PsiType::BC22_PSI_2PC}, FailedTestParams{2, 0, PsiType::ECDH_PSI_3PC}, // invalid receiver_rank FailedTestParams{3, 4, PsiType::ECDH_PSI_3PC}, diff --git a/psi/legacy/memory_psi.cc b/psi/legacy/memory_psi.cc index 9722a682..7cce4412 100644 --- a/psi/legacy/memory_psi.cc +++ b/psi/legacy/memory_psi.cc @@ -41,8 +41,7 @@ void MemoryPsi::CheckOptions() const { // check world size if (config_.psi_type() == PsiType::ECDH_PSI_2PC || - config_.psi_type() == PsiType::KKRT_PSI_2PC || - config_.psi_type() == PsiType::BC22_PSI_2PC) { + config_.psi_type() == PsiType::KKRT_PSI_2PC) { YACL_ENFORCE(lctx_->WorldSize() == 2, "psi_type:{}, only two parties supported, got " "{}", diff --git a/psi/legacy/memory_psi_test.cc b/psi/legacy/memory_psi_test.cc index f10b8f1f..4b7cfb44 100644 --- a/psi/legacy/memory_psi_test.cc +++ b/psi/legacy/memory_psi_test.cc @@ -152,19 +152,14 @@ INSTANTIATE_TEST_SUITE_P( MemoryTaskTestParams{{0, 3}, 0, PsiType::ECDH_PSI_2PC}, // MemoryTaskTestParams{{3, 0}, 0, PsiType::KKRT_PSI_2PC}, // MemoryTaskTestParams{{0, 0}, 0, PsiType::KKRT_PSI_2PC}, // - MemoryTaskTestParams{{3, 0}, 0, PsiType::BC22_PSI_2PC}, // - MemoryTaskTestParams{{0, 0}, 0, PsiType::BC22_PSI_2PC}, // MemoryTaskTestParams{{4, 3, 0}, 0, PsiType::ECDH_PSI_3PC}, // MemoryTaskTestParams{{4, 3, 0, 6}, 0, PsiType::ECDH_PSI_NPC}, // // - MemoryTaskTestParams{{20, 20}, 10, PsiType::KKRT_PSI_2PC}, // - MemoryTaskTestParams{{20, 17}, 10, PsiType::KKRT_PSI_2PC}, // - MemoryTaskTestParams{{17, 20}, 10, PsiType::KKRT_PSI_2PC}, // - MemoryTaskTestParams{{33, 45}, 20, PsiType::ECDH_PSI_2PC}, // - MemoryTaskTestParams{{100, 100}, 30, PsiType::BC22_PSI_2PC}, // - MemoryTaskTestParams{{200, 100}, 60, PsiType::BC22_PSI_2PC}, // - MemoryTaskTestParams{{100, 200}, 50, PsiType::BC22_PSI_2PC}, // + MemoryTaskTestParams{{20, 20}, 10, PsiType::KKRT_PSI_2PC}, // + MemoryTaskTestParams{{20, 17}, 10, PsiType::KKRT_PSI_2PC}, // + MemoryTaskTestParams{{17, 20}, 10, PsiType::KKRT_PSI_2PC}, // + MemoryTaskTestParams{{33, 45}, 20, PsiType::ECDH_PSI_2PC}, // MemoryTaskTestParams{{20, 17, 14}, 10, PsiType::ECDH_PSI_3PC}, // MemoryTaskTestParams{{20, 17, 14, 30}, 10, PsiType::ECDH_PSI_NPC}, // @@ -197,7 +192,6 @@ INSTANTIATE_TEST_SUITE_P(FailedWorks_Instances, MemoryTaskPsiTestFailedTest, // invalid link world size FailedTestParams{3, 0, PsiType::KKRT_PSI_2PC}, FailedTestParams{4, 0, PsiType::ECDH_PSI_2PC}, - FailedTestParams{1, 0, PsiType::BC22_PSI_2PC}, FailedTestParams{2, 0, PsiType::ECDH_PSI_3PC}, // invalid receiver_rank FailedTestParams{3, 4, PsiType::ECDH_PSI_3PC}, diff --git a/psi/proto/psi.proto b/psi/proto/psi.proto index fbd654ed..47e7a638 100644 --- a/psi/proto/psi.proto +++ b/psi/proto/psi.proto @@ -20,16 +20,17 @@ package psi; // The algorithm type of psi. enum PsiType { + reserved 3; + INVALID_PSI_TYPE = 0; // DDH based PSI ECDH_PSI_2PC = 1; + // Efficient Batched Oblivious PRF with Applications to Private Set // Intersection https://eprint.iacr.org/2016/799.pdf KKRT_PSI_2PC = 2; - // PSI from Pseudorandom Correlation Generators - // https://eprint.iacr.org/2022/334 - BC22_PSI_2PC = 3; + // Multi-party PSI based on ECDH (Say A, B, C (receiver)) // notice: two-party intersection cardinarlity leak (|A intersect B|) ECDH_PSI_3PC = 4; @@ -37,6 +38,7 @@ enum PsiType { // Iterative running 2-party ecdh psi to get n-party PSI. // Notice: two-party intersection leak ECDH_PSI_NPC = 5; + // Iterative running 2-party kkrt psi to get n-party PSI. // Notice: two-party intersection leak KKRT_PSI_NPC = 6; diff --git a/psi/version.h b/psi/version.h index 373379c9..6ba41933 100644 --- a/psi/version.h +++ b/psi/version.h @@ -17,4 +17,4 @@ #define PSI_VERSION_MAJOR 0 #define PSI_VERSION_MINOR 4 #define PSI_VERSION_PATCH 0 -#define PSI_DEV_IDENTIFIER ".dev240517" +#define PSI_DEV_IDENTIFIER ".dev240521" From f0dad15d2537279e68d14debb167d3b56c25661c Mon Sep 17 00:00:00 2001 From: Yancheng Zheng <103552181+anakinxc@users.noreply.github.com> Date: Fri, 24 May 2024 14:12:18 +0800 Subject: [PATCH 6/6] Repo sync (#135) * repo-sync-2024-05-24T13:48:38+0800 * fix patch --- bazel/patches/emp-tool.patch | 13 +++++++++++++ bazel/repositories.bzl | 6 +++--- psi/apsi/BUILD.bazel | 4 ++-- psi/rr22/BUILD.bazel | 2 +- psi/utils/BUILD.bazel | 4 ++-- psi/utils/csv_checker.h | 1 + psi/utils/ec.h | 1 + 7 files changed, 23 insertions(+), 8 deletions(-) diff --git a/bazel/patches/emp-tool.patch b/bazel/patches/emp-tool.patch index 48220f89..6a7e6185 100644 --- a/bazel/patches/emp-tool.patch +++ b/bazel/patches/emp-tool.patch @@ -161,3 +161,16 @@ index 23bbf42..5101d7e 100644 } } +diff --git a/emp-tool/utils/block.h b/emp-tool/utils/block.h +index f7d3d34..3c25a73 100644 +--- a/emp-tool/utils/block.h ++++ b/emp-tool/utils/block.h +@@ -19,6 +19,7 @@ inline __m128i _mm_aesdeclast_si128 (__m128i a, __m128i RoundKey) + #include + #include + #include ++#include + + namespace emp { + + diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 131ac903..1aa54f95 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -51,10 +51,10 @@ def _yacl(): http_archive, name = "yacl", urls = [ - "https://github.com/secretflow/yacl/archive/refs/tags/0.4.5b0.tar.gz", + "https://github.com/secretflow/yacl/archive/refs/tags/0.4.5b1.tar.gz", ], - strip_prefix = "yacl-0.4.5b0", - sha256 = "68d1dbeb255d404606d3ba9380b915fbbe3886cde575bbe89795657286742bd2", + strip_prefix = "yacl-0.4.5b1", + sha256 = "28064053b9add0db8e1e8e648421a0579f1d3e7ee8a4bbd7bd5959cb59598088", ) def _bazel_platform(): diff --git a/psi/apsi/BUILD.bazel b/psi/apsi/BUILD.bazel index 8e17b28d..36de1271 100644 --- a/psi/apsi/BUILD.bazel +++ b/psi/apsi/BUILD.bazel @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("//bazel:psi.bzl", "psi_cc_binary", "psi_cc_library", "psi_cc_test") -load("@rules_proto//proto:defs.bzl", "proto_library") load("@rules_cc//cc:defs.bzl", "cc_proto_library") +load("@rules_proto//proto:defs.bzl", "proto_library") +load("//bazel:psi.bzl", "psi_cc_binary", "psi_cc_library", "psi_cc_test") package(default_visibility = ["//visibility:public"]) diff --git a/psi/rr22/BUILD.bazel b/psi/rr22/BUILD.bazel index a8668cc9..cd112370 100644 --- a/psi/rr22/BUILD.bazel +++ b/psi/rr22/BUILD.bazel @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("//bazel:psi.bzl", "psi_cc_binary", "psi_cc_library", "psi_cc_test") load("@yacl//bazel:yacl.bzl", "AES_COPT_FLAGS") +load("//bazel:psi.bzl", "psi_cc_binary", "psi_cc_library", "psi_cc_test") package(default_visibility = ["//visibility:public"]) diff --git a/psi/utils/BUILD.bazel b/psi/utils/BUILD.bazel index c3605b8b..d35ff7eb 100644 --- a/psi/utils/BUILD.bazel +++ b/psi/utils/BUILD.bazel @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("//bazel:psi.bzl", "psi_cc_library", "psi_cc_test") -load("@rules_proto//proto:defs.bzl", "proto_library") load("@rules_cc//cc:defs.bzl", "cc_proto_library") +load("@rules_proto//proto:defs.bzl", "proto_library") load("@yacl//bazel:yacl.bzl", "AES_COPT_FLAGS") +load("//bazel:psi.bzl", "psi_cc_library", "psi_cc_test") package(default_visibility = ["//visibility:public"]) diff --git a/psi/utils/csv_checker.h b/psi/utils/csv_checker.h index bf9094c8..e4e3a63b 100644 --- a/psi/utils/csv_checker.h +++ b/psi/utils/csv_checker.h @@ -14,6 +14,7 @@ #pragma once +#include #include #include diff --git a/psi/utils/ec.h b/psi/utils/ec.h index 2ea801a3..0fd34bc1 100644 --- a/psi/utils/ec.h +++ b/psi/utils/ec.h @@ -14,6 +14,7 @@ #pragma once +#include #include #include