初学零知识证明,准备从SNARK开始入手,还是自己做个整理会印象深刻一些

目前看来GGPR13是最先提出基于QAP的SNARK这个框架的工作,而Groth16则是目前效率表现最好的SNARK方案。话说Craig Gentry大佬也太牛了吧,不止是全同态加密的鼻祖,这边SNARK的提出原来也有他的关系啊。

文章目录

  • 零知识证明
  • SNARK的基本思路
  • 为什么现在SNARK的证明要基于多项式(QAP)
  • 零知识地证明一个多项式
    • 简化版的证明过程
    • 通过混淆的计算来优化上述多项式证明
      • 使用具有同态性质的加密方法
      • 计算一个加密的多项式值
    • 对多项式的运算进行承诺
    • 如何做到零知识性
    • 如何构造一个非交互的方案
      • 建立 CRS
        • 双线性配对
        • 通过双线性配对建立CRS
        • 通过CRS构造一个非交互的协议
        • 多方共同生成CRS
  • 通用的零知识证明
  • GGPR13
  • Groth16

零知识证明

首先回答一个问题,零知识证明(ZK)是什么,为什么要学他。关于零知识证明的基础框架可以戳这里。

ZK是解决了这样一个问题:我知道一个YYY,满足F(X,Y)=ZF(X,Y)=ZF(X,Y)=Z,要怎么在不公开YYY的情况下证明这个式子成立。其中FFF是任意函数(也叫做CIrcuit),XXX是公开的输入(也叫做Instance),YYY是私有的输入(叫做Witness),ZZZ是公开的输出(和X一样叫做Instance)。把F(X,Y)=ZF(X,Y)=ZF(X,Y)=Z这一个句子叫做一个statement。

这里我给出三个常见的场景:

  1. 机器学习(可验证计算)
    很显然,ZK可以用来做可验证的计算,比如机器学习模型训练里面,我把数据外包给一家公司进行训练,这家公司训练完之后,对我来说,我得知道这个模型(YYY)是确实用我要的方法(FFF)以及我给的数据(X,ZX,ZX,Z)训练出来的才能付钱,对于公司来说,我付钱之前不能把模型的参数给我。所以这时候就可以进行一个零知识证明来让我相信他们确实诚实的进行了训练。

  2. 交易凭证(隐私保护)
    如果说我要进行一笔电子支付,假设我要付333块钱,那么我就需要证明我确实有3块钱,即我钱包里有YYY元,我要证明Y>3Y>3Y>3,但同时我也不希望别人知道这个YYY具体是多少,这时候也可以使用一个零知识证明来做。

  3. 区块链扩容
    这里YYY除了可以当做一个隐私信息来做可验证外,还可以作为一个大信息量的信息源,比如XXX和ZZZ都是几+十byte,而YYY是几MB的消息。比如在区块链中,FFF可以是验证交易以及更新账本,XXX是先前值的Merkle root,ZZZ是当前值的Merkle root,YYY是一整个Merkle tree。那么就可以使用较少的数据对大量信息进行验证了。

SNARK的基本思路

SNARKs的基本思路是Computation→\to→Arithmetic Circuit→\to→R1CS→\to→QAP→\to→SNARKs

SNARK:succinct non-interactive arguments of knowledge

用人话说的话,就是将证明知道一个函数的结果,变成证明知道一个满足某种性质的多项式。

下面会简略的过一遍这整个流程,只需要有个大概的理解就行了。后续会具体地说明为什么要把计算变为多项式来做ZK,怎么对多项式做ZK,以及里面涉及到的原理和细节。最后会说明一下为什么可以把证明一个函数变为证明一个多项式。


由一个算式,比如x∗y+4=10x*y+4=10x∗y+4=10,要不暴露x,yx,yx,y的情况下证明这个算式是成立的。


先把他转换为算数电路:


再由算数电路变为R1CS:

R1CS是一种描述电路的语言。具体来说,一条R1CS由四个向量组成,分别为三个描述向量(L,R,O)\left(L,R,O\right)(L,R,O),以及一个解向量vvv,要满足
⟨L,v⟩∗⟨R,v⟩=⟨O,v⟩\langle L, v\rangle *\langle R, v\rangle=\langle O, v\rangle ⟨L,v⟩∗⟨R,v⟩=⟨O,v⟩

如果不考虑细节,那么就记住电路能等价的转换为一种向量语言R1CS

首先这个算数电路中的Constraint有3个,分别是:

  1. output1=x∗y\text{output}_1=x*youtput1​=x∗y
  2. output1+4=10\text{output}_1+4=10output1​+4=10

将其转换为R1CS,则令v=[1,x,y,output1]v=[1,x,y,output_1]v=[1,x,y,output1​],对于两个Constraint,分别取:

  1. L=[0,1,0,0],R=[0,0,1,0],O=[0,0,0,1]L=[0,1,0,0],R=[0,0,1,0],O=[0,0,0,1]L=[0,1,0,0],R=[0,0,1,0],O=[0,0,0,1]

⟨L,v⟩=x,⟨R,v⟩=y,⟨O,v⟩=output1\langle L, v\rangle=x,\langle R, v\rangle=y,\langle O, v\rangle=output_1⟨L,v⟩=x,⟨R,v⟩=y,⟨O,v⟩=output1​

⟨L,v⟩∗⟨R,v⟩=⟨O,v⟩⟺x∗y=output1\langle L, v\rangle *\langle R, v\rangle=\langle O, v\rangle \Longleftrightarrow x*y = output_1⟨L,v⟩∗⟨R,v⟩=⟨O,v⟩⟺x∗y=output1​

  1. L=[4,0,0,1],R=[1,0,0,0],O=[10,0,0,0]L=[4,0,0,1],R=[1,0,0,0],O=[10,0,0,0]L=[4,0,0,1],R=[1,0,0,0],O=[10,0,0,0]

⟨L,v⟩=output1+4,⟨R,v⟩=1,⟨O,v⟩=10\langle L, v\rangle=output_1 +4,\langle R, v\rangle=1,\langle O, v\rangle=10⟨L,v⟩=output1​+4,⟨R,v⟩=1,⟨O,v⟩=10

⟨L,v⟩∗⟨R,v⟩=⟨O,v⟩⟺(output1+4)∗1=10\langle L, v\rangle *\langle R, v\rangle=\langle O, v\rangle \Longleftrightarrow (output_1+4) * 1 = 10⟨L,v⟩∗⟨R,v⟩=⟨O,v⟩⟺(output1​+4)∗1=10


从R1CS得到QAP:

如果不考虑细节,那么就记住R1CS可以等价的转换为一种多项式的表达

QAP的构造方式如下,L,R,O都是长度为4的向量,我们构造3*4个多项式Li(X),Ri(X),Oi(X),i∈[1,4]L_i(X),R_i(X),O_i(X), i\in[1,4]Li​(X),Ri​(X),Oi​(X),i∈[1,4]。

拿Li(X)L_i(X)Li​(X)举例,他代表着LLL向量的第iii个元素,在不同的Constraint下的多项式,也就是说L1(1)=0,L1(2)=4L_1(1)=0,L_1(2)=4L1​(1)=0,L1​(2)=4,通过多项式插值可以得到L1(X)=4X−4L_1(X)=4X-4L1​(X)=4X−4

罗列一下所有Li(X)L_i(X)Li​(X)的可得:
Constraint1:L(1)=[0,1,0,0]Constraint2:L(2)=[4,0,0,1]L1(1)=0,L1(2)=4,L1(X)=4X−4L2(1)=1,L2(2)=0,L2(X)=−X+2L3(1)=0,L3(2)=0,L3(X)=0L3(1)=0,L3(2)=1,L4(X)=X−1Constraint~1:L^{(1)}=[0,1,0,0]\\ Constraint~2:L^{(2)}=[4,0,0,1]\\ \begin{aligned} L_1(1)&=0,&&L_1(2)=4,&&L_1(X)=4X-4\\ L_2(1)&=1,&&L_2(2)=0,&&L_2(X)=-X+2\\ L_3(1)&=0,&&L_3(2)=0,&&L_3(X)=0\\ L_3(1)&=0,&&L_3(2)=1,&&L_4(X)=X-1\\ \end{aligned} Constraint 1:L(1)=[0,1,0,0]Constraint 2:L(2)=[4,0,0,1]L1​(1)L2​(1)L3​(1)L3​(1)​=0,=1,=0,=0,​​L1​(2)=4,L2​(2)=0,L3​(2)=0,L3​(2)=1,​​L1​(X)=4X−4L2​(X)=−X+2L3​(X)=0L4​(X)=X−1​
可以看出Li(n)=L(n)[i]L_i(n)=L^{(n)}[i]Li​(n)=L(n)[i]。

L(X)→=(L1(X),L2(X),L3(X),L4(X))\overrightarrow{L(X)}=(L_1(X),L_2(X),L_3(X),L_4(X))L(X)​=(L1​(X),L2​(X),L3​(X),L4​(X))

R(X)→=(R1(X),R2(X),R3(X),R4(X))\overrightarrow{R(X)}=(R_1(X),R_2(X),R_3(X),R_4(X))R(X)​=(R1​(X),R2​(X),R3​(X),R4​(X))

O(X)→=(O1(X),O2(X),O3(X),O4(X))\overrightarrow{O(X)}=(O_1(X),O_2(X),O_3(X),O_4(X))O(X)​=(O1​(X),O2​(X),O3​(X),O4​(X))

很容易验证L(1)→=L(1),L(2)→=L(2)\overrightarrow{L(1)}=L^{(1)},\overrightarrow{L(2)}=L^{(2)}L(1)​=L(1),L(2)​=L(2)

L(X)=⟨L(X)→,v⟩L(X)=\langle \overrightarrow{L(X)},v \rangleL(X)=⟨L(X)​,v⟩

R(X)=⟨R(X)→,v⟩R(X)=\langle \overrightarrow{R(X)},v \rangleR(X)=⟨R(X)​,v⟩

O(X)=⟨O(X)→,v⟩O(X)=\langle \overrightarrow{O(X)},v \rangleO(X)=⟨O(X)​,v⟩

由此可以得到L(X)∗R(X)−O(X)=0forX∈{1,2}L(X)*R(X) - O(X)=0~for~X\in\{1,2\}L(X)∗R(X)−O(X)=0 for X∈{1,2}

令P(X)=L(X)∗R(X)−O(X)P(X)=L(X)*R(X)-O(X)P(X)=L(X)∗R(X)−O(X),将P(X)P(X)P(X)拆为两个因子多项式P(X)=T(X)∗H(X)P(X)=T(X)*H(X)P(X)=T(X)∗H(X)。

其中T(X)T(X)T(X)公开。

这样的T(X)T(X)T(X)其实很容易找,因为X=1,X=2X=1,X=2X=1,X=2是P(X)=0P(X)=0P(X)=0的两个根,所以可以令T(X)=(X−1)(X−2)T(X)=(X-1)(X-2)T(X)=(X−1)(X−2),用多项式的除法得到H(X)H(X)H(X)即可。


现在要向外部证明的其实是:

P(X)=L(X)∗R(X)−O(X)=T(X)∗H(X)P(X)=L(X)*R(X)-O(X)=T(X)*H(X)P(X)=L(X)∗R(X)−O(X)=T(X)∗H(X)这样一个式子在所有XXX上成立。

也就是证明某个多项式成立

但是公开的多项式只有T(X)T(X)T(X),也就是说,如果取一个数X=sX=sX=s,那么其他人最多能知道T(s)T(s)T(s)的值。那如何在不暴露L(s),R(s),O(s),H(s)L(s),R(s),O(s),H(s)L(s),R(s),O(s),H(s)的情况下证明L(s)∗R(s)−O(s)=T(s)∗H(s)L(s)*R(s)-O(s)=T(s)*H(s)L(s)∗R(s)−O(s)=T(s)∗H(s)成了现在要考虑的问题。

那这里就用到了一种具有同态性质的加密:E(s)E(s)E(s)

这样的加密如下性质:

  1. 单项性:给定xxx,很容易计算y=E(x)y=E(x)y=E(x),但给定yyy,很难找到一个xxx满足y=E(x)y=E(x)y=E(x)。
  2. 线性:E(αx+βy)=αE(x)+βE(y)E(\alpha x+\beta y)=\alpha E(x) + \beta E(y)E(αx+βy)=αE(x)+βE(y)
  3. 单射:x≠y⟶E(x)≠E(y)x\neq y \longrightarrow E(x) \neq E(y)x​=y⟶E(x)​=E(y)

所以Prover可以直接计算出P(E(s))=E(P(s))P(E(s))=E(P(s))P(E(s))=E(P(s))的值。

SNARKs的协议如下:
1. Setup: 将E(s)E(s)E(s)公开,T(s)T(s)T(s)公开,sss本身丢弃
2. Prover自己有L,R,O,HL,R,O,HL,R,O,H四个多项式
3. Prover发送E(L(s)),E(R(s)),E(O(s)),E(H(s))E(L(s)),E(R(s)),E(O(s)),E(H(s))E(L(s)),E(R(s)),E(O(s)),E(H(s))
4. 任何人都可以验证:
E(L(s))∗E(R(s))−E(O(s))=E(H(s))∗E(T(s))E(L(s))*E(R(s)) - E(O(s)) = E(H(s)) * E(T(s))E(L(s))∗E(R(s))−E(O(s))=E(H(s))∗E(T(s))

最后证明了多项式的值就相当于证明了这个电路成立。

为什么现在SNARK的证明要基于多项式(QAP)

考虑一种最简单的交互式零知识证明:我有一个数组bbb,我想证明它里面全是111。一个很简单的方法就是,每次交互时打乱这个数组,让验证者来每次随机查看打乱后的数组中的一个值,如果这个值是111,数组的长度是ddd,那验证者就有1d\frac{1}{d}d1​的信心相信这个数组全是111。一般来说这样的交互过程要重复d2d^2d2次,那么验证者才会接近100%100 \%100%地相信这个数组里面全是111。

但如果我要证明我知道一个多项式呢?
假设Verifier已经知道了一个多项式f1(x)f_1(x)f1​(x),我知道一个多项式f2(x)f_2(x)f2​(x),我想要证明f1=f2f_1=f_2f1​=f2​。
假设这两个多项式都是ddd阶的,如果f1≠f2f_1 \ne f_2f1​​=f2​,则f1(x)−f2(x)=0f_1(x)-f_2(x)=0f1​(x)−f2​(x)=0最多只有ddd个解。


Verifier可以随机选取s∈[0,277)s\in[0,2^{77})s∈[0,277),并计算得到f1(s)f_1(s)f1​(s),然后将sss发给我。
我本地计算出f2(s)f_2(s)f2​(s),发送给Verifier。
Verifier可以验证这两个值是否相等。
因为f1(x)−f2(x)f_1(x)-f_2(x)f1​(x)−f2​(x)只有ddd个解,
那么如果f1≠f2f_1\neq f_2f1​​=f2​,则f1(s)=f2(s)f_1(s)=f_2(s)f1​(s)=f2​(s)的概率只有d277\frac{d}{2^{77}}277d​。


可以注意到使用多项式进行验证的话,只需要验证一次,就可以有极大概率可以相信这个证明是正确的。而使用普通的交互式证明则需要n2n^2n2次。
而且可以注意到,无论多项式本身有多大,他的证明是只需要给出f(s)f(s)f(s)这一个值的。依旧是说证明的大小与计算本身的复杂度是无关的,即满足了succinct性质。
因此可以考虑把实际要证明的问题转换为多项式验证的问题。

零知识地证明一个多项式

在上面的场景里面,我们是证明了自己拥有的f2f_2f2​与Verifier拥有的f1f_1f1​一致。现在考虑我要想Verifier证明我知道满足某个性质的一个多项式。

“知道一个多项式”的概念如何定义呢?nnn阶的多项式可以被表达为:
cnxn+...+c1x1+c0x0c_n x^n + ...+c_1x^1 + c_0x^0 cn​xn+...+c1​x1+c0​x0
所谓的知道一个多项式的意思就是,知道所有的(c0,...,cn)(c_0,...,c_n)(c0​,...,cn​)。

那么,如果我说我知道一个3阶多项式,满足的性质是x=1x=1x=1和x=2x=2x=2是这个多项式的两个根,那么这样一个多项式可以是p(x)=x3−3x2+2x=0p(x)=x^3-3x^2+2x=0p(x)=x3−3x2+2x=0。那么p(1)=1−3+2=0,p(2)=8−3∗4+2∗2=0p(1)=1-3+2=0,p(2)=8-3*4+2*2=0p(1)=1−3+2=0,p(2)=8−3∗4+2∗2=0

根据多项式的分解规则,一个可解的多项式可以写为多个一次多项式的组合:p(x)=(x−x1)(x−x2)...(x−xn)p(x)=(x-x_1)(x-x_2)...(x-x_n)p(x)=(x−x1​)(x−x2​)...(x−xn​),其中(x1,...,xn)(x_1,...,x_n)(x1​,...,xn​)是这个多项式的根。那么我们上述的p(x)=x3−3x2+2xp(x)=x^3-3x^2+2xp(x)=x3−3x2+2x也可以写为p(x)=(x−0)(x−1)(x−2)p(x)=(x-0)(x-1)(x-2)p(x)=(x−0)(x−1)(x−2)。如果说我要证明x=1,x=2x=1,x=2x=1,x=2是p(x)p(x)p(x)的两个根,那就相当于证明p(x)p(x)p(x)可以整除t(x)=(x−1)(x−2)t(x)=(x-1)(x-2)t(x)=(x−1)(x−2)。即p(x)=t(x)h(x)p(x)=t(x) h(x)p(x)=t(x)h(x),其中h(x)=p(x)t(x)h(x)=\frac{p(x)}{t(x)}h(x)=t(x)p(x)​。

简化版的证明过程

现在要证明的是,我知道一个nnn次的多项式p(x)p(x)p(x),其中(x1,...xd)(x_1,...x_d)(x1​,...xd​)是这个多项式的根。即证明p(x)=t(x)h(x),t(x)=(x−x1)...(x−xd)p(x)=t(x)h(x),t(x)=(x-x_1)...(x-x_d)p(x)=t(x)h(x),t(x)=(x−x1​)...(x−xd​)且h(x)h(x)h(x)是一个整系数多项式。

那么证明p(x)p(x)p(x)有两个根的问题就变为了证明p(x)=t(x)h(x)p(x)=t(x)h(x)p(x)=t(x)h(x)的问题,其中t(x)t(x)t(x)是公开给Verifier,p(x)p(x)p(x)和h(x)h(x)h(x)保密。

证明过程如下:
ProverVerifier←rrandomr,gett=t(r)h=h(r),p=p(r)→h,fCheck p=t⋅h\begin{aligned} &\quad\quad\quad\bold{Prover} &&\quad\quad\quad\bold{Verifier}\\ &&\xleftarrow{~~~~~~~~~~~~~~~~~~~~~~r~~~~~~~~~~~~~~~~~~~}&\quad random~r,get~t=t(r)\\ &h=h(r),p=p(r)&\xrightarrow{~~~~~~~~~~~~~~~~~~~~h,f~~~~~~~~~~~~~~~~~}& \quad \text{Check } p= t\cdot h\\ \end{aligned} ​Proverh=h(r),p=p(r)​                      r                   ​                    h,f                 ​​Verifierrandom r,get t=t(r)Check p=t⋅h​

例子,p(x)=x3−3x2+2x,t(x)=(x−1)(x−2),h(x)=xp(x)=x^3-3x^2+2x,t(x)=(x-1)(x-2),h(x)=xp(x)=x3−3x2+2x,t(x)=(x−1)(x−2),h(x)=x:
ProverVerifier←r=23r=23,gett=t(23)=462h=h(23)=23,p=p(23)=10626→h,fCheck 10626=462⋅23\begin{aligned} &\quad\quad\quad\bold{Prover} &&\quad\quad\quad\bold{Verifier}\\ &&\xleftarrow{~~~~~~~~~~~~~~~~~~r=23~~~~~~~~~~~~~~~}&\quad r=23,get~t=t(23)=462\\ &h=h(23)=23\\&,p=p(23)=10626&\xrightarrow{~~~~~~~~~~~~~~~~~~~~h,f~~~~~~~~~~~~~~~~~}& \quad \text{Check } 10626=462 \cdot 23\\ \end{aligned} ​Proverh=h(23)=23,p=p(23)=10626​                  r=23               ​                    h,f                 ​​Verifierr=23,get t=t(23)=462Check 10626=462⋅23​

考虑如果t(x)t(x)t(x)不是p(x)p(x)p(x)的根的情况,p(x)=2x3−3x2+2x,t(x)=(x−1)(x−2),h(x)=2x+3+7x−6t(x)p(x)=2x^3-3x^2+2x,t(x)=(x-1)(x-2),h(x)=2x+3+\frac{7x-6}{t(x)}p(x)=2x3−3x2+2x,t(x)=(x−1)(x−2),h(x)=2x+3+t(x)7x−6​:

ProverVerifier←r=23r=23,gett=t(23)=h=h(23)=49.33,p=p(23)=22793→h,fFailed since h is not Integer\begin{aligned} &\quad\quad\quad\bold{Prover} &&\quad\quad\quad\bold{Verifier}\\ &&\xleftarrow{~~~~~~~~~~~~~~~~~~r=23~~~~~~~~~~~~~~~}&\quad r=23,get~t=t(23)=\\ &h=h(23)=49.33\\&,p=p(23)=22793&\xrightarrow{~~~~~~~~~~~~~~~~~~~~h,f~~~~~~~~~~~~~~~~~}& \quad \text{Failed since h is not Integer}\\ \end{aligned} ​Proverh=h(23)=49.33,p=p(23)=22793​                  r=23               ​                    h,f                 ​​Verifierr=23,get t=t(23)=Failed since h is not Integer​

注意:当上述的方案限制很大,首先多项式得是整数系数的,要不然无法通过是否是小数来判断,其次Prover也必须是按照协议执行,否则:

  1. Prover直接用rrr算出t=t(r)t=t(r)t=t(r),并根据ttt来找两个数p′=t⋅h′p'=t \cdot h'p′=t⋅h′,而不是通过多项式计算出p=p(r),h=h(r)p=p(r),h=h(r)p=p(r),h=h(r)。
  2. 这样的方案对于p(x)p(x)p(x)的次数没有限制,如果Prover直接取一个高次的多项式,那么t(x)t(x)t(x)是这个高次多项式的因子的概率就很高。

通过混淆的计算来优化上述多项式证明

上面的问题1的来源是Prover知道rrr和t(r)t(r)t(r)的值,所以可以根据他们来构造p,hp,hp,h,那如果有一个方法使得Prover只能把rrr当做黑盒来使用,没办法得知rrr的值就能解决问题1了。

使用具有同态性质的加密方法

在这里使用一个简化的同态加密,即E(v)=gvmodpE(v)=g^v \bmod pE(v)=gvmodp,其中ggg是素数ppp的一个生成元,vvv是要加密的数。
那这个加密满足:
E(v1)⋅E(v2)=gv1⋅gv2=gv1+v2modp=E(v1+v2)E(v)2=(gv)2=g2vmodp=E(2v)E(v_1)\cdot E(v_2)=g^{v_1} \cdot g^{v_2} =g^{v_1+v_2} \bmod p=E(v_1+v_2)\\ E(v)^2=(g^v)^2=g^{2v}\bmod p=E(2v) E(v1​)⋅E(v2​)=gv1​⋅gv2​=gv1​+v2​modp=E(v1​+v2​)E(v)2=(gv)2=g2vmodp=E(2v)
可以看到这样的加密可以进行密文之间同态的加法,以及密文和明文的乘法。

可能有人注意到了,这个加密方案是没办法解密的

计算一个加密的多项式值

那么如果Verifier使用加密了E(r)E(r)E(r)然后发送给Prover,Prover要如何计算E(p(r))E(p(r))E(p(r))呢?

假设p(x)=x3−3x2+2xp(x)=x^3-3x^2+2xp(x)=x3−3x2+2x是一个三次的多项式,那么Verifier就发送E(r3),E(r2),E(r)E(r^3),E(r^2),E(r)E(r3),E(r2),E(r)给Prover,然后Prover计算:
E(r3)1⋅E(x2)−3⋅E(x)2=(gr3)1⋅(gr2)−3⋅(gr)2=gr3⋅g−3r2⋅g2r=gr3−3r2+2r=E(p(r))E(r^3)^1\cdot E(x^2)^{-3}\cdot E(x)^2\\ =(g^{r^3})^1 \cdot (g^{r^2})^{-3}\cdot (g^r)^2\\ =g^{r^3}\cdot g^{-3r^2}\cdot g^{2r}\\ =g^{r^3-3r^2+2r}=E(p(r))\\ E(r3)1⋅E(x2)−3⋅E(x)2=(gr3)1⋅(gr2)−3⋅(gr)2=gr3⋅g−3r2⋅g2r=gr3−3r2+2r=E(p(r))

那么通过混淆方法构造的证明过程如下:
注:因为之前rrr是随机数(random),而现在是一个秘密值(secret),所以这里用sss符号来代替rrr。

Prover要向Verifier证明p(x)p(x)p(x)是一个ddd次多项式,且p(x)=t(x)⋅h(x)p(x)=t(x)\cdot h(x)p(x)=t(x)⋅h(x):

令p(x)=c0+c1x+c2x2+...+cdxdp(x)=c_0+c_1x+c_2x^2+...+c_dx^dp(x)=c0​+c1​x+c2​x2+...+cd​xd
ProverVerifierrandoms,gett=t(s)←{E(s0)...E(sd)}E(si)=gsi,i∈[d]Get E(p(s)),E(h(s))→E(p),E(h)Check E(p)=E(h)t\begin{aligned} &\quad\quad\quad\bold{Prover} &&\quad\quad\quad\bold{Verifier}\\ &&&\quad random~s,get~t=t(s)\\ &&\xleftarrow{~~~~~~~~\{E(s^0)...E(s^d)\}~~~~~~}&\quad E(s^i)=g^{s^i},i \in [d]\\ &\text{Get } E(p(s)),E(h(s)) &\xrightarrow{~~~~~~~~~~~~~E(p),E(h)~~~~~~~~~~~}& \quad \text{Check } E(p)=E(h)^{t}\\ \end{aligned} ​ProverGet E(p(s)),E(h(s))​        {E(s0)...E(sd)}      ​             E(p),E(h)           ​​Verifierrandom s,get t=t(s)E(si)=gsi,i∈[d]Check E(p)=E(h)t​

Verifier最后的验证就相当于是在指数上做了p=t⋅hp=t\cdot hp=t⋅h的验证,因为:
E(p)=E(h)tgp=(gh)tgp=gth\begin{aligned} E(p)&=E(h)^t\\ g^p &= (g^{h})^t\\ g^p &=g^{th} \end{aligned} E(p)gpgp​=E(h)t=(gh)t=gth​

虽然这里Prover并不能知道sss的值,所以没办法根据sss来构造结果。但还是没有对于Prover进行约束,也就是说,Prover仍然可以用其他方式来给出zp,zhz_p,z_hzp​,zh​使得zp=(zh)tz_p = (z^h)^tzp​=(zh)t,而不是使用E(si)E(s^i)E(si)来算出zp=E(p(s)),zh=E(h(s))z_p=E(p(s)),z_h=E(h(s))zp​=E(p(s)),zh​=E(h(s))。

对多项式的运算进行承诺

所以需要Prover能给出一个证明,就是他给出的zpz_pzp​和zhz_hzh​确实是用E(si)E(s^i)E(si)算出来的,而不是通过别的手段伪造出来的。这里就用到了一种类似于同态消息认证码的方法:

比如Alice和Bob进行交互,Alice拥有数据aaa,发送aaa给Bob,Bob返回b=acb=a^cb=ac,Alice希望Bob返回的数据是aaa的某个幂次,而不是别的东西,就可以通过加上一个承诺α\alphaα来做,具体交互过程如下:

BobAliceα∈[1,n]←a,a′a′=aαmodnb=acmodn,b′=(a′)cmodn→b,b′Check b′=bαmodn\begin{aligned} &\quad\quad\quad\bold{Bob} &&\quad\quad\quad\bold{Alice}\\ &&&\quad \alpha \in [1,n]\\ &&\xleftarrow{~~~~~~~~~~~~~~~~~a,a'~~~~~~~~~~~~~~~~~~~}&\quad a' = a^\alpha \bmod n\\ &b = a^c \bmod n,\\ &b' = (a')^c \bmod n&\xrightarrow{~~~~~~~~~~~~~~~~~~~b,b'~~~~~~~~~~~~~~~~~}& \quad \text{Check } b' = b^{\alpha} \bmod n\\ \end{aligned} ​Bobb=acmodn,b′=(a′)cmodn​                 a,a′                   ​                   b,b′                 ​​Aliceα∈[1,n]a′=aαmodnCheck b′=bαmodn​

根据离散对数难题,Bob就算得到了a,aαa,a^\alphaa,aα,也无法计算出α\alphaα,因此没办法随便给出一个bbb,然后计算b′=bαb' = b^\alphab′=bα这样来伪造。

那可以使用类似的方法来保证在多项式的证明中Prover是使用p(x),h(x)p(x),h(x)p(x),h(x)计算出结果,而不是使用其他的方法来返回zp,zhz_p,z_hzp​,zh​的值。

加上消息认证后的方法如下:

Prover要向Verifier证明p(x)p(x)p(x)是一个nnn次多项式,且p(x)=t(x)⋅h(x)p(x)=t(x)\cdot h(x)p(x)=t(x)⋅h(x):

令p(x)=c0+c1x+c2x2+...+cdxdp(x)=c_0+c_1x+c_2x^2+...+c_dx^dp(x)=c0​+c1​x+c2​x2+...+cd​xd

ProverVerifierrandoms,gett=t(s)←{E(s0)...E(sd)}E(si)=gsi,i∈[d]←{E(αs0)...E(αsd)}E(αsi)=(gsi)α,i∈[d]E(p(s))=∏i∈[d]E(si)ci→E(p(s))E(p(αs))=∏i∈[d]E(αsi)ci→E(p(αs))Same for E(h(s))→E(h(s))Check E(p(s))=E(h(s))tE(p(αs))=E(p(s))α\begin{aligned} &\quad\quad\quad\bold{Prover} &&\quad\quad\quad\bold{Verifier}\\ &&&\quad random~s,get~t=t(s)\\ &&\xleftarrow{~~~~~~~~\{E(s^0)...E(s^d)\}~~~~~~}&\quad E(s^i)=g^{s^i},i \in [d]\\ &&\xleftarrow{~~~~~\{E(\alpha s^0)...E(\alpha s^d)\}~~~~}&\quad E(\alpha s^i)=(g^{ s^i})^{\alpha},i \in [d]\\ &E(p(s))=\prod_{i \in [d]} E(s^i)^{c_i} &\xrightarrow{~~~~~~~~~~~~~~~~E(p(s))~~~~~~~~~~~~~}& \quad \\ &E(p(\alpha s))=\prod_{i \in [d]} E(\alpha s^i)^{c_i} &\xrightarrow{~~~~~~~~~~~~~~~E(p(\alpha s))~~~~~~~~~~~~}& \quad \\ &\text{Same for }E(h(s)) &\xrightarrow{~~~~~~~~~~~~~~~~E(h(s))~~~~~~~~~~~~~~}& \quad \text{Check }E(p(s))= E(h(s))^t\\ &&& \quad E(p(\alpha s)) = E(p(s))^\alpha \end{aligned} ​ProverE(p(s))=i∈[d]∏​E(si)ci​E(p(αs))=i∈[d]∏​E(αsi)ci​Same for E(h(s))​        {E(s0)...E(sd)}      ​     {E(αs0)...E(αsd)}    ​                E(p(s))             ​               E(p(αs))            ​                E(h(s))              ​​Verifierrandom s,get t=t(s)E(si)=gsi,i∈[d]E(αsi)=(gsi)α,i∈[d]Check E(p(s))=E(h(s))tE(p(αs))=E(p(s))α​
这样下来就能保证Prover按照协议执行,而不是用其他方式取两个值返回。但零知识性质无法保证,Verifier可以通过穷举的方式来试验出p(x)p(x)p(x)的系数,因为系数的取值范围往往比较小,且最高次数已经确定,因此这样的攻击是可能的。具体来说,Verifier选择p′(x)p'(x)p′(x),计算p′(s)=p′p'(s)=p'p′(s)=p′,看一下gp′g^{p'}gp′与Prover发过来的gpg^{p}gp是否相等,如果相等的话,就可能是找到了p’(x)=p(x)。

如何做到零知识性

其实和之前的做承诺的方法类似,使用承诺的方法里面Verifier发送了gsig^{s^i}gsi以及gαsig^{\alpha s^i}gαsi,通过离散对数难题的假设来保证Prover无法得到α\alphaα。类似的,Prover在最后返回的时候,可以不选择返回E(p(s))=gp,E(p(αs))=gαpE(p(s))=g^{p},E(p(\alpha s))=g^{\alpha p}E(p(s))=gp,E(p(αs))=gαp,而是在外面再套一个δ\deltaδ,返回gδp,gδαp,gδh,gδαhg^{\delta p},g^{\delta \alpha p},g^{\delta h},g^{\delta \alpha h}gδp,gδαp,gδh,gδαh。由于返回的是gδpg^{\delta p}gδp,Verifier通过穷举只能得到p′p'p′使得gp′=gδpg^{p'}=g^{\delta p}gp′=gδp。而根据离散对数难题,δ\deltaδ是Verifier不可知的。

那协议就变为了:

Prover要向Verifier证明p(x)p(x)p(x)是一个nnn次多项式,且p(x)=t(x)⋅h(x)p(x)=t(x)\cdot h(x)p(x)=t(x)⋅h(x):

令p(x)=c0+c1x+c2x2+...+cdxdp(x)=c_0+c_1x+c_2x^2+...+c_dx^dp(x)=c0​+c1​x+c2​x2+...+cd​xd

ProverVerifierrandoms,gett=t(s)←{E(s0)...E(sd)}E(si)=gsi,i∈[d]←{E(αs0)...E(αsd)}E(αsi)=(gsi)α,i∈[d]E(p(s))=∏i∈[d]E(si)ci=gpgδp=(gp)δ→gδpE(p(αs))=∏i∈[d]E(αsi)cigδαp=E(p(αs))δ→⋅gδαpSame for h→gδhCheck gδp=(gδh)tgδαp=(gδp)α\begin{aligned} &\quad\quad\quad\bold{Prover} &&\quad\quad\quad\bold{Verifier}\\ &&&\quad random~s,get~t=t(s)\\ &&\xleftarrow{~~~~~~~~\{E(s^0)...E(s^d)\}~~~~~~}&\quad E(s^i)=g^{s^i},i \in [d]\\ &&\xleftarrow{~~~~~\{E(\alpha s^0)...E(\alpha s^d)\}~~~~}&\quad E(\alpha s^i)=(g^{ s^i})^{\alpha},i \in [d]\\ &E(p(s))=\prod_{i \in [d]} E(s^i)^{c_i}=g^{p}\\&g^{\delta p} = (g^{p})^\delta &\xrightarrow{~~~~~~~~~~~~~~~~~~~~g^{\delta p}~~~~~~~~~~~~~~~~~}& \quad \\ &E(p(\alpha s))=\prod_{i \in [d]} E(\alpha s^i)^{c_i}\\ &g^{\delta \alpha p}=E(p(\alpha s))^\delta &\xrightarrow{~~~~~~~~~~~~~~·~~~~~g^{\delta \alpha p}~~~~~~~~~~~~~~~}& \quad \\ &\text{Same for } h &\xrightarrow{~~~~~~~~~~~~~~~~~~~~g^{\delta h}~~~~~~~~~~~~~~~~~}& \quad \text{Check } g^{\delta p}= (g^{\delta h})^t\\ &&& \quad g^{\delta \alpha p}= (g^{\delta p})^{\alpha}\\ \end{aligned} ​ProverE(p(s))=i∈[d]∏​E(si)ci​=gpgδp=(gp)δE(p(αs))=i∈[d]∏​E(αsi)ci​gδαp=E(p(αs))δSame for h​        {E(s0)...E(sd)}      ​     {E(αs0)...E(αsd)}    ​                    gδp                 ​              ⋅     gδαp               ​                    gδh                 ​​Verifierrandom s,get t=t(s)E(si)=gsi,i∈[d]E(αsi)=(gsi)α,i∈[d]Check gδp=(gδh)tgδαp=(gδp)α​

这样构造的方案满足零知识性质,而且相比之前的方法,基本上没有额外的开销,所以这种构造方法叫做"free" zero-knowledge

如何构造一个非交互的方案

上面的方案是一个交互式的零知识证明方案,因为除了和Prover交互的Verifier,其他人看到他们之间的交互脚本,并不能相信Prover确实有用p(x)p(x)p(x)的知识。因为Prover和Verifier完全可以合谋,比如Verifer直接将α,s\alpha,sα,s告诉Prover。

所以要向其他人证明的时候,Prover和那个人之间要再跑一次上述的零知识证明协议,那效率就太差了。

而且呢,Verifier在协议执行过程中,要保留ttt和α\alphaα,最后用来做验证,那就可能会导致潜在的信息泄露。

建立 CRS

假如有一个可信的第三方,随机选取了α,s\alpha,sα,s,计算并公开了gα,gsi,gαsig^\alpha,g^{s^i},g^{\alpha s^i}gα,gsi,gαsi,然后删除α,s\alpha,sα,s,那么这些{gα,gsi,gαsi}i∈[d]\{g^{\alpha},g^{s^i},g^{\alpha s^i}\}_{i\in [d]}{gα,gsi,gαsi}i∈[d]​就是CRS。

双线性配对

这里要引入基于椭圆曲线的双线性配对。ggg是椭圆曲线的一个生成元,gag^aga表示aaa个ggg相加。双线性配对指一个映射,e(g∗,g∗)e(g^*,g^*)e(g∗,g∗),满足e(ga,gb)=e(g,g)abe(g^a,g^b)=e(g,g)^{ab}e(ga,gb)=e(g,g)ab。

注意到使用双线性配对的一个优势:在原来Verifer要存储α\alphaα,最后验证的时候计算gαp=(gp)αg^{\alpha p}=(g^{p})^{\alpha}gαp=(gp)α。这会带来严重的信息泄露问题以及不信任的问题。而使用椭圆曲线之后,就不需要存储α\alphaα,只需要存gαg^{\alpha}gα,最后的验证变成了e(gp,gα)=e(gpα,g)e(g^{p},g^{\alpha})=e(g^{p\alpha},g)e(gp,gα)=e(gpα,g)。

通过双线性配对建立CRS

假设存在一个可信的参与方,让他挑选sss和α\alphaα,然后计算出(gα,gsi,gαsi)i∈[d](g^{\alpha},g^{s^i},g^{\alpha s^i})_{i \in [d]}(gα,gsi,gαsi)i∈[d]​之后把sss和α\alphaα删除。这个可信的参与方就广播他计算出的(gα,gsi,gαsi)i∈[d](g^{\alpha},g^{s^i},g^{\alpha s^i})_{i \in [d]}(gα,gsi,gαsi)i∈[d]​。

在优化后的证明的情况下,会把gt(s)g^{t(s)}gt(s)也作为CRS的一部分存起来,可以在验证的时候少计算一些东西。所以现在的CRS是可以分为两部分:

  1. Proving Key: (gsi,gαsi)i∈[d](g^{s^i},g^{\alpha s^i})_{i \in [d]}(gsi,gαsi)i∈[d]​
  2. Verification Key: (gt(s),gα)(g^{t(s)},g^{\alpha})(gt(s),gα)

通过CRS构造一个非交互的协议

这里的非交互的协议并不是指Prover和Verifier没有互动,而是指Prover生成一次证明之后,可以广播这个证明,那所有的Verfier都可以相信这个证明。交互式的协议指的是,每有一个Verifer,Prover都需要重新执行一遍协议来使这个Verifier相信这个证明。

我们通过CRS构造出来的零知识证明协议如下:

  • 初始化阶段:

    • 一个可信第三方生成一些CRS,其中包含了:
    • Proving Key: {gsi,gαsi}i∈[d]\{g^{s^i},g^{\alpha s^i}\}_{i \in [d]}{gsi,gαsi}i∈[d]​
    • Verification Key: (gt(s),gα)(g^{t(s)},g^{\alpha})(gt(s),gα)
  • 证明阶段:
    • Prover使用{gsi}i∈[d]\{g^{s^i}\}_{i \in [d]}{gsi}i∈[d]​来计算出gp(s),gh(s)g^{p(s)},g^{h(s)}gp(s),gh(s).
    • 通过{gαsi}i∈[d]\{g^{\alpha s^i}\}_{i \in [d]}{gαsi}i∈[d]​来计算出gαp(s)g^{\alpha p(s)}gαp(s).
    • 随机选取δ\deltaδ,并广播π=(gδp(s),gδh(s),gδαp(s))\pi = (g^{\delta p(s)},g^{\delta h(s)},g^{\delta \alpha p(s)})π=(gδp(s),gδh(s),gδαp(s)).
  • 验证阶段
    • Verifier记π=(gp,gh,gp′)\pi = (g^p,g^h,g^{p'})π=(gp,gh,gp′).
    • 验证e(gp′,g)=e(gp,gα)e(g^{p'},g)=e(g^p,g^{\alpha})e(gp′,g)=e(gp,gα).
    • 验证e(gp,g)=e(gt(s),gh)e(g^p,g)=e(g^{t(s)},g^h)e(gp,g)=e(gt(s),gh).

到次为止我们已经构造出了一个对于多项式的非交互式零知识证明方案,但还需要补充一些细节。下面的章节可以略过,可以直接看通用的零知识证明部分。

多方共同生成CRS

待补充…

通用的零知识证明

待补充…

GGPR13

待补充…

Groth16

待补充…

SNARK超详细解释,从GGPR13到Groth16相关推荐

  1. 关于三次握手与四次挥手的超详细解释

    关于三次握手与四次挥手的超详细解释 看了很多博客的文章,再结合自己想要知道的知识,发现还是需要东看一下西看一下,才能明白大概的过程.所以这里博主决定来小小的结合一下自己记录的内容.来写一个容易看明白并 ...

  2. C++引用的超详细解释及误区纠正,不看后悔哦(中)

    书接上文,这次我想向大家谈一谈关于函数引用返回的问题 非引用返回,非引用接收: 首先我们来看这样一段代码: #include<iostream> using namespace std; ...

  3. C++引用的超详细解释及误区纠正,不看后悔哦(上)

    首先,我们来谈一谈什么是引用以及为什么存在? 引用:即为对象起立另一个名字,引用类型引用另一种类型. 基于C语言的指针,C++将其封装并创建出新的类型--引用.熟悉C语言的小伙伴们会发现,在C语言中指 ...

  4. 超详细解释XGBoost,一篇文章搞懂XGBoost

    XGBoost 文章目录 XGBoost 背景 工程原理 具体形式 怎么做出预测 目标函数 引言 数学详解 明确符号 化简目标函数 符号注释 结论 生成一棵完整的树 贪心算法 加权分位法 工作原理 数 ...

  5. Linux用户(user)与用户组(group)管理(超详细解释)

    Linux用户(user)和用户组(group)管理 用户/组基本概念与特性 1.查看用户的uid/gid 2.用户基本信息文件 (1)用户名 (2)密码 (3)UID (4)GID (5)主目录 ( ...

  6. 无迹卡尔曼滤波(UKF)超详细解释

    前言:本文运用了大量python语言,读不懂没关系,重点在于理解UKF的思想,如何利用概率分布逼近非线性(通过仿真例子理解).通过对比KF与UKF更容易理解. 引用自: https://github. ...

  7. Python爬虫案例,腾讯动漫爬虫,步骤超详细解释。

    实施步骤 (1) 数据爬取:使用requests请求为基础获取数据源. (2) UA伪装:模拟浏览器访问网址. (3) 数据解析:使用xpath语法处理数据. (4) 数据存储:获取需求数据后使用Ex ...

  8. leetcode 403.青蛙过河 C,C++超详细解释

    一只青蛙想要过河. 假定河流被等分为若干个单元格,并且在每一个单元格内都有可能放有一块石子(也有可能没有). 青蛙可以跳上石子,但是不可以跳入水中. 给你石子的位置列表 stones(用单元格序号升序 ...

  9. C++引用的超详细解释及误区纠正,不看后悔哦(下)

    最后一个板块,我们来详细的探讨下const引用,基本格式为-----const 类型& 在我们编写函数时,经常会发现常将const引用作为形参,这不仅仅是因为想让const修饰的值不在函数体内 ...

最新文章

  1. 程序员网购18年不拆快递!意外离世后满屋快递被拆开,价值3500万!
  2. 实现多种方式对MYSQL进行备份
  3. redisson 看门狗_Redisson的分布式锁
  4. spring mysql整合_springboot mybatis mysql 整合
  5. CentOS 7.4 ifconfig, ip/ss, nmcli, nmtui, 配置文件 修改ip信息用法
  6. 算法之路——插入排序篇3:希尔排序
  7. html 空链接 href=#与href=javascript:void(0)的区别
  8. java并发:初探用户线程和守护线程
  9. CTS(14)---Android O CTS 测试之Media相关测试小结(二)
  10. c性能大容量cket_5千左右预算,既轻薄(高颜值)又高性能的笔记本推荐(畅玩LOL、CF、DNF、流放之路、梦幻西游)...
  11. 开课吧:数据分析能够给企业带来什么价值?
  12. 【基础】深入浅出神经网络基础
  13. zabbix 安装和基础监控
  14. 实验板FPGA型号在哪里看_【VE】一文看懂乙烯基树脂发展史!
  15. 如何查询硬盘序列号?百度基本都是错的,其实一条命令搞定!
  16. 利用java程序实现文件加密
  17. 支付宝接口对接指南(二、对接框架基础搭建 springboot+支付宝SDK)【保姆级】
  18. java word 复制_Java 复制Word文档
  19. 每天吃多少才不会胖?食物和卡路里对照表
  20. 蓝桥杯--并查集1 sdutoj 小雷的冰茶几

热门文章

  1. 阿里巴巴国际站产品标题的组合和优化方法一览
  2. 畅想计算机的未来50字,新学期畅想50字
  3. OpenStack Victoria搭建(七)安装并验证 Placement
  4. 炼石喜获“2020中国网络安全产业联盟突出贡献单位奖”
  5. 个人电子邮箱格式,邮箱如何绑定微信?个人邮箱签名设置
  6. math ceil函数python_Python ceil函数
  7. android安装nodejs6,使用安卓手机搭建node-red
  8. java设计模式 之适配器模式
  9. win10桌面右下角网络图标中找不到网络
  10. Mac电脑-mysql密码忘了怎么处理