CRC 校验值计算
一次算出 CRC-8、CRC-16/CCITT-FALSE、CRC-16/MODBUS 和 CRC-32,输入可以是文本或十六进制字节。
算法参数
| 算法 | 多项式 | 初始值 | 输入反转 | 输出反转 | 异或值 |
|---|---|---|---|---|---|
| CRC-8 | 0x07 | 0x00 | 否 | 否 | 0x00 |
| CRC-16/CCITT-FALSE | 0x1021 | 0xFFFF | 否 | 否 | 0x0000 |
| CRC-16/MODBUS | 0x8005 | 0xFFFF | 是 | 是 | 0x0000 |
| CRC-32 | 0x04C11DB7 | 0xFFFFFFFF | 是 | 是 | 0xFFFFFFFF |
宽度由算法名上的数字决定。对 ASCII 字符串「123456789」求校验值,四种算法的结果分别是 0xF4、0x29B1、0x4B37 和 0xCBF43926,可以拿来核对实现。
CRC 在算什么
把要发送的数据看成一个很长的大整数,再约定一个生成多项式,用模 2 除法(加减都是异或,不产生进位)去除它,得到的余数就是 CRC 校验值。发送方在数据末尾附上这个余数,接收方用同样的参数重算一遍并比对,就能发现传输过程中有没有比特被翻转。它本质上是一段带余除法,硬件上可以用移位寄存器和几个异或门实现,开销很小。
同一个「CRC-16」其实是一族算法,差别在五项参数:多项式、初始值(init)、输入字节是否按位反转(refin)、输出寄存器是否反转(refout)、最后是否再异或一个值(xorout)。参数不同就是两种互不兼容的 CRC。例如 CRC-16/CCITT-FALSE 用多项式 0x1021、初始值 0xFFFF、不反转;CRC-16/MODBUS 用多项式 0x8005、初始值 0xFFFF、输入输出都反转,所以两者对同一段数据的结果完全不同。
CRC 和 MD5、SHA 这类密码学哈希是两回事。CRC 的输出很短(8、16、32 位),速度快,目标是发现传输中的随机比特错误:单比特、双比特、奇数个错误和一定长度内的突发错误都能被检出。哈希的输出长(128 到 512 位),并且是单向、雪崩的,改一个比特结果面目全非,用来做完整性校验和防篡改。CRC 是线性的,很容易被人为构造出让校验值不变的数据,所以它只防意外、不防攻击。
实际使用里最容易踩的坑是字节顺序。协议文档给的 CRC-16 值通常是数值,但写进报文时要看约定用大端还是小端;Modbus RTU 就是把 16 位校验值的低字节放在前面传输,所以抓包时看到的两字节和计算出的数值是反的。另外 CRC-16 有 CCITT、XMODEM、IBM、DNP 一大堆变体,参数表对不上时结果自然对不上,先用「123456789」这串标准输入分别验证实现和参数再排查更省事。