CRC(Cyclic Redundancy Check)
CRC(Cyclic Redundancy Check,循环冗余校验)是一种用于检测数据传输或存储过程中随机错误的校验算法。它通过对数据生成固定长度校验码来发现常见传输错误,但不提供密码学认证、抗篡改或来源验证;安全完整性应使用 MAC、数字签名或认证加密。
CRC 的工作原理
Section titled “CRC 的工作原理”CRC 基于二进制除法原理来进行校验。它的核心思想是将数据视为一个二进制数,然后使用一个预先定义的生成多项式(也是一个二进制数)对数据进行模除运算。得到的余数就是 CRC 校验码。
-
生成多项式:
- CRC 的生成多项式是一个预定义的二进制多项式,例如常见的 CRC-32 生成多项式为
0x04C11DB7。生成多项式的位数决定了 CRC 校验码的位数。
- CRC 的生成多项式是一个预定义的二进制多项式,例如常见的 CRC-32 生成多项式为
-
数据处理:
- 输入数据被视为一个二进制数,将生成多项式的最高位与数据对齐后进行二进制除法。得到的余数即为 CRC 校验码。
-
校验:
- 在接收端,接收到的数据与生成多项式再次进行相同的除法运算。如果余数为零,则表示数据没有发生错误;否则,说明数据在传输或存储中发生了错误。
常见的 CRC 多项式
Section titled “常见的 CRC 多项式”不同的应用场景可能使用不同的生成多项式。以下是一些常见的 CRC 多项式:
-
CRC-8:
- 生成多项式:
x^8 + x^2 + x + 1(0x07) - 用于低位数校验,如小型通信协议。
- 生成多项式:
-
CRC-16-IBM:
- 生成多项式:
x^16 + x^15 + x^2 + 1(0x8005) - 广泛用于串口通信等场合。
- 生成多项式:
-
CRC-32:
- 生成多项式:
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(0x04C11DB7) - 用于网络通信、文件校验等场合,如在以太网、ZIP 文件格式中。
- 生成多项式:
CRC 的优点和局限性
Section titled “CRC 的优点和局限性”优点:
- 高效性:CRC 计算简单且效率高,非常适合硬件实现,因此在实时数据校验中应用广泛。
- 强大的检测能力:CRC 能够有效检测单比特错误、双比特错误、奇数个比特错误以及某些特定类型的突发错误。
局限性:
- 不能纠错:CRC 只能检测错误,不能纠正错误。因此,在检测到错误后通常需要重新传输数据。
- 不是安全机制:CRC 主要用于检测随机传输错误,攻击者可以有意构造同一 CRC 或同步修改数据与校验码,因此不能替代密码学完整性校验。
CRC 的应用
Section titled “CRC 的应用”- 网络通信:CRC 广泛应用于网络通信协议中,如以太网(Ethernet)帧校验中使用的 CRC-32。
- 数据存储:在存储设备中,如硬盘、光盘等,使用 CRC 校验来检测和校验数据完整性。
- 文件传输:CRC 经常用于文件传输过程中,如 ZIP 文件的校验,以确保文件在传输过程中没有损坏。
- 嵌入式系统:许多嵌入式系统使用 CRC 来确保数据传输的可靠性,特别是在资源受限的环境中。
CRC-8 示例
Section titled “CRC-8 示例”#include <stdio.h>#include <stdint.h>
uint8_t crc8(uint8_t *data, size_t length) { uint8_t crc = 0x00; // 初始化CRC值 uint8_t polynomial = 0x07; // CRC-8多项式
for (size_t i = 0; i < length; i++) { crc ^= data[i]; // 将当前数据字节与CRC寄存器进行异或
for (uint8_t j = 0; j < 8; j++) { // 对每个字节的每一位进行处理 if (crc & 0x80) { // 检查最高位 crc = (crc << 1) ^ polynomial; // 左移并异或多项式 } else { crc <<= 1; // 仅左移 } } }
return crc;}
int main() { uint8_t data[] = {0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31}; // 示例数据 size_t length = sizeof(data) / sizeof(data[0]);
uint8_t crc = crc8(data, length); printf("CRC-8: 0x%02X\n", crc);
return 0;}CRC-16-MODBUS 示例
Section titled “CRC-16-MODBUS 示例”#include <stdio.h>#include <stdint.h>
uint16_t crc16_modbus(uint8_t *data, size_t length) { uint16_t crc = 0xFFFF; // CRC初始值
for (size_t i = 0; i < length; i++) { crc ^= data[i]; // 将当前字节与CRC寄存器异或
for (uint8_t j = 0; j < 8; j++) { // 对每个字节的每一位进行处理 if (crc & 0x0001) { // 检查最低位 crc = (crc >> 1) ^ 0xA001; // 右移并异或多项式 } else { crc >>= 1; // 仅右移 } } }
return crc;}
int main() { uint8_t data[] = {0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31}; // 示例数据 size_t length = sizeof(data) / sizeof(data[0]);
uint16_t crc = crc16_modbus(data, length); printf("CRC-16-MODBUS: 0x%04X\n", crc);
return 0;}- RFC 1952: GZIP File Format Specification(访问日期:2026-05-31)
- RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3(访问日期:2026-05-31)