Python Cookbook-2.30 计算 CRC-64 循环冗余码校验
任务
需要对某些数据进行循环几余码校验(CRC)以确定数据的完整无误,而且必须遵循ISO-3309 关于 CRC-64 校验的规定。
解决方案
Python 标准库并没有提供 CRC-64 的任何实现(但提供了CRC-32函数,即 zlib.crc32)所以我们需要自己来提供实现。幸而 Python 能够处理位操作(与、或、非、异或、移位等),就像C那样(实际上,它们的语法几乎完全相同),所以很容易根据CRC-64的参考实现写出如下的Python 函数:
#使用两个辅助表(为了速度我们用了一个函数),
#之后删除该函数,因为我们再也用不着它了:
CRCTableh = [0]*256
CRCTablel = [0]*256
def _inittables(CRCTableh,CRCTablel,POLY64REVh,BIT_TOGGLE):
for i in xrange(256):
partl = i
parth = 0L
for j in xrange(8):
rflag = partl & 1L
part1 >>= 1L
if parth & 1:
part1 ^= BIT_TOGGLE
parth >>= 1L
if rflag:
parth ^= POLY64REVh
CRCTableh[i] = parth
CRCTablel[i] = partl
#CRC64 的高32位的生成多项式(低32位被假设为0)
#以及_inittables 所用的bit-toggle掩码
POLY64REVh = 0xd8000000L
BIT_TOGGLE = 1L << 31L
#运行函数来准备表
_inittables(CRcTableh,CRCTablel,POLY64REVh,BIT_TOGGLE)
#删除我们不需要的名字,包括生成表的函数
del _inittables,POLY64REVh,BIT_TOGGLE
#此模块公开了这两个函数:crc64和crc64digest
def crc64(bytes,(crch,crcl)=(0,0)):
for byte in bytes:
shr = (crch & 0xFF) << 24
templh = crch >> 8L
temp1l = (crcl>>8) | shr
tableindex = (crcl ^ ord(byte)) & 0xFF
crch = temp1h ^ CRCTableh[tableindex]
crcl = temp1l ^ CRCTablel[tableindex]
return crch,crcl
def crc64digest(astring):
return "%08X%08X" %(crc64(bytes))
if __name_ == '__main__':
#当此模块作为主脚本运行时,一个小测试/展示
assert cr64("IHATEMATH") == (3822890454, 2600578513)
assert crc64digest("IHATEMATH") == "E3DCADD69B01ADD1"
print 'crc64: dumb test successful'
讨论
循环冗余码校验(CRC)是一种流行的确保数据(例如,文件)未被损坏的方法。CRC可以稳定地检查出一些随机偶发的损坏,但是对于恶意的针对性攻击并不像其他一些加密的校验和方法那么强劲。CRC 的计算比其他校验和方式都要快,因此在那些只需要检测偶发和随机损坏,而不用担心别人故意伪造数据进行欺骗的情况下,它比其他校验方式用得更多。
从数学的角度讲,CRC是把需要校验和的数据的位当做多项式进行计算的。实际上正如本节代码所示,经过正确的索引处理,计算可以一次完成并将结果存储在表中,数据中的每一个字节都对最终的结果产生影响。这样,在初始化之后(我们用一个辅助函数来初始化,这是因为在Python 中使用局部变量进行计算要比使用全局变量计算快得多),CRC 计算的速度非常快。表的计算和相关的处理用到了很多位操作,不过,幸运的是,Python对这类操作的处理效果和其他语言一样,比如C,速度非常快。(实际上 Python 关于位操作的语法和C完全一样)。
这个标准的 CRC-64 校验和的算法在ISO-3309 标准中有详细说明,本节的实现方法完全地遵照了该标准的描述。生成器多项式是x64+x4+x3+x+1。
用一对 Python 的 int类型来存储 64 位的结果,分别代表其高 32 位和低 32 位。为了能够递进地计算CRC——有时数据可能是逐步到达的,给crc64调用函数提供了一个可选的“初始值”,即(crch,crcl)构成的数对,这样可以在前一步计算结果的基础上继续计算。如果要一次计算出全部数据的 CRC,只需要提供完整的数据(字节的序列),如本节中一样,同时数对会被默认地设置为(0,0)。