3.3 应用案例:解决“百万富翁”难题
3.3.1 具体代码实现
首先在C:\ppct\obliv-c\目录下创建3个文件:million.h、million.c、million.oc。
提示
C:\ppct是本书项目代码放置的目录,读者在实践时可根据实际系统修改。
在million.h文件中,我们需要定义隐私输入(即两个富翁的财富值)、输出(即两个富翁中谁更富有)以及隐私计算函数millionaire,具体如代码清单3-16所示。
代码清单3-16 million.h中的代码
typedef struct protocolIO { int cmp; //隐私计算输出,-1:Alice小于Bob, 0:Alice等于Bob,1:Alice大于Bob int mywealth; //隐私计算输入 } protocolIO; void millionaire(void* args); //隐私计算函数
然后在million.oc文件中编写具体的隐私计算函数millionaire,具体如代码清单3-17所示。
代码清单3-17 million.oc中的代码
#include<obliv.oh> #include"million.h" void millionaire(void* args) { protocolIO *io=args; obliv int aliceWealth,bobWealth; aliceWealth=feedOblivInt(io->mywealth,1); //获取隐私输入 bobWealth=feedOblivInt(io->mywealth,2); bool eq,lt; revealOblivBool(&eq, aliceWealth==bobWealth, 0); //首先比较两人是否同样富有 revealOblivBool(<, aliceWealth < bobWealth, 0); //然后比较是否Bob更富有 io->cmp=(!eq? lt?-1:1 : 0); //最后输出比较结果 }
接下来在million.c文件中进入主函数进行编写,其主要流程为在参与方之间建立网络连接、设置自己的参与方编号、输入自己的财富值、执行混淆电路代码进行财富值比较、输出结果并清理,具体如代码清单3-18所示。
代码清单3-18 million.c中的代码
#include<stdio.h> #include<obliv.h> #include"million.h" int main(int argc,char *argv[]) { ProtocolDesc pd; protocolIO io; const char* remote_host=(strcmp(argv[2], "--")==0?NULL:argv[2]); if(!remote_host){ if(protocolAcceptTcp2P(&pd, argv[1])){ //Alice等待Bob连接 fprintf(stderr, "TCP accept failed\n"); exit(1); } } else{ if(protocolConnectTcp2P(&pd,remote_host,argv[1])!=0){ //Bob主动连接Alice fprintf(stderr,"TCP connect failed\n"); exit(1); } } setCurrentParty(&pd, remote_host?2:1); //设置参与方编号,Alice是1,Bob是2 sscanf(argv[3],"%d",&io.mywealth); //这里省略输入合法性检验 execYaoProtocol(&pd,millionaire,&io); //执行百万富翁财富值比较 cleanupProtocol(&pd); fprintf(stderr,"Result: %d\n",io.cmp); return 0; }
最后创建Makefile文件,修改一下3.2.8节提到的Makefile文件,将其中的privacy Program值修改为million。
利用上面的Docker镜像,通过以下命令运行Alice方实例:
docker network create obliv-c-net docker run -it --rm --name Alice --network obliv-c-net ` -v C:\ppct\obliv-c:/root/projects obliv-c make
提示
上面的代码中因docker run运行的参数较多,为方便阅读,使用“`”进行换行处理。这是Windows的PowerShell环境下将命令拆分成多行的特殊字符。在Windows的CMD环境下,我们则需要改用“^”字符,在Linux环境则需要改用“\”字符。
另外,为了方便Alice和Bob方的两个容器直接使用机器名进行网络通信,特意创建了obliv-c-net网络来通信。在实际应用中,两个参与方一般位于不同的机器,可直接基于IP或者域名进行通信,不需要创建obliv-c-net网络。
编译成功,我们即可看到同目录下生成隐私计算执行文件a.out。使用如下命令运行Bob方实例:
docker run -it --rm --name Bob --network obliv-c-net ` -v C:\ppct\obliv-c:/root/projects obliv-c
进入Alice方容器执行以下命令,其中,“1234”代表端口号,“--”代表服务方等待对方连接,“8”代表Alice的财富值:
./a.out 1234 -- 8
进入Bob方容器执行以下命令,其中,“1234”代表端口号,“Alice”代表需要连接的对方服务地址,“15”代表Bob的财富值:
./a.out 1234 Alice 15
最后,双方都输出执行结果-1,即Alice财富值小于Bob。
3.3.2 网络抓包及分析
从混淆电路原理可以了解到,相比明文计算,使用混淆电路计算的网络通信次数及通信量会大大增加。这里通过对上面解决“百万富翁”难题的程序进行网络抓包及分析,希望读者对密文计算和明文计算的差异有一个大概的了解。
首先是密文计算下通过tcpdump工具进行网络抓包。进入Bob方容器执行以下命令进行抓包:
tcpdump -XX -vvv tcp port 1234 and host Alice and Bob -w garbled.cap
如图3-4所示,结果显示共抓取到75个报文,输出的garbled.cap文件大小为62 562字节。
图3-4 密文计算下抓取到的报文
然后在明文计算下执行同样的操作。那么,如何进行明文计算呢?Obliv-C提供execDebugProtocol接口来替代execYaoProtocol接口。该接口可直接进行明文计算,没有采用混淆电路。因此,在上面的million.c文件中直接进行替代,然后重新编译即可。通过以下命令把抓取到的报文输出到plaintxt.cap文件。
tcpdump -XX -vvv tcp port 1234 and host Alice and Bob -w plaintxt.cap
如图3-5所示,结果显示共抓取到11个报文,输出的plaintxt.cap文件大小为990字节。
图3-5 明文计算下抓取到的报文
考虑到“百万富翁”问题只涉及比较运算,相对简单,我们对上面提到的求向量内积案例(主要涉及乘法和加法)也进行了抓包。在使用execYaoProtocol时,共抓取到361个报文,tcpdump输出文件大小为652 378字节;在使用execDebugProtocol时,共抓取到12个报文,tcpdump输出文件大小为1584字节。由此可见,混淆电路相比明文计算的网络通信次数以及通信量都会大大增加。