基于秘密共享与OPRF的模糊隐私集合求交(PSI)协议设计与实现

发布时间:2026/6/21 4:11:10
基于秘密共享与OPRF的模糊隐私集合求交(PSI)协议设计与实现 1. 项目概述当隐私计算遇上“模糊匹配”最近在做一个挺有意思的隐私计算项目核心是解决一个看似矛盾的需求两个互不信任的机构比如一家银行和一家电商平台都想看看自己的客户名单里有多少重合用户但又不想让对方知道自己具体有哪些客户甚至连自己客户的确切信息都不想泄露。这听起来有点像“盲人摸象”还要比谁摸到的部分更相似传统方案要么太慢要么太“精确”导致不实用。我们这次要聊的就是基于秘密共享和OPRF不经意伪随机函数来设计一个高效的“模糊”隐私集合求交协议。简单说它允许双方在数据有微小差异比如手机号输错一位、名字用了简称的情况下还能安全地找出交集而且效率要高到能上生产环境。这玩意儿在风控联防联控、医疗研究数据对齐、甚至广告精准触达但又保护用户隐私的场景下需求越来越迫切。你想想银行的风控名单和电商的欺诈用户名单如果因为数据录入的格式不统一就匹配不上那损失可就大了。但直接明文比对法律和用户信任这一关又过不去。所以一个能容忍“模糊度”、且计算通信开销可控的隐私集合求交协议就成了刚需。我这次的设计与实现重点攻克了两个难点一是如何在不泄露任何额外信息的前提下实现“模糊”匹配二是如何利用秘密共享和OPRF这两个密码学原语将计算压力分散避免成为性能瓶颈。最终的目标是拿出一套从理论协议到可运行代码的完整方案并且用实测数据说话。下面我就把这几个月折腾的东西从设计思路到代码实现再到踩过的坑给大家掰开揉碎了讲讲。2. 核心密码学原语与设计思路拆解2.1 为什么是“模糊”PSI刚性需求与挑战传统的隐私集合求交协议要求双方的数据元素必须完全一致才能匹配成功。这在实际业务中几乎是个“理想模型”。现实中的数据总是充满噪音身份证号最后一位的‘X’大小写不一致、手机号国家代码有无、姓名中间的空格或点号、甚至简单的拼写错误。传统的精确PSI在这些场景下交集结果会大幅减少甚至为零完全失去业务价值。因此“模糊”PSI的核心需求是引入一个容错机制。这个“模糊”通常通过计算数据元素之间的相似度如编辑距离、Jaccard相似度或经过某种变换后的距离如嵌入向量的余弦距离来实现当相似度超过某个阈值时即认为匹配。但挑战随之而来隐私泄露计算相似度的过程本身就可能泄露数据特征。例如直接交换数据的哈希值通过碰撞分析可能反推原数据。计算复杂度许多模糊匹配算法如动态规划计算编辑距离复杂度很高在密文或隐私保护状态下执行开销会指数级增长。结果准确性如何在保护隐私的同时保证匹配结果的准确性高召回率与精确率是一个平衡难题。我们的设计思路是不直接在高维的、复杂的原始数据上做模糊匹配而是先通过一个“模糊化”或“编码”的过程将原始数据映射到一个新的空间。在这个新空间里“相似”的原始数据会以高概率映射到相同或相近的“代表值”上然后在这个代表值集合上执行高效的精确PSI。这样我们就把一个复杂的模糊匹配问题转化成了一个相对高效的精确匹配问题同时通过密码学手段保证映射过程不会泄露原始数据信息。2.2 OPRF不经意伪随机函数的核心角色OPRF是我们这个协议能够实现“隐私”的基石。你可以把它理解为一个“盲化的哈希函数”。它允许客户端持有输入x从服务器持有密钥k那里获得F(k, x)的值但服务器在整个过程中不知道x是什么客户端也无法得知密钥k。具体过程通常如下客户端将输入x“盲化”例如计算r * H(x)其中r是随机数H是哈希函数发送给服务器。服务器用私钥k对这个盲化值进行签名或运算返回结果。客户端“去盲化”例如计算(服务器返回结果)^(1/r)得到最终的F(k, x)。在这个过程中服务器看到的只是乱码不知道原始x客户端最终得到了一个只有持有密钥k的服务器才能计算出的、与x绑定的伪随机值。这个值可以作为x在协议中的“唯一且隐匿的代表”。在我们的模糊PSI设计中OPRF的作用至关重要。我们并非直接对原始数据应用OPRF而是先对数据进行“模糊化编码”生成一个中间表示然后对这个中间表示应用OPRF。这样最终用于求交的标识符既包含了原始数据的模糊特征由编码过程决定又经过了OPRF的隐匿保护服务器无法将其与任何具体数据关联。2.3 秘密共享分担风险与提升效率的利器秘密共享Secret Sharing是将一个秘密比如一个数字、一个密钥拆分成多个份额Shares分发给不同参与方。单个或多个份额无法恢复秘密只有收集到足够数量达到阈值的份额才能重构出原始秘密。在我们的协议中秘密共享主要扮演两个角色分担计算与存储将OPRF密钥k通过秘密共享分发给两个或多个非共谋的服务器。这样没有单个服务器掌握完整的密钥提升了系统的安全性。客户端需要与所有服务器交互才能完成完整的OPRF计算这个过程自然地将计算负载分散了。实现安全多方计算我们可以将模糊匹配的相似度计算设计成一个安全多方计算MPC电路。将这个电路的输入即编码后的数据用秘密共享的方式分发给参与方各方在本地份额上进行计算和交互最终也能得到交集结果的份额而不会泄露各自的输入。这种方式特别适合对计算精度要求高、但数据维度经过编码后已经降低的场景。我们最终采用的是一种混合架构利用秘密共享来保护OPRF的密钥构建一个两服务器辅助的OPRF服务同时在后续的精确PSI阶段也利用秘密共享的思想来优化通信轮次。这比传统的单服务器OPRF或纯MPC方案在效率和安全性之间取得了更好的平衡。2.4 整体协议流程设计我们的协议主要涉及三方客户端A持有集合X、客户端B持有集合Y、以及两个非共谋的辅助服务器S1和S2。协议的核心流程分为离线阶段和在线阶段这是提升效率的常见手段。离线阶段系统初始化一个可信第三方或通过MPC协议生成OPRF密钥k并将其通过(2,2)加法秘密共享拆分为k1和k2分别安全地分发给服务器S1和S2。即k k1 k2 mod p在某个有限域上。数据预处理客户端A和B各自对自己的原始数据集进行模糊化编码。例如对于每个字符串可以提取其n-gram集合如3-gram或者使用局部敏感哈希LSH函数族生成一组固定长度的“模糊指纹”或“桶ID”。这个编码过程是公开的算法目的是让相似的数据项以高概率产生至少一个相同的指纹。在线阶段客户端交互OPRF阶段客户端A为其每个模糊指纹f_a生成一个盲化因子r_a计算盲化值blind_a H(f_a)^r_a然后将所有blind_a发送给服务器S1和S2。服务器S1收到后用自己的份额k1计算response1 blind_a ^ k1返回给客户端A。S2同理用k2计算response2 blind_a ^ k2。客户端A收到两个响应后计算oprf_a (response1 * response2) ^ (1/r_a)。根据乘法同态性这等价于H(f_a)^k。至此客户端A为其每个模糊指纹获得了一个由密钥k签名的OPRF值。客户端B执行完全相同的流程为其模糊指纹f_b获得OPRF值oprf_b。关键点服务器S1和S2从未见过原始的f_a或f_b只看到盲化值也从未拥有完整的密钥k。客户端得到了H(f)^k但无法自己计算其他值的OPRF结果。求交阶段客户端A将其计算得到的所有(oprf_a, 原始数据标识)对按照oprf_a排序后发送给客户端B。客户端B也将其(oprf_b, 原始数据标识)对排序。双方执行一个高效的隐私集合求交协议。这里可以直接采用基于排序列表的简单PSI-CA求交基数或PSI协议。因为oprf_a和oprf_b是确定性的相同的f会产生相同的H(f)^k所以如果f_a f_b则oprf_a oprf_b。双方通过比较排序后的OPRF值列表即可找出相等的项从而得知哪些模糊指纹匹配上了。结果映射与聚合由于一个原始数据项可能对应多个模糊指纹例如一个名字“张三”可能产生多个n-gram匹配可能发生在多个指纹上。双方根据匹配的指纹回溯到原始的本地数据标识并进行聚合去重最终得到模糊匹配后的交集结果。这个设计的巧妙之处在于将复杂的模糊相似度计算提前到本地的、明文的编码阶段如n-gram提取。而耗时的密码学操作OPRF和隐私求交是在编码后的、数量可能增多但长度固定的指纹集合上进行的精确匹配大大提升了整体效率。3. 关键实现细节与工程化挑战3.1 模糊化编码策略的选择与优化编码策略直接决定了模糊匹配的召回率和精确率是整个协议的“灵魂”。我们对比了几种常见方案N-gramN元语法原理将字符串按长度为N的滑动窗口切分。例如“hello”的3-gram集合是 {“hel” “ell” “llo”}。优点实现简单对拼写错误、插入删除错误有较好的容错性。两个字符串如果编辑距离很小它们会有大量相同的n-gram。缺点数据膨胀严重。一个长度为L的字符串会产生 L-N1 个n-gram。这会导致后续OPRF和PSI处理的数据量剧增成为性能瓶颈。我们的优化采用布隆过滤器Bloom Filter对n-gram集合进行压缩。将字符串的所有n-gram哈希到一个m位的布隆过滤器位数组中。这样每个字符串最终用一个固定长度m的位数组表示。求交时比较两个布隆过滤器的相似度如计算汉明距离或与运算后1的个数。但注意这需要将PSI协议从基于相等性测试扩展到基于阈值比较复杂度会增加。我们折中方案是使用多个独立的哈希函数生成多个“指纹”每个指纹作为一个独立的元素参与后续的精确PSI。这相当于把布隆过滤器“拆开”了。局部敏感哈希LSH原理一族哈希函数保证相似的数据点以高概率哈希到相同的桶中。优点理论完备尤其适用于高维向量如词嵌入。对于集合相似度Jaccard或余弦相似度有成熟的MinHash、SimHash等LSH方案。缺点为了达到高的召回率通常需要多个哈希函数即多个桶ID同样面临数据膨胀问题。参数如哈希函数个数、桶宽度调优需要根据数据分布进行。我们的选择对于文本数据我们最终采用了SimHash作为默认编码。它将文本的n-gram集合或TF-IDF向量映射为一个固定长度的二进制指纹如64位。SimHash有一个重要性质两个指纹的汉明距离近似反映了原始文本的相似度。我们可以通过设置汉明距离阈值来判断是否匹配。在协议中我们可以将整个SimHash指纹作为一个元素或者为了提升召回率将指纹分段每段作为一个独立的“桶ID”。实操心得编码参数调优数据采样分析在正式运行前务必用一小部分采样数据测试不同编码方案和参数下的召回率与精确率。例如测试n-gram中N取3、4、5的效果或测试SimHash长度取64位、128位以及不同阈值下的效果。膨胀率控制监控编码后集合大小相对于原始集合的膨胀倍数。如果膨胀超过10倍就需要考虑优化比如对低频n-gram进行过滤或者采用更激进的布隆过滤器压缩。与业务对齐模糊匹配的“度”需要与业务方确认。是允许一个字符的差异还是允许语义相似这直接决定了编码方案和阈值的选择。3.2 高效OPRF与秘密共享的工程实现我们选择在椭圆曲线密码学ECC上实现OPRF具体采用Ristretto255群Curve25519的一个素数阶子群使用哈希到曲线Hash-to-Curve标准将指纹映射为曲线点。选择ECC是因为其密钥长度短、计算相对高效且天然支持我们需要的幂运算同态性。核心计算盲化blind H_to_curve(f) * r其中r是一个随机标量。服务器计算response blind * k_share其中k_share是服务器持有的密钥份额。去盲化oprf_output response * (1/r)。这里H_to_curve(f)是将指纹f确定性地映射到曲线上的一个点。乘法是椭圆曲线上的标量乘法。秘密共享的集成 我们采用简单的加法秘密共享。两个服务器分别持有k1和k2满足k k1 k2 mod L其中L是曲线群的阶。客户端将同一个盲化值blind发送给两个服务器。服务器分别返回response1 blind * k1和response2 blind * k2。客户端计算final_oprf response1 * response2 * (1/r)。因为blind * k1 * blind * k2 blind^(k1k2) blind^k在群运算中乘法对应点的加法但概念同态再乘以1/r就得到了H(f)^k。工程优化点批处理Batching客户端将成千上万个盲化值一次性发送给服务器服务器进行批量的标量乘法运算。这能极大利用底层密码学库如libsodium, OpenSSL的优化和CPU的向量化指令比逐个计算快一个数量级以上。网络通信使用高效的二进制序列化协议如Protocol Buffers、MessagePack来传输曲线点数据而不是JSON以减少网络开销。连接复用客户端与两个服务器保持长连接避免每次OPRF交互都进行TCP握手和TLS握手。错误处理与重试设计幂等的请求ID确保网络波动或临时失败时可以安全重试。3.3 精确PSI阶段的高性能实现经过OPRF处理后双方得到的是两个关于模糊指纹的OPRF值集合。我们需要在这两个集合上执行精确PSI。由于集合可能已经膨胀一个原始数据对应多个指纹选择高效的PSI协议至关重要。我们评估了三种方案基于哈希表的朴素PSI一方构建其OPRF值的哈希表另一方查询。通信复杂度为O(n)但需要暴露一方的集合大小且可能通过查询模式泄露信息。基于排序的PSI双方各自将OPRF值排序然后像归并排序一样同步遍历两个列表找出相同的元素。这是最常用的方式通信复杂度也是O(n)但比哈希表方案更平衡不暴露集合大小给对方。我们选择了这种方式。基于布谷鸟哈希/简单哈希的PSI这是目前学术界最高效的PSI协议之一如KKRT16, PSTY19。它们通过巧妙的哈希和不经意传输OT技术将通信复杂度降低到接近线性且计算也很快。但实现复杂度高且对于中等规模百万级数据基于排序的PSI在实践中往往更简单稳定。我们的实现基于排序的PSI客户端A和B分别对自己的OPRF值列表进行本地排序。双方需要同步地进行列表比较。这里不能直接交换列表否则就泄露了所有元素。我们采用了一种不经意比较的流程假设双方已通过其他方式如Diffie-Hellman协商了一个公共的伪随机数生成器PRG种子。双方用这个种子生成一个相同的随机排列分别应用到各自已排序的列表上。这相当于在保持相对顺序不变的情况下对列表进行了“同步加扰”。然后双方可以安全地例如在加法秘密共享下比较当前指针位置的元素是否相等。如果相等则记录下这个位置在加扰序列中的索引双方同时移动指针如果不相等值较小的一方移动指针。这个过程直到任一列表遍历结束。最终双方得到一组相同的索引位置。根据这些索引位置双方可以定位到本地原始数据项的ID。由于之前应用了同步的随机排列索引本身不泄露匹配元素在原始列表中的位置信息。性能瓶颈与优化排序开销对于百万级别的OPRF值256位排序是主要的内存和CPU消耗点。我们使用系统原生的快速排序如C的std::sort或针对整数的高效排序算法如基数排序。比较开销不经意比较步骤需要多轮交互。我们将其设计成批处理模式一次传输一批元素的密文或份额减少网络往返次数。结果去重一个原始数据ID可能因为多个模糊指纹匹配而出现多次。需要在本地进行去重得到最终的交集。4. 协议安全分析与威胁模型任何隐私计算协议都必须明确其安全假设威胁模型。我们的协议在半诚实Semi-honest敌手模型下被证明是安全的即所有参与方都会诚实地执行协议流程但可能会尝试从接收到的消息中推断其他方的私有输入信息。我们的安全目标确保除了交集结果以及交集大小外不泄露任何额外信息。具体来说客户端A不能得知客户端B集合中不属于交集的部分。客户端B同理。辅助服务器S1和S2除了知道它们参与了协议以及处理的盲化值数量即模糊指纹的总数外不应得知关于客户端原始数据的任何信息包括交集信息。关键安全论证点OPRF的隐私性依赖于底层椭圆曲线离散对数问题的困难性。服务器看到的盲化值是随机曲线点无法与原始指纹关联。客户端得到的OPRF输出在没有密钥k的情况下是伪随机的无法逆向或与其他输出关联。秘密共享的安全性只要两个服务器不共谋任何一个服务器都无法恢复出完整的OPRF密钥k因此无法独自计算任何有效的OPRF值也无法将客户端请求关联起来。模糊编码的隐私影响这是一个需要仔细权衡的地方。编码过程本身是公开的如果编码后的指纹集合信息量过大可能间接泄露原始数据特征。例如如果直接使用n-gram集合且某些n-gram非常独特如专业术语敌手可能通过背景知识推测出原始数据。因此我们建议在编码前对原始数据进行标准化和泛化如统一大小写、去除标点。考虑在编码后引入差分隐私噪声但这样可能会影响匹配精度。使用SimHash等将高维特征压缩为固定长度指纹的方法本身提供了一定的信息压缩和隐私保护。精确PSI阶段的安全性我们使用的基于排序和同步随机排列的比较协议在秘密共享或同态加密的保障下可以确保在比较过程中不泄露元素的具体值和比较顺序。威胁模型局限性共谋攻击如果两个辅助服务器S1和S2共谋它们可以恢复出完整的OPRF密钥k从而能够将客户端发送的盲化值解码回指纹的哈希值H(f)。如果指纹f本身信息量较大且敌手拥有强大的背景知识字典可能发起暴力破解或推理攻击。缓解措施包括使用更多的辅助服务器如(3,2)秘密共享或定期更换OPRF密钥。交集大小泄露协议不可避免地会泄露交集的大小。在某些严格的安全定义下这被视为一种泄露。可以通过在求交前对双方集合进行填充Padding至相同大小来隐藏真实集合大小但这会增加开销。主动攻击我们的协议目前不抵抗主动攻击恶意敌手。恶意服务器可能返回错误的计算结果破坏协议的正确性。这需要引入更复杂的零知识证明或验证机制会显著增加开销。5. 性能实测、问题排查与调优指南我们使用Go语言实现了整个协议的原型并在模拟环境中进行了测试。测试环境为两台客户端机器和两台服务器机器均为4核CPU8GB内存位于同一数据中心以模拟低延迟网络。测试数据集为随机生成的英文姓名和手机号通过引入随机编辑错误插入、删除、替换来构造模糊匹配场景。5.1 性能基准测试我们测试了不同数据规模下的端到端运行时间包括编码、OPRF、PSI所有阶段和网络通信量。数据集大小 (每方)编码后平均膨胀倍数端到端时间 (秒)总通信量 (MB)主要耗时阶段10, 000~5x (SimHash)4.2~15OPRF计算、网络RTT100, 000~5x28.5~150OPRF计算、排序1, 000, 000~5x320.7~1500排序、PSI比较结果分析OPRF是主要开销在数据量较小时OPRF的批处理计算和网络交互是主要时间消耗。优化密码学库和减少网络延迟至关重要。排序成为瓶颈当数据量达到百万级编码后即五百万条排序和PSI比较阶段的内存与CPU消耗显著上升。需要关注内存中的排序算法效率。通信量可控通信量与编码后的集合大小基本成线性关系。使用SimHash64位相比使用原始n-gram集合通信量减少了90%以上。5.2 常见问题与排查技巧在实际部署和测试中我们遇到了不少问题这里总结一下问题1匹配召回率Recall过低。现象明明有很多应该匹配的数据对但协议找出的交集很少。可能原因与排查编码方案或参数不匹配双方使用了不同的编码算法、参数如n-gram的N值不同、SimHash长度不同或预处理流程如大小写转换、分词。解决严格统一双方的编码代码库和配置参数。建议将编码函数和参数作为协议的一部分进行协商或硬编码。阈值设置过严如果使用SimHash并比较汉明距离阈值设得太小。解决在采样数据上绘制“相似度-汉明距离”曲线根据业务可接受的误匹配率False Positive来选择合适的阈值。数据预处理不一致例如一方去除了空格和标点另一方没有。解决定义并共享一份详细的数据清洗和标准化规范。问题2误匹配率False Positive过高。现象协议报告了交集但经人工核对发现并不是真正的匹配对。可能原因与排查编码冲突尤其是使用布隆过滤器或短SimHash时两个不相似的数据可能映射到相同的指纹或落入同一个桶。解决增加指纹长度如从64位SimHash增加到128位或使用多个独立的LSH函数增加“桶”的数量降低冲突概率。但这会增大计算和通信开销需要权衡。阈值设置过宽汉明距离阈值太大。解决同召回率问题需要在采样数据上调整阈值。问题3协议运行速度远慢于预期。现象在小数据量上测试正常数据量稍大就耗时剧增。可能原因与排查未启用批处理OPRF阶段是逐个元素请求服务器的。解决务必实现批处理接口一次性发送/处理整个集合。序列化/反序列化开销大使用了低效的序列化方式如JSON传输曲线点二进制数据。解决换用Protobuf、MessagePack或直接发送字节数组。内存排序算法不佳对于非常大的集合默认的快速排序在极端情况下可能退化为O(n^2)。解决使用内省排序introsort或针对整数的基数排序。网络延迟和带宽服务器部署在公网延迟高。解决尽可能将辅助服务器部署在低延迟的网络环境中或使用专线。压缩传输数据。问题4服务器内存占用过高。现象处理大量请求时服务器进程内存持续增长。可能原因与排查未及时释放资源服务器为每个客户端连接或每批请求分配了大量临时缓冲区如存储所有盲化值用于批处理计算计算完成后未释放。解决优化内存管理使用对象池复用大内存块确保函数作用域结束后内存可被GC回收。数据膨胀客户端使用了产生大量指纹的编码方案如细粒度的n-gram。解决与客户端协商优化编码方案或在服务器端对过大的请求进行限流或拒绝。5.3 调优建议与最佳实践分阶段测试不要一开始就运行端到端协议。先单独测试编码模块的召回率和精确率再测试OPRF模块的正确性和性能最后集成测试。性能剖析Profiling使用性能剖析工具如Go的pprofPython的cProfile定位代码热点。通常热点在密码学运算、序列化和排序上。渐进式部署在生产环境中先从非核心业务、小数据量开始试运行监控系统稳定性、资源消耗和匹配效果再逐步扩大规模和范围。密钥管理OPRF的密钥需要安全生成和分发。可以考虑使用硬件安全模块HSM或基于MPC的密钥生成仪式来初始化密钥。并制定定期轮换密钥的策略。日志与审计记录协议执行的元数据如参与方ID、时间戳、处理的数据量大小、运行时间但不记录任何隐私数据如原始数据、指纹、OPRF值。这些日志用于监控、计费和故障排查。这个基于秘密共享OPRF的模糊PSI协议从理论到落地是一个充满挑战但也收获颇丰的过程。它让我深刻体会到隐私计算不是一个炫技的密码学玩具而是需要紧密贴合业务需求、在安全、效率和准确性之间不断权衡的系统工程。目前这个方案在处理百万级别数据、容忍轻微数据差异的场景下已经可以实用。下一步我们正在探索如何将其与更复杂的相似度度量如编辑距离的精确计算在MPC框架下结合以应对对匹配精度要求极高的场景当然那又是另一个层面的挑战了。