【隐私计算篇】隐私计算匿踪查询技术深入浅出

        之前对隐私计算中关键的安全求交技术(PSI)不经意传输技术(OT)做了分析和解读,在有了这两种技术铺垫之后,今天开始对隐私计算中应用极广的匿踪查询(PIR)技术进行分享,PIR通常也可以被称为隐私信息检索、隐匿查询等。

1. 匿踪查询定义

        匿踪查询技术,属于隐私保护查询技术,是一种确保用户在查询信息时,其身份和查询内容不被泄露的技术方案。PIR的主要目的是保护用户隐私,确保服务器无法知道用户查询的具体内容,同时具有严格访问控制,用户只能访问其查询的数据,其他数据对用户完全隐藏。匿踪查询技术广泛应用于网络搜索、数据库查询、位置服务等领域,为用户提供了一种安全、私密的信息检索方式。

2. 匿踪查询的业务应用场景

        老规矩,谈技术之前,先讲一下技术可以用在哪,解决哪些真实的业务问题?

(典型应用场景)联合风控:匿踪查询允许用户从数据库中检索信息,而无需暴露他们查询的内容。支持服务端无感知查询黑名单信息,构建多头画像。并且通过结合匿踪查询和MPC技术,多个机构可以共同计算风控模型,而各方的数据仍然保持私密。各金融机构可以在不透露各自客户数据的情况下,共同计算风险评分模型。

        此外,匿踪查询还可以应用于秘密上网,比如秘密从搜索引擎搜索信息,而无需暴露查询的信息给对应的搜索服务提供商。也可以从电影网站下载对应的电影,不会让服务端感知下载了哪一部。将匿踪查询应用于保护用户隐私的场景,基于密码学的技术,能给用户带来更大的信任感。

3. 匿踪查询分类

        匿踪查询实现的形式有多种:

        从服务器数量,可以分为单服务器方案(Single Server)和多服务器方案(Multi-Server),学术界两种方式都有相应的研究,但是在行业生产应用的,普遍是单服务器方案,因为在实际场景中,很难规避服务器合谋的风险。

        这里给出近25年来,single server算法的演进发展图,simplePIR的吞吐性能接近two-server PIR。

        从查询类型,可以分为基于索引的隐私查询方案(Index PIR)和基于关键词的隐私查询方案(Keyword PIR) 。用户基于数据的位置索引进行查询,如服务端有n条数据,用户查询第k个数据。基于同态和基于OT的PIR,属于Index PIR, 比如Single Server Index PIR典型的算法有SealPIR。数据变化比较频繁的情况,Index PIR不适用。而服务端的数据是(key, value)构成的n对元素,用户根据key进行查询,属于Keyword PIR,Single Server Keyword PIR典型算法有Labeled PSI。

        从查询条目量级,可以分为单条隐私查询(一查多)和批量隐私查询(多查多)。在线阶段的隐私查询往往是一查多,对于系统结果返回时延有较高的要求,目前1查1000万业内最佳的可以做到50ms以内。离线阶段,对于实时性要求不高,采用批量查询的模式,目前10万查100亿业内最佳可以做到分钟级别。

        衡量PIR性能一般用两个指标:(1)服务端计算量;(2)查询请求通信量和响应通信量。

4. 具体算法介绍

4.1 Single Server Index PIR(单服务器索引PIR)

        这里我们介绍SealPIR【1】的实现,SealPIR是一种基于同态加密的隐私信息检索协议,能够在不泄露数据隐私的前提下进行数据检索和计算。给出技术讲解视频链接

        首先介绍一种最朴素的基于同态的PIR实现方案:   

假设服务端数据量为n,基本流程如下

  1. 客户端生成BFV算法的私钥和公钥(sk, pk);
  2. 客户端生成查询n维0-1向量(0,...,0,1,0,...,0),其中查询index i的位置位1,其它位置为0;
  3. 客户端使用公钥加密查询向量的每个分量,得到(E(0),...,E(0),E(1),E(0),...,E(0)),发送给服务端;
  4. 服务端接收到密态查询向量后,和本地数据构成的𝑛维向量(DB_1,DB_2,...,DB_n),进行点乘,得到E(0\cdot DB_1+...+0\cdot DB_{i-1}+DB_i+0\cdot DB_{i+1}+...+0\cdot DB_n),发送给客户端;
  5. 客户端对查询响应密文解密,得到待查询的数据DB_i

        HE-based PIR,用户将数据加密后上传到服务器,服务器可以对加密数据进行处理并返回加密结果。用户可以在本地解密结果以获得所需的数据。由于整个过程都是在加密状态下进行的,因此服务器无法得知用户的查询信息和数据内容,从而保护了用户的隐私。优点在于能够保护用户的隐私和数据的机密性,同时还能保证数据的完整性和可用性。由于数据是加密存储在服务器上的,因此即使服务器被黑客攻击或被恶意使用,也无法获取到用户的敏感信息。

        但是,朴素的HE-based PIR基本原理中的协议,存在明显的性能问题:

1. 查询的计算量和通信量都大,生成和发送的密文数量为n

2. 服务端每个数据表示为一个同态明文,需要计算n向量的内积,容易因噪声太大无法解密

        SealPIR提出了一系列优化点【11】,如下所示:

  • 多个数据打包为一个HE明文
  • 查询向量被压缩为单个密文,并在服务器上展开
  • 支持多维查询
  • 利用布谷鸟哈希支持一次进行多个查询
  • 从包含百万条数据的数据库中在几秒内完成查询

        SealPIR基于BFV近似同态算法,BFV算法的参数包括:多项式次数(N),明文模( t),密文模(q)。BFV参数选择:多项式次数8192,明文模16bit。数据库中数据长度288Bytes,那Ceil(288*8/16) = 144 ,Floor(8192/144) = 56,也就是最多可以打包56个数据库明文数据。

4.1.1 查询向量压缩

        假设查询向量为(0,...,0,1,0,...,0),将其压缩到一个HE明文多项式为例,查询向量中的每个分量对应为HE明文多项式中的系数。

a^0+a^1x+...+a^{N-2}x^{N-2}+a^{N-1}x^{N-1}=x^{query\_index},其中:

a_i=0, i\neq query\_index, 

a_i=1, i = query\_index

对查询明文进行加密,得到Enc(a^0+a^1x+...+a^{N-2}x^{N-2}+a^{N-1}x^{N-1})=Enc(x^{query\_index})

服务端接收到查询密文后,执行Expand算法,得到查询密文向量:(E(0),...,E(0),E(1),E(0),...,E(0))

以上面的打包参数为例,每个HE明文可以pack 56个数据库数据,客户端查询时,将db_index转换为plain_index,即对HE明文数据库进行查询,最多可以查询8192个HE明文,转换成数据库数据,最多可以查询8192*56=458752条数据,不能满足实际业务中的需求。为了满足实际需求,将查询向量压缩到多个HE明文来表示查询向量,对于百万数据来讲,需要𝐶𝑒𝑖𝑙(1000000/8192)=123个HE明文,对应123个HE密文,才能表示百万数据的查询向量。为了进一步压缩查询密文的数量,可以使用下面的多维表示方法。

关于Expand算法,理解红框部分就能明白扩展算法做的事情了。

4.1.2 多维查询

        这个思路比较直观,其实就是把原来的数据库数据,由一维的向量,转换为二维矩阵甚至是三维张量的形式,这样查询端发送的查询query,只需要发送查询对象的列向量和行向量,也就是打包的其实是两个查询密文。比如1,000,000的数据,只需要发送两个大小为1000、1000的查询向量(当然实际上会对1000大小的向量做多项式压缩)。

        还是以上述的打包例子,将数据库表示为2维数据时,通过两个查询密文可以查询的数据量是8192*8192*56≈37亿,事实上可以满足大多数的数据库查询任务。数据库数据量为n,通过打包后得到HE明文数量为n',F是密文扩张因子,二维矩阵的查询流程如下: 

服务端:

  • 将HE明文表示为√𝑛′*√𝑛′\sqrt{n} \times \sqrt{n}的矩阵𝑀,其中√𝑛′<8192。
  • 𝑉𝑐是密文查询列向量,𝐴𝑐=𝑀·𝑉𝑐,其中𝐴𝑐是√𝑛′ * 1的密文列向量。
  • 将𝐴𝑐中每个密文拆分为𝐹份明文多项式,得到√𝑛′ * 𝐹矩阵𝐴𝑆𝑐。
  • 𝑉𝑟是密文查询行向量,𝐴𝑆𝑐·𝑉𝑟,得到𝐹 * 1的密文向量。

客户端:

  • 客户端收到𝐹个密文向量,使用私钥𝑠𝑘解密,将其组合成密文𝐸𝑛𝑐(𝑀[𝑟,𝑐]),对使用私钥𝑠𝑘解密𝐸𝑛𝑐(𝑀[𝑟,𝑐])得到真正的查询HE明文𝑀[𝑟,𝑐],对HE明文𝑀[𝑟,𝑐]中对应的系数进行组合,得到真正查询的数据。

服务端为了避免进行密文乘以密文的同态运算,第3步中将密文拆解成𝐹个明文进行操作,最后服务端发给客户端的查询响应是𝐹个密文

4.1.3 一次进行多个查询

        SealPIR论文通过PBC(probabilistic batch code)将数据库中的内容分成若干batch,同时执行k个查询时,分别对不同的batch进行查询,降低整体的性能开销。论文给出了基于CuckooHash的PBC构造方案。这种模式在KKRT16 PSI中也被使用到了,是一种比较通用的分桶并行方案。

        CuckooHash的hash数为3,bin数量为1.5𝑘,k为同时查询的数量。

服务端 预处理:

  • 对DB中n个index,分别计算cuckooHash的3个Hash,得到3个bin_index,将(db_index, data)插入到3个bin_index中。

客户端 预处理:

  • 对DB中n个index,分别计算cuckooHash的3个Hash,得到3个bin_index,将(db_index)插入到3个bin_index中。
  • 将k个查询,通过cuckooHash,插入到𝑏=1.5𝑘个bin中,对空的bin进行随机填充。
  • 对每个bin执行PIR,共计b个PIR,每个PIR的index,是客户端实际查询的data_idx,在bin中的索引。

4.1.4 SealPIR的性能

在包含 2^{20} 个元素的数据库上,以不同批量大小,每个请求(摊销)CPU 和网络成本的两种多查询 PIR 方案。方案分别为 Pung 的多检索协议和基于 PBC 的 mPIR(Cuckoo 变体)。第二列给出了检索单个元素的成本(不摊销)。基础 PIR 库为 SealPIR,其中t = 2^{20}且元素大小为 288 字节。

4.2 Single Server Keyword PIR(单服务器关键词PIR)

        这里我们主要介绍Labeled PSI【2】的实现。微软开源了针对Labeled PSI的代码实现,相应的项目时APSI(Asymmetric PSI)库,基于【2】中描述的协议,提供了适用于不对称集合大小的PSI功能以及匿踪查询功能的实现。例如,在许多情况下,一方可能持有包含数百万条记录的大型数据集,而另一方希望了解某一特定记录或少量记录是否出现在该数据集中。称之为未标记模式的APSI。然而,在许多情况下,查询方还希望检索到每个匹配记录的某些信息。这可以看作是一个具有隐私保护批量查询能力的键值存储。在这种键值存储中,我们使用项目和标签来指代键和值,并将其称为标记模式的APSI。

4.2.1 unlabeled模式(仅判断查询项是否匹配)

        使用发送者和接收者来表示APSI协议中的两方:发送者将结果发送给接收者。例如,在一个常见的使用案例中,服务器托管一个可以由多个客户端使用加密记录进行查询的查找表。在这种情况下,服务器充当发送者,而客户端充当(独立的)接收者。

        APSI使用微软SEAL库中实现的BFV加密方案。SEAL库支持使用算术电路(例如模素数的加法和乘法)进行计算,具有有限的深度,而不是在加密数据上进行任意计算。这些计算可以以批处理方式进行,即单个SEAL密文可以加密一个大的值向量,并且每个向量中的值同时且独立地进行计算;批处理对于性能的保证非常重要。

        加密数据上可以进行的计算能力由每个密文携带的噪声预算跟踪。新加密的密文有一定量的噪声预算,该预算会在计算(特别是乘法)中消耗。一旦密文的噪声预算完全耗尽,它就不能再被正确解密。为了支持更大的乘法深度的计算,有必要从更大的初始噪声预算开始,这可以通过适当调整加密参数来实现。

       APSI需要使用者明确提供SEAL加密参数。这里描述三个重要的加密参数:

  • plain_modulus是最容易理解的。它必须是一个模2 * poly_modulus_degree同余于1的素数,并定义了BFV方案加密的有限域数据类型。例如,如果plain_modulus是65537(一个17位的素数),则该方案加密模65537的整数,并且加密数据上的计算保留模65537的整数算术。较大的plain_modulus会导致噪声预算消耗更快。尽可能设计一个较小的plain_modulus来进行计算。

  • poly_modulus_degree是一个正的2次幂的整数,决定了多少模plain_modulus的整数可以编码到单个SEAL明文中;典型的值有2048、4096、8192和16384。数千个值的计算可以以一次加密数据计算的成本完成。poly_modulus_degree还会影响加密方案的安全级别:如果其他参数保持不变,较大的poly_modulus_degree更安全。

  • coeff_modulus是一组素数,决定了新加密密文的噪声预算。在SEAL中,coeff_modulus的素数很少以值的形式明确给出,而是以位数的形式给出。在APSI中,有必要在coeff_modulus中至少指定两个素数,但素数越少越好;使用2到8个素数可能是合理的。单个素数最多可以达到60位。噪声预算线性依赖于素数的总位数。coeff_modulus还会影响加密方案的安全级别:如果其他参数保持不变,总位数越大,安全性越低。因此,要获得更多的计算能力,即更多的噪声预算,需要增加coeff_modulus的总位数,并因此可能需要增加poly_modulus_degree以确保安全。

        SEAL为了防止用户意外设置不安全的参数。会检查给定的poly_modulus_degree,总的coeff_modulus位数不超过以下界限:

poly_modulus_degreemax coeff_modulus bit count
102427
204854
4096109
8192218
16384438
32768881

        APSI的直观思路:假设发送者持有一个集合 {Y_i},其中每个元素都是模plain_modulus的整数;接收者持有一个单独的项 X,同样是模plain_modulus的整数。接收者可以选择一个秘密密钥,加密X以获得密文 Q = Enc(X),并将其发送给发送者。发送者现在可以在 x = Q 处评估匹配多项式 M(x) = (x - Y_0)(x - Y_1)...(x - Y_n)。这里的 Y_i 值是发送者持有的未加密数据。由于同态加密的能力,M(Q) 将包含(X - Y_0)(X - Y_1)...(X - Y_n)的加密结果,如果 X 与发送者的某一项匹配则结果为零,否则结果为非零。执行X加密数据计算的发送者将无法知道结果,因为只有接收者持有秘密密钥。

        上述方法存在一个问题,就是计算具有很高的乘法深度。发送者通常会持有数百万甚至数亿项数据。这需要非常高的初始噪声预算,随后需要非常大的加密参数,并导致不可接受的计算开销。

        为了能够找到可以落地的方案,第一个步骤是找出降低计算乘法深度的方法。首先,注意发送者可以将其集合分成S个等大小的部分,并独立评估每个部分的匹配多项式,产生 S 个结果 {M_i(Q)}。所有这些结果必须发送回接收者,因此发送者到接收者的通信量增加了S倍。不过这有助于我们减少加密参数的大小。

        第二步是使用SEAL中的批处理。对于上述的每一个S部分,发送者可以进一步将其集合分成poly_modulus_degree个等大小的部分,接收者可以将其批量加密到一个单一的批量查询密文 Q = Enc([ X, X, ..., X ])。发送者可以对 Q 进行向量化版本的匹配多项式评估,通过poly_modulus_degree倍地提高计算复杂度,能够大幅度降低乘法深度。

        第三步是让接收者计算其查询的高次幂,分别加密这些幂,然后将它们全部发送给发送者。假设发送者希望评估的匹配多项式的次数为d。那么,发送者将需要加密接收者查询的所有幂次密文,直到d次幂。虽然发送者总是可以从给定的Q计算出 Q^2, ..., Q^d,但即使有上述改进,这种计算仍然可能具有较高的乘法深度。相反,假设接收者预先计算其查询的某些幂次,加密它们,并将它们与Q一起发送给发送者。如果幂次选择得当,发送者可以使用更低深度的电路计算Q的所有剩余必要幂次。接收者到发送者的通信成本会增加发送幂次的数量倍数。这可以有利于减少匹配多项式的乘法深度,从而减少加密参数的大小。这一段可能会比较复杂理解起来,后续会给一个示例来详细讲解一下。

        另外,还有一点需要特别指出,上述描述的方案在接收者有多个项目查询时,扩展性较差。因为每个接收者的项目都需要重复一次查询,因此如果接收者持有10000项,通信和计算成本将大幅增加。

        为解决此问题,可以使用cuckoo hashing布谷鸟哈希,布谷鸟哈希使用多个哈希函数(通常是2-4个)来实现非常高的哈希表填充率,桶大小为1。接收者不再通过在每个批处理槽中重复其单项X来批量加密查询Q,而是使用布谷鸟哈希将多个项{X_i}插入大小为poly_modulus_degree(批量大小)、桶大小为1的哈希表。然后对布谷鸟哈希表进行加密形成查询Q,并发送给发送者。

        发送者使用所有的布谷鸟哈希函数将其项{Y_i}哈希到一个具有任意大小桶的较大哈希表中,这里需要注意一下,发送方不再使用布谷鸟哈希。实际上,它将每个项插入多次——每个布谷鸟哈希函数各一次。因为发送者无法知道接收者的布谷鸟哈希过程最终使用了哪个哈希函数处理每个项。如果布谷鸟哈希函数的数量为H,那么显然这将发送者的集合大小增加了H倍。处理完哈希之后,发送者将其哈希表分解成如刚才降低深度中描述的部分,并在接收到Q后继续如前所述的计算。

        布谷鸟哈希允许接收者的项目密集地打包到单个查询Q中。例如,接收者能够将成千上万个查询项{X_i}适配到单个查询Q中,发送者可以同时执行所有这些查询项的匹配,代价是发送者的数据集大小增加一个小因子H。

        此外,回顾每个项目必须表示为模plain_modulus的整数。然而在SEAL中,plain_modulus通常只有16到30位,并且总是少于60位。较大的plain_modulus也会导致更大的噪声预算消耗,降低对加密数据进行计算的能力。另一方面,我们可能需要支持任意长度的项。比如某个项可能是一整份文件、一个电子邮件地址、一条街道地址或一个驾驶证号码。

        有两种可行方案,可以对大的项目进行处理:

1. 对发送者和接收者双方的所有项应用哈希函数,使它们具有固定的标准长度。我们将哈希长度设为128位,并在必要时将哈希截断为更短的长度(足够防碰撞)。我们支持的最短项长度(截断后)是80位,这远高于plain_modulus的实际大小。

2. 将每个项分成多个部分,并分别编码到连续的批处理槽中。即,如果plain_modulus是一个B位的素数,那么我们将项的前B-1位写入一个批处理槽,下一B-1位写入下一个槽。一个缺点是,批处理的明文/密文现在只能容纳poly_modulus_degree项的一部分。例如,如果plain_modulus是一个21位的素数,那么4个槽可以编码长度为80的项,查询密文Q(及其幂)可以加密接收者的poly_modulus_degree/4项。接收者现在查询的项数量显著减少。解决方法是将布谷鸟哈希表的大小与poly_modulus_degree解耦,只需使用两个或更多密文来加密{X_i}(及其幂)。

上述方法存在比较明显的问题,是泄露了匹配之外的更多信息,允许接收者知道其查询的部分是否匹配,而且匹配多项式的结果揭示了发送者的数据,即使没有匹配。这些都是重要的问题,是不可接受的。      

        解决方法是使用一种称为不经意伪随机函数(OPRF)的技术,关于OPRF可以参考文章安全求交技术(PSI)。OPRF可以被看作是一个由发送者知道密钥的哈希函数OPRF(s, -);这里s表示发送者的密钥。此外,接收者可以在不知道函数OPRF(s, -)或密钥s的情况下获取OPRF(s, X),而发送者不会得知X。

        实现方法很简单。接收者将其项X哈希到一个密码学安全的椭圆曲线点A上。接着,接收者选择一个秘密数r,计算点B = rA,并将其发送给发送者。发送者使用其秘密s计算C = sB,并将其发送回接收者。接收到C后,接收者计算椭圆曲线阶数的逆r^(-1),进一步计算r^(-1) C = r^(-1) srA = sA。接收者随后从这个点提取OPRF哈希值OPRF(s, X),例如通过将其x坐标哈希到适当的域。

        发送者知道s,因此可以简单地将其项{Y_i}替换为{OPRF(s, Y_i)}。接收者需要与发送者通信以获取{OPRF(s, X_i)};一旦接收者接收到这些值,协议可以如上所述继续进行。使用OPRF,接收者了解其查询部分是否匹配的问题就消失了。由于所有项都被发送者唯一知道的哈希函数哈希,接收者从了解发送者哈希项的部分中得不到任何好处。选择OPRF(s, -)输出256位,并将其前128位表示为ItemHash(s, -)。使用{ItemHash(s, X_i)}而不是{OPRF(s, X_i)}作为项;原因将在标签加密中进一步说明。

        在某些情况下,对于发送方而言,使用Patterson-Stockmeyer Algorithm来评估匹配和标签多项式更为有利。考虑一个简单的例子,其中匹配多项式恰好为

M(x) = 1 + 2x + 3x^2 + 4x^3 + 5x^4 + 6x^5 + 7x^6 + 8x^7 + 9x^8 + 10x^9 + 11x^{10} + 12x^{11} + 13x^{12} + 14x^{13} + 15x^{14}
        为了评估M(Q)对于加密查询Q=Enc(X),发送方必须首先从接收方发送给发送方的源幂中计算X的所有加密幂,直到X^14。接下来,将X的加密幂与M的加密系数相乘,然后将结果相加。重要的是要注意,第一步(计算所有幂)涉及大量的密文-密文乘法,而第二步(与系数相乘并相加)仅涉及密文-明文乘法,这比密文-密文乘法快得多。如果发送方将其数据集分成S个相等的部分,那么只需要计算一次幂,然后可以重复用于S的每一部分。

        现在考虑以下方法,这是Patterson-Stockmeyer Algorithm的一个特殊情况。多项式M(x)可以被重写为:

M(x) = (1 + 2x + 3x^2 + 4x^3) + x^4(5 + 6x + 7x^2 + 8x^3) + x^8(9 + 10x + 11x^2 + 12x^3) + x^12(13 + 14x + 15x^2 + 0x^3)
        为了评估M(Q),发送方需要计算X的所有加密幂直到X^3,以及X^4,X^8和X^12:总共6个幂,而不是上面所需的14个幂。接下来,发送方需要通过计算与适当幂的密文-明文乘法并相加项来评估四个三次多项式。然后,需要将上面获得的三次多项式结果与X^4的适当幂(X^4,X^8或X^12)相乘,并将结果相加。与早期方法的一个不同之处是密文-密文乘法的数量:在第一种方法中,数量是14减去接收方发送的预计算幂的数量;在第二种方法中,它是6减去接收方发送的预计算幂的数量。另一个关键的区别是,现在耗时的密文-密文乘法的数量与S成正比。这里回答了刚才提到的乘法深度问题。

        将上述项组合成三次多项式,但也可以选择使用更低或更高的次数多项式。不同的选择会导致不同的通信-计算权衡。将这些内部多项式的次数称为(Patterson-Stockmeyer)low-degree。从上面可以看出,发送方还必须计算查询的低次数+1的幂;我们将low-degree+1称为(Patterson-Stockmeyer)高次数high-degree。
        在某些情况下,协议可能导致假阳性匹配。例如,假设一个项目被分成P部分,每部分B - 1位长,如大项目所述。在某些参数化中,发送方的哈希表箱大小K可能如此之大,以至于一个特定项目部分仅凭随机机会而非真实匹配出现在相应箱中的概率相当大。如果P很小,接收方的每个项目部分在相应发送方哈希表箱中被发现的概率变得不可忽略。如果接收方每次查询提交数千个项目,并且协议执行多次,假阳性可能成为常见现象。有多种方法可以防止这种情况发生。对于单个接收方项目的假阳性概率的负对数大约是P(B - 1 - log2(K))。因此,可以通过增加plain_modulus和项目部分的数量P,或减小发送方的箱大小K来降低假阳性概率。

        以上仅考虑unlabeled mode,labeled mode并没有太大的不同,稍后再来讨论它。为了简单起见,我们假设OPRF步骤已经执行,考虑简化的情况,即接收方只需要一个密文(及其幂)来加密查询。例如,如果poly_modulus_degree是16,每个项目使用2个批处理槽,且布谷鸟哈希表大小是8;假设接收方想要对以下项目向量执行查询。

接收方的查询向量

[ item92  ]
[ item14  |
[ item79  ]
[ item3   ]
[ item401 ]

        在布谷鸟哈希处理之后,接收者的视图如下所示。整个大小为16的向量变成了一个单独的SEAL密文。

        需要注意下,这里哈希表大小之所以变成16,是因为采用了2个批处理槽,将原始的item拆分为2个part,每一个part都需要存储一份大小为8的hash表。这样总的大小就变成了16。实际一般一个item都是80~128 bit,而plain_modulus通常只有16到30位,并且总是少于60位,因此一般都需要涉及到对item的拆分。参数设置一般遵循这样的规则:table_size * slots = N * poly_moduls_degree。

接收方布谷鸟哈希表

[ item79-part1  ]
[ item79-part2  ]
[ empty         ]
[ empty         ]
[ item14-part1  ]
[ item14-part2  ]
[ item92-part1  ]
[ item92-part2  ]  ==>  query-ctxt
[ item401-part1 ]
[ item401-part2 ]
[ empty         ]
[ empty         ]
[ empty         ]
[ empty         ]
[ item3-part1   ]
[ item3-part2   ]

        发送者创建一个大的哈希表,然后将其分成几个独立的bin bundles(其实就是分桶的思想)。对于每个bin bundle,匹配多项式在查询密文query-ctxt上独立进行评估;为简单起见,我们忽略了发送者必须使用所有布谷鸟哈希函数来插入每个项。

发送方的哈希大表(使用全部布谷鸟hash函数都要插一遍)

[ item416-part1 | item12-part1  ][ item71-part1  | item611-part1 ]
[ item416-part2 | item12-part2  ][ item71-part2  | item611-part2 ]
[ item125-part1 | item9-part1   ][ item512-part1 | empty         ]
[ item125-part2 | item9-part2   ][ item512-part2 | empty         ]
[ item500-part1 | item277-part1 ][ item14-part1  | item320-part1 ]
[ item500-part2 | item277-part2 ][ item14-part2  | item320-part2 ]
[ item92-part1  | empty         ][ empty         | empty         ]
[ item92-part2  | empty         ][ empty         | empty         ]
[ item498-part1 | item403-part1 ][ item88-part1  | item5-part1   ]
[ item498-part2 | item403-part2 ][ item88-part2  | item5-part2   ]
[ item216-part1 | empty         ][ empty         | empty         ]
[ item216-part2 | empty         ][ empty         | empty         ]
[ item315-part1 | item491-part1 ][ item262-part1 | empty         ]
[ item315-part2 | item491-part2 ][ item262-part1 | empty         ]
[ item100-part1 | item37-part1  ][ item90-part1  | item3-part1   ]
[ item100-part2 | item37-part2  ][ item90-part2  | item3-part2   ]

\-------------------------------/\-------------------------------/
           Bin bundle 1                     Bin bundle 2

        发送方的表格创建开始于单一的桶包(bin bundle)。设想首先插入的是item416,它恰好落在哈希表的第一个桶中。接下来,假设我们依次添加item500、item125、item12、item9,最后是item512,如图所示,将它们放入相应的桶中。

        APSI(Asymmetric Private Set Intersection,非对称私有集合交集)允许指定发送方在每个桶包中可以容纳的项目数量(横向)。为了这个例子,我们假设这个数值是2,但在实际应用中,这个数值会更大。

        当需要更多的空间时,一个新的桶包就会被创建。发送方最初只有Bin bundle 1,但item512会落在与item125和item9相同的桶中,而根据我们设定的2的上限,这个桶已经满了。因此,APSI创建了Bin bundle 2,并将item512插入其中。接下来,item277被插入到Bin bundle 1中,因为还有空间容纳它。最终,我们可能会得到数十或数百个桶包,而一些最后添加的桶包可能留有许多空位。

        在匹配过程中,加密查询query-ctxt以加密形式与Bin bundle 1和Bin bundle 2进行匹配,产生结果result-ctxt-1和result-ctxt-2,并将这些结果发送回接收方。接收方解密这些结果,找到匹配结果如下。

接收方解密结果

                    [ item79-no-match  ]                            [ item79-no-match  ]
                    [ empty            ]                            [ empty            ]
                    [ item14-no-match  ]                            [ item14-match     ]
result-ctxt-1  ==>  [ item92-match     ]        result-ctxt-2  ==>  [ item92-no-match  ]
                    [ item401-no-match ]                            [ item401-no-match ]
                    [ empty            ]                            [ empty            ]
                    [ empty            ]                            [ empty            ]
                    [ item3-no-match   ]                            [ item3-match      ]

        APSI计算每个结果密文匹配值的逻辑或运算,并根据原始查询中项目出现的顺序对结果进行排序,例如,生成如下结果向量。查询中项目的顺序是任意的,且与结果无关。在更具体的语境下,这意味着APSI算法会处理所有结果密文的匹配信息,通过逻辑或运算合并这些信息,确保即使一个项目在多个桶包中出现,也只被计算一次。然后,它会根据原始查询中项目的顺序来排列这些结果,生成一个有序的结果向量。查询中项目的原始顺序并不影响最终匹配结果的准确性,但会影响结果向量中项目出现的顺序。

接收方查询向量                  接收方匹配结果向量

[ item92  ]                  [ match    ]
[ item14  ]                  [ match    ]
[ item79  ]                  [ no-match ]
[ item3   ]                  [ match    ]
[ item401 ]                  [ no-match ]

        在此情况下,接收方得出结论:item92、item14和item3均存在于发送方的数据库中,而其他项目则不在其中。首先,双方的原始项目并不会直接被插入到APSI协议中,而是使用其OPRF(Oblivious Pseudorandom Function,不经意伪随机函数)哈希值进行操作。其次,发送方需要使用每一种布谷鸟哈希函数多次插入每个项目。例如,如果使用了三种布谷鸟哈希函数,发送方将插入Hash1(item92)、Hash2(item92)和Hash3(item92)。而接收方仅插入Hash?(item92),其中Hash?是三种哈希函数之一;无论如何,匹配项都将被发现。因此,上述图表具有误导性:我们应当使用如item92-hash1-part1这样的名称。第三,如“大型项目”部分所述,在许多情况下,接收方的查询由多个密文组成,而不仅仅是一个。例如,假设我们使用大小为32的布谷鸟哈希表,而非8。此时,单个明文无法再编码接收方的查询。相反,查询被分割成4个密文,每个密文编码了更大布谷鸟哈希表的连续部分。在实际操作中,这意味着为了适应更大的数据集,接收方的查询被分解成多个部分,每个部分对应哈希表的一个子集。这种设计确保了即使在处理大规模数据时,APSI协议也能有效运行,同时保持了查询的隐私性和效率。

接收方布谷鸟哈希表

[ item79-part1  ]
[ item79-part2  ]
[ empty         ]
[ empty         ]
[ item14-part1  ]
[ item14-part2  ]
[ item92-part1  ]
[ item92-part2  ]  ==>  query-ctxt0
[ item401-part1 ]
[ item401-part2 ]
[ empty         ]
[ empty         ]
[ empty         ]
[ empty         ]
[ item3-part1   ]
[ item3-part2   ]

[ ...           ]  ==>  query-ctxt1

[ ...           ]  ==>  query-ctxt2

[ ...           ]  ==>  query-ctxt3

        在发送方一侧,也会发生类似的分解过程,形成一个参差不齐的桶包数组。以下是发送方视图的一个示例。这意味着发送方的数据也会被分解成多个桶包,形成一个不规则的数组结构。每个桶包可能包含不同数量的项目,这取决于项目的哈希值和桶包的容量限制。

                +------------++------------++------------+
                |            ||            ||            |
Bundle index 0  | Bin bundle || Bin bundle || Bin bundle |
                |            ||            ||            |
                +------------++------------++------------+
                +------------++------------+
                |            ||            |
Bundle index 1  | Bin bundle || Bin bundle |
                |            ||            |
                +------------++------------+
                +------------++------------++------------++------------+
                |            ||            ||            ||            |
Bundle index 2  | Bin bundle || Bin bundle || Bin bundle || Bin bundle |
                |            ||            ||            ||            |
                +------------++------------++------------++------------+
                +------------++------------++------------+
                |            ||            ||            |
Bundle index 3  | Bin bundle || Bin bundle || Bin bundle |
                |            ||            ||            |
                +------------++------------++------------+

        当发送者接收到查询密文 query-ctxt0 时,它必须计算 bundle index 为 0 的每个bin bundle的匹配。类似地,查询密文 query-ctxt1 必须与 bundle index 为 1 的每个bin bundle进行匹配,依此类推。接收者获得的结果密文数量将等于发送者持有的bin bundle的总数;客户端无法事先知道这个数量。

        这里需要注意,每个bundle index其实对应的是拆分的密文块,比如bundle index等于0对应的是第一块密文,依此类推。然后每个bundle index中的bin bundle,是因为插入不同的哈希的时候,动态扩展,就会形成这种不规则的bin bundles。

4.2.2 labeled模式(除了判断查询项是否匹配,还关心匹配到的关联数据)

        标记模式与之前的模式并没有太大的区别,但需要一些额外的解释。接收者除了了解其查询项是否在发送者的集合中,还会了解发送者与这些项关联的数据。可以将其视为具有保护隐私查询功能的键值存储。

        先回顾一下基本思想中的匹配多项式 M(x) 在接收者的加密项 Q 上进行评估时,输出的是零的加密还是非零值的加密。在labeled模式中,发送者创建另一个多项式 L(x),即label插值多项式,它具有以下属性:如果{(Y_i, V_i)}表示发送者的item-label对集合,则 L(Y_i) = V_i。在接收到 Q 后,发送者计算密文对 (M(Q), L(Q)) 并将其返回给接收者。接收者解密该对并检查第一个值是否解密为零。如果是,第二个值解密为相应的label。
        一个直接的问题是,所有的加密计算都是在 plain_modulus 下进行的,但发送者的标记可能比 plain_modulus 长得多。对于item来说,这是一个问题,并且在大项中通过先将项哈希到有界大小(80-128位),然后使用一系列批处理槽来编码项来解决。这个解决方案对于标记也在一定程度上有效。也就是说,label可以像item一样分成多个部分,并且对于每个部分,可以形成一个label插值多项式,当在相应的项的部分上进行评估时,输出该部分的标记。

        这还不是一个完美的解决方案,因为我们的项没有固定的大小,而且长度相当短(最多128位)。比item长的label可以分成多个部分,每个部分的长度与item的长度相同。对于每个部分,我们可以构造一个单独的label插值多项式,将它们全部在加密查询上进行评估,并将每个加密结果返回给接收者。接收者解密结果并将它们连接起来以恢复匹配的项的标记。

        上述方法还存在一个严重的问题,必须加以解决。回想一下我们如何使用OPRF来防止发送方的物品部分(或全部)泄露给接收方:给定一个物品Y,匹配多项式实际上并非针对Y本身计算,而是针对ItemHash(s, Y)计算,其中ItemHash(s, Y)表示物品OPRF值OPRF(s, Y)的前128位。这意味着标签插值多项式L实际上应该具有以下属性:对于发送方的每个物品Y_i,L(ItemHash(s, Y_i)) = V_i。然而,如果接收方可以猜测到某个ItemHash(s, Y_i)的一部分,它可以使用该部分来查询该物品对应的label的相应部分,这是不可接受的,因为接收方实际上并不知道物品Y_i。

        为了解决这个问题,发送方使用对称加密函数Enc(<input>, <key>, <nonce>)来使用由OPRF(s, Y_i)派生的密钥加密标签V_i。具体而言,作为物品Y_i对应的标签V_i的加密密钥LabelKey(s, Y_i),我们使用OPRF(s, Y_i)的256位输出的剩余128位,因此我们必须向接收方传递的标签变为Enc(V_i, LabelKey(s, Y_i), nonce)。这种方式仍然存在一个小问题,因为接收方必须以某种方式知道nonce。一种选择是使用恒定的或空的nonce。在这种情况下,必须非常小心,因为如果对于相同的OPRF密钥s,对于同一物品的两个不同标签的加密可以被敌方学习到,那么它们可能能够获取有关标签的信息。这可能发生,因为APSI支持更新物品的标签。另一种选择是使用长随机生成的nonce——对每个加密都是不同的——接收方必须以某种方式学习。APSI通过随机采样nonce,将其与V_i的加密串联起来,并将串联用于插值多项式L来实现这一目标。换句话说,发送方为每个物品Y_i随机采样一个随机nonce,并计算label插值多项式L,使得L(ItemHash(s, Y_i)) = nonce | Enc(V_i, LabelKey(s, Y_i), nonce)。

        接收方从学习加密标签的部分(或全部)中获益不大,除非它还知道原始物品。此外,即使接收方通过猜测ItemHash(s, Y_i)并在离线攻击中列举所有可能的物品Y_i(或通过其他方式后来了解到Y_i),它仍然无法获取标签,因为LabelKey(s, Y_i)是由OPRF(s, Y_i)派生的,而不仅仅是Y_i。APSI允许发送方指定nonce的字节大小。默认nonce大小设置为16字节。

        此外,回想一下,在之前实战示例中发送方是如何构建一个大的哈希表,并将其分成一个个的bundle bin。在labeled模式下,每个bundle bin中不仅包含了其中的item部分,还包括了相应的label部分和label插值多项式,如上所述。插值多项式并不是一次性为整个label创建的,而是为每个部分单独创建的,尽管加密是在将完整的item分解成部分之前应用的。

        考虑一下当item416-part1和item12-part1(与实践中相同)相同时会发生什么。如果相应的label部分label416-part1和label12-part1是不同的,那么将无法创建一个label插值多项式L,因为它无法在相应的item部分上同时输出label416-part1和label12-part1。这个问题通过在将item插入到bundle bin之前检查label部分是否已经出现在相同的位置中来解决。如果其中任何一个label部分已经出现过,那么该item就不能插入到该bundle bin中,必须创建一个新的与相同的bin index相关的bundle bin。这个问题只存在于labeled模式中,并且可能导致比unlabeled模式下更差的装载率(已插入的item数/理论最大值),因为unlabeled模式下不存在这样的限制。

4.2.3 计算流程描述 

  

设置阶段

  1. 配置参数 ItemParams、TableParams、QueryParams、SEALParams
  2. 发送方的 OPRF:发送方对 OPRF 采样一个密钥\beta,并将其项目集更新为\{\{H_2(s_i, H_1(s_i)^\beta )\}\}_{s_i \in S}
  3. 发送方的哈希处理:发送方将所有s_i \in S插入到集合B[h_0(s_i)], B[h_1(s_i)], B[h_2(s_i)]中。
  4. 拆分:对于每个集合 B[i],发送方将其拆分成到bin bundles,即B[i, 1], ..., B[i, k]
  5. 计算系数:
    交集匹配多项式:对于每个bin bundle B[i, j],发送方在有限域F_t计算匹配多项式。
    标签多项式:如果发送方具有与其项目集相关的标签,则对于每个bin bundle B[i, j],发送方在有限域F_t上插值标签多项式。

相交阶段

  1. 接收方加密r_i \in R

    接收方的OPRF:接收方和发送方运行ECDH-OPRF协议,得到\{\{H_2(r_i, H_1(r_i)^\beta)\}\}_{r_i \in R}​。

    接收方的布谷鸟哈希:接收方对集合 R 进行布谷鸟哈希,得到包含m个bin的布谷鸟表 C,使用哈希函数 h1,h2,h3。

    打包:接收方将布谷鸟表 C中的项目打包成同态加密的明文多项式。

    分窗:接收方计算逐个组件的查询幂。

    加密:接收方使用FHE.Encrypt加密查询幂,并将密文发送给发送方。

  2. 发送方同态评估匹配多项式:发送方接收一系列密文,同态评估匹配多项式。如果需要标签PSI,发送方同态评估标签多项式。发送方将评估后的密文发送给接收方。

  3. 接收方解密并获得结果:接收方接收并解密匹配密文和(如果需要)标签密文,输出匹配集合和标签。

4.3 基于OPRF-PSI实现批量匿踪查询

        这种方式比较直观,通过PSI算法对需要比较的项目进行编码,编码完成后不直接求交,而是将编码结果拆分为两部分,假如编码之后对象为256bit长度,那么将前128bit作为iv,用户匹配计算,而后128bit作为对称加密密钥,对需要发送的信息进行对称加密。这样发送方将iv||enc(msg)发送给接收方后,接收方就可以利用iv进行匹配定位,假如匹配上,那么用后128bit作为对称密钥进行解密,完成匿踪查询过程。因为对称加密的性能较好,因此整体方案的计算速度较高。

        这里有一个问题是,iv会不会存在误判的情况,针对这种case,可以在解密的时候,如果对称密钥不对,进行报错处理。

4.4 基于不经意传输实现在线匿踪查询

        业内也有一些厂商,会采用基于不经意传输实现在线匿踪查询,当然也可以实现批量匿踪,这里主要讨论一下1查多的匿踪查询场景。

        第一步通过PSI定位要查询的位置索引信息,第二步通过n选1 OT进行信息的获取。关于n选1 OT的实现,可以看OT&OT扩展(不经意传输扩展)深入浅出

4.5 可感知在线匿踪查询

        匿踪查询从定义上看,其实还存在一个问题,服务端无法知道用户查询的具体内容,因此不能判断查询方是否查中所需要的信息,这种情况会对于基于查中结算带来困难。为了防止查询方作弊,必须由服务端计算查询方所查询的项x是否在服务端集合中。另外,还需要确保服务端仅能知道查询项是否匹配,不能获得额外的其他信息。

        这里需要构造一个零化多项式,服务端将多项式的系数,通过同态加密,发送给客户端,客户端计算多项式,将结果的密文结果乘上一个随机数r后,发送给服务端,服务端解密,如果Z(x)结果为0,则表明在集合中,服务端即可感知。

5. 工程化trick

        对于十亿、百亿数据源的查询场景,直接进行匿踪查询计算和通信代价非常大。常用的工程优化手段,包括分桶并行计算、预计算数据、数据压缩优化通信。除了这些,在工程上可能会利用一定的trick手段,达到缩量的目的,常用的是基于hash函数的PIR(MD5截取)。

        客户端计算查询数据的哈希值,并将哈希值的前k位或者后k位发送给服务器。服务器计算所有数据的哈希值,并与客户端发送的位匹配。然后,服务器将所有匹配结果作为后续的匿踪查询候选数据源,这将会大大降低数据源规模,保留的量级一般涉及到不可区分度的概念,比如万级、百万级等。

        查询的安全性取决于用于匹配的哈希值长度。该方案可能会泄露一些查询信息,因为服务器可以推断客户端的查询属于原始数据的某个子集。哈希值长度越大,子集越小,查询不可区分度越低。反之,哈希值长度越短,子集越大,安全性越低。

        通信开销也取决于用于匹配的哈希值长度。由于服务器知道客户端的查询属于原始数据的某个子集,因此它将该子集中所有数据作为新的候选数据源。哈希值长度越大,子集越小,通信开销越低。反之,哈希值长度越短,子集越大,通信开销越高。

        另外有一些技巧,可以参考材料【6】。

6. 公开的行业PIR性能数据

6.1 信通院2021年、2022年匿踪查询性能测试结果【8】

资源:不限带宽,6台32核256G内存服务器

6.2 厂商一数据【8】

资源: 限制网络的上/下行通信带宽均为25Mbps,CPU及内存资源为8核16G

6.3 厂商二数据【7】

资源:本地计算引擎、ODPS数据源、通信带宽1000Mbps、PIR查询value长度33 bytes

7. 前沿跟踪

7.1 Simple PIR

        针对23年发表的simple PIR【9】,这里做一些性能介绍,详细的原理分析见【Simple PIR】单服务器开源最快匿踪查询算法解析,可以对照论文看,也可以看解读视频Simple and Fast Private Information Retrieval

        吞吐量与每次查询通信量在1 CGB数据库上的对比。对于每个PIR方案,展示了通信量和相应的吞吐量,针对两种条目大小的选择:一种是最大化吞吐量,另一种是最小化通信量。(对于只显示一次的方案,两种条目大小是相同的。)通信成本是总通信量(即离线和在线通信量),并且摊销到100次查询中。

        本PIR方案在2^{33}(约85亿)位量级条目数据库上的性能表现。offline up指离线上传等于每个客户端的服务器存储。offline down指离线下载等于客户端存储。*由于测量差异,这里的吞吐量存在一些波动。

用绿色标出在各自列中接近最佳值5倍以内的单元格,用红色标出接近最差值5倍以内的单元格。(没有标出同时接近最佳和最差5倍以内的未上色单元格。)自动重新平衡没有自动参数选择工具的方案(SealPIR、FastPIR和OnionPIR),通过在一个大小为2^{33} / d的数据库上执行它们,其中d是最接近方案“最佳”条目大小的有效2的幂,见下表数据。

8. 参考资料

【1】Angel, Sebastian, et al. "PIR with compressed queries and amortized query processing." 2018 IEEE symposium on security and privacy (SP). IEEE, 2018.

【2】Cong, Kelong, et al. "Labeled PSI from homomorphic encryption with reduced computation and communication." Proceedings of the 2021 ACM SIGSAC Conference on Computer and Communications Security. 2021.

【3】APSI: C++ library for Asymmetric PSI

【4】PSI Protocols Introduction

【5】匿踪查询技术分享(腾讯云数据技术分享)

【6】基于全同态加密的隐匿查询

【7】基于全同态加密的高安全性隐匿信息查询(PIR)工程框架

【8】PPC Insights 系列:高效在线匿踪查询技术

【9】Henzinger, Alexandra, et al. "One Server for the Price of Two: Simple and Fast {Single-Server} Private Information Retrieval." 32nd USENIX Security Symposium (USENIX Security 23). 2023.

【10】PRIVATE INFORMATION RETRIEVAL     

【11】基于同态的隐私信息检索协议-SealPIR介绍  

【12】Private Information Retrieval: Recent Advances and Challenges

  • 42
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值