diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 455833b4..9b092254 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -20,6 +20,8 @@ SECRETFLOW_GIT = "https://github.com/secretflow" YACL_COMMIT_ID = "f933d7ff4caf0d9f7ea84cc3e9f51a9a6ee9eeca" +HEU_COMMIT_ID = "68c3bfa12ea5d340196a2092519398a57f8476e6" + def spu_deps(): _rules_cuda() _rules_proto_grpc() @@ -51,6 +53,13 @@ def spu_deps(): remote = "{}/yacl.git".format(SECRETFLOW_GIT), ) + maybe( + git_repository, + name = "heu", + commit = HEU_COMMIT_ID, + remote = "{}/heu.git".format(SECRETFLOW_GIT), + ) + # Add homebrew openmp for macOS, somehow..homebrew installs to different location on Apple Silcon/Intel macs.. so we need two rules here native.new_local_repository( name = "local_homebrew_x64", diff --git a/libspu/psi/core/fnp04_mp_psi/BUILD.bazel b/libspu/psi/core/fnp04_mp_psi/BUILD.bazel new file mode 100644 index 00000000..982d6232 --- /dev/null +++ b/libspu/psi/core/fnp04_mp_psi/BUILD.bazel @@ -0,0 +1,36 @@ +# Copyright 2023 zhangwfjh +# +# 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:spu.bzl", "spu_cc_library", "spu_cc_test") + +package(default_visibility = ["//visibility:public"]) + +spu_cc_library( + name = "fnp04_mp_psi", + srcs = ["fnp04_mp_psi.cc"], + hdrs = ["fnp04_mp_psi.h"], + deps = [ + "//libspu/core:prelude", + "@yacl//yacl/link", + "@heu//heu/library/algorithms/paillier_zahlen", + "//libspu/psi/utils:test_utils", + "//libspu/psi/utils", + ], +) + +spu_cc_test( + name = "fnp04_mp_psi_test", + srcs = ["fnp04_mp_psi_test.cc"], + deps = [":fnp04_mp_psi"], +) \ No newline at end of file diff --git a/libspu/psi/core/fnp04_mp_psi/fnp04_mp_psi.cc b/libspu/psi/core/fnp04_mp_psi/fnp04_mp_psi.cc new file mode 100644 index 00000000..807ec9cd --- /dev/null +++ b/libspu/psi/core/fnp04_mp_psi/fnp04_mp_psi.cc @@ -0,0 +1,360 @@ +// Copyright 2023 zhangwfjh +// +// 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 "libspu/psi/core/fnp04_mp_psi/fnp04_mp_psi.h" + +#include +#include +#include +#include +#include + +#include "yacl/utils/serialize.h" + +#include "libspu/core/prelude.h" +#include "libspu/psi/utils/utils.h" + +namespace { + +static std::random_device rd{}; +static std::independent_bits_engine + rng{rd()}; + +constexpr size_t BinCountHint(size_t size) { + auto lnln_size = std::max(1., std::log(std::log(size))); + return (size + lnln_size - 1) / lnln_size; +} + +size_t ToUnsigned(const yacl::math::MPInt& x) { + unsigned char bytes[sizeof(size_t)]; + x.ToBytes(bytes, sizeof(size_t)); + return *reinterpret_cast(bytes); +} + +yacl::Buffer Serialize(size_t size) { + yacl::Buffer buf(sizeof(size_t)); + std::memcpy(buf.data(), &size, sizeof(size_t)); + return buf; +} + +size_t Deserialize(const yacl::Buffer& buf) { + size_t size; + std::memcpy(&size, buf.data(), sizeof(size_t)); + return size; +} + +} // namespace + +namespace spu::psi { + +FNP04Party::FNP04Party(const Options& options) : options_{options} { + auto [ctx, wsize, me, leader] = CollectContext(); + encryptors_.resize(wsize); + SecretKey sk; + PublicKey pk; + KeyGenerator::Generate(&sk, &pk); + encryptors_[me] = std::make_shared(pk); + decryptor_ = std::make_shared(pk, sk); +} + +std::vector FNP04Party::Run( + const std::vector& inputs) { + auto [ctx, wsize, me, leader] = CollectContext(); + auto counts = AllGatherItemsSize(ctx, inputs.size()); + size_t count{}; + for (auto cnt : counts) { + if (cnt == 0) { + return {}; + } + count = std::max(cnt, count); + } + // Step 0: Encode the inputs + auto items = EncodeInputs(inputs, count); + // Step 1: Broadcast the public key + BroadcastPubKey(); + // Step 2: Zero sharing + auto shares = ZeroSharing(count); + if (leader == me) { + for (size_t i{}; i != count; ++i) { + shares[i][(leader + 1) % wsize] ^= items[i]; + } + } + if (leader == me) { + // Step 3: Receive the encrypted set + auto hashings = RecvEncryptedSet(count); + // Step 4: Swap the encrypted shares + SwapShares(shares, items, hashings); + } else { + // Step 3: Send the encrypted set + SendEncryptedSet(items); + // Step 4: Swap encrypted shares + shares = SwapShares(shares); + } + // Step 5: Aggregate share + auto aggregate = AggregateShare(shares); + // Step 6: Get intersection + auto intersection_items = GetIntersection(items, aggregate); + std::vector intersection; + for (size_t i{}; i != inputs.size(); ++i) { + if (std::find(intersection_items.begin(), intersection_items.end(), + items[i]) != intersection_items.end()) { + intersection.emplace_back(inputs[i]); + } + } + return intersection; +} + +std::vector FNP04Party::EncodeInputs( + const std::vector& inputs, size_t count) const { + std::vector items; + std::transform(inputs.begin(), inputs.end(), std::back_inserter(items), + [](std::string_view input) { + return std::hash{}(input); + }); + // Add random dummy elements + std::generate_n(std::back_inserter(items), count - inputs.size(), + std::ref(rng)); + return items; +} + +void FNP04Party::BroadcastPubKey() { + auto [ctx, wsize, me, leader] = CollectContext(); + // Publish + if (leader != me) { + auto pk = encryptors_[me]->public_key().Serialize(); + for (size_t dst{}; dst != wsize; ++dst) { + if (dst != me) { + ctx->SendAsyncThrottled( + dst, pk, + fmt::format("Party {} sends the public key to {}", me, dst)); + } + } + } + // Collect + for (size_t src{}; src != wsize; ++src) { + if (src != me && src != leader) { + auto buf = ctx->Recv( + src, + fmt::format("Party {} receives the public key from {}", me, src)); + PublicKey pk; + pk.Deserialize(buf); + encryptors_[src] = std::make_shared(pk); + } + } +} + +void FNP04Party::SendEncryptedSet(const std::vector& items) const { + auto [ctx, wsize, me, leader] = CollectContext(); + assert(me != leader); + // Encode + size_t B{BinCountHint(items.size())}; + std::vector> hashing(B); + std::for_each(items.begin(), items.end(), + [&](auto item) { hashing[item % B].emplace_back(item); }); + // Hashing + SecretPolynomial bins(B); + std::vector buffers; + for (auto& roots : hashing) { + SPU_ENFORCE(roots.size() <= BinSize, "Severe hash collisions"); + roots.resize(BinSize); + std::array coeffs{ + -(roots[0] * roots[1] * roots[2] * roots[3] * roots[4]), + +(roots[0] * roots[1] * roots[2] * roots[3] + + roots[0] * roots[1] * roots[2] * roots[4] + + roots[0] * roots[1] * roots[3] * roots[4] + + roots[0] * roots[2] * roots[3] * roots[4] + + roots[1] * roots[2] * roots[3] * roots[4]), + -(roots[0] * roots[1] * roots[2] + roots[0] * roots[1] * roots[3] + + roots[0] * roots[1] * roots[4] + roots[0] * roots[2] * roots[3] + + roots[0] * roots[2] * roots[4] + roots[0] * roots[3] * roots[4] + + roots[1] * roots[2] * roots[3] + roots[1] * roots[2] * roots[4] + + roots[1] * roots[3] * roots[4] + roots[2] * roots[3] * roots[4]), + +(roots[0] * roots[1] + roots[0] * roots[2] + roots[0] * roots[3] + + roots[0] * roots[4] + roots[1] * roots[2] + roots[1] * roots[3] + + roots[1] * roots[4] + roots[2] * roots[3] + roots[2] * roots[4] + + roots[3] * roots[4]), + -(roots[0] + roots[1] + roots[2] + roots[3] + roots[4])}; + for (const auto& coeff : coeffs) { + buffers.emplace_back( + encryptors_[me]->Encrypt(Plaintext(coeff)).Serialize()); + } + } + ctx->SendAsyncThrottled( + leader, yacl::SerializeArrayOfBuffers({buffers.begin(), buffers.end()}), + fmt::format("Party {} sends the encrypted set", me)); +} + +auto FNP04Party::RecvEncryptedSet(size_t count) const + -> std::vector { + auto [ctx, wsize, me, leader] = CollectContext(); + assert(me == leader); + size_t B{BinCountHint(count)}; + std::vector hashings(wsize, SecretPolynomial(B)); + for (size_t src{}; src != wsize; ++src) { + if (src != me) { + auto buf = ctx->Recv( + src, fmt::format( + "The leader receives the encrypted set from party {}", src)); + auto buffers = yacl::DeserializeArrayOfBuffers(buf); + size_t i{}; + for (auto& bin : hashings[src]) { + for (auto& coeff : bin) { + coeff.Deserialize(buffers[i++]); + } + } + } + } + return hashings; +} + +auto FNP04Party::ZeroSharing(size_t count) const -> std::vector { + auto [ctx, wsize, me, leader] = CollectContext(); + std::vector shares(count, Share(wsize)); + for (auto& share : shares) { + std::generate_n(share.begin(), wsize, std::ref(rng)); + auto sum = std::reduce(share.begin(), share.end(), share[leader], + [](const auto& x, const auto& y) { return x ^ y; }); + share[(leader + 1) % wsize] ^= sum; + share[leader] = 0; + } + return shares; +} + +auto FNP04Party::SwapShares(const std::vector& shares) const + -> std::vector { + auto [ctx, wsize, me, leader] = CollectContext(); + auto count = shares.size(); + // Send + for (size_t dst{}; dst != wsize; ++dst) { + if (dst != me && dst != leader) { + std::vector buffers; + for (auto& share : shares) { + auto cipher = encryptors_[dst]->Encrypt(Plaintext(share[dst])); + buffers.emplace_back(cipher.Serialize()); + } + ctx->SendAsyncThrottled( + dst, yacl::SerializeArrayOfBuffers({buffers.begin(), buffers.end()}), + fmt::format("Party {} sends secret shares to {}", me, dst)); + } + } + // Receive + std::vector recv_shares(count, Share(wsize)); + for (size_t src{}; src != wsize; ++src) { + if (src != me) { + auto buf = ctx->Recv( + src, fmt::format("Party {} receives secret shares from {}", me, src)); + auto buffers = yacl::DeserializeArrayOfBuffers(buf); + size_t i{}; + for (auto& share : recv_shares) { + Ciphertext cipher; + cipher.Deserialize(buffers[i++]); + share[src] = ToUnsigned(decryptor_->Decrypt(cipher)); + } + } else { + for (size_t i{}; i != count; ++i) { + recv_shares[i][me] = shares[i][me]; + } + } + } + return recv_shares; +} + +void FNP04Party::SwapShares( + const std::vector& shares, const std::vector& items, + const std::vector& hashings) const { + auto [ctx, wsize, me, leader] = CollectContext(); + auto count = items.size(); + size_t B{BinCountHint(count)}; + for (size_t dst{}; dst != wsize; ++dst) { + if (dst != me) { + Evaluator evaluator{encryptors_[dst]->public_key()}; + std::vector share(count); + for (size_t i{}; i != count; ++i) { + auto coeffs = hashings[dst][items[i] % B]; + Plaintext scale{rng()}, bias{shares[i][dst]}; + Plaintext x{items[i]}; + // share[i] = (((((x+c4).x+c3).x+c2).x+c1).x+c0).s+b + share[i] = coeffs[4]; + evaluator.AddInplace(&share[i], x); + evaluator.MulInplace(&share[i], x); + evaluator.AddInplace(&share[i], coeffs[3]); + evaluator.MulInplace(&share[i], x); + evaluator.AddInplace(&share[i], coeffs[2]); + evaluator.MulInplace(&share[i], x); + evaluator.AddInplace(&share[i], coeffs[1]); + evaluator.MulInplace(&share[i], x); + evaluator.AddInplace(&share[i], coeffs[0]); + evaluator.MulInplace(&share[i], scale); + evaluator.AddInplace(&share[i], bias); + } + std::vector buffers(count); + std::transform(share.begin(), share.end(), buffers.begin(), + [&](const auto& s) { return s.Serialize(); }); + ctx->SendAsyncThrottled( + dst, yacl::SerializeArrayOfBuffers({buffers.begin(), buffers.end()}), + fmt::format("The leader sends secret shares to {}", dst)); + } + } +} + +auto FNP04Party::AggregateShare(const std::vector& shares) const + -> Share { + auto [ctx, wsize, me, leader] = CollectContext(); + Share share(shares.size()); + if (me != leader) { + for (size_t i{}; i != shares.size(); ++i) { + for (size_t src{}; src != wsize; ++src) { + share[i] ^= shares[i][src]; + } + } + } + return share; +} + +std::vector FNP04Party::GetIntersection( + const std::vector& items, const Share& share) const { + auto [ctx, wsize, me, leader] = CollectContext(); + if (me != leader) { + std::vector buffers(share.size()); + std::transform(share.begin(), share.end(), buffers.begin(), + [&](size_t s) { return Serialize(s); }); + ctx->SendAsyncThrottled( + leader, yacl::SerializeArrayOfBuffers({buffers.begin(), buffers.end()}), + fmt::format("Party {} sends aggregated shares to the leader", me)); + return {}; + } + std::vector universe = share; + for (size_t src{}; src != wsize; ++src) { + if (src != me) { + auto buf = ctx->Recv( + src, + fmt::format("The leader receives aggregated shares from {}", src)); + auto buffers = yacl::DeserializeArrayOfBuffers(buf); + size_t i{}; + for (auto& item : universe) { + item ^= Deserialize(buffers[i++]); + } + } + } + std::vector intersection; + std::copy_if(items.begin(), items.end(), std::back_inserter(intersection), + [&](size_t item) { + return std::find(universe.begin(), universe.end(), item) != + universe.end(); + }); + return intersection; +} + +} // namespace spu::psi diff --git a/libspu/psi/core/fnp04_mp_psi/fnp04_mp_psi.h b/libspu/psi/core/fnp04_mp_psi/fnp04_mp_psi.h new file mode 100644 index 00000000..68e90079 --- /dev/null +++ b/libspu/psi/core/fnp04_mp_psi/fnp04_mp_psi.h @@ -0,0 +1,70 @@ +// Copyright 2023 zhangwfjh +// +// 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 + +#include "heu/library/algorithms/paillier_zahlen/paillier.h" +#include "yacl/link/link.h" + +namespace spu::psi { + +// Efficient Private Matching and Set Intersection +// http://www.pinkas.net/PAPERS/FNP04.pdf + +using namespace heu::lib::algorithms::paillier_z; + +class FNP04Party { + public: + struct Options { + std::shared_ptr link_ctx; + size_t leader_rank; + }; + static constexpr size_t BinSize{5}; + using SecretPolynomial = std::vector>; + using Share = std::vector; + + FNP04Party(const Options& options); + virtual std::vector Run(const std::vector& inputs); + + private: + std::vector EncodeInputs(const std::vector& inputs, + size_t count) const; + void BroadcastPubKey(); + void SendEncryptedSet(const std::vector& items) const; + std::vector RecvEncryptedSet(size_t count) const; + std::vector ZeroSharing(size_t count) const; + std::vector SwapShares(const std::vector& shares) const; + void SwapShares(const std::vector& shares, + const std::vector& items, + const std::vector& hashings) const; + Share AggregateShare(const std::vector& shares) const; + std::vector GetIntersection(const std::vector& items, + const Share& share) const; + + Options options_; + std::vector> encryptors_; + std::shared_ptr decryptor_; + + // (ctx, world_size, my_rank, leader_rank) + auto CollectContext() const { + return std::make_tuple(options_.link_ctx, options_.link_ctx->WorldSize(), + options_.link_ctx->Rank(), options_.leader_rank); + } +}; + +} // namespace spu::psi diff --git a/libspu/psi/core/fnp04_mp_psi/fnp04_mp_psi_test.cc b/libspu/psi/core/fnp04_mp_psi/fnp04_mp_psi_test.cc new file mode 100644 index 00000000..2b5c39a8 --- /dev/null +++ b/libspu/psi/core/fnp04_mp_psi/fnp04_mp_psi_test.cc @@ -0,0 +1,119 @@ +// Copyright 2023 zhangwfjh +// +// 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 "libspu/psi/core/fnp04_mp_psi/fnp04_mp_psi.h" + +#include +#include +#include + +#include "gtest/gtest.h" +#include "yacl/link/test_util.h" + +#include "libspu/core/prelude.h" +#include "libspu/psi/utils/test_utils.h" + +namespace spu::psi { + +namespace { + +struct FnpMpTestParams { + std::vector item_size; + size_t intersection_size; +}; + +std::vector> CreateNPartyItems( + const FnpMpTestParams& params) { + std::vector> ret(params.item_size.size() + 1); + ret[params.item_size.size()] = + test::CreateRangeItems(1, params.intersection_size); + + for (size_t idx = 0; idx < params.item_size.size(); ++idx) { + ret[idx] = + test::CreateRangeItems((idx + 1) * 1000000, params.item_size[idx]); + } + + for (size_t idx = 0; idx < params.item_size.size(); ++idx) { + std::set idx_set; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, params.item_size[idx] - 1); + + while (idx_set.size() < params.intersection_size) { + idx_set.insert(dis(gen)); + } + size_t j = 0; + for (const auto& iter : idx_set) { + ret[idx][iter] = ret[params.item_size.size()][j++]; + } + } + return ret; +} + +} // namespace + +class FnpMpPsiTest : public testing::TestWithParam {}; + +TEST_P(FnpMpPsiTest, Works) { + std::vector> items; + + auto params = GetParam(); + items = CreateNPartyItems(params); + size_t leader_rank = 0; + + auto ctxs = yacl::link::test::SetupWorld(params.item_size.size()); + + auto proc = [&](int idx) -> std::vector { + FNP04Party::Options opts; + opts.link_ctx = ctxs[idx]; + opts.leader_rank = leader_rank; + FNP04Party op(opts); + return op.Run(items[idx]); + }; + + size_t world_size = ctxs.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 intersection = + test::GetIntersection(items[params.item_size.size()], items[0]); + std::sort(intersection.begin(), intersection.end()); + + std::vector> results(world_size); + for (size_t i = 0; i < world_size; i++) { + results[i] = f_links[i].get(); + if (i == leader_rank) { + std::sort(results[i].begin(), results[i].end()); + EXPECT_EQ(results[i].size(), intersection.size()); + EXPECT_EQ(results[i], intersection); + } else { + EXPECT_EQ(results[i].size(), 0); + } + } +} + +INSTANTIATE_TEST_SUITE_P( + Works_Instances, FnpMpPsiTest, + testing::Values(FnpMpTestParams{{0, 3}, 0}, // + FnpMpTestParams{{3, 0}, 0}, // + FnpMpTestParams{{0, 0}, 0}, // + FnpMpTestParams{{4, 3}, 2}, // + FnpMpTestParams{{20, 17, 14}, 10}, // + FnpMpTestParams{{20, 17, 14, 30}, 10}, // + FnpMpTestParams{{20, 17, 14, 30, 35}, 11}, // + FnpMpTestParams{{20, 17, 14, 30, 35}, 0})); + +} // namespace spu::psi \ No newline at end of file diff --git a/libspu/psi/operator/BUILD.bazel b/libspu/psi/operator/BUILD.bazel index 5a34bcfd..bfe3c99c 100644 --- a/libspu/psi/operator/BUILD.bazel +++ b/libspu/psi/operator/BUILD.bazel @@ -107,6 +107,17 @@ spu_cc_library( ":factory", "//libspu/psi/core/dp_psi", ], +) + +spu_cc_library( + name = "fnp04_mp_psi", + srcs = ["fnp04_mp_psi.cc"], + hdrs = ["fnp04_mp_psi.h"], + deps = [ + ":base_operator", + ":factory", + "//libspu/psi/core/fnp04_mp_psi", + ], alwayslink = True, ) @@ -118,5 +129,6 @@ spu_cc_library( ":ecdh_3party_psi", ":kkrt_2party_psi", ":nparty_psi", + ":fnp04_mp_psi", ], ) diff --git a/libspu/psi/operator/fnp04_mp_psi.cc b/libspu/psi/operator/fnp04_mp_psi.cc new file mode 100644 index 00000000..15f4308e --- /dev/null +++ b/libspu/psi/operator/fnp04_mp_psi.cc @@ -0,0 +1,36 @@ +// Copyright 2023 zhangwfjh +// +// 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 "libspu/psi/operator/fnp04_mp_psi.h" + +#include + +#include "libspu/psi/operator/factory.h" + +namespace spu::psi { + +namespace { + +std::unique_ptr CreateOperator( + const MemoryPsiConfig& config, + const std::shared_ptr& lctx) { + return std::make_unique( + FnpMpPsiOperator::Options({lctx, config.receiver_rank()})); +} + +REGISTER_OPERATOR(FNP_PSI_NPC, CreateOperator); + +} // namespace + +} // namespace spu::psi \ No newline at end of file diff --git a/libspu/psi/operator/fnp04_mp_psi.h b/libspu/psi/operator/fnp04_mp_psi.h new file mode 100644 index 00000000..62df656b --- /dev/null +++ b/libspu/psi/operator/fnp04_mp_psi.h @@ -0,0 +1,42 @@ +// Copyright 2023 zhangwfjh +// +// 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 "yacl/link/link.h" + +#include "libspu/psi/core/fnp04_mp_psi/fnp04_mp_psi.h" +#include "libspu/psi/operator/base_operator.h" + +namespace spu::psi { + +class FnpMpPsiOperator : public PsiBaseOperator { + public: + using Options = FNP04Party::Options; + + explicit FnpMpPsiOperator(const Options& options) + : PsiBaseOperator(options.link_ctx), options_(options) {} + + std::vector OnRun(const std::vector& inputs) final { + return FNP04Party{options_}.Run(inputs); + } + + private: + Options options_; +}; + +} // namespace spu::psi diff --git a/libspu/psi/psi.proto b/libspu/psi/psi.proto index 39ca3476..fed5cbe4 100644 --- a/libspu/psi/psi.proto +++ b/libspu/psi/psi.proto @@ -60,6 +60,9 @@ enum PsiType { // Differentially-Private PSI https://arxiv.org/pdf/2208.13249.pdf // bases on ECDH-PSI, and provides: Differentially private PSI results. DP_PSI_2PC = 12; + + // Efficient Private Matching and Set Intersection http://www.pinkas.net/PAPERS/FNP04.pdf + FNP_PSI_NPC = 13; } // The specified elliptic curve cryptography used in psi.