kdf.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. unit kdf;
  2. {(Password Based) Key Derivation Functions}
  3. interface
  4. (*************************************************************************
  5. DESCRIPTION : (Password Based) Key Derivation Functions
  6. REQUIREMENTS : TP5-7, D1-D7/D9-D10/D12/D17-D18, FPC, VP, WDOSX
  7. EXTERNAL DATA : ---
  8. MEMORY USAGE : ---
  9. DISPLAY MODE : ---
  10. REFERENCES : http://tools.ietf.org/html/rfc2898
  11. http://tools.ietf.org/html/rfc3211 [includes test vectors]
  12. http://tools.ietf.org/html/rfc5869 [includes test vectors]
  13. http://www.di-mgt.com.au/cryptoKDFs.html [includes test vectors]
  14. Version Date Author Modification
  15. ------- -------- ------- ------------------------------------------
  16. 0.10 17.01.06 W.Ehrhardt Initial version based on keyderiv 1.29
  17. 0.11 17.01.06 we error codes
  18. 0.12 23.01.06 we new names: unit pb_kdf, functions kdf2/s
  19. 0.13 22.06.08 we Make IncMSB work with FPC -dDebug
  20. 0.14 12.07.08 we Rename old kdf2 to pbkdf2, unit kdf
  21. 0.15 12.07.08 we pbkdf1
  22. 0.16 12.07.08 we kdf1, kdf2, kdf3, mgf1
  23. 0.17 12.11.08 we uses BTypes, Ptr2Inc and/or Str255
  24. 0.18 25.04.09 we updated RFC URL(s)
  25. 0.19 19.08.10 we kdf_err_nil_input
  26. 0.20 20.08.10 we hkdf/hkdfs
  27. 0.21 15.08.14 we pbkdf2 functions with longint sLen, dkLen
  28. 0.22 16.08.15 we Removed $ifdef DLL / stdcall
  29. **************************************************************************)
  30. (*-------------------------------------------------------------------------
  31. (C) Copyright 2006-2015 Wolfgang Ehrhardt
  32. This software is provided 'as-is', without any express or implied warranty.
  33. In no event will the authors be held liable for any damages arising from
  34. the use of this software.
  35. Permission is granted to anyone to use this software for any purpose,
  36. including commercial applications, and to alter it and redistribute it
  37. freely, subject to the following restrictions:
  38. 1. The origin of this software must not be misrepresented; you must not
  39. claim that you wrote the original software. If you use this software in
  40. a product, an acknowledgment in the product documentation would be
  41. appreciated but is not required.
  42. 2. Altered source versions must be plainly marked as such, and must not be
  43. misrepresented as being the original software.
  44. 3. This notice may not be removed or altered from any source distribution.
  45. ----------------------------------------------------------------------------*)
  46. {$i STD.INC}
  47. uses
  48. BTypes,Hash,HMAC;
  49. const
  50. kdf_err_nil_pointer = $0001; {phash descriptor is nil}
  51. kdf_err_digestlen = $0002; {digest length from descriptor is zero}
  52. kdf_err_invalid_dKLen = $0003; {dKLen greater than hash digest length}
  53. kdf_err_nil_input = $0004; {input nil pointer and non-zero length}
  54. function kdf1(phash: PHashDesc; Z: pointer; zLen: word; pOtherInfo: pointer; oiLen: word; var DK; dkLen: word): integer;
  55. {-Derive key DK from shared secret Z using optional OtherInfo, hash function from phash}
  56. function kdf2(phash: PHashDesc; Z: pointer; zLen: word; pOtherInfo: pointer; oiLen: word; var DK; dkLen: word): integer;
  57. {-Derive key DK from shared secret Z using optional OtherInfo, hash function from phash}
  58. function kdf3(phash: PHashDesc; Z: pointer; zLen: word; pOtherInfo: pointer; oiLen: word; var DK; dkLen: word): integer;
  59. {-Derive key DK from shared secret Z using optional OtherInfo, hash function from phash}
  60. function mgf1(phash: PHashDesc; pSeed: pointer; sLen: word; var Mask; mLen: word): integer;
  61. {-Derive Mask from seed, hash function from phash, Mask Generation Function 1 for PKCS #1}
  62. function pbkdf1(phash: PHashDesc; pPW: pointer; pLen: word; salt: pointer; C: longint; var DK; dkLen: word): integer;
  63. {-Derive key DK from password pPW using 8 byte salt and iteration count C, uses hash function from phash}
  64. function pbkdf1s(phash: PHashDesc; sPW: Str255; salt: pointer; C: longint; var DK; dkLen: word): integer;
  65. {-Derive key DK from password string sPW using 8 byte salt and iteration count C, uses hash function from phash}
  66. function pbkdf2(phash: PHashDesc; pPW: pointer; pLen: word; salt: pointer; sLen,C: longint; var DK; dkLen: longint): integer;
  67. {-Derive key DK from password pPW using salt and iteration count C, uses hash function from phash}
  68. function pbkdf2s(phash: PHashDesc; sPW: Str255; salt: pointer; sLen,C: longint; var DK; dkLen: longint): integer;
  69. {-Derive key DK from password string sPW using salt and iteration count C, uses hash function from phash}
  70. function hkdf(phash: PHashDesc; {Descriptor of the Hash to use}
  71. pIKM: pointer; L_IKM: word; {input key material: addr/length}
  72. salt: pointer; L_salt: word; {optional salt; can be nil: see below }
  73. info: pointer; L_info: word; {optional context/application specific information}
  74. var DK; dkLen: word): integer; {output key material: addr/length}
  75. {-Derive key DK from input key material and salt/info, uses hash function from phash}
  76. { If salt=nil then phash^.HDigestLen binary zeros will be used as salt.}
  77. function hkdfs(phash: PHashDesc; sIKM: Str255; {Hash; input key material as string}
  78. salt: pointer; L_salt: word; {optional salt; can be nil: see below }
  79. info: pointer; L_info: word; {optional context/application specific information}
  80. var DK; dkLen: word): integer; {output key material: addr/length}
  81. {-Derive key DK from input key material and salt/info, uses hash function from phash}
  82. { If salt=nil then phash^.HDigestLen binary zeros will be used as salt.}
  83. implementation
  84. {helper type}
  85. type
  86. TMSBA = array[0..3] of byte;
  87. {---------------------------------------------------------------------------}
  88. procedure IncMSB(var CTR: TMSBA);
  89. {-Increment CTR[3]..CTR[0], i.e. 32 Bit MSB counter}
  90. var
  91. j: integer;
  92. begin
  93. for j:=3 downto 0 do begin
  94. if CTR[j]=$FF then CTR[j] := 0
  95. else begin
  96. inc(CTR[j]);
  97. exit;
  98. end;
  99. end;
  100. end;
  101. {---------------------------------------------------------------------------}
  102. function pbkdf1(phash: PHashDesc; pPW: pointer; pLen: word; salt: pointer; C: longint; var DK; dkLen: word): integer;
  103. {-Derive key DK from password pPW using 8 byte salt and iteration count C, uses hash function from phash}
  104. var
  105. ctx: THashContext;
  106. Digest: THashDigest;
  107. hlen: word;
  108. begin
  109. if (phash=nil) or (phash^.HSig<>C_HashSig) then begin
  110. pbkdf1 := kdf_err_nil_pointer;
  111. exit;
  112. end;
  113. hLen := phash^.HDigestLen;
  114. if hLen=0 then begin
  115. pbkdf1 := kdf_err_digestlen;
  116. exit;
  117. end;
  118. if hLen<dKLen then begin
  119. pbkdf1 := kdf_err_invalid_dKLen;
  120. exit;
  121. end;
  122. if ((pPW=nil) and (pLen>0)) or (salt=nil) then begin
  123. pbkdf1 := kdf_err_nil_input;
  124. exit;
  125. end;
  126. pbkdf1 := 0;
  127. {calculate T_1 = hash(PW || salt)}
  128. with phash^ do begin
  129. HInit(ctx);
  130. HUpdateXL(ctx, pPW, pLen);
  131. HUpdateXL(ctx, salt, 8);
  132. HFinal(ctx, Digest);
  133. end;
  134. while C>1 do begin
  135. HashFullXL(phash, Digest, @Digest, hlen);
  136. dec(C);
  137. end;
  138. move(Digest, DK, dKLen);
  139. end;
  140. {---------------------------------------------------------------------------}
  141. function pbkdf1s(phash: PHashDesc; sPW: Str255; salt: pointer; C: longint; var DK; dkLen: word): integer;
  142. {-Derive key DK from password string sPW using 8 byte salt and iteration count C, uses hash function from phash}
  143. begin
  144. pbkdf1s := pbkdf1(phash, @sPW[1], length(sPW), salt, C, DK, dkLen);
  145. end;
  146. {---------------------------------------------------------------------------}
  147. function pbkdf2(phash: PHashDesc; pPW: pointer; pLen: word; salt: pointer; sLen,C: longint; var DK; dkLen: longint): integer;
  148. {-Derive key DK from password pPW using salt and iteration count C, uses hash function from phash}
  149. var
  150. k, hLen: word;
  151. i, j, lk: longint;
  152. pDK: pByte; {pointer to DK }
  153. ii: TMSBA; {i in big endian}
  154. u, ucum: THashDigest;
  155. ctx: THMAC_Context;
  156. begin
  157. if phash=nil then begin
  158. pbkdf2 := kdf_err_nil_pointer;
  159. exit;
  160. end;
  161. if phash^.HDigestLen=0 then begin
  162. pbkdf2 := kdf_err_digestlen;
  163. exit;
  164. end;
  165. if ((pPW=nil) and (pLen>0)) or ((salt=nil) and (sLen>0)) then begin
  166. pbkdf2 := kdf_err_nil_input;
  167. exit;
  168. end;
  169. pbkdf2 := 0;
  170. hLen := phash^.HDigestLen;
  171. lk := 0;
  172. pDK := pByte(@DK);
  173. fillchar(ii, sizeof(ii), 0);
  174. for i:=1 to 1 + pred(dkLen) div hLen do begin
  175. IncMSB(ii);
  176. fillchar(ucum, sizeof(ucum),0);
  177. for j:=1 to C do begin
  178. hmac_init(ctx, phash, pPW, pLen);
  179. if j=1 then begin
  180. {U_1 = PRF(pPW, salt || ii)}
  181. hmac_updateXL(ctx, salt, sLen);
  182. hmac_updateXL(ctx, @ii, 4);
  183. end
  184. else begin
  185. {U_i = PRF(pPW, U_(i-1)}
  186. hmac_updateXL(ctx, @u, hLen);
  187. end;
  188. hmac_final(ctx, u);
  189. {update cumulative XOR U_i}
  190. for k:=0 to hLen-1 do ucum[k] := ucum[k] xor u[k];
  191. end;
  192. {T_i = F(P,S,C,l) = ucum}
  193. for k:=0 to hLen-1 do begin
  194. {concat T_i}
  195. if lk<dkLen then begin
  196. pDK^ := ucum[k];
  197. inc(lk);
  198. inc(Ptr2Inc(pDK));
  199. end;
  200. end;
  201. end;
  202. end;
  203. {---------------------------------------------------------------------------}
  204. function pbkdf2s(phash: PHashDesc; sPW: Str255; salt: pointer; sLen, C: longint; var DK; dkLen: longint): integer;
  205. {-Derive key DK from password string sPW using salt and iteration count C, uses hash function from phash}
  206. begin
  207. pbkdf2s := pbkdf2(phash, @sPW[1], length(sPW), salt, sLen, C, DK, dkLen);
  208. end;
  209. {---------------------------------------------------------------------------}
  210. function kdfx(phash: PHashDesc; x: byte; Z, pOtherInfo: pointer; zLen, oLen: word; var DK; dkLen: word): integer;
  211. {-Derive key DK from shared secret Z using optional OtherInfo, hash function from phash}
  212. { Internal function for all kdf1, kdf2, kdf3, mgf1}
  213. var
  214. ctr: TMSBA; {i in big endian}
  215. ctx: THashContext;
  216. pDK: pByte; {pointer to DK }
  217. Digest: THashDigest;
  218. i, k, lk, hLen: word;
  219. begin
  220. if (phash=nil) or (phash^.HSig<>C_HashSig) then begin
  221. kdfx := kdf_err_nil_pointer;
  222. exit;
  223. end;
  224. hLen := phash^.HDigestLen;
  225. if hLen=0 then begin
  226. kdfx := kdf_err_digestlen;
  227. exit;
  228. end;
  229. if ((Z=nil) and (zLen>0)) or ((pOtherInfo=nil) and (oLen>0)) then begin
  230. kdfx := kdf_err_nil_input;
  231. exit;
  232. end;
  233. kdfx := 0;
  234. fillchar(ctr, sizeof(ctr), 0);
  235. if x=2 then IncMSB(ctr);
  236. lk := 0;
  237. pDK := pByte(@DK);
  238. for i:=1 to 1 + pred(dkLen) div hLen do begin
  239. if x=3 then begin
  240. {Hash(ctr || Z || [OtherInfo])} {x=3}
  241. phash^.HInit(ctx);
  242. phash^.HUpdateXL(ctx, @ctr, sizeof(ctr));
  243. phash^.HUpdateXL(ctx, Z, zLen);
  244. end
  245. else begin
  246. {Hash(Z || ctr || [OtherInfo])} {x=1,2}
  247. phash^.HInit(ctx);
  248. phash^.HUpdateXL(ctx, Z, zLen);
  249. phash^.HUpdateXL(ctx, @ctr, sizeof(ctr));
  250. end;
  251. if oLen<>0 then phash^.HUpdateXL(ctx, pOtherInfo, oLen);
  252. phash^.HFinal(ctx, Digest);
  253. for k:=0 to hLen-1 do begin
  254. {store T_i}
  255. if lk<dkLen then begin
  256. pDK^ := Digest[k];
  257. inc(lk);
  258. inc(Ptr2Inc(pDK));
  259. end
  260. else exit;
  261. end;
  262. IncMSB(ctr);
  263. end;
  264. end;
  265. {---------------------------------------------------------------------------}
  266. function kdf1(phash: PHashDesc; Z: pointer; zLen: word; pOtherInfo: pointer; oiLen: word; var DK; dkLen: word): integer;
  267. {-Derive key DK from shared secret Z using optional OtherInfo, hash function from phash}
  268. begin
  269. kdf1 := kdfx(phash, 1, Z, pOtherInfo, zLen, oiLen, DK, dkLen);
  270. end;
  271. {---------------------------------------------------------------------------}
  272. function kdf2(phash: PHashDesc; Z: pointer; zLen: word; pOtherInfo: pointer; oiLen: word; var DK; dkLen: word): integer;
  273. {-Derive key DK from shared secret Z using optional OtherInfo, hash function from phash}
  274. begin
  275. kdf2 := kdfx(phash, 2, Z, pOtherInfo, zLen, oiLen, DK, dkLen);
  276. end;
  277. {---------------------------------------------------------------------------}
  278. function kdf3(phash: PHashDesc; Z: pointer; zLen: word; pOtherInfo: pointer; oiLen: word; var DK; dkLen: word): integer;
  279. {-Derive key DK from shared secret Z using optional OtherInfo, hash function from phash}
  280. begin
  281. kdf3 := kdfx(phash, 3, Z, pOtherInfo, zLen, oiLen, DK, dkLen);
  282. end;
  283. {---------------------------------------------------------------------------}
  284. function mgf1(phash: PHashDesc; pSeed: pointer; sLen: word; var Mask; mLen: word): integer;
  285. {-Derive Mask from seed, hash function from phash, Mask Generation Function 1 for PKCS #1}
  286. begin
  287. mgf1 := kdfx(phash, 1, pSeed, nil, sLen, 0, Mask, mLen);
  288. end;
  289. {---------------------------------------------------------------------------}
  290. function hkdf(phash: PHashDesc; {Descriptor of the Hash to use}
  291. pIKM: pointer; L_IKM: word; {input key material: addr/length}
  292. salt: pointer; L_salt: word; {optional salt; can be nil: see below }
  293. info: pointer; L_info: word; {optional context/application specific information}
  294. var DK; dkLen: word): integer; {output key material: addr/length}
  295. {-Derive key DK from input key material and salt/info, uses hash function from phash}
  296. { If salt=nil then phash^.HDigestLen binary zeros will be used as salt.}
  297. var
  298. PRK,TI: THashDigest;
  299. ctx: THMAC_Context;
  300. i,k,hLen,lt,lk: word;
  301. ctr: byte;
  302. pDK: pByte;
  303. begin
  304. {Ref: (IETF) rfc5869 - H. Krawczyk, P. Eronen, May 2010}
  305. {HMAC-based Extract-and-Expand Key Derivation Function (HKDF)}
  306. {Check input parameters}
  307. if (phash=nil) or (phash^.HSig<>C_HashSig) then begin
  308. hkdf := kdf_err_nil_pointer;
  309. exit;
  310. end;
  311. if ((pIKM=nil) and (L_IKM>0)) or ((info=nil) and (L_info>0)) then begin
  312. hkdf := kdf_err_nil_input;
  313. exit;
  314. end;
  315. hLen := phash^.HDigestLen;
  316. if hLen=0 then begin
  317. hkdf := kdf_err_digestlen;
  318. exit;
  319. end;
  320. {Stage 1: Extract}
  321. hkdf := 0;
  322. {if salt=nil then hLen binary zeros are used}
  323. if salt=nil then begin
  324. fillchar(TI, sizeof(TI), 0);
  325. hmac_init(ctx, phash, @TI, hLen);
  326. end
  327. else hmac_init(ctx, phash, salt, L_salt);
  328. hmac_update(ctx, pIKM, L_IKM);
  329. hmac_final(ctx, PRK);
  330. {Stage 2: Expand}
  331. lt := 0;
  332. lk := 0;
  333. ctr := 1;
  334. pDK := pByte(@DK);
  335. for i:=1 to 1 + pred(dkLen) div hLen do begin
  336. {calculate T_i from T_(i-1), info, and ctr}
  337. hmac_init(ctx, phash, @PRK, hLen);
  338. hmac_update(ctx, @TI, lt);
  339. hmac_update(ctx, info, L_info);
  340. hmac_update(ctx, @ctr, 1);
  341. hmac_final(ctx, TI);
  342. for k:=0 to hLen-1 do begin
  343. {store T_i}
  344. if lk<dkLen then begin
  345. pDK^ := TI[k];
  346. inc(lk);
  347. inc(Ptr2Inc(pDK));
  348. end
  349. else exit;
  350. end;
  351. lt := hLen;
  352. inc(ctr);
  353. end;
  354. end;
  355. {---------------------------------------------------------------------------}
  356. function hkdfs(phash: PHashDesc; sIKM: Str255; {Hash; input key material as string}
  357. salt: pointer; L_salt: word; {optional salt; can be nil: see below }
  358. info: pointer; L_info: word; {optional context/application specific information}
  359. var DK; dkLen: word): integer; {output key material: addr/length}
  360. {-Derive key DK from input key material and salt/info, uses hash function from phash}
  361. { If salt=nil then phash^.HDigestLen binary zeros will be used as salt.}
  362. begin
  363. hkdfs := hkdf(phash,@sIKM[1],length(sIKM),salt,L_salt,info,L_info,DK,dkLen);
  364. end;
  365. end.