零知识证明 一文看懂 zkSNARK

一、零知识证明(Zero—Knowledge Proof)

1. 又叫最小暴露证明。

2. 零知识证明的定义为:证明者prover)能够在不向验证者verifier)提供任何有用的信息的情况下,使验证者(verifier)相信某个论断是正确的。

 

二、应用场景举例理解:

1. 非对称加密做身份认证

场景:小A想验证小B是否具有某个私钥sk,但是小B不想让小A看到私钥

小A使用该私钥对应的公钥pk加密一段只有小A自己知道的数据d,得到密文sd;

小A将密文sd发送给小B;

小B使用私钥对密文sd解开,得到d_new;

小B将d_new发送给小A;

如果d_new == d,则小A可以认为小B的确拥有公钥pk对应的私钥sk。

 

2. 验证所有权

小A想验证小B是否知道保险箱密码,但是小B不想让小A看到保险箱密码:

小A站在远处;

小B在遮挡下,输入密码,打开了保险箱;

小B拿出了保险箱中的一个东西;

如果小A认为该东西只有该保险箱中才有,则小A可以认为小B的确拥有该保险箱的密码。

 

3. 企业信贷

场景:银行A给小B贷款前,需要验证小B的工资是否大于2000元,但小B不想让银行A知道自己的工资具体是多少。

 

三、分类

1. 交互式零知识证明:需要验证者和证明者进行交互,如例子中的1。

2. 非交互式零知识证明:不需要验证者和证明者交互,如例子中的2。

 

四、零知识证明技术zkSNARK

(以下内容来自,元家昕 https://www.jianshu.com/p/b6a14c472cc1,本文增加了修订和备注)

1. zkSNARK(zero-knowledge succint non-interactive arguments of knowledge)

里面的每个单词都有特定的含义:

Zero knowledge:零知识证明。

succinct:简明的,证据信息较短,方便验证。

Non-interactivity:非交互的,证明者只要提供一个字符串,可放在链上公开验证。

Arguments:证明过程是计算完好(computationally soundness)的,证明者无法在合理的时间内造出伪证(破解)。

of knowledge:对于一个证明者来说,在不知晓特定证明 (witness) 的前提下,构建一个有效的零知识证据是不可能的。

 

2. 同态加密算法

加法同态加密算法:

(1)E(a + b) = E(a) + E(b)

E(kb) = E(b + b +  ..... + b) = E(b) + E(b) + E(b) + .... + E(b) = kE(b)

(2)E(kb) = kE(b)

详细可看:https://blog.csdn.net/u013288190/article/details/108235171

 

3. 同态隐藏

同态隐藏可以实现一定程度的零知识证明。

小A拥有x和y两个秘密的数字,需要向小B证明这两个数字的和是7,只需要执行下面三个步骤:

1.A计算E(x)和E(y),并发送给B

2.因为函数E(x)满足加法同态,B可以通过E(x)和E(y)计算E(x+y)

3.B独立计算E(7),并验证E(x+y)=E(7)

 

4. 多项式盲验证

零知识证明推广到多项式中。

假定小A知道一个最高d次的多项式P(X)。

小B想要知道对应某个值s的E(P(s))。

如何实现:小A只知道多项式P,不知道值x;小B只知道值x,不知道多项式P;E(x)是同态加密函数。

举一个具体场景

小A是一家人工智能公司,多项式P是小A公司的数据训练模型,属于公司机密;

小B是一家医院,值x是小B医院的患者数据,属于隐私数据;

小B医院想让小A公司使用数据训练模型(多项式P)训练患者隐私数据(值x)。

当然,小A公司不想让小B医院知道自己的数据训练模型(多项式P),小B医院也不想让小A公司知道自己的患者隐私数据。

可以通过下面方式实现:

1.对s的每个指数,小B计算E(x^0),E(x^1),...,E(x^d),并发送给小A

2.小A知道多项式的所有系数,可以利用同态特性计算P(x),并回送给小B

E(P(X)) = E(a0 + a1*X^1 + ... + ad*X^d)

             = E(a0) + E(a1*X^1) + ... + E(ad*X^d)

             = a0*E(1) + a1*E(X^1) + ...  + ad*E(X^d)

3.小B可以同态解密E(P(X)),得到明文结果

 

4. KCA以及完整的多项式盲验证

上面的验证存在问题:小B根本没法验证小A是真正利用多项式P(s)去计算结果,也就是说无法证明A真正知道这个多项式P(X)。

α对:满足b=α*a的一对值(a,b)。

这里的乘法*其实是椭圆曲线(ECC)上的乘法,椭圆曲线上的运算符合两个特性:一是当α值很大的情况下,很难通过a和b倒推出α;二是加法和乘法满足可交换群的特性,也就是说加法和乘法交换律在椭圆曲线上也是成立的,满足同态隐藏的特性。

我们利用α对的特性,构建一个称为KCA(Knowledge of Coefficient Test and Assumption)的过程。

B验证A是否知道某个参数γ,但是A不想让B知道参数γ的值具体是多少:

1.B随机选择一个α生成α对(a,b),α自己保存,(a,b)发送给A

2.A使用参数γ生成(a′,b′)=(γ⋅a,γ⋅b),把(a′,b′)回传给B,由于椭圆曲线(ECC)乘法的性质,A无法算出α是什么。

3.利用交换律,(a′,b′)也应该是一个α对,b′=γ⋅b=γα⋅a=α(γ⋅a)=α⋅a′

4.B校验(a′,b′),证实是α对,即b′是否等于α⋅a′,如果相等就可以断言A知道γ,但B不知道γ具体的值

(假设γ=4,但是A使用5去生成(a′,b′)不也可以构成α对?

这里只是为了保证A在a上做了运算,不是为了隐藏γ值)

这个证明可以推广到多个α对的场景,称为d-KCA。

1.B发送一系列的α对(a1,b1),(a2,b2)给A

2.A使用c数组[c1,c2],生成新的α对(a′,b′)=(c1⋅a1+c2⋅a2,c1⋅b1+c2⋅b2),把(a′,b′)回传给B。

3.利用交换律,可以证明(a′,b′)也是一个α对,b'=c1⋅b1+c2⋅b2=c1⋅α⋅a1 + c2⋅α⋅a2=α⋅(c1⋅a1 + c2⋅a2)=α⋅a′

4.B验证通过,可以断言A知道c数组[c1,c2]

(使用其他数组生成(a′,b′),不也可以构成α对?)

这个KCA咋看似乎没有什么用,但正好可以补足了之前多项式盲验证的缺陷,一个完整的多项式盲验证过程如下

0. 因为椭圆曲线的乘法符合同态隐藏的特性,A和B可以共同选择x⋅g作为同态加密函数E(x) = x⋅g

1. B计算P(s)也就是(s^0⋅g,s^1⋅g,…,s^d⋅g)和αP(s)也就是(αs^0⋅g,αs^1⋅g,…,αs^d⋅g)并发送给A,实际上过程同上一章的第一步,只是把E(x)替代成乘法,增加了αs相应的多项式结果

2. A计算a=E(P(s))=P(s)⋅g,b=E(αP(s))=αP(s)⋅g并回传

3. a值即为B所需校验的E(P(s))结果,同时KCA保证了a值必然是通过多项式生成

(g是什么?)

好了,到这里喘口气,回顾一下我们现在到底做到了些什么。

通过加法同态,我们可以实现加法隐藏,让B在不知道x和y的情况下,校验x+y的值。进一步,通过多项式盲验证,我们可以在不暴露多项式P(X)的情况下,让B校验任意给定s对应的P(s)

接下来坐好扶稳,我们要从多项式推广到任意计算的盲验证了。

 

 

任意计算转换到多项式证明

直接上例子,假定A需要向B证明他知道c1,c2,c3,使(c1⋅c2)⋅(c1+c3)=7,按照惯例,c1,c2,c3需要对B保密。

我们要做的第一步就是把计算“拍平”,通过基本的运算符把原计算画成这样的“计算门电路”。

 

当然我们也可以用程序员比较熟悉的方式来表达

S1=C1*C2

S2=C1+C3

S3=S1*S2

通过增加中间变量,我们把复杂的计算拍平,使用最简单的门电路表达。新的门电路跟原计算是等价的。

 

我们要做的第二步就是把每一个门电路表示为等价的向量点积形式,这个过程成为R1CS(rank-1 constraint system)。

每个门电路,我们定义一组向量(a,b,c),使得s . a * s . b - s . c = 0

其中s代表全部输入的向量,也就是[C1,C2,C3,S1,S2,S3],为了让加法门也能用同样的方式表达,我们增加一个虚拟的变量成为one,s向量变成[one,C1,C2,C3,S1,S2,S3]。

对应到第一个门

a=[0,1,0,0,0,0,0]

b=[0,0,1,0,0,0,0]

c=[0,0,0,0,1,0,0]

把s,a,b和c代入s . a * s . b - s . c = 0,得到C1*C2-S1=0,即这个向量表达跟第一个门是完全等价的。

同理我们可以计算第二个门

a=[1,0,0,0,0,0,0]

b=[0,1,0,1,0,0,0]

c=[0,0,0,0,0,1,0]

第三个门

a=[0,0,0,0,1,0,0]

b=[0,0,0,0,0,1,0]

c=[0,0,0,0,0,0,1]

好了,到这里,我们把一个计算式拍平成为门电路,接着又通过R1CS把门电路“编码”成向量的表达方式。

接下来是最重要的一步,把向量表达式表示为多项式,从而把向量的验证转化为多项式的验证,这个过程称为QAP(Quadratic Arithmetic Programs)。

具体办法是,在Fp上面选定任意三个不同的值,例如我们选定1,2,3,寻找一组多项式

使得多项式在x取值1,2,3的时候a,b,c数组的取值分别对应到前述三个门电路的向量。

问题转化为通过已知解倒推多项式定义,这部分可以使用拉格朗日插值完成,本文不再详述。这个过程中需要对向量的每个取值做拉格朗日插值,对于复杂问题,这个向量会非常庞大,计算过程会很复杂,这里可以利用快速傅里叶变换进行优化。

到这里,我们把原来的三个向量组表示成为一个用x表示的数组a(x),b(x),c(x)。

取多项式P(x)=s . a(x) * s . b(x) - s . c(x),根据我们原来的定义,在x取值为1,2或3的时候,P(x)=0。根据多项式特性,P(a)=0等价于P可以被(x-a)整除,P(x)一定能被(x-1)(x-2)(x-3)整除,也就是说存在H(X),使P(x)=T(x)*H(x),其中T(x)=(x-1)(x-2)(x-3)。

注意QAP这个过程把原来三个点的取值转化成为一个多项式,相当于中间插入了很多没有意义的值,这些值的取值与原公式是无关的。也就是说多项式的验证与原计算的验证本质并不等价,但验证了多项式也就验证了元计算。

好了,最终我们把原算式的证明转化成为多项式的证明,只要证明P(x)=T(x)*H(x),即可验证原算式。

匹诺曹协议

通过QAP,我们已经把计算式的证明转化为多项式的证明,现在万事具备,只欠东风,就差一个完整的验证流程了。

为了简化下文描述,我们定义s . a(x)为L(x),s . b(x)为R(x),s . c(x)为O(x),那么我们需要证明的等式就改写成L(x)*R(x)-O(x)=T(x)*H(x)。L,R和O的最高阶数是d,所以这个等式的最高阶数是2d,我们知道,两个不等价的多项式交点数量最多只有2d个,2d相较于有限域的元素个数p来说很小的情况下,我们可以采用采样的方式验证多项式相等,A随意选择多项式P(x)被校验通过的概率只有2d/p。随机采样校验的过程如下:

1.A按照上一章方法选择多项式L,R,O,H

2.B选择随机点s,计算E(T(s))

3.A计算E(L(s)),E(R(s)),E(O(s)),E(H(s))  (根据B发过来的E(s),E(s2),...)

4.B检验E(L(s)*R(s)-O(s))=E(H(s)*T(s))

这个证明过程还有四个问题需要解决:

1.保证L,R,O从同一组参数s生成

这个证明过程存在一个缺陷,正如按照我们的定义L(x)=s . a(x),R(x)=s . b(x),O(x)=s . c(x),这里隐含了一个限定条件是L,R和O必须是由同一个向量s生成,证明中忽略了这一点,也就是说A可以通过选择不符合这个限定条件的多项式来作弊。解决办法仍然是KCA,只不过这次的KCA要复杂一些。

先定义两个公式:

 

这个公式的含义是要把L,R,O的指数错开,如果L,R,O真是从同一组s=[s1,....sm]生成的话,必然有

换句话说,只要A能给出F和Fi的线性组合,即可证明L,R,O符合限定条件。这个限定条件的问题就转化为一个d-KCA的问题了。

1.B选择隐秘的α,计算E(α*Fi)并发送给A

2.A计算E(αF)回传给B

3.B根据本文公式自行计算E(F)并校验α对

2.防止暴力破解

在现在的流程里面,A需要把E(L(s)),E(R(s)),E(O(s)),根据同态隐藏的特性,根据这些值无法倒推原多项式。但是如果需要验证的问题,解不多的情况下,B还是可以通过穷举的方式暴力破解原问题,得到A的原始数据。例如我们已知A有两个正整数,要求盲验证这两个正整数的乘积是12,那么B完全可以穷举乘积是12的所有正整数组合,正向执行验证过程,与E(L(s)),E(R(s))和E(O(s))比对即可知道正确的答案是什么。

当然,我们也有解决办法。解决思路就是在生成L,R,O的时候引入随机偏置

 

因为

 

新的组合

任然可以通过多项式的校验,而因为B不知道随机数,也无法通过暴力破解的方式知晓原始参数。

3.乘法同态

匹诺曹协议的最后一步,B需要检验E(L(s)*R(s)-O(s))=E(H(s)*T(s)),而事实上,我们之前只提到E(x)满足加法同态,B是无法通过E(H(s))计算出E(H(s)*T(s))的。

解决办法需要回归到我们的数学工具上,我们需要用到椭圆曲线配对的特性,这里说来话长,本文只给出结论。通过椭圆曲线配对,我们可以得到一个弱化版的乘法同态。

定义E1(x):=x⋅g,E2(x):=x⋅h,E(x):=x⋅g,因为三个函数都是椭圆曲线,自然分别都符合加法同态,同时椭圆曲线配对特性可以保证我们能通过E1(x),E2(y)计算E(xy)。

4.减少交互

最后一个问题也是最关键的一个问题是,匹诺曹协议中需要A和B之间做很多的消息交互,而在区块链中,我们想要做到的是“公开认证”。最理想的情况就是只要A把证据作为一个字符串放置到链上,任何人都能验证结论。

可惜的是,实际上这种严格意义上的零交互证明已经被证明不能满足所有的证明场景。我们退而求其次,采用了一种称为CRS(COMMON REFERENCE STRING)的方式。原理很简单,实际上就是把随机数α和s内置于“系统”中。

所以终极版的zkSNARK过程就是:

0.配置α和s,以之计算 (E1(1),E1(s),…,E1(sd),E2(α),E2(αs),…,E2(αsd))E2(α),并公示

1.A使用公示参数计算验证多项式

2.B校验多项式,乘法同态部分利用椭圆曲线配对的特性完成,形如E(αx)=Tate(E1(x),E2(α))

当然CRS有一个极其严重的问题就是,“系统”内建的随机参数非常重要,知道这个秘密参数的人就拥有超级管理员的权限,可以任意制造伪币,这在一个去中心化的系统中几乎是不可接受的。

事实上,ZCash的系统参数采用了一种影视剧中经常出现的桥段去“保护”这个不应该也不需要由任何人掌握的配置数据。选择世界各地六个可信任的人,每人生成密钥一部分,六个人的密码拼接在一起生成公示的数据后,再分别销毁掉各自手上的密钥。除非六人合谋作弊,否则没有人拥有超级管理员的权限。


五、类似libsnark实现零知识证明还有哪些库?

  • libsnark: C++语言编写,支持GGPR13、BCTV14a等zk-SNARKs方案;
  • bellman: Rust语言编写,支持Groth16方案(该方案已经用在了Zcash 2.0 - Sapling升级中);
  • snarkjs:JS语言编写,支持GGPR13、Groth16方案;
  • dalek-cryptography/bulletproofs: 支持Bulletproof零知识方案。

 

参考:

https://mp.weixin.qq.com/s/3SMJ5VJcUR0zESTLxNX7tQ

https://www.jianshu.com/p/b6a14c472cc1

https://zhuanlan.zhihu.com/p/139591341

https://www.zhihu.com/question/289805456/answer/569039874

软件工程小施同学 CSDN认证博客专家 如果对你有用 点个赞哇
区块链、软件工程、DevOps、小程序(微博:“软件工程小施同学”,公号”微程序学堂“)
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页