一,哈希函数
对于密码学中的哈希函数,需要满足两个要求:
- 单向性:只能正向计算而不能逆向计算;输出的长度与输入的长度无关。
- 防碰撞:不存在两个不同的消息M1和M2,使得H(M1)=H(M2)。
然而,哈希函数并不是真正的随机函数,随机函数是难以实现的。但是我们可以利用假装随机函数来证明安全性。
二,随机预言机
在密码学里面,随机预言机(英语:Random oracle)是一部预言机(简单说像是理论的黑箱),对任何输入都回传一个真正均匀随机的输出,不过对相同的输入,该预言机每次都会用同一方法输出。换句话说,随机预言机是一个将所有可能输入与输出作随机映射的函数。
安全性证明的解释
安全性证明通常考虑两个或多个当事方的交互,其中一方是就是我们的对手。 在一个典型的例子中,大多数玩家都是“诚实的” ,这意味着他们会按照一个明确的安全协议进行操作。 另一方面,对手可以为所欲为。 对手的目标是破坏这个计划。
对于加密方案,允许对手做能够在“真实世界”环境中可以做的事情。 例如: 他可能能够获得选择明文的加密和选择密文的解密。 在大多数情况下,他会拦截由一个诚实的加密器传输的合法密文。 在这种情况下对手的目标是学习潜在的明文(即解密)。
标准的做法是假设各方都知道方案或协议,包括如何计算使用的哈希函数。 这是一个常识性的假设,有时也被称为科克霍夫斯原理(Kerckhoffs 原理)。 此外,我们通常假设对手在某些方面是有限的——他没有无限的计算能力或时间,这意味着对手不能暴力破解解密的密钥。
随机预言机模型中的安全性证明。 所有参与者(Encryptor、 Decryptor 和对手)都可以调用随机预言,后者为它们计算哈希函数并向所有参与者提供一致的响应。即将理想的哈希函数(随机函数)放在预言模型,任何一方需要的哈希操作都由随机预言机模型完成。
利用哈希函数的流密码
1. 随机选择一个密钥(比如128位长),并与发送方和接收方共享。将 IV 设置为 1。
2. 将明文消息切割成等长的块 M1,…,MN,其中每个块的长度正好与哈希函数的输出长度相等。
3. 使用 || 表示连接,对消息进行加密,如下:
C1 = H(key || IV) ⊕ M1
C2 = H(key || IV+1) ⊕ M2
…
CN = H(key || IV+N) ⊕ MN
4. 输出 (IV, C1, C2, ..., CN) 作为密文。
5. 确保在加密下一条消息之前设置IV = (IV+N+1)。
简单的证明
假设我想证明这个方案在随机预言模型中是安全的,通常我们会使用“安全”的特定定义来处理真正的对手可以做的一些事情。 我们将使用以下简化的描述:
某个诚实的人——加密机——要加密一条消息。敌人会截获密文。如果敌人能够了解除文本长度之外的任何有关潜在明文的信息,那么他就“赢了”。
根据上面的方案描述,加密者首先选择一个随机密钥(步骤1)。 他把他的明文分成块(步骤2)。 对于每个块 i=1到 N,他查询预言以获得 H(key || i) 。 最后,他用纯文本块(步骤3)对接收到的哈希表进行 XOR 操作,并发送密文(C1,... ,CN)以便敌方拦截(步骤4)。
我们现在可以做以下观察
- 在内部,预言从一个空表开始。
- 每次预言从加密器收到一个新的表单查询,它都会为输出值产生一个新的完全随机的字符串表示输出值。 它将输入和输出都存储在表中,并将输出发送回加密器。
- 加密器使用这些随机字符串中的某一个对每个消息块进行 XOR 操作,从而形成密文块(C1,... ,CN)。
- 加密器从来不会对相同的输入字符串请求两次,因为IV总是递增的。最后,我们做一个最重要的观察。
- 使用完全随机的密钥字符串是一种非常有效的加密方法。事实上,它是一次性的,只要对手不知道随机字符串,结果的密文本身就是随机分布的,因此不会显示关于底层消息的任何信息(除了它的长度)。
根据观察(5),我们所要做的就是证明当预言对“key || i”进行哈希时,(a)每个返回值都是完全随机的;(b)对手不了解这些返回值。如果我们能做到这一点,那么我们就能保证密文(C1,... ,CN)将是一个随机字符串,而对手对底层明文一无所知!
最终分析
上面所提出的(a)部分是很简单的。当加密器要求预言计算H(key || i),前提是以前没有计算过这个值,那么预言将生成一个完全随机的字符串。而且,加密器从来不会对相同的输入值请求两次,因为它总是更新它的IV值。
(b)部分稍微难一点。有没有什么方法可以让对手知道即使是i从1到N的一个值H(key|| i)的预言输出?预言不会将它的表公开,而且对手无法“侦听”加密器发出的哈希调用。
但是对手可以查询预言。具体来说,他所要做的就是在“key|| i”上查询任何1 <= i <= N的预言,他将得到加密器用于加密密文的一个字符串。如果对手掌握了这些字符串中的一个,她可以很容易地恢复(至少部分恢复)明文,我们的论证就不再有效了。
这里有一个明显的问题:对手不知道密钥。因此,为了在“key || i”上查询,对手必须猜测密钥。
但是对我们来说幸运的是,密钥是随机的,而且相当大(128位)。 这告诉了我们一些关于对手成功概率的信息。
具体来说: 如果她只做了一个猜测,那么她猜对正确的密钥的概率是相当小的: 可能的密钥数中的一个。 对于我们的具体例子来说,她的成功概率是1/(2^128) ,这是个天文数字。 当然,她可以尝试多次猜测;如果是q,那么成功概率上升到q/(2^128)。
我们开始时的基本假设是,对手的计算能力有限。 这意味着她只能执行一定数量的哈希。 在正式的安全性证明中,我们可以使用多项式和指数时间。 但是,与其这样做,不如让我们插入一些具体的数字。
假设对手只有时间计算2^40(1.1万亿)哈希值。 假设我们使用了一个128位的密钥,那么她猜测密钥的概率最多是(2 ^ 40) / (2 ^ 128)1 / (2 ^ 88)。 这仍然是一个相当小的数字。
你认为在现实世界中对手能够计算超过2^40的哈希值吗? 代入你自己的数字——计算保持不变。如果你认为这个方案不够安全,你可以尝试让密钥变长。
这里的要点是,我们已经证明了在随机预言模型中,上面的方案“与密钥一样安全”。换句话说,对手攻击该方案的最大希望是通过暴力猜解的方式找到密钥。只要密钥足够大,我们就可以有把握地得出这样一个“现实的”结论,即对手知道信息的概率很低。
三,证明流程
一般流程
当我们说一个加密方案是“可证明的安全”时,我们通常指的是在某种假设下它是安全的。 对于大多数实际的方案来说,这个假设本质上是数学性质的。 例如,我们可以假设很难分解大的复合数,或者很难在格中找到最短的向量,从而找到一个加密方案。 在这种情况下,“难搞”并不意味着“我不知道该怎么做”。它的意思是: 没有(有效的)算法可以做到这一点。
现在,我提到这些都是假设。 我们并不完全知道这些问题是困难的。 然而,我们倾向于认为,如果一些聪明的人已经注意到这个问题有一段时间了,但是没有人在解决这个问题上取得任何进展,那就意味着这可能并不容易。 在某些情况下,问题甚至可能属于一类有用的问题,例如,它可能是一个 NP-Hard 问题。
即使你不愿盲目相信这些假设,安全性证据仍然是有用的。 分析一个新的密码系统可以消耗大量的时间和精力。 如果我们至少可以将每个新方案“简化”为少数几个数学问题中的一个,我们就可以倾注我们的脑力来解决这些问题,因为我们知道我们的工作将适用于每个使用这些问题的方案。
具体来说,这种证明的一般流程如下:
1. 假设问题 X 很难。
2. 证明如果存在一个(有效的)算法来“破坏”我们的加密方案,那么就存在一个(有效的)算法来解决问题X。
3. 含蓄地指出(2)意味着如果方案可以被打破,那么问题X就不会很难。
如果第(1)点为真,那么第(3)点显然不可能为真。 根据这个逻辑,我们可以得出这样的结论: 如果问题 X 很难,那么就不可能有算法破解我们的加密方案。
当然,还没有解释如何实际完成步骤(2) ,这是非常重要的。 这也是最美妙的部分。 在大多数简化中,步骤(2)实际上包括编写一个解决问题 X 的有效算法。
因此,这就是争论的症结所在:假设有一种算法有效地“破坏”了我们的加密方案,那么我们可以把它塞进我们的“求解器”算法留下的洞(求解算法缺少的子程序)里。这将产生一个有效地解决 X 问题的算法,因此,它完全满足我们在点(2)中所要求的。
黑匣子
有一个基本的简化证明必须遵守的限制。 我们的“问题 X 解决程序”算法必须使用“加密方案破坏程序”的子程序作为一个黑盒。它无法对破坏程序算法的内部工作方式做出假设。例如,我们不能在“求解器”算法中对破坏程序算法进行反编译,或者窥视它的内部内存寄存器,然后假定它们将包含我们需要的部分结果。
这是因为我们的简化只有当它与打破方案的每一个可能的算法一起工作时才有效。 因为我们不能预先猜测这样的算法在内部是如何工作的,我们不能依赖于它的内部工作。 破解加密程序的算法会被可怕地混淆,以至于我们无法理解它,即使我们把代码握在手中。
因此,在传统的黑盒简化中,我们可以指望破坏程序算法有一个标准的“ API”(我们将在某个时候进行定义)。 它通常接受公钥和密文(或签名等) ,并输出一些有用的信息,这些信息构成了我们对“破坏”的定义。 尽管它并不总是需要成功,但我们希望它能够以合理的概率成功。 但是算法中发生的所有事情都必须对我们隐瞒。
回到随机预言模型
随机预言模型中没有人能够自己计算哈希。 每一方——包括敌方(“破解程序”算法的同义词)——都必须通过向特定的外部方发送消息来进行哈希。 该方使用随机函数计算哈希,并将其发送回调用方。
在随机预言模型中,预言为包括敌方在内的所有各方计算哈希。
如果我们建立一个简化,则意味着我们可以利用从这些调用中泄露的额外信息。 我们知道算法必须进行这些哈希调用,因此必须在 API 中存在一个“端口”来进行这些调用。 一旦我们制定了这个规定,我们的简化就可以进入这个端口,拦截敌方的哈希调用,查看它要求的消息,甚至可能篡改它返回的结果。更正式地说,在随机预言模型中,对手(也就是我们的“破解程序”算法)不再完全是一个黑盒子。 在一个非常重要的方面,我们可以看到它。 如果它想要进行哈希操作,它就必须在自身之外进行调用。
在一个极端的情况下,我们的简化甚至可以运行自己的假“预言”,只要我们始终回复令人信服的答复。