在前一篇文章的例子里,验证者要知道整个多项式才能验证,我们说了,这不算是「零知识」。那如何让产生多项式的证明者(prover)在不让任何人知道多项式的情况下,还能让验证者(verifier)进行验证呢?这篇文章我们来解决这个问题。
术语解释:
prover: 证明者。prover 拥有原始数据,并通过使用多项式的方式,隐藏原始数据。 verifier: 验证者。对 prover 发起挑战,进行验证。prover 和 verifier 是在提及零知识证明时,常用的指代产生证明和进行验证的双方的术语,后面我们也会使用这样的称呼,不再使用「证明者」或「验证者」这样的名称。
首先我不得不说,在 verifier 完全什么也不知道的情况下,让 verifier 去验证、然后还能验证成功,我觉得是不可能的。顺着这个思路往下想,那就变成了:我们得让 verifier 知道点什么,让 ta 可以根据知道的这点东西进行验证,但知道的这点东西又不足以让他知道事情的全貌。 具体来说,就是我们得让 verifier 知道 prover 拥有的多项式的一部分,让 ta 可以对多项式进行验证;但 verifier 知道的这部分多项式,不足以让 ta 推导出整个多项式来。
1. 拆分多项式
如何能把多项式的一部分给 verifier ,又不足以让 verifier 推导出整个多项式呢?
1.1 加法拆分?
多项式嘛,无非就是 $f(x)=a_nx^n + a_{n-1}x^{n-1}+…+a_3x^3+a_2x^2+a_1x+a_0$ ,如果我们把 $a_2x^2+a_1x+a_0$ 给 verifier ,剩下的部分 prover 自己留着,不就可以了嘛。我们形式化整理一下:
令
那么 $f(x)$ 就变成了:
\[f(x)=h(x)+t(x)\]我们把 $t(x)$ 给 verifier ,在 $n$ 远大于 2 的情况下,只有 $t(x)$ verifier 肯定无法推导出整个多项式。那么验证过程就变成了:
- verifier 选择一个随机数 $s$ ,自己计算 $t(s)$ 后,将 $s$ 发给 prover。
- prover 拿到 $s$ 后,分别计算 $f(s)$ 和 $h(s)$,并将 $f(s)$ 和 $h(s)$ 发给 verifier。
- verfier 拿到 prover 返回的 $f(s)$ 和 $h(s)$ 后,验证 $f(s)=h(s)+t(s)$ 是否成立(注意这里的 $t(s)$ 是 verifier 自己算的)。如果成立,则验证成功。
就这?
1.2 不,乘法拆分
差不多吧,上面我们的思路是对的,加法($f(x)=h(x) + t(x)$)也是最简单、我们最熟悉,最容易想出来的解决方法。但真正的零知识证明,其实用的是乘法,即 $f(x)=h(x)t(x)$ ,所以验证过程应该是:
- verifier 选择一个随机数 $s$ ,自己计算 $t(s)$ 后,将 $s$ 发给 prover。
- prover 拿到 $s$ 后,分别计算 $t(s)$ 和 $h(s)$ ,并将 $f(s)$ 和 $h(s)$ 发给 verifier。
- verfier 拿到 prover 返回的 $f(s)$ 和 $h(s)$ 后,验证 $f(s)=h(s)t(s)$ 是否成立(注意这里的 $t(s)$ 是 verifier 自己算的)。如果成立,则验证成功。
为什么要用乘法呢?因为这个验证过程实在太简单,漏洞百出,真正的零知识证明肯定不会这么简单。使用乘法,更有利于后面使用各种手段,补上这此「漏洞」,而加法,让这些手段无法施展。
你可能会产生一个疑问:$f(x)$ 变成 $h(x)+t(x)$ 显然易见,非常直观;但 $f(x)$ 肯定能变成 $h(x)t(x)$ 的方式吗?
答案是肯定的,只要 prover 精心选择 $t(x)$ 。基础的线性代数理论告诉我们,如果 $a$ 是 $f(x)$ 的根,即 $f(a)=0$ ,那么 $f(x)$ 肯定能被 $(x-a)$ 整除;如果 $a$ 和 $b$ 都是 $f(x)$ 的根,那么 $f(x)$ 肯定能被 $(x-a)(x-b)$ 整除。
所以,我们只要找到 $f(x)$ 的几个根,比如两个,$a$ 和 $b$ ,令 $t(x)=(x-a)(x-b)$ ,那么 $f(x)$ 肯定能被 $t(x)$ 整除,得到 $h(x)$ 多项式。(当然你不能把 $f(x)$ 的所有根都放到 $t(x)$ 里,那样的话 $h(x)$ 就只能等于 1 了,并且 $t(x)$ 是要给 verifier 的,相当于把整个 $f(x)$ 直接暴露给了 verifier)
这样,prover 就可以在不暴露整个多项式的情况下,让 verifier 进行验证了。
2. 总结
这篇文章主要解决一个问题:如何在让 verifier 不知道具体多项式是什么的情况下,prover 还可以证明自己有这样一个多项式。方法就是把 $f(x)$ 折成两部分:$h(x)t(x)$ ,其中 :
- $t(x)=(x-a_1)(x-a_2)…(x-a_i)$ 。 $a_1,a_2,…,a_i$ 是 $f(x)$ 的 $i$ 个根,但不能是全部的根。假设 $f(x)$ 有 $n$ 个根,那么 $i$ 要远小于 $n$
- $h(x)=\frac{f(x)}{t(x)}$
到目前为止,除了「如何将一个问题转等价转换成多项式」这个问题,你已经基本掌握了零知识证明的精髓。
就这?你肯定会这么问。
「精髓」的意思是,它是事物的本质;但从另一个角度看,「精髓」需要依赖骨、肉等其它元素,才能组成一个完全的生命体。当你看到一个完整的生命体时,ta 呈现出来的肯定不是「精髓」的本来面目。
零知识证明的「精髓」就是 $f(x)=h(x)t(x)$ 。但直接使用它按着前面说的过程进行验证,依然是漏洞百出的。比如,当 prover 收到 verifier 的随机数 $s$ 时,由于 prover 知道 verifier 有 $t(x)$ ,也就当然知道 verifier 计算出来的 $t(s)$ 值,那么 prover 就可以随便调整 $f(x)$ 和 $h(x)$ 这两个多项式,比如调整为 $f’(x)$ 和 $h’(x)$ ,只要最终 $f’(s)=h’(s)t(s)$ 就可以了;把 $f’(s)$ 和 $h’(s)$ 发给 verifier ,依然可以通过验证(但实际上 prover 可能根本没有一个固定的多项式 $f(x)$ )。更甚至,prover 根本不需要多项式,ta 算出 $t(s)$ 的值后,随便选两个数 $m$ 和 $n$ ,只要满足 $m=nt(s)$ ,就可以把 $m$ 和 $n$ 发给 verifier ,依然能通过验证。
所以有了这个「精髓」还是不行的,后面要解决掉这些漏洞,让 prover 和 verifier 双方在完全不信任对方的情况下,依然能就某个问题达成一致(即 verifier 被说服。这就是零知识证明的用武之地,因为如果双方互相信任,也不用证明什么了,直接相信结果就得了)。可是如何做到呢?别着急,我们后面的文章会一一介绍。