PBKDF2.pas 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. unit PBKDF2;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2024 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. PBKDF2-HMAC-SHA256 password-based key derivation
  8. }
  9. interface
  10. uses
  11. System.SysUtils;
  12. function PBKDF2SHA256(Password: TBytes; const Salt: TBytes; const Iterations, KeyLength: Integer): TBytes; overload;
  13. function PBKDF2SHA256(const Password: String; const Salt: TBytes; const Iterations, KeyLength: Integer): TBytes; overload;
  14. implementation
  15. uses
  16. System.Hash, System.Math;
  17. function PBKDF2SHA256(Password: TBytes; const Salt: TBytes; const Iterations, KeyLength: Integer): TBytes;
  18. begin
  19. var HashVersion := THashSHA2.TSHA2Version.SHA256;
  20. var Hash := THashSHA2.Create(HashVersion); { This is a record so no need to free }
  21. var HashSize := Hash.GetHashSize;
  22. if Length(Password) > Hash.GetBlockSize then begin
  23. { Pre-hash password so THashSHA2.GetHMACAsBytes wont do this over and over again }
  24. Hash.Update(Password);
  25. Password := Hash.HashAsBytes;
  26. end;
  27. SetLength(Result, KeyLength);
  28. var BytesDone := 0;
  29. var L := Ceil(KeyLength / HashSize);
  30. for var Block := 1 to L do begin
  31. var SaltAndBlock := Salt + [Byte(Block shr 24), Byte(Block shr 16), Byte(Block shr 8), Byte(Block)];
  32. var U := THashSHA2.GetHMACAsBytes(SaltAndBlock, Password, HashVersion);
  33. var F := U;
  34. for var I := 2 to Iterations do begin
  35. U := THashSHA2.GetHMACAsBytes(U, Password, HashVersion);
  36. for var J := 0 to High(F) do
  37. F[J] := F[J] xor U[J];
  38. end;
  39. var BytesLeft := KeyLength - BytesDone;
  40. var BytesToCopy := Min(BytesLeft, Length(F));
  41. Move(F[0], Result[BytesDone], BytesToCopy);
  42. Inc(BytesDone, BytesToCopy);
  43. end;
  44. end;
  45. function PBKDF2SHA256(const Password: String; const Salt: TBytes; const Iterations, KeyLength: Integer): TBytes;
  46. function StringToBytes(const S: String): TBytes;
  47. begin
  48. var N := Length(S)*SizeOf(S[1]);
  49. SetLength(Result, N);
  50. if N > 0 then
  51. Move(S[1], Result[0], N);
  52. end;
  53. begin
  54. Result := PBKDF2SHA256(StringToBytes(Password), Salt, Iterations, KeyLength);
  55. end;
  56. {.$DEFINE TEST}
  57. {$IFDEF TEST}
  58. {$C+}
  59. procedure TestPBKDF2SHA256;
  60. function AnsiStringToBytes(const S: AnsiString): TBytes;
  61. begin
  62. var N := Length(S)*SizeOf(S[1]);
  63. SetLength(Result, N);
  64. if N > 0 then
  65. Move(S[1], Result[0], N);
  66. end;
  67. procedure Test(const Key1, Key2: TBytes);
  68. begin
  69. Assert(Length(Key1) = Length(Key2));
  70. for var I := 0 to High(Key1) do
  71. Assert(Key1[I] = Key2[I]);
  72. end;
  73. begin
  74. //https://stackoverflow.com/a/5136918/301485
  75. var Password := AnsiStringToBytes('password');
  76. var Salt := AnsiStringToBytes('salt');
  77. Test(PBKDF2SHA256(Password, Salt, 1, 32),
  78. [$12, $0f, $b6, $cf, $fc, $f8, $b3, $2c, $43, $e7, $22, $52, $56, $c4, $f8, $37, $a8, $65, $48, $c9, $2c, $cc, $35, $48, $08, $05, $98, $7c, $b7, $0b, $e1, $7b]);
  79. Test(PBKDF2SHA256(Password, Salt, 4096, 32),
  80. [$c5, $e4, $78, $d5, $92, $88, $c8, $41, $aa, $53, $0d, $b6, $84, $5c, $4c, $8d, $96, $28, $93, $a0, $01, $ce, $4e, $11, $a4, $96, $38, $73, $aa, $98, $13, $4a]);
  81. Password := AnsiStringToBytes('passwordPASSWORDpassword');
  82. Salt := AnsiStringToBytes('saltSALTsaltSALTsaltSALTsaltSALTsalt');
  83. Test(PBKDF2SHA256(Password, Salt, 4096, 40),
  84. [$34, $8c, $89, $db, $cb, $d3, $2b, $2f, $32, $d8, $14, $b8, $11, $6e, $84, $cf, $2b, $17, $34, $7e, $bc, $18, $00, $18, $1c, $4e, $2a, $1f, $b8, $dd, $53, $e1, $c6, $35, $51, $8c, $7d, $ac, $47, $e9]);
  85. Password := AnsiStringToBytes('pass'#0'word');
  86. Salt := AnsiStringToBytes('sa'#0'lt');
  87. Test(PBKDF2SHA256(Password, Salt, 4096, 16),
  88. [$89, $b6, $9d, $05, $16, $f8, $29, $89, $3c, $69, $62, $26, $65, $0a, $86, $87]);
  89. //https://en.wikipedia.org/wiki/PBKDF2 - but with SHA256 instead of SHA1 - first tested using SHA1
  90. Password := AnsiStringToBytes('plnlrtfpijpuhqylxbgqiiyipieyxvfsavzgxbbcfusqkozwpngsyejqlmjsytrmd');
  91. Salt := [$a0, $09, $c1, $a4, $85, $91, $2c, $6a, $e6, $30, $d3, $e7, $44, $24, $0b, $04];
  92. Test(PBKDF2SHA256(Password, Salt, 1000, 16),
  93. [$28, $86, $9b, $5f, $31, $ae, $29, $23, $6f, $16, $4c, $5c, $b3, $3e, $2e, $3b]);
  94. end;
  95. initialization
  96. TestPBKDF2SHA256;
  97. {$ENDIF}
  98. end.