三件事:全对、抓得住、什么也没学到
「证明」这个词在这里不是数学课上的意思。它是一套协议——两个人来回说几句话,说完其中一个人做出「信」或「不信」的决定。要让这套协议有用,它必须同时满足三条互相独立的性质。这一章的价值在于:三条各自被打破时,坏掉的样子完全不同,而分清这三种坏法,是后面二十一章所有讨论的前提。
先把词定下来
这本书从头到尾只有两个角色和三样东西。术语在这里一次说清,后面不再解释:
| 词 | 意思 | 这本书里的颜色 |
|---|---|---|
| 证明者(prover) | 手里有秘密、想说服对方的那个人。传统上叫 Peggy | — |
| 验证者(verifier) | 要么信要么不信的那个人。传统上叫 Victor | — |
| 陈述(statement) | 要证的那句话。双方都看得见,比如「这道数独有解」 | 墨蓝 |
| 见证(witness) | 能让那句话成立的具体证据,比如那份解。只有证明者有 | 紫 |
| 挑战(challenge) | 验证者临时抛出的随机数。可靠性的唯一来源 | 琥珀 |
全书的配色就是这张表:公开的陈述是墨蓝,秘密的见证是紫(而且一定带斜纹遮罩 ⟦…⟧,表示你看不见里面),随机挑战是琥珀,被抓住的作弊是朱红,验过的东西是松绿。这五种颜色在整本书里含义不变,一次都不换。
三条性质
完备性 completeness
如果陈述是真的,而且证明者是诚实的,
那么验证者会接受。
——「诚实的人不会被冤枉。」
可靠性 soundness
如果陈述是假的,那么无论证明者怎么耍花招,
验证者接受的概率都小到可以忽略。
——「骗子过不去。」
零知识 zero-knowledge
验证者从整个过程里,除了「陈述是真的」之外,
学不到任何东西。
——「学费为零。」
三句话都很像废话。它们的价值全在于:把其中任意一条拿掉,剩下两条依然可以被完美满足,而拿掉的那一条对应一种非常具体的失败模式。
| 缺的那条 | 一个满足另外两条的极端例子 | 它坏在哪 |
|---|---|---|
| 可靠性 | 一台永远回答「通过」的验证器 | 完备性 100%(诚实的人永远能过),零知识 100%(它连看都不看,当然学不到东西)。它一点用都没有。 |
| 完备性 | 一台永远回答「拒绝」的验证器 | 可靠性 100%(骗子一个都过不去),零知识 100%。它同样一点用都没有,而且这种坏最容易被误当成「安全」 |
| 零知识 | 「把数独的解直接给他,他自己检查」 | 完备、可靠,而且非常实用——世界上绝大多数验证就是这么干的。它只是泄露了见证 |
第三行值得停一下。缺零知识的系统是完全可用的,这跟另外两行的性质截然不同。这就是第 1 章那句话的技术版本:三条性质里,零知识是唯一一条拿掉之后系统还能干活的。第 18 章那个上千亿美元的应用,就把它拿掉了。
山洞那个故事,和它没讲的那部分
几乎所有零知识的科普都从这里开始,所以我们也讲一遍——但重点在最后那段。
一个环形山洞,进门后分成左右两条通道,尽头被一扇门隔开。门上有锁,知道咒语的人能打开它,从一边走到另一边。Peggy 说她知道咒语,但不肯说。
第一步 Peggy 走进洞,随机选左或右,走到门前。Victor 在洞口,看不见她进了哪边。 第二步 Victor 走到岔口,随机喊:「从左边出来!」或「从右边出来!」 第三步 Peggy 从被指定的那边走出来。 真的知道咒语:无论她当初在哪边,都能开门穿过去 → 100% 成功。 不知道咒语: 只能赌自己当初正好站对了边 → 每轮 50%。
一轮说明不了什么。但这件事可以重复:
| 轮数 | 骗子全部蒙对的概率 | 相当于 |
|---|---|---|
| 1 | 5.0000 × 10⁻¹ | 抛一次硬币 |
| 10 | 9.7656 × 10⁻⁴ | 1/1024 |
| 20 | 9.5367 × 10⁻⁷ | 1/1048576,大约是被雷劈的年概率 |
| 40 | 9.0949 × 10⁻¹³ | 1/1099511627776 |
这个故事把三条性质都演示到了:诚实的 Peggy 永远能出来(完备),骗子过 40 轮的概率是万亿分之一(可靠),而 Victor 全程只看到「她从我指定的那边走出来了」——这件事他自己找个演员也能演出来(零知识,第 7 章会把这句话变成一个精确的等式)。
山洞演示的是「怎么在不泄露的前提下说服人」。它完全没有涉及那件更值钱的事:「怎么让验证比重做便宜」。
因为山洞里要证的那句话——「我知道咒语」——本来就没什么可重做的。这里没有一百万步计算需要被压缩,没有一张巨大的表需要被抽查。山洞是一个零知识的故事,不是一个简洁性的故事。
而且它还很贵:为了把作弊概率压到万亿分之一,两个人要在洞口来回四十次。第 5 章会给出一个一轮就买到 256 比特可靠性的做法,第 8 章会把这个来回次数变成 0。
proof 还是 argument:一字之差值多少钱
可靠性有两个强度不同的版本,中文常常都译成「可靠」,但它们差着一个世界:
| 证明 proof | 论证 argument | |
|---|---|---|
| 骗子过关的前提 | 无论他有多少算力,都过不去 | 只要他的算力是有限的(多项式时间),就过不去 |
| 安全性来自 | 纯粹的组合/概率事实 | 某个计算困难假设(离散对数难解、哈希抗碰撞……) |
| 山洞故事 | ✓ 是 proof | — |
| 今天所有的 zk-SNARK | — | ✓ 全都是 argument |
为什么工业界全都退而求其次?因为有一条不可能定理挡在那里:一个「无条件可靠」的证明,它的长度不可能比陈述本身短太多。要拿到「证明比计算短几个数量级」这件事,必须付出「假设骗子算力有限」的代价。
这笔交易是整个领域的地基。它意味着:今天每一个 zk-rollup 的安全性,最终都建立在「没有人能在合理时间内打破某个哈希函数或某条椭圆曲线」上面。第 15、16 章会把这些假设一条条摆出来,第 23 章会告诉你哪几条今天还在被人怀疑。
把三条性质变成可以跑的代码,比任何定义都清楚。下面四台「验证器」,每台都缺一条:
import random
def cave_round(knows_spell):
"""Peggy 先进洞选一边,Victor 再喊一边。"""
peggy_side = random.choice(['L', 'R'])
victor_asks = random.choice(['L', 'R'])
return True if knows_spell else (peggy_side == victor_asks)
def honest_verifier(knows, rounds=20):
return all(cave_round(knows) for _ in range(rounds))
always_yes = lambda knows, rounds=20: True # 缺可靠性
always_no = lambda knows, rounds=20: False # 缺完备性
for name, v in [('诚实验证器', honest_verifier),
('永远说通过', always_yes),
('永远说拒绝', always_no)]:
good = sum(v(True) for _ in range(1000)) # 诚实的 Peggy 1000 次
bad = sum(v(False) for _ in range(1000)) # 骗子 1000 次
print('%s: 诚实者通过 %4d/1000, 骗子通过 %4d/1000' % (name, good, bad))
跑出来大致是这样(骗子那一列偶尔会有 0 以外的数,因为 20 轮的漏网概率是百万分之一):
诚实验证器: 诚实者通过 1000/1000, 骗子通过 0/1000 ✓ 三条都满足 永远说通过: 诚实者通过 1000/1000, 骗子通过 1000/1000 ✗ 缺可靠性 永远说拒绝: 诚实者通过 0/1000, 骗子通过 0/1000 ✗ 缺完备性
把 rounds 从 20 改成 1,看诚实验证器那一行的「骗子通过」跳到多少(大约 500)。这就是「一轮买多少比特」这个概念的第一次出现。
python3 -c "import random;print(sum(all(random.choice('LR')==random.choice('LR') for _ in range(20)) for _ in range(10**6)))"
上面这行跑一百万个骗子过 20 轮,期望通过数约等于 1。在线跑:python.org/shell。
- 「永远说通过」不是段子,是最常见的安全漏洞形态。JWT 库里那个著名的
alg: none洞:攻击者把令牌的签名算法字段改成「无」,某些库就跳过验签直接放行。这就是一台完美满足完备性和零知识、可靠性为零的验证器。 - 「永远说拒绝」在生产环境同样致命,而且更难发现。它表现为「服务可用性事故」而不是「安全事故」,于是常常被归到运维头上。证书过期导致的全站宕机就属于这一类。
- 抽样审计。审计师抽查凭证时依赖的正是可靠性:造假者事先不知道会抽到哪几张。一旦这个随机性泄露(比如被审计方提前拿到抽样名单),整套体系立刻退化成「永远说通过」。随机数从哪里来,是这本书反复出现的死穴——第 9 章有一个因此彻底崩掉的真实事故。
- 论证 vs 证明的现实版本。你家门锁是「论证」:它挡得住普通小偷,挡不住带切割机的人。防盗门厂商标的「B 级锁芯,技术开启大于 270 分钟」,说的正是「对算力有限的攻击者安全」。密码学从来不做「谁都打不开」的锁,只做「打开它不划算」的锁。
「既然验证者学不到任何东西,那他怎么可能被说服?他一定至少学到了点什么吧。」
这是所有人第一次听到「零知识」时的反应,而且它指向一个真问题。答案是:他确实学到了一样东西——「这句话是真的」。零知识要求的是「除此之外什么也没学到」,不是「什么也没学到」。
更精确的说法在第 7 章:他看到的全部内容,他自己不用任何秘密就能伪造出来。既然他能自己造出一份一模一样的记录,那这份记录当然没给他增加任何知识。
但这里有一个非常容易被绕晕的地方:既然他自己能造,那为什么真的那一份能说服他?因为他自己造的时候是「先定答案再倒推问题」,而真交互里问题是他自己临时出的——顺序不同,说服力完全不同。第 4 章整章都在讲这个顺序。
这一章的一句话
一个证明系统是「诚实的人过得去、骗子过不去、验证者学不到」三条独立性质的合取;三条各自能被单独打破,而其中只有「零知识」这一条拿掉之后系统仍然能干活——这就是为什么它最出名,却最不重要。
下一章是这本书的第一个坏消息,也是它真正的起点。前两章我一直在说「随机抽查几格就够了」,下一章要告诉你:在原始的形式下,这句话是彻底错误的。一份 1024 格的记录,作弊者只改一格,你要抽到 99% 的把握,得抽 4714 格——比整份记录还长 4.6 倍。抽查不但没用,还比重做更贵。