IdHashSHA.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  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.6 2003-10-12 15:25:50 HHellström
  18. Comments added
  19. Rev 1.5 2003-10-12 03:08:24 HHellström
  20. New implementation; copyright changed. The source code formatting has been
  21. adjusted to fit the margins. The new implementation is faster on dotNet
  22. compared to the old one, but is slightly slower on Win32.
  23. Rev 1.4 2003-10-11 18:44:54 HHellström
  24. Range checking and overflow checking disabled in the Coder method only. The
  25. purpose of this setting is to force the arithmetic operations performed on
  26. LongWord variables to be modulo $100000000. This hack entails reasonable
  27. performance on both Win32 and dotNet.
  28. Rev 1.3 10/10/2003 2:20:56 PM GGrieve
  29. turn range checking off
  30. Rev 1.2 2003-09-21 17:31:02 HHellström Version: 1.2
  31. DotNET compatibility
  32. Rev 1.1 2/16/2003 03:19:18 PM JPMugaas
  33. Should now compile on D7 better.
  34. Rev 1.0 11/13/2002 07:53:48 AM JPMugaas
  35. }
  36. unit IdHashSHA;
  37. interface
  38. {$i IdCompilerDefines.inc}
  39. uses
  40. Classes,
  41. IdFIPS,
  42. IdGlobal, IdHash;
  43. {
  44. Microsoft.NET notes!!!!
  45. In Microsoft.NET, there are some limitations that you need to be aware of.
  46. 1) In Microsoft.NET 1.1, 2.0, and 3.0, only the CryptoService SHA1 class is
  47. FIPS-complient. Unfortunately, SHA1 will not be permitted after 2010.
  48. 2) In Microsoft.NET 3.5,There are more classes ending in CryptoServiceProvider" or
  49. "Cng" that are complient.
  50. 3) SHA224 is not exposed.
  51. }
  52. type
  53. T5x4LongWordRecord = array[0..4] of UInt32;
  54. T512BitRecord = array [0..63] of Byte;
  55. {$IFNDEF DOTNET}
  56. TIdHashSHA1 = class(TIdHashNativeAndIntF)
  57. {$ELSE}
  58. TIdHashSHA1 = class(TIdHashIntF)
  59. {$ENDIF}
  60. protected
  61. {$IFNDEF DOTNET}
  62. FCheckSum: T5x4LongWordRecord;
  63. FCBuffer: TIdBytes;
  64. procedure Coder;
  65. function NativeGetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; override;
  66. function HashToHex(const AHash: TIdBytes): String; override;
  67. {$ENDIF}
  68. function InitHash : TIdHashIntCtx; override;
  69. public
  70. {$IFNDEF DOTNET}
  71. constructor Create; override;
  72. {$ENDIF}
  73. class function IsAvailable : Boolean; override;
  74. class function IsIntfAvailable: Boolean; override;
  75. end;
  76. {$IFNDEF DOTNET}
  77. TIdHashSHA224 = class(TIdHashIntF)
  78. protected
  79. function InitHash : TIdHashIntCtx; override;
  80. public
  81. class function IsAvailable : Boolean; override;
  82. end;
  83. {$ENDIF}
  84. TIdHashSHA256 = class(TIdHashIntF)
  85. protected
  86. function InitHash : TIdHashIntCtx; override;
  87. public
  88. class function IsAvailable : Boolean; override;
  89. end;
  90. TIdHashSHA384 = class(TIdHashIntF)
  91. protected
  92. function InitHash : TIdHashIntCtx; override;
  93. public
  94. class function IsAvailable : Boolean; override;
  95. end;
  96. TIdHashSHA512 = class(TIdHashIntF)
  97. protected
  98. function InitHash : TIdHashIntCtx; override;
  99. public
  100. class function IsAvailable : Boolean; override;
  101. end;
  102. implementation
  103. { TIdHashSHA1 }
  104. {$IFDEF DOTNET}
  105. function TIdHashSHA1.GetHashInst : TIdHashInst;
  106. begin
  107. //You can not use SHA256Managed for FIPS complience.
  108. Result := System.Security.Cryptography.SHA1CryptoServiceProvider.Create;
  109. end;
  110. class function TIdHashSHA1.IsIntfAvailable : Boolean;
  111. begin
  112. Result := True;
  113. end;
  114. {$ELSE}
  115. function SwapLongWord(const AValue: UInt32): UInt32;
  116. begin
  117. Result := ((AValue and $FF) shl 24) or ((AValue and $FF00) shl 8) or ((AValue and $FF0000) shr 8) or ((AValue and $FF000000) shr 24);
  118. end;
  119. constructor TIdHashSHA1.Create;
  120. begin
  121. inherited Create;
  122. SetLength(FCBuffer, 64);
  123. end;
  124. function TIdHashSHA1.InitHash: TIdHashIntCtx;
  125. begin
  126. Result := GetSHA1HashInst;
  127. end;
  128. class function TIdHashSHA1.IsIntfAvailable: Boolean;
  129. begin
  130. Result := (inherited IsIntfAvailable) and IsSHA1HashIntfAvail;
  131. end;
  132. {$i IdOverflowCheckingOff.inc} // Operations performed modulo $100000000
  133. {$i IdRangeCheckingOff.inc}
  134. procedure TIdHashSHA1.Coder;
  135. var
  136. T, A, B, C, D, E: UInt32;
  137. { The size of the W variable has been reduced to make the Coder method
  138. consume less memory on dotNet. This change has been tested with the v1.1
  139. framework and entails a general increase of performance by >50%. }
  140. W: array [0..19] of UInt32;
  141. i: UInt32;
  142. begin
  143. { The first 16 W values are identical to the input block with endian
  144. conversion. }
  145. for i := 0 to 15 do
  146. begin
  147. W[i]:= (FCBuffer[i*4] shl 24) or
  148. (FCBuffer[i*4+1] shl 16) or
  149. (FCBuffer[i*4+2] shl 8) or
  150. FCBuffer[i*4+3];
  151. end;
  152. { In normal x86 code all of the remaining 64 W values would be calculated
  153. here. Here only the four next values are calculated, to reduce the code
  154. size of the first of the four loops below. }
  155. for i := 16 to 19 do
  156. begin
  157. T := W[i-3] xor W[i-8] xor W[i-14] xor W[i-16];
  158. W[i] := (T shl 1) or (T shr 31);
  159. end;
  160. A := FCheckSum[0];
  161. B := FCheckSum[1];
  162. C := FCheckSum[2];
  163. D := FCheckSum[3];
  164. E := FCheckSum[4];
  165. { The following loop could be expanded, but has been kept together to reduce
  166. the code size. A small code size entails better performance due to CPU
  167. caching.
  168. Note that the code size could be reduced further by using the SHA-1
  169. reference code:
  170. for i := 0 to 19 do begin
  171. T := E + (A shl 5) + (A shr 27) + (D xor (B and (C xor D))) + W[i];
  172. Inc(T,$5A827999);
  173. E := D;
  174. D := C;
  175. C := (B shl 30) + (B shr 2);
  176. B := A;
  177. A := T;
  178. end;
  179. The reference code is usually (at least partly) expanded, mostly because
  180. the assignments that circle the state variables A, B, C, D and E are costly,
  181. in particular on dotNET. (In x86 code further optimization can be achieved
  182. by eliminating the loop variable, which occupies a CPU register that is
  183. better used by one of the state variables, plus by expanding the W array
  184. at the beginning.) }
  185. i := 0;
  186. repeat
  187. Inc(E,(A shl 5) + (A shr 27) + (D xor (B and (C xor D))) + W[i+0]);
  188. Inc(E,$5A827999);
  189. B := (B shl 30) + (B shr 2);
  190. Inc(D,(E shl 5) + (E shr 27) + (C xor (A and (B xor C))) + W[i+1]);
  191. Inc(D,$5A827999);
  192. A := (A shl 30) + (A shr 2);
  193. Inc(C,(D shl 5) + (D shr 27) + (B xor (E and (A xor B))) + W[i+2]);
  194. Inc(C,$5A827999);
  195. E := (E shl 30) + (E shr 2);
  196. Inc(B,(C shl 5) + (C shr 27) + (A xor (D and (E xor A))) + W[i+3]);
  197. Inc(B,$5A827999);
  198. D := (D shl 30) + (D shr 2);
  199. Inc(A,(B shl 5) + (B shr 27) + (E xor (C and (D xor E))) + W[i+4]);
  200. Inc(A,$5A827999);
  201. C := (C shl 30) + (C shr 2);
  202. Inc(i,5);
  203. until i = 20;
  204. { The following three loops will only use the first 16 elements of the W
  205. array in a circular, recursive pattern. The following assignments are a
  206. trade-off to avoid having to split up the first loop. }
  207. W[0] := W[16];
  208. W[1] := W[17];
  209. W[2] := W[18];
  210. W[3] := W[19];
  211. { In the following three loops the recursive W array expansion is performed
  212. "just in time" following a circular pattern. Using circular indicies (e.g.
  213. (i+2) and $F) is not free, but the cost of declaring a large W array would
  214. be higher on dotNET. Before attempting to optimize this code, please note
  215. that the following language features are also costly:
  216. * Assignments and moves/copies, in particular on dotNET
  217. * Constant lookup tables, in particular on dotNET
  218. * Sub functions, in particular on x86
  219. * if..then and case..of. }
  220. i := 20;
  221. repeat
  222. T := W[(i+13) and $F] xor W[(i+8) and $F];
  223. T := T xor W[(i+2) and $F] xor W[i and $F];
  224. T := (T shl 1) or (T shr 31);
  225. W[i and $F] := T;
  226. Inc(E,(A shl 5) + (A shr 27) + (B xor C xor D) + T + $6ED9EBA1);
  227. B := (B shl 30) + (B shr 2);
  228. T := W[(i+14) and $F] xor W[(i+9) and $F];
  229. T := T xor W[(i+3) and $F] xor W[(i+1) and $F];
  230. T := (T shl 1) or (T shr 31);
  231. W[(i+1) and $F] := T;
  232. Inc(D,(E shl 5) + (E shr 27) + (A xor B xor C) + T + $6ED9EBA1);
  233. A := (A shl 30) + (A shr 2);
  234. T := W[(i+15) and $F] xor W[(i+10) and $F];
  235. T := T xor W[(i+4) and $F] xor W[(i+2) and $F];
  236. T := (T shl 1) or (T shr 31);
  237. W[(i+2) and $F] := T;
  238. Inc(C,(D shl 5) + (D shr 27) + (E xor A xor B) + T + $6ED9EBA1);
  239. E := (E shl 30) + (E shr 2);
  240. T := W[i and $F] xor W[(i+11) and $F];
  241. T := T xor W[(i+5) and $F] xor W[(i+3) and $F];
  242. T := (T shl 1) or (T shr 31);
  243. W[(i+3) and $F] := T;
  244. Inc(B,(C shl 5) + (C shr 27) + (D xor E xor A) + T + $6ED9EBA1);
  245. D := (D shl 30) + (D shr 2);
  246. T := W[(i+1) and $F] xor W[(i+12) and $F];
  247. T := T xor W[(i+6) and $F] xor W[(i+4) and $F];
  248. T := (T shl 1) or (T shr 31);
  249. W[(i+4) and $F] := T;
  250. Inc(A,(B shl 5) + (B shr 27) + (C xor D xor E) + T + $6ED9EBA1);
  251. C := (C shl 30) + (C shr 2);
  252. Inc(i,5);
  253. until i = 40;
  254. { Note that the constant $70E44324 = $100000000 - $8F1BBCDC has been selected
  255. to slightly reduce the probability that the CPU flag C (Carry) is set. This
  256. trick is taken from the StreamSec(R) StrSecII(TM) implementation of SHA-1.
  257. It entails a marginal but measurable performance gain on some CPUs. }
  258. i := 40;
  259. repeat
  260. T := W[(i+13) and $F] xor W[(i+8) and $F];
  261. T := T xor W[(i+2) and $F] xor W[i and $F];
  262. T := (T shl 1) or (T shr 31);
  263. W[i and $F] := T;
  264. Inc(E,(A shl 5) + (A shr 27) + ((B and C) or (D and (B or C))) + T);
  265. Dec(E,$70E44324);
  266. B := (B shl 30) + (B shr 2);
  267. T := W[(i+14) and $F] xor W[(i+9) and $F];
  268. T := T xor W[(i+3) and $F] xor W[(i+1) and $F];
  269. T := (T shl 1) or (T shr 31);
  270. W[(i+1) and $F] := T;
  271. Inc(D,(E shl 5) + (E shr 27) + ((A and B) or (C and (A or B))) + T);
  272. Dec(D,$70E44324);
  273. A := (A shl 30) + (A shr 2);
  274. T := W[(i+15) and $F] xor W[(i+10) and $F];
  275. T := T xor W[(i+4) and $F] xor W[(i+2) and $F];
  276. T := (T shl 1) or (T shr 31);
  277. W[(i+2) and $F] := T;
  278. Inc(C,(D shl 5) + (D shr 27) + ((E and A) or (B and (E or A))) + T);
  279. Dec(C,$70E44324);
  280. E := (E shl 30) + (E shr 2);
  281. T := W[i and $F] xor W[(i+11) and $F];
  282. T := T xor W[(i+5) and $F] xor W[(i+3) and $F];
  283. T := (T shl 1) or (T shr 31);
  284. W[(i+3) and $F] := T;
  285. Inc(B,(C shl 5) + (C shr 27) + ((D and E) or (A and (D or E))) + T);
  286. Dec(B,$70E44324);
  287. D := (D shl 30) + (D shr 2);
  288. T := W[(i+1) and $F] xor W[(i+12) and $F];
  289. T := T xor W[(i+6) and $F] xor W[(i+4) and $F];
  290. T := (T shl 1) or (T shr 31);
  291. W[(i+4) and $F] := T;
  292. Inc(A,(B shl 5) + (B shr 27) + ((C and D) or (E and (C or D))) + T);
  293. Dec(A,$70E44324);
  294. C := (C shl 30) + (C shr 2);
  295. Inc(i,5);
  296. until i = 60;
  297. { Note that the constant $359D3E2A = $100000000 - $CA62C1D6 has been selected
  298. to slightly reduce the probability that the CPU flag C (Carry) is set. This
  299. trick is taken from the StreamSec(R) StrSecII(TM) implementation of SHA-1.
  300. It entails a marginal but measurable performance gain on some CPUs. }
  301. repeat
  302. T := W[(i+13) and $F] xor W[(i+8) and $F];
  303. T := T xor W[(i+2) and $F] xor W[i and $F];
  304. T := (T shl 1) or (T shr 31);
  305. W[i and $F] := T;
  306. Inc(E,(A shl 5) + (A shr 27) + (B xor C xor D) + T - $359D3E2A);
  307. B := (B shl 30) + (B shr 2);
  308. T := W[(i+14) and $F] xor W[(i+9) and $F];
  309. T := T xor W[(i+3) and $F] xor W[(i+1) and $F];
  310. T := (T shl 1) or (T shr 31);
  311. W[(i+1) and $F] := T;
  312. Inc(D,(E shl 5) + (E shr 27) + (A xor B xor C) + T - $359D3E2A);
  313. A := (A shl 30) + (A shr 2);
  314. T := W[(i+15) and $F] xor W[(i+10) and $F];
  315. T := T xor W[(i+4) and $F] xor W[(i+2) and $F];
  316. T := (T shl 1) or (T shr 31);
  317. W[(i+2) and $F] := T;
  318. Inc(C,(D shl 5) + (D shr 27) + (E xor A xor B) + T - $359D3E2A);
  319. E := (E shl 30) + (E shr 2);
  320. T := W[i and $F] xor W[(i+11) and $F];
  321. T := T xor W[(i+5) and $F] xor W[(i+3) and $F];
  322. T := (T shl 1) or (T shr 31);
  323. W[(i+3) and $F] := T;
  324. Inc(B,(C shl 5) + (C shr 27) + (D xor E xor A) + T - $359D3E2A);
  325. D := (D shl 30) + (D shr 2);
  326. T := W[(i+1) and $F] xor W[(i+12) and $F];
  327. T := T xor W[(i+6) and $F] xor W[(i+4) and $F];
  328. T := (T shl 1) or (T shr 31);
  329. W[(i+4) and $F] := T;
  330. Inc(A,(B shl 5) + (B shr 27) + (C xor D xor E) + T - $359D3E2A);
  331. C := (C shl 30) + (C shr 2);
  332. Inc(i,5);
  333. until i = 80;
  334. FCheckSum[0]:= FCheckSum[0] + A;
  335. FCheckSum[1]:= FCheckSum[1] + B;
  336. FCheckSum[2]:= FCheckSum[2] + C;
  337. FCheckSum[3]:= FCheckSum[3] + D;
  338. FCheckSum[4]:= FCheckSum[4] + E;
  339. end;
  340. function TIdHashSHA1.NativeGetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes;
  341. var
  342. LSize: Integer;
  343. LLenHi: UInt32;
  344. LLenLo: UInt32;
  345. I: Integer;
  346. begin
  347. Result := nil;
  348. FCheckSum[0] := $67452301;
  349. FCheckSum[1] := $EFCDAB89;
  350. FCheckSum[2] := $98BADCFE;
  351. FCheckSum[3] := $10325476;
  352. FCheckSum[4] := $C3D2E1F0;
  353. LLenHi := 0;
  354. LLenLo := 0;
  355. // Code the entire file in complete 64-byte chunks.
  356. while ASize >= 64 do begin
  357. LSize := ReadTIdBytesFromStream(AStream, FCBuffer, 64);
  358. // TODO: handle stream read error
  359. Inc(LLenLo, LSize * 8);
  360. if LLenLo < UInt32(LSize * 8) then begin
  361. Inc(LLenHi);
  362. end;
  363. Coder;
  364. Dec(ASize, LSize);
  365. end;
  366. // Read the last set of bytes.
  367. LSize := ReadTIdBytesFromStream(AStream, FCBuffer, ASize);
  368. // TODO: handle stream read error
  369. Inc(LLenLo, LSize * 8);
  370. if LLenLo < UInt32(LSize * 8) then begin
  371. Inc(LLenHi);
  372. end;
  373. FCBuffer[LSize] := $80;
  374. if LSize >= 56 then begin
  375. for I := (LSize + 1) to 63 do begin
  376. FCBuffer[i] := 0;
  377. end;
  378. Coder;
  379. LSize := -1;
  380. end;
  381. for I := (LSize + 1) to 55 do begin
  382. FCBuffer[i] := 0;
  383. end;
  384. FCBuffer[56] := (LLenHi shr 24);
  385. FCBuffer[57] := (LLenHi shr 16) and $FF;
  386. FCBuffer[58] := (LLenHi shr 8) and $FF;
  387. FCBuffer[59] := (LLenHi and $FF);
  388. FCBuffer[60] := (LLenLo shr 24);
  389. FCBuffer[61] := (LLenLo shr 16) and $FF;
  390. FCBuffer[62] := (LLenLo shr 8) and $FF;
  391. FCBuffer[63] := (LLenLo and $FF);
  392. Coder;
  393. FCheckSum[0] := SwapLongWord(FCheckSum[0]);
  394. FCheckSum[1] := SwapLongWord(FCheckSum[1]);
  395. FCheckSum[2] := SwapLongWord(FCheckSum[2]);
  396. FCheckSum[3] := SwapLongWord(FCheckSum[3]);
  397. FCheckSum[4] := SwapLongWord(FCheckSum[4]);
  398. SetLength(Result, SizeOf(UInt32)*5);
  399. for I := 0 to 4 do begin
  400. CopyTIdUInt32(FCheckSum[I], Result, SizeOf(UInt32)*I);
  401. end;
  402. end;
  403. function TIdHashSHA1.HashToHex(const AHash: TIdBytes): String;
  404. begin
  405. Result := LongWordHashToHex(AHash, 5);
  406. end;
  407. {$ENDIF}
  408. class function TIdHashSHA1.IsAvailable : Boolean;
  409. begin
  410. Result := True;
  411. end;
  412. {$IFNDEF DOTNET}
  413. { TIdHashSHA224 }
  414. function TIdHashSHA224.InitHash: TIdHashIntCtx;
  415. begin
  416. Result := GetSHA224HashInst;
  417. end;
  418. class function TIdHashSHA224.IsAvailable: Boolean;
  419. begin
  420. Result := IsIntfAvailable and IsSHA224HashIntfAvail;
  421. end;
  422. {$ENDIF}
  423. { TIdHashSHA256 }
  424. function TIdHashSHA256.InitHash: TIdHashIntCtx;
  425. begin
  426. Result := GetSHA256HashInst;
  427. end;
  428. class function TIdHashSHA256.IsAvailable : Boolean;
  429. begin
  430. Result := IsIntfAvailable and IsSHA256HashIntfAvail;
  431. end;
  432. { TIdHashSHA384 }
  433. function TIdHashSHA384.InitHash: TIdHashIntCtx;
  434. begin
  435. Result := GetSHA384HashInst;
  436. end;
  437. class function TIdHashSHA384.IsAvailable: Boolean;
  438. begin
  439. Result := IsIntfAvailable and IsSHA384HashIntfAvail;
  440. end;
  441. { TIdHashSHA512 }
  442. function TIdHashSHA512.InitHash: TIdHashIntCtx;
  443. begin
  444. Result := GetSHA512HashInst;
  445. end;
  446. class function TIdHashSHA512.IsAvailable: Boolean;
  447. begin
  448. Result := IsIntfAvailable and IsSHA512HashIntfAvail;
  449. end;
  450. end.