CRC32 是添加剂吗?
Posted
技术标签:
【中文标题】CRC32 是添加剂吗?【英文标题】:Is CRC32 additive? 【发布时间】:2011-10-22 03:28:25 【问题描述】:在几个地方我读到 crc32 是加法的,因此:CRC(A xor B) = CRC(A) xor CRC(B)。
上面的说法被我写的下面的代码反驳了:
import zlib
def crc32(data):
return zlib.crc32(data) & 0xffffffff
print crc32(chr(ord("A") ^ ord("B")))
print crc32("A") ^ crc32("B")
程序输出:
1259060791
2567524794
有人可以提供一个正确的代码来证明这个理论或指出我失败的地方吗?
【问题讨论】:
一开始我看错了——我想,好吧,我确实经常使用 CRC32,但我可以随时退出...真的可以... 您能否提供一些消息来源,说明crc(A ^ B) = crc(A) ^ crc(B)
as google 让我失望了。
您没有通过证明该假设的失败来回答您自己的问题吗?
CRC 是关于消息连接的“加法”:CRC(A || B, iv) == CRC(B, CRC(A, iv))
其中A
和B
是消息的两个部分,||
是相应的连接运算符,@987654328 @ 是 CRC 计算的“初始化向量”,例如常见的0xffffffff
。这意味着,仅给定消息A
和消息B
的CRC 值,CRC(A || B)
可以简单地计算,而无需参考实际消息A
。
【参考方案1】:
如果 a、b 和 c 的长度相同,则 CRC(a) xor CRC(b) xor CRC(c) 等于 CRC(a xor b xor c)。回到原来的公式,这意味着 CRC(a xor b) 等于 CRC(a) xor CRC(b) xor CRC(z),其中 z 是与其他两个序列长度相同的零序列。
【讨论】:
我可以确认CRC(A ⊕ B) = CRC(A) ⊕ CRC(B)
不是真的,而是CRC(A ⊕ B ⊕ C) = CRC(A) ⊕ CRC(B) ⊕ CRC(C)
,因此CRC(A ⊕ B ⊕ C ⊕ D ⊕ E) = CRC(A) ⊕ CRC(B) ⊕ CRC(C) ⊕ CRC(D) ⊕ CRC(E)
(如果您将C
更改为C ⊕ D ⊕ E
)。为什么它适用于奇数个操作数而不适用于偶数个?
如果 CRC32 使用 0x00000000 的初始化向量,则 CRC(A ⊕ B) = CRC(A) ⊕ CRC(B)
将成立,但对前导零不敏感。 CRC 通常使用 0xFFFFFFFF 的初始化向量(使其行为类似于前四个字节与 FF 异或的序列的零初始化 CRC)。如果将 CRCZ 定义为使用零初始化向量执行的 CRC,则 CRC(X) = CRCZ(x ⊕ FFFFFFFF00..)
和 CRC(X ⊕ Y) = CRCZ(x ⊕ y ⊕ FFFFFFFF00...)
。二元素表达式CRC(X) ⊕ CRC(Y)
...
...等价于CRCZ(x ⊕ FFFFFFFF00...) ⊕ CRCZ(y ⊕ FFFFFFFF00...)
,后者又等价于CRCZ(x ⊕ Y ⊕ FFFFFFFF00... ⊕ FFFFFFFF00...)
,因此等价于CRCZ(x ⊕ Y)
[不等于CRCZ(x ⊕ Y ⊕ FFFFFFFF00...)
,因此不等于CRC(x ⊕ Y)
] .对于算术类比,定义 f(x)=-x,并使用乘法而不是 xor。那么 f(x·y·z) = -(x·y·z) = (-x)(-y)(-z) = f(x)·f(y)·f(z),但是 f( x·y) = -(x·y) = -((-x)·(-y)) = -(f(x)·f(y))。【参考方案2】:
CRC-32 算法基于多项式除法,增加了一些额外的步骤。纯多项式余数是加法的。
我的意思是:mod(poly1 + poly2, poly3) = mod(mod(poly1, poly3) + mod(poly2, poly3), poly3)
CRC-32 算法以此为基础,并且是非累加的。计算字节数组 m 的 CRC-32:
-
前 4 个字节与 0xFFFFFFFF 异或。
将较早的字节视为较高的多项式幂,并将较低阶位视为较高的多项式幂。例如,字节 0x01 0x04 将是多项式 x^15 + x^3。
将多项式乘以 x^32。
将此多项式的余数除以 CRC-32 多项式 0x104C11DB7。余数多项式的次数
将低次幂视为高阶位。例如,多项式 x^2 将是 32 位整数 0x40000000。
将结果与 0xFFFFFFFF 异或。
纯多项式余数运算在步骤#4。是步骤 #1 和 #6 使 CRC-32 算法不加性。所以如果你撤消步骤#1和#6的效果,那么你可以将CRC-32算法修改为可加法。
(另见:Python CRC-32 woes)
【讨论】:
【参考方案3】:CRC 在数学意义上是加法的,因为 CRC 哈希只是所有数据(被视为大整数)的无进位除以多项式常数的余数。使用您的示例,它类似于这种事情:
7 模 5 = 2
6 模 5 = 1
(7 mod 5) + (6 mod 5) = 3
(7 + 6) 模 5 = 3
在那个类比中,“5”是我们的 CRC 多项式。
这是一个可以使用的示例(基于 gcc):
#include <stdio.h>
#include <x86intrin.h>
int main(void)
unsigned int crc_a = __builtin_ia32_crc32si( 0, 5);
printf( "crc(5) = %08X\n", crc_a );
unsigned int crc_b = __builtin_ia32_crc32si( 0, 7);
printf( "crc(7) = %08X\n", crc_b );
unsigned int crc_xor = crc_a ^ crc_b;
printf( "crc(5) XOR crc(7) = %08X\n", crc_xor );
unsigned int crc_xor2 = __builtin_ia32_crc32si( 0, 5 ^ 7);
printf( "crc(5 XOR 7) = %08X\n", crc_xor2 );
return 0;
输出如预期:
plxc15034> gcc -mcrc32 -Wall -O3 crctest.c
plxc15034> ./a.out
crc(5) = A6679B4B
crc(7) = 1900B8CA
crc(5) XOR crc(7) = BF672381
crc(5 XOR 7) = BF672381
由于此代码使用 x86 CRC32 指令,它只能在 Intel i7 或更新版本上运行。内在函数将运行的 CRC 哈希作为第一个参数,将要累积的新数据作为第二个参数。返回值为新运行的 CRC。
上面代码中初始运行的 CRC 值 0 很关键。使用任何其他初始值,CRC 在实际意义上不是“加法”,因为您已经有效地丢弃了有关您要划分的整数的信息。这正是您的示例中正在发生的事情。 CRC 函数从不将初始运行的 CRC 值初始化为零,但通常是 -1。原因是 0 的初始 CRC 允许数据中任意数量的前导 0 简单地通过而不改变运行的 CRC 值,该值保持为 0。因此,将 CRC 初始化为 0 在数学上是合理的,但出于计算的实际目的哈希,这是你最不想要的。
【讨论】:
【参考方案4】:这意味着 CRC 结果的每个位位置仅由输入中的等效位位置驱动。以 B == 0 为例。
对于某些原始异或或加法校验和算法,您所描述的关系更可能为真。
【讨论】:
以上是关于CRC32 是添加剂吗?的主要内容,如果未能解决你的问题,请参考以下文章