本文主要用于自己回忆,写的不详细,只有大致框架,详细内容移步原paper
Zero Knowledge Static Program Analysis
摘要:
本文提出了零知识静态分析的概念,构建了一个关于静态分析输出的零知识证明,帮助验证秘密代码是否存在漏洞,且不泄漏秘密代码的内容。作者分别提出了intra-和inter-procedural两种抽象表达。
1. Introduction
静态分析是一种常见的用于验证代码的工具/方法。但是静态分析需要由证明者获得源码,因此对于秘密的不愿公开的代码很难使用静态分析,如涉及敏感信息(如人中,性或其他法律规定的敏感信息)。本文提出了零知识静态分析,一种可以让不可信第三方验证程序同时不会泄漏代码的方法。
在零知识静态分析中,我们设想在实践中应用一下三个步骤:
- 该代码的所有者承诺程序P拥有某种属性并向公众发布承诺。
- 接下来所有者可以使用零知识静态分析的方法证明一些P中存在的属性(即验证代码所有者发布的承诺)同时不泄漏P中的源代码。
- 最后,使用零知识程序执行,所有者可以进一步证明,在某些(公共的或以前提交的)输入上执行相同的程序P会产生特定的输出。
除了给定的例子,其他有用的零知识静态分析的应用还包括证明程序的最坏情况下的执行边界,没有运行时异常,没有与不安全的内存访问相关的安全漏洞,或由于定时通道而缺少安全漏洞。
零知识静态分析也能用于证明代码是否可能出现漏洞。这里有两种方法可以证明:1)提供一个秘密输入使程序发生不期望的行为。2)证明一个合理精确的静态分析不能证明漏洞的存在。第一种方法可以保证出现了漏洞,但找到这样一个输入总是很难的。例如,如果一个程序使用了差分隐私,人们就无法找到一个输入来证明他。相反,我们的技术通过静态分析证明程序很大可能不具有被关注的属性,而一个更精确的分析比一个不精确的分析更能提供有力的证据。
静态分析转换成ZKP系统。最近的ZKP工作已经产生了许多有效的系统,能够证明可被建模为算数或布尔电路的任意函数。不幸的是,静态分析通常是使用有状态算法进行的,这些算法不容易被直接转换成电路。或者,我们可以基于现有的基于RAM的零知识证明方案将静态分析转换成电路。然而,他们通常会引入高额开销。
1.1 我们的贡献
本文首先对零知识静态分析进行了研究,并提出了基于抽象解释和工作表算法的有效方案。具体而言,我们的贡献是:
- 零知识过程内分析。
- 零知识过程间分析。
- 实现和评估。
2. Preliminaries
我们使用 n e g l ( ⋅ ) negl(\cdot) negl(⋅)来表示可微方程,其中对每一个正多项式 f ( ⋅ ) f(\cdot) f(⋅)和足够大的整数k,有 n e g l ( k ) < 1 f ( k ) negl(k)<\frac{1}{f(k)} negl(k)<f(k)1。定义 λ \lambda λ为安全参数。
2.1 零知识参数
零知识参数方法是一个在证明者和验证者之间的协议,在协议的最后,验证者被证明这说服,认为计算结果C在公共输入x和证明这的秘密证人w上的结果y=C(x, w)。零知识参数拥有(1)正确性,如果结果被证明者诚实正确的计算,那么验证者总是会接受。(2)soundness,如果计算结果不正确,验证者几乎可以以忽略不计的概率拒绝。(3)零知识:除了C(x, w)=y以外,证明不会泄漏任何关于证人w的信息。
2.2 实例化抽象解释
此部分自行查阅抽象解释的相关知识。
3. 零知识抽象解释
本文针对证明program是否有bug进行实验,采用了静态分析中抽象解释的方法,重构了关于抽象解释的零知识证明。一个简单的做法是将代码先重构成数学表示,并将需要证明的program特征转化成数学公式。然后由验证者提供输入,经过公式计算后得到结果,并判断结果是否符合prover所承诺的的特征。
证明流程:假设证明者和验证者都统一一个抽象解释,该抽象表达被描述为格val#,转移函数A p , l _{p,l} p,l,工作表算法Alg,最终计算g。g是0/1表示存在/不存在漏洞。零知识抽象解释包含以下算法:
- pp<-zkAIG(1):给定一个安全参数,算法生成一个公开参数pp。
- comp<-zkAI.commit(p,pp):prover对一个秘密程序p进行承诺(承诺该程序包含某种特征属性)。
- (y, π \pi π)<-zkAI.P(p, (val#, A p , l _{p,l} p,l, Alg, g), pp):证明者在p上运行Alg,获得一个sound的分析结果S,在S上运行g获得分析的结果y,并设给你成一格联合证明 π \pi π。
- {1,0}<-zkAI.V(comp, (val#,A p , l _{p,l} p,l, Alg, g), y, π \pi π, pp):验证者验证附带有参数分析的程序声明,并证明 π \pi π。
但是该方法有一个致命的问题,即简单的将抽象解释编码成数学表达后,在运行零知识证明时所需要的电路开销会非常大,从而影响到算法的效率。本文因此进一步对算法进行了改进,不再简单的重构算法,而是使用下面的方法来减小电路开销。
3.1 程序表达
在开始描述算法之前,首先需要将代码进行重新编码,生成一个程序表达。
针对赋值,条件和循环语句建立状态转移表,给每行程序一个行号,每一行表示一个抽象状态。由于本文特别设定了条件和循环语句的状态,因此每个状态最多有两条出边。详细可看论文table1和figure1内容。
3.2 证明程序内分析
拥有程序表达后,我们需要将程序将表达重构成零知识证明的形式。
构成分为四部分:1)检查CFG(程序流图)与源码的一致性,2)检查每次迭代的正确执行,3)检查个操作和转移函数,4)确定是否有漏洞。
检查CFG的一致性: 得到CFG后,分别在CFG和程序上获得两个特征函数,然后带入随机参数r到两函数内,如果函数计算结果相同,则CFG和源程序有很大概率是一致的(Schwartz-Zippel Lemma)。
检查执行路径的正确性: 即检查程序是不是按照所给出的execution trace来执行的。该检查可分为四部分:执行队列操作,取指令操作,读写分析状态,提取接下来的流。
内存一致性检查: 通过检查内存init并WS=RS并final来确认一致性。
格操作和转换函数: 格的加减并交操作,状态转换操作。
本文在这四个步骤里都进行了优化,使复杂度更低,且消耗更少。
4. 证明过程间分析
过程间难点在于存在函数调用,使得每个状态的入边和出边变成多条而不是两条。
4.1 过程间抽象表达
对于要被调用的函数p,增加了指令init§,final§,enter§,exit§四个。init表示函数开始,final表示函数结束,enter表示输入,exit表示输出。调用函数的流程可表示为enter§->init§,final§->exit§,enter§->exit§三种。
4.2 调整方法zkAI
挑战:由于出入边增多,若使用矩阵表示会浪费大量资源,因此需要改进方法。
函数表达: 在之前的基础上增加调用函数的table
将程序流图使用链表表示: 不使用矩阵表示,改为链表表示,三个数组,data,next和head数组。head储存每条流的开始坐标,如流l的开始坐标为head[l],next存该流内结点的下一个结点坐标,如流l的开始坐标为pt=head[l],则下一个坐标为pt=next[pt],data存储该结点的数据。
循环合并: 将之前的工作表算法改写成用链表表示的形式,论文中的算法3即为改写后的验证算法。为将算法进一步简化,将算法中的11-16行改写成算法4的形式。
通过上述简化方式,算法的效率可以得到进一步提高。
5. 证明没有漏洞
证明没有漏洞需要给出一个不动点,由证明者给出一个解后,验证者需要验证该解是否真的为不动点。根据算法5即可验证。
6. 实现和评估
略