这个校验和算法可以改进吗?

时间:2009-06-25 17:23:52

标签: c++ algorithm checksum

我们有一个非常古老的,不受支持的程序,它可以跨SMB共享文件。它有一个校验和算法,用于确定在复制之前文件内容是否已更改。该算法似乎很容易上当 - 我们刚刚找到了一个例子,其中两个文件相同,只有一个'1'变为'2',返回相同的校验和。这是算法:

unsigned long GetFileCheckSum(CString PathFilename)
{
        FILE* File;
        unsigned long CheckSum = 0;
        unsigned long Data = 0;
        unsigned long Count = 0;

        if ((File = fopen(PathFilename, "rb")) != NULL)
        {
                while (fread(&Data, 1, sizeof(unsigned long), File) != FALSE)
                {
                        CheckSum ^= Data + ++Count;
                        Data = 0;
                }
                fclose(File);
        }
        return CheckSum;
}

我不是一个程序员(我是一个系统管理员),但我知道基于XOR的校验和将非常粗糙。对于具有不同内容的两个相同大小的文件,此算法返回相同校验和的可能性是多少? (我不期待一个确切的答案,“远程”或“很可能”没问题。)

如果没有巨大的性能影响,怎么能改进呢?

最后,fread()发生了什么?我快速扫描了文档,但我无法弄清楚。是Data依次设置为文件的每个字节? 编辑:好的,所以它正在将文件读入unsigned long(让我们假设这里有一个32位操作系统)块。每个块包含什么?如果文件的内容是abcd,第一遍的Data值是多少?是(在Perl中):

(ord('a') << 24) & (ord('b') << 16) & (ord('c') << 8) & ord('d')

8 个答案:

答案 0 :(得分:6)

MD5通常用于验证传输文件的完整性。源代码在c ++中很容易获得。它被广泛认为是一种快速准确的算法。

另见Robust and fast checksum algorithm?

答案 1 :(得分:4)

我建议你看看Fletcher's checksum,特别是fletcher-32,它应该相当快,并检测当前XOR链不会发现的各种事情。

答案 2 :(得分:3)

您可以使用类似这样的公式轻松改进算法:

Checksum = (Checksum * a + Data * b) + c;

如果a,b和c是大质数,这应该会返回良好的结果。在此之后,旋转(不移位!)校验和的位将进一步改善它。

使用素数,这与Linear congruential generators使用的算法类似 - 它保证了长时间和良好的分布。

答案 3 :(得分:0)

我认为您的算法不会处理不是4字节大小的精确倍数的文件。 fread的返回值不是布尔值,而是实际读取的字节数,在EOF的情况下或发生错误时,它将与4不同。您没有检查过,但只是假设如果它没有返回0,那么'data'中有4个有效字节来计算你的哈希值。

如果你真的想使用哈希,我会推荐几件事。首先,使用像MD5这样的简单加密哈希,而不是CRC32。 CRC32用于检查数据有效性,但是对于跨越文件系统并确保没有冲突,由于其他地方的评论中提到的生日悖论,它不是一个很好的工具。其次,不要自己写这个功能。查找现有实施。最后,考虑使用rsync来复制文件而不是滚动自己的解决方案。

答案 4 :(得分:0)

fread位一次读取一个块中的文件。每个块都是一个长的大小(在c中,这不是一个明确定义的大小,但你可以假设32或64位)。根据缓冲的方式,这可能不会很糟糕。 OTOH,将更大的块读入数组并在其上循环可能要快得多。

答案 5 :(得分:0)

即使是“昂贵的”加密哈希函数通常也需要多次迭代才能占用大量时间。虽然不再建议用于加密目的,但用户会故意尝试创建冲突,但SHA1和MD5等功能广泛可用,并且适用于此目的。

如果需要较小的哈希值,CRC就没问题,但不是很好。 n 位CRC将无法检测到比 n 位更长的一小部分更改。例如,假设文件中的单个金额已更改,从$ 12,345变为$ 34,567。 32位CRC可能会错过这种变化。

截断更长的加密哈希的结果将比CRC更可靠地检测更改。

答案 6 :(得分:0)

{
   CheckSum ^= Data + ++Count;
   Data = 0;
}

我不认为“++ Count”做了很多工作。代码等同于

{
  CheckSum ^= Data;
}

对一个字节序列进行异或是不够的。特别是文本文件。

我建议使用hash function

答案 7 :(得分:0)

SHA-1和(最近的SHA-2)提供了出色的散列函数,我相信由于更好的散列特性,可以慢慢取代MD5。所有这些(md2,sha等等)都有高效的实现,并返回一个长度为几个字符的缓冲区(尽管总是固定长度)。比将哈希减少为整数更可靠。如果我有我的druthers,我会使用SHA-2。对于实现SHA校验和的库,请关注this link

如果你不想在这些库中编译,linux(可能还有cygwin)有以下可执行文件:md5sum,sha1sum,sha224sum,sha256sum,sha384sum,sha512sum;你可以提供你的文件,他们将校验和打印为十六进制字符串。 您可以使用popen来执行这些程序 - 使用类似这样的程序:

const int maxBuf=1024;
char buf[maxBuf];
FILE* f = popen( "sha224sum myfile", "w" );
int bytesRead = f.read( buf, maxBuf );
fclose( f );

显然这会慢很多,但是会有一个有用的第一遍。 如果速度是一个问题,假设像这样的文件哈希操作和I / O绑定(内存和磁盘访问将是你的瓶颈),我希望所有这些算法运行速度与生成unsigned int的算法一样快。 Perl和Python还提供了MD5 SHA1和SHA2的实现,并且可能会像在C / C ++中一样快。