如何计算CRC32校验和?


102

也许我只是看不到它,但是CRC32似乎不必要地复杂,或者在我能在网上找到的任何地方都没有足够的解释。

我知道这是消息值的非基于进位算术除法的余数,再除以(生成器)多项式,但是它的实际实现使我无所适从。

我已经阅读了《 CRC错误检测算法的无痛指南》,我必须说这并非没有痛苦。它在理论上讲得相当不错,但是作者从来没有想到一个简单的“就是这样”。他确实说过标准CRC32算法的参数是什么,但是他却忽略了清楚地说明如何实现它。

让我感到高兴的部分是,当他说“这就是它”,然后接着说:“哦,顺便说一句,它可以被颠倒或以不同的初始条件开始”,而没有给出最终方法的明确答案。给定他刚刚添加的所有更改,计算CRC32校验和的过程。

  • 关于CRC32的计算方式有更简单的解释吗?

我试图用C语言编写表的形成方式:

for (i = 0; i < 256; i++)
{
    temp = i;

    for (j = 0; j < 8; j++)
    {
        if (temp & 1)
        {
            temp >>= 1;
            temp ^= 0xEDB88320;
        }
        else {temp >>= 1;}
    }
    testcrc[i] = temp;
}

但这似乎产生了与我在互联网上其他地方发现的价值观不一致的价值观。我可以使用在网上找到的值,但是我想了解它们是如何创建的。

在清理这些难以置信的混乱号任何帮助将非常感激。


9
您生成CRC32表的代码似乎正确。您的lsbit-first(反向)CRC32多项式0xEDB88320也可以写为msbit-first(正常0x04C11DB7。您在其他地方找到的表值是使用相同的CRC多项式生成的吗?
jschmier 2011年

1
@jschmier嗨,我想我要跟这个人问问题吗?stackoverflow.com/questions/62168128/...
bluejayke

如果任何人是好奇地阅读“无痛指南CRC错误检测算法”链接到上面,即原始URL是大清洗,但谷歌很容易找到几个副本,包括这一个:zlib.net/crc_v3.txt
斯特凡

Answers:


114

CRC32的多项式为:

x 32 + x 26 + x 23 + x 22 + x 16 + x 12 + x 11 + x 10 + x 8 + x 7 + x 5 + x 4 + x 2 + x + 1

或以十六进制和二进制形式:

0x 01 04 C1 1D B7
1 0000 0100 1100 0001 0001 1101 1011 0111

最高期限(x 32)通常没有明确地写成,因此可以用十六进制表示,就像

0x 04 C1 1D B7

随意计算1和0,但是您会发现它们与多项式匹配,其中1位0(或第一位)和x位1(或第二位)。

为什么是这个多项式?因为需要一个给定的多项式标准,并且该标准由IEEE 802.3设置。同样,找到有效检测不同位错误的多项式也是极其困难的。

您可以将CRC-32视为一系列“不带进位的二进制算术”,或者基本上是“ XOR和移位运算”。这在技术上称为多项式算法。

为了更好地理解它,请考虑以下乘法:

(x^3 + x^2 + x^0)(x^3 + x^1 + x^0)
= (x^6 + x^4 + x^3
 + x^5 + x^3 + x^2
 + x^3 + x^1 + x^0)
= x^6 + x^5 + x^4 + 3*x^3 + x^2 + x^1 + x^0

如果我们假设x为2,则得到:

x^7 + x^3 + x^2 + x^1 + x^0

为什么?因为3x ^ 3是11x ^ 11(但我们只需要1或0个数位),所以我们继续:

=1x^110 + 1x^101 + 1x^100          + 11x^11 + 1x^10 + 1x^1 + x^0
=1x^110 + 1x^101 + 1x^100 + 1x^100 + 1x^11 + 1x^10 + 1x^1 + x^0
=1x^110 + 1x^101 + 1x^101          + 1x^11 + 1x^10 + 1x^1 + x^0
=1x^110 + 1x^110                   + 1x^11 + 1x^10 + 1x^1 + x^0
=1x^111                            + 1x^11 + 1x^10 + 1x^1 + x^0

但是数学家更改了规则,使其成为mod2。因此,基本上任何二进制多项式mod 2都只是加法而没有进位或XOR。因此我们的原始方程式如下:

=( 1x^110 + 1x^101 + 1x^100 + 11x^11 + 1x^10 + 1x^1 + x^0 ) MOD 2
=( 1x^110 + 1x^101 + 1x^100 +  1x^11 + 1x^10 + 1x^1 + x^0 )
= x^6 + x^5 + x^4 + 3*x^3 + x^2 + x^1 + x^0 (or that original number we had)

我知道这是信念的飞跃,但这超出了我作为行程序员的能力。如果您是CS的核心学生或工程师,那么我将提出挑战。每个人都将从此分析中受益。

因此,举一个完整的例子:

   Original message                : 1101011011
   Polynomial of (W)idth 4         :      10011
   Message after appending W zeros : 11010110110000

现在,我们使用CRC算法将增强后的消息除以Poly。这与以前相同:

            1100001010 = Quotient (nobody cares about the quotient)
       _______________
10011 ) 11010110110000 = Augmented message (1101011011 + 0000)
=Poly   10011,,.,,....
        -----,,.,,....
         10011,.,,....
         10011,.,,....
         -----,.,,....
          00001.,,....
          00000.,,....
          -----.,,....
           00010,,....
           00000,,....
           -----,,....
            00101,....
            00000,....
            -----,....
             01011....
             00000....
             -----....
              10110...
              10011...
              -----...
               01010..
               00000..
               -----..
                10100.
                10011.
                -----.
                 01110
                 00000
                 -----
                  1110 = Remainder = THE CHECKSUM!!!!

除法运算得到的商除以余数,即余数即计算出的校验和。这样就结束了计算。通常,然后将校验和附加到消息中并发送结果。在这种情况下,传输将为:11010110111110。

仅将32位数字用作除数,并将整个流用作除数。丢弃商并保留余数。在邮件末尾添加其余内容,然后得到CRC32。

普通人评论:

         QUOTIENT
        ----------
DIVISOR ) DIVIDEND
                 = REMAINDER
  1. 取前32位。
  2. 移位位
  3. 如果32位小于DIVISOR,请转到步骤2。
  4. 通过DIVISOR对32位进行XOR。转到步骤2。

(请注意,该流必须可被32位整除,或者应对其进行填充。例如,必须填充8位ANSI流。同样在该流的末尾,该除法被暂停。)


13
最后为“ Average Guy Review” +1,可能是考虑将其移至顶部-一种TL;DR:P
aaronsnoswell,

4
@abstractnature请记住,我们正在划分多项式,而不仅仅是二进制数。我们无法进行“正常”减法,因为我们无法从$ x ^ {n + 1} $中“借用” $ x ^ n $;他们是不同的东西。另外,由于这些位仅是0或1,-1甚至是多少?确实,我们正在系数在$ Z / 2Z $字段中的多项式环中工作,该字段只有两个元素0和1,其中$ 1 + 1 = 0 $。通过将系数放在一个字段中,然后多项式就形成了所谓的欧几里得域,该域基本上只允许我们尝试首先定义的内容。
calavicci 2015年

6
只是为了说明实际的多项式是100000100110000010001110110110111 = 0x104C11DB7。MSB是隐式的,但在实现中仍应考虑在内。由于多项式必须为33位长(因此余数可以为32位长),因此将始终设置该位,因此有人省略了MSB。
Felipe T.

2
x^6 + x^5 + x^4 + 3*x^3 + x^2 + x^1 + x^0 ... If we assume x is base 2 then we get: x^7 + x^3 + x^2 + x^1 + x^0。这不是数学的原理。多项式的系数为mod(2)或GF(2),将x保留下来,得到x ^ 6 + x ^ 5 + x ^ 4 + x ^ 3 + x ^ 2 + x ^ 1 + x ^ 0(因为3 mod(2)= 1)。Tack the remainder on the end of your message-从技术上讲,从添加到消息的0位中减去余数,但是由于这是mod(2)数学,加法和减法都与XOR相同,并且与余数进行XOR的零位相同其余的。
rcgldr

2
@MarcusJ-- Why did you append four 0s though?计算crc的软件算法有效地附加了0,即使它不是很明显。如果显示的是使用长手除法的CRC计算,则为了使除法示例正确显示,需要添加0。
rcgldr

11

对于IEEE802.3,CRC-32。将整个消息视为串行比特流,在消息末尾附加32个零。接下来,您必须反转消息的每个字节的位,并对前32位进行1的补码。现在除以CRC-32多项式0x104C11DB7。最后,您必须对该除法的32位余数进行1的补码位反转,将余数的4个字节中的每个字节取反。这将成为附加在消息末尾的32位CRC。

这种奇怪过程的原因是,第一个以太网实现会一次将消息串行化一个字节,然后首先传输每个字节的最低有效位。然后,串行位流经过串行CRC-32移位寄存器计算,该过程得到了简单的补充,并在消息完成后通过导线发送出去。对消息的前32位进行补充的原因是,即使消息全为零,也不会得到全零的CRC。


2
到目前为止,这是最好的答案,尽管我将“对4个字节中的每个字节进行位反转”替换为“对4个字节进行位反转,将它们视为一个实体”,例如将“ abcdefgh ijklmnop qrstuvwx yzABCDEF”替换为“ FEDCBAzy xwvutsrq” ponmlkji hgfedcba'。另请参阅:CRC-32哈希教程-AutoHotkey社区
vafylec

1
嗨,确切的“信息”是什么?stackoverflow.com/questions/62168128/...
bluejayke

10

CRC非常简单。您可以采用表示为位和数据的多项式,然后将多项式划分为数据(或者将数据表示为多项式并执行相同的操作)。余数(在0和多项式之间)是CRC。您的代码有点难以理解,部分原因是它不完整:没有声明temp和testcrc,因此不清楚正在索引的内容以及通过该算法运行的数据量。

理解CRC的方法是尝试使用带有短多项式(可能为4位)的一小段数据(大约16位)来计算一些。如果您以这种方式进行练习,您将真正了解如何进行编码。

如果您经常这样做,则在软件中计算CRC的速度将非常慢。硬件计算效率更高,只需要几个门。


1
对于CRC32或CRC32b,做我们得到哈希冲突的含义有两个不同的字符串,我们会得到相同的CRC
indianwebdevil

1
嗨,我有点困惑“将多项式除以数据”的意思是什么?stackoverflow.com/questions/62168128/…表示的多项式中的X是什么?我是否使用块中的其他字节?
bluejayke

7

除了Wikipedia 循环冗余校验CRC计算文章外,我还发现了一篇名为《Reverseing CRC-Theory and Practice *》的论文,是不错的参考。

本质上,存在三种用于计算CRC的方法:代数方法,面向位的方法和表驱动的方法。在CRC反转-理论与实践 *中,在理论上解释了这三种算法/方法中的每一种,并在附录中以C编程语言对CRC32的实现进行了说明。

* PDF链接
反向CRC –理论与实践。
HU柏林公共报告
SAR-PR- 2006-05
2006年5月
作者:
马丁·斯蒂格,亨利克·普洛茨,沃尔夫·穆勒,延斯·彼得·雷德利希


嗨,您能详细说明一下吗?
bluejayke

6

我花了一段时间试图找出这个问题的答案,最后我今天在CRC-32上发布了一个教程: CRC-32哈希教程-AutoHotkey Community

在此示例中,我演示了如何计算ASCII字符串“ abc”的CRC-32哈希:

calculate the CRC-32 hash for the ASCII string 'abc':

inputs:
dividend: binary for 'abc': 0b011000010110001001100011 = 0x616263
polynomial: 0b100000100110000010001110110110111 = 0x104C11DB7

011000010110001001100011
reverse bits in each byte:
100001100100011011000110
append 32 0 bits:
10000110010001101100011000000000000000000000000000000000
XOR the first 4 bytes with 0xFFFFFFFF:
01111001101110010011100111111111000000000000000000000000

'CRC division':
01111001101110010011100111111111000000000000000000000000
 100000100110000010001110110110111
 ---------------------------------
  111000100010010111111010010010110
  100000100110000010001110110110111
  ---------------------------------
   110000001000101011101001001000010
   100000100110000010001110110110111
   ---------------------------------
    100001011101010011001111111101010
    100000100110000010001110110110111
    ---------------------------------
         111101101000100000100101110100000
         100000100110000010001110110110111
         ---------------------------------
          111010011101000101010110000101110
          100000100110000010001110110110111
          ---------------------------------
           110101110110001110110001100110010
           100000100110000010001110110110111
           ---------------------------------
            101010100000011001111110100001010
            100000100110000010001110110110111
            ---------------------------------
              101000011001101111000001011110100
              100000100110000010001110110110111
              ---------------------------------
                100011111110110100111110100001100
                100000100110000010001110110110111
                ---------------------------------
                    110110001101101100000101110110000
                    100000100110000010001110110110111
                    ---------------------------------
                     101101010111011100010110000001110
                     100000100110000010001110110110111
                     ---------------------------------
                       110111000101111001100011011100100
                       100000100110000010001110110110111
                       ---------------------------------
                        10111100011111011101101101010011

remainder: 0b10111100011111011101101101010011 = 0xBC7DDB53
XOR the remainder with 0xFFFFFFFF:
0b01000011100000100010010010101100 = 0x438224AC
reverse bits:
0b00110101001001000100000111000010 = 0x352441C2

thus the CRC-32 hash for the ASCII string 'abc' is 0x352441C2

1
如果您想提高速度,Intel的一些工程师在2006年左右就提出了一种方法,该方法通常同时使用机器数据总线宽度的4或8字节。学术论文:static.aminer.org/pdf/PDF/000/432/446/…Sourceforge上的 项目:sourceforge.net/projects/slicing-by-8 General crc页面:create.stephan-brumme.com/crc32
Alan科里

1
嗨,谢谢,看起来很棒,但是您究竟如何得到多项式值呢?X代表什么?当它说x ^ 32时,x是32的幂还是按位运算符^stackoverflow.com/questions/62168128/…–
bluejayke


1

为了减少crc32使用提醒,您需要:

  1. 每个字节取反
  2. 与0xFF的前四个字节进行异或(这是为了避免前导0出现错误)
  3. 在末尾添加填充(这是为了使最后4个字节参与哈希)
  4. 计算提醒
  5. 再次反转位
  6. 再次对结果进行异或。

在代码中,这是:


func CRC32 (file []byte) uint32 {
    for i , v := range(file) {
        file[i] = bits.Reverse8(v)
    }
    for i := 0; i < 4; i++ {
        file[i] ^= 0xFF
    }

    // Add padding
    file = append(file, []byte{0, 0, 0, 0}...)
    newReminder := bits.Reverse32(reminderIEEE(file))

    return newReminder ^ 0xFFFFFFFF
}

其中提醒IEEE是GF(2)[x]上的纯提醒


1
我有一点(双关语意)麻烦理解这一点吗?stackoverflow.com/questions/62168128/...
bluejayke

1
嘿@bluejayke,请检查此库github.com/furstenheim/sparse_crc32/blob/master/main.go它为稀疏文件实现了crc32,您可以在其中看到有关计算的细节。它没有经过优化,因此比普通实现更容易遵循。可能是您不了解的是GF(2)[x]部分。基本上x ^ 3 + x表示1010,x ^ 4 + x + 1表示10011。然后您需要执行除法,例如x ^ 3 + x是x *(x ^ 2 + 1)。因此x上的x ^ 3 + x的提醒为0,但x ^ 2上的提醒为x ^ 2 * x + x,即提醒为x。
加布里埃尔·弗斯滕海姆

1
@bluejayke和hinterIEEE表示针对众所周知的多项式,即IEEE多项式的提醒
Gabriel Furstenheim

您好,再次感谢您的回复。我只是想了解(出于JavaScript的目的)多项式中的“ x”代表什么。“ x”是我在这里缺少的某种代码字吗?这里有很多术语使我感到困惑,我以前从未听说过CRC32,甚至在搜索后我也找不到它的真正解释。例如,对于一个PNG,它说我需要获取“每个块的CRC”,这是否意味着“对于块中的所有数据”?但是,如何将其“插入”多项式?“ x”代表什么?同样,当它说x ^ 32时,就像Math.pow(x,32)或按位^
bluejayke

1
@bluejayke,您好,x是简化计算的抽象。不应被任何东西替代。x ^ 2我的意思是x * x,作为形式乘法。在chrisballance.com/wp-content/uploads/2015/10/CRC-Primer.html上,您可以找到有关该划分的很好的解释。我尝试用答案来填补(该链接中的)除法与实际计算之间的空白
Gabriel Furstenheim
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.