1. PSI定义和种类
定义:PSI(Private Set Intersection)是一种协议,用于比较两个私有集合的元素,并在不泄露集合成员身份的情况下确定两个集合的交集。
在隐私计算领域,有几种不同类型的PSI协议,包括以下几种:
- 基于加密技术的PSI(Cryptography-based PSI):这种类型的协议使用加密技术,如同态加密、零知识证明等,来实现在不泄露原始数据的情况下执行集合交集操作。这些协议可以确保参与者之间的隐私,但可能需要较高的计算成本。
- 基于扰动的PSI(Perturbation-based PSI):这种类型的协议通过向集合中的元素添加一定程度的噪声或扰动,然后执行交集操作。扰动通常被设计成足够的程度,以隐藏个体元素的身份,同时允许进行有效的交集计算。但是,需要注意的是,在某些情况下,这种方法可能会导致误差或信息泄露。
- 基于哈希的PSI(Hash-based PSI):这种类型的协议利用哈希函数,将集合中的元素映射到固定长度的哈希值,并在哈希值的基础上执行集合交集操作。由于哈希函数的单向性质,参与者可以通过比较哈希值来确定两个集合的交集,同时不暴露原始数据。但是,需要注意的是,哈希函数可能会导致哈希冲突,从而引入一定的误差。
2. 隐语PSI功能分层
SPU实现的PSI种类
- 半诚实模型
- 两方
- ecdh、kkrt16,bc22(pcg-psi)
- ec-oprf PSI(Unbalanced PSI)
- dp-psi
- 多方
- ecdh-3-party(可扩展到多方)
- 恶意模型
- mini-PSI(适合小数据集)
- 两方
2.1 Echd
ECDH-PSI(Elliptic Curve Diffie-Hellman Private Set Intersection)是一种协议,用于在两个参与者之间安全地计算出彼此集合的交集,而不泄露集合的其他信息。
- 隐私保护: ECDH-PSI协议旨在保护参与者的隐私。在协议执行期间,任何一方都无法得知对方集合的具体元素,只能获取到集合的交集。
- 安全性: 协议建立在Diffie-Hellman密钥交换和椭圆曲线加密算法的基础上,提供了安全性保障。这意味着即使在公共通信渠道上,恶意方也无法轻易获得交换的信息。
- 计算效率: ECDH-PSI协议在计算交集时具有高效性。通过利用椭圆曲线加密的快速运算特性,协议能够在较短的时间内完成交集计算。
- 通用性: ECDH-PSI协议并不限制于特定类型的数据集,因此适用于各种场景,包括但不限于数据库查询、隐私保护的数据分析等。
- 端到端保护: 协议在两个参与者之间直接运行,不需要第三方的参与或者信任。这种端到端的保护方式确保了信息在通信过程中的安全性和完整性。
- 灵活性: ECDH-PSI协议具有一定的灵活性,允许参与者在不同的环境和需求下进行定制和调整,以满足特定的安全和性能要求。
- 计算复杂性: 尽管ECDH-PSI协议在计算效率上相对高效,但在处理大型数据集时,仍然可能面临计算复杂性的挑战。特别是当参与者的集合大小非常大时,协议的执行可能需要大量的计算资源和时间。
- 通信开销: 在执行ECDH-PSI协议时,参与者之间需要进行多次通信来完成密钥交换和交集计算,这可能会导致一定程度的通信开销。尤其是在高延迟或带宽有限的网络环境下,这种通信开销可能会对性能产生影响。
2.2 KKPT16
KKPT16协议是一种用于计算两个参与者之间的私有集合交集的协议。参与者分别持有各自的集合,而协议允许他们计算出这两个集合的交集,同时尽可能地减少信息泄露。
- 线性复杂度:KKPT16协议的主要特点之一是具有线性复杂度。这意味着在处理大型数据集时,协议的执行时间与数据集大小成线性关系,因此在实践中具有良好的可扩展性。
- 隐私保护:协议旨在保护参与者的隐私。在执行期间,协议设计了机制,使得每个参与者只能获取到集合的交集,而无法获得其他信息,如集合的差集或并集。
- 高效性:除了具有线性复杂度外,KKPT16协议还具有高效性。它采用了一系列技术和优化措施,以减少通信开销和计算开销,从而提高协议的执行效率。
- 通用性:KKPT16协议不限制于特定类型的数据集,因此适用于各种场景。无论是数据库查询、隐私保护的数据分析还是其他应用,都可以使用该协议来计算私有集合的交集。
- 安全性:协议建立在安全的密码学基础上,使用加密技术和安全协议来保护参与者的数据和交互过程,防止信息泄露和攻击。
可验证性:KKPT16协议通常具有可验证性,参与者可以验证计算结果的正确性,确保没有被篡改或伪造。
2.3 BC22 PCG
2.4 不平衡的PSI
具体协议包括:ecdh/kkrt16/bc22协议,这些协议更适合双方数据量差别不大的场景,称为平衡PSI(Balanced PSI)。在现实的隐私求交场景中,有时双方数据量级差异很大,例如:百万vs十亿,2千万vs 20亿。针对这种场景,隐语实现并开源了专门的非平衡PSI(Unbalanced PSI)协议,能得到更好的性能。
2.4.1 ec-oprf based
2.4.2 SHE based
2.5 基于ecdh的三方PSI协议
2.6 核心代码实现位置
3.SPU PSI调用架构
3.1 启动ray集群
# Alice启动ray集群
ray start --head --node-ip-address=127.0.0.1 --port=9001 --include-dashboard=False --disable-usage-stats
# Bob启动ray集群
ray start --head --node-ip-address=127.0.0.1 --port=9002 --include-dashboard=False --disable-usage-stats
3.2 初始化secretflow
生产模式
cluster_config ={
'parties': {
'alice': {
# replace with alice's real address.
'address': '127.0.0.1:9101',
'listen_addr': '0.0.0.0:9201'
},
'bob': {
# replace with bob's real address.
'address': '127.0.0.1:9102',
'listen_addr': '0.0.0.0:9202'
},
},
'self_party': 'bob'
}
tls_config = {
"ca_cert":"ca root cert of other parties",
"cert":"server cert of alice in pem",
"key":"server cert of alice in pem",
}
sf.init(address='alice ray head node address', cluster_config=cluster_config,tls_config=tls_config)
sf.init(address='bob ray head node address', cluster_config=cluster_config,tls_config=tls_config)
3.3 启动SPU设备
spu_cluster_def={
'nodes': [
{
'party': 'alice',
# Please choose an unused port.
'address': '127.0.0.1:9101',
'listen_addr': '0.0.0.0:9101'
},
{
'party': 'bob',
# Please choose an unused port.
'address': '127.0.0.1:9102',
'listen_addr': '0.0.0.0:9102'
},
],
'runtime_config': {
'protocol': spu.spu_pb2.SEMI2K,
'field': spu.spu_pb2.FM128,
'sigmoid_mode': spu.spu_pb2.RuntimeConfig.SIGMOID_REAL,
}
}
spu = sf.SPU(spu_cluster_def)
3.4执行PSI
# 进行PSI操作
reports = spu.psi_csv(
key = select_keys,
input_path = input_path,
out_put = out_put,
receiver = 'alice',#receiver get output file
protocol='ECDH_PSI_2PC'
curve_type='CURVE_25519'
precheck_input=False
sort=False
broadcast_result=False
)
4. 作业
场景:银行x公安联合打击黑产
随着在线金融服务的兴起,银行和金融机构在提供便捷服务的同时,也越来越多地遭受到开户欺诈的挑战。“银行账户开户过程中的欺诈行为”通常指的是个人或团体在申请开通银行账户时提供虚假信息、使用伪造或盗窃的身份证件,或者通过欺骗性的手段来实现不正当目的。
案例数据集:两方分别为银行卡数据、身份证数据,请对其进行隐私求交。
4.1 启动secretNote
利用docker-compose启动两个secretnote
services:
alice:
image: 'secretflow/secretnote:unstable-amd64'
platform: linux/amd64
environment:
- SELF_PARTY=alice
- ALL_PARTIES=alice,bob
ports:
- 8090:8888
entrypoint: /root/scripts/start.sh
volumes:
- /root/scripts
bob:
image: 'secretflow/secretnote:unstable-amd64'
platform: linux/amd64
environment:
- SELF_PARTY=bob
- ALL_PARTIES=alice,bob
ports:
- 8092:8888
entrypoint: /root/scripts/start.sh
volumes:
本地服务器访问:http://服务器ip:8090/secretnote/secretflow即可进入secretnote
点击右上角的加号添加用户,节点添加成功的话应该是在线状态,离线状态的话大概是地址没有填写对。如图所示已经添加了Alice和Bob两个节点:
在SecretNote左边将对应的文件上传到Alice和Bob节点
4.2 初始化SecretFlow
接下来的代码需要再两端同时运行,我们在运行的时候选择两端一起即可
import secretflow as sf
import spu
import os
network_conf = {
"parties": {
"alice": {
"address": "alice:8000",
},
"bob": {
"address": "bob:8000",
},
},
}
# 我们已经在secretnote中配置了
party = os.getenv("SELF_PARTY", "alice")
sf.shutdown()
sf.init(
address="127.0.0.1:6379",
cluster_config={**network_conf, "self_party": party},
log_to_driver=True,
)
4.3 初始化 SPU
- alice 的 address 请填写可以被 bob 访通的地址,并且选择一个未被占用的端口 ,注意不要和 Ray 端口冲突。
- alice 的 listen_addr 可以和 alice address 里的端口一样。
- bob 的 address 请填写可以被 alice 访通的地址,并且选择一个未被占用的端口 ,注意不要和 Ray 端口冲突。
- bob 的 listen_addr 可以和 bob address 里的端口一样。
alice, bob = sf.PYU("alice"), sf.PYU("bob")
spu_conf = {
"nodes": [
{
"party": "alice",
"address": "alice:8001",
"listen_addr": "alice:8001",
},
{
"party": "bob",
"address": "bob:8001",
"listen_addr": "bob:8001",
},
],
"runtime_config": {
"protocol": spu.spu_pb2.SEMI2K,
"field": spu.spu_pb2.FM128,
"sigmoid_mode": spu.spu_pb2.RuntimeConfig.SIGMOID_REAL,
},
}
spu = sf.SPU(cluster_def=spu_conf)
4.4 隐私求交
psi_csv(
key: str | List[str],
input_path: str,
output_path: str,
receiver: str,
protocol="KKRT_PSI_2PC",
precheck_input=True,
sort=True,
broadcast_result=True,
bucket_size=1048576,
curve_type="CURVE_25519",
preprocess_path=None,
ecdh_secret_key_path=None,
dppsi_bob_sub_sampling=0.9,
dppsi_epsilon=3,
progress_callbacks: Callable[[str, ProgressData], None] | None = None,
callbacks_interval_ms: int = 5000,
ic_mode: bool = False,
)
隐私求交
current_dir = os.getcwd()
input_path = {
alice: f"{current_dir}/payment.csv",
bob: f"{current_dir}/record.csv",
}
output_path = {
alice: f"{current_dir}/psi_alice_output.csv",
bob: f"{current_dir}/psi_bob_output.csv",
}
spu.psi_csv("uid", input_path, output_path, "alice")