翻译自:www.shirpeled.com/2018/09/a-h…
零知识证明
零知识是如下类型的故事:A陈述了一个声明,并向B证明该声明,在他们之间经过了一些信息交互后:
- B可以确信声明有99.99999%的概率是真的。
- B在该过程中没有学习到任何知识,除了声明是真的。
本节首先讲述 "零知识的知识论据(ZK arguments of knowledge)",这与零知识证明并不完全一样,但足够接近。概况来说:一个零知识证明的证据是可以被完全信任的,即使试图证明声明的人(通常被称为证明者)有着无限的算力。信任“零知识的知识论据”需要满足下面的假设:如果证明者确实想作假,这会是多项式界限的。
在零知识证明的世界中,交换信息的另一方被称为“验证者”。下面将沿用该术语。
分割问题
给定一个数字的序列 , 能否将该序列分割为两个子集合,使得两个子集合的和相同?
如果问题中的序列是,那答案明显是可以,因为有。
然而,如果序列是 ,那答案是否,因为和是一个奇数,不可能两个子集合的和正好是奇数的一半(这些数字都是整数)。
尽管这是些简单的实例,通常这个问题是NP完全的(即使它有个伪多项式时间的算法)。
开始证明
假设我们有个数字的python列表,定义了我们的分割问题实例。我们认为另一个列表是一个满足的分配,如果
-
-
中所有的元素都是或
-
和的点积是0。
这等价于分割问题的陈述。如果我们认为中的‘1’,是将它对应的中的数字放在等式左边,‘-1’是放在等式右边。
让我们重写与点积的部分和的列表。用数学表达为
因此,如果, 而且,那么 将是更长的元素。
注意到 现在有两个有趣的属性,如果确实是满足的分配:
(p的属性1)它以0开始和结尾
(p的属性2)对每个, 都有
所以有了第一个简单的零知识协议的草案:
验证者选择随机的, 如果 , 验证者要求证明者提供和并检查他们都为0.
否则,验证者要求证明者提供和并检查确实有(是验证者已知的,作为证明者发出的声明的一部分)。
证明者撒谎怎么办???
上面的示例包含了一个隐含的假设:验证者要求证明者提供数据时,证明者会诚实的提供。我们并不认为会这样、这个问题将在下一篇文章中讨论,现在我们假设所有参与者都是诚实的。
这没有证明任何事!
细心的读者会指出,询问一个单独的元素并不意味着什么。这是正确的,我们将进行多次查询,并在足够多的查询后,我们确信声明是正确的。我们将在第三篇文章进行更精确的量化。
这并不是零知识!
每一次查询都揭示了关于的信息,因此它并不是零知识的。因此,在足够多的查询之后,可以被完全揭示。
这是糟糕的,让我们修复这个问题
制造零知识
从数学的角度来讲,我们通常会说一些信息没有提供新知识,如果这些信息是随机的,更确切地说---是指信息是均匀地分布在合理选择的域上。在没有了解“提取(exact)”定义前,要想使某些信息变成零知识的,需要将这些信息和随机混合在一起。这是我们的做法:
- 并不需要把揭示给我们,我们需要掷一枚硬币。如果是正面,我们将不对作处理,如果是反面,我们将的所有元素乘以。注意到元素被初始化为和,而且与的点积被初始化为0,这并没有改变它与的点积。
- 我们选择一个随机数,将它与中所有的元素相加,这并没有影响到的第二个属性,但改变了第一个属性,现在的第一个元素和最后一个元素也许不是0。然而,他们必须与另一个保持一致。
现在假设在每次查询前,我们重新生成随机数(抛硬币并改变,选择随机数,并将其添加在元素中)。
如果我们仔细选择,那么中每两个连续元素将会不同,由于中对应的元素看起来更随机。
下面是我们所需要的代码的第一部分,这段代码输入问题(比如)和一个满足的分配(比如)并构建一个证据(比如),该证据可以证明问题实例的可满足性。
imort random
def get_witness(problem, assignment):
"""
Given an instance of a partition problem via a list of numbers (the problem) and a list of
(-1, 1), we say that the assignment satisfies the problem if their dot product is 0.
"""
sum = 0
mx = 0
side_obfuscator = 1 - 2 * random.randint(0, 1)
witness = [sum]
assert len(problem) == len(assignment)
for num, side in zip(problem, assignment):
assert side == 1 or side == -1
sum += side * num * side_obfuscator
witness += [sum]
mx = max(mx, num)
# make sure that it is a satisfying assignment
assert sum == 0
shift = random.randint(0, mx)
witness = [x + shift for x in witness]
return witness