IdHash.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.10 7/24/04 12:54:32 PM RLebeau
  18. Compiler fix for TIdHash128.HashValue()
  19. Rev 1.9 7/23/04 7:09:12 PM RLebeau
  20. Added extra exception handling to various HashValue() methods
  21. Rev 1.8 2004.05.20 11:37:06 AM czhower
  22. IdStreamVCL
  23. Rev 1.7 2004.03.03 11:54:30 AM czhower
  24. IdStream change
  25. Rev 1.6 2004.02.03 5:44:48 PM czhower
  26. Name changes
  27. Rev 1.5 1/27/2004 4:00:08 PM SPerry
  28. StringStream ->IdStringStream
  29. Rev 1.4 11/10/2003 7:39:22 PM BGooijen
  30. Did all todo's ( TStream to TIdStream mainly )
  31. Rev 1.3 2003.10.24 10:43:08 AM czhower
  32. TIdSTream to dos
  33. Rev 1.2 10/18/2003 4:28:30 PM BGooijen
  34. Removed the pchar for DotNet
  35. Rev 1.1 10/8/2003 10:15:10 PM GGrieve
  36. replace TIdReadMemoryStream (might be fast, but not compatible with DotNet)
  37. Rev 1.0 11/13/2002 08:30:24 AM JPMugaas
  38. Initial import from FTP VC.
  39. }
  40. unit IdHash;
  41. interface
  42. {$i IdCompilerDefines.inc}
  43. uses
  44. Classes,
  45. IdFIPS,
  46. IdGlobal;
  47. type
  48. TIdHash = class(TObject)
  49. protected
  50. function GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; virtual; abstract;
  51. function HashToHex(const AHash: TIdBytes): String; virtual; abstract;
  52. function WordHashToHex(const AHash: TIdBytes; const ACount: Integer): String;
  53. function LongWordHashToHex(const AHash: TIdBytes; const ACount: Integer): String;
  54. public
  55. constructor Create; virtual;
  56. class function IsAvailable : Boolean; virtual;
  57. function HashString(const ASrc: string; ADestEncoding: IIdTextEncoding = nil{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}): TIdBytes;
  58. function HashStringAsHex(const AStr: String; ADestEncoding: IIdTextEncoding = nil{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}): String;
  59. function HashBytes(const ASrc: TIdBytes): TIdBytes;
  60. function HashBytesAsHex(const ASrc: TIdBytes): String;
  61. function HashStream(AStream: TStream): TIdBytes; overload;
  62. function HashStreamAsHex(AStream: TStream): String; overload;
  63. function HashStream(AStream: TStream; const AStartPos, ASize: TIdStreamSize): TIdBytes; overload;
  64. function HashStreamAsHex(AStream: TStream; const AStartPos, ASize: TIdStreamSize): String; overload;
  65. end;
  66. TIdHash16 = class(TIdHash)
  67. protected
  68. function GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; override;
  69. function HashToHex(const AHash: TIdBytes): String; override;
  70. public
  71. function HashValue(const ASrc: string; ADestEncoding: IIdTextEncoding = nil{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}): UInt16; overload;
  72. function HashValue(const ASrc: TIdBytes): UInt16; overload;
  73. function HashValue(AStream: TStream): UInt16; overload;
  74. function HashValue(AStream: TStream; const AStartPos, ASize: TIdStreamSize): UInt16; overload;
  75. procedure HashStart(var VRunningHash : UInt16); virtual; abstract;
  76. procedure HashEnd(var VRunningHash : UInt16); virtual;
  77. procedure HashByte(var VRunningHash : UInt16; const AByte : Byte); virtual; abstract;
  78. end;
  79. TIdHash32 = class(TIdHash)
  80. protected
  81. function GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; override;
  82. function HashToHex(const AHash: TIdBytes): String; override;
  83. public
  84. function HashValue(const ASrc: string; ADestEncoding: IIdTextEncoding = nil{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}): UInt32; overload;
  85. function HashValue(const ASrc: TIdBytes): UInt32; overload;
  86. function HashValue(AStream: TStream): UInt32; overload;
  87. function HashValue(AStream: TStream; const AStartPos, ASize: TIdStreamSize): UInt32; overload;
  88. procedure HashStart(var VRunningHash : UInt32); virtual; abstract;
  89. procedure HashEnd(var VRunningHash : UInt32); virtual;
  90. procedure HashByte(var VRunningHash : UInt32; const AByte : Byte); virtual; abstract;
  91. end;
  92. TIdHashClass = class of TIdHash;
  93. TIdHashIntF = class(TIdHash)
  94. protected
  95. function HashToHex(const AHash: TIdBytes): String; override;
  96. function InitHash : TIdHashIntCtx; virtual; abstract;
  97. procedure UpdateHash(ACtx : TIdHashIntCtx; const AIn : TIdBytes);
  98. function FinalHash(ACtx : TIdHashIntCtx) : TIdBytes;
  99. function GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; override;
  100. public
  101. {$IFNDEF DOTNET}
  102. constructor Create; override;
  103. {$ENDIF}
  104. class function IsAvailable : Boolean; override;
  105. class function IsIntfAvailable : Boolean; virtual;
  106. end;
  107. TIdHashNativeAndIntF = class(TIdHashIntF)
  108. protected
  109. function NativeGetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; virtual;
  110. function GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; override;
  111. end;
  112. {$IFDEF DOTNET}
  113. EIdSecurityAPIException = class(EIdException);
  114. EIdSHA224NotSupported = class(EIdSecurityAPIException);
  115. {$ENDIF}
  116. function HashFunctionsLoaded: Boolean;
  117. implementation
  118. uses
  119. {$IFDEF DOTNET}
  120. IdStreamNET,
  121. {$ELSE}
  122. IdStreamVCL,
  123. {$ENDIF}
  124. IdGlobalProtocols, SysUtils;
  125. {$IFDEF DOTNET}
  126. function HashFunctionsLoaded : Boolean;
  127. {$IFDEF USE_INLINE} inline; {$ENDIF}
  128. begin
  129. Result := True;
  130. end;
  131. {$ELSE}
  132. function HashFunctionsLoaded : Boolean;
  133. begin
  134. Result := LoadHashLibrary;
  135. if Result then begin
  136. Result := IsHashingIntfAvail;
  137. end;
  138. end;
  139. {$ENDIF}
  140. { TIdHash }
  141. constructor TIdHash.Create;
  142. begin
  143. inherited Create;
  144. end;
  145. function TIdHash.HashString(const ASrc: string; ADestEncoding: IIdTextEncoding = nil
  146. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  147. ): TIdBytes;
  148. var
  149. LStream: TStream; // not TIdStringStream - Unicode on DotNet!
  150. begin
  151. LStream := TMemoryStream.Create; try
  152. WriteStringToStream(LStream, ASrc, ADestEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF});
  153. LStream.Position := 0;
  154. Result := HashStream(LStream);
  155. finally FreeAndNil(LStream); end;
  156. end;
  157. function TIdHash.HashStringAsHex(const AStr: String; ADestEncoding: IIdTextEncoding = nil
  158. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  159. ): String;
  160. begin
  161. Result := HashToHex(HashString(AStr, ADestEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}));
  162. end;
  163. function TIdHash.HashBytes(const ASrc: TIdBytes): TIdBytes;
  164. var
  165. LStream: TStream;
  166. begin
  167. // TODO: use TBytesStream on versions that support it
  168. LStream := TMemoryStream.Create; try
  169. WriteTIdBytesToStream(LStream, ASrc);
  170. LStream.Position := 0;
  171. Result := HashStream(LStream);
  172. finally FreeAndNil(LStream); end;
  173. end;
  174. function TIdHash.HashBytesAsHex(const ASrc: TIdBytes): String;
  175. begin
  176. Result := HashToHex(HashBytes(ASrc));
  177. end;
  178. function TIdHash.HashStream(AStream: TStream): TIdBytes;
  179. begin
  180. Result := HashStream(AStream, -1, -1);
  181. end;
  182. function TIdHash.HashStreamAsHex(AStream: TStream): String;
  183. begin
  184. Result := HashToHex(HashStream(AStream));
  185. end;
  186. function TIdHash.HashStream(AStream: TStream; const AStartPos, ASize: TIdStreamSize): TIdBytes;
  187. var
  188. LSize, LAvailable: TIdStreamSize;
  189. begin
  190. if AStartPos >= 0 then begin
  191. AStream.Position := AStartPos;
  192. end;
  193. LAvailable := AStream.Size - AStream.Position;
  194. if ASize < 0 then begin
  195. LSize := LAvailable;
  196. end else begin
  197. LSize := IndyMin(LAvailable, ASize);
  198. end;
  199. Result := GetHashBytes(AStream, LSize);
  200. end;
  201. function TIdHash.HashStreamAsHex(AStream: TStream; const AStartPos, ASize: TIdStreamSize): String;
  202. begin
  203. Result := HashToHex(HashStream(AStream, AStartPos, ASize));
  204. end;
  205. function TIdHash.WordHashToHex(const AHash: TIdBytes; const ACount: Integer): String;
  206. var
  207. LValue: UInt16;
  208. I: Integer;
  209. begin
  210. Result := '';
  211. for I := 0 to ACount-1 do begin
  212. LValue := BytesToUInt16(AHash, SizeOf(UInt16)*I);
  213. Result := Result + IntToHex(LValue, 4);
  214. end;
  215. end;
  216. function TIdHash.LongWordHashToHex(const AHash: TIdBytes; const ACount: Integer): String;
  217. begin
  218. Result := ToHex(AHash, ACount*SizeOf(UInt32));
  219. end;
  220. class function TIdHash.IsAvailable : Boolean;
  221. begin
  222. Result := True;
  223. end;
  224. { TIdHash16 }
  225. function TIdHash16.GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes;
  226. const
  227. cBufSize = 1024; // Keep it small for dotNet
  228. var
  229. I: Integer;
  230. LBuffer: TIdBytes;
  231. LSize: Integer;
  232. LHash: UInt16;
  233. begin
  234. Result := nil;
  235. HashStart(LHash);
  236. SetLength(LBuffer, cBufSize);
  237. while ASize > 0 do
  238. begin
  239. LSize := ReadTIdBytesFromStream(AStream, LBuffer, IndyMin(cBufSize, ASize));
  240. if LSize < 1 then begin
  241. Break; // TODO: throw a stream read exception instead?
  242. end;
  243. for i := 0 to LSize - 1 do begin
  244. HashByte(LHash, LBuffer[i]);
  245. end;
  246. Dec(ASize, LSize);
  247. end;
  248. HashEnd(LHash);
  249. SetLength(Result, SizeOf(UInt16));
  250. CopyTIdUInt16(LHash, Result, 0);
  251. end;
  252. function TIdHash16.HashToHex(const AHash: TIdBytes): String;
  253. begin
  254. Result := IntToHex(BytesToUInt16(AHash), 4);
  255. end;
  256. procedure TIdHash16.HashEnd(var VRunningHash : UInt16);
  257. begin
  258. end;
  259. function TIdHash16.HashValue(const ASrc: string; ADestEncoding: IIdTextEncoding = nil
  260. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  261. ): UInt16;
  262. begin
  263. Result := BytesToUInt16(HashString(ASrc, ADestEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}));
  264. end;
  265. function TIdHash16.HashValue(const ASrc: TIdBytes): UInt16;
  266. begin
  267. Result := BytesToUInt16(HashBytes(ASrc));
  268. end;
  269. function TIdHash16.HashValue(AStream: TStream): UInt16;
  270. begin
  271. Result := BytesToUInt16(HashStream(AStream));
  272. end;
  273. function TIdHash16.HashValue(AStream: TStream; const AStartPos, ASize: TIdStreamSize): UInt16;
  274. begin
  275. Result := BytesToUInt16(HashStream(AStream, AStartPos, ASize));
  276. end;
  277. { TIdHash32 }
  278. function TIdHash32.GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes;
  279. const
  280. cBufSize = 1024; // Keep it small for dotNet
  281. var
  282. I: Integer;
  283. LBuffer: TIdBytes;
  284. LSize: Integer;
  285. LHash: UInt32;
  286. begin
  287. Result := nil;
  288. HashStart(LHash);
  289. SetLength(LBuffer, cBufSize);
  290. while ASize > 0 do
  291. begin
  292. LSize := ReadTIdBytesFromStream(AStream, LBuffer, IndyMin(cBufSize, ASize));
  293. if LSize < 1 then begin
  294. Break; // TODO: throw a stream read exception instead?
  295. end;
  296. for i := 0 to LSize - 1 do begin
  297. HashByte(LHash, LBuffer[i]);
  298. end;
  299. Dec(ASize, LSize);
  300. end;
  301. HashEnd(LHash); // RLebeau: TIdHashCRC32 uses this to XOR the hash with $FFFFFFFF
  302. SetLength(Result, SizeOf(UInt32));
  303. CopyTIdUInt32(LHash, Result, 0);
  304. end;
  305. function TIdHash32.HashToHex(const AHash: TIdBytes): String;
  306. begin
  307. Result := UInt32ToHex(BytesToUInt32(AHash));
  308. end;
  309. procedure TIdHash32.HashEnd(var VRunningHash : UInt32);
  310. begin
  311. end;
  312. function TIdHash32.HashValue(const ASrc: string; ADestEncoding: IIdTextEncoding = nil
  313. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  314. ): UInt32;
  315. begin
  316. Result := BytesToUInt32(HashString(ASrc, ADestEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}));
  317. end;
  318. function TIdHash32.HashValue(const ASrc: TIdBytes): UInt32;
  319. begin
  320. Result := BytesToUInt32(HashBytes(ASrc));
  321. end;
  322. function TIdHash32.HashValue(AStream: TStream) : UInt32;
  323. begin
  324. Result := BytesToUInt32(HashStream(AStream));
  325. end;
  326. function TIdHash32.HashValue(AStream: TStream; const AStartPos, ASize: TIdStreamSize) : UInt32;
  327. begin
  328. Result := BytesToUInt32(HashStream(AStream, AStartPos, ASize));
  329. end;
  330. { TIdHashIntf }
  331. {$IFNDEF DOTNET}
  332. constructor TIdHashIntf.Create;
  333. begin
  334. inherited;
  335. // not checking for load failure here, in case the library
  336. // is not available but a native implementation is...
  337. LoadHashLibrary;
  338. end;
  339. {$ENDIF}
  340. function TIdHashIntf.FinalHash(ACtx: TIdHashIntCtx): TIdBytes;
  341. {$IFDEF DOTNET}
  342. var
  343. LDummy : TIdBytes;
  344. {$ENDIF}
  345. begin
  346. {$IFDEF DOTNET}
  347. //This is a funny way of coding. I have to pass a dummy value to
  348. //TransformFinalBlock so that things can work similarly to the OpenSSL
  349. //Crypto API. You can't pass nul to TransformFinalBlock without an exception.
  350. SetLength(LDummy,0);
  351. ACtx.TransformFinalBlock(LDummy,0,0);
  352. Result := ACtx.Hash;
  353. {$ELSE}
  354. Result := IdFIPS.FinalHashInst(ACtx);
  355. {$ENDIF}
  356. end;
  357. function TIdHashIntf.GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes;
  358. var
  359. LBuf : TIdBytes;
  360. LSize : Int64;
  361. LCtx : TIdHashIntCtx;
  362. begin
  363. LCtx := InitHash;
  364. try
  365. if ASize > 0 then begin
  366. SetLength(LBuf, 2048);
  367. repeat
  368. LSize := ReadTIdBytesFromStream(AStream,LBuf,IndyMin(ASize, 2048));
  369. if LSize < 1 then begin
  370. break; // TODO: throw a stream read exception?
  371. end;
  372. if LSize < 2048 then begin
  373. SetLength(LBuf,LSize);
  374. UpdateHash(LCtx,LBuf);
  375. break;
  376. end;
  377. UpdateHash(LCtx,LBuf);
  378. Dec(ASize, LSize);
  379. until ASize = 0;
  380. end;
  381. finally
  382. Result := FinalHash(LCtx);
  383. end;
  384. end;
  385. function TIdHashIntf.HashToHex(const AHash: TIdBytes): String;
  386. begin
  387. Result := ToHex(AHash);
  388. end;
  389. {$IFDEF DOTNET}
  390. class function TIdHashIntf.IsAvailable: Boolean;
  391. begin
  392. Result := True;
  393. end;
  394. class function TIdHashIntF.IsIntfAvailable: Boolean;
  395. begin
  396. Result := False;
  397. end;
  398. {$ELSE}
  399. //done this way so we can override IsAvailble if there is a native
  400. //implementation.
  401. class function TIdHashIntf.IsAvailable: Boolean;
  402. begin
  403. Result := IsIntfAvailable;
  404. end;
  405. class function TIdHashIntF.IsIntfAvailable: Boolean;
  406. begin
  407. Result := IsHashingIntfAvail;
  408. end;
  409. {$ENDIF}
  410. procedure TIdHashIntf.UpdateHash(ACtx: TIdHashIntCtx; const AIn: TIdBytes);
  411. begin
  412. UpdateHashInst(ACtx,AIn);
  413. {$IFDEF DOTNET}
  414. ACtx.TransformBlock(AIn,0,Length(AIn),AIn,0);
  415. {$ELSE}
  416. {$ENDIF}
  417. end;
  418. { TIdHashNativeAndIntF }
  419. function TIdHashNativeAndIntF.GetHashBytes(AStream: TStream;
  420. ASize: TIdStreamSize): TIdBytes;
  421. begin
  422. if IsIntfAvailable then begin
  423. Result := inherited GetHashBytes(AStream, ASize);
  424. end else begin
  425. Result := NativeGetHashBytes(AStream, ASize);
  426. end;
  427. end;
  428. function TIdHashNativeAndIntF.NativeGetHashBytes(AStream: TStream;
  429. ASize: TIdStreamSize): TIdBytes;
  430. begin
  431. Result := nil;
  432. end;
  433. end.