前期说明
Obliv-C是一款GCC包装器,其作者在C语言的基础上对其进行了一定的类C处理,添加了一些规则限制,用于模拟实现混淆电路
Obliv-C不需要手动混淆,只需要调用其中的函数便可实现混淆电路,其不涉及真实硬件电路仿真,即无法导出函数对应的基本元电路(也可能是博主没有成功实现,但其提供的所有帮助文档论文中没有提及该方面的内容,若某位同僚有解决的方法,欢迎分享学习)
Obliv-C源码公开在GitHub中,可从中下载安装Obliv-C使用,因其自带的安装教程简单实用,在此不提及其安装的操作方法(GitHub网址:Obliv-C)
Obliv-C的设计团队为其编写了一个官方网站:Obliv-C
从中可获取Obliv-C的原著论文(PDF版)以及其帮助文档、实现项目等具体资料
倘若刚开始接触使用Obliv-C,建议仔细阅读其论文以及帮助文档,论文中详细说明了Obliv-C编程的规则,而帮助文档则提供了简单的案例分析
硬件平台
Obliv-C运行于Linux系统上,博主使用了Ubuntu系统,具体的系统支持可查看GitHub中提供的Obliv-C帮助手册
文件目录
Obliv-C的设计者并没有提供其各个文件的具体介绍,简单使用Obliv-C软件也仅需了解其如何编程、如何运行、如何调试即可
打开安装好的obliv-c文件夹,可在一级目录中找到一个名为 test 的文件夹,打开它
打开后,找到一个名为 oblivc 的文件夹,打开它
打开后所显示的文件目录即为设计者提供的一系列已完成案例,其中 README.txt 文件中详细说明了各个案例的功能实现,该文件位置也是Obliv-C编程的workspace(图中某些文件夹是博主自己创建的项目文件)
对于obliv-c的其他文件夹,可暂时忽略,如果之后需要查看某一文件内容,可直接在obliv-c文件夹中搜索该文件
案例分析
以 million 文件夹为例,该文件夹中包含了百万富翁问题的混淆电路实现
million文件目录如下(未编译过该程序不会有a.out文件):
其中 a.out 为可执行程序,相当于Windows系统中的exe文件;
million.c、million.h、million.oc 文件均为代码文件,可对其进行编程;
README.txt 介绍了该代码程序如何编译、运行的方法
代码编程
学习一个案例先从其程序代码开始,即图中的 million.c、million.h、million.oc 文件,因Obliv-C使用类C语言,所以要求使用者要有一定的C语言基础
million.c 文件:编程规则与C语言完全相同,用于获取命令行参数、设置混淆电路环境、输出混淆计算结果等
million.h 文件:编程规则与C语言完全相同,用于定义混淆电路相关结构体、声明函数等
million.oc 文件:编程规则与C语言类似,具体差别可查看Obliv-C论文内容,用于定义混淆计算函数
million.h 代码如下:
typedef struct protocolIO
{ int cmp; // -1,0, or 1
int mywealth;
} protocolIO;
void millionaire(void* args);
protocolIO结构体中定义参与混淆计算的全部参数,包括各方的秘密输入(mywealth)以及最后的共享结果(cmp),特别地,各方的秘密输入可以定义为同一变量名,也可以定义为不同的变量
对于protocolIO结构体,其中的变量不能使用指针,需要用数组代替,否则编译可能不会报错,但最终运行结果错误
millionaire 函数为混淆计算函数的声明,具体的函数定义可在 million.oc 代码中查看编写
million.c 代码如下:
#include<stdio.h>
#include<obliv.h>
#include"million.h"
int main(int argc,char *argv[])
{
ProtocolDesc pd;
protocolIO io;
if(argc<3)
{ if(argc<2) fprintf(stderr,"Party missing\n");
else fprintf(stderr,"Wealth missing\n");
fprintf(stderr,"Usage: %s <1|2> <wealth>\n",argv[0]);
return 1;
}
// skip input sanitization
sscanf(argv[2],"%d",&io.mywealth);
protocolUseStdio(&pd);
setCurrentParty(&pd,argv[1][0]=='1'?1:2);
execYaoProtocol(&pd,millionaire,&io);
cleanupProtocol(&pd);
fprintf(stderr,"Result: %d\n",io.cmp);
return 0;
}
头文件声明部分,包含了 stdio.h、obliv.h、million.h 三个头文件,stdio.h 为C语言编程必要的头文件,不过多描述;obliv.h 头文件声明了混淆电路相关的内容,若想查看其中详细内容,可通过搜索该文件直接打开阅读;million.h 头文件是自行定义的文件,因此需要使用 “” 声明
main函数需要从命令行中获得相关参数,因此固定 int main(int argc,char *argv[]) 写法,其中:
argc 统计程序运行时发送给main函数的命令行参数的个数
argv[0] 指向程序运行的全路径名
argv[1] 指向在DOS命令行中执行程序名后的第一个字符串
argv[2] 指向执行程序名后的第二个字符串
argv[3] 指向执行程序名后的第三个字符串
ProtocolDesc pd 该部分并未找到对其的具体说明,但涉及混淆电路的函数都将其作为自己的参数,特此注意
protocolIO io 为在 million.h 头文件中定义的结构体
以下代码用于判断命令行输入的格式是否正确,实际使用时可根据自身需求修改判断(argc 统计程序运行时发送给main函数的命令行参数的个数)
if(argc<3)
{ if(argc<2) fprintf(stderr,"Party missing\n");
else fprintf(stderr,"Wealth missing\n");
fprintf(stderr,"Usage: %s <1|2> <wealth>\n",argv[0]);
return 1;
}
sscanf(argv[2],"%d",&io.mywealth); 赋值语句,通过获取命令行输入,赋值给对应结构体中的变量
protocolUseStdio(&pd); 未找到对应的解释说明,博主后续的编程中从未使用过该函数,遂可忽略
setCurrentParty(&pd,argv[1][0]==‘1’?1:2); 设置混淆计算的各方,判断该程序是哪一方在使用,argv[1][0]==‘1’?1:2 判断语句,参数类型 int
execYaoProtocol(&pd,millionaire,&io); 执行混淆计算,millionaire 为混淆计算函数,&io 为存储有混淆计算输入输出的结构体
cleanupProtocol(&pd); 固定用法,清除 ProtocolDesc pd
fprintf(stderr,“Result: %d\n”,io.cmp); 混淆结果输出到命令行
million.oc 代码如下:
#include<obliv.oh>
#include"million.h"
void millionaire(void* args)
{
protocolIO *io=args;
obliv int v1,v2;
bool eq,lt;
v1 = feedOblivInt(io->mywealth,1);
v2 = feedOblivInt(io->mywealth,2);
revealOblivBool(&eq,v1==v2,0);
revealOblivBool(<,v1<v2,0);
io->cmp = (!eq?lt?-1:1:0);
}
头文件部分与 million.c 类似,不再描述
void millionaire(void* args) 为混淆计算函数,囊括该次混淆计算的具体计算过程
protocolIO *io=args; 混淆计算参数对应结构体获取
obliv int v1,v2; 随机值替换后的参数变量,注意 obliv 关键字
v1 = feedOblivInt(io->mywealth,1); 混淆数据获取,feedOblivInt 函数专用于 int 类型数据,第一个参数为数据,第二个参数为参与计算的某方,其余 feedObliv* 函数可查看 obliv.oh 头文件中的内容获取其声明用法
revealOblivBool(&eq,v1==v2,0); 解密计算,revealObliv* 函数与 feedObliv* 函数类似,都可在 obliv.oh 头文件中找到其具体的声明,该函数用于将混淆计算结果解密,在Obliv-C中,有且仅有该函数可以解密混淆数据
最后注意一下,虽然提供了 million.h 头文件,但写入该头文件中的头文件声明在 million.c 与 million.oc 中不起作用,因此,million.c 与 million.oc 需要的头文件必须在各自的代码中声明
编译运行
了解具体代码的功能实现后,便可编译运行,查看结果,此时可打开 README.txt 文件,按其中的步骤实现
文件中涉及程序编译的命令如下:
/path/to/oblivcc million.c million.oc -I .
其中 /path/to/oblivcc 要按照自身的 oblivcc 所在位置修改,可直接搜索该文件获取其位置目录,博主最后的编译命令如下:
../../../bin/oblivcc million.c million.oc -I .
命令行编译结果如下:
编译成功后,会产生 a.out 文件,而后找到运行命令:
cycle './a.out 1 15 | ./a.out 2 10'
若没有 cycle 命令,直接回车,将会报错:
此时可查看文件中的描述,它提供了一个网址,可下载该命令:cycle
下载完成后,修改命令所在的位置,再回车运行,便可获得结果:
该部分使用了million作为案例进行详细分析,对于Obliv-C的初学者而言,仅仅掌握一个案例是远远不够的,建议将全部的范例都浏览试运行一遍,再入手编写自己的混淆电路程序,会更加明了
实战编程
该部分不会涉及具体的代码编写,代码函数的解释学习在案例分析中已经描述得足够详尽,因此,该部分主要介绍如何创建自己的项目、编译、运行
创建项目
推荐将自己的项目创建在与案例相同的位置,项目的创建其实就是文件夹与文件的建立过程
(1)在oblivc文件目录下新建文件夹,自主命名
(2)在新建的文件夹中建立三个文件,分别为 .c、.h、.oc 类型文件,文件命名自定,推荐命名相同便于使用
至此,便可编写 .c、.h、.oc 类型文件,实现自己的混淆电路(可复制某一案例的代码,修改其中的函数变量等,实现自己所需的功能)
编译
除去million案例的编译方法,在某些案例中,提供有 Makefile 文件,该文件中说明了项目的具体编译操作
Obliv-C大多案例中提供的 Makefile 文件内容如下:
testName=editdist
include ../common/Makefile.simple
这是一个简易的 Makefile 文件,基于 …/common/Makefile.simple 实现,可以打开 …/common/Makefile.simple 文件查看内部内容:
$(if $(testName),,$(error 'testName' must be defined before Makefile.simple is used))
CILPATH=../../../
REMOTE_HOST=localhost
CFLAGS += -DREMOTE_HOST=$(REMOTE_HOST) -O3
./a.out: $(testName).oc $(testName).c ../common/util.c $(CILPATH)/_build/libobliv.a
$(CILPATH)/bin/oblivcc $(CFLAGS) -I . $(testName).oc $(testName).c ../common/util.c
clean:
rm -f a.out
很明显可以将这两个 Makefile 文件内容合并,修改一下其中涉及的文件位置即可
testName=editdist 为项目名称,若 .c、.h、.oc 类型文件命名相同,可使用该方法,若不同,则需手动修改命令中涉及的相关文件名
以下为错误提示,可直接复制使用:
$(if $(testName),,$(error 'testName' must be defined before Makefile.simple is used))
以下可以看做一些变量的赋值,可直接复制,修改一下文件位置便可:
CILPATH=../../../
REMOTE_HOST=localhost
CFLAGS += -DREMOTE_HOST=$(REMOTE_HOST) -O3
以下为编译命令,一般可直接复制使用,同样可能需要修改文件位置,若有特殊编译要求,可在其基础上按需修改:
./a.out: $(testName).oc $(testName).c ../common/util.c $(CILPATH)/_build/libobliv.a
$(CILPATH)/bin/oblivcc $(CFLAGS) -I . $(testName).oc $(testName).c ../common/util.c
以下为清除命令:
clean:
rm -f a.out
Makefile 文件的编译只需在对应的文件目录打开命令行终端,输入 make 回车,清除命令输入 make clean 即可
编译成功,同样产生一个 a.out 可执行程序文件
运行
一般运行,打开终端,执行 a.out 文件,不同的案例会有不同的参数要求,按提供的格式输入便可,同样,某些案例在一个终端中输入命令便可运行,某些案例可能会需求打开两个终端,进行连接通信后,才可运行,具体的运行方法案例中都会说明,试运行所有案例后便可寻求一个最适合自己代码的运行方法
备注
Obliv-C实现了混淆电路,也有许多成功的项目,但其也存在一定的缺陷,例如,无法使用常用库、不能真正实现电路、仅支持C语言而不能使用C++等
对于Obliv-C的更多使用方法,如果某些同僚更为了解,欢迎分享,大家互相学习互相进步!