在Python中计算/验证bz2 (bzip2) CRC32
我正在尝试计算和验证压缩的bzip2档案的CRC32校验和。
.magic:16 = 'BZ' signature/magic number
.version:8 = 'h' for Bzip2 ('H'uffman coding)
.hundred_k_blocksize:8 = '1'..'9' block-size 100 kB-900 kB
.compressed_magic:48 = 0x314159265359 (BCD (pi))
.crc:32 = checksum for this block
...
...
.eos_magic:48 = 0x177245385090 (BCD sqrt(pi))
.crc:32 = checksum for whole stream
.padding:0..7 = align to whole byte
http://en.wikipedia.org/wiki/Bzip2
我知道bz2文件中CRC校验和的位置,但我该如何验证它们呢?我应该对哪些部分使用binascii.crc32()
来获取两个CRC?我尝试逐字节计算不同部分的CRC,但一直没有成功匹配。
谢谢。我会查看bzip2的源代码和bz2
的Python库代码,看看能否找到一些线索,特别是在decompress()
方法中。
更新 1:
根据我的观察,块头是通过以下标签来识别的。但是小的bz2文件不包含ENDMARK标签。(感谢adw的帮助,我们发现应该寻找ENDMARK的位移值,因为压缩数据并没有填充到字节。)
#define BLOCK_HEADER_HI 0x00003141UL
#define BLOCK_HEADER_LO 0x59265359UL
#define BLOCK_ENDMARK_HI 0x00001772UL
#define BLOCK_ENDMARK_LO 0x45385090UL
这是来自bzlib2recover.c
的源代码,块似乎总是从第80位开始,正好在CRC校验和之前,这个CRC在计算时应该被忽略,因为你不能用自己的CRC去计算出相同的CRC(你明白我的意思)。
searching for block boundaries ...
block 1 runs from 80 to 1182
正在查看计算这个的代码。
更新 2:
bzlib2recover.c
没有CRC计算的函数,它只是从损坏的文件中复制CRC。不过,我确实在Python中复制了块计算器的功能,以标记每个块在bz2
压缩文件中的起始和结束位。回到正轨,我发现compress.c
引用了一些在bzlib_private.h
中的定义。
#define BZ_INITIALISE_CRC(crcVar) crcVar = 0xffffffffL;
#define BZ_FINALISE_CRC(crcVar) crcVar = ~(crcVar);
#define BZ_UPDATE_CRC(crcVar,cha) \
{ \
crcVar = (crcVar << 8) ^ \
BZ2_crc32Table[(crcVar >> 24) ^ \
((UChar)cha)]; \
}
这些定义也被bzlib.c
访问,s->blockCRC
在bzlib.c
中初始化和更新,并在compress.c
中最终确定。这里有超过2000行的C代码,需要一些时间来查看并弄清楚哪些内容是必要的,哪些不是。我也给这个问题添加了C
标签。
顺便说一下,这里是bzip2的C源代码 http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz
更新 3:
结果发现bzlib2
块的CRC32是使用以下算法计算的:
dataIn
是要编码的数据。
crcVar = 0xffffffff # Init
for cha in list(dataIn):
crcVar = crcVar & 0xffffffff # Unsigned
crcVar = ((crcVar << 8) ^ (BZ2_crc32Table[(crcVar >> 24) ^ (ord(cha))]))
return hex(~crcVar & 0xffffffff)[2:-1].upper()
其中BZ2_crc32Table在crctable.c
中定义。
对于dataIn = "justatest"
,返回的CRC是7948C8CB
,压缩了包含该数据的文本文件后,bz2文件中的crc:32校验和是79 48 c8 cb
,这是一致的。
结论:
bzlib2的CRC32是(引用自crctable.c
)
大致来源于Rob Warnock在 comp.compression FAQ第51节中的代码...
...因此,按照我的理解,不能使用标准的CRC32校验和计算器进行预计算或验证,而是需要bz2lib
的实现(在bzlib_private.h
的第155-172行)。
3 个回答
可以看看fastcrc这个Python库,它里面有bzip2格式的crc32实现。
https://fastcrc.readthedocs.io/en/latest/#fastcrc.crc32.bzip2
在已有答案的基础上,最后在数据流的末尾有一个最终的校验和(就是在eos_magic
之后的那个)。这个校验和是用来检查所有单独的霍夫曼块校验和的。它一开始是零。每次你验证完一个霍夫曼块的校验和后,就会更新这个值。更新的方法如下:
crc: u32 = # latest validated Huffman block CRC
ccrc: u32 = # current combined checksum
ccrc = (ccrc << 1) | (ccrc >> 31);
ccrc ^= crc;
最后,要把ccrc
的值和你从压缩文件中读取的32位无符号值进行对比,看看它们是否一致。
下面是 bzip2
使用的 CRC 算法,代码是用 Python 写的:
crcVar = 0xffffffff # Init
for cha in list(dataIn):
crcVar = crcVar & 0xffffffff # Unsigned
crcVar = ((crcVar << 8) ^ (BZ2_crc32Table[(crcVar >> 24) ^ (ord(cha))]))
return hex(~crcVar & 0xffffffff)[2:-1].upper()
(C 语言的定义可以在 bzlib_private.h
文件的第 155 到 172 行找到)
BZ2_crc32Table
这个数组/列表可以在 crctable.c
文件中找到,这个文件是 bzip2
源代码的一部分。这个 CRC 校验和算法的来源是:"..大致来源于 Rob Warnock 的代码,见 comp.compression FAQ 的第 51 节..."(crctable.c
)
这些校验和是针对 未压缩的数据 进行计算的。
你可以在这里下载源代码:http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz