使用SuperFastHash替代CRC32?

3
注意:我不想使用SuperFastHash,也不希望它给出与CRC32相同的输出值。
我正在编写一个简单的LZSS压缩/解压例程,以提供非常快速的解压和在解压时没有内存开销。输入数据被分成4096字节长度的块,并按顺序压缩。
我的问题是:我想为每个压缩块(块大小<= 4096字节)添加一些错误检测。时间限制很严格,因此校验和例程必须非常快。我避免了加密算法(MD5、SHA1),因为它们涉及大量计算,而选择了CRC32(一个更简单和明显的算法)。
经过一些测试,我发现在我的项目约束下CRC32太慢了。我使用了来自这里的enwik9(维基百科的10^9字节文本转储)。我使用我的LZSS例程对其进行了压缩,并获得了一个570Mb的文件。 我测量了以下持续时间(单线程,磁盘IO排除在外,在处理之前将所有数据加载到内存中,平均进行10次试验):

|          操作            |  时间 (GCC4.4.5/Linux)   |  时间 (MSVC2010/Win7)  |
|-------------------------------+--------------------------+------------------------|
|        解压          |        6.8 秒       |      6.95 秒      |
|  在解压后的结果上执行CRC32 |        4.9 秒       |      4.62 秒      |
|   在压缩后的结果上执行CRC32  |        2.8 秒       |      2.69 秒      |

然后我只是出于好奇测试了SuperFastHash:

|          操作              |  时间 (GCC4.4.5/Linux)   |  时间 (MSVC2010/Win7)  |
|-------------------------------+--------------------------+------------------------|
| 对解压缩结果进行SFH计算   |        1.1秒             |      1.33秒           |
| 对压缩结果进行SFH计算       |        0.7秒             |      0.75秒           |

这是我的CRC32实现(我遵循了以下文档的描述:http://www.ross.net/crc/download/crc_v3.txt):

# include <stdint.h>

// CRC32 lookup table (corresponding to the polynom 0x04C11DB7)
static const uint32_t  crc32_lookup_table[256] =
{
    0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
    0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
    0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
    // many lines skipped
    // ...
    0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
} ;

uint32_t crc32_hash(const uint8_t * data, size_t len)
{
    uint32_t crc32_register = 0xFFFFFFFF ;
    while( len-- )
    {
        crc32_register = (crc32_register >> 8)
                       ^ crc32_lookup_table[(crc32_register & 0x000000FF) ^ *data++] ;
    }
    return crc32_register ^ 0xFFFFFFFF ;
}

我的问题是:
在压缩数据块中,我能否使用哈希替代循环冗余校验值以执行错误检测?据我所知(并记得从我的电子课程中学到的),CRC算法设计用于在数据通过嘈杂的信道传输时出现突发错误的情况下非常高效,而这并不适用于从硬盘读取的数据。如果我理解有误,请纠正我。
谢谢任何建议!

2
如果你只是防止压缩文件意外损坏,并检查解压后数据的校验和,那么任何奇怪的校验和方法都可能很好地工作,因为压缩数据中的任何错误都会倾向于破坏其后的所有内容。除了一些简单的模块化求和或异或运算之外,甚至像Adler32这样的东西也应该能胜任。 - hmakholm left over Monica
谢谢!我也有同样的想法,但将错误检测步骤放在解压缩之后要求解压缩例程具备非常良好的错误处理能力以应对错误数据。这里所说的错误是指在处理压缩块中无效的LZ位置/长度时可能发生的内存下溢/上溢。如果我能够消除解压缩例程主循环中的边界测试,我可以轻松获得15%以上的速度提升... - overcoder
出于安全原因,您需要在任何情况下处理内存溢出。否则,攻击者可以轻松创建导致程序崩溃(或可能颠覆)的输入 - 任何合法压缩器为_压缩_数据生成的校验和也可以被手工模拟,由此制作攻击数据包。 - hmakholm left over Monica
3个回答

3
SuperFastHash已经被发现存在一些问题,以及和其它快速哈希函数murmur2一起。如果你正在寻找适用于更大数据块且低冲突的东西,可以尝试google的city hash (http://code.google.com/p/cityhash/) 或者murmur3的128位变体。还有一些比较奇特的哈希函数如crap8和crapwow,声称提供了几乎完美的位变化和漏斗效果,从而几乎没有碰撞。你可以在这里阅读相关信息和其他非加密哈希函数:http://www.team5150.com/~andrew/noncryptohashzoo/

1

哈希算法旨在使输入即使发生非常小的更改,结果也会有很大的变化。

我认为SuperFastHash具有这个特性。它可能更容易发生碰撞(因为似乎没有被社区深入分析),但这不应该阻止您打算使用它。

祝你好运 :)


如果SuperFastHash声称自己是加密哈希函数,那么句子“它可能更容易发生碰撞(因为社区似乎对其进行的分析较少)”就有意义了。但它并不是加密哈希函数,所以没有必要像任何非加密哈希函数一样去分析它,构建碰撞是微不足道的。 - Pascal Cuoq
我喜欢Bob Jenkins的哈希函数,因为它们有大量统计测试的证据,表明它们被精心选择以实现输入不同位翻转的良好扩散和独立性。据我所知,类似的测试程序可能已经针对SuperFastHash进行过,但从其网站上来看,这一点不太明显。 - hmakholm left over Monica
@Pascal:我刚才的意思是说,在那个哈希函数中,碰撞概率并不确定。我理解OP只是想要CRC-哈希来确保在解压缩时获得原始数据。在那里,碰撞也可能是一个问题,否则一个简单的奇偶校验就足够了。 - woliveirajr

1

由于您的问题与安全无关,因此可以使用“破解”的加密哈希函数,这些函数对有感知攻击者的攻击不安全,但在检测传输错误方面非常出色。我想到了MD4,在某些平台上已经被证明比CRC32更快。您还可以查看RadioGatún和Panama;请参见this library,其中包含各种加密哈希函数的C和Java开源实现。

如果您的目标架构是最新/足够大的x86 CPU,具有AES-NI指令,则可以通过使用块密码AES和传统密钥(例如全零密钥)简单地计算CBC-MAC来制作出非常快速且非常好的校验和;由于这不是为了安全,因此甚至可以使用比标准AES更少的轮数(例如5轮而不是标准的10轮)。


抱歉回复晚了!我最终使用了Adler16,这是著名的Adler32算法的修改版本。感谢您提供的好参考! - overcoder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接