深入浅出隐私计算:技术解析与应用实践
上QQ阅读APP看书,第一时间看更新

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(&lt, 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字节。

057-01

图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字节。

057-02

图3-5 明文计算下抓取到的报文

考虑到“百万富翁”问题只涉及比较运算,相对简单,我们对上面提到的求向量内积案例(主要涉及乘法和加法)也进行了抓包。在使用execYaoProtocol时,共抓取到361个报文,tcpdump输出文件大小为652 378字节;在使用execDebugProtocol时,共抓取到12个报文,tcpdump输出文件大小为1584字节。由此可见,混淆电路相比明文计算的网络通信次数以及通信量都会大大增加。