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

11 投票
3 回答
7076 浏览
提问于 2025-04-16 08:40

我正在尝试计算和验证压缩的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->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行)。

3 个回答

0

可以看看fastcrc这个Python库,它里面有bzip2格式的crc32实现。

https://fastcrc.readthedocs.io/en/latest/#fastcrc.crc32.bzip2

0

在已有答案的基础上,最后在数据流的末尾有一个最终的校验和(就是在eos_magic之后的那个)。这个校验和是用来检查所有单独的霍夫曼块校验和的。它一开始是零。每次你验证完一个霍夫曼块的校验和后,就会更新这个值。更新的方法如下:

crc: u32 = # latest validated Huffman block CRC
ccrc: u32 = # current combined checksum

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

最后,要把ccrc的值和你从压缩文件中读取的32位无符号值进行对比,看看它们是否一致。

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()

(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

撰写回答