私钥加密
"What's that you are reading?"
"It's about Crytography."
"Like secret messages?"
"Not secret, that's the brilliant part. Messages that anyone can see but no one knows what they mean, unless you have the key."
—— The Imitation Game
私钥密码学(Private Key Crypography)
\(\newcommand{\Enc}{\text{Enc}}\newcommand{\Dec}{\text{Dec}}\newcommand{\M}{\mathcal{M}}\newcommand{\K}{\mathcal{K}}\newcommand{\C}{\mathcal{C}}\)假设A要给B发送一条消息(message),一条消息可以看作一个(二进制的)字符串。然而,这条消息可能会被窃听者(eavesdropper)截获。为此,我们想要对这条消息做某种变换以后再发出去,使得即便被窃听者截获也不会暴露消息内容。这种变换就称为加密(encryption),加密前的消息称为明文(plaintext),加密后的消息称为密文(cyphertext)。接收方需要能够在接收到密文以后把密文变换回明文,这个变换称为解密(decryption)。
消息的发送方A与接收方B如何能够实现有效的加密和解密,使得窃听者不能轻易破解消息内容呢?一种方法是,它们提前约定好一个密钥(key)(在保证不被监听的时候,例如物理意义上见面),这个密钥全世界只有A和B两人知道,所以称为私钥(private key)。加密和解密都关于这个密钥来进行。换言之,加密过程实际上是一个关于明文和密钥的函数,解密是一个关于密文和密钥的函数。
我们把这整个过程严格描述如下:
- 所有能够被发送的消息构成消息空间(message space),这是一个二进制字符串的集合,记为\(\M\)。A会从消息空间中选择一条消息\(m\in \M\)发送,我们用随机变量\(M\)表示被选择的消息,这个随机变量的样本空间就是\(\M\)。
- 所有可能被选取的密钥构成密钥空间(key space),这也是一个二进制字符串的集合,记为\(\K\)。A,B会从\(\K\)中以某个概率分布(通常是均匀分布)选取一个私钥\(k\in \K\),这个选取过程就是算法\(\text{Gen}\),记作\(k\leftarrow \text{Gen}\)。我们也用随机变量\(K\)表示这个被选的私钥,该随机变量的样本空间就是\(\K\)。
- 如果A要发送消息\(m\in \M\),那么他会基于密钥\(k\)用加密算法\(\text{Enc}\)生成密文\(c=\text{Enc}_k(m)\)。所有可能的密文构成密文空间(cypher space),这也是一个二进制字符串的集合,记为\(\C\)。同样,我们用一个随机变量\(C\)表示密文,注意\(C\)的样本空间为\(\C\),所以同时有样本空间的随机性和密钥空间的随机性,\(M,K,C\)始终满足关系式\(C=\text{Enc}_K(M)\)。
- B在接收到\(c\)以后基于密钥\(k\)用解密算法\(\text{Dec}_k(c)\)还原出原文。为了保证这一过程的正确性,显然我们应当要求加密算法和解密算法满足关系\(\text{Dec}_k(\text{Enc}_k(m))=m\)。
- 我们用三元组\((\text{Gen},\Enc,\Dec)\)表示一个方案。
为了能从一个分布中(例如均匀分布)中选出一个样本,在密码学中我们假定我们有能力生成随机数。
在私钥密码学中,我们只要求密钥这唯一的一个信息是保密的。也就是说,我们默认窃听者已经提前掌握所有关于加密算法、解密算法、消息空间和密钥空间及其对应的概率分布等等的信息。这个原则称为Kerckhoffs’ Principle。这是合理的,因为通常一个加密算法是难以保密的,并且这也让要保护的信息减少了许多。同时,把一个加密算法公之于众,也有利于公众一起来检查这个加密算法的保密性有多好。现代密码学和古典密码学的不同就在于,现代密码学的目标不是希望能够通过侥幸逃脱窃听者的攻击,而是希望能建立一套严格的理论来描述加密方案的安全性,使得我们能够选择充分安全的加密方案。这里的充分安全就是指我们可以从数学上证明窃听者破译的概率微乎其微。
完全保密(Perfect Secrecy)
安全是一个相对的概念,并不存在绝对的安全。例如,攻击者总可以穷举密钥空间并逐一检查翻译出来的内容是否合理来完成破译,或者即便是随机乱猜也有大于0的概率会恰好猜对明文。那么,什么样的加密方案足以被认为是安全的呢?
最理想的加密应当是每条消息的密文都不会暴露原文的任何“信息”。这就是信息论意义下的安全性,要发送的消息以及其对应的密文可以看作两个随机变量\(M,C\)。因此最完美的加密应当使得随机变量\(M\)和\(C\)的互信息为0——\(I(M;C)=0\)。我们定义:如果一个加密方案满足\(\forall m\in \M,\forall c\in C\),\(\Pr[M=m\mid C=c]\)\(=\Pr[M=m]\),就称这个方案是完全保密的(Perfect Secret)(可以证明这一定义等价于\(M,C\)互信息为0)。如何理解这一定义呢?注意到\(\Pr[M=m]\)这一项就是各个消息被发送的概率,这是窃听者已知的。\(\Pr[M=m\mid C=c]\)这一项可以看作是窃听者做的推测,假如密文\(c_0\)暴露了大量信息(比如它几乎就和明文一模一样),那么窃听者就会推测有很大概率明文就是某个\(m_0\),那么\(\Pr[M=m_0\mid C=c_0]\)这一项的值就会非常接近\(1\),当然也就不满足\(\Pr[M=m_0\mid C=c_0]=\Pr[M=m_0]\)。所以,如果一个方案是完全保密的,也即\(\Pr[M=m_0\mid C=c_0]=\Pr[M=m_0]\)对任意的\(m_0,c_0\)都要成立,那么说明窃听者所能推测的概率分布应当就是消息发送的概率分布——密文被窃听并不能为窃听者提供任何帮助。完全保密是理论上最安全的加密方案。
今后如果没有特别说明,可以默认消息空间中消息的分布是均匀分布的,也即每条消息被发送的概率相等。可以默认密钥空间中密钥的分布是均匀分布的,也即每条密钥被选择的概率相等。
以上完全保密条件等价于:对于每条的密文\(c\),\(\forall m,m'\in \M,\Pr[\text{Enc}_K(m)=c]\)\(=\Pr[\text{Enc}_K(m')=c]\)(注意这里的样本空间是密钥空间),也即一个加密方案是完全保密的当且仅当每条密文被消息空间中的任意一条消息加密的可能性都是相等的。这一点可以用简单的概率变换证明。直观上,如果窃听者能够根据某个\(c\)推断出这是\(m\)的概率比这是\(m'\)的概率更大,那么说明密文\(c\)势必暴露了某些信息使得他能做出这一推断。
完全保密条件还有另一种等价定义,称为“完全不可区分(perfect indistinguishable)”。完全不可区分是基于“实验”定义的,我们假想有一个adversary和我们举行一次类似“博弈”一样的实验。这种定义方法在现代密码学中非常重要且非常常见。对于一个特定的加密方案,我们描述下面这样一个实验:首先,让adversary从\(\M\)中选取两条明文\(m_0,m_1\)交给我们;我们让\(\text{Gen}\)生成一个密钥\(k\),并随机选取\(m_0\)或\(m_1\)用\(\Enc_k\)做加密操作得到\(c\);把\(c\)交还给adversary,让他猜这是由\(m_0\)加密得到的还是由\(m_1\)加密得到的。基于这个实验,我们定义“完全不可区分性”:加密方案是完全不可区分的,当且仅当任何adversary在这个实验中猜对的概率都不超过\(1/2\)。(注意到,随机乱猜的概率恰好就是\(1/2\))。可以证明,完全不可区分和完全保密是等价的。直观上这和上一段中的等价条件是一样的,窃听者得到密文\(c\)并不能为区分任何两条消息提供任何信息。
我们能否在构造出一个具体的完全保密方案?凯撒密码不是完全保密的,因为相同的字符会被加密为相同的密文字符,那么在得到密文的基础上只需观察重复的字符就可以排除掉消息空间中的一部分消息,因此密文暴露了信息。基于替换的其他一系列古典加密方案都可以证明不是完全保密的。
下面这种称为“one-time pad”的加密方案是完全保密的:假设消息是长度固定的二进制串(如果长度不同就补0),密钥是与明文相同长度的二进制串。加密时,用密钥与明文做按位异或,解密时用密钥与密文做按位异或。因为做两次异或刚好抵消,所以这是一个可行的加密方案。One-time pad是完全保密的,由于密钥的选取是等概率的,所以对于任意给定的密文,其对应任何明文的概率都是相等的。但是,每当我们需要发送一条新的信息时,发送者和接收者之间必须重新生成并约定密钥,这就是为什么这是一个“one-time”的方案。假设同一个密钥被反复使用,那么窃听者只需要对两条密文做异或,就可以得到两条明文的异或,这就暴露了任意两条明文之间在哪些位上相同,哪些位上不同——暴露了大量信息。另一方面,one-time pad要求密钥长度和明文长度相同,这使得密钥保存更加不方便。
注意,one-time pad的加密算法是确定性的。因为密钥是在发送之前就选定的。
事实上,不仅在于one-time pad,密钥长度太长是完全保密本身的缺陷。可以证明:任何完全保密方案的密钥空间都必须大于等于消息空间的大小。进而,当消息空间和密钥空间中字符串的长度都固定时,想要完全保密则密钥的长度必须大于等于消息本身的长度。反证法,假如密钥空间小于消息空间,那么假如此时给定一条密文\(c\),一定有一部分消息空间中的消息不可能作为对\(c\)解密的结果:因为我们用密钥空间中的一个密钥在\(c\)上运行解密程序只会得到一个结果,因此总的结果数量不可能超过密钥空间的大小,因此小于消息空间的大小。那么这部分消息也就是不可能加密成为\(c\)的,这就说明\(c\)暴露了信息。
计算安全性(Computational Security)
由此可见,完全保密在现实中几乎是不可能实现的:我们不可能总是使用与原文长度相同的密钥来加密。这不仅不方面而且容易受攻击。所以我们认为完全保密的要求太强了。而且,完全保密在讨论信息论意义下的安全性,它假设窃听者有无限的空间和时间来做计算。在现实中,窃听者总是有着一些计算能力限制。在现实中,如果我们能够保证密文只泄露了一点点信息,以至于窃听者需要很大的空间和时间资源才能以很小的概率破解密码,我们也认为这样的加密方案是安全的。相对于“信息论意义下的安全”,这称为“计算意义下的安全”。下面我们来严格定义它。
我们为每个加密方案假定一个系数\(n\),称这个方案是关于\(n\)的——我们可以这样定义一个加密方案:密钥生成算法\(\text{Gen}\)会接受一个输入\(n\),输出一个长度为\(n\)的二进制随机串。这样,\(n\)越大,密钥空间也越大,安全性也就越高。我们总是认为,窃听者只有关于\(n\)的多项式复杂度时间来破解密码。这样假定的好处在于,当\(n\)很大时多项式级别的复杂度已经足够大了,我们无需关心更长时间(比如指数的复杂度)以后窃听者的行为;另一方面,现有的大多数计算模型都可以被证明在多项式内和图灵机等价,这样我们的算法就具有很好的普适性;多项式还有良好的闭包性质,即调用多项式次多项式算法依然是多项式复杂度的。其次,我们希望窃听者破译密码的概率足够小,我们同样可以基于系数\(n\)定义“足够小”:称函数\(f(n)\)是可忽略的(negligible),如果对于任意多项式\(p(x)\)都存在\(N>0\)使得\(\forall n>N\),\(f(n)<\dfrac{1}{p(n)}\)。我们通常把这样的\(f(n)\)简记为\(\text{negl}\)。容易证明,一个可忽略的函数乘以任意多项式依然是可忽略的。
如果一个关于系数\(n\)的加密方案满足任意窃听者在关于\(n\)的多项式时间内只能以关于\(n\)可忽略的概率破译密码,那么这个方案就被称为是计算安全(Computational Secure)的。
EAV-security
当我们考虑计算安全性时,我们假定敌手具有多项式时间算力。而为了进一步的讨论,我们还需要对敌手的能力做进一步的假定。例如,敌手是否能够截获我们发送的每一条密文还是只能截获其中的一小部分?敌手是否已经掌握了某几条明文和密文的对应关系?虽然敌手不能从密文翻译成明文,但是否有能力把明文翻译成密文?
现在我们对敌手的能力做出以下假定:在一个私钥加密的消息发送途中,敌手仅仅窃听到了某一条密文\(c_0\),除此之外没有其他特殊的能力(例如它并未截获其它不同的密文,不知道任何明文和密文的配对,但是它知道发送消息的概率分布以及加密方案等等)。在这样的假定下,我们这样定义安全性(再一次,我们用实验的方式来定义):首先,让adversary从\(\M\)中选取两条明文\(m_0,m_1\)交给我们;我们选取\(n\)输入\(\text{Gen}\)生成一个密钥\(k\),并随机选取\(m_0\)或\(m_1\)用\(\Enc_k\)做加密操作得到\(c\);把\(c\)交还给adversary,让他猜这是由\(m_0\)加密得到的还是由\(m_1\)加密得到的。如果任何adversary在这个实验中猜对的概率都不超过\(1/2+\text{negl}(n)\),就称这个加密方案是EAV-secure的(EAV指代eavesdropper)。
EAV-security保证了拥有多项式算力的敌手不能由单一一条窃听到的密文以显著的概率分辨任何两条明文,说明密文几乎没有泄露关于明文的任何信息。
下面我们来构造一个具体的EAV-secure的加密方案。还记得在one-time pad中,我们用了一个与原文相同长度的二进制串来做密钥。现在我们的要求由完全保密放松到了EAV-secure,所以我们期待密钥可以变短一点,但依旧能具有one-time pad的效果。因此,一个关键的问题是:是否存在一个确定性的(deterministic)程序,它接受一个较短的真随机串,输出一个“看上去随机”的更长的随机串?如果存在这样的程序,那么我们就可以用短串做密钥,然后用这个程序把串变成长度与明文相等的串再做加密,这样就实现了密钥长度短于明文长度的one-time pad。这个确定性的程序就称为一个伪随机生成器(pseudorandom generator)。
伪随机生成器是一个确定性的程序\(G\),输入一个较短的二进制串(通常称为种子(seed)),输出一个较长的二进制串。不妨设输入长度和输出长度都是固定的,输入长度为\(n\),输出长度为\(\ell(n)\),\(\ell(n)>n\)。既然输入最多\(2^n\)个字符串,那么最多只有\(2^n\)个不同的输出,而真随机生成器应当能够输出\(2^{\ell(n)}\)个不同的串,所以伪随机生成器不可能实现真正的更长的随机。然而在计算安全性意义下,我们只需要保证生成器给出的输出能够以很大的概率骗过所有多项式算力的用户就可以了。怎么样算“骗过”了呢?人们可以设计各种各样的分辨器(distinguisher),当我们输入一个随机串时,它会判断这个串是否是随机生成的。我们不知道怎么样的分辨器是最精确的,但我们知道一个伪随机生成器应当能骗过任何一台多项式算力的分辨器才应当被称为是“随机”的。因此我们定义:对于给定的\(n\)和\(\ell(n)\),设\(s\in\{0,1\}^n\)是由我们随机均匀选取的一个串,\(r\)是\(\{0,1\}^{\ell(n)}\)中随机均匀选取的一个串,如果对于任意的多项式算力的分辨器\(D\),对任何\(s\)都成立\(\Pr[D(G(s))=1]-\Pr[D(r)=1]=\text{negl}(n)\),就称\(G\)是一个伪随机生成器。
到目前为止,人们还不能不基于任何假设地证明伪随机生成器的存在性。人们已经可以证明,如果假定one-way function存在,那么伪随机生成器存在。以及,人们已经构造了一些具体的方案(比如stream cipher),这些方案现在还没有找到无法骗过的分辨器。
以上用伪随机生成器来生成“密钥”的算法是EAV-secure的。假如我们不用伪随机生成器而用一个真正的随机生成器,那么这从分析的角度和原来的one-time pad是相同的,因此是完全保密的。因此任给两条消息和一条由其中一条消息加密的密文我们都只能以\(1/2\)的概率辨别原始消息。当我们把真正的随机生成器替换为伪随机生成器以后,如果突然间我们变得能够以超过\(1/2+\text{negl}\)的概率实现辨别了,那么我们可以由密文与消息做异或,得到伪随机生成器的两个输出——也就是说我们能以超过\(1/2+\text{negl}\)的概率辨别伪随机生成器的两个输出,这与伪随机生成器的定义矛盾!可见,只要伪随机生成器存在这一假设成立,以上加密算法就是EAV-secure的。
CPA-Security
在EAV-security中我们假设的情景是敌手截获单条密文。现在我们加强敌手的能力,也就是考虑一种更强的安全性——敌手能够“控制”消息发送方去发送某条敌手希望发送的消息\(m\),并且截获\(m\)加密后的密文\(c\)。这种控制可以发生多次,使得敌手能够获得多个配对\((m_1,c_1),(m_2,c_2),\cdots\)。这种攻击方式称为选择明文攻击(Chosen-Plaintext Attack, CPA)。
为了定义CPA-security,我们需要引入oracle的概念。一个oracle是一个黑箱,在这里我们假定敌手拥有一个encryption oracle:当给这个oracle输入一个字符串\(s\),它会输出\(\text{Enc}_k(s)\)。敌手并不能知道\(k\)是什么,但他确实获得了加密后的结果。我们构造下面的实验来定义CPA-security:首先生成密钥\(k\),并假定敌手拥有一个关于\(k\)和加密方案\(\Enc_k\)的oracle;敌手选择两条信息交给我们;我们选择一条信息用\(\Enc_k\)加密以后交还给敌手;敌手现在有权访问oracle关于\(n\)的多项式次(注意总的明文数量是\(2^n\),也即敌手能够输入oracle的明文数远小于总的明文数),然后做出猜测。如果敌手猜对的概率不超过\(1/2+\text{negl}(n)\),就称这个方案是CPA-secure的。
我们注意到,根据以上定义,一个CPA-secure的加密算法不可能是确定性算法。因为假设加密方案是确定性的,那么当敌手选定\(m_0,m_1\)以后,他可以把它们放进encryption oracle里得到\(c_0,c_1\),既然方案是确定性的,他在猜测时只需要看拿到的是\(c_0\)还是\(c_1\)就好了。换言之敌手猜对的概率是\(100\%\)。因此,要想使得一个方案是CPA-secure的,用同一个密钥加密时每次都必须生成不同的密文。
在现实中,真的有敌手能够实现选择明文攻击吗?如何实现对要发送消息的控制呢?二战中,美国在对日本的中途岛海战的胜利就得益于一次选择明文攻击。当时美国破译了日本的部分密文,但无法破译密文中的一个词“AF”。经过调查以后,美军猜测“AF”很可能是“中途岛”的意思。为了确认这一点,美方传播假消息称中途岛缺水,而后截获并部分破译出一条“AF缺水”的密文。这样,美军就确定“AF”确实是中途岛的意思,最终打赢了中途岛海战。可见,日本的加密系统不是CPA-secure的,因为美方控制日方发送了消息“中途岛”,由此实现了破译。
既然一个CPA-secure的加密方案不能是确定性的,因此我们需要一个随机算法来实现加密。为此我们首先定义伪随机函数(pseudorandom functions)的概念。如同讨论一个数是否随机时我们需要一族数,讨论一个函数的随机时我们也需要一族函数。而一族函数可以看作一个二元函数。对于系数\(n\),我们定义函数\(F(k,x):\{0,1\}^n\times \{0,1\}^n\to \{0,1\}^n\),当\(k\)固定时记为\(F_k(x):\{0,1\}^n\to \{0,1\}^n\)。我们称\(F\)是伪随机函数,如果当\(k\)均匀随机给定时\(F_k\)与一个均匀随机的函数\(f\in \{0,1\}^n\to \{0,1\}^n\)是“不可辨别”的。在这里,我们很想仿照伪随机生成器中的不可辨别性的定义,但是作为多项式时间的使用者,我们甚至没有时间检查完一个函数的所有输入上的输出。所以,为了定义不可辨别性,我们再一次需要引入实验:设由oracle \(O\)提前选好了“一个函数\(f\in \{0,1\}^n\to \{0,1\}^n\)”或“某个\(k\)对应的\(F_k(x)\)”中的一个(前者是关于函数空间均匀随机的,后者是关于\(k\)均匀随机的);敌手有权访问\(O\)任意多项式次,给定输入\(O\)会给出输出;敌手需要给出猜测哪一个是真正的随机函数。如果敌手正确判断的概率不超过\(1/2+\text{negl}\),就称\(F\)是一个伪随机函数。
现在我们假定生成伪随机函数是可能的(这也能直接推出伪随机生成器是存在的),选定一个伪随机函数\(F(k,x)\),基于\(F\)给出以下加密方案:给定输入\(n\),均匀随机生成密钥\(k\in \{0,1\}^n\)。对于要加密的消息\(m\)(长度是\(\ell(n)\)),每次加密时均匀随机生成一个(利用某个伪随机数生成器)长度为\(\ell(n)\)的串\(r\in \{0,1\}^{\ell(n)}\),得到密文\(c:=r \| (F_k(r)\oplus m)\),其中\(\|\)表示连接(密文长于原文)。解密时,给定密钥\(k\),我们可以提取密文的前\(\ell(n)\)位得到\(r\),对后半部分再次与\(F_k(r)\)做异或还原出\(m\)。可以发现,这个加密方案同样是one-time pad的一个修改版本,只是用来异或的串带有了随机性,这种随机性包含在了\(r\)里,所以我们把它附加在开头。这使得密文比明文更长,因为它携带了更多的信息。
我们证明以上加密方案是CPA-secure的。首先,把以上方案中的伪随机函数生成器替换为真正的随机函数生成器不会改变任何多项式算力的敌手正确判断的概率(以一个\(\text{negl}\)),否则我们就有一个辨别随机与伪随机的方案了,与伪随机的定义矛盾。接着,考虑做选择明文攻击的敌手(此时方案中使用真随机函数)。敌手选择\(m_0,m_1\)交给我们加密,我们会返还给他\(r^*\|(f(r^*)\oplus m)\)。敌手同时也可以选择把\(m_0,m_1\)丢给oracle,假设它会由oracle的输出得到\(r_0\|(f(r_0)\oplus m_0)\), \(r_1\|(f(r_1)\oplus m_1)\)。现在分两种情况讨论,假设\(r^*\)恰好等于\(r_0\)或\(r_1\)(由于敌手重复询问oracle的次数可以多达\(p(n)\),这一事件发生的概率是\(p(n)/2^n\)),那么敌手可以由oracle的输出与\(m_0,m_1\)分别异或,一定能够得到\(f(r^*)\),这样就成功破译了,因此成功破译的概率为1;如果\(r^*\)不等于\(r_0\)和\(r_1\),那么和one-time pad一样相当于乱猜,成功破译的概率为\(1/2\)。综上,成功破译的概率不超过\(1/2+p(n)/2^n=1/2+\text{negl}(n)\),因此是CPA-secure的。
多重加密(Multiple Encryptions)
以上讨论中,我们都假定消息的发送方每一次只选用一个密钥加密一条消息发送。而如果发送方选择更加“偷懒”的方式,用一个密钥加密若干消息然后发送,我们直观上能看到这将会暴露更多信息。我们由此定义多重加密的安全性。
生成两列消息,每列消息包含\(t\)条随机的消息,其中一列消息被依次加密。如果敌手在给定任意的这样两列消息以及某列消息的加密以后,都不能以超过\(1/2+\text{negl}\)分辨是哪一列消息被加密,那么这个加密方案就称为多重不可辨别的。一个多重不可辨别的加密方案就称为是多重EAV-secure的。
多重EAV-secure的加密方案同样不可能是确定性的。对于一个确定性的方案,如果敌手被给定的一列消息中所有消息都相同,另一列中不同,那么一个判断密文是否全相同的程序就能以1的概率回答正确。换言之,在多重加密中如果采用确定性方案,那么消息的重复这一现象本身就泄露了信息。由此我们也看到,多重EAV-secure严格地强于EAV-secure,因为one-time pad是一个确定性的EAV-secure方案,却不可能是多重EAV-secure的。
而可以证明CPA-secure和多重CPA-secure是等价的。其中,多重CPA-secure可以定义如下:敌手有权任意多次访问这样的一个程序,这个程序被设定为左或右,每次输入给它左右两条消息时它会输出对应的左或右的密文,敌手被要求分辨机器被设定为左还是右。如果正确分辨的概率不超过\(1/2+\text{negl}\),就称这个方案是多重CPA-secure的。如果每次输入给这个左右程序的是两条相同的程序,那么就实现了选择明文攻击了,可见多重CPA-secure强于CPA-secure。另一个方向的证明此处省略,我们将会在公钥加密中再次讨论这个问题。
流密码(Stream Ciphers)
理论上我们可以不用考虑要发送的消息是否都有相同的长度,因为只需把所有消息都用前导零等等补足到相同长度即可。但在实际应用中,有时这样效率不高且浪费资源。下面我们实现一个可以支持消息长度任意的加密方案。
假设伪随机数生成器能够构造。考虑两个伪随机数生成器。第一个称为Init:输入一个种子\(s\),程序会输出一个初始“状态”(state) \(st_0\),其中\(st_0\)长度确定。第二个称为Next:输入\(st_i\),输出\(st_{i+1}\)和一个随机的二进制位\(y_i\)。我们在初始时运行Init,再运行任意\(t\)次Next,把得到的\(y_i\)串在一起就得到了一个任意长度的随机数,这就称为一个流密码生成器。如果一个流密码生成器是一个伪随机数生成器,我们就称这个流密码生成器是安全的。
根据以上流密码生成器,可以实现一个同步(synchronized)加密方案。发送方和接收方提前约定好密钥\(k\)。双方都用\(k\)作为种子生成流密码的初始状态\(st_0\),这样双方得到的初始状态是相同的,因为流密码生成器是确定性算法。每次发送消息时,接收方用当前流密码生成器生成与消息长度相同的随机二进制串与原文做异或以后发送,并更新流密码的状态字符串。接收方每次也同步地更新流密码的状态,从而能够计算得到同样的二进制串与密文做异或。由于是相同的两个字符串做了异或,接收方能够成功解密。这就是一个带状态的加密方案(stateful encryption)。容易证明(在正确的定义下)这个方案是CPA-secure的。
类似地,也可以定义生成伪随机函数的流密码。我们只需把Init修改为另外接受一个初始向量\(IV\),初始程序对于不同的\(IV\)要输出不同的初始状态。这样,Next运行得到的结果就是一个二元函数的值\(F(s,IV)\)。如果\(F\)是一个伪随机函数,就称这个流密码生成器是安全的。这样的流密码生成器可以实现异步(unsynchronized)的加密方案,本质上就是我们在CPA-Security一节中构造的加密方案,此处不再赘述。
块密码(Block Ciphers)
我们定义块密码是一个伪随机函数\(F:\{0,1\}^n\times \{0,1\}^\ell\to \{0,1\}^\ell\),其中\(\forall k\in\{0,1\}^n\),\(F_k(x)\)是一个双射且\(F_k\)和\(F_k^{-1}\)都是多项式时间可计算的。有限定义域下的双射其实构成了一个置换(permutation),所以我们也称这样的\(F\)是一个伪随机置换(pseudorandom permutation)。在这里,由于映射和逆映射都是多项式可计算的,进一步称为强伪随机置换(strong pseudorandom permutation)。
我们假设消息长度都是\(\ell\)的倍数(如果不是就补足,这样的开销相比于假设所有消息长度都相同要要很多),\(\ell\)称为块大小(block length),那么我们可以把消息分块加密。一个直接的想法就是取私钥\(k\),设消息\(m\)被以\(\ell\)的长度截断为消息块\(m_1,m_2,\cdots,m_t\),那么\(c:=c_1,\cdots,c_t\),其中\(c_i=F_k(m_i)\)。解密只需\(m_i=F_k^{-1}(c_i)\)。这称为电子密码本方法(Eletronic Code Book mode, ECB mode),这是一个确定性算法,肯定不是CPA-secure的。它甚至不是EAV-secure的,因为相同的消息块会加密得到相同的密文从而暴露信息。我们需要更强的块密码加密方案。
一类方法是,用作为伪随机函数的块密码实现流密码的加密。只要原本基于流密码的方案是CPA-secure的,就能保证新的方法依然是CPA-secure的。
另一种方法是基于块与块之间的链接的。每次要加密消息\(m\)时,对\(m\)分块为\(m_1,\cdots,m_t\)。设私钥为\(k\),首先随机生成种子\(IV\in \{0,1\}^\ell\),令\(c_0=IV\),\(c_i=F_k(c_{i-1}\oplus m_i)\)。\(c:=c_0,c_1,\cdots,c_t\)。解密只需\(m_i=F_k^{-1}(c_i)\oplus c_{i-1}\)。我们看到,上一块的密文被用来与下一块的消息链接起来做加密,所以这个方案称为块密码链接方法(Cipher Block Chaining mode, CBC mode)。可以证明,CBC方法是CPA-secure的(略)。
由于CBC加密时下一块必须等待上一块加密以后才能进行,所以在允许并行的机器上运行效率可能不算高。