这是找到校验和的正确方法吗?

Is this the right way to find a checksum?

我正在尝试计算某些数据的校​​验和。这是代码:

#include <stdio.h>
#include <string.h>

int main()
{
    char MyArray[] = "my secret data";
    char checksum = 0;
    int SizeOfArray = strlen(MyArray);

    for(int x = 0; x < SizeOfArray; x++)
    {
          checksum += MyArray[x];
    }
    printf("Sum of the bytes for MyArray is: %d\n", checksum);

    printf("The checksum: \n");
    checksum = (checksum ^ 0xFF);
    printf("%d\n",checksum);
}

输出:

Sum of the bytes for MyArray is: 70
The checksum:
-71

代码中的修改:

#include <stdio.h>
#include <string.h>

int main()
{
    char MyArray[] = "my secret data";
    char checksum = 0; // could be an int if preferred
    int SizeOfArray = strlen(MyArray);

    for(int x = 0; x < SizeOfArray; x++)
    {
          checksum += MyArray[x];
    }
    printf("Sum of the bytes for MyArray is: %d\n", checksum);

    //Perform bitwise inversion
    checksum=~checksum;
    //Increment
    checksum++;
    printf("Checksum for MyArray is: %d\n", checksum);
    }

输出:

Sum of the bytes for MyArray is: 70
Checksum for MyArray is: -70

为什么要修改校验值?不同的算法会提供不同的校验和吗?

最终价值有什么用?好吧,实际上我不清楚校验和及其在数据验证中的用途。我在网上搜索了一下,发现了很多文章,但还是不清楚。希望我今天能在这里了解校验和。

在考虑如何生成校验和之前,您需要了解什么是校验和。假设通过不可靠的通信通道(例如网络连接)发送数据的问题。您需要确保没有干扰影响您的消息。

实现此目的的一种方法是将消息发送两次,并检查差异(实际上,在两次消息的传输过程中发生完全相同的错误的可能性很小)。但是,这需要使用相当多的带宽(发送消息两次)。

一种更有效的方法是根据消息计算一个值并将其附加到消息中。收件人然后应用相同的功能并检查值是否相同。

举个更直观的例子,一本书的校验和可能就是页数。您从图书馆买了一本书并数了数页数。如果页数不是您预期的,则有问题。

您实现了一个特定的校验和函数(总和的 LSB),这很好。所有校验和函数都有一些您应该注意的属性,但关键是没有正确的方法来计算校验和。有许多功能可用于此目的。

校验和通常用于检测数据的变化。通信,encryption/signature,等等...校验和无处不在。

校验和如何有用?

  • 它检测到例如 1 位的变化
  • 它甚至可以检测到超过 1 位的变化

这可能看起来很矛盾,但是当只有 1 位发生变化时,您的校验和就会起作用。然而,拿

(A) checksum += 0x11 instead of 0x10

以后

(B) checksum += 0x30 instead of 0x31

在 (A) 中校验和将为 -1... 而在 (B) 中将为 +1。正负 1 == 0。这两个错误将不会 被您的校验和检测到。

基本上校验和的质量取决于

  • 关于校验和的长度(校验和越大越能容纳更大的数据,没有"looping"(一个字节只有256个校验和,2个字节有65536个;请注意,在上述情况下,您的算法不会改变结果)

  • 校验和计算的质量,以尽可能防止两个差异相互抵消。

有很多可用的算法。 This answer on SO 是一个好的开始。

这就是校验和算法的优点:生成校验和的方式以及检查校验和的方式在某种程度上是对称

  1. 关于校验和

校验和通常用于验证数据的完整性,尤其是在noisy/unrealiable 通信通道上。因此,它主要用于错误检测。也就是说,知道接收到的数据是否正确

这与例如纠错完全不同。因为它的用途是 not 只是为了 check 如果有错误,还要 correct .

错误检测和错误纠正都有开销(或增强)数据。即不属于原始数据的数据,但为了检查原始数据是否被更改(例如在案例或错误检测中)或更正它(例如在大小写或纠错)。

与错误检测算法不同,纠错算法的开销数据大小往往 与原始数据成正比 增长(因为您拥有的数据越多,您需要的开销就越大恢复它)。

我们通常不希望开销数据量很大,因为大数据意味着我们需要处理更多的资源(即处理时间、传输时间等)。因此,从这个意义上说,良好校验和算法通常是使用最小开销数据量来检测错误 但具有很好的 鲁棒性 (极少产生错误结果)。

有了这个理解,问题就来了,因为校验和的稳健性实际上不仅取决于 算法 ,还取决于 通道 特征。一些渠道可能容易出现某些错误,而其他渠道可能会出现不同的错误。

但是,总的来说,有一些校验和比其他校验和更受欢迎、更可靠。对于后者,部分是因为它具有固有的 属性 以确保它在大多数(如果不是全部)通道中都是健壮的(我最喜欢的错误检测算法之一是 CRC - 循环冗余校验 因为它具有固有的 属性)。 但是没有适合所有场景的完美校验和,这真的取决于用途和场景。

但是,您仍然可以测量校验和算法的 robustness。并且有一种数学方法可以做到这一点,我认为这超出了本次讨论的范围。因此,在这些意义上,某些校验和可以说比其他校验和弱。您在问题中显示的校验和也是弱校验和,因为它们可能比强校验和(例如 CRC)更容易产生错误结果。

  1. 关于代码

与 0xFF 的 8 位 XOR 与二进制反转值完全等价,并且不难看出。

与 0xFF 异或

1110 0010
1111 1111
--------- XOR
0001 1101 //notice that this is exactly the same as binary inverting!

因此,当您对 0xFF 和 ~checksum 进行异或运算时,您会得到相同的结果 -71(并且由于您的数据类型是 char,它具有负数)。然后你将它递增 1,因此你得到 -70.

  1. 大约 2' 补码

Two's complement is a mathematical operation on binary numbers, as well as a binary signed number representation based on this operation. Its wide use in computing makes it the most important example of a radix complement. (wikipedia)

换句话说,2'补码是找到一个值的负表示(在计算机二进制中),其方法正如您正确所做的那样,将其所有位取反,然后将其加一。这就是为什么 你得到 -70 乘以 2' 补 70。但这确实意味着2'补码和0xFF的异或是一样的,而且你可以通过例子看到,它真的不是相同的。

0xFF 对 8 位数据执行的异或操作简单地相当于反转其所有位。它不会加一.

  1. 关于add/read校验和的读取方式

由于校验和用于了解数据的完整性(无论是否被更改),人们试图找到最佳实践来做到这一点。您所做的实际上是通过 2' 补码或与 0xFF 异或来获取校验和。

他们是这样做的:

  • 对于 2' 补码校验和。 假设你的消息长度是 N。因为你通过 N 个数字的总和得到的是,比方说,70。然后通过添加 2' 补码校验和(即 -70), 在接收端,你只需需要对包括校验和在内的所有 N+1 条消息求和,如果消息未更改,您应该得到 0。这就是正确使用2'补码校验和的方法。
  • 与 0xFF 异或 同样,在与前面相同的示例中,如果对所有 N+1 条消息(包括校验和)求和,则应该得到 -1。由于 -1 的十六进制表示是 8 位有符号的 0xFF,因此 通过将结果 (-1) 与 0xFF 进行异或运算,如果消息不包含错误,你应该得到 0xFF ^ 0xFF = 0

因此在这两种情况下,您只需要通过检查最终结果是否为 0(无错误)来检查消息是否包含错误! 而这个对于校验和算法通常是正确的!

这就是校验和算法的优点:您生成校验和的方式以及您检查的方式不知何故对称