CMU-15-356-密码学导论笔记-全-
CMU 15-356 密码学导论笔记(全)
001:古典密码与一次性密码本


在本节课中,我们将要学习密码学的基础概念,特别是加密这一基本原语。我们将从定义对称密钥加密系统开始,然后探讨几种历史上著名的古典密码,最后介绍一个理论上完美的加密方案——一次性密码本,并讨论其局限性。
对称密钥加密的定义
在深入讨论具体加密方案之前,我们首先需要明确对称密钥加密系统是什么。一个对称密钥加密方案由三个算法构成。

以下是构成一个对称密钥加密方案的三个算法:
- 密钥生成算法 (KeyGen):该算法输入一个安全参数,输出一个秘密密钥。安全参数决定了密钥的长度和系统的安全级别。
- 加密算法 (Enc):该算法输入一个秘密密钥和一个明文消息,输出一个密文。
- 解密算法 (Dec):该算法输入一个秘密密钥和一个密文,输出原始的明文消息。
关于加密方案,有两个重要的备注。首先,每个方案都会指定一个消息空间,明文消息必须是该空间中的元素。其次,方案必须满足正确性要求:对于任何由密钥生成算法产生的密钥 k 和任何消息空间中的消息 m,用 k 加密 m 得到密文 c 后,再用 k 解密 c 必须总是得到原始的 m。用概率公式表示即:
Pr[ Dec(k, Enc(k, m)) = m ] = 1

古典密码示例
上一节我们介绍了对称加密的基本框架,本节中我们来看看历史上几种具体的古典密码。
凯撒密码
凯撒密码是最古老的密码之一,由尤利乌斯·凯撒使用。其密钥是一个0到25之间的整数,代表字母的位移量。
以下是凯撒密码的工作方式:
- 加密:将明文中的每个字母,按照字母表顺序向后移动密钥所指定的位数(例如,密钥为3时,A变成D)。
- 解密:将密文中的每个字母,按照字母表顺序向前移动密钥所指定的位数。
例如,密钥为1,明文“ATTACK”会被加密为“BUUBDL”。然而,凯撒密码非常不安全,因为密钥空间只有26种可能,攻击者可以轻易地尝试所有可能的密钥(穷举攻击)。这种仅凭一个密文就能发起的攻击,称为唯密文攻击,是最弱的攻击模型,也意味着方案毫无安全性可言。
在现代密码学中,我们遵循一个核心原则:不能依赖算法的保密性来保证安全。安全应完全依赖于密钥的保密。这是因为算法可能通过逆向工程、内部泄露等方式被获知,而随机生成一个长密钥则相对简单且可控。
替换密码
替换密码可以看作是凯撒密码的推广。其密钥是26个英文字母的一个随机排列(即一张替换表)。



以下是替换密码的工作方式:
- 加密:根据密钥(替换表),将明文中的每个字母替换为表中对应的字母。
- 解密:根据密钥(替换表),进行反向查找,将密文中的每个字母替换回原始字母。
替换密码的密钥空间是26!(阶乘),远大于凯撒密码。然而,它仍然可以通过频率分析来攻击。在英语等自然语言中,字母的出现频率有固定模式(例如,E是最常见的字母)。通过分析密文中字母的频率分布,攻击者可以推测出部分甚至全部的替换规则。虽然这需要较多的密文样本和一些尝试,但替换密码在实践中仍被认为是不安全的。
维吉尼亚密码
维吉尼亚密码使用一个单词或短语作为密钥。加密时,将密钥重复至与明文等长,然后根据密钥每个字母对应的位移量(A=0, B=1, ...)来逐位加密明文。
以下是维吉尼亚密码的工作方式:
- 加密:明文每个字符的位移量由对应位置的密钥字符决定。
- 解密:反向进行相同的位移。
对维吉尼亚密码的一种有效攻击是先猜测密钥长度。假设猜中长度为 L,那么可以将密文分成 L 组,其中每一组内的字符都是用凯撒密码加密的(因为使用了相同的位移字母)。然后,对每一组分别进行频率分析,从而破解。如果密钥长度与消息长度相等,则每一组只有一个字符,频率分析失效。这种情况实际上引出了我们接下来要讨论的完美密码。


一次性密码本
前面我们看到了古典密码的各种弱点,现在让我们来看一个理论上完美的加密方案——一次性密码本。
一次性密码本定义如下:
- 密钥生成:生成一个与待加密明文等长的随机二进制串
K。 - 加密:密文
C是明文M与密钥K的逐比特异或运算结果。即C = M ⊕ K。 - 解密:明文
M是密文C与密钥K的逐比特异或运算结果。即M = C ⊕ K。
其正确性显而易见,因为异或操作满足 (M ⊕ K) ⊕ K = M。
一次性密码本满足完美安全性。完美安全性的定义是:对于消息空间中的任意两个消息 M1 和 M2,以及任意一个密文 C,用随机密钥加密 M1 得到 C 的概率,与加密 M2 得到 C 的概率完全相同。用公式表示即:
Pr[ Enc(K, M1) = C ] = Pr[ Enc(K, M2) = C ]
这意味着,仅观察密文 C,攻击者无法获得关于原始明文的任何信息(无论其计算能力有多强)。密文的分布与明文完全独立。
然而,一次性密码本有一个重大限制:密钥只能使用一次,且必须与消息等长。如果重复使用同一个密钥 K 加密两个不同的消息 M1 和 M2,得到 C1 = M1 ⊕ K 和 C2 = M2 ⊕ K,那么攻击者计算 C1 ⊕ C2 = (M1 ⊕ K) ⊕ (M2 ⊕ K) = M1 ⊕ M2。虽然无法直接得到 M1 或 M2,但获得了两者的异或值,这通常会泄露大量信息(例如,如果知道 M1,就能立刻得到 M2)。

香农定理与完美安全的局限性
一次性密码本的局限性不是偶然的。香农证明了一个重要的定理:任何实现完美安全的加密方案,其密钥空间的大小必须至少等于其消息空间的大小。
这意味着,如果你想安全地加密一个可能很长的消息,你需要一个至少同样长的密钥。这在实际中(如加密大文件或多次通信)是非常不便的。香农定理也解释了为什么所有古典密码(其密钥空间有限)都不可能是完美安全的。

香农定理的证明思路是反证法:假设存在一个密钥空间比消息空间小的完美安全加密方案。那么对于任何一个密文 C,用所有可能的密钥去解密它,只能得到消息空间的一个子集 S(因为密钥数量少)。因此,必然存在一些消息 M‘ 不在 S 中。这意味着密文 C 绝不可能由 M‘ 加密产生。但对于完美安全,任何消息产生 C 的概率应该相同,这就产生了矛盾。
这个定理带来的好消息是,攻击者要实施这种理论上的攻击,需要尝试所有可能的密钥。如果密钥足够长(例如128位),即使对于超级计算机,穷举所有 2^128 种可能性也是完全不可行的。这为我们设计计算安全而非完美安全的实用加密方案提供了可能,我们将在后续课程中探讨。
总结
本节课中我们一起学习了密码学的起点——加密。我们首先定义了对称密钥加密系统的三个核心算法(密钥生成、加密、解密)及其正确性要求。然后,我们分析了凯撒密码、替换密码和维吉尼亚密码等古典密码,并了解了它们如何被唯密文攻击和频率分析等方法破解。接着,我们介绍了一次性密码本,这是一个理论上具有完美安全性的方案,但其要求密钥与消息等长且仅能使用一次。最后,通过香农定理,我们理解了完美安全性在实践中的根本限制,即需要至少与消息等长的密钥,这引出了对计算安全性(而非完美安全性)的实用密码方案的需求。
002:单向函数

在本节课中,我们将要学习密码学的一个核心基础概念:单向函数。我们将从回顾上节课的结论开始,然后引入计算复杂性理论中的基本概念,最后正式定义单向函数并探讨其构造方法。
回顾与动机
上一节我们介绍了香农定理。香农定理告诉我们,如果考虑完美安全性,任何密钥长度小于消息长度的加密方案,或者任何重复使用密钥的加密方案,在某种意义上都是可被攻破的。其核心思想是尝试所有可能的密钥。
好消息是,如果我们把注意力限制在那些没有无限计算能力或无限时间的对手上,也许这种攻击对他们来说就不切实际了。这是一个向前推进的思路。我们可以称之为一个伟大的想法。这个想法是只考虑计算能力有限的对手。这些对手无法尝试所有可能的密钥,如果密钥数量非常多,例如2的100次方,他们无法遍历所有密钥。因此,我们仍然可以期望获得针对这类对手的安全性。
如果我们依赖对手是计算能力有限的这一假设,我们需要什么?我们需要一些对于这类计算能力有限的对手来说是困难的,但对于无限制的对手来说可能是容易的问题。因为香农定理告诉我们,对于无限制的对手,我们无法期望获得任何接近完美安全性的东西。事实上,香农定理在某种意义上也给了我们一个明确的攻击方法。
所以,我们现在希望寻找一些计算能力有限的对手无法解决的难题。我们可能已经想到了一些候选问题。例如,如果P不等于NP,我们肯定都见过像NP完全问题,甚至是其他问题,比如因数分解。这就是我们将要依赖的基础。
基本概念

为了能够论证这类问题以及基于这些问题的构造,让我先介绍一些符号和基本概念,例如什么是多项式时间。这些基本概念我们将在整个课程中使用。
多项式时间

第一个概念是多项式时间。我们用PT表示。我们说一个算法是多项式时间的,当且仅当存在某个多项式P,使得对于所有足够大的输入x,算法A在输入x上的运行时间受P(|x|)的限制。通常我们用n表示输入x的长度,并称n为输入规模。本质上,这意味着你观察任何算法A,其运行时间可能依赖于特定输入,但我们需要对于每个输入,该算法都在多项式时间内停止。我们也将这种算法称为高效算法。
概率多项式时间
接下来是概率多项式时间的概念,我们称之为PPT。这本质上与P相同,只是现在我们讨论的是算法的期望运行时间。算法可能是随机的,其运行时间可能依赖于它在任何给定运行中产生的随机数。我们需要运行时间是多项式的。同样,一个算法A被称为概率多项式时间,当且仅当存在某个多项式P(x),使得对于所有足够大的x,A(x)的期望运行时间受P(|x|)的限制。
例如,考虑以下实验:我不断抛硬币直到得到正面。在这种情况下,我们无法限制该算法在所有情况下的运行时间,因为我们可能一直抛硬币却永远得不到正面。另一方面,该算法的期望运行时间是概率多项式时间。所以,抛硬币直到得到正面是PPT,但不是PT。
在密码学中,我们通常考虑运行时间是概率多项式时间的算法或对手。到目前为止有任何问题吗?是的,好问题。每个多项式时间算法也是概率多项式时间算法。如果总运行时间受某个多项式限制,那么期望运行时间也受同一个多项式限制。
可忽略函数与显著函数
这些概念,多项式时间和概率多项式时间,通常用于讨论算法的运行时间。可忽略函数和显著函数,我们通常用它们来讨论概率、成功概率等。

什么是可忽略函数?直观上,这是一个非常小的函数,几乎是指数级小。一个函数μ(n)被称为可忽略的,当且仅当对于每个多项式p(n),对于所有足够大的n,μ(n) < 1/p(n)。我们通常用negl(n)表示。一个简单的例子是1/2n。因为你可以选择任何多项式,对于足够大的n,1/2n将小于1/p(n)。通常,我们希望对手的成功概率是可忽略的。
最后,让我介绍显著函数。这些函数可能很小,但不一定是指数级小。一个函数f(n)是显著的,当且仅当存在多项式p(n)和q(n),使得对于所有足够大的n,f(n) ≤ q(n) 且 f(n) ≥ 1/p(n)。我们将其表示为notice(n)。它们可能大到多项式,也可能小到1/p(n)。当然,如果我们在这里讨论概率,那么f(n)不能大于1,并且可以小到1/p(n)。如果我们只讨论概率,那么上限q(n)可能可以去掉,但我们只是试图保持定义的一般性。
需要记住的一些简单事实:
- 两个可忽略函数相乘,结果仍是可忽略的。
- 可忽略函数乘以显著函数,结果是可忽略的。
- 可忽略函数加上显著函数,结果是显著的。
- 可忽略函数加上可忽略函数,结果仍是可忽略的。
有任何问题吗?
单向函数
现在让我们进入本节课的重点:单向函数。我们为什么要研究这些对象?在某种意义上,单向函数是我们可以设计的最简单的密码学原语。在加密方案中,密文必须隐藏整个消息。你可以用不同的方式定义加密,但任何好的加密方案,即使它泄露了消息的一半,甚至只泄露了一位,我们都不会称其为好的加密方案。另一方面,在单向函数中,输出必须至少隐藏输入的某些部分。换句话说,输出或密文,你可以随意称呼它。我们唯一想要的是它不应该泄露整个输入或整个消息。如果它泄露了一半,那也没关系。所以在某种意义上,单向函数的要求比加密方案要弱得多,这就是为什么我们将从单向函数开始。
直观理解
粗略地说,什么是单向函数?这只是一种直觉,我们稍后会给出正式定义。在单向函数中,没有密钥,也没有解密过程。事实上,当我们谈论单向函数时,可能不会立即明白单向函数有什么用处,但我们将看到,一旦有了单向函数,你实际上可以设计加密方案,并且可以做更多事情。
一个单向函数从某个定义域获取输入,并在值域中给出输出。它应该具有两个属性:
- 易于计算:对于任何输入x,计算f(x)是容易的,可以在多项式时间内完成。
- 难以求逆:给定某个y = f(x),求逆y是困难的。一种思考方式是,如果y = f(x),x被称为y的一个原像。我们在这里说的是,给定y,找到y的任何原像是困难的。我们稍后将正式定义“求逆是困难的”意味着什么。
正式定义
现在让我们给出单向函数的正式定义。一个单向函数可能接受n位输入,并给出m位输出。这个函数被称为单向函数,如果它满足以下两个条件:
-
条件一:f必须是多项式时间的。注意,不是概率多项式时间,只是多项式时间。
-
条件二:对于每个概率多项式时间算法A(你也可以将这个算法视为对手),以下概率是可忽略函数μ(n):
Pr[ f(x) = f(x') : x ← {0,1}^n; x' ← A(f(x)) ] ≤ μ(n)

让我们尝试理解这个等式的含义。这个等式是说,首先你采样一个长度为n的随机字符串x,然后你在这个随机字符串上计算f(x),接着你将输出给对手A。现在这个对手A尝试猜测x是什么,假设x‘是它的猜测。现在我们关注x’是f(x)的有效原像的概率,即f(x) = f(x‘)的概率。
为了更好地理解,让我们考虑以下问题。如果我们只要求以下概率:Pr[ x = x' : x ← {0,1}^n; x' ← A(f(x)) ] 是可忽略的,其余保持不变。那么,这本质上要求对手给出完全相同的输入x。问题是,这是一个好的定义吗?有什么想法吗?这是好是坏?如果坏,为什么坏?
本质上,要求对手给出我们开始时的确切输入,这是一个更严格的限制。可能是存在两个输入映射到同一个输出,在这种情况下,对手无法有意义地区分。让我给你一个提示。如果f(x)总是等于0呢?那么,这个平凡函数实际上满足这个条件。因为输出根本没有关于输入的信息,对手如何猜测输入是什么?对手将无法做到。不幸的是,显然这个函数完全没有意义,我们不能指望用这个函数在密码学中做任何有趣的事情。另一方面,如果我们看这个定义,并看这个函数,那么显然这个函数不满足这个概率等式,因为任何对手都可以输出任何x,并且每个x都是有效的原像,因为每个x都给出相同的输出。这就是为什么我们将摆脱这个要求,并注意到f可能有很多碰撞。碰撞发生在两个不同的输入映射到同一个输出时。
我有个小问题。当我们计算这个概率时,对手知道函数f,对吗?对手知道函数,是的,正确。对手可以设计他的算法,知道函数是什么。是的,这是一个普遍的评论,在密码学中,稍后我们将讨论加密,并讨论各种算法,算法总是为对手所知的。只有你采样的随机输入或随机密钥,可能不为对手所知。顺便说一下,这个概率是在什么上取的?这个概率是在x的选择上取的,因为在整个实验中,我采样的唯一随机的东西是x。
回到注释,f可能有很多碰撞。要求A返回我们开始时的确切输入是没有意义的。顺便说一下,这个定义,正如我解释的,也被称为强单向函数。我们也可以定义一些被称为弱单向函数的东西。强单向函数说对手猜测输出的概率非常小,是可忽略的。弱单向函数要求这个概率小于等于1减去某个显著函数。所以我们本质上说的是,至少在某些显著比例的时间里,对手会失败,也许不是所有时间。所以概率,同样是相同的东西,小于等于1减去显著函数。我们本质上说的是,如果f以显著概率失败。在这里,我们要求A以非常接近1的概率失败。正如你可以想象的,弱单向函数在某种意义上比强单向函数更容易设计。
另一件需要注意的事情是,单向函数也可以为特殊定义域定义。你的单向函数将从定义域D中的输入映射到某个值域R,这里我们假设我们的定义域是均匀输入,但输入可能来自其他分布或其他定义域。在这种情况下,我们要求单向函数的安全性仅当输入是从这个定义域中采样时才成立。为了以更一般的方式书写,现在x是从D中均匀采样的,其他一切保持不变。这必须至多是可忽略的。
我们也可以定义更强的单向函数概念:单射或一一对应的单向函数。本质上,这里我们要求函数没有碰撞,这意味着在单射单向函数中,如果x不等于x‘,那么f(x)也必须不等于f(x’)。对于单射单向函数,事实上,这个概率实际上等于要求x和x‘相同。我们也可以定义单向置换。在单向置换中,我们要求它是一个一一对应的单向函数,且定义域和值域相同。
单向函数的性质与局限性

我们将尝试设计单向函数、弱单向函数、单射单向函数等等,它们有不同的应用,我们稍后会看到。但让我们首先尝试理解单向函数是什么,它们隐藏什么,不隐藏什么。
首先,因为没有密钥,所以没有解密过程。给定f(x),没有办法恢复任何原像,没有办法恢复x或x‘。所以,单向函数的一个主要局限性,我们所有人都应该记住,有时我们所有人都会忘记,这里的局限性是f(x)可能泄露关于x的信息。定义只要求它泄露所有x或所有x‘的概率,即泄露任何完整原像的概率是可忽略的。但是,例如,一个函数f可能几乎总是泄露一半的输入。事实上,给定一个单向函数f,我将设计另一个单向函数f‘,它总是泄露其输入的一半。让我明确一下输入定义域。f作用于n位输入。我将设计f‘,它实际上作用于2n位输入,并给出n+m位的输出。现在考虑以下f‘。f‘将x连接x‘作为输入。这两个字符串都是n位,每个都是随机的。它的作用如下:它给出f(x)和x‘明文。所以这个函数泄露了其输入的第二半。现在,我的主张是,如果f是单向函数,那么f‘也是单向函数。然而f‘泄露了其输入的一半。
为什么这个主张是正确的?如果f是单向函数,那么给定f(x),你无法计算x。现在给定这个完整输出,你无法计算x。但是要逆推f‘,你必须计算x和x‘两者。所以主张似乎是正确的。有任何问题吗?所以这是一个主要的局限性,我们所有人都应该记住。
另一个需要记住的事实是,如果P等于NP,单向函数就不可能存在。在这里,让我只谈谈证明思路,它非常简单。如果P等于NP,这意味着如果我可以验证一个解,我也可以想出一个解。第一个观察是,找到单向函数的原像是在NP中。这是因为给定一个原像,很容易验证。如果有人给我一个原像,比如x‘,我可以直接对其应用f,然后我可以验证f(x‘)确实等于y。因此,如果P等于NP,你也可以在多项式时间内计算某个原像。因此,这里的概率将正好是1。因为存在一个多项式时间算法,它总是可以找到一个原像。
下一个问题是,这是一个主要的开放问题。假设我们假设P不等于NP,单向函数是否存在?仅基于这个假设。如果P不等于NP,这意味着存在NP完全问题无法在多项式时间内解决。你能从其中一个NP完全问题构建单向函数吗?这个问题已经开放了大约40年。不幸的是,我们无法解决这个开放问题,至少我们不知道如何解决。但是,我们将做一个稍强的假设。我们将使用像因数分解这样的东西。顺便说一下,因数分解是一个强假设。它是一个比仅仅假设P不等于NP更强的假设,因为我们不知道因数分解是否是NP完全的。很可能P不等于NP,但因数分解是容易的。但我们将假设因数分解是困难的,并尝试基于此设计一个单向函数。
基于因数分解假设的单向函数
现在让我定义因数分解假设。这应该给我们某种单向函数。因数分解假设在很高层次上说:你选择两个大素数p和q,将它们相乘,称这个数为N。给定N,实际上很难恢复这两个大素数。定义P_n为所有n位素数的集合。它是一个数p,使得p < 2^n,并且p是素数。现在因数分解假设如下:对于每个PPT对手A,以下成立:
Pr[ (p, q) = A(N) : p, q ← P_n; N = p * q ] ≤ negl(n)
所以我从采样两个素数开始,将它们相乘,将乘积作为输入给对手,并要求对手产生p和q。任何对手能够做到这一点的概率是可忽略的,这就是因数分解假设。这已经开放了大约300年。人们试图找到因数分解的有效算法,但他们失败了,许多非常聪明的数学家尝试过,所以我们对因数分解假设的信心相当高。注意,当然,如果你有无限时间,我可以尝试每个可能的p和q,但我们寻找的是一个概率多项式时间算法,我们的假设是它不可能存在。
现在让我从一个非常简单的单向函数构造开始,但是针对一个特殊的定义域,一个特殊的输入定义域。针对特殊输入定义域D的单向函数。让我尝试定义D是什么。实际上,将D视为一个分布可能更容易。所以它只是说从P_n中采样p, q,然后输出x,即p连接q。所以输入x保证有两个n位素数相互连接。现在,单向函数f定义在这个D上,其工作如下。f(x)的工作方式如下:
- 第一步:将输入x解释为p, q。所以x应该是2n位,你首先解析这个输入,将其分成两个n位数。
- 第二步:输出N = p * q。
现在我的主张是,给定输出N,实际上很难计算输入。正如你所看到的,这几乎直接遵循因数分解假设。因数分解假设说,给定N,你无法计算p和q,你只能以可忽略的概率做到。我们将对此进行正式证明,所以这是一个很好的直觉。但正如我在本课程中提到的,如果我们想尝试正式地证明事情,这将本质上是我们第一个安全性证明,可能也是最简单的安全性证明,因为正如你所看到的,这几乎直接遵循因数分解假设,但我想传达的实际上是安全性证明的写法以及安全性证明的含义。
让我回答一些问题。对于因数分解假设,p和q是由对手输出的吗?为什么需要是随机的?我认为它们只需要属于P_n。所以你随机选择素数非常重要,例如,如果你总是选择固定的素数,那么对手可能已经知道那些素数。p和q不是由对手输出的,p和q首先被选择,然后你从这些构造出N。因数分解假设仅在你正确随机选择p和q时才成立。因数分解假设不是假设P不等于NP且因数分解不在P中吗?正如所写,因数分解假设不假设P不等于NP,尽管P不等于NP这一事实是由因数分解假设隐含的。所以这是一个充分的陈述。它也隐含了P不等于NP,因为如果P等于NP,因数分解就变得容易了。因数分解也假设平均情况是困难的,而不是存在一对困难的p, q。是的,所以我陈述的假设本质上假设平均情况下的因数分解是困难的。我只是说你随机得到p和q,所以我实际上是在谈论平均素数,而不是特殊素数,因数分解对于这样选择的素数应该是困难的。D等于p连接q,p是...是的,完全正确,你写得比我更正式。还有其他问题吗?
实际上,让我擦掉白板的左侧。我们将尝试在这里进行证明。我们将尝试证明这个构造按照这个定义是安全的。所以,现在的定理是,f是关于这个分布或定义域D的单向函数。密码学中几乎所有的证明都是通过矛盾来完成的。所以假设不成立。假设f不是单向函数。那么你想做的是构造一个算法B来打破因数分解假设。所以,为了矛盾,假设给定一个PPT算法A,它以f(x)作为输入,并以显著概率给出一个原像。它不是以高概率失败。这个定义的逆是什么?矛盾在哪里?这里的矛盾是如果存在一个算法A,它可以以显著概率而不是可忽略概率给我们原像。为了完成证明,我们将构造另一个PPT算法B,它以大写N作为输入,并给出相应的素数p和q,从而违反因数分解假设。如果我们确实相信因数分解假设,那就意味着不可能存在这样的B,因此也不可能存在这样的A。
B(N)的工作如下。所以这里没有悬念,B(N)在输入N上运行A。假设A返回某个x‘。将x’解释为p‘连接q’。检查N是否确实等于p‘乘以q’。如果是,输出p‘, q’。否则输出失败。我的主张是,如果A成功,那么B也成功。它以显著概率输出原像。为什么我们在定义中独立采样两个素数?假设两个素数相同,那么N = p^2,取平方根实际上并不那么困难。所以可能有一些特殊的分布,其中p和q彼此不同,但因数分解仍然是困难的。但最自然的定义是你从独立的p和q开始。这是我的主张。如果A以显著概率输出原像,那么B也以显著概率分解因数。在这里你可以清楚地看到B和A的成功概率是相同的。这就是证明的概要。有任何问题吗?
针对均匀输入的单向函数
所以,我们的下一个目标是设计针对均匀输入的单向函数,而不是这个特殊的输入定义域。我们非常高层次的想法如下:我们的输入将是均匀的,但会很大。我们将在输入中寻找素数。如果我们在输入中找到两个素数,那么我们将它们相乘并将结果放入输出。在那种情况下,我们将保证给定输出,找到整个输入是困难的。不幸的是,如果我们在输入中没有找到一对素数,那么这个构造就不起作用。所以我们需要两件事。首先,我们需要理解一个随机数是素数的概率是多少。其次,我们将希望从一个足够大的输入开始,以便我们确实可以以高概率在其中找到两个素数。
所以这里我们将依赖关于素数密度的切比雪夫定理。切比雪夫定理本质上说,一个均匀选择的n位数p是素数的概率大于等于1/(2n)。素数并不那么稀少。如果你选择一个随机数,那么有显著的、合理的机会这个数是素数。我们将不尝试证明它。这是数论中的一个事实。
现在我们将尝试从中构建单向函数。单向函数f。输入将相当长,n3。输出,我认为是两个索引。这个单向函数的工作方式如下。2n或者2,只是2n。以下是f(x)的描述:
- 步骤1:将x解释为一组n2个数,每个数n位。所以如果x是n3位,那么你可以将其写成n^2个n位数。
- 步骤2:在这些n^2个数上运行一个确定性的素数测试。如果我们没有找到一对素数,输出“失败”。否则,你输出N = p * q,其中p和q是前两个素数。
顺便说一下,这个确定性的素数测试非常重要,因为正如我们之前提到的,算法必须在多项式时间内运行,它不能是一个随机算法。测试一个数是否是素数,以确定性的方式进行,这曾经是一个开放问题。这仅在2000年才被解决。自那时以来,已经提出了一些改进。所以幸运的是,这简化了单向函数的构造。
现在观察,如果输出是“失败”,这个函数实际上很容易求逆。只需选择n^2个数,它们中必须没有素数,那就是“失败”的有效原像。所以我们真的不希望“失败”这个输出出现得太频繁。这就是我们将使用切比雪夫定理的地方。所以,总体上,我们想要证明的定理是:f是一个针对均匀输入定义域的单向函数。在我们开始证明之前,让我证明一个非常基本的事实,这已经能让你相信这确实是一个单向函数。
这个事实是,概率Pr[ f(x) 输出“失败” : x 从所需输入定义域中均匀选择 ]是可忽略的。那么f(x)输出“失败”的概率是多少?这等同于你选择n^2个随机数,并且我们在其中没有找到两个素数的概率。所以这个概率,f(x)给你“失败”的概率,粗略地说,是集合S中至多有一个数是素数的概率。这也等于我们没有素数加上我们恰好有一个素数的概率。这两个陈述只是直觉,现在让我尝试写得更正式。那么没有一个数是素数的概率是多少?这个概率至多是,一个随机数不是素数的概率,即(1 - 1/(2n))的n2次方。恰好有一个素数的概率意味着相同的事情,但你得到它n2 - 1次,然后最后一个或其中一个数是素数,乘以n2。这来自于并集界限,因为我们有n2个数。这是第一个是素数但其余都不是的概率,然后你需要检查第二个是素数而其余都不是的概率,依此类推,所以n^2只是为了并集。所以这个数以及这个数,它们都是可忽略的。我认为这个的确切公式是小于,比如,1 - 1/e之类的,你可以查看笔记中的确切计算。但总的来说,直觉是,每当这个显著高于这个,你最终会得到一些可忽略的东西。

这很好。现在,至少直观上,我们有以下结论:输出为“失败”的概率是可忽略的,所以在这种情况下,即使对手逆向了边界函数,我们也没问题。这不会经常发生。其余时间,输出中有一个大写N,你需要分解它来计算输入。对手成功做到这一点的概率也是可忽略的。
现在我需要描述...我有个小问题。是的,就像即使我们做了f(x)等于0的例子,即使我们输出“失败”,对手能够逆转它的概率不是也很低吗?直到我们证明这个,这是好的,就像我们大多数时候不会失败,但即使我们输出“失败”,从“失败”到输入几乎是一个随机猜测,对吗?如果只给他们“失败”,没有人能猜出输入是什么?所以我们正在使用单向函数的定义。我们正在使用这个定义。所以对手只需要输出某个x‘,它是有效的原像,使得f(x) = f(x‘)。你说对手可以输出任何那些其中没有素数的数,完全正确。对手只需要选择n^2个数,使得它们中没有一个是素数,那么那将是一个有效的原像,这就是我说错的地方,是的,完全正确,这不是一个一一对应的单向函数。是的,否则,你可以总是输出“失败”,那永远不会帮助你。谢谢。
现在,让我们尝试得出一个矛盾。有了这个事实,我们将尝试构造B。假设不成立。假设这个f不是单向函数。我们将再次构建B来分解某个给定的输入。B(N)将工作如下。正如你所期望的,B(N)将调用A,查看原像,并寻找两个导致N的素数。如果没有找到这样的两个素数,输出,比如,失败。现在,我们的目标是考虑A(N)给出某些东西的概率。这个概率是多少?理想情况下,我想说这个概率等于A(f(x))的求逆概率。这是真的吗?不幸的是,这里这不成立。这是因为“失败”。在这里,我从不给A(N)“失败”作为输入。但通常,f(x)有时也会有“失败”,如果f(x)被正确计算。所以我不能做这样的标记。由于时间不够,让我只做一个非常高层次的证明草图,你可以在笔记中看到正式证明。
这个概率,它输出p和q,使得p和q是素数,并且N = p * q,这就是我想要的。这个概率等于,让我称之为成功。这等于A成功的概率,条件是f(x)等于“失败”,乘以f(x)等于“失败”的概率。顺便说一下,这里我们将有条件,即x是随机生成的。A成功的概率,给定f(x)不等于“失败”,乘以f(x)不等于“失败”的概率。所以,正如我们所看到的,这个数量是可忽略的。这个概率至多是1,所以这只是negl(n)。最后,看这一项。所以它小于等于加上A成功的概率,使得f(x)不等于“失败”,乘以...让我用1减去这个概率代替。现在我们注意到这个是显著的。所以这意味着,A成功的概率,给定f(x)不等于“失败”,也必须显著。如果f(x)不等于“失败”,这意味着f(x)等于大写N,这是两个素数的乘积。这意味着当A被给予大写N,即两个素数的乘积时,A以显著概率成功。这本质上也意味着B也以显著概率成功。
有任何问题吗?所以,在很高层次上,我们在这个证明中唯一的担忧是,如果我们有一个期望单向函数输出的对手,但单向函数的输出可能是大写N,也可能是“失败”。如果我们证明单向函数的输出是“失败”的概率非常小,这意味着对手必须至少在部分时间,某些显著比例的时间里,即使在被给予大写N时也能求逆。如果A这样做,那就意味着A在分解数字。
总结
在本节课中,我们一起学习了密码学的核心基础——单向函数。我们从回顾香农定理的局限性出发,引入了计算复杂性理论中的基本概念,包括多项式时间、概率多项式时间、可忽略函数和显著函数。我们正式定义了单向函数,理解了其“易于计算、难以求逆”的核心特性,并探讨了强单向函数、弱单向函数、单射单向函数和单向置换等变体。
我们深入分析了单向函数的性质与局限性,例如它可能泄露部分输入信息,以及其存在性与P vs. NP问题的深刻联系。基于经典的因数分解难题假设,我们构造了一个针对特殊输入分布的单向函数,并概述了其安全性证明。最后,我们探讨了如何利用素数定理,构造一个针对均匀输入的单向函数,尽管其构造和证明更为复杂。
单向函数是构建更复杂密码学协议(如加密、数字签名)的基石。理解其定义、假设和构造,是进入现代密码学世界的关键第一步。
003:硬核谓词与计算不可区分性

在本节课中,我们将要学习如何克服单向函数的局限性,即它可能泄露输入的大部分信息。我们将引入硬核谓词的概念,它能从单向函数中提取出一个对敌手来说完全不可预测的比特。接着,我们将探讨计算不可区分性这一核心概念,它是构建伪随机生成器等更高级密码学原语的基础。
回顾单向函数
上一节我们介绍了单向函数。粗略地说,单向函数是易于计算但难以求逆的函数。我们甚至看到了一个基于因数分解假设的单向函数构造。
但是,单向函数有一个主要的局限性:我们不知道它会泄露多少输入信息。单向函数只保证不泄露整个输入,但它可能泄露一半甚至更多的输入比特。这对于我们最终构建加密方案等目标是远远不够的,因为加密方案要求消息被完全隐藏。
硬核谓词
我们的目标是克服单向函数可能泄露过多信息的局限性。一个自然的想法是:对于一个给定的单向函数 f,是否存在至少一个输入比特,是 f 保证不会泄露的?
更正式地说,对于一个单向函数 f,我们想找到一个谓词(单比特输出函数)h,使得给定 f(x),任何多项式时间敌手成功猜出 h(x) 的概率不会显著优于 1/2。
然而,我们很快发现两个令人失望的事实:
- 对于任意一个固定的比特位置
i,我们总能构造一个单向函数f'来泄露这个比特。 - 不存在一个通用的函数
h,能作为所有单向函数的硬核谓词。
既然固定的或通用的硬核谓词不存在,我们能否退而求其次,为每个特定的单向函数 f,随机地选择一个谓词 h,并希望它成为 f 的硬核谓词呢?
答案是肯定的,这由 Goldreich-Levin 定理保证。
Goldreich-Levin 定理
定理的核心思想是:对于任意单向函数 f,如果我们随机选择一个 n 比特串 r,并定义谓词 h(x) = <x, r> mod 2(即 x 和 r 的内积模 2),那么以极高的概率(超过 1 - negl(n)),这个 h 就是 f 的一个硬核谓词。
内积定义:
<x, r> = (Σ_{i=1}^{n} x_i * r_i) mod 2
其中 x_i 和 r_i 分别是 x 和 r 的第 i 个比特。直观上,r 选择了一个比特子集,h(x) 输出这些被选中的比特的异或和。
定理意义:
这个定理非常强大。它告诉我们,虽然我们不能预先指定哪个比特是硬的,但我们可以通过一个简单的随机过程,为任何单向函数配对一个“隐藏得很好”的单比特输出。这为后续构造更复杂的密码学原语提供了基础。
证明直觉:
证明的核心思想是反证法。假设存在一个敌手 A,在给定 f(x) 和 r 后,能以显著优于 1/2 的概率猜出 <x, r>。那么我们可以利用 A 作为子程序,构造另一个敌手 B 来求逆 f(x)。B 通过巧妙选择一系列特定的 r(例如 (1,0,...,0), (0,1,0,...,0) 等),从 A 那里获得关于 x 各个比特的“嘈杂”方程,最终利用纠错码技术(如 Goldreich-Levin 列表解码)恢复出整个 x,从而与 f 是单向函数的假设矛盾。
伪随机生成器与计算不可区分性
拥有了硬核谓词,我们就可以生成对计算受限的敌手看起来完全随机的比特。这引出了伪随机生成器的概念。但在定义 PRG 之前,我们需要一个关键工具:计算不可区分性。
为什么需要计算不可区分性?
在物理世界,生成真随机性很直接(如抛硬币)。但在电子世界中,生成高质量随机性既困难又昂贵,而密码学严重依赖随机性的质量。
一个天真的想法是:能否用少量真随机比特(种子),通过一个确定性的函数 G,扩张成大量看起来随机的比特?从信息论角度看,这是不可能的,因为确定性函数的输出范围不可能超过其输入范围。
然而,如果我们只要求输出对于计算能力有限(多项式时间)的敌手来说看起来随机,那么奇迹就出现了。这就是伪随机生成器的核心思想。
定义计算不可区分性
计算不可区分性是密码学中最重要的概念之一。我们首先需要定义概率 ensembles,它是一系列随着安全参数 n 增长的概率分布,记为 {X_n} 和 {Y_n}。
两个分布 ensembles {X_n} 和 {Y_n} 是计算不可区分的,如果对于任何多项式时间概率算法 D(区分器),其区分优势是可忽略的。
区分优势定义:
| Pr[D(X_n) = 1] - Pr[D(Y_n) = 1] | ≤ negl(n)
直观理解:任何高效的区分器 D,在看到来自 X_n 或 Y_n 的样本时,输出 1 的概率几乎相同。这意味着 D 无法有效判断样本来自哪个分布。
一个等价定义——预测优势:
我们也可以要求敌手直接“猜”样本来自哪个分布。两个分布是计算不可区分的,当且仅当对于任何多项式时间敌手 A,其预测优势是可忽略的:
| Pr_{b←{0,1}, x←X_b}[A(x) = b] - 1/2 | ≤ negl(n)
可以证明,区分优势和预测优势在常数因子内是等价的。我们通常使用区分优势的定义,因为它在组合性质上更优雅、更容易处理。

重要例子:
考虑两个 ensembles:X_n 是均匀随机的 n 比特偶数串,Y_n 是均匀随机的 n 比特奇数串。
- 从信息论或统计角度看,它们完全不同,很容易区分(检查奇偶性即可)。
- 但从计算不可区分性的定义看,如果我们检查单个样本点
s的概率差|Pr[X_n = s] - Pr[Y_n = s]|,这个值要么是0,要么是2/(2^n)(当n很大时可忽略)。这说明了为什么不能基于单个事件的概率来定义计算不可区分性,必须考虑区分器的整体输出行为。
总结
本节课中我们一起学习了:
- 硬核谓词:我们探讨了单向函数泄露信息的问题,并引入了硬核谓词作为解决方案。Goldreich-Levin 定理告诉我们,对于任何单向函数,随机内积函数以极高概率是其硬核谓词,这为我们提供了一个对敌手不可预测的单比特。
- 计算不可区分性:我们介绍了密码学的基石概念——计算不可区分性。它形式化地定义了对于多项式时间敌手而言,两个概率分布“看起来一样”的含义。这一定义克服了信息论区分性的限制,使得伪随机性等强大构造成为可能。
下一节,我们将利用这些概念正式定义伪随机生成器,并探索计算不可区分性优美的组合性质。
004:混合论证与伪随机生成器

在本节课中,我们将学习计算不可区分性的重要性质,特别是混合论证(Hybrid Argument)技术,并利用它来构造一个简单的伪随机生成器(PRG)。
计算不可区分性回顾
上一节我们介绍了计算不可区分性的定义。本节中,我们来看看它的几个关键性质。
计算不可区分性(Computational Indistinguishability, CI)的定义如下:给定两个分布(或分布族)X0 和 X1,如果对于所有概率多项式时间(PPT)算法 A,其区分优势(Distinguishing Advantage)是可忽略的,则称它们是计算不可区分的。
公式:
| Pr[A(X0) = 0] - Pr[A(X1) = 0] | ≤ negl(n)
其中,negl(n) 表示关于安全参数 n 的可忽略函数。
计算不可区分性的基本性质
计算不可区分性具有几个非常实用的性质,这使得它在密码学证明中特别有用。
1. 闭包性(Closure)
如果两个分布是计算不可区分的,那么对它们应用任何PPT算法 M 后,得到的输出分布也保持计算不可区分。
公式:
若 X0 ≈c X1,则对于任意PPT算法 M,有 M(X0) ≈c M(X1)。
直观理解是:如果没有任何算法能区分原始分布,那么对它们进行任何多项式时间计算后,结果也无法被区分。
2. 传递性(Transitivity)
这是最重要的性质,也是混合论证的基础。它类似于三角不等式。
公式:
若 X 和 Y 的区分优势为 ε1,Y 和 Z 的区分优势为 ε2,则 X 和 Z 的区分优势至多为 ε1 + ε2。
这意味着,如果我们能证明一系列分布中,相邻的两个都是计算不可区分的,那么首尾两个分布也必然是计算不可区分的。
混合论证(Hybrid Argument)
传递性引出了一个强大的证明技术——混合引理(Hybrid Lemma)。
混合引理:
假设我们有 m 个分布 X1, X2, ..., Xm。如果存在一个PPT敌手 A 能以优势 ε 区分 X1 和 Xm,那么必然存在某个 i,使得 A 能以至少 ε/(m-1) 的优势区分 Xi 和 Xi+1。
证明思路:使用反证法。如果所有相邻分布的区分优势都小于 ε/(m-1),那么通过传递性,X1 和 Xm 的区分优势将小于 (m-1) * [ε/(m-1)] = ε,这与假设矛盾。
推论:
如果 m 是 n 的多项式,并且对于所有 i,Xi 和 Xi+1 都是计算不可区分的,那么 X1 和 Xm 也是计算不可区分的。
这个推论非常有用。在密码学系统中,我们经常组合多个安全原语。混合论证允许我们通过逐一替换组件(从“全品牌”系统到“全通用”系统),并证明每一步的替换都是不可区分的,从而证明整个组合系统的安全性。
应用:多样本情况下的不可区分性
让我们通过一个练习来应用混合论证。
定理:如果两个分布族 X0 和 X1 是计算不可区分的,那么即使给敌手 k 个独立样本(k 为多项式),他仍然无法有效区分这些样本是全部来自 X0 还是全部来自 X1。

证明:
我们定义一系列混合分布 H0, H1, ..., Hk:
H0: 所有k个样本均来自X0。Hi: 前i个样本来自X1,后k-i个样本来自X0。Hk: 所有k个样本均来自X1。
我们的目标是证明 H0 ≈c Hk。根据混合引理的推论,我们只需证明对于每个 i,Hi ≈c Hi+1。
假设存在敌手 A 能显著区分 Hi 和 Hi+1。我们可以构造一个新敌手 B 来区分单个样本:
B收到一个挑战样本x(可能来自X0或X1)。B自己生成样本:前i个来自X1,第i+1个设为挑战样本x,剩余的k-i-1个来自X0。B将这k个样本交给A,并输出A的结果。

分析:
- 如果
x来自X0,则B为A构造的正是分布Hi。 - 如果
x来自X1,则B为A构造的正是分布Hi+1。
因此,B的区分优势与A相同。但根据前提,X0和X1是计算不可区分的,不存在这样的B。由此矛盾可知,A不可能存在,即Hi ≈c Hi+1。证毕。
构造伪随机生成器(PRG)
现在,我们利用混合论证来构造一个简单的伪随机生成器。
定义:一个伪随机生成器 G 是一个确定性的多项式时间算法,它接受一个较短的随机种子 s ∈ {0,1}^n,输出一个较长的串 G(s) ∈ {0,1}^{l(n)},其中 l(n) > n。其安全性要求是:
公式:
{ G(s) | s ← {0,1}^n } ≈c { r | r ← {0,1}^{l(n)} }
即,G(s) 的输出分布与真正的均匀随机分布计算不可区分。
单比特扩展PRG
我们首先构造一个只能将种子扩展一比特的PRG。我们需要的构件是:
- 单向置换(One-Way Permutation, OWP)
F:{0,1}^n → {0,1}^n。它是一个易于计算但难以求逆的双射。 - 硬核谓词(Hardcore Predicate)
h:{0,1}^n → {0,1}。给定F(x),h(x)看起来像一个均匀随机比特,即使知道F(x)也无法预测。
构造:
G(s) = F(s) || h(s)
其中 || 表示连接。G 的输入是 n 比特种子 s,输出是 n+1 比特。
安全性证明(使用混合论证):
我们定义三个混合分布:
H0:F(s) || h(s),其中s均匀随机。这正是G(s)的输出。H1:F(s) || u,其中s均匀随机,u是一个独立的均匀随机比特。H2:u_{n+1},一个真正的n+1比特均匀随机串。
目标:证明 H0 ≈c H2。
证明步骤:
H0 ≈c H1:这直接来自于硬核谓词h的定义。根据定义,给定F(s),h(s)与一个均匀随机比特计算不可区分。因此,将h(s)替换为独立的u,分布不可区分。H1 ≡ H2:这里≡表示完全相同的分布(不仅仅是计算不可区分)。我们需要证明H1本身就是一个均匀分布。- 因为
F是置换,当s均匀随机时,F(s)也是{0,1}^n上的均匀分布。 - 比特
u独立且均匀。 - 因此,
(F(s), u)这个二元组恰好是{0,1}^{n+1}上的均匀分布。所以H1就是均匀分布H2。
- 因为
根据传递性,H0 ≈c H2,即我们的构造 G 是一个安全的单比特扩展PRG。

总结
本节课中我们一起学习了:
- 计算不可区分性的核心性质:闭包性和传递性。
- 混合论证:一种通过分析一系列“中间”或“混合”分布来证明两个复杂分布不可区分的强大技术。它是密码学安全性证明中最常用的工具之一。
- 应用混合论证:我们证明了即使给予敌手多项式个样本,计算不可区分性依然保持。
- 构造伪随机生成器:利用单向置换和硬核谓词,我们构造并证明了一个安全的单比特扩展PRG。其安全性证明完美地展示了如何运用混合论证来处理由不同密码学原语组合而成的系统。
下一讲,我们将看到如何通过对这个单比特扩展PRG进行迭代,构造出能产生任意多项式长度输出的伪随机生成器。
005:伪随机函数

在本节课中,我们将要学习如何从伪随机生成器(PRG)构造出具有多项式拉伸能力的PRG,并进一步引入一个更强大的密码学原语——伪随机函数(PRF)。我们将看到PRF如何作为随机函数的密码学等价物,并理解其基本构造和安全性证明。
从单比特拉伸到多项式拉伸 PRG
上一节我们介绍了伪随机生成器(PRG),并构造了一个只能将种子拉伸一比特的PRG。本节中,我们来看看如何构造一个能将种子拉伸到任意多项式长度的PRG。
我们的目标是构造一个函数 G,它接受一个 n 比特的输入,并将其拉伸到 L(n) 比特,其中 L(n) 是 n 的任意多项式(例如 n², n³)。我们假设已经拥有一个来自上一节课的单比特拉伸PRG,记作 G₁。
以下是构造多项式拉伸PRG G 的步骤:
- 设初始种子 S₀ 为输入密钥 K。
- 对于 i 从 1 到 L(n),重复以下过程:
- 计算 G₁(Sᵢ₋₁),得到一个 n+1 比特的输出。
- 将这个输出的前 n 比特作为新的种子 Sᵢ。
- 将输出的最后一个比特保存为 Bᵢ。
- 最终,输出所有收集到的比特 B₁ || B₂ || ... || Bₗ₍ₙ₎。
核心公式:
输出 = B₁ || B₂ || ... || Bₗ₍ₙ₎,其中 (Sᵢ || Bᵢ) = G₁(Sᵢ₋₁)。
安全性注意事项:在构造过程中产生的中间种子(如 S₁, S₂)不能出现在最终输出中,因为给定任何一个中间种子,攻击者可以计算出之后所有的比特,破坏随机性。但最后一个种子 Sₗ₍ₙ₎ 可以被安全地包含在输出中(尽管通常为了简洁而省略)。
混合论证证明:我们可以通过一系列混合实验来证明 G 的安全性。从真实构造 H₀ 开始,逐步将每一步 G₁ 的输出替换为真正的随机字符串,最终到达所有输出比特均为随机的混合实验 Hₗ₍ₙ₎。根据单比特拉伸PRG G₁ 的安全性,相邻的混合实验在计算上是不可区分的,因此 G 也是一个安全的PRG。
PRG 的应用:支持长消息的一次一密
了解了多项式拉伸PRG后,我们来看看它的一个直接应用:克服经典一次一密密钥必须与消息等长的限制。
一个粗略的构造如下:假设共享密钥 K 较短,而消息 M 很长。加密时,首先使用PRG G 将密钥 K 拉伸到与消息 M 等长的比特串 G(K),然后用这个比特串作为“垫”对消息进行异或加密。
核心公式:
密文 C = M ⊕ G(K)
解密时,接收方使用相同的密钥 K 重新计算 G(K),然后与密文异或即可恢复明文。
局限性:这个方案本质上仍然是“一次”的。虽然密钥可以很短,但同一个密钥 K 在加密后就不能再安全地使用了。为了支持多次加密,我们需要更强大的工具——伪随机函数。
引入伪随机函数 (PRF)
为了支持用同一个密钥安全地加密多条消息,我们引入伪随机函数(PRF)。PRF可以看作是PRG的泛化,它允许我们用一个短密钥生成一个指数级大的“随机”表,并能按需访问其中的任意位置。
一个随机函数 RF 可以想象成一个巨大的表格,为每一个可能的输入(例如,所有 n 比特字符串)都对应一个完全随机的输出。然而,存储这样一个表格需要指数级的空间,是不现实的。
一个伪随机函数 F 则是一个高效的可计算函数,它接受一个秘密密钥 k 和一个输入 x,输出一个值 F(k, x)。其安全性要求是:对于任何多项式时间的攻击者,如果他只能通过“查询”的方式与函数交互(即提供 x 获得 F(k, x)),那么他无法区分自己是在与一个真正的随机函数交互,还是在与 F(k, ·) 交互。
安全性游戏(挑战者C vs. 敌手A):
- C 随机生成密钥 k 和一个随机比特 b。
- A 可以自适应地多次提交查询 xᵢ。
- 如果 b=0,C 用 F(k, xᵢ) 回答。
- 如果 b=1,C 用一个真正的随机函数 RF 来回答(保证对相同 x 的查询回答一致)。
- A 最终输出一个猜测比特 b‘。
- 如果 Pr[b‘ = b] ≤ 1/2 + negl(n),则 PRF 是安全的。
从 PRG 构造 PRF
现在,我们展示如何从一个能将 n 比特输入拉伸为 2n 比特输出的 PRG(记作 G)来构造一个 PRF。
构造的核心思想是建立一个二叉树(Garbled Tree):
- 根节点:密钥 k(n 比特)。
- 扩展规则:对于任何一个节点(一个 n 比特串 s),应用 PRG:
G(s) → (s₀, s₁),其中s₀和s₁各为 n 比特,分别作为该节点的左孩子和右孩子。 - 输入映射:将 PRF 的输入 x(例如 m 比特)解释为从根节点到叶子节点的一条路径(0 表示向左,1 表示向右)。
- 输出:最终到达的叶子节点的值,就是 F(k, x) 的输出。
示例(m=2):
对于输入 x = 01,计算过程为:
F(k, 01) = G₁( G₀(k) ),其中 G(s) = (G₀(s), G₁(s))。
安全性证明(混合论证):
我们同样使用混合论证。定义一系列混合实验 Hᵢ,其中 i 从 0 到 m(输入长度)。
- 在 Hᵢ 中,所有被敌手查询到的、深度为 i 的树节点(即路径上第 i 层的节点)的值都被替换为真正的随机字符串,而更深层的节点仍使用 PRG G 从这些随机值生成。
- H₀ 是真实的 PRF 构造。
- Hₘ 中,所有被查询到的叶子节点都是随机的,这正好模拟了一个随机函数对查询的回答。
我们需要证明 Hᵢ 和 Hᵢ₊₁ 在计算上不可区分。关键在于,从 Hᵢ 到 Hᵢ₊₁,我们实际上是将第 i+1 层某些节点的生成方式从 G(父节点) 替换为直接随机采样。由于敌手只能进行多项式次查询,受影响的节点数量也是多项式的。我们可以进一步细化混合步骤,一次只替换一个父节点对应的两个孩子节点。这样,相邻细化混合之间的差异正好可以归约到底层 PRG G 的安全性上。
PRF 的应用展望

PRF 为实现安全的多消息加密提供了关键思路。一个直观(但需完善)的加密方案如下:
- 加密:为了加密消息 M,随机选择一个值 r(作为“临时索引”),计算密文
C = (r, M ⊕ F(k, r))。 - 解密:收到
(r, C‘)后,计算M = C‘ ⊕ F(k, r)。
由于 r 的空间很大(指数级),随机选择时发生碰撞(即两次加密使用相同的 r)的概率可忽略不计。这保证了每次加密都相当于使用了一个新鲜的、随机的“一次一密”密钥 F(k, r)。我们将在后续课程中正式定义并分析此类加密方案。
此外,如果需要更长的输出,可以组合使用 PRF 和 PRG:先使用 PRF 得到一个较短的“种子”输出,再使用 PRG 将其拉伸到所需长度。
总结
本节课中我们一起学习了:
- 多项式拉伸PRG的构造:通过迭代应用单比特拉伸PRG,并利用混合论证证明其安全性。
- PRG的局限性:它主要解决密钥拉伸问题,但难以优雅地支持同一密钥的多次使用。
- 伪随机函数(PRF):作为随机函数的密码学等价物,允许用短密钥模拟一个巨大的随机表,并支持按需查询。
- PRF的构造:通过从PRG构建的“紊乱树”来实现,并再次通过精巧的混合论证证明其安全性。
- PRF的应用前景:为构建支持多次加密的安全方案奠定了基础,例如通过引入随机“索引”来生成每次加密时独立的密钥流。
PRF是一个极其重要的密码学基础构件,在消息认证码、加密方案等多种密码协议中都有核心应用。
006:对称密钥加密与混合加密的乐趣

在本节课中,我们将要学习对称密钥加密方案。我们将回顾其定义,并探讨如何构建可证明安全的方案,以克服一次性密码本的局限性。我们还将学习如何定义多消息安全性,并利用伪随机函数来构建满足该定义的加密方案。
对称密钥加密的定义
上一节我们介绍了课程目标,本节中我们来看看对称密钥加密的正式定义。
任何对称密钥加密方案必须指定三种算法:
- 密钥生成算法:输入安全参数,输出一个秘密密钥
K。 - 加密算法:输入密钥
K和消息M,输出密文C。 - 解密算法:输入密钥
K和密文C,输出一个消息M'。
一个好的对称加密方案需要满足两个属性:正确性和保密性。
正确性 要求在没有敌手的情况下,加密和解密操作能正确恢复原始消息。公式描述如下:
Pr[ Decrypt(K, Encrypt(K, M)) = M ] = 1
其中,密钥 K 由密钥生成算法正确生成。
保密性 则是对完美保密概念的推广。完美保密要求对于任意两个消息 M0 和 M1,以及任意密文 C,以下概率相等:
Pr[ Encrypt(K, M0) = C ] = Pr[ Encrypt(K, M1) = C ]
香农定理指出,任何满足完美保密且密钥长度小于消息长度的方案都是可被攻破的。为了克服这个限制,我们转向计算安全性。
基于不可区分性的安全性
上一节我们回顾了完美保密,本节中我们来看看其计算安全的推广版本。
我们定义 基于不可区分性的安全性。该定义要求,对于任意两个消息 M0 和 M1,由 M0 加密得到的密文分布与由 M1 加密得到的密文分布是计算不可区分的。公式描述如下:
{ Encrypt(K, M0) } ≈_c { Encrypt(K, M1) }
其中,≈_c 表示计算不可区分,密钥 K 被正确生成。
这等价于一个 替代定义:考虑一个实验,挑战者随机选择比特 b ∈ {0,1},加密消息 Mb 得到密文 C 发送给敌手 A。敌手的目标是猜测 b。安全性要求任何PPT敌手成功的概率仅比1/2高出可忽略的量:
| Pr[ A(Encrypt(K, Mb)) = b ] - 1/2 | ≤ negligible
这两个定义是等价的,证明思路与计算不可区分性和预测不可区分性之间的等价性证明类似。


使用伪随机生成器进行一次性加密
上一节我们定义了安全性,本节中我们来看看如何利用伪随机生成器构建一个简单的加密方案。
给定一个伪随机生成器 PRG,它将 n 比特种子扩展为 l(n) 比特输出。我们可以构建一个加密方案:
- 密钥生成:随机均匀选择
n比特字符串作为密钥K。 - 加密:对于消息
M ∈ {0,1}^{l(n)},计算密文C = M ⊕ PRG(K)。 - 解密:给定密文
C,计算消息M = C ⊕ PRG(K)。
该方案的正确性是显然的。其安全性证明依赖于 PRG 的输出与真随机字符串的计算不可区分性,并通过混合论证完成。该方案克服了一次性密码本密钥必须与消息等长的限制,允许用短密钥加密长消息。但其主要局限仍是“一次性”的,即密钥不能安全地重复使用。
迈向多消息安全
上一节我们构建了一次性安全方案,本节中我们来看看如何实现可重复使用的密钥。
首先,我们需要定义 多消息安全性。该定义要求,对于任意两个等长的消息向量 (M0_1, ..., M0_q) 和 (M1_1, ..., M1_q),以下两个密文向量分布是计算不可区分的:
{ (Encrypt(K, M0_1), ..., Encrypt(K, M0_q)) } ≈_c { (Encrypt(K, M1_1), ..., Encrypt(K, M1_q)) }
一个关键的观察是:任何满足多消息安全的、无状态的对称密钥加密方案必须是随机化的。证明很简单:如果一个确定性的无状态方案两次加密同一个消息 M,会得到相同的密文 C;而加密两个不同的消息 M1 和 M2,会得到不同的密文 C1 和 C2。敌手通过比较密文是否相同就能轻松区分这两种情况。
使用伪随机函数构建多消息安全加密
上一节我们指出了随机化的必要性,本节中我们利用伪随机函数来构建一个满足多消息安全的随机化加密方案。
给定一个伪随机函数 PRF: {0,1}^n × {0,1}^{m1} -> {0,1}^{m2},我们可以构建加密方案:
- 密钥生成:随机均匀选择
n比特字符串作为密钥K。 - 加密:对于消息
M ∈ {0,1}^{m2},随机均匀选择r ∈ {0,1}^{m1},计算密文C = (r, M ⊕ PRF(K, r))。 - 解密:给定密文
C = (r, s),计算消息M = s ⊕ PRF(K, r)。
该方案是随机化的,因为每次加密都使用新的随机数 r。其安全性证明的核心思想是:利用 PRF 的安全性,将 PRF(K, ·) 替换为真随机函数;由于随机数 r 几乎不会碰撞,每次加密所用的“一次一密”密钥(即 PRF(K, r) 或随机函数的输出)都是独立且均匀的,从而保证了多消息安全性。通过混合论证可以严格证明这一点。
关于混合论证的常见错误
上一节我们完成了安全方案的构建,在本节最后,我们通过几个例子来探讨在使用混合论证证明安全性时的常见陷阱。
以下是几个需要判断对错的陈述:
- 分布
(K, Encrypt(K, M0))与(K, Encrypt(K, M1))计算不可区分。- 错误。加密方案的安全性依赖于密钥
K对敌手是均匀未知的。这里将K直接给出,破坏了这一条件,敌手可以解密并直接获得消息。
- 错误。加密方案的安全性依赖于密钥
- 分布
(Encrypt(K1, M0), Encrypt(K2, M0))与(Encrypt(K1, M1), Encrypt(K2, M1))计算不可区分(K1和K2独立均匀生成)。- 正确。即使敌手知道
M0和M1,由于两个加密使用独立且均匀的密钥,安全性仍然成立。可以通过混合论证证明(先改变第一个密文,再改变第二个)。
- 正确。即使敌手知道
- 分布
(Encrypt(K, M0), Encrypt(K, M0))与(Encrypt(K, M1), Encrypt(K, M1))计算不可区分。- 不一定。这需要加密方案满足多消息安全性。如果方案只是单消息安全(如某些随机化方案),重用密钥加密多个消息可能会泄露信息。
- 考虑一个随机化加密方案。分布
(Encrypt(K, M0; r), Encrypt(K, M0‘; r))与(Encrypt(K, M1; r), Encrypt(K, M1‘; r))计算不可区分(使用相同的随机数r)。- 错误。加密方案的安全性同样依赖于随机数
r对敌手是均匀未知的。重复使用r破坏了这一条件,标准的安全性保证不再适用。
- 错误。加密方案的安全性同样依赖于随机数
核心要点:密码学方案的安全性定义明确指出了其依赖的均匀随机量(如密钥、随机数)。在构造混合论证或使用方案时,必须确保这些量在敌手视角下始终保持均匀随机性,否则安全性将无法保证。

总结
本节课中我们一起学习了对称密钥加密。我们从定义出发,介绍了基于不可区分性的计算安全性概念。我们首先利用伪随机生成器构建了一次性安全加密方案,克服了密钥长度限制。接着,我们定义了更强的多消息安全性,并指出无状态方案必须引入随机化。最后,我们利用伪随机函数构建了一个满足多消息安全的实用随机化加密方案。在课程末尾,我们通过辨析几个例子,加深了对混合论证和安全性依赖条件的理解。
007:数论基础、离散对数问题与Diffie-Hellman问题

在本节课中,我们将学习数论的基础知识,这是理解公钥密码学的关键。我们将介绍群、生成元等核心概念,并深入探讨离散对数问题、计算性Diffie-Hellman问题和判定性Diffie-Hellman问题。这些问题是构建现代密码学协议(如密钥交换)的基石。
数论背景与符号
首先,我们回顾一些基本符号和概念。
- 自然数 (N):从1开始的正整数集合。
- 实数 (R):包含所有有理数和无理数的集合。
- 整数 (Z):包含所有正整数、负整数和零的集合。
两个数A和B的最大公约数 (GCD) 是能同时整除A和B的最大正整数。如果 GCD(A, B) = 1,我们称A和B互质。
计算GCD最著名的算法是欧几里得算法。其扩展版本——扩展欧几里得算法——不仅能计算GCD,还能找到整数x和y,使得 A*x + B*y = GCD(A, B)。
由此引出一个重要的引理:如果A和B互质,则存在整数x,使得 A*x ≡ 1 (mod B)。这意味着x是A在模B运算下的乘法逆元。在密码学中,我们经常在模素数下工作,因为除了0以外的每个数都有乘法逆元,这是一个非常有用的性质。

另一个基本定理是算术基本定理:每个大于1的整数N都可以唯一地(不考虑顺序)表示为一系列素数的幂的乘积,即 N = p1^e1 * p2^e2 * ... * pk^ek,其中pi是素数。
群论简介
现在,让我们更贴近密码学,回顾群的概念。
一个群 (G, ·) 是一个集合G与一个二元运算“·”的组合,该运算满足以下四个条件:
- 封闭性:对于所有
a, b ∈ G,有a · b ∈ G。 - 单位元存在性:存在一个元素
e ∈ G,使得对所有a ∈ G,有e · a = a · e = a。 - 结合律:对于所有
a, b, c ∈ G,有(a · b) · c = a · (b · c)。 - 逆元存在性:对于每个
a ∈ G,存在一个元素x ∈ G,使得a · x = x · a = e。
如果一个群还满足交换律(即对所有 a, b ∈ G,有 a · b = b · a),则称为阿贝尔群。群的阶,记作 |G|,是指群中元素的数量。
以下是两个重要的群例子:

- 加法群 Zn:集合为
{0, 1, ..., n-1},运算为模n加法(a + b) mod n。这是一个阿贝尔群。 - 乘法群 Zp*:集合为
{1, 2, ..., p-1}(p为素数),运算为模p乘法(a * b) mod p。这也是一个阿贝尔群。其阶为p-1。
更一般地,对于任意整数n,可以定义 Zn* 为所有小于n且与n互质的正整数集合,运算为模n乘法。这是一个群,其阶由欧拉函数 φ(n) 给出。φ(n) 表示小于n且与n互质的正整数的个数。如果 n = p1^e1 * ... * pk^ek,则 φ(n) = Π (pi^ei - pi^(ei-1))。
群的性质与素数阶群
群有一个非常重要的通用性质:对于群G中的任意元素a,有 a^k = a^(k mod |G|)。这意味着指数运算本质上是在模群的阶下进行的。
由此可以推导出一些推论:
- 对于任意
a ∈ G,有a^|G| = e(单位元)。 - 元素a的阶是满足
a^k = e的最小正整数k。元素的阶整除群的阶。 - 如果群的阶
|G|是一个素数,那么每个非单位元元素的阶都等于|G|。
素数阶群在密码学中特别受青睐,因为在其指数运算中,模运算是模一个素数,这保证了指数(在模群阶的意义下)存在乘法逆元。这个性质在后续构造密码协议时非常有用。
Zp*(模p乘法群)的阶是p-1,通常不是素数。我们需要构造乘法素数阶群。一种常见方法是选择两个素数p和q,满足 p = 2q + 1(这样的p称为安全素数)。然后定义群 Gq = {x^2 mod p | x ∈ Zp*}。可以证明,Gq是一个阶为q的乘法群。
生成元
在群论中,生成元是一个强大的概念。元素 g ∈ G 被称为生成元,如果集合 {g, g^2, g^3, ..., g^|G|} 包含了群G中的所有元素。也就是说,通过不断对g进行群运算,可以生成整个群。
一个美妙的事实是:在素数阶群中,每一个非单位元元素都是生成元。这是因为在素数阶群中,任何非单位元元素的阶都必须等于群的阶本身,从而能生成所有元素。生成元的存在使得离散对数问题变得困难,而这正是密码学所需要的。
核心密码学问题
基于上述数论和群论基础,我们现在可以描述三个核心的、被认为是计算困难的密码学问题。它们是构建公钥密码系统的基石。
1. 离散对数问题
设 Gq 是一个素数阶乘法群,g 是它的一个生成元。
离散对数假设认为:对于任何概率多项式时间(PPT)敌手A,解决以下问题是困难的:
- 给定
h = g^x,其中x是从Zq中随机均匀选取的。 - 敌手A成功输出
x的概率是可忽略的。
函数 f(x) = g^x 是一个单向函数(基于离散对数假设)。因为g是生成元,该函数是一对一的:如果 g^x = g^y,则 x ≡ y (mod q),由于x和y都在Zq中,所以 x = y。因此,求逆(即求x)的困难性直接等价于解决离散对数问题。
离散对数假设被认为在某些特定的群(如上面构造的Gq)中成立。密码学家持续研究在不同群中该问题的难度。
2. 计算性Diffie-Hellman问题

在同一个素数阶群 Gq 和生成元 g 的设置下。
计算性Diffie-Hellman假设认为:对于任何PPT敌手A,解决以下问题是困难的:
- 给定
g^x和g^y,其中x, y是从Zq中独立随机均匀选取的。 - 敌手A成功输出
g^(xy)的概率是可忽略的。
注意,CDH问题可能比DL问题更容易(如果存在不通过求解x或y就能计算 g^(xy) 的方法),但绝不会更难。因为如果敌手能解DL问题(求出x或y),就能轻易计算出 g^(xy)。因此,CDH假设是比DL假设更强的假设。
3. 判定性Diffie-Hellman问题
判定性Diffie-Hellman假设认为:对于任何PPT敌手A,区分以下两个分布是计算上不可行的:
- 分布0:
(g^x, g^y, g^(xy)),其中x, y随机取自Zq。 - 分布1:
(g^x, g^y, g^r),其中x, y, r随机取自Zq。
DDH问题要求敌手不仅能计算 g^(xy),甚至当把正确答案 g^(xy) 给它时,它也无法将其与一个随机的群元素区分开来。显然,如果能解决CDH问题(计算出 g^(xy)),就能轻松解决DDH问题(通过比较计算结果)。因此,DDH假设是比CDH假设更强的假设。
总结
本节课我们一起学习了公钥密码学所需的数论基础。我们首先回顾了最大公约数、扩展欧几里得算法和算术基本定理。然后,我们深入探讨了群的概念,包括加法群Zn和乘法群Zp*,并学习了群的阶和欧拉函数。
我们了解到素数阶群具有优良的性质,并学会了如何构造乘法素数阶群Gq。我们证明了在素数阶群中,每个非单位元都是生成元。
最后,我们介绍了三个核心的密码学困难问题:离散对数问题、计算性Diffie-Hellman问题和判定性Diffie-Hellman问题。我们理解了它们之间的层级关系:DDH假设强于CDH假设,而CDH假设又强于DL假设。与因式分解问题相比,基于离散对数的这些问题具有更丰富的代数结构,使得更多样的密码学原语(如下节课将看到的Diffie-Hellman密钥交换)能够基于它们构建。这些概念是理解现代密码协议的关键。
008:Diffie-Hellman密钥交换与ElGamal公钥加密

在本节课中,我们将学习如何基于数论假设构建更高级的密码学原语。我们将重点介绍Diffie-Hellman密钥交换协议和ElGamal公钥加密方案,它们都依赖于DDH假设。
回顾DDH假设
上一节我们介绍了数论知识,目的是为了构建密钥交换和公钥加密等高级原语。本节中,我们来看看一个关键的假设——DDH假设。
DDH假设定义如下:给定一个阶为Q的群G及其生成元g,以下两个分布是计算不可区分的:
- 分布1:
(g^x, g^y, g^(x*y)),其中x和y均匀随机选自Zq。 - 分布2:
(g^x, g^y, g^r),其中x, y, r均匀随机选自Zq。
该假设表明,给定g^x和g^y,不仅计算g^(x*y)是困难的,甚至无法将g^(x*y)与一个随机的群元素区分开来。
密钥交换问题
对称密钥加密的一个主要限制是,通信双方需要预先安全地共享一个秘密密钥。密钥交换协议旨在解决这个问题:双方仅通过公开信道通信,最终能协商出一个只有他们知道的秘密密钥,而窃听者无法得知该密钥。
这听起来几乎不可能,因为双方最初没有共享任何秘密,且所有通信都是公开的。然而,Diffie和Hellman在1976年的开创性工作中提出了解决方案。
密钥交换协议定义
一个密钥交换协议π涉及两方,Alice和Bob。
- 双方拥有各自的私密随机带RA和RB。
- 双方通过交换消息生成协议记录τ。
- Alice的视图是
(RA, τ),Bob的视图是(RB, τ)。 - 双方根据各自的视图输出密钥KA和KB。
协议需满足两个属性:
- 正确性:
Pr[KA = KB] = 1。双方最终协商出相同的密钥K。 - 安全性:对于任何PPT窃听者Eve,其视图仅为公开记录τ。要求密钥K与τ的联合分布,与一个随机密钥K‘与τ的联合分布是计算不可区分的。即,
(K, τ) ≈ (K‘, τ),其中K‘是均匀随机的。
Diffie-Hellman密钥交换协议
基于DDH假设,我们可以构建Diffie-Hellman密钥交换协议。
协议描述:
- 公共参数:一个阶为Q的循环群G,及其生成元g。
- Alice随机选择
x ← Zq,计算X = g^x,并将X发送给Bob。 - Bob随机选择
y ← Zq,计算Y = g^y,并将Y发送给Alice。 - Alice计算密钥
K = Y^x = g^(x*y)。 - Bob计算密钥
K = X^y = g^(x*y)。
正确性:显然,双方计算出相同的密钥g^(x*y)。
安全性:窃听者Eve的视图τ = (g^x, g^y)。根据DDH假设,(g^x, g^y, g^(x*y)) ≈ (g^x, g^y, g^r),其中r是随机的。这正是安全性定义所要求的:(K, τ) ≈ (随机群元素, τ)。
主动攻击与中间人攻击
Diffie-Hellman密钥交换协议无法抵抗主动攻击者(即中间人攻击)。攻击者可以分别与Alice和Bob建立独立的密钥交换会话,从而知晓双方协商出的密钥,并能够解密、篡改或注入消息。
抵抗中间人攻击需要额外的机制,例如证书和TLS/SSL协议,这超出了基本密钥交换的范围。
公钥加密
公钥加密比密钥交换功能更强大。在Diffie和Hellman的论文中,他们提出了密钥交换,但将公钥加密的构造留作开放问题,后来由RSA和ElGamal解决。
公钥加密定义
一个公钥加密方案包含三种算法:
- 密钥生成(Gen):输入安全参数,输出公私钥对
(pk, sk)。 - 加密(Enc):输入公钥pk和消息m,输出密文c。加密过程是随机的。
- 解密(Dec):输入私钥sk和密文c,输出消息m。
方案需满足:
- 正确性:对于所有消息m,
Pr[Dec(sk, Enc(pk, m)) = m] = 1。 - 安全性(IND-CPA):对于任意一对消息
(m0, m1)和任意PPT敌手A,以下两个分布计算不可区分:
(pk, Enc(pk, m0)) ≈ (pk, Enc(pk, m1))
其中(pk, sk)由Gen生成。
等价地,敌手在获取公钥后,难以区分对m0或m1的加密。
公钥加密的性质
以下是关于公钥加密的两个重要事实:
- 确定性公钥加密是灾难性的:如果加密算法是确定性的,敌手可以简单地用公钥加密所有可能的候选消息(如果消息空间较小或熵较低),并与截获的密文比较,从而完全恢复明文。因此,公钥加密必须是随机的。不过,确定性公钥加密在加密搜索等特定应用中也有研究价值。
- 单消息安全等价于多消息安全:对于公钥加密,抵抗单次加密攻击的安全性(IND-CPA)自动意味着抵抗多次加密攻击的安全性。这一点与对称加密不同(例如,一次一密仅单次安全)。证明使用混合论证,关键点在于敌手拥有公钥,可以自行生成其他消息的加密。
ElGamal公钥加密方案

ElGamal加密方案本质上“隐藏”在Diffie-Hellman密钥交换中。它将密钥交换中的第一轮消息视为公钥,第二轮消息视为密文的一部分。
方案描述:
- 公共参数:阶为Q的群G,生成元g。
- 密钥生成(Gen):随机选择
x ← Zq。私钥sk = x,公钥pk = g^x。 - 加密(Enc(pk, m)):消息m必须是群G中的元素。随机选择
r ← Zq,计算:
c1 = g^r
c2 = m * (pk^r) = m * g^(x*r)
输出密文c = (c1, c2)。 - 解密(Dec(sk, c)):输入
sk = x和c = (c1, c2),计算:
s = c1^x = g^(r*x)
输出m = c2 * s^(-1) = m * g^(x*r) * g^(-x*r) = m。
正确性:根据上述计算,解密可正确恢复消息m。
安全性证明(IND-CPA):
我们只需证明单消息安全性。考虑以下混合序列,其中 (pk, c) = (g^x, (g^r, m_b * g^(x*r))) 需要与b=0或1不可区分。
- H0:真实情况,加密
m0。即(g^x, g^r, m0 * g^(x*r))。 - H1:将
g^(x*r)替换为随机群元素g^z。即(g^x, g^r, m0 * g^z)。根据DDH假设,H0 ≈ H1。 - H2:由于g是生成元,设
m0 = g^(z0)。则m0 * g^z = g^(z+z0)。由于z是随机的,z+z0也是随机的。因此H1与H2的分布完全相同。 - H3:现在加密
m1。设m1 = g^(z1),则H3为(g^x, g^r, m1 * g^z) = (g^x, g^r, g^(z+z1))。同样,由于z随机,H2与H3的分布完全相同。 - H4:回到真实结构,但加密
m1。即(g^x, g^r, m1 * g^(x*r))。根据DDH假设(逆向),H3 ≈ H4。
因此,H0 ≈ H4,证明了ElGamal加密的IND-CPA安全性。
更强的安全概念与总结
IND-CPA安全是公钥加密的基本要求。还存在更强的安全概念,如IND-CCA(选择密文攻击安全),其中敌手除了获得密文外,还可以访问一个解密预言机(不能解密挑战密文本身)。这模拟了敌手可能诱骗接收者解密某些特定密文以获取信息的场景。ElGamal方案不具有CCA安全性,但可以通过其他技术进行转换。
本节课中我们一起学习了:
- 利用DDH假设构建了Diffie-Hellman密钥交换协议,实现了双方通过公开信道协商秘密密钥。
- 理解了该协议无法抵抗中间人攻击。
- 学习了公钥加密的形式化定义及其关键性质(必须随机化、单消息安全蕴含多消息安全)。
- 基于DDH假设和Diffie-Hellman协议的思想,构建并证明了ElGamal公钥加密方案的IND-CPA安全性。
这些构造展示了如何将数论中的计算困难性问题转化为实用的密码学协议。
009:RSA与作业讨论

概述
在本节课中,我们将继续学习公钥加密。上一节我们介绍了ElGamal加密,本节我们将学习RSA函数。请注意,RSA通常被认为是一种加密方案,但教科书中的RSA本身并不是一个直接的加密方案。我们将探讨其原因,并学习如何将其转化为安全的加密方案。此外,我们还将选取一些作业问题进行讲解。
IND-CPA安全定义回顾
首先,让我们回顾一下加密的IND安全定义,即IND-CPA(选择明文攻击)。定义如下:给定一个公钥加密方案,攻击者无法区分公钥 Pk 下对消息 M0 的加密和对消息 M1 的加密。其中,(Pk, Sk) 来自密钥生成算法。这必须对每一对消息 M0 和 M1 都成立。
另一个需要牢记的重要事实是,任何好的公钥加密方案都必须是随机的。这是为了避免一个非常简单的攻击:如果加密是确定性的,攻击者可以自行加密消息,然后与密文进行匹配,如果匹配成功,就能恢复出消息。
RSA函数与RSA假设
RSA实际上是第一个被提出的公钥加密方案,由Rivest、Shamir和Adleman于1978年提出,这比Diffie和Hellman的开创性论文晚了大约两年。但RSA并不是最简单的公钥加密方案。最简单的方案,至少在我看来,是ElGamal,它非常直接,我们在上一节课已经学习过。
当然,Rivest、Shamir和Adleman因提出RSA而获得了图灵奖。RSA现在基本上在互联网上无处不在。
在讨论RSA加密之前,让我们先看看RSA函数。将RSA视为一个函数而不是直接的加密方案会更有帮助。
RSA函数的构造
以下是RSA函数的构造步骤:
- 选择两个大素数:选择两个大素数
P和Q,且P ≠ Q。你可以决定它们的长度,这通常由安全参数决定。例如,可以选择512位的不同素数。 - 计算RSA模数:计算
n = P * Q,这被称为RSA模数。 - 理解群结构:
Z_n*是模n的乘法群,包含所有从1到n-1且与n互质的正整数。该群的阶是φ(n),即欧拉函数,其值为(P-1) * (Q-1)。 - 选择指数:选择一个指数
e,它来自Z_{φ(n)}*,或者说,e是从1到φ(n)-1且与φ(n)互质的一个数。这是因为e是指数,在指数运算中,模n会变为模φ(n)。
RSA函数的定义
RSA函数定义为:
F_{n,e}(x) = x^e mod n
其中,x 是 Z_n* 中的一个元素。
为了从 x^e mod n 恢复 x,我们需要计算 e 次方根。这可以通过计算一个数 d 来实现,使得:
e * d ≡ 1 (mod φ(n))
这个 d 存在,因为 e 与 φ(n) 互质,可以使用扩展欧几里得算法计算。
现在,给定 d,我们可以计算:
(x^e)^d mod n = x^{e*d} mod n = x^{e*d mod φ(n)} mod n = x^1 mod n = x
因此,我们恢复了原始的 x。
这里有一个重要的观察:为了从 e 计算 d,你需要知道群的阶 φ(n)。而计算 φ(n) 实际上需要 n 的因式分解。因此,e 可以看作是允许你加密消息 x 的公钥,而 d 可以看作是允许你从密文 x^e mod n 中恢复 x 的私钥。
然而,我们必须在这里停下来。这是因为RSA函数是确定性的,这意味着它不能直接作为一个安全的加密方案。因此,RSA被称为一个陷门单向置换,可以通过引入随机性转化为加密方案。
RSA假设的形式化

RSA假设基本上是说,给定 x^e mod n,你无法计算 x,除非你有 n 的因子。更形式化地说,对于任何PPT(概率多项式时间)敌手 A,给定 (e, n) 和一个随机 y ∈ Z_n*,敌手输出 x 使得 x^e ≡ y (mod n) 的概率是可忽略的。
这本质上表明RSA提供了一个单向函数。但更有趣的是,RSA函数也是一个置换。这是因为其定义域和值域都是 Z_n*,并且对于每个输出,都存在唯一的输入(如果你有 d 就可以恢复它)。此外,RSA还带有一个陷门 d,允许你求逆。因此,RSA是一个陷门单向置换。
陷门单向置换的定义
陷门单向置换是一个函数族 F,满足以下条件:
- 函数采样器:存在一个PPT算法,输出函数描述
I、函数f_I和陷门t。 - 输入采样器:可以从定义域中随机采样。
- 高效计算:对于所有
I和x,f_I(x)可以在多项式时间内计算。 - 无陷门时难以求逆:对于任何PPT敌手
A,给定(I, f_I)和y,A输出x'使得f_I(x') = y的概率是可忽略的。 - 有陷门时易于求逆:存在一个多项式时间算法,给定
(I, f_I)、陷门t和y,输出x使得f_I(x) = y。 - 置换性质:对于所有
I,f_I是一个置换。
基于RSA假设,RSA实际上是一个陷门单向置换。
从陷门单向置换到公钥加密
现在,我们来看看如何将任何陷门单向置换转化为一个安全的公钥加密方案。这使用了我们之前见过的旧思想:我们曾使用单向置换来构造安全的对称密钥加密方案,这使用了硬核谓词,我们在这里也将这样做。
公钥加密方案包含三个算法:密钥生成、加密和解密。
以下是构造方法:
- 密钥生成:运行陷门单向置换的函数采样器,得到
(I, f_I, t)。公钥Pk是(I, f_I),私钥Sk是陷门t。 - 加密(单比特消息):
- 使用输入采样器从定义域中随机采样
r。 - 计算
c1 = f_I(r)。 - 计算
c2 = m ⊕ h(r),其中h是f_I的硬核谓词,m是单比特消息。 - 输出密文
(c1, c2)。
- 使用输入采样器从定义域中随机采样
- 解密:
- 使用私钥(陷门
t)从c1中恢复r。 - 计算硬核谓词
h(r)。 - 恢复消息
m = c2 ⊕ h(r)。
- 使用私钥(陷门
要加密更长的消息,只需逐比特加密,对每个比特使用不同的随机值 r。
安全性证明思路

安全性证明基本上遵循以下思路:由于 f_I 在没有陷门时是单向函数,其硬核谓词的输出看起来是均匀的。我们可以通过一系列混合论证来证明该方案是IND-CPA安全的:
- 混合0:真实的加密
(f_I(r), m ⊕ h(r))。 - 混合1:将硬核谓词
h(r)替换为一个均匀随机比特b,得到(f_I(r), m ⊕ b)。由于h是硬核谓词,混合0和混合1在计算上不可区分。 - 混合2:将
m ⊕ b替换为m' ⊕ b,其中m'是另一个消息比特。由于b是均匀随机的,这就像一次一密,混合1和混合2在统计上不可区分。 - 混合3:将均匀随机比特
b改回h(r),得到(f_I(r), m' ⊕ h(r))。这相当于加密消息m',并且混合2和混合3在计算上不可区分。
因此,敌手无法区分对 m 的加密和对 m' 的加密。
需要指出的是,这个方案在实践中并不常用,因为它需要将消息逐比特加密,效率很低。对于每个消息比特,密文都包含一个群元素(例如 f_I(r)),导致密文膨胀。存在更实用的方案。
作业问题讨论
在剩余的时间里,我们将讨论一些作业中的问题。
问题9:PRG的组合
给定两个伪随机生成器(PRG)G1 和 G2,它们都将 n 比特种子扩展为 2n 比特输出。定义一个新的函数 G(s) = G1(s) || G2(s),它将 n 比特种子扩展为 4n 比特输出。问题是:G 也是一个PRG吗?
答案是否定的。一个简单的反例是:如果 G1 = G2,那么 G1 和 G2 各自仍然是PRG,但 G 的输出中,前一半和后一半总是相同的。这与均匀的 4n 比特字符串非常不同,很容易被区分。
此外,我们需要看看标准的混合论证在这里为何失效。假设我们尝试用混合论证来证明:首先用均匀字符串替换 G1(s),然后用均匀字符串替换 G2(s)。在第一步中,我们需要证明 (G1(s), G2(s)) 与 (U_{2n}, G2(s)) 在计算上不可区分。这需要依赖于 G1 的安全性,而 G1 的安全性要求其种子 s 是均匀的。然而,在敌手的视角中,给定 G2(s),s 是否仍然看起来均匀?我们无法证明这一点。PRG的安全性依赖于种子的均匀性,但这里种子的部分信息可能通过 G2(s) 泄露。因此,混合论证在此处断裂。
问题5:单向置换的复合
给定一个单向置换 G,证明 G 与自身的复合 G∘G(即 G(G(x)))也是一个单向置换。

证明分为两部分:证明它是单向的,以及证明它是一个置换。
单向性:已知对于均匀随机的 x,给定 G(x) 计算 x 是困难的。我们需要证明,对于均匀随机的 x',给定 G(G(x')) 计算 x' 也是困难的。首先,注意到由于 G 是置换,当 x' 均匀随机时,x = G(x') 也是均匀随机的。因此,问题转化为:给定 G(x)(其中 x 均匀随机),计算 x' 使得 G(x') = x。如果存在敌手能以不可忽略的概率计算 x',那么由于 G 是多项式时间可计算的,该敌手也能计算 G(x') = x,这与 G 是单向置换矛盾。
置换性质:由于 G 是置换,其复合显然也是置换。
问题6:具有硬核谓词的单射函数是单向的
给定一个单射函数 F,它有一个硬核谓词 h。目标是证明 F 也是一个强单向函数。
证明思路:假设存在PPT敌手 A 能够以不可忽略的概率求逆 F。那么我们可以构造一个敌手 B 来预测硬核谓词 h(x)。B 的工作方式如下:给定 y = F(x),运行 A(y)。如果 A 成功输出 x,则 B 计算并输出 h(x)(此时总是正确)。如果 A 失败,则 B 随机输出一个比特。由于 A 以不可忽略的概率成功,B 整体上能以略高于1/2的概率正确预测硬核谓词,这与 h 是硬核谓词(即难以预测)矛盾。因此,这样的 A 不可能存在,F 必须是单向的。
总结
本节课我们一起学习了RSA函数及其作为陷门单向置换的形式化定义。我们探讨了RSA假设,并理解了为什么教科书RSA本身不是一个安全的加密方案。接着,我们学习了一个通用的编译器,可以将任何陷门单向置换转化为IND-CPA安全的公钥加密方案,其核心思想是使用硬核谓词和一次性密码本。最后,我们讨论了几个作业问题,深入理解了伪随机生成器组合的陷阱、单向置换复合的性质,以及具有硬核谓词的单射函数必然是单向的。这些概念和证明技巧对于构建和理解密码学方案至关重要。
010:消息认证码与抗碰撞哈希函数

在本节课中,我们将要学习密码学的两个核心概念:消息认证码和抗碰撞哈希函数。我们将首先探讨为什么仅靠加密不足以保证消息的完整性,然后学习如何通过消息认证码来防止消息被篡改。最后,我们将深入研究抗碰撞哈希函数的定义、性质以及如何基于离散对数问题来构造它。
加密方案的局限性
上一节我们介绍了加密方案,例如ElGamal和RSA。许多人认为加密后的消息就像一个黑盒,无法对其进行任何有意义的操作。然而,事实并非如此。即使攻击者没有解密消息所需的密钥,他们仍然可以对加密方案执行一些非常巧妙且可能成功的操作,这被称为篡改攻击。
让我们从一个一次性密码本的例子开始。我们通常认为一次性密码本是一种安全的加密方案,但存在一些意想不到的操作。
例如,假设Alice和Bob共享一个秘密密钥K,并使用一次性密码本。攻击者Trudy可以拦截并篡改密文。如果Alice的原始指令是“卖出苹果股票”,Trudy希望将其改为“买入苹果股票”。在一次性密码本中,密文C是明文M与密钥K的异或结果:C = M XOR K。Trudy可以计算一个新的密文C' = C XOR (“卖出苹果” XOR “买入苹果”)。如果C是“卖出苹果”的加密结果,那么C'将是“买入苹果”的加密结果,反之亦然。Trudy可以在完全不知道密钥K的情况下翻转消息。
ElGamal加密方案也存在类似的篡改问题。在ElGamal中,密文由两部分组成:(g^r, m * h^r),其中h = g^a是公钥。攻击者Trudy可以构造一个新的密文(g^r, 2 * m * h^r),从而将出价m翻倍,即使她无法解密出原始消息m。
RSA等其他加密方案也存在类似攻击。防止这类攻击并不容易。
因此,我们需要设计一种解决方案来防止对密文或消息的任何篡改。密码学有两个高级目标:保护机密性和保护完整性。我们通常将这两者分开处理。我们已经学习了加密(机密性),现在让我们单独学习完整性。如果你需要两者,只需同时使用这两种原语。
消息认证码
可能最简单的用于处理完整性的密码学原语是消息认证码。在下一节课中,我们还将学习数字签名。
消息认证码本质上是一种数学函数。在这里,Alice和Bob共享一个秘密密钥K(这与数字签名不同)。他们希望通信的消息不能被篡改。如果攻击者Trudy在信道上篡改了消息,接收方可以检测并拒绝该消息。
MAC的定义
任何MAC方案都由三个概率多项式时间算法组成:
- 密钥生成算法:输入安全参数
1^n,输出共享密钥k。 - MAC算法:输入密钥
k和消息m,输出认证标签σ。 - 验证算法:输入密钥
k、消息m和标签σ,输出一个比特b(0表示拒绝,1表示接受)。
需要满足两个属性:
- 正确性:如果没有篡改,验证算法必须总是接受。对于所有消息
m,如果k由密钥生成算法生成,σ是MAC算法的有效输出,那么验证算法输出1的概率为1。 - 安全性(不可伪造性):直观地说,即使攻击者能够看到多个不同消息(甚至可能是攻击者选择的)的MAC标签,攻击者也不应该能够为一个新消息生成有效的MAC标签。
我们通过一个“伪造游戏”来形式化定义安全性:
- 挑战者C使用密钥生成算法生成密钥
k。 - 学习阶段:敌手A可以多次向C发送消息
m_i。C用σ_i = MAC(k, m_i)进行响应。 - 猜测阶段:敌手A输出一对
(m, σ)。 - 如果对于所有
i,都有m ≠ m_i,并且验证算法Verify(k, m, σ) = 1,则敌手A获胜。
我们说一个MAC方案是安全的,如果对于所有概率多项式时间的敌手A,其在这个游戏中获胜的概率都是可忽略的。
从伪随机函数构造MAC
一个非常简单的构造是使用伪随机函数。伪随机函数F有一个秘密密钥k,可以在任何输入x上计算输出F(k, x)。其关键属性是,即使给定F在多个不同消息上的输出,F在一个新消息上的输出对于敌手来说也与随机串不可区分。
基于PRF构造MAC的方案如下:
- 密钥生成:运行PRF的密钥生成算法,输出密钥
k。 - MAC算法:
σ = F(k, m)。 - 验证算法:如果
σ = F(k, m),则输出1;否则输出0。
正确性是直接的。不可伪造性的证明几乎直接源于PRF的定义。思路是:如果存在一个PPT敌手A能以不可忽略的概率赢得MAC伪造游戏,那么我们可以构造一个敌手B来区分PRF和真正的随机函数,这与PRF的安全性假设相矛盾。

重放攻击
即使使用MAC,仍然可能存在重放攻击。例如,Alice每天向Bob发送命令“进攻”或“防守”。MAC保证攻击者无法将“进攻”改为“防守”,但攻击者可以简单地重放之前有效的“进攻”消息及其MAC标签。
解决方案是在消息中包含一个不会重复的值,例如日期、时间戳或递增的序列号。这样,即使消息内容相同,包含时间戳的完整消息也不同,旧消息的MAC标签对新消息无效。
抗碰撞哈希函数
抗碰撞哈希函数与MAC和数字签名的研究高度相关,因为它们有时可用于压缩消息,然后你只需要对较小的消息进行MAC或签名。我们将在下一节课中更详细地看到这一点。
哈希函数回顾
我们可能都见过某种哈希函数,例如通用哈希函数。通用哈希函数族H满足:对于任意两个不同的输入x和y,随机选择的哈希函数h满足h(x) = h(y)的概率小于等于1 / |范围|。这个属性只在哈希函数是随机选择之后才成立。如果给定了具体的h,可能很容易找到碰撞。
抗碰撞哈希函数族的定义
一个抗碰撞哈希函数族H包含许多函数h_i,每个函数将域D_i映射到范围R_i。通常,所有函数的域和范围相同。它是一个CRHF族,如果满足以下四个属性:
- 易采样:存在一个PPT算法,可以采样得到
h_i(以及索引i)。 - 高效计算:对于所有
x在域D_i中,h_i(x)可在多项式时间内计算。 - 压缩性:对于所有
i,域的大小大于范围的大小(|D_i| > |R_i|)。 - 抗碰撞性:对于所有PPT敌手A,给定随机选择的哈希函数
h_i,A能够输出一对(x, y)使得x ≠ y且h_i(x) = h_i(y)的概率是可忽略的。

关于定义的一些事实:
- 为什么需要“族”? 不能只使用一个固定的哈希函数。因为根据鸽巢原理,碰撞必然存在。敌手A可以简单地将一个特定的碰撞对
(x, y)硬编码在其代码中,这个A仍然是PPT的,并且总能输出碰撞。因此,我们需要从一族函数中随机选择一个来使用,使得敌手无法预先知道所有碰撞。 - CRHF与单向函数的关系:充分压缩的CRHF也是单向函数。直观证明:如果一个函数不是单向的(容易求逆),那么给定输出
y = h(x),求逆算法可能返回一个不同的原像x'(x' ≠ x),这就产生了一个碰撞(x, x')。由于压缩性,许多输出有多个原像,因此返回不同原像的概率不可忽略。

基于离散对数假设构造CRHF
我们基于离散对数假设来构造CRHF。该构造大致将输入压缩到原来的一半。
设G是一个阶为素数q的循环群,生成元为g。哈希函数族定义如下:
- 采样:随机选择
h = g^r ∈ G,其中r从Z_q中随机选取。哈希函数就是h。 - 计算:输入是
(x0, x1),其中x0, x1 ∈ Z_q。输出为h(x0, x1) = g^{x0} * h^{x1} ∈ G。
分析:
- 输入大小:
2 * log(q)比特(两个Z_q元素)。 - 输出大小:
log(|G|) ≈ log(q)比特(一个群元素)。 - 压缩因子约为2。
- 前三个属性显然满足。
抗碰撞性证明(思路):
假设存在PPT敌手A,在给定随机h后能以不可忽略的概率找到碰撞(x0, x1) ≠ (x0‘, x1’)使得g^{x0} * h^{x1} = g^{x0’} * h^{x1’}。
代入h = g^r,得到g^{x0 + r*x1} = g^{x0’ + r*x1’},这意味着在模q下:
x0 + r*x1 ≡ x0’ + r*x1’ (mod q)
整理得:r*(x1 - x1’) ≡ x0’ - x0 (mod q)
如果x1 ≠ x1’,那么我们可以解出r = (x0’ - x0) * (x1 - x1’)^{-1} mod q。这就意味着敌手A帮助我们计算了h = g^r的离散对数r,与离散对数假设矛盾。
因此,只需证明在碰撞中必有x1 ≠ x1’。用反证法:如果x1 = x1’,那么根据上面的等式,x0 ≡ x0’ (mod q),这意味着两个输入完全相同,与“碰撞”的定义(x0, x1) ≠ (x0‘, x1’)矛盾。因此,x1必须不等于x1’。
开放性问题: 能否从任意单向函数通用地构造出抗碰撞哈希函数?这是一个数十年来未解决的开放性问题,人们普遍认为答案是否定的,构造CRHF需要更强的假设。
总结
本节课中我们一起学习了:
- 加密的局限性:加密只能保证机密性,不能防止篡改攻击(如一次性密码本和ElGamal中的攻击)。
- 消息认证码:用于保证消息完整性和真实性的密码学原语。我们学习了其形式化定义、安全属性(不可伪造性),以及如何从伪随机函数简单构造MAC。同时,我们也注意到了重放攻击及其防范措施(如添加时间戳)。
- 抗碰撞哈希函数:一种压缩的、难以找到碰撞的哈希函数族。我们学习了其严格的定义,理解了为什么需要“函数族”的概念,以及它与单向函数的关系。最后,我们基于离散对数假设,学习了一个具体的CRHF构造。
下一节课,我们将学习数字签名,并看到抗碰撞哈希函数在其中的一个漂亮应用。
011:数字签名

在本节课中,我们将要学习数字签名。数字签名是公钥密码学的重要组成部分,它允许持有私钥的人对消息进行签名,而任何拥有对应公钥的人都可以验证签名的有效性。这与需要共享密钥的消息认证码不同,使其成为互联网安全(如HTTPS协议)的基石。
上一节我们介绍了消息认证码,本节中我们来看看数字签名如何解决在没有共享密钥情况下的消息认证问题。
数字签名定义
数字签名方案包含三个概率多项式时间算法。
- 密钥生成算法:生成一个私钥
SK和一个公钥PK。公钥可以公开。 - 签名算法:输入私钥
SK和消息M,输出一个数字签名σ。 - 验证算法:输入公钥
PK、消息M和签名σ,输出1(接受)或0(拒绝)。
该方案必须满足两个属性:
- 正确性:如果密钥生成、签名和验证都正确执行,验证算法总是接受。用公式表示:
Pr[Verify(PK, M, Sign(SK, M)) = 1] = 1 - 安全性(不可伪造性):即使攻击者可以获得许多消息的合法签名,他也无法伪造一个新消息的有效签名。
安全模型(不可伪造性)
安全模型通过挑战者 C 和攻击者 A 之间的游戏来定义。
- 初始化:挑战者运行密钥生成算法,得到
(SK, PK)。将PK发送给攻击者A,自己保留SK。 - 学习阶段:攻击者
A可以适应性地选择一系列消息M1, M2, ..., Mq,并向挑战者请求这些消息的签名。挑战者用私钥SK计算签名σi = Sign(SK, Mi)并返回给A。 - 伪造阶段:最终,攻击者
A输出一个伪造的(M*, σ*)。如果满足以下两个条件,则攻击者获胜:M*从未在查询阶段被请求过(即M* ∉ {M1, ..., Mq})。- 验证算法接受:
Verify(PK, M*, σ*) = 1。
一个安全的数字签名方案要求,对于任何概率多项式时间的攻击者 A,其在这个游戏中获胜的概率是可忽略的。
一次性签名
我们首先考虑一个较弱的概念:一次性签名。在这种方案中,攻击者只能进行一次签名查询(即 q = 1)。虽然比完全的数字签名弱,但一次性签名更容易构造,并且是构建完全签名方案的基础。
Lamport一次性签名方案
该方案仅使用一个单向函数 f 来构造。假设消息长度为 n 比特。
- 密钥生成:
- 私钥
SK:选择2n个随机的字符串。即,对于i = 1 to n,随机选择x_{0,i}和x_{1,i}。 - 公钥
PK:计算所有字符串在单向函数f下的像。即,对于i = 1 to n,计算y_{0,i} = f(x_{0,i})和y_{1,i} = f(x_{1,i})。公钥是所有y_{b,i}的集合。
- 私钥
- 签名:要对一个
n比特消息M = (m1, m2, ..., mn)签名,签名σ由n个原像组成:σ = (x_{m1,1}, x_{m2,2}, ..., x_{mn,n})。简单来说,消息的第i个比特mi决定了从第i对原像(x_{0,i}, x_{1,i})中选择哪一个。 - 验证:给定消息
M和签名σ,验证者检查对于每个i,是否有f(σ[i]) == y_{mi,i}。如果所有n个检查都通过,则接受签名。
安全性直觉:攻击者只获得了一个消息 M 的签名,即一组原像 {x_{mi,i}}。为了伪造一个新消息 M‘ 的签名,M’ 至少有一个比特位置 j 与 M 不同。这意味着攻击者需要输出原像 x_{1-mj, j},但他只知道其像 y_{1-mj, j} = f(x_{1-mj, j})。因此,伪造签名等价于反转单向函数 f,这在计算上是不可行的。
该方案的明显缺点是密钥和签名长度与消息长度线性相关,且只能使用一次。

扩展方案能力
以下是克服Lamport方案局限性的两种关键技术。
1. 签署长消息:哈希然后签名范式
为了签署任意长度的消息,我们引入一个抗碰撞的哈希函数族 H。
- 密钥生成:除了签名方案的密钥对,公钥中还包含一个从
H中随机选出的哈希函数h。 - 签名:要签署消息
M,先计算哈希值h(M),然后使用底层的一次性签名方案对这个哈希值进行签名。即σ = Sign_SK(h(M))。 - 验证:计算
h(M),然后使用验证算法检查σ是否是h(M)的有效签名。
安全性:如果攻击者能伪造一个新消息 M* 的签名,有两种情况:
h(M*) = h(Mi)对于某个已查询过的消息Mi:这意味着攻击者找到了哈希函数的一个碰撞,与抗碰撞性矛盾。h(M*) ≠ h(Mi)对所有i成立:那么攻击者就伪造了哈希值h(M*)的签名,这与底层一次性签名方案的安全性矛盾。
2. 签署多条消息:从链到树
为了签署多条消息,我们可以使用状态性签名方案。核心思想是建立一个密钥链或密钥树。

链式方法(基本思想):
- 首先生成一个一次性签名密钥对
(PK0, SK0)作为根。 - 要签署第一条消息
M1,生成一个新的密钥对(PK1, SK1)。然后用SK0签署“M1拼接PK1”。签名包含这个签名以及PK1。 - 要签署第二条消息
M2,再生成(PK2, SK2),用SK1签署“M2拼接PK2”。此时的签名需要包含从根到当前的所有签名和公钥。 - 验证时,验证者从根公钥
PK0开始,验证第一个签名以获取并信任PK1,再用PK1验证第二个签名,如此递推。
这种方法的问题是签名长度随着消息数量线性增长。
树形方法(改进):
使用二叉树而非单链。每个节点可以签署两个子节点的公钥。要签署一条消息,将其关联到某个叶子节点,并用从根到该叶子路径上的私钥依次签名。验证时只需要提供路径上的签名和兄弟节点的公钥。这样,签名长度仅与树的高度(即消息数量的对数)成正比,效率大幅提升。
基于RSA的数字签名

在实践中,我们使用像RSA这样基于特定数论难题的高效方案。教科书式RSA签名方案是:
- 密钥生成:与RSA加密相同,生成大素数
p, q,计算N = p*q,选择e满足gcd(e, φ(N)) = 1,计算d使得e*d ≡ 1 mod φ(N)。公钥为(N, e),私钥为d。 - 签名:对消息
M ∈ Z_N*,计算σ = M^d mod N。 - 验证:检查
M ≡ σ^e mod N是否成立。
安全问题:教科书式RSA签名是同态的。如果 σ1 是 M1 的签名,σ2 是 M2 的签名,那么 σ1 * σ2 mod N 就是 M1 * M2 mod N 的有效签名。这使得攻击者可以轻易伪造新消息的签名,即使只见过一个签名(例如,通过计算 σ^2 来伪造 M^2 的签名)。
修复方案:哈希然后签名:
与之前类似,在签名前先对消息进行哈希。即 σ = (H(M))^d mod N,验证时检查 H(M) ≡ σ^e mod N。这里要求哈希函数 H 将消息映射到 Z_N*。
为了证明其安全性,通常需要假设哈希函数 H 是一个随机预言机。这意味着 H 被建模为一个完全随机的函数,攻击者只能通过查询来获取其输出值。在这个强假设下,由于 H(M1) * H(M2) = H(M3) 的概率极低,前述的同态攻击就不再有效。在实际中,我们使用像SHA-256这样的加密哈希函数并假设其行为接近随机预言机。
总结
本节课中我们一起学习了数字签名的核心概念。我们从形式化定义和安全模型开始,介绍了较弱但易构造的一次性签名方案(Lamport方案)。为了使其实用化,我们探讨了使用抗碰撞哈希函数来签署任意长度消息的“哈希然后签名”范式,以及通过构建密钥链或密钥树来实现签署多条消息的状态性方案。最后,我们分析了基于RSA的高效签名方案及其安全问题,并指出通过结合哈希函数(在随机预言机模型下)可以构建安全的实用签名方案。数字签名是构建安全网络通信不可或缺的密码学原语。
012:秘密共享

在本节课中,我们将学习一个非常有趣且重要的密码学基础概念——秘密共享方案。我们将探讨如何将一个秘密安全地分割成多份,并分发给不同的参与者,确保只有满足特定条件的参与者集合才能恢复原始秘密,而任何不满足条件的集合都无法获得关于秘密的任何信息。
引言
假设你拥有大量敏感数据,并希望将其安全地存储在云端。一种选择是加密数据后存储,但随之而来的问题是:加密密钥本身如何安全存储?另一种方案是,你可以将数据分割成多个部分,并分别存储在不同的云服务提供商处。关键在于,如何分割数据才能确保单个云服务商无法获取关于原始数据的任何信息?这就是秘密共享方案要解决的问题。
N取N秘密共享
首先,我们来看一种较简单的情况:N取N秘密共享。在这种方案中,秘密被分割成N份,只有当所有N份都聚齐时,才能恢复出原始秘密。
定义
一个N取N秘密共享方案包含两个算法:
- 分享算法:
Share(s) -> (s1, s2, ..., sN)。该算法以秘密s为输入,输出N个份额。 - 重构算法:
Reconstruct(s1, s2, ..., sN) -> s。该算法以所有N个份额为输入,输出原始秘密s。
该方案必须满足两个要求:
- 正确性:如果诚实地执行分享和重构算法,总能恢复出原始秘密。即对于所有秘密
s,Pr[ Reconstruct(Share(s)) = s ] = 1。 - 安全性:任何少于N个份额的集合,都无法提供关于原始秘密的任何信息。更正式地说,对于任意两个不同的秘密
s和s',以及任意大小为N-1的份额索引集合I,由秘密s生成的份额子集{si | i ∈ I}的分布,与由秘密s'生成的份额子集{s‘i | i ∈ I}的分布,是完全相同的。
构造方法
构造一个N取N秘密共享方案非常简单,其核心思想是利用异或运算。
分享算法 Share(s):
- 随机均匀地选取前
N-1个份额:s1, s2, ..., s(N-1) ← {0,1}^{|s|}。 - 计算第N个份额:
sN = s ⊕ s1 ⊕ s2 ⊕ ... ⊕ s(N-1)。 - 输出所有份额
(s1, s2, ..., sN)。
重构算法 Reconstruct(s1, s2, ..., sN):
- 计算所有份额的异或:
s = s1 ⊕ s2 ⊕ ... ⊕ sN。 - 输出
s。

安全性证明
我们需要证明,任何N-1个份额的集合看起来都像是均匀随机字符串,与原始秘密无关。
情况一:攻击者拥有前N-1个份额 (s1, ..., s(N-1))。根据算法,这些份额是独立均匀随机选取的,因此其分布就是N-1个均匀随机字符串的联合分布,与秘密s无关。
情况二:攻击者拥有的N-1个份额中包含第N个份额sN,但缺少了某个其他份额(例如第j个份额)。此时,攻击者看到的是sN和除了sj之外的其他N-2个份额。我们可以将条件“sN等于某个特定值”等价地转化为条件“sj等于由秘密和其他已知份额计算出的某个特定值”。由于sj是独立均匀随机选取的,这个条件成立的概率是1/2^{|s|}。结合其他N-2个独立均匀的份额,整个N-1元组的概率仍然是1/2^{|s|*(N-1)},即均匀分布。
因此,无论攻击者拥有哪N-1个份额,其分布都与均匀分布相同,从而与秘密无关。这个构造是信息论安全的,不依赖于任何计算复杂性假设。

上一节我们介绍了简单但有效的N取N秘密共享。然而,它有一个明显的缺点:所有份额的大小都和原始秘密一样大,存储开销大。此外,它要求所有份额都必须到场才能恢复秘密,缺乏灵活性。本节中,我们将探讨一个更强大的概念——门限秘密共享。
门限秘密共享
门限秘密共享,或称T取N秘密共享,允许我们在N个参与者中分享一个秘密,并设定一个门限值T(1 ≤ T ≤ N)。只要任意T个或更多的参与者汇集他们的份额,就能恢复秘密;而任何少于T个份额的集合,则完全无法获得关于秘密的信息。
定义
一个T取N秘密共享方案包含:
- 分享算法:
Share(s) -> (s1, s2, ..., sN)。输出N个份额。 - 重构算法:
Reconstruct({si | i ∈ I}) -> s or ⊥。输入一个份额集合I。如果|I| ≥ T,则输出秘密s;否则输出失败符号⊥。
要求:
- 正确性:对于任意满足
|I| ≥ T的集合I,Pr[ Reconstruct({si | i ∈ I}) = s ] = 1。 - 安全性:对于任意满足
|I| < T的集合I,由秘密s生成的份额子集{si | i ∈ I}的分布,与由另一个秘密s'生成的对应份额子集的分布相同。
沙米尔秘密共享方案
沙米尔(Shamir)提出了一种优雅的门限秘密共享方案,其安全性基于多项式插值的数学性质。
在介绍方案之前,我们先回顾关于多项式的几个关键性质。我们将在有限域(例如以一个大素数p为模的整数域)上操作。
- 唯一确定:一个
d次多项式由d+1个点唯一确定。 - 插值:给定一个
d次多项式上的d+1个点(x1, y1), ..., (xd+1, yd+1),可以通过拉格朗日插值法计算出该多项式在任何其他x坐标处的值y。 - 信息缺失:如果只给定一个
d次多项式上的d个点,那么对于第d+1个点(例如x=0)的值y,所有可能的值(在有限域中)出现的可能性是均等的,即我们无法获得关于这个缺失值的任何信息。
沙米尔方案的核心思想是:用多项式来“隐藏”秘密。
分享算法 Share(s):
- 设定门限
T,令多项式次数d = T - 1。 - 将秘密
s编码为有限域中的一个元素。令常数项a0 = s。 - 随机均匀地选取有限域中的其他
d个系数:a1, a2, ..., ad。这样就定义了一个随机d次多项式:f(x) = a0 + a1*x + a2*x^2 + ... + ad*x^d。 - 为第
i个参与者(i=1,...,N)生成份额:si = (i, f(i))。即份额包含参与者的公开索引i和多项式在该点的取值。
重构算法 Reconstruct({(i, f(i)) | i ∈ I}):
- 检查输入集合
I的大小。如果|I| < T,输出⊥。 - 如果
|I| ≥ T,则利用集合中任意T个点,通过拉格朗日插值法重构出唯一的d次多项式f(x)。 - 计算
s = f(0),即恢复出的常数项,也就是原始秘密。 - 输出
s。
安全性分析
方案的安全性直接依赖于上述多项式性质3(信息缺失)。攻击者如果拥有少于T个份额,即少于d+1个点,那么对于多项式在x=0处的值(即秘密s),所有可能的值仍然是等可能的。因此,这些份额不泄露关于秘密的任何信息。其证明思路与N取N方案类似,可以通过计算概率或利用多项式随机性的论证来完成。
门限秘密共享不仅是一个理论概念,它还是构建更复杂密码学协议的基础组件。本节中,我们来看看如何将秘密共享与公钥加密结合,实现门限公钥加密。
门限公钥加密
在标准公钥加密中,一个发送者用接收者的公钥加密消息,只有拥有对应私钥的接收者才能解密。门限公钥加密将解密能力分散到多个参与者身上。例如,一个加密文件可能要求一个委员会中至少T名成员合作才能解密。
定义
一个T取N门限公钥加密方案包含以下算法:
- 密钥生成:
KeyGen() -> (PK, SK1, SK2, ..., SKN)。生成一个公钥PK和N个私钥份额SKi,分发给N个解密者。 - 加密:
Encrypt(PK, m) -> CT。使用公钥加密消息m,得到密文CT。 - 部分解密:
PartialDecrypt(CT, SKi) -> CT_i。第i个解密者使用自己的私钥份额对密文进行部分解密,得到一个部分解密结果CT_i。 - 恢复:
Recover({CT_i | i ∈ I}) -> m or ⊥。如果提供的部分解密结果集合I的大小|I| ≥ T,则能恢复出原始消息m;否则失败。
要求包括正确性(诚实的T方部分解密能恢复消息)和安全性(即使拥有T-1个私钥份额和密文,也无法区分对两个不同消息的加密)。
一个简单构造
我们可以结合一个标准公钥加密方案(如ElGamal、RSA)和沙米尔秘密共享来构造门限公钥加密。
密钥生成:
- 运行标准公钥加密方案的密钥生成算法
N次,得到N个独立的公私钥对:(PK1, SK1), ..., (PKN, SKN)。 - 系统公钥为所有公钥的串联:
PK = (PK1, PK2, ..., PKN)。 - 第
i个解密者的私钥份额就是SKi。
加密消息m:
- 使用沙米尔T取N秘密共享方案,将消息
m分割成N个份额:(s1, s2, ..., sN) = Share(m)。 - 用第
i个公钥加密第i个份额:CT_i = Encrypt(PKi, si)。 - 最终密文为所有加密份额的集合:
CT = (CT_1, CT_2, ..., CT_N)。
部分解密:
第i个解密者收到密文CT后,解密属于自己的部分:s_i = Decrypt(SKi, CT_i),并将s_i作为部分解密结果CT_i公布。
恢复消息:
收集到至少T个部分解密结果(即份额s_i)后,运行沙米尔秘密共享的重构算法Reconstruct,即可恢复出原始消息m。
这个构造简单直观,但其缺点是密文和公钥尺寸都扩大了N倍,效率较低。存在更高效的构造(例如基于ElGamal的特定门限变体),其公钥尺寸不变,仅私钥被秘密共享,但原理更为复杂。

总结
本节课中我们一起学习了秘密共享这一密码学核心概念。
- 我们从N取N秘密共享开始,学习了如何使用简单的异或操作来安全地分割和重构秘密,并理解了其信息论安全性的证明。
- 接着,我们探讨了更通用的门限秘密共享,重点介绍了沙米尔秘密共享方案。该方案利用多项式插值的数学性质,优雅地实现了“T取N”的门限访问控制。
- 最后,我们看到了秘密共享的应用之一——门限公钥加密。通过将秘密共享与标准加密方案结合,可以实现解密权力的分散化,增强系统的安全性和鲁棒性。
秘密共享是构建分布式系统、安全多方计算、区块链和数字资产管理等众多高级密码学协议不可或缺的基石。
013:期中与作业问题解析

在本节课中,我们将回顾课程期中考试以及部分作业中的问题。课程已过半,我们将从下周开始进入第二部分,重点探讨如何将基础密码学原语应用于更复杂的系统,如区块链和零知识证明。本节课旨在澄清一些常见错误,并深入解析几个关键问题。
期中考试问题解析
上一节我们概述了课程结构,本节中我们来看看期中考试的具体问题。
问题一:为Alice设计邮件加密方案
Alice是一家大公司的总裁,需要向数百名员工发送加密邮件。她只有一部移动设备,无法存储上百个不同的密钥,但能记住每个员工的邮箱地址。需要设计一个方案,使Alice只需存储少量信息即可为所有员工生成加密密钥。
以下是解决方案的核心思路:
- 使用伪随机函数(PRF)而非伪随机数生成器(PRG)。
- Alice仅存储一个PRF密钥
K。 - 对于邮箱地址为
ID的员工,其加密密钥为PRF(K, ID)。 - 由于所有邮箱地址唯一,生成的密钥看起来是随机的。
- 随后可使用任何加密方案(如AES或ElGamal)进行加密。
安全性证明简述:由于所有邮箱地址不同,PRF在不同输入上被调用,其输出在计算上无法与随机函数区分。因此,该方案是安全的。
问题二:分析一个对称加密方案
给定一个伪随机函数(PRF)F,考虑以下加密方案:密文 C = M XOR F(K, 0),其中 0 代表全零字符串。
A部分:该方案安全吗?
是安全的。这类似于使用一次性密码本(One-Time Pad)。因为 F(K, 0) 的输出在计算上无法与均匀随机字符串区分,所以 M XOR F(K, 0) 隐藏了消息 M。
B部分:该方案对多条消息安全吗?
不安全。因为每次加密都使用相同的密钥 K 和相同的输入 0,导致每次的掩码 F(K, 0) 都相同。这类似于重复使用一次性密码本的密钥,攻击者可以通过对两个密文进行异或操作来取消掩码,从而获取两条消息异或的结果,泄露信息。

问题三:关于混合论证的思考题(仅硕士/博士部分)
该问题展示了一系列分布 D0, D1, ..., D_{T(n)},其中 T(n) 是指数级的。任意两个相邻分布 D_i 和 D_{i+1} 非常接近(甚至是统计不可区分的),但第一个分布 D_0 和最后一个分布 D_{T(n)} 却很容易区分。
核心问题:这揭示了标准混合论证(Hybrid Argument)的局限性。混合论证要求混合步骤的数量是多项式级的,这样才能保证整体的不可区分性。当步骤数量是指数级时,即使每一步的差异可忽略,这些差异的累积总和也可能变得显著(即可区分)。因此,在这种情况下,无法用混合论证证明 D_0 和 D_{T(n)} 计算不可区分。
问题四:设计乘法同态加密


给定 n 条消息 s1, s2, ..., sn,使用公钥加密方案分别加密得到密文 c1, c2, ..., cn 并存储在云端。用户只保留私钥。目标是:让云端能够计算某个子集 A 中所有消息的乘积 ∏_{i in A} si,并生成一个单一的密文 c 返回给用户,用户解密 c 即可得到该乘积,而云端无法得知任何消息内容。
解决方案:使用ElGamal加密方案。
- 私钥为
x,公钥为g^x。 - 对消息
si的加密:ci = (g^{ri}, si * (g^x)^{ri}),其中ri是随机数。 - 云端计算:
c = (∏_{i in A} g^{ri}, ∏_{i in A} si * (g^x)^{ri}) = (g^{∑ri}, (∏ si) * (g^x)^{∑ri})。 - 结果
c正是消息S = ∏_{i in A} si的一个有效ElGamal密文,用户可用私钥x正常解密。
这展示了ElGamal具有乘法同态性。完全同态加密(FHE)则允许对密文进行任意计算(加法和乘法),是密码学中的一个强大工具。
问题五:基于离散对数问题构造抗碰撞哈希函数
给定一个阶为 q 的循环群,生成元为 g。需要构造一个哈希函数族 {H_k},其密钥 k 包含 k 个随机群元素 (h1, h2, ..., hk),其中 hi = g^{ai},ai 随机选自 Z_q。对于输入 x = (x0, x1, ..., xk),哈希输出为 H_k(x) = g^{x0} * h1^{x1} * ... * hk^{xk}。需要证明该哈希函数族在离散对数问题困难的假设下是抗碰撞的。
证明思路(归约):
- 假设存在敌手
A能以不可忽略的概率找到碰撞。 - 构造敌手
B来解决离散对数问题:B收到挑战y = g^r,需要找出r。 B随机选择索引j ∈ {1, ..., k},并设置hj = y。对于其他i ≠ j,B随机选择ai并设置hi = g^{ai}。B将哈希函数描述(h1, ..., hk)给A。- 如果
A输出碰撞(x, x')且x ≠ x',但H_k(x) = H_k(x')。 - 通过代数变换,碰撞条件等价于一个包含未知数
r(即aj)的线性方程。由于x和x'至少有两个位置不同(否则会导致矛盾),且B随机猜测的j恰好是其中一个不同位置的概率至少为1/k(不可忽略)。 - 当这种情况发生时,
B可以从方程中解出r,从而成功解决离散对数问题。 - 这与离散对数问题困难的假设矛盾,因此原哈希函数族是抗碰撞的。

问题六:从PRG到单向函数
证明:如果一个函数 G: {0,1}^n -> {0,1}^{2n} 是一个安全的伪随机数生成器(PRG),那么它也是一个单向函数(OWF)。
证明思路(归约):
- 假设
G不是单向函数,即存在PPT敌手A,能以不可忽略的概率ε在给定y = G(s)时找到原像s'使得G(s') = y。 - 构造区分器
B来攻击G的伪随机性。B收到一个2n比特的字符串y,需要判断y是来自G(即y = G(s))还是完全均匀随机的。 B运行A(y)。- 如果
A成功输出s'使得G(s') = y,则B输出“PRG”。 - 否则,
B随机猜测(以1/2概率输出“PRG”或“随机”)。
- 如果
- 分析:
- 当
y来自G时,A以概率ε成功,此时B正确输出“PRG”。A失败时,B有1/2概率猜对。因此,B的总优势约为ε/2。 - 当
y均匀随机时,y有原像的概率至多为2^n / 2^{2n} = 2^{-n}(可忽略)。因此,A几乎总是失败,B的行为近乎随机猜测,优势可忽略。
- 当
- 综合来看,
B能以不可忽略的优势区分G的输出与均匀随机字符串,这与G是安全PRG的假设矛盾。因此,G必须是单向函数。
作业问题解析
上一节我们分析了期中问题,本节中我们来看看作业中的两个典型问题。
作业3 - 问题2 & 3:一次性签名方案
问题2:分析一个不安全的一次性签名方案。该方案为 n 比特消息生成 n 个随机数 (x1, ..., xn) 作为私钥,公钥为 (f(x1), ..., f(xn)),其中 f 是单向函数。对消息 m 的签名包含所有满足 m[i]=0 的位置对应的 xi。

不安全原因:
- 全零消息的签名为空,任何人都可以伪造。
- 给定对消息
m的签名,可以轻松伪造任何通过将m中的1翻转为0得到的消息m'的签名(只需从原签名中移除对应位置的xi)。 - 但是,无法伪造将
0翻转为1的消息,因为这需要提供该位置xi的原像,而这是困难的。
问题3:利用问题2的思路,设计一个更高效(密钥大小 n + log n + 1)的一次性签名方案。
解决方案:
- 将待签名的
n比特消息m转换为m' = m || z,其中z是m中0的个数的二进制表示(长度为log n + 1)。 - 现在
m'的长度为n + log n + 1。 - 直接使用问题2中的方案对
m'进行签名。


安全性证明思路:
假设敌手在获得对消息 m 的一个签名后,伪造了另一个消息 m1 (≠ m) 的签名。考虑两种情况:
- 情况1:
m1在某位将m的0改成了1。根据问题2的分析,这在原方案中就是不可能的(需要提供原像)。 - 情况2:
m1只将m中的一些1改成了0,但没有将任何0改成1。这意味着m1中0的个数z1大于m中0的个数z。由于z和z1是m'和m1'的一部分,且z1 > z,那么在z1和z的二进制表示中,必然存在某一位,在z1中是1而在z中是0。这又回到了情况1——在扩展消息m'的某一位上发生了0到1的翻转,这是不可能的。
因此,任何成功的伪造都会导致矛盾,从而证明方案安全。

作业2 - 问题6 & 7:密钥交换与单向函数构造
问题6:使用一个安全的公钥加密方案构造一个密钥交换协议。
协议:
- Alice 生成公私钥对
(PK, SK),将PK发送给 Bob。 - Bob 生成一个均匀随机的会话密钥
K,用 Alice 的公钥加密得到c = Enc(PK, K),将c发送给 Alice。 - Alice 用私钥解密得到
K = Dec(SK, c)。
安全性证明思路:
敌手窃听到的视图是 (PK, c)。需要证明会话密钥 K 与视图在计算上不可区分。通过公钥加密的语义安全性,可以将密文 c = Enc(PK, K) 替换为对另一个随机密钥 K' 的加密 c' = Enc(PK, K'),而敌手无法区分。在替换后的分布中,会话密钥 K 与视图 (PK, c') 完全独立,且 K 均匀随机,满足密钥交换的安全定义。

问题7:使用一个安全的公钥加密方案构造一个单向函数。
构造:
将单向函数 f 定义为:f(r) = PK,其中 (PK, SK) = KeyGen(1^n; r),即 r 作为密钥生成算法的随机硬币,输出是公钥 PK。
单向性证明思路:
如果存在敌手能给定 PK 以不可忽略的概率找到原像 r,那么根据 r 可以重新运行 KeyGen 得到对应的私钥 SK。这意味着该敌手实际上能够从公钥 PK 计算出对应的私钥 SK,这直接破坏了公钥加密方案的安全性(因为有了私钥就能解密任何消息)。因此,在公钥加密安全的假设下,f 是单向函数。
总结
本节课中我们一起回顾并深入解析了期中考试和作业中的多个关键问题。我们探讨了如何为特定场景设计加密方案、分析了各种方案的安全性、理解了混合论证的边界、学习了同态加密的概念、练习了基于数论难题构造密码原语以及进行安全性归约证明。这些练习巩固了我们对密码学基本概念和证明技术的理解,为学习课程后半部分更高级的主题打下了坚实的基础。
014:比特币 I

在本节课中,我们将正式开启课程的第二部分,这部分将更侧重于应用,而非构建基础。我们将首先探讨比特币和区块链技术。
概述
在第一部分,我们学习了密码学的基本原语、定义和安全性证明。在第二部分,我们将广泛涵盖三个主题:比特币与区块链、零知识证明以及安全多方计算。这些都是密码学研究中非常活跃且令人兴奋的领域。

今天,我们将聚焦于比特币。比特币是第一个真正去中心化的货币,它不由任何中央机构、政府或银行支持。自20世纪90年代以来,密码学界就多次尝试构建这样的货币,但直到2008/2009年,一个化名为中本聪的人提出了比特币系统,才真正取得成功。
比特币成功的一个关键因素是它巧妙地利用了“人性贪婪”作为早期采用者的激励。系统通过“挖矿”过程产生新比特币,早期参与者更容易获得大量比特币,而随着时间推移,获取难度会增加。这种设计吸引了早期采用者,并推动了系统的普及。
关于比特币价格的剧烈波动使其成为一个极具争议的话题。有人认为它是骗局,有人则认为它将取代传统银行系统。现实可能介于两者之间。比特币是一项非常有用的技术,其价值不应仅由每日价格衡量。它在国际转账等场景中提供了比传统银行更高效、更低成本的解决方案。此外,区块链技术本身在加密货币之外也有广泛的应用潜力,有助于推动社会的去中心化。
比特币的核心特征包括去中心化(无中央控制)和一定程度的匿名性(并非完美匿名)。这些特性也带来了一些负面影响,例如早期曾被用于勒索软件等非法活动。但随着大型科技公司开始投资该技术,公众对其看法逐渐转向积极。
本节课的目标是深入比特币内部,理解挖矿过程的工作原理及其各个组成部分。
构建公共账本
比特币的核心是一个仅可追加的公共账本。你可以将其视为一个数据库或账簿,可以添加数据,但无法删除已有数据。理解该技术分为两部分:一是如何构建这个账本,二是如何在账本上记录信息(即应用层,加密货币只是应用之一)。
首先,我们来看如何构建账本。
数据是以“区块”为单位添加到账本中的。关键问题是:在去中心化的环境中,谁来决定添加下一个区块?内容是什么?添加的流程又如何?
答案是:通过解决一个密码学谜题来竞争添加下一个区块的权利。这场计算的赢家获得将新区块添加到区块链的权利。这是一个高度简化的描述,在分布式环境中,确定赢家、决定下一个谜题等细节更为复杂。
为了理解基础,我们先看一个只有单个矿工的简单系统。
单矿工系统
比特币系统中有矿工和用户。矿工是系统的骨干,他们尝试创建新区块并在此过程中获利。用户是最终用户,他们进行转账等操作,并为此支付费用。
挖矿是逐块进行的。当你挖出第 i 个区块时,它会给出挖掘第 i+1 个区块的谜题。整个过程的起点是一个创世区块(B0),由中本聪设定。该区块包含了2009年1月3日《泰晤士报》的头条标题:“财政大臣正处于对银行进行第二轮紧急援助的边缘”。选择公开新闻标题是为了表明该区块没有隐藏后门。
每个矿工都有一个用于签名的密钥对(私钥SK和公钥PK)。公钥有时也称为“地址”,因为当你想向某人发送比特币时,需要知道他的公钥。
要挖掘下一个区块(例如第 i+1 个区块),矿工需要:
- 决定写入新区块的信息
I_i(例如,收集自用户的所有有效交易)。 - 解决一个特定的谜题。
谜题定义如下:
需要找到一个随机数 N_i(称为Nonce),使得以下哈希值以 k 个零开头:
H(B_i, I_i, N_i, PK_miner) = 000...0***...*
其中:
B_i是前一个区块。I_i是本区块要记录的信息(交易集合)。N_i是矿工可以自由调整的随机数。PK_miner是矿工自己的公钥。k是难度参数。
如果成功找到这样的 N_i,那么哈希结果本身就成为了下一个区块 B_{i+1}。
为什么需要包含矿工的公钥?
因为挖出新区块的奖励会自动与这个公钥关联。矿工自然会放入自己的公钥,当然他也可以选择将奖励赠予他人。
如何寻找 N_i?
由于 B_i、I_i 和 PK_miner 是固定的,矿工只能不断尝试不同的 N_i 值,计算哈希,直到找到一个使结果以 k 个零开头的值。这是一个穷举搜索的过程。
哈希函数的要求:
这里不能使用伪随机函数(需要密钥),仅抗碰撞也不够。我们需要假设哈希函数(如SHA-256)的行为像一个随机预言机,即其输出在任意输入下本质上是随机的。
难度参数 k:
k 是难度参数,由系统自动调整。比特币的目标是大约每10分钟产生一个新区块。随着全网算力增加,解题速度可能加快,此时系统会自动增加 k 值,使谜题更难,以维持10分钟的出块间隔。反之,如果算力减少,则会降低 k 值。这一逻辑被编码在最初发布的比特币客户端中。
挖矿激励:
矿工投入算力和电力解决谜题,激励主要来自两方面:
- 区块奖励:成功挖出新区块后,系统会创造一定数量的新比特币并奖励给矿工(关联到其公钥)。初始奖励是50比特币,每四年减半,直至约2040年不再有新比特币产生。
- 交易手续费:用户发起交易时,会承诺支付一笔手续费。矿工将交易打包进区块时,会获得这些手续费。手续费由市场决定,手续费高的交易更可能被优先打包。
多矿工与分叉
现在,我们转向更现实的多矿工情况。
当多个矿工同时竞争解决下一个谜题时,可能会出现几乎同时找到解的情况。由于网络延迟,不同矿工可能先接收到不同的解,这就导致了分叉。

此时,区块链上出现了两个(或多个)候选的新区块。作为一个诚实的矿工,需要遵循以下规则来决定哪条链是“合法”的主链:
- 最长链原则:总是将计算难度加权后最长的链视为主链。如果发现自己当前认为的主链不再是全网最长的,应立即切换。
- 先到先得原则:如果两条链长度(难度加权后)相同,则选择自己最先看到的那条链。
每个矿工基于自己的视角确定主链,并尝试在此基础上延伸。
分叉如何解决?
分叉后,不同矿工可能在不同的链上继续工作。由于解题是随机过程,很快其中一条链会率先被延伸,变得更长。此时,根据最长链原则,几乎所有诚实的矿工都会切换到这条更长的链上,另一条链则会被逐渐抛弃,其上区块的奖励也随之无效(除非被重新纳入主链)。这个过程保证了系统的最终一致性。
关于链的同步:
矿工不需要持续广播整个区块链。他们只需要在成功挖出新区块时广播该区块的“谜题解”(即包含Nonce等信息的区块头)。新区块会通过网络传播,其他矿工接收并验证后,会将其添加到本地的链副本中。新加入的矿工则需要从网络节点下载完整的区块链历史。
恶意行为与51%攻击
上一节我们介绍了多矿工环境下的分叉与解决机制。本节中我们来看看如果矿工不遵守规则会怎样。

假设你是一个理性的(可能恶意的)矿工,只关心最大化自己的收益。你挖出了一个区块,但与此同时,另一个矿工也挖出了一个竞争区块。你的奖励只存在于你所在的这条链上。如果另一条链胜出,你的奖励将化为乌有。
因此,当出现更长的竞争链时,诚实的矿工会切换,但你可能选择不切换,而是继续在你原来的短链上挖掘,希望它能反超。然而,如果诚实矿工的总算力远大于你,这是一个失败策略,因为长链会因更多算力投入而变得更长,差距只会越来越大。
但是,如果你控制了全网超过51%的算力,情况就不同了。你可以实施 51%攻击:
- 你可以让任何你喜欢的链成为最长链。
- 你甚至可以重新从创世区块开始挖掘一条更长的链,来“覆盖”现有的历史记录(需要加权计算难度)。
为什么比特币尚未遭遇成功的51%攻击?
发动51%攻击很可能导致比特币价值归零,这对于拥有大量算力的攻击者而言是巨大的财务损失。对他们来说,更理性的选择是诚实挖矿获利。然而,某些意图摧毁加密货币体系的组织(如某些国家行为体)可能不惜成本发动此类攻击。
此外,51%攻击也出现在“山寨币”领域。强大的比特币矿工为了维护自身利益,可能会联合起来攻击新兴的竞争币种,这被称为“弑婴”攻击。
需要记住的是,在比特币系统中,最新的几个区块并非最终确定的,因为仍有较小概率发生分叉并被重组。通常需要等待若干个区块确认后,交易才被认为是比较安全的。
梅克尔树与挖矿池
在结束之前,我们简要提一下区块内交易信息的组织方式,以及矿工如何协作。
梅克尔树
区块中的交易信息 I_i 并非简单拼接,而是组织成一种叫做梅克尔树(或哈希树)的数据结构。这允许用户在不下载整个区块的情况下,快速验证某笔特定交易是否被包含在区块中。我们将在下节课详细讨论。
挖矿池
由于单个矿工获得区块奖励的几率很低且波动大,挖矿池应运而生。矿工们联合算力,共同挖矿,并按贡献分享奖励。
挖矿池通常有一个可信的经理。池中所有矿工挖矿时,不是指向自己的公钥,而是指向经理的公钥 PK_M。这样,一旦挖出区块,奖励会直接发给经理。
为了公平分配奖励,经理需要衡量每个矿工的贡献。矿工并非只有找到完整解(k个零)才能提交,当他们找到接近的解(例如哈希以 k' 个零开头,且 k' 略小于 k)时,就可以将此作为部分工作量证明提交给经理。经理根据矿工提交的部分证明的数量和质量(k' 越接近 k,权重越高)来 proportional 地分配奖励。这种方式也防止了矿工在挖到区块后携款跑路,因为他们的工作始终是针对经理的公钥进行的。
总结
本节课中,我们一起学习了比特币系统的基础原理。我们了解到比特币的核心是一个去中心化的、仅可追加的公共账本(区块链)。账本通过矿工解决密码学谜题(工作量证明)来逐块添加,其中包含了交易信息。我们探讨了单矿工与多矿工模型、分叉的产生与解决机制、最长链原则、以及恶意矿工可能发起的51%攻击。最后,我们简要介绍了用于高效验证交易的梅克尔树,以及矿工为平滑收益而组成的挖矿池的工作方式。下节课,我们将继续深入比特币的应用层,看看如何在这个账本上实现加密货币的具体功能。
015:比特币与默克尔树

在本节课中,我们将继续学习比特币。上一节我们主要探讨了公共账本(区块链)的维护机制,例如如何确保矿工对账本内容达成共识,以及如何防止数据被篡改或删除。本节中,我们将看看如何基于这个公共账本构建应用,特别是最初的、也是最流行的应用——加密货币。我们还将深入了解比特币中信息组织的重要结构:默克尔树。
加密货币的工作原理
一旦有了公共账本,设计加密货币就相对简单了。你可以在账本上记录所有交易:谁有多少钱、谁在何时向谁转账了多少等等。
核心问题在于:谁有权在公共账本上写入信息?答案是:成功挖出下一个区块的矿工。那么,如何确保矿工不会写入未经授权的信息呢?这通过数字签名来保证。
转账过程
以下是比特币转账的简化流程(忽略了一些实现细节):
- 创建新币:新比特币的唯一产生方式是通过挖矿。每当一个新的区块被挖出,一定数量的新币会“凭空”出现在挖出该区块的矿工的公钥地址下。
- 发起转账:假设爱丽丝(公钥
PK1)想转1个比特币给鲍勃(公钥PK2)。爱丽丝需要使用她的私钥SK1对以下声明进行数字签名:我,PK1,转账1个币给PK2。 - 广播交易:爱丽丝将这个签名后的声明广播到网络。所有正在竞争挖下一个区块的矿工都会看到这个交易。通常,交易中还会包含一笔给矿工的交易费(此处暂不展开)。
- 矿工验证:矿工在决定是否将此交易包含进下一个区块前,会进行验证:
- 验证签名是否有效。
- 验证公钥
PK1是否有足够的余额(在比特币中,实际需要指明花费的是哪个具体的“币”,此处简化)。
- 打包入块:如果验证通过且交易费足够有吸引力,矿工就会将这个交易添加到他们正在构建的区块信息页(
I_i)中。 - 解决难题:矿工们开始尝试解决工作量证明难题(即找到一个随机数,使得
H(前一个区块哈希, H(I_i), 随机数, 矿工公钥)的结果以特定数量的零开头)。 - 确认交易:当某个矿工成功解出难题,他挖出的新区块(包含爱丽丝的交易)就会被添加到区块链上。但此时交易尚未最终确认,因为区块链可能存在分叉。通常需要等待该区块后面再追加几个区块(变得“足够深”),才能确信该交易不会被回滚。
关于交易速度与安全性的讨论
上述流程存在一个明显的用户体验问题:交易确认可能需要长达一小时。一个自然的想法是:爱丽丝能否直接把签名后的交易声明发给鲍勃,鲍勃验证签名和余额后立即发货,然后再由鲍勃(或爱丽丝)去广播交易上链?这样交易不就即时完成了吗?
然而,这存在一个严重问题:双花攻击。爱丽丝可能只有1个比特币,但她可以同时将同一笔钱的签名交易发送给鲍勃、查理等多个人。这些人都验证通过并发了货,但最终只有最先被打包进区块链的那笔交易会生效,其他人将蒙受损失。因此,不等待链上确认就接受交易对收款方是危险的。
另一个问题是重放攻击:如果交易设计不当(例如没有唯一的“币标识符”),鲍勃在收到一笔钱后,可能会尝试重复广播同一笔交易记录,试图再次从爱丽丝那里获得支付。在实际的比特币协议中,通过引入币标识符等机制防止了此类攻击。
区块链的防篡改性
如果有人试图修改区块链历史中某个较早的区块(例如删除其中一笔交易),会发生什么?

由于区块链的哈希链结构,修改一个区块 B_i 的内容会导致其哈希 H(I_i) 改变。这进而会导致指向它的下一个区块 B_{i+1} 的哈希值无效(因为 B_{i+1} 的头部包含了 H(I_i))。攻击者必须从这个被修改的区块开始,重新计算之后所有区块的工作量证明难题,以生成一条新的、有效的链。
比特币遵循 “最长链胜出” 规则。除非攻击者拥有全网超过50%的计算力(即发起51%攻击),否则他生成篡改后链条的速度将赶不上诚实矿工维护的主链增长速度。被修改的区块埋得越深,重新计算其后所有区块的难度就越大,攻击几乎不可能成功。
默克尔树:高效的数据验证
现在,让我们关注区块链中的信息组织方式。作为一个轻量级用户(如鲍勃),你可能只关心与自己相关的一两笔交易,而不想下载和处理整个区块链(可能高达数百GB)。默克尔树就是为了解决这个数据验证效率问题而设计的。
首先,我们回顾一下比特币工作量证明难题的构成:
难题:找到随机数 Nonce,使得
H(前一个区块哈希, H(当前区块交易信息 I_i), Nonce, 矿工公钥)
的结果以特定数量的零开头。
注意,这里哈希函数的输入是交易信息的哈希 H(I_i),而不是庞大的交易信息 I_i 本身。这本身就是一个重要的优化。
从简单方案到默克尔树

假设客户端 C(存储有限)需要将大量交易数据 T1, T2, ..., Tn 存储在不可信的服务器 S 上,并希望日后能高效地验证并获取其中某笔特定交易 T_i 的完整性。
以下是几种渐进优化的方案:
-
方案一(存储全部哈希):
- 客户端存储所有交易的哈希
H(T1, T2, ..., Tn)。 - 问题:要验证单笔交易
T1,客户端必须从服务器下载所有交易,重新计算哈希并与存储值比对。通信量和临时存储开销都很大(O(n))。
- 客户端存储所有交易的哈希
-
方案二(分半存储):
- 客户端存储第一半交易的哈希
H1 = H(T1...T_{n/2})和第二半的哈希H2 = H(T_{n/2+1}...Tn)。 - 要验证
T1,客户端只需下载第一半交易,计算H1‘并与存储的H1比对。通信量减半(O(n/2)),但客户端长期存储翻倍(2个哈希值)。
- 客户端存储第一半交易的哈希
-
方案三(引入树形结构):
- 客户端只存储一个“根哈希”
H_root。服务器存储所有交易及一个树状结构:- 将交易两两分组,计算每组哈希。
- 将这些哈希值再两两分组,计算上一层哈希。
- 如此递归,直到最终得到一个根哈希
H_root。这棵树就是默克尔树。
- 要验证交易
T1,客户端不需要下载整棵树。它只需要从服务器下载:- 交易
T1本身。 - 从
T1到根节点路径上所需的所有“兄弟节点”哈希值(图中红色节点)。
- 交易
- 利用这些数据,客户端可以层层计算,最终得到一个根哈希值
H_root'。如果H_root'等于本地存储的H_root,则证明T1是完整且未被篡改的。 - 此方案的通信量仅为
O(log n),而客户端的长期存储仅为一个根哈希(O(1))。
- 客户端只存储一个“根哈希”
核心概念公式:
默克尔树的构建可以形式化地表示为递归哈希。对于叶子节点(交易)T_i,其哈希为 H(T_i)。对于非叶子节点,其哈希值为其两个子节点哈希值的连接后的哈希:H(左子哈希 || 右子哈希)。根哈希 H_root 代表了整组数据的唯一指纹。
在比特币中,每个区块的交易列表就组织成一棵默克尔树,区块头中存储的是该默克尔树的根哈希。轻客户端只需同步区块头链(包含根哈希),当需要验证某笔交易时,向全节点请求一个 O(log n) 大小的默克尔证明即可,无需下载整个区块。
比特币的局限性与未来发展
比特币作为先驱,也存在一些局限性,推动了后续加密货币的研究:
- 可扩展性:比特币区块大小限制(约1MB)和约10分钟的出块时间,将其吞吐量限制在每秒约7笔交易,远低于Visa等传统支付网络(每秒数千笔)。单纯增大区块会带来网络传播延迟和分叉增加等问题。
- 交易延迟:为确保交易最终性,通常建议等待6个区块确认(约1小时),这无法满足即时支付场景。
- 弱匿名性:比特币地址(公钥)是伪匿名。通过分析公开账本上的交易图,有可能将地址与现实身份关联起来。像 Zcash 这样的加密货币使用零知识证明实现了更强的交易匿名性。
- 协议升级困难:去中心化导致升级共识难以达成。升级分为:
- 软分叉:向后兼容,未升级的节点仍能接受新区块,但升级节点会拒绝某些旧规则产生的区块,促使矿工升级。
- 硬分叉:不兼容,通常会导致区块链永久分裂成两条链(如ETH和ETC)。
- 可用性:私钥丢失即永久失去资产,没有“忘记密码”找回的中央机构。研究正在探索如何引入类似双因素认证的机制而不损害隐私。
- 隐私性:区块链上所有数据(智能合约代码、状态、交易)公开。如何存储和计算加密数据、实现隐私智能合约,是一个活跃的研究方向。
总结

本节课中,我们一起学习了比特币如何利用数字签名和区块链实现加密货币的基本转账流程,并深入探讨了其核心数据结构——默克尔树。默克尔树通过巧妙的哈希树形结构,允许用户以极小的开销(O(log n))高效验证大量数据中某个元素的完整性,这对于区块链的轻客户端应用至关重要。最后,我们分析了比特币在可扩展性、延迟、匿名性、升级和可用性等方面面临的挑战,这些挑战也正是驱动密码学和分布式系统研究向前发展的重要动力。下一讲,我们将继续探讨其他加密货币(Altcoins)以及共识机制中的工作量证明与权益证明。
016:其他区块链方案

在本节课中,我们将学习比特币之外的其他几种加密货币提案。上一节我们介绍了比特币及其默克尔树等基础概念,本节中我们来看看一些旨在解决比特币可扩展性、能耗等问题的替代方案。
可扩展性问题与GHOST协议
我们首先回顾比特币的一个主要限制:可扩展性。在比特币中,区块大小被固定为1兆字节,这限制了每10分钟能处理的交易数量。一个显而易见的提议是直接增加区块大小,例如增加到5兆字节或10兆字节。
然而,这带来了一个问题。如果区块变大,其传播所需的时间就会变长。这意味着当有人解出谜题(挖出新区块)时,区块需要更长时间才能传播到全网。在此期间,诚实的矿工仍在旧的区块上工作,这导致了更多的分叉。
即使使用默克尔树,矿工也需要下载整个区块来验证其有效性。另一方面,不诚实的矿工可能连接性更好、协调性更强。一旦一个不诚实的矿工成功,他可以迅速通知其他不诚实的矿工停止在当前区块上挖矿,并开始在新的谜题上挖矿。因此,不诚实的矿工计算力浪费更少。
这可能导致一个危险情况:即使不诚实的矿工拥有少于50%的计算力,但由于他们协调得更好,他们可能控制最长的链。这意味着他们可以发动低于50%算力的“51%攻击”,从而回滚和重写历史,完全破坏比特币的安全性。
因此,提高比特币的可扩展性并非简单地增加区块大小那么简单。
为了解决这个问题,出现了一个名为GHOST的提案。GHOST代表“贪婪最重观察子树”。其核心思想是:不再选择最长的链,而是选择投入了最多计算力的分支(即最重的子树)。
即使诚实的矿工产生了最终消亡的分叉,该分叉在决定哪个链被选中时仍然发挥作用。规则是选择节点数最多的子树(包括分叉中的节点),这代表了最多的计算投入。如果出现平局,则选择最先出现的子树。如果一个矿工正在某个分支上工作,但发现另一个分支变得更重,他应该切换到那个更重的分支。
需要记住的是,系统中仍然存在一条主链,只有主链上的交易才被认为是有效的。分叉只是用来决定哪条链是主链。
以下是决定主链的算法:
- 从创世区块开始。
- 在每一步,如果遇到多个可能的分支,选择当前最重的分支。
- 递归地重复此过程,直到到达一个叶子节点,这样就标记出了主链。
与比特币类似,GHOST协议也有一个很好的稳定性属性:最重的子树会变得越来越重。这是因为根据规则,当出现冲突时,矿工们会随机分散到不同的分支上,其中一个分支会被扩展并变得更重,然后所有诚实的矿工最终都会切换到那个分支。由于诚实的矿工控制着超过51%的计算力,那个分支会变得更重,而其他分支则会逐渐消亡。
GHOST协议的另一个优点是,诚实的矿工不再因为分叉而浪费计算力,因为每个分叉都在决定哪个子树最重的过程中发挥了作用。但请记住,所有有效的交易仍然只在主链上。分叉只是告诉你应该选择哪条路径作为主链。
以太坊采用了一个名为Casper的GHOST变体,它在解决可扩展性问题方面取得了进展。
包容性区块链
另一种处理可扩展性并降低交易成本的提案是包容性区块链方法。其目标是保持交易成本更低。
到目前为止,我们总是丢弃分叉。这在某种程度上是重要的,因为分叉中的信息可能与主链上的信息冲突,我们必须选择其一。但关键在于,分叉中可能包含一些并不一定与区块链信息冲突的信息。在这种情况下,我们为什么要丢弃这些信息呢?
例如,我不想支付高额交易费,我愿意等待几个小时以确保我的交易进入区块链。我广播了我的交易,它可能出现在某个分叉中,但不在主链上。只要它与主链上的任何内容不冲突,它就应该被保留,并被视为有效交易。这正是包容性区块链的提议。
当然,这里仍然存在一些问题。例如,如果主链后来被扩展,并且主链上出现了冲突的交易,那么会发生什么?分叉会被撤销吗?如果是这样,那么分叉永远无法被信任,我们又回到了旧的方案。
这里的想法是,我们查看区块链的当前状态以及所有分叉的当前状态。如果分叉中的交易与区块链的当前状态不冲突,我们就保留它们,并使其最终确定。这意味着,从此刻起,主链的任何扩展都将被禁止包含任何冲突的交易。因此,分叉中的交易现在可以被最终确定。
更详细地说,提案如下:假设在某个点有一个分叉,但后来主链变得更长。假设分叉中有某个交易Ti,且Ti与主链任何区块中的内容都不冲突。那么,当你尝试挖下一个区块时,你会查看这个分叉区块(称为Bj)。你尝试解决的谜题将是主链最后一个区块与Bj的某种连接。规则是:主链上的下一个区块要“认证”所有迄今为止未被认证的“无子节点”。

这意味着,如果分叉中存在非冲突交易,它们被视为有效并成为区块链的一部分。主链可能会继续扩展,主链上的下一个区块也会认证这个分叉区块。

这样做的优点是,你可以在分叉上进行挖矿,并希望它们被包含进来。分叉上的矿工也能获得一些交易费,这可能比主链上的交易费低得多。这更具包容性,因为在比特币中,由于竞争激烈,一个矿工可能多年都无法成功挖出下一个区块。但在这个提案中,你可以去任何长期存在的分叉,尝试扩展它们,包含新的交易。希望主链上会有一个区块认证你创建的分叉扩展,这样你就能获得报酬,账本得以扩展,并且以非常低的交易费包含了新的交易。
关于新币的产生,最清晰的提案可能是新币只在主链上产生,而在分叉上,链的扩展仅基于交易费的激励。
这导致了一种可以被视为有向无环图的结构,这是一种比树更高级的结构。
权益证明
到目前为止,我们讨论的一切都与工作量证明相关。在工作量证明中,你需要解决一个计算谜题,许多矿工同时尝试解决相同的谜题,这导致了计算资源和电力的巨大浪费。事实上,比特币消耗的电力比瑞士整个国家还多,而这些计算除了维护比特币本身外,对人类基本无用。
因此,问题是:我们能否有一个更好、更环保、更便宜的比特币替代方案?其中一个提案是基于权益证明。当然还有很多其他建议,例如存储证明、内存证明等。
在权益证明中,你在系统中的投票权与你拥有的代币数量成正比。这是一个更环保的系统,因为你不需要解决无意义的计算谜题,不需要消耗大量电力,它依赖于密码学,并且具有比特币所不具备的一些优点。但它也有自身的问题。
一个常见的批评是:你拥有的权益越多,权力就越大,你更有可能挖出下一个区块,这意味着新产生的代币更有可能归你所有。这导致了“富者愈富”的问题。但即使在工作量证明中,你拥有的钱越多,就能买越多的计算机,从而获得更多的比特币。这本质上与社会运作方式相似。
权益证明的另一个问题是它更加复杂。其优点是更环保,缺点是更复杂,并且可能更加中心化。
那么,在权益证明系统中如何挖出下一个区块呢?有几种方案。第一种方案是让持有最多代币的人来挖下一个区块,但这会导致系统中心化。显然,需要引入一些随机性。
在工作量证明中,随机性来自计算谜题。在这里,我们需要的是:你的成功概率应与你在系统中的权益成正比。权益即你拥有的代币数量或占总量的百分比。
我们需要一个去中心化的抛币机制,使得任何特定公钥的成功概率与其持有的权益成正比。实现这一点的方法是,在每一轮或每个区块中,为每个公钥分配一个分数。你的分数应等于你的权益乘以某个随机数ID。这个随机ID对于不同方是不同的,并且取决于你的公钥。
随机ID的生成可能是权益证明系统中最复杂的部分之一。以下是一些尝试:
- 最简单的尝试:每一方选择自己的随机ID。但这会失败,因为不诚实的方总是会选择最大的数字来最大化自己的分数。
- 第二次尝试:随机ID = Hash(区块索引 || 公钥)。这里的问题是,公钥是公开的,攻击者可以预先计算未来许多区块的哈希值,并为每个区块选择最优的公钥,然后在区块生成前将权益转移到该公钥上。
- 第三次尝试:随机ID = Hash(上一个区块 || 公钥)。这比前一个好,但仍有问题。挖出上一个区块的人拥有很大的权力,因为他可以精心构造上一个区块,以确保未来某个敌对公钥会持续获胜。
- 最终尝试(本课程内):随机ID = Hash(你对上一个区块的签名 || 你的公钥)。这样,对手无法计算诚实方的随机ID,因为他没有他们的私钥来生成签名。但对手仍然可以通过构造上一个区块来最大化自己的随机数。这个攻击通常通过委员会机制来应对:不是由单个人决定上一个区块,而是由一个委员会来决定。
此外,使用的签名方案必须是确定性的和唯一的,以防止攻击者生成多个签名并选择最优的那个。例如,教科书式的RSA签名(先哈希消息再签名)就是一种确定性签名方案。
权益证明中还有一个棘手的问题,称为“事后腐败”。这在工作量证明中不会发生。假设在某个时间点,一组矿工S拥有系统中的大部分权益。后来,S卖掉了所有权益,然后变得恶意。S可以从那个时间点开始创建一个分叉。在这个分叉中,S仍然拥有大部分权益(因为他从未卖掉)。一个新加入的矿工会看到两个不同版本的区块链,很难区分哪个是真的,哪个是伪造的。

在工作量证明中,如果你想创建一个与主链一样长的分叉,你需要持续拥有大部分计算力,成本高昂。但在权益证明中,你只需回到那个时间点,拥有大部分权益,就可以创建任意长的链,这几乎不需要什么工作。这个问题有一些解决方案,但都不是非常简单的彻底解决方案。
总结
本节课我们一起学习了比特币之外的其他区块链提案。我们探讨了GHOST协议如何通过选择最重的子树而非最长的链来解决可扩展性问题。我们还了解了包容性区块链,它试图通过保留非冲突的分叉交易来降低交易成本并提高可扩展性。最后,我们介绍了权益证明作为工作量证明的一种更环保的替代方案,分析了其基本原理、优势以及面临的挑战,如随机领导者选举的复杂性和事后腐败问题。这些方案展示了区块链技术仍在不断发展和完善中。
017:零知识证明 I

在本节课中,我们将要学习一个非常激动人心的新主题——零知识证明。这是一个在密码学中既迷人又反直觉的概念。我们将从基本定义开始,并通过一个具体的例子来理解其工作原理。
零知识证明涉及一个证明者(Prover)和一个验证者(Verifier)。证明者试图向验证者证明某个陈述为真,并且证明者拥有一个秘密(例如,某个问题的解或见证)。关键在于,证明者希望在不泄露任何额外知识的情况下,让验证者信服。
零知识证明的定义
上一节我们介绍了零知识证明的基本概念,本节中我们来看看其形式化定义。
一个零知识证明是一个在证明者(P)和验证者(V)之间进行的交互式协议。两者都是概率多项式时间(PPT)算法。
- 公共输入(x):这是证明者和验证者都知道的陈述。例如,一个数学方程或一个布尔公式。
- 私有输入(w):这是只有证明者知道的见证(witness),用于证明陈述
x ∈ L(L是某个语言)。
协议的执行过程如下:
- 证明者算法
P接收公共输入x、私有见证w以及验证者的上一条消息,然后生成下一条证明者消息m_{i+1}^P。 - 验证者算法
V接收公共输入x和证明者的上一条消息,然后生成下一条验证者消息m_{i+1}^V。 - 协议结束后,验证者输出“接受”或“拒绝”。
我们将协议中交换的所有消息序列称为协议记录(transcript),记作 τ。
一个安全的零知识证明需要满足三个核心属性:
1. 完备性(Completeness)
如果陈述为真(x ∈ L),并且证明者拥有正确的见证 w,那么当双方都诚实地执行协议时,验证者总是会接受。
2. 可靠性(Soundness)
如果陈述为假(x ∉ L),那么对于任何(恶意的)PPT证明者算法 P*,诚实验证者拒绝的概率至少为 p(p 被称为可靠性参数,理想情况下应接近1)。
3. 零知识性(Zero-Knowledge)
这是最有趣也最微妙的属性。其直观含义是:验证者从与证明者的交互中“学不到任何东西”。更形式化地说,对于任何(可能是恶意的)PPT验证者算法 V*,都存在一个预期的PPT算法 S(称为模拟器),使得以下两个分布是计算不可区分的:
- 真实记录分布:当真实的证明者
P(拥有见证w)与V*交互时产生的记录τ_real。 - 模拟记录分布:模拟器
S(仅输入公共陈述x和V*的代码描述,而没有见证w)输出的记录τ_sim。
这意味着,验证者 V* 即使不与真正的证明者交互,仅凭自身(通过运行模拟器 S)也能生成一个看起来“一模一样”的交互记录。因此,与真实证明者的交互并没有给 V* 带来任何新的、它自己无法计算出的知识。
一个关键问题:既然存在一个公开的模拟器 S 可以在没有见证的情况下生成有效的记录,那么一个恶意的证明者 P* 是否可以直接使用 S 来欺骗验证者呢?答案是否定的。原因在于“单次尝试”与“多次尝试”的区别:模拟器 S 拥有验证者 V* 的代码,可以反复“重置”并尝试与 V* 交互,直到生成一个看起来成功的记录。而一个恶意的证明者在与一个“活的”验证者进行实时交互时,只有一次机会,一旦出错,验证者就会停止交互。
图同构问题的零知识证明
为了更具体地理解,我们来看一个经典的例子:图同构问题的零知识证明。
问题描述:给定两个图 G0 和 G1,证明者想要向验证者证明这两个图是同构的(即,可以通过重新标记顶点使它们完全相同),而无需透露具体的同构映射(即见证 π,使得 G0 = π(G1))。
以下是该协议的具体步骤,它依赖于图同构问题的三个基本性质(此处略去证明):
- 证明者(P):
- 随机选择一个置换
σ。 - 计算图
H = σ(G0)。 - 将
H发送给验证者。
- 随机选择一个置换
- 验证者(V):
- 随机选择一个挑战比特
b ∈ {0, 1}。 - 将
b发送给证明者。
- 随机选择一个挑战比特
- 证明者(P):
- 如果
b = 0,则发送φ = σ。 - 如果
b = 1,则发送φ = σ ∘ π(即先应用π,再应用σ)。
- 如果
- 验证者(V):
- 验证
H是否等于φ(G_b)。 - 如果相等,则接受;否则拒绝。
- 验证
协议分析
- 完备性:如果双方诚实,且图确实同构,验证总能通过检查。
- 可靠性:如果两个图不同构,那么证明者生成的图
H不可能同时与G0和G1都同构。因此,无论恶意证明者如何选择H,当验证者随机选择的b恰好要求证明H与它不同构的那个图同构时,证明者就会失败。这发生的概率至少是1/2。 - 零知识性(直观):模拟器
S可以这样工作:- 随机猜测验证者会挑战哪个图(
b')。 - 随机置换图
G_{b'}得到H‘并发送。 - 从(恶意的)验证者
V*那里获得实际的挑战比特b。 - 如果
b == b',则模拟器成功,可以构造出正确的响应φ‘(因为H‘正是从G_b置换而来)。 - 如果
b != b',则模拟器“重置”并回到步骤1重新开始。
由于H‘的分布与真实协议中H的分布相同(基于图同构的性质),验证者V*无法从H‘中获知b‘的信息。因此,模拟器每次尝试有1/2的概率成功,在期望的多项式时间内可以生成一个与真实记录计算不可区分的模拟记录。
- 随机猜测验证者会挑战哪个图(

可靠性放大
上述基础协议的可靠性参数仅为 1/2。为了提高可靠性,可以将协议独立重复执行 n 次。验证者仅在所有 n 轮中都接受时才最终接受。此时,恶意证明者成功欺骗的概率降至 (1/2)^n,可靠性参数提升至 1 - (1/2)^n。
重要提示:在每一轮重复中,证明者必须使用全新的随机置换 σ 来生成新的图 H。如果重复使用相同的 H,恶意的验证者可能通过在不同轮次要求不同的挑战(b=0 和 b=1)来组合信息,最终推导出同构映射 π,从而破坏零知识性。
总结
本节课中我们一起学习了零知识证明的基本概念。我们首先形式化定义了零知识证明协议及其必须满足的三个属性:完备性、可靠性和零知识性。然后,我们通过图同构问题这一具体实例,深入剖析了一个零知识证明协议是如何构建和工作的,并理解了其安全性背后的直观原理。最后,我们提到了通过重复执行协议来放大可靠性的方法。在下一讲中,我们将探讨重复协议时的零知识性如何保持,并介绍更强大的零知识证明概念。
018:承诺方案

在本节课中,我们将继续学习零知识证明,并引入一个关键的密码学原语——承诺方案。我们将了解承诺方案的定义、安全属性,并探讨如何基于不同的密码学假设来构建它。
从图同构到零知识证明的扩展
上一节我们介绍了图同构问题的零知识证明。我们看到了一个三轮协议,其可靠性参数为1/2。这意味着作弊的证明者至少有1/2的概率会失败,但也有1/2的概率可能通过猜测验证者的挑战而成功。
协议回顾
让我们简要回顾一下之前的协议。证明者P和验证者V的输入是两个图G0和G1。证明者还拥有一个见证(witness),即一个置换π,使得G0 = π(G1)。
- 证明者:生成一个随机置换σ,并发送图H = σ(G0)。
- 验证者:发送一个随机挑战比特b ∈ {0, 1}。
- 证明者:根据b的值进行响应:
- 如果b=0,则发送置换σ,以证明H与G0同构。
- 如果b=1,则发送置换σ ∘ π,以证明H与G1同构。
如果G0与G1不同构,那么对于至少一个挑战(b=0或b=1),作弊的证明者将无法正确响应。因此,其成功作弊的概率最多为1/2。
提升可靠性:重复执行协议
显然,1/2的可靠性是不够的。一个非常自然的想法是重复执行协议多次。如果证明者每次都能成功,那么验证者就有足够的信心相信证明者没有撒谎。
以下是重复k次协议的过程:
- 对于第i轮(i从1到k):
- 证明者生成一个新的随机置换σ_i,并发送图H_i = σ_i(G0)。
- 验证者发送一个随机挑战比特b_i。
- 证明者根据b_i的值,发送σ_i或σ_i ∘ π。
关键点:每一轮都必须使用全新的随机置换σ_i。如果重复使用相同的σ,验证者可以通过发送不同的挑战比特来学习σ和σ ∘ π,从而计算出见证π,这将破坏零知识性。
现在,我们来分析这个重复协议的可靠性。如果G0与G1不同构,作弊的证明者在单轮中成功的概率最多为1/2。因此,他在所有k轮中都成功的概率最多是(1/2)^k。这意味着验证者拒绝(即至少有一轮发现作弊)的概率至少是1 - (1/2)^k。当k足够大时,这个概率无限接近于1。
保持零知识性:重绕技术
单个协议执行是零知识的。但重复执行k次后,它还是零知识的吗?答案是肯定的,但证明需要引入一个重要的技术——重绕。
回忆一下单轮协议的模拟器S是如何工作的:
- S随机猜测一个挑战比特b‘,并生成一个随机置换σ,计算H = σ(G_{b‘})发送给验证者V*。
- S接收来自V*的实际挑战比特b。
- 如果b恰好等于b‘,则S可以成功模拟,发送σ作为响应。
- 如果b ≠ b‘,则S“重启”模拟,回到第1步,重新猜测b‘。
对于k轮协议,一个简单的想法是让模拟器为每一轮都这样做。但问题在于,模拟器可能在某一轮失败(概率1/2),失败后如果总是退回到第一轮重新开始,那么它成功完成所有k轮模拟的期望时间将是指数级的(约为2^k)。
重绕技术解决了这个问题。其核心思想是:模拟器可以在协议执行的任何时刻保存验证者V*的“状态”(可以理解为程序运行到某一步的所有内存信息)。当模拟器在某一轮失败时,它不必退回到最开始,而是可以退回到这一轮开始时的保存点,用新的随机数重新尝试这一轮。由于每一轮在期望上只需要尝试2次就能成功,因此模拟所有k轮的总期望时间大约是2k,是多项式时间的。
这个“重绕对手”的思想在密码学中极其重要和强大,被广泛应用于零知识证明、安全多方计算等交互式协议的安全性证明中。
为什么需要承诺方案?
到目前为止,我们看到了图同构问题的零知识证明。但这个问题的实际应用似乎有限。我们更希望为NP完全问题(如电路可满足性问题SAT)构建零知识证明。因为如果能为一个NP完全问题构建零知识证明,那么通过归约,我们就能为所有NP问题构建零知识证明。
一个经典的NP完全问题是图三着色问题:
- 输入:一个图G。
- 问题:是否存在一种使用三种颜色(例如红、绿、蓝)对图的每个顶点着色的方案,使得任意一条边连接的两个顶点颜色都不同?
我们的目标是:让证明者P向验证者V证明,他拥有图G的一个有效三着色方案(即见证),但不泄露这个着色方案本身。
为了构建图三着色问题的零知识证明,我们将需要一个新的密码学工具——承诺方案。即使我们在零知识的背景下引入它,承诺方案本身也是一个基础且应用广泛的密码学原语。
承诺方案详解
直观理解
承诺方案可以类比于一个上锁的盒子。
- 承诺阶段:承诺方C将写有秘密消息m的纸条放入盒子,锁上,然后将盒子发送给接收方R。此时,R看不到m(隐藏性),同时C也无法再更改盒子里的m(绑定性)。
- 打开阶段:在之后的某个时间,C将盒子的钥匙发送给R。R用钥匙打开盒子,取出并验证消息m。
形式化定义
一个承诺方案涉及两方:承诺方C和接收方R。它包含两个阶段:
- 承诺阶段:C对消息m选择一个随机数s(作为随机性),计算承诺值
c = Commit(m, s),并将c发送给R。 - 打开阶段:C将
(m, s)发送给R。R验证Commit(m, s) == c是否成立。如果成立,则接受m;否则拒绝。
安全属性
一个安全的承诺方案需要满足两个核心属性:
-
隐藏性:在承诺阶段结束后,接收方R无法获得关于消息m的任何信息。形式化地说,对于任意两个不同的消息m0和m1,它们的承诺分布是计算不可区分的:
Commit(m0, s) ≈ Commit(m1, s)(其中s是均匀随机选取的)。 -
绑定性:承诺阶段结束后,承诺方C无法找到两组不同的打开值
(m0, s0)和(m1, s1)(其中m0 ≠ m1)使得它们对应同一个承诺值c。形式化地说,对于任意m0 ≠ m1,以及任意s0, s1,都有:
Commit(m0, s0) ≠ Commit(m1, s1)。
注意:承诺方案与加密方案不同。加密主要关注隐藏性,而承诺方案额外要求绑定性。一个简单的“加密”可能无法提供绑定性(例如,使用一次一密加密消息后,发送方可以用不同的密钥“打开”成任何他想要的消息)。
承诺方案的构造
构造一:基于DDH假设的ElGamal承诺
假设我们有一个循环群,其生成元为g,阶为q。
-
承诺阶段:承诺方C选择随机数a, b ∈ Z_q,计算并发送:
c = (g^a, g^b, m * g^(ab)) -
打开阶段:C发送
(m, a, b)。接收方R验证ga和gb是否与收到的前两项匹配,并计算m * g^(ab)是否与收到的第三项匹配。 -
隐藏性:基于DDH假设,
(g^a, g^b, g^(ab))看起来像随机三元组。因此,用g^(ab)“掩盖”后的消息m被隐藏。 -
绑定性:由于g是生成元,给定
g^a和g^b,a和b在模q意义下是唯一确定的。这进而唯一确定了g^(ab),从而唯一确定了被掩盖的消息m。

构造二:基于单向函数的Blum承诺
我们可以基于一个一对一单向函数f及其硬核谓词h(x)来构造比特承诺方案。
-
承诺阶段:承诺方C选择随机输入x,计算并发送:
c = (f(x), h(x) ⊕ m),其中m ∈ {0,1}是要承诺的比特。 -
打开阶段:C发送
(m, x)。接收方R验证f(x)是否与收到的第一项匹配,并计算h(x) ⊕ m是否与收到的第二项匹配。 -
隐藏性:由于h(x)是f(x)的硬核谓词,即使给定f(x),h(x)也看起来像均匀随机比特。因此,
h(x) ⊕ m完美地隐藏了m。 -
绑定性:由于f是一对一函数,给定
f(x),x是唯一确定的。这进而唯一确定了h(x),从而使得承诺方无法找到另一个x‘来打开成不同的消息m‘。
扩展到多比特消息:要对一个长消息进行承诺,只需对其每个比特独立地运行上述比特承诺方案。通过混合论证(hybrid argument),可以证明多比特承诺方案同样满足隐藏性和绑定性。
总结
本节课中我们一起学习了以下内容:
- 我们通过重复执行协议,将图同构零知识证明的可靠性从1/2提升到了接近1。
- 我们引入了重绕技术,证明了重复执行后的协议仍然保持零知识性,并且模拟器可以在多项式期望时间内运行。
- 我们指出了为零知识证明寻找更广泛应用(如NP完全问题)的需求。
- 我们正式介绍了密码学原语——承诺方案,明确了其隐藏性和绑定性两大安全属性。
- 我们探讨了两种承诺方案的构造:基于数论难题(DDH假设)的ElGamal承诺,以及基于密码学基础(一对一单向函数)的Blum承诺。
承诺方案是构建许多高级密码协议(如图三着色问题的零知识证明)的基石。在下一节课中,我们将利用承诺方案,具体构建图三着色问题的零知识证明协议。
019:NP问题的零知识证明

在本节课中,我们将学习如何为所有NP问题构造零知识证明。我们将通过一个具体的NP完全问题——图的三着色问题——来展示这一构造过程。我们将看到如何利用承诺方案,构建一个交互式协议,使得证明者能够在不泄露任何额外信息的情况下,向验证者证明自己知道一个有效的三着色方案。
协议概述
我们有一个图G,包含一组顶点和一组边。证明者P拥有该图的一个有效三着色方案(见证),而验证者V仅知道图的结构。我们的目标是让P向V证明图G是三着色的,同时不泄露任何关于具体着色方案的信息。

协议的核心思想是:证明者首先随机置换其拥有的三着色方案,然后承诺所有顶点的颜色。验证者随后随机选择一条边,要求证明者打开该边两个端点的颜色承诺。验证者接受证明,当且仅当这两个颜色不同。
协议详述
以下是协议的具体步骤:
- 证明者P:随机置换其拥有的有效三着色方案。设置换后顶点V_i的颜色为
color_i。 - 证明者P:为每个顶点V_i的颜色
color_i生成一个承诺C_i,并发送所有承诺给验证者V。 - 验证者V:随机选择一条边
(i, j),并将其作为挑战发送给证明者P。 - 证明者P:验证
(i, j)确实是图的一条边。如果是,则打开(即解除承诺)顶点V_i和V_j对应的承诺C_i和C_j,将颜色color_i、color_j以及用于生成承诺的随机数r_i、r_j发送给验证者。如果不是有效边,则不响应。 - 验证者V:检查收到的两个颜色是否不同。如果不同,则接受证明;否则拒绝。
协议属性分析
上一节我们介绍了协议的具体流程,本节中我们来看看该协议需要满足的三个核心属性:完备性、可靠性和零知识性。
完备性
如果证明者确实拥有一个有效的三着色方案,并且双方都诚实地执行协议,那么验证者总是会接受。这是因为:
- 随机置换一个有效的三着色方案,得到的仍然是有效的三着色方案。
- 对于任何有效的三着色方案,任意一条边的两个端点颜色必然不同。
- 因此,无论验证者选择哪条边进行检查,证明者都能展示两个不同的颜色。
可靠性
可靠性要求,如果图G实际上不是三着色的,那么任何(可能恶意的)证明者P*都无法以高概率欺骗验证者接受。
其核心论证如下:
- 如果图G不是三着色的,那么无论采用何种着色方案,至少存在一条边,其两个端点被着上了相同的颜色。
- 由于承诺方案的绑定属性,证明者在第一步发送承诺后,就无法再改变其承诺的颜色。
- 验证者随机选择一条边进行检查。如果恰好选中了那条“坏边”,证明者将被迫打开两个相同的颜色,从而导致验证者拒绝。
- 假设图有
|E|条边,验证者选中“坏边”的概率至少为1/|E|。虽然这个概率可能不够小,但我们可以通过顺序重复协议多次来将成功欺骗的概率降至可忽略不计。
零知识性
零知识性是最有趣也最复杂的属性。我们需要构造一个模拟器S,它没有图的有效三着色方案(见证),但能够生成一个与真实协议执行过程(即证明者与验证者交互的记录)计算不可区分的“模拟记录”。

模拟器S的工作流程如下:
- 猜测挑战:S首先随机猜测验证者将要询问的边
(i‘, j’)。 - 准备“着色”:S为顶点
V_i‘和V_j‘随机选择两个不同的颜色。对于图中所有其他顶点,S则填入“垃圾”值(例如0)。 - 发送承诺:S为所有顶点(包括那两个特殊顶点和其他“垃圾”顶点)的颜色生成承诺,并发送给(恶意的)验证者V*。
- 接收挑战:S从V*处收到实际挑战边
(i, j)。 - 检查与响应:
- 如果
(i, j)恰好等于之前猜测的(i‘, j’),则模拟成功。S打开顶点V_i和V_j的承诺,展示两个不同的随机颜色。这看起来与真实协议中打开一条边的结果相同。 - 如果
(i, j)不等于(i‘, j’),则模拟失败。S放弃此次尝试,回退到步骤1重新开始。
- 如果
为什么模拟记录与真实记录不可区分?
- 对于被打开的边:在模拟记录中,我们看到两个不同的随机颜色;在真实记录中,我们看到的是经过随机置换后的两个不同颜色。由于置换是随机的,这两者在分布上是相同的。
- 对于未被打开的承诺:在模拟记录中,它们承诺的是“垃圾”值(0);在真实记录中,它们承诺的是有效颜色。然而,由于承诺方案的隐藏属性,验证者V*无法区分一个承诺是给0的还是给一个有效颜色的,只要这些承诺永远不会被打开。在协议中,除了被选中的那条边,其他顶点的承诺确实永远不会被打开。
模拟器的运行时间:模拟器可能需要多次重试才能猜对验证者选择的边。可以证明,在承诺方案具有隐藏性的前提下,任何多项式时间的验证者V选择边的策略都无法使模拟器的期望运行时间超过多项式时间。否则,我们就可以利用V来构造一个攻击,打破承诺方案的隐藏性。
从NP完全问题到所有NP问题
上一节我们证明了可以为图的三着色问题构造零知识证明。由于三着色问题是NP完全的,这意味着我们可以为所有NP问题构造零知识证明。
构造思路如下:
- 假设我们有一个NP语言L,以及一个待证明的陈述x ∈ L,证明者P拥有相应的见证w。
- 利用一个NP归约(例如从L归约到图三着色问题),证明者P可以将
(x, w)转化为一个图G_x和一个该图的有效三着色方案w‘。 - 验证者V(仅知道x)可以独立地进行相同的NP归约,得到同一个图G_x(但不知道着色方案)。
- 现在,P和V只需针对图G_x执行我们之前为三着色问题设计的零知识协议。如果V被说服G_x是三着色的,那么根据归约的正确性,V也就被说服了x ∈ L。

由于NP类包含了所有具有“简短证明”(即多项式大小见证)的问题,上述构造表明,任何具有简短证明的陈述,都可以拥有零知识证明。
应用示例与讨论
零知识证明是密码学中一个极其强大的工具。以下是几个思考题,帮助我们理解其应用范围:
示例1:承诺值的范围证明
承诺者发送了对某个消息m的承诺。后来,他想向接收者证明m是5或6,但不想透露具体是哪一个。这能用零知识证明吗?
分析:是的。这是一个NP问题。见证是(m, r),其中r是生成承诺时使用的随机数。接收者可以用见证验证承诺是否正确,并检查m是否为5或6。因此,可以应用我们的通用构造。

示例2:密文非零证明
加密者发送了消息m在某个密钥下的加密密文。后来,他想证明密文中的消息m不等于0。这能用零知识证明吗?
分析:是的。这是一个NP问题。见证是解密密钥k(以及可能的随机数)。接收者可以用k解密密文,并验证结果非零。因此,可以应用零知识证明。
示例3:证明图不是三着色的
证明者想向验证者证明一个图G不是三着色的。这能用(我们刚学的)零知识证明吗?
分析:很可能不行。这不是一个明显的NP问题。证明者如何提供一个“简短的”见证,让验证者能在多项式时间内验证“所有可能的着色方案都无效”这个事实?这类“不存在见证”的问题属于co-NP类,目前不清楚是否有通用的零知识证明。

效率、轮次与非交互式零知识
我们构建的协议存在一些实际问题:
- 可靠性弱:单轮可靠性概率仅为
1/|E|,需要多次顺序重复才能达到高可靠性。 - 轮次多:顺序重复导致交互轮次很多。
一个自然的想法是:能否进行并行重复,将多轮压缩成三轮(即证明者发一条消息,验证者回复一条挑战,证明者再回复一条响应),同时保持零知识性?
遗憾的是,对于许多零知识协议(包括我们的图三着色协议),并行重复后是否仍为零知识是一个长期未决的开放问题。主要困难在于模拟器的构造:在并行会话中,模拟器需要同时猜对所有会话中验证者将要询问的边,这极其困难;而尝试“部分重绕”的策略可能会因为恶意验证者将不同会话的挑战关联起来(例如,通过对证明者的第一条消息整体进行哈希)而失败。
然而,在随机预言机模型下,有一个重要的变通方案:
- 修改协议,让验证者的挑战不是随机选择,而是通过一个公开的哈希函数
H作用于证明者的第一条消息来计算得出。 - 这样,证明者可以自己计算挑战,从而将整个协议非交互化:证明者生成第一条消息,计算挑战,生成响应,然后将整个证明(第一条和第三条消息的合并)一次性发送给验证者。
- 验证者通过重新计算哈希并检查响应来验证证明。
这种构造被称为非交互式零知识证明,在区块链等场景中非常有用,因为证明可以被一次性生成并永久公开验证,无需证明者和验证者进行在线交互。
总结
本节课中我们一起学习了如何为所有NP问题构造零知识证明。
- 我们以图的三着色这个NP完全问题为例,设计了一个基于承诺方案的交互式零知识证明协议。
- 我们分析了协议的完备性、可靠性和零知识性,其中零知识性的证明依赖于承诺方案的隐藏性和绑定性,并通过构造一个模拟器来实现。
- 利用NP完全问题的特性,我们将该协议推广到了所有NP问题,从而证明了任何具有简短证明的陈述都可以拥有零知识证明。
- 我们讨论了协议的实际效率问题、并行重复的困难,并引出了在随机预言机模型下构造非交互式零知识证明的重要思路。
零知识证明是构建许多高级密码学协议(如安全多方计算、匿名加密货币)的核心基石,其重要性不言而喻。
020:安全计算与不经意传输

在本节课中,我们将要学习安全多方计算,特别是安全两方计算的基本概念。我们将从一个经典问题——姚氏百万富翁问题入手,理解安全计算的目标。接着,我们将学习一个核心的密码学原语:不经意传输,并了解如何利用陷门单向置换来构建它。最后,我们会看到如何利用不经意传输来解决输入空间较小情况下的安全计算问题。
安全两方计算概述
上一节我们介绍了零知识证明,本节中我们来看看一个更广义的概念:安全多方计算。安全两方计算是安全多方计算的一个特例。
在安全两方计算中,有两个参与方 P1 和 P2。P1 拥有私有输入 x1,P2 拥有私有输入 x2。双方都知道一个公开的函数 F。他们希望运行一个协议,在协议结束时,双方都能学习到函数输出 F(x1, x2),但除此之外,任何一方都不应学到关于对方输入的额外信息。
这个概念的灵感来源于零知识证明。在零知识证明中,验证者只学到“陈述为真”这一事实,而学不到任何“证据”信息。安全两方计算将其推广:参与方只学到函数输出,而学不到超出输出之外的任何信息。
姚氏百万富翁问题
安全两方计算的一个经典实例是姚氏百万富翁问题,由姚期智教授提出。
想象两个百万富翁在海滩上交谈,他们想知道谁更富有,但又不愿意向对方透露自己的具体净资产。
一种方案是求助于可信第三方,但密码学中我们通常不依赖可信第三方。因此,百万富翁们希望运行一个多方协议,通过相互通信,最终只得知谁更富有的结果,而不泄露具体的净资产数额。
本质上,他们希望计算一个“大于等于”函数。P1 有输入 x1,P2 有输入 x2,协议应揭示 x1 >= x2 是否为真。更精确地说,协议输出可以是三种情况之一:
- 如果
x1 > x2,则 P1 胜出。 - 如果
x1 < x2,则 P2 胜出。 - 如果
x1 = x2,则为平局。
关键要求是:任何一方都不应学到对方输入(即具体净资产)的确切值。
安全两方计算的形式化定义
受零知识证明定义的启发,我们尝试形式化安全两方计算。我们主要关注两方情况,其思想可以推广到多方。
首先,我们有正确性要求:如果 P1 和 P2 都诚实地执行协议 π,那么双方最终都会学到 F(x1, x2)。
对于安全性,直觉是:存在一个概率多项式时间模拟器 S。对于腐败方(例如 P2*),模拟器 S 可以访问一个输出预言机 O。S 可以向 O 查询一次,提交一个输入 x2*,并获得 F(x1, x2*)。然后,S 必须能够生成一个协议转录本 τ_sim,使得 τ_sim 与真实交互产生的转录本 τ_real 在计算上不可区分。
这意味着,腐败方从真实协议中学到的一切,都可以仅从函数输出(在其选择的输入上)中模拟出来。因此,协议没有泄露超出函数输出的任何信息。
需要注意的是,我们分别考虑 P1 腐败或 P2 腐败的情况。如果双方都腐败,则无需讨论安全性保护。
对抗者模型:诚实但好奇
在密码学中,对抗者通常是任意恶意的。但作为构建协议的第一步,我们先考虑一种较弱的对抗者模型:诚实但好奇(也称半诚实)对抗者。
以下是诚实但好奇对抗者的特点:
- 他们完全遵循协议的所有指令。
- 他们使用良好的随机性。
- 他们不会发送任何偏离协议的消息。
- 然而,在协议执行后,他们会分析整个协议交互过程(转录本),试图推断出诚实方的私有输入。
可以理解为,协议执行期间他们是诚实的,协议结束后他们变坏了。我们首先针对这种对抗者构建协议,后续再通过零知识证明等技术升级到防御完全恶意对抗者。
构建模块:不经意传输
不经意传输是安全两方计算中最基础的功能之一。我们首先定义 1-out-of-2 不经意传输。
有一个发送者 Alice 和一个接收者 Bob。
- Alice 有两个输入:
x0和x1(通常是字符串或比特)。 - Bob 有一个选择比特
b(0 或 1)。 - 协议结束时:
- Bob 学到
x_b,但学不到x_{1-b}。 - Alice 学不到
b的值。
- Bob 学到
我们将展示如何利用陷门单向置换来构建针对诚实但好奇对抗者的 OT 协议。
陷门单向置换回顾
一个陷门单向置换族包含以下算法:
- 生成算法:生成一个函数
f及其陷门t。f是一个单向置换。 - 采样算法:从函数定义域
D中均匀采样一个输入x。 - 求逆算法:给定陷门
t和输出y = f(x),可以高效计算x。 - 硬核谓词:对于单向函数
f,存在一个谓词h(x),使得给定y = f(x),h(x)在计算上与随机比特不可区分。
例如,RSA 函数可以构建陷门单向置换,其中陷门是分解模数 N 的质因数。
1-out-of-2 OT 协议构造
假设发送者 Alice 的输入为 (x0, x1)(两个比特),接收者 Bob 的输入为选择比特 b。协议步骤如下:
-
第一轮(Alice):
- Alice 生成一个陷门单向置换
(f, t)。她将函数f发送给 Bob,自己保留陷门t。 - 公式:
(f, t) <- Gen(1^n)
- Alice 生成一个陷门单向置换
-
第二轮(Bob):
- Bob 选择定义域中的一个随机元素
r。 - 他计算
y_b = f(r)。 - 他再从值域中随机选择一个元素
y_{1-b}(即一个随机的群元素)。 - 他将
(y0, y1)发送给 Alice。注意,Bob 只知道r = f^{-1}(y_b),而不知道y_{1-b}的原像。
- Bob 选择定义域中的一个随机元素
-
第三轮(Alice):
- 利用陷门
t,Alice 可以计算两个原像:r0 = f^{-1}(y0)和r1 = f^{-1}(y1)。 - 对于
i ∈ {0,1},她计算zi = xi XOR h(ri),其中h是f的硬核谓词。 - 她将
(z0, z1)发送给 Bob。 - 公式:
zi = xi ⊕ h(f^{-1}(yi))
- 利用陷门

- 输出(Bob):
- Bob 计算
xb = zb XOR h(r)。 - 因为
r是yb的原像,所以h(r) = h(f^{-1}(yb)),从而能正确解密出xb。 - 对于
x_{1-b},由于 Bob 不知道y_{1-b}的原像,h(f^{-1}(y_{1-b}))对他而言是随机的,因此他无法解密z_{1-b}。 - 公式:
xb = zb ⊕ h(r)
- Bob 计算
协议安全性分析(诚实但好奇)
我们需要针对发送者腐败和接收者腐败两种情况分别构造模拟器。
情况一:发送者 Alice 腐败*
- 模拟器 S 运行 Alice* 获得其发送的第一条消息
f。 - S 随机选择两个值
(y0, y1)作为“第二轮”消息发送给 Alice*。 - Alice* 回复
(z0, z1)。 - S 输出整个转录本
(f, y0, y1, z0, z1)。 - 由于真实的 Bob 发送的
(y0, y1)中一个是f(r),一个是随机值,而f(r)与随机值在计算上不可区分(因为f是置换),因此模拟的转录本与真实转录本不可区分。模拟器甚至不需要查询输出预言机,因为腐败的发送者本应得不到任何输出。
情况二:接收者 Bob 腐败*
- 模拟器 S 诚实生成
(f, t),并将f发送给 Bob*。 - 由于 Bob* 是诚实但好奇的,S 可以观察其代码获得输入比特
b。 - S 查询输出预言机,获得
xb(即函数输出)。 - S 需要生成第三条消息
(z0, z1)。它如下计算:zb = xb XOR h(f^{-1}(yb))(这里yb来自 Bob* 发送的消息,S 可以用陷门t求逆)。z_{1-b}设置为一个随机比特R。
- S 输出整个转录本。
- 在真实协议中,
z_{1-b} = x_{1-b} XOR h(f^{-1}(y_{1-b}))。对于 Bob* 来说,由于他不知道y_{1-b}的原像,根据硬核谓词的性质,h(f^{-1}(y_{1-b}))是伪随机的。因此,用随机比特R替换加密后的x_{1-b},Bob* 无法区分。这就证明了模拟的转录本与真实转录本计算不可区分。
不经意传输的扩展
上一节我们构建了 1-out-of-2 比特 OT,本节中我们来看看如何将其扩展。
1-out-of-n 不经意传输
在 1-out-of-n OT 中,发送者有 n 个比特 (x1, ..., xn),接收者有一个索引 i ∈ [1, n],最终只学到 xi。
构造方法是对之前协议的简单推广:
- 接收者 Bob 生成 n 个值
(y1, ..., yn),其中只有yi = f(r),他知道原像r;其他yj(j≠i) 均为随机值。 - 发送者 Alice 用陷门求出所有
yj的原像,并计算zj = xj XOR h(f^{-1}(yj))发送给 Bob。 - Bob 只能解密出
zi。
1-out-of-n 字符串不经意传输
现在,发送者的每个输入 xj 是一个长度为 L 的字符串,而不仅仅是比特。

构造方法非常简单:并行运行 L 次 1-out-of-n 比特 OT。
- 在第
j次(j=1 to L)调用中:- 发送者 Alice 的输入是
(x1[j], x2[j], ..., xn[j]),即所有字符串的第j个比特。 - 接收者 Bob 的输入始终是索引
i。 - 协议结束时,Bob 获得
xi[j]。
- 发送者 Alice 的输入是
- 经过
L次调用,Bob 就获得了整个字符串xi。
应用于安全两方计算
拥有了强大的字符串 OT 工具,我们已经可以为一大类函数构造安全两方计算协议。具体来说,当函数 F(a1, a2) 的某一个输入(例如 a2)取值空间较小(多项式大小)时。
假设 a2 有 n 种可能取值 {1, 2, ..., n}。协议如下:
- 将 Alice(输入
a1)作为 OT 发送者,Bob(输入a2)作为 OT 接收者。 - Alice 计算一个列表:对于每一个可能的
i ∈ [1, n],她计算Xi = F(a1, i)。这个列表(X1, ..., Xn)将作为她 OT 的输入。 - Bob 将他的实际输入
a2作为 OT 的选择索引i。 - 双方执行 1-out-of-n 字符串 OT 协议。
- 协议结束时,Bob 获得
X_{a2} = F(a1, a2),即所需的函数输出。
安全性直觉:Alice 不知道 Bob 的 a2,因此不知道 Bob 具体下载了列表中的哪个输出。Bob 除了 F(a1, a2) 外,也看不到列表中其他 n-1 个输出,这些输出可能包含了关于 a1 的额外信息,但被 OT 协议完美隐藏了。

回顾百万富翁问题
利用上述方法,我们已经解决了姚氏百万富翁问题!我们只需要假设其中一个百万富翁的净资产有一个多项式上限(现实中确实如此,比如不超过数万亿)。那么,另一个百万富翁就可以扮演 Alice 的角色,枚举所有可能的净资产值(从0到上限),计算一个“谁更富”的结果列表,然后通过 OT 让另一位百万富翁(Bob)安全地获取对应于其真实净资产的结果。
如果要求双方都获得输出,可以在协议结束后让 Bob 将结果告诉 Alice(在诚实但好奇模型下这是可行的),或者双方互换角色再执行一次协议。
总结与展望
本节课中我们一起学习了安全两方计算的基础知识。我们从姚氏百万富翁问题引入,形式化定义了安全两方计算的安全目标。接着,我们深入探讨了核心原语——不经意传输,并利用陷门单向置换和硬核谓词,构造了针对诚实但好奇对抗者的 OT 协议。我们还看到了如何将 OT 扩展为更通用的 1-out-of-n 字符串 OT。最后,我们展示了如何利用 OT 为输入空间较小的函数构造安全两方计算协议,从而理论上解决了百万富翁问题。
然而,仍然存在许多开放问题:
- 对抗完全恶意敌手:我们目前的协议只安全 against 诚实但好奇敌手。如何防御任意偏离协议的恶意敌手?
- 通用安全两方计算:当双方的输入都可能非常大(例如两个大型数据库)时,枚举所有可能性是指数级的,不可行。如何为任意多项式时间函数构造高效的安全两方计算协议?
这些激动人心的问题将在接下来的课程中探讨。
021:抛硬币协议与GMW编译器

在本节课中,我们将学习如何将仅能抵抗“半诚实”敌手的多方计算协议,升级为能抵抗“完全恶意”敌手的协议。核心工具是抛硬币协议和零知识证明。
抛硬币协议
上一节我们介绍了不经意传输,本节我们来看看一个看似简单但至关重要的基础问题:如何在互不信任的双方或多方之间,公平地生成一个随机比特。
问题定义
抛硬币协议是在两个或多个参与方之间执行的协议。协议结束时,所有参与方获得一个共同的比特 R。安全性要求是:即使其中一方或多方是恶意且任意偏离协议的,比特 R 是 0 或 1 的概率都应非常接近 1/2。用公式表示,对于安全参数 n,有:
Pr[R = 0] ≤ 1/2 + negl(n)
Pr[R = 1] ≤ 1/2 + negl(n)
其中 negl(n) 表示可忽略函数。
简单尝试及其缺陷
首先,我们考虑一些简单的方案为何失败。
以下是几种不安全的尝试:
- 尝试1:双方各自生成随机比特后异或。 问题在于后发送的一方可以根据先收到的比特,选择自己的比特以完全控制最终结果。
- 尝试2:双方先承诺各自的比特,再同时打开。 问题在于恶意方可以“复制”诚实方的承诺(例如,在基于离散对数的承诺方案中,通过同态操作生成一个对相同值的不同承诺),从而确保双方的比特相同,使最终结果恒为0。
正确的两方抛硬币协议
基于承诺方案,我们可以构建一个安全的协议。承诺方案具有隐藏性(在打开前,承诺值保密)和绑定性(一旦承诺,无法更改为其他值)。
协议步骤如下:
- Alice 生成随机比特
r_A,并发送其承诺comm(r_A)给 Bob。 - Bob 生成随机比特
r_B,并直接(不承诺)将r_B发送给 Alice。 - Alice 打开她的承诺,向 Bob 揭示
r_A。 - 双方计算最终结果
R = r_A ⊕ r_B。
安全性分析:
- 如果 Alice 是恶意的: 她可以任意选择
r_A。但由于 Bob 是诚实的,r_B是均匀随机的。因此,无论r_A为何值,R = r_A ⊕ r_B是均匀随机的(概率各为 1/2)。承诺的绑定性确保了 Alice 在步骤 3 中必须打开她在步骤 1 中承诺的值。 - 如果 Bob 是恶意的: 他可以在看到
comm(r_A)后选择r_B。根据承诺的隐藏性,Bob 无法获得关于r_A的任何信息(除了可忽略的优势)。因此,他无法使r_B与r_A相关,从而无法显著地使R偏向 0 或 1。
扩展到多方抛硬币
现在,我们将协议扩展到 n 个参与方 P1, P2, ..., Pn 的场景。安全性要求是:即使 n-1 方合谋,只要有一方是诚实的,最终比特 R 就接近均匀随机。
一个安全的协议如下:
- 承诺阶段: 参与方按顺序
P1, P2, ..., Pn依次发送对自己随机比特r_i的承诺。 - 打开阶段: 参与方按相反顺序
Pn, ..., P2, P1依次打开自己的承诺。 - 计算结果: 所有参与方计算
R = r_1 ⊕ r_2 ⊕ ... ⊕ r_n。
安全性思路:
假设只有 Pi 是诚实的。
- 在
Pi之后承诺的各方 (P_{i+1}到Pn),必须在Pi打开其承诺之前打开自己的承诺。根据承诺的隐藏性,他们无法使其比特r_{i+1}, ..., r_n与Pi的比特r_i相关联。 - 在
Pi之前承诺的各方 (P_1到P_{i-1}),其承诺在Pi选择r_i之前就已确定,因此与r_i独立。 - 结合以上两点,可以证明最终异或结果
R是接近均匀随机的。
生成长随机串: 要生成一个长的随机字符串,只需重复运行上述单比特抛硬币协议,并将结果拼接即可。
GMW编译器
现在,我们回到核心目标:将仅针对半诚实敌手安全的协议 π,编译成针对完全恶意敌手安全的协议 Σ。GMW(Goldreich-Micali-Wigderson)编译器使用抛硬币协议和零知识证明来实现这一目标。
为何需要编译?
一个完全恶意的敌手相比半诚实敌手能做两件额外的事:
- 偏离协议指令: 不按协议规定计算和发送消息。
- 使用有偏的随机数: 不使用均匀随机数,而使用精心选择的、可能破坏协议安全性的随机数。
GMW编译器分别用以下方法解决这两个问题:
- 零知识证明 解决第一个问题,确保各方都正确执行了计算。
- 抛硬币协议 解决第二个问题,确保各方使用的随机数是共同生成的、无偏的。
编译器构造(两方场景)
假设有一个针对半诚实敌手的两方计算协议 π。设双方为 P1(输入 x1)和 P2(输入 x2)。在协议 π 中,任何一方在计算下一轮消息时,都依赖于自己的输入、自己的随机带以及至今收到的消息历史。
编译后的协议 Σ 构造如下:
-
初始承诺:
P1生成随机串r1(将作为其在π中的全部随机性)。P1发送对其输入x1的承诺comm(x1)和对其随机串r1的承诺comm(r1)。P2同理,发送comm(x2)和comm(r2)。
-
生成无偏随机性:
P1和P2运行一个两方抛硬币协议,共同生成一个随机串ρ1。P1在后续协议π中实际使用的随机性将是r1 ⊕ ρ1。- 同理,双方再运行一次抛硬币协议生成
ρ2,P2实际使用的随机性将是r2 ⊕ ρ2。这确保了任何一方都无法单独控制协议中使用的随机性。
-
执行并证明:
- 双方开始模拟执行原协议
π。 - 假设
P1需要发送第一条消息m1。P1根据输入x1和随机性r1 ⊕ ρ1计算m1并发送。 - 关键步骤:
P1随后附上一个零知识证明,向P2证明:“我所发送的消息m1,确实是按照协议π的规定,使用承诺comm(x1)中的输入和承诺comm(r1)中的随机性与共同抛硬币结果ρ1异或后,正确计算得出的。” P2验证这个零知识证明。如果通过,说明P1的行为符合π。P2接着计算并发送自己的消息m2,并同样附上零知识证明,证明其计算是正确使用了自己的输入x2、随机性r2 ⊕ ρ2以及收到的消息m1。- 如此交替进行,直到协议结束。
- 双方开始模拟执行原协议
零知识证明的可行性: 上述证明语句是 NP 语句。证明者(如 P1)的“证据”是其承诺 comm(x1) 和 comm(r1) 的打开值。验证者(P2)虽然不知道这些打开值,但可以利用零知识证明的特性,在不获取证据的情况下确信该 NP 语句为真。
编译器如何应对恶意行为
- 如果恶意方试图使用坏随机数: 由于实际随机性是其初始承诺值
r_i与抛硬币结果ρ_i的异或,而ρ_i是双方共同生成的,恶意方无法完全控制最终的随机性。 - 如果恶意方试图发送错误消息: 他无法为错误的消息生成一个有效的零知识证明(基于承诺的绑定性和证明系统的可靠性),因此其作弊行为会被发现,协议可中止。
扩展到多方及效率
GMW编译器可以自然地扩展到多方场景,只需将两方抛硬币协议替换为上文描述的多方抛硬币协议即可。
需要指出的是,基础的GMW编译器是一个理论可行性结果。在实际中,为每个协议步骤构造零知识证明可能效率很低。后续有大量研究致力于改进其效率,但在本课程中,我们主要关注其核心思想和理论可行性。
总结

本节课中我们一起学习了:
- 抛硬币协议:利用承诺方案,我们可以在互不信任的参与方之间公平地生成随机比特。核心是通过承诺顺序和打开顺序的巧妙设计,结合承诺的隐藏性和绑定性来保证安全性。
- GMW编译器:这是一个强大的理论工具,能够将任何针对半诚实敌手安全的多方计算协议,编译成针对完全恶意敌手安全的协议。其核心思想是:
- 使用抛硬币协议强制各方使用无偏的随机性。
- 使用零知识证明强制各方每一步都遵循原协议的正确计算。
- 通过让各方在初始阶段承诺其输入和随机性,为零知识证明提供可验证的基础。
下一节,我们将探讨如何构建支持任意长输入的安全两方计算通用协议,即著名的“姚氏混淆电路”方案。
022:姚氏混淆电路 🧩

在本节课中,我们将学习安全多方计算的第三个协议——姚氏混淆电路。这个协议由Andrew Yao提出,它允许两个或多个参与方在不泄露各自私有输入的情况下,共同计算一个函数的结果。我们将从基本概念入手,逐步解析其工作原理、协议步骤以及安全考量。
协议背景与问题设定
上一节我们介绍了适用于一方输入较小的安全计算协议。本节中,我们来看看当所有参与方的输入都很大时(例如,双方都拥有大型数据库),如何进行计算。
我们有两个参与方:P1 和 P2。P1 拥有输入 x1,P2 拥有输入 x2。他们的目标是共同计算函数 F(x1, x2),同时除了最终结果外,不泄露任何关于对方输入的信息。我们暂时假设双方都是“半诚实”的,即他们会诚实地遵守协议,但会好奇地试图从协议交互中获取额外信息。
核心思想:在加密状态下执行电路
如果不考虑安全性,P1 可以直接将 x1 发送给 P2,由 P2 在电路上计算 F(x1, x2)。但这会泄露 P1 的输入。姚氏混淆电路的核心思想是让 P2 能够在一种“混淆”或“加密”的状态下执行电路,从而无法得知中间值和 P1 的输入。
我们做出以下简化假设:
- 函数
F由一个布尔电路C表示。 - 电路
C仅由“与非门”(NAND)构成,因为 NAND 门是通用门。 - 电路规模是多项式的。
在混淆电路中,有一个生成方(Sender,通常是 P1)负责准备混淆电路,一个评估方(Evaluator,通常是 P2)负责执行它。
混淆电路的构造 🛠️
以下是混淆电路的关键组成部分及其作用。
1. 为每条导线生成密钥
对于电路中的每一条导线 W(包括输入线、中间线和输出线),生成方选取两个随机的加密密钥:
KW0:代表导线W上的值为0。KW1:代表导线W上的值为1。
这些密钥将用于后续的加密操作。
2. 创建加密门表
目标是让评估方在拥有两条输入线的正确密钥后,能解密获得对应输出线的正确密钥,而无需知道导线上的实际比特值。
以一个连接输入线 I、J 和输出线 L 的 NAND 门为例。其真值表如下:
| 输入 I | 输入 J | 输出 L |
|---|---|---|
| 0 | 0 | 1 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
加密门表由四个密文项构成,每一项对应真值表的一行。对于输入组合 (a, b),生成方使用对应的输入线密钥 KIa 和 KJb 进行双重加密,将输出线的密钥 KLc(其中 c = NAND(a, b))加密进去。
例如,对于第一行 (a=0, b=0),生成的密文项是:
Enc(KI0, Enc(KJ0, KL1))
完整的加密门表包含四个这样的密文项。生成方会随机打乱这四个项的顺序后发送给评估方。这样,评估方即使拥有密钥,也无法从密文顺序推断出输入值。
3. 提供输出解码表
评估方通过解密门表,最终会获得输出线对应的密钥,但他不知道这个密钥代表 0 还是 1。因此,生成方还需要提供一个输出解码表。

对于每条输出线 W,解码表明确列出:
KW0-> 0KW1-> 1
这样,评估方在得到输出密钥后,查表即可获得最终的明文输出比特。
完整的两方计算协议 🤝
现在,我们将这些组件组合成一个完整的协议。
步骤 1:生成混淆电路
P1(生成方)为函数 F 的电路准备完整的混淆电路,包括所有导线密钥、加密门表和输出解码表。
步骤 2:传输输入线密钥
- 对于 P1 自己的输入
x1,P1 知道每条输入线对应的比特值,因此他可以直接将对应的密钥(KW0或KW1)发送给 P2。 - 对于 P2 的输入
x2,P1 不知道其值。这里使用 1-out-of-2 不经意传输。对于x2的每一个比特,P1 作为发送方提供该输入线对应的两个密钥(KW0, KW1),P2 作为接收方根据自己的输入比特选择接收其中一个密钥。P1 无法得知 P2 的选择,而 P2 也无法得知另一个未选择的密钥。
步骤 3:评估电路
P1 将加密门表和输出解码表发送给 P2。此时,P2 拥有了每条输入线的一个正确密钥,以及所有门表和输出表。P2 可以开始逐门解密:
- 对于每个门,P2 用自己拥有的两条输入线密钥,尝试解密门表中的四个密文项。
- 由于加密是确定性的,只有对应正确输入组合的那一项会被成功解密,得到一个看起来像密钥的数据(例如,后面带有一串零作为标识)。其他三项解密会得到乱码。
- P2 由此获得该门输出线的正确密钥。
- 重复此过程,直到获得所有输出线的密钥。
- P2 使用输出解码表,将这些密钥转换为最终的输出比特,即
F(x1, x2)。
步骤 4:输出结果
P2 将计算结果告知 P1(如果协议要求双方都获得输出)。
扩展到多方计算 👥
上述思想可以扩展到 n 个参与方 P1, P2, ..., Pn,各自拥有输入 x1, x2, ..., xn。
- 电路生成:指定一方(例如 P1)作为生成方,准备混淆电路。
- 密钥分发:
- P1 将自己的输入
x1对应的密钥直接发送给评估方(例如指定 Pn)。 - 对于其他各方
Pi (i=2 to n-1),他们通过不经意传输从 P1 处获得自己输入xi对应的密钥。 - 这些中间方
P2到P(n-1)将他们获得的密钥私下转发给评估方 Pn。
- P1 将自己的输入
- 电路评估:P1 将加密门表和输出解码表发送给 Pn。Pn 利用收集到的所有输入线密钥,评估混淆电路,得到最终结果并广播给所有人。
安全性注意:此协议在半诚实模型下是安全的。但是,如果生成方 P1 和评估方 Pn 合谋,他们可以联合推断出其他所有方的输入,因为 P1 知道密钥到比特的映射,而 Pn 拥有密钥。防止此类合谋需要更复杂的协议(如 GMW 协议)。
应对恶意敌手 ⚔️
上述协议仅针对半诚实敌手。如果生成方 P1 是恶意的,他可能构造一个错误的混淆电路,导致 P2 计算出错误的结果。
一种高效的解决方案是 “剪裁选择” 方法:
- P1 生成多个(例如
N个)独立的混淆电路。 - P2 随机选择其中一部分(例如一半)要求 P1 “打开”。
- P1 必须公开这些被选电路的内部所有随机性(所有密钥),P2 可以彻底检查它们是否正确构造。
- 如果所有被检查的电路都正确,P2 就有理由相信剩余的电路也很有可能是正确的。P2 然后用剩余的电路和双方的真实输入执行协议。
- 由于恶意 P1 不知道哪些电路会被检查,如果他准备了大量错误电路,很可能在检查阶段就被发现。P2 可以对剩余电路的结果取多数值作为最终输出,以进一步提高正确性。
这种方法避免了使用计算昂贵的通用零知识证明。
其他高级密码学概念掠影 ✨
课程最后简要提到了几个现代密码学的重要方向:
- 全同态加密:允许直接对密文进行计算,得到的结果解密后等同于对明文进行同样计算的结果。这实现了“在加密数据上计算”,对于云计算隐私至关重要。
- 非延展性加密/承诺:确保敌手在看到某个密文/承诺后,无法构造出另一个与之相关的密文/承诺。这在电子拍卖等场景中非常重要,可以防止对手根据你的出价密文构造一个刚好高于你的出价。
- 程序混淆:目标是分发一个程序的混淆版本,使得用户能够运行它并获得输出,但无法理解其内部逻辑或进行逆向工程。这是一个非常困难但活跃的研究领域。
本节课中我们一起学习了姚氏混淆电路,这是一种强大且高效的安全多方计算协议。我们理解了其通过为电路导线加密、构造加密门表,并结合不经意传输来分发输入密钥的核心机制。我们还探讨了其扩展到多方场景的步骤,以及如何通过“剪裁选择”方法来防御恶意敌手。最后,我们概览了全同态加密、非延展性和程序混淆等前沿密码学概念,它们正在不断拓展密码学的能力边界。
023:作业解答

在本节课中,我们将一起回顾并解答课程最后两次作业(作业4和作业5)中的核心问题。我们将重点讲解承诺方案的组合、零知识证明的构造、非交互式证明的不可能性,以及一些基础密码学协议的设计思路。
问题一:承诺方案的组合
上一节我们介绍了作业的背景,本节中我们来看看如何组合有缺陷的承诺方案以构建一个同时满足隐藏性和绑定性(Binding)的安全承诺方案。
1.1 两个方案均满足绑定性,仅一个满足隐藏性
给定两个承诺方案 Com1 和 Com2,它们都满足绑定性,但只有其中一个满足隐藏性。目标是构建一个新的安全承诺方案 Com。
核心思路:使用一个 (2,2) 秘密共享方案。将待承诺的消息 m 分割成两个份额 m0 和 m1,使得仅知其中一个份额无法获得 m 的任何信息。然后,用 Com1 承诺 m0,用 Com2 承诺 m1。最终的承诺是这两个承诺的拼接:Com(m) = (Com1(m0), Com2(m1))。
- 绑定性:由于
Com1和Com2都具有绑定性,m0和m1在承诺后即被固定,因此原始消息m也被唯一确定。 - 隐藏性:由于
Com1和Com2中至少有一个具有隐藏性,因此至少有一个份额(m0或m1)的信息被隐藏。结合 (2,2) 秘密共享的性质,整个消息m的信息也被隐藏。
1.2 两个方案均满足隐藏性,仅一个满足绑定性
给定两个承诺方案 Com1 和 Com2,它们都满足隐藏性,但只有其中一个满足绑定性。目标是构建一个新的安全承诺方案 Com。
核心思路:对同一条消息 m 进行两次承诺。最终的承诺定义为:Com(m) = (Com1(m), Com2(m))。在打开承诺时,必须同时打开两个承诺,且解密出的消息必须相同,验证者才接受。
- 绑定性:由于
Com1和Com2中至少有一个具有绑定性,m在承诺阶段后即被该方案固定。攻击者无法在不被检测到的情况下,同时改变两个承诺中的消息使其仍然一致。 - 隐藏性:由于
Com1和Com2都具有隐藏性,因此承诺值不会泄露m的信息。
1.3 一个方案满足隐藏性,另一个满足绑定性
给定两个承诺方案,一个仅满足隐藏性,另一个仅满足绑定性。问题在于,仅凭这两个方案作为基础模块,能否在不引入其他未经验证的密码学假设(如单向函数)的情况下,构建一个同时满足隐藏性和绑定性的承诺方案?
结论:不能。
解释:
- 一个仅满足绑定性的承诺方案可以非常简单地构造(例如,直接发送明文消息),这不需要任何密码学假设。
- 一个仅满足隐藏性的承诺方案也可以简单构造(例如,发送一个随机串),同样不需要密码学假设。
- 然而,一个同时满足隐藏性和绑定性的“好”的承诺方案,意味着单向函数的存在。因为如果能从承诺值反推出消息和随机数,就破坏了隐藏性。
- 因此,要构建一个安全的承诺方案,本质上需要假设单向函数存在(一个未经验证的密码学假设),或者去证明
P ≠ NP(这远超当前课程范围)。仅凭题目给出的两个“有缺陷”的模块,无法完成构建。
问题二:顶点覆盖问题的零知识证明
本节我们探讨如何为NP完全问题——顶点覆盖(Vertex Cover)设计一个零知识证明协议。
问题定义:给定一个图 G=(V,E) 和一个整数 B,证明者想向验证者证明:存在一个顶点集合 S ⊆ V,其大小 |S| ≤ B,且 S 覆盖了图 G 的所有边(即每条边至少有一个端点属于 S),同时不泄露 S 的具体信息。
协议设计:
以下是协议的核心步骤:
- 证明者:随机选择一个置换
π,计算置换后的图G‘ = π(G)。然后,承诺整个图G‘的邻接矩阵(即对n x n矩阵的每个元素进行承诺)。 - 验证者:发送一个挑战比特
ch ∈ {0, 1}。 - 证明者:根据
ch进行响应:- 如果
ch = 0:打开所有对图G‘的承诺,并发送置换π。验证者检查G‘是否确实与G同构。 - 如果
ch = 1:令S‘ = π(S)为置换后的顶点覆盖。证明者打开所有不在S‘中的顶点之间的边的承诺(即,对于所有两个端点都不在S‘中的边,揭示其承诺值为0)。验证者检查所有被打开的边是否确实为0(即不存在),并确认被揭示的顶点数至少为|V| - B(这对应于S‘的大小至多为B)。
- 如果
协议分析:
- 完备性:如果证明者诚实且陈述为真,验证者总是接受。
- 可靠性:如果陈述为假(不存在大小≤B的顶点覆盖),那么作弊的证明者
P*在第一步承诺的图G‘只有两种情况:G‘不与G同构:当ch=0时会被发现。G‘与G同构:那么G‘也不存在小顶点覆盖。当ch=1时,P*无法找到一个大小≤B的集合S‘使得其外无边,因此会被发现。
无论P*选择哪种策略,它被抓住的概率至少是1/2。通过顺序重复执行,可以将错误概率降至可忽略水平。
- 零知识性:模拟器可以猜测挑战比特
ch‘。- 如果猜
ch‘=0,它承诺一个随机置换后的图G‘。 - 如果猜
ch‘=1,它承诺一个空图(所有边为0)。
如果猜对,则模拟成功;如果猜错,则回滚重试。由于承诺方案具有隐藏性,模拟器产生的视图与真实交互视图计算不可区分。
- 如果猜
问题三:非交互式零知识证明的不可能性
本节我们论证:对于没有高效概率算法(即不在期望多项式时间 BPP 内)的语言 L,不可能存在非交互式零知识(NIZK)证明系统。
证明思路(反证法):
假设存在一个针对语言 L 的NIZK证明系统。我们可以利用其模拟器 S 来构造一个判定 L 的期望多项式时间算法 A,这与 L 的定义矛盾。
算法 A 如下:
- 输入实例
x。 - 运行NIZK的模拟器
S(x),得到一个证明π‘。 - 运行验证者算法
V(x, π‘)。 - 如果
V接受,则输出“x ∈ L”;否则输出“x ∉ L”。
分析:
- 算法
A是期望多项式时间的,因为它调用了期望多项式时间的模拟器S和多项式时间的验证者V。 - 我们需要证明
A能以高概率正确判定L。- 情况1:
x ∈ L。根据零知识性,模拟器产生的证明π‘必须与真实证明计算不可区分。由于真实证明会被V以概率1接受,因此V接受π‘的概率也必须是1 - negl(n)。所以A输出正确的概率极高。 - 情况2:
x ∉ L。根据NIZK的可靠性,任何(即使是计算无界的)证明者都无法让V接受一个错误陈述的证明,除非以可忽略的概率。模拟器S作为一个特定的算法,它产生的证明π‘被V接受的概率也必须是可忽略的。否则,S本身就可以作为一个作弊的证明者来破坏可靠性。因此,A输出“x ∉ L”的概率极高。
- 情况1:
综上,算法 A 以高概率正确判定 L,这与 L 没有期望多项式时间算法的前提矛盾。因此,最初的假设(存在NIZK)不成立。
关键点:这个论证之所以适用于NIZK而不适用于交互式零知识(IZK),是因为在NIZK中,模拟器 S 可以独立生成证明,这与作弊证明者的能力相同。而在IZK中,模拟器拥有“回滚”验证者的超能力,但真实的作弊证明者没有这个能力,因此无法用模拟器来构造判定算法。
问题五:见证不可区分性与并行重复
本节我们简要介绍如何构建一个三轮的、具有高可靠性参数的见证不可区分(Witness Indistinguishable, WI)证明系统。
目标:构造一个协议,其中证明者可能使用两个不同的见证 w0 或 w1 来证明同一个陈述 x ∈ L,使得验证者无法区分证明者使用的是哪一个见证。
构造方法:直接对之前学过的图三着色(Graph 3-Coloring)的零知识证明协议进行 n 次并行重复。
为什么可行:
- 零知识性蕴含WI:由于图三着色协议是零知识的,它自然也是WI的(如果验证者能区分见证,它也就学到了一些知识)。
- 并行重复提升可靠性:单个三轮协议的可靠性误差是
1/2。进行n次并行重复后,可靠性误差可以降至(1/2)^n,从而变得非常小。 - WI在并行重复下保持:这是关键点。对于WI,我们可以使用“混合论证(Hybrid Argument)”来证明并行重复后的协议仍然是WI的。
- 定义一系列混合实验
H0, H1, ..., Hn。在Hi中,前i个并行会话使用见证w1,后n-i个会话使用见证w0。 H0对应所有会话使用w0,Hn对应所有会话使用w1。- 相邻两个混合实验
Hi和Hi+1仅在第i+1个会话的见证上不同。如果存在区分器能区分H0和Hn,则必然能区分某对相邻的混合实验,从而可以构造一个算法来攻破单个会话的WI性质(通过将该会话嵌入到混合实验中,并自行模拟其他所有会话)。
- 定义一系列混合实验
这与零知识证明不同,零知识证明的模拟器在并行会话中会遇到困难(需要同时猜测多个挑战),但WI的证明不需要模拟器,只需要依赖单个会话的WI属性即可。
问题六:茫然传输的扩展
本节我们看如何用更强的茫然传输(Oblivious Transfer, OT)协议构造较弱的OT,以及如何扩展消息长度。
6.1 从 1-out-of-4 OT 构造 1-out-of-2 OT
发送者有两条消息 (a0, a1),接收者有一个选择比特 b,希望获得 ab 而对另一条消息一无所知。
构造:使用一个 1-out-of-4 字符串OT协议。
- 发送者将输入设置为
(a0, a1, 0, 0)。 - 接收者根据选择比特
b设置选择值:如果b=0,则选择第1条;如果b=1,则选择第2条。 - 协议执行后,接收者获得
ab,并且对a_{1-b}一无所知(因为OT协议保证了这一点),同时额外的两个0值没有影响。
6.2 扩展OT的消息长度
给定一个 1-out-of-4 OT 协议,其发送者的输入是 n 比特长的字符串。我们希望构造一个协议,使发送者的输入可以是更长的 l 比特字符串(l >> n)。
构造:利用伪随机生成器(PRG)G: {0,1}^n → {0,1}^l。
- 发送者生成4个随机的
n比特种子s1, s2, s3, s4。 - 发送者计算
c_i = m_i ⊕ G(s_i),其中i=1,...,4,m_i是长的输入消息,⊕表示按位异或。 - 发送者和接收者运行基础的 1-out-of-4 OT 协议,发送者的输入是
(s1, s2, s3, s4)。 - 接收者根据其选择
j获得种子s_j。 - 发送者公开发送
(c1, c2, c3, c4)。 - 接收者使用获得的
s_j计算G(s_j),然后恢复消息m_j = c_j ⊕ G(s_j)。
安全性:
- 对于接收者选择的消息
m_j,他可以正确恢复。 - 对于其他消息
m_i (i≠j),由于接收者不知道种子s_i,而G(s_i)是伪随机的,因此c_i相当于一个一次一密加密,保护了m_i的信息。
回顾作业四的核心问题
问题一:支持线性组合的解密
场景:多个消息 s1, ..., sn 被加密后存储在服务器。用户想要获取 ∑_{i∈A} c_i * s_i,其中 A 是一个索引集合,c_i 是小整数。
方案:使用ElGamal加密的变种。
- 密钥生成:与标准ElGamal相同。私钥
x,公钥h = g^x。 - 加密消息
s:Enc(s) = (g^r, h^r * g^s)。注意这里加密的是g^s而不是直接加密s。 - 解密:给定密文
(c1, c2),计算g^s = c2 / (c1^x)。由于s是一个小整数(题目给定),可以通过暴力计算g^0, g^1, g^2, ...并与g^s比对来恢复s。 - 计算线性组合:
- 给定密文
(c1_i, c2_i) = Enc(s_i)。 - 计算组合密文:
(C1, C2) = ( ∏_{i∈A} c1_i^{c_i}, ∏_{i∈A} c2_i^{c_i} )。 - 根据ElGamal的同态性质,
(C1, C2)解密后得到的是g^{∑_{i∈A} c_i * s_i}。 - 由于
∑ c_i * s_i仍然是一个小整数(小整数之和),可以同样通过暴力搜索从g^{∑ c_i * s_i}中恢复出最终的线性组合结果。
- 给定密文
问题二:简单的安全投票方案
场景:n 个投票者,k 个服务器。每个投票者 i 有一个投票 v_i ∈ {0,1}。目标是统计总票数 V = ∑ v_i,但不泄露任何个人的投票。
方案:使用模 p(p > n)下的加法秘密共享。
- 每个投票者
i将其投票v_i在模p下分割成k个份额:v_i = ∑_{j=1}^{k} s_{i,j} mod p。 - 投票者
i将份额s_{i,j}秘密地发送给服务器j。 - 投票结束后,每个服务器
j计算自己收到的所有份额之和:S_j = ∑_{i=1}^{n} s_{i,j} mod p。 - 每个服务器广播自己的
S_j。 - 任何人可以计算总和:
V = ∑_{j=1}^{k} S_j mod p = ∑_{i=1}^{n} v_i mod p。由于p > n且每个v_i是0或1,所以V就是实际的总票数,不会取模溢出。
安全性:每个服务器只看到一个份额,无法得知任何投票者的完整信息。只有所有服务器合谋才能恢复个人投票。
问题三的扩展:如果要求任何 t 个服务器就能统计结果,而少于 t 个则不能,只需将上述加法秘密共享替换为Shamir秘密共享,并在统计时由服务器执行多项式插值来恢复总票数即可。

本节课总结
本节课中我们一起学习了:
- 承诺方案的组合技巧:如何利用有缺陷的组件构建安全的承诺方案,并理解了其安全性的边界。
- 针对NP问题(顶点覆盖)的零知识证明构造:学习了如何通过置换和承诺来隐藏见证,并设计验证挑战。
- 非交互式零知识(NIZK)的局限性:证明了对于困难问题,NIZK可能不存在,关键点在于模拟器的能力与作弊证明者等价。
- 见证不可区分性(WI):了解了WI的概念,以及如何通过并行重复来提升可靠性同时保持WI属性。
- 茫然传输(OT)协议的构造与扩展:学习了如何降级使用OT以及利用PRG扩展OT的消息空间。
- 同态加密与安全计算的应用:回顾了如何利用ElGamal变种支持对加密数据的线性组合计算,以及如何使用秘密共享构建简单的安全投票协议。
这些内容涵盖了现代密码学中协议设计与分析的核心思想,希望对你理解密码学的深度与广度有所帮助。

浙公网安备 33010602011771号