在Python中计算/验证bz2(bzip2)CRC32

11

我正在尝试计算/验证压缩的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 计算以获得相同的 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->blockCRCbzlib.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行)。


bzip2 使用 AUTODIN-II、Ethernet 和 FDDI 32 位 CRC 标准。 - soulseekah
我尝试的第一件事是计算未压缩数据的CRC。但是它不匹配。数据是文本文件中的简单“hello”。我尝试了不同的在线CRC32计算器和我可以使用的库。要么我非常不幸,要么我做错了什么。 - soulseekah
open("test.txt.bz2","wb").write(bz2.compress("hello"))。CRC32标签表示该块的CRC32为19 31 65 3d,而hex(crc32("hello"))(来自binascii)返回36 10 a6 86 - soulseekah
2
但是小的bz2文件不包含ENDMARK。--实际上是包含的;块没有填充到字节,所以您还需要查找魔术值的位移版本。 - adw
1
@Soulseekah 既然你已经编辑了标题并包含了“[已解决]”,那么你能否发布一个回答,陈述你的结论,并(可选)将其标记为你正在寻找的答案? - spade78
显示剩余2条评论
3个回答

3
以下是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()

bzlib_private.h文件的155-172行可以找到C代码的定义。

BZ2_crc32Table数组/列表可在bzip2源代码中的crctable.c中找到。这个CRC校验算法引用了Rob Warnock在comp.compression FAQ的第51节中的代码,但进行了相应修改。(crctable.c

校验和是通过对未压缩数据计算得出的。

可以从这里下载源代码:http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz


这是Java中的bzlib2 CRC实现:http://www.zetagrid.net/zeta/doc/src/org/apache/tools/bzip2/CRC.java.html - soulseekah

0
补充现有的答案,流结尾处(eos_magic 后面)有一个最终的校验和。它用于校验所有单独的哈夫曼块校验和。它初始化为零。每当您完成验证现有哈夫曼块的校验和时,它就会更新。要更新它,请执行以下操作:
crc: u32 = # latest validated Huffman block CRC
ccrc: u32 = # current combined checksum

ccrc = (ccrc << 1) | (ccrc >> 31);
ccrc ^= crc;

最后,验证ccrc的值是否与从压缩文件中读取的32位无符号值相匹配。

0

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