//****************************************************************************** //MD5算法:实现 //****************************************************************************** //作者:Cai //日期:2011-10-25 //修改:2011-11-15 添加 支持大头字节序编译开关,可供Lazarus使用 //****************************************************************************** //MD5算法源码 unit MD5Class;
interface uses SysUtils, Classes;
type
INT4 = Integer; UINT4 = Cardinal; UINT2 = WORD;
PUINT4 = ^UINT4; TBytes_4 = array[0..4-1] of Byte; TBytes_16 = array[0..16-1]of Byte; TBytes_32 = array[0..32-1]of Byte; TBytes_64 = array[0..64-1]of Byte;
PBytes_64 = ^TBytes_64;
TUINT4s_2 = array[0..2-1]of UINT4; TUINT4s_4 = array[0..4-1]of UINT4; TUINT4s_16 = array[0..16-1]of UINT4; TUINT4s = array of UINT4;
TMD5_CTX = record State : TUINT4s_4; // /* state (ABCD) */ Count : TUINT4s_2; ///* number of bits, modulo 2^64 (lsb first) */ Buffer: TBytes_64; // /* input buffer */ end; TMD5Context = TMD5_CTX;
TMD5Digest = TBytes_16; PMD5Digest = ^TMD5Digest;
TMD5Class = Class protected //MD5核心算法 procedure MD5Transform(var States: TUINT4s_4; pBlock: PBytes_64); procedure Encode(pOutBuf: PByte; iInBuf: PUINT4; iInBufNumOfBytes: UINT4); procedure Decode(iOutBuf: PUINT4; pInBuf: PByte; iInBufNumOfBytes: UINT4); //其他 procedure MD5_MemCpy(pDest, pSrc: Pointer; iLen: UINT4); procedure MD5_MemSet(P: POINTER; iValue, iLen: UINT4); public constructor Create(); virtual; destructor Destroy; override; class function DigestToString(MD5Digest: TMD5Digest):String; class function StringToDigest(sMD5Str: string): TMD5Digest; //=========MD5算法:实现============== procedure MD5Init(var Context: TMD5Context); procedure MD5Update(var Context: TMD5Context; pInBuffer: PByte; iInBufLen: UINT4); procedure MD5Final(var Digest: TMD5Digest; var Context: TMD5Context); end;
implementation //implementation of the CMd5A class.
function ArrUINT4s(pArrUINT4s: PUINT4; iIndex: UINT4): UINT4;overload; begin Inc(pArrUINT4s, iIndex); Result := pArrUINT4s^; end;
procedure ArrUINT4s(pArrUINT4s: PUINT4; iIndex: UINT4; iValue: UINT4);overload; begin Inc(pArrUINT4s, iIndex); pArrUINT4s^ := iValue; end;
////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // Construction/Destruction //////////////////////////////////////////////////////////////////////
const //Constants for MD5Transform routine S11 = 7; S12 = 12; S13 = 17; S14 = 22; S21 = 5; S22 = 9; S23 = 14; S24 = 20; S31 = 4; S32 = 11; S33 = 16; S34 = 23; S41 = 6; S42 = 10; S43 = 15; S44 = 21;
PADDING: array[0..64-1] of Byte = ( $80, 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); // F, G, H and I are basic MD5 functions. (*************************************** 以一下是每次操作中用到的四个非线性函数(每轮一个)。 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)) ****************************************) function F(x, y, z: UINT4):UINT4; begin Result := (x and y) or ((not x) and z); end;
function G(x, y, z: UINT4): UINT4; begin Result := (x and z) or (y and (not z)); end;
function H(x, y, z: UINT4): UINT4; begin Result := x xor y xor z; end;
function I(x, y, z: UINT4): UINT4; begin Result := y xor (x or (not z)); end;
//rotates x left n bits. function ROL_32(xParam, nBits:UINT4):UINT4; begin Result := (((xParam) shl (nBits)) or ((xParam) shr (32-(nBits)))) end;
(**************************************** FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. Rotation is separate from addition to prevent recomputation. ****************************************) procedure FF(var a: UINT4; b, c, d, x, s, ac: UINT4); begin Inc(a, F(b, c, d) + x + ac); a := ROL_32(a, s); Inc(a, b); end;
procedure GG(var a: UINT4; b, c, d, x, s, ac: UINT4); begin Inc(a, G(b, c, d) + x + ac); a := ROL_32(a, s); Inc(a, b); end;
procedure HH(var a: UINT4; b, c, d, x, s, ac: UINT4); begin Inc(a, H(b, c, d) + x + ac); a := ROL_32(a, s); Inc(a, b); end;
procedure II(var a: UINT4; b, c, d, x, s, ac: UINT4); begin Inc(a, I(b, c, d) + x + ac); a := ROL_32(a, s); Inc(a, b); end;
const SUPPORT_BIG_ENDIAN = FALSE; //编译开关 TRUE为 支持大头字节序 CPU 编译
{$IF SUPPORT_BIG_ENDIAN} //是否小端字节序列 const IsLittleEndian: Boolean = ByteBOOL($00000001); (* Code: IsLittleEndian function IsLittleEndian():Boolean; var dwValue: UINT4; Bytes_4: TBytes_4; begin dwValue := $00000001; Bytes_4 := TBytes_4(dwValue); Result := (Bytes_4[0]=$01); end; *) (* //ntohl, 大头字序转换 function BigEndianToHostEndian(const nValue: UINT4):UINT4; begin if IsLittleEndian then begin TBytes_4(Result)[0] := TBytes_4(nValue)[3]; TBytes_4(Result)[1] := TBytes_4(nValue)[2]; TBytes_4(Result)[2] := TBytes_4(nValue)[1]; TBytes_4(Result)[3] := TBytes_4(nValue)[0]; end else begin Result := nValue; end; end; *)
//小头字节序转换 function LittleEndianToHostEndian(const nValue: UINT4):UINT4; //这里使用函数的方式实现起来简单,但会降低MD5Transform的效率, //实际应用中若对效率要求较高,可使用数组在进程初始化时就根据字节序填充即可 begin if IsLittleEndian then begin Result := nValue; end else begin TBytes_4(Result)[0] := TBytes_4(nValue)[3]; TBytes_4(Result)[1] := TBytes_4(nValue)[2]; TBytes_4(Result)[2] := TBytes_4(nValue)[1]; TBytes_4(Result)[3] := TBytes_4(nValue)[0]; end; end; {$ELSE} type LittleEndianToHostEndian = UINT4;//不支持字节序时直接定义为类型强制转换即可 {$IFEND}
{ TMD5Class }
constructor TMD5Class.Create; begin
end;
destructor TMD5Class.Destroy; begin
inherited; end;
//MD5 initialization. Begins an MD5 operation, writing a new context. procedure TMD5Class.MD5Init(var Context: TMD5Context); begin FillChar(Context, SizeOf(TMD5Context), 0); (*MD5中有四个32位被称作链接变量(Chaining Variable)的整数参数,他们分别为: A=0x01234567, B=0x89abcdef, C=0xfedcba98, D=0x76543210 // word A: 01 23 45 67 // word B: 89 ab cd ef // word C: fe dc ba 98 // word D: 76 54 32 10 *) //Load magic initialization constants. //检查CPU字节序列模式 //if IsLittleEndian then begin // Little endian 小头字节序列 Context.State[0] := LittleEndianToHostEndian($67452301); Context.State[1] := LittleEndianToHostEndian($EFCDAB89); Context.State[2] := LittleEndianToHostEndian($98BADCFE); Context.State[3] := LittleEndianToHostEndian($10325476); end (*else begin // Big endian 大头字节序列 Context.State[0] := $01234567; Context.State[1] := $89ABCDEF; Context.State[2] := $FEDCBA98; Context.State[3] := $76543210; end;*) end;
(* MD5 block update operation. Continues an MD5 message-digest operation, processing another message block, and updating the context. MD5数据更新操作*) procedure TMD5Class.MD5Update(var Context: TMD5Context; pInBuffer: PByte; iInBufLen: UINT4); var iIndex, iPartLen, I: UINT4; begin // Compute number of bytes mod 64 iIndex := UINT4((Context.Count[0] shr 3) and $3F); //(Context.Count[0] / 8) mod 64
// Update number of bits Inc(Context.Count[0], UINT4(iInBufLen shl 3)); //iInBufLen * 8 if Context.Count[0]< UINT4(iInBufLen shl 3) then Inc(Context.Count[1]); //进位 Context.Count[1] := Context.Count[1] + iInBufLen shr (32 - 3); // iInBufLen + iInBufLen / 2^29 iPartLen := 64 - iIndex;
//Transform as many times as possible. if (iInBufLen >= iPartLen) then begin MD5_Memcpy(@Context.Buffer[iIndex], pInBuffer, iPartLen); MD5Transform(Context.State, @Context.Buffer); I := iPartLen; while (I+64<=iInBufLen) do begin MD5Transform(Context.State, PBytes_64(@PChar(pInBuffer)[I])); Inc(I, 64); end; iIndex := 0; end else I := 0; // Buffer remaining input Inc(pInBuffer, I); MD5_Memcpy(@Context.Buffer[iIndex], pInBuffer, iInBufLen-I); end;
(* MD5 finalization. Ends an MD5 message-digest operation, writing the the message digest and zeroizing the context.*) procedure TMD5Class.MD5Final(var Digest: TBytes_16; var Context: TMD5Context); var Bits: array[0..8-1]of Byte; iIndex, iPadLen: UINT4; begin //Save number of bits Encode(@Bits, @Context.Count, 8);
//Pad out to 56 mod 64. iIndex := UINT4((Context.Count[0] shr 3) and $3F); if iIndex < 56 then iPadLen := (56 - iIndex) else iPadLen := (120 - iIndex); MD5Update(Context, @PADDING, iPadLen); //Append length (before padding) MD5Update(Context, @Bits, 8); //Store state in digest Encode(@Digest, @Context.State, 16);
//Zeroize sensitive information. MD5_MemSet(@Context, 0, SizeOf(TMD5Context)); end;
// MD5 basic transformation. Transforms state based on block. procedure TMD5Class.MD5Transform(var States: TUINT4s_4; pBlock: PBytes_64); var //I, A, B, C, D: UINT4; x: TUINT4s_16; begin //I := 0; a := States[0]; b := States[1]; c := States[2]; d := States[3]; Decode(@x, PByte(pBlock), 64); //Round 1 FF (a, b, c, d, x[ 0], S11, LittleEndianToHostEndian($d76aa478)); // 1 */ FF (d, a, b, c, x[ 1], S12, LittleEndianToHostEndian($e8c7b756)); // 2 */ FF (c, d, a, b, x[ 2], S13, LittleEndianToHostEndian($242070db)); // 3 */ FF (b, c, d, a, x[ 3], S14, LittleEndianToHostEndian($c1bdceee)); // 4 */ FF (a, b, c, d, x[ 4], S11, LittleEndianToHostEndian($f57c0faf)); // 5 */ FF (d, a, b, c, x[ 5], S12, LittleEndianToHostEndian($4787c62a)); // 6 */ FF (c, d, a, b, x[ 6], S13, LittleEndianToHostEndian($a8304613)); // 7 */ FF (b, c, d, a, x[ 7], S14, LittleEndianToHostEndian($fd469501)); // 8 */ FF (a, b, c, d, x[ 8], S11, LittleEndianToHostEndian($698098d8)); // 9 */ FF (d, a, b, c, x[ 9], S12, LittleEndianToHostEndian($8b44f7af)); // 10 */ FF (c, d, a, b, x[10], S13, LittleEndianToHostEndian($ffff5bb1)); // 11 */ FF (b, c, d, a, x[11], S14, LittleEndianToHostEndian($895cd7be)); // 12 */ FF (a, b, c, d, x[12], S11, LittleEndianToHostEndian($6b901122)); // 13 */ FF (d, a, b, c, x[13], S12, LittleEndianToHostEndian($fd987193)); // 14 */ FF (c, d, a, b, x[14], S13, LittleEndianToHostEndian($a679438e)); // 15 */ FF (b, c, d, a, x[15], S14, LittleEndianToHostEndian($49b40821)); // 16 */ // Round 2 */ GG (a, b, c, d, x[ 1], S21, LittleEndianToHostEndian($f61e2562)); // 17 */ GG (d, a, b, c, x[ 6], S22, LittleEndianToHostEndian($c040b340)); // 18 */ GG (c, d, a, b, x[11], S23, LittleEndianToHostEndian($265e5a51)); // 19 */ GG (b, c, d, a, x[ 0], S24, LittleEndianToHostEndian($e9b6c7aa)); // 20 */ GG (a, b, c, d, x[ 5], S21, LittleEndianToHostEndian($d62f105d)); // 21 */ GG (d, a, b, c, x[10], S22, LittleEndianToHostEndian($02441453)); // 22 */ GG (c, d, a, b, x[15], S23, LittleEndianToHostEndian($d8a1e681)); // 23 */ GG (b, c, d, a, x[ 4], S24, LittleEndianToHostEndian($e7d3fbc8)); // 24 */ GG (a, b, c, d, x[ 9], S21, LittleEndianToHostEndian($21e1cde6)); // 25 */ GG (d, a, b, c, x[14], S22, LittleEndianToHostEndian($c33707d6)); // 26 */ GG (c, d, a, b, x[ 3], S23, LittleEndianToHostEndian($f4d50d87)); // 27 */ GG (b, c, d, a, x[ 8], S24, LittleEndianToHostEndian($455a14ed)); // 28 */ GG (a, b, c, d, x[13], S21, LittleEndianToHostEndian($a9e3e905)); // 29 */ GG (d, a, b, c, x[ 2], S22, LittleEndianToHostEndian($fcefa3f8)); // 30 */ GG (c, d, a, b, x[ 7], S23, LittleEndianToHostEndian($676f02d9)); // 31 */ GG (b, c, d, a, x[12], S24, LittleEndianToHostEndian($8d2a4c8a)); // 32 */ // Round 3 */ HH (a, b, c, d, x[ 5], S31, LittleEndianToHostEndian($fffa3942)); // 33 */ HH (d, a, b, c, x[ 8], S32, LittleEndianToHostEndian($8771f681)); // 34 */ HH (c, d, a, b, x[11], S33, LittleEndianToHostEndian($6d9d6122)); // 35 */ HH (b, c, d, a, x[14], S34, LittleEndianToHostEndian($fde5380c)); // 36 */ HH (a, b, c, d, x[ 1], S31, LittleEndianToHostEndian($a4beea44)); // 37 */ HH (d, a, b, c, x[ 4], S32, LittleEndianToHostEndian($4bdecfa9)); // 38 */ HH (c, d, a, b, x[ 7], S33, LittleEndianToHostEndian($f6bb4b60)); // 39 */ HH (b, c, d, a, x[10], S34, LittleEndianToHostEndian($bebfbc70)); // 40 */ HH (a, b, c, d, x[13], S31, LittleEndianToHostEndian($289b7ec6)); // 41 */ HH (d, a, b, c, x[ 0], S32, LittleEndianToHostEndian($eaa127fa)); // 42 */ HH (c, d, a, b, x[ 3], S33, LittleEndianToHostEndian($d4ef3085)); // 43 */ HH (b, c, d, a, x[ 6], S34, LittleEndianToHostEndian($04881d05)); // 44 */ HH (a, b, c, d, x[ 9], S31, LittleEndianToHostEndian($d9d4d039)); // 45 */ HH (d, a, b, c, x[12], S32, LittleEndianToHostEndian($e6db99e5)); // 46 */ HH (c, d, a, b, x[15], S33, LittleEndianToHostEndian($1fa27cf8)); // 47 */ HH (b, c, d, a, x[ 2], S34, LittleEndianToHostEndian($c4ac5665)); // 48 */ // Round 4 */ II (a, b, c, d, x[ 0], S41, LittleEndianToHostEndian($f4292244)); // 49 */ II (d, a, b, c, x[ 7], S42, LittleEndianToHostEndian($432aff97)); // 50 */ II (c, d, a, b, x[14], S43, LittleEndianToHostEndian($ab9423a7)); // 51 */ II (b, c, d, a, x[ 5], S44, LittleEndianToHostEndian($fc93a039)); // 52 */ II (a, b, c, d, x[12], S41, LittleEndianToHostEndian($655b59c3)); // 53 */ II (d, a, b, c, x[ 3], S42, LittleEndianToHostEndian($8f0ccc92)); // 54 */ II (c, d, a, b, x[10], S43, LittleEndianToHostEndian($ffeff47d)); // 55 */ II (b, c, d, a, x[ 1], S44, LittleEndianToHostEndian($85845dd1)); // 56 */ II (a, b, c, d, x[ 8], S41, LittleEndianToHostEndian($6fa87e4f)); // 57 */ II (d, a, b, c, x[15], S42, LittleEndianToHostEndian($fe2ce6e0)); // 58 */ II (c, d, a, b, x[ 6], S43, LittleEndianToHostEndian($a3014314)); // 59 */ II (b, c, d, a, x[13], S44, LittleEndianToHostEndian($4e0811a1)); // 60 */ II (a, b, c, d, x[ 4], S41, LittleEndianToHostEndian($f7537e82)); // 61 */ II (d, a, b, c, x[11], S42, LittleEndianToHostEndian($bd3af235)); // 62 */ II (c, d, a, b, x[ 2], S43, LittleEndianToHostEndian($2ad7d2bb)); // 63 */ II (b, c, d, a, x[ 9], S44, LittleEndianToHostEndian($eb86d391)); // 64 */
Inc(States[0], a); Inc(States[1], b); Inc(States[2], c); Inc(States[3], d);
//Zeroize sensitive information.*/ MD5_MemSet(@x, 0, SizeOf(x)); end;
(* Encodes input (UINT4) into output (unsigned char). Assumes len is a multiple of 4. *) procedure TMD5Class.Encode(pOutBuf: PByte; iInBuf: PUINT4; iInBufNumOfBytes: UINT4); var I, J: UINT4; begin I:=0; J:=0; while J<=(iInBufNumOfBytes-1) do begin Byte(PChar(pOutBuf)[j]) := Byte((ArrUINT4s(iInBuf, i) ) and $ff); Byte(PChar(pOutBuf)[j+1]) := Byte((ArrUINT4s(iInBuf, i) shr 8) and $ff); Byte(PChar(pOutBuf)[j+2]) := Byte((ArrUINT4s(iInBuf, i) shr 16) and $ff); Byte(PChar(pOutBuf)[j+3]) := Byte((ArrUINT4s(iInBuf, i) shr 24) and $ff); Inc(I); Inc(J, 4); end; end;
(* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4.*) procedure TMD5Class.Decode(iOutBuf: PUINT4; pInBuf: PByte; iInBufNumOfBytes: UINT4); var I, J: UINT4; begin I:=0; J:=0; while J<=(iInBufNumOfBytes-1) do begin try ArrUINT4s(iOutBuf, I, (UINT4(PChar(pInBuf)[J]) ) or (UINT4(PChar(pInBuf)[J+1]) shl 8) or (UINT4(PChar(pInBuf)[J+2]) shl 16) or (UINT4(PChar(pInBuf)[J+3]) shl 24)); except Break; end; Inc(I); Inc(J, 4); end; end;
// Note: Replace "for loop" with standard memcpy if possible. */ procedure TMD5Class.MD5_MemCpy (pDest, pSrc: Pointer; iLen: UINT4); var I: UINT4; begin if iLen>0 then for I := 0 to iLen-1 do PChar(pDest)[I] := PChar(pSrc)[I]; end;
// Note: Replace "for loop" with standard memset if possible. */ procedure TMD5Class.MD5_MemSet(P: POINTER; iValue, iLen: UINT4); var I: UINT4; begin for I := 0 to iLen-1 do PChar(P)[I] := Char(iValue); end;
class function TMD5Class.DigestToString(MD5Digest: TMD5Digest):string; var pBuf: PChar; begin pBuf := AllocMem(64); try BinToHex(@MD5Digest, pBuf, SizeOf(TMD5Digest)); Result := string(pBuf); finally FreeMem(pBuf); end; end;
class function TMD5Class.StringToDigest(sMD5Str: string): TMD5Digest; var pBuf: PChar; begin pBuf := AllocMem(64); try HexToBin(PChar(sMD5Str), pBuf, 64); Result := PMD5Digest(pBuf)^; finally FreeMem(pBuf); end; end;
end.
|
请发表评论