Browse Source

Add password pre-hash as described on https://en.wikipedia.org/wiki/PBKDF2 and in other places. Also some cleanup.

Martijn Laan 11 months ago
parent
commit
3016cc5912
1 changed files with 23 additions and 9 deletions
  1. 23 9
      Components/PBKDF2.pas

+ 23 - 9
Components/PBKDF2.pas

@@ -14,7 +14,7 @@ interface
 uses
   System.SysUtils;
 
-function PBKDF2SHA256(const Password, Salt: TBytes; const Iterations, KeyLength: Integer): TBytes; overload;
+function PBKDF2SHA256(Password: TBytes; const Salt: TBytes; const Iterations, KeyLength: Integer): TBytes; overload;
 function PBKDF2SHA256(const Password: String; const Salt: TBytes; const Iterations, KeyLength: Integer): TBytes; overload;
 
 implementation
@@ -22,11 +22,19 @@ implementation
 uses
   System.Hash, System.Math;
 
-function PBKDF2SHA256(const Password, Salt: TBytes; const Iterations, KeyLength: Integer): TBytes;
+function PBKDF2SHA256(Password: TBytes; const Salt: TBytes; const Iterations, KeyLength: Integer): TBytes;
 begin
-  var Hash := THashSHA2.Create(THashSHA2.TSHA2Version.SHA256); { This is a record so no need to free }
+  var HashVersion := THashSHA2.TSHA2Version.SHA256;
+
+  var Hash := THashSHA2.Create(HashVersion); { This is a record so no need to free }
   var HashSize := Hash.GetHashSize;
 
+  if Length(Password) > Hash.GetBlockSize then begin
+    { Pre-hash password so THashSHA2.GetHMACAsBytes wont do this over and over again }
+    Hash.Update(Password);
+    Password := Hash.HashAsBytes;
+  end;
+
   SetLength(Result, KeyLength);
   var BytesDone := 0;
 
@@ -34,11 +42,11 @@ begin
 
   for var Block := 1 to L do begin
     var SaltAndBlock := Salt + [Byte(Block shr 24), Byte(Block shr 16), Byte(Block shr 8), Byte(Block)];
-    var U := Hash.GetHMACAsBytes(SaltAndBlock, Password);
+    var U := THashSHA2.GetHMACAsBytes(SaltAndBlock, Password, HashVersion);
     var F := U;
 
     for var I := 2 to Iterations do begin
-      U := Hash.GetHMACAsBytes(U, Password);
+      U := THashSHA2.GetHMACAsBytes(U, Password, HashVersion);
       for var J := 0 to High(F) do
         F[J] := F[J] xor U[J];
     end;
@@ -92,19 +100,25 @@ begin
   var Password := AnsiStringToBytes('password');
   var Salt := AnsiStringToBytes('salt');
   Test(PBKDF2SHA256(Password, Salt, 1, 32),
-       TBytes.Create($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));
+       [$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]);
   Test(PBKDF2SHA256(Password, Salt, 4096, 32),
-       TBytes.Create($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));
+       [$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]);
 
   Password := AnsiStringToBytes('passwordPASSWORDpassword');
   Salt := AnsiStringToBytes('saltSALTsaltSALTsaltSALTsaltSALTsalt');
   Test(PBKDF2SHA256(Password, Salt, 4096, 40),
-       TBytes.Create($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));
+       [$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]);
 
   Password := AnsiStringToBytes('pass'#0'word');
   Salt := AnsiStringToBytes('sa'#0'lt');
   Test(PBKDF2SHA256(Password, Salt, 4096, 16),
-       TBytes.Create($89, $b6, $9d, $05, $16, $f8, $29, $89, $3c, $69, $62, $26, $65, $0a, $86, $87));
+       [$89, $b6, $9d, $05, $16, $f8, $29, $89, $3c, $69, $62, $26, $65, $0a, $86, $87]);
+
+  //https://en.wikipedia.org/wiki/PBKDF2 - but with SHA256 instead of SHA1 - first tested using SHA1
+  Password := AnsiStringToBytes('plnlrtfpijpuhqylxbgqiiyipieyxvfsavzgxbbcfusqkozwpngsyejqlmjsytrmd');
+  Salt := [$a0, $09, $c1, $a4, $85, $91, $2c, $6a, $e6, $30, $d3, $e7, $44, $24, $0b, $04];
+  Test(PBKDF2SHA256(Password, Salt, 1000, 16),
+       [$28, $86, $9b, $5f, $31, $ae, $29, $23, $6f, $16, $4c, $5c, $b3, $3e, $2e, $3b]);
 end;
 
 initialization