密码学.NET,避免时间攻击

我正在浏览crackstation.net网站并遇到这个代码,其评论如下:

在长度 – 恒定时间内比较两个字节数组。 使用此比较方法,以便无法使用定时攻击从在线系统中提取密码哈希值,然后离线攻击。

private static bool SlowEquals(byte[] a, byte[] b) { uint diff = (uint)a.Length ^ (uint)b.Length; for (int i = 0; i < a.Length && i < b.Length; i++) diff |= (uint)(a[i] ^ b[i]); return diff == 0; } 

任何人都可以解释一下这个函数实际是如何工作的,为什么我们需要将长度转换为无符号整数以及这种方法如何避免定时攻击? 行diff |= (uint)(a[i] ^ b[i]); 做?

这根据ab之间是否存在diff来设置diff

它总是穿过ab中的两个中较短的一个来避免定时攻击,无论是否存在不匹配的错误。

diff |= (uint)(a[i] ^ (uint)b[i])取a的一个字节的异或与b的相应字节。 如果两个字节相同则为0,如果它们不同则为非零。 然后它ordiff

因此,如果在该迭代中的输入之间发现差异,则迭代中的diff将被设置为非零。 一旦diff在循环的任何迭代中被赋予非零值,它将通过进一步的迭代保留非零值。

因此,如果在ab相应字节之间发现任何差异,则diff的最终结果将为非零,并且仅当ab所有字节(和长度)相等时才为0。

然而,与典型的比较不同,这将始终执行循环,直到两个输入中较短的输入中的所有字节都与另一个中的字节进行比较。 一个典型的比较会有一个早期的问题,一旦发现不匹配,循环就会被打破:

 bool equal(byte a[], byte b[]) { if (a.length() != b.length()) return false; for (int i=0; i 

有了这个,基于返回false消耗的时间量,我们可以学习(至少近似) ab之间匹配的字节数。 假设长度的初始测试需要10 ns,并且循环的每次迭代需要另外10 ns。 基于此,如果它在50 ns内返回false,我们可以快速猜测我们有正确的长度,并且ab的前四个字节匹配。

即使不知道确切的时间量,我们仍然可以使用时间差来确定正确的字符串。 我们从一个长度为1的字符串开始,一次增加一个字节,直到我们看到返回false所花费的时间增加。 然后我们遍历第一个字节中的所有可能值,直到我们看到另一个增加,表明它已经执行了循环的另一次迭代。 对于连续的字节继续相同,直到所有字节匹配,并返回true

原来仍然有一点时间攻击 - 虽然我们不能根据时间轻松确定正确字符串的内容,但我们至少可以根据时间找到字符串长度 。 因为它只比较两个字符串中较短的字符串,所以我们可以从长度为1,然后是2,然后是3的字符串开始,依此类推,直到时间变​​得稳定。 只要时间增加,我们建议的字符串就比正确的字符串短。 当我们给它更长的字符串,但时间保持不变时,我们知道我们的字符串比正确的字符串长。 正确的字符串长度将是最短的字符串,需要最长的测试持续时间。

这是否有用取决于具体情况,但无论如何,它显然都会泄露一些信息。 为了真正实现最大的安全性,我们可能希望在实际字符串的末尾添加随机垃圾,使其成为用户输入的长度,因此时间与输入的长度成正比,无论它是否更短,相等to,或比正确的字符串长。