近日要用到文件校验算法,查看了一下相关资料,得到以下理论与实践经验。
一、理论部分:
1、预备知识
1.1什么是数据校验
通俗的说,就是为保证数据的完整性,用一种指定的算法对原始数据计算出的一个校验值。接收方用同样的算法计算一次校验值,如果和随数据提供的校验值一样,就说明数据是完整的。
1.2最简单的检验
实现方法:最简单的校验就是把原始数据和待比较数据直接进行比较,看是否完全一样这种方法是最安全最准确的。同时也是效率最低的。
适用范围:简单的数据量极小的通讯。
应用例子:龙珠cpu在线调试工具bbug.exe。它和龙珠cpu间通讯时,bbug发送一个字节cpu返回收到的字节,bbug确认是刚才发送字节后才继续发送下一个字节的。
1.3奇偶校验Parity Check
实现方法:在数据存储和传输中,字节中额外增加一个比特位,用来检验错误。校验位可以通过数据位异或计算出来。
应用例子:单片机串口通讯有一模式就是8位数据通讯,另加第9位用于放校验值。
1.4 bcc异或校验法(block check character)
实现方法:很多基于串口的通讯都用这种既简单又相当准确的方法。它就是把所有数据都和一个指定的初始值(通常是0)异或一次,最后的结果就是校验值,通常
把她附在通讯数据的最后一起发送出去。接收方收到数据后自己也计算一次异或和校验值,如果和收到的校验值一致就说明收到的数据是完整的。
校验值计算的代码类似于:
unsigned uCRC=0;//校验初始值
for(int i=0;i<DataLenth;i++) uCRC^=Data[i];
适用范围:适用于大多数要求不高的数据通讯。
应用例子:ic卡接口通讯、很多单片机系统的串口通讯都使用。
1.5 crc循环冗余校验(Cyclic Redundancy Check)
实现方法:这是利用除法及余数的原理来进行错误检测的.将接收到的码组进行除法运算
,如果除尽,则说明传输无误;如果未除尽,则表明传输出现差错。crc校验
具还有自动纠错能力。
crc检验主要有计算法和查表法两种方法,网上很多实现代码。
适用范围:CRC-12码通常用来传送6-bit字符串;CRC-16及CRC-CCITT码则用是来传送
8-bit字符。CRC-32:硬盘数据,网络传输等
应用例子:rar,以太网卡芯片、MPEG解码芯片中
1.6 md5校验和数字签名
实现方法:主要有md5和des算法。
适用范围:数据比较大或要求比较高的场合。如md5用于大量数据、文件校验,des用于保密数据的校验(数字签名)等等。
应用例子:文件校验、银行系统的交易数据
2、具体的实现理论
2.1 算法概述
MD5算法是MD4算法的改进算法。Ron Rivest 于1990年提出MD4单向散列函数,MD表示消息摘要(Message Digest),对输入消息,算法产生128位散列值。该算法首次公布之后,Bert den Boer和Antoon Bosselaers 对算法三轮中的后两轮进行了成功的密码分析。在一个不相关的分析结果中,Ralph MerKle成功地攻击了前两轮。尽管这些攻击都没有扩展到整个算法,但Rivest还是改进了其算法,结果就是MD5算法。
MD5算法是MD4的改进算法,它比MD4更复杂,但设计思想相似,输入的消息可任意长,输出结果也仍为128位,特别适用于高速软件实现,是基于32-位操作数的一些简单的位操作。
2.2 算法步骤
l 将输入消息按512-位分组,最后要填充成为512位的整数倍,且最后一组的后64位用来填充消息长度(填充前)。填充方法为附一个1在消息后,后接所要求的多个0。这样可以确保不同消息在填充后不相同。
l 由于留出64位用来表示消息长度,那么消息的长度最多可达264字节,相当于4G×4G字节,文件的长度是不可能达到这么大,因此通常都是只采用64位中的低32位来表示消息长度,高32位填充0。
l 初始化MD变量。由于每轮输出128位,这128位可用下面四个32位字A,B,C,D来表示。其初始值设为:
A=0x01234567
B=0x89ABCDEF
C=0xFEDCBA98
D=0x76543210
l 开始进入算法主循环,循环的次数是消息中512位消息分组的数目。先将上面A、B、C、D四个变量分别复制到另外四个变量a、b、c、d中去。主循环有四轮,每轮很相似。每轮进行16次操作,每次操作对a、b、c、d四个变量中的三个作一次非线性函数运算,然后将所得结果加上第四个变量,消息的一个子分组和一个常数。再将所得结果向右环移一个不定的数,并加上a,b,c或d中之一。最后用该结果取代a,b,c或d中之一。
以下是每次操作中用到的四个非线性函数(每轮一个)。
F(X,Y,Z)=(X∧Y)∨(( X)∧Z)
G(X,Y,Z)=(X∧Z)∨(Y∧( Z))
H(X,Y,Z)=X⊕Y⊕Z
I(X,Y,Z)=Y⊕(X∨( Z))
其中,⊕是异或,∧是与,∨是或, 是反符号。
这些函数是这样设计的:如果X、Y和Z的对应位是独立和均匀的,那么结果的每一位也应是独立和均匀的。函数F是按逐位方式操作:如果X,那么Y,否则Z。函数H是逐位奇偶操作符。
设Mj表示消息的第j个子分组(从0到15),<<<s表示循环左移s,则四种操作为:
FF(a,b,c,d,Mj,s,ti)表示a = b+((a+F(b,c,d)+ Mj + ti)<<<s)
GG(a,b,c,d,Mj,s,ti)表示a = b+((a+G(b,c,d)+ Mj + ti)<<<s)
HH(a,b,c,d,Mj,s,ti)表示a = b+((a+H(b,c,d)+ Mj + ti)<<<s)
II(a,b,c,d,Mj,s,ti)表示a = b+((a+I(b,c,d)+ Mj + ti)<<<s)
四轮(64步)结果略。
注:常数ti的选择:
第i步中,ti是232 ×abs (sin(i))的整数部分,i的单位是弧度。
所有这些完成之后,将A,B,C,D分别加上a,b,c,d。然后用下一分组数据继续运行算法,最后的输出是A,B,C和D的级联。
l 最后得到的A,B,C,D就是输出结果,A是低位,D为高位,DCBA组成128位输出结果。
2.3 MD5的安全性
Ron Rivest概述了MD5安全性[8]:
l 与MD4相比,增加了第四轮。
l 每一步均有唯一的加法常数。
l 为减弱第二轮中函数G的对称性从((X∧Y) ∨(X∧Z) ∨(Y∧Z))变为((X∧Z) ∨(Y∧( Z)))。
l 每一步加上了上一步的结果,引起更快的雪崩效应。
l 改变了第二轮和第三轮中访问消息子分组的次序,使其形式更不相似。
l 近似优化了每一轮中的循环左移位移量以实现更快的雪崩效应。各轮的位移量互不相同。
从安全角度讲,MD5的输出为128位,若采用纯强力攻击寻找一个消息具有给定Hash值的计算困难性为2128,用每秒可试验1 000 000 000个消息的计算机需时1.07×1022年。若采用生日攻击法,寻找有相同Hash值的两个消息需要试验264个消息,用每秒可试验1 000 000 000个消息的计算机需时585年。
二、实现方法
由于此处的文件校验用到要求比较高的场合,故采用了方法6,md5校验算法,从CodeGuru下载了一个md5校验算法的实现模块,加入自己要校验的文件名,实现完成。下面具体描述一下实现过程:
1、创建一个简单的对话框程序;
2、设置CString类型的变量m_filename和m_strFileChecksum以存放要校验的文件名和校验和;
3、在对话框类中创建ChecksumSelectedFile()函数,调用md5校验和类(附录中有其实现文件)中的GetMD5计算文件校验和。
4、使用定时器定时巡检该文件的校验和,一旦发现校验和发生变化,立刻出现提示。
三、附录(md5算法实现的源码)
以下代码实现均来自www.codeguru.com。
一、理论部分:
1、预备知识
1.1什么是数据校验
通俗的说,就是为保证数据的完整性,用一种指定的算法对原始数据计算出的一个校验值。接收方用同样的算法计算一次校验值,如果和随数据提供的校验值一样,就说明数据是完整的。
1.2最简单的检验
实现方法:最简单的校验就是把原始数据和待比较数据直接进行比较,看是否完全一样这种方法是最安全最准确的。同时也是效率最低的。
适用范围:简单的数据量极小的通讯。
应用例子:龙珠cpu在线调试工具bbug.exe。它和龙珠cpu间通讯时,bbug发送一个字节cpu返回收到的字节,bbug确认是刚才发送字节后才继续发送下一个字节的。
1.3奇偶校验Parity Check
实现方法:在数据存储和传输中,字节中额外增加一个比特位,用来检验错误。校验位可以通过数据位异或计算出来。
应用例子:单片机串口通讯有一模式就是8位数据通讯,另加第9位用于放校验值。
1.4 bcc异或校验法(block check character)
实现方法:很多基于串口的通讯都用这种既简单又相当准确的方法。它就是把所有数据都和一个指定的初始值(通常是0)异或一次,最后的结果就是校验值,通常
把她附在通讯数据的最后一起发送出去。接收方收到数据后自己也计算一次异或和校验值,如果和收到的校验值一致就说明收到的数据是完整的。
校验值计算的代码类似于:
unsigned uCRC=0;//校验初始值
for(int i=0;i<DataLenth;i++) uCRC^=Data[i];
适用范围:适用于大多数要求不高的数据通讯。
应用例子:ic卡接口通讯、很多单片机系统的串口通讯都使用。
1.5 crc循环冗余校验(Cyclic Redundancy Check)
实现方法:这是利用除法及余数的原理来进行错误检测的.将接收到的码组进行除法运算
,如果除尽,则说明传输无误;如果未除尽,则表明传输出现差错。crc校验
具还有自动纠错能力。
crc检验主要有计算法和查表法两种方法,网上很多实现代码。
适用范围:CRC-12码通常用来传送6-bit字符串;CRC-16及CRC-CCITT码则用是来传送
8-bit字符。CRC-32:硬盘数据,网络传输等
应用例子:rar,以太网卡芯片、MPEG解码芯片中
1.6 md5校验和数字签名
实现方法:主要有md5和des算法。
适用范围:数据比较大或要求比较高的场合。如md5用于大量数据、文件校验,des用于保密数据的校验(数字签名)等等。
应用例子:文件校验、银行系统的交易数据
2、具体的实现理论
2.1 算法概述
MD5算法是MD4算法的改进算法。Ron Rivest 于1990年提出MD4单向散列函数,MD表示消息摘要(Message Digest),对输入消息,算法产生128位散列值。该算法首次公布之后,Bert den Boer和Antoon Bosselaers 对算法三轮中的后两轮进行了成功的密码分析。在一个不相关的分析结果中,Ralph MerKle成功地攻击了前两轮。尽管这些攻击都没有扩展到整个算法,但Rivest还是改进了其算法,结果就是MD5算法。
MD5算法是MD4的改进算法,它比MD4更复杂,但设计思想相似,输入的消息可任意长,输出结果也仍为128位,特别适用于高速软件实现,是基于32-位操作数的一些简单的位操作。
2.2 算法步骤
l 将输入消息按512-位分组,最后要填充成为512位的整数倍,且最后一组的后64位用来填充消息长度(填充前)。填充方法为附一个1在消息后,后接所要求的多个0。这样可以确保不同消息在填充后不相同。
l 由于留出64位用来表示消息长度,那么消息的长度最多可达264字节,相当于4G×4G字节,文件的长度是不可能达到这么大,因此通常都是只采用64位中的低32位来表示消息长度,高32位填充0。
l 初始化MD变量。由于每轮输出128位,这128位可用下面四个32位字A,B,C,D来表示。其初始值设为:
A=0x01234567
B=0x89ABCDEF
C=0xFEDCBA98
D=0x76543210
l 开始进入算法主循环,循环的次数是消息中512位消息分组的数目。先将上面A、B、C、D四个变量分别复制到另外四个变量a、b、c、d中去。主循环有四轮,每轮很相似。每轮进行16次操作,每次操作对a、b、c、d四个变量中的三个作一次非线性函数运算,然后将所得结果加上第四个变量,消息的一个子分组和一个常数。再将所得结果向右环移一个不定的数,并加上a,b,c或d中之一。最后用该结果取代a,b,c或d中之一。
以下是每次操作中用到的四个非线性函数(每轮一个)。
F(X,Y,Z)=(X∧Y)∨(( X)∧Z)
G(X,Y,Z)=(X∧Z)∨(Y∧( Z))
H(X,Y,Z)=X⊕Y⊕Z
I(X,Y,Z)=Y⊕(X∨( Z))
其中,⊕是异或,∧是与,∨是或, 是反符号。
这些函数是这样设计的:如果X、Y和Z的对应位是独立和均匀的,那么结果的每一位也应是独立和均匀的。函数F是按逐位方式操作:如果X,那么Y,否则Z。函数H是逐位奇偶操作符。
设Mj表示消息的第j个子分组(从0到15),<<<s表示循环左移s,则四种操作为:
FF(a,b,c,d,Mj,s,ti)表示a = b+((a+F(b,c,d)+ Mj + ti)<<<s)
GG(a,b,c,d,Mj,s,ti)表示a = b+((a+G(b,c,d)+ Mj + ti)<<<s)
HH(a,b,c,d,Mj,s,ti)表示a = b+((a+H(b,c,d)+ Mj + ti)<<<s)
II(a,b,c,d,Mj,s,ti)表示a = b+((a+I(b,c,d)+ Mj + ti)<<<s)
四轮(64步)结果略。
注:常数ti的选择:
第i步中,ti是232 ×abs (sin(i))的整数部分,i的单位是弧度。
所有这些完成之后,将A,B,C,D分别加上a,b,c,d。然后用下一分组数据继续运行算法,最后的输出是A,B,C和D的级联。
l 最后得到的A,B,C,D就是输出结果,A是低位,D为高位,DCBA组成128位输出结果。
2.3 MD5的安全性
Ron Rivest概述了MD5安全性[8]:
l 与MD4相比,增加了第四轮。
l 每一步均有唯一的加法常数。
l 为减弱第二轮中函数G的对称性从((X∧Y) ∨(X∧Z) ∨(Y∧Z))变为((X∧Z) ∨(Y∧( Z)))。
l 每一步加上了上一步的结果,引起更快的雪崩效应。
l 改变了第二轮和第三轮中访问消息子分组的次序,使其形式更不相似。
l 近似优化了每一轮中的循环左移位移量以实现更快的雪崩效应。各轮的位移量互不相同。
从安全角度讲,MD5的输出为128位,若采用纯强力攻击寻找一个消息具有给定Hash值的计算困难性为2128,用每秒可试验1 000 000 000个消息的计算机需时1.07×1022年。若采用生日攻击法,寻找有相同Hash值的两个消息需要试验264个消息,用每秒可试验1 000 000 000个消息的计算机需时585年。
二、实现方法
由于此处的文件校验用到要求比较高的场合,故采用了方法6,md5校验算法,从CodeGuru下载了一个md5校验算法的实现模块,加入自己要校验的文件名,实现完成。下面具体描述一下实现过程:
1、创建一个简单的对话框程序;
2、设置CString类型的变量m_filename和m_strFileChecksum以存放要校验的文件名和校验和;
3、在对话框类中创建ChecksumSelectedFile()函数,调用md5校验和类(附录中有其实现文件)中的GetMD5计算文件校验和。
4、使用定时器定时巡检该文件的校验和,一旦发现校验和发生变化,立刻出现提示。
三、附录(md5算法实现的源码)
以下代码实现均来自www.codeguru.com。
- //1、MD5ChecksumDefines.h(定义相关常量的头文件)
- //Magic initialization constants
- #define MD5_INIT_STATE_0 0x67452301
- #define MD5_INIT_STATE_1 0xefcdab89
- #define MD5_INIT_STATE_2 0x98badcfe
- #define MD5_INIT_STATE_3 0x10325476
- //Constants for Transform routine.
- #define MD5_S11 7
- #define MD5_S12 12
- #define MD5_S13 17
- #define MD5_S14 22
- #define MD5_S21 5
- #define MD5_S22 9
- #define MD5_S23 14
- #define MD5_S24 20
- #define MD5_S31 4
- #define MD5_S32 11
- #define MD5_S33 16
- #define MD5_S34 23
- #define MD5_S41 6
- #define MD5_S42 10
- #define MD5_S43 15
- #define MD5_S44 21
- //Transformation Constants - Round 1
- #define MD5_T01 0xd76aa478 //Transformation Constant 1
- #define MD5_T02 0xe8c7b756 //Transformation Constant 2
- #define MD5_T03 0x242070db //Transformation Constant 3
- #define MD5_T04 0xc1bdceee //Transformation Constant 4
- #define MD5_T05 0xf57c0faf //Transformation Constant 5
- #define MD5_T06 0x4787c62a //Transformation Constant 6
- #define MD5_T07 0xa8304613 //Transformation Constant 7
- #define MD5_T08 0xfd469501 //Transformation Constant 8
- #define MD5_T09 0x698098d8 //Transformation Constant 9
- #define MD5_T10 0x8b44f7af //Transformation Constant 10
- #define MD5_T11 0xffff5bb1 //Transformation Constant 11
- #define MD5_T12 0x895cd7be //Transformation Constant 12
- #define MD5_T13 0x6b901122 //Transformation Constant 13
- #define MD5_T14 0xfd987193 //Transformation Constant 14
- #define MD5_T15 0xa679438e //Transformation Constant 15
- #define MD5_T16 0x49b40821 //Transformation Constant 16
- //Transformation Constants - Round 2
- #define MD5_T17 0xf61e2562 //Transformation Constant 17
- #define MD5_T18 0xc040b340 //Transformation Constant 18
- #define MD5_T19 0x265e5a51 //Transformation Constant 19
- #define MD5_T20 0xe9b6c7aa //Transformation Constant 20
- #define MD5_T21 0xd62f105d //Transformation Constant 21
- #define MD5_T22 0x02441453 //Transformation Constant 22
- #define MD5_T23 0xd8a1e681 //Transformation Constant 23
- #define MD5_T24 0xe7d3fbc8 //Transformation Constant 24
- #define MD5_T25 0x21e1cde6 //Transformation Constant 25
- #define MD5_T26 0xc33707d6 //Transformation Constant 26
- #define MD5_T27 0xf4d50d87 //Transformation Constant 27
- #define MD5_T28 0x455a14ed //Transformation Constant 28
- #define MD5_T29 0xa9e3e905 //Transformation Constant 29
- #define MD5_T30 0xfcefa3f8 //Transformation Constant 30
- #define MD5_T31 0x676f02d9 //Transformation Constant 31
- #define MD5_T32 0x8d2a4c8a //Transformation Constant 32
- //Transformation Constants - Round 3
- #define MD5_T33 0xfffa3942 //Transformation Constant 33
- #define MD5_T34 0x8771f681 //Transformation Constant 34
- #define MD5_T35 0x6d9d6122 //Transformation Constant 35
- #define MD5_T36 0xfde5380c //Transformation Constant 36
- #define MD5_T37 0xa4beea44 //Transformation Constant 37
- #define MD5_T38 0x4bdecfa9 //Transformation Constant 38
- #define MD5_T39 0xf6bb4b60 //Transformation Constant 39
- #define MD5_T40 0xbebfbc70 //Transformation Constant 40
- #define MD5_T41 0x289b7ec6 //Transformation Constant 41
- #define MD5_T42 0xeaa127fa //Transformation Constant 42
- #define MD5_T43 0xd4ef3085 //Transformation Constant 43
- #define MD5_T44 0x04881d05 //Transformation Constant 44
- #define MD5_T45 0xd9d4d039 //Transformation Constant 45
- #define MD5_T46 0xe6db99e5 //Transformation Constant 46
- #define MD5_T47 0x1fa27cf8 //Transformation Constant 47
- #define MD5_T48 0xc4ac5665 //Transformation Constant 48
- //Transformation Constants - Round 4
- #define MD5_T49 0xf4292244 //Transformation Constant 49
- #define MD5_T50 0x432aff97 //Transformation Constant 50
- #define MD5_T51 0xab9423a7 //Transformation Constant 51
- #define MD5_T52 0xfc93a039 //Transformation Constant 52
- #define MD5_T53 0x655b59c3 //Transformation Constant 53
- #define MD5_T54 0x8f0ccc92 //Transformation Constant 54
- #define MD5_T55 0xffeff47d //Transformation Constant 55
- #define MD5_T56 0x85845dd1 //Transformation Constant 56
- #define MD5_T57 0x6fa87e4f //Transformation Constant 57
- #define MD5_T58 0xfe2ce6e0 //Transformation Constant 58
- #define MD5_T59 0xa3014314 //Transformation Constant 59
- #define MD5_T60 0x4e0811a1 //Transformation Constant 60
- #define MD5_T61 0xf7537e82 //Transformation Constant 61
- #define MD5_T62 0xbd3af235 //Transformation Constant 62
- #define MD5_T63 0x2ad7d2bb //Transformation Constant 63
- #define MD5_T64 0xeb86d391 //Transformation Constant 64
- //Null data (except for first BYTE) used to finalise the checksum calculation
- static unsigned char PADDING[64] = {
- 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- };
- //2、CountChecksum.h(md5校验和类的头文件)
- class CMD5Checksum
- {
- public:
- //interface functions for the RSA MD5 calculation
- static CString GetMD5(BYTE* pBuf, UINT nLength);
- static CString GetMD5(CFile& File);
- static CString GetMD5(const CString& strFilePath);
- protected:
- //constructor/destructor
- CMD5Checksum();
- virtual ~CMD5Checksum() {};
- //RSA MD5 implementation
- void Transform(BYTE Block[64]);
- void Update(BYTE* Input, ULONG nInputLen);
- CString Final();
- inline DWORD RotateLeft(DWORD x, int n);
- inline void FF( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T);
- inline void GG( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T);
- inline void HH( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T);
- inline void II( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T);
- //utility functions
- void DWordToByte(BYTE* Output, DWORD* Input, UINT nLength);
- void ByteToDWord(DWORD* Output, BYTE* Input, UINT nLength);
- private:
- BYTE m_lpszBuffer[64]; //input buffer
- ULONG m_nCount[2]; //number of bits, modulo 2^64 (lsb first)
- ULONG m_lMD5[4]; //MD5 checksum
- };
- #endif // !defined(AFX_MD5CHECKSUM_H__2BC7928E_4C15_11D3_B2EE_A4A60E20D2C3__INCLUDED_)
- //3、CountChecksum.cpp (md5校验和类的实现文件)
- /*****************************************************************************************
- FUNCTION: CMD5Checksum::GetMD5
- DETAILS: static, public
- DESCRIPTION: Gets the MD5 checksum for a specified file
- RETURNS: CString : the hexadecimal MD5 checksum for the specified file
- ARGUMENTS: CString& strFilePath : the full pathname of the specified file
- NOTES: Provides an interface to the CMD5Checksum class. 'strFilePath' name should
- hold the full pathname of the file, eg C:/My Documents/Arcticle.txt.
- NB. If any problems occur with opening or reading this file, a CFileException
- will be thrown; callers of this function should be ready to catch this
- exception.
- *****************************************************************************************/
- CString CMD5Checksum::GetMD5(const CString& strFilePath)
- {
- //open the file as a binary file in readonly mode, denying write access
- CFile File(strFilePath, CFile::shareDenyNone);
- //the file has been successfully opened, so now get and return its checksum
- return GetMD5(File);
- }
- /*****************************************************************************************
- FUNCTION: CMD5Checksum::GetMD5
- DETAILS: static, public
- DESCRIPTION: Gets the MD5 checksum for a specified file
- RETURNS: CString : the hexadecimal MD5 checksum for the specified file
- ARGUMENTS: CFile& File : the specified file
- NOTES: Provides an interface to the CMD5Checksum class. 'File' should be open in
- binary readonly mode before calling this function.
- NB. Callers of this function should be ready to catch any CFileException
- thrown by the CFile functions
- *****************************************************************************************/
- CString CMD5Checksum::GetMD5(CFile& File)
- {
- try
- {
- CMD5Checksum MD5Checksum; //checksum object
- int nLength = 0; //number of bytes read from the file
- const int nBufferSize = 1024; //checksum the file in blocks of 1024 bytes
- BYTE Buffer[nBufferSize]; //buffer for data read from the file
- //checksum the file in blocks of 1024 bytes
- while ((nLength = File.Read( Buffer, nBufferSize )) > 0 )
- {
- MD5Checksum.Update( Buffer, nLength );
- }
- //finalise the checksum and return it
- return MD5Checksum.Final();
- }
- //report any file exceptions in debug mode only
- catch (CFileException* e )
- {
- TRACE0("CMD5Checksum::GetMD5: CFileException caught");
- throw e;
- }
- }
- /*****************************************************************************************
- FUNCTION: CMD5Checksum::GetMD5
- DETAILS: static, public
- DESCRIPTION: Gets the MD5 checksum for data in a BYTE array
- RETURNS: CString : the hexadecimal MD5 checksum for the specified data
- ARGUMENTS: BYTE* pBuf : pointer to the BYTE array
- UINT nLength : number of BYTEs of data to be checksumed
- NOTES: Provides an interface to the CMD5Checksum class. Any data that can
- be cast to a BYTE array of known length can be checksummed by this
- function. Typically, CString and char arrays will be checksumed,
- although this function can be used to check the integrity of any BYTE array.
- A buffer of zero length can be checksummed; all buffers of zero length
- will return the same checksum.
- *****************************************************************************************/
- CString CMD5Checksum::GetMD5(BYTE* pBuf, UINT nLength)
- {
- //entry invariants
- AfxIsValidAddress(pBuf,nLength,FALSE);
- //calculate and return the checksum
- CMD5Checksum MD5Checksum;
- MD5Checksum.Update( pBuf, nLength );
- return MD5Checksum.Final();
- }
- /*****************************************************************************************
- FUNCTION: CMD5Checksum::RotateLeft
- DETAILS: private
- DESCRIPTION: Rotates the bits in a 32 bit DWORD left by a specified amount
- RETURNS: The rotated DWORD
- ARGUMENTS: DWORD x : the value to be rotated
- int n : the number of bits to rotate by
- *****************************************************************************************/
- DWORD CMD5Checksum::RotateLeft(DWORD x, int n)
- {
- //check that DWORD is 4 bytes long - true in Visual C++ 6 and 32 bit Windows
- ASSERT( sizeof(x) == 4 );
- //rotate and return x
- return (x << n) | (x >> (32-n));
- }
- /*****************************************************************************************
- FUNCTION: CMD5Checksum::FF
- DETAILS: protected
- DESCRIPTION: Implementation of basic MD5 transformation algorithm
- RETURNS: none
- ARGUMENTS: DWORD &A, B, C, D : Current (partial) checksum
- DWORD X : Input data
- DWORD S : MD5_SXX Transformation constant
- DWORD T : MD5_TXX Transformation constant
- NOTES: None
- *****************************************************************************************/
- void CMD5Checksum::FF( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T)
- {
- DWORD F = (B & C) | (~B & D);
- A += F + X + T;
- A = RotateLeft(A, S);
- A += B;
- }
- /*****************************************************************************************
- FUNCTION: CMD5Checksum::GG
- DETAILS: protected
- DESCRIPTION: Implementation of basic MD5 transformation algorithm
- RETURNS: none
- ARGUMENTS: DWORD &A, B, C, D : Current (partial) checksum
- DWORD X : Input data
- DWORD S : MD5_SXX Transformation constant
- DWORD T : MD5_TXX Transformation constant
- NOTES: None
- *****************************************************************************************/
- void CMD5Checksum::GG( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T)
- {
- DWORD G = (B & D) | (C & ~D);
- A += G + X + T;
- A = RotateLeft(A, S);
- A += B;
- }
- /*****************************************************************************************
- FUNCTION: CMD5Checksum::HH
- DETAILS: protected
- DESCRIPTION: Implementation of basic MD5 transformation algorithm
- RETURNS: none
- ARGUMENTS: DWORD &A, B, C, D : Current (partial) checksum
- DWORD X : Input data
- DWORD S : MD5_SXX Transformation constant
- DWORD T : MD5_TXX Transformation constant
- NOTES: None
- *****************************************************************************************/
- void CMD5Checksum::HH( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T)
- {
- DWORD H = (B ^ C ^ D);
- A += H + X + T;
- A = RotateLeft(A, S);
- A += B;
- }
- /*****************************************************************************************
- FUNCTION: CMD5Checksum::II
- DETAILS: protected
- DESCRIPTION: Implementation of basic MD5 transformation algorithm
- RETURNS: none
- ARGUMENTS: DWORD &A, B, C, D : Current (partial) checksum
- DWORD X : Input data
- DWORD S : MD5_SXX Transformation constant
- DWORD T : MD5_TXX Transformation constant
- NOTES: None
- *****************************************************************************************/
- void CMD5Checksum::II( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T)
- {
- DWORD I = (C ^ (B | ~D));
- A += I + X + T;
- A = RotateLeft(A, S);
- A += B;
- }
- /*****************************************************************************************
- FUNCTION: CMD5Checksum::ByteToDWord
- DETAILS: private
- DESCRIPTION: Transfers the data in an 8 bit array to a 32 bit array
- RETURNS: void
- ARGUMENTS: DWORD* Output : the 32 bit (unsigned long) destination array
- BYTE* Input : the 8 bit (unsigned char) source array
- UINT nLength : the number of 8 bit data items in the source array
- NOTES: Four BYTES from the input array are transferred to each DWORD entry
- of the output array. The first BYTE is transferred to the bits (0-7)
- of the output DWORD, the second BYTE to bits 8-15 etc.
- The algorithm assumes that the input array is a multiple of 4 bytes long
- so that there is a perfect fit into the array of 32 bit words.
- *****************************************************************************************/
- void CMD5Checksum::ByteToDWord(DWORD* Output, BYTE* Input, UINT nLength)
- {
- //entry invariants
- ASSERT( nLength % 4 == 0 );
- ASSERT( AfxIsValidAddress(Output, nLength/4, TRUE) );
- ASSERT( AfxIsValidAddress(Input, nLength, FALSE) );
- //initialisations
- UINT i=0; //index to Output array
- UINT j=0; //index to Input array
- //transfer the data by shifting and copying
- for ( ; j < nLength; i++, j += 4)
- {
- Output[i] = (ULONG)Input[j] |
- (ULONG)Input[j+1] << 8 |
- (ULONG)Input[j+2] << 16 |
- (ULONG)Input[j+3] << 24;
- }
- }
- /*****************************************************************************************
- FUNCTION: CMD5Checksum::Transform
- DETAILS: protected
- DESCRIPTION: MD5 basic transformation algorithm; transforms 'm_lMD5'
- RETURNS: void
- ARGUMENTS: BYTE Block[64]
- NOTES: An MD5 checksum is calculated by four rounds of 'Transformation'.
- The MD5 checksum currently held in m_lMD5 is merged by the
- transformation process with data passed in 'Block'.
- *****************************************************************************************/
- void CMD5Checksum::Transform(BYTE Block[64])
- {
- //initialise local data with current checksum
- ULONG a = m_lMD5[0];
- ULONG b = m_lMD5[1];
- ULONG c = m_lMD5[2];
- ULONG d = m_lMD5[3];
- //copy BYTES from input 'Block' to an array of ULONGS 'X'
- ULONG X[16];
- ByteToDWord( X, Block, 64 );
- //Perform Round 1 of the transformation
- FF (a, b, c, d, X[ 0], MD5_S11, MD5_T01);
- FF (d, a, b, c, X[ 1], MD5_S12, MD5_T02);
- FF (c, d, a, b, X[ 2], MD5_S13, MD5_T03);
- FF (b, c, d, a, X[ 3], MD5_S14, MD5_T04);
- FF (a, b, c, d, X[ 4], MD5_S11, MD5_T05);
- FF (d, a, b, c, X[ 5], MD5_S12, MD5_T06);
- FF (c, d, a, b, X[ 6], MD5_S13, MD5_T07);
- FF (b, c, d, a, X[ 7], MD5_S14, MD5_T08);
- FF (a, b, c, d, X[ 8], MD5_S11, MD5_T09);
- FF (d, a, b, c, X[ 9], MD5_S12, MD5_T10);
- FF (c, d, a, b, X[10], MD5_S13, MD5_T11);
- FF (b, c, d, a, X[11], MD5_S14, MD5_T12);
- FF (a, b, c, d, X[12], MD5_S11, MD5_T13);
- FF (d, a, b, c, X[13], MD5_S12, MD5_T14);
- FF (c, d, a, b, X[14], MD5_S13, MD5_T15);
- FF (b, c, d, a, X[15], MD5_S14, MD5_T16);
- //Perform Round 2 of the transformation
- GG (a, b, c, d, X[ 1], MD5_S21, MD5_T17);
- GG (d, a, b, c, X[ 6], MD5_S22, MD5_T18);
- GG (c, d, a, b, X[11], MD5_S23, MD5_T19);
- GG (b, c, d, a, X[ 0], MD5_S24, MD5_T20);
- GG (a, b, c, d, X[ 5], MD5_S21, MD5_T21);
- GG (d, a, b, c, X[10], MD5_S22, MD5_T22);
- GG (c, d, a, b, X[15], MD5_S23, MD5_T23);
- GG (b, c, d, a, X[ 4], MD5_S24, MD5_T24);
- GG (a, b, c, d, X[ 9], MD5_S21, MD5_T25);
- GG (d, a, b, c, X[14], MD5_S22, MD5_T26);
- GG (c, d, a, b, X[ 3], MD5_S23, MD5_T27);
- GG (b, c, d, a, X[ 8], MD5_S24, MD5_T28);
- GG (a, b, c, d, X[13], MD5_S21, MD5_T29);
- GG (d, a, b, c, X[ 2], MD5_S22, MD5_T30);
- GG (c, d, a, b, X[ 7], MD5_S23, MD5_T31);
- GG (b, c, d, a, X[12], MD5_S24, MD5_T32);
- //Perform Round 3 of the transformation
- HH (a, b, c, d, X[ 5], MD5_S31, MD5_T33);
- HH (d, a, b, c, X[ 8], MD5_S32, MD5_T34);
- HH (c, d, a, b, X[11], MD5_S33, MD5_T35);
- HH (b, c, d, a, X[14], MD5_S34, MD5_T36);
- HH (a, b, c, d, X[ 1], MD5_S31, MD5_T37);
- HH (d, a, b, c, X[ 4], MD5_S32, MD5_T38);
- HH (c, d, a, b, X[ 7], MD5_S33, MD5_T39);
- HH (b, c, d, a, X[10], MD5_S34, MD5_T40);
- HH (a, b, c, d, X[13], MD5_S31, MD5_T41);
- HH (d, a, b, c, X[ 0], MD5_S32, MD5_T42);
- HH (c, d, a, b, X[ 3], MD5_S33, MD5_T43);
- HH (b, c, d, a, X[ 6], MD5_S34, MD5_T44);
- HH (a, b, c, d, X[ 9], MD5_S31, MD5_T45);
- HH (d, a, b, c, X[12], MD5_S32, MD5_T46);
- HH (c, d, a, b, X[15], MD5_S33, MD5_T47);
- HH (b, c, d, a, X[ 2], MD5_S34, MD5_T48);
- //Perform Round 4 of the transformation
- II (a, b, c, d, X[ 0], MD5_S41, MD5_T49);
- II (d, a, b, c, X[ 7], MD5_S42, MD5_T50);
- II (c, d, a, b, X[14], MD5_S43, MD5_T51);
- II (b, c, d, a, X[ 5], MD5_S44, MD5_T52);
- II (a, b, c, d, X[12], MD5_S41, MD5_T53);
- II (d, a, b, c, X[ 3], MD5_S42, MD5_T54);
- II (c, d, a, b, X[10], MD5_S43, MD5_T55);
- II (b, c, d, a, X[ 1], MD5_S44, MD5_T56);
- II (a, b, c, d, X[ 8], MD5_S41, MD5_T57);
- II (d, a, b, c, X[15], MD5_S42, MD5_T58);
- II (c, d, a, b, X[ 6], MD5_S43, MD5_T59);
- II (b, c, d, a, X[13], MD5_S44, MD5_T60);
- II (a, b, c, d, X[ 4], MD5_S41, MD5_T61);
- II (d, a, b, c, X[11], MD5_S42, MD5_T62);
- II (c, d, a, b, X[ 2], MD5_S43, MD5_T63);
- II (b, c, d, a, X[ 9], MD5_S44, MD5_T64);
- //add the transformed values to the current checksum
- m_lMD5[0] += a;
- m_lMD5[1] += b;
- m_lMD5[2] += c;
- m_lMD5[3] += d;
- }
- /*****************************************************************************************
- CONSTRUCTOR: CMD5Checksum
- DESCRIPTION: Initialises member data
- ARGUMENTS: None
- NOTES: None
- *****************************************************************************************/
- CMD5Checksum::CMD5Checksum()
- {
- // zero members
- memset( m_lpszBuffer, 0, 64 );
- m_nCount[0] = m_nCount[1] = 0;
- // Load magic state initialization constants
- m_lMD5[0] = MD5_INIT_STATE_0;
- m_lMD5[1] = MD5_INIT_STATE_1;
- m_lMD5[2] = MD5_INIT_STATE_2;
- m_lMD5[3] = MD5_INIT_STATE_3;
- }
- /*****************************************************************************************
- FUNCTION: CMD5Checksum::DWordToByte
- DETAILS: private
- DESCRIPTION: Transfers the data in an 32 bit array to a 8 bit array
- RETURNS: void
- ARGUMENTS: BYTE* Output : the 8 bit destination array
- DWORD* Input : the 32 bit source array
- UINT nLength : the number of 8 bit data items in the source array
- NOTES: One DWORD from the input array is transferred into four BYTES
- in the output array. The first (0-7) bits of the first DWORD are
- transferred to the first output BYTE, bits bits 8-15 are transferred from
- the second BYTE etc.
- The algorithm assumes that the output array is a multiple of 4 bytes long
- so that there is a perfect fit of 8 bit BYTES into the 32 bit DWORDs.
- *****************************************************************************************/
- void CMD5Checksum::DWordToByte(BYTE* Output, DWORD* Input, UINT nLength )
- {
- //entry invariants
- ASSERT( nLength % 4 == 0 );
- ASSERT( AfxIsValidAddress(Output, nLength, TRUE) );
- ASSERT( AfxIsValidAddress(Input, nLength/4, FALSE) );
- //transfer the data by shifting and copying
- UINT i = 0;
- UINT j = 0;
- for ( ; j < nLength; i++, j += 4)
- {
- Output[j] = (UCHAR)(Input[i] & 0xff);
- Output[j+1] = (UCHAR)((Input[i] >> 8) & 0xff);
- Output[j+2] = (UCHAR)((Input[i] >> 16) & 0xff);
- Output[j+3] = (UCHAR)((Input[i] >> 24) & 0xff);
- }
- }
- /*****************************************************************************************
- FUNCTION: CMD5Checksum::Final
- DETAILS: protected
- DESCRIPTION: Implementation of main MD5 checksum algorithm; ends the checksum calculation.
- RETURNS: CString : the final hexadecimal MD5 checksum result
- ARGUMENTS: None
- NOTES: Performs the final MD5 checksum calculation ('Update' does most of the work,
- this function just finishes the calculation.)
- *****************************************************************************************/
- CString CMD5Checksum::Final()
- {
- //Save number of bits
- BYTE Bits[8];
- DWordToByte( Bits, m_nCount, 8 );
- //Pad out to 56 mod 64.
- UINT nIndex = (UINT)((m_nCount[0] >> 3) & 0x3f);
- UINT nPadLen = (nIndex < 56) ? (56 - nIndex) : (120 - nIndex);
- Update( PADDING, nPadLen );
- //Append length (before padding)
- Update( Bits, 8 );
- //Store final state in 'lpszMD5'
- const int nMD5Size = 16;
- unsigned char lpszMD5[ nMD5Size ];
- DWordToByte( lpszMD5, m_lMD5, nMD5Size );
- //Convert the hexadecimal checksum to a CString
- CString strMD5;
- for ( int i=0; i < nMD5Size; i++)
- {
- CString Str;
- if (lpszMD5[i] == 0) {
- Str = CString("00");
- }
- else if (lpszMD5[i] <= 15) {
- Str.Format("0%x",lpszMD5[i]);
- }
- else {
- Str.Format("%x",lpszMD5[i]);
- }
- ASSERT( Str.GetLength() == 2 );
- strMD5 += Str;
- }
- ASSERT( strMD5.GetLength() == 32 );
- return strMD5;
- }
- /*****************************************************************************************
- FUNCTION: CMD5Checksum::Update
- DETAILS: protected
- DESCRIPTION: Implementation of main MD5 checksum algorithm
- RETURNS: void
- ARGUMENTS: BYTE* Input : input block
- UINT nInputLen : length of input block
- NOTES: Computes the partial MD5 checksum for 'nInputLen' bytes of data in 'Input'
- *****************************************************************************************/
- void CMD5Checksum::Update( BYTE* Input, ULONG nInputLen )
- {
- //Compute number of bytes mod 64
- UINT nIndex = (UINT)((m_nCount[0] >> 3) & 0x3F);
- //Update number of bits
- if ( ( m_nCount[0] += nInputLen << 3 ) < ( nInputLen << 3) )
- {
- m_nCount[1]++;
- }
- m_nCount[1] += (nInputLen >> 29);
- //Transform as many times as possible.
- UINT i=0;
- UINT nPartLen = 64 - nIndex;
- if (nInputLen >= nPartLen)
- {
- memcpy( &m_lpszBuffer[nIndex], Input, nPartLen );
- Transform( m_lpszBuffer );
- for (i = nPartLen; i + 63 < nInputLen; i += 64)
- {
- Transform( &Input[i] );
- }
- nIndex = 0;
- }
- else
- {
- i = 0;
- }
- // Buffer remaining input
- memcpy( &m_lpszBuffer[nIndex], &Input[i], nInputLen-i);
- }