验证 CRC32 校验和
Validating CRC32 checksum
我能够成功生成与预期输出匹配的 CRC32。但是,当我尝试将带有校验和 C(由 CRC32 函数生成)的消息 M 重新插入同一个 CRC32 生成器时,我无法获得 0x00000000 的预期输出。
我的 CRC32 生成器基本上就是这里的代码 (https://lxp32.github.io/docs/a-simple-example-crc32-calculation/),带有校验和的附加函数参数,因为在我的例子中,消息和校验和在收到时位于不同的内存位置。
uint32_t crc32(const char* s, size_t n, uint32_t checksum) {
uint32_t crc = 0xFFFFFFFF;
size_t i; size_t j;
for (i = 0; i < n; i++) {
char ch = s[i];
uint32_t t = (ch ^ crc) & 0xFF;
crc = (crc >> 8) ^ crc32_table[t];
}
if (i & 1) { // if i ended odd, write a 0x00 pad bit
uint32_t t = (0x00 ^ crc) & 0xFF;
crc = (crc >> 8) ^ crc32_table[t];
}
// include checksum bits if not 0
if (checksum) {
for (j = 0; j < 4; j++) {
char ch = (checksum & 0xFF);
uint32_t t = (ch ^ crc) & 0xFF;
crc = (crc >> 8) ^ crc32_table[t];
checksum >>= 8;
}
}
else {
// append 32-bits of 0's
for (j = 0; j < 4; j++) {
uint32_t t = (0x00 ^ crc) & 0xFF;
crc = (crc >> 8) ^ crc32_table[t];
}
}
return ~crc;
}
bool validate_crc32(const char* msg, size_t n, uint32_t checksum) {
uint32_t res = crc32(msg, n, checksum);
return res == 0;
}
CRC32 输入 1:0x61 0x62 0x63 0x64 0x00 0x00 0x00 0x00
CRC32 输出 1:0x87988EF9
CRC32 输入 2:0x61 0x62 0x63 0x64 0x87 0x98 0x8E 0xF9
CRC32 输出 2:0x5D19F7CF
我觉得我在这里有什么地方不明白...
- 如果你做对了,你就不会得到零分。您每次都得到相同的常量,但对于此 CRC,它不是零。对于这个,常数是
0x2144df1c
。 (它不是零,因为有一个非零值的最终异或。)
- 你做的不对。首先,您不需要也不应该附加四个零。只需计算消息的 CRC。其次,以 little-endian 顺序附加 CRC。不是大端顺序。 (因为这是向下移动的反射 CRC。)
- 以这种方式检查 CRC 没有什么意义。只需计算消息的 CRC,然后 将结果与发送的 CRC 进行比较。这对阅读您的代码的人来说更容易、更透明,并且避免了对额外四个字节进行不必要的 CRC 计算。比较比四个字节的 CRC 计算快得多。
- 为什么要为奇数长度的消息多加一个零?
我能够成功生成与预期输出匹配的 CRC32。但是,当我尝试将带有校验和 C(由 CRC32 函数生成)的消息 M 重新插入同一个 CRC32 生成器时,我无法获得 0x00000000 的预期输出。
我的 CRC32 生成器基本上就是这里的代码 (https://lxp32.github.io/docs/a-simple-example-crc32-calculation/),带有校验和的附加函数参数,因为在我的例子中,消息和校验和在收到时位于不同的内存位置。
uint32_t crc32(const char* s, size_t n, uint32_t checksum) {
uint32_t crc = 0xFFFFFFFF;
size_t i; size_t j;
for (i = 0; i < n; i++) {
char ch = s[i];
uint32_t t = (ch ^ crc) & 0xFF;
crc = (crc >> 8) ^ crc32_table[t];
}
if (i & 1) { // if i ended odd, write a 0x00 pad bit
uint32_t t = (0x00 ^ crc) & 0xFF;
crc = (crc >> 8) ^ crc32_table[t];
}
// include checksum bits if not 0
if (checksum) {
for (j = 0; j < 4; j++) {
char ch = (checksum & 0xFF);
uint32_t t = (ch ^ crc) & 0xFF;
crc = (crc >> 8) ^ crc32_table[t];
checksum >>= 8;
}
}
else {
// append 32-bits of 0's
for (j = 0; j < 4; j++) {
uint32_t t = (0x00 ^ crc) & 0xFF;
crc = (crc >> 8) ^ crc32_table[t];
}
}
return ~crc;
}
bool validate_crc32(const char* msg, size_t n, uint32_t checksum) {
uint32_t res = crc32(msg, n, checksum);
return res == 0;
}
CRC32 输入 1:0x61 0x62 0x63 0x64 0x00 0x00 0x00 0x00
CRC32 输出 1:0x87988EF9
CRC32 输入 2:0x61 0x62 0x63 0x64 0x87 0x98 0x8E 0xF9
CRC32 输出 2:0x5D19F7CF
我觉得我在这里有什么地方不明白...
- 如果你做对了,你就不会得到零分。您每次都得到相同的常量,但对于此 CRC,它不是零。对于这个,常数是
0x2144df1c
。 (它不是零,因为有一个非零值的最终异或。) - 你做的不对。首先,您不需要也不应该附加四个零。只需计算消息的 CRC。其次,以 little-endian 顺序附加 CRC。不是大端顺序。 (因为这是向下移动的反射 CRC。)
- 以这种方式检查 CRC 没有什么意义。只需计算消息的 CRC,然后 将结果与发送的 CRC 进行比较。这对阅读您的代码的人来说更容易、更透明,并且避免了对额外四个字节进行不必要的 CRC 计算。比较比四个字节的 CRC 计算快得多。
- 为什么要为奇数长度的消息多加一个零?