CRC 原理
CRC 是一种用于检测数据传输错误或数据损坏的算法。
CRC-n-XX
其中 n 表示 CRC 的位宽,XX 表示 CRC 的标准名。例如:
- CRC-64-ECMA-182: 表示 ECMA-182 标准的 64 位 CRC。
- CRC-64-ISO: 表示 ISO 3309 (HDLC) 标准的 64 位 CRC。
数据完整性 Data integrity
Firstly, as there is no authentication, an attacker can edit a message and recompute the CRC without the substitution being detected.
没有身份验证,攻击者可以编辑message
来骗过 CRC 校验,怎么编辑我也不知道。
Secondly, unlike cryptographic hash functions, CRC is an easily reversible function, which makes it unsuitable for use in digital signatures.
CRC 函数可逆,不适用于数字签名。
Thirdly, CRC satisfies a relation similar to that of a linear function (or more accurately, an affine function):
计算过程 Computation
- 首先将多项式系数以二进制形式表示。
- 获取
message
的二进制编码 - 填充 n(多项式长度) 个 0 到
message
二进制编码的末尾。 - 设置初始值,通常全部为 0。除非特定的协议要求其他的值。
- 从最高位开始,逐位进行异或运算,如果最高为 0 则不进行运算,直接跳过。
- 直到
message
的二进制编码的末尾,也就是倒数第 n+1 位。 - 这样就会将
message
二进制编码的左边全部归零,只剩下右侧 n 位可以为非零。 - 这剩余的 n 位就是 CRC 的值。除非某些特定的规范有后续操作。
- 将 CRC 的值加到
message
二进制编码的末尾,然后重新进行步骤 5~8,会得到全零。
The most significant bit of a polynomial is always 1, and is not shown in the hex representations. 计算的时候是使用的多项式编码,是需要转为二进制并在最高为加 1 的,例如 CRC-8 的多项式编码是
0x07
,但是真正计算的时候用的是二进制编码100000111
。
举例,message
=11010011101100
,3 位 CRC 编码CRC-3
的多项式为,系数编码为1011
,那么计算过程如下:
11010011101100 000 <--- 右侧填充3位0
1011 <--- divisor (4 bits) = x^3 + x + 1
------------------
01100011101100 000
1011
------------------
00111011101100 000
1011
------------------
00010111101100 000
1011
------------------
00000001101100 000 <--- 前面的最高位为0,所以不进行运算,直接跳过了
1011
------------------
00000000110100 000
1011
------------------
00000000011000 000
1011
------------------
00000000001110 000
1011
------------------
00000000000101 000
101 1
-----------------
00000000000000 100 <--- 余数 (3 bits) 此时dividend全部为零
此时 CRC 的值为 100。想要验证,只需要将 CRC 的值加到message
二进制编码的末尾,然后重新进行步骤 5~8,会得到全零。
11010011101100 100 <--- 右侧填充CRC值
1011 <--- divisor
01100011101100 100
1011
00111011101100 100
......
00000000001110 100
1011
00000000000101 100
101 1
------------------
00000000000000 000
接下来演示一个实际的例子:
对字符 a
计算 CRC-8 的值,多项式编码为0x07
,CRC 初始值为 0x00,那么计算过程如下:
- 首先获取字符
a
的字节码:01100001
- 获取多项式
0x07
的二进制编码:100000111
- 填补 8 位 0,开始异或计算
01100001 00000000
1000001 11
------------------
00100000 11000000
100000 111
------------------
00000000 00100000
out: 00100000 => 20
如果有结果反转的话就是: reflect out: 00000100 => 04
多项式 Polynomial
Specification of a CRC code requires definition of a so-called generator polynomial. This polynomial becomes the divisor in a polynomial long division, which takes the message as the dividend and in which the quotient is discarded and the remainder becomes the result.
CRC 的多项式可以是任意的,但常用的几个多项式如下:
name | 标准 | 多项式编码(HEX) | 多项式 |
---|---|---|---|
CRC-8 | DVB-S2 | 0xD5 | |
CRC-16-CCITT | HDLC FCS,Bluetooth... | 0x1021 | |
CRC-32 | ISO,IEEE,ITU... | 0x04C11DB7 | |
CRC-64-ECMA | ECMA-182 | 0x42F0E1EBA9EA3693 | |
CRC-64-ISO | ISO 3309 | 0x000000000000001B |
位宽 Bit Width
Number of bits of CRC check result. Supports 8-bit, 16-bit, 32-bit, and 64-bit.
位宽表示校验结果的位数,例如 CRC-8、CRC-16、CRC-32、CRC-64,分别表示校验结果位的位数为:8、16、32、64。
多项式编码 Polynomial Formula(HEX)
The abbreviation of the generated formula, expressed in hexadecimal. Ignore the highest "1".
生成的编码通常是 16 进制,由于 bit 限制,需要忽略最高位的 1。例如:
CRC-3 的多项式,对应二进制为1011
,但是 CRC-3 只有 3 位校验位,则忽略最高位的 1,所以对应 16 进制编码则表示为0x3
CRC-8 同理:多项式为,对应二进制为 100000111
,但是只有 8 为校验位 ,所以忽略最高位的 1,对应 16 进制编码为0x7
- CRC-64-ECMA-182:
0x42F0E1EBA9EA3693
其他处理
有一些标准中会定义一些额外的计算,例如反向处理或位反转、结果反转、生成多项式反转等。
输入反转 Reflect In
在进行 CRC 计算之前,对输入数据的每个字节进行反转。
输出反转 Reflect Out
在输出 CRC 结果之前,对 CRC 数据的每个字节进行反转。
初始值 Initial Value
定义校验位的初始值,这样在计算的时候就不是在。message
后补零了,而是用初始值去填充
我原本以为初始值是 crc 的初始值,是我错了。其实简单来说就是对数据进行一次异或初始化使用的值,然后再进行 crc 计算。 例如:对字符串"aa"计算 crc8,多项式为 0x07,初始值为 0xFF,那么计算过程如下:
# 首先对数据进行一次初始化
01100001 01100001
11111111 <- 初始值
-----------------
10011110 01100001
# 然后开始计算
10011110 01100001 00000000
10000011 1 <- 多项式
--------------------------
00011101 11100001 00000000
10000 0111
--------------------------
00001101 10010001 00000000
1000 00111
--------------------------
00000101 10101001 00000000
100 000111
--------------------------
00000001 10110101 00000000
1 00000111
--------------------------
00000000 10110010 00000000
10000011 1
--------------------------
00000000 00110001 10000000
100000 111
--------------------------
00000000 00010001 01100000
10000 0111
--------------------------
00000000 00000001 00010000
1 00000111
--------------------------
00000000 00000000 00010111
所以结果为 0x17
结果异或 XOR Out
在 CRC 计算结束后,一些标准规定要对最终的 CRC 值与指定的值进行异或操作。
通常在没有特别说明的情况下,是结果与 0xFFFFFFFF 进行异或。
例如上面的例子中,计算"aa"的 crc 值,多项式 0x07,初始值为 0xFF,如果加上一条:结果异或值:0xFF。 那么最终结果应该为:0x17 ^ 0xFF = 0xE8
字节顺序 Bit Order
定义是以大端字节序(big-endian)还是小端字节序(little-endian)处理输入数据。、
举个例子: 例如存在某个字节编码AB12CD
如果规范定义的是大端字节序,则会转化为CDAB12
;如果小端字节序,则会转化为12ABCD