最近在学习MPC下的Yao's protocol双方安全计算协议。这篇博客主要记录下自己对Yao's protocol for semi-honest adversary的学习。
博客内容基于:
知乎上的排版可能更清晰些:https://zhuanlan.zhihu.com/p/392860543
有理解错误的地方烦请大佬斧正orz
有关多方安全计算(MPC)
MPC简单来说,就是一堆人想要用自己和别人手上的隐私数据来计算一些东西(比如一个函数),但每一个人都不想让其他人知道自己的隐私数据是什么。
而双方安全(2PC),便是MPC的特殊情况。如下图,其中只有两个人(Alice and Bob)想用自己的隐私数据(x)和对方的隐私数据(y)一起计算一个函数f,同时不想让对方知道自己的数据输入。
半诚实敌手(semi-honest adversary)
依然在双方安全的语境下(后文不特别指出都做此假设),如果有其中一方被腐坏(corrupted),此处假设是y方。在遵守协议的前提下,y会希望能获取到x的输入。这样的一方被称为半诚实敌手。
注意,这样的一方很想获取到对方的输入,但一定会遵守双方的计算协议。
生成混淆电路(garbled circuit)
首先我们设想,对于一个函数,我们能不能用“不真实”的输入,得到真实的结果?
我们可否建立一个这样的映射,对于我们的每个输入,与其使用真实输入参与函数计算,我们为每个输入构造一个“替身”,让这个替身为其完成计算的任务。
想象有天晚上你在酒吧里和朋友喝酒,见到吧台边有一个漂亮的女生,你很想要她的微信。但你是个很害羞的人,于是让你的朋友代你去向那个女生要微信。你的朋友答应了,去向那个女生要了微信,回来给你。于是你在那个女生不知道你是男是女,长什么样的前提下,获取到了她的微信。在这个例子中,我们可以把向女生要微信的过程看成一个要计算的函数,而“你”则是这个函数的真实输入,你的朋友则作为你的“替身”,代替你去完成了要微信的过程,得到了女生的微信——函数的计算结果。
若我们可以为函数构造出类似的计算过程,那么在不暴露我们真实输入的前提下,我们确实可以计算函数,得到真实的结果。
下面我们就来介绍姚期智院士在1986年提出的混淆电路是如何解决这个问题的。
我们知道,任何函数都可以被表示为一个门电路,计算一个函数其实就是计算一个门电路,得到电路输出的过程。一个门电路是由许多个门组成的,我们这里先用只由一个门组成的电路来解释混淆电路的原理。
比如有一个计算两个1 bit数a,b模2加法的函数 f(a,b) ,则我们可以用下面的电路来表示。
这个电路很简单,只有一个异或门,给电路提供两个输入a和b,其输出一个结果c。
下面,我们来基于这个电路(其实只有一个门),来构造一个混淆电路。
首先我们列出这个门的真值表
可以看出,电路中有三条线(两条输入线a、b,一条输出线c),我们将其标为 w1、w2、w3 。
每条线上可能会存在两种逻辑值——0和1,我们想隐藏每条线上真实的逻辑值(不想让别人知道现在是0还是1在这条线上),可以通过以下的步骤来实现。
首先,为电路中所有线路分配两个分别代表逻辑值0和1的密钥。记一条线路为w_i,则为其分配两个密钥记为k_i^b, b\in \{0,1\}。b为0则此密钥代表的逻辑值为0,为1则代表的逻辑值为1。
回到上面那个例子,电路中只有三条线路,则对线路w_1,其有密钥k_1^0和k_1^1;对线路w_2,其有密钥k_2^0和k_2^1;对线路w_3,其有密钥k_3^0和k_3^1 。(这里提一下,因为w_3为输出线路,所以不一定要为其生成密钥,但这里暂时与其他线路保持一致,具体原因后面解释)。
对电路中的每个门,我们使用上述密钥和门的真值表,对门的输出进行加密。
我们直接通过上述例子来理解加密步骤。例子中,我们的电路只有一个门,所以只需要对这个门进行加密。我们将上面真值表的a,b,c 换为 w1,w2,w3,以表示这个门对应的三条线路,其中w3为输出线路,我们对其进行加密。
对真值表的每一行,我们将输入线w1和w2的逻辑值替换成对应的密钥值。
用w1和w2的密钥值,对w3的逻辑值对应的密钥值进行加密,将w3替换为该加密值。
如第一行,w1上的0替换为k_1^0,w2上的0替换为k_2^0,w3上的0替换为k_3^0后,用k_1^0和k_2^0通过加密算法 Enc加密得到 Enc_{k_1^0,k_2^0}(k_3^0)。
其他三行也是如此操作。最后生成的表如下图的右表格。
3. 然后,我们将表中的行进行随机置换,打乱原来行的排布。这步非常关键,因为这样一来,行的位置便不包含任何信息。(若不打乱,则我们能知道表的每一行的加密值是由哪两个逻辑值加密而来的)。这一步称为混淆(garbled)。下图的右表格即为混淆后的表。
4. 最后,取出上表的第三列,得到这个电路最终的混淆表。注意,其实这只是一个门的混淆表,但因为我们这个电路中只有这一个门,所以便也是这个电路的混淆表。若电路中不只有一个门,我们要为电路中的所有门都构造一个不同的混淆表,所有门的混淆表组合而成的才是整个电路的混淆表。
注意,因为我们是构造此混淆电路的人,所以我们知道上表的含义(每行的密文代表的是什么输出)。但对于单纯只是拿到此表的人(未参与GC构造的人),对此表每行所代表的信息是一脸懵逼的。若加密后的输出是7位十进制,外人看起来就是下图这种感觉。
最后,因为我们要为电路的输出提供一个映射表。在这个例子中,w3的输出即为电路的输出。因为w3输出的v仍是密钥值,我们需要提供将其转化为真实值的映射
至此,我们的混淆电路——一个原始电路与电路的混淆表,就构造完毕了。那么我们该怎么来计算混淆电路呢?
计算混淆电路
若我们要计算原始电路,我们需要为电路的每条输入线提供真实的逻辑值0或1,才能得到真实的结果。但现在在混淆电路中,我们要为电路提供的不是真实的逻辑值,而是每条输入线上逻辑值所对应的密钥值!
我们用我们刚刚为计算函数 构造的混淆电路(一个异或门与它的混淆表)来解释。为方便展示,左表是未混淆前的表格。
此时为了方便理解,我将电路混淆表的符号转化为具体的数字。
w1上的逻辑值0和1分别对应密钥1111和2222;w2上的逻辑值0和1分别对应密钥3333和6666;w3上的逻辑值0和1分别对应密钥10000和20000。而加密算法就是简单的将输入线的值与输出线的值相加。
(注意,真实情况的密钥是随机选取,加密算法也不可能如此retarded,此处是为了简化)
现在,我们将上表混淆(行打乱)后,提取出第三列。
最后,我们定义了输出密文与真实值的映射。有了以下信息,我们就能完成计算。
现在假设输入a=1111, b=6666,此时a和b是真实输入的密钥值。我们想知道输出c是什么。
现在我们有混淆表,而表中的值就是电路输出的密文,我们得通过解密才能还原我们对应输入的真实值。我们知道加密算法是a+b+c,则我们可以解密还原出真实值。那么表中的哪个值才是我们应该解密的值呢?很简单,我们遍历表中的四个值进行解密计算,若计算出的值没有意义,则不是我们要的值。
对表中的第一行27777-1111-6666=20000,对照输出映射表,即对应真实值1,于是我们得到了电路的输出为1。
这里有个点需要特别注意:我们构造混淆电路的加密算法保证混淆表中不会(以很小概率)出现能解密出两个有意义的值的情况!
至此,我们完成了电路的计算,而且我们并没有输入a和b的真实值!
基于半诚实模型的姚式协议(Yao's protocol for semi-honest adversary)
现在,我们可以非形式化的定义姚式协议,基于半诚实模型的2PC协议。
现在假设Alice和Bob想基于姚式协议共同计算一个函数f(x,y),其中x是Alice对函数的输入,y是Bob对函数的输入。协议可以简单的分为三步:
- Alice先构造一个计算函数f的电路,然后加电路加密混淆,构造出计算函数 的混淆电路 C(包括原始电路与混淆表)。假设电路总共有n条输入线,其中i条为Alice的输入,另外n-i条则是Bob的输入。
Alice将电路C,以及Alice的电路输入对应的密钥 (k_1^b,k_2^b,...,k_i^b),b \in \{0,1\}发送给Bob。同时,Alice和Bob运用一种叫不经意传输(Oblivious Transfer)的技术,使得Bob从Alice处获取到电路有关Bob输入对应的密钥 (k_{i+1}^b,k_{i+2}^b,...,k_n^b),b \in \{0,1\},并且Alice不知道每个密钥对应的b等于0还是1(即Alice不知道Bob要用的是对应逻辑值0的密钥还是逻辑值1的密钥)。所以Alice并不知道Bob的输入。
- Bob将自己的输入密钥和Alice的输入密钥输入进电路C中,并得到最终计算结果。因为电路是Alice构造的,所以Bob不知道Alice的输入密钥所对应的逻辑值是什么。所以Bob并不知道Alice的输入。
剩下的问题
在上面的描述中,其实忽略了一些问题没有解决。
A.在加密电路中门的真值表时,我们应该用怎样的加密算法,在保证安全性的同时,满足以下两个条件:
1. 在解密混淆表时,不会出现一对密钥能将多于一个密文解密成有意义的明文。如上面的例子,如果给定我们两个输入密钥a,b,我们遍历混淆表解密时,应该只有一个值是能被正确解密出有意义的门输出的,而其他值是没有意义的。不然我们不知道该用哪个作为门输出继续计算。
2. 同时,我们在遍历混淆表时,需要能高效的辨别这个值是不是能被正确解密的,若不能则跳过。
B. 什么是不经意传输,我们该怎么实现不经意传输?
后文将会解答这两个问题