Home About Units Download Documents Links Contact SourceForge
Units: Hash: Source

{                                                                              }
{                           Hashing functions v3.06                            }
{                                                                              }
{             This unit is copyright © 2002-2004 by David J Butler             }
{                                                                              }
{                  This unit is part of Delphi Fundamentals.                   }
{                     Its original file name is cHash.pas                      }
{       The latest version is available from the Fundamentals home page        }
{                     http://fundementals.sourceforge.net/                     }
{                                                                              }
{                I invite you to use this unit, free of charge.                }
{        I invite you to distibute this unit, but it must be for free.         }
{             I also invite you to contribute to its development,              }
{             but do not distribute a modified copy of this file.              }
{                                                                              }
{          A forum is available on SourceForge for general discussion          }
{             http://sourceforge.net/forum/forum.php?forum_id=2117             }
{                                                                              }
{                                                                              }
{ Description:                                                                 }
{                                                                              }
{   Hashes are algorithms for computing (fixed size) 'condensed                }
{   representations' of 'messages'. The 'message' refers to the (usually       }
{   large variable size) data being hashed. The 'condensed representation'     }
{   is refered to as the 'message digest'.                                     }
{                                                                              }
{   Hashing functions are useful when a (unique) 'fingerprint' is              }
{   needed to represent a (large) piece of data.                               }
{                                                                              }
{   Hashes are refered to as 'secure' if it is computationally infeasible to   }
{   find a message that produce a given digest.                                }
{                                                                              }
{   'Keyed' hashes use a key (password) in the hashing process.                }
{                                                                              }
{ Supported hashes:                                                            }
{                                                                              }
{   Algorithm    Digest size (bits)  Type                                      }
{   ------------ ------------------  ------------------------------            }
{   Checksum       32                Checksum                                  }
{   XOR8           8                 Checksum                                  }
{   XOR16          16                Checksum                                  }
{   XOR32          32                Checksum                                  }
{   CRC16          16                Checksum / Error detection                }
{   CRC32          32                Checksum / Error detection                }
{   ELF            32                Checksum                                  }
{   MD5            128               Secure hash                               }
{   SHA1           160               Secure hash                               }
{   HMAC/MD5       128               Secure keyed hash                         }
{   HMAC/SHA1      160               Secure keyed hash                         }
{                                                                              }
{ Other:                                                                       }
{                                                                              }
{   Algorithm    Type                                                          }
{   ------------ --------------------------------------------------            }
{   ISBN         Check-digit for International Standard Book Number            }
{   LUHN         Check-digit for credit card numbers                           }
{                                                                              }
{                                                                              }
{ Revision history:                                                            }
{   02/04/2002  0.01  Initial version from cUtils unit.                        }
{                     Hash: Checksum, XOR, CRC, MD5, SHA1, SNS                 }
{                     Keyed hash: HMAC-MD5, HMAC-SHA1                          }
{   03/04/2002  0.02  Securely clear passwords from memory after use.          }
{   05/04/2002  0.03  Added SNS hashing.                                       }
{   19/04/2002  0.04  Added ISBN checksum.                                     }
{   26/07/2003  0.05  Added ELF hashing.                                       }
{   09/08/2003  3.06  Revise for Fundamentals 3.                               }
{                                                                              }

{$INCLUDE ..\cDefines.inc}
unit cHash;

interface

uses
  { Delphi }
  SysUtils;



{                                                                              }
{ Hash digests                                                                 }
{                                                                              }
type
  PByte = ^Byte;
  PWord = ^Word;
  PLongWord = ^LongWord;
  T128BitDigest = record
    case integer of
      0 : (Int64s : Array [0..1] of Int64);
      1 : (Longs  : Array [0..3] of LongWord);
      2 : (Words  : Array [0..7] of Word);
      3 : (Bytes  : Array [0..15] of Byte);
    end;
  P128BitDigest = ^T128BitDigest;
  T160BitDigest = record
    case integer of
      0 : (Longs : Array [0..4] of LongWord);
      1 : (Words : Array [0..9] of Word);
      2 : (Bytes : Array [0..19] of Byte);
    end;
  P160BitDigest = ^T160BitDigest;
  
const
  MaxHashDigestSize = Sizeof (T160BitDigest);

Procedure DigestToHexBuf (const Digest; const Size : Integer; const Buf);
Function  DigestToHex (const Digest; const Size : Integer) : String;
Function  Digest128Equal (const Digest1, Digest2 : T128BitDigest) : Boolean;
Function  Digest160Equal (const Digest1, Digest2 : T160BitDigest) : Boolean;



{                                                                              }
{ Hash errors                                                                  }
{                                                                              }
const
  hashNoError            = 0;
  hashInternalError      = 1;
  hashInvalidHashType    = 2;
  hashInvalidBuffer      = 3;
  hashInvalidBufferSize  = 4;
  hashInvalidDigest      = 5;
  hashInvalidKey         = 6;
  hashInvalidFileName    = 7;
  hashFileOpenError      = 8;
  hashFileSeekError      = 9;
  hashFileReadError      = 10;
  hashNotKeyedHashType   = 11;
  hashTooManyOpenHandles = 12;
  hashInvalidHandle      = 13;
  hashMAX_ERROR          = 13;

Function  GetHashErrorMessage (const ErrorCode : LongWord) : PChar;

type
  EHashError = class (Exception)
    protected
    FErrorCode : LongWord;

    public
    Constructor Create (const ErrorCode : LongWord; const Msg : String = '');
    Property ErrorCode : LongWord read FErrorCode;
  end;



{                                                                              }
{ Secure memory clear                                                          }
{   Used to clear keys (passwords) from memory                                 }
{                                                                              }
Procedure SecureClear (var Buf; const BufSize : Integer);
Procedure SecureClearStr (var S : String);



{                                                                              }
{ Checksum hashing                                                             }
{                                                                              }
Function  CalcChecksum32 (const Buf; const BufSize : Integer) : LongWord; overload;
Function  CalcChecksum32 (const Buf : String) : LongWord; overload;



{                                                                              }
{ XOR hashing                                                                  }
{                                                                              }
Function  CalcXOR8 (const Buf; const BufSize : Integer) : Byte; overload;
Function  CalcXOR8 (const Buf : String) : Byte; overload;

Function  CalcXOR16 (const Buf; const BufSize : Integer) : Word; overload;
Function  CalcXOR16 (const Buf : String) : Word; overload;

Function  CalcXOR32 (const Buf; const BufSize : Integer) : LongWord; overload;
Function  CalcXOR32 (const Buf : String) : LongWord; overload;



{                                                                              }
{ CRC 16 hashing                                                               }
{                                                                              }
{   The theory behind CCITT V.41 CRCs:                                         }
{                                                                              }
{      1. Select the magnitude of the CRC to be used (typically 16 or 32       }
{         bits) and choose the polynomial to use. In the case of 16 bit        }
{         CRCs, the CCITT polynomial is recommended and is                     }
{                                                                              }
{                       16    12    5                                          }
{               G(x) = x   + x   + x  + 1                                      }
{                                                                              }
{         This polynomial traps 100% of 1 bit, 2 bit, odd numbers of bit       }
{         errors, 100% of <= 16 bit burst errors and over 99% of all           }
{         other errors.                                                        }
{                                                                              }
{      2. The CRC is calculated as                                             }
{                               r                                              }
{               D(x) = (M(x) * 2 )  mod G(x)                                   }
{                                                                              }
{         This may be better described as : Add r bits (0 content) to          }
{         the end of M(x). Divide this by G(x) and the remainder is the        }
{         CRC.                                                                 }
{                                                                              }
{      3. Tag the CRC onto the end of M(x).                                    }
{                                                                              }
{      4. To check it, calculate the CRC of the new message D(x), using        }
{         the same process as in 2. above. The newly calculated CRC            }
{         should be zero.                                                      }
{                                                                              }
{   This effectively means that using CRCs, it is possible to calculate a      }
{   series of bits to tag onto the data which makes the data an exact          }
{   multiple of the polynomial.                                                }
{                                                                              }
Procedure CRC16Init (var CRC16 : Word);
Function  CRC16Byte (const CRC16 : Word; const Octet : Byte) : Word;
Function  CRC16Buf (const CRC16 : Word; const Buf; const BufSize : Integer) : Word;

Function  CalcCRC16 (const Buf; const BufSize : Integer) : Word; overload;
Function  CalcCRC16 (const Buf : String) : Word; overload;



{                                                                              }
{ CRC 32 hashing                                                               }
{                                                                              }
Procedure SetCRC32Poly (const Poly : LongWord);

Procedure CRC32Init (var CRC32 : LongWord);
Function  CRC32Byte (const CRC32 : LongWord; const Octet : Byte) : LongWord;
Function  CRC32Buf (const CRC32 : LongWord; const Buf; const BufSize : Integer) : LongWord;
Function  CRC32BufNoCase (const CRC32 : LongWord; const Buf; const BufSize : Integer) : LongWord;

Function  CalcCRC32 (const Buf; const BufSize : Integer) : LongWord; overload;
Function  CalcCRC32 (const Buf : String) : LongWord; overload;



{                                                                              }
{ ELF hashing                                                                  }
{                                                                              }
Procedure ELFInit (var Digest : LongWord);
Function  ELFBuf (const Digest : LongWord; const Buf; const BufSize : Integer) : LongWord;

Function  CalcELF (const Buf; const BufSize : Integer) : LongWord; overload;
Function  CalcELF (const Buf : String) : LongWord; overload;



{                                                                              }
{ ISBN checksum                                                                }
{                                                                              }
Function  IsValidISBN (const S : String) : Boolean;



{                                                                              }
{ LUHN checksum                                                                }
{                                                                              }
{   The LUHN forumula (also known as mod-10) is used in major credit card      }
{   account numbers for validity checking.                                     }
{                                                                              }
Function  IsValidLUHN (const S : String) : Boolean;



{                                                                              }
{ MD5 hashing                                                                  }
{                                                                              }
{   MD5 is an Internet standard secure hashing function, that was              }
{   developed by Professor Ronald L. Rivest in 1991. Subsequently it has       }
{   been placed in the public domain.                                          }
{   MD5 was developed to be more secure after MD4 was 'broken'.                }
{   Den Boer and Bosselaers estimate that if a custom machine were to be       }
{   built specifically to find collisions for MD5 (costing $10m in 1994) it    }
{   would on average take 24 days to find a collision.                         }
{                                                                              }
Procedure MD5InitDigest (var Digest : T128BitDigest);
Procedure MD5Buf (var Digest : T128BitDigest; const Buf; const BufSize : Integer);
Procedure MD5FinalBuf (var Digest : T128BitDigest; const Buf; const BufSize : Integer;
          const TotalSize : Int64);

Function  CalcMD5 (const Buf; const BufSize : Integer) : T128BitDigest; overload;
Function  CalcMD5 (const Buf : String) : T128BitDigest; overload;

Function  MD5DigestAsString (const Digest : T128BitDigest) : String;
Function  MD5DigestToHex (const Digest : T128BitDigest) : String;



{                                                                              }
{ HMAC-MD5 keyed hashing                                                       }
{                                                                              }
{   HMAC allows secure keyed hashing (hashing with a password).                }
{   HMAC was designed to meet the requirements of the IPSEC working group in   }
{   the IETF, and is now a standard.                                           }
{   HMAC, are proven to be secure as long as the underlying hash function      }
{   has some reasonable cryptographic strengths.                               }
{   See RFC 2104 for details on HMAC.                                          }
{                                                                              }
Procedure HMAC_MD5Init (const Key : Pointer; const KeySize : Integer;
          var Digest : T128BitDigest; var K : String);
Procedure HMAC_MD5Buf (var Digest : T128BitDigest; const Buf; const BufSize : Integer);
Procedure HMAC_MD5FinalBuf (const K : String; var Digest : T128BitDigest;
          const Buf; const BufSize : Integer; const TotalSize : Int64);

Function  CalcHMAC_MD5 (const Key : Pointer; const KeySize : Integer;
          const Buf; const BufSize : Integer) : T128BitDigest; overload;
Function  CalcHMAC_MD5 (const Key : String; const Buf; const BufSize : Integer) : T128BitDigest; overload;
Function  CalcHMAC_MD5 (const Key, Buf : String) : T128BitDigest; overload;



{                                                                              }
{ SHA1 Hashing                                                                 }
{                                                                              }
{   Specification at http://www.itl.nist.gov/fipspubs/fip180-1.htm             }
{   Also see RFC 3174.                                                         }
{   SHA1 was developed by NIST and is specified in the Secure Hash Standard    }
{   (SHS, FIPS 180) and corrects an unpublished flaw the original SHA          }
{   algorithm.                                                                 }
{   SHA1 produces a 160-bit digest and is considered more secure than MD5.     }
{   SHA1 has a similar design to the MD4-family of hash functions.             }
{                                                                              }
Procedure SHA1InitDigest (var Digest : T160BitDigest);
Procedure SHA1Buf (var Digest : T160BitDigest; const Buf; const BufSize : Integer);
Procedure SHA1FinalBuf (var Digest : T160BitDigest; const Buf; const BufSize : Integer;
          const TotalSize : Int64);

Function  CalcSHA1 (const Buf; const BufSize : Integer) : T160BitDigest; overload;
Function  CalcSHA1 (const Buf : String) : T160BitDigest; overload;

Function  SHA1DigestAsString (const Digest : T160BitDigest) : String;
Function  SHA1DigestToHex (const Digest : T160BitDigest) : String;



{                                                                              }
{ HMAC-SHA1 keyed hashing                                                      }
{                                                                              }
Procedure HMAC_SHA1Init (const Key : Pointer; const KeySize : Integer;
          var Digest : T160BitDigest; var K : String);
Procedure HMAC_SHA1Buf (var Digest : T160BitDigest; const Buf; const BufSize : Integer);
Procedure HMAC_SHA1FinalBuf (const K : String; var Digest : T160BitDigest;
          const Buf; const BufSize : Integer; const TotalSize : Int64);

Function  CalcHMAC_SHA1 (const Key : Pointer; const KeySize : Integer;
          const Buf; const BufSize : Integer) : T160BitDigest; overload;
Function  CalcHMAC_SHA1 (const Key : String; const Buf; const BufSize : Integer) : T160BitDigest; overload;
Function  CalcHMAC_SHA1 (const Key, Buf : String) : T160BitDigest; overload;



{                                                                              }
{ Hash class wrappers                                                          }
{                                                                              }
type
  { AHash                                                                      }
  {   Base class for hash classes.                                             }
  AHash = class
    protected
    FDigest    : Pointer;
    FTotalSize : Int64;

    Procedure InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer); virtual; abstract;
    Procedure ProcessBuf (const Buf; const BufSize : Integer); virtual; abstract;
    Procedure ProcessFinalBuf (const Buf; const BufSize : Integer; const TotalSize : Int64); virtual;

    public
    class Function DigestSize : Integer; virtual; abstract;

    Procedure Init (const Digest : Pointer; const Key : Pointer = nil;
              const KeySize : Integer = 0); overload;
    Procedure Init (const Digest : Pointer; const Key : String = ''); overload;

    Procedure HashBuf (const Buf; const BufSize : Integer; const FinalBuf : Boolean);
    Procedure HashFile (const FileName : String; const Offset : Int64 = 0;
              const MaxCount : Int64 = -1);
  end;
  THashClass = class of AHash;

  { TChecksum32Hash                                                            }
  TChecksum32Hash = class (AHash)
    protected
    Procedure InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer); override;
    Procedure ProcessBuf (const Buf; const BufSize : Integer); override;

    public
    class Function DigestSize : Integer; override;
  end;

  { TXOR8Hash                                                                  }
  TXOR8Hash = class (AHash)
    protected
    Procedure InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer); override;
    Procedure ProcessBuf (const Buf; const BufSize : Integer); override;

    public
    class Function DigestSize : Integer; override;
  end;

  { TXOR16Hash                                                                 }
  TXOR16Hash = class (AHash)
    protected
    Procedure InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer); override;
    Procedure ProcessBuf (const Buf; const BufSize : Integer); override;

    public
    class Function DigestSize : Integer; override;
  end;

  { TXOR32Hash                                                                 }
  TXOR32Hash = class (AHash)
    protected
    Procedure InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer); override;
    Procedure ProcessBuf (const Buf; const BufSize : Integer); override;

    public
    class Function DigestSize : Integer; override;
  end;

  { TCRC16Hash                                                                 }
  TCRC16Hash = class (AHash)
    protected
    Procedure InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer); override;
    Procedure ProcessBuf (const Buf; const BufSize : Integer); override;

    public
    class Function DigestSize : Integer; override;
  end;

  { TCRC32Hash                                                                 }
  TCRC32Hash = class (AHash)
    protected
    Procedure InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer); override;
    Procedure ProcessBuf (const Buf; const BufSize : Integer); override;

    public
    class Function DigestSize : Integer; override;
  end;

  { TELFHash                                                                   }
  TELFHash = class (AHash)
    protected
    Procedure InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer); override;
    Procedure ProcessBuf (const Buf; const BufSize : Integer); override;

    public
    class Function DigestSize : Integer; override;
  end;

  { TMD5Hash                                                                   }
  TMD5Hash = class (AHash)
    protected
    Procedure InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer); override;
    Procedure ProcessBuf (const Buf; const BufSize : Integer); override;
    Procedure ProcessFinalBuf (const Buf; const BufSize : Integer; const TotalSize : Int64); override;

    public
    class Function DigestSize : Integer; override;
  end;

  { THMAC_MD5Hash                                                              }
  THMAC_MD5Hash = class (AHash)
    protected
    FKey : String;

    Procedure InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer); override;
    Procedure ProcessBuf (const Buf; const BufSize : Integer); override;
    Procedure ProcessFinalBuf (const Buf; const BufSize : Integer; const TotalSize : Int64); override;

    public
    class Function DigestSize : Integer; override;

    Destructor Destroy; override;
  end;

  { TSHA1Hash                                                                  }
  TSHA1Hash = class (AHash)
    protected
    Procedure InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer); override;
    Procedure ProcessBuf (const Buf; const BufSize : Integer); override;
    Procedure ProcessFinalBuf (const Buf; const BufSize : Integer; const TotalSize : Int64); override;

    public
    class Function DigestSize : Integer; override;
  end;

  { THMAC_SHA1Hash                                                             }
  THMAC_SHA1Hash = class (AHash)
    protected
    FKey : String;

    Procedure InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer); override;
    Procedure ProcessBuf (const Buf; const BufSize : Integer); override;
    Procedure ProcessFinalBuf (const Buf; const BufSize : Integer; const TotalSize : Int64); override;

    public
    class Function DigestSize : Integer; override;

    Destructor Destroy; override;
  end;



{                                                                              }
{ THashType                                                                    }
{                                                                              }
type
  THashType = (hashChecksum32, hashXOR8, hashXOR16, hashXOR32,
               hashCRC16, hashCRC32,
               hashELF,
               hashMD5, hashSHA1,
               hashHMAC_MD5, hashHMAC_SHA1);



{                                                                              }
{ GetHashClassByType                                                           }
{                                                                              }
Function  GetHashClassByType (const HashType : THashType) : THashClass;
Function  GetDigestSize (const HashType : THashType) : Integer;



{                                                                              }
{ CalculateHash                                                                }
{                                                                              }
Procedure CalculateHash (const HashType : THashType;
          const Buf; const BufSize : Integer; const Digest : Pointer;
          const Key : Pointer = nil; const KeySize : Integer = 0); overload;
Procedure CalculateHash (const HashType : THashType;
          const Buf; const BufSize : Integer;
          const Digest : Pointer; const Key : String = ''); overload;
Procedure CalculateHash (const HashType : THashType;
          const Buf : String; const Digest : Pointer;
          const Key : String = ''); overload;


          
{                                                                              }
{ HashString                                                                   }
{                                                                              }
{   HashString is a fast general purpose ASCII string hashing function.        }
{   It returns a 32 bit value in the range 0 to Slots - 1. If Slots = 0 then   }
{   the full 32 bit value is returned.                                         }
{   If CaseSensitive = False then HashString will return the same hash value   }
{   regardless of the case of the characters in the string.                    }
{                                                                              }
{   The implementation is based on CRC32. It uses up to 48 characters from     }
{   the string (first 16 characters, last 16 characters and 16 characters      }
{   uniformly sampled from the remaining characters) to calculate the hash     }
{   value.                                                                     }
{                                                                              }
Function  HashString (const StrBuf : Pointer; const StrLength : Integer;
          const Slots : LongWord = 0; const CaseSensitive : Boolean = True) : LongWord; overload;
Function  HashString (const S : String; const Slots : LongWord = 0;
          const CaseSensitive : Boolean = True) : LongWord; overload;



implementation



{                                                                              }
{ Hash errors                                                                  }
{                                                                              }
const
  hashErrorMessages : Array [0..hashMAX_ERROR] of String = (
      '',
      'Internal error',
      'Invalid hash type',
      'Invalid buffer',
      'Invalid buffer size',
      'Invalid digest',
      'Invalid key',
      'Invalid file name',
      'File open error',
      'File seek error',
      'File read error',
      'Not a keyed hash type',
      'Too many open handles',
      'Invalid handle');

Function GetHashErrorMessage (const ErrorCode : LongWord) : PChar;
  Begin
    if (ErrorCode = hashNoError) or (ErrorCode > hashMAX_ERROR) then
      Result := nil else
      Result := PChar (hashErrorMessages [ErrorCode]);
  End;



{                                                                              }
{ EHashError                                                                   }
{                                                                              }
Constructor EHashError.Create (const ErrorCode : LongWord; const Msg : String);
  Begin
    FErrorCode := ErrorCode;
    if (Msg = '') and (ErrorCode <= hashMAX_ERROR) then
      inherited Create (hashErrorMessages [ErrorCode]) else
      inherited Create (Msg);
  End;



{                                                                              }
{ Checksum hashing                                                             }
{                                                                              }
{$IFDEF CPU_INTEL386}
Function CalcChecksum32 (const Buf; const BufSize : Integer) : LongWord;
  Asm
      or eax, eax              // eax = Buf
      jz @fin
      or edx, edx              // edx = BufSize
      jbe @finz
      push esi
      mov esi, eax
      add esi, edx
      xor eax, eax
      xor ecx, ecx
    @l1:
      dec esi
      mov cl, [esi]
      add eax, ecx
      dec edx
      jnz @l1
      pop esi
    @fin:
      ret
    @finz:
      xor eax, eax
  End;
{$ELSE}
Function CalcChecksum32 (const Buf; const BufSize : Integer) : LongWord;
var I : Integer;
    P : PByte;
  Begin
    Result := 0;
    P := @Buf;
    For I := 1 to BufSize do
      begin
        Inc (Result, P^);
        Inc (P);
      end;
  End;
{$ENDIF}

Function CalcChecksum32 (const Buf : String) : LongWord;
  Begin
    Result := CalcChecksum32 (Pointer (Buf)^, Length (Buf));
  End;



{                                                                              }
{ XOR hashing                                                                  }
{                                                                              }
{$IFDEF CPU_INTEL386}
Function XOR32Buf (const Buf; const BufSize : Integer) : LongWord;
  Asm
      or eax, eax
      jz @fin
      or edx, edx
      jz @finz

      push esi
      mov esi, eax
      xor eax, eax

      mov ecx, edx
      shr ecx, 2
      jz @rest

    @l1:
      xor eax, [esi]
      add esi, 4
      dec ecx
      jnz @l1

    @rest:
      and edx, 3
      jz @finp
      xor al, [esi]
      dec edx
      jz @finp
      inc esi
      xor ah, [esi]
      dec edx
      jz @finp
      inc esi
      mov dl, [esi]
      shl edx, 16
      xor eax, edx

    @finp:
      pop esi
      ret
    @finz:
      xor eax, eax
    @fin:
      ret
  End;
{$ELSE}
Function XOR32Buf (const Buf; const BufSize : Integer) : LongWord;
var I : Integer;
    L : Byte;
    P : PChar;
  Begin
    Result := 0;
    L := 0;
    P := @Buf;
    For I := 1 to BufSize do
      begin
        Result := Result xor (Byte (P^) shl L);
        Inc (L, 8);
        if L = 32 then
          L := 0;
        Inc (P);
      end;
  End;
{$ENDIF}

Function CalcXOR8 (const Buf; const BufSize : Integer) : Byte;
var L : LongWord;
  Begin
    L := XOR32Buf (Buf, BufSize);
    Result := Byte (L) xor
              Byte (L shr 8) xor
              Byte (L shr 16) xor
              Byte (L shr 24);
  End;

Function CalcXOR8 (const Buf : String) : Byte;
  Begin
    Result := CalcXOR8 (Pointer (Buf)^, Length (Buf));
  End;

Function CalcXOR16 (const Buf; const BufSize : Integer) : Word;
var L : LongWord;
  Begin
    L := XOR32Buf (Buf, BufSize);
    Result := Word (L) xor
              Word (L shr 16);
  End;

Function CalcXOR16 (const Buf : String) : Word;
  Begin
    Result := CalcXOR16 (Pointer (Buf)^, Length (Buf));
  End;

Function CalcXOR32 (const Buf; const BufSize : Integer) : LongWord;
  Begin
    Result := XOR32Buf (Buf, BufSize);
  End;

Function CalcXOR32 (const Buf : String) : LongWord;
  Begin
    Result := XOR32Buf (Pointer (Buf)^, Length (Buf));
  End;



{                                                                              }
{ CRC 16 hashing                                                               }
{                                                                              }
const
  CRC16Table : Array [Byte] of Word = (
    $0000, $1021, $2042, $3063, $4084, $50a5, $60c6, $70e7,
    $8108, $9129, $a14a, $b16b, $c18c, $d1ad, $e1ce, $f1ef,
    $1231, $0210, $3273, $2252, $52b5, $4294, $72f7, $62d6,
    $9339, $8318, $b37b, $a35a, $d3bd, $c39c, $f3ff, $e3de,
    $2462, $3443, $0420, $1401, $64e6, $74c7, $44a4, $5485,
    $a56a, $b54b, $8528, $9509, $e5ee, $f5cf, $c5ac, $d58d,
    $3653, $2672, $1611, $0630, $76d7, $66f6, $5695, $46b4,
    $b75b, $a77a, $9719, $8738, $f7df, $e7fe, $d79d, $c7bc,
    $48c4, $58e5, $6886, $78a7, $0840, $1861, $2802, $3823,
    $c9cc, $d9ed, $e98e, $f9af, $8948, $9969, $a90a, $b92b,
    $5af5, $4ad4, $7ab7, $6a96, $1a71, $0a50, $3a33, $2a12,
    $dbfd, $cbdc, $fbbf, $eb9e, $9b79, $8b58, $bb3b, $ab1a,
    $6ca6, $7c87, $4ce4, $5cc5, $2c22, $3c03, $0c60, $1c41,
    $edae, $fd8f, $cdec, $ddcd, $ad2a, $bd0b, $8d68, $9d49,
    $7e97, $6eb6, $5ed5, $4ef4, $3e13, $2e32, $1e51, $0e70,
    $ff9f, $efbe, $dfdd, $cffc, $bf1b, $af3a, $9f59, $8f78,
    $9188, $81a9, $b1ca, $a1eb, $d10c, $c12d, $f14e, $e16f,
    $1080, $00a1, $30c2, $20e3, $5004, $4025, $7046, $6067,
    $83b9, $9398, $a3fb, $b3da, $c33d, $d31c, $e37f, $f35e,
    $02b1, $1290, $22f3, $32d2, $4235, $5214, $6277, $7256,
    $b5ea, $a5cb, $95a8, $8589, $f56e, $e54f, $d52c, $c50d,
    $34e2, $24c3, $14a0, $0481, $7466, $6447, $5424, $4405,
    $a7db, $b7fa, $8799, $97b8, $e75f, $f77e, $c71d, $d73c,
    $26d3, $36f2, $0691, $16b0, $6657, $7676, $4615, $5634,
    $d94c, $c96d, $f90e, $e92f, $99c8, $89e9, $b98a, $a9ab,
    $5844, $4865, $7806, $6827, $18c0, $08e1, $3882, $28a3,
    $cb7d, $db5c, $eb3f, $fb1e, $8bf9, $9bd8, $abbb, $bb9a,
    $4a75, $5a54, $6a37, $7a16, $0af1, $1ad0, $2ab3, $3a92,
    $fd2e, $ed0f, $dd6c, $cd4d, $bdaa, $ad8b, $9de8, $8dc9,
    $7c26, $6c07, $5c64, $4c45, $3ca2, $2c83, $1ce0, $0cc1,
    $ef1f, $ff3e, $cf5d, $df7c, $af9b, $bfba, $8fd9, $9ff8,
    $6e17, $7e36, $4e55, $5e74, $2e93, $3eb2, $0ed1, $1ef0);

Function CRC16Byte (const CRC16 : Word; const Octet : Byte) : Word;
  Begin
    Result := CRC16Table [Byte (Hi (CRC16) xor Octet)] xor Word (CRC16 shl 8);
  End;

Function CRC16Buf (const CRC16 : Word; const Buf; const BufSize : Integer) : Word;
var I : Integer;
    P : PByte;
  Begin
    Result := CRC16;
    P := @Buf;
    For I := 1 to BufSize do
      begin
        Result := CRC16Byte (Result, P^);
        Inc (P);
      end;
  End;

Procedure CRC16Init (var CRC16 : Word);
  Begin
    CRC16 := $FFFF;
  End;

Function CalcCRC16 (const Buf; const BufSize : Integer) : Word;
  Begin
    CRC16Init (Result);
    Result := CRC16Buf (Result, Buf, BufSize);
  End;

Function CalcCRC16 (const Buf : String) : Word;
  Begin
    Result := CalcCRC16 (Pointer (Buf)^, Length (Buf));
  End;



{                                                                              }
{ CRC 32 hashing                                                               }
{                                                                              }
var
  CRC32TableInit : Boolean = False;
  CRC32Table     : Array [Byte] of LongWord;
  CRC32Poly      : LongWord = $EDB88320;

Procedure InitCRC32Table;
var I, J : Byte;
    R    : LongWord;
  Begin
    For I := $00 to $FF do
      begin
        R := I;
        For J := 8 downto 1 do
          if R and 1 <> 0 then
            R := (R shr 1) xor CRC32Poly else
            R := R shr 1;
        CRC32Table [I] := R;
      end;
    CRC32TableInit := True;
  End;

Procedure SetCRC32Poly (const Poly : LongWord);
  Begin
    CRC32Poly := Poly;
    CRC32TableInit := False;
  End;

Function CalcCRC32Byte (const CRC32 : LongWord; const Octet : Byte) : LongWord;
  Begin
    Result := CRC32Table [Byte (CRC32) xor Octet] xor ((CRC32 shr 8) and $00FFFFFF);
  End;

Function CRC32Byte (const CRC32 : LongWord; const Octet : Byte) : LongWord;
  Begin
    if not CRC32TableInit then
      InitCRC32Table;
    Result := CalcCRC32Byte (CRC32, Octet);
  End;

Function CRC32Buf (const CRC32 : LongWord; const Buf; const BufSize : Integer) : LongWord;
var P : PByte;
    I : Integer;
  Begin
    if not CRC32TableInit then
      InitCRC32Table;
    P := @Buf;
    Result := CRC32;
    For I := 1 to BufSize do
      begin
        Result := CalcCRC32Byte (Result, P^);
        Inc (P);
      end;
  End;

Function CRC32BufNoCase (const CRC32 : LongWord; const Buf; const BufSize : Integer) : LongWord;
var P : PByte;
    I : Integer;
    C : Byte;
  Begin
    if not CRC32TableInit then
      InitCRC32Table;
    P := @Buf;
    Result := CRC32;
    For I := 1 to BufSize do
      begin
        C := P^;
        if Char (C) in ['A'..'Z'] then
          C := C or 32;
        Result := CalcCRC32Byte (Result, C);
        Inc (P);
      end;
  End;

Procedure CRC32Init (var CRC32 : LongWord);
  Begin
    CRC32 := $FFFFFFFF;
  End;

Function CalcCRC32 (const Buf; const BufSize : Integer) : LongWord;
  Begin
    CRC32Init (Result);
    Result := not CRC32Buf (Result, Buf, BufSize);
  End;

Function CalcCRC32 (const Buf : String) : LongWord;
  Begin
    Result := CalcCRC32 (Pointer (Buf)^, Length (Buf));
  End;



{                                                                              }
{ ELF hashing                                                                  }
{                                                                              }
Procedure ELFInit (var Digest : LongWord);
  Begin
    Digest := 0;
  End;

Function ELFBuf (const Digest : LongWord; const Buf; const BufSize : Integer) : LongWord;
var I : Integer;
    P : PByte;
    X : LongWord;
  Begin
    Result := Digest;
    P := @Buf;
    For I := 1 to BufSize do
      begin
        Result := (Result shl 4) + P^;
        Inc (P);
        X := Result and $F0000000;
        if X <> 0 then
          Result := Result xor (X shr 24);
        Result := Result and (not X);
      end;
  End;


Function CalcELF (const Buf; const BufSize : Integer) : LongWord;
  Begin
    Result := ELFBuf (0, Buf, BufSize);
  End;


Function CalcELF (const Buf : String) : LongWord;
  Begin
    Result := CalcELF (Pointer (Buf)^, Length (Buf));
  End;



{                                                                              }
{ ISBN checksum                                                                }
{                                                                              }
Function IsValidISBN (const S : String) : Boolean;
var I, L, M, D, C : Integer;
    P : PChar;
  Begin
    L := Length (S);
    if L < 10 then // too few digits
      begin
        Result := False;
        exit;
      end;
    M := 10;
    C := 0;
    P := Pointer (S);
    For I := 1 to L do
      begin
        if (P^ in ['0'..'9']) or ((M = 1) and (P^ in ['x', 'X'])) then
          begin
            if M = 0 then // too many digits
              begin
                Result := False;
                exit;
              end;
            if P^ in ['x', 'X'] then
              D := 10 else
              D := Ord (P^) - Ord ('0');
            Inc (C, M * D);
            Dec (M);
          end;
        Inc (P);
      end;
    if M > 0 then // too few digits
      begin
        Result := False;
        exit;
      end;
    Result := C mod 11 = 0;
  End;



{                                                                              }
{ LUHN checksum                                                                }
{                                                                              }
Function IsValidLUHN (const S : String) : Boolean;
var P : PChar;
    I, L, M, C, D : Integer;
    R : Boolean;
  Begin
    L := Length (S);
    if L = 0 then
      begin
        Result := False;
        exit;
      end;
    P := Pointer (S);
    Inc (P, L - 1);
    C := 0;
    M := 0;
    R := False;
    For I := 1 to L do
      begin
        if P^ in ['0'..'9'] then
          begin
            D := Ord (P^) - Ord ('0');
            if R then
              begin
                D := D * 2;
                D := (D div 10) + (D mod 10);
              end;
            Inc (C, D);
            Inc (M);
            R := not R;
          end;
        Dec (P);
      end;
    Result := (M >= 1) and (C mod 10 = 0);
  End;



{                                                                              }
{ Digests                                                                      }
{                                                                              }
Procedure DigestToHexBuf (const Digest; const Size : Integer; const Buf);
const s_HexDigitsLower : String [16] = '0123456789abcdef';
var I : Integer;
    P : PChar;
    Q : PByte;
  Begin
    P := @Buf;;
    Assert (Assigned (P), 'Assigned (Buf)');
    Q := @Digest;
    Assert (Assigned (Q), 'Assigned (Digest)');
    For I := 0 to Size - 1 do
      begin
        P^ := s_HexDigitsLower [Q^ shr 4 + 1];
        Inc (P);
        P^ := s_HexDigitsLower [Q^ and 15 + 1];
        Inc (P);
        Inc (Q);
      end;
  End;

Function DigestToHex (const Digest; const Size : Integer) : String;
  Begin
    SetLength (Result, Size * 2);
    DigestToHexBuf (Digest, Size, Pointer (Result)^);
  End;

Function Digest128Equal (const Digest1, Digest2 : T128BitDigest) : Boolean;
var I : Integer;
  Begin
    For I := 0 to 3 do
      if Digest1.Longs[I] <> Digest2.Longs[I] then
        begin
          Result := False;
          exit;
        end;
    Result := True;
  End;

Function Digest160Equal (const Digest1, Digest2 : T160BitDigest) : Boolean;
var I : Integer;
  Begin
    For I := 0 to 4 do
      if Digest1.Longs[I] <> Digest2.Longs[I] then
        begin
          Result := False;
          exit;
        end;
    Result := True;
  End;



{                                                                              }
{ MD5 hashing                                                                  }
{                                                                              }
const
  MD5Table_1 : Array [0..15] of LongWord = (
      $D76AA478, $E8C7B756, $242070DB, $C1BDCEEE,
      $F57C0FAF, $4787C62A, $A8304613, $FD469501,
      $698098D8, $8B44F7AF, $FFFF5BB1, $895CD7BE,
      $6B901122, $FD987193, $A679438E, $49B40821);
  MD5Table_2 : Array [0..15] of LongWord = (
      $F61E2562, $C040B340, $265E5A51, $E9B6C7AA,
      $D62F105D, $02441453, $D8A1E681, $E7D3FBC8,
      $21E1CDE6, $C33707D6, $F4D50D87, $455A14ED,
      $A9E3E905, $FCEFA3F8, $676F02D9, $8D2A4C8A);
  MD5Table_3 : Array [0..15] of LongWord = (
      $FFFA3942, $8771F681, $6D9D6122, $FDE5380C,
      $A4BEEA44, $4BDECFA9, $F6BB4B60, $BEBFBC70,
      $289B7EC6, $EAA127FA, $D4EF3085, $04881D05,
      $D9D4D039, $E6DB99E5, $1FA27CF8, $C4AC5665);
  MD5Table_4 : Array [0..15] of LongWord = (
      $F4292244, $432AFF97, $AB9423A7, $FC93A039,
      $655B59C3, $8F0CCC92, $FFEFF47D, $85845DD1,
      $6FA87E4F, $FE2CE6E0, $A3014314, $4E0811A1,
      $F7537E82, $BD3AF235, $2AD7D2BB, $EB86D391);

{ Calculates a MD5 Digest (16 bytes) given a Buffer (64 bytes)                 }
{$Q-}
Procedure TransformMD5Buffer (var Digest : T128BitDigest; const Buffer);
var A, B, C, D : LongWord;
    P          : PLongWord;
    I          : Integer;
    J          : Byte;
    Buf        : Array [0..15] of LongWord absolute Buffer;
  Begin
    A := Digest.Longs [0];
    B := Digest.Longs [1];
    C := Digest.Longs [2];
    D := Digest.Longs [3];

    P := @MD5Table_1;
    For I := 0 to 3 do
      begin
        J := I * 4;
        Inc (A, Buf [J]     + P^ + (D xor (B and (C xor D))));  A := A shl  7 or A shr 25 + B; Inc (P);
        Inc (D, Buf [J + 1] + P^ + (C xor (A and (B xor C))));  D := D shl 12 or D shr 20 + A; Inc (P);
        Inc (C, Buf [J + 2] + P^ + (B xor (D and (A xor B))));  C := C shl 17 or C shr 15 + D; Inc (P);
        Inc (B, Buf [J + 3] + P^ + (A xor (C and (D xor A))));  B := B shl 22 or B shr 10 + C; Inc (P);
      end;

    P := @MD5Table_2;
    For I := 0 to 3 do
      begin
        J := I * 4;
        Inc (A, Buf [J + 1]           + P^ + (C xor (D and (B xor C))));  A := A shl  5 or A shr 27 + B; Inc (P);
        Inc (D, Buf [(J + 6) mod 16]  + P^ + (B xor (C and (A xor B))));  D := D shl  9 or D shr 23 + A; Inc (P);
        Inc (C, Buf [(J + 11) mod 16] + P^ + (A xor (B and (D xor A))));  C := C shl 14 or C shr 18 + D; Inc (P);
        Inc (B, Buf [J]               + P^ + (D xor (A and (C xor D))));  B := B shl 20 or B shr 12 + C; Inc (P);
      end;

    P := @MD5Table_3;
    For I := 0 to 3 do
      begin
        J := 16 - (I * 4);
        Inc (A, Buf [(J + 5) mod 16]  + P^ + (B xor C xor D));  A := A shl  4 or A shr 28 + B; Inc (P);
        Inc (D, Buf [(J + 8) mod 16]  + P^ + (A xor B xor C));  D := D shl 11 or D shr 21 + A; Inc (P);
        Inc (C, Buf [(J + 11) mod 16] + P^ + (D xor A xor B));  C := C shl 16 or C shr 16 + D; Inc (P);
        Inc (B, Buf [(J + 14) mod 16] + P^ + (C xor D xor A));  B := B shl 23 or B shr  9 + C; Inc (P);
      end;

    P := @MD5Table_4;
    For I := 0 to 3 do
      begin
        J := 16 - (I * 4);
        Inc (A, Buf [J mod 16]        + P^ + (C xor (B or not D)));  A := A shl  6 or A shr 26 + B; Inc (P);
        Inc (D, Buf [(J + 7) mod 16]  + P^ + (B xor (A or not C)));  D := D shl 10 or D shr 22 + A; Inc (P);
        Inc (C, Buf [(J + 14) mod 16] + P^ + (A xor (D or not B)));  C := C shl 15 or C shr 17 + D; Inc (P);
        Inc (B, Buf [(J + 5) mod 16]  + P^ + (D xor (C or not A)));  B := B shl 21 or B shr 11 + C; Inc (P);
      end;

    Inc (Digest.Longs [0], A);
    Inc (Digest.Longs [1], B);
    Inc (Digest.Longs [2], C);
    Inc (Digest.Longs [3], D);
  End;
{$IFDEF DEBUG}{$Q+}{$ENDIF}

Procedure MD5InitDigest (var Digest : T128BitDigest);
  Begin
    Digest.Longs [0] := $67452301;        // fixed initialization key
    Digest.Longs [1] := $EFCDAB89;
    Digest.Longs [2] := $98BADCFE;
    Digest.Longs [3] := $10325476;
  End;

Procedure MD5Buf (var Digest : T128BitDigest; const Buf; const BufSize : Integer);
var P : PByte;
    I, J : Integer;
  Begin
    I := BufSize;
    if I <= 0 then
      exit;
    Assert (I mod 64 = 0, 'BufSize must be multiple of 64 bytes');
    P := @Buf;
    For J := 0 to I div 64 - 1 do
      begin
        TransformMD5Buffer (Digest, P^);
        Inc (P, 64);
      end;
  End;

Procedure ReverseMem (var Buf; const BufSize : Integer);
var I : Integer;
    P : PByte;
    Q : PByte;
    T : Byte;
  Begin
    P := @Buf;
    Q := P;
    Inc (Q, BufSize - 1);
    For I := 1 to BufSize div 2 do
      begin
        T := P^;
        P^ := Q^;
        Q^ := T;
        Inc (P);
        Dec (Q);
      end;
  End;

Procedure StdFinalBuf (const Buf; const BufSize : Integer; const TotalSize : Int64; var Buf1, Buf2 : String; const SwapEndian : Boolean);
var P, Q : PByte;
    I : Integer;
    L : Int64;
  Begin
    Assert (BufSize < 64, 'Final BufSize must be less than 64 bytes');
    Assert (TotalSize >= BufSize, 'TotalSize >= BufSize');

    P := @Buf;
    SetLength (Buf1, 64);
    Q := Pointer (Buf1);
    if BufSize > 0 then
      begin
        Move (P^, Q^, BufSize);
        Inc (Q, BufSize);
      end;
    Q^ := $80;
    Inc (Q);

    L := Int64 (TotalSize * 8);
    if SwapEndian then
      ReverseMem (L, 8);
    if BufSize + 1 > 64 - Sizeof (Int64) then
      begin
        FillChar (Q^, 64 - BufSize - 1, #0);
        SetLength (Buf2, 64);
        Q := Pointer (Buf2);
        FillChar (Q^, 64 - Sizeof (Int64), #0);
        Inc (Q, 64 - Sizeof (Int64));
        PInt64 (Q)^ := L;
      end else
      begin
        I := 64 - Sizeof (Int64) - BufSize - 1;
        FillChar (Q^, I, #0);
        Inc (Q, I);
        PInt64 (Q)^ := L;
        Buf2 := '';
      end;
  End;

Procedure MD5FinalBuf (var Digest : T128BitDigest; const Buf; const BufSize : Integer; const TotalSize : Int64);
var S1, S2 : String;
  Begin
    StdFinalBuf (Buf, BufSize, TotalSize, S1, S2, False);
    TransformMD5Buffer (Digest, Pointer (S1)^);
    if S2 <> '' then
      TransformMD5Buffer (Digest, Pointer (S2)^);
  End;

Function CalcMD5 (const Buf; const BufSize : Integer) : T128BitDigest;
var I, J : Integer;
    P    : PByte;
  Begin
    MD5InitDigest (Result);
    P := @Buf;
    if BufSize <= 0 then
      I := 0 else
      I := BufSize;
    J := (I div 64) * 64;
    if J > 0 then
      begin
        MD5Buf (Result, P^, J);
        Inc (P, J);
        Dec (I, J);
      end;
    MD5FinalBuf (Result, P^, I, BufSize);
  End;

Function CalcMD5 (const Buf : String) : T128BitDigest;
  Begin
    Result := CalcMD5 (Pointer (Buf)^, Length (Buf));
  End;

Function MD5DigestAsString (const Digest : T128BitDigest) : String;
  Begin
    SetLength (Result, Sizeof (Digest));
    Move (Digest, Pointer (Result)^, Sizeof (Digest));
  End;

Function MD5DigestToHex (const Digest : T128BitDigest) : String;
  Begin
    Result := DigestToHex (Digest, Sizeof (Digest));
  End;



{                                                                              }
{ HMAC-MD5 keyed hashing                                                       }
{                                                                              }
Procedure XORBlock (var Buf : String; const XOR8 : Byte);
var I : Integer;
  Begin
    For I := 1 to Length (Buf) do
      Buf [I] := Char (Byte (Buf [I]) xor XOR8);
  End;

Procedure SecureClear (var Buf; const BufSize : Integer);
  Begin
    if BufSize <= 0 then
      exit;
    // Securely clear memory
    FillChar (Buf, BufSize, #$AA);
    FillChar (Buf, BufSize, #$55);
    FillChar (Buf, BufSize, #0);
  End;

Procedure SecureClearStr (var S : String);
  Begin
    SecureClear (Pointer (S)^, Length (S));
  End;

Procedure HMAC_KeyBlock (const Key; const KeySize : Integer; var Buf : String);
var P : PChar;
  Begin
    Assert (KeySize <= 64, 'KeySize <= 64');
    SetLength (Buf, 64);
    P := Pointer (Buf);
    if KeySize > 0 then
      begin
        Move (Key, P^, KeySize);
        Inc (P, KeySize);
      end;
    FillChar (P^, 64 - KeySize, #0);
  End;

Procedure HMAC_MD5Init (const Key : Pointer; const KeySize : Integer; var Digest : T128BitDigest; var K : String);
var S : String;
    D : T128BitDigest;
  Begin
    MD5InitDigest (Digest);

    if KeySize > 64 then
      begin
        D := CalcMD5 (Key^, KeySize);
        HMAC_KeyBlock (D, Sizeof (D), K);
      end else
      HMAC_KeyBlock (Key^, KeySize, K);

    S := K;
    XORBlock (S, $36);
    TransformMD5Buffer (Digest, Pointer (S)^);
    SecureClearStr (S);
  End;

Procedure HMAC_MD5Buf (var Digest : T128BitDigest; const Buf; const BufSize : Integer);
  Begin
    MD5Buf (Digest, Buf, BufSize);
  End;

Procedure HMAC_MD5FinalBuf (const K : String; var Digest : T128BitDigest; const Buf; const BufSize : Integer; const TotalSize : Int64);
var S : String;
  Begin
    MD5FinalBuf (Digest, Buf, BufSize, TotalSize + 64);
    S := K;
    XORBlock (S, $5C);
    Digest := CalcMD5 (S + MD5DigestAsString (Digest));
    SecureClearStr (S);
  End;

Function CalcHMAC_MD5 (const Key : Pointer; const KeySize : Integer; const Buf; const BufSize : Integer) : T128BitDigest;
var I, J : Integer;
    P    : PByte;
    K    : String;
  Begin
    HMAC_MD5Init (Key, KeySize, Result, K);
    P := @Buf;
    if BufSize <= 0 then
      I := 0 else
      I := BufSize;
    J := (I div 64) * 64;
    if J > 0 then
      begin
        HMAC_MD5Buf (Result, P^, J);
        Inc (P, J);
        Dec (I, J);
      end;
    HMAC_MD5FinalBuf (K, Result, P^, I, BufSize);
    SecureClearStr (K);
  End;

Function CalcHMAC_MD5 (const Key : String; const Buf; const BufSize : Integer) : T128BitDigest;
  Begin
    Result := CalcHMAC_MD5 (Pointer (Key), Length (Key), Buf, BufSize);
  End;

Function CalcHMAC_MD5 (const Key, Buf : String) : T128BitDigest;
  Begin
    Result := CalcHMAC_MD5 (Key, Pointer (Buf)^, Length (Buf));
  End;



{                                                                              }
{ SHA hashing                                                                  }
{                                                                              }
Procedure SHA1InitDigest (var Digest : T160BitDigest);
var P : P128BitDigest;
  Begin
    P := @Digest;
    MD5InitDigest (P^);
    Digest.Longs [4] := $C3D2E1F0;
  End;

Function SwapEndian (const Value : LongWord) : LongWord;
  Asm
        XCHG    AH, AL
        ROL     EAX, 16
        XCHG    AH, AL
  End;

Procedure SwapEndianBuf (var Buf; const Count : Integer);
var P : PLongWord;
    I : Integer;
  Begin
    P := @Buf;
    For I := 1 to Count do
      begin
        P^ := SwapEndian (P^);
        Inc (P);
      end;
  End;

Function RotateLeftBits (const Value : LongWord; const Bits : Byte) : LongWord;
  Asm
     MOV   CL, DL
     ROL   EAX, CL
  End;

{ Calculates a SHA Digest (20 bytes) given a Buffer (64 bytes)                 }
{$Q-}
Procedure TransformSHABuffer (var Digest : T160BitDigest; const Buffer; const SHA1 : Boolean);
var A, B, C, D, E : LongWord;
    W : Array [0..79] of LongWord;
    P, Q : PLongWord;
    I : Integer;
    J : LongWord;
  Begin
    P := @Buffer;
    Q := @W;
    For I := 0 to 15 do
      begin
        Q^ := SwapEndian (P^);
        Inc (P);
        Inc (Q);
      end;
    For I := 0 to 63 do
      begin
        P := Q;
        Dec (P, 16);
        J := P^;
        Inc (P, 2);
        J := J xor P^;
        Inc (P, 6);
        J := J xor P^;
        Inc (P, 5);
        J := J xor P^;
        if SHA1 then
          J := RotateLeftBits (J, 1);
        Q^ := J;
        Inc (Q);
      end;

    A := Digest.Longs [0];
    B := Digest.Longs [1];
    C := Digest.Longs [2];
    D := Digest.Longs [3];
    E := Digest.Longs [4];

    P := @W;
    For I := 0 to 3 do
      begin
        Inc (E, (A shl 5 or A shr 27) + (D xor (B and (C xor D))) + P^ + $5A827999); B := B shr 2 or B shl 30; Inc (P);
        Inc (D, (E shl 5 or E shr 27) + (C xor (A and (B xor C))) + P^ + $5A827999); A := A shr 2 or A shl 30; Inc (P);
        Inc (C, (D shl 5 or D shr 27) + (B xor (E and (A xor B))) + P^ + $5A827999); E := E shr 2 or E shl 30; Inc (P);
        Inc (B, (C shl 5 or C shr 27) + (A xor (D and (E xor A))) + P^ + $5A827999); D := D shr 2 or D shl 30; Inc (P);
        Inc (A, (B shl 5 or B shr 27) + (E xor (C and (D xor E))) + P^ + $5A827999); C := C shr 2 or C shl 30; Inc (P);
      end;

    For I := 0 to 3 do
      begin
        Inc (E, (A shl 5 or A shr 27) + (D xor B xor C) + P^ + $6ED9EBA1); B := B shr 2 or B shl 30; Inc (P);
        Inc (D, (E shl 5 or E shr 27) + (C xor A xor B) + P^ + $6ED9EBA1); A := A shr 2 or A shl 30; Inc (P);
        Inc (C, (D shl 5 or D shr 27) + (B xor E xor A) + P^ + $6ED9EBA1); E := E shr 2 or E shl 30; Inc (P);
        Inc (B, (C shl 5 or C shr 27) + (A xor D xor E) + P^ + $6ED9EBA1); D := D shr 2 or D shl 30; Inc (P);
        Inc (A, (B shl 5 or B shr 27) + (E xor C xor D) + P^ + $6ED9EBA1); C := C shr 2 or C shl 30; Inc (P);
      end;

    For I := 0 to 3 do
      begin
        Inc (E, (A shl 5 or A shr 27) + ((B and C) or (D and (B or C))) + P^ + $8F1BBCDC); B := B shr 2 or B shl 30; Inc (P);
        Inc (D, (E shl 5 or E shr 27) + ((A and B) or (C and (A or B))) + P^ + $8F1BBCDC); A := A shr 2 or A shl 30; Inc (P);
        Inc (C, (D shl 5 or D shr 27) + ((E and A) or (B and (E or A))) + P^ + $8F1BBCDC); E := E shr 2 or E shl 30; Inc (P);
        Inc (B, (C shl 5 or C shr 27) + ((D and E) or (A and (D or E))) + P^ + $8F1BBCDC); D := D shr 2 or D shl 30; Inc (P);
        Inc (A, (B shl 5 or B shr 27) + ((C and D) or (E and (C or D))) + P^ + $8F1BBCDC); C := C shr 2 or C shl 30; Inc (P);
      end;

    For I := 0 to 3 do
      begin
        Inc (E, (A shl 5 or A shr 27) + (D xor B xor C) + P^ + $CA62C1D6); B := B shr 2 or B shl 30; Inc (P);
        Inc (D, (E shl 5 or E shr 27) + (C xor A xor B) + P^ + $CA62C1D6); A := A shr 2 or A shl 30; Inc (P);
        Inc (C, (D shl 5 or D shr 27) + (B xor E xor A) + P^ + $CA62C1D6); E := E shr 2 or E shl 30; Inc (P);
        Inc (B, (C shl 5 or C shr 27) + (A xor D xor E) + P^ + $CA62C1D6); D := D shr 2 or D shl 30; Inc (P);
        Inc (A, (B shl 5 or B shr 27) + (E xor C xor D) + P^ + $CA62C1D6); C := C shr 2 or C shl 30; Inc (P);
      end;

    Inc (Digest.Longs [0], A);
    Inc (Digest.Longs [1], B);
    Inc (Digest.Longs [2], C);
    Inc (Digest.Longs [3], D);
    Inc (Digest.Longs [4], E);
  End;
{$IFDEF DEBUG}{$Q+}{$ENDIF}

Procedure SHA1Buf (var Digest : T160BitDigest; const Buf; const BufSize : Integer);
var P : PByte;
    I, J : Integer;
  Begin
    I := BufSize;
    if I <= 0 then
      exit;
    Assert (I mod 64 = 0, 'BufSize must be multiple of 64 bytes');
    P := @Buf;
    For J := 0 to I div 64 - 1 do
      begin
        TransformSHABuffer (Digest, P^, True);
        Inc (P, 64);
      end;
  End;

Procedure SHA1FinalBuf (var Digest : T160BitDigest; const Buf; const BufSize : Integer; const TotalSize : Int64);
var S1, S2 : String;
  Begin
    StdFinalBuf (Buf, BufSize, TotalSize, S1, S2, True);
    TransformSHABuffer (Digest, Pointer (S1)^, True);
    if S2 <> '' then
      TransformSHABuffer (Digest, Pointer (S2)^, True);
    SwapEndianBuf (Digest, Sizeof (Digest) div Sizeof (LongWord));
  End;

Function CalcSHA1 (const Buf; const BufSize : Integer) : T160BitDigest;
var I, J : Integer;
    P    : PByte;
  Begin
    SHA1InitDigest (Result);
    P := @Buf;
    if BufSize <= 0 then
      I := 0 else
      I := BufSize;
    J := (I div 64) * 64;
    if J > 0 then
      begin
        SHA1Buf (Result, P^, J);
        Inc (P, J);
        Dec (I, J);
      end;
    SHA1FinalBuf (Result, P^, I, BufSize);
  End;

Function CalcSHA1 (const Buf : String) : T160BitDigest;
  Begin
    Result := CalcSHA1 (Pointer (Buf)^, Length (Buf));
  End;

Function SHA1DigestAsString (const Digest : T160BitDigest) : String;
  Begin
    SetLength (Result, Sizeof (Digest));
    Move (Digest, Pointer (Result)^, Sizeof (Digest));
  End;

Function SHA1DigestToHex (const Digest : T160BitDigest) : String;
  Begin
    Result := DigestToHex (Digest, Sizeof (Digest));
  End;



{                                                                              }
{ HMAC-SHA1 keyed hashing                                                      }
{                                                                              }
Procedure HMAC_SHA1Init (const Key : Pointer; const KeySize : Integer; var Digest : T160BitDigest; var K : String);
var D : T160BitDigest;
    S : String;
  Begin
    SHA1InitDigest (Digest);

    if KeySize > 64 then
      begin
        D := CalcSHA1 (Key^, KeySize);
        HMAC_KeyBlock (D, Sizeof (D), K);
      end else
      HMAC_KeyBlock (Key^, KeySize, K);

    S := K;
    XORBlock (S, $36);
    TransformSHABuffer (Digest, Pointer (S)^, True);
    SecureClearStr (S);
  End;

Procedure HMAC_SHA1Buf (var Digest : T160BitDigest; const Buf; const BufSize : Integer);
  Begin
    SHA1Buf (Digest, Buf, BufSize);
  End;

Procedure HMAC_SHA1FinalBuf (const K : String; var Digest : T160BitDigest; const Buf; const BufSize : Integer; const TotalSize : Int64);
var S : String;
  Begin
    SHA1FinalBuf (Digest, Buf, BufSize, TotalSize + 64);
    S := K;
    XORBlock (S, $5C);
    Digest := CalcSHA1 (S + SHA1DigestAsString (Digest));
    SecureClearStr (S);
  End;

Function CalcHMAC_SHA1 (const Key : Pointer; const KeySize : Integer; const Buf; const BufSize : Integer) : T160BitDigest;
var I, J : Integer;
    P    : PByte;
    K    : String;
  Begin
    HMAC_SHA1Init (Key, KeySize, Result, K);
    P := @Buf;
    if BufSize <= 0 then
      I := 0 else
      I := BufSize;
    J := (I div 64) * 64;
    if J > 0 then
      begin
        HMAC_SHA1Buf (Result, P^, J);
        Inc (P, J);
        Dec (I, J);
      end;
    HMAC_SHA1FinalBuf (K, Result, P^, I, BufSize);
    SecureClearStr (K);
  End;

Function CalcHMAC_SHA1 (const Key : String; const Buf; const BufSize : Integer) : T160BitDigest;
  Begin
    Result := CalcHMAC_SHA1 (Pointer (Key), Length (Key), Buf, BufSize);
  End;

Function CalcHMAC_SHA1 (const Key, Buf : String) : T160BitDigest;
  Begin
    Result := CalcHMAC_SHA1 (Key, Pointer (Buf)^, Length (Buf));
  End;



{                                                                              }
{ CalculateHash                                                                }
{                                                                              }
Procedure CalculateHash (const HashType : THashType;
          const Buf; const BufSize : Integer;
          const Digest : Pointer;
          const Key : Pointer; const KeySize : Integer);
  Begin
    if KeySize > 0 then
      Case HashType of
        hashHMAC_MD5  : P128BitDigest (Digest)^ := CalcHMAC_MD5 (Key, KeySize, Buf, BufSize);
        hashHMAC_SHA1 : P160BitDigest (Digest)^ := CalcHMAC_SHA1 (Key, KeySize, Buf, BufSize);
      else
        raise EHashError.Create (hashNotKeyedHashType);
      end
    else
      Case HashType of
        hashChecksum32 : PLongWord (Digest)^     := CalcChecksum32 (Buf, BufSize);
        hashXOR8       : PByte (Digest)^         := CalcXOR8 (Buf, BufSize);
        hashXOR16      : PWord (Digest)^         := CalcXOR16 (Buf, BufSize);
        hashXOR32      : PLongWord (Digest)^     := CalcXOR32 (Buf, BufSize);
        hashCRC16      : PWord (Digest)^         := CalcCRC16(Buf, BufSize);
        hashCRC32      : PLongWord (Digest)^     := CalcCRC32 (Buf, BufSize);
        hashMD5        : P128BitDigest (Digest)^ := CalcMD5 (Buf, BufSize);
        hashSHA1       : P160BitDigest (Digest)^ := CalcSHA1 (Buf, BufSize);
        hashHMAC_MD5   : P128BitDigest (Digest)^ := CalcHMAC_MD5 (nil, 0, Buf, BufSize);
        hashHMAC_SHA1  : P160BitDigest (Digest)^ := CalcHMAC_SHA1 (nil, 0, Buf, BufSize);
      else
        raise EHashError.Create (hashInvalidHashType);
      end;
  End;

Procedure CalculateHash (const HashType : THashType; const Buf; const BufSize : Integer; const Digest : Pointer; const Key : String);
  Begin
    CalculateHash (HashType, Buf, BufSize, Digest, Pointer (Key), Length (Key));
  End;

Procedure CalculateHash (const HashType : THashType; const Buf : String; const Digest : Pointer; const Key : String);
  Begin
    CalculateHash (HashType, Pointer (Buf)^, Length (Buf), Digest, Key);
  End;

  

{                                                                              }
{ AHash                                                                        }
{                                                                              }
Procedure AHash.ProcessFinalBuf (const Buf; const BufSize : Integer; const TotalSize : Int64);
  Begin
    ProcessBuf (Buf, BufSize);
  End;

Procedure AHash.Init (const Digest : Pointer; const Key : Pointer; const KeySize : Integer);
  Begin
    Assert (Assigned (Digest), 'Assigned (Digest)');
    FDigest := Digest;
    FTotalSize := 0;
    InitHash (Digest, Key, KeySize);
  End;

Procedure AHash.Init (const Digest : Pointer; const Key : String);
  Begin
    Init (Digest, Pointer (Key), Length (Key));
  End;

Procedure AHash.HashBuf (const Buf; const BufSize : Integer; const FinalBuf : Boolean);
var I : Integer;
    P : PChar;
  Begin
    Inc (FTotalSize, BufSize);

    P := @Buf;
    I := (BufSize div 64) * 64;
    if I > 0 then
      begin
        ProcessBuf (P^, I);
        Inc (P, I);
      end;

    I := BufSize mod 64;
    if FinalBuf then
      ProcessFinalBuf (P^, I, FTotalSize) else
      if I > 0 then
        raise EHashError.Create (hashInvalidBufferSize, 'Buffer must be multiple of 64 bytes');
  End;

Procedure AHash.HashFile (const FileName : String; const Offset : Int64; const MaxCount : Int64);
const ChunkSize = 8192;
var Handle : Integer;
    Buf    : Pointer;
    I, C   : Integer;
    Left   : Int64;
    Fin    : Boolean;
  Begin
    if FileName = '' then
      raise EHashError.Create (hashInvalidFileName);
    Handle := FileOpen (FileName, fmOpenReadWrite or fmShareDenyNone);
    if Handle = -1 then
      raise EHashError.Create (hashFileOpenError);
    if Offset > 0 then
      I := FileSeek (Handle, Offset, 0) else
    if Offset < 0 then
      I := FileSeek (Handle, Offset, 2) else
      I := 0;
    if I = -1 then
      raise EHashError.Create (hashFileSeekError);
    try
      GetMem (Buf, ChunkSize);
      try
        if MaxCount < 0 then
          Left := High (Int64) else
          Left := MaxCount;
        Repeat
          if Left > ChunkSize then
            C := ChunkSize else
            C := Left;
          if C = 0 then
            begin
              I := 0;
              Fin := True;
            end else
            begin
              I := FileRead (Handle, Buf^, C);
              if I = -1 then
                raise EHashError.Create (hashFileReadError);
              Dec (Left, I);
              Fin := (I < C) or (Left <= 0);
            end;
          HashBuf (Buf^, I, Fin);
        Until Fin;
      finally
        FreeMem (Buf, ChunkSize);
      end;
    finally
      FileClose (Handle);
    end;
  End;



{                                                                              }
{ TChecksum32Hash                                                              }
{                                                                              }
Procedure TChecksum32Hash.InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer);
  Begin
    PLongWord (Digest)^ := 0;
  End;

Procedure TChecksum32Hash.ProcessBuf (const Buf; const BufSize : Integer);
  Begin
    PLongWord (FDigest)^ := PLongWord (FDigest)^ + CalcChecksum32 (Buf, BufSize);
  End;

class Function TChecksum32Hash.DigestSize : Integer;
  Begin
    Result := 4;
  End;



{                                                                              }
{ TXOR8Hash                                                                    }
{                                                                              }
Procedure TXOR8Hash.InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer);
  Begin
    PByte (Digest)^ := 0;
  End;

Procedure TXOR8Hash.ProcessBuf (const Buf; const BufSize : Integer);
  Begin
    PByte (FDigest)^ := PByte (FDigest)^ xor CalcXOR8 (Buf, BufSize);
  End;

class Function TXOR8Hash.DigestSize : Integer;
  Begin
    Result := 1;
  End;



{                                                                              }
{ TXOR16Hash                                                                   }
{                                                                              }
Procedure TXOR16Hash.InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer);
  Begin
    PWord (Digest)^ := 0;
  End;

Procedure TXOR16Hash.ProcessBuf (const Buf; const BufSize : Integer);
  Begin
    PWord (FDigest)^ := PWord (FDigest)^ xor CalcXOR16 (Buf, BufSize);
  End;

class Function TXOR16Hash.DigestSize : Integer;
  Begin
    Result := 2;
  End;



{                                                                              }
{ TXOR32Hash                                                                   }
{                                                                              }
Procedure TXOR32Hash.InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer);
  Begin
    PLongWord (Digest)^ := 0;
  End;

Procedure TXOR32Hash.ProcessBuf (const Buf; const BufSize : Integer);
  Begin
    PLongWord (FDigest)^ := PLongWord (FDigest)^ xor CalcXOR32 (Buf, BufSize);
  End;

class Function TXOR32Hash.DigestSize : Integer;
  Begin
    Result := 4;
  End;



{                                                                              }
{ TCRC16Hash                                                                   }
{                                                                              }
Procedure TCRC16Hash.InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer);
  Begin
    CRC16Init (PWord (Digest)^);
  End;

Procedure TCRC16Hash.ProcessBuf (const Buf; const BufSize : Integer);
  Begin
    PWord (FDigest)^ := CRC16Buf (PWord (FDigest)^, Buf, BufSize);
  End;

class Function TCRC16Hash.DigestSize : Integer;
  Begin
    Result := 2;
  End;



{                                                                              }
{ TCRC32Hash                                                                   }
{                                                                              }
Procedure TCRC32Hash.InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer);
  Begin
    CRC32Init (PLongWord (Digest)^);
  End;

Procedure TCRC32Hash.ProcessBuf (const Buf; const BufSize : Integer);
  Begin
    PLongWord (FDigest)^ := CRC32Buf (PLongWord (FDigest)^, Buf, BufSize);
  End;

class Function TCRC32Hash.DigestSize : Integer;
  Begin
    Result := 4;
  End;



{                                                                              }
{ TELFHash                                                                     }
{                                                                              }
Procedure TELFHash.InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer);
  Begin
    ELFInit (PLongWord (Digest)^);
  End;

Procedure TELFHash.ProcessBuf (const Buf; const BufSize : Integer);
  Begin
    PLongWord (FDigest)^ := ELFBuf (PLongWord (FDigest)^, Buf, BufSize);
  End;

class Function TELFHash.DigestSize : Integer;
  Begin
    Result := 4;
  End;



{                                                                              }
{ TMD5Hash                                                                     }
{                                                                              }
Procedure TMD5Hash.InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer);
  Begin
    MD5InitDigest (P128BitDigest (FDigest)^);
  End;

Procedure TMD5Hash.ProcessBuf (const Buf; const BufSize : Integer);
  Begin
    MD5Buf (P128BitDigest (FDigest)^, Buf, BufSize);
  End;

Procedure TMD5Hash.ProcessFinalBuf (const Buf; const BufSize : Integer; const TotalSize : Int64);
  Begin
    MD5FinalBuf (P128BitDigest (FDigest)^, Buf, BufSize, TotalSize);
  End;

class Function TMD5Hash.DigestSize : Integer;
  Begin
    Result := 16;
  End;



{                                                                              }
{ THMAC_MD5Hash                                                                }
{                                                                              }
Destructor THMAC_MD5Hash.Destroy;
  Begin
    SecureClearStr (FKey);
    inherited Destroy;
  End;

Procedure THMAC_MD5Hash.InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer);
  Begin
    HMAC_MD5Init (Key, KeySize, P128BitDigest (FDigest)^, FKey);
  End;

Procedure THMAC_MD5Hash.ProcessBuf (const Buf; const BufSize : Integer);
  Begin
    HMAC_MD5Buf (P128BitDigest (FDigest)^, Buf, BufSize);
  End;

Procedure THMAC_MD5Hash.ProcessFinalBuf (const Buf; const BufSize : Integer; const TotalSize : Int64);
  Begin
    HMAC_MD5FinalBuf (FKey, P128BitDigest (FDigest)^, Buf, BufSize, TotalSize);
  End;

class Function THMAC_MD5Hash.DigestSize : Integer;
  Begin
    Result := 16;
  End;



{                                                                              }
{ TSHA1Hash                                                                    }
{                                                                              }
Procedure TSHA1Hash.InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer);
  Begin
    SHA1InitDigest (P160BitDigest (FDigest)^);
  End;

Procedure TSHA1Hash.ProcessBuf (const Buf; const BufSize : Integer);
  Begin
    SHA1Buf (P160BitDigest (FDigest)^, Buf, BufSize);
  End;

Procedure TSHA1Hash.ProcessFinalBuf (const Buf; const BufSize : Integer; const TotalSize : Int64);
  Begin
    SHA1FinalBuf (P160BitDigest (FDigest)^, Buf, BufSize, TotalSize);
  End;

class Function TSHA1Hash.DigestSize : Integer;
  Begin
    Result := 20;
  End;



{                                                                              }
{ THMAC_SHA1Hash                                                               }
{                                                                              }
Destructor THMAC_SHA1Hash.Destroy;
  Begin
    SecureClearStr (FKey);
    inherited Destroy;
  End;

Procedure THMAC_SHA1Hash.InitHash (const Digest : Pointer; const Key : Pointer; const KeySize : Integer);
  Begin
    HMAC_SHA1Init (Key, KeySize, P160BitDigest (FDigest)^, FKey);
  End;

Procedure THMAC_SHA1Hash.ProcessBuf (const Buf; const BufSize : Integer);
  Begin
    HMAC_SHA1Buf (P160BitDigest (FDigest)^, Buf, BufSize);
  End;

Procedure THMAC_SHA1Hash.ProcessFinalBuf (const Buf; const BufSize : Integer; const TotalSize : Int64);
  Begin
    HMAC_SHA1FinalBuf (FKey, P160BitDigest (FDigest)^, Buf, BufSize, TotalSize);
  End;

class Function THMAC_SHA1Hash.DigestSize : Integer;
  Begin
    Result := 20;
  End;



{                                                                              }
{ HashString                                                                   }
{                                                                              }
Function HashString (const StrBuf : Pointer; const StrLength : Integer; const Slots : LongWord; const CaseSensitive : Boolean) : LongWord;
var P    : PChar;
    I, J : Integer;

  Procedure CRC32StrBuf (const Size : Integer);
    Begin
      if CaseSensitive then
        Result := CRC32Buf (Result, P^, Size) else
        Result := CRC32BufNoCase (Result, P^, Size);
    End;

  Begin
    // Return 0 for an empty string
    Result := 0;
    if (StrLength <= 0) or not Assigned (StrBuf) then
      exit;

    if not CRC32TableInit then
      InitCRC32Table;
    Result := $FFFFFFFF;
    P := StrBuf;

    if StrLength <= 48 then // Hash everything for short strings
      CRC32StrBuf (StrLength) else
      begin
        // Hash first 16 bytes
        CRC32StrBuf (16);

        // Hash last 16 bytes
        Inc (P, StrLength - 16);
        CRC32StrBuf (16);

        // Hash 16 bytes sampled from rest of string
        I := (StrLength - 48) div 16;
        P := StrBuf;
        Inc (P, 16);
        For J := 1 to 16 do
          begin
            CRC32StrBuf (1);
            Inc (P, I + 1);
          end;
      end;

    // Mod into slots
    if (Slots <> 0) and (Slots <> High (LongWord)) then
      Result := Result mod Slots;
  End;

Function HashString (const S : String; const Slots : LongWord; const CaseSensitive : Boolean) : LongWord;
  Begin
    Result := HashString (Pointer (S), Length (S), Slots, CaseSensitive);
  End;



{                                                                              }
{ Hash by THashType                                                            }
{                                                                              }
const
  HashTypeClasses : Array [THashType] of THashClass = (
      TChecksum32Hash, TXOR8Hash, TXOR16Hash, TXOR32Hash,
      TCRC16Hash, TCRC32Hash, TELFHash,
      TMD5Hash, TSHA1Hash,
      THMAC_MD5Hash, THMAC_SHA1Hash);

Function GetHashClassByType (const HashType : THashType) : THashClass;
  Begin
    Result := HashTypeClasses [HashType];
  End;

Function GetDigestSize (const HashType : THashType) : Integer;
  Begin
    Result := GetHashClassByType (HashType).DigestSize;
  End;

  

{                                                                              }
{ Self testing code                                                            }
{                                                                              }
{$IFDEF SELFTEST}
Procedure Test_Unit (const U : TUnit);
var S, T : String;
  Begin
    With U do
      begin
        TestID := 'Hash';

        AssertEq (CalcChecksum32 (''), 0, 'CalcChecksum32');
        AssertEq (CalcChecksum32 ('A'), 65, 'CalcChecksum32');
        AssertEq (CalcChecksum32 ('Delphi Fundamentals'), 1880, 'CalcChecksum32');

        AssertEq (CalcXOR8 (''), 0, 'CalcXOR8');
        AssertEq (CalcXOR8 ('A'), 65, 'CalcXOR8');
        AssertEq (CalcXOR8 ('Delphi Fundamentals'), 40, 'CalcXOR8');

        AssertEq (CalcXOR16 (''), 0, 'CalcXOR16');
        AssertEq (CalcXOR16 ('A'), 65, 'CalcXOR16');
        AssertEq (CalcXOR16 ('AB'), $4241, 'CalcXOR16');
        AssertEq (CalcXOR16 ('Delphi Fundamentals'), $4860, 'CalcXOR16');

        AssertEq (CalcXOR32 (''), 0, 'CalcXOR32');
        AssertEq (CalcXOR32 ('A'), 65, 'CalcXOR32');
        AssertEq (CalcXOR32 ('ABCD'), $44434241, 'CalcXOR32');
        AssertEq (CalcXOR32 ('Delphi Fundamentals'), $23356B55, 'CalcXOR32');

        AssertEq (CalcCRC16 (''), $FFFF, 'CalcCRC16');
        AssertEq (CalcCRC16 ('Delphi Fundamentals'), 18831, 'CalcCRC16');

        AssertEq (CalcCRC32 (''), 0, 'CalcCRC32');
        AssertEq (CalcCRC32 ('Delphi Fundamentals'), 3810393938, 'CalcCRC32');

        AssertEq (MD5DigestToHex (CalcMD5 ('')),                    'd41d8cd98f00b204e9800998ecf8427e', 'CalcMD5');
        AssertEq (MD5DigestToHex (CalcMD5 ('Delphi Fundamentals')), 'ea98b65da23d19756d46a36faa481dd8', 'CalcMD5');

        AssertEq (SHA1DigestToHex (CalcSHA1 ('')),                    'da39a3ee5e6b4b0d3255bfef95601890afd80709', 'CalcSHA1');
        AssertEq (SHA1DigestToHex (CalcSHA1 ('Delphi Fundamentals')), '6c412217909d2767d36a6bbeab5e50e14b19b941', 'CalcSHA1');

        AssertEq (MD5DigestToHex (CalcHMAC_MD5 ('', '')),                    '74e6f7298a9c2d168935f58c001bad88', 'CalcHMAC_MD5');
        AssertEq (MD5DigestToHex (CalcHMAC_MD5 ('', 'Delphi Fundamentals')), 'b9da02d5f94bd6eac410708a72b05d9f', 'CalcHMAC_MD5');
        AssertEq (MD5DigestToHex (CalcHMAC_MD5 ('Delphi Fundamentals', '')), 'a09f3300c236156d27f4d031db7e91ce', 'CalcHMAC_MD5');
        AssertEq (MD5DigestToHex (CalcHMAC_MD5 ('Delphi', 'Fundamentals')),  '1c4e8a481c2c781eb43ca58d9324c37d', 'CalcHMAC_MD5');

        AssertEq (SHA1DigestToHex (CalcHMAC_SHA1 ('', '')),                    'fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', 'CalcHMAC_SHA1');
        AssertEq (SHA1DigestToHex (CalcHMAC_SHA1 ('', 'Delphi Fundamentals')), '62f9196071f587cde151d8b99919ed0f6e51bf26', 'CalcHMAC_SHA1');
        AssertEq (SHA1DigestToHex (CalcHMAC_SHA1 ('Delphi Fundamentals', '')), 'e4dbfa59f410ee75c368c1ba6df1a2c701e0cea0', 'CalcHMAC_SHA1');
        AssertEq (SHA1DigestToHex (CalcHMAC_SHA1 ('Delphi', 'Fundamentals')),  'fa96341a0b790f3a6f3248b7053372ede8d41e7c', 'CalcHMAC_SHA1');

        AssertEq (HashString ('Delphi Fundamentals', 0, False),
                  HashString ('dELPHI fundamentalS', 0, False), 'HashString');
        AssertEq (HashString ('Delphi Fundamentals', 0, True), 484573357);

        AssertEq (IsValidISBN ('3880530025'), True, 'ISBN');

        AssertEq (IsValidLUHN ('49927398716'), True, 'ISBN');

        // Test cases from RFC 2202
        AssertEq (MD5DigestToHex (CalcHMAC_MD5 ('Jefe', 'what do ya want for nothing?')), '750c783e6ab0b503eaa86e310a5db738', 'CalcHMAC_MD5');
        SetLength (S, 16); FillChar (Pointer (S)^, 16, #$0B);
        AssertEq (MD5DigestToHex (CalcHMAC_MD5 (S, 'Hi There')), '9294727a3638bb1c13f48ef8158bfc9d', 'CalcHMAC_MD5');
        SetLength (S, 16); FillChar (Pointer (S)^, 16, #$AA);
        SetLength (T, 50); FillChar (Pointer (T)^, 50, #$DD);
        AssertEq (MD5DigestToHex (CalcHMAC_MD5 (S, T)), '56be34521d144c88dbb8c733f0e8b3f6', 'CalcHMAC_MD5');
        SetLength (S, 80); FillChar (Pointer (S)^, 80, #$AA);
        AssertEq (MD5DigestToHex (CalcHMAC_MD5 (S, 'Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data')), '6f630fad67cda0ee1fb1f562db3aa53e', 'CalcHMAC_MD5');

        AssertEq (SHA1DigestToHex (CalcHMAC_SHA1 ('Jefe', 'what do ya want for nothing?')), 'effcdf6ae5eb2fa2d27416d5f184df9c259a7c79', 'CalcHMAC_SHA1');
        SetLength (S, 20); FillChar (Pointer (S)^, 20, #$0B);
        AssertEq (SHA1DigestToHex (CalcHMAC_SHA1 (S, 'Hi There')), 'b617318655057264e28bc0b6fb378c8ef146be00', 'CalcHMAC_SHA1');
        SetLength (S, 80); FillChar (Pointer (S)^, 80, #$AA);
        AssertEq (SHA1DigestToHex (CalcHMAC_SHA1 (S, 'Test Using Larger Than Block-Size Key - Hash Key First')), 'aa4ae5e15272d00e95705637ce8a3b55ed402112', 'CalcHMAC_SHA1');
      end;
  End;
{$ENDIF}



end.